一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

高手分享:一篇通俗易懂的關(guān)于FFT的講解

 西北望msm66g9f 2018-12-23

相信網(wǎng)上現(xiàn)在有很多關(guān)于FFT的教程,我曾經(jīng)也參閱了很多網(wǎng)上的教程,感覺都不怎么通俗易懂。在基本上的研究FFT,并且通過編程的形式實現(xiàn)之后。我決定寫一篇通俗易懂的關(guān)于FFT的講解。因此我在接下來的敘述中盡量非常通俗細致的講解。


本人最早知道傅里葉變換的時候是沉迷于音樂的頻譜跳動無法自拔,當時就很想做一個音樂頻譜顯示器。搜閱了很多資料之后,才了解到傅里葉變換,和FFT。當然這都是以前的事情了,經(jīng)過了系統(tǒng)的學習+2個星期的研究,自制了一個FFT的算法,不敢說速度上的優(yōu)勢,但是個人認為是一種通俗易懂的實現(xiàn)方法。經(jīng)過實際的VC++模擬實驗、和STM32跑的也很成功。


         首先,要會FFT,就必須要對DFT有所了解,因為兩者之間本質(zhì)上是一樣的。在此之前,先列出離散傅里葉變換對(DFT):


           ,k=0,1,…N-1


           n=0,1…N-1

      其中: 


但是FFT之所以稱之為快速傅里葉變換,就利用了以下的幾個性質(zhì)(重中之重!)


         周期性: 

         對稱性: 

         可約性: 


         先把這仨公式放到這,接下來會用到。


根據(jù)這幾個特性,就可以將一個長的DFT運算分解為若干短序列的DFT運算的組合,從而減少運算量。在這里,為了方便理解,我就用了一個按時間抽取的快速傅里葉變換(DIT-FFT)的方法。


首先,將一個序列x(n)一分為二


對于  ,k=0,1,…N-1   設N是2的整次冪 也就是N=2^M

將x(n)按照奇偶分組


x(2r)=x1(r)

x(2r+1)=x2(r)                     r=0,1,…..(N/2)-1

那么將DFT也分為兩組來預算 

            (第一項是偶  第二項是奇)


         這個時候,我們上邊提的三個性質(zhì)中的可約性就起到作用了:

回顧一下: 

那么這個式子就可以化為:

           (式1-1)

      則X1(k)和X2(k)都是長度為N/2的序列 x1(k)和x2(k)的N/2點的離散傅里葉變換

                   其中:

          

  K=0,1,2…N/2-1

至此,一個N點的DFT就被分解為2個N/2的DFT。但是X1(k),和X2(k)只有N/2個點,也就是說式子(1-1)只是DFT前半部分。要求DFT的后半部分可以利用其對稱性求出后半部分為:

(式1-2)


那么式(1-1)和(1-2)就可以專用一個蝶形信號流圖符號來表示。如圖:


以N=8為例,可以用下圖表示:


    通過這樣的分解,每一個N/2DFT只需(N^2)/4次復數(shù)相乘計算,明顯的節(jié)省了運算量。

以此類推,繼續(xù)將已經(jīng)得出的X1(k)和X2(k)按照奇偶分解,還是和上邊一樣的方法。那么就得出了百度上都可以找到的一大推的這個圖片了。  (笑)


對于這張圖片我想強調(diào)的一點就是第二階蝶形運算的時候,WN0之后不應該是WN1嗎,為什么是W2N了,這個問題之前困擾了我一段時間,但是不要忘了,前者的    W0N的展開是W0N/2  因為此時N已經(jīng)按照奇偶分開了,所以是N/2  而W2N/2也就是  W2N是根據(jù)可約性得出的,在這里不能混淆.。


對于運算效率就不用多提了


以上就是FFT算法的理論內(nèi)容了,接下來就是用C語言對這個算法的實現(xiàn)了,對于FFT算法C語言的實現(xiàn),網(wǎng)上的方法層出不窮,介于本人比較懶(懶得看別人的程序),再加上自給自足豐衣足食的原則,我自己也寫了一個個人認為比較通俗易懂的程序,并且為了幫助讀者理解,我特意盡量減少了庫函數(shù)的使用,一些基本的函數(shù)都是自己寫的(難免有很多BUG),但是作為FFT算法已經(jīng)夠用了,目前這個程序只能處理2^N的數(shù)據(jù),理論上來講如果不夠2^N的話可以在后面數(shù)列補0這種操作為了簡約我也就沒有實現(xiàn),但是主要的功能是具備的,下面是代碼:

/*

        時間:2018年11月24日

        功能:將input里的數(shù)據(jù)進行快速傅里葉變換  并且輸出

*/


#include

#include

#define PI 3.1415926

#define FFT_LENGTH                8

double input[FFT_LENGTH]={1,1,1,1,1,1,1,1};

struct complex1{                //定義一個復數(shù)結(jié)構(gòu)體

        double real;        //實部

        double image;        //虛部

};        

//將input的實數(shù)結(jié)果存放為復數(shù)

struct complex1 result_dat[8];

/*        虛數(shù)的乘法

*/

struct complex1 con_complex(struct complex1 a,struct complex1 b){

        struct complex1 temp;

        temp.real=(a.real*b.real)-(a.image*b.image);

        temp.image=(a.image*b.real)+(a.real*b.image);

        return temp;

}


/*

        簡單的a的b次方

*/

int mypow(int a,int b){

        int i,sum=a;

        if(b==0)return 1;

        for(i=1;i<>

                sum*=a;        

        }

        return sum;

}

/*

        簡單的求以2為底的正整數(shù)

*/

int log2(int n){

        unsigned i=1;

        int sum=1;

        for(i;;i++){

                sum*=2;

                if(sum>=n)break;

        }

        return i;

}

/*

        簡單的交換數(shù)據(jù)的函數(shù)

*/

void swap(struct complex1 *a,struct complex1 *b){

        struct complex1 temp;

    temp=*a;

    *a=*b;

    *b=temp;

}

/*

        dat為輸入數(shù)據(jù)的數(shù)組

        N為抽樣次數(shù)  也代表周期  必須是2^N次方

*/

void fft(struct complex1 dat[],unsigned char N){        

        /*最終  dat_buf計算出 當前蝶形運算奇數(shù)項與W  乘積

                        dat_org存放上一個偶數(shù)項的值

        */

        struct complex1 dat_buf,dat_org;

        /*        L為幾級蝶形運算    也代表了2進制的位數(shù)

                n為當前級蝶形的需要次數(shù)  n最初為N/2 每級蝶形運算后都要/2

                i j為倒位時要用到的自增符號  同時  i也用到了L碟級數(shù)   j是計算當前碟級的計算次數(shù) 

                re_i i_copy均是倒位時用到的變量

                k為當前碟級  cos(2*pi/N*k)的  k   也是e^(-j2*pi/N)*k  的  k

        */

        unsigned char L,i,j,re_i=0,i_copy=0,k=0,fft_flag=1;

        //經(jīng)過觀察,發(fā)現(xiàn)每級蝶形運算需要N/2次運算,共運算N/2*log2N  次  

        unsigned char fft_counter=0;

        //在此要進行補2   N必須是2^n   在此略

        //蝶形級數(shù)  (L級)

        L=log2(N);        

        //計算每級蝶形計算的次數(shù)(這里只是一個初始值)  之后每次要/2

        //n=N/2;


        //對dat的順序進行倒位

        for(i=1;i

                i_copy=i;

                re_i=0;

                for(j=L-1;j>0;j--){

                        //判斷i的副本最低位的數(shù)字  并且移動到最高位  次高位  ..

                        //re_i為交換的數(shù)   每次它的數(shù)字是不能移動的 并且循環(huán)之后要清0

                        re_i|=((i_copy&0x01)

                        i_copy>>=1;

                }

                swap(&dat[i],&dat[re_i]);

        }

        //進行fft計算

        for(i=0;i<>

                

                fft_flag=1;

                fft_counter=0;

                for(j=0;j<>

                        if(fft_counter==mypow(2,i)){                //控制隔幾次,運算幾次,

                                fft_flag=0;

                        }else if(fft_counter==0){                //休止結(jié)束,繼續(xù)運算

                                fft_flag=1;

                        }

                        //當不判斷這個語句的時候  fft_flag保持  這樣就可以持續(xù)運算了

                        if(fft_flag){

                                dat_buf.real=cos((2*PI*k)/(N/mypow(2,L-i-1)));

                                dat_buf.image=-sin((2*PI*k)/(N/mypow(2,L-i-1)));

                                dat_buf=con_complex(dat[j+mypow(2,i)],dat_buf);

                                                                                                //計算 當前蝶形運算奇數(shù)項與W  乘積


                                dat_org.real=dat[j].real;

                                dat_org.image=dat[j].image;                //暫存


                                dat[j].real=dat_org.real+dat_buf.real;

                                dat[j].image=dat_org.image+dat_buf.image;                

                                                                                                        //實部加實部   虛部加虛部


                                dat[j+mypow(2,i)].real=dat_org.real-dat_buf.real;

                                dat[j+mypow(2,i)].image=dat_org.image-dat_buf.image;

                                                                                                        //實部減實部        虛部減虛部


                                k++;

                                fft_counter++;

                        }else{

                                fft_counter--;                                //運算幾次,就休止幾次

                                k=0;

                        }

                }

        }


        

}

void main(){

        int i;

        //先將輸入信號轉(zhuǎn)換成復數(shù)

        for(i=0;i<>

                result_dat[i].image=0;        

                                                                //輸入信號是二維的,暫時不存在復數(shù)

                result_dat[i].real=input[i];

                //result_dat[i].real=10;

                                                                //輸入信號都為實數(shù)

        }

        fft(result_dat,FFT_LENGTH);

        for(i=0;i<>

                input[i]=sqrt(result_dat[i].real*result_dat[i].real+result_dat[i].image*result_dat[i].image);

                //取模

                printf('%lf\n',input[i]);

        }

        while(1);

}


 這個程序中input這個數(shù)組是輸入信號,在這里只模擬抽樣了8次,輸出的數(shù)據(jù)也是input,如果想看其它序列的話,可以改變FFT_LENGTH的值以及 input里的內(nèi)容,程序輸出的是實部和虛部的模,如果單純想看實部或者虛部的幅度的話,請自行修改程序~
這就是本次淺談FFT以及FFT算法的基本實現(xiàn)的全部內(nèi)容了
參考書籍:數(shù)字信號處理 


(本文摘自21ic論壇,感謝原作者辛苦分享)


    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    国产传媒免费观看视频| 草草草草在线观看视频| 国产精品丝袜一二三区| 久热久热精品视频在线观看| 国产精品福利一级久久| 亚洲中文字幕剧情在线播放| 中文字幕无线码一区欧美| 国产成人亚洲精品青草天美 | 国产在线日韩精品欧美| 久久精品国产99国产免费| 婷婷色香五月综合激激情| 丰满人妻一二区二区三区av| 粉嫩国产美女国产av| 国产黑人一区二区三区| 日本在线高清精品人妻| 麻豆剧果冻传媒一二三区| 欧美整片精品日韩综合| 欧美成人黄色一级视频| 午夜福利直播在线视频| 亚洲av熟女一区二区三区蜜桃| 亚洲一区二区三区免费的视频| 日韩人妻中文字幕精品| 国产精品乱子伦一区二区三区| 亚洲国产天堂av成人在线播放| 国产美女网红精品演绎| 亚洲一区二区三区国产| 熟女一区二区三区国产| 日本一级特黄大片国产| 98精品永久免费视频| 日本黄色美女日本黄色| 亚洲一区在线观看蜜桃| 亚洲av专区在线观看| 麻豆果冻传媒一二三区| 日本少妇三级三级三级| 久久精品福利在线观看| 日韩一区二区三区四区乱码视频| 国产亚洲精品一二三区| 91爽人人爽人人插人人爽| 国产色偷丝袜麻豆亚洲| 亚洲国产精品一区二区毛片| 九九热精彩视频在线播放|