# FFT: Sinewave frequency displacement when zero-padding

I am creating a signal consisting of a single-cycle sine wave in Python. This signal has 32 samples. I am using a sampling rate Fs of 48 kHz so the signal's frequency is 1500 Hz. The signal looks like this:

If I take the 32-point FFT (no zero-padding, no windowing) of this real time-domain signal, and I normalize the amplitude, I get a magnitude frequency response that looks like this (I show only positive frequencies):

There is a single non-zero frequency bin at 1500 Hz, exactly the signal's frequency. This is also the 1st bin, because the signal makes exactly 1 cycle in the selected window length of 32 samples. So far so good.

Now I want to increase the sampling rate in the frequency domain to reduce the spacing between frequency bins. What I expect to see is a curve similar to a sinc function, with the center of the main lobe exactly at 1500 Hz, and with zero-crossings happening every 1500 Hz: at 3000 Hz, 4500 Hz, etc.

I zero-pad the signal with 4064 zeros and take the 4096-point FFT (again, no windowing). The magnitude frequency response now looks like this:

The overall sinc function shape seems fine, and the zero-crossings between lobes seem to be at the expected frequencies:

When I zoom into the peak of the main lobe, however, I find that the center of the lobe is around 1260 Hz, and that its normalized amplitude is not exactly 1, but rather something like 1.04.

Also, the amplitude of the bin at 1500 Hz is exactly 1.

There is also a zero-crossing at 0 Hz, which makes sense. So the main lobe is between 0 Hz and 3000 Hz, but the center is not 1500 Hz, so the main lobe is not symetric. Strange.

Why isn't the main lobe peak at 1500 Hz? Is this normal behavior or do I have a bug in my code?

• The most likely cause is not a bug in your code, but rather that zero-padding produces not one sinc, but many, and they overlap. In other words, the value you see at frequency 1500 Hz is not due only to the sine wave; it's also affected by the overlapping sincs. This may indeed cause the spectral peak to shift.
– MBaz
Nov 8, 2023 at 1:41

(I show only positive frequencies):

which is part of the problem here :-)

You would get the expected behavior if you used a complex sine, i.e. $$x[n] = e^{j2\pi\frac{n}{N}}$$ but a sine wave actually consists of two complex sines:

$$x[n] = \sin(2\pi\frac{n}{N}) = \frac{1}{2j}(e^{j2\pi\frac{n}{N}} - e^{-j2\pi\frac{n}{N}})$$

That means your spectrum has two peaks: one at 1500Hz and one at -1500Hz. Zero padding is equivalent to multiplying with a rectangular function, and that indeed is convolution with a sinc function in the frequency domain. Since we are using a DFT, that's a circular (not linear) convolution.

Since you have two frequency peaks you end up with two sinc functions that are addded. In other words the sinc from the negative peak "leaks" into the positive frequencies and interferes with the sinc from the positive frequencies (and vice versa). The exact interference depends on the distance between the peaks and the phase of the original sine wave: for a sine the peak will move down in frequency, for a cosine it will move up.

If you look at a single complex sine, you can see that the sinc does indeed stretch over the entire frequency band so it WILL interfere with the sinc of the negative peak for the real sine wave.

• Thanks for your answer. I have done similar tests with a complex sinusoid instead of a real sinusoid, and now I am getting a single peak at 1500 Hz in the whole spectrum between -Fs/2 and Fs/2. The main lobe of the sinc function is exactly at 1500 Hz. Nov 9, 2023 at 13:05
• I tried to analyze where the interferences were coming from in the case of the real sinusoid, and I conclude there are 2 sources of interference: 1) The interference due to the side lobes of the sinc centered at -1500 Hz. 2) The interference due to aliasing in the frequency domain, because the sinc function is infinite, so side lobes after Fs/2 are reflected back. This is also present in the complex sinusoid. Am I correct? You also mentioned circular convolution in your answer, but I don't think this is a cause of interference in the frequency domain, right? Nov 9, 2023 at 13:08

The Fourier transformation (FFT is a faster method with the same result) attempts to replace an infinitely long signal with a combination of a finite number of sin and cos functions. The frequencies are not arbitrary, but multiples of the fundamental frequency $$F_s$$. In your case $$f_n=n\cdot F_s$$ with n=1, 2, 3,...

In Figure_1 you show a short data segment of duration $$\Delta t\approx 667~\mu$$s. You're probably assuming that there are only Zeros to the left and right of it. FFT knows nothing about this.

FFT adds infinite copies of the data segment to both sides before starting to work. This is a quirk that you can't get rid of in FFT. If you don't like that, try Wavelets.

FFT then tries to recreate this overall signal as best as possible with multiples of the fundamental frequency. Figure_2 shows that this works without errors with the fundamental frequency. Don't forget: The result is infinitely long because the amplitude of sin/cos does not go asymptotically to zero, no matter how long you wait!

Then you create a strange signal: a single sin oscillation (32 samples) followed by 4096 zeros. FFT adds an infinite number of copies of the 4128 sample long signal on both sides. Then FFT attempts to recreate the overall signal through a combination of the given set of sin/cos functions. Not easy, because the result usually has to have the value zero (that's your padding) and then looks like a single sin oscillation over a short distance of only 32 samples. As a result, you can see that it is somewhat successful with a lot of spectral lines.

• Isn’t it rather $n \cdot F_s/N$ with $N$ the length of the input to the FFT?
– Jdip
Nov 8, 2023 at 21:57