头文件:
/* * Copyright (c) 2008-2011 Zhang Ming (M. Zhang), [email protected] * * This program 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 2 or any later version. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * This program 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. A copy of the GNU General Public License is available at: * http://www.fsf.org/licensing/licenses */ /***************************************************************************** * dfd.h * * Digital Filter Design. * * This is a base class for FIR and IIR filter design. It contains the design * specifies and some universal parameters of filter. * * Zhang Ming, 2010-03, Xi'an Jiaotong University. *****************************************************************************/ #ifndef DFD_H #define DFD_H #include <string> #include <vector.h> namespace splab { const double FREQ_MIN = 1.0E-3; // min freqency const double FREQ_MAX = 1.0E12; // max freqency const double GAIN_PASS = -1.0E-2; // max passband gain const double GAIN_TRAN = -3.0103; // min pb, max sb gain const double GAIN_STOP = -1.0E02; // min stopband gain class DFD { public: DFD( const string &select ); virtual ~DFD(); void setParams( double fs, double f1, double a1, double f2, double a2 ); void setParams( double fs, double f1, double a1, double f2, double f3, double a2, double f4, double a3 ); virtual void design() = 0; virtual void dispInfo() const = 0; protected: double fsamp; // sampling frequency double wpass1, wpass2; // passband edge frequency double wstop1, wstop2; // stopband edge frequency double apass1, apass2; // passband gain double astop1, astop2; // stopband gain string filtType; // filter type int order; // length of filter inline double getValue( double x, double min, double max ); }; // class DFD #include <dfd-impl.h> } // namespace splab #endif // DFD_H
/* * Copyright (c) 2008-2011 Zhang Ming (M. Zhang), [email protected] * * This program 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 2 or any later version. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * This program 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. A copy of the GNU General Public License is available at: * http://www.fsf.org/licensing/licenses */ /***************************************************************************** * fir.h * * Finite Impulse Response Digital Filter. * * This class is designed for designing the FIR filter using window function * method. The unit of frequency and gain parameters are "Hz" and "dB", * respectively. * * The valid filter types are: * lowpass highpass bandpass bandstop * and the valid windows are: * Rectangle Bartlett Blackman Hanning Hamming * Gauss Kaiser * * The length of filter( filter's order plus one ) are multiples of 4, which * is the least number(L=4n) satisfying the design specifies. * * Zhang Ming, 2010-03, Xi'an Jiaotong University. *****************************************************************************/ #ifndef FIR_H #define FIR_H #include <iomanip> #include <window.h> #include <dfd.h> namespace splab { using std::setw; using std::ios; using std::setiosflags; using std::setprecision; class FIR : public DFD { public: FIR( const string &select, const string &win ); FIR( const string &select, const string &win, double a ); ~FIR(); void design(); void dispInfo() const; Vector<double> getCoefs() const; private: void orderEst(); void idealCoef(); void calcCoef(); double frqeResp( double freq ); void calcGain(); bool isSatisfy(); string windType; // window type Vector<double> wind, // window function coefs, // coefficients edgeGain; // gain at edge frquency double alpha; // window parameter }; // class FIR #include <fir-impl.h> } // namespace splab #endif // FIR_H
实现文件:
/* * Copyright (c) 2008-2011 Zhang Ming (M. Zhang), [email protected] * * This program 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 2 or any later version. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * This program 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. A copy of the GNU General Public License is available at: * http://www.fsf.org/licensing/licenses */ /***************************************************************************** * dfd-impl.h * * Implementation for DFD class. * * Zhang Ming, 2010-03, Xi'an Jiaotong University. *****************************************************************************/ /** * constructors and destructor */ DFD::DFD( const string &select ) : filtType(select) { } DFD::~DFD() { } /** * Get a number which falls between min and max. */ double DFD::getValue( double x, double min, double max ) { if( (x > min) && (x < max) ) return x; else { cerr << "The parameter is out of range!" << endl; return 0; } } /** * Set filter's parameters */ void DFD::setParams( double fs, double f1, double a1, double f2, double a2 ) { fsamp = getValue( fs, FREQ_MIN, FREQ_MAX ); double maxFreq = fsamp / 2; if( filtType == "lowpass" ) { wpass1 = getValue( f1, FREQ_MIN, maxFreq ); apass1 = getValue( a1, GAIN_TRAN, GAIN_PASS ); wstop1 = getValue( f2, wpass1, maxFreq ); astop1 = getValue( a2, GAIN_STOP, GAIN_TRAN ); } else if( filtType == "highpass" ) { wstop1 = getValue( f1, FREQ_MIN, maxFreq ); astop1 = getValue( a1, GAIN_STOP, GAIN_TRAN ); wpass1 = getValue( f2, wstop1, maxFreq ); apass1 = getValue( a2, GAIN_TRAN, GAIN_PASS ); } else cerr << "Parameters setting has failed!" << endl; // Convert all edge frequencies to rad/sec wpass1 *= TWOPI; wstop1 *= TWOPI; } void DFD::setParams( double fs, double f1, double a1, double f2, double f3, double a2, double f4, double a3 ) { fsamp = getValue( fs, FREQ_MIN, FREQ_MAX ); double maxFreq = fsamp / 2; if( filtType == "bandpass" ) { wstop1 = getValue( f1, FREQ_MIN, maxFreq ); astop1 = getValue( a1, GAIN_STOP, GAIN_TRAN ); wpass1 = getValue( f2, wstop1, maxFreq ); wpass2 = getValue( f3, wpass1, maxFreq ); apass1 = getValue( a2, GAIN_TRAN, GAIN_PASS ); wstop2 = getValue( f4, wpass2, maxFreq ); astop2 = getValue( a3, GAIN_STOP, GAIN_TRAN ); apass2 = apass1; if( astop1 < astop2 ) astop2 = astop1; else astop1 = astop2; } else if( filtType == "bandstop" ) { wpass1 = getValue( f1, FREQ_MIN, maxFreq ); apass1 = getValue( a1, GAIN_TRAN, GAIN_PASS ); wstop1 = getValue( f2, wpass1, maxFreq ); wstop2 = getValue( f3, wstop1, maxFreq ); astop1 = getValue( a2, GAIN_STOP, GAIN_TRAN ); wpass2 = getValue( f4, wstop2, maxFreq ); apass2 = getValue( a3, GAIN_TRAN, GAIN_PASS ); astop2 = astop1; if( apass1 < apass2 ) apass1 = apass2; else apass2 = apass1; } else cerr << "Parameters setting has failed!" << endl; // Convert all edge frequencies to rad/sec wpass1 *= TWOPI; wpass2 *= TWOPI; wstop1 *= TWOPI; wstop2 *= TWOPI; }
/* * Copyright (c) 2008-2011 Zhang Ming (M. Zhang), [email protected] * * This program 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 2 or any later version. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * This program 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. A copy of the GNU General Public License is available at: * http://www.fsf.org/licensing/licenses */ /***************************************************************************** * fir-impl.h * * Implementation for FIR class. * * Zhang Ming, 2010-03, Xi'an Jiaotong University. *****************************************************************************/ /** * constructors and destructor */ FIR::FIR( const string &select, const string &win ) : DFD( select ), windType(win) { bool cond = ( filtType=="lowpass" || filtType=="highpass" || filtType=="bandpass" || filtType=="bandstop" ); assert(cond); cond = ( windType=="Rectangle" || windType=="Bartlett" || windType=="Blackman" || windType=="Hanning" || windType=="Hamming" || windType=="Gauss" ); assert(cond); } FIR::FIR( const string &select, const string &win, double a ) : DFD( select ), windType(win), alpha(a) { bool cond = ( filtType=="lowpass" || filtType=="highpass" || filtType=="bandpass" || filtType=="bandstop" ); assert(cond); cond = ( windType=="Kaiser" || windType=="Gauss" ); assert(cond); } FIR::~FIR() { } /** * Design digital FIR filter. */ void FIR::design() { // Estimate the length of filter. orderEst(); calcCoef(); calcGain(); while( !isSatisfy() ) { order += 4; calcCoef(); calcGain(); } } /** * Displays the design result of filter. */ void FIR::dispInfo() const { int i, j, k; cout << endl; cout << "\t\t Filter selectivity : " << filtType << endl; cout << "\t\t Window type : " << windType << endl; cout << "\t\t Sampling Frequency (Hz) : " << fsamp << endl; // gains and edge frequency if( filtType=="lowpass" ) { cout << "\t\t Passband frequency (Hz) : " << wpass1/TWOPI << endl; cout << "\t\t Passband gain (dB) : " << apass1 << endl; cout << "\t\t Stopband frequency (Hz) : " << wstop1/TWOPI << endl; cout << "\t\t Stopband gain (dB) : " << astop1 << endl; } else if( filtType=="highpass" ) { cout << "\t\t Stopband frequency (Hz) : " << wstop1/TWOPI << endl; cout << "\t\t Stopband gain (dB) : " << astop1 << endl; cout << "\t\t Passband frequency (Hz) : " << wpass1/TWOPI << endl; cout << "\t\t Passband gain (dB) : " << apass1 << endl; } else if( filtType=="bandpass" ) { cout << "\t\t Lower stopband frequency (Hz) : " << wstop1/TWOPI << endl; cout << "\t\t Lower stopband gain (dB) : " << astop1 << endl; cout << "\t\t Lower passband frequency (Hz) : " << wpass1/TWOPI << endl; cout << "\t\t Upper passband frequency (Hz) : " << wpass2/TWOPI << endl; cout << "\t\t Passband gain (dB) : " << apass1 << endl; cout << "\t\t Upper stopband frequency (Hz) : " << wstop2/TWOPI << endl; cout << "\t\t Upper stopband gain (dB) : " << astop2 << endl; } else { cout << "\t\t Lower passband frequency (Hz) : " << wpass1/TWOPI << endl; cout << "\t\t Lower passband gain (dB) : " << apass1 << endl; cout << "\t\t Lower stopband frequency (Hz) : " << wstop1/TWOPI << endl; cout << "\t\t Upper stopband frequency (Hz) : " << wstop2/TWOPI << endl; cout << "\t\t Stopband gain (dB) : " << astop1 << endl; cout << "\t\t Upper passband frequency (Hz) : " << wpass2/TWOPI << endl; cout << "\t\t Upper passband gain (dB) : " << apass2 << endl; } // display coefficients cout << endl << endl; cout << "\t\t\t\t Filter Coefficients" << endl << endl; cout << " N [ N + 0 N + 1" ; cout << " N + 2 N + 3 ]" << endl; cout << " === ============================" ; cout << "========================================" ; for( i=0; i<order/4; ++i ) { j = i * 4; cout << endl << setw(4) << j << " "; cout << setiosflags(ios::scientific) << setprecision(8); for( k=0; k<4; ++k ) cout << setw(16) << coefs[j+k] << " " ; } cout << endl << endl << endl << "\t ==================== Edge Frequency Response"; cout << " ====================" << endl; cout << setiosflags(ios::fixed); if( edgeGain.size() == 2 ) { cout << "\t Mag(fp) = " << edgeGain[0] << "(dB)"; cout << " Mag(fs) = " << edgeGain[1] << "(dB)" << endl; } else { cout << "\t Mag(fp1) = " << edgeGain[0] << "(dB)"; cout << " Mag(fp2) = " << edgeGain[1] << "(dB)" << endl; cout << "\t Mag(fs1) = " << edgeGain[2] << "(dB)"; cout << " Mag(fs2) = " << edgeGain[3] << "(dB)" << endl; } } /** * Get the FIR filter's coefficients. */ inline Vector<double> FIR::getCoefs() const { return coefs; } /** * Estimates the length of FIR filter. */ void FIR::orderEst() { double deltaFreq, // delta frequency lowerDF, // delta freq of lower band upperDF, // delta freq of upper band errSB, // stopband error errPB, // passband error errMin, // minimum of sb/pb errors errDB; // minimum error in dB // Determine frequency delta if( filtType=="lowpass" || filtType=="highpass" ) deltaFreq = abs(wstop1-wpass1) / fsamp; else { lowerDF = abs(wstop1-wpass1) / fsamp; upperDF = abs(wstop2-wpass2) / fsamp; if( lowerDF > upperDF ) deltaFreq = upperDF; else deltaFreq = lowerDF; } // Determine stopband and passband errors errPB = 1 - pow( 10, 0.05*apass1 ); errSB = pow( 10, 0.05*astop1 ); if( errSB < errPB ) errMin = errSB; else errMin = errPB; errDB = -20 * log10(errMin); // Store filter length in pFilt and return beta. if( errDB > 21 ) order = int( ceil( 1 + (errDB-7.95) / (2.285*deltaFreq) ) ); else order = int( ceil( 1 + (5.794/deltaFreq) ) ); order += 4 - order%4; } /** * Calculates ideal FIR coefficients. */ void FIR::idealCoef() { int i; double t, tau, Wc1, Wc2; // Calculate tau as non-integer if order is even. tau = ( order - 1 ) / 2.0; // Adjust cutoff frequency to midway point between stop // and pass edge frequency and convert to digital frequency. Wc1 = (wstop1 + wpass1) / (2*fsamp); Wc2 = (wstop2 + wpass2) / (2*fsamp); // Calc coefs based on selectivity of filter. if( filtType == "lowpass" ) for( i=0; i<order; ++i ) { if( i == tau ) coefs[i] = Wc1/PI; else { t = i - tau; coefs[i] = sin(Wc1*t) / (PI*t); } } else if( filtType == "highpass" ) for( i=0; i<order; ++i ) { if( i == tau ) coefs[i] = (PI-Wc1) / PI; else { t = i - tau; coefs[i] = ( sin(PI*t) - sin(Wc1*t) ) / (PI*t); } } else if( filtType == "bandpass" ) for( i=0; i<order; ++i ) { if( i == tau ) coefs[i] = ( Wc2-Wc1 ) / PI; else { t = i - tau; coefs[i] = ( sin(Wc2*t) - sin(Wc1*t) ) / (PI*t); } } else for( i=0; i<order; ++i ) { if( i == tau ) coefs[i] = ( PI+Wc1-Wc2 ) / PI; else { t = i - tau; coefs[i] = ( sin(PI*t)-sin(Wc2*t)+sin(Wc1*t) ) / (PI*t); } } } /** * Calculates the digital FIR coefficients. */ void FIR::calcCoef() { coefs.resize(order); wind.resize(order); // Calculate the ideal FIR coefficients. idealCoef(); // Get window function. if( windType=="Kaiser" || windType=="Gauss" ) wind = window( windType, order, alpha, 1.0 ); else wind = window( windType, order, 1.0 ); // Multiply window and ideal coefficients. coefs *= wind; } /** * Calculate the response at edge freqency. */ double FIR::frqeResp( double freq ) { double mag = 1.0, rea = 0.0, img = 0.0, omega = TWOPI*freq / fsamp; // Loop through all the coefficients. for( int i=0; i<order; ++i ) { double domega = i * omega; rea += coefs[i] * cos(domega); img += coefs[i] * sin(domega); } // Calculate final result and convert to degrees. mag = sqrt( rea*rea + img*img ); if( mag < EPS ) mag = EPS; mag = 20 * log10( mag ); return mag; } /** * Calculate the response at edge freqency. */ void FIR::calcGain() { // Determine the edge freqency. if( filtType=="lowpass" || filtType=="highpass" ) { double f1 = wpass1 / TWOPI, f2 = wstop1 / TWOPI; edgeGain.resize(2); edgeGain(1) = frqeResp( f1 ); edgeGain(2) = frqeResp( f2 ); } else { double f1 = wpass1 / TWOPI, f2 = wpass2 / TWOPI, f3 = wstop1 / TWOPI, f4 = wstop2 / TWOPI; edgeGain.resize(4); edgeGain(1) = frqeResp( f1 ); edgeGain(2) = frqeResp( f2 ); edgeGain(3) = frqeResp( f3 ); edgeGain(4) = frqeResp( f4 ); } } /** * Design digital FIR filter. */ bool FIR::isSatisfy() { if( edgeGain.size() == 2 ) { if( edgeGain(1)<apass1 || edgeGain(2)>astop1 ) return false; } else { if( edgeGain(1)<apass1 || edgeGain(2)<apass2 || edgeGain(3)>astop1 || edgeGain(4)>astop2 ) return false; } return true; }
测试代码:
/***************************************************************************** * fir_test.cpp * * FIR class testing. * * Zhang Ming, 2010-03, Xi'an Jiaotong University. *****************************************************************************/ #define BOUNDS_CHECK #include <iostream> #include <fir.h> using namespace std; using namespace splab; int main() { string wType = "Hamming"; // string fType = "lowpass"; // double fs = 1000, // fpass = 200, // apass = -3, // fstop = 300, // astop = -20; // FIR fir( fType, wType ); // fir.setParams( fs, fpass, apass, fstop, astop ); // string fType = "highpass"; // double fs = 1000, // fstop = 200, // astop = -20, // fpass = 300, // apass = -3; // FIR fir( fType, wType ); // fir.setParams( fs, fstop, astop, fpass, apass ); // string fType = "bandpass"; // double fs = 1000, // fstop1 = 100, // astop1 = -20, // fpass1 = 200, // fpass2 = 300, // apass1 = -3, // fstop2 = 400, // astop2 = -20; // FIR fir( fType, wType ); // fir.setParams( fs, fstop1, astop1, fpass1, fpass2, apass1, fstop2, astop2 ); string fType = "bandstop"; double fs = 1000, fpass1 = 100, apass1 = -3, fstop1 = 200, fstop2 = 300, astop1 = -20, fpass2 = 400, apass2 = -3; FIR fir( fType, wType ); fir.setParams( fs, fpass1, apass1, fstop1, fstop2, astop1, fpass2, apass2 ); fir.design(); fir.dispInfo(); cout << endl; return 0; }运行结果:
Filter selectivity : bandstop Window type : Hamming Sampling Frequency (Hz) : 1000 Lower passband frequency (Hz) : 100 Lower passband gain (dB) : -3 Lower stopband frequency (Hz) : 200 Upper stopband frequency (Hz) : 300 Stopband gain (dB) : -20 Upper passband frequency (Hz) : 400 Upper passband gain (dB) : -3 Filter Coefficients N [ N + 0 N + 1 N + 2 N + 3 ] === ==================================================================== 0 -3.85192764e-003 8.42472981e-003 3.11153775e-003 -2.03549729e-003 4 -3.55185234e-002 2.30171390e-002 -1.41331145e-001 2.61755439e-001 8 2.88881794e-002 3.56158158e-001 3.56158158e-001 2.88881794e-002 12 2.61755439e-001 -1.41331145e-001 2.30171390e-002 -3.55185234e-002 16 -2.03549729e-003 3.11153775e-003 8.42472981e-003 -3.85192764e-003 ==================== Edge Frequency Response ==================== Mag(fp1) = -0.85449456(dB) Mag(fp2) = -0.80635855(dB) Mag(fs1) = -21.347821(dB) Mag(fs2) = -21.392825(dB) Process returned 0 (0x0) execution time : 0.078 s Press any key to continue.