How FIR filters work | Applying the filter
In the last post, we turned all the theory about FIR filters into a practical equation that we can use to calculate the taps necessary for our low pass filter. In this post, we’re going to find out how to filter a signal with our FIR filter.
The following signal is made up of 2 frequencies: 5Hz and 20Hz. The task is to get rid of the 20Hz frequency using a low pass filter.
Throughout the post, I’ll demonstrate how to perform the calculations with snippets of JavaScript code that you can run in your browser.
Code Structure
The code we’re going to develop is made up of 3 separate pieces which fit together like a jigsaw.
At the beginning of this series of posts on FIR filters, I used a moving average as an example of a specific type of FIR filter that many of us will have met before. FIR filters are all based on the moving average filter. However, to improve the frequency response of the filter, the average is weighted according to which filter you are applying.
The Convolution Loop
A moving average is a convolution between the signal and a rectangular function. A rectangular function is the impulse response of a moving average filter. Convolution is where we slide the impulse response of our filter over the signal we are filtering, multiplying and adding each point on the signal with each point on the impulse response as we go.
The convolution loop (blue jigsaw piece) changes the position of the filter’s impulse response each time it is called, making it slide over the signal as shown in the following animation.
Code Example
The convolution loop receives the signal contained in an array, x_n
, as its input. Each time around the loop, it increments the offset
variable, sliding the impulse response of the filter along the x-axis.
For each new position of the impulse response, the convolution loop calls the weighted average routine which performs the add and multiply part of the convolution operation.
The convolution loop returns an output
array containing the filtered signal.
The Weighted average routine
The weighted average routine (green jigsaw piece), performs the averaging operation. A traditional average is calculated by adding together all the samples from the signal, \( x_n \) currently in the averaging window, and dividing them by the number of samples, \( N \) in the window. This is the process shown in the animation above. The red rectangle is the window. The average of all the samples in the window, \( Y_n \) can be calculated as shown below.
$$ Y_n = \sum_{n=0}^{N-1} x_n \times \frac{1}{N} $$
The \( \frac{1}{N} \) term is the impulse response for a simple moving average filter. It is constant across the entire width of the window, hence its rectangular shape. In the animation above, the height of the rectangle has been normalized to make it easier to see together with the signal. However, in reality, it looks like the graph below.
To make the weighted average routine able to implement different types of filter, this \( \frac{1}{N} \) rectangular function will be replaced by the output of the filter taps generator (red jigsaw piece), more of which later on.
Code Example
The weighted average routine accepts 2 inputs: the signal, x_n
, and the current position, or offset
, of the filter window. Before any average can be calculated, the routine has to wait until the offset
of the window has increased enough to encompass at least the first N
samples of the signal, where N
is the width of the averaging window. Therefore, the output, Y_n = NaN
, until this happens. This is the phase delay of a FIR filter that we spoke about in previous posts.
Once the offset
of the averaging window has increased enough so that the start
of the window has reached the first sample in the signal, the averaging process can begin. The average is calculated by a for
loop which sums all the samples in the window, multiplying each sample by the averaging window’s current filter tap as it goes.
The weighted average routine returns a single number, Y_n
, that is the weighted average of all the samples currently within the window.
The Filter Taps Generator
The weights, or taps, that are used to weight the average of the weighted average routine are generated by the filter taps generator (red jigsaw piece). The taps will change depending on the filter being applied.
Therefore, each type of filter will have it’s own filter taps generator function.
A moving average filter
The impulse response, \( g_n \) of a moving average filter can be calculated as follows:
$$ g_n = \frac{1}{N} $$
Code for the filter taps generator of a moving average filter
The moving average filter and its output
As we saw in the introduction to this series, the frequency response of a simple moving average, doesn’t make for a particularly good low-pass filter. We can see this from the filtered signal (shown in the second graph in green). It looks rather pointy. The moving average filter is obviously not completely getting rid of the 20Hz frequency.
A low-pass filter using a weighted moving average
To improve the frequency response of the filter, let’s exchange the rectangular impulse response of a moving average filter for the sinc like impulse response of a low-pass filter.
Setting the cutoff frequency
We know that there are two frequencies in this signal, 5Hz and 20Hz, and we want to isolate the 5Hz frequency. So let’s set the cutoff frequency of the filter, \( f_c \) to 12.5Hz which is exactly half way between the two frequencies.
I sampled the signal at a sampling rate, \( f_s \) of 256Hz. So, the normalized cutoff frequency of my filter, \( f_t \) is:
$$ f_t = \frac{f_c}{f_s} = \frac{12.5}{256} \approx 0.049 $$
Deciding on the number of taps
The number of taps, \( N \), in my filter will determine the steepness of the roll-off of the filter. On the one hand, we want it to be steep enough so that we can totally remove the 20Hz frequency from the signal, but on the other hand, the more taps there are, the greater the delay will be before we see an output from our filter. Through a little trial and error, I have decided to set \( N \) to 65. Remember that this number should be odd to ensure that there is a tap at the center point of the impulse function, as we discussed in the last post.
Calculating the impulse function
We now have all the specifications we need to calculate the taps of the impulse function for this filter using the equation we derived previously.
$$
g_n = \begin{cases}
\frac{sin(2\pi f_t \left (n-\frac{N-1}{2}\right ))}{\pi \left ( n-\frac{N-1}{2} \right ) } & \text{ if } n \neq \frac{N-1}{2} \\
\;\;\;\;\;\;\; 2f_t & \text{ if } n = \frac{N-1}{2} \\
\end{cases}
$$
Calculating a window function
We can improve the frequency response of the filter by applying a window function, for example: a Hanning window, to smooth the edges of the impulse function where we truncated it to make the impulse response finite (click here for further explanation of the reason for the truncation). The coefficients of a Hanning window are calculated by the following equation:
$$ w_n = 0.5 \; – \; 0.5 \times cos \left ( 2 \pi \times \frac{n}{N-1} \right ) $$
Multiplying the impulse response of the low-pass filter by a Hanning window gives us the following output from the low-pass filter taps generator:
Code for the filter taps generator of a low pass filter
The low-pass weighted moving average filter and its output
This is much better. The 20Hz frequency has gone completely from our signal and we are left with only the 5Hz frequency.
Practical digital filter design
In this series of posts, we have introduced the concept of FIR filters and covered two specific FIR filter types. There are many other types, for example, high-pass, band-pass, and band-stop filters. One of the advantages of the FIR filter is its flexibility. We can define almost any response we desire in the frequency domain, and by using the inverse Fourier transform, we can calculate the impulse response of the filter and generate the taps we need to apply it to a signal. However, FIR filters also have disadvantages. The number of taps required can sometimes be quite large, requiring lots of memory and computation time. This also causes large phase delays.
The rise of smartphones, wearables, remote monitoring, and the Internet of Things, has massively increased the need for digital filters to tease ever more useful data out of often very noisy signals from the sensors mounted on these devices. There has therefore been a massive push to design filters that are better, quicker, and more intelligent.
In addition to FIR filters, there is another family of digital filters known as IIR, or Infinite Impulse Response filters which I hope to cover in the future. Both families have their advantages and disadvantages and it is not always clear which filter type is best for our application without a lot of testing and experimentation.
In the next post (coming next week) which will be a video post, I’ll be talking to an expert on digital filter design about all the things we need to consider when designing a practical digital filter. We’ll also be talking about a special tool he has developed to help you design, test, and deploy your digital filter.