IIR数字滤波器设计方法的C++实现

头文件:

/*
 * 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
 */


/*****************************************************************************
 *                                  advmath.h
 *
 * Some advance math functions used in IIR filter design.
 *
 * Adapted form "Analog and Digital Filter Design", Les Thede, 2004.
 *
 * Zhang Ming, 2010-03, Xi'an Jiaotong University.
 *****************************************************************************/


#ifndef ADVMATH_H
#define ADVMATH_H


#include <cmath>
#include <complex>


namespace splab
{

    inline double acosh( double );
    inline double asinh( double );

    double arcsc( double, double );
    void ellipticFun( double, double, double*, double*, double* );
    double ellipticIntegral( double );

    void quadradicRoot( complex<double>, complex<double>, complex<double>,
                        complex<double>&, complex<double>& );


    #include <advmath-impl.h>

}
// namespace splab


#endif
// ADVMATH_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
 */


/*****************************************************************************
 *                                  iir.h
 *
 * Infinite Impulse Response.
 *
 * This class is designed for designing the IIR filter using bilinear
 * transform 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 approximation method are:
 *      Butterworth     Chebyshev   Inverse Chebyshev   Elliptic
 *
 * The filter's coefficients consist of quadratic terms( the linear term can
 * be viewed as: a + b*z^-1 + 0*z^-2 ). The numerator and denominator
 * coefficients are stored in "aCoefs" and "bCoefs", respectively. The kth
 * quadratic term is as follow:
 *
 *             aCoefs[3k] + aCoefs[3k+1]*z^-1 + aCoefs[3k+2]*z^-2
 *           -------------------------------------------------------
 *             bCoefs[3k] + bCoefs[3k+1]*z^-1 + bCoefs[3k+2]*z^-2
 *
 * Zhang Ming, 2010-03, Xi'an Jiaotong University.
 *****************************************************************************/


#ifndef IIR_H
#define IIR_H


#include <iomanip>
#include <cstdio>
#include <dfd.h>
#include <advmath.h>


namespace splab
{

    using std::ios;
    using std::setw;
    using std::setiosflags;
    using std::setprecision;

    class IIR : public DFD
    {

     public:

        IIR( const string &select, const string &appMethod );
        ~IIR();

        void    design();
        void    dispInfo() const;
        Vector<double> getNumCoefs() const;
        Vector<double> getDenCoefs() const;

    private:

        void    freqWrap();
        bool    orderEst();
        void    normCoefs();
        void    unnormCoefs();
        void    bilinTran();
        void    freqUnwrap();

        void    butterCoefs();
        void    chebyCoefs();
        void    invChebyCoefs();
        void    elliptCoefs();

        void    unnormLP( double freq );
        void    unnormHP( double freq );
        void    unnormBP( double BW, double Wo );
        void    unnormBS( double BW, double Wo );

        double  frqeResp( double freq );
        void    calcGain();

        string  approx;             // approximation method
        double  gain;               // overall gain

        Vector<double>  aCoefs,     // numerator
                        bCoefs,     // denominator
                        edgeGain;   // gain at edge frquency

    };
    // class IIR


    #include <iir-impl.h>

}
// namespace splab


#endif
// IIR_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
 */


/*****************************************************************************
 *                               advmath-impl.h
 *
 * Implementationfor advance math functions.
 *
 * Zhang Ming, 2010-03, Xi'an Jiaotong University.
 *****************************************************************************/


/**
 * Calcutes the inverse hyperbolic cosine of x.
 */
inline double acosh( double x )
{
    return log( x + sqrt(x*x-1) );
}


/**
 * Calculates the inverse hyperbolic sine of x.
 */
inline double asinh( double x )
{
    return log( x + sqrt(x*x + 1) );
}


/**
 * Calculates Jacobian elliptic arctan function using
 * arithmetic-geometric mean method.
 */
double arcsc( double u, double k )
{
    int     i, L;
    double  A, B, BT, Y;

    A = 1;
    B = k;
    Y = 1.0 / u;
    L = 0;

    for( i=0; i<MAXTERM; ++i )
    {
        BT = A * B;
        A = A + B;
        B = 2 * sqrt(BT);
        Y = Y - BT / Y;

        if( Y == 0 )
            Y = sqrt(BT) * EPS;
        if( abs(A-B) < (A*EPS) )
            break;

        L = 2 * L;
        if( Y < 0 )
            L++;
    }

    if( Y < 0 )
        L++;

    return ( (atan(A/Y) + PI * L) / A );
}


/**
 * Calculates Jacobian elliptic functions using
 * arithmetic-geometric mean method.
 */
void ellipticFun( double u, double k,
                  double *sn, double *cn, double *dn )
{
    int     i, imax;
    double  A[MAXTERM], B[MAXTERM],
            C[MAXTERM], P[MAXTERM];

    k = k * k;
    A[0] = 1;
    B[0] = sqrt(1-k);
    C[0] = sqrt(k);

    for( i=1; i<MAXTERM; ++i )
    {
        A[i] = (A[i-1] + B[i-1]) / 2;
        B[i] = sqrt( A[i-1] * B[i-1] );
        C[i] = (A[i-1] - B[i-1]) / 2;
        if( C[i] < EPS )
            break;
    }

    if( i == MAXTERM )
        imax = i - 1;
    else
        imax = i;

    P[imax] = pow(2.0,imax) * A[imax] * u;
    for( i=imax; i>0; i-- )
        P[i-1] = (asin(C[i]*sin(P[i])/A[i]) + P[i]) / 2;

    *sn = sin(P[0]);
    *cn = cos(P[0]);
    *dn = sqrt( 1 - k*(*sn)*(*sn) );
}


/**
 * Calculates complete elliptic integral using
 * arithmetic-geometric mean method.
 */
double ellipticIntegral( double k )
{
    int     i;
    double  A[MAXTERM], B[MAXTERM], C[MAXTERM];

    k = k*k;
    A[0] = 1;
    B[0] = sqrt(1-k);
    C[0] = sqrt(k);

    for( i=1; i<MAXTERM; ++i )
    {
        A[i] = (A[i-1] + B[i-1]) / 2;
        B[i] = sqrt(A[i-1]*B[i-1]);
        C[i] = (A[i-1] - B[i-1]) / 2;
        if( C[i] < EPS )
            break;
    }

    return PI / (2*A[i]);
}


/**
 * Factoring quadratic equation with complex coefficients.
 * Equation form is a*x^2 + b*x + c, solutions will be placed
 * in complex numbers pointers d and e.
 */
void quadradicRoot( complex<double> a, complex<double> b, complex<double> c,
                    complex<double> &x1, complex<double> &x2 )
{
    complex<double> a2,
                    ac4,
                    sq;

    a2  = 2.0 * a;
    sq  = sqrt( b*b - 4.0*a*c );
    x1  = ( -b + sq ) / a2;
    x2  = ( -b - sq ) / a2;
}
/*
 * 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
 */


/*****************************************************************************
 *                               iir-impl.h
 *
 * Implementation for IIR class.
 *
 * Zhang Ming, 2010-03, Xi'an Jiaotong University.
 *****************************************************************************/


/**
 * constructors and destructor
 */
IIR::IIR( const string &select, const string &app )
        : DFD( select ), approx(app)
{
    bool cond = ( filtType=="lowpass" || filtType=="highpass" ||
                  filtType=="bandpass" || filtType=="bandstop" );
    assert(cond);

    cond = ( approx=="Butterworth" || approx=="Chebyshev" ||
             approx=="InvChebyshev" || approx=="Elliptic" );
    assert(cond);
}

IIR::~IIR()
{
}


/**
 * Calculates the digital FIR coefficients.
 */
void IIR::design()
{
    freqWrap();
    if( orderEst() )
    {
        normCoefs();
        unnormCoefs();
        bilinTran();
        freqUnwrap();
        calcGain();
    }
    else
        cerr << "The filter's order exceeds exceed the range!" << endl;
}


/**
 * Displays the design result of filter.
 */
void IIR::dispInfo() const
{
    int i, j;

    cout << endl;
    cout << "\t\t    Filter selectivity      :  " << filtType << endl;
    cout << "\t\t    Approximation method    :  " << approx << endl;
    cout << "\t\t    Filter order            :  " << order << endl;
    cout << "\t\t    Overall gain            :  " << gain << 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 << "           Numerator Coefficients    ";
    cout << "            Denominator Coefficients" << endl << endl ;
    cout << "  N   [  1       z^-1           z^-2  ]";
    cout  << "     [  1       z^-1           z^-2  ]" << endl;
    cout << " ===  =================================";
    cout  << "     =================================" << endl ;
    for( i=0; i<(order+1)/2; ++i )
    {
        j = i * 3;
        cout << setw(3) << i+1 << " ";

        cout << setiosflags(ios::fixed );
        cout << setw(6) << setprecision(1) << aCoefs[j+0];
        cout << setw(14) << setprecision(8) << aCoefs[j+1];
        cout << setw(14) << setprecision(8) << aCoefs[j+2];
        cout << setw(10) << setprecision(1) << bCoefs[j+0];
        cout << setw(14) << setprecision(8) << bCoefs[j+1];
        cout << setw(14) << setprecision(8) << bCoefs[j+2] << endl ;
    }

    // display edge frequency response
    cout << 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> IIR::getNumCoefs() const
{
    return aCoefs;
}

inline Vector<double> IIR::getDenCoefs() const
{
    return bCoefs;
}


/**
 * Pre-warps analog frequency for IIR filtrs.
 */
void IIR::freqWrap( )
{
    // Pre-warps critical frequency based on selectivity.
    if( filtType=="lowpass" || filtType=="highpass" )
    {
        wpass1 = 2*fsamp * tan( wpass1/(2*fsamp) );
        wstop1 = 2*fsamp * tan( wstop1/(2*fsamp) );
    }
    else
    {
        wpass1 = 2*fsamp * tan( wpass1/(2*fsamp) );
        wpass2 = 2*fsamp * tan( wpass2/(2*fsamp) );
        wstop1 = 2*fsamp * tan( wstop1/(2*fsamp) );
        wstop2 = 2*fsamp * tan( wstop2/(2*fsamp) );
    }
}


/**
 * Calculate the order of digital IIR filter.
 */
bool IIR::orderEst()
{
    double  kernel, ratio,
            n,
            rt, kn,
            CEIrt, CEIrtq,
            CEIkn, CEIknq,
            wp1, wp2, ws1, ws2;

    // Simplify the variables.
    wp1 = wpass1;
    wp2 = wpass2;
    ws1 = wstop1;
    ws2 = wstop2;

    // Calculate frequency ratio based on filter selectivity.
    if( filtType == "lowpass" )
        ratio = ws1 / wp1;
    else if( filtType == "highpass" )
        ratio = wp1 / ws1;
    else if( filtType == "bandpass" )
    {
        if( ws1 > (wp1*wp2)/ws2 )
        {
            ws2 = (wp1*wp2) / ws1;
            wstop2 = ws2;
        }
        else
        {
            ws1 = (wp1*wp2) / ws2;
            wstop1 = ws1;
        }
        ratio = (ws2-ws1) / (wp2-wp1);
    }
    else
    {
        if( wp1 > (ws1*ws2)/wp2 )
        {
            wp2 = (ws1*ws2) / wp1;
            wpass2 = wp2;
        }
        else
        {
            wp1 = (ws1*ws2) / wp2;
            wpass1 = wp1;
        }
        ratio = (wp2-wp1) / (ws2-ws1);
    }

    // Calculate kernel which is used in all calculations.
    kernel = ( pow(10.0,-0.1*astop1) - 1 )
           / ( pow(10.0,-0.1*apass1) - 1 );

    // Calculate order and round up to integer value.
    if( approx == "Butterworth" )
        n = log10(kernel) / (2*log10(ratio));
    else if( approx == "Chebyshev" || approx == "InvChebyshev" )
        n = acosh(sqrt(kernel)) / acosh(ratio);
    else
    {
        rt = 1 / ratio;
        if( rt > 0.9999 )
            return false;
        kn = 1 / sqrt(kernel);
        if( kn < 2e-8 )
            return false;

        CEIrt   = ellipticIntegral(rt);
        CEIrtq  = ellipticIntegral(sqrt(1-rt*rt));
        CEIkn   = ellipticIntegral(kn);
        CEIknq  = ellipticIntegral(sqrt(1-kn*kn));
        n = ( CEIrt*CEIknq ) / ( CEIrtq * CEIkn );
    }

    if( n > 200 )
        return false;

    order = int( std::ceil(n) );
    return true;
}


/**
 * Calculate normal filter coefficients.
 */
void IIR::normCoefs()
{
    // There are 3 coefficients for each quadratic.
    // First order factors are considered as quadratics.
    int coefsNum = 3 * ( (order+1) / 2 );
    aCoefs.resize(coefsNum);
    bCoefs.resize(coefsNum);

    // Calculate coefs based on approximation.
    if( approx == "Butterworth" )
        butterCoefs();
    else if( approx == "Chebyshev" )
        chebyCoefs();
    else if( approx == "InvChebyshev" )
        invChebyCoefs();
    else
        elliptCoefs();
}


/**
 * Converts normal lowpass coefficients to unnormalized LP/HP/BP/BS.
 */
void IIR::unnormCoefs()
{
    double  freq, BW, Wo;

    // Calculate frequency, Wo and BW based on approx method.
    if( approx == "InvChebyshev" )
    {
        freq = wstop1;
        Wo = sqrt( wstop1 * wstop2 );
        BW = wstop2 - wstop1;
    }
    else
    {
        freq = wpass1;
        Wo = sqrt( wpass1 * wpass2 );
        BW = wpass2 - wpass1;
    }

    // Call unnormal function based on selectivity.
    if( filtType == "lowpass" )
        unnormLP( freq );
    else if( filtType == "highpass" )
        unnormHP( freq );
    else if( filtType == "bandpass" )
        unnormBP( BW, Wo );
    else
        unnormBS( BW, Wo );
}


/**
 * Use bilinear transform to convert transfer function
 * from s-domain to z-domain.
 */
void IIR::bilinTran()
{
    int     i, j, start, numQuads;
    double  f2, f4,
            N0, N1, N2,
            D0, D1, D2;

    f2 = 2 * fsamp;
    f4 = f2 * f2;
    numQuads = ( order + 1 ) / 2;

    // Handle first order factor if present start
    // indicates if one quad factor handled.
    start = 0;
    if( order % 2 )
    {
        N0 = aCoefs[2] + aCoefs[1] * f2;
        N1 = aCoefs[2] - aCoefs[1] * f2;
        D0 = bCoefs[2] + bCoefs[1] * f2;
        D1 = bCoefs[2] - bCoefs[1] * f2;

        aCoefs[0] = 1.0;
        aCoefs[1] = N1 / N0;
        aCoefs[2] = 0.0;
        bCoefs[0] = 1.0;
        bCoefs[1] = D1 / D0;
        bCoefs[2] = 0.0;
        gain *= (N0 / D0);
        start = 1;
    }

    // Handle quadratic factors. j indexes quad factors
    // since each quad factor has three coefs.
    for( i=start; i<numQuads; ++i )
    {
        j = 3 * i;
        N0 = aCoefs[j]*f4 + aCoefs[j+1]*f2 + aCoefs[j+2];
        N1 = 2 * ( aCoefs[j+2] - aCoefs[j]*f4 );
        N2 = aCoefs[j]*f4 - aCoefs[j+1]*f2 + aCoefs[j+2];
        D0 = bCoefs[j]*f4 + bCoefs[j+1]*f2 + bCoefs[j+2];
        D1 = 2 * ( bCoefs[j+2] - bCoefs[j]*f4 );
        D2 = bCoefs[j]*f4 - bCoefs[j+1]*f2 + bCoefs[j+2];

        aCoefs[j] = 1.0;
        aCoefs[j+1] = N1 / N0;
        aCoefs[j+2] = N2 / N0;
        bCoefs[j] = 1.0;
        bCoefs[j+1] = D1 / D0;
        bCoefs[j+2] = D2 / D0;
        gain *= (N0 / D0);
    }
}


/**
 * Unwarps analog frequency for IIR filtrs.
 */
void IIR::freqUnwrap()
{
    if( filtType=="lowpass" || filtType=="highpass" )
    {
        wpass1 = 2*fsamp * atan( wpass1 / (2*fsamp) );
        wstop1 = 2*fsamp * atan( wstop1 / (2*fsamp) );
    }
    else
    {
        wpass1 = 2*fsamp * atan( wpass1 / (2*fsamp) );
        wpass2 = 2*fsamp * atan( wpass2 / (2*fsamp) );
        wstop1 = 2*fsamp * atan( wstop1 / (2*fsamp) );
        wstop2 = 2*fsamp * atan( wstop2 / (2*fsamp) );
    }
}


/**
 * Calculates normal Butterworth filter coefficients.
 */
void IIR::butterCoefs()
{
    int     m, a, b;
    double  R, epsilon, theta, sigma, omega;

    // Make calculations of necessary constants.
    epsilon = sqrt( pow(10.0,-0.1*apass1) - 1.0 );
    R = pow( epsilon, -1.0/order );

    // Initialize gain to 1.0, start indices at 0.
    gain = 1.0;
    a = 0;
    b = 0;

    // Handle odd order if necessary.
    if( order % 2 )
    {
        aCoefs[a++] = 0.0;
        aCoefs[a++] = 0.0;
        aCoefs[a++] = R;
        bCoefs[b++] = 0.0;
        bCoefs[b++] = 1.0;
        bCoefs[b++] = R;
    }

    // Handle all quadratic terms.
    for( m=0; m<order/2; ++m )
    {
        // Calculate angle first, then real and imag position.
        theta = PI*( 2*m + order+1 ) / ( 2*order );
        sigma = R * cos(theta);
        omega = R * sin(theta);

        // Set the quadratic coefficients.
        aCoefs[a++] = 0.0;
        aCoefs[a++] = 0.0;
        aCoefs[a++] = sigma*sigma + omega*omega;
        bCoefs[b++] = 1.0;
        bCoefs[b++] = -2*sigma;
        bCoefs[b++] = sigma*sigma + omega*omega;
    }
}


/**
 * Calculaets normal Chebyshev coefficients.
 */
void IIR::chebyCoefs()
{
    int     m, a, b;
    double  D, epsilon, phi, sigma, omega;

    // Make calculations of necessary constants.
    epsilon = sqrt( pow(10.0,-0.1*apass1) - 1.0 );
    D = asinh(1/epsilon) / order;

    // Handle odd order if necessary.
    a = 0;
    b = 0;

    if( order % 2 )
    {
        gain = 1.0;
        aCoefs[a++] = 0.0;
        aCoefs[a++] = 0.0;
        aCoefs[a++] = sinh(D);
        bCoefs[b++] = 0.0;
        bCoefs[b++] = 1.0;
        bCoefs[b++] = sinh(D);
    }
    else
        gain = pow( 10.0, 0.05*apass1 );

    // Handle all quadratic terms.
    for( m=0; m<order/2; m++ )
    {
        // Calculate angle first, then real and imag position.
        phi = PI * ( 2*m+1 ) / ( 2*order );
        sigma = -1 * sinh(D) * sin(phi);
        omega = cosh(D) * cos(phi);

        // Set the quadratic coefs.
        aCoefs[a++] = 0.0;
        aCoefs[a++] = 0.0;
        aCoefs[a++] = sigma*sigma + omega*omega;
        bCoefs[b++] = 1.0;
        bCoefs[b++] = -2 * sigma;
        bCoefs[b++] = sigma*sigma + omega*omega;
    }
}


/**
 * Calculates normal inverse Chebyshev coefficients.
 */
void IIR::invChebyCoefs()
{
    int     m, a, b;
    double  D, epsilon, mag2, phi, zero, sigma, omega;

    // Make calculations of necessary constants.
    epsilon = 1 / sqrt( pow(10.0,-0.1*astop1) - 1.0 );
    D = asinh( 1/epsilon ) / order;

    // Initialize gain to 1.0, start indices at 0.
    gain = 1.0;
    a = 0;
    b = 0;

    // Handle odd order if necessary.
    if( order % 2 )
    {
        aCoefs[a++] = 0.0;
        aCoefs[a++] = 0.0;
        aCoefs[a++] = 1 / sinh(D);
        bCoefs[b++] = 0.0;
        bCoefs[b++] = 1.0;
        bCoefs[b++] = 1 / sinh(D);
    }

    // Handle all quadratic terms.
    for( m=0; m<order/2; m++ )
    {
        // Calculate angle first, then real and imag position.
        phi = PI * ( 2*m+1) / ( 2*order );
        sigma = -1 * sinh(D) * sin(phi);
        omega = cosh(D) * cos(phi);

        // Calculate inverse of pole locations and zero location.
        mag2 = omega*omega + sigma*sigma;
        omega = -1 * omega / mag2;
        sigma = sigma / mag2;
        zero = 1. / cos(phi);

        // Set the quadratic coefficients.
        mag2 = omega*omega + sigma*sigma;
        aCoefs[a++] = 1.0;
        aCoefs[a++] = 0.0;
        aCoefs[a++] = zero * zero;
        bCoefs[b++] = 1.0;
        bCoefs[b++] = -2 * sigma;
        bCoefs[b++] = mag2;
        gain *= mag2 / (zero*zero);
    }
}


/**
 * Calculates normal elliptic coefficients.
 */
void IIR::elliptCoefs()
{
    int     m, a, b, odd;
    double  kernel, ratio, epsilon, kn, rt, sigma, omega, zero,
            CEIrt, CEIkn, vo, fm,
            SP, CP, DP, SN, CN, DN;

    // Make calculations of necessary constants.
    epsilon = sqrt( pow(10.0,-0.1*apass1) - 1.0 );

    // Calculate frequency ratio based on filter selectivity.
    if( filtType == "lowpass" )
        ratio = wstop1 / wpass1;
    else if( filtType == "highpass" )
        ratio = wpass1 / wstop1;
    else if( filtType == "bandpass" )
        ratio = (wstop2-wstop1) / (wpass2-wpass1);
    else
        ratio = (wpass2-wpass1) / (wstop2-wstop1);

    // Calculate values used in future calculations.
    kernel = ( pow(10,-0.1*astop1) - 1 ) / ( pow(10,-0.1*apass1) - 1 );
    rt = 1 / ratio;
    kn = 1 / sqrt(kernel);

    // Calculate ellip integrals, vo and ellipFun.
    CEIrt = ellipticIntegral(rt);
    CEIkn = ellipticIntegral(kn);
    vo = ( CEIrt / (CEIkn*order) ) * arcsc( 1/epsilon, kn );
    ellipticFun( vo, sqrt(1-rt*rt), &SP, &CP, &DP );

    // Handle odd order if necessary.
    a = 0;
    b = 0;
    odd = order % 2;
    if( odd )
    {
        gain = 1.0;
        aCoefs[a++] = 0.0;
        aCoefs[a++] = 0.0;
        aCoefs[a++] = SP * CP / (1-SP*SP);
        bCoefs[b++] = 0.0;
        bCoefs[b++] = 1.0;
        bCoefs[b++] = SP * CP / (1-SP*SP);
    }
    else
        gain = pow( 10.0, 0.05*apass1 );

    // Handle all quadratic terms.
    for( m=0; m<order/2; ++m )
    {
        // Make intermediate calculations.
        fm = CEIrt * ( 2*m+1+odd ) / order;
        ellipticFun( fm, rt, &SN, &CN, &DN );

        // Calculate real and imag coordinates of poles.
        sigma = -1*CN*DN*SP*CP / ( 1 - DN*DN*SP*SP );
        omega = SN*DP / ( 1 - DN*DN*SP*SP );
        zero = 1 / (rt*SN);

        // Set the quadratic coefficients.
        aCoefs[a++] = 1.0;
        aCoefs[a++] = 0.0;
        aCoefs[a++] = zero*zero;
        bCoefs[b++] = 1.0;
        bCoefs[b++] = -2*sigma;
        bCoefs[b++] = sigma*sigma + omega*omega;
        gain *= (sigma*sigma + omega*omega) / (zero*zero);
    }
}


/**
 * Converts normal lowpass coefs to unnormal lowpass
 * coefficients at a specific frequency.
 */
void IIR::unnormLP( double freq )
{
    int qd, cf, qdStart;

    // Handle first order, if odd; set qdStart.
    if( order % 2 )
    {
        aCoefs[2] *= freq;
        bCoefs[2] *= freq;
        qdStart = 1;
    }
    else
        qdStart = 0;

    // Handle quadratic factors, qd indexes through
    // quadratic factors, cf converts to coef number.
    for( qd=qdStart; qd<(order+1)/2; ++qd )
    {
        cf = qd * 3;
        aCoefs[cf+1] *= freq;
        aCoefs[cf+2] *= (freq*freq);
        bCoefs[cf+1] *= freq;
        bCoefs[cf+2] *= (freq*freq);
    }
}


/**
 * Converts normal lowpass coefs to unnormal highpass
 * coefficients at a specific frequency.
 */
void IIR::unnormHP( double freq )
{
    int qd, cf, qdStart;

    // Handle first order, if odd; set qdStart
    if( order % 2 )
    {
        gain *= ( aCoefs[2] / bCoefs[2] );
        aCoefs[2] = freq*aCoefs[1] / aCoefs[2];
        aCoefs[1] = 1.0;
        bCoefs[2] = freq / bCoefs[2];
        qdStart = 1;
    }
    else
        qdStart = 0;

    // Handle quadratic factors, qd indexes through
    // quadratic factors, cf converts to coef number.
    for( qd=qdStart; qd<(order+1)/2; ++qd )
    {
        cf = qd * 3;
        gain *= ( aCoefs[cf+2] / bCoefs[cf+2] );
        aCoefs[cf+1] *= ( freq / aCoefs[cf+2] );
        aCoefs[cf+2] = freq * freq *aCoefs[cf]
                            / aCoefs[cf+2];
        aCoefs[cf] = 1.0;
        bCoefs[cf+1] *= ( freq / bCoefs[cf+2] );
        bCoefs[cf+2] = freq * freq * bCoefs[cf]
                            / bCoefs[cf+2];
        bCoefs[cf] = 1.0;
    }
}


/**
 * Converts normal lowpass coefficients to unnormal
 * bandpass coefficients at a specific frequency.
 */
void IIR::unnormBP( double BW, double Wo )
{
    int     qd, ocf, ncf, qdStart,
            numCoefs, orgQuads, orgOrder;
    complex<double> A, B, C, D, E;
    Vector<double>  orgNum,
                    orgDen,
                    newNum,
                    newDen;

    // Store orig number of quadratics and order,
    // new order will be twice original.
    orgOrder = order;
    orgQuads = (orgOrder + 1)/2;
    order = orgOrder * 2;
    orgNum = aCoefs;
    orgDen = bCoefs;

    numCoefs = 3 * orgOrder;
    newNum.resize(numCoefs);
    newDen.resize(numCoefs);

    // If orgOrder odd, convert first order factor to
    // quadratic, qdStart indic start pt for loop.
    if( orgOrder % 2 )
    {
        newNum[0] = orgNum[1];
        newNum[1] = BW * orgNum[2];
        newNum[2] = orgNum[1] * Wo * Wo;
        newDen[0] = orgDen[1];
        newDen[1] = BW * orgDen[2];
        newDen[2] = orgDen[1] * Wo * Wo;
        qdStart = 1;
    }
    else
        qdStart = 0;

    // Each orig quad term will be converted to two new
    // quadratics via complex quadratic factoring.
    for( qd=qdStart; qd<orgQuads; ++qd )
    {
        ocf = qd * 3;
        ncf = qd * 6 - qdStart * 3;

        // For numers which DON'T have s^2 or s terms.
        if( orgNum[ocf] == 0.0 )
        {
            newNum[ncf] = 0.0;
            newNum[ncf+1] = sqrt(orgNum[ocf+2]) * BW;
            newNum[ncf+2] = 0.0;
            newNum[ncf+3] = 0.0;
            newNum[ncf+4] = sqrt(orgNum[ocf+2]) * BW;
            newNum[ncf+5] = 0.0;
        }

        // For numers which DO have s^2 and s terms.
        else
        {
            A = complex<double>( orgNum[ocf], 0.0 );
            B = complex<double>( orgNum[ocf+1], 0.0 );
            C = complex<double>( orgNum[ocf+2], 0.0 );
            quadradicRoot( A, B, C, D, E );

            A = complex<double>( 1.0, 0.0 );
            B = - D * complex<double>( BW, 0.0 );
            C = complex<double>( Wo*Wo, 0.0 );
            quadradicRoot( A, B, C, D, E );

            newNum[ncf] = 1.0;
            newNum[ncf+1] = -2.0 * real(D);
            newNum[ncf+2] = norm(D);
            newNum[ncf+3] = 1.0;
            newNum[ncf+4] = -2.0 * real(E);
            newNum[ncf+5] = norm(E);
        }

        A = complex<double>( orgDen[ocf], 0.0 );
        B = complex<double>( orgDen[ocf+1], 0.0 );
        C = complex<double>( orgDen[ocf+2], 0.0 );
        quadradicRoot( A, B, C, D, E );

        // Make required substitutions, factor again.
        A = complex<double>( 1.0, 0.0 );
        B = - D * complex<double>( BW, 0.0 );
        C = complex<double>( Wo*Wo, 0.0 );
        quadradicRoot( A, B, C, D, E );

        // Make required substitutions, factor again.
        newDen[ncf] = 1.0;
        newDen[ncf+1] = -2.0 * real(D);
        newDen[ncf+2] = norm(D);
        newDen[ncf+3] = 1.0;
        newDen[ncf+4] = -2.0 * real(E);
        newDen[ncf+5] = norm(E);
    }

    aCoefs = newNum;
    bCoefs = newDen;
}


/**
 * Converts normal lowpass coefficients to unnormal
 * bandstop coefficients at a specific frequency.
 */
void IIR::unnormBS( double BW, double Wo )
{
    int     qd, ocf, ncf, qdStart,
            numCoefs, orgQuads, orgOrder;
    complex<double> A, B, C, D, E;
    Vector<double>  orgNum,
                    orgDen,
                    newNum,
                    newDen;

    // Store orig number of quadratics and order,
    // new order will be twice original.
    orgOrder = order;
    orgQuads = (orgOrder + 1)/2;
    order = orgOrder * 2;
    orgNum = aCoefs;
    orgDen = bCoefs;

    numCoefs = 3 * orgOrder;
    newNum.resize(numCoefs);
    newDen.resize(numCoefs);

    // If orgOrder odd, convert first order factor to
    // quadratic, qdStart indic start pt for loop.
    if( orgOrder % 2 )
    {
        gain *= (orgNum[2] / orgDen[2]);
        newNum[0] = 1.0;
        newNum[1] = BW * orgNum[1] / orgNum[2];
        newNum[2] = Wo * Wo;
        newDen[0] = 1.0;
        newDen[1] = BW * orgDen[1] / orgDen[2];
        newDen[2] = Wo * Wo;
        qdStart = 1;
    }
    else
        qdStart = 0;

    // Each orig quad term will be converted to two new
    // quadratics via complex quadratic factoring.
    for( qd=qdStart; qd<orgQuads; qd++ )
    {
        ocf = qd * 3;
        ncf = qd * 6 - qdStart * 3;
        gain *= (orgNum[ocf+2] / orgDen[ocf+2]);

        // For numers which DON'T have s^2 or s terms.
        if(orgNum[ocf] == 0)
        {
            newNum[ncf] = 1.0;
            newNum[ncf+1] = 0.0;
            newNum[ncf+2] = Wo * Wo;
            newNum[ncf+3] = 1.0;
            newNum[ncf+4] = 0.0;
            newNum[ncf+5] = Wo * Wo;
        }

        // For numers which DO have s^2 and s terms.
        else
        {
            A = complex<double>( orgNum[ocf], 0.0 );
            B = complex<double>( orgNum[ocf+1], 0.0 );
            C = complex<double>( orgNum[ocf+2], 0.0 );
            quadradicRoot( A, B, C, D, E );

            A = complex<double>( 1.0, 0.0 );
            B = - 1.0/D * complex<double>( BW, 0.0 );
            C = complex<double>( Wo*Wo, 0.0 );
            quadradicRoot( A, B, C, D, E );

            newNum[ncf] = 1.0;
            newNum[ncf+1] = -2.0 * real(D);
            newNum[ncf+2] = norm(D);
            newNum[ncf+3] = 1.0;
            newNum[ncf+4] = -2.0 * real(E);
            newNum[ncf+5] = norm(E);
        }

        A = complex<double>( orgDen[ocf], 0.0 );
        B = complex<double>( orgDen[ocf+1], 0.0 );
        C = complex<double>( orgDen[ocf+2], 0.0 );
        quadradicRoot( A, B, C, D, E );

        // Make required substitutions, factor again.
        A = complex<double>( 1.0, 0.0 );
        B = - 1.0/D * complex<double>( BW, 0.0 );
        C = complex<double>( Wo*Wo, 0.0 );
        quadradicRoot( A, B, C, D, E );

        // Make required substitutions, factor again.
        newDen[ncf] = 1.0;
        newDen[ncf+1] = -2.0 * real(D);
        newDen[ncf+2] = norm(D);
        newDen[ncf+3] = 1.0;
        newDen[ncf+4] = -2.0 * real(E);
        newDen[ncf+5] = norm(E);
    }

    aCoefs = newNum;
    bCoefs = newDen;
}


/**
 * Calculates response for IIR filters.
 */
double IIR::frqeResp( double freq )
{
    int     c, q;
    double  omega, omega2,
            rea, img, tmp,
            mag;

    // Initialize magnatude.
    mag = gain;

    // Pre-calculate omega and omega squared.
    omega = TWOPI * freq / fsamp;
    omega2 = 2 * omega;

    // Loop through coefficients for each quadratic.
    for( q=0; q<(order+1)/2; q++ )
    {
        c = 3*q;

        rea = aCoefs[c] + aCoefs[c+1]*cos(omega) + aCoefs[c+2]*cos(omega2);
        img = -aCoefs[c+1]*sin(omega) - aCoefs[c+2]*sin(omega2);
        tmp = sqrt( rea*rea + img*img );
        mag *= tmp;

        rea = bCoefs[c] + bCoefs[c+1]*cos(omega) + bCoefs[c+2]*cos(omega2);
        img = -bCoefs[c+1]*sin(omega) - bCoefs[c+2]*sin(omega2);
        tmp = sqrt(rea*rea + img*img);
        mag /= tmp;
    }

    // Convert magnitude response to dB.
    if( mag < EPS )
        mag = EPS;
    mag = 20 * log10(mag);

    return mag;
}


/**
 * Calculate the response at edge freqency.
 */
void IIR::calcGain()
{

    // Determine the edge frequency.
    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 );
    }
}

测试代码:

/*****************************************************************************
 *                               iir_test.cpp
 *
 * IIR class testing.
 *
 * Zhang Ming, 2010-03, Xi'an Jiaotong University.
 *****************************************************************************/


#define BOUNDS_CHECK

#include <iostream>
#include <iir.h>


using namespace std;
using namespace splab;


int main()
{
//    string  fType = "lowpass";
//    double  fs = 1000,
//            fpass = 200,
//            apass = -3,
//            fstop = 300,
//            astop = -20;
//    string  method = "Butterworth";
//    string  method = "Chebyshev";
//    string  method = "InvChebyshev";
//    string  method = "Elliptic";
//    IIR iir( fType, method );
//    iir.setParams( fs, fpass, apass, fstop, astop );

//    string  fType = "highpass";
//    double  fs = 1000,
//            fstop = 200,
//            astop = -20,
//            fpass = 300,
//            apass = -3;
//    string  method = "Butterworth";
//    string  method = "Chebyshev";
//    string  method = "InvChebyshev";
//    string  method = "Elliptic";
//    IIR iir( fType, method );
//    iir.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;
//    string  method = "Butterworth";
//    string  method = "Chebyshev";
//    string  method = "InvChebyshev";
//    string  method = "Elliptic";
//    IIR iir( fType, method );
//    iir.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;
//    string  method = "Butterworth";
//    string  method = "Chebyshev";
//    string  method = "InvChebyshev";
    string  method = "Elliptic";
    IIR iir( fType, method );
    iir.setParams( fs, fpass1, apass1, fstop1, fstop2, astop1, fpass2, apass2 );

    iir.design();
    iir.dispInfo();

    cout << endl;
    return 0;
}

运行结果:

                    Filter selectivity      :  bandstop
                    Approximation method    :  Elliptic
                    Filter order            :  4
                    Overall gain            :  0.153272
                    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


           Numerator Coefficients                Denominator Coefficients

  N   [  1       z^-1           z^-2  ]     [  1       z^-1           z^-2  ]
 ===  =================================     =================================
  1    1.0   -0.45087426    1.00000000       1.0   -1.44482415    0.70572930
  2    1.0    0.45087426    1.00000000       1.0    1.44482415    0.70572930


         ==================== Edge Frequency Response ====================
             Mag(fp1) = -3.00000000(dB)       Mag(fp2) = -3.00000000(dB)
             Mag(fs1) = -36.87418446(dB)       Mag(fs2) = -36.87418446(dB)


Process returned 0 (0x0)   execution time : 0.078 s
Press any key to continue.

你可能感兴趣的:(IIR数字滤波器设计方法的C++实现)