Note
Go to the end to download the full example code.
Bit error rate of M-QAM over AWGN#
The waterfall curves of Gray-mapped 4-, 16- and 64-QAM over the additive white Gaussian noise channel, against the closed form
\[P_b \approx \frac{4}{\log_2 M}\left(1 - \frac{1}{\sqrt{M}}\right)
Q\left(\sqrt{\frac{3 \log_2 M}{M - 1}\frac{E_b}{N_0}}\right)\]
(Proakis & Salehi, Digital Communications, Sec. 4.3). The campaign is a declarative specification run on every core by the Rust runner: each point stops after 200 bit errors, and the sweep of a constellation stops once its BER falls below \(10^{-6}\). The error bars are the 95 % Wilson intervals.
import matplotlib.pyplot as plt
import numpy as np
import comnum
from comnum.plot import BACKGROUND, MUTED
results = comnum.run_spec(
"""
name: qam_awgn
seed: 2024
grid:
m: [4, 16, 64]
ebn0_db: {from: 0, to: 20, step: 1}
link:
- {type: source, constellation: {kind: qam, m: $m}, n_symbols: 8192}
- {type: awgn_ebn0, ebn0_db: $ebn0_db}
metrics: {report: [ber]}
stop: {min_errors: {ber: 200}, min_trials: 8, max_trials: 4000}
callbacks:
skip_below: {metric: ber, threshold: 1e-6, along: ebn0_db}
"""
)
Simulated points with their intervals, and the closed form.
fig, ax = plt.subplots()
eb = np.linspace(0, 20, 201)
for m in [4, 16, 64]:
# the conditions skipped below 1e-6 have no metric: series leaves them out
x, ber, lo, hi = results.series("ebn0_db", "ber", interval=True, m=m)
keep = ber > 0
(line,) = ax.semilogy(eb, comnum.theory_ber(m, eb, "qam"), linewidth=1.4)
ax.errorbar(
x[keep],
ber[keep],
yerr=[ber[keep] - lo[keep], hi[keep] - ber[keep]],
fmt="o",
color=line.get_color(),
markeredgecolor=BACKGROUND,
capsize=0,
label=f"{m}-QAM",
)
ax.plot([], [], color=MUTED, linewidth=1.4, label="theory")
ax.set(
ylim=(1e-6, 0.5),
xlim=(0, 20),
xlabel="$E_b/N_0$ (dB)",
ylabel="bit error rate",
title="Gray-mapped QAM over AWGN",
)
ax.legend()
fig.tight_layout()

The Eb/N0 needed for a BER of 1e-5 grows by 3.8 dB from QPSK to 16-QAM, and by 4.4 dB more to 64-QAM: the price of two more bits per symbol.
4-QAM: Eb/N0 = 9.59 dB at a BER of 1e-5
16-QAM: Eb/N0 = 13.43 dB at a BER of 1e-5
64-QAM: Eb/N0 = 17.79 dB at a BER of 1e-5
Total running time of the script: (0 minutes 3.877 seconds)