MATLAB里面的filter和filtfilt的C语言源代码

MATLAB里面的filter和filtfilt的C语言源代码

嗯,算法非常简单,就是网上搜不到C代码实现。filter是个很万能的数字滤波器函数,只要有滤波器的差分方程系数,IIR呀FIR呀都能通过它实现。在MATLAB里面,filter最常用的格式是这两个:

[y,zf] = filter(b,a,X)
[y,zf] = filter(b,a,X,zi)

其中b和a就是差分方程的系数,X是输入向量,zi是“初始状态”。可能这么说明还是不很清晰,那么请看图(注意,a(1)为1,这个可以通过差分方程所有系数除以a(1)所得):

嗯,这样子很轻松地就能把各个y值给算出来了,哦注意上面式子里面的n是“向量a或者b中最长的长度”,在实际编程的时候,如果a和b长度不一样,短者显然是要用0补齐的。对于那个初始状态zi,忽略的时候,比如(顺便提醒一句MATLAB的下标从1开始)

y(1)=b(1)x(1)

y(2)=b(1)x(2)+Z1(1)= b(1)x(2) + b(2)x(1) - a(2)y(1)

不忽略的时候

y(1)=b(1)x(1) + Z1(0)

y(2)=b(1)x(2)+Z1(1)= b(1)x(2) + b(2)x(1) - a(2)y(1) + Z2(0)

因为实际程序中自己定义的东西比较多(=,=|||这也是没办法的事情不是),而filtfilt这个超级无敌的“零相移滤波函数”更是复杂到稍微调用了一下自己写的矩阵运算函数,所以代码全部贴上来实在是太乱。等这次工程做完会大概地把代码打包发一下。现在就先贴点代码片段好了……但愿我的代码风格没那么难懂……

#include "filter.h"
#include <string.h>

//Transposed Direct-Form II Realization
//initial conditions: zi, length==nfilt-1. ignore when zi==NULL


#ifndef EPS
#define EPS 0.000001
#endif

void
filter(const double* x, double* y, int xlen, double* a, double* b, int nfilt, double* zi){
    double tmp;
    int i,j;

    //normalization
    if( (*a-1.0>EPS) || (*a-1.0<-EPS) ){
        tmp=*a;
        for(i=0;i<nfilt;i++){
            b[i]/=tmp;
            a[i]/=tmp;
        }
    }

    memset(y,0,xlen*sizeof(double));

    a[0]=0.0;
    for(i=0;i<xlen;i++){
        for(j=0;i>=j&&j<nfilt;j++){
            y[i] += (b[j]*x[i-j]-a[j]*y[i-j]);
        }
        if(zi&&i<nfilt-1) y[i] += zi[i];
    }
    a[0]=1.0;

}

OK,接下来是神奇的零相移滤波filtfilt,操作上不复杂,原理上小小有点复杂。它主要是先找到一个“合理的”初始状态Zi,使得无论先从反向滤波还是先从正向滤波结果一致,然后filter一次,逆向,再filter一次,再逆向,就是结果了。这里面包括3个要点:

1. Zi的确定。

2. 边缘效应的消除。

3. 正反向滤波的数学原理。

对于要点1,可以参阅Fredrik Gustafsson的论文Determining the Initial States in Forward-backward Filtering的数学证明。要点2,filtfilt对数据两头做了镜像拓延,最后滤完波再截掉头尾。要点3,这个貌似不大难推(

#include <stdlib.h>
#include "filter.h"
#include "mulMat.h"
#include "invMat.h"

int
filtfilt(const double* x, double* y, int xlen, double* a, double* b, int nfilt){
    int nfact;
    int tlen;    //length of tx
    int i;
    double *tx,*tx1,*p,*t,*end;
    double *sp,*tvec,*zi;
    double tmp,tmp1;

    nfact=nfilt-1;    //3*nfact: length of edge transients
    
    if(xlen<=3*nfact || nfilt<2) return -1;    //too short input x or a,b
    
    //Extrapolate beginning and end of data sequence using a "reflection
    //method". Slopes of original and extrapolated sequences match at
    //the end points.
    //This reduces end effects.
    tlen=6*nfact+xlen;
    tx=malloc(tlen*sizeof(double));
    tx1=malloc(tlen*sizeof(double));

    sp=malloc( sizeof(double) * nfact * nfact );
    tvec=malloc( sizeof(double) * nfact );
    zi=malloc( sizeof(double) * nfact );

    if( !tx || !tx1 || !sp || !tvec || !zi ){
        free(tx);
        free(tx1);
        free(sp);
        free(tvec);
        free(zi);
        return 1;
    }
    
    tmp=x[0];
    for(p=x+3*nfact,t=tx;p>x;--p,++t) *t=2.0*tmp-*p;
    for(end=x+xlen;p<end;++p,++t) *t=*p;
    tmp=x[xlen-1];
    for(end=tx+tlen,p-=2;t<end;--p,++t) *t=2.0*tmp-*p;
    //now tx is ok.

    end = sp + nfact*nfact;
    p=sp;
    while(p<end) *p++ = 0.0L; //clear sp
    sp[0]=1.0+a[1];
    for(i=1;i<nfact;i++){
        sp[i*nfact]=a[i+1];
        sp[i*nfact+i]=1.0L;
        sp[(i-1)*nfact+i]=-1.0L;
    }

    for(i=0;i<nfact;i++){
        tvec[i]=b[i+1]-a[i+1]*b[0];
    }

    if(invMat(sp,nfact)){
        free(zi);
        zi=NULL;
    }
    else{
        mulMat(sp,tvec,zi,nfact,nfact,1);
    }//zi is ok

    free(sp);free(tvec);

    //filtering tx, save it in tx1
    tmp1=tx[0];
    if(zi)
        for( p=zi,end=zi+nfact; p<end;) *(p++) *= tmp1;
    filter(tx,tx1,tlen,a,b,nfilt,zi);

    //reverse tx1
    for( p=tx1,end=tx1+tlen-1; p<end; p++,end--){
        tmp = *p;
        *p = *end;
        *end = tmp;
    }

    //filter again
    tmp1 = (*tx1)/tmp1;
    if(zi)
        for( p=zi,end=zi+nfact; p<end;) *(p++) *= tmp1;
    filter(tx1,tx,tlen,a,b,nfilt,zi);

    //reverse to y
    end = y+xlen;
    p = tx+3*nfact+xlen-1;
    while(y<end){
        *y++ = *p--;
    }

    free(zi);
    free(tx);
    free(tx1);

    return 0;
}

与MATLAB对比(MATLAB代码):

x=[1 2 3 4 5 6 7 8];
a=[1 2 3];
b=[4 5 6];
t=filtfilt(b,a,x);
for i=1:8, fprintf(1,'%f\n',t(i)), end;

结果为:

-6731884.250000
7501778.750000
-2757230.250000
-662443.250000
1360955.750000
-686678.250000
4135.750000
227147.750000

这个例子用上面给出的C语言版的filtfilt计算结果完全一致:

  • 8
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: polyfit函数是MATLAB中的一个用于多项式拟合的函数,它在MATLAB的polyfit文档中有详细的说明。但是,polyfit的具体实现是在MATLAB的底层通过C语言编写的。由于MATLAB源代码是闭源的,因此无法直接获取polyfit函数的C语言源代码。 polyfit函数的算法通常使用最小二乘法来拟合多项式曲线。它通过计算数据点与拟合曲线之间的误差,并调整多项式系数,使误差最小化。MATLAB的polyfit函数使用的算法可能是基于一些经典的数值方法,如QR分解或LU分解。 如果你需要在C语言中实现多项式拟合的功能,可以参考polyfit函数的算法步骤,并使用C语言编写相应的代码。您可以根据多项式拟合的算法自行实现,如使用最小二乘法或其他数值方法,通过计算误差最小化来调整多项式系数。 这里是一个简单的示例,使用C语言实现2次多项式拟合的算法步骤: 1. 输入原始数据点的坐标(x, y)。 2. 定义拟合多项式的阶数n为2。 3. 创建一个矩阵A和一个向量B,用于存储方程组Ax=B的系数和常数项。 4. 遍历所有的数据点,分别计算矩阵A和向量B的元素。 - A中的第(i, j)个元素是x的i次方的和,其中j表示多项式的次数。 - B的第(i)个元素是y与x的i次方的乘积的和。 5. 使用数值方法(如高斯消元法或QR分解)求解方程组Ax=B,得到多项式系数。 6. 输出多项式的系数,即将多项式曲线用一组Cn表示。 需要注意的是,以上是一个简化的示例,实际的C语言实现可能需要更多的代码和复杂的数值计算方法。如果你需要更详细的C语言源代码实现,可以参考相关的数值计算库,如GSL(GNU Scientific Library)或使用其他开源的数值计算库来实现多项式拟合。 ### 回答2: polyfit函数是MATLAB中用于多项式拟合的函数,用于通过最小二乘法拟合数据点到一个多项式曲线。polyfit函数的C语言源代码如下: ```c #include <stdio.h> #include <stdlib.h> void polyfit(double *x, double *y, int n, double *coefficients, int order) { int i, j, k; double *X = (double *) malloc((2 * order + 1) * sizeof(double)); double *Y = (double *) malloc((order + 1) * sizeof(double)); double *B = (double *) malloc((order + 1) * sizeof(double)); double *A = (double *) malloc((order + 1) * (order + 1) * sizeof(double)); for (i = 0; i < 2 * order + 1; i++) { X[i] = 0.0; for (j = 0; j < n; j++) { X[i] += pow(x[j], i); } } for (i = 0; i <= order; i++) { Y[i] = 0.0; for (j = 0; j < n; j++) { Y[i] += pow(x[j], i) * y[j]; } } for (i = 0; i <= order; i++) { for (j = 0; j <= order; j++) { A[i * (order + 1) + j] = X[i + j]; } } for (i = 0; i <= order; i++) { B[i] = Y[i]; } for (k = 0; k <= order; k++) { for (i = k + 1; i <= order; i++) { double factor = A[i * (order + 1) + k] / A[k * (order + 1) + k]; B[i] -= factor * B[k]; for (j = k; j <= order; j++) { A[i * (order + 1) + j] -= factor * A[k * (order + 1) + j]; } } } coefficients[order] = B[order] / A[order * (order + 1) + order]; for (i = order - 1; i >= 0; i--) { double sum = B[i]; for (j = i + 1; j <= order; j++) { sum -= A[i * (order + 1) + j] * coefficients[j]; } coefficients[i] = sum / A[i * (order + 1) + i]; } free(X); free(Y); free(B); free(A); } int main() { double x[] = {1, 2, 3, 4, 5}; double y[] = {2, 3, 5, 8, 10}; int n = 5; int order = 2; double *coefficients = (double *) malloc((order + 1) * sizeof(double)); polyfit(x, y, n, coefficients, order); for (int i = 0; i <= order; i++) { printf("Coefficient %d: %.2f\n", i, coefficients[i]); } free(coefficients); return 0; } ``` 这是一个简单的多项式拟合的例子,输入的数据点为x和y,n为数据点个数,order为拟合多项式的阶数。在主函数中调用polyfit函数进行拟合,拟合结果存储在coefficients数组中,然后打印出每个系数的值。 需要注意的是,此为简化版本的多项式拟合代码,实际情况可能还需要添加其他处理和优化策略,以适应更加复杂和实际的数据拟合需求。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值