本文地址https://www.cnblogs.com/jacob1934/p/10478257.html
DFT(离散傅立叶变换)的原理:
要比较两组长度相同的数据的相似性,只需要将两组数据点乘,再求和就行了。
假设两组数据分别为a[N-1:0]和b[N-1:0],他们的相似性(记为函数r(a,b,N)吧)为
r(a,b,N)=a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + ......+ a[N-2]*b[N-2] + a[N-1]*b[N-1];
如果输入信号为一维数组data,其长度为N,采样率fs=480K。想要求得data的频谱分布,频谱精度为fs/M,也就是输入信号与频率为0*fs/M,1*fs/M,2*fs/M,3*fs/M ...... (M-2)*fs/M,(M-1)*fs/M的波形的相似度。自然是生成长度为N的M组波形,分别与data点乘求和。
那么,测试波形从什么相位开始呢?输入信号中的各个频率也是有相位的,万一相位错了,岂不是都抵消了?!
测试波形干脆用互相正交的cos(x)和sin(x)不就好了!每组抵消部分都能在另一组上体现出来,那怎么算最终结果呢?
cos和sin,那不就是水平轴和垂直轴的么!!
那么,输入信号在该频率上的幅度就是cos和sin的平方根,arctan(sin/cos)就是输入信号的相位了。
等等,有点眼熟,这不就是复数么?
W(k) = cos(k) + sin(k)*i;
一般的,测试波形的相位从0开始,并逐点减少(顺时针旋转),这样就能得到正增长的频谱。而得到的相位和待测波形中sin分量相差90°,或者说与cos分量对齐。
上面说输入信号的各个频率分量都是有相位的,可sin函数值关于0,PI/4,PI/2,PI*3/4都是对称的。我怎么知道,它是相位增加产生的,还是相位减少产生的?
如果输入信号也是由两组互相正交的sin和cos组成的呢?sin(1/8pi)和sin(3/8pi)值相等,cos(1/8pi)和cos(3/8pi)可不相等。
哪来的相位减少的情况?正交下变频中,输入的实数(一维)信号,分别和互相正交(还是cos和sin)的两组本振相乘,得到互相正交的两组信号I和Q。输入信号中频率高于本振的部分被搬移至0~+∞,输入信号的频率低于本振的部分被搬移至-∞~0。这些“负频率”,就是相位减少的。
data也变成复数了,怎么办?还是点乘求和,只不过,是复数和复数的点乘求和。
实数信号能直接和复数测试波形比较么?能啊。实数不正是虚部为0的复数么?不过使用实数信号,频谱将会关于频率0对称,也关于频率fs/2对称。


fs = 480e3; %采样率
N = 2048; %信号长度
M = 2048; %求的DFT点数
%生成待测波形
freq_list=[15e3,-22.5e3,-25e3]; %频率低于本振的信号在正交下变频后,将表现为【负频率】信号
amp_list=[1,0.5,0.5];
src_sin_t=[];
src_cos_t=[];
for i=0:N - 1
src_sin_t(i + 1)=0;
src_cos_t(i + 1)=0;
for j=1:length(freq_list)
freq = freq_list(j);
amp = amp_list(j);
if(freq > 0)
k=i;
else
k=0-i;
end
src_sin_t(i + 1) = src_sin_t(i + 1) + amp*sin(2*pi*abs(freq)*k/fs);
src_cos_t(i + 1) = src_cos_t(i + 1) + amp*cos(2*pi*abs(freq)*k/fs);
end
data(i + 1) = complex(src_cos_t(i + 1),src_sin_t(i + 1)); %复数信号
%data(i + 1) = complex(src_cos_t(i + 1),0); %实数信号
end
figure(1);
subplot(2,1,1);
plot(src_sin_t);
grid on;
subplot(2,1,2);
plot(src_cos_t);
grid on;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%对测试波形求DFT
r_dft=[];
w=[];
%freq=0;
phase_inc=0;
for i=1:M
phase=0;
%生成测试波形
for k=1:N
radian = 2*pi*phase;
%w(i,k) = complex(cos(radian),sin(radian));
w(i,k) = exp(1j*radian); %复指数形式
%phase=phase - freq/fs;
phase = phase - phase_inc;
end
%freq=freq + fs/M;
phase_inc = phase_inc + 1/M;
%比较待测波形与测试波形
r_dft(i) = 0;
for k = 1:N
r_dft(i) = r_dft(i) + data(k) * w(i,k);
end
r_dft(i) = r_dft(i)/N;
end
%显示
x=[];
q=[];
for i=1:M
if(i <= M/2)
q(i + M/2) = r_dft(i);
else
q(i - M/2) = r_dft(i);
end
%x轴
x(i)=(i - 1 - M/2)*fs/M/1000;
end
figure(2);
subplot(2,1,1);
plot(x,abs(q));
grid on;
subplot(2,1,2);
plot(x,atan2(real(q),imag(q))/pi*180);
grid on;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
上面的matlab代码中,M个频率点,每个频率点都要做N个复数乘加运算。
对于频率(k/M*fs),k是第几个频率点,M是频率点的总数,fs是输入信号的采样频率。测试波形的第i个点的波形值是
freq=k/M*fs;
sin_t=sin(2*pi*i*freq/fs);
cos_t=cos(2*pi*i*freq/fs);
约掉fs
sin_t=sin(2*pi*i*k/M);
cos_t=cos(2*pi*i*k/M);
写成复指数形式,exp(-1j*radian)是matlab的语法,就是辐角=radian,模值=1的复数。
w(k,M,i)=exp(-1j*2*pi*i*k/M);
共M个频率点,待测波形长度为N,第k个频率点,是这样运算的
DFT(k,M,N)=
data(0)*exp(-1j*0*k/M*2*pi) +
data(1)*exp(-1j*1*k/M*2*pi)+
data(2)*exp(-1j*2*k/M*2*pi)+
data(3)*exp(-1j*3*k/M*2*pi)+
data(4)*exp(-1j*4*k/M*2*pi)+
data(5)*exp(-1j*5*k/M*2*pi)+
......+
data(N-1)*exp(-1j*(N-1)*k/M*2*pi);
根据【复数相乘,辐角相加,模值相乘】,可以写成这样:
DFT(k,M,N)=
data(0)*exp(-1j*0*k/M*2*pi) + data(1)*exp(-1j*0*k/M*2*pi)*exp(-1j*1*k/M*2*pi) +
data(2)*exp(-1j*2*k/M*2*pi) + data(3)*exp(-1j*2*k/M*2*pi)*exp(-1j*1*k/M*2*pi) +
data(4)*exp(-1j*4*k/M*2*pi) + data(5)*exp(-1j*4*k/M*2*pi)*exp(-1j*1*k/M*2*pi) +
......
+ data(N-2)*exp(-1j*(N-2)*k/M*2*pi) + data(N-1)*exp(-1j*(N-2)*k/M*2*pi)*exp(-1j*1*k/M*2*pi);
为了方便,取M=N=8,提取公共项
DFT(k,8,8)=
(
data(0)*exp(-1j*0*k/8*2*pi) +
data(2)*exp(-1j*2*k/8*2*pi) + // 2/8=1/4,约掉
data(4)*exp(-1j*4*k/8*2*pi) +
data(6)*exp(-1j*6*k/8*2*pi)
)
+
(
data(1)*exp(-1j*0*k/8*2*pi) +
data(3)*exp(-1j*2*k/8*2*pi) +
data(5)*exp(-1j*4*k/8*2*pi) +
data(7)*exp(-1j*6*k/8*2*pi)
)*exp(-1j*1*k/8*2*pi) // 当k>=4时,这里能提一个exp(-1j*1*4/8*2*pi),也就是旋转半个圆周,等于改变符号
=
(
data(0)*exp(-1j*0*k/4*2*pi) +
data(2)*exp(-1j*1*k/4*2*pi) +
data(4)*exp(-1j*2*k/4*2*pi) +
data(6)*exp(-1j*3*k/4*2*pi)
)
+
(
data(1)*exp(-1j*0*k/4*2*pi)+
data(3)*exp(-1j*1*k/4*2*pi)+
data(5)*exp(-1j*2*k/4*2*pi)+
data(7)*exp(-1j*3*k/4*2*pi)
)*exp(-1j*1*k/8*2*pi)
= // 继续分解
(
(
data(0) + // exp(-1j*0*k/2*2*pi),等于没乘,直接省略。
data(4)*exp(-1j*1*k/2*2*pi)
)
+
(
data(2) +
data(6)*exp(-1j*1*k/2*2*pi)
)*exp(-1j*1*k/4*2*pi) // k>=4时,这里能提一个exp(-1j*1*4/4*2*pi),旋转一个圆周,等于没旋转
)
+
(
(
data(1)+
data(5)*exp(-1j*1*k/2*2*pi)
)
+
(
data(3)+
data(7)*exp(-1j*1*k/2*2*pi)
)*exp(-1j*1*k/4*2*pi)
)*exp(-1j*1*k/8*2*pi)
代入k,


DFT(0,8,8)
=
(
(
data(0) +
data(4)*exp(-1j*1*0/2*2*pi)
)
+
(
data(2) +
data(6)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*0/4*2*pi)
)
+
(
(
data(1)+
data(5)*exp(-1j*1*0/2*2*pi)
)
+
(
data(3)+
data(7)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*0/4*2*pi)
)*exp(-1j*1*0/8*2*pi)


DFT(1,8,8)
=
(
(
data(0) -
data(4)*exp(-1j*1*0/2*2*pi)
)
+
(
data(2) -
data(6)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*1/4*2*pi)
)
+
(
(
data(1) -
data(5)*exp(-1j*1*0/2*2*pi)
)
+
(
data(3) -
data(7)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*1/4*2*pi)
)*exp(-1j*1*1/8*2*pi)


DFT(2,8,8)
=
(
(
data(0) +
data(4)*exp(-1j*1*0/2*2*pi)
)
-
(
data(2) +
data(6)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*0/4*2*pi)
)
+
(
(
data(1)+
data(5)*exp(-1j*1*0/2*2*pi)
)
-
(
data(3)+
data(7)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*0/4*2*pi)
)*exp(-1j*1*2/8*2*pi)


DFT(3,8,8)
=
(
(
data(0) -
data(4)*exp(-1j*1*0/2*2*pi)
)
-
(
data(2) -
data(6)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*1/4*2*pi)
)
+
(
(
data(1) -
data(5)*exp(-1j*1*0/2*2*pi)
)
-
(
data(3) -
data(7)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*1/4*2*pi)
)*exp(-1j*1*3/8*2*pi)


DFT(4,8,8)
=
(
(
data(0) +
data(4)*exp(-1j*1*0/2*2*pi)
)
+
(
data(2) +
data(6)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*0/4*2*pi)
)
-
(
(
data(1) +
data(5)*exp(-1j*1*0/2*2*pi)
)
+
(
data(3) +
data(7)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*0/4*2*pi)
)*exp(-1j*1*0/8*2*pi)


DFT(5,8,8)
=
(
(
data(0) -
data(4)*exp(-1j*1*0/2*2*pi)
)
+
(
data(2) -
data(6)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*1/4*2*pi)
)
-
(
(
data(1) -
data(5)*exp(-1j*1*0/2*2*pi)
)
+
(
data(3) -
data(7)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*1/4*2*pi)
)*exp(-1j*1*1/8*2*pi)


DFT(6,8,8)
=
(
(
data(0) +
data(4)*exp(-1j*1*0/2*2*pi)
)
-
(
data(2) +
data(6)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*0/4*2*pi)
)
-
(
(
data(1) +
data(5)*exp(-1j*1*0/2*2*pi)
)
-
(
data(3) +
data(7)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*0/4*2*pi)
)*exp(-1j*1*2/8*2*pi)


DFT(7,8,8)
=
(
(
data(0) -
data(4)*exp(-1j*1*0/2*2*pi)
)
-
(
data(2) -
data(6)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*1/4*2*pi)
)
-
(
(
data(1) -
data(5)*exp(-1j*1*0/2*2*pi)
)
-
(
data(3) -
data(7)*exp(-1j*1*0/2*2*pi)
)*exp(-1j*1*1/4*2*pi)
)*exp(-1j*1*3/8*2*pi)
整理一下,从下至上的过程
################# ######################################### ################################# #################################
#A(0)= data(0) # #B(0)= A(0) + # #C(0)= B(0) + # #D(0)= C(0) + #
# # # A(1)*exp(-1j*1*0/2*2*pi) # # B(2)*exp(-1j*1*0/4*2*pi)# # C(4)*exp(-1j*1*0/8*2*pi)#
# # # # # # # #
#A(1)= data(4) # #B(1)= A(0) - # #C(1)= B(1) + # #D(1)= C(1) + #
# # # A(1)*exp(-1j*1*0/2*2*pi) # # B(3)*exp(-1j*1*1/4*2*pi)# # C(5)*exp(-1j*1*1/8*2*pi)#
################# ######################################### # # # #
#A(2)= data(2) # #B(2)= A(2) + # #C(2)= B(0) - # #D(2)= C(2) + #
# # # A(3)*exp(-1j*1*0/2*2*pi) # # B(2)*exp(-1j*1*0/4*2*pi)# # C(6)*exp(-1j*1*2/8*2*pi)#
# # # # # # # #
#A(3)= data(6) # #B(3)= A(2) - # #C(3)= B(1) - # #D(3)= C(3) + #
# # # A(3)*exp(-1j*1*0/2*2*pi) # # B(3)*exp(-1j*1*1/4*2*pi)# # C(7)*exp(-1j*1*3/8*2*pi)#
################# ######################################### ################################# # #
#A(4)= data(1) # #B(4)= A(4) + # #C(4)= B(4) + # #D(4)= C(0) - #
# # # A(5)*exp(-1j*1*0/2*2*pi) # # B(6)*exp(-1j*1*0/4*2*pi)# # C(4)*exp(-1j*1*0/8*2*pi)#
# # # # # # # #
#A(5)= data(5) # #B(5)= A(4) - # #C(5)= B(5) + # #D(5)= C(1) - #
# # # A(5)*exp(-1j*1*0/2*2*pi) # # B(7)*exp(-1j*1*1/4*2*pi)# # C(5)*exp(-1j*1*1/8*2*pi)#
################# ######################################### # # # #
#A(6)= data(3) # #B(6)= A(6) + # #C(6)= B(4) - # #D(6)= C(2) - #
# # # A(7)*exp(-1j*1*0/2*2*pi) # # B(6)*exp(-1j*1*0/4*2*pi)# # C(6)*exp(-1j*1*2/8*2*pi)#
# # # # # # # #
#A(7)= data(7) # #B(7)= A(6) - # #C(7)= B(5) - # #D(7)= C(3) - #
# # # A(7)*exp(-1j*1*0/2*2*pi) # # B(7)*exp(-1j*1*1/4*2*pi)# # C(7)*exp(-1j*1*3/8*2*pi)#
################# ######################################### ################################# #################################
所以,计算将分为两步:
【1】按照奇偶重新排列输入数据
【2】从DFT-2开始向上迭代
编写Matlab函数


function [r] = fft_jacob (data,N,invert)
%按照奇偶重新排列,从上至下迭代
r=data;
for n=log2(N):-1:2
blk_head=0;
blk_len=power(2,n);
p=[];
for j=1:N/blk_len
for k=0:2:blk_len - 2
a=blk_head + k/2;
c=blk_head + k;
p(a + 1) = r(c + 1);
b=blk_head + k/2 + power(2,n - 1);
d=blk_head + k + 1;
p(b + 1) = r(d + 1);
end
blk_head = blk_head + blk_len;
end
r=p;
end
%从2点DFT开始从下至上迭代
for n=1:log2(N)
blk_head = 0;
blk_len = power(2,n);
p=[];
for j=1:N/blk_len
for k=0:blk_len - 1
a=blk_head + rem(k,power(2,n - 1));
b=blk_head + rem(k,power(2,n - 1)) + power(2,n - 1);
c=rem(k,power(2,n - 1));
d=power(2,n);
if(invert == 1)
f=exp(1j*1*c/d*2*pi);
else
f=exp(-1j*1*c/d*2*pi);
end
v=blk_head + k;
if(k < power(2,n - 1))
p(v + 1) = r(a+1) + r(b+1)*f;
else
p(v + 1) = r(a+1) - r(b+1)*f;
end
end
blk_head = blk_head + blk_len;
end
r=p;
end
if(invert == 0)
r=r/N;
end
测试用的文件,生成测试波形--计算FFT---对FFT结果计算IFFT---对IFFT结果再求FFT


fs = 480e3; %采样率
N = 2048; %信号长度和FFT点数
M= 2048;
%生成待测波形
freq_list=[15e3,-22.5e3,-25e3]; %频率低于本振的信号在正交下变频后,将表现为【负频率】信号
amp_list=[1,0.5,0.5];
src_sin_t=[];
src_cos_t=[];
for i=0:N - 1
src_sin_t(i + 1)=0;
src_cos_t(i + 1)=0;
for j=1:length(freq_list)
freq = freq_list(j);
amp = amp_list(j);
if(freq > 0)
k=i;
else
k=0-i;
end
src_sin_t(i + 1) = src_sin_t(i + 1) + amp*sin(2*pi*abs(freq)*k/fs);
src_cos_t(i + 1) = src_cos_t(i + 1) + amp*cos(2*pi*abs(freq)*k/fs);
end
data(i + 1) = complex(src_cos_t(i + 1),src_sin_t(i + 1)); %复数信号
%data(i + 1) = complex(src_cos_t(i + 1),0); %实数信号
end
figure(1);
subplot(2,1,1);
plot(src_sin_t);
grid on;
subplot(2,1,2);
plot(src_cos_t);
grid on;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%对测试波形求FFT
t0=cputime;
r=fft_jacob (data,N,0);
t=cputime-t0
%显示
x=[];
q=[];
for i=1:M
if(i <= M/2)
q(i + M/2) = r(i);
else
q(i - M/2) = r(i);
end
%x轴
x(i)=(i - 1 - M/2)*fs/M/1000;
end
figure(2);
subplot(2,1,1);
plot(x,abs(q));
grid on;
subplot(2,1,2);
plot(x,atan2(real(q),imag(q))/pi*180);
grid on;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%求IFFT
r_ifft=fft_jacob (r,N,1);
figure(3);
subplot(2,1,1);
plot(imag(r_ifft));
grid on;
subplot(2,1,2);
plot(real(r_ifft));
grid on;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%对r_ifft求FFT
q_fft=fft_jacob (r_ifft,N,0);
%显示
x=[];
q=[];
for i=1:M
if(i <= M/2)
q(i + M/2) = q_fft(i);
else
q(i - M/2) = q_fft(i);
end
%x轴
x(i)=(i - 1 - M/2)*fs/M/1000;
end
figure(4);
subplot(2,1,1);
plot(x,abs(q));
grid on;
subplot(2,1,2);
plot(x,atan2(real(q),imag(q))/pi*180);
grid on;
我写的dft_test.m,2048点,运算耗时10.6S
我写的fft,2048点,运算耗时0.0624S
matlab自带函数fft,2048点,耗时0.0312S
我写的fft函数中有多处可以优化的地方,比如取数组下标都运算了两遍(matlab的数组下标从1开始),比如p和r的初始化为空,比如power和rem
IFFT可以将频谱和相位转换为波形,与FFT的运算过程相比,测试波形的旋转方向相反。至于IFFT的原理,看看我计算测试波形的过程和IDFT是不是有些相似?
我这里推导的FFT算法,也被称为基2的时间分治FFT算法,是最常见的FFT算法,由J. W. Cooley和 John Tukey 于1965年提出,wikipedia链接Cooley–Tukey FFT algorithm
来源:oschina
链接:https://my.oschina.net/u/4381342/blog/3625734