第四节 傅里叶变换的MATLAB实现
1、向量的离散傅里叶变换
参考MATLAB的描述,定义,当是向量时,为该向量的傅里叶变换。
当信号为长度为的实信号时,由以下向量的离散傅里叶变换可知,返回值为复信号。 即,对于第个信号,,其幅值为,其幅角满足。
而下式为复信号的傅里叶逆变换: 以下两个事实/问题需要了解,即①复信号Y(k)中的数据由一对对共轭复数组成;②这些复数的幅值和辐角含义。
对于事实①,有如下推导:
1°当为奇数时,除去为实数(又叫直流分量),其余数据两两共轭。
2°当为偶数时,除去为实数(又叫直流分量),其余数据两两共轭。 对于问题②,由傅里叶逆变换: 考虑共轭的两个数据与,则 式中,,,当时。
所以原来的实信号可以表示为:
1°当为奇数时, 2°当为偶数时, 此时可以发现,原实信号频率分量组成的强度与相位角由复信号的幅值和辐角表示。
关于频率分量的组成
设采样频率为,则离散信号与连续信号的联系为。以为偶数时为例有: 上式说明,原信号中的频率分量的组成为 这说明,当采样频率为时,在频域内最多只能不失真地采样到最大为的频率。这即为奈奎斯特采样定理:在进行模拟/数字信号的转换过程中,当采样频率大于信号中最高频率的2倍时,采样之后的数字信号完整地保留了原始信号中的信息。
关于频率分量的组成的强度与相位角
由式(9)和式(7)可以得到,原实信号频率分量组成的强度与相位角可以由前个复信号的幅值和辐角表示。
即中频率分量的强度为前个复信号的幅值×2(直流分量除外);
中频率分量的相位角与前个复信号的辐角一致。
2、MATLAB傅里叶变换简要介绍
MATLAB内关于傅里叶变换的介绍可以参考其官方支持文档:傅里叶变换 - MATLAB & Simulink - MathWorks 中国
该文档简要介绍了:
- 离散傅里叶变换基本原理
- 内置函数fft,fftshift等的使用
- 含噪信号清除随机噪声并显现频率
- 提高计算效率
3、MATLAB官方fft函数介绍
MATLAB内关于fft函数的介绍可以参考其官方支持文档:快速傅里叶变换 - MATLAB fft - MathWorks 中国
下面截取一部分简单展开介绍。
Fs = 1000; % Sampling frequency
T = 1/Fs; % Sampling period
L = 1500; % Length of signal
t = (0:L-1)*T; % Time vector
% Form a signal containing a 50 Hz sinusoid of amplitude 0.7 and a 120 Hz sinusoid of amplitude 1.
S = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
% Compute the two-sided spectrum P2. Then compute the single-sided spectrum P1 based on P2 and the even-valued signal length L.
Y = fft(S);
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1);
% Define the frequency domain f and plot the single-sided amplitude spectrum P1.
f = Fs*(0:(L/2))/L;
plot(f,P1)
title('Single-Sided Amplitude Spectrum of S(t)')
xlabel('f (Hz)')
ylabel('|P1(f)|')
上述代码进行了几项操作:
- 构造一个信号,其中包含幅值为 0.7 的 50 Hz 正弦量和幅值为 1 的 120 Hz 正弦量。
- 计算信号的傅里叶变换。
- 计算双侧频谱P2。然后基于P2和偶数信号长度L计算单侧频谱P1。
- 定义频域f并绘制单侧幅值频谱P1。
可以看到MATLAB内置函数只是完成了式(1)的变换,返回值为复信号。并没有给出原信号的频率分量、频率分量强度以及频率分量相位角。而这些参数是实际问题需要考虑和研究的对象。因此可以重写fft函数,使其满足实际问题的需要,返回原信号的频率分量、频率分量强度以及频率分量相位角等参数。
4、重写fft函数
本节给出一个fft函数(fft1.m
)的重写例子,具体情况可以根据个人需要删除、更改或添加。
function [fx,mag,phi] = fft1(data_x,freq,varargin)
%正频域傅里叶变换
% data_x为数据时程,列向量
% freq为采样频率,单位为Hz
% fx,mag,phi分别为频率分量以及对应的幅值和辐角
if nargin == 2
wintype = 'rec';%默认为矩形窗
elseif nargin == 3
wintype = varargin{1};
else
disp('输入有误');
end
%%窗函数的定义
switch wintype
case 'han'
w=hann(length(data_x));r=2;%汉宁窗,能量系数为2
case 'ham'
w=hamming(length(data_x));r=1.852;%海明窗,能量系数为1.852
case 'tri'
w=triang(length(data_x));r=2;%三角窗,能量系数为2
case 'gau'
w=gausswin(length(data_x));r=2.396;%高斯窗,能量系数为2.396
case 'bkm'
w=blackman(length(data_x),'periodic');r=2.381;%布莱克曼窗,能量系数为2.381
case 'kai'
w=kaiser(length(data_x),3*pi);r=2.48;%凯泽-贝塞尔窗,能量系数为2.48
case 'ftw'
w=flattopwin(length(data_x),'periodic');r=4.5455;%平顶窗,能量系数为4.5455
otherwise
w=1;r=1;%矩形窗,能量系数为1
end
%%向量按照列向量排布
if size(data_x,1)==1
data_x=data_x';
end
%%傅里叶变换
data_x=data_x.*w;%时程上加窗函数
y=fft(data_x)*2/length(data_x);%得到复信号y,×2表示负频域向正频域折叠
phi=angle(y)/pi*180;%复信号y的辐角
y_mag=abs(y);y_mag=y_mag*r;%复信号y的幅值, ×r表示考虑能量系数r
fx=[0:freq/length(data_x):freq/2];%采样定理决定频率区间
mag=mag(1:length(fx));%频率区间对应的幅值
phi=phi(1:length(fx));%频率区间对应的辐角
end
值得指出的是,的辐角为0°,的辐角为-90°。
以上代码加入了窗函数。关于离散傅里叶变换窗函数与信号泄漏问题,可以参考什么是窗函数? - 知乎 (zhihu.com)等网页介绍。
窗函数部分的代码可以不封装在fft1.m
函数内,根据实际使用情况来决定与使用。
下面给出一个实际例子,以检测重写的fft1.m
函数的功能。
%%构造信号
fs=50;%采样频率50Hz
time=0:1/fs:120;%采样时间120s
x=cos(2*pi*1/4.*time)+3*sin(2*pi*1/3.*time);%构造信号,幅值分别为1和3,频率分别为1/4和1/3Hz
figure(1)
plot(time,x,'-k','LineWidth',1.5)
xlabel('Time,s')
ylabel('x(t)')
title('Test Signal')
[fx,mag,phi]=fft1(x,fs);%傅里叶变换
figure(2)
stem(fx,mag,'-k','LineWidth',1.5)
xlim([0 1])
grid on
xlabel('f(Hz)')
ylabel('Magnitude')
title('Single-Sided Amplitude Spectrum of X(t)')
i=find(abs(fx-1/4)*4<0.05);Cp_mag=max(mag(i));
phi1=phi(find(mag==Cp_mag))%显示频率成分对应的相位角
i=find(abs(fx-1/3)*3<0.05);Cp_mag=max(mag(i));
phi2=phi(find(mag==Cp_mag))%显示频率成分对应的相位角
运行后结果如下:
phi1=0.8017
phi2=-88.8084
至此,更贴近解决实际问题的MATLAB-FFT函数重写完成。