def freqz(self, worN, fs): """ Returns an audioSample of the frequency response corresponding to the zpk filter. Inputs are the same as scipy.signal.freqz_zpk --------------------------------------------------------------------- INPUTS --------------------------------------------------------------------- worN | (int) length of the frequency response (assumes | single-sided) --------------------------------------------------------------------- fs | (int) sampling frequency --------------------------------------------------------------------- --------------------------------------------------------------------- OUTPUTS --------------------------------------------------------------------- (audioSample) containing the frequency response (frequency domain) of the filter --------------------------------------------------------------------- """ if isinstance(worN, float): print( "Float given instead of integer for length of freqz. This will be converted to an integer" ) worN = int(worN) w, h = sig.freqz_zpk(self.get_zs(), self.get_ps(answer_complex=True), \ self.get_k(), worN=worN) return audioSample(h, type="f", Fs=fs)
def plot_zpk(system, fp, fs, Amax, Amin, sample_rate=48e3, num_samples=1024, ax=None, plot_focus='all'): fmin = np.floor(np.log10(fp))-1 fmax = np.ceil(np.log10(fs)) f = np.logspace(fmin, fmax, num_samples) f, h = signal.freqz_zpk(*system, fs=sample_rate, worN=f) if ax is None: fig, ax = plt.subplots() if plot_focus == 'all': axis_focus = [1e2, 10**fmax, -50, 1] ax.set_xscale('log') elif plot_focus == 'pass': axis_focus = [1000, 2500, -5, 0] elif plot_focus == 'stop': axis_focus = [3000, 5000, -50, -35] # Plot data ax.plot(f, 20 * np.log10(np.abs(h)), linewidth=2) # Plot boxes box_style = dict(linewidth=2, linestyle='--', edgecolor='k', facecolor='0.9') ax.fill([10**fmin, 10**fmin, fp, fp], [-Amin*2, -Amax, -Amax, -Amin*2], **box_style) # pass ax.fill([fs, fs, 10**fmax, 10**fmax], [-Amin, Amax, Amax, -Amin], **box_style) # stop # Set plot properties ax.set_title('Lowpass filter') ax.set_xlabel('Frequency [Hz]') ax.set_ylabel('Amplitude [dB]') ax.grid(True, which='both', axis='both') ax.axis(axis_focus)
def test__get_causal_signal_preserves_roots_inside_unit_circle(): n_signals = 1 _, transfer_function = freqz_zpk(0.25, 0.5, 1.00, whole=True) n_fft_samples = transfer_function.shape[0] linear_predictor = np.zeros((1, n_fft_samples, n_signals, n_signals), dtype=np.complex) linear_predictor[0, :, 0, 0] = transfer_function _, expected_transfer_function = freqz_zpk(0.25, 0.5, 1.00, whole=True) linear_coef = ifft(expected_transfer_function) linear_coef[0] *= 0.5 expected_causal_signal = np.zeros((1, n_fft_samples, n_signals, n_signals), dtype=np.complex) expected_causal_signal[0, :, 0, 0] = fft(linear_coef) causal_signal = _get_causal_signal(linear_predictor) assert np.allclose(causal_signal, expected_causal_signal)
def test_minimum_phase_decomposition(): n_signals = 1 # minimum phase is all poles and zeros inside the unit circle _, transfer_function = freqz_zpk(0.25, 0.50, 1.00, whole=True) n_fft_samples = transfer_function.shape[0] expected_minimum_phase_factor = np.zeros( (2, n_fft_samples, n_signals, n_signals), dtype=np.complex) expected_minimum_phase_factor[0, :, 0, 0] = transfer_function _, transfer_function2 = freqz_zpk(0.125, 0.25, 1.00, whole=True) expected_minimum_phase_factor[1, :, 0, 0] = transfer_function2 expected_cross_spectral_matrix = np.matmul( expected_minimum_phase_factor, _conjugate_transpose(expected_minimum_phase_factor)) minimum_phase_factor = minimum_phase_decomposition( expected_cross_spectral_matrix) cross_spectral_matrix = (minimum_phase_factor * _conjugate_transpose(minimum_phase_factor)) assert np.allclose(minimum_phase_factor, expected_minimum_phase_factor) assert np.allclose(cross_spectral_matrix, expected_cross_spectral_matrix)
def test_freq_resp_zpk(self): # Test that frequency response meets tolerance from ITU-R BS.468-4 fs = 270000 z, p, k = ITU_R_468_weighting(fs, 'zpk') w, h = signal.freqz_zpk(z, p, k, 2*pi*frequencies/fs) levels = 20 * np.log10(abs(h)) if mpl: plt.figure('468') plt.semilogx(frequencies, levels, alpha=0.7, label='zpk') plt.legend() assert all(np.less_equal(levels, responses + upper_limits)) assert all(np.greater_equal(levels, responses + lower_limits))
def test__get_causal_signal_removes_roots_outside_unit_circle(): n_signals = 1 _, transfer_function = freqz_zpk(4, 2, 1.00, whole=True) n_fft_samples = transfer_function.shape[0] linear_predictor = np.zeros((1, n_fft_samples, n_signals, n_signals), dtype=np.complex) linear_predictor[0, :, 0, 0] = transfer_function expected_causal_signal = np.ones((1, n_fft_samples, n_signals, n_signals), dtype=np.complex) causal_signal = _get_causal_signal(linear_predictor) assert np.allclose(causal_signal, expected_causal_signal)
def test_freq_resp_zpk(self): # Test that frequency response meets tolerance from ANSI S1.4-1983 fs = 270000 z, p, k = A_weighting(fs, 'zpk') w, h = signal.freqz_zpk(z, p, k, 2 * pi * frequencies / fs) levels = 20 * np.log10(abs(h)) if mpl: plt.figure('A') plt.semilogx(frequencies, levels, alpha=0.7, label='zpk') plt.legend() assert all(np.less_equal(levels, responses['A'] + upper_limits)) assert all(np.greater_equal(levels, responses['A'] + lower_limits))
def plot_zpk(z, p, k, fp, fs, Amax, Amin, sample_rate=48e3): f, h = signal.freqz_zpk(z, p, k, fs=sample_rate) plt.plot(f, 20 * np.log10(abs(h))) plt.xscale('log') plt.title('Lowpass filter fit to constraints') plt.xlabel('Frequency [Hz]') plt.ylabel('Amplitude [dB]') plt.grid(True, which='both', axis='both') plt.fill([1e2, 1e2, fp, fp], [-Amin * 2, -Amax, -Amax, -Amin * 2], '0.9', lw=0) # pass plt.fill([fs, fs, 1e4, 1e4], [-Amin, Amax, Amax, -Amin], '0.9', lw=0) # stop plt.axis([1e2, 1e4, -50, 1])
def get_specbasis(bandpass): low = bandpass[0] high = bandpass[1] corners = bandpass[2] low = low / (0.5*df) high = high / (0.5*df) z, p, k = iirfilter(corners, [low, high], btype='band', ftype='butter', output='zpk') w, h = freqz_zpk(z,p,k, worN=len(freq)) # always zerophase h2 = h*np.conjugate(h) return np.real(h2)
def PinkNoise(fs, fstart=10, fend=None, N=3): """ Creates SOS filter for pink noise. The filter has a flat response below fstart, and rolls of close to Nyquist, or flattens out above fend. The ripple (i.e.) closeness the filter rolls of depends on the number of sections. The default, N=3 results in Args: fs: Sampling frequency [Hz] fstart: Frequency of first pole of the filter fend: Frequency of last pole of the filter, if not given, set to 5/6th of the Nyquist frequency. N: Number of sections. Returns: sos: Array of digital filter coefficients """ order = N * 2 if fend is None: fend = 5 * fs / 6 / 2 # Not the fastest implementation, but this will not be the bottleneck # anyhow. fpoles = np.array( [fstart * (fend / fstart)**(n / (order - 1)) for n in range(order)]) # Put zeros inbetween poles fzeros = np.sqrt(fpoles[1:] * fpoles[:-1]) poles = -2 * np.pi * fpoles zeros = -2 * np.pi * fzeros z, p, k = bilinear_zpk(zeros, poles, 1, fs=fs) # Compute the frequency response and search for the gain at fstart, we # normalize such that this gain is ~ 0 dB. Rounded to an integer frequency # in Hz. Omg, h = freqz_zpk(z, p, k, worN=int(fs / 2), fs=fs) h_fstart = np.abs(h[int(fstart)]) k *= 1 / h_fstart sos = zpk2sos(z, p, k) return sos
def check_limits(system, spec, num_samples=1000): # fp, fs, Amax=1, Amin=42, sample_rate=48e3 f1 = np.logspace(np.floor(np.log10(spec['fp']))-1, np.log10(spec['fp']), 2*num_samples) f2 = np.logspace(np.log10(spec['fs']), np.log10(spec['sample_rate']/2), num_samples) # f1 = np.linspace(0, spec['fp'], 2*num_samples) # f2 = np.linspace(spec['fs'], spec['sample_rate']/2, num_samples) f = np.hstack([f1, f2]) f, h = signal.freqz_zpk(*system, fs=spec['sample_rate'], worN=f) Hdb = 20 * np.log10(np.abs(h)) pass_band = Hdb[f <= spec['fp']] pass_band_faults = (pass_band < -spec['Amax']).sum() + (pass_band > 0).sum() stop_band_faults = (Hdb[f >= spec['fs']] > -spec['Amin']).sum() total_faults = pass_band_faults + stop_band_faults + system[1].size return total_faults
def get_specbasis(bandpass): low = bandpass[0] high = bandpass[1] corners = bandpass[2] low = low / (0.5 * df) high = high / (0.5 * df) z, p, k = iirfilter(corners, [low, high], btype='band', ftype='butter', output='zpk') w, h = freqz_zpk(z, p, k, worN=len(freq)) # always zerophase h2 = h * np.conjugate(h) return np.real(h2)
def Z2(w3,n): #Krok 1 wc = 2*np.tan(w3/2) poles = np.array([]) #Krok 2 for k in range(0,n): sk = wc*(np.exp(1j*np.pi*(1+2*k)/(2*n)))*np.exp(1j*np.pi/2) poles = np.append(poles,sk) plt.figure(2) plt.plot(np.real(sk),np.imag(sk),marker = 'x') plt.title('Położenie biegunów filtru analogowego') plt.xlabel('Real part') plt.ylabel('Imaginary part') plt.show() #Krok 3 G0_num,G0_den = 1,1 for k in range(0,n): G0_num = G0_num*(-poles[k]) G0_den = G0_den*(2-poles[k]) G0 = G0_num/G0_den poles_digital = (2+poles)/(2-poles) for n in poles_digital: plt.figure(3) plt.scatter(np.real(poles_digital),np.imag(poles_digital),marker='x') plt.xlim((-1.5,1.5)) plt.title('Położenie zer oraz biegunów filtru cyfrowego') plt.ylabel('Imaginary part') plt.xlabel('Real part') plt.plot(-1,0,marker = 'o') plt.show() #Krok 4 w,h = signal.freqz_zpk([-1,-1,-1], poles_digital, G0) fig = plt.figure(4) print(np.where(np.isclose(w, 2.50959257))) print(abs(h)[409]) plt.title('Digital filter frequency response') plt.plot(w, (abs(h)), 'b') plt.xlabel('Frequency [rad/s]') plt.grid() plt.show()
def plotFilterResponse(zpk, fs): """ Plot the filter frequency response. Parameters ---------- zpk : array-like The 3 parameters of the filter [z, p, k]. fs : float Sampling frequency in Hz. Returns: fig : instance of matplotlib.figure.Figure The figure of the filter response. """ z, p, k = zpk w, h = freqz_zpk(z, p, k, worN=8000) plt.plot(0.5 * fs * w / np.pi, 20 * np.log10(abs(h)), 'b') plt.title('Chebyshev II bandstop filter') plt.xlabel('Normalized frequency') plt.ylabel('Amplitude [dB]')
from scipy import signal z, p, k = signal.butter(4, 0.2, output='zpk') w, h = signal.freqz_zpk(z, p, k) import matplotlib.pyplot as plt fig = plt.figure() plt.title('Digital filter frequency response') ax1 = fig.add_subplot(111) plt.plot(w, 20 * np.log10(abs(h)), 'b') plt.ylabel('Amplitude [dB]', color='b') plt.xlabel('Frequency [rad/sample]') ax2 = ax1.twinx() angles = np.unwrap(np.angle(h)) plt.plot(w, angles, 'g') plt.ylabel('Angle (radians)', color='g') plt.grid() plt.axis('tight') plt.show()
def get_respuesta_frecuencial(self, cantidad_muestras, filtro): z = filtro[0] p = filtro[1] k = filtro[2] return signal.freqz_zpk(z, p, k, worN=cantidad_muestras)
def s2z_zpk(s_zeros, s_poles, s_gain, s2z, fs, f0): z_zeros, z_poles, z_gain = s2z(s_zeros, s_poles, s_gain, fs=fs) z_gain *= _np.abs(_sig.freqs_zpk(s_zeros, s_poles, s_gain, worN=[2*_np.pi*f0])[1])\ / _np.abs(_sig.freqz_zpk(z_zeros, z_poles, z_gain, worN=[f0], fs=fs)[1]) return z_zeros, z_poles, z_gain
# Design a 4th-order digital Butterworth filter with cut-off of 100 Hz in a # system with sample rate of 1000 Hz, and plot the frequency response: from scipy import signal z, p, k = signal.butter(4, 100, output='zpk', fs=1000) w, h = signal.freqz_zpk(z, p, k, fs=1000) import matplotlib.pyplot as plt fig = plt.figure() ax1 = fig.add_subplot(1, 1, 1) ax1.set_title('Digital filter frequency response') ax1.plot(w, 20 * np.log10(abs(h)), 'b') ax1.set_ylabel('Amplitude [dB]', color='b') ax1.set_xlabel('Frequency [Hz]') ax1.grid() ax2 = ax1.twinx() angles = np.unwrap(np.angle(h)) ax2.plot(w, angles, 'g') ax2.set_ylabel('Angle [radians]', color='g') plt.axis('tight') plt.show()
def plot_zpk(z, p, k, worN=5000, fs=44.1e3, show=True): """ Visualize a system given the a list of zeros, poles and a gain --------------------------------------------------------------------- INPUTS --------------------------------------------------------------------- z | (1-d iterable) of zeros of the system --------------------------------------------------------------------- p | (1-d iterable) of poles of the system --------------------------------------------------------------------- k | (float) gain of the system --------------------------------------------------------------------- worN | as required by scipy.signal.freqz_zpk, list of | frequencies or number of frequencies to calculate the | reponse at --------------------------------------------------------------------- fs | (float) sampling frequency used to calculate the | frequencies in Hz --------------------------------------------------------------------- show | whether or not to display the resulting graph using | plt.show() --------------------------------------------------------------------- """ if isinstance(worN, float): print( "Float given instead of integer for length of freqz. This will be converted to an integer" ) worN = int(worN) # int casting to ensure supplied the number of frequencies to calculate w, h = sig.freqz_zpk(z, p, k, worN=worN) # rad/samp, magnitudes fig = plt.figure(figsize=(20, 4)) gs = GridSpec(2, 2, width_ratios=[1, 2]) #plot poles/zeros ax1 = fig.add_subplot(gs[0:, 0]) #gives the unit circle unit_circle = patches.Circle((0, 0), radius=1, fill=False, color='grey', ls='dotted', alpha=0.5) ax1.add_patch(unit_circle) # plot poles and zeros for pole in p: plt.plot(pole.real, pole.imag, 'x', markersize=7, alpha=0.7, color='blue') for zero in z: plt.plot(zero.real, zero.imag, 'o', markersize=7, markerfacecolor='none', markeredgecolor=colorConverter.to_rgba('mediumseagreen', alpha=0.7)) ax1.grid(True) ax1.set_xlim(-1.2, 1.2) ax1.set_ylim(-1.2, 1.2) ax1.set_title('Z domain Poles/Zeros') #plot mag/phase ax2 = fig.add_subplot(gs[0, 1:]) db = 20 * np.log10(np.abs(h)) ax2.plot(w / np.pi * fs / 2, db) ax2.set_xscale('symlog') spacing = octaves_to(fs / 2) ax2.set_xticks(spacing) ax2.set_xticklabels(spacing, rotation=30) ax2.set_ylim(-75, 5) ax2.grid(True) ax2.set_yticks([0, -20, -40, -60]) ax2.set_ylabel('Gain [dB]') ax2.set_title('Frequency Response') ax3 = fig.add_subplot(gs[1, 1:]) ax3.plot(w / np.pi * fs / 2, np.angle(h)) ax3.set_xscale('symlog') ax3.set_xticks(spacing) ax3.set_xticklabels(spacing, rotation=30) ax3.grid(True) ax3.set_yticks([-np.pi, -0.5 * np.pi, 0, 0.5 * np.pi, np.pi], [r'$-\pi$', r'$-\pi/2$', '0', r'$\pi/2$', r'$\pi$']) ax3.set_ylabel('Phase [rad]') ax3.set_xlabel('Frequency (Hz)') if show: plt.show()
from scipy import signal import matplotlib.pyplot as plt fs = 100 bf = 2 * np.pi * np.array([7, 13]) filts = signal.lti(*signal.butter(4, bf, btype='bandpass', analog=True, output='zpk')) filtz = signal.lti(*signal.bilinear_zpk(filts.zeros, filts.poles, filts.gain, fs)) wz, hz = signal.freqz_zpk(filtz.zeros, filtz.poles, filtz.gain) ws, hs = signal.freqs_zpk(filts.zeros, filts.poles, filts.gain, worN=fs*wz) plt.semilogx(wz*fs/(2*np.pi), 20*np.log10(np.abs(hz).clip(1e-15)), label=r'$|H(j \omega)|$') plt.semilogx(wz*fs/(2*np.pi), 20*np.log10(np.abs(hs).clip(1e-15)), label=r'$|H_z(e^{j \omega})|$') plt.legend() plt.xlabel('Frequency [Hz]') plt.ylabel('Magnitude [dB]') plt.grid()