Write an IIR filter using C++
We first need to design an IIR filter in MATLAB and generate a header file that reflects the frequency response characteristics of the IIR filter
theoretical support
IIR filtering is called a recursive filter, and it is a filter with feedback. When the order is large, multiple second-order section filters are generally used in series, which can improve the stability of the system.
A second-order nodal coefficient law is shown in the figure:
The difference equation for the Kth second-order section can be written
The cascade structure of N second-order sections is shown in the following figure:
According to the second-order nodal diagram, the output of the previous stage is regarded as the input of the latter stage, and the function of IIR digital filtering can be realized by software.
Generate header files using Matlab
First open the Filter Design & Analysis Tool in MATLAB
Here we first design a low-pass filter
Fs represents the sampling frequency, the sampling frequency must be greater than twice the highest frequency of the original signal,
Otherwise spectral aliasing will occur.
Fpass is the passband frequency, Fstop is the stopband cutoff frequency
After these parameters are set, click Design Filter
What is generated is a second-order section filter combination, with a total of 31 orders, that is, a combination of multiple second-order filters
Then generate the C Header File in the Target option
Numerator is the name of the numerator coefficient array, Numerator length is the length of the numerator coefficient array,
Denominator is the denominator.
Analyze the generated header file
The following is an example of a low-pass filter with Fpass as 10K and Fstop as 12K
Before using the header file, it is necessary to convert the data type of Matlab to the data type supported by C++ according to the situation. Here we use the double type
Before analyzing the header file, look at the first section of filtering parameters provided by Matlab
Take the data of the first second-order section as an example:
- Numerator: 1 2 1
- Denominator: 1 -0.55930961405944157 0.92579835996619642
- Gain:0.34162218647668868
Numerator is the coefficient of the numerator, b0, b1, b2 respectively
Denominator is the coefficient of the denominator, a0, a1, a2 respectively.
Gain is the gain of each section. In order to stabilize each section, this item can stabilize the signal size.
Then compare the header file, the following is an interception of the main part of the header file:
#define MWSPT_NSEC 41 int NL[MWSPT_NSEC] = { 1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1, 3,1,3,1,2,1 }; double NUM[MWSPT_NSEC][3] = { { 0.3416221864767, 0, 0 }, { 1, 2, 1 }, { 0.3180955154747, 0, 0 }, { 1, 2, 1 },......
MWSPT_NSEC is the filter order, the specific number of sections is in the comments at the beginning of the header file
NL[MWSPT_NSEC] This array defines the number of useful data in each row of the NUM[MWSPT_NSEC][3] array (it is optional)
In the NUM[MWSPT_NSEC][3] array (numerator parameters), the first item of the odd row is the gain item, and the even row has 3 coefficients, which are b0, b1, and b2 respectively.
From this, we can find the law, define K as the current order, and p as the first pointer of the array, then, the gain term of each section is (p+6*K), and the first coefficient is (p+3+ 6*K),
The second coefficient is (p+3+6*K+1) and the third coefficient is (p+3+6*K+2).
C++ programming implementation
In the process of software design, the delay variable of each second-order section is only summed, and is directly assigned as an intermediate variable in the process. This is because the delay variable of the next input data n+1 is the sum of the previous input data. Designing in this way can save register space.
In order to improve the processing speed, the program needs to use pointers for parameter transfer, and pay special attention to the transfer method of the first address of the two-dimensional array as &a[0][0]->double* a.
filter function
double iir(double *a, double *b,double* w, double xin, int N_IIR)
{
int k;
double temp = xin;
for (k = 0; k<N_IIR; k++)
{
*(w+k*3) = temp - *(a + 3+6 * k + 1) *(*(w + k * 3+1)) - *(a + 3 + 6 * k + 2) *(*(w + k * 3+2));
//Here temp is the input of the second-order section and the output of the previous second-order section
temp = *(b + 3 + 6 * k )* (*(w + k * 3)) + *(b + 3 + 6 * k + 1) * (*(w + k * 3+1)) + *(b + 3+6 * k + 2)* (*(w + k * 3+2));
//Here temp is the output of the second-order section and the input of the next second-order section
*(w + k * 3 + 2) = *(w + k * 3 + 1);
*(w + k * 3 + 1) = *(w + k * 3);
temp = temp*(*(b + 6 * k));//Amplification, stable signal
}
return temp;
}
practical testing
Test a low-pass filter with Fpass of 10K and Fstop of 12K
Input three signals with frequencies of 2K, 11K, and 20K in the program. In theory, 2k is completely passed, 11k is partially attenuated, and 20K is completely filtered out.
The upper picture is the original signal, and the lower picture is the filtered signal.
The actual test found that it meets the design requirements, and the signal is basically completely attenuated in the transition band.
Test C++ program void main() { const int N = 100; int i,j; double xn[N]; double w[20][3]; double yn[N]; for (i = 0; i < 20; i++)//初始化 { for (j = 0; j < 3; j++) w[i][j] = 0; } for (i = 0; i < N; i++) { xn[i] = sin(2 * 3.1416 * 20 / 50 * i)+ sin(2 * 3.1416 * 2 / 50 * i)+ sin(2 * 3.1416 * 11 / 50 * i); yn [i] = iir (& DEN [ 0 ] [ 0 ], & NUM [ 0 ] [ 0 ], & w [ 0 ] [ 0 ], xn [i], 20 ); } ofstream SaveFile_a("xn.txt"); for (i = 0; i<N; i++) SaveFile_a << " " << xn[i] << endl; SaveFile_a.close(); ofstream SaveFile_b("yn.txt"); for (i = 0; i<N; i++) SaveFile_b << " " << yn[i] << endl; SaveFile_a.close(); } Matlab program for analysis xn1=fopen('xn.txt','r'); [xn,count]=fscanf(xn1,'%f'); fclose(xn1); N = length(xn);% Find the number of sampling points xn_f = fft(xn);% Fourier transform the signal xn_f=abs(xn_f(1:N/2)); f = 50000/N*(0:N/2-1); subplot(211); stem(f,abs(xn_f)); xlabel('Frequency / (s)');ylabel('Amplitude'); title( ' Original signal spectrum ' ); grid; yn1 = fopen ( ' yn.txt ' , ' r ' ); [in, count] = fscanf (in1, ' % f ' ); fclose(yn1); yn_f = fft(yn);% Fourier transform the signal yn_f = abs (yn_f ( 1 : N / 2 )); subplot(212); stem (f, abs (in_f)); xlabel('Frequency / (s)');ylabel('Amplitude'); title( ' Filtered signal spectrum ' ); grid;