The parameters \(b = [b_0, \ldots, b_Q]\) and \(a = [a_0, \ldots, a_P]\)
are 1d arrays of length \(Q+1\) and \(P+1\). They define the analog
transfer function
This expression can be simplified by multiplying numerator and denominator by
\((z+1)^N\), with \(N=\max(P, Q)\). This allows \(H_d(z)\) to be
reformulated as
This is the equation implemented to perform the bilinear transform. Note that for
large \(f_s\), \(\kappa^Q\) or \(\kappa^P\) can cause a numeric
overflow for sufficiently large \(P\) or \(Q\).
The following example shows the frequency response of an analog bandpass filter and
the corresponding digital filter derived by utilitzing the bilinear transform:
>>> fromscipyimportsignal>>> importmatplotlib.pyplotasplt>>> importnumpyasnp...>>> fs=100# sampling frequency>>> om_c=2*np.pi*np.array([7,13])# corner frequencies>>> bb_s,aa_s=signal.butter(4,om_c,btype='bandpass',analog=True,output='ba')>>> bb_z,aa_z=signal.bilinear(bb_s,aa_s,fs)...>>> w_z,H_z=signal.freqz(bb_z,aa_z)# frequency response of digitial filter>>> w_s,H_s=signal.freqs(bb_s,aa_s,worN=w_z*fs)# analog filter response...>>> f_z,f_s=w_z*fs/(2*np.pi),w_s/(2*np.pi)>>> Hz_dB,Hs_dB=(20*np.log10(np.abs(H_).clip(1e-10))forH_in(H_z,H_s))>>> fg0,ax0=plt.subplots()>>> ax0.set_title("Frequency Response of 4-th order Bandpass Filter")>>> ax0.set(xlabel='Frequency $f$ in Hertz',ylabel='Magnitude in dB',... xlim=[f_z[1],fs/2],ylim=[-200,2])>>> ax0.semilogx(f_z,Hz_dB,alpha=.5,label=r'$|H_z(e^{j 2 \pi f})|$')>>> ax0.semilogx(f_s,Hs_dB,alpha=.5,label=r'$|H_s(j 2 \pi f)|$')>>> ax0.legend()>>> ax0.grid(which='both',axis='x')>>> ax0.grid(which='major',axis='y')>>> plt.show()
../../_images/scipy-signal-bilinear-1_00_00.png
The difference in the higher frequencies shown in the plot is caused by an effect
called "frequency warping". [1] describes a method called "pre-warping" to
reduce those deviations.