Skip to content
Permalink
release
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
function [filt, B, A] = ft_preproc_bandpassfilter(dat, Fs, Fbp, order, type, dir, instabilityfix, df, wintype, dev, plotfiltresp, usefftfilt)
% FT_PREPROC_BANDPASSFILTER applies a band-pass filter to the data and thereby
% removes the spectral components in the data except for the ones in the
% specified frequency band.
%
% Use as
% [filt] = ft_preproc_bandpassfilter(dat, Fs, Fbp, order, type, dir, instabilityfix, df, wintype, dev, plotfiltresp, usefftfilt)
% where
% dat data matrix (Nchans X Ntime)
% Fs sampling frequency in Hz
% Fbp frequency band, specified as [Fhp Flp] in Hz
% order optional filter order, default is 4 (but) or dependent on frequency band and data length (fir/firls)
% type optional filter type, can be
% 'but' Butterworth IIR filter (default)
% 'firws' FIR filter with windowed sinc
% 'fir' FIR filter using MATLAB fir1 function
% 'firls' FIR filter using MATLAB firls function (requires MATLAB Signal Processing Toolbox)
% 'brickwall' frequency-domain filter using forward and inverse FFT
% dir optional filter direction, can be
% 'onepass' forward filter only
% 'onepass-reverse' reverse filter only, i.e. backward in time
% 'onepass-zerophase' zero-phase forward filter with delay compensation (default for firws, linear-phase symmetric FIR only)
% 'onepass-reverse-zerophase' zero-phase reverse filter with delay compensation
% 'onepass-minphase' minimum-phase converted forward filter (non-linear, only for firws)
% 'twopass' zero-phase forward and reverse filter (default, except for firws)
% 'twopass-reverse' zero-phase reverse and forward filter
% 'twopass-average' average of the twopass and the twopass-reverse
% instabilityfix optional method to deal with filter instabilities
% 'no' only detect and give error (default)
% 'reduce' reduce the filter order
% 'split' split the filter in two lower-order filters, apply sequentially
% df optional transition width (only for firws)
% wintype optional window type (only for firws), can be
% 'hamming' (default) maximum passband deviation 0.0022 [0.22%], stopband attenuation -53dB
% 'hann' maximum passband deviation 0.0063 [0.63%], stopband attenuation -44dB
% 'blackman' maximum passband deviation 0.0002 [0.02%], stopband attenuation -74dB
% 'kaiser'
% dev optional max passband deviation/stopband attenuation (only for firws with kaiser window, default = 0.001 [0.1%, -60 dB])
% plotfiltresp optional, 'yes' or 'no', plot filter responses (only for firws, default = 'no')
% usefftfilt optional, 'yes' or 'no', use fftfilt instead of filter (only for firws, default = 'no')
%
% Note that a one- or two-pass filter has consequences for the strength of the
% filter, i.e. a two-pass filter with the same filter order will attenuate the signal
% twice as strong.
%
% Further note that the filter type 'brickwall' filters in the frequency domain,
% but may have severe issues. For instance, it has the implication that the time
% domain signal is periodic. Another issue pertains to that frequencies are
% not well defined over short time intervals; particularly for low frequencies.
%
% If the data contains NaNs, these will affect the output. With an IIR
% filter, and/or with FFT-filtering, local NaNs will spread to the whole
% time series. With a FIR filter, local NaNs will spread locally, depending
% on the filter order.
%
% See also PREPROC
% Copyright (c) 2003-2022, Robert Oostenveld, Arjen Stolk, Andreas Widmann,
% Jan-Mathijs Schoffelen
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
% FieldTrip is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% FieldTrip is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$
% set the default filter order later
if nargin<4 || isempty(order)
order = [];
end
% set the default filter type
if nargin<5 || isempty(type)
type = 'but';
end
% set the default filter direction
if nargin<6 || isempty(dir)
if strcmp(type, 'firws')
dir = 'onepass-zerophase';
else
dir = 'twopass';
end
end
% set the default method to deal with filter instabilities
if nargin<7|| isempty(instabilityfix)
instabilityfix = 'no';
end
% Set default transition width later
if nargin < 8 || isempty(df)
df = [];
end
% Set default window type
if nargin < 9 || isempty(wintype)
wintype = 'hamming';
end
% Set default passband deviation/stopband attenuation for Kaiser window
if nargin < 10 || isempty(dev)
if strcmp(wintype, 'kaiser')
dev = 0.001;
else
dev = [];
end
end
% Set default passband deviation/stopband attenuation for Kaiser window
if nargin < 11 || isempty(plotfiltresp)
plotfiltresp = 'no';
end
% Set default filter function
if nargin < 12 || isempty(usefftfilt)
usefftfilt = false;
else
% convert to boolean value
usefftfilt = istrue(usefftfilt);
end
% Filtering does not work on integer data
if ~isa(dat, 'double') && ~isa(dat, 'single')
dat = cast(dat, 'double');
end
% preprocessing fails on channels that contain NaN
if any(isnan(dat(:)))
ft_warning('FieldTrip:dataContainsNaN', 'data contains NaN values');
end
% Nyquist frequency
Fn = Fs/2;
% demean the data before filtering
meandat = nanmean(dat,2);
dat = bsxfun(@minus, dat, meandat);
% compute filter coefficients
switch type
case 'but'
if isempty(order)
order = 4;
end
[B, A] = butter(order, [min(Fbp)/Fn max(Fbp)/Fn]);
case 'firws'
% Input arguments
if length(Fbp) ~= 2
ft_error('Two cutoff frequencies required.')
end
% Filter order AND transition width set?
if ~isempty(order) && ~isempty(df)
ft_warning('firws:dfOverridesN', 'Filter order AND transition width set - transition width setting will override filter order.')
elseif isempty(order) && isempty(df) % Default transition width heuristic
df = fir_df(Fbp, Fs);
end
% Compute filter order from transition width
[foo, maxDf] = fir_df(Fbp, Fs); %#ok<ASGLU>
isOrderLow = false;
if ~isempty(df)
if df > maxDf
ft_error('Transition band too wide. Maximum transition width is %.2f Hz.', maxDf)
end
[order, dev] = firwsord(wintype, Fs, df, dev);
else % Check filter order otherwise
[df, dev] = invfirwsord(wintype, Fs, order, dev);
if df > maxDf
nOpt = firwsord(wintype, Fs, maxDf, dev);
ft_warning('firws:filterOrderLow', 'Filter order too low. For better results a minimum filter order of %d is recommended. Effective cutoff frequency might deviate from requested cutoff frequency.', nOpt)
isOrderLow = true;
end
end
% Window
if strcmp(wintype, 'kaiser')
beta = kaiserbeta(dev);
win = windows('kaiser', order + 1, beta);
else
win = windows(wintype, order + 1);
end
% Impulse response
Fbp = sort(Fbp);
B = firws(order, sort(Fbp) / Fn, 'band', win);
A = 1;
% Convert to minimum phase
if strcmp(dir, 'onepass-minphase')
B = minphaserceps(B);
end
% Twopass filtering
if strncmp(dir, 'twopass', 7)
pbDev = (dev + 1)^2 - 1;
sbAtt = 20 * log10(dev^2);
order = 2 * (length(B) - 1);
isTwopass = true;
else
pbDev = dev;
sbAtt = 20 * log10(dev);
order = length(B) - 1;
isTwopass = false;
end
% Reporting
ft_info once
ft_info('Bandpass filtering data: %s, order %d, %s-windowed sinc FIR\n', dir, order, wintype);
if ~isTwopass && ~isOrderLow % Do not report shifted cutoffs
ft_info(' cutoff (-6 dB) %g Hz and %g Hz\n', Fbp(1), Fbp(2));
tb = [max([Fbp(1) - df / 2 0]), Fbp(1) + df / 2, Fbp(2) - df / 2, min([Fbp(2) + df / 2 Fn])]; % Transition band edges
ft_info(' transition width %.1f Hz, stopband 0-%.1f Hz, passband %.1f-%.1f Hz, stopband %.1f-%.0f Hz\n', df, tb, Fn);
end
if ~isOrderLow
ft_info(' maximum passband deviation %.4f (%.2f%%), stopband attenuation %.0f dB\n', pbDev, pbDev * 100, sbAtt);
end
case 'fir'
if isempty(order)
order = 3*fix(Fs / Fbp(1));
end
if order > floor( (size(dat,2) - 1) / 3)
order=floor(size(dat,2)/3) - 1;
end
B = fir1(order, [min(Fbp)/Fn max(Fbp)/Fn]);
A = 1;
case 'firls' % from NUTMEG's implementation
% Deprecated: see bug 2453
ft_warning('The filter type you requested is not recommended for neural signals, only proceed if you know what you are doing.')
if isempty(order)
order = 3*fix(Fs / Fbp(1));
end
if order > floor( (size(dat,2) - 1) / 3)
order=floor(size(dat,2)/3) - 1;
end
f = 0:0.001:1;
if rem(length(f),2)~=0
f(end)=[];
end
z = zeros(1,length(f));
if(isfinite(min(Fbp)))
[val,pos1] = min(abs(Fs*f/2 - min(Fbp)));
else
[val,pos2] = min(abs(Fs*f/2 - max(Fbp)));
pos1=pos2;
end
if(isfinite(max(Fbp)))
[val,pos2] = min(abs(Fs*f/2 - max(Fbp)));
else
pos2 = length(f);
end
z(pos1:pos2) = 1;
A = 1;
B = firls(order,f,z); % requires MATLAB signal processing toolbox
case 'brickwall'
ix = round((0:Fs) ./ (Fs ./ size(dat,2)));
ax = ix ./ (size(dat,2)./Fs); % frequency axis, including the other end of the spectrum
a = ones(1, size(dat,2));
fbin1 = nearest(ax, [min(Fbp) max(Fbp)]);
fbin2 = nearest(ax, [Fs-max(Fbp) Fs-min(Fbp)]); % same band at the other end of the spectrum
a(1:(fbin1(1)-1)) = 0;
a((fbin1(2)+1):(fbin2(1)-1)) = 0;
a((fbin2(2)+1):end) = 0;
f = fft(dat,[],2); % FFT
f = f.*a(ones(size(dat,1),1),:); % brickwall
filt = real(ifft(f,[],2)); % iFFT
otherwise
ft_error('unsupported filter type "%s"', type);
end
% Plot filter responses
if strcmp(plotfiltresp, 'yes')
plotfresp(B, A, [], Fs, dir)
end
if ~isequal(type, 'brickwall')
try
filt = filter_with_correction(B,A,dat,dir,usefftfilt);
catch
switch instabilityfix
case 'no'
rethrow(lasterror);
case 'reduce'
ft_warning('off','backtrace');
ft_warning('instability detected - reducing the %dth order filter to an %dth order filter', order, order-1);
ft_warning('on','backtrace');
filt = ft_preproc_bandpassfilter(dat,Fs,Fbp,order-1,type,dir,instabilityfix,df,wintype,dev,plotfiltresp,usefftfilt);
case 'split'
N1 = ceil(order/2);
N2 = floor(order/2);
ft_warning('off','backtrace');
ft_warning('instability detected - splitting the %dth order filter in a sequential %dth and a %dth order filter', order, N1, N2);
ft_warning('on','backtrace');
filt = ft_preproc_bandpassfilter(dat ,Fs,Fbp,N1,type,dir,instabilityfix,df,wintype,dev,plotfiltresp,usefftfilt);
filt = ft_preproc_bandpassfilter(filt,Fs,Fbp,N2,type,dir,instabilityfix,df,wintype,dev,plotfiltresp,usefftfilt);
otherwise
ft_error('incorrect specification of instabilityfix');
end % switch
end
end