如何在C中实现IIR过滤器?

时间:2018-05-29 16:23:12

标签: c pointers filter

我正在尝试实施IIR过滤器。

我尝试实现以下过滤器,但

FIR : y(n) = b0(x[n]) + ... +bM-1(x[n-M+1])

IIR : y(n) = {b0(x[n]) + ... +bM-1(x[n-M+1])} - {a1(y[n-1]) + ... +aN(y[n-N}

我对如何实施y[n-1].....感到困惑。

这是我的代码。

void IIRFloat(double *coeffs_B, double *coeffs_A, double *input, double *output, int length, int filterLength)
{
    double bcc, acc;
    double *coeffa, *coeffb;
    double *inputp;
    double *outputp;
    int n,k;

    //filter length =7
    for (n = 0; n < length; n++) {
        coeffa = coeffs_A;
        coeffb = coeffs_B;
        inputp = &insamp[filterLength - 1 + n]; //insamp[6]~insamp[85]

        acc = 0;
        bcc = 0;

        for (k = 0; k < filterLength; k++) {
            bcc += (*coeffb++) * (*inputp--); //b[0] * x[6] + b[1] * x[5]...+b[6] * x[0]
        }

        for (k = 1; k < filterLength; k++) {
            acc += (*coeffa++) * (*output--); //a[1] * y[5] + a[2] * y[4]...+a[6] * y[0]
        }
        output[n] = bcc-acc;

    }
}

我不会在这里复制memove函数的代码以寻求简洁。

4 个答案:

答案 0 :(得分:1)

IIR滤波器是递归的,这意味着它采用输出的过去值(相当于它可以表示为输入的无限序列)。

假设您有过滤器

y[n] = b*x[n]-a*y[n-1]

通常,第一个输出初始化为给定值,例如0。

假设您要过滤长度为N的信号x,那么您应该执行以下操作:

double out[N]; //output vector
double a = 0.3, b=0.5; //assign the coefficients a value
out[0] = 0; //initialize the first element

for (int i=1; i<N; i++)
{
    out[i] = b*x[i] -a*[i-1];  
}

对于您的代码,我无法知道您在第inputp = &insamp[filterLength - 1 + n];行中正在做什么,这可能是个问题。

我将假设inputp是您要过滤的信号。

另一个问题:您使用过滤器filterLength来指示输入元素和输出元素的长度:通常不在IIR过滤器中。

从0到filterlength的输出元素应该以某种方式初始化,假设为0.然后在第二个循环中,你使循环索引从1开始,但系数数组应该从0开始。

(DOUBT:如果最旧的元素是y[5],那么长度如何为7?)

使用索引而不是取消引用数组,您的代码应该是这样的:

void IIRFloat(double *coeffs_B, double *coeffs_A, double *input, double *output, int length, int filterLength)
{
    double bcc, acc;
    double *inputp;
    int n,k;


    for (int ii=0; ii<filterLength; ii++)
    {
        output[ii] = 0;
    }

    //filter length =7
    for (n = 0; n < length; n++) {
        inputp = &insamp[filterLength - 1 + n]; //insamp[6]~insamp[85]

        acc = 0;
        bcc = 0;

        for (k = 0; k < filterLength; k++) 
        {
            bcc += coeffb[k] * inputp[filterLength-k-1]; //b[0] * x[6] + b[1] * x[5]...+b[6] * x[0]
        }

        for (k = 0; k < filterLength; k++) 
        {
            acc += coeffa[k] * output[filterLength-k-1]; //a[1] * y[5] + a[2] * y[4]...+a[6] * y[0]
        }
        output[n] = bcc-acc;

    }
}

修改 我认为长度相同的两个for可以合并在一起:

for (k = 0; k < filterLength; k++) 
{
    output[n] += (coeffb[k] * inputp[filterLength-k-1] - coeffa[k] * output[filterLength-k-1]); 
}

答案 1 :(得分:1)

如果你真的想用指针做这件事:

void filter1(const double *b, const double *a, size_t filterLength, const double *in, double *out, size_t length) {
    const double a0 = a[0];
    const double *a_end = &a[filterLength-1];
    const double *out_start = out;
    a++;
    out--;
    size_t m;
    for (m = 0; m < length; m++) {
        const double *b_macc = b;
        const double *in_macc = in;
        const double *a_macc = a;
        const double *out_macc = out;
        double b_acc = (*in_macc--) * (*b_macc++);
        double a_acc = 0;
        while (a_macc <= a_end && out_macc >= out_start) {
            b_acc += (*in_macc--) * (*b_macc++);
            a_acc += (*out_macc--) * (*a_macc++);
        }
        *++out = (b_acc - a_acc) / a0;
        in++;
    }
}

我将此算法的结果与MATLAB的过滤函数进行了比较。

注意:如果您对系数进行标准化(即a0 == 1),则可以获得较大的性能提升。为此,您只需将ab向量除以a0,然后在每次迭代时不必将b_acc - a_acc除以a0

答案 2 :(得分:0)

y[n-1]只是上一个时间步骤ex的结果:output[n-1]y[n-N]是从当前时间开始的第N个时间步长)。因此,您需要使用适当的output[n-k](?)量来索引输出数组(注意避免索引超出数组的开头,但是,您需要代码来防止这种情况)。

你也没有正确地索引你的coeffa / coeffb系数(我认为)。要获得您想要的结果,您可能需要执行此操作*(coeffa ++)以确保在取消引用之前递增指针。我会敦促你通过直接索引到数组coeff_A[k]来做到这一点,但是,因为它更容易看到发生了什么。

最后一点注意:操作(* output--)正在以我认为无意的方式移动输出指针。索引到数组中。

答案 3 :(得分:0)

我只想回答 iir 过滤器,因为它比 fir 过滤器更容易实现,并且在许多情况下就足够了。最重要的是,你永远不想分别指定 A 和 B,因为 B 应该总是等于 (1-A)。因此,可以说基本 IIR 滤波器的实现是:

class IIR_Filter
{
    IIR_Filter(double _alpha, double initialValue = 0) 
    { 
        alpha = _alpha; 
        lastVal = initialValue;
    }
    
    double processNextStep(double newVal)
    {
        lastVal = alpha * newVal + (1.0 - alpha) * lastVal;
        return lastVal;
    }
private:
    double alpha;
    double lastVal;
};