# This filter hypothetically achieves zero ripple in the frequency domain, # perfect attenuation, and perfect steepness. However, due to the discontunity # in the frequency response, the filter would require infinite ringing in the # time domain (i.e., infinite order) to be realized. Another way to think of # this is that a rectangular window in frequency is actually sinc_ function # in time, which requires an infinite number of samples, and thus infinite # time, to represent. So although this filter has ideal frequency suppression, # it has poor time-domain characteristics. # # Let's try to naïvely make a brick-wall filter of length 0.1 sec, and look # at the filter itself in the time domain and the frequency domain: n = int(round(0.1 * sfreq)) + 1 t = np.arange(-n // 2, n // 2) / sfreq # center our sinc h = np.sinc(2 * f_p * t) / (4 * np.pi) plot_filter(h, sfreq, freq, gain, 'Sinc (0.1 sec)', flim=flim) ############################################################################### # This is not so good! Making the filter 10 times longer (1 sec) gets us a # bit better stop-band suppression, but still has a lot of ringing in # the time domain. Note the x-axis is an order of magnitude longer here, # and the filter has a correspondingly much longer group delay (again equal # to half the filter length, or 0.5 seconds): n = int(round(1. * sfreq)) + 1 t = np.arange(-n // 2, n // 2) / sfreq h = np.sinc(2 * f_p * t) / (4 * np.pi) plot_filter(h, sfreq, freq, gain, 'Sinc (1.0 sec)', flim=flim) ############################################################################### # Let's make the stop-band tighter still with a longer filter (10 sec),
def test_plot_filter(): """Test filter plotting.""" l_freq, h_freq, sfreq = 2., 40., 1000. data = np.zeros(5000) freq = [0, 2, 40, 50, 500] gain = [0, 1, 1, 0, 0] h = create_filter(data, sfreq, l_freq, h_freq, fir_design='firwin2') plot_filter(h, sfreq) plt.close('all') plot_filter(h, sfreq, freq, gain) plt.close('all') iir = create_filter(data, sfreq, l_freq, h_freq, method='iir') plot_filter(iir, sfreq) plt.close('all') plot_filter(iir, sfreq, freq, gain) plt.close('all') iir_ba = create_filter(data, sfreq, l_freq, h_freq, method='iir', iir_params=dict(output='ba')) plot_filter(iir_ba, sfreq, freq, gain) plt.close('all') plot_filter(h, sfreq, freq, gain, fscale='linear') plt.close('all')
# in the frequency response, the filter would require infinite ringing in the # time domain (i.e., infinite order) to be realized. Another way to think of # this is that a rectangular window in the frequency domain is actually a sinc_ # function in the time domain, which requires an infinite number of samples # (and thus infinite time) to represent. So although this filter has ideal # frequency suppression, it has poor time-domain characteristics. # # Let's try to naïvely make a brick-wall (sinc) filter of length 0.1 s, and # look at the filter itself in the time domain and the frequency domain: n = int(round(0.1 * sfreq)) n -= n % 2 - 1 # make it odd t = np.arange(-(n // 2), n // 2 + 1) / sfreq # center our sinc h = np.sinc(2 * f_p * t) / (4 * np.pi) kwargs = dict(flim=flim, dlim=dlim) plot_filter(h, sfreq, freq, gain, 'Sinc (0.1 s)', compensate=True, **kwargs) # %% # This is not so good! Making the filter 10 times longer (1 s) gets us a # slightly better stop-band suppression, but still has a lot of ringing in # the time domain. Note the x-axis is an order of magnitude longer here, # and the filter has a correspondingly much longer group delay (again equal # to half the filter length, or 0.5 seconds): n = int(round(1. * sfreq)) n -= n % 2 - 1 # make it odd t = np.arange(-(n // 2), n // 2 + 1) / sfreq h = np.sinc(2 * f_p * t) / (4 * np.pi) plot_filter(h, sfreq, freq, gain, 'Sinc (1.0 s)', compensate=True, **kwargs) # %%
def learn_filters(self, fs, log_approx_levels=4): """Learn digital filters that approximate the Gaussian peaks and invert the background""" from scipy.signal import cheby1, remez from mne.viz import plot_filter nyquist = fs / 2 log_filter_coeffs, gaussian_filter_coeffs = [], [] log_amplitudes, gaussian_amplitudes = [], [] ideal_gains, figs = [], [] frequencies = np.linspace(0, nyquist, 10000) if self.background_mode == 'knee': slope = self.background_params_[2] knee = self.background_params_[1] else: slope = self.background_params_[1] knee = 0. if knee < 0.: knee = 0. offset = self.background_params_[0] print('slope, knee, offset:') print(slope, knee, offset) logarg = knee + frequencies**slope logarg[logarg < 0] = 1e-20 ideal_gain = np.log10(logarg) ideal_gain = ideal_gain / np.max(ideal_gain) ideal_gain[0] = 0 ideal_gain = np.clip(ideal_gain, 1e-20, 1) print('ideal_gain', np.min(ideal_gain), np.max(ideal_gain)) ideal_gains.append(ideal_gain) print('Cutoffs/ripple for log approximation:') for i in range(log_approx_levels): L_half = 1 - (1 / (2**(i + 1))) # Half of remaining Log amplitude f_half = ((knee + nyquist**slope)**L_half - knee)**( 1 / slope) # Frequency of half of remaining log amp max_ripple = 0.4 * np.sqrt((2**i) * 4 * slope) print('L, f_half, max_ripple', L_half, f_half, max_ripple) b, a = cheby1(1, max_ripple, f_half, btype='highpass', fs=fs) coeffs = tuple((b, a)) log_filter_coeffs.append(coeffs) log_amplitudes.append(1 - L_half) iir_params = dict(b=b, a=a) f = plot_filter(iir_params, fs, freq=frequencies, gain=ideal_gain, color='#1f77b4', flim=[0, 40], fscale='linear', alim=(-30, 10), show=False) f.set_figwidth(6) f.set_figheight(12) figs.append(f) print('Gaussian approximations:') # bandpass each of the peaks for idx, ([centre_frequency, amplitude, std_dev]) in enumerate(self._gaussian_params): ideal_gain = ((1 / (std_dev * np.sqrt(2 * np.pi))) * np.exp(-(1 / 2) * (( (frequencies - centre_frequency) / std_dev)**2))) ideal_gain = ((ideal_gain - np.min(ideal_gain)) / (np.max(ideal_gain) - np.min(ideal_gain))) ideal_gain = np.clip(ideal_gain, 1e-20, 1) # wp = np.sqrt((np.ln(0.5) + np.ln(std_dev) + np.ln(np.sqrt(2*np.pi))))*std_dev wp = 1 / (np.sqrt( np.log(0.25 * np.pi * std_dev**2) * (std_dev**2))) # ws = np.sqrt(-(np.log(0.001) + np.log(std_dev) + np.log(np.sqrt(2*np.pi))))*std_dev wp_low = (centre_frequency - wp) wp_high = (centre_frequency + wp) ws_low = (centre_frequency - 3 * std_dev) ws_high = (centre_frequency + 3 * std_dev) if ws_low < 0: ws_low = 0.1 if ws_high > nyquist: ws_high = nyquist print(ws_low, wp_low, wp_high, ws_high) result = None counter = 0 while result is None: try: ws_low = centre_frequency - 3 * std_dev + (counter / 10 * std_dev) ws_high = centre_frequency + 3 * std_dev - (counter / 10 * std_dev) wp_low = centre_frequency - wp - (counter / 10 * std_dev) wp_high = centre_frequency + wp + (counter / 10 * std_dev) if counter > 100: result = 1 print( 'Could not fit a digital filter to this Gaussian') b = remez(256, [0, ws_low, wp_low, wp_high, ws_high, nyquist], [0, 1, 0], fs=fs) result = 1 except: counter += 1 print('Transition band too wide! Relaxing the math...', counter) gaussian_filter_coeffs.append(b) gaussian_amplitudes.append(amplitude) ideal_gains.append(ideal_gain) f = plot_filter(b, fs, freq=frequencies, gain=ideal_gain, color='#1f77b4', flim=[0, 40], fscale='linear', alim=(-60, 10), show=False) f.set_figwidth(6) f.set_figheight(12) figs.append(f) return log_filter_coeffs, gaussian_filter_coeffs, log_amplitudes, gaussian_amplitudes, figs, ideal_gains, offset
def test_plot_filter(): """Test filter plotting.""" l_freq, h_freq, sfreq = 2., 40., 1000. data = np.zeros(5000) freq = [0, 2, 40, 50, 500] gain = [0, 1, 1, 0, 0] h = create_filter(data, sfreq, l_freq, h_freq, fir_design='firwin2') plot_filter(h, sfreq) plt.close('all') plot_filter(h, sfreq, freq, gain) plt.close('all') iir = create_filter(data, sfreq, l_freq, h_freq, method='iir') plot_filter(iir, sfreq) plt.close('all') plot_filter(iir, sfreq, freq, gain) plt.close('all') iir_ba = create_filter(data, sfreq, l_freq, h_freq, method='iir', iir_params=dict(output='ba')) plot_filter(iir_ba, sfreq, freq, gain) plt.close('all') fig = plot_filter(h, sfreq, freq, gain, fscale='linear') assert len(fig.axes) == 3 plt.close('all') fig = plot_filter(h, sfreq, freq, gain, fscale='linear', plot=('time', 'delay')) assert len(fig.axes) == 2 plt.close('all') fig = plot_filter(h, sfreq, freq, gain, fscale='linear', plot=['magnitude', 'delay']) assert len(fig.axes) == 2 plt.close('all') fig = plot_filter(h, sfreq, freq, gain, fscale='linear', plot='magnitude') assert len(fig.axes) == 1 plt.close('all') fig = plot_filter(h, sfreq, freq, gain, fscale='linear', plot=('magnitude')) assert len(fig.axes) == 1 plt.close('all') with pytest.raises(ValueError, match='Invalid value for the .plot'): plot_filter(h, sfreq, freq, gain, plot=('turtles')) _, axes = plt.subplots(1) fig = plot_filter(h, sfreq, freq, gain, plot=('magnitude'), axes=axes) assert len(fig.axes) == 1 _, axes = plt.subplots(2) fig = plot_filter(h, sfreq, freq, gain, plot=('magnitude', 'delay'), axes=axes) assert len(fig.axes) == 2 plt.close('all') _, axes = plt.subplots(1) with pytest.raises(ValueError, match='Length of axes'): plot_filter(h, sfreq, freq, gain, plot=('magnitude', 'delay'), axes=axes)
def test_plot_filter(): """Test filter plotting.""" import matplotlib.pyplot as plt l_freq, h_freq, sfreq = 2., 40., 1000. data = np.zeros(5000) freq = [0, 2, 40, 50, 500] gain = [0, 1, 1, 0, 0] h = create_filter(data, sfreq, l_freq, h_freq) plot_filter(h, sfreq) plt.close('all') plot_filter(h, sfreq, freq, gain) plt.close('all') iir = create_filter(data, sfreq, l_freq, h_freq, method='iir') plot_filter(iir, sfreq) plt.close('all') plot_filter(iir, sfreq, freq, gain) plt.close('all') iir_ba = create_filter(data, sfreq, l_freq, h_freq, method='iir', iir_params=dict(output='ba')) plot_filter(iir_ba, sfreq, freq, gain) plt.close('all') plot_filter(h, sfreq, freq, gain, fscale='linear') plt.close('all')
fpath = subject.dataset_path(proc=proc, suffix=suffix, ext=ext) raw = mne.io.read_raw_fif(fpath, preload=True) X = raw.get_data() #%% Filter properties h = mne.filter.create_filter(X, sfreq=sfreq, l_freq=l_freq, h_freq=h_freq, filter_length='auto', l_trans_bandwidth=l_trans_bandwidth, h_trans_bandwidth=h_trans_bandwidth, fir_window=fir_window, phase=phase) plot_filter(h, sfreq=500) #%% Extract events time stamp of 2 sitmulus presentation (Place and Face) events = mne.events_from_annotations(raw) events_sample_stamp = events[0][:, 0] events_time_stamp = events_sample_stamp / sfreq #%% Envelope of a representative visual channel during 2 stimulus raw_band = raw.copy().pick_channels(['LTo6']).filter( l_freq=l_freq, h_freq=l_freq + band_size, phase=phase, filter_length='auto', l_trans_bandwidth=l_trans_bandwidth,