【matlab】从DFT到FFT的推导

落爺英雄遲暮 提交于 2020-04-24 21:13:11

本文地址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;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
dft_test.m

 

上面的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(0,8,8)
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(1,8,8)
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(2,8,8)
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(3,8,8)
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(4,8,8)
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(5,8,8)
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(6,8,8)
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)
DFT(7,8,8)

 

整理一下,从下至上的过程

#################       #########################################       #################################       #################################
#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_jacob

测试用的文件,生成测试波形--计算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;
fft_test.m

我写的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. CooleyJohn Tukey 于1965年提出,wikipedia链接Cooley–Tukey FFT algorithm

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!