def _process_waveforms(self, waveforms): """Process the waveforms with the DataLogger information.""" # Correcting electric Field with actual length of dipole for runid in self.runlist: try: mask = ((waveforms.index > self.runinfo[runid]['Start']) & (waveforms.index < self.runinfo[runid]['End'])) waveforms["QN"].loc[mask] *= (100.0/self.runinfo[runid]['Ex']) # mV/km waveforms["QE"].loc[mask] *= (100.0/self.runinfo[runid]['Ey']) # mV/km # If there's no info about length, then use default length. except KeyError: pass if self.zpk is None: return waveforms # Filtering waveforms by NIMS system response for channel in waveforms: # loop for FE, FN, FZ, QN, QE for filt in self.zpk[channel]: # loop for filter zval = self.zpk[channel][filt]['z'] pval = self.zpk[channel][filt]['p'] kval = self.zpk[channel][filt]['k'] # convert analog zpk to digital filter zval, pval, kval = bilinear_zpk(zval, pval, kval, self.samplingrate) b, a = zpk2tf(zval, pval, kval) waveforms[channel] = filtfilt(b, a, waveforms[channel].interpolate()) return waveforms
def C_Sos_design(self): """ Create filter coefficients of the C-weighting filter. Uses the bilinear transform to convert the analog filter to a digital one. Returns: Sos: Second order sections """ fs = self.fs p1 = 2 * np.pi * self.f1 p4 = 2 * np.pi * self.f4 zeros_analog = [0, 0] poles_analog = [-p1, -p1, -p4, -p4] k_analog = p4**2 / self._C_uncor(self.fr) z, p, k = bilinear_zpk(zeros_analog, poles_analog, k_analog, fs) sos = zpk2sos(z, p, k) return sos
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
exit() # numtaps_iir = 55 # b, a = utils.yulewalk(numtaps_iir, filt_gains_freq / np.max(filt_gains_freq), filt_gains_clean) # w, h = freqz(b, a, worN=32768) # w_hz = w / max(w) * RATE / 2 # # utils.plot_resp(abs(h), w_hz, '-', '%i tap IIR filter' % numtaps_iir, meas_type='IIR/gain filter response', plot_in_db=True, xlim=[st_freq, en_freq], log_x=plot_log) # plt.legend() # # plt.ylim([-2, 10]) # plt.savefig('plot_%s_iir-%i_fir-%i.png' % (freq_filt_str, numtaps_iir, numtaps_fir)) # plt.show() z, p, k = bilinear_zpk([0.0, 0.0, 0.0, 0.0], [-129.4, -129.4, -676.7, -4636.0, -76655.0, -76655.0], 7.39705E9, RATE) b, a = zpk2tf(z, p, k) print('B taps:') for tap in b: print('{:10.12f}'.format(tap)) print('A taps:') for tap in a: print('{:10.12f}'.format(tap)) w, h = freqz(b, a, worN=32768) w_hz = w / max(w) * RATE / 2 utils.plot_resp(abs(h), w_hz, '-', '%i tap A weighting IIR filter' % len(a), meas_type='IIR/gain filter response', plot_in_db=True, xlim=[st_freq, en_freq], log_x=plot_log) plt.show()
def crrc_shaper(order, peaktime, dt=1E-9, pz=0.0): rc = peaktime / order poles = np.full(order + 1, -1. / rc) zeros = np.array([-pz]) dzpk = signal.bilinear_zpk(zeros, poles, 1, 1. / dt) return dzpk, (zeros, poles)
def gaussian_shaper(order, peaktime, dt=1E-9, pz=0.0, user_tf=None): # Based on Ohkawa 1976 #"Direct synthesis of the Gaussian filter for nuclear pulse amplifiers" if order == 1: tf = 2 * 1.0844 poles = [complex(-1, 0)] elif order == 2: tf = 9.734458e-01 c = np.sqrt(np.sqrt(2) + 2) * 0.5 poles = [ c * complex(-1, np.sqrt(2) - 1), c * complex(-1, 1 - np.sqrt(2)) ] elif order == 3: tf = 6.740357e-01 poles = [ complex(-1.2633573, 0), complex(-1.1490948, 0.7864188), complex(-1.1490948, -0.7864188) ] elif order == 4: tf = 5.106046e-01 poles = [ complex(-1.3553576, 0.3277948), complex(-1.3553576, -0.3277948), complex(-1.1810803, 1.0603749), complex(-1.1810803, -1.0603749) ] elif order == 5: tf = 4.267639e-01 poles = [ complex(-1.4766878, 0), complex(-1.4166647, 0.5978596), complex(-1.4166647, -0.5978596), complex(-1.2036832, 1.2994843), complex(-1.2036832, -1.2994843) ] elif order == 6: tf = 3.737515e-01 poles = [ complex(-1.5601279, 0.2686793), complex(-1.5601279, -0.2686793), complex(-1.4613750, 0.8329565), complex(-1.4613750, -0.8329565), complex(-1.2207388, 1.5145343), complex(-1.2207388, -1.5145343) ] elif order == 7: tf = 3.371212e-01 poles = [ complex(-1.6610245, 0), complex(-1.6229725, 0.5007975), complex(-1.6229725, -0.5007975), complex(-1.4949993, 1.0454546), complex(-1.4949993, -1.0454546), complex(-1.2344141, 1.7113028), complex(-1.2344141, -1.7113028) ] else: raise ValueError( 'only gaussian shaper orders between 1 and 7 are supported') #this gave roughly the right answer, but a separate script solved for the tf values above, which give extremely acurate peaking times #sigma = 2 * 1.0844 * peaktime * (1./order) if user_tf is not None: tf = user_tf sigma = tf * peaktime poles = np.array(poles) / sigma zeros = np.array([-pz]) dzpk = signal.bilinear_zpk(zeros, poles, 1, 1. / dt) return dzpk, (zeros, poles)
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()