Aliasing
Contents
2.2. Aliasing#
The previous section introduced uniform sampling, which allows us to represent a continuous signal \(x(t)\) by a discrete sequence of sample values \(x[n]\).
In this section, we’ll see that this idea comes with some restrictions.
2.2.1. What is aliasing?#
Aliasing is the name we give to the phenomenon when two distinct continuous signals \(x_1(t)\) and \(x_2(t)\) produce the same sequence of sample values \(x[n]\) when sampled at a fixed rate \(f_s\). More specifically, we usually think of aliasing in terms of pure (sinusoidal) tones \(x(t) = A \cdot \cos\left(2\pi \cdot f \cdot t + \phi\right)\).
(Aliasing)
Given a sampling rate \(f_s\), two frequencies \(f\) and \(f'\) are aliases of each other if for some integer \(k\),
If sampled at rate \(f_s\), two waves \(x\) (at frequency \(f\)) and \(y\) (at frequency \(f'\))
will produce identical samples: \(x[n] = y[n]\) for all \(n = 0, 1, 2 \dots\).
Or, in words, frequency \(f'\) is \(f\) plus some whole number multiples of the sampling rate \(f_s\). Equation (2.3) is known as the aliasing equation, and it tells us how to find all aliasing frequencies for a given \(f\) and sampling rate.
Fig. 2.3 illustrates this effect: for any \(f\) that we choose, once a sampling rate \(f_s\) is chosen, there are infinitely many frequencies that produce an identical sequence of samples.
2.2.2. Why is aliasing a problem?#
Aliasing is an unavoidable consequence of digital sampling: there will always be frequencies that look the same after sampling. The consequence of this fact is that once you’ve sampled a signal, you may not be able to determine the frequency of the wave that produced the samples you’ve observed.
We’ll see in the next section that the Nyquist-Shannon theorem suggests a resolution to this problem, but for now let’s work toward a better understanding of why aliasing occurs.
2.2.3. Why does aliasing happen?#
In the previous section, we sampled a pure tone by using the following equation
To see why aliasing occurs between frequencies \(f\) and \(f +\red{k \cdot f_s}\), we can plug the latter into the equation above and see what happens. The key idea that we’ll need is the following identity:
This identity works because if \(m\) is an integer, then \(2\pi\cdot m\) is a whole number of rotations around the circle (clockwise if \(m < 0\), counter-clockwise if \(m > 0\)). As a result, adding \(2\pi\cdot m\) to any angle \(\theta\) leaves you back at \(\theta\).
By analogy, you can think of \(\theta\) as the minute hand on a clock, and each \(m\) counts an hour offset. Regardless of the current time, adding one hour (or two, or three, counted by \(m\)) will leave the minute hand in exactly the same place.
2.2.3.1. Proof of aliasing#
The proof of Theorem 2.1 uses a bit of algebra to re-arrange the terms of the equation, and then exploits the identity defined above to simplify the equation. We start with the definition for the sequence generated by sampling a pure tone at \(f + \red{k\cdot f_s}\), and work our way toward the sequence generated by \(f\). We’ll ignore amplitude \(A\) and phase offset \(\phi\) to avoid cluttering the notation, but the argument goes through just as well when those features are included.
Proof.
This shows that the two frequencies \(f\) and \(f + \red{k\cdot f_s}\) produce the same sequence of samples, regardless of \(k\). (But definitely depending on \(f_s\).)
If we were to listen to sampled tones at these frequencies, we shouldn’t be able to tell them apart. Let’s test that hypothesis.
2.2.4. Example: aliased tones#
The following code example generates two tones at aliasing frequencies, but is otherwise similar to the example in the previous section. The two sequences of samples will be numerically identical, and therefore sound identical.
Here, we just used \(k=1\), but any integer \(k\) will produce the same results.
import numpy as np
from IPython.display import display, Audio
# Sampling at 8 KHz
fs = 8000
# We'll make a pure tone at 220, and at 220 + 8000 = 8220
f0_original = 220
f0_aliased = f0_original + fs
# 2 seconds of audio should be plenty
duration = 2
# How many samples is 2 seconds?
N = int(duration * fs)
# Generate the sample positions
n = np.arange(N)
# Construct the first signal
x_original = np.cos(2 * np.pi * f0_original * n / fs)
# And the aliased signal
x_aliased = np.cos(2 * np.pi * f0_aliased * n / fs)
# Let's hear them both
print("fs = {} Hz, f0 = {} Hz".format(fs, f0_original))
display(Audio(data=x_original, rate=fs))
print("fs = {} Hz, f0 aliased = {} Hz".format(fs, f0_aliased))
display(Audio(data=x_aliased, rate=fs))
fs = 8000 Hz, f0 = 220 Hz
fs = 8000 Hz, f0 aliased = 8220 Hz
2.2.5. Summary#
Although we usually view aliasing as a problem to be overcome, it is not always a bad thing. In some applications (though usually not audio), aliasing can be exploited to efficiently sample high-frequency signals with low sampling rates.
Within the context of audio, one of the most important applications of aliasing is in the development of Fast Fourier Transform (FFT) algorithms, covered later in this text.
The important thing is not necessarily to prevent aliasing, but to understand its effects thoroughly.