Digital High-Pass Filter#
In this section, we are going to do the following activities:
discretize a continuous-time high-pass filter by using bilinear transform (trapezoidal or Tustin) method
implement the discretized high-pass filter into MATLAB Simulink
compare the results from the implemented discrete high-pass filter to the shipped discrete high-pass filter in MATLAB Simulink
Required Imports#
from IPython.core.display import HTML
from sympy import *
from mathprint import *
Ts, tau = symbols('T_s tau', positive=True)
s = symbols('s', complex=True)
z = symbols('z')
omega = symbols('omega' , positive=True)
wc = symbols('omega_c', positive=True)
x, y = symbols('x y')
x0, x1, x2, x3 = symbols('x_k x_{k-1} x_{k-2} x_{k-3}')
y0, y1, y2, y3 = symbols('y_k y_{k-1} y_{k-2} y_{k-3}')
First-Order High-Pass Filter#
The Complementary Form#
Transfer function of a first-order highpass filter as a complementary to a first-order lowpass filter(time-constant filter):
where \(\tau\) is the filter time constant (in seconds).
Discretization with Bilinear Transformation
Next, we transform \(s\) into \(z\) by applying the following substitution.
H = 1 - 1 / (tau*s+1)
mprint('H=',latex(H))
H = H.subs(1/s, Ts/2 * (z+1)/(z-1))
mprint('H=',latex(H))
Let us define \(x\) as the input to the filter and \(y\) as the output (filtered input).
eq = Eq(y, H * x)
mprint(latex(eq))
eq = simplify(eq)
mprint(latex(eq))
eq = Eq(numer(eq.rhs), expand(eq.lhs * denom(eq.rhs)))
mprint(latex(eq))
eq =expand(Eq(numer(eq.rhs)/z/Ts, eq.lhs * denom(eq.rhs)/z/Ts))
mprint(latex(eq))
Apply the following substitutions:
\(y\) becomes \(y_{k}\)
\(y/z\) becomes \(y_{k-1}\)
\(x\) becomes \(x_{k}\)
\(x/z\) becomes \(x_{k-1}\)
eq = eq.subs(x/z**3, x3).subs(x/z**2, x2).subs(x/z, x1).subs(x, x0).subs(y/z**3, y3).subs(y/z**2, y2).subs(y/z, y1).subs(y, y0)
mprint("\\small ", latex(eq))
Finally, by grouping the variables, we obtain:
eq = Eq(collect(eq.rhs, [2,tau/Ts, x0, x1, x2, x3]), collect(eq.lhs, [y0, y1, y2, y3]) )
mprintb("\\small ", latex(eq))
Second Order High-Pass Butterworth Filter#
The Complementary Form#
Transfer function of a second order highpass filter as a complementary to a second-order lowpass filter (Butterworth):
where \(\omega_c\) is the filter cut-off frequency (in Hz).
Discretization with Bilinear Transformation
Similiar to the previous section, here we also transform \(s\) into \(z\) by applying the following substitution.
H = 1 - wc**2 / (wc**2+sqrt(2)*s*wc+s**2)
mprint('H=',latex(H))
H = simplify(1 - wc**2 / (wc**2+sqrt(2)*s*wc+s**2))
mprint('H=',latex(H))
H = simplify(H.subs(1/s, Ts/2 * (z+1)/(z-1)))
mprint('H=',latex(H))
Let us define \(x\) as the input to the filter and \(y\) as the output (filtered input).
eq = Eq(y, H * x)
mprint(latex(eq))
eq = simplify(eq)
mprint(latex(eq))
eq = Eq(numer(eq.rhs), expand(eq.lhs * denom(eq.rhs)))
mprint(latex(eq))
eq =expand(Eq(numer(eq.rhs)/z**2/Ts**2/wc**2, eq.lhs * denom(eq.rhs)/z**2/Ts**2/wc**2))
mprint(latex(eq))
Apply the following substitutions:
\(y\) becomes \(y_{k}\)
\(y/z\) becomes \(y_{k-1}\)
\(y/z^2\) becomes \(y_{k-2}\)
\(x\) becomes \(x_{k}\)
\(x/z\) becomes \(x_{k-1}\)
\(x/z^2\) becomes \(x_{k-2}\)
eq = eq.subs(x/z**3, x3).subs(x/z**2, x2).subs(x/z, x1).subs(x, x0).subs(y/z**3, y3).subs(y/z**2, y2).subs(y/z, y1).subs(y, y0)
mprint(latex(eq))
Finally, by grouping the variables, we obtain:
eq = Eq(collect(eq.rhs, [x0, x1, x2, x3]), collect(eq.lhs, [y0, y1, y2, y3]) )
mprintb(latex(eq))
However, the complementary form does NOT have +40 dB/decade at \(\omega=\omega_c\). We will show this through these steps:
Substitute \(j\omega\) to \(s\) in \(H(s)\) and convert it to dB. We name is as \(M(j\omega)\).
Calculate \(S=\frac{dM}{d\omega}\)
These steps will give us the slope in db/(rad/s).
H = simplify(1 - wc**2 / (wc**2+sqrt(2)*s*wc+s**2))
Hjw = H.subs(s, I*omega)
M = 20*log(abs(Hjw))/log(10)
mprint('M(j\\omega)=', latex(M))
dM = simplify(diff(M, omega))
mprint('S(\\omega)=\\frac{dM(\\omega)}{d\\omega}=', latex(dM))
However, the unit here is dB/(rad/s), not dB/decade.
To obtain dB/decade:
Sdb = simplify(omega * log(10) * dM)
mprint("S_{dB}(\\omega)=", latex(Sdb))
Let us take \(\omega_c=1\) and plot the slope for arbitrary \(\omega\).
Show code cell source
Sdb_wc1 = simplify(Sdb.subs(wc, 1))
mprint(
r'S_{\mathrm{dB}}(\omega)\big|_{\omega_c=1}=',
latex(Sdb_wc1)
)
p = plot(
Sdb_wc1,
(omega, 0.1, 100),
size=(5, 2),
show=False,
title=r'$S_{\mathrm{dB}}(\omega),\quad \omega_c=1$',
xlabel=r'$\omega$',
ylabel=r'dB/decade',
xscale='log'
)
p.show()
Sdb_wc = simplify(Sdb.subs(omega, wc))
Sdb_wc
Thus, it is only about 6.7 dB / decade! We can assure this by checking maximum an minimum possible slopes, which is 0 dB / decade and 20 db / decade, respectively.
Sdb_low = simplify(limit(Sdb, omega, 0, dir='+'))
Sdb_high = simplify(limit(Sdb, omega, oo))
mprint(
r'\lim_{\omega\to0}S_{\mathrm{dB}}(\omega)=',
latex(Sdb_low)
)
mprint(
r'\lim_{\omega\to\infty}S_{\mathrm{dB}}(\omega)=',
latex(Sdb_high)
)
The complementary form has an asymptotic low-frequency slope of only +20 dB/decade, rather than the +40 dB/decade of a conventional second-order HPF. At the cutoff, its local slope is approximately +6.67 dB/decade.
Standard Form#
Since the complementary form only gives us 6.7dB / decade, while for a second order filter our target is 40 dB / decade, we must modify the filter equation slightly.
Infact, this is the standard form of a secon-dorder Butterworth highpass filter.
H = s**2 / (wc**2+sqrt(2)*s*wc+s**2)
mprintb('H=',latex(H))
H = simplify(H.subs(1/s, Ts/2 * (z+1)/(z-1)))
mprint('H=',latex(H))
eq = Eq(y, H * x)
mprint(latex(eq))
eq = simplify(eq)
mprint(latex(eq))
eq = Eq(numer(eq.rhs), expand(eq.lhs * denom(eq.rhs)))
mprint(latex(eq))
eq =expand(Eq(numer(eq.rhs)/z**2/Ts**2/wc**2, eq.lhs * denom(eq.rhs)/z**2/Ts**2/wc**2))
mprint(latex(eq))
eq = eq.subs(x/z**3, x3).subs(x/z**2, x2).subs(x/z, x1).subs(x, x0).subs(y/z**3, y3).subs(y/z**2, y2).subs(y/z, y1).subs(y, y0)
mprint(latex(eq))
eq = Eq(collect(eq.rhs, [x0, x1, x2, x3]), collect(eq.lhs, [y0, y1, y2, y3]) )
mprintb(latex(eq))
Implementation in Simulink#
function y = HPF(y1, y2, x0, x1, x2, wc, Ts)
% Second-order Butterworth high-pass filter
%
% y1 = y(k-1)
% y2 = y(k-2)
% x0 = x(k)
% x1 = x(k-1)
% x2 = x(k-2)
% wc = cutoff angular frequency [rad/s]
% Ts = sampling period [s]
sq22 = 2*sqrt(2);
Tswc = Ts * wc;
A = 1 + sq22/Tswc + 4/Tswc^2;
B = 2 - 8/Tswc^2;
C = 1 - sq22/Tswc + 4/Tswc^2;
D = 4/Tswc^2;
y = (D*(x0 - 2*x1 + x2) - B*y1 - C*y2) / A;
end
Download Simulink file here (R2026a).

