FFT程序說明

allan3344 2006-02-22 01:59:27
小弟近日在网上看见一个FFT(快速傅立叶变换)的C源代码,正好需要,可是看完代码后还是不是很明白。希望各大侠给与帮祝,能否给该代码加上注解?谢谢!

PS:这个程序的输入数据是什么,输出数据是什么?


代码如下:

#include <iostream.h>
#include <math.h>
#include <iomanip.h>
#include <stdlib.h>
#include <fstream.h>
#include <string>
#include <process.h>
#include <stdio.h>

void four1(double data[65], int nn, int isign)
{
int n,j,i,m,mmax,istep;
double tempr,tempi,theta,wpr,wpi,wr,wi,wtemp;
n = 2 * nn;
j = 1;
for (i = 1; i<=n; i=i+2)
{
if(j > i)
{
tempr = data[j];
tempi = data[j + 1];
data[j] = data[i];
data[j + 1] = data[i + 1];
data[i] = tempr;
data[i + 1] = tempi;
}
m = n / 2;
while (m >= 2 && j > m)
{
j = j - m;
m = m / 2;
}
j = j + m;
}
mmax = 2;
while(n > mmax)
{
istep = 2 * mmax;
theta = 6.28318530717959 / (isign * mmax);
wpr = -2.0 * sin(0.5 * theta)*sin(0.5 * theta);
wpi = sin(theta);
wr = 1.0;
wi = 0.0;
for(m = 1; m<=mmax; m=m+2)
{
for (i = m; i<=n; i=i+istep)
{
j = i + mmax;
tempr=double(wr)*data[j]-double(wi)*data[j+1];
tempi=double(wr)*data[j+1]+double(wi)*data[j];
data[j] = data[i] - tempr;
data[j + 1] = data[i + 1] - tempi;
data[i] = data[i] + tempr;
data[i + 1] = data[i + 1] + tempi;
}
wtemp = wr;
wr = wr * wpr - wi * wpi + wr;
wi = wi * wpr + wtemp * wpi + wi;
}
mmax = istep;
}
}


void twofft(double data1[], double data2[], double fft1[], double fft2[], int& n)
{
int j,n2,j2;
double c1r,c1i,c2r,c2i,conjr,conji,h1r,h1i,h2r,h2i;
c1r = 0.5;
c1i = 0.0;
c2r = 0.0;
c2i = -0.5;
for (j = 1; j<=n; j++)
{
fft1[2 * j - 1] = data1[j];
fft1[2 * j] = data2[j];
}
four1(fft1, n, 1);
fft2[1] = fft1[2];
fft2[2] = 0.0;
fft1[2] = 0.0;
n2 = 2 * (n + 2);
for (j = 2; j<=n / 2 + 1; j++)
{
j2 = 2 * j;
conjr = fft1[n2 - j2 - 1];
conji = -fft1[n2 - j2];
h1r=c1r*(fft1[j2-1]+conjr)-c1i*(fft1[j2]+conji);
h1i=c1i*(fft1[j2-1]+conjr)+c1r*(fft1[j2]+conji);
h2r=c2r*(fft1[j2-1]-conjr)-c2i*(fft1[j2]-conji);
h2i=c2i*(fft1[j2-1]-conjr)+c2r*(fft1[j2]-conji);
fft1[j2 - 1] = h1r;
fft1[j2] = h1i;
fft1[n2 - j2 - 1] = h1r;
fft1[n2 - j2] = -h1i;
fft2[j2 - 1] = h2r;
fft2[j2] = h2i;
fft2[n2 - j2 - 1] = h2r;
fft2[n2 - j2] = -h2i;
}
}


int cint(double x)
{
int temp;
double iprt;
if (x>0)
{
x=modf(x,&iprt);
if(fabs(x)<0.5)
temp=int(iprt);
else
temp=int(iprt+1);
}
else if(x==0)
temp=0;
else
{
x=modf(x,&iprt);
if(fabs(x)<0.5)
temp=int(iprt);
else
temp=int(iprt)-1;
}
return temp;
}

void prntft(double data[], double nn2)
{
int n,m,mm;
cout<<"n real(n) imag.(n) real(N-n) imag.(N-n)"<<endl;
cout<<setw(1)<<"0";
cout<<setw(14)<<data[1];
cout<<setw(14)<<data[2];
cout<<setw(14)<<data[1];
cout<<setw(14)<<data[2]<<endl;
for (n = 3; n<=(nn2 / 2) + 1; n=n+2)
{
m = (n - 1) / 2;
mm = nn2 + 2 - n;
cout<<setiosflags(ios::fixed);
cout<<setprecision(0)<<setw(1)<<m;
cout<<setprecision(6)<<setw(14)<<data[n];
cout<<setprecision(6)<<setw(14)<<data[n+1];
cout<<setprecision(6)<<setw(14)<<data[mm];
cout<<setprecision(6)<<setw(14)<<data[mm+1]<<endl;
}
}

void main()
{
//program d12r2
//driver for routine twofft
int n,i,n2,isign;
double data1[33], data2[33], fft1[65], fft2[65],per,x;
n = 32;
n2 = 2 * n;
per = 8.0;
const double pi = 3.1415926;
for(i = 1; i<=n; i++)
{
x = 2.0 * pi * i / per;
data1[i] = cint(cos(x));
data2[i] = cint(sin(x));
}
twofft(data1, data2, fft1, fft2, n);

cout<<setw(1)<<"Fourier transform of first function:"<<endl;
prntft(fft1, n2);
cout<<setw(1)<<"Fourier transform of second function:"<<endl;
prntft(fft2, n2);
//invert transform
isign = -1;
four1(fft1, n, isign);
cout<<setw(1)<<"Inverted transform = first function:"<<endl;
prntft(fft1, n2);
four1(fft2, n, isign);
cout<<setw(1)<<"Inverted transform = second function:"<<endl;
prntft(fft2, n2);
}
...全文
971 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
jackczar 2006-05-19
  • 打赏
  • 举报
回复
有兄弟可以帮忙讲解一下上面的程序吗?运行好了,为什么不能输入数据呢?我的毕业设计是做基2FFT和基4FFT的程序,有兄弟可以帮忙吗?谢谢我的邮箱是xiachaoyu-1984@163.com
liangbch 2006-03-28
  • 打赏
  • 举报
回复
这几天,我一直在看FFT算法,下面分享一下我这几天学到的东西

1。直接计算离散傅立叶变换具有n^2的复杂度,而cooley 和tukey在1965年发现了一种计算离散傅立叶变换的快速算法(即通

常所说的FFT算法),这个算法在计算变换长度n=2^k的离散傅立叶变换时,具有 n*k 的复杂度,即O(n)=n*log2(n), 下面以此

为例,讲讲快FFT的特点。
1)复数运算:傅立叶变换是基于复数的,因此首先知道复数的运算规则,在FFT算法中,只涉及复数的加、减和乘法三种运

算。一个复数可表示为 c=( x,yi), x 为复数的实部,y为复数的虚部,i为虚数单位,等于-1的平方根。复数的运算规则是:

若c1 表示为 (x1,y1),c2 表示为(x2,y2), 则 (x1+x2,y1+y2)和(x1-x2,y1-y2)分别等于c1+c2的和,c1-c2的差,复数的乘法

相对复杂一些,c1*c2 的积为 (x1*x2-y1*y2,x1*y2+x2*y1).

2)蝶形变换:普通的FFT算法称为基2的FFT算法,这种算法的核心是蝶形变换 长度为n=2^k1的变换共需要做 k1 * n/2 次

蝶形变换,若需变换数据表示为一个复数数组c[],则每次蝶形变换有2个输入 c[i],c[i+s],两个输出:c[i],c[i+s],s成为翅

间距。 每个变换的基本算法是:

t=wr * c[i+s];
c[i+s]=c[i]-t;
c[i]=c[i]+t;

前面说过,长度为n=2^k1的变换共需要做 k1 *

n/2次变换,实际的程序是一个3层循环,共需要k1*k2*(k3/2)次变换(k2*k3/2=n/2)。前面的wr是w的整数次方,w=e^(-2*PI/k3

) (k3=2,4,8,16...n,PI是圆周率),也成为旋转因子,例如n=32的变换需要log2(32)=5趟变换:
第1趟变换需要16*1次变换,翅间距是1, 若w=e^(-2*PI/2), 则wr=w^1
第2趟变换需要8*2次变换, 翅间距是2, 若w=e^(-2*PI/4), 则wr=w^1,w^2
第3趟变换需要4*2次变换, 翅间距是4, 若w=e^(-2*PI/8), 则wr=w^1,w^2,w^3,w^4
第4趟变换需要2*8次变换, 翅间距是8, 若w=e^(-2*PI/16),则wr=w^1,w^2,w^3,w^4,w^5,w^6,w^7,w^8
第5趟变换需要16*1次变换,翅间距是16, 若w=e^(-2*PI/32),则wr=w^1,w^2,w^3,w^4,w^5...w^15,w^16

3)w数组,w 的实部=cos(2*PI/k3),w的虚部= -sin(2*PI/k3),计算出w,则wr数组就好求了,不断即相乘即可,当然也可以通

过三角函数直接求。w^p 的实部=cos(2*PI/K3*p),虚部=-sin(2*PI/k3*p)

4)复数数组排序,在基2的蝶形变换中,复数数组需要重新排序,c[i] 要放置到数组c的第 reverse(c[i])

的位置,m=reverse(n) 函数的算法是这样的,若 n的 k位2进制的为b[], b[k-1],B[k-2],...b[2],b[1],b[0],( b[i] 等于1

或者0,b[0]为最低bit). 则m=reverse(n)的2进制的为 b[0],b[1],b[2],b[3],...b[k-1] (b[k-1]为最低bit).

  更复杂的变换算法:基2的蝶形变换算法不止一种,它可分为2类,一类为基2时分傅立叶变换,另一类为基2频分傅立叶变

换。上例的变为基2时分算法,在每一趟变换中,翅间距依次变大,第一趟为2,最后一趟为n/2,数组重排在变换之前进行,基

2频分算法正好相反,翅间距依次缩小,起于n/2,止于2,数组重排在蝶形变换之后进行。 在<傅立叶变换>一书中,提到3

种基2时分变换,3种基2频分变换。上述算法称为基2时分FFT第二种算法。我在看你的这个程序的同时,还看到朱志刚写的一

个FFT程序,这个程序的算法是基2时分FFT第一种算法,它比经典的算法更复杂,需要对wr数组进行逆序排列。

///这个程序不太直观的地方。


在计算wp是,虚部使用sin函数直接计算,但是实部没有直接计算,按照数学公式cos(x)=1-2*sin(x/2)^2,这里,wpr只取-2*s

in(x/2),比实际值小1,故在计算w时,采用以下算式:
wr = wr * wpr - wi * wpi + wr;
wi = wi * wpr + wtemp * wpi + wi;
wr*wpr-wi*wpi+wr --> wr*(1+wpr)-wi*wpi,可以看到这里将1重新补上了。


//更复杂的FFT算法,除了基2 的FFT算法外,还有更加复杂的基4算法,基8算法,甚至基3,基5算法,纯粹的基4算法只能计算

长度为4^k的变换,但它比基2的算法速度更高。为了提高速度,很多FFT算法使用混合基算法。如我看到的2个效率很高程序均

使用了混合基算法。第一个程序是来自:http://momonga.t.u-tokyo.ac.jp/~ooura/fft.html,它使用了基2,基4,甚至基8混

合算法,共提供了6同样功能的算法。可对长度为2^k的序列做FFT,这个程序的写的很复杂,我现在尚不能完全看懂。另一个

程序来自:http://hjem.get2net.dk/jjn/fft.htm。相对于前者,这个程序相对简单一点。它使用了基2,基3,基4,基5,基8,

基10 混合算法,几乎可以计算任意长度的FFT。具体的,当序列长度n为2,3,5,7,11,13,17,19,23,29,31,37等小素数时,或

者n的最大素因数小于等于37时,可计算这个序列的FFT。

关于FFT算法的其它文档:http://www.jjj.de/fxt/fxtbook.pdf, websuite: http://www.jjj.de/fxt/, 这是我见过的关于变换算法的最全面的文档。







mmmcd 2006-03-22
  • 打赏
  • 举报
回复
看函数void main()里面
这两个调用的方法:
prntft
twofft
就可以知道输入数据,和输出数据了
karlzheng 2006-03-22
  • 打赏
  • 举报
回复
不懂地说:要先把FFT的碟形算法搞明白
liangbch 2006-03-22
  • 打赏
  • 举报
回复
mark,
我现在正在看冷建华的 <福里叶变换>(2004.6出版),等我有时间,给你看看这个程序.

4,450

社区成员

发帖
与我相关
我的任务
社区描述
图形图像/机器视觉
社区管理员
  • 机器视觉
  • 迪菲赫尔曼
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧