Sunday, 6 January 2013

Let's Do Some Digital Signal Processing (Part 2) - One Pole Filter


One-pole filters are very useful and efficient techniques for filtering signals. They use a feedback value to implement a low-pass filter. They can also be used to design efficient peak-hold filters in the time-domain and are also used in power spectrum analyzers and can help extract features of the signals that are being analyzed.

The structure of the one-pole filter is shown in the following diagram :
And the following equation :
y(n) = x(n) - a * y(n-1),          0 < a < 1.0

The single coefficient defines the gain and time-constant of the filter as follows :

Time constant :
Gain :
Here is the code for the one-pole filter :

#include <stdio.h>

#define COUNT 8 /* Number of samples to process */
#define ALPHA 0.6 /* Feedback value */

float impulseSrc[] = {
 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
 };
float stepSrc[] = {
 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0
 };
float Dst[] = {
 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
 };

void one_pole (const float *, float *, const float, float *, const int count);

void main (void)

{
 float State = 0.0; /* Reset the state value */
 one_pole (impulseSrc, Dst, 0.6, &State, COUNT);
 printf ("One Pole Impulse = %f, %f, %f, %f, %f, %f, %f, %f\n", Dst[0], Dst[1], Dst[2], Dst[3], Dst[4], Dst[5], Dst[6], Dst[7]);
 State = 0.0; /* Reset the state value */
 one_pole (stepSrc, Dst, 0.6, &State, COUNT);
 printf ("One Pole Step = %f, %f, %f, %f, %f, %f, %f, %f\n", Dst[0], Dst[1], Dst[2], Dst[3], Dst[4], Dst[5], Dst[6], Dst[7]);
}


void one_pole (const float *pSrc,
float *pDst,
const float Alpha,
float *pState,
const int VectorLength)
{
    float LocalState = *pState; /* Use a local variable to make the code more efficient */
    int   i;
    for (i = 0; i < VectorLength; i++)
    {
        LocalState = *pSrc++ + (LocalState * Alpha);
        *pDst++ = LocalState;
    }
    *pState = LocalState;
}

And here is the output :
One Pole Impulse = 1.000000, 0.600000, 0.360000, 0.216000, 0.129600, 0.077760, 0.046656, 0.027994
One Pole Step = 1.000000, 1.600000, 1.960000, 2.176000, 2.305600, 2.383360, 2.430016, 2.458010

This implementation shows one problem with feedback filters and that is the gain, as shown above with the step response. In an ideal scenario (and definitely when using fixed point devices) then the magnitude of the output should be the same as the magnitude of the input (i.e. 0 -> 1). In this example it can be seen that the output value (i.e. the gain) tends to 2.5 (i.e. 1/(1-Alpha)). To counter this we can rewrite the one-pole filter equation as follows :
y(n) = x(n) * (1- a) + a * y(n-1) 0 < a < 1.0

And the new flow diagram looks like :
Now when we rewrite the code, it looks like this :

void one_pole (float *pSrc,
float *pDst,
float Alpha,
         float *pState,
int VectorLength)
{
    float OneMinusAlpha;
    float LocalState = *pState;
    int   i;
    OneMinusAlpha = 1.0 - Alpha;

    for (i = 0; i < VectorLength; i++)
    {
        LocalState = ((LocalState * Alpha) + (*pSrc++ * OneMinusAlpha);
        *pDst++ = LocalState;
    }

    *pState = LocalState;
}

And now the gain is normalized.
As mentioned at the start of this blog, scaling is a big issue with feedback filters and the more feedback coefficients the bigger the problem. Solving the problem for one-pole filters is a trivial task, doing the same more biquad IIR filters is a much more complex task - maybe something for a future blog.



No comments:

Post a Comment