C語言實現FFT與matlab中的fft函數比較

C程序編寫的FFT與matlab中的fft函數比較

一、FFT的C程序編寫

在我的上一篇博客已經講過如何用C語言編寫一個FFT變換,不清楚的可以打開下面這個鏈接閱讀

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函數的作用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章