3.1.1 一维傅立叶(1D-FT)与一维离散傅立叶变换(1D-DFT)
信号学中,将沿平面分布的信号称为一维信号(1D Signal),例如音频信号。
一维傅里叶变换,能够将一组满足狄利克雷条件(Dirichlet Theorem)的一维信号分解到周期性复指数波(Complex Exponential Wave)构成的二维向量空间。
从傅里叶级数(FS)到傅里叶变换(FT)
狄利克雷条件 最初被用作傅里叶级数(FS [Fourier Series])在三角函数域上进行分解的充分不必要条件 [2] [7]。在狄利克雷条件描述中,如果选定分析的周期信号 同时满足:
- 【单周期内,连续或存在有限个第一类间断点】;
- 【单周期内,存在有限数目的极大值与极小值】;
- 【单周期内,绝对可积】;
则,此周期信号就一定存在傅里叶三角级数的分解表示。
如果记周期信号函数 s(t) 的波长(周期)为 T ,角频率(角速度)为 T2π 。则以信号函数波长 T 做可变 n∈[0, N] 等分(即步长 Step=N1 )选取分离函数。有分离函数(周期)为 nT ,角频率(角速度)为 ωn=T2πn 。原周期信号函数 s(t) 就可以被分解为:
s(t)an=N1n=0∑Nan⋅cos(T2πnt) + N1n=0∑Nbn⋅sin(T2πnt)=∫−2T+2Ts(t)⋅cos(ωnt) dt bn=∫−2T+2Ts(t)⋅sin(ωnt) dt
如果我们对函数周期进行平移,将区间从 (−2T, +2T) 偏移 +2T ,即变换到 (0, T) ,使原周期信号函数 s(t) 偏移为奇函数(即 s(−t)=−s(t) ),而奇函数式可证明是不需要余弦函数项的。此时,就可以进一步化简 s(t) 为存粹正弦函数表示:
s(t)bn=N1n=0∑Nbn⋅sin(λ2πnt)=N1n=0∑Nbn⋅sin(ωnt)=∫0Ts(t)⋅sin(ωnt) dt
简化表示 ωn 为 ω ,当我们将傅里叶级数从三角函数域,扩展到复变函数域时,基底函数由正余弦函数变为了以 λ=ω2π=nT 为周期(波长)的复指数函数 Sω(t)=eiωt 。信号函数 s(t) 的分解函数就可以表示为:
s(t)s^(ω)=N1n=0∑Ns^(T2πn)⋅eiT2πnt=N1ω=0∑ωNs^(ω)⋅eiωt=N1n=0∑Ns^(ω)⋅Sω(t)=∫0Ts(t)⋅e−iωt dt
根据 欧拉公式(Euler's Formula) 可知 eix=cos(x)+i⋅sin(x) , 带入上式有:
s(t)a^ω=N1n=0∑Na^ω⋅cos(ωt)+i⋅b^ω⋅sin(ωt)=s^(−ω)+s^(ω)b^ω=i1⋅(s^(−ω)−s^(ω))
转换到欧氏空间下的三角函数表示 Sω(t) ,记构成原信号函数 s(t) 的复指数函数 Sω(t) 的初相为 ∠ϕω ,振幅为 Aω ,则:
Sω(t):∠ϕω=arctan(b^ωa^ω)Aω=√(a^ω)2+(b^ω)2
同三角函数域的情况,复变函数域下的傅里叶级数仍然可以进一步精简。我们仍然需要对原函数 s(t) 平移 +2λ 并将周期变换到 (0, λ) ,使 s(t) 表现为奇函数。由于原信号函数 s(t) 必为实函数的特性,会使得 aω 与 bω 互为共轭复数。因此在奇函数条件下, aω 与 bω 表现为符号相反的纯虚数,此时:
a^ωs(t)=1⋅[s^(−ω)+s^(ω)]=0 b^ω=i1⋅[s^(−ω)−s^(ω)]=i2⋅s^(−ω)=N1ω=0∑ωN 0⋅cos(ωt) + i⋅(i2⋅s^(−ω))⋅sin(ωt)= N1n=0∑Ns^(−ω)⋅sin(ωt)
如果我们将 s^(−ω) 的负号划入公式,并将离散级数扩展到原信号函数 s(t) 的连续实数空间上以积分形式表示。则 s(t) 与 s^(−ω) 的关系就展现为:
s(t)s^(ω)=N1∫0Ns^(ω)⋅sin(ωt) dn=∫0Ts(t)⋅sin(−ωt) dt
这就是傅里叶变换的奇函数表达式,也被称为 正弦傅里叶变换(SFT [Sine Fourier Transform])。
同理,如果我们取偶函数,有 aω 与 bω 表现为符号相同的纯实数。即:
a^ωs(t)=1⋅[s^(−ω)+s^(ω)]=2⋅s^(ω) b^ω=i1⋅[s^(−ω)−s^(ω)]=0=N1ω=0∑ωN 2⋅s^(ω)⋅cos(ωt) + i⋅0⋅sin(ωt)= N1n=0∑Ns^(ω)⋅cos(ωt)
采用相同处理,有余 弦傅里叶变换(CFT [Cosine Fourier Transform]) 结果如下:
s(t)s^(ω)=N1∫0Ns^(ω)⋅cos(ωt) dn=∫−2T+2Ts(t)⋅cos(−ωt) dt
然而工程中的信号并不存在有限周期且并不都能判定奇偶性,这是否意味着我们无法对其进行分解和化简?
答案是否定的。首先来看,针对周期性需要进行的操作。
解构一维信号 - 时频分离(Time-Frequency Separation)
如果我们换个角度就会发现,不存在有限周期只不过是因为周期太长,以至函数周期等于信号完整时长或着趋近无穷而导致的。所以我们分解原函数到对应的复指数函数和,所选择基底复指数函数也趋近于无穷,并使其对应频率从 0 到 ∞ 而周期从极大到极小即可。不过在计算上就需要利用傅立叶变化的空间特征了。
结合上文,记被分解的原信号函数为 f(t) 。根据傅立叶基的正交特性,如果存在 F(t) 为当前 f(t) 的解函数空间,则必然有 f(t)⋅Fω−1(t) 内积在时间 t 范围为 (0, ∞) 有固定值 f^(ω) ,使得:
f^(ω)=∫0∞f(t)⋅Fω−1(t) dt=∫0∞f(t)⋅e−iωt dt
以函数空间角度排除 f(t) 周期干扰。而复指数波的波函数,顾名思义就是复指数函数,有:
f^(ω)=∫−∞+∞aω⋅cos(ωt)+i⋅bω⋅sin(ωt) dt
使 bω 可取复数域,就可以转换为:
f^(ω)=∫−∞+∞aω⋅cos(ωt)+bω⋅sin(ωt) dt
由于实际信号并不能严格确定奇偶性,不过对于小于四维的情况下,大多数条件都能保证其本身为实函数(即函数只有实数域取值),因而构成原信号的分离基底函数是存在不同强度和初项的。我们沿用前文中对初相和振幅的定义,记 Fω(t) 初相为 ∠ϕω ,振幅为 Aω ,则有:
Fω(t):∠ϕω=arctan(b^ωa^ω) Aω=√(a^ω)2+(b^ω)2
根据 帕西瓦尔定理(Parseval’s Theorem) 转复数空间,我们会发现 Aω 就是 f^(ω) 取 2 范数后的结果,而初项其实就是 f^(ω) 在 t=0 时,自身相位在复数空间上与实轴的夹角。即:
Fω(t)∠ϕωAω:=∠∣f^(t)∣ =arctan(b^ωa^ω)= ∥f^(t)∥2=√(a^ω)2+(b^ω)2
进而有:
Fω(t)f^(ω)=Aω⋅sin(ωt−∠ϕω)=Aω⋅cos(ωt+∠ϕω)=∥f^(t)∥2⋅sin(ωt−∠∣f^(t)∣)=∥f^(t)∥2⋅cos(ωt+∠∣f^(t)∣)=∫0∞f(t)⋅e−iωt dt ⇔ f(t)=N1∫−∞+∞f^(ω)⋅Fω(t) dω
显然,大部分信号都是有限时间下的,且基本都能满足无穷区间的狄利克雷条件,也因此可以使用傅里叶变换分解。
如果频率范围在 ω∈[ω0, ω1] ,对于选定的时间点 t=tc ,有频率 ω 、原函数 f(t) 在 t=tc 时的取值 f(tc) 、基底函数族 Fω(t) 锁定时间 t=tc 的变体 Ftc(ω) ,构成该频率范围的 频域投影(FDP [Frequency Domain Projection]);
反之,如果时间范围在 t∈[ t0, t1] ,对于频率范围 ω∈[ω0, ω1] ,有时间 t 、原函数 f(t) 、基底函数族 Fω(t),就构成了原函数在该时间范围的 时域投影(TDP [Time Domain Projection])。
两者的区别仅在于观察角度的不同:
Frequency Domain Projection:Time Domain Projection:ω∈[ω0, ωn] ( ω , f(tc) , Ftc(ω) ) ( t , f(t) , Fω(t) ) t ∈[ t0, tn ]
周期的问题解决了,现在我们能够拿到时频分离(Time-Frequency Separation)的原信号函数信息并可以依此还原信号本身。但积分对于计算机来说任务有些繁重。同时,由于计算机只能处理离散化后的数字信号,因此离散化的算法才能够被计算机有效使用。
所以还需要在此基础上,找到更为便捷的算法实现。
精简运算过程 - 一维离散傅立叶变换(1D-DFT)
如果将积分重新转换为级数形式积化和差表示,并在允许误差范围内取有限子集。那么就能够化解掉大部分运算量,从而得到一个相对理论而言的低时间复杂度算法。这种想法促成了基于计算机运算的一维离散傅立叶(1D-DFT)的诞生。
一维离散傅立叶(1D-DFT [1D-Discrete Fourier Transform])本质上包含了两部分离散化作业,即对时域的离散化(TDD [Time Domain Discrete])和对频域的离散化(FDD [Frequency Domain Discrete])。
时域离散化(TDD) 方面,一维离散傅立叶采用了离散时间傅立叶变化(DTFT [Discrete Time Fourier Transform])中,对时域信号间隔采样的操作。即将:
f^(ω)=∫0∞f(t)⋅e−iωt dt
以时间采样(切片)数量为 n1 ,转为级数形式:
f^(ω)=t=t0∑tn1f(t)⋅e−iωt
打破时间上的连续性。需要注意的是,此时频域仍然是连续的。
频域离散化(FDD) 方面,离散傅立叶做的操作就更为直观了。如果在频率采样时就以离散化的方式采样数据,那得到的频域信息天然就是离散的。同样,从某个时刻 t=tc 离散化的频域信息上还原当前实际频率,则也是一个线性求和的过程。因此有:
f(t)=N1∫−∞+∞f^(ω)⋅Fω(t) dω
以频率采样(切片)数量为 n2 ,转为级数形式:
f(t)=n21ω=ω0∑ωn2f^(ω)⋅Fω(t)
而随着有限采样,基底函数族 Fω(t)$ 构成的解函数空间也是有限维的,即:
Fω=[Fω1,Fω2, ... ,Fωn2]
至此,由时域离散化(TDD)与频域离散化(FDD)共同构成离散傅立叶(DFT)的完整表达如下所示:
Fω=[Fω1,f^(ω)=t=t0∑tn1f(t)⋅e−iωt Fω2, ... ,Fωn2]⇔ f(t)=n21ω=ω0∑ωn2f^(ω)⋅Fω(t)
经过离散化后的有限采样更适合计算机有限的算力,因此才能被程序化。不过由于并没有保存连续的完整信息,经过离散傅里叶变换后再还原的数据,相对于采样自然源的原始数据终归还是会有一定损失的。但是由于变换与逆变换,并不会导致解构再还原后的数据存在差异。所以离散傅里叶变换被归类为 有损采样(Lossy Sampling)的无损算法(Lossless Algorithm)。
一维离散傅立叶变换(1D-DFT)的 C 语言实现
既然需要做程序化,那么首先需要将离散傅里叶变换的过程抽象化。理清逻辑思路的同时,方便构造迭代器和代码的处理流水线。这里我们使用伪码表示:
同时,我们还需要提供离散傅里叶变换的逆变换(IDFT [Inverse Discrete Fourier Transform])来使得电脑能够还原信息:
现在思路有了,只需要以代码实现即可:
#include "stdio.h"
#include "math.h"
#define PI 3.1415926f
typedef struct FBasis {
double re_;
double im_;
double w_;
} FBasis;
void dft_1d(double *Fo, FBasis *Fn, size_t T, size_t N) {
for (int n = 0; n < N; ++n) {
double An = 0;
double Bn = 0;
double Wn = (2 * PI * n) / T;
for (int t = 0; t < T; ++t) {
An += cos(Wn * t) * Fo[t];
Bn += sin(Wn * t) * Fo[t];
}
FBasis e_ = {An, Bn, Wn};
Fn[n] = e_;
}
}
void idft_1d(double *Fo, FBasis *Fn, size_t T, size_t N) {
for (int t = 0; t < T; ++t) {
for (int n = 0; n < N; ++n) {
FBasis e_ = Fn[n];
Fo[t] += (e_.re_ * cos(e_.w_ * t) + e_.im_ * sin(e_.w_ * t)) / N;
}
}
}
写完后简单测试一下:
int main(void) {
FBasis Fn[6] = {};
double Fo[6] = {1, 2, 3, 4, 5, 6};
double iFo[6] = {};
size_t T = sizeof(Fo) / sizeof(double);
size_t N = sizeof(Fn) / sizeof(FBasis);
printf("\n Original_data: \n");
for (int t = 0; t < T; ++t) {
printf("%f ", Fo[t]);
}
printf("\n DFT_result: \n");
dft_1d(Fo, Fn, T, N);
for (int n = 0; n < N; ++n) {
printf("%f + i %f \n", Fn[n].re_, Fn[n].im_);
}
printf("\n IDFT_result: \n");
idft_1d(iFo, Fn, T, N);
for (int t = 0; t < T; ++t) {
printf("%f ", iFo[t]);
}
return 0;
}
得到结果和标准几近相同:
Original data:
1.000000 2.000000 3.000000 4.000000 5.000000 6.000000
DFT_result:
21.000000 + i 0.000000
-3.000003 + i -5.196152
-3.000002 + i -1.732048
-3.000000 + i -0.000002
-2.999996 + i 1.732057
-2.999979 + i 5.196158
IDFT_result:
1.000003 2.000000 2.999999 3.999999 4.999999 6.000000
运行结束。
到这里,我们已经基本掌握了傅里叶变换原理和最基础的应用。
如果拓展傅里叶变换到相对复杂的二维情况,那么和一维时有哪些不同呢?