4.1 Design approaches
Last updated
Last updated
As before, we will first understand the problem and design (various) solutions in Python. We will then implement a real-time version in Python so that implementing it on the microcontroller will be primarily that of porting Python to C. Let's get started!
In the following analysis, we assume a sampling frequency of , as this is the sampling rate of our test recording. This parameter can be changed in the referenced scripts.
TASK 1: You are strongly encouraged to play around with the scripts mentioned below!
In the previous chapter, we implemented a very simple high pass filter by subtraction the previous sample from the current one, namely:
From this difference equation, we could perform an analysis of its frequency response by taking the Z-transform:
from which we obtain the transfer function:
From such an expression, we can create the standard pole-zero plot as seen below. With such a plot, we can extract a lot of useful information, such as stability and causality.
Figure: Pole-zero plot of our simple HPF. Thanks to this software for the visualization function.
For certain system, we can also compute the Fourier Transform, which may be more intuitive to understand as we can "read-off" the attenuation at various frequencies. For our simple HPF, the frequency response is shown below.
Figure: Frequency response of our simple HPF (log scale).
As noted in the last chapter, this filter does not have a sharp cutoff, which means frequencies of interest within the voice spectrum will be attenuated.
We can observe this with a simple example: we will take a zero-mean audio signal and add an artificial offset to it; we will then apply our simple HPF and compare the filtered signal with the zero-mean one.
You can run this script to generate the plots below and to listen to the filtered signal.
Figure: Original zero-mean signal (blue) and signal with artificial offset (orange).
Figure: Signal with artificial offset (blue) and HPF'ed signal (orange).
Figure: Original zero-mean signal (blue) and HPF'ed signal (orange).
If you would be just looking at the resulting plots, you would think the high pass filtered (HPF'ed) signal is completely different from the original! However, if you listen to the output, you will hear the same sentence but with the lower frequencies severely attenuated.
Nonetheless, looking at this plot and hearing the output, we can conclude that this simple filter will simply not cut it.
Figure: Comparing FIR filters of different order with our simple HPF filter from before.
The above figure can be generated with this script, which uses the function firwin
from the scipy.signal
library in order to compute the coefficients with the window method.
Let's take a look at the resulting output when we pass the artificial DC-biased signal from before.
The above figure can be generated with this script, which again uses firwin
to design the filter and lfilter
to apply the filter.
Recalling our original motivation of the HPF, we discussed a simple solution for removing the DC offset by subtracting the average/mean of the signal. This can be done with a single line of Python when we have a recording, but is not possible when we are streaming an input, as we do not have access to future values!
The above form falls under the category of infinite impulse response (IIR) filters, which will be discussed shortly, as the output depends on past output value(s).
With this estimate of the average, we can remove it from the current sample towards the goal of removing the DC offset:
We can observe a tradeoff between a large number of coefficients and a desirable (low) cutoff frequency versus a small number of coefficients with a better attenuation at DC but an undesirable (high) cutoff frequency. Nonetheless, we can obtain desirable performance without using as many coefficients as we saw earlier with the window method.
A more efficient and better performing DC removal moving average filter is discussed here. As we will not adopt this approach in our implementation, we leave this resource for the interested reader.
So are we stuck with using FIR filters with lots of coefficients? Fortunately not!
With recursion, we can compute filters that theoretically have an infinite impulse response (IIR). The filters we saw earlier have a finite impulse response since they rely on a finite number of past input samples. With IIR filters, we also use past output values, and this recursion is what creates an infinite impulse response. The difference equation of an IIR filter is typically written as such:
As with FIR filter design, there are various design approaches and criteria. The table below from the very useful online DSP guide provides a nice summary of the different design approaches for a variety of applications.
Figure: Filters can be divided by their use, and how they are implemented. Source.
Looking at the "Time Domain" row, as we are interested in DC removal, we can observe two approaches of interest:
Moving average (which as we saw earlier could be implemented more efficiently as an IIR filter).
Single pole IIR filter.
We will now look into this second approach, as we will be implementing this filter for DC removal.
Another name for this filter is a DC blocker, and it can be expressed with the following difference equation:
we can obtain the following transfer function:
But let's see what the frequency response looks like! The figure below is generated with this script.
In the next section, we will see how we can implement the second order IIR filter, also known as a biquad filter, in real-time with Python.
Our simple filter from before is a special case of a causal discrete-time Finite Impulse Response (FIR) filter. Causal means that only current and previous samples are used, while FIR implies only current and past input samples are used. For such a filter of order , we can write the output as:
where are often called the filter coefficients. A common way to visualize such filters is with a block diagram as seen below.
Figure: Causal discrete-time FIR filter of order . The top part is an -stage delay line with taps. Each unit delay is a operator in Z-transform notation. Source.
For our simple HPF, we had with .
With more coefficients, filters with sharper roll-offs can be achieved, but at the cost of more computation (and memory to store the coefficients and previous sample values). For selecting the values of the coefficients, there are different design criteria. A common approach is the window method, which we will consider here for different orders .
Although our simple HPF from before attenuates DC more than most of the FIR filters considered above, the other filters would preserve important signal in the voice spectrum , i.e. (unit gain / passband) above . However, using taps barely attenuates the DC component while taps demands a large amount of memory.
Figure: Signal with artificial offset filtered by -tap FIR filter.
Figure: Signal with artificial offset filtered by -tap FIR filter.
As expected from the frequency response curves we saw earlier, the -tap filter is unable to attenuate the DC offset, while the -tap filter is able to do so (but at the cost of more memory and computation). Nonetheless, both filters preserve the original signal much better than what we saw with the simple -tap HPF we were using earlier. The resulting filtered signals should sound nearly identical to the original (zero-mean) file. The -tap output, on the other hand, will begin with a small click due to the DC offset.
We can however perform a moving average that is estimate the mean from the past samples and subtract this from the current sample value. The moving average can be written as the following FIR operation:
The average at time index can also be computed using the previous average at index in order to reduce the number of computations, particularly if is large.
The figure below, generated by this script, demonstrates the performance of the above filter for varying values of .
Notice that starts with ; in literature you might see an coefficient but this will be typically set to . In total, we have coefficients, meaning this many multiplies for the output.
where specifies the location of the pole. By taking the Z-transform:
A larger results in a more selective filter (not suppressing desired lower frequencies), but at the expense of an impulse response with a longer tail. More on this tradeoff can be read here.
In the above figure, generated by this script, we vary the value of to demonstrate the resulting response. As approaches , we obtain a narrower notch at DC, that is the attenuation "begins" at a lower frequency. The value corresponds precisely to the simple two-tap filter we saw earlier.
Although produces a filter with a more desirable cutoff frequency, the roll-off is still the same, as we can observe roughly parallel lines with a decay of roughly . We can produce a sharper filter by cascading multiple single pole filters. A cascade of stages corresponds to raising the frequency response to the power of , namely:
As increases, more coefficients (and samples) will be needed to compute the output, which means more computations and memory needed to store the coefficients and past samples. An -stage filter would require coefficients, past input samples, and past output samples.
For example, for :
from which we can read off the coefficients and .
For , we could use the filter from to compute the coefficients:
yielding the coefficients and .
For a general , the transfer function (and thus coefficients) can be computed in this recursive fashion:
We can observe this sharper decay for higher values of . Quantitavely, it is around for stages. Moreover, comparing to our FIR analysis from before, we need much less coefficients in order to obtain a filter with a desirable performance!
The figure below, generated with this script, shows that even the single stage () does a sufficient job in removing the DC offset for the (artificially generated) signal with a DC offset. This filter is implemented with just coefficients, whereas the FIR filter needed taps with the window method and taps with the moving average approach for comparable performance.