diff --git a/applications/lofar2/model/pfb_os/dsp.py b/applications/lofar2/model/pfb_os/dsp.py index 52e1c86d5e80596e926b5493dbcab42334f07011..1fe35a2ac4f1a8385661bb3c216d1ddcbfb0dbca 100644 --- a/applications/lofar2/model/pfb_os/dsp.py +++ b/applications/lofar2/model/pfb_os/dsp.py @@ -22,11 +22,15 @@ # Author: Eric Kooistra # Purpose: Utilities and functions for DSP # Description: - +# +# References: +# [1] dsp_study_erko.txt +# [2] https://github.com/chipmuenk/dsp/blob/main/notebooks/02_LTF/LTF-IIR-allgemein.ipynb import numpy as np +from scipy import signal import matplotlib.pyplot as plt - +import dsp_fpga_lib # from [2] c_interpolate = 10 c_atol = 1e-15 @@ -335,6 +339,58 @@ def plot_time_response(h, name='', markers=False): plt.grid(True) +def plot_iir_filter_analysis(b, a, fs=1, Ntime=100, step=False): + """Plot iir filter analysis results. + + Input: + . b, a: IIR filter coefficients in same format as for scipy.signal.freqz + . fs: sample frequency + . Ntime: number of timesamples for impulse response + . step: False for impulse response, True for step response + """ + + # Plot poles / zeros diagram in z-plane + fig1, ax1 = plt.subplots(1) + z, p, k = dsp_fpga_lib.zplane(b, a, plt_ax=ax1) # uses np.roots(a), np.roots(b) + print('Zeros, poles and gain from b, a coefficients:') + if len(z) > 0: + print('. zeros:') + for zero in z: + print(' z = %s' % str(zero)) + if len(p) > 0: + print('. poles:') + for pole in p: + print(' p = %s' % str(pole)) + print('. gain: k = %.3f' % k) + + # Derive b, a coefficients back from z, p, k + print('Coefficients back from z, p, k:') + print(' b = %s' % str(np.poly(z))) + print(' a = %s' % str(np.poly(p) / k)) + + # Plot transfer function H(f), is H(z) for z = exp(j w), so along the unit circle + # . 0 Hz at 1 + 0j, fs / 4 at 0 + 1j, fNyquist = fs / 2 at -1 + 0j + # . use whole=False to have only positve frequencies, so 0 to fs / 2 + # . use Nfreq frequency points + fig2, ax2 = plt.subplots(1) + Nfreq = 1024 + [f, HF] = signal.freqz(b, a, Nfreq, whole=False, fs=fs) + ax2.plot(f, pow_db(HF)) + ax2.set_xlabel('frequency [fs = %f]' % fs) + ax2.set_ylabel('HF power [dB]') + + # Plot impulse response + Ts = 1 / fs + fig3, ax3 = plt.subplots(1) + step = True # step response (makes impz use np.cumsum(h)) + step = False + [h, t] = dsp_fpga_lib.impz(b, a, FS=fs, N=Ntime, step=step) # uses signal.lfilter() + (ml, sl, bl) = ax3.stem(t, h, linefmt='b-', markerfmt='ro', basefmt='k') + ax3.set_xlabel('time [Ts = %f]' % Ts) + ax3.set_ylabel('h[n]') + return z, p, k + + def plot_spectra(f, HF, fs=1.0, fLim=None, dbLim=None): """Plot spectra for power, magnitude, phase, real, imag