FFT算法

[复制链接]
1942|4
 楼主| hudi008 发表于 2024-2-21 11:00 | 显示全部楼层 |阅读模式
  1. /*********************************************************************
  2.                          快速福利叶变换C程序包
  3. 函数简介:此程序包是通用的快速傅里叶变换C语言函数,移植性强,以下部分不依
  4.           赖硬件。此程序包采用联合体的形式表示一个复数,输入为自然顺序的复
  5.           数(输入实数是可令复数虚部为0),输出为经过FFT变换的自然顺序的
  6.           复数.此程序包可在初始化时调用create_sin_tab()函数创建正弦函数表,
  7.           以后的可采用查表法计算耗时较多的sin和cos运算,加快可计算速度.与
  8.           Ver1.1版相比较,Ver1.2版在创建正弦表时只建立了1/4个正弦波的采样值,
  9.           相比之下节省了FFT_N/4个存储空间
  10. 使用说明:使用此函数只需更改宏定义FFT_N的值即可实现点数的改变,FFT_N的
  11.           应该为2的N次方,不满足此条件时应在后面补0。若使用查表法计算sin值和
  12.           cos值,应在调用FFT函数前调用create_sin_tab()函数创建正弦表
  13. 函数调用:FFT(s);
  14. 参考文献:  
  15. **********************************************************************/
  16. #include <math.h>
  17. #include "fft_fp.h"

  18. //#define FFT_N 64                                                   //定义福利叶变换的点数
  19. //#define PI 3.1415926535897932384626433832795028841971               //定义圆周率值
  20. //
  21. //struct compx {float real,imag;};                                    //定义一个复数结构
  22. struct compx s[FFT_N];                                              //FFT输入和输出:从S[0]开始存放,根据大小自己定义
  23. float SIN_TAB[FFT_N/4+1];                                             //定义正弦表的存放空间

  24. /*******************************************************************
  25. 函数原型:struct compx EE(struct compx b1,struct compx b2)  
  26. 函数功能:对两个复数进行乘法运算
  27. 输入参数:两个以联合体定义的复数a,b
  28. 输出参数:a和b的乘积,以联合体的形式输出
  29. *******************************************************************/
  30. struct compx EE(struct compx a,struct compx b)      
  31. {
  32. struct compx c;
  33. c.real=a.real*b.real-a.imag*b.imag;
  34. c.imag=a.real*b.imag+a.imag*b.real;
  35. return(c);
  36. }

  37. /******************************************************************
  38. 函数原型:void create_sin_tab(float *sin_t)
  39. 函数功能:创建一个正弦采样表,采样点数与福利叶变换点数相同
  40. 输入参数:*sin_t存放正弦表的数组指针
  41. 输出参数:无
  42. ******************************************************************/
  43. void create_sin_tab(float *sin_t)                     
  44. {
  45.   int i;
  46.   for(i=0;i<=FFT_N/4;i++)
  47.   sin_t[ i]=sin(2*PI*i/FFT_N);
  48. }
  49. /******************************************************************
  50. 函数原型:void sin_tab(float pi)
  51. 函数功能:采用查表的方法计算一个数的正弦值
  52. 输入参数:pi 所要计算正弦值弧度值,范围0--2*PI,不满足时需要转换
  53. 输出参数:输入值pi的正弦值
  54. ******************************************************************/
  55. float sin_tab(float pi)
  56. {
  57.   int n;
  58.   float a=0;
  59.    n=(int)(pi*FFT_N/2/PI);
  60.    
  61.   if(n>=0&&n<=FFT_N/4)
  62.     a=SIN_TAB[n];
  63.   else if(n>FFT_N/4&&n<FFT_N/2)
  64.     {
  65.      n-=FFT_N/4;
  66.      a=SIN_TAB[FFT_N/4-n];
  67.     }
  68.   else if(n>=FFT_N/2&&n<3*FFT_N/4)
  69.     {
  70.      n-=FFT_N/2;
  71.      a=-SIN_TAB[n];
  72.    }
  73.   else if(n>=3*FFT_N/4&&n<3*FFT_N)
  74.     {
  75.      n=FFT_N-n;
  76.      a=-SIN_TAB[n];
  77.    }
  78.   
  79.   return a;
  80. }
  81. /******************************************************************
  82. 函数原型:void cos_tab(float pi)
  83. 函数功能:采用查表的方法计算一个数的余弦值
  84. 输入参数:pi 所要计算余弦值弧度值,范围0--2*PI,不满足时需要转换
  85. 输出参数:输入值pi的余弦值
  86. ******************************************************************/
  87. float cos_tab(float pi)
  88. {
  89.    float a,pi2;
  90.    pi2=pi+PI/2;
  91.    if(pi2>2*PI)
  92.      pi2-=2*PI;
  93.    a=sin_tab(pi2);
  94.    return a;
  95. }
  96. /*****************************************************************
  97. 函数原型:void FFT(struct compx *xin,int N)
  98. 函数功能:对输入的复数组进行快速傅里叶变换(FFT)
  99. 输入参数:*xin复数结构体组的首地址指针,struct型
  100. 输出参数:无
  101. *****************************************************************/
  102. void FFT(struct compx *xin)
  103. {
  104.   register int f,m,nv2,nm1,i,k,l,j=0;
  105.   struct compx u,w,t;
  106.    
  107.    nv2=FFT_N/2;                  //变址运算,即把自然顺序变成倒位序,采用雷德算法
  108.    nm1=FFT_N-1;  
  109.    for(i=0;i<nm1;i++)        
  110.    {
  111.     if(i<j)                    //如果i<j,即进行变址
  112.      {
  113.       t=xin[j];           
  114.       xin[j]=xin[ i];
  115.       xin[ i]=t;
  116.      }
  117.     k=nv2;                    //求j的下一个倒位序
  118.     while(k<=j)               //如果k<=j,表示j的最高位为1   
  119.      {           
  120.       j=j-k;                 //把最高位变成0
  121.       k=k/2;                 //k/2,比较次高位,依次类推,逐个比较,直到某个位为0
  122.      }
  123.    j=j+k;                   //把0改为1
  124.   }
  125.                         
  126.   {
  127.    int le,lei,ip;                            //FFT运算核,使用蝶形运算完成FFT运算
  128.     f=FFT_N;
  129.    for(l=1;(f=f/2)!=1;l++)                  //计算l的值,即计算蝶形级数
  130.            ;
  131.   for(m=1;m<=l;m++)                         // 控制蝶形结级数
  132.    {                                        //m表示第m级蝶形,l为蝶形级总数l=log(2)N
  133.     le=2<<(m-1);                            //le蝶形结距离,即第m级蝶形的蝶形结相距le点
  134.     lei=le/2;                               //同一蝶形结中参加运算的两点的距离
  135.     u.real=1.0;                             //u为蝶形结运算系数,初始值为1
  136.     u.imag=0.0;
  137.     //w.real=cos(PI/lei);                  //不适用查表法计算sin值和cos值
  138.     // w.imag=-sin(PI/lei);
  139.     w.real=cos_tab(PI/lei);                //w为系数商,即当前系数与前一个系数的商
  140.     w.imag=-sin_tab(PI/lei);
  141.     for(j=0;j<=lei-1;j++)                  //控制计算不同种蝶形结,即计算系数不同的蝶形结
  142.      {
  143.       for(i=j;i<=FFT_N-1;i=i+le)           //控制同一蝶形结运算,即计算系数相同蝶形结
  144.        {
  145.         ip=i+lei;                          //i,ip分别表示参加蝶形运算的两个节点
  146.         t=EE(xin[ip],u);                   //蝶形运算,详见公式
  147.         xin[ip].real=xin[ i].real-t.real;
  148.         xin[ip].imag=xin[ i].imag-t.imag;
  149.         xin[ i].real=xin[ i].real+t.real;
  150.         xin[ i].imag=xin[ i].imag+t.imag;
  151.        }
  152.       u=EE(u,w);                          //改变系数,进行下一个蝶形运算
  153.      }
  154.    }
  155.   }
  156.   
  157. }


评分

参与人数 1威望 +10 收起 理由
呐咯密密 + 10 很给力!

查看全部评分

呐咯密密 发表于 2024-2-22 15:00 | 显示全部楼层
哇塞,这个是好东西,我得码一下,感谢感谢。
发货后已经wi 发表于 2024-2-25 19:30 | 显示全部楼层
这段代码中的FFT实现使用了查表法以提高计算效率。
发货后已经wi 发表于 2024-2-25 20:40 | 显示全部楼层
对于cos和sin的计算,采用了查表法,而不是直接调用库函数,提高效率666
申小林一号 发表于 2024-4-30 16:57 | 显示全部楼层
非常不错的帖子,值得推广扩散!!!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

253

主题

9898

帖子

11

粉丝
快速回复 在线客服 返回列表 返回顶部