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.