Reputation: 291
I try to find the strongest frequency component with Matlab. It works, but if the datapoints and periods are not nicely aligned, I need to zero-pad my data to increase the FFT resolution. So far so good.
The problem is that, when I zero-pad too much, the frequency with the maximal power changes, even if everything is aligned nicely and I would expect a clear result.
This is my MWE:
Tmax = 1024;
resolution = 1024;
period = 512;
X = linspace(0,Tmax,resolution);
Y = sin(2*pi*X/period);
% N_fft = 2^12; % still fine, max_period is 512
N_fft = 2^13; % now max_period is 546.1333
F = fft(Y,N_fft);
power = abs(F(1:N_fft/2)).^2;
dt = Tmax/resolution;
freq = (0:N_fft/2-1)/N_fft/dt;
[~, ind] = max(power);
max_period = 1/freq(ind)
With zero-padding up to 2^12
everything works fine, but when I zero-pad to 2^13
, I get a wrong result. It seems like too much zero-padding shifts the spectrum, but I doubt it. I rather expect a bug in my code, but I cannot find it. What am I doing wrong?
EDIT: It seems like the spectrum is skewed towards the low frequencies. Zero-padding just makes this visible:
Why is my spectrum skewed? Shouldn't it be symmetric?
Upvotes: 1
Views: 621
Reputation: 291
TL;DR: Use a better window function!
The long version:
After searching further, I finally found the explanation. Neither is indexing the problem, nor the additional low frequency components added by the zero-padding. The frequency response of the rectangular window, combined with the negative frequency components is the culprit. I found out on this website explaining window functions.
I made more plots to explain:
Top: The frequency response without windowing: two delta peaks, one at the positive and one at the negative frequency. I always plotted the positive part, since I didn't expect to need the negative frequency components. Middle: The frequency response of the rectangular window function. It is relatively broad, but I didn't care, because I thought I'd have only a single peak. Bottom: The frequency response of the zero-padded signal. In time domain, this is the multiplication of window function and sine-wave. In frequency domain, this amounts to the convolution of the frequency response of the window function with the frequency response of the perfect sine. Since there are two peaks, the relatively broad frequency responses of the window overlap significantly, leading to a skewed spectrum and therefore a shifted peak.
The solution: A way to circumvent this is to use a proper window function, like a Hamming window, to have a much smaller frequency response of the window, leading to less overlap.
Upvotes: 0
Reputation: 507
Its not a bug in your code. It has to do with the properties of the DFT (and thus the FFT, which is merely a fast version of the DFT).
When you zero-pad, you add frequency resolution, particularly on the lower end.
Here you use a sine wave as test, so you are basically convolving a finite length sine with finite sines and cosines (see here https://en.wikipedia.org/wiki/Fast_Fourier_transform details), which have almost the same or lower frequency.
If you were doing a "proper" fft, i.e. doing integrals from -inf to +inf, even those low frequency components would give you zero coefficients for the FFT, but since you are doing finite sums, the result of those convolutions is not zero and hence the actual computed fourier transform is inaccurate.
Upvotes: 2
Reputation: 21492
Here is a graphic explanation of what you're doing wrong (which is mostly a resolution problem). EDIT: this shows the power for each fft data point, mapped to the indices of the 2^14 dataset. That is, the indices for the 2^13 data numbered 1,2,3 map to 1,3,5 on this graph; the indices for 2^12 data numbered 1,2,3 map to 1,5,9; and so on.
. You can see that the "true" value should in fact not be 512 -- your indexing is off by 1 or a fraction of 1.
Upvotes: 2