Мой вопрос похож, но более общий, чем этот пост, и я думаю, что там есть ошибка в отношении нормализации, так или иначе, с новейшей версией Matlab (2015). Я не решался опубликовать это на CodeReview SE, если вы считаете, что это будет более уместно, дайте мне знать в комментариях.
Я хотел бы проверить следующий код преобразования Фурье с использованием Matlab fft
, потому что я нашел противоречивые источники информации в Интернете, в том числе в самой справке Matlab, и мне не удалось проверить теорему Парсеваля с некоторыми такими "рецептами" (включая ответы от команды MathWorks, см. ниже), особенно те, которые извлекают односторонние спектры для реальных входных данных.
Например, удвоение амплитуды, которое обычно можно найти в Интернете для учета симметричных спектров реальных значений сигналов при извлечении положительных частот, только кажется неправильным (теорема Парсеваля не выполняется), и вместо этого представляется необходимым использовать квадратный корень из два коэффициента в Matlab (я не знаю почему). Некоторые люди также, кажется, нормализуют коэффициенты ДПФ, например, Y = fft(X)/L
, но я думаю, что это сбивает с толку и не должно поощряться; амплитуда определяется как модуль комплексного коэффициента DFT, деленный на длину сигнала, сами коэффициенты не должны делиться. После проверки я намереваюсь опубликовать этот код в GistHub.
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 theorem
ans =
-2.7285e-11
Неправильный пример 1
Из собственного примера Matlab и размещенного на 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 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 theorem
Неправильный пример 2
От команды MathWorks собственный разъясняющий пост:
>> 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:
>> 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 )