Search⌘ K
AI Features

DFT Leakage

Understand how the Discrete Fourier Transform (DFT) frequency index relates to real-world frequencies and why fractional frequency components cause energy leakage across DFT bins. This lesson explains the difference between integer and fractional discrete frequencies and their impact on spectral analysis, helping you grasp the phenomenon of DFT leakage common in real-world signals.

The DFT frequency index kk represents the pointer to the real-world frequency of the signal. This has been established through the relation

k=Nffsk = N \frac{f}{f_s}

where NN is the DFT size and fsf_s is the sample rate. Since ff is a continuous variable (e.g., 1.5 GHz, 2.4 GHz), the frequency index kk can be an integer or a fractional number.

Integer discrete frequency

For an integer kk, sinusoids with analysis frequencies of ff complete an integer number of cycles within a given duration. For example, k=1k=1 implies one cycle per interval that consists of NN samples (which is why the frequency is k/Nk/N cycles/sample). In this case, limiting the sinusoid in a duration of TT seconds is the same as multiplying an infinitely long sinusoid with a rectangular signal of TT seconds. This results in a spectrum that has a sinc signal at the location of that frequency. The reason behind this will be discussed later.

Let’s see an example. At N=16N=16, we can see that the sinusoid and the DFT-analysis frequency corresponding to k=4k=4 completes four cycles in a given interval. The shape is still a sinc signal. However, the spectrum is sampled at the peak of this sinc signal at index k=4k=4 while remaining at zero-crossings for all other values of kk. This is why we see perfect impulses in the spectrum for integer kk.

No DFT leakage
No DFT leakage

We can change a few values and run the code below to verify the results.

Fractional discrete frequency

For a fractional value, say 4.74.7, everything stays the same but the input frequency does not complete an integer number of cycles in the given interval. The correlation result for this sinusoid and all NN DFT-analysis frequencies are nonzero, causing the energy to leak into all the remaining bins! In other words, the spectrum is still a sinc signal, but it is not sampled at integer spectral locations.

Let’s test this by changing different values in the code below:

Python 3.10.4
import numpy as np
import matplotlib.pyplot as pl
figWidth = 20
figHeight = 12
# Generating the signal
fs = 16000 # sample rate
Ts = 1/fs # sample time
f = 4700
T = 1/f
A = 1
phi = 0
t1 = np.arange(0, 4*T-Ts, Ts)
x1 = A*np.cos(2*np.pi*f*t1 + phi)
t2 = np.arange(0, 4*T-Ts, 1e-6)
x2 = A*np.cos(2*np.pi*f*t2 + phi)
# Plotting the signal
fig, axs = pl.subplots(2, figsize=(figWidth, figHeight))
nfft1 = 16
xf1 = np.fft.fft(x1,nfft1) # from 0 to 2pi
xf1 = np.fft.fftshift(xf1) # from -pi to pi
k1 = np.arange(-nfft1/2,nfft1/2,1)
axs[0].plot(t2, x2, color='b', linestyle='--', linewidth=2)
axs[0].vlines(t1, ymin=x1, ymax=0, color='b', linewidth=2)
axs[0].plot(t1, x1, 'o', color='b', markersize=16, zorder=10, clip_on=False)
axs[1].vlines(k1, ymin=np.abs(xf1), ymax=0, color='b', linewidth=2)
axs[1].plot(k1, np.abs(xf1), 'o', color='b', markersize=16, zorder=10, clip_on=False)
nfft2 = 1024
xf2 = np.fft.fft(x1,nfft2) # from 0 to 2pi
xf2 = np.fft.fftshift(xf2) # from -pi to pi
k2 = np.arange(-nfft1/2, nfft1/2-1, nfft1/nfft2)
axs[1].plot(k2, np.abs(xf2[:-int(nfft2/nfft1)]), color='b', linestyle='--', linewidth=1)
for ax in axs:
ax.axhline(y=0, color='k', linewidth=1)
ax.axvline(x=0, color='k', linewidth=1)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.grid()
axs[0].set_xlim(0,4*T+Ts)
axs[0].set_xticks(np.arange(0, 4*T, 0.2))
axs[0].set_ylim(-1.1, 1.1)
axs[0].set_yticks(np.arange(-1, 1.1, 0.5))
axs[0].tick_params(labelsize=18)
axs[1].set_xlim(min(k1),max(k1)+1)
axs[1].set_xticks(np.arange(min(k1),max(k1)+1,1))
axs[1].set_ylim(0, nfft1/2+2)
axs[1].set_yticks(np.arange(0, nfft1/2+1, nfft1/4))
axs[1].tick_params(labelsize=18)
axs[0].set_ylabel("$x[n]$", fontsize=18)
axs[1].set_ylabel("$|X[k]|$", fontsize=18)
pl.savefig('output/dft-leakage.png', bbox_inches='tight')

Clearly, the number of cycles in the time-domain signal is not an integer, which is why no DFT-analysis frequency would perfectly match this input. A sudden truncation at the endpoint causes all frequencies to contribute in the formation of this signal, giving rise to leakage in those frequencies.

Since almost all real-world signals are not perfect sinusoids, we encounter DFT leakage in spectral analysis.