Simple FIR filter to audio - Page 1
Forum section: electronics / projects

author: Dave_PT
author_rank: Frequent Contributor
timestamp: August 09, 2017, 04:35:06 pm
content: Hello.I need help to build a simple audio filter.Currently my system (a 48MHz ARM M0+) can read the ADC at 20KHz and make an LPF FIR (20taps) at the frequency of 2KHz.I need to make a BPF with a bandwidth of 100Hz centered at 1KHz.Basically I'm using 2 cascade FIR filters.First I calculate the LPF to 2KHz and then I apply a BPF FIR to 1KHz.This cycle is repeated at the frequency of 5KHz, because only with this frequency of sampling I can get the BW of 100Hz.Basically I first filter the entire signal to avoid aliasing above 2KHz and then sample at 5KHz to run the BPF.I was able to make a 20taps FIR filter, in cycles of 20KHz, with a cut-off frequency of 2KHz.Works well! The audio is filtered and have quality.When I put the 2 filters together, the sound sucks and it looks like the BPF is not working.Can someone give me some help?I'm using my own algorithm, but do you think using the CMSIS algorithm could be better?Thanks!

author: Benta
author_rank: Super Contributor
timestamp: August 09, 2017, 06:34:13 pm
content: What's your input signal? Is it already band-limited? Or do you have an anti-aliasing filter before the ADC?

author: Dave_PT
author_rank: Frequent Contributor
timestamp: August 09, 2017, 07:21:04 pm
content: Thank you for the reply.Yes I did a 3rd order filter with Fc = 5KHz.I can sample at 20KHz without aliasing problems.

author: Dave_PT
author_rank: Frequent Contributor
timestamp: August 09, 2017, 08:02:08 pm
content: These are the 2 main functions in my code.I do not know if there will be any problem in using 2 cascaded FIR filters ...All times are OK. I change a pin state in the various places and the ADC is read every 1/20KHz, the filter runs every 1/5KHz and the duration of the calculations does not exceed the time available. So everything seems to be right ...Code:#define LPF_ORDER 20#define LPF_HALF_ORDER (LPF_ORDER/2)#define FILTER_COEFF_MULT 13 //2^13#define BPF_ORDER 50#define BPF_ORDER_1 (BPF_ORDER - 1)#define BPF_HALF_ORDER (BPF_ORDER/2)//buffer com as últimas leituras da ADCint16_t ADC_readings_array;//Index para executar buffer circular da ADCuint8_t adc_buf_head;//buffer com as últimas leituras da ADCint16_t LPF_results_array;//ultimo resultado do calculo do FIRint32_t fir_result;//LOW-PASS FILTERconst int16_t LPF_2000Hz ={ -14, -23, -12, 41, 155, 335, 565, 807, 1010, 1127 };//BAND-PASS FILTERconst int16_t BPF_100Hz ={ 3, -3, -18, -9, 35, 49, -25, -106, -41, 133, 161, -73, -273, -97, 285, 318, -133, -469, -156, 433, 456, -181, -604,-190, 503 };//Interrupt routine to read the ADC valuevoid Routine_20KHz(void){//save the ADC reading to circular bufferADC_readings_array = ADC_DMA - ADC_in_avg;//update buffer head indexadc_buf_head++;if (adc_buf_head >= LPF_ORDER)adc_buf_head = 0;}//Function called every 1/5KHzvoid FIR_cascade_filter(void){int8_t i;uint8_t circular_head;int16_t ADC_order_buffer;int32_t fir_result;//Update the ouput "DAC" with the last filter calculation to maintain syncronismupdate_output(fir_result);//*************************************** INIT LOW PASS FILTER****************************//Order the ADC readingscircular_head = adc_buf_head;for (i = 0; i < LPF_ORDER; i++){ADC_order_buffer = ADC_readings_array;circular_head++;if (circular_head >= LPF_ORDER)circular_head = 0;}//Reset fir_result variablefir_result = 0;//first part of coeffsfor (i = 0; i < LPF_HALF_ORDER; i++){//multiplication and sum of filterfir_result += ADC_order_buffer * LPF_2000Hz;}//second part of coeffscircular_head = LPF_HALF_ORDER;for (i = (LPF_HALF_ORDER - 1); i >= 0; i--){//multiplication and sum of filterfir_result += ADC_order_buffer * LPF_2000Hz;circular_head++;}//Divide filter result by 2^13fir_result = fir_result >> FILTER_COEFF_MULT;//*************************************** END LOW PASS FILTER****************************//*************************************** INIT BAND PASS FILTER**************************//order the LPF resultsfor (i = 0; i < (BPF_ORDER - 1); i++){LPF_results_array[i + 1] = LPF_results_array;}//add the last calculationLPF_results_array = (int16_t) fir_result;fir_result = 0;//first part of coeffsfor (i = 0; i < BPF_HALF_ORDER; i++){//multiplication and sum of filterfir_result += LPF_results_array * BPF_100Hz;}//second part of coeffscircular_head = BPF_HALF_ORDER;for (i = (BPF_HALF_ORDER - 1); i >= 0; i--){//multiplication and sum of filterfir_result += LPF_results_array * BPF_100Hz;circular_head++;}//Divide filter result by 2^13fir_result = fir_result >> FILTER_COEFF_MULT;//*************************************** END BAND PASS FILTER**************************}

author: Kalvin
author_rank: Super Contributor
timestamp: August 09, 2017, 08:15:40 pm
content: You can test your filter in PC using C by creating suitable stimulus file or sample stream, and by analyzing its performance by plotting the output samples and some intermediate samples. Of course, you can do quick testing using Excel or OpenOffice Calc.

author: Dave_PT
author_rank: Frequent Contributor
timestamp: August 09, 2017, 08:29:08 pm
content: I will try it with excel...

author: Benta
author_rank: Super Contributor
timestamp: August 09, 2017, 09:41:05 pm
content: Quote from: Dave_PT on August 09, 2017, 07:21:04 pmIn that case, why use cascading filters? You can make a bandpass FIR directly.

author: diyaudio
author_rank: Frequent Contributor
timestamp: August 09, 2017, 09:47:16 pm
content: Quote from: Dave_PT on August 09, 2017, 08:29:08 pmyuck! no, use Matlab or Octave.if you not using these tools by now, you not armed to debug DSP algorithms. its non negotiable debugging tools.

author: Dave_PT
author_rank: Frequent Contributor
timestamp: August 09, 2017, 09:52:02 pm
content: But will not I have aliasing?I need to do sampling at 5KHz, which is the same frequency as my external filter cuts ...In this case I need to have an external filter to cut at least 2.5KHz.Am I wrong?Quote from: diyaudio on August 09, 2017, 09:47:16 pmI do not have any of them installed ...I will install the Octave.

author: trampas
author_rank: Regular Contributor
timestamp: August 09, 2017, 10:35:04 pm
content: I used MATLAB for years and even octave, I have not switched to Python, which I like better.Trampaswww.misfittech.net

author: Benta
author_rank: Super Contributor
timestamp: August 09, 2017, 10:44:07 pm
content: This is a mess... 20 kHz, 5 kHz, 2.5 kHz, 1 kHz, 100 Hz. My eyes are rolling in my head when I try to follow this.Dave_PT, could you PLEASE list the requirements and signals and limitations here?Perhaps start from scratch? What you have etc.Thanks (or rather, you should be thankful that someone is following this...)

author: Dave_PT
author_rank: Frequent Contributor
timestamp: August 09, 2017, 11:02:27 pm
content: OK ... from the beginning.I want to make a FIR filter that has a passband of 100Hz centered at 1KHz (passband 950HZ <-> 1050HZ).My input signal will be up to about 10KHz.Before the ADC I put a 3rd order filter with Fc = 5KHz.As proof that the basic operating principle is right, I can sample the signal at 20KHz and apply a 20-taps FIR LP Filter without problems.I wanted to apply the filter in the next picture ... but the sampling frequency has to lower at least 5KHz. At this frequency I will have aliasing. So I had the idea to sample at 20KHz, make a LP Filter at 2KHz and then "feed" the BP Filter that would make 5KHz loops.

author: Benta
author_rank: Super Contributor
timestamp: August 09, 2017, 11:22:34 pm
content: I understand your setup now.So why not just do the bandpass filtering directly on your 20 ksps sample stream? Do you have computational limitations?

author: Dave_PT
author_rank: Frequent Contributor
timestamp: August 09, 2017, 11:27:06 pm
content: Yes ... I have and can do all the calculations within the loop time.But with 20Ksps, I can not run any BP Filter with a 100Hz BW. The minimum with 20Ksps is around 500Hz. My old setup worked that way, but 500Hz is a very large BW.

author: Dave_PT
author_rank: Frequent Contributor
timestamp: August 15, 2017, 10:29:51 pm
content: Hello.I've done some calculations using Octave, and now I see what's going on.The problem is when I sample at 5KHz the signal that has passed through the low pass filter.So, what is your suggestion?What is the best way for me to have a 100Hz passband using what I have available?

author: nctnico
author_rank: Super Contributor
timestamp: August 15, 2017, 10:34:22 pm
content: I'd use an IIR filter. These need much less computing power for the same filter.

author: Dave_PT
author_rank: Frequent Contributor
timestamp: August 15, 2017, 10:43:51 pm
content: Thanks for the answer.I'm going to try an IIR filter. With these filters are fewer calculation operations, but as it uses floats, each operation will take more time...

author: Dave_PT
author_rank: Frequent Contributor
timestamp: August 15, 2017, 10:46:08 pm
content: Quote from: blueskull on August 15, 2017, 10:40:10 pmYes. That's what I'm doing.My samples are made at 20KHz, but only at 5KHz I perform LPF followed by BPF ...

author: Dave_PT
author_rank: Frequent Contributor
timestamp: August 15, 2017, 11:05:12 pm
content: Yes, you're right.But using FIR this does not work ...Let's try again before I go to test the IIR filters.1ºSampling the signal at 20KHz (20ksps).2ºA timer will change a flag (1/5KHz) so in my main program I can perform the filter.3ºCalculate the 20tap low pass filter to 2KHz. For this I use the last 20 readings of the ADC (20ksps).4ºAfter calculating the LPF, I "feed" the band pass filter so that it can then filter the signal and pass only the 100Hz that I want.5thAfter all the calculations, I wait for the new cycle to update my output. I do this to isolate the delays caused by calculations.What am I doing wrong in the process?I think it must be some calculation problem ...

author: danadak
author_rank: Super Contributor
timestamp: August 15, 2017, 11:32:40 pm
content: Caution on ISR controlling filter loop. ISR should be very tersein code, not call other functions (results in lots of stack push),set a flag, and return to main() to process the filter. Make sureother ISRs are inactive if at all possible to minimize samplingjitter on output writes. Same can be said for input sampling de-pending on how its implemented.Regards, Dana.

author: f5r5e5d
author_rank: Frequent Contributor
timestamp: August 16, 2017, 01:23:25 am
content: the signal processing theory and specification is confused - in addition to getting sampling/decimation/anti-alliasing correct, many calcs need ADC bits and acceptable noise budget, both analog and for the DSP5 kHz 3rd order analog low pass is feeble for 20k sample rate - (20/5)**3, only a factor of 64 attenuation for any 20 kHz signal component - 6 bitssometimes theoretically poor anti-alias works if the signal before filtering has low enough higher frequency content - but you need numbers, what's the signal look like at 200 kHz sample rate? (or the 192k that many PC sound cards can do)if all you want is to detect the narrow band @ 1 kHz then I'd move the analog anti-alias down to 2 kHz or even incorporate its cutoff (& Q) into the wanted notch filter designbiquad filters are named for the Quadratic s or z domain polynomials in both numerator and denominator - in general that means 6 coefficients and multiplies per each biquadand fixed point biquads are fine, can use integer multipliers - but often need very long wordlengths for the accumulatorssince that's still a lot of ops per biquad you may want several stages of Half Band FIR filtering with decimationhttps://www.google.com/search?q=half+band+filter+theory&oq=half+band+filter+theory&aqs=chrome..69i57.2999j0j1&sourceid=chrome&ie=UTF-8https://www.mathworks.com/help/dsp/examples/fir-halfband-filter-design.html

author: Dave_PT
author_rank: Frequent Contributor
timestamp: August 16, 2017, 09:26:27 am
content: Quote from: danadak on August 15, 2017, 11:32:40 pmThere is only 2 ISRs: 1 for acquisition at 20ksps, 1 to start the filter calculation (1/5KHz).In both routines there it's just enter, modify and exit.

author: nctnico
author_rank: Super Contributor
timestamp: August 16, 2017, 09:40:14 am
content: Quote from: Dave_PT on August 16, 2017, 09:26:27 amI would do the filtering in the 20ksps interrupt. That way you don't waste time to store/recover the data. You need to process each sample anyway sooner or later. An option is to filter in 2 stages where you decimate to 1kHz first and filter that signal using a lower samplerate. BTW IIR doesn't need floating point. 32 bit integers will do just fine but you have to check overflow conditions. 14 bit coefficients help to give some extra headroom.

author: f5r5e5d
author_rank: Frequent Contributor
timestamp: August 16, 2017, 10:54:29 am
content: sorry but this is just wrong - this prescription is decimating without the required filtering - you will get aliasingQuoteagain you need more problem definition info - the spectrum/level of frequency components in the raw signal, the required output resolution to design the ADC sampling and analog antialias filter frequencies and order, and to design the DSP processingas an example if your actual input signal has the high frequency content rolling fast off enough to work with your current 5 kHz 3rd order filter in front of 20 kHz sampling ADC, then maybe reducing the anaolg filter fc by 4x would let you cut the sample frequency by 2xthen with FIR Half Band filtering and decimation to cut the sample rate to 2.5 kHz in 2 stages may let you save enough processing time to be able to do the 1 kHz bandpass

author: Dave_PT
author_rank: Frequent Contributor
timestamp: August 16, 2017, 11:12:08 am
content: Quote from: f5r5e5d on August 16, 2017, 10:54:29 amCan you explain this part better?I'm sure I'm doing a lot of confusion ...

author: f5r5e5d
author_rank: Frequent Contributor
timestamp: August 16, 2017, 11:24:39 am
content: I linked to Half Band Filter search and design site in my 1st postHalf Band Filters are used to efficiently digitally filter before decimating by 2x - by exploiting symmetries they have 0 in 1/2 of their coefficientsbut you still need the other info I've mentioned to design for the required stopband attenuation

author: Dave_PT
author_rank: Frequent Contributor
timestamp: August 16, 2017, 11:33:43 am
content: I am using a 10bits ADC (with the possibility change to 12bits) preceded by a 3rd order filter with Fc=5KHz.The signal I'm sampling is audio up to 10KHz, and there will be several separate peaks ~200Hz between them. I am interested in 100Hz centered at 1KHz (950Hz to 1050Hz).If I get -30dB at 100Hz it would be very good ...