<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>
"); //-->

博客專(zhuān)欄

EEPW首頁(yè) > 博客 > 淺談FFT以及FFT算法的基本實(shí)現

淺談FFT以及FFT算法的基本實(shí)現

發(fā)布人:凋葉棕 時(shí)間:2018-12-14 來(lái)源:工程師 發(fā)布文章

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

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

 

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

1.png

2.png

其中:3.png


但是FFT之所以稱(chēng)之為快速傅里葉變換,就利用了以下的幾個(gè)性質(zhì)(重中之重?。?/span>

周期性:4.png


對稱(chēng)性:5.png


可約性:6.png



先把這仨公式放到這,接下來(lái)會(huì )用到。

 

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

 

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

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

x(n)按照奇偶分組


x(2r)=X1(r)

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

那么將DFT也分為兩組來(lái)預算

8.png


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


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

回顧一下:

9.png


那么這個(gè)式子就可以化為:

10.png


則X1和X2都是長(cháng)度為N/2的序列x1和x2N/2點(diǎn)的離散傅里葉變換

                   其中:


11.png

至此,一個(gè)N點(diǎn)的DFT就被分解為2個(gè)N/2DFT。但是只有N/2個(gè)點(diǎn),也就是說(shuō)式(1-1)只是DFT的前半部分。要求DFT的后半部分可以利用其對稱(chēng)性求出后半部分為:

12.png

(1-2)


那么式(1-1)和(1-2)就可以專(zhuān)用一個(gè)蝶形信號流圖符號來(lái)表示。如圖:

tgawertgfawet.png

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

dft.png

那么,通過(guò)這樣的分解,每一個(gè)N/2點(diǎn)DFT只需(N^2)/4次復數相乘計算,明顯的節省了運算量。

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

fft.png

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

 

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

 

 

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


/*
    時(shí)間:2018年11月24日
    功能:將input里的數據進(jìn)行快速傅里葉變換  并且輸出
*/
 
#include <stdio.h>
#include <math.h>
#define PI 3.1415926
#define FFT_LENGTH      8
double input[FFT_LENGTH]={1,1,1,1,1,1,1,1};
struct complex1{        //定義一個(gè)復數結構體
    double real;    //實(shí)部
    double image;   //虛部
};  
//將input的實(shí)數結果存放為復數
struct complex1 result_dat[8];
/*
    虛數的乘法
*/
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;
}
 
/*
    簡(jiǎn)單的a的b次方
*/
int mypow(int a,int b){
    int i,sum=a;
    if(b==0)return 1;
    for(i=1;i<b;i++){
        sum*=a; 
    }
    return sum;
}
/*
    簡(jiǎn)單的求以2為底的正整數
*/
int log2(int n){
    unsigned i=1;
    int sum=1;
    for(i;;i++){
        sum*=2;
        if(sum>=n)break;
    }
    return i;
}
/*
    簡(jiǎn)單的交換數據的函數
*/
void swap(struct complex1 *a,struct complex1 *b){
    struct complex1 temp;
    temp=*a;
    *a=*b;
    *b=temp;
}
/*
    dat為輸入數據的數組
    N為抽樣次數  也代表周期  必須是2^N次方
*/
void fft(struct complex1 dat[],unsigned char N){    
    /*最終  dat_buf計算出 當前蝶形運算奇數項與W  乘積
            dat_org存放上一個(gè)偶數項的值
    */
    struct complex1 dat_buf,dat_org;
    /*  L為幾級蝶形運算    也代表了2進(jìn)制的位數
        n為當前級蝶形的需要次數  n最初為N/2 每級蝶形運算后都要/2
        i j為倒位時(shí)要用到的自增符號  同時(shí)  i也用到了L碟級數   j是計算當前碟級的計算次數 
        re_i i_copy均是倒位時(shí)用到的變量
        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)過(guò)觀(guān)察,發(fā)現每級蝶形運算需要N/2次運算,共運算N/2*log2N  次  
    unsigned char fft_counter=0;
    //在此要進(jìn)行補2   N必須是2^n   在此略
    //蝶形級數  (L級)
    L=log2(N);  
    //計算每級蝶形計算的次數(這里只是一個(gè)初始值)  之后每次要/2
    //n=N/2;
 
    //對dat的順序進(jìn)行倒位
    for(i=1;i<N/2;i++){
        i_copy=i;
        re_i=0;
        for(j=L-1;j>0;j--){
            //判斷i的副本最低位的數字  并且移動(dòng)到最高位  次高位  ..
            //re_i為交換的數   每次它的數字是不能移動(dòng)的 并且循環(huán)之后要清0
            re_i|=((i_copy&0x01)<<j);       
            i_copy>>=1;
        }
        swap(&dat[i],&dat[re_i]);
    }
    //進(jìn)行fft計算
    for(i=0;i<L;i++){
        
        fft_flag=1;
        fft_counter=0;
        for(j=0;j<N;j++){
            if(fft_counter==mypow(2,i)){        //控制隔幾次,運算幾次,
                fft_flag=0;
            }else if(fft_counter==0){       //休止結束,繼續運算
                fft_flag=1;
            }
            //當不判斷這個(gè)語(yǔ)句的時(shí)候  fft_flag保持  這樣就可以持續運算了
            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);
                                                //計算 當前蝶形運算奇數項與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;       
                                                    //實(shí)部加實(shí)部   虛部加虛部
 
                dat[j+mypow(2,i)].real=dat_org.real-dat_buf.real;
                dat[j+mypow(2,i)].image=dat_org.image-dat_buf.image;
                                                    //實(shí)部減實(shí)部 虛部減虛部
 
                k++;
                fft_counter++;
            }else{
                fft_counter--;              //運算幾次,就休止幾次
                k=0;
            }
        }
    }
 
    
}
void main(){
    int i;
    //先將輸入信號轉換成復數
    for(i=0;i<FFT_LENGTH;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<FFT_LENGTH;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);
}

這就是本次淺談FFT以及FFT算法的基本實(shí)現的全部?jì)热萘?/span>



原創(chuàng )內容轉載其注明原作者.  



參考書(shū)籍:數字信號處理




*博客內容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀(guān)點(diǎn),如有侵權請聯(lián)系工作人員刪除。



關(guān)鍵詞:

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>