DSPRelated.com
Forums

References for fixed point bandpass

Started by Dan Rhodes January 29, 2004
Hi All -

First off, this seems like a great group - a lot of expertise floating
around out there.  Hopefully someone can point me in the right
direction...

I am new to digital filter design and need to implement a bandpass
filter on a fixed point (ARM7) processor.  Searching for info I have
found a lot of theory, but no actual source code that I could use as a
reference.  I downloaded ScopeFIR and generated some coefficients, but
I'm still unsure of what the source code for the algorithm would look
like.  Are there any C source files out there I could use as a
reference?

From what I have read, it seems like an FIR implementation is more
conducive to a fixed point implementation than an IIR.  Is this true?

Any help is appreciated.

Thanks much,

Dan
daniel.e.rhodes@saic.com (Dan Rhodes) wrote in 
news:86375c4a.0401291031.73f948bf@posting.google.com:

> Hi All - > > First off, this seems like a great group - a lot of expertise floating > around out there. Hopefully someone can point me in the right > direction... > > I am new to digital filter design and need to implement a bandpass > filter on a fixed point (ARM7) processor. Searching for info I have > found a lot of theory, but no actual source code that I could use as a > reference. I downloaded ScopeFIR and generated some coefficients, but > I'm still unsure of what the source code for the algorithm would look > like. Are there any C source files out there I could use as a > reference?
There are C examples in this book: Digital Signal Processing A Practical Approach Emmanuel C Ifeachor Barrie W Jervis
> > From what I have read, it seems like an FIR implementation is more > conducive to a fixed point implementation than an IIR. Is this true? >
Not necessarily. FIR filters are much easier to implement and generally perform as expected. IIR filters are much more sensitive to precision and filter topology. You may find that a 16 bit FIR filter works fine, but that you need 32 bits for an IIR. It really depends on what you are trying to do. Generally, fixed point IIRs are best implemented as Direct Form I filters, sometimes with first order noise shaping. A Google search on Direct Form I IIR might be useful. -- Al Clark Danville Signal Processing, Inc. -------------------------------------------------------------------- Purveyors of Fine DSP Hardware and other Cool Stuff Available at http://www.danvillesignal.com
Dan Rhodes wrote:
> Hi All - > > First off, this seems like a great group - a lot of expertise floating > around out there. Hopefully someone can point me in the right > direction... > > I am new to digital filter design and need to implement a bandpass > filter on a fixed point (ARM7) processor. Searching for info I have > found a lot of theory, but no actual source code that I could use as a > reference. I downloaded ScopeFIR and generated some coefficients, but > I'm still unsure of what the source code for the algorithm would look > like. Are there any C source files out there I could use as a > reference? > > From what I have read, it seems like an FIR implementation is more > conducive to a fixed point implementation than an IIR. Is this true? > > Any help is appreciated. > > Thanks much, > > Dan
It's really quite simple. You are trying to compute a discrete convolution, which is really just a moving average with specially chosen weights (the coefficients, or "taps"). To do that, you are performing the operation: y(n) = Sum[ x(n-i) * h(i) , { i, 0, NUM_TAPS - 1 } ] where y(n) is the n-th output data point, x(n-i) is the input data indexing backwards in time from the current datum, which would be x(n), and h(i) is the i-th filter coefficient starting from the beginning of the coefficient list h(0) and going to the end h(NUM_TAPS-1). Obviously, the NUM_TAPS parameter is the number of filter coefficients, and the syntax Sum[ expr, { iterator_variable, start, end} ] is borrowed from Mathematica, where the "expr" is the expression for the i-th term in the series, and the stuff in braces indicates the iterator variable, and its start and end values. This is not yet a program, or course. The first problem that crops up is what to do when you are just starting to collect data. You don't want to be indexing "to the left" of the lowest x data array location, which would be some other junk in your RAM. The easy way to deal with this is to keep your input data in a circular buffer (assuming you are trying to do a real-time filter). Then if you initialize the buffer to zeros before starting to collect data, the filter will "start up" just fine. The only trick then will be to handle backwards rollover when the convolution indexing tries to look to the left of the first sample array location. Here's how to do it for a filter with 16-bit signed coefficients, and 16-bit signed sample data: ---------------------------------------------------------- #define NUM_TAPS 100 /* you put what you want here */ #define AD_BUFFER_DEPTH 100 /* you put what you want here */ #define FIR_COEFF_SCALE_FACTOR 16384 /* some number, see below */ uint8_t AD_buffer_index = AD_BUFFER_DEPTH - 1; /* global index always indexes most recent sample, assumes less than 256 samples. But start from the end so the first time we increment the index, it will wind up pointing at the beginning of the buffer <see below> */ int16_t FIR_coeff[NUM_TAPS] = {put your coefficient list here}; int16_t AD_buffer[AD_BUFFER_DEPTH]; /* make sure you or the compiler inits this array to zeros */ /* This function computes FIR filter on the AD_buffer, using the coefficients in FIR_coeff, returning the result */ int16_t compute_fir(void) { uint8_t ad_i = AD_buffer_index; /* index into AD_buffer */ uint8_t coeff_i; /* index into FIR coefficient array */ int32_t sum = 0; /* accumulator for multiply accumulate (MAC) operations */ for ( coeff_i = 0; coeff_i < NUM_TAPS; ++coeff_i ) { sum = sum + ( (int32_t)AD_buffer[ad_i] * FIR_coeff[coeff_i] ); /* handle backwards rollover of the AD_buffer index: */ --ad_i; /* we are moving backward through the AD_buffer (toward older data) */ if ( ad_i == 255 ) /* we assume the AD_BUFFER_DEPTH is never more than 255 */ ad_i = AD_BUFFER_DEPTH - 1; } return( (int16_t)(sum / FIR_COEFF_SCALE_FACTOR) ); } /* This function is whatever you do to sample data, like a periodic interrupt that triggers the A/D converter, and waits until converson is done */ void sample(void) { int16_t new_sample, filtered_result; new_sample = whatever_you_get_samples_from(); ++AD_buffer_index; /* handle forward rollover of AD_buffer_index to implement circular buffer: */ if ( AD_buffer_index == AD_BUFFER_DEPTH ) AD_buffer_index = 0; AD_buffer[AD_buffer_index] = new_sample; filtered_result = compute_fir_filter(); } -------------------------------------------------------------- You have to pick a scale factor of course. You will multiply all of your normalized (sum of 1) coefficients by this factor, so the sum of the coefficient list will be FIR_COEFF_SCALE_FACTOR after scaling. You pick this factor so that the maximum absolute value of your samples, times the scale factor, is less than the largest positive value of the MAC accumulator "sum" in the compute_fir() function, and the scaling gives reasonable resolution to your coefficients. If you choose a power of two, it will make the final divide to un-scale more efficient, but this may not be important if you have already computed a long filter. Note that you dare not choose a number larger than (2^15)-1 for 16-bit signed data (can you figure out why is that?). Ok, this should get you started. This approach is not optimized for computational efficiency, only algorithmic straightforwardness. I may have made errors in this quick explanation. No guarantees implied. Good day! -- ____________________________________ Christopher R. Carlen Principal Laser/Optical Technologist Sandia National Laboratories CA USA crcarle@sandia.gov