【发布时间】:2016-02-03 12:35:29
【问题描述】:
我的问题与this post 类似,但比this post 更笼统,而且我认为在标准化方面存在错误,无论如何都是最新版本的Matlab(2015)。我犹豫是否将其发布在 CodeReview SE 上,如果您认为在 cmets 中告诉我更合适的话。
我想使用 Matlab 的 fft 验证傅立叶变换的以下代码,因为我在网络上发现了相互冲突的信息来源,包括在 Matlab 帮助本身中,我有无法使用某些此类“配方”验证 Parseval 定理(包括来自 MathWorks 团队的answers,见下文),尤其是那些为真实输入提取单面光谱的。
例如,仅在提取正频率时,通常在网上发现的用于解释实值信号的对称频谱的幅度加倍似乎是错误的(Parseval 定理失败),相反,似乎有必要使用平方- Matlab中两个系数的根(我不知道为什么)。有些人似乎也像Y = fft(X)/L 一样直接对DFT 系数进行归一化,但我认为这很混乱,应该不鼓励;幅度是defined,作为复数DFT系数除以信号长度的模,系数本身不应该被除。一旦通过验证,我打算将此代码作为要点发布在 GitHub 上。
function [frq,amp,phi] = fourier_transform( time, vals )
% FOURIER_TRANSFORM computes the Fast Fourier Transform of a given time-series.
%
% [freq,amp,phi] = fourier_transform(time,vals)
%
% Inputs:
%
% time - Nx1 column vector of equally-spaced timepoints (if not, input will be resampled).
% vals - NxM matrix of real- or complex-valued observations at previous timepoints.
%
% Outputs:
%
% frq - column vector of frequencies in Hz
% amp - corresponding matrix of amplitudes for each frequency (row) and signal (column)
% phi - corresponding unwrapped phase for each frequency (row) and signal (column)
%
% Note:
% To compute the power from the output amplitude, you need to multiply by the number of timepoints:
% power = numel(time) * amp.^2;
%
% References:
% https://en.wikipedia.org/wiki/Discrete_Fourier_transform
% make sure input time-series is uniformly sampled
assert( iscolumn(time), 'Input time should be a column vector.' );
assert( ismatrix(vals) && size(vals,1) == numel(time), 'Input values should be a matrix with same number of rows than of timepoints.' );
if std(diff(time)) > 1e-6
warning('Input time-course does not appear to be uniformly sampled. Resampling before continuing.');
[vals,time] = resample(vals,time);
end
% sampling information
nt = numel(time);
dt = time(2)-time(1);
fs = 1/dt;
df = fs/nt;
% complex spectrum coefficients
coef = fft(vals);
% real input
if isreal(vals)
% extract one-sided spectrum (spectrum is symmetric)
nfft = floor( nt/2 + 1 ); % eg 8 -> 5, and 7 -> 4
coef = coef( 1:nfft, : );
frq = (0:nfft-1)*df;
% correct amplitude values to account for previous extraction
fac = sqrt(2);
amp = fac*abs(coef)/nt;
amp(1,:) = amp(1,:)/fac; % .. except for the DC component
if mod(nt,2) == 0
amp(end,:) = amp(end,:)/fac; % .. and for the Nyquist frequency (only if nt is even)
end
% complex input
else
% shift the spectrum to center frequencies around 0
coef = fftshift( coef );
frq = fftshift( (0:nt-1)*df );
amp = abs(coef)/nt;
end
% make sure frq is a column vector and compute phases
frq = frq(:);
phi = unwrap(angle(coef));
end
使用示例
>> fs=1e3; t=transpose(0:1/fs:10); nt=numel(t); X=rand(nt,1);
>> [frq,amp,phi] = fourier_transform( t, X );
>> sum( abs(X).^2 ) - nt*sum( amp.^2 ) % Parseval's theorem
ans =
-2.7285e-11
错误示例 1
来自 Matlab 的 own example 和 SO 上的 posted:
>> Fs = 1000; % Sampling frequency
T = 1/Fs; % Sample time
L = 1000; % Length of signal
t = (0:L-1)*T; % Time vector
x = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
y = x + 2*randn(size(t)); % Sinusoids plus noise
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
Y = fft(y,NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
>> sum(abs(y).^2) - NFFT*sum(abs(Y).^2) % Parseval's theorem
ans =
-220.4804
问题及解决方案:
来自Y = fft(y,NFFT)/L 行中的规范化。
这应该是:
>> Y = fft(y,NFFT);
Ya = abs(Y)/NFFT; % correctly normalised amplitudes
sum(abs(y).^2) - NFFT*sum(Ya.^2) % Parseval's theorem
错误示例 2
来自 MathWorks 团队自己的clarification post:
>> Fs = 1000; % Sampling frequency
T = 1/Fs; % Sample time
L = 1000; % Length of signal
t = (0:L-1)*T; % Time vector
% Sum of a 50 Hz sinusoid and a 120 Hz sinusoid
x = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
y = x + 2*randn(size(t)); % Sinusoids plus noise
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
Y = fft(y,NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
NumUniquePts = ceil((NFFT+1)/2);
Y=Y(1:NumUniquePts);
MX=2*abs(Y);
MX(1)=MX(1)/2; % DC component
if ~rem(NFFT,2) % when NFFT is even, Y(1+Y/2) is the Nyquist frequency component of x, and needs to make sure it is unique.
MX(length(MX))=MX(length(MX))/2;
end
>> sum( abs(y).^2 ) - NFFT*sum( MX.^2 )
ans =
-5.3812e+03
问题及解决方案:
再次标准化。将Y = fft(y,NFFT)/L; 替换为Y = fft(y,NFFT),并将MX=2*abs(Y); 替换为MX=2*abs(Y)/NFFT;。但是这里出现了倍频问题;校正因子似乎是 sqrt(2) 而不是 2。
错误示例 3
在 MatlabCentral 上以 answer 的形式找到:
>> Fs = 1000; % Sampling frequency
T = 1/Fs; % Sample time
L = 1000; % Length of signal
t = (0:L-1)*T; % Time vector
x = 0.7*sin(2*pi*Fs/8*t) + sin(2*pi*Fs/4*t);
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
Y = fft(x,NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
>> sum( abs(x).^2 ) - NFFT*sum( abs(Y).^2 )
ans =
-36.1891
问题及解决方案:
和第一个例子一样,归一化问题。改为写:
Y = fft(x,NFFT);
Ya = abs(Y)/NFFT;
sum( abs(x).^2 ) - NFFT*sum( abs(Ya).^2 )
【问题讨论】:
-
我现在没有时间进行进一步调查,但我注意到对于 Worng example 1,您可以(在最后一行)将
L替换为 @987654345 @你得到正确的结果。据我所知,Matlabsfft/ifft不会像通常那样缩放值。我建议将help fft中给出的公式与fft的结果进行比较。 -
@flawr 这与我所说的直接缩放系数一致,例如
Y = fft(X)/L。这是个坏主意,在使用 N 点变换Y = fft(X,N)/L时甚至会出现错误。它应该是Y = fft(X,N)/N,尽管我再一次不鼓励写这个。此外,这并不能回答单面情况下离散幅度和功率应该是多少的问题。 -
什么是标准化离散傅里叶变换及其逆变换的 ISO(或等效)标准?例如,来自 Wikipedia 的声明:“将 DFT 和 IDFT(此处为 1 和 1/N)与指数符号相乘的归一化因子仅仅是约定,在某些处理上有所不同。这些约定的唯一要求是DFT 和 IDFT 具有相反符号的指数,并且它们的归一化因子的乘积为 1/N。对于 DFT 和 IDFT,sqrt{1/N} 的归一化使得变换是单一的。”