def infer_passtype(f_range): """Given frequency definition of a filter, infer the passtype. Parameters ---------- f_range : tuple of (float, float) Cutoff frequency(ies) used for filter, specified as f_lo & f_hi. Returns ------- pass_type : str Which kind of filter pass_type is consistent with the frequency definition provided. Notes ----- Assumes that a definition with two frequencies is a 'bandpass' (not 'bandstop'). """ if f_range[0] is None: pass_type = 'lowpass' elif f_range[1] is None: pass_type = 'highpass' else: pass_type = 'bandpass' # Check the inferred passtype & frequency definition is valid _ = check_filter_definition(pass_type, f_range) return pass_type
def compute_pass_band(fs, pass_type, f_range): """Compute the pass bandwidth of a filter. Parameters ---------- fs : float Sampling rate, in Hz. pass_type : {'bandpass', 'bandstop', 'lowpass', 'highpass'} Which kind of filter to apply: * 'bandpass': apply a bandpass filter * 'bandstop': apply a bandstop (notch) filter * 'lowpass': apply a lowpass filter * 'highpass' : apply a highpass filter f_range : tuple of (float, float) or float Cutoff frequency(ies) used for filter, specified as f_lo & f_hi. For 'bandpass' & 'bandstop', must be a tuple. For 'lowpass' or 'highpass', can be a float that specifies pass frequency, or can be a tuple and is assumed to be (None, f_hi) for 'lowpass', and (f_lo, None) for 'highpass'. Returns ------- pass_bw : float The pass bandwidth of the filter. """ f_lo, f_hi = check_filter_definition(pass_type, f_range) if pass_type in ['bandpass', 'bandstop']: pass_bw = f_hi - f_lo elif pass_type == 'highpass': pass_bw = compute_nyquist(fs) - f_lo elif pass_type == 'lowpass': pass_bw = f_hi return pass_bw
def design_fir_filter(sig_length, fs, pass_type, f_range, n_cycles=3, n_seconds=None): """Design an FIR filter. Parameters ---------- sig_length : int The length of the signal to be filtered. fs : float Sampling rate, in Hz. pass_type : {'bandpass', 'bandstop', 'lowpass', 'highpass'} Which kind of filter to apply: * 'bandpass': apply a bandpass filter * 'bandstop': apply a bandstop (notch) filter * 'lowpass': apply a lowpass filter * 'highpass' : apply a highpass filter f_range : tuple of (float, float) or float Cutoff frequency(ies) used for filter, specified as f_lo & f_hi. For 'bandpass' & 'bandstop', must be a tuple. For 'lowpass' or 'highpass', can be a float that specifies pass frequency, or can be a tuple and is assumed to be (None, f_hi) for 'lowpass', and (f_lo, None) for 'highpass'. n_cycles : float, optional, default: 3 Length of filter, in number of cycles, defined at the 'f_lo' frequency. This parameter is overwritten by `n_seconds`, if provided. n_seconds : float, optional Length of filter, in seconds. This parameter overwrites `n_cycles`. Returns ------- filter_coefs : 1d array The filter coefficients for an FIR filter. """ # Check filter definition f_lo, f_hi = check_filter_definition(pass_type, f_range) filt_len = compute_filt_len(sig_length, fs, pass_type, f_lo, f_hi, n_cycles, n_seconds) f_nyq = compute_nyquist(fs) if pass_type == 'bandpass': filter_coefs = firwin(filt_len, (f_lo, f_hi), pass_zero=False, nyq=f_nyq) elif pass_type == 'bandstop': filter_coefs = firwin(filt_len, (f_lo, f_hi), nyq=f_nyq) elif pass_type == 'highpass': filter_coefs = firwin(filt_len, f_lo, pass_zero=False, nyq=f_nyq) elif pass_type == 'lowpass': filter_coefs = firwin(filt_len, f_hi, nyq=f_nyq) return filter_coefs
def design_iir_filter(fs, pass_type, f_range, butterworth_order): """Design an IIR filter. Parameters ---------- fs : float Sampling rate, in Hz. pass_type : {'bandpass', 'bandstop', 'lowpass', 'highpass'} Which kind of filter to apply: * 'bandpass': apply a bandpass filter * 'bandstop': apply a bandstop (notch) filter * 'lowpass': apply a lowpass filter * 'highpass' : apply a highpass filter f_range : tuple of (float, float) or float Cutoff frequency(ies) used for filter, specified as f_lo & f_hi. For 'bandpass' & 'bandstop', must be a tuple. For 'lowpass' or 'highpass', can be a float that specifies pass frequency, or can be a tuple and is assumed to be (None, f_hi) for 'lowpass', and (f_lo, None) for 'highpass'. butterworth_order : int Order of the butterworth filter, if using an IIR filter. See input 'N' in scipy.signal.butter. Returns ------- sos : 2d array Second order series coefficients for an IIR filter. Has shape of (n_sections, 6). Examples -------- Compute coefficients for a bandstop IIR filter: >>> sos = design_iir_filter(fs=500, pass_type='bandstop', ... f_range=(55, 65), butterworth_order=7) """ # Check filter definition f_lo, f_hi = check_filter_definition(pass_type, f_range) f_nyq = compute_nyquist(fs) if pass_type in ('bandpass', 'bandstop'): win = (f_lo / f_nyq, f_hi / f_nyq) elif pass_type == 'highpass': win = f_lo / f_nyq elif pass_type == 'lowpass': win = f_hi / f_nyq # Design filter sos = butter(butterworth_order, win, pass_type, output='sos') return sos
def design_iir_filter(fs, pass_type, f_range, butterworth_order): """Design an IIR filter. Parameters ---------- fs : float Sampling rate, in Hz. pass_type : {'bandpass', 'bandstop', 'lowpass', 'highpass'} Which kind of filter to apply: * 'bandpass': apply a bandpass filter * 'bandstop': apply a bandstop (notch) filter * 'lowpass': apply a lowpass filter * 'highpass' : apply a highpass filter f_range : tuple of (float, float) or float Cutoff frequency(ies) used for filter, specified as f_lo & f_hi. For 'bandpass' & 'bandstop', must be a tuple. For 'lowpass' or 'highpass', can be a float that specifies pass frequency, or can be a tuple and is assumed to be (None, f_hi) for 'lowpass', and (f_lo, None) for 'highpass'. butterworth_order : int Order of the butterworth filter, if using an IIR filter. See input 'N' in scipy.signal.butter. Returns ------- b_vals : 1d array B value filter coefficients for an IIR filter. a_vals : 1d array A value filter coefficients for an IIR filter. """ # Warn about only recommending IIR for bandstop if pass_type != 'bandstop': warn('IIR filters are not recommended other than for notch filters.') # Check filter definition f_lo, f_hi = check_filter_definition(pass_type, f_range) f_nyq = compute_nyquist(fs) if pass_type in ('bandpass', 'bandstop'): win = (f_lo / f_nyq, f_hi / f_nyq) elif pass_type == 'highpass': win = f_lo / f_nyq elif pass_type == 'lowpass': win = f_hi / f_nyq # Design filter b_vals, a_vals = butter(butterworth_order, win, pass_type) return b_vals, a_vals
def sim_powerlaw(n_seconds, fs, exponent=-2.0, f_range=None, **filter_kwargs): """Simulate a power law time series, with a specified exponent. Parameters ---------- n_seconds : float Simulation time, in seconds. fs : float Sampling rate of simulated signal, in Hz. exponent : float, optional, default: -2 Desired power-law exponent, of the form P(f)=f^exponent. f_range : list of [float, float] or None, optional Frequency range to filter simulated data, as [f_lo, f_hi], in Hz. **filter_kwargs : kwargs, optional Keyword arguments to pass to `filter_signal`. Returns ------- sig: 1d array Time-series with the desired power law exponent. """ # Get the number of samples to simulate for the signal # If filter is to be filtered, with FIR, add extra to compensate for edges if f_range and filter_kwargs.get('filter_type', None) != 'iir': pass_type = infer_passtype(f_range) filt_len = compute_filter_length(fs, pass_type, *check_filter_definition(pass_type, f_range), n_seconds=filter_kwargs.get('n_seconds', None), n_cycles=filter_kwargs.get('n_cycles', 3)) n_samples = int(n_seconds * fs) + filt_len + 1 else: n_samples = int(n_seconds * fs) sig = _create_powerlaw(n_samples, fs, exponent) if f_range is not None: sig = filter_signal(sig, fs, infer_passtype(f_range), f_range, remove_edges=True, **filter_kwargs) # Drop the edges, that were compensated for, if not using IIR (using FIR) if not filter_kwargs.get('filter_type', None) == 'iir': sig, _ = remove_nans(sig) return sig
def sim_powerlaw(n_seconds, fs, exponent=-2.0, f_range=None, **filter_kwargs): """Simulate a power law time series, with a specified exponent. Parameters ---------- n_seconds : float Simulation time, in seconds. fs : float Sampling rate of simulated signal, in Hz. exponent : float, optional, default: -2 Desired power-law exponent, of the form P(f)=f^exponent. f_range : list of [float, float] or None, optional Frequency range to filter simulated data, as [f_lo, f_hi], in Hz. **filter_kwargs : kwargs, optional Keyword arguments to pass to `filter_signal`. Returns ------- sig : 1d array Time-series with the desired power law exponent. Notes ----- - Powerlaw data with exponents is created by spectrally rotating white noise [1]_. References ---------- .. [1] Timmer, J., & Konig, M. (1995). On Generating Power Law Noise. Astronomy and Astrophysics, 300, 707–710. Examples -------- Simulate a power law signal, with an exponent of -2 (brown noise): >>> sig = sim_powerlaw(n_seconds=1, fs=500, exponent=-2.0) Simulate a power law signal, with a highpass filter applied at 2 Hz: >>> sig = sim_powerlaw(n_seconds=1, fs=500, exponent=-1.5, f_range=(2, None)) """ # Compute the number of samples for the simulated time series n_samples = compute_nsamples(n_seconds, fs) # Get the number of samples to simulate for the signal # If signal is to be filtered, with FIR, add extra to compensate for edges if f_range and filter_kwargs.get('filter_type', None) != 'iir': pass_type = infer_passtype(f_range) filt_len = compute_filter_length( fs, pass_type, *check_filter_definition(pass_type, f_range), n_seconds=filter_kwargs.get('n_seconds', None), n_cycles=filter_kwargs.get('n_cycles', 3)) n_samples += filt_len + 1 # Simulate the powerlaw data sig = _create_powerlaw(n_samples, fs, exponent) if f_range is not None: sig = filter_signal(sig, fs, infer_passtype(f_range), f_range, remove_edges=True, **filter_kwargs) # Drop the edges, that were compensated for, if not using FIR filter if not filter_kwargs.get('filter_type', None) == 'iir': sig, _ = remove_nans(sig) return sig