Waveforms beyond OFDM#

CP-OFDM turns a frequency-selective channel into flat subcarriers at the price of a prefix, of sidelobes that fall slowly, and of interference between subcarriers when the channel changes within a symbol. comnum implements the waveforms proposed to relax each of these limits, with the same conventions as Ofdm: modulate takes the symbols of whole blocks and returns the samples, demodulate inverts it, and the round trip is exact without a channel. The gallery page Filtered multicarrier and OTFS compares their spectra.

FBMC/OQAM#

Fbmc shapes every subcarrier with the PHYDYAS prototype of overlapping factor 2, 3 or 4 and drops the prefix. Orthogonality holds in the real sense only, so each subcarrier carries a real symbol every half symbol: oqam_stagger() splits complex symbols into them and oqam_destagger() takes the real part back.

import comnum, numpy as np

fbmc = comnum.Fbmc(64, overlap=4)
c = comnum.Constellation("qam", 16).normalized().map_bits(comnum.bits(4 * 64 * 20, comnum.Rng(1)))
x = fbmc.modulate(comnum.oqam_stagger(c, 64))
c_hat = comnum.oqam_destagger(fbmc.demodulate(x).real, 64)

The prototype spans \(K\) symbols, so a burst is \(K - 1/2\) symbols longer than its data, and PHYDYAS is orthogonal only nearly: the round trip leaves a residual of about \(10^{-3}\), far below any noise of interest. In a doubly-selective channel the residual imaginary interference adds to the noise; validation/tests/multicarrier/nissel2017.rs compares FBMC with OFDM and CP-OFDM in the Vehicular A channel of Nissel & Rupp (2017).

UFMC#

Ufmc groups the active subcarriers in sub-bands and filters each one with a Dolph–Chebyshev filter of length \(L\) (chebyshev_window()), as Schaich & Wild (2014) propose: the filter’s ramps replace the prefix, a symbol is \(N + L - 1\) samples, and the receiver takes a DFT of \(2N\) points and keeps the even bins. The filter attenuates the subcarriers at the edges of each sub-band, which the one-tap equaliser undoes.

OTFS#

Otfs places \(MN\) symbols on a grid of \(M\) delays and \(N\) Doppler shifts, maps them to time and frequency by the inverse symplectic Fourier transform and transmits them as OFDM symbols with one prefix per frame. A channel of paths of whole delays and Doppler bins is a 2-D circular convolution on that grid: effective_channel() builds it (Raviteja et al. 2019, Theorem 1), and delay_doppler_paths() draws paths from a power-delay profile with Jakes’ Doppler.

otfs = comnum.Otfs(32, 16, n_cp=8)
fs = 32 * 15e3
paths = comnum.delay_doppler_paths("eva", 500.0, fs, comnum.Rng(2))
# paths: (gain, delay in samples, Doppler in Hz); with whole bins
# (Doppler multiple of fs / (M N)), H @ x is the demodulated frame
H = otfs.effective_channel([(1.0, 0, 0.0), (0.5, 3, fs / 512)], fs)

Every path then reaches every symbol: a detector on the whole frame (linear MMSE on H, or message passing) collects the full diversity of the channel, where one-tap OFDM loses it (validation/tests/multicarrier/raviteja2019.rs).

Chirp spread spectrum#

Css is the modulation of LoRa: \(2^{SF}\) cyclic shifts of an up-chirp, detected by dechirping and an FFT. theory_css_ser() and theory_css_ber() give the error rates over AWGN, the css_* functions the spectrum and the interference between two signals; the gallery page The LoRa modulation: error rates, spectrum and interference compares them with three papers.