Signal Atlas comms-v1 Phase-2 RF physics validation¶
Validated with fixes applied, documented limitations, one corrected finding (section 3.7), and known open defects awaiting a user decision (section 5).
Post-migration evidence mapping
This report measured the former SionnaCIRDataset composite at the report’s
pinned commit. The current graph authoring surface separates that behavior:
declared_power_delay_profile owns the two analytic PDP tables and their RMS
facts, pdp_rayleigh_fading owns the seeded complex-Gaussian tap draw, and
apply_cir owns unit-transfer CIR application to a separately power-scaled
waveform. The composite selector is no longer registered.
The report’s PDP formula, tap-count, and RMS-delay evidence maps to
DeclaredPowerDelayProfile; its per-tap Rayleigh statistics map to
PDPRayleighFading. Measurements of the old composite’s final IQ, Sionna
CIRDataset loader behavior, and process-global RNG containment do not by
themselves validate the replacement graph. The replacement has focused typed,
determinism, Sionna-application, and stock-graph tests; report-critical comms
measurements are rerun against replacement pilots in the later mature-variant
regression milestone before any conclusion is carried forward.
1. The component¶
This validation covers two groups of Phase-2 additions to the core rfgen
package, treated as one component because they are the RF-physics surface a
single Phase-2 delivery introduced.
Group A (src/rfgen/waveforms/ and src/rfgen/domains/comms/): three Sionna-backed emitters plus a
shared bandwidth-sizing helper.
NRPuschEmitter(cellular.py): a 5G NR (New Radio) PUSCH (Physical Uplink Shared Channel) transmitter, completed from a Pass-1 stub by delegating all uplink PHY (physical-layer) work tosionna.phy.nr.PUSCHTransmitter/PUSCHConfig.ConformantOFDMEmitter(ofdm_conformant.py): a genuine OFDM (Orthogonal Frequency-Division Multiplexing) resource grid with a real pilot pattern, built onsionna.phy.ofdm.ResourceGrid.FECConstellationEmitter(fec_constellation.py): a single-carrier, FEC-coded QAM emitter supporting four code families (LDPC, Polar, Turbo, convolutional) via Sionna’ssionna.phy.fecencoders.resource_blocks_for_bandwidth(_resource_grid.py, “Amendment 4”): a shared helper mapping a requested occupied bandwidth to a resource-block (one resource block is 12 subcarriers, 3GPP TS 38.211 section 4.4.4.1) count, used by bothNRPuschEmitterandConformantOFDMEmitter’soccupied_bandwidth_hzoverride, plus the use-case-level sampler constraint (constrain_bandwidth_for_wideband_classes, inuse_cases/signal-atlas/comms-v1/rfgen_signal_atlas_comms/joint_sampler/constraints.py) that excludes bandwidth-ladder rungs below each class’s physical minimum.
# NRPuschEmitter usage (Group A). Interface signature; not a standalone program.
signal = NRPuschEmitter().generate(
class_label="nr_pusch",
sample_rate=60_000_000.0,
duration_s=1e-3,
f_offset_hz=0.0,
rng=torch.Generator().manual_seed(11),
params=NRPuschParams(occupied_bandwidth_hz=20_000_000.0),
)
assert signal.metadata.bandwidth_hz == 20_520_000.0 # realized, not requested
Group B (src/rfgen/engine/propagation_sionna.py): three further
Sionna-backed statistical channels.
RayleighBlockFading: one complex Gaussian gain per call, tiled over the requested time steps (i.i.d. block fading, no delay spread).SionnaFlatFading: a correlated flat-fading channel exercising Sionna’sKroneckerModel/PerColumnModelspatial-correlation machinery, repurposed here as a time-block correlation axis for a single-antenna signal.SionnaCIRDataset: asionna.phy.channel.CIRDataset-backed channel over one of two declared, generic (non-3GPP) exponential power-delay profiles.
# RayleighBlockFading usage (Group B). Interface signature; not a standalone program.
out_signal = RayleighBlockFading().apply(signal, channel_context)
Every Group A/B class is a simulator-to-simulator component: emitters generate baseband waveforms in isolation, and channels transform them with statistical or Sionna-native physics, never a reconstruction of an over-the-air capture. This validation makes no sim-to-real fidelity or transfer-performance claim about any class in either group.
2. What we validated¶
This validation establishes seven load-bearing claims. Each is restated and supported by evidence in section 3.
Library-first construction (§3.1): every class in both groups delegates its RF math to Sionna, and no PHY, FEC, or fading algorithm is reimplemented in rfgen.
NR PUSCH is conformant by delegation at the transmitter (§3.2): real DMRS placement and a real, sized transport block, though a rendered example window is not guaranteed to contain a whole slot.
Conformant OFDM is standards-shaped, explicitly uncoded (§3.3): a real resource grid and pilot pattern at correct 3GPP numerology, with no transport-block encoding, sharing NR PUSCH’s slot-fragment truncation exposure.
Amendment-4 bandwidth-to-resource-block mapping is physically sound, with an MCS-contingent floor (§3.4): realized occupied bandwidth tracks the requested value; the excluded 200 kHz rung sits below each class’s real structural floor, and that floor is arithmetic conditioned on the pipeline’s pinned MCS, not a fixed physical constant.
All four FEC families genuinely encode and decode (§3.5): real bit recovery through a real Sionna decoder, at the code rate the emitter itself reports as achieved.
The three new channels produce statistically correct, distinct fading (§3.6): Rayleigh amplitude, controllable correlation, and profile-consistent delay spread all hold under direct statistical test.
Determinism holds given the same seed, with a corrected fix (§3.7): a hidden RNG leak was found in all three Group A emitters; the first fix and its regression tests were both incomplete, and this pass corrects both, proven under true process isolation.
Limits and scope-bounded items appear in section 4; known open defects awaiting a user decision appear in section 5; full citations are in section 6.
3. Evidence per claim¶
3.1 Library-first construction¶
Claim. No class in Group A or Group B reimplements PHY, FEC, or fading math; every one delegates to a real Sionna (or, for FEC pulse shaping, TorchSig) entry point.
Evidence. Reading cellular.py, ofdm_conformant.py, and
fec_constellation.py confirms each constructs a real Sionna object
(PUSCHTransmitter, ResourceGrid/OFDMModulator, and one of
LDPC5GEncoder/Polar5GEncoder/TurboEncoder/ConvEncoder) and calls it
directly; fec_constellation.py’s pulse shaping reuses
torchsig.signals.builders.constellation’s SRRC taps and polyphase
resampler, the same primitives APSKEmitter already uses. _resource_grid.py
is 39 lines of arithmetic (round a bandwidth to the nearest resource-block
count) with zero backend calls, confirmed by reading the full module.
propagation_sionna.py’s three new channels each construct one of
sionna.phy.channel.RayleighBlockFading, FlatFadingChannel, or
CIRDataset and call it directly; the module’s own correlation matrices come
from sionna.phy.channel.exp_corr_mat, KroneckerModel, and
PerColumnModel, not a hand-rolled covariance construction.
3.2 NR PUSCH is conformant by delegation at the transmitter¶
Claim. NRPuschEmitter produces a real 3GPP NR uplink slot at
construction time: a DMRS (Demodulation Reference Signal, the pilot
symbols a real receiver uses to estimate the channel) at a real, non-empty
resource-element pattern, and a genuinely sized, LDPC-encoded transport
block, not a plausible-looking approximation. This conformance holds at
the transmitter: every PHY decision is made by Sionna’s own NR module,
never reimplemented in rfgen. A separate, distinct question is whether one
RENDERED example window is guaranteed to contain a whole slot; it is not,
for a mechanism explained below.
Evidence. Constructing a PUSCHConfig at n_size_grid=52,
subcarrier_spacing=30 kHz, mcs_index=10 and building its
PUSCHTransmitter directly confirms pusch_config.dmrs_symbol_indices == [2] (a single-symbol DMRS at the third OFDM symbol of the 14-symbol slot,
one of 3GPP TS 38.211’s standard single-symbol mapping-type-A
configurations) and that the resulting pilot_pattern.mask carries exactly
624 pilot resource elements at that symbol, one per active subcarrier
(fft_size=624, no guard carriers at this allocation): a real, full-band
DMRS mapping, not a placeholder. pusch_config.tb_size
resolves to 10,760 bits, confirming a genuinely sized transport block (3GPP
TS 38.212’s LDPC base-graph selection, encoder rate matching, and scrambling
all execute inside PUSCHTransmitter; none is reimplemented in rfgen). This
is a standards-conformant construction because it delegates every PHY
decision (resource mapping, DMRS placement, transport-block encoding, OFDM
modulation) to Sionna’s own NR module, which independently implements 3GPP
TS 38.211/38.212 in full.
Mechanism: window duration versus slot duration. NRPuschEmitter.generate
resamples Sionna’s native-rate slot output onto the caller’s requested
sample_rate/duration_s grid, then crops or zero-pads to exactly the
requested sample count (the same crop/pad helper ConformantOFDMEmitter
also uses; see §3.3). The number of OFDM symbols actually visible inside
one rendered window is duration_s / ofdm_symbol_duration, and duration_s
is fixed by the corpus’s window length while a slot’s own duration is fixed
by the numerology, so a wider drawn bandwidth (a larger n_rb, hence a
wider realized sample_rate = 2 * bandwidth_hz) shrinks how much of one
slot fits inside the window, not how much bandwidth fits. Concretely, at the
4096-sample window in force when Phase 2 was measured and mcs=10,
subcarrier_spacing_hz=30 kHz: the narrowest bandwidth-ladder rung (1 MHz)
delivered about 51 of the slot’s 14 OFDM symbols (more than three whole
slots), while the widest rung (20 MHz) delivered only about 2.8 symbols,
cutting through the DMRS symbol (index 2) mid-symbol. Re-measuring the
identical mechanism at 16,384 samples, now the corpus’s window, shows the
DMRS-cut-mid-symbol harm does not reproduce at any rung: the 20 MHz rung
delivers about 11 of 14 symbols, which fully covers the DMRS symbol.
The more general mechanism still holds at 16,384 samples: the 20 MHz rung
still delivers only about 0.79 of one whole slot. Widening the window
reduces, but does not eliminate, the exposure: a rendered example is not
guaranteed to contain a whole NR resource-grid slot, at either window size
measured to date.
3.3 Conformant OFDM is standards-shaped, explicitly uncoded¶
Claim. ConformantOFDMEmitter builds a real resource grid at correct
3GPP numerology (a numerology fixes the subcarrier spacing
and symbol timing a standard defines) with a real pilot pattern, but is
explicitly uncoded: no transport-block encoding, scrambling, or rate
matching, matching what the module’s own docstring already states.
Evidence. Building a ResourceGrid at the module’s fixed constants
(num_ofdm_symbols=14, subcarrier_spacing=15,000 Hz, 3GPP numerology 0,
TS 38.211 Table 4.2-1; pilot_pattern="kronecker") with fft_size=512 and
guard carriers (206, 206) (100 active subcarriers) confirms a real
KroneckerPilotPattern: 200 nonzero pilot resource elements across the two
requested pilot symbols (indices 0 and 7), one genuine orthogonal pilot per
active subcarrier at each pilot symbol. This is a full-band, block-type
pilot arrangement in time (every active subcarrier carries a pilot at the
chosen symbols), a standards-plausible design distinct from a specific named
3GPP DMRS comb-pattern; the module’s own docstring already states this
precisely with “standards-SHAPED” language that scopes the claim to
structure, stopping short of exact protocol conformance. Reading
generate() confirms the data path is BinarySource bits mapped directly
to QPSK by a bare Mapper("qam", 2), with no encoder, scrambler, or
transport-block object constructed anywhere in the method; extras on the
returned signal carry no code-rate or transport-block field, so the uncoded
claim holds structurally, in the code itself, and not only in the module’s
prose (test_conformant_ofdm_places_a_real_pilot_pattern_but_stays_uncoded).
Shared truncation mechanism. ConformantOFDMEmitter builds its
time-domain waveform through the same architecture as NRPuschEmitter
(§3.2): concatenate as many native-rate Sionna slots as needed, resample
onto the requested grid, then crop or zero-pad to the exact requested
sample count through the same shared n_samples helper
(_waveform_contract.py). Nothing in ConformantOFDMEmitter snaps the
rendered length to a whole OFDM symbol or a whole 14-symbol slot, so its
pilot-bearing resource grid is exposed to the identical slot-fragment
truncation mechanism §3.2 describes for NR PUSCH’s DMRS: a truncated render
can cut through a pilot-bearing OFDM symbol exactly as it can cut through
NR PUSCH’s DMRS symbol. This is a property of the render path’s shared
crop/pad mechanism across both Sionna-slot-based Group A emitters, not an
NR-PUSCH-specific caveat.
3.4 Amendment-4 bandwidth-to-resource-block mapping is physically sound, with an MCS-contingent floor¶
Claim. resource_blocks_for_bandwidth rounds a requested occupied
bandwidth to the nearest constructible resource-block count, and the
resulting realized bandwidth stays close to the request at every
bandwidth-ladder rung the sampler’s cross-axis constraint actually allows to
be drawn. The 200 kHz rung is excluded for both wideband classes because it
sits below their real structural floor, not as an arbitrary policy choice.
For NR PUSCH specifically, that floor is arithmetic conditioned on the
pipeline’s pinned MCS index, not a fixed physical constant independent of
MCS.
Evidence. Direct calls to both emitters’
occupied_bandwidth_hz override confirm: at 1 MHz, both NRPuschEmitter
(30 kHz subcarrier spacing, 360 kHz per resource block)
and ConformantOFDMEmitter (15 kHz spacing, 180 kHz per resource block)
realize 1.08 MHz (ratio 1.08; both ladder values happen to round to a
common multiple of the coarser 360 kHz grid); at 5 MHz, both realize
5.04 MHz (ratio 1.008), by the same coincidence. At 20 MHz the two diverge:
ConformantOFDMEmitter (uncoded, no downstream validity search) rounds to
the nearest resource-block count directly and realizes 19.98 MHz (ratio
0.999, the specific 20 MHz-drawn, 19.98 MHz-realized case this
delivery’s own Phase-2 evidence cites); NRPuschEmitter’s naive rounding
(n_rb=56) fails Sionna’s own LDPC base-graph construction check (“Only
coderate>1/3 supported for BG1”, confirmed directly from the raised
ValueError), so _build_pusch_config_for_bandwidth’s documented ±8-RB
search lands on the nearest constructible neighbor (n_rb=57), realizing
20.52 MHz (ratio 1.026). Both emitters’ worst observed overshoot across the
three eligible rungs stays at or below 8%
(test_nr_pusch_bandwidth_ladder_realizes_close_to_requested,
test_conformant_ofdm_bandwidth_ladder_realizes_close_to_requested).
On the exclusion itself: nr_pusch_occupied_bandwidth_bounds_hz at the
schema’s default 30 kHz spacing returns a structural minimum of 360,000 Hz
(one resource block); conformant_ofdm_occupied_bandwidth_bounds_hz at the
schema’s default 36-sample cyclic-prefix length returns a structural minimum
of 540,000 Hz, bounded by the cyclic-prefix length, not the raw one-resource-
block floor, because Sionna’s own ResourceGrid construction requires
cyclic_prefix_length <= fft_size. Both floors exceed the 200 kHz rung and
sit below the 1 MHz rung, so the sampler’s cross-axis constraint
(constrain_bandwidth_for_wideband_classes) correctly excludes exactly the
200 kHz rung and leaves at least one eligible rung to redraw to
(test_200khz_rung_is_genuinely_below_each_wideband_classs_physical_floor).
Calling ConformantOFDMEmitter.generate directly at 200 kHz bypassing the
sampler’s constraint (a real construction, not a mock) confirms why
exclusion is the right call over silently clamping: the emitter cannot
refuse a below-floor bandwidth on its own (Sionna forces the effective grid
up to the cyclic-prefix floor), so a caller that skipped the sampler-level
constraint would silently realize 540 kHz against a 200 kHz request (a 2.7x
overshoot) instead of hitting the exclusion; NRPuschEmitter raises a clear
EmitterError naming the physical minimum instead
(test_nr_pusch_below_physical_minimum_raises_rather_than_silently_clamping).
Both minimums are read from the same bounds helpers the emitters themselves
expose, confirmed by direct comparison against the sampler module’s own
computed constants: no minimum is a hardcoded, independently-drifting number.
The 360 kHz floor is MCS-contingent arithmetic, not a fixed physical
constant. nr_pusch_occupied_bandwidth_bounds_hz’s minimum is
n_rb_min * 12 * subcarrier_spacing_hz, a pure resource-block-grid
computation that takes no MCS argument. Whether n_rb=1 is actually
constructible at that floor depends separately on the transport-block code
rate clearing Sionna’s LDPC base-graph floor, and that floor does depend on
MCS: constructing PUSCHTransmitter at n_rb=1,
subcarrier_spacing_hz=30 kHz fails at mcs=0 and mcs=1 (“Unsupported
coderate (r<1/5)”) and succeeds at mcs=2 and above, including the
pipeline’s pinned default mcs=10. Because the pipeline never varies MCS
away from that default, every render that hits n_rb=1 in practice
succeeds, and the contingency stays invisible during normal operation. The
360 kHz figure is not a bandwidth-grid-only physical constant independent
of coding: it is the floor this dataset’s pinned MCS happens to clear, and
a caller running NRPuschEmitter at a lower MCS index would see a
materially different structural minimum.
3.5 All four FEC families genuinely encode and decode¶
Claim. FECConstellationEmitter’s four supported code families each
build a real Sionna encoder and, run through the matching real Sionna
decoder, recover the transmitted bits exactly at high signal quality, at the
code rate the emitter itself reports as achieved.
Evidence. An independent encode→map→AWGN→demap→decode
round trip (sionna.phy.channel.AWGN, sionna.phy.mapping.Demapper, and
the family’s own real decoder: LDPC5GDecoder, Polar5GDecoder with
list-8 SCL (Successive Cancellation List) decoding, TurboDecoder, ViterbiDecoder) at
code rate (the fraction of transmitted bits that carry real payload, the rest being FEC redundancy) 1/3 (distinct from the pinned unit test’s 1/2)
recovered zero bit errors over 15 trials per family at 20 dB
Eb/N0 (energy per bit over noise spectral density, a code-rate-normalized SNR)
(test_fec_family_round_trip_recovers_bits_at_a_different_rate_than_the_pinned_unit_test).
At 2 dB Eb/N0 the same pipeline (independent of the pinned unit test, run
directly during this validation) produced non-zero bit-error rates that
differ by code family, consistent with each family’s known relative
error-correcting strength at this operating point (LDPC 17.2%, Polar 46.7%,
Turbo 12.6%, convolutional 11.7%, all at achieved rate ≈0.5 over 20 trials
× 20 blocks each), confirming the
zero-error result at 20 dB reflects genuine decoding under noise, not a
code so redundant it always succeeds regardless of channel quality.
Reading
_build_ldpc_encoder/_build_polar_encoder/_build_turbo_encoder/
_build_conv_encoder and FECConstellationEmitter.generate’s own extras
assembly confirms the code rate generate() reports
(fec_constellation_code_rate) is computed from the encoder’s real
(k, n), not the caller’s requested value. Verified directly: a requested
LDPC rate of 1/3 is honored exactly (k=2000, n=4000 in the round-trip test
above); a rate requested below a family’s real floor is documented to
floor-clamp to the achievable rate, and to report that achieved value
instead of silently misreporting the request.
Un-modeled real-world imperfection: Polar5GEncoder’s small-block parity
bits. Real 3GPP NR Polar-coded control channels at small transport-block
sizes (12<=k<=19 information bits) include 3 additional parity-check bits
per 3GPP TS 38.212, used by the receiver’s list decoder for early pruning.
Sionna’s Polar5GEncoder, which FECConstellationEmitter’s polar path
delegates to entirely, does not implement these bits at this block-length
range: constructing Polar5GEncoder(k=12, n=18), a small-k corner inside
the emitter’s own documented reachable range, surfaces Sionna’s own runtime
warning (“For 12<=k<=19 additional 3 parity-check bits are defined in
38.212. They are currently not implemented by this encoder and, thus,
ignored.”). This is a library-inherited limitation, not an rfgen defect,
and no fix is owed here (fixing it means patching Sionna itself). A
polar-labeled example at a small k is therefore not fully
3GPP-conformant at that corner, disclosed here instead of left for a
reader to discover.
3.6 The three new channels produce statistically correct, distinct fading¶
Claim. RayleighBlockFading’s per-draw gain magnitude follows a
Rayleigh distribution; SionnaFlatFading’s empirical inter-block
correlation tracks its requested correlation coefficient; SionnaCIRDataset
realizes exactly its declared profile’s tap delays. All three are
measurably distinct from AWGNChannel and from each other.
Evidence. 1,500 independent RayleighBlockFading draws against a fixed
input, fit to a Rayleigh distribution by maximum likelihood, pass a
Kolmogorov-Smirnov (a nonparametric goodness-of-fit test comparing an empirical distribution to a reference one) test against that fit
(p = 0.517, fixed alpha = 0.01 decided before the numbers were seen; not
curated to pass): the fading gain’s magnitude is genuinely Rayleigh-
distributed, not merely “non-constant”
(test_rayleigh_block_fading_amplitude_matches_rayleigh_distribution).
For SionnaFlatFading at num_fading_blocks=8, the empirical correlation
coefficient between the first two blocks over 1,500 draws tracks the
requested correlation parameter directly: requested 0.0 → empirical 0.012,
requested 0.5 → empirical 0.494, requested 0.9 → empirical 0.899, all within
0.08 absolute tolerance
(test_flat_fading_empirical_correlation_matches_requested). This confirms
Sionna’s KroneckerModel correlation machinery is genuinely wired to the
requested parameter.
Un-modeled real-world imperfection: SionnaFlatFading’s correlation
family does not match real Rayleigh-fading time correlation.
SionnaFlatFading repurposes exp_corr_mat’s exponential SPATIAL-
correlation construction (R[i,j] = rho^|i-j|) as a time-block correlation
axis; the measurement above confirms this exponential decay is faithfully
realized. Real flat (frequency-flat) Rayleigh fading instead correlates in
time according to Doppler spread, per the Jakes/Clarke model: normalized
time-autocorrelation R(tau) = J0(2*pi*f_D*tau), a Bessel function that
oscillates and periodically goes negative, a qualitatively different
function family from a monotonic, always-non-negative exponential decay.
SionnaFlatFading’s correlation parameter also carries no unit tie to
velocity, carrier frequency, or coherence time, unlike SionnaTDL/
SionnaCDL’s doppler_speed_mps. This is a physically different
correlation family, not merely an incomplete one: a dataset consumer
cannot map any correlation value here to a real-world speed or coherence
time, and examples drawn from sionna_flat_fading teach a fading-block
correlation shape that does not occur in real mobile Rayleigh fading.
For SionnaCIRDataset,
applying the channel against both declared profiles
(custom_pdp_short_office, 6 taps at 20 ns spacing;
custom_pdp_long_urban_macro, 8 taps at 200 ns spacing) confirms the
realized CIR’s tap count matches the declared profile exactly for both
(test_cir_dataset_realized_delays_match_declared_profile_exactly), and the
two profiles’ closed-form RMS delay spreads (29.8 ns and 365.1 ns,
computed from the declared power-weighted tap positions, Rappaport eq.
5.5/5.6) differ by more than 10x, well past the >=10% distinctness bar
already pinned by
test_custom_pdp_profiles_declare_at_least_two_entries_with_distinct_rms_delay_spread
in tests/unit/test_propagation.py. Distinctness from AWGNChannel and
from each other is established by the existing
test_module_13_fading_channel_output_power_variance_exceeds_awgn_by_3x
(same test file): both RayleighBlockFading and SionnaFlatFading’s
output-power variance over 1,000 draws exceeds AWGNChannel’s by more than
3x at a fixed input and SNR, confirming each fading channel imposes
buffer-wide power swings AWGN’s per-sample noise does not.
3.7 Determinism holds given the same seed, with a corrected fix¶
Claim. Every random draw inside a Group A or Group B class traces back
to the caller’s own rng; the same seed reproduces bit-identical output,
a different seed produces different output, and no class leaks into or
reads from any process-global random-number stream.
Evidence. All three Group B channels (RayleighBlockFading,
SionnaFlatFading, SionnaCIRDataset) already passed this bar before this
validation pass: tests/unit/test_propagation.py’s existing
test_module_13_channel_draws_randomness_only_from_ctx_rng and
test_module_13_channel_does_not_leak_process_global_rng_state (both
re-run and confirmed passing during this validation) pin same-seed
reproducibility and zero perturbation of the process-global Torch and
random state, using the same torch.random.fork_rng/state-snapshot
scoping (_SionnaTorchRngScope, _scoped_python_random_seed) that already
fixed two of the three hidden-RNG-axis bugs Phase 2 previously found
(Sionna’s per-call global-config seed, and CIRDataset’s stdlib-random
shuffle buffer and internal DataLoader’s process-global-Torch-RNG draw).
This validation found a fourth instance of the same bug class in Group A,
which this pass fixed. All three Group A emitters set
sionna.phy.config.seed = seed (Sionna’s documented reseed hook) without
scoping it. A direct probe confirms this assignment alone perturbs Torch’s
process-global default generator as a side effect: seeding torch.manual_seed
to a fixed value, drawing a reference torch.rand(4), re-seeding to the
same fixed value, then merely assigning sionna.phy.config.seed before
drawing again produces a different torch.rand(4) result. Each emitter’s
own output remained reproducible given its own rng (confirmed before the
fix: same-seed calls were byte-identical, and output did not depend on the
Torch global generator’s pre-state), but the assignment’s side effect leaked
into whatever any other code drew next from that global generator later in
the same process, an undeclared second RNG axis of exactly the kind
_SionnaTorchRngScope already exists to prevent for the channel backends.
The first fix attempt was incomplete, and its own regression tests were
vacuous; both are corrected here. An earlier version of the fix wrapped
only the sionna.phy.config.seed = seed write and the call that reads it
(e.g. transmitter(num_slots)) in torch.random.fork_rng(devices=[]), and
this report originally stated the leak “verified gone.” That claim was
false. A direct probe shows the process-global Torch RNG is perturbed the
FIRST time sionna.phy is imported in a process: a ONE-TIME
import-caching effect (Python caches a module in sys.modules after its
first import, so only the very first Sionna touch in a process can trigger
it), not something config-object construction itself does (constructing a
CarrierConfig/TBConfig/PUSCHConfig/ResourceGrid/FEC encoder a
second time in the same process perturbs nothing). All three emitters’
first-fix versions performed a Sionna-facing import
(_build_pusch_config’s from sionna.phy.nr import ..., or an
equivalent) BEFORE entering the fork_rng scope, leaving that one-time
leak unscoped on a cold first call. The three “no leak” regression tests
written alongside that fix also FAILED under true process isolation and
passed only inside the full test suite, because an earlier test in the
same file had already imported sionna.phy and consumed the one-time leak
first: test-order pollution masking a real defect, the same pattern this
repository already fixed once at commit 69a193a4
(test_dataset_consumer permanently corrupting sys.modules).
The corrected fix widens each emitter’s torch.random.fork_rng scope to
begin before the FIRST Sionna-facing statement in that emitter (the
import sionna.phy/from sionna.phy... import ... line, whichever comes
first) and end after the generation call, so config-object/encoder
construction and the sionna.phy.config.seed write and read all sit
inside one scope (cellular.py, ofdm_conformant.py,
fec_constellation.py). The three regression tests now spawn a fresh
subprocess per assertion (sys.executable -c ..., inheriting the parent’s
environment unmodified, never a from-scratch mapping) so no earlier
test, in this file or any other, can have already imported sionna.phy in
that process; each measures torch.get_rng_state() immediately before and
after the emitter’s own cold first call in that fresh process. Run against
the pre-fix code, all three subprocess-isolated tests fail; run against the
corrected fix, all three pass
(test_nr_pusch_does_not_leak_into_process_global_torch_rng,
test_conformant_ofdm_does_not_leak_into_process_global_torch_rng,
test_fec_constellation_does_not_leak_into_process_global_torch_rng, the
last parameterized over all four FEC families). All 75 existing emitter
unit tests and the full 111-test signal-atlas-comms-v1 use-case suite pass
unchanged after the corrected fix.
4. Limits and scope-bounded claims¶
NR PUSCH’s realized occupied bandwidth can overshoot the requested value by up to 8% at coarser bandwidth-ladder rungs. This follows directly from 30 kHz-numerology resource-block granularity (360 kHz per RB) combined with the documented ±8-RB LDPC-constructibility search; it is not a defect in the mapping, but a reader relying on tight bandwidth precision at the 1 MHz rung specifically should budget for it.
The Reference contract’s emitter table (
docs/reference/api/waveforms.md) has a documentation-completeness gap this validation did not close.FECConstellationEmitterandConformantOFDMEmitterhave no row in the class index, and the page’s “17 built-in selectors” count predates the five Phase-2/prior selectors that bring the real count to 22. This validation fixed the two claims in that table and indocs/glossary.mdthat actively contradicted the shipped code (NR PUSCH’s “stub” status, and FEC being “not modeled”) because leaving them would have made this report cite a glossary that disagreed with it; adding full new table rows and refreshing the selector count is substantive documentation authoring distinct from RF-physics validation, better suited to a dedicated documentation-completeness pass.A pre-existing mypy finding in
cellular.py(_build_pusch_config_for_bandwidth) predates this validation and was left as-is. It is a type-annotation mismatch (a caught exception’s declared type does not include one branch’s real exception type), confirmed present on the unmodified pre-validation commit; it does not affect runtime behavior and is outside this pass’s RF-physics scope.31 pre-existing
SionnaRT(ray-tracing) unit-test failures are unrelated to this validation. They stem fromsionna.__version__not existing on the installed Sionna 2.0.1 distribution (a version-detection assumption inpropagation_sionna_rt.py), confirmed present before this validation’s changes and unrelated to any Group A or Group B class;SionnaRTis Phase-1 machinery outside this delivery’s Amendment-4/Group-B scope.This dataset generates a simulator-to-simulator corpus. No class validated here claims fidelity to an over-the-air capture, and this validation makes no sim-to-real transfer or real-receiver-performance claim about any of them.
ConformantOFDMEmitter’s pilot pattern is standards-plausible, not a named 3GPP configuration. Its full-band, block-type Kronecker pilots differ from a specific 3GPP DMRS comb pattern; §3.3 states this distinction precisely and does not imply full downlink-reference-signal conformance.The two
SionnaCIRDatasetpower-delay profiles are analytically generated (Rappaport’s exponential PDP model), not transcribed standards-body table values.propagation_sionna.py’s own module comment states this design choice; this validation confirmed the realized channel matches the declared profile exactly, not that the profile itself matches a specific published measurement campaign.
5. Known open defects (awaiting user decision)¶
Four corpus-definition defects were found after this report’s original validation pass. None is fixed here: each awaits a user design decision. Unlike section 4’s items, which describe accepted, scope-bounded constraints, the four defects below are not accepted; they are open. This section exists so this report is never read as a clean bill of health while they remain so.
DEFECT D1: 12 of
ConformantOFDMEmitter’s catalog labels collapse into one. The Signal Atlas comms-v1 use case’s render path always overridesConformantOFDMEmitter’soccupied_bandwidth_hzfrom the drawn bandwidth-ladder rung for everynr-grid-<N>rbclass label, and that override fully determines the rendered resource-grid size, so the rendered waveform stops depending on which of the 12nr-grid-<N>rblabels was drawn. Renderingnr-grid-1rb,nr-grid-8rb, andnr-grid-20rbat a fixed seed and bandwidth produces bit-for-bit identical IQ, independently reproduced three times (a blind classifier, a physical PAPR/kurtosis cross-check, and a direct live render). Effectively 69, not 80, of the catalog’s classes are distinguishable. This is a use-case wiring defect, not a defect inConformantOFDMEmitteritself, whose documented override behavior is an intentional, separately correct feature.DEFECT D3:
coding_schemehas no causal effect on the rendered signal, and its recorded provenance value can directly contradict what was actually rendered.condition.coding_schemeis drawn, cross-axis-constrained to Sionna-PHY-family labels, and recorded in provenance, but the render path never threads it into any emitter parameter:nr_puschalways renders LDPC-coded regardless of the drawn value,nr_ofdm_conformantalways renders uncoded, andfec_constellationlabels always render coded. Roughly half of all Sionna-PHY-family draws therefore carry a provenance label that misstates the rendered signal’s actual coding status.DEFECT D4: the reviewed, tested delay-spread augmentation is dead code; production draws from a different, untested distribution. A tested
augment_with_scenariofunction draws TDL/CDL delay spread uniformly over [10 ns, 3000 ns], but the render path never calls it. Production instead draws delay spread log-uniformly over [10 ns, 1000 ns] through a separate, earlier code path. The corpus’s actual delay-spread statistics differ from the reviewed and tested contract by roughly an order of magnitude.DEFECT D5: the Doppler axis is inert on every fading channel; the corpus contains no time-varying fading.
condition.doppler_speed_mpsis threaded correctly into each fading channel’s Sionna construction call, but every one ofsionna_tdl,sionna_cdl,sionna_cir_dataset,rayleigh_block_fading, andsionna_flat_fadingis realized as a single time-step snapshot, so Doppler has no time axis to act on and is mathematically inert on all five. Varyingdoppler_speed_mpsfrom 5 to 200 m/s at a fixed seed produces byte-identical IQ on every fading channel; onlyawgn_channelis correctly unaffected, since Doppler is not applicable there by construction. No example in the corpus exhibits intra-window channel evolution, though mobility diversity is one of this dataset’s stated differentiators over the earlier reference corpus this dataset goes beyond (see the use case’s own README for that comparison).
use_cases/signal-atlas/comms-v1/tests/test_axis_causality.py (added in
this repair pass) pins D3 and D5 as pytest.mark.xfail(strict=True) cases,
each naming its defect ID in the reason. A future fix must update that
marker: an unexpected pass on a strict=True xfail fails the suite, so the
fix cannot silently land without the marker changing too.
6. References¶
3GPP, NR; Physical channels and modulation, 3GPP TS 38.211, specification record. Cited for the DMRS mapping-type-A single-symbol configuration and the 15 kHz/30 kHz numerology confirmed in §3.2 and §3.3.
3GPP, NR; Multiplexing and channel coding, 3GPP TS 38.212, specification record. Cited for the LDPC base-graph selection and transport-block encoding confirmed in §3.2.
3GPP, NR; Physical layer procedures for data, 3GPP TS 38.214, specification record. Cited by
NRPuschParams.mcs’s own docstring for the MCS index table; not independently re-derived in this pass.T. S. Rappaport, Wireless Communications: Principles and Practice, 2nd ed., Prentice Hall, 2002, ISBN 0-13-042232-0, section 5.4 (power-delay profiles and RMS delay spread, eq. 5.5/5.6). Cited by
propagation_sionna.py’s own module comment for the exponential PDP modelSionnaCIRDataset’s two declared profiles use; confirmed in §3.6.NVIDIA, Sionna
nr.PUSCHTransmitter, PyPI distributionsionna, installed version 2.0.1, source.NVIDIA, Sionna
fec.ldpc/fec.polar/fec.turbo/fec.convencoders and decoders, PyPI distributionsionna, installed version 2.0.1, sources: ldpc, polar, turbo, conv.NVIDIA, Sionna
ofdm.ResourceGrid/KroneckerPilotPattern, PyPI distributionsionna, installed version 2.0.1, source.NVIDIA, Sionna
channel.RayleighBlockFading,channel.FlatFadingChannel,channel.CIRDataset,channel.exp_corr_mat, PyPI distributionsionna, installed version 2.0.1, sources: RayleighBlockFading, FlatFadingChannel, CIRDataset.TorchSig contributors,
torchsig.signals.builders.constellationSRRC taps and polyphase resampler, PyPI distributiontorchsig, installed version 2.1.1, PyPI project page.SciPy developers,
scipy.stats.rayleighandscipy.stats.kstest, PyPI distributionscipy, installed version 1.18.0, used for this validation’s Rayleigh-fit and goodness-of-fit tests in §3.6,rayleighandkstestdocumentation.PyTorch contributors,
torch.random.fork_rngandtorch.Generator, PyPI distributiontorch, installed version 2.13.0, randomness documentation andtorch.Generatordocumentation. Cited for the RNG-scoping fix applied in §3.7.