Programming Reference:IIRFilterBase Class
From BCI2000 Wiki
Contents |
Location
BCI2000/src/shared/modules/signalprocessing
Synopsis
The IIRFilterBase class is a base class for BCI2000 filters that provide digital filters. Classes derived from IIRFilterBase are supposed to implement IIRFilterBase::DesignFilter(), specifying filter behavior by means of a transfer function. Differently from typical IIR filter implementations, which expect the transfer function in terms of filter coefficients, the BCI2000 IIRFilterBase class expects the transfer function in terms of its numerator's roots ("zeros") and denominator's roots ("poles"); this approach improves numerical stability at higher filter orders.
FilterDesign Library
While any method may be used to determine the transfer function, the FilterDesign library provides a convenient way to compute a transfer function from desired filter properties. It is located at BCI2000/src/extlib/math/FilterDesign, and was written for BCI2000 based on public-domain code by A.J. Fisher; it provides a number of classic filter design methods in form of C++ classes. To use such a class, instantiate it, use its member functions to configure parameters, and obtain its TransferFunction property (see the example section below).
FilterDesign::Butterworth
Order(order)
Set the filter order to the specified value.
Lowpass(corner frequency)
Design a low pass with the specified corner frequency, i.e. suppress signals above the given frequency. Frequencies are specified in terms of the sampling rate, and expected to be below 0.5 (the Nyquist frequency).
Highpass(corner frequency)
Design a high pass with the specified corner frequency, i.e. suppress signals below the given frequency.
Bandpass(low corner, high corner)
Design a bandpass with the specified corner frequencies, i.e. suppress signals outside the given frequency range.
Bandstop(low corner, high corner)
Design a bandstop with specified corner frequencies, i.e. suppress signals inside the given frequency range.
TransferFunction
Returns a transfer function as a rational polynomial with complex roots as declared in BCI2000/src/extlib/math/Polynomials.h. From the Ratpoly object returned, numerator and denominator polynomials may be extracted using Ratpoly::Numerator() and Ratpoly::Denominator(), respectively. Both functions return a polynomial, from which roots may be obtained using Polynomial::Roots(), and coefficients may be obtained using Polynomial::Coefficients().
FilterDesign::Chebychev
The Chebychev class provides an interface identical to the Butterworth class, and an additional function to specify ripple amplitude.
Ripple_dB(amplitude)
Set the amplitude of the filter's passband ripples in dB.
FilterDesign::Resonator
Rather than through corner frequencies, the Resonator class is parameterized specifying a resonant frequency in conjunction with a quality factor (which is inversely proportional to the resonance peak's width).
QFactor(q)
Set the resonator's quality factor to the specified value.
Bandpass(center frequency)
Design a bandpass around the specified center frequency.
Bandstop(center frequency)
Design a bandstop around the specified center frequency.
Allpass(center frequency)
Design a filter with a constant frequency response but altering phase at the center frequency.
Remarks
- You may obtain a combination of filters by multiplying their individual transfer functions prior to extracting gain, zeros, and poles.
- The actual IIR filter implementation uses a sequence of order-one filter stages corresponding to pairs of zeros and poles. Thus, there is no limitation to the filter order, as it would be the case for implementations based on coefficients, due to numerical instability.
- Roots common to both numerator and denominator will be automatically removed from the overall transfer function, so the combined filter may be of an order which is lower than the sum of individual filter orders.
Example
To implement a 2nd order Butterworth low pass filter, you might derive a class ButterworthLP from IIRFilterBase, with its declaration being
class ButterworthLP : public IIRFilterBase
{
public:
ButterworthLP();
virtual ~ButterworthLP() {}
private:
virtual void DesignFilter( const SignalProperties&,
IIRFilterBase::Real& gain,
IIRFilterBase::ComplexVector& zeros,
IIRFilterBase::ComplexVector& poles ) const;
};
The filter's corner frequency is specified in a parameter "ButterworthLPCorner":
ButterworthLP::ButterworthLP()
{
BEGIN_PARAMETER_DEFINITIONS
"Filtering float ButterworthLPCorner= 30Hz % % % // Low pass corner frequency",
END_PARAMETER_DEFINITIONS
}
In the DesignFilter method, we use the FilterDesign library to obtain poles and zeros from parameters:
void
ButterworthLP::DesignFilter( const SignalProperties& inSignalProperties,
IIRFilterBase::Real& outGain,
IIRFilterBase::ComplexVector& outZeros,
IIRFilterBase::ComplexVector& outPoles ) const
{
float corner = MeasurementUnits::ReadAsFreq( Parameter( "ButterworthLPCorner" ) )
/ inSignalProperties.Elements() * Parameter( "SampleBlockSize" );
if( corner < 0.0 || corner > 0.5 )
bcierr << "ButterworthLPCorner exceeds range" << endl;
Ratpoly<FilterDesign::Complex> tf = FilterDesign::Butterworth()
.Order( 2 )
.Lowpass( corner )
.TransferFunction();
outGain = 1.0 / abs( tf.Evaluate( 1.0 ) ); // make sure that LF gain is unity
outZeros = tf.Numerator().Roots();
outPoles = tf.Denominator().Roots();
}
For an example that combines a notch filter with a high pass filter, please refer to the SourceFilter's source code.
