Skip to main content
Ctrl+K

comnum

  • Getting started
  • Tutorials
  • User guide
  • Gallery of classical results
  • Why the results can be trusted
  • Maturity
    • Comparisons with other libraries
    • Why comnum
    • Benchmarks against other libraries
    • Classical performance
    • API reference
  • GitHub
  • Getting started
  • Tutorials
  • User guide
  • Gallery of classical results
  • Why the results can be trusted
  • Maturity
  • Comparisons with other libraries
  • Why comnum
  • Benchmarks against other libraries
  • Classical performance
  • API reference
  • GitHub

Section Navigation

  • Foundations
    • Constellations and Gray labels
    • Bit error rate of M-QAM over AWGN
    • M-PSK over AWGN with the Python runner
    • BPSK over flat Rayleigh fading
    • Information rates of QAM
  • Coding and shaping
    • Channel coding: from convolutional codes to the finite-length limit
    • Hu, Eleftheriou & Arnold 2005, Fig. 5: a regular PEG code of length 504
    • 5G NR channel codes: LDPC and polar
    • Probabilistic shaping: the rate gain and probabilistic amplitude shaping
  • Waveforms
    • PAPR of OFDM: the CCDF and its closed forms
    • PAPR reduction techniques
    • OFDM with impairments: imperfect channel estimates and nonlinear amplifiers
    • Proakis & Salehi 2008, Sec. 3.3-2: MSK has the error rate of BPSK
    • Thompson et al. 2008, Fig. 2: the CE-OFDM phase demodulator
    • CE-OFDM receivers, spectrum and bandwidth
    • The LoRa modulation: error rates, spectrum and interference
    • Filtered multicarrier and OTFS
  • MIMO
    • MIMO detection: linear, successive cancellation and maximum likelihood
    • Transmit diversity: Alamouti and space-time block codes
    • Alamouti 1998, Fig. 4: transmit diversity against MRRC
    • Ergodic capacity of MIMO channels
    • Large MIMO: message passing (LAMA and OAMP)
    • Biguesh & Gershman 2006, Fig. 4: LS, scaled LS, relaxed MMSE and LMMSE
  • Optical
    • Chromatic dispersion and its compensation
    • Nonlinear fibre propagation and digital back-propagation
    • Häger & Pfister 2021, Fig. 5: linear equalisation and back-propagation
    • WDM nonlinearity: the GN model and digital back-propagation
    • Raman amplification: pumps, gain flattening and the hybrid link
    • Mach–Zehnder and IQ modulators
    • Optical wireless: an LED-lit room and a turbulent free-space link
  • Hardware impairments
    • Power amplifiers and digital predistortion
    • DAC and ADC: quantisation, clipping and jitter
  • Receiver DSP
    • Nyquist pulse shaping and the eye diagram
    • OFDM over multipath: channel estimation
    • OFDM synchronisation and MIMO-OFDM
    • Blind receiver: modulation recognition, SNR and symbol rate
  • Metrics and analysis
    • Analysing a link: variants, probes and signals
  • Systems and standards
    • DVB-S2: MODCODs, constellations and frame error rates
    • 5G NR: numerology, synchronisation block and reference signals
    • DVB-S: the error rate after the Viterbi decoder against Table 3
    • DVB-T: the frame and the error rate after the Viterbi decoder against Annex A
    • CCSDS: the codes of telemetry against the normal approximation
    • 5G NR PDSCH: TS 38.101-4 test 1-2 against the RAN4 alignment
  • Simulation and specifications
    • A coherent receiver as a chain of blocks
    • Wireless links as chains: diversity, OFDM channel estimation, coded modulation
    • Recorded signals: SigMF in, blind receiver out
    • Coverage of the confidence intervals of an error rate
  • Gallery of classical results
  • Metrics and analysis
  • Analysing a link: variants, probes and signals

Note

Go to the end to download the full example code.

Analysing a link: variants, probes and signals#

The tools of the analysis after a campaign: comnum.compare() runs variants of a chain on the same grid, comnum.Results plots them with their confidence intervals and a closed form, probes measure inside the link during the campaign, and comnum.Chain.trace() with comnum.plot shows the signal after every block.

Variants. 16-QAM with Wiener phase noise (increments of 0.02 rad per symbol) and blind phase search (Pfau, Hoffmann & Noé 2009): a window of 5 symbols averages so little noise that the estimate slips by π/2 (cycle slips, after which every symbol is in error until the next slip); a long window averages the noise but lags the phase, and its errors fall slowly; the best window lies in between, within about 1 dB of the AWGN curve near a BER of 10⁻³.

Probes. LDPC-coded 16-QAM over fast Rayleigh fading: a probe after the MIMO detector measures the symbol error rate before decoding in the same campaign as the error rate after decoding.

Signals. The constellation, the eye diagram and the spectrum after each block of a root-raised-cosine link with phase noise.

import matplotlib.pyplot as plt
import numpy as np

import comnum
from comnum import blocks as B
from comnum.plot import GREY

Variants: the window of blind phase search.

link = comnum.Chain(
    tx=B.Source(constellation="16-QAM", n_symbols=8192),
    pn=B.PhaseNoise(sigma_rad=0.02),
    ch=B.AwgnEbn0(ebn0_db=comnum.var("ebn0_db")),
    cpr=B.Bps(n=8, b=32),
    sync=B.FrameSync(length=256),
)
variants = {f"BPS, {2 * n + 1} symbols": link.replace(cpr=B.Bps(n=n, b=32)) for n in [2, 8, 32]}
res = comnum.compare(
    variants,
    grid={"ebn0_db": list(np.arange(8.0, 16.1, 1.0))},
    metrics={"report": ["ber"], "skip_start": 64, "skip_end": 64},
    stop={"min_errors": {"ber": 200}, "max_trials": 200},
)
ax = res.plot("ebn0_db", "ber", by="variant", min_count=20)
eb = np.linspace(8, 16, 81)
ax.semilogy(eb, comnum.theory_ber(16, eb, "qam"), color=GREY, label="AWGN")
ax.set(
    xlabel="$E_b/N_0$ (dB)", ylabel="bit error rate", title="16-QAM, phase noise 0.02 rad/symbol"
)
ax.legend(loc="lower left")
ax.figure.tight_layout()
16-QAM, phase noise 0.02 rad/symbol

Probes: the symbol errors before decoding and the bit errors after it, in one campaign.

code = {"type": "ldpc", "n": 1024, "k": 512, "seed": 1}
bicm = comnum.Chain(
    enc=B.FecEncode(code=code, blocks=4, interleave=True),
    map=B.Map(constellation="16-QAM"),
    fading=B.Rayleigh(coherence_samples=1),
    noise=B.AwgnEbn0(ebn0_db=comnum.var("ebn0_db")),
    rx=B.MimoRx(),
    demap=B.Demap(),
    dec=B.FecDecode(code=code, interleave=True),
).probe("rx", ["ser"], scale="none")
res = bicm.run(
    grid={"ebn0_db": list(np.arange(5.0, 9.1, 0.5))},
    metrics=["coded_ber"],
    stop={"min_errors": {"coded_ber": 100}, "min_trials": 4, "max_trials": 200},
)
print(res)
fig, ax = plt.subplots()
res.plot("ebn0_db", "rx.ser", ax=ax)
res.plot("ebn0_db", "coded_ber", ax=ax, min_count=20)
ax.legend(["symbol errors before decoding", "bit errors after decoding"], loc="lower left")
ax.set(
    xlabel="$E_b/N_0$ (dB)", ylabel="error rate", title="LDPC (1024, 512), 16-QAM, fast Rayleigh"
)
fig.tight_layout()
LDPC (1024, 512), 16-QAM, fast Rayleigh
Results: 9 conditions, metrics ['coded_ber', 'rx.ser']
  (ebn0_db=5.0) 32 trials: coded_ber=0.091, rx.ser=0.4565
  (ebn0_db=5.5) 32 trials: coded_ber=0.04948, rx.ser=0.4348
  (ebn0_db=6.0) 32 trials: coded_ber=0.01489, rx.ser=0.4057
  (ebn0_db=6.5) 64 trials: coded_ber=0.00164, rx.ser=0.3843
  (ebn0_db=7.0) 128 trials: coded_ber=0.0005379, rx.ser=0.362
  (ebn0_db=7.5) 200 trials: coded_ber=0, rx.ser=0.3349
  (ebn0_db=8.0) 200 trials: coded_ber=0, rx.ser=0.3155
  (ebn0_db=8.5) 200 trials: coded_ber=0, rx.ser=0.2938
  (ebn0_db=9.0) 200 trials: coded_ber=0, rx.ser=0.2713

Signals: the frame after every block of one trial.

shaped = comnum.Chain(
    tx=B.Source(constellation="16-QAM", n_symbols=4096),
    shaping=B.PulseShape(sps=8, rolloff=0.2),
    channel=B.Awgn(snr_db=22.0),
    pn=B.PhaseNoise(sigma_rad=0.002),
    mf=B.MatchedFilter(rolloff=0.2),
    decimate=B.Decimate(),
    cpr=B.Bps(n=16, b=32),
)
frames = shaped.trace({}, comnum.Rng(3))
frames.plot("constellation", stages=["channel", "decimate", "cpr"], skip=256)

fig, (a, b) = plt.subplots(1, 2, figsize=(9, 3.4))
comnum.plot.eye(frames["mf"], ax=a, skip=1024)
a.set_title("eye diagram after the matched filter")
comnum.plot.psd(frames["channel"], ax=b, nfft=512)
b.set_title("spectrum at the channel output")
fig.tight_layout()
  • channel, decimate, cpr
  • eye diagram after the matched filter, spectrum at the channel output

Total running time of the script: (0 minutes 5.822 seconds)

Download Jupyter notebook: plot_01_analysis.ipynb

Download Python source code: plot_01_analysis.py

Download zipped: plot_01_analysis.zip

Gallery generated by Sphinx-Gallery

previous

Metrics and analysis

next

Systems and standards

Show Source

© Copyright 2022-2026, Vincent Choqueuse.

Created using Sphinx 9.1.1.

Built with the PyData Sphinx Theme 0.24.0.