原文地址:FFT初解
FFT初解
谨以此文纪念过往的岁月
一.前言
首先申明俺不是一个算法工程师,俺是一个底层驱动工程师,有人会发问一个底层驱动工程师需要这个吗?但是我不幸的告诉你,确实是需要的,不过我们不要像算法工程师那样搞得很精通,但是还是需要去了解这是个什么东西。说实话,这个东西在大学时候学过,还好好的去理解了一样,不过到现在忘的差不多了,这愈发的让我明白一句话,好记性不如烂笔头,如果以前有好好记录的好习惯,那现在只要把以前的东西拿出来看看再印证一下就可以了。不过历史不可以如果。为了不让明天继续懊悔今天,在这里记录下本人学习的一些记录。
二.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的):
对于复数序列![FFT初解(转) x_{0},. x_{1},. ...,. x_{n-1}](https://i-blog.csdnimg.cn/blog_migrate/a4c26d1e5885305701be709a3d33442f.gif)
下面,我们用N次单位根WN来表示。
WN的性质:
- 周期性,WN具有周期N。
- 对称性:
。
-
为了简单起见,我们下面设待变换序列长度n 时,可以将求和区间分为两部分:
Fodd(k) 奇数号和偶数号序列N/2点变换。由此式只能计算出yk的前N/2个点,对于后N/2个点,注意
-
-
。
这样,一个N点变换就分解成了两个N/2点变换。照这样可继续分解下去。这就是库利-图基快速傅里叶变换算法的基本原理。根据主定理不难分析出此时算法的时间复杂度为O(NlogN)
三.8点快速傅里叶变换
主要是蝶形算法
FFT算法图
![FFT初解(转) FFT初解(转)](https://i-blog.csdnimg.cn/blog_migrate/a4c26d1e5885305701be709a3d33442f.gif)
__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
关于旋转因子,就是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数字信号处理的重中之重。