Introduction Moving Average Filters FIR Filters Code and data IIR Filters Transfer Functions

Coefficients View

If we go to the coefficients view, we can see the coefficients used to descibe the filter in numerical form which we can copy and paste into other applications. If you're using a third party DSP library, a third party product or writing your own code then you can select, copy and paste these directly.

The first set of coefficients is in floating point format and defines the unscaled coefficients for the whole filter.

Integer Filters

If you are developing for an embedded system, then your device might not have a floating point unit or you may choose integer computation because it is faster. To select integer computation, use the "Arithmetic" field to select the word-lengths for memory, coefficients and accumulator. A good rule of thumb is to match these to the hardware multiplier on your device. If your embedded device has a 16x16 multiplier then one of the 16x16 options is usually a good choice. (A 16x16 multiplier multiples 2 16 bit numbers together to produce a 32 bit number). When using integer computation, it is a good idea to also check the output of the filter simulator by clicking on the right hand graph buttons. The simulator will imitate the effects of integer round-off and is a good way to spot if the real filter will work in the same way as the theoretical one.

If you are using integer filters then the remaining coefficients will be optimally scaled and listed in integer format.

The remaining coefficients are listed for each filter section. Sometimes IIR filters are split into sub-filters (such as biquads) for stability reasons. Since FIR filters are always stable, the entire filter is usually in one section. If you have selected integer arithmetic then the remaining coefficients will also have been optimally scaled and converted to your selected integer format. The following diagram shows the scaled integer coefficients for an IIR filter.

Code View

If we go to the code view, we can see that it has automatically generated C code for the filter that we can copy and paste into our project. The header is listed first, followed by the C file and some test cases. Note that the test cases can increate the code size.

Looking at the header, we can see the following functions:

filter1Type * filter1_create( void );                         // Create a new instance of the filter
void filter1_destroy( filter1Type *pFilter );            // Destroy an instance of the filter
void filter1_init( filter1Type *pFilter );               // Initialize a statically allocated filter
void filter1_reset( filter1Type *pFilter );              // Reset the filter to its initial state

void filter1_writeInput( filter1Type *pFilter, float input );          // Write to the filters input
#define filter1_readOutput(THIS) ((THIS)->output)                      // Read from the filters output

We call create() to create a new filter and destroy() to destroy it once we are finished using it. We use writeInput() to feed data into the filter and readOutput() to read data from it.

Naming your filter

Large projects can have many symbols and variables and we want to ensure that our filter does not collide with the names of other functions or libraries. We also want to give our filter a meaningful name so that we can identify it. You can use the "Class" field to change the name of the filter. All of the functions and structure definitions are prefixed by this name, esuring their uniqueness.

Sample Rate

The frequency response of a digital filter is always measured relative to the sample rate, which is the speed (in samples per second) that numbers will be fed into the filter. The default is 1 Hz or 1 sample per second but a typical audio application might use, say 48000 samples per second. We can enter this value in the "Sample Rate" box and our displays will be updated to reflect the sample rate of that signal. Filters are always scaled relative to the sample rate, so changing the sample rate does not alter the filter coefficients or code. However, it does alter the scaling of the frequency displays on the graph and it's useful to see the actual frequencies and times of the signals rather than the normalized ones.

Code to use the filter

The following simple example shows how we might use the filter to process data coming from an analog to digital converter (ADC) then write it to a digital to analog converter (DAC). An ADC converts an analog voltage into a digital number. The DAC converts the digital number back into an analog voltage:

#include "fir1.h"

short ADCRead( void );                            // User supplied function to read an analog voltage
void  DACWrite( short value );                    // User supplied function to write an analog voltage

int main( int argc, char **argv )                 // Typical C style main()
{
    fir1Type *fir1 = fir1_create();               // Create the filter
    while( 1 )                                    // Infinite loop
    {
        fir1_writeInput( fir1, ADCRead() );       // Read a sample from the ADC and write it into the filter 
        DACWrite( fir1_readOutput( fir1 ) );      // Read the output from the filter and write it to the DAC
    }
    fir1_destroy( fir1 );                       // Done. If we every reach here, destroy the filter
}

In practice, ADCRead() and DACWrite() would be implemented via an interrupt service routine so that the program does not have to stop while the peripheral is working. However, this simple example serves to illustrate the basic use of the filter.

Statically allocating the filter

On tiny embedded systems with tightly constrained memory, allocating memory can be hazardous. You may wish to pre-declare the filter then use the init() function to initialize it, rather than create() and destroy().

#include "fir1.h"

short ADCRead( void );                            // User supplied function to read an analog voltage
void  DACWrite( short value );                    // User supplied function to write an analog voltage

extern fir1Type fir1;

fir1Type fir1;                                    // Statically declare the filter

int main( int argc, char **argv )                 // Typical C style main()
{
    fir1_init( &fir1 );                           // Initialize the filter
    while( 1 )                                    // Infinite loop
    {
        fir1_writeInput( &fir1, ADCRead() );       // Read a sample from the ADC and write it into the filter 
        DACWrite( fir1_readOutput( &fir1 ) );      // Read the output from the filter and write it to the DAC
    }
}

Filtering data from arrays

The following example shows how we might initially try to use a filter to process a 1D array of data:

#include "fir1.h"

void filterSignal( short *inputArray, short *outputArray, int length )
{
    int i;
    fir1Type *fir1 = fir1_create();                          // Create the filter

    for( i = 0; i < length; ++ i )                           // Loop for the length of the array
    {
        fir1_writeInput( fir1, inputArray[i] );              // Write one sample into the filter
        outputArray[ i ] = fir1_readOutput( fir1 );        // Read one sample from the filter and store it in the array.
    }
    fir1_destroy( fir1 );                                  // Done. Destroy the filter
}

Filter delay

You should be aware that filters delay the signal they are processing. If you were to run this code, then you would notice that the output has been offset from the input because of this delay, which is not what we want.

For FIR filters, the group delay (in samples) is half the filter length - 1. So, for a 31 tap FIR filter, the delay is (31 - 1)/2 = 15 samples.
FIR filters with even numbers of taps have half sample delays, which makes matching their delays more difficult. If you stick to the default Type 2 filter, the length will always be odd and you won't have this problem.

The delay is calculated automatically, which you can access via the delay attribute (e.g. fir1_delay)
The inner loop of the above code can been corrected for the delay as follows:

    for( i = 0; i < length + fir1_delay; ++ i )                         // Loop for the length of the array, but feed in fir1_delay extra zeros at the end
    {
        fir1_writeInput( fir1, i < length ? inputArray[i] : 0 );        // Write one sample into the filter. Feed in zeros after the end of the input array.
        if( i >= fir1_delay )
            outputArray[ i - fir1_delay ] = fir1_readOutput( filter );  // Read one sample from the filter and store it in the array.
    } 

Basically, instead of writing into output array element [i], we have to write into element [i - fir1_delay] in order to compensate for the delay.

We don't want to read past the end of the input array, so we feed in extra 0's by changing the input to "i < length ? inputArray[i] : 0"

We don't want to write into negative array elements, we check that i >= fir1_delay before-hand.




Introduction Moving Average Filters FIR Filters Code and data IIR Filters Transfer Functions