(本人为本文原作者,转载请标明出处)
上文请转:
DBinary:傅里叶变换推导详解zhuanlan.zhihu.com其中,式3.51及3.52被称为傅里叶变换对,其中3.52为正变换,表示离散时间信号变换为频域复信号,式3.51位逆变换,表示由频域复信号还原为离散时间信号,在本文之后的论述中, 将有限长度且离散的有限长离散时域傅里叶变换的分析公式统一简称为离散傅里叶变换(Discrete Fourier Transform)或者其英文缩写DFT,将它的逆变换(Inverse Discrete Fourier Transform)也就是逆变换公式简称为IDFT[3]。
第一节 采样
自然信号若要保留为数字离散信号,首先要做的就是对该信号进行采样,一般情况下,使用的采样方式都是等间距采样,即为样本两两之间的采样间隔相同,在数学表达上,常常使用单位冲击采样来表示这一过程。现定义单位冲击函数
![4469832cf38c5b1c66ba578abb49bd10.png](https://i-blog.csdnimg.cn/blog_migrate/4556f52cb4a1e72109dff0ad427405c7.jpeg)
在实际的使用中,常常定义
![fd0c51bb34285a1063fa30d4d272d50f.png](https://i-blog.csdnimg.cn/blog_migrate/1960b17c7b4682a147f668f2f52b9d5d.jpeg)
假设一连续时间信号函数
因此假设我们需要对一个信号多点进行采样,可以使用式4.2的方式进行表达:
在《信号与系统》[2] 第7.1.1章节推论如下,设对一信号以T间隔取样,那么就有如下数学式
根据卷积性质时域内的相乘对应于频域内的卷积,其中
根据公式4.7,4.8:
可得出:
其中
因此可以得出一个结论,信号的采样频率至少需要该信号最高频率的2倍,否则无法还原出原信号导致失真。
同时依据第二章第六节的推论,有DFT正变换公式如下:
其中,N表示信号的采样点数,n的范围是0到
因此得出另一个结论,当对一个信号以频率
第二节 DFT共轭对称性
若某一函数在坐标系上以x=N轴对称,那么其可以写成式4.10的形式
如图4-2-1,其中的函数
![aed29e0232b2ca65a28cc9a0e2b74698.png](https://i-blog.csdnimg.cn/blog_migrate/fa44b5088b0cf657d454adf6679d790d.jpeg)
在复数中,两个实部相等,虚部互为相反数的复数互为共轭复数(conjugate complex number),假设一复数z,则其共轭写作
则其共轭为
现定义一个离散序列x[n]若x[n]满足式4.13
则称x[n]为共轭对称序列。
假设:
依据欧拉公式,可以得出
那么,上式可根据4.8写成
设
由式4.17可知,傅里叶变换结果在
也即说明变换的第一个结果为
第三节 DFT应用
离散傅里叶的正变换的结果为离散复数序列,设某一结果为
在实际的使用中,常常使用
DFT在声学的分析中被广泛的使用,WavFreq是一款能够加载Wav音乐(波形文件)并进行解析的软件。下图显示的是该软件对一段音乐进行分析的频谱图:
![f87c07afc784ab61accc785e313995cf.png](https://i-blog.csdnimg.cn/blog_migrate/f9b38c7d5d9c75dfa7af7cad24556deb.jpeg)
其中,该音频信号的采样频率为44100HZ,每个样本以16位短整形记录,这也就意味着声波信号被量化在范围[-32768,32767]内,从图中的频谱图中可以看出,大部分能量较高的频率被集中在了低频当中,在正常的音乐中,人声大多被集中在了低频范围。女声的频率也会相对的比男声的频率高出一些。
第四章 使用C语言实现DFT、IDFT代码
第一节 C语言与环境
本文所述的程序部分,全部都使用C语言来完成,同时,本文使用开发环境为windows 10 64位系统,集成开发环境使用微软官方发布的Visual Studio 2010(编译器MSVC 2010),C语言依据C99标准编写。因此,程序运行需要安装VC++2010的支持库。
为了保证代码的可移植性,程序部分仅使用C语言提供的数学标准库math.h,该标准库在众多的编译器中都被支持。
第二节 使用C语言编写DFT代码
因为涉及到复数运算,因此,本文定义一个结构体来描述一个复数
typedef
其中该结构体名为complex,当中有两个float类型的成员分别代表复数的实部和虚部,其中re意味really,表示复数的实部,im表示Imaginary表示虚部。同时,实现三个函数用于对虚部进行运算。
complex complexBuild(float re,float im)
{
complex cx;
cx.re=re;
cx.im=im;
return cx;
}
其中 complex complexBuild(float re,float im);用于构建并初始化一个复数结构体,re表示构建复数的实部,im表示构建复数的虚部
complex
complex complexAdd(complex a,complex b)用于实现两个复数的相加运算,即虚部加虚部,实部加实部.
complex complexMult(complex a,complex b) { complex ret; ret.re=a.re*b.re-a.im*b.im; ret.im=a.im*b.re+a.re*b.im; return ret; }
complex complexMult(complex a,complex b)用于实现两个复数的乘法运算.依据公式
根据第一章节第六节的推导公式3.52
根据欧拉公式上式可以写成
那么,DFT的公式就可以写成
#include
其中x[]表示输入的复信号序列,实信号可以当做虚数为0的复数信号进行输入,X[]表示输出的复数序列,其分配的内存大小至少应该为输入序列的大小,N为输入序列的复信号个数。
在项目 DFT_DEMO中,对一个长度为10的序列进行DFT变换,其中,该序列为1,2,3,4,5,6,7,8,9,10十个数,如图4-2-1
![3ab4ae07c0eb023f460050a96b2a1482.png](https://i-blog.csdnimg.cn/blog_migrate/84af6864e554e325e838e6ca67c43378.jpeg)
代码如下
int
将上图结果与matlab中的DFT变换结果对比计算结果一致,可知代码编写正确。
第三节 使用C语言编写IDFT代码
根据第六节傅里叶变换对的逆变换公式3.46
可得到适合代码编写的公式5.2
通过第三节的准备工作,可以直接套用第三节给出的结构体声明,直接在源代码当中写出逆变换的代码。
定义IDFT变换函数为
void IDFT(complex x[],complex X[],int N);
实现代码如下所示
void
其中X[]表示输入的复信号序列,x[]表示输出的信号序列,N表示输入复信号序列的长度,如果逆变换编写正确,那么将第二节的输出作为逆变换的输入,就可以得到正变换前的离散信号序列,为了验证这一结果,将main函数更改如下:
int
运行结果如图4-3-1
![7a3265a783eb4a93a0f2b64ab56b27af.png](https://i-blog.csdnimg.cn/blog_migrate/dfffa8d12df39271a4f17fca72e85cda.jpeg)
从结果来看IDFT的程序成功将DFT变换的结果还原成原信号,逆变换的结果有极小的偏差,这是因为计算过程中浮点运算存在极小的精度损失,导致变换的结果有极小的误差。