Figure generators
The SVG figures in these notes are pasted into the .md as inline SVG — that is what
makes a note self-contained and exportable. These scripts are what produced them.
Keep them. A figure whose numbers came out of a script should be regenerated, never hand-edited: the alternative is a drawing that slowly stops agreeing with the prose, and that failure is invisible until a reader adds two of its numbers together.
| Script | Figure | In term-subcarrier.md |
|---|---|---|
numerology.py |
not a figure — the Carrier every other script reads its numbers from |
— |
subcarrier_combined.py |
the four magnifications, channel → carrier → block → grid | fig-sub-zoom, fig-sub-zoom60 |
subcarrier_budget.py |
where the 100 MHz goes, one bar with breaks | fig-sub-budget, fig-sub-budget60 |
subcarrier_window.py |
the block grid, and the carrier as a window onto it | fig-sub-window, fig-sub-window60 |
subcarrier_ladder.py |
the four levels at the bottom of the band | fig-sub-ladder, fig-sub-ladder60 |
subcarrier_asym.py |
why an even grid is not symmetric about its reference | fig-sub-asym |
guard_band.py |
both guard bands with the spectrum coming down them; also checks all 41 entries of TS 38.101-1 Table 5.3.3-1, and emits the two Chart.js series | fig-sub-guard, and the data behind fig-sub-skirt / fig-sub-guardsweep / tab-sub-guard |
ofdm.py |
the mathematics of term-ofdm.md, computed: the closure assertions of TS 38.211 clause 5.3.1, the cyclic prefix as a circular shift, the circulant/Toeplitz diagonalisation (its own radix-2 FFT and matrix product), the Dirichlet-kernel ICI coefficients, a water-filling solve, and a Monte-Carlo PAPR run |
fig-ofdm-cp, fig-ofdm-matrix, and the data behind fig-ofdm-chan / fig-ofdm-papr / fig-ofdm-ici / fig-ofdm-paprsim / fig-ofdm-water |
pdsch_tbs.py |
TS 38.214 §5.1.3.2 transport block size — used to check term-pdsch.md, draws nothing |
— |
Every subcarrier script takes --scs 30 (the default) or --scs 60, and takes every number
from numerology.py, which derives them from the numerology, the block count of TS 38.101-1
Table 5.3.2-1, point A and offsetToCarrier. Nothing derived is typed in anywhere, so the two
numerologies of a figure cannot disagree — and python3 numerology.py prints both carriers and
asserts the guard band against Table 5.3.3-1.
cd ~/Documents/md-engine/"5G Systems Notes"/figures
python3 subcarrier_combined.py --scs 60 > /tmp/fig.svg # then paste into the note, inside
# <div style="overflow-x:auto"> … </div>
python3 guard_band.py --check # reproduce Table 5.3.3-1 entry by entry
python3 guard_band.py --table # the <tr> rows of tab-sub-guard
python3 guard_band.py --skirt # Chart.js data for fig-sub-skirt
python3 guard_band.py --sweep # Chart.js data for fig-sub-guardsweep
python3 ofdm.py --check # T_u = 1/df and the 1 ms closure, all seven numerologies
python3 ofdm.py --svg # fig-ofdm-cp
python3 ofdm.py --chan # Chart.js data for fig-ofdm-chan
python3 ofdm.py --papr # Chart.js data for fig-ofdm-papr
python3 ofdm.py --matrix # fig-ofdm-matrix, plus the off-diagonal energies
python3 ofdm.py --ici # fig-ofdm-ici, and the Parseval check on the coefficients
python3 ofdm.py --water # fig-ofdm-water, and the capacity table of §10
python3 ofdm.py --paprsim # fig-ofdm-paprsim — 40 000 symbols, about 20 seconds
cd ~/Documents/md-engine && ./.venv/bin/python tools/check_svg_labels.py
Check after every regeneration. tools/check_svg_labels.py measures each label in the
browser and reports any that fall outside the frame; tools/check_notes.py covers the rest.