基2时选快速傅里叶变换算法(FFT)

此程序是以前做双色点阵音乐频谱时参考数字信号处理写的。相对于网上的一些代码,我这里对一些特殊的旋转因子做了特别处理程度稍微快了些,当然相对了基2,使用分裂基、基4肯定会更快。
//cr2_fft.h

#ifndef _CR_2_FFT_H_
#define _CR_2_FFT_H_

#define FFT_N 256 //输入序列的点数,要求FFT_N=2^FFT_M
#define FFT_M 8 //快速傅里叶变换蝶形运算的级数,满足FFT_N=2^FFT_M,即当FFT_N=16时,FFT_M=4
//特殊旋转因子的系数,预先计算出来
#define N__2 FFT_N/2
#define N__4 FFT_N/4
#define N__8 FFT_N/8
#define sqrt2_2 181 //sqrt2_2*256

typedef struct
{
    long real;//实部
    long image;//虚部 
}Complex_Typedef;

void FFT_CR2(Complex_Typedef *SampleData);

#endif

//cr2_fft.c

/*************FFT旋转因子查找表,上位机生成********************** *cos_tab:cos(2*pi*r/FFT_N) * 256 ,放大256倍 *sin_tab:sin(2*pi*r/FFT_N) * 256 ,放大256倍 *其中pi为常数:3.141592;FFT_N:傅里叶变换的点数;r:0~N/2-1;**/
#if FFT_N==256
const int sin_tab[FFT_N/2]=
{
 00,06,13,19,25,31,
38,44,50,56,62,68,
74,80,86,92,98,104,
109,115,121,126,132,137,
142,147,152,157,162,167,
172,177,181,185,190,194,
198,202,206,209,213,216,
220,223,226,229,231,234,
237,239,241,243,245,247,
248,250,251,252,253,254,
255,255,256,256,256,256,
256,255,255,254,253,252,
251,250,248,247,245,243,
241,239,237,234,231,229,
226,223,220,216,213,209,
206,202,198,194,190,185,
181,177,172,167,162,157,
152,147,142,137,132,126,
121,115,109,104,98,92,
86,80,74,68,62,56,
50,44,38,31,25,19,
13,06
};

const int cos_tab[FFT_N/2]=
{
 256,256,256,255,255,254,
253,252,251,250,248,247,
245,243,241,239,237,234,
231,229,226,223,220,216,
213,209,206,202,198,194,
190,185,181,177,172,167,
162,157,152,147,142,137,
132,126,121,115,109,104,
98,92,86,80,74,68,
62,56,50,44,38,31,
25,19,13,06,00,-06,
-13,-19,-25,-31,-38,-44,
-50,-56,-62,-68,-74,-80,
-86,-92,-98,-104,-109,-115,
-121,-126,-132,-137,-142,-147,
-152,-157,-162,-167,-172,-177,
-181,-185,-190,-194,-198,-202,
-206,-209,-213,-216,-220,-223,
-226,-229,-231,-234,-237,-239,
-241,-243,-245,-247,-248,-250,
-251,-252,-253,-254,-255,-255,
-256,-256
};
#else
#error "查找表的长度必须是256"
#endif

/****************************************************************** *函数名:ReverseAddr *函数功能:对有限采样序列进行倒序运算 *输入参数:SampleData:采样数据序列 *返回值:指向变址后的复数序列 ******************************************************************/
static Complex_Typedef *ReverseAddr(Complex_Typedef *SampleData)
{
    int N = FFT_N;
    int M = FFT_M;
    u16 half_N;//用于存储序列点数的一半,可以减少运算次数,
    u16 N_2;//减少运算次数用
    u16 i=0,j=0,k=0;//计数用

    Complex_Typedef tempotary;//临时数据
    half_N = N/2; 
    j = half_N;
    N_2 = N-2;
    //雷德算法
    for(i = 1;i <= N_2;i ++)
    {
        if(i < j)
        {
            tempotary = SampleData[j];
            SampleData[j] = SampleData[i];//码位交换
            SampleData[i] = tempotary;
        }
        k = half_N;//求j的下一个倒位序
        while(k <= j)//
        {
            j = j - k;//把最高位变成0
            k = k>>1;//K/2,比较次高位,依次类推,逐个比较,直到某个位为0

        }
        j = j + k;//把0改为1 
    }
    return (SampleData);

}

/*********************************************************** *函数名:FFT_CR2:基2时选FFT *函数功能:对采样有限序列进行快速傅里叶变换 *输入参数:SampleData- 采样序列 *输出参数: SampleData- 变换后的频谱序列 *返回值:无 ***********************************************************/
void FFT_CR2(Complex_Typedef *SampleData)
{
    int N = FFT_N;                  //N:有限序列的点数
    int M = FFT_M;                  //满足N=2^M
    u16 L;//蝶形结的长度
    u16 p,k;//第L级各蝶形的复数结点
    int r=0;//旋转因子的指数
    int B=0;//第L级,每个蝶形的2个输入数据相距的点数=2^(L-1)
    u16 Interval;//第L级,同一旋转因子对应着的间隔=2^L
    long TR,TI,tmp;//临时变量
    ReverseAddr(&SampleData);//变址运算
    for(L = 1;L<= M;L ++)//最外层循环,用以完成N/2L个蝶形运算
    {
        B=1;
        B = B<<(L-1);//用移位替代乘法,相当于2^(L-1)
        Interval = B<<1;//相当于2^L
        for(p = 0;p <= B - 1;p ++)//)中间层循环完成每一级的N/2 个蝶形运算
        {   
            r=1;    
            r = (r<<(M-L)) * p;//求旋转因子指数r=(2^(M-L))*j 
            for(k = p;k < N;k += Interval)//求出相同旋转因子的蝶形
            {
                /***********暂存临时数据*************/
                TR  = SampleData[k].real;
                TI  = SampleData[k].image;
                tmp = SampleData[k+B].real;
                /*****优化计算量:先完成特殊蝶形运算,为第四类蝶形单元运算*****/
                /*******先利用欧拉公式将旋转因子转换为三角函数*****/
                switch(r)
                {
                    case 0://旋转因子的值为1,不用计算乘法
                        {
                                SampleData[k].real    = SampleData[k].real  + SampleData[k+B].real;    
                                SampleData[k].image   = SampleData[k].image + SampleData[k+B].image; 
                                SampleData[k+B].real  = TR                  - SampleData[k+B].real;    
                                SampleData[k+B].image = TI                  - SampleData[k+B].image;

                        }break;
                    case N__2://幅角为-PI
                        {
                                SampleData[k].real    = SampleData[k].real  - SampleData[k+B].real;    
                                SampleData[k].image   = SampleData[k].image - SampleData[k+B].image; 
                                SampleData[k+B].real  = TR                  + SampleData[k+B].real;    
                                SampleData[k+B].image = TI                  + SampleData[k+B].image;

                        }break;
                    case N__4://幅角为PI/2
                        {
                                SampleData[k].real    = SampleData[k].real  + SampleData[k+B].image;    
                                SampleData[k].image   = SampleData[k].image - SampleData[k+B].real; 
                                SampleData[k+B].real  = TR                  - SampleData[k+B].image;    
                                SampleData[k+B].image = TI                  + tmp ;

                        }break;
                    case N__8://幅角为PI/4,可以减少2次乘法
                        {
                                SampleData[k].real    = SampleData[k].real  + ( ( SampleData[k+B].real  + SampleData[k+B].image)  *   sqrt2_2 >>8) ;//由于之前放大了256倍,故最后结果应缩小256倍 
                                SampleData[k].image   = SampleData[k].image + ( ( SampleData[k+B].image - SampleData[k+B].real )  *   sqrt2_2 >>8) ; 
                                SampleData[k+B].real  = TR                  - ( ( SampleData[k+B].real  + SampleData[k+B].image)  *   sqrt2_2 >>8) ;    
                                SampleData[k+B].image = TI                  + ( ( tmp                   - SampleData[k+B].image)  *   sqrt2_2 >>8) ;

                        }break;
                    default://普通蝶形
                        {
                                SampleData[k].real    = SampleData[k].real  + ((SampleData[k+B].real  * cos_tab[r]) >> 8)  +  ((SampleData[k+B].image *  sin_tab[r]) >> 8);    
                                SampleData[k].image   = SampleData[k].image - ((SampleData[k+B].real  * sin_tab[r]) >> 8)  +  ((SampleData[k+B].image *  cos_tab[r]) >> 8); 
                                SampleData[k+B].real  = TR                  - ((SampleData[k+B].real  * cos_tab[r]) >> 8)  -  ((SampleData[k+B].image *  sin_tab[r]) >> 8);    
                                SampleData[k+B].image = TI                  + ((tmp                   * sin_tab[r]) >> 8)  -  ((SampleData[k+B].image *  cos_tab[r]) >> 8);
                        }
                }

            }

        }

    }
}

顺便附上FFT的旋转因子生成软件:
《基2时选快速傅里叶变换算法(FFT)》

http://download.csdn.net/detail/jieffantfyan/9707975

点赞