Add FM filtering

This commit is contained in:
Geoffrey Merck 2020-04-24 09:10:50 +02:00
parent 507a4bf64c
commit f8d082e2dd
4 changed files with 170 additions and 9 deletions

14
FM.cpp
View File

@ -23,9 +23,9 @@
// 3 stage IIR Butterworth filter generated (if you change the order change the size of m_filterState). Also change the ordre in init call below
// 0.2db band pass ripple
// 300 - 2700Hz
q15_t FILTER_COEFFS[] = { 362, 724, 362,16384,-18947,10676, 14,//1st stage
16384, 0, -16384,16384,-25170, 9526, 14,//2nd stage
16384,-32768, 16384,16384,-32037,15730,14};//3rd stage
q15_t FILTER_COEFFS[] = {362,724,362,16384,-18947,10676,
16384,0,-16384,16384,-25170,9526,
16384,-32768,16384,16384,-32037,15730};
CFM::CFM() :
@ -44,14 +44,14 @@ m_kerchunkTimer(),
m_ackMinTimer(),
m_ackDelayTimer(),
m_hangTimer(),
m_filter()
m_filterStage1( 724, 1448, 724, 32768, -37895, 21352),
m_filterStage2(32768, 0,-32768, 32768, -50339, 19052),
m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460)
{
arm_biquad_cascade_df1_init_q15(&m_filter, 3, FILTER_COEFFS, m_filterState, 0);
}
void CFM::samples(q15_t* samples, uint8_t length)
{
arm_biquad_casd_df1_inst_q15* filterPtr = &m_filter;
uint8_t i = 0;
for (; i < length; i++) {
q15_t currentSample = samples[i];//save to a local variable to avoid indirection on every access
@ -97,7 +97,7 @@ void CFM::samples(q15_t* samples, uint8_t length)
if (!m_callsign.isRunning() && !m_rfAck.isRunning())
currentSample += m_timeoutTone.getAudio();
arm_biquad_cascade_df1_fast_q15(filterPtr, samples +i, &currentSample, 1);
currentSample = q15_t(m_filterStage3.filter(m_filterStage2.filter(m_filterStage1.filter(currentSample))));
currentSample += m_ctcssTX.getAudio();

6
FM.h
View File

@ -26,6 +26,7 @@
#include "FMTimeout.h"
#include "FMKeyer.h"
#include "FMTimer.h"
#include "FMDirectForm1.h"
enum FM_STATE {
FS_LISTENING,
@ -70,8 +71,9 @@ private:
CFMTimer m_ackMinTimer;
CFMTimer m_ackDelayTimer;
CFMTimer m_hangTimer;
arm_biquad_casd_df1_inst_q15 m_filter;
q15_t m_filterState[12];//must be filterOrder * 4 long
CFMDirectFormI m_filterStage1;
CFMDirectFormI m_filterStage2;
CFMDirectFormI m_filterStage3;
void stateMachine(bool validSignal, uint8_t length);
void listeningState(bool validSignal);

114
FMDirectForm1.h Normal file
View File

@ -0,0 +1,114 @@
/*******************************************************************************
This header file has been taken from:
"A Collection of Useful C++ Classes for Digital Signal Processing"
By Vinnie Falco
Bernd Porr adapted it for Linux and turned it into a filter using
fixed point arithmetic.
--------------------------------------------------------------------------------
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
Copyright (c) 2009 by Vinnie Falco
Copyright (C) 2013-2017, Bernd Porr, mail@berndporr.me.uk
Copyright (C) 2020 , Mario Molitor , mario_molitor@web.de
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*******************************************************************************/
// based on https://raw.githubusercontent.com/berndporr/iir_fixed_point/master/DirectFormI.h
#include "Globals.h"
#ifndef DIRECTFORMI_HPP_
#define DIRECTFORMI_HPP_
class CFMDirectFormI
{
public:
// convenience function which takes the a0 argument but ignores it!
CFMDirectFormI(const q31_t b0, const q31_t b1, const q31_t b2,
const q31_t, const q31_t a1, const q31_t a2)
{
// FIR coefficients
c_b0 = b0;
c_b1 = b1;
c_b2 = b2;
// IIR coefficients
c_a1 = a1;
c_a2 = a2;
reset();
}
CFMDirectFormI(const CFMDirectFormI &my)
{
// delay line
m_x2 = my.m_x2; // x[n-2]
m_y2 = my.m_y2; // y[n-2]
m_x1 = my.m_x1; // x[n-1]
m_y1 = my.m_y1; // y[n-1]
// coefficients
c_b0 = my.c_b0;
c_b1 = my.c_b1;
c_b2 = my.c_b2; // FIR
c_a1 = my.c_a1;
c_a2 = my.c_a2; // IIR
// scaling factor
q_scaling = my.q_scaling; // 2^q_scaling
}
void reset ()
{
m_x1 = 0;
m_x2 = 0;
m_y1 = 0;
m_y2 = 0;
}
// filtering operation: one sample in and one out
inline q15_t filter(const q15_t in)
{
// calculate the output
register q31_t out_upscaled = c_b0 * in //F4FXL puting stauration here made everything quiet, not sure why
+ c_b1 * m_x1
+ c_b2 * m_x2
- c_a1 * m_y1
- c_a2 * m_y2;
q15_t out = __SSAT(out_upscaled >> 15, 15);
// update the delay lines
m_x2 = m_x1;
m_y2 = m_y1;
m_x1 = in;
m_y1 = out;
return out;
}
private:
// delay line
q31_t m_x2; // x[n-2]
q31_t m_y2; // y[n-2]
q31_t m_x1; // x[n-1]
q31_t m_y1; // y[n-1]
// coefficients
q31_t c_b0,c_b1,c_b2; // FIR
q31_t c_a1,c_a2; // IIR
};
#endif /* DIRECTFORMI_HPP_ */

View File

@ -0,0 +1,45 @@
# based on https://github.com/berndporr/iir_fixed_point/blob/master/gen_coeff.py
import numpy as np
import scipy.signal as signal
import pylab as pl
# Calculate the coefficients for a pure fixed point
# integer filter
# sampling rate
fs = 24000
# cutoffs
f1 = 300
f2 = 2700
# ripple
rp = 0.2
# scaling factor in bits, do not change !
q = 15
# scaling factor as facor...
scaling_factor = 2**q
# let's generate a sequence of 2nd order IIR filters
#sos = signal.butter(2,[f1/fs*2,f2/fs*2],'pass',output='sos')
sos = signal.cheby1(3,rp,[f1/fs*2,f2/fs*2],'bandpass', output='sos')
sos = np.round((sos) * scaling_factor)
# print coefficients
for biquad in sos:
for coeff in biquad:
print(int(coeff),",",sep="",end="")
#print((coeff),",",sep="",end="")
print("")
# plot the frequency response
b,a = signal.sos2tf(sos)
w,h = signal.freqz(b,a)
pl.plot(w/np.pi/2*fs,20*np.log(np.abs(h)))
pl.xlabel('frequency/Hz');
pl.ylabel('gain/dB');
pl.ylim(top=1,bottom=-20);
pl.xlim(left=250, right=12000);
pl.show()