C99相当于MATLAB" filter"?

时间:2016-11-18 13:08:44

标签: c matlab filter signal-processing c99

为什么MATLAB和C版本会产生不同的结果?

MATLAB:

gender

C:

[B_coeffs, A_coeffs ] = butter(4, 100/(16000/2), 'high');

state  = zeros( 4, 1 );
input = zeros( 64,1 );

for i=1:64
    input(i)=i;
end

[filtered_output, state] = filter( B_coeffs, A_coeffs, input, state );

,其中

int main(...)
{
    for(int test=0; test<64;test++)
        Xin[test]=test+1;
    ...
    high_pass_filter_init(...)
    high_pass_filter_do(...)
}
// Do the filtering
void high_pass_filter_do( t_high_pass_filter* hpf, float *Xin, float *Yout )
{
    double Xi, Yi;

    double z0 = hpf->state[0],
           z1 = hpf->state[1],
           z2 = hpf->state[2],
           z3 = hpf->state[3];

    unsigned int N = 64;
    unsigned int i = 0;

    for(i=0;i<N;i++)
    {
        Xi = Xin[i];

        Yi = hpf->B_coeffs[0] * Xi + z0;
        z0 = hpf->B_coeffs[1] * Xi + z1 - hpf->A_coeffs[1] * Yi;
        z1 = hpf->B_coeffs[2] * Xi + z2 - hpf->A_coeffs[2] * Yi;
        z2 = hpf->B_coeffs[3] * Xi + z3 - hpf->A_coeffs[3] * Yi;
        z3 = hpf->B_coeffs[4] * Xi      - hpf->A_coeffs[4] * Yi;

        Yout[i] = (float) Yi;
    }

    hpf->state[0] = z0;
    hpf->state[1] = z1;
    hpf->state[2] = z2;
    hpf->state[3] = z3;

    return;
}

**输出为:**

typedef struct
{
    float A_coeffs[5];
    float B_coeffs[5];
    float state[4];    
} t_high_pass_filter;

void high_pass_filter_init( t_high_pass_filter* hpf)
{

    hpf->A_coeffs[0] =  1.0000;
    hpf->A_coeffs[1] = -3.8974;
    hpf->A_coeffs[2] =  5.6974;
    hpf->A_coeffs[3] = -3.7025;
    hpf->A_coeffs[4] =  0.9025;

    hpf->B_coeffs[0] =  0.9500;
    hpf->B_coeffs[1] = -3.7999;
    hpf->B_coeffs[2] =  5.6999;
    hpf->B_coeffs[3] = -3.7999;
    hpf->B_coeffs[4] =  0.9500;

    hpf->state[0] = 0.0;
    hpf->state[1] = 0.0;
    hpf->state[2] = 0.0;
    hpf->state[3] = 0.0;
}

前几个值相同(或类似),但随后它们发散。此外,在第一次迭代后,过滤器状态完全不同。

我做错了什么?

2 个答案:

答案 0 :(得分:3)

在第二次修改后,您的问题变得清晰:chaotic behavior

第一次,您似乎刚刚将MATLAB命令窗口中的系数复制到C函数中。但是,MATLAB的format似乎已设置为short,因为C函数中的系数舍入到4位小数。这个舍入(以及第一次在C函数中使用float)是你的问题。

这次是我这次做的事情:

  1. 复制您的MATLAB脚本
  2. 复制您的C代码,并将其转换为MATLAB MEX格式以便更轻松地比较
  3. 调整C代码,使其接受
    1. 没有(在这种情况下,它使用“内置”,舍入版本,如前所述
    2. 与MATLAB脚本中使用的相同系数(带有附加数字)
  4. 运行脚本并进行比较。
  5. 结论:您看到对初始值的敏感度非常高。

    TL; DR :此代码:

    clc
    
    [B_coeffs, A_coeffs] = butter(4, 100/(16000/2), 'high');
    
    state = zeros(4, 1);
    input = 1:64;
    
    % MATLAB version
    [filtered_output0, state0] = filter(B_coeffs, A_coeffs, input, state);
    
    mex filter_test.c
    
    % MEX, using built-in constants (of which only the first few digits are equal)
    [filtered_output1, state1] = filter_test([],[], input, state, 0);
    
    % MEX, using the exact same constants 
    [filtered_output2, state2] = filter_test(B_coeffs, A_coeffs, input, state, 1);
    
    % Compare!
    [filtered_output0.' filtered_output1.' filtered_output2.']
    
    [state0 state1 state2]
    

    filter_test.c包含的位置:

    #include <stdio.h>
    #include "mex.h"
    
    #define N ( 64u)
    #define C (  5u)
    #define S (C-1u)
    
    /* helper struct for HPF */    
    typedef struct
    {
        double A_coeffs[C];
        double B_coeffs[C];
        double    state[S]; 
    
    } t_high_pass_filter;
    
    /* "Default" values (note that these are ROUNDED to 4 digits only)
    void 
    high_pass_filter_init(t_high_pass_filter* hpf)
    {
        hpf->A_coeffs[0] =  1.0000;
        hpf->A_coeffs[1] = -3.8974;
        hpf->A_coeffs[2] =  5.6974;
        hpf->A_coeffs[3] = -3.7025;
        hpf->A_coeffs[4] =  0.9025;
    
        hpf->B_coeffs[0] =  0.9500;
        hpf->B_coeffs[1] = -3.7999;
        hpf->B_coeffs[2] =  5.6999;
        hpf->B_coeffs[3] = -3.7999;
        hpf->B_coeffs[4] =  0.9500;
    
        hpf->state[0] = 0.0;
        hpf->state[1] = 0.0;
        hpf->state[2] = 0.0;
        hpf->state[3] = 0.0;
    }
    
    /* the actual filter */
    void 
    high_pass_filter_do(t_high_pass_filter* hpf, 
                        const double *Xin, 
                        double *Yout)
    {
        double Xi, Yi;
    
        double z0 = hpf->state[0],
               z1 = hpf->state[1],
               z2 = hpf->state[2],
               z3 = hpf->state[3];
    
        unsigned int i = 0u;
    
        for(i=0; i<N; ++i)
        {     
            Xi = Xin[i];
    
            Yi = hpf->B_coeffs[0] * Xi + z0;
            z0 = hpf->B_coeffs[1] * Xi + z1 - hpf->A_coeffs[1] * Yi;
            z1 = hpf->B_coeffs[2] * Xi + z2 - hpf->A_coeffs[2] * Yi;
            z2 = hpf->B_coeffs[3] * Xi + z3 - hpf->A_coeffs[3] * Yi;
            z3 = hpf->B_coeffs[4] * Xi      - hpf->A_coeffs[4] * Yi;
    
            Yout[i] =  Yi;
        }
    
        hpf->state[0] = z0;
        hpf->state[1] = z1;
        hpf->state[2] = z2;
        hpf->state[3] = z3;    
    }
    
    /* Wrapper between MATLAB MEX and filter function */
    void
    filter(const double *B_coeffs,        
           const double *A_coeffs,
           const double *input,       
           const double *state,
           double *filtered_output, 
           double *state_output)
    {
        unsigned int i = 0u;    
        t_high_pass_filter hpf;
    
        /* Use built-in defaults when coefficients 
         * have not been passed explicitly */    
        if (B_coeffs == NULL) 
        {
            high_pass_filter_init(&hpf);
        }
    
        /* Otherwise, use the coefficients on the arguments */
        else
        {          
            for (i=0u; i<C; ++i) {
                hpf.B_coeffs[i] = B_coeffs[i];
                hpf.A_coeffs[i] = A_coeffs[i];                        
            }
    
            for (i=0u; i<S; ++i)             
                hpf.state[i] = state[i];                    
    
        }
    
        /* Apply filter function */    
        high_pass_filter_do(&hpf,
                            input, 
                            filtered_output);
    
        /* Assign output state explicitly */
        for (i=0u; i<S; ++i)             
            state_output[i] = hpf.state[i];                    
    }
    
    /* MATLAB/MEX Gateway function */
    void mexFunction(int nlhs,       mxArray *plhs[],
                     int nrhs, const mxArray *prhs[])
    {
        unsigned int i = 0u;    
    
        double *B_coeffs = mxGetPr(prhs[0]),
               *A_coeffs = mxGetPr(prhs[1]),
               *input    = mxGetPr(prhs[2]),
               *state    = mxGetPr(prhs[3]);   
    
        int flag = *mxGetPr(prhs[4]);
    
        double *filtered_output,
               *state_output;
    
        /* filtered_output, state */
        plhs[0] = mxCreateDoubleMatrix(1, N, mxREAL);
        plhs[1] = mxCreateDoubleMatrix(S, 1, mxREAL);
    
        filtered_output = mxGetPr(plhs[0]);    
        state_output    = mxGetPr(plhs[1]);
        if (flag == 0)
        {
            filter(NULL, 
                   NULL, 
                   input,       
                   state,      
                   filtered_output, 
                   state_output);
    
        }
        else 
        {        
            filter(B_coeffs, 
                   A_coeffs, 
                   input,       
                   state,      
                   filtered_output, 
                   state_output);
        }
    }
    

    给出了这个输出:

    %   MATLAB                    C with rounding           C without rounding
    %===============================================================================
    
    filtered values: 
        9.499817826393004e-001    9.500000000000000e-001    9.499817826393004e-001
        1.802482117980145e+000    1.802630000000000e+000    1.802482117980145e+000
        2.562533610562189e+000    2.563140162000000e+000    2.562533610562189e+000        
        ...  (58 more values)     ...                       ...
       -5.477082906502112e+000    2.637900416547026e+002   -5.477082906502112e+000
       -5.550054712403821e+000    2.808186934967214e+002   -5.550054712403821e+000
       -5.610782439991141e+000    2.985749768301545e+002   -5.610782439991141e+000
    
    states after filter run: 
       -6.740826417997180e+001    2.553210086702404e+002   -6.740826417997180e+001
        2.006572641218511e+002   -7.151404729138806e+002    2.006572641218511e+002
       -1.991114894348111e+002    6.686913808328562e+002   -1.991114894348111e+002
        6.586237103693912e+001   -2.086639165892145e+002    6.586237103693912e+001
    

    下次,请确保在复制粘贴之前使用format long e :)

答案 1 :(得分:2)

当我运行你的代码时,我明白了:

MATLAB     C
================================
0.9500     0.950000
1.8026     1.802630
2.5631     2.563139
... 
(58 more values)
...
263.7900   263.687500
280.8187   280.705475
298.5750   298.450012

但是,当我将C代码更改为使用double代替float时,我明白这一点:

MATLAB     C
================================
0.9500     0.950000
1.8026     1.802630
2.5631     2.563140
... 
(58 more values)
...
263.7900   263.790042
280.8187   280.818693
298.5750   298.574977 

所以,@ Richard是正确的:

  1. 您在MATLAB方面做了某些错误,我无法重现原始值
  2. 您的C代码是正确的,但与MATLAB版本不同,因为MATLAB使用double代替float