In this article, we will illustrate several basic properties of the Fourier Transform, which are essential for working with the transform from day to day. First, let us define a Python function which approximates the Fourier transform
$$ X(f) = \int_{-\infty}^{\infty}x(t)\exp(-j2\pi ft) dt $$from a sampled version $x(\frac{n}{F_s}-t_0), n=0,\dots,N-1$ of the signal, by using the discrete Fourier transform (refer to Approximation of Fourier Transform with DFT for an explanation):
def ft(samples, Fs, t0):
"""Approximate the Fourier Transform of a time-limited signal
by means of the discrete Fourier Transform.
samples: signal values sampled at the positions t0 + n/Fs
Fs: Sampling frequency of the signal
t0: starting time of the sampling of the signal
"""
f = np.linspace(-Fs/2, Fs/2, len(samples), endpoint=False)
return np.fft.fftshift(np.fft.fft(samples)/Fs * np.exp(-2j*np.pi*f*t0))
Let us start with some basic known correspondences of the Fourier Transform and plot these to ensure the correctnes of our Fourier Transform function.
Fs = 10000 # The sampling frequency we use for the simulation
t0 = 10 # The half time interval we look at
t = np.arange(-t0, t0, 1/Fs) # the time samples
f = np.arange(-Fs/2, Fs/2, Fs/len(t)) # the corresponding frequency samples
g = lambda t: (abs(t) <=0.5).astype(float) # define rect function
plt.subplot(121)
plt.plot(t, g(t))
plt.subplot(122)
plt.plot(f, ft(g(t), Fs, -t0).real)
g = lambda t: np.sin(2*np.pi*2*t)
plt.subplot(121)
plt.plot(t, g(t))
plt.subplot(122)
plt.plot(f, ft(g(t), Fs, -t0).real, label='real')
plt.plot(f, ft(g(t), Fs, -t0).imag, label='imag')
g = lambda t: np.cos(3*np.pi*2*t)
plt.subplot(121)
plt.plot(t, g(t))
plt.subplot(122)
plt.plot(f, ft(g(t), Fs, -t0).real, label='real')
plt.plot(f, ft(g(t), Fs, -t0).imag, label='imag')
g = lambda t: np.exp(-abs(t*t))
plt.subplot(121)
plt.plot(t, g(t))
plt.subplot(122)
plt.plot(f, ft(g(t), Fs, -t0).real, label='real')
plt.plot(f, ft(g(t), Fs, -t0).imag, label='imag')
g = lambda t: np.sinc(t)**2
plt.subplot(121)
plt.plot(t, g(t))
plt.subplot(122)
plt.plot(f, ft(g(t), Fs, -t0).real, label='real')
plt.plot(f, ft(g(t), Fs, -t0).imag, label='imag')
plt.tight_layout()
The Fourier Transform is a linear operation, i.e. it does not matter if you perform scaling and summation of two functions before or after Fourier Transform. Let $x(t)$ and $y(t)$ be two signals. Then the following holds:
\begin{align} \mathcal{F}\{x(t)\} &= X(f) \\ \mathcal{F}\{y(t)\} &= Y(f) \\ \mathcal{F}\{ax(t)+by(t)\} &= aX(f)+bY(f) \\ \end{align}Let us verify this operation numerically by calculating the Fourier transform of a the sum of a rect plus a Gaussian:
def rect(t):
return (abs(t) <= 0.5).astype(float)
def gauss(t):
return np.exp(-t*t)
def triang(t):
return (1-abs(t)) * rect(t/2)
tstart = 10
Fs = 1000
t = np.arange(-tstart, tstart, 1/Fs)
f = np.arange(-Fs/2, Fs/2, Fs/len(t))
x = rect
y = gauss
X = ft(x(t), Fs, -tstart)
Y = ft(y(t), Fs, -tstart)
def showLinearity(a):
plt.subplot(221)
plt.plot(t, x(t), label='$x(t)$')
plt.plot(t, y(t), label='$y(t)$')
plt.subplot(222)
plt.plot(f, X.real, label='$X(f)$')
plt.plot(f, Y.real, label='$Y(f)$')
xplusay = lambda t: x(t) + a*y(t)
XplusaY = ft(xplusay(t), Fs, -t0)
plt.subplot(223)
plt.plot(t, xplusay(t), '-*', label='$x(t)+ay(t)$', markevery=400)
plt.plot(t, x(t), label='$x(t)$')
plt.plot(t, a*y(t), label='$ay(t)$')
plt.subplot(224)
plt.plot(f, XplusaY.real, '-*', label='$X(f)+aY(f)$', markevery=10)
plt.plot(f, X.real, label='$X(f)$')
plt.plot(f, a*Y.real, label='$aY(f)$')
As visible, the Fourier Transform of a linear combination of two input signals is the same linear combination of the Fourier Transforms of the input signal.
The time-shifting property means that a shift in time corresponds to a phase rotation in the frequency domain:
$$\mathcal{F}\{x(t-t_0)\} = \exp(-j2\pi ft_0)X(f). $$Let us verify this property numerically with the code below. For example, see that for a time-shift of 1s, in the spectrum the phase rotates by one full period every Hz.
x = rect
X = ft(x(t), Fs, -tstart)
def showShift(t0):
plt.gcf().clear()
plt.subplot(121)
plt.plot(t, x(t), label='$x(t)$')
plt.plot(t, x(t-t0), label='$x(t-t_0)$')
plt.subplot(122)
Xt0 = ft(x(t-t0), Fs, -tstart)
plt.plot(f, X.real, label='Re $X(f)$')
plt.plot(f, Xt0.real, label='Re $X_{t_0}(f)$')
plt.plot(f, Xt0.imag, label='Im $X_{t_0}(f)$')