很难找到使用Matlab fft
使用的在线示例,正确地规范化幅度/功率值。如果您要在不同长度的不同信号上比较这些值,这一点至关重要。这通常是实值输入的问题,因为在这种情况下通常会提取单侧频谱,因此在计算幅度或功率值时应手动应用幅度变化。你可以找到gist on GitHub here(请告诉我任何错误)。
带回家的消息是:
Y = fft(x)/L
); fft(x,nfft)
),则规范化器为nfft
而不是numel(x)
; 我的问题与this post类似,但比answers更为普遍,我认为在最新版本的Matlab(2015)中,关于规范化存在错误。我对在CodeReview SE上发布此内容犹豫不决,如果您认为更合适,请在评论中告诉我。
我想使用Matlab的fft
验证傅里叶变换的以下代码,因为我在网上找到了相互矛盾的信息来源,包括在Matlab帮助本身,以及我无法用某些这样的“配方”(包括来自MathWorks团队的defined来验证Parseval定理,见下文),特别是那些为实际输入提取单面光谱的那些。
例如,在提取正频率时,通常在网上发现的用于解释实值信号的对称频谱的幅度加倍似乎是错误的(Parseval定理失败),而似乎有必要使用正方形在Matlab中有两个系数(我不知道为什么)。有些人似乎也像Y = fft(X)/L
一样直接归一化DFT系数,但我认为这很令人困惑,应该不鼓励;当复数DFT系数的模数除以信号长度时,幅度为own example,系数本身不应被分割。一旦经过验证,我打算将此代码作为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
来自Matlab的posted和clarification post上的SO:
>> 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
来自MathWorks团队自己的answer:
>> 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
。
在MatlabCentral上找到example:
>> 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 )
答案 0 :(得分:1)
我想我找出了两个主要问题;两者都与系数归一化的方式有关。让我再说一遍,我不能阻止你 NOT 来直接归一化fft
函数返回的复数DFT系数。
在下文中,我们假设以下命名法:
t - Nx1 vector of regularly sampled time-points
x - Nx1 real- or complex-valued signal
定义并解释on Wikipedia:
1/N
因子。这在某种意义上是自然的,因为在时间 - 频率方向上的移动可以被看作在具有不同频率的(正交)波的基础上的投影,而在频率 - 时间方向上的移动可以被视为加权叠加。波浪。|Xk| / N
。同样,每个wave的功率为|Xk|^2 / N
。 Matlab也使用了规范化(好吧,FFTW)。N * amp_adjusted[k]^2 = N * (2*|Xk|/N)^2
不等于2*|Xk|^2 / N
(这是两个平方根来自OP的地方)。因此,有必要从DFT系数中计算独立幅度和功率值(另一个不扩展它们的好理由)。在线的许多示例使用明确的N点变换:Y = fft(x,NFFT)
其中NFFT
通常是2的幂,使得FFTW的计算效率更高。
在这种情况下的有效差异(假设NFFT >= N
)是x
在其末尾用0填充,直到达到NFFT时间点的长度。这意味着分解中的频率数会发生变化,因此应该相对于NFFT
波分量进行归一化,而不是原始的N
时间点。
因此,几乎所有在线发现的例子在对系数进行标准化的方式上都是错误的。它不应该是Y = fft(x,NFFT)/N
,而是Y = fft(x,NFFT)/NFFT
- 这是摆脱复杂系数归一化习惯的另一个好理由。
请注意,这与Parseval的相等没有区别,因为时域中添加的项都是零,因此它们对现在更大的和的贡献也是零。然而,在频域中,所添加的离散频率将通常对原始信号具有响应,这给出了为什么所获得的系数在填充和非填充变换之间确实完全不同的直观感觉。
OP中的代码因此是错误的,相反,似乎有必要输出幅度和功率,因为没有可以容纳复杂和真实情况的一般归一化系数,偶数或奇数个时间-points。您可以找到Gist here。
答案 1 :(得分:0)
我发现此在线fft示例(https://habr.com/post/112068/)非常有用。检查一下:
%% Parameters
Tm=5;% Length of signal (s)
Fd=512;% Sampling frequency (Hz)
Ak=0.5;% Constant component (Unit)
A1=1;% The amplitude of the first sinusoid (Unit)
A2=0.7;% Amplitude of the second sinusoid (Unit)
F1=13;% Frequency of the first sinusoid (Hz)
F2=42;% Frequency of the second sinusoid (Hz)
Phi1=0;% Initial phase of the first sinusoid (Degrees)
Phi2=37;% The initial phase of the second sinusoid (Degrees)
An=3*A1;% Noise Dispersion (Unit)
FftL=1024;% Number of Fourier Spectrum Lines
%% Generating work arrays
T=0:1/Fd:Tm;% Time Arrays
Noise=An*randn(1,length(T));% An array of random noise with a length equal to the array of time
Signal=Ak+A1*sind((F1*360).*T+Phi1)+A2*sind((F2*360).*T+Phi2);% Signal array (a mixture of 2x sinusoids and a constant component)
%% Spectral representation of the signal
FftS=abs(fft(Signal,FftL));% The amplitudes of the Fourier transform of the signal
FftS=2*FftS./FftL;% Spectrum normalization by amplitude
FftS(1)=FftS(1)/2;% The normalization of the constant component in the spectrum
FftSh=abs(fft(Signal+Noise,FftL));% The amplitudes of the Fourier transform of the signal + noise mixture
FftSh=2*FftSh./FftL;% Spectrum normalization by amplitude
FftSh(1)=FftSh(1)/2;% The normalization of the constant component in the spectrum
%% Plotting
subplot(2,1,1);
plot(T,Signal);
title('Signal');
xlabel('Time (s)');
ylabel('Amplitude (Unit)');
subplot(2,1,2);
plot(T,Signal+Noise);
title('Signal+Noise');
xlabel('Time (s)');
ylabel('Amplitude (Unit)');
F=0:Fd/FftL:Fd/2-1/FftL;% The frequency array of the calculated Fourier spectrum
figure();
subplot(2,1,1);
plot(F,FftS(1:length(F)));% Plotting of the spectrum of the Fourier signal
title('Signal spectrum');
xlabel('Frequency (Hz)');
ylabel('Amplitude (Unit)');
subplot(2,1,2);
plot(F,FftSh(1:length(F)));% Plotting of the Fourier signal spectrum
title('Signal spectrum');
xlabel('Frequency (Hz)');
ylabel('Amplitude (Unit)');