forked from grahamwhaley/DSPham
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dynamicFilters.cpp
355 lines (297 loc) · 11.1 KB
/
dynamicFilters.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
// From the $19 dsp filter project
// https://github.com/gi1mic/19Dollar-DSP-Filter
// MIT licensed.
#include <Audio.h>
#include <math.h>
#include "dynamicFilters.h"
#include "dspfilter.h"
#include "global.h"
unsigned int filterIndex = 0; // index to currently selected filter above
short fir_active1[200]; // 1st DSP filter array holding the coefficient as 32bit (short)
/*
* Structure to hold the required filters (Add, delete or modify as required)
*
* ID, FilterType, Low Freq, Hi Freq, Window, FilterName
*/
struct filter filterList[] = {
{FILTER_PASSTHRU, ID_BANDPASS, 60.0, 20000.0, W_HAMMING, NUM_COEFFICIENTS},
{FILTER_SSB, ID_BANDPASS, 300.0, 2700.0, W_HAMMING, NUM_COEFFICIENTS},
{FILTER_CW, ID_BANDPASS, 450.0, 950.0, W_HAMMING, NUM_COEFFICIENTS},
{FILTER_AM, ID_BANDPASS, 50.0, 11000.0, W_HAMMING, NUM_COEFFICIENTS}, //Xiegu G90 has 10.8Khz AM bandwidth
{FILTER_FM, ID_BANDPASS, 50.0, 16000.0, W_HAMMING, NUM_COEFFICIENTS}, //Guess at a wide FM bandwidth
};
double fir_tmp[NUM_COEFFICIENTS]; // Temp array used for coefficient calculations which are performed in 64bit
//---------------------------------------------------------------
// Generate lowpass filter
//
// This is done by generating a sinc function and then windowing it
void wsfirLP(double h[], // h[] will be written with the filter coefficients
const int &N, // size of the filter (number of taps)
const int &WINDOW, // window function (W_BLACKMAN, W_HANNING, etc.)
const double &fc) // cutoff frequency
{
int i;
double *w = new double[N]; // window function
double *sinc = new double[N]; // sinc function
// 1. Generate Sinc function
genSinc(sinc, N, fc);
// 2. Generate Window function
switch (WINDOW) {
case W_BLACKMAN: // W_BLACKMAN
wBlackman(w, N);
break;
case W_HANNING: // W_HANNING
wHanning(w, N);
break;
case W_HAMMING: // W_HAMMING
wHamming(w, N);
break;
default:
break;
}
// 3. Make lowpass filter
for (i = 0; i < N; i++) {
h[i] = sinc[i] * w[i];
}
// Delete dynamic storage
delete []w;
delete []sinc;
return;
}
//---------------------------------------------------------------
// Generate highpass filter
//
// This is done by generating a lowpass filter and then spectrally inverting it
void wsfirHP(double h[], // h[] will be written with the filter coefficients
const int &N, // size of the filter
const int &WINDOW, // window function (W_BLACKMAN, W_HANNING, etc.)
const double &fc) // cutoff frequency
{
int i;
// 1. Generate lowpass filter
wsfirLP(h, N, WINDOW, fc);
// 2. Spectrally invert (negate all samples and add 1 to center sample) lowpass filter
// = delta[n-((N-1)/2)] - h[n], in the time domain
for (i = 0; i < N; i++) {
h[i] *= -1.0; // = 0 - h[i]
}
h[(N - 1) / 2] += 1.0; // = 1 - h[(N-1)/2]
return;
}
//---------------------------------------------------------------
// Generate bandstop filter
//
// This is done by generating a lowpass and highpass filter and adding them
void wsfirBS(double h[], // h[] will be written with the filter taps
const int &N, // size of the filter
const int &WINDOW, // window function (W_BLACKMAN, W_HANNING, etc.)
const double &fc1, // low cutoff frequency
const double &fc2) // high cutoff frequency
{
int i;
double *h1 = new double[N];
double *h2 = new double[N];
// 1. Generate lowpass filter at first (low) cutoff frequency
wsfirLP(h1, N, WINDOW, fc1);
// 2. Generate highpass filter at second (high) cutoff frequency
wsfirHP(h2, N, WINDOW, fc2);
// 3. Add the 2 filters together
for (i = 0; i < N; i++) {
h[i] = h1[i] + h2[i];
}
// Delete dynamic memory
delete []h1;
delete []h2;
return;
}
//---------------------------------------------------------------
// Generate bandpass filter
//
// This is done by generating a bandstop filter and spectrally inverting it
void wsfirBP(double h[], // h[] will be written with the filter taps
const int &N, // size of the filter
const int &WINDOW, // window function (W_BLACKMAN, W_HANNING, etc.)
const double &fc1, // low cutoff frequency
const double &fc2) // high cutoff frequency
{
int i;
// 1. Generate bandstop filter
wsfirBS(h, N, WINDOW, fc1, fc2);
// 2. Spectrally invert bandstop (negate all, and add 1 to center sample)
for (i = 0; i < N; i++) {
h[i] *= -1.0; // = 0 - h[i]
}
h[(N - 1) / 2] += 1.0; // = 1 - h[(N-1)/2]
return;
}
//---------------------------------------------------------------
// Generate sinc function - used for making lowpass filter
void genSinc(double sinc[], // sinc[] will be written with the sinc function
const int &N, // size (number of taps)
const double &fc) // cutoff frequency
{
int i;
const double M = N - 1;
double n;
// Generate sinc delayed by (N-1)/2
for (i = 0; i < N; i++) {
if (i == M / 2.0) {
sinc[i] = 2.0 * fc;
}
else {
n = (double)i - M / 2.0;
sinc[i] = sin(2.0 * M_PI * fc * n) / (M_PI * n);
}
}
return;
}
//---------------------------------------------------------------
// Generate window function (Blackman)
void wBlackman(double w[], // w[] will be written with the Blackman window
const int &N) // size of the window
{
int i;
const double M = N - 1;
for (i = 0; i < N; i++) {
w[i] = 0.42 - (0.5 * cos(2.0 * M_PI * (double)i / M)) + (0.08 * cos(4.0 * M_PI * (double)i / M));
}
return;
}
//---------------------------------------------------------------
// Generate window function (Hanning)
void wHanning(double w[], // w[] will be written with the Hanning window
const int &N) // size of the window
{
int i;
const double M = N - 1;
for (i = 0; i < N; i++) {
w[i] = 0.5 * (1.0 - cos(2.0 * M_PI * (double)i / M));
}
return;
}
//---------------------------------------------------------------
// Generate window function (Hamming)
void wHamming(double w[], // w[] will be written with the Hamming window
const int &N) // size of the window
{
int i;
const double M = N - 1;
for (i = 0; i < N; i++) {
w[i] = 0.54 - (0.46 * cos(2.0 * M_PI * (double)i / M));
}
return;
}
//---------------------------------------------------------------
void coeffConvert(double in[], short out[], const int &N) {
#ifdef SHOWCOEFF
Serial.println(";---- Cut and paste into a text file (.coe extension) -----");
Serial.println(";------------ for import and analysis by MATLAB -----------");
Serial.println("; FIR Filter");
Serial.println("Radix = 10;");
Serial.print("CoefData= ");
for (int j = 0; j < N; j++) {
//out[j] = (short)(in[j] * 10000);
out[j] = (short)(in[j] * 32767);
Serial.print(out[j]);
Serial.print(", ");
}
Serial.println("0;");
Serial.println("; ---------------------- End Cut ---------------------------");
#else
for (int j = 0; j < N; j++) {
//out[j] = (short)(in[j] * 10000);
out[j] = (short)(in[j] * 32767);
};
#endif
}
//---------------------------------------------------------------
void lowpass(short h[], const int &N, const int &WINDOW, const double &fc) {
wsfirLP(fir_tmp, N, WINDOW, fc );
coeffConvert(fir_tmp, h, N);
}
//---------------------------------------------------------------
void highpass(short h[], const int &N, const int &WINDOW, const double &fc) {
wsfirHP(fir_tmp, N, WINDOW, fc);
coeffConvert(fir_tmp, h, N);
}
//---------------------------------------------------------------
void bandpass(short h[], const int &N, const int &WINDOW, const double &fc1, const double &fc2) {
wsfirBP(fir_tmp, N, WINDOW, fc1, fc2 );
coeffConvert(fir_tmp, h, N);
}
//---------------------------------------------------------------
void bandstop(short h[], const int &N, const int &WINDOW, const double &fc1, const double &fc2) {
wsfirBS(fir_tmp, N, WINDOW, fc1, fc2 );
coeffConvert(fir_tmp, h, N);
}
//---------------------------------------------------------------
void audioFilter(short h[], const int &N, const int &TYPE, const int &WINDOW, const double &fc1, const double &fc2) {
switch (TYPE) {
case ID_LOWPASS:
#if DEBUG
Serial.print("LowPass Freq:");
Serial.println(fc1);
#endif
lowpass(h, N, WINDOW, fc1/AUDIO_SAMPLE_RATE_EXACT);
break;
case ID_HIGHPASS:
#if DEBUG
Serial.print("HiPass Freq:");
Serial.println(fc1);
#endif
highpass(h, N, WINDOW, fc1/AUDIO_SAMPLE_RATE_EXACT);
break;
case ID_BANDPASS:
#if DEBUG
Serial.print("BandPass LFreq:");
Serial.print(fc1);
Serial.print(" HFreq:");
Serial.println(fc2);
#endif
bandpass(h, N, WINDOW, fc1/AUDIO_SAMPLE_RATE_EXACT, fc2/AUDIO_SAMPLE_RATE_EXACT);
break;
case ID_BANDSTOP:
#if DEBUG
Serial.print("Bandstop LFreq:");
Serial.print(fc1);
Serial.print(" HFreq:");
Serial.println(fc2);
#endif
bandstop(h, N, WINDOW, fc1/AUDIO_SAMPLE_RATE_EXACT, fc2/AUDIO_SAMPLE_RATE_EXACT);
break;
default:
#if DEBUG
Serial.println("Unknown");
#endif
break;
}
}
//Additions from Graham
// The filters come out with some attenuation - too much for my linking.
// If we can measure the gain at the requested bandpass, then we can scale
// the coefficients to actual 0db (neutral, no gain or attenuation) for the
// chosen frequency, which should, ideally, be the peak of the filter.
//
// Gain is the root of the sum of the squares of cos and sin waves. Don't ask me for
// the theory!!!
float32_t getFilterGain(int16_t *coeffs, int ncoeffs, float32_t frequency, float32_t samplerate) {
float32_t cgain=0.0, sgain=0.0;
float32_t gain = 0.0;
float32_t f = frequency / samplerate;
if (DEBUG) Serial.printf("getFilterGain(ncoeff: %d, f:%f, rate:%f\n", ncoeffs, frequency, samplerate);
for(int i=0; i<ncoeffs; i++ ) {
float32_t c = cos(2.0 * M_PI * f * i);
float32_t s = sin(2.0 * M_PI * f * i);
cgain += (((float32_t)coeffs[i])/32768.0) * c;
sgain += (((float32_t)coeffs[i])/32768.0) * s;
}
gain = (cgain*cgain) + (sgain*sgain);
if (DEBUG) Serial.printf(" Gain: %fhz, %f sample: %f gain\n", frequency, samplerate, sqrt(gain));
//FIXME - we probably should limit the gain returned somewhere - possibly here.
return (sqrt(gain));
}
void normaliseCoeffs(int16_t *coeffs, int ncoeffs, float32_t multiplier) {
for( int i=0; i<ncoeffs; i++ ) {
coeffs[i] *= multiplier;
}
}