原文地址: 作者:
初解
谨以此文纪念过往的岁月
一.前言
首先申明俺不是一个算法工程师,俺是一个底层驱动工程师,有人会发问一个底层驱动工程师需要这个吗?但是我不幸的告诉你,确实是需要的,不过我们不要像算法工程师那样搞得很精通,但是还是需要去了解这是个什么东西。说实话,这个东西在大学时候学过,还好好的去理解了一样,不过到现在忘的差不多了,这愈发的让我明白一句话,好记性不如烂笔头,如果以前有好好记录的好习惯,那现在只要把以前的东西拿出来看看再印证一下就可以了。不过历史不可以如果。为了不让明天继续懊悔今天,在这里记录下本人学习的一些记录。
二.FFT的数学基础
FFT是什么,额,说白了就是一种时域和频域变换的手段。什么是时域什么是频域,额,你google吧,鄙视百度!
这里说明一下,相比较FFT而言,我们更加关心离散傅里叶变换(DTF),因为我们底层驱动工程师面对的是一个一个离散的数据。也许大家还是对FFT的变换的概念还不理解,那我们举个例子来说吧:
1 AD在一个1Hz正弦波周期采集了1024个数据(即你的采样频率为1024Hz),从时域上看1s内采集了1024数据,那1024个数据做1024点的FFT变换。我们以数据的下标作为横轴,而数据的值作为纵轴,你会发现第一个点的值最大,我们可以从该值计算出我们被采样的频率为1Hz,值是这样算出来的1024(Hz)/1024*1 = 1Hz,如果最大的值点是第8个点,那被采样频率是8Hz。不过要满足乃奎斯定律,即采样频率大于被采样频率的两倍。
为什么会有东西出现呢,主要是时域的信号好采样,至于频域的信号难以采样,于是傅里叶这个天才就发明了这个东东,不过这可是现代数字信号处理的基础。
闲话不说了,还是来看DFT的数学基础(下面的内容不少拷贝wiki的):
对于复数序列
,离散傅里叶变换公式为:
下面,我们用N次单位根WN来表示。
WN的性质:
- 周期性,WN具有周期N。
- 对称性:。
-
为了简单起见,我们下面设待变换序列长度n = 2r。 根据上面单位根的对称性,求级数时,可以将求和区间分为两部分:
Fodd(k) 和 Feven(k)是两个分别关于序列奇数号和偶数号序列N/2点变换。由此式只能计算出yk的前N/2个点,对于后N/2个点,注意 Fodd(k) 和 Feven(k) 都是周期为N/2的函数,由单位根的对称性,于是有以下变换公式:
-
- 。
这样,一个N点变换就分解成了两个N/2点变换。照这样可继续分解下去。这就是库利-图基快速傅里叶变换算法的基本原理。根据不难分析出此时算法的时间复杂度为O(NlogN)
三.8点快速傅里叶变换
主要是蝶形算法
FFT算法图
__no_init float fft_r[FFT_N]; //FFT输入实数序列,保存变换后的频域实部 __no_init float fft_i[FFT_N]; //保存变换后的频域虚部 __no_init float harm[HARM_M+1]; //各次谐波幅值 const Uint8 FFT_BIT[8]={ //供完成倒位序排列使用的位定义 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
- void fft(void)
- {
- Uint16 i,j,k,L;
- Uint16 b,c,p,n;
- Uint8 x,xc;
- float Gr,Gi,Wr,Wi; for(i=0;i<FFT_N;i++)
- {
- x =i;
- xc=0;
- for(j=0;j<FFT_M;j++)
- {
- if((x&0x01)!=0)
- {
- xc|=FFT_BIT[(FFT_M-1)-j];
- }
- x>>=1;
- }
- fft_i[xc]=fft_r[i];
- }
- for(i=0;i<FFT_N;i++)
- {
- fft_r[i]=fft_i[i];
- fft_i[i]=0;
- }
-
- //以上是倒叙就是将数组中的数据进行倒叙,即将数组序号倒序,如FFT_BIT[5]存储到FFT_BIT[3],为什么呢?5 =0x110,进行二进制倒转变为0x011,变为3.
-
- for(L=1;L<=FFT_M;L++)
- {
- b=1;
- p=FFT_N>>1;
- for(i=0;i<L-1;i++)
- {
- b<<=1; //b=2^(L-1)
- p>>=1; //p=N/(2^L)
- }
- c=b<<1; //c=b*2
- for(j=0;j<b;j++)
- {
- n=p*j;
- for(k=j;k<FFT_N;k+=c)
- {
- Gr = fft_r[k];
- Gi = fft_i[k];
- Wr = Sinf[n+FFT_N/4]*fft_r[k+b] + Sinf[n]*fft_i[k+b];
- Wi = Sinf[n+FFT_N/4]*fft_i[k+b] - Sinf[n]*fft_r[k+b];
- fft_r[k] = Gr + Wr;
- fft_i[k] = Gi + Wi;
- fft_r[k+b] = Gr - Wr;
- fft_i[k+b] = Gi - Wi;
- }
- }
- }
- }
//以上循环分为三个一个根据变换点的阶数循环,即上图中level1->level2->level3。第二个循环是group步进,level1为2 level 2为4。 第三个循环是需要两两相乘数据之间的,如level1的length为1,level2的length为2,level3的length为4。
关于旋转因子,就是cos和sin的值,不过这两者之间是可以互相转换的。
也许到此大家对于快速傅里叶有了大概的理解,下面数C++的code,也是源于网上。这段code更好的描述的FFT的蝶形算法,值得参考。
- void FFT(unsigned long & ulN, vector<complex<double> >& vecList)
-
- {
-
- //得到幂数
-
- unsigned long ulPower = 0; //幂数
-
- unsigned long ulN1 = ulN - 1;
-
- while(ulN1 > 0)
-
- {
-
- ulPower++;
-
- ulN1 /= 2;
-
- }
-
- //反序
-
- bitset<sizeof(unsigned long) * 8> bsIndex; //二进制容器
-
- unsigned long ulIndex; //反转后的序号
-
- unsigned long ulK;
-
- for(unsigned long p = 0; p < ulN; p++)
-
- {
-
- ulIndex = 0;
-
- ulK = 1;
-
- bsIndex = bitset<sizeof(unsigned long) * 8>(p);
-
- for(unsigned long j = 0; j < ulPower; j++)
-
- {
-
- ulIndex += bsIndex.test(ulPower - j - 1) ? ulK : 0;
-
- ulK *= 2;
-
- }
-
-
-
- if(ulIndex > p)
-
- {
-
- complex<double> c = vecList[p];
-
- vecList[p] = vecList[ulIndex];
-
- vecList[ulIndex] = c;
-
- }
-
- }
-
- //计算旋转因子
-
- vector<complex<double> > vecW;
-
- for(unsigned long i = 0; i < ulN / 2; i++)
-
- {
-
- vecW.push_back(complex<double>(cos(2 * i * PI / ulN) , -1 * sin(2 * i * PI / ulN)));
-
- }
-
-
-
- for(unsigned long m = 0; m < ulN / 2; m++)
-
- {
-
- cout<< "\nvW[" << m << "]=" << vecW[m];
-
- }
-
-
-
- //计算FFT
-
- unsigned long ulGroupLength = 1; //段的长度
-
- unsigned long ulHalfLength = 0; //段长度的一半
-
- unsigned long ulGroupCount = 0; //段的数量
-
- complex<double> cw; //WH(x)
-
- complex<double> c1; //G(x) + WH(x)
-
- complex<double> c2; //G(x) - WH(x)
-
- for(unsigned long b = 0; b < ulPower; b++)
-
- {
-
- ulHalfLength = ulGroupLength;
-
- ulGroupLength *= 2;
-
- for(unsigned long j = 0; j < ulN; j += ulGroupLength)
-
- {
-
- for(unsigned long k = 0; k < ulHalfLength; k++)
-
- {
-
- cw = vecW[k * ulN / ulGroupLength] * vecList[j + k + ulHalfLength];
-
- c1 = vecList[j + k] + cw;
-
- c2 = vecList[j + k] - cw;
-
- vecList[j + k] = c1;
-
- vecList[j + k + ulHalfLength] = c2;
-
- }
-
- }
-
- }
-
- }
四.总结
FFT数字信号处理的重中之重。