FFT原理及C++实现.doc_第1页
FFT原理及C++实现.doc_第2页
FFT原理及C++实现.doc_第3页
FFT原理及C++实现.doc_第4页
FFT原理及C++实现.doc_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

两点DFT简化假设输入为x0,x1;输出为X0,X1. 伪代码如下 :/ -#define N 2#define PI 3.1415926/tw为旋转因子 / -int i, jfor(i=0, Xi=0.0; iN; i+) for(j=0; jN; j+) Xi += xj * ( cos(2*PI*i*j/N) - sin(2*PI*i*j/N) ); 注意到 j=0j=1 i=0cos 1sin 0 tw 1cos 1sin 0 tw 1 i=1cos 1Sin 0 tw 1cos -1sin 0 tw -1 X0 = x0*(1-0) + x1*(1-0) = x0 + 1*x1;X1 = x0*(1-0) + x1*(-1-0) = x0 - 1*x1;这就是单个2点蝶形算法. FFT实现流程图分析(N=8,以8点信号为例)FFT implementation of an 8-point DFT as two 4-point DFTs and four 2-point DFTs 8点FFT流程图(Layer表示层, gr表示当前层的颗粒) 下面以LayerI为例.LayerI部分,具有4个颗粒,每个颗粒2个输入(注意2个输入的来源,由时域信号提供)将输入xk分为两部分x_rk, x_ik.具有实部和虚部,时域信号本没有虚部的,因此可以让x_ik为0.因为LayerII, LayerIII的输入是复数,为了编码统一而强行分的.当然编码时可以判断当前层是否为1来决定是否分.旋转因子 tw = cos(2*PI*k/N)-j*sin(2*PI*k/N);也可以分为实部和虚部,令其为tw_r, tw_i;则tw = tw_r - j*tw_i; Xk = (x_rk + j*x_ik) + (tw_rj*tw_i) * (x_rk+N/2+j*x_ik+N/2)则 X_Rk = x_rk + tw_r*x_rk+N/2 + tw_i*x_ik+N/2; X_Ik = x_ik - tw_i*x_rk+N/2 + tw_r*x_ik+N/2; LayerII部分,具有2个颗粒,每个颗粒4个输入(注意4个输入的来源,由LayerI提供) LayerIII部分,具有1个颗粒,每个颗粒8个输入(注意8个输入的来源,由LayerII提供) LayerI, LayerII, LayerIII从左往右,蝶形信号运算流非常明显! 假令输入为xk, xk+N/2,输出为Xk, Xk+N/2. xk分解为x_rk, x_ik部分则该蝶形运算为Xk = (x_rk-j*x_ik) + (x_rk+N/2-j*x_ik+N/2)*(cos(2*PI*k/N)-j*sin(2*PI*k/N);再令cos(2*PI*k/N)为tw1, sin(2*PI*k/N)为tw2则Xk = (x_rk-j*x_ik) + (x_rk+N/2-j*x_ik+N/2)*(tw1-j*tw2); X_Rk = x_rk + x_rk+N/2*tw1 - x_ik+N/2*tw2;X_IK = x_ik x_rk = x_rk + x_rk+b*tw1 + x_ik+b*tw2;x_ik = x_ik - x_rk+b*tw2 + x_ik+b*tw1; 譬如8点输入x81. 先分割成2部分: x0, x2, x4, x6和 x1, x3, x5, x72. 信号x0, x2, x4, x6再分割成x0, x4和 x2, x6 信号x1, x3, x5, x7再分割成x1, x5和 x3, x7 如上图:在LayerI的时候,我们是对2点进行DFT.(一共4次DFT )输入为 x0&x4; x2&x6; x1&x5; x3&x7输出为 y0,y1; Y2,y3; Y4,y5; Y6,y7; 流程:I.希望将输入直接转换为x0, x4, x2, x6, x1, x5, x3, x7的顺序II.对转换顺序后的信号进行4次DFT 步骤I代码实现/* *反转算法. 这个算法效率比较低!先用起来在说,之后需要进行优化. */static void bitrev(void ) int p=1, q, i; int bit_rev N ; float xx_r N ; bit_rev 0 = 0; while( p N ) for(q=0; qp; q+) bit_rev q = bit_rev q * 2; bit_rev q + p = bit_rev q + 1; p *= 2; for(i=0; iN; i+) xx_r i = x_r i ; for(i=0; iN; i+) x_ri = xx_r bit_revi ;/ -此刻序列x重排完毕- 步骤II代码实现int j;float TR; /临时变量float tw1; /旋转因子/*两点DFT */for(k=0; kN; k+=2) /两点DFT简化告诉我们tw1=1 TR = x_rk; / TR就是A, x_rk+b就是B. x_rk = TR + tw1*x_rk+b; x_rk+b = TR - tw1*x_rk+b; 在LayerII的时候,我们希望得到z,就需要对y进行DFT.y0,y2; y1,y3; y4,y6; y5,y7;z0, z1; z2,z3; z4,z5; z6,z7; 在LayerIII的时候,我们希望得到v,就需要对z进行DFT.z0,z4; z1,z5; z2,z6; z3,z7;v0,v1; v2,v3; v4,v5; v6,v7; 准备令输入为xs, xs+N/2,输出为ys, ys+N/2这个N绝对不是上面的8,这个N是当前颗粒的输入样本总量对于LayerI而言N是2;对于LayerII而言N是4;对于LayerIII而言N是8 复数乘法:(a+j*b) * (c+j*d)实部= a*c bd;虚部= ad + bc; 旋转因子: 实现(C描述)#include #include #include /#include complex.h / -#define N 8 /64 #define M 3 /6 /2m=N #define PI 3.1415926/ - float twiddleN/2 = 1.0, 0.707, 0.0, -0.707;float x_rN = 1, 1, 1, 1, 0, 0, 0, 0; float x_iN; /N=8 /*float twiddleN/2 = 1, 0.9951, 0.9808, 0.9570, 0.9239, 0.8820, 0.8317, 0.7733, 0.7075, 0.6349, 0.5561, 0.4721, 0.3835, 0.2912, 0.1961, 0.0991, 0.0000,-0.0991,-0.1961,-0.2912,-0.3835,-0.4721,-0.5561,-0.6349, -0.7075,-0.7733, 0.8317,-0.8820,-0.9239,-0.9570,-0.9808,-0.9951; /N=64 float x_rN=1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,;float x_iN;*/FILE *fp; / - func -/* *初始化输出虚部 */static void fft_init(void ) int i; for(i=0; iN; i+) x_ii = 0.0; /* *反转算法.将时域信号重新排序. *这个算法有改进的空间 */static void bitrev(void ) int p=1, q, i; int bit_rev N ; / float xx_r N ; / bit_rev 0 = 0; while( p N ) for(q=0; qp; q+) bit_rev q = bit_rev q * 2; bit_rev q + p = bit_rev q + 1; p *= 2; for(i=0; iN; i+) xx_r i = x_r i ; for(i=0; iN; i+) x_ri = xx_r bit_revi ; /* - add by sshc625 - */static void bitrev2(void ) return ; /* */ void display(void ) printf(nn); int i; for(i=0; iN; i+) printf(%ft%fn, x_ri, x_ii); /* * */void fft1(void ) fp = fopen(log1.txt,a+); int L, i, b, j, p, k, tx1, tx2; float TR, TI, temp; /临时变量 float tw1, tw2; /*深M.对层进行循环. L为当前层,总层数为M. */ for(L=1; L 0) b *= 2; i-; / -是否外层对颗粒循环,内层对样本点循环逻辑性更强一些呢! - /* * outter对参与DFT的样本点进行循环 * L=1,循环了1次(4个颗粒,每个颗粒2个样本点) * L=2,循环了2次(2个颗粒,每个颗粒4个样本点) * L=3,循环了4次(1个颗粒,每个颗粒8个样本点) */ for(j=0; j 0) p = p*2; i-; p = p * j; tx1 = p % N; tx2 = tx1 + 3*N/4; tx2 = tx2 % N; / tw1是cos部分,实部; tw2是sin部分,虚数部分. tw1 = ( tx1=N/2)? -twiddletx1-N/2 : twiddle tx1 ; tw2 = ( tx2=N/2)? -twiddletx2-(N/2) : twiddletx2; /* * inner对颗粒进行循环 * L=1,循环了4次(4个颗粒,每个颗粒2个输入) * L=2,循环了2次(2个颗粒,每个颗粒4个输入) * L=3,循环了1次(1个颗粒,每个颗粒8个输入) */ for(k=j; kN; k=k+2*b) TR = x_rk; / TR就是A, x_rk+b就是B. TI = x_ik; temp = x_rk+b; /* *如果复习一下 (a+j*b)(c+j*d)两个复数相乘后的实部虚部分别是什么 *就能理解为什么会如下运算了,只有在L=1时候输入才是实数,之后层的 *输入都是复数,为了让所有的层的输入都是复数,我们只好让L=1时候的 *输入虚部为0 * x_ik+b*tw2是两个虚数相乘 */ fprintf(fp,tw1=%f, tw2=%fn, tw1, tw2); x_rk = TR + x_rk+b*tw1 + x_ik+b*tw2; x_ik = TI - x_rk+b*tw2 + x_ik+b*tw1; x_rk+b = TR - x_rk+b*tw1 - x_ik+b*tw2; x_ik+b = TI + temp*tw2 - x_ik+b*tw1; fprintf(fp,k=%d, x_rk=%f, x_ik=%fn, k, x_rk, x_ik); fprintf(fp,k=%d, x_rk=%f, x_ik=%fn, k+b, x_rk+b, x_ik+b); / / / /* * - add by sshc625 - *该实现的流程为 * for( Layer ) * for( Granule ) * for( Sample ) * * * * */void fft2(void ) fp = fopen(log2.txt,a+); int cur_layer, gr_num, i, k, p; float tmp_real, tmp_imag, temp; /临时变量,记录实部 float tw1, tw2;/旋转因子,tw1为旋转因子的实部cos部分, tw2为旋转因子的虚部sin部分. int step; /步进 int sample_num; /颗粒的样本总数(各层不同,因为各层颗粒的输入不同) /*对层循环 */ for(cur_layer=1; cur_layer 0) i-; gr_num *= 2; /*每个颗粒的输入样本数N */ sample_num = (int)pow(2, cur_layer); /*步进.步进是N/2 */ step = sample_num/2; /* */ k = 0; /*对颗粒进行循环 */ for(i=0; igr_num; i+) /* *对样本点进行循环,注意上限和步进 */ for(p=0; psample_num/2; p+) /旋转因子,需要优化. tw1 = cos(2*PI*p/pow(2, cur_layer); tw2 = -sin(2*PI*p/pow(2, cur_layer); tmp_real = x_rk+p; tmp_imag = x_ik+p; temp = x_rk+p+step; /*(tw1+jtw2)(x_rk+jx_ik) * * real : tw1*x_rk - tw2*x_ik * imag : tw1*x_ik + tw2*x_rk *我想不抽象出一个 * typedef struct * double real; /实部 * double imag; /虚部 * complex;以及针对complex的操作 *来简化复数运算是否是因为效率上的考虑! */ /*蝶形算法 */ x_rk+p = tmp_real + ( tw1*x_rk+p+step - tw2*x_ik+p+step ); x_ik+p = tmp_imag + ( tw2*x_rk+p+step + tw1*x_ik+p+step ); /* Xk = A(k)+WB(k) * Xk+N/2 = A(k)-WB(k)的性质可以优化这里*/ /旋转因子,需要优化. tw1 = cos(2*PI*(p+step)/pow(2, cur_layer); tw2 = -sin(2*PI*(p+step)/pow(2, cur_layer); x_rk+p+step = tmp_real + ( tw1*temp - tw2*x_ik+p+step ); x_ik+p+step = tmp_imag + ( tw2*temp + tw1*x_ik+p+step ); printf(k=%d, x_rk=%f, x_ik=%fn, k+p, x_rk+p, x_ik+p); printf(k=%d, x_rk=%f, x_ik=%fn, k+p+step, x_rk+p+step, x_ik+p+step); /*开跳!:) */ k += 2*step; /* *后记: *究竟是颗粒在外层循环还是样本输入在外层,好象也差不多,复杂度完全一样. *但以我资质愚钝花费了不少时间才弄明白这数十行代码. *从中我发现一个于我非常有帮助的教训,很久以前我写过一部分算法,其中绝大多数都是递归. *将数据量减少,减少再减少,用归纳的方式来找出数据量加大代码的规律 *比如FFT * 1.先写死LayerI的代码;然后再把LayerI的输出作为LayerII的输入,又写死代码; . *

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论