Discretization of a Fourth-Order Butterworth Filter
Pieter PThis is an example on how to design a filter in the analog domain, and then use the bilinear transform to transform it to the digital domain, while preserving the cut-off frequency.
We'll be using formulas derived on the Bilinear Transform and Butterworth Filters pages.
Design criteria
In this example, we'll design a digital fourth order Butterworth low-pass filter, with a sample frequency of
Frequency Pre-Warping
As discussed in the page on the Bilinear Transform, we have to apply pre-warping to the cut-off frequency before designing a filter. If we don't the cut-off frequency will shift to an incorrect frequency when we discretize the filter.
First, let's calculate the normalized digital frequency
Next, we'll use the pre-warping formula we derived in the page on the Bilinear Transform, in order to calculate the analog design frequency
Designing the Butterworth filter in the Analog Domain
Now that we know the pre-warped analog cut-off frequency, we can start designing the analog filter.
We'll use the formula for the Butterworth low-pass filter derived in the page on Butterworth Filters:
Discretizing the Analog Filter
We can now just apply the Bilinear Transform to the analog transfer function, by substituting
Finally, we end up with an expression for the transfer function, using Equation
Frequency Response & Pole-Zero Map
We can check the filter's frequency response to make sure that we didn't make any mistakes.
As mentioned in other pages, the frequency response of a digital system can be a obtained by evaluating the
transfer function
You can see that the corner frequency lies around
MATLAB & GNU Octave
If you have to design many different filters, you don't want to calculate them all by hand. Luckily, MATLAB and GNU Octave come with a command to calculate the coefficients of Butterworth filters.
f_s = 360; % Sample frequency in Hzf_c = 45; % Cut-off frequency in Hzorder = 4; % Order of the butterworth filteromega_c = 2 * pi * f_c; % Cut-off angular frequencyomega_c_d = omega_c / f_s; % Normalized cut-off frequency (digital)[b, a] = butter(order, omega_c_d / pi); % Design the Butterworth filterdisp('a = '); disp(a); % Print the coefficientsdisp('b = '); disp(b);H = tf(b, a, 1 / f_s); % Create a transfer functionbode(H); % Show the Bode plot
Note that MATLAB expects the normalized frequency as a number from 0 to 1, so we have to divide by butter function.
Python
A similar function is available in the SciPy signal package:
butter.
from scipy.signal import butter, freqz, freqsimport matplotlib.pyplot as pltfrom math import piimport numpy as npf_s = 360 # Sample frequency in Hzf_c = 45 # Cut-off frequency in Hzorder = 4 # Order of the butterworth filteromega_c = 2 * pi * f_c # Cut-off angular frequencyomega_c_d = omega_c / f_s # Normalized cut-off frequency (digital)# Design the digital Butterworth filterb, a = butter(order, omega_c_d / pi)print('Coefficients')print("b =", b) # Print the coefficientsprint("a =", a)w, H = freqz(b, a, 4096) # Calculate the frequency responsew *= f_s / (2 * pi) # Convert from rad/sample to Hz# Plot the amplitude responseplt.subplot(2, 1, 1)plt.suptitle('Bode Plot')H_dB = 20 * np.log10(abs(H)) # Convert modulus of H to dBplt.plot(w, H_dB)plt.ylabel('Magnitude [dB]')plt.xlim(0, f_s / 2)plt.ylim(-80, 6)plt.axvline(f_c, color='red')plt.axhline(-3, linewidth=0.8, color='black', linestyle=':')# Plot the phase responseplt.subplot(2, 1, 2)phi = np.angle(H) # Argument of Hphi = np.unwrap(phi) # Remove discontinuitiesphi *= 180 / pi # and convert to degreesplt.plot(w, phi)plt.xlabel('Frequency [Hz]')plt.ylabel('Phase [°]')plt.xlim(0, f_s / 2)plt.ylim(-360, 0)plt.yticks([-360, -270, -180, -90, 0])plt.axvline(f_c, color='red')plt.show()
Comparison Between the Analog and Digital Filter
We can easily plot the Bode plots of the two filters on top of each other, in order to compare their properties.
from scipy.signal import butter, freqz, freqsimport matplotlib.pyplot as pltfrom math import piimport numpy as npf_s = 360 # Sample frequency in Hzf_c = 45 # Cut-off frequency in Hzorder = 4 # Order of the butterworth filteromega_c = 2 * pi * f_c # Cut-off angular frequencyomega_c_d = omega_c / f_s # Normalized cut-off frequency (digital)# Design the digital Butterworth filterb_d, a_d = butter(order, omega_c_d / pi)print('Digital Coefficients')print("b =", b_d) # Print the coefficientsprint("a =", a_d)w, H_d = freqz(b_d, a_d, 4096) # Calculate the frequency responsew *= f_s / (2 * pi) # Convert from rad/sample to Hz# Design the analog Butterworth filterb_a, a_a = butter(order, f_c, analog=True)print('Analog Coefficients')print("b =", b_a) # Print the coefficientsprint("a =", a_a)w, H_a = freqs(b_a, a_a, w) # Calculate the frequency response# Plot the amplitude responseplt.subplot(2, 1, 1)plt.suptitle('Bode Plot')H_d_dB = 20 * np.log10(abs(H_d)) # Convert modulus of H to dBH_a_dB = 20 * np.log10(abs(H_a))plt.plot(w, H_d_dB, color='blue', label='Digital')plt.plot(w, H_a_dB, color='green', label='Analog')plt.legend()plt.ylabel('Magnitude [dB]')plt.xlim(0, f_s / 2)plt.ylim(-80, 6)plt.axvline(f_c, color='red')plt.axhline(-3, linewidth=0.8, color='black', linestyle=':')# Plot the phase responseplt.subplot(2, 1, 2)phi_d = np.angle(H_d) # Argument of Hphi_a = np.angle(H_a)phi_d = np.unwrap(phi_d) * 180 / pi # Remove discontinuitiesphi_a = np.unwrap(phi_a) * 180 / pi # and convert to degreesplt.plot(w, phi_d, color='blue')plt.plot(w, phi_a, color='green')plt.xlabel('Frequency [Hz]')plt.ylabel('Phase [°]')plt.xlim(0, f_s / 2)plt.ylim(-360, 0)plt.yticks([-360, -270, -180, -90, 0])plt.axvline(f_c, color='red')plt.show()
Discretization using Second Order Sections (SOS)
For higher order filters, small quantization errors on the transfer function
coefficients can result in large errors on the pole and zero locations.
A solution is to factor the transfer function into second order factors or
sections.
Recall from the previous sections, and define
Second Order Sections
We'll use the same technique as before to substitute
First Order Section
For odd orders