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
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 Hz
f_c = 45; % Cut-off frequency in Hz
order = 4; % Order of the butterworth filter
omega_c = 2 * pi * f_c; % Cut-off angular frequency
omega_c_d = omega_c / f_s; % Normalized cut-off frequency (digital)
[b, a] = butter(order, omega_c_d / pi); % Design the Butterworth filter
disp('a = '); disp(a); % Print the coefficients
disp('b = '); disp(b);
H = tf(b, a, 1 / f_s); % Create a transfer function
bode(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, freqs
import matplotlib.pyplot as plt
from math import pi
import numpy as np
f_s = 360 # Sample frequency in Hz
f_c = 45 # Cut-off frequency in Hz
order = 4 # Order of the butterworth filter
omega_c = 2 * pi * f_c # Cut-off angular frequency
omega_c_d = omega_c / f_s # Normalized cut-off frequency (digital)
# Design the digital Butterworth filter
b, a = butter(order, omega_c_d / pi)
print('Coefficients')
print("b =", b) # Print the coefficients
print("a =", a)
w, H = freqz(b, a, 4096) # Calculate the frequency response
w *= f_s / (2 * pi) # Convert from rad/sample to Hz
# Plot the amplitude response
plt.subplot(2, 1, 1)
plt.suptitle('Bode Plot')
H_dB = 20 * np.log10(abs(H)) # Convert modulus of H to dB
plt.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 response
plt.subplot(2, 1, 2)
phi = np.angle(H) # Argument of H
phi = np.unwrap(phi) # Remove discontinuities
phi *= 180 / pi # and convert to degrees
plt.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, freqs
import matplotlib.pyplot as plt
from math import pi
import numpy as np
f_s = 360 # Sample frequency in Hz
f_c = 45 # Cut-off frequency in Hz
order = 4 # Order of the butterworth filter
omega_c = 2 * pi * f_c # Cut-off angular frequency
omega_c_d = omega_c / f_s # Normalized cut-off frequency (digital)
# Design the digital Butterworth filter
b_d, a_d = butter(order, omega_c_d / pi)
print('Digital Coefficients')
print("b =", b_d) # Print the coefficients
print("a =", a_d)
w, H_d = freqz(b_d, a_d, 4096) # Calculate the frequency response
w *= f_s / (2 * pi) # Convert from rad/sample to Hz
# Design the analog Butterworth filter
b_a, a_a = butter(order, f_c, analog=True)
print('Analog Coefficients')
print("b =", b_a) # Print the coefficients
print("a =", a_a)
w, H_a = freqs(b_a, a_a, w) # Calculate the frequency response
# Plot the amplitude response
plt.subplot(2, 1, 1)
plt.suptitle('Bode Plot')
H_d_dB = 20 * np.log10(abs(H_d)) # Convert modulus of H to dB
H_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 response
plt.subplot(2, 1, 2)
phi_d = np.angle(H_d) # Argument of H
phi_a = np.angle(H_a)
phi_d = np.unwrap(phi_d) * 180 / pi # Remove discontinuities
phi_a = np.unwrap(phi_a) * 180 / pi # and convert to degrees
plt.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