C程序編寫的FFT與matlab中的fft函數比較
一、FFT的C程序編寫
在我的上一篇博客已經講過如何用C語言編寫一個FFT變換,不清楚的可以打開下面這個鏈接閱讀
經過對原來程序的修改,現在可以實現一下功能:
1、可以對一個連續的時域信號進行採樣,將其轉換爲離散的時域信號。
2、可以輸入輸入採樣點數目和採樣頻率來進行FFT變換。
3、可以將FFT變換結果通過gnuplot畫出來。
程序如下:
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#define N 1024
typedef struct{
double real;
double imag;
}complex;
complex x[N], *W;
int size=0;
double l[5000],h[5000];
double PI=4.0*atan(1);
void output()
{
int i;
for(i=0;i<size;i++)
{
printf("%lf %lf %.4f",h[i],l[i],x[i].real);//輸出頻域序列 模長 和信號的實部
if(x[i].imag>=0.0001)
{
printf("+%.4fj\n",x[i].imag);
}
else if(fabs(x[i].imag)<0.0001)
{
printf("\n");
}
else
{
printf("%.4fj\n",x[i].imag);
}
}
}
void change()
{
complex temp;
unsigned short i=0,j=0,k=0;
double t;
for(i=0;i<size;i++)
{
k=i;
j=0;
t=(log(size)/log(2));
while( (t--)>0 )
{
j=j<<1;
j|=(k & 1);
k=k>>1;
}
if(j>i)
{
temp=x[i];
x[i]=x[j];
x[j]=temp;
}
}
}
void transform()
{
int i;
W=(complex *)malloc(sizeof(complex) * size);
for(i=0;i<size;i++)
{
W[i].real=cos(2*PI/size*i);
W[i].imag=-1*sin(2*PI/size*i);
}
}
void add(complex a,complex b,complex *c)
{
c->real=a.real+b.real;
c->imag=a.imag+b.imag;
}
void sub(complex a,complex b,complex *c)
{
c->real=a.real-b.real;
c->imag=a.imag-b.imag;
}
void mul(complex a,complex b,complex *c)
{
c->real=a.real*b.real - a.imag*b.imag;
c->imag=a.real*b.imag + a.imag*b.real;
}
void fft()
{
int i=0,j=0,k=0,m=0;
complex q,y,z;
change();
for(i=0;i<log(size)/log(2) ;i++)
{
m=1<<i;
for(j=0;j<size;j+=2*m)
{
for(k=0;k<m;k++)
{
mul(x[k+j+m],W[size*k/2/m],&q);
add(x[j+k],q,&y);
sub(x[j+k],q,&z);
x[j+k]=y;
x[j+k+m]=z;
}
}
}
}
int main()
{
int i;
double fs;
double t[5000];
printf("輸入採樣個數和採樣頻率\n");
scanf("%d %lf",&size,&fs);//輸入的採樣點數和採樣頻率
for(i=0;i<size;i++)
{
t[i]=i/fs; //時間序列
x[i].real=0.5*sin(2*PI*25*t[i]);//輸入連續時間信號,並對其進行採樣
x[i].imag=0;
h[i]=i*(fs/(double)size);//頻率序列
}
transform();
fft();
for(i=0;i<size;i++) //計算變換結果的模長
{
l[i]=sqrt(x[i].imag*x[i].imag+x[i].real*x[i].real) ;
}
printf("輸出FFT後的結果\n");
output();
return 0;
}
二、輸出結果演示
從程序中可以看出輸入的信號爲0.5sin(2PI25t)
程序運行演示如下:
輸入採樣點數爲512 採樣頻率爲100。
第一行對應的是頻率,
第二行對應的是變換結果的模長。
第三行對應的是每個採樣點的變換結果。
三、使用gnuplot畫出函數圖像
plot [0:100] [0:150] "F6.dat" u 1:2 w l
畫圖結果:
橫座標爲頻率,縱座標爲模值
四、與matlab中的fft函數進行比較
>> fs=100;
>> N=512;
>> n=0:N-1;
>> t=n/fs;
>> y=0.5*sin(2*pi*25*t);
>> x=fft(y,N);
>> m=abs(x);
>> f=n*fs/N;
>> plot(f,m);
>> xlabel('頻率/Hz');
>> ylabel('振幅');title('N=512');
>> grid on;
輸出結果爲
結論:用C語言編寫的FFT程序輸出效果跟matlab基本一致。
總結:通過這幾天對FFT的學習,基本瞭解了它的算法原理,對它的輸入和輸出也有了深刻的瞭解,但是這次的C程序還有一些不足之處,就是當採樣點沒有達到2的整數次冪的時候沒有自動補零的功能。
後續
經過對程序改進,現在可以實現自動補0.
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#define N 1024
typedef struct{
double real;
double imag;
}complex;
complex x[N], *W;
int size=0;
double l[5000],h[5000];
double PI;
double fs;
int FN;
void output()
{
int i;
for(i=0;i<FN;i++)
{
printf("%lf %lf %.4f",h[i],l[i],x[i].real);
if(x[i].imag>=0.0001)
{
printf("+%.4fj\n",x[i].imag);
}
else if(fabs(x[i].imag)<0.0001)
{
printf("\n");
}
else
{
printf("%.4fj\n",x[i].imag);
}
}
}
void change()
{
complex temp;
unsigned short i=0,j=0,k=0;
double t;
for(i=0;i<FN;i++)
{
k=i;
j=0;
t=(log(FN)/log(2));
while( (t--)>0 )
{
j=j<<1;
j|=(k & 1);
k=k>>1;
}
if(j>i)
{
temp=x[i];
x[i]=x[j];
x[j]=temp;
}
}
//output();
}
void transform()
{
int i;
W=(complex *)malloc(sizeof(complex) * FN);
for(i=0;i<FN;i++)
{
W[i].real=cos(2*PI/FN*i);
W[i].imag=-1*sin(2*PI/FN*i);
}
}
void add(complex a,complex b,complex *c)
{
c->real=a.real+b.real;
c->imag=a.imag+b.imag;
}
void sub(complex a,complex b,complex *c)
{
c->real=a.real-b.real;
c->imag=a.imag-b.imag;
}
void mul(complex a,complex b,complex *c)
{
c->real=a.real*b.real - a.imag*b.imag;
c->imag=a.real*b.imag + a.imag*b.real;
}
void fft()
{
int i=0,j=0,k=0,m=0;
complex q,y,z;
change();
for(i=0;i<log(FN)/log(2) ;i++)
{
m=1<<i;
for(j=0;j<FN;j+=2*m)
{
for(k=0;k<m;k++)
{
mul(x[k+j+m],W[FN*k/2/m],&q);
add(x[j+k],q,&y);
sub(x[j+k],q,&z);
x[j+k]=y;
x[j+k+m]=z;
}
}
}
}
int main()
{
int i;
PI=4.0*atan(1);
double t[5000];
printf("輸入數據個數\n");
scanf("%d %d %lf",&size,&FN,&fs);
for(i=0;i<size;i++)
{
t[i]=i/fs;
x[i].real=0.5*sin(2*PI*25*t[i]);
x[i].imag=0;
}
transform();
fft();
for(i=0;i<FN;i++)
{
l[i]=sqrt(x[i].imag*x[i].imag+x[i].real*x[i].real) ;
h[i]=i*(fs/(double)FN);
}
printf("輸出FFT後的結果\n");
output();
return 0;
}
在命令窗口中輸入指令:
plot [0:100] [0:15] "ff66.dat" u 1:2 w l
運行結果:
數據點爲50,採樣點爲128,採樣頻率爲100
matlab實現:
>> N=50;
>> NFFT=128;
>> fs=100;
>> n=0:N-1;
>> t=n/fs;
>> y=0.5*sin(2*pi*25*t);
>> x=fft(y,NFFT);
>> m=abs(x);
>> f=(0:NFFT-1)*fs/NFFT;
>> plot(f,m);
>> xlabel('頻率/Hz');ylabel('振幅');
>> title('N=50 NFFT=128');grid on;
輸出結果:
增大采樣點數和數據點數,
現在把數據點設爲800,採樣點設爲1024。
運行結果:
matlab實現
>> N=800;
>> NFFT=1024;
>> n=0:N-1;
>> fs=100;
>> t=n/fs;
>> y=0.5*sin(2*pi*25*t);
>> x=fft(y,NFFT);
>> m=abs(x);
>> f=(0:NFFT-1)*fs/NFFT;
>> plot(f,m);
>> xlabel('頻率/Hz');ylabel('振幅');
>> title('N=800 NFFT=1024');grid on;
運行結果:
結論:用C語言基本實現了matlab中fft函數的作用。