Scientific validation: additive white Gaussian noise channel¶
Validated.
1. The component¶
AWGNChannel (additive white Gaussian noise channel, a channel that adds random noise independently to every sample) adds complex Gaussian noise to an in-phase / quadrature (IQ) baseband signal so that the realised signal-to-noise ratio (SNR, the ratio of signal power to noise power) equals a caller-specified value in decibels (dB). It is the standard first-order channel model in digital communications: spectrally flat noise (same power at every frequency), statistically independent samples, and purely additive, with no frequency shift, no multipath fading, and no distortion.
class AWGNChannel(BaseChannelPropagation):
transformation: ClassVar[Transformation] = Transformation.PROPAGATION
def __init__(self, *, snr_db: float = 20.0) -> None: ...
def apply(self, signal: Signal, ctx: ChannelContext) -> Signal: ...
Parameter |
Type |
Units |
Default |
Purpose |
|---|---|---|---|---|
|
|
dB |
|
Target signal-to-noise ratio in decibels. The implementation adjusts noise power so that the measured SNR of the output equals this value. Must be finite; negative values produce noise-dominated output. |
import torch
from rfgen.channels.protocols import ChannelContext, ChannelRxParams
from rfgen.core_types import Signal, SignalMetadata
from rfgen.propagation import AWGNChannel
# Construct a 2 048-sample continuous-wave (CW) signal: I = 1, Q = 0.
iq = torch.stack((torch.ones(2048), torch.zeros(2048)), dim=0)
md = SignalMetadata(
family="comms", class_name="cw", class_taxonomy=("comms", "cw"),
generator_name="example", device_id="dev", sample_rate_hz=1e6,
bandwidth_hz=1e3, realized_carrier_hz=0.0, start_sample=0,
duration_samples=2048, snr_db=float("inf"), extras={},
)
sig = Signal(iq=iq, metadata=md)
rng = torch.Generator().manual_seed(0)
ctx = ChannelContext(
emitter_meta=md,
rx_params=ChannelRxParams(center_freq_hz=0.0, bandwidth_hz=1e6,
sample_rate_hz=1e6, noise_figure_db=0.0),
scene_id="scene", sample_idx=0, rng=rng,
)
out = AWGNChannel(snr_db=10.0).apply(sig, ctx)
# out.iq has shape (2, 2048) and dtype torch.float32
assert out.iq.shape == (2, 2048) and out.iq.dtype == torch.float32
assert out.metadata.snr_db == 10.0
The component sits in the channel-propagation layer. It does not model the Friis cascaded noise-figure equation (F_total = F1 + (F2 - 1)/G1 + ...) or antenna-temperature conversion (P_noise = k T_ant B F_rx); receiver-side noise figure is handled by LinearLNANoise in the device-fingerprint module. Hardware impairments, including IQ imbalance, phase noise, power-amplifier nonlinearity, carrier frequency offset (CFO), and quantisation, are separate classes in the transmit-impairment and receiver-frontend modules. Frequency-selective fading (Rayleigh, Rician) and the 3GPP TR 38.901 TDL/CDL profiles (standardised multipath models) are covered by the Sionna-backed channel classes in the same module.
2. What we validated¶
This validation establishes 5 load-bearing claims. Each is restated and supported by evidence in §3.
SNR contract (§3.1): the output SNR matches the requested value across the validated range.
Per-rail noise variance (§3.2): each I and Q rail independently carries half the total noise power, consistent with the textbook formula.
QPSK BER agreement with Proakis closed form (§3.3): the end-to-end bit error rate for a rectangular-pulse QPSK waveform matches the theoretical closed-form curve at five operating points.
Determinism, metadata, and input safety (§3.4): same-seed calls produce bit-identical output, metadata is updated on every call, and the input tensor is never modified in place.
Operating envelope (§3.5): the component produces finite output across the documented SNR range, handles near-zero signal power via a guard clamp, and accepts a wide range of input lengths without error.
Limits and scope-bounded items appear in §4; full citations are in §5.
3. Evidence per claim¶
3.1 SNR contract¶
Claim. For any input signal with mean power above the 1e-12 guard floor, the output SNR (measured as mean(|x|²) / mean(|y − x|²)) equals the requested snr_db within ±0.5 dB.
Mathematical basis. The implementation computes:
signal_power = mean(|x[n]|²).clamp_min(1e-12)
noise_power = signal_power / 10^(snr_db / 10)
sigma = sqrt(noise_power / 2)
and draws independent zero-mean Gaussian I and Q noise samples with standard deviation sigma. The total complex noise power is 2 sigma² = noise_power = signal_power / SNR_linear, so the realised SNR by construction equals signal_power / noise_power = SNR_linear. Sklar, Digital Communications, 2nd ed., Prentice-Hall, 2001, Ch. 3 gives this as the standard AWGN per-rail decomposition.
Evidence. Six SNR operating points (snr_db ∈ {0, 5, 10, 15, 20, 30} dB), N = 200 000 samples each. Measured SNR recovered from 10 log₁₀(signal_power / noise_power). All six points fell within ±0.5 dB.
snr_db (dB) |
Tolerance |
Result |
|---|---|---|
0 |
±0.5 dB |
Within tolerance |
5 |
±0.5 dB |
Within tolerance |
10 |
±0.5 dB |
Within tolerance |
15 |
±0.5 dB |
Within tolerance |
20 |
±0.5 dB |
Within tolerance |
30 |
±0.5 dB |
Within tolerance |
Test: tests/validation/propagation/awgn/test_experiment_contract.py::test_measured_snr_matches_requested_within_half_db (6 parametrized cases).
Figure 2 shows the measured complex noise power versus SNR across the −10 to +30 dB range against the theoretical line, with the right panel confirming that the relative error stays below 5 % at every point.

3.2 Per-rail noise variance¶
Claim. The I and Q rails are independent and each carries half the total complex noise power, equal to signal_power / (2 * SNR_linear).
Mathematical basis. For complex Gaussian noise n[t] = n_I[t] + j n_Q[t] with independent zero-mean Gaussian rails:
E[|n|²] = E[n_I²] + E[n_Q²] = 2 sigma²
Setting 2 sigma² = noise_power = signal_power / SNR_linear gives sigma² = signal_power / (2 * SNR_linear). This is the standard per-rail variance stated in Sklar (2001), Ch. 3, and Oppenheim & Schafer, Discrete-Time Signal Processing, 3rd ed., Pearson, 2010, Ch. 2.
Evidence: complex noise power. CW input (I = 1, Q = 0), N = 1 000 000, SNR = 10 dB. Measured complex noise power matched signal_power / SNR_linear within 5 % relative error.
Test: tests/validation/propagation/awgn/test_experiment_contract.py::test_measured_complex_noise_variance_matches_theoretical.
Evidence: per-rail symmetry. Same CW input, same conditions. Each rail independently measured at sigma² = signal_power / (2 * SNR_linear) within 5 % relative error.
Test: tests/validation/propagation/awgn/test_experiment_contract.py::test_per_rail_noise_variance_is_half_complex_noise_power.
Evidence: Sklar cross-check. SNR = 20 dB, N = 1 000 000. Each rail measured at sigma² = 1.0 / (2 * 100) = 0.005 within 5 % relative error.
Test: tests/validation/propagation/awgn/test_empirical_known_results.py::test_noise_variance_against_proakis_noise_model.
3.3 QPSK BER agreement with Proakis closed form¶
Claim. For a rectangular-pulse QPSK waveform (quadrature phase-shift keying, a digital modulation where each symbol encodes two bits as one of four phases), the channel produces a measured BER (bit error rate, the fraction of transmitted bits decoded incorrectly) that matches the Proakis & Salehi closed-form curve within ±20 % relative at five Eb/N0 (energy per bit per noise spectral density) operating points.
Reference formula. Proakis & Salehi, Digital Communications, 5th ed., McGraw-Hill, 2008, eq. 8.2-20:
BER_QPSK(Eb/N0) = Q(sqrt(2 Eb/N0_linear)) = erfc(sqrt(Eb/N0_linear)) / 2
where Q(x) = 0.5 erfc(x / sqrt(2)) is the Q-function (tail probability of a standard Gaussian), and erfc is the complementary error function.
SNR-to-Eb/N0 conversion. For 4 samples per symbol and k = 2 bits per symbol:
Eb/N0 [dB] = SNR [dB] + 10 log₁₀(4 / 2) ≈ SNR [dB] + 3.01 dB
Evidence. Five operating points, N = 200 000 symbols per point. All five points fell within ±20 % relative of the Proakis theoretical BER.
Eb/N0 (dB) |
Theoretical BER |
Tolerance |
Result |
|---|---|---|---|
0 |
0.159 |
±20 % relative |
Within tolerance |
2 |
0.078 |
±20 % relative |
Within tolerance |
4 |
0.023 |
±20 % relative |
Within tolerance |
6 |
3.8 × 10⁻³ |
±20 % relative |
Within tolerance |
8 |
1.9 × 10⁻⁴ |
±20 % relative |
Within tolerance |
At the 8 dB point, the expected error count is approximately 38 with binomial standard deviation 6; the ±20 % tolerance corresponds to roughly 12 standard deviations from zero errors, so the tolerance is not artificially wide.
Test: tests/validation/propagation/awgn/test_empirical_known_results.py::test_qpsk_ber_matches_proakis_closed_form (5 parametrized cases).
Figure 1 shows the measured BER at five operating points plotted against the dense Proakis theoretical curve.

3.4 Determinism, metadata, and input safety¶
Claim. (a) Two calls with the same torch.Generator seed produce byte-identical IQ output. (b) Two calls with different seeds produce different noise samples. © output.metadata.snr_db is updated to self.snr_db on every call. (d) The transformation log appended to metadata carries the correct group and transformation values and the snr_db parameter. (e) The input Signal tensor is not mutated in place.
Evidence.
Sub-claim |
Test |
Result |
|---|---|---|
Same seed → byte-identical output |
|
Passes |
Different seeds → different noise |
|
Passes |
|
|
Passes |
Transformation log appended |
|
Passes |
Input not mutated |
|
Passes |
All tests in tests/validation/propagation/awgn/test_experiment_contract.py.
3.5 Operating envelope¶
Claim. The component produces finite IQ output across snr_db ∈ [−30, +60] dB, handles near-zero signal power via a guard clamp at 1e-12, and accepts input lengths from N = 1 to N = 10⁶ without error or memory fault.
SNR range. Six operating points (snr_db ∈ {−30, −10, 0, 10, 30, 60} dB), N = 1 000 each. All produced finite float32 IQ.
Test: tests/validation/propagation/awgn/test_robustness_envelope.py::test_operating_envelope_snr_range_documented.
Extreme SNR probe. Three extreme points (snr_db ∈ {−20, 0, 60} dB), N = 10 000. All completed without error and produced finite IQ.
Test: tests/validation/propagation/awgn/test_robustness_envelope.py::test_extreme_snr_produces_finite_output (3 parametrized cases).
Sub-floor regime. Input amplitude 1e-9 gives signal_power = 1e-18, which is below the clamp_min(1e-12) floor. The clamp sets the effective signal power to 1e-12, so noise power equals 1e-12 / SNR_linear rather than the actual signal power divided by SNR_linear. Output is finite; noise power matched the clamped formula within 5 %.
Test: tests/validation/propagation/awgn/test_robustness_envelope.py::test_tiny_signal_power_uses_clamp_floor.
Zero input. All-zero IQ: the clamp prevents division by zero and noise is still added. Output is finite.
Test: tests/validation/propagation/awgn/test_robustness_envelope.py::test_zero_iq_input_produces_finite_noise.
Dtype handling. float64 input IQ is cast to complex64 internally; output is always float32.
Test: tests/validation/propagation/awgn/test_robustness_envelope.py::test_float64_input_iq_produces_float32_output.
N extremes. N = 1 and N = 10⁶ both complete without error.
Tests: test_single_sample_signal_completes, test_large_n_completes_without_oom.
4. Limits and what’s not validated¶
Validated scope. The implementation reproduces the textbook AWGN model: per-rail variance, SNR contract, and Proakis QPSK BER all hold to the tolerances in §3 across the operating envelope documented below.
Operating envelope. The validated parameter ranges are:
Parameter |
Validated range |
Boundary behaviour |
|---|---|---|
|
−30 dB to +60 dB |
Finite output across the range; no overflow or NaN |
|
Clamped to 1e-12 floor |
Below 1e-12 the SNR contract does not hold; noise is computed from the clamp floor |
N (signal length) |
1 to 10⁶ |
No minimum enforced; no OOM at 10⁶ |
Input dtype |
float32 or float64 |
Cast to complex64 internally; output is always float32 |
Float32 precision limit. float32 (single-precision floating-point, providing approximately seven significant decimal digits) carries roughly seven significant decimal digits. At SNR = 60 dB the noise standard deviation is about 1e-3 relative to a unit-amplitude signal, leaving four significant digits of headroom. Beyond approximately 90 dB the noise falls below float32 rounding; this regime is not tested.
Friis cascaded noise figure. The class does not implement F_total = F1 + (F2 − 1)/G1 + ... or antenna-temperature conversion via P_noise = k T_ant B F_rx (where k is Boltzmann’s constant, T_ant is antenna noise temperature, B is bandwidth, and F_rx is receiver noise figure). Receiver-side noise figure is handled by LinearLNANoise in the device-fingerprint module.
Frequency-selective fading. Rayleigh fading (random amplitude envelope following a Rayleigh distribution, caused by multipath propagation), Rician fading (similar but with a dominant line-of-sight component), and the 3GPP TR 38.901 TDL/CDL profiles (standardised multipath channel models for cellular systems) are out of scope and are covered by the Sionna-backed channel classes in the same module.
Coloured noise. All noise samples are independent; band-edge anti-aliasing-filter colouration (where a filter shapes the noise power spectrum so it is no longer flat) is a receiver-frontend concern.
Hardware impairments. IQ imbalance, phase noise, power-amplifier nonlinearity, CFO, and quantisation are separate classes in the transmit-impairment and receiver-frontend modules.
Validated SNR window. Behaviour outside the −30 dB to +60 dB window is not characterised by the current test suite.
5. References¶
Published works¶
Citation |
Role |
|---|---|
Proakis, J. G., and Salehi, M. Digital Communications, 5th ed., McGraw-Hill, 2008. ISBN 978-0-07-295716-7. Ch. 8.2, eq. 8.2-15, 8.2-20. |
QPSK BER closed form under AWGN; primary empirical reference for §3.3 |
Sklar, B. Digital Communications: Fundamentals and Applications, 2nd ed., Prentice-Hall, 2001. ISBN 978-0-13-084788-7. Ch. 3. |
AWGN per-rail variance formula (N₀/2); primary citation for per-rail decomposition in §3.1 and §3.2 |
Oppenheim, A. V., and Schafer, R. W. Discrete-Time Signal Processing, 3rd ed., Pearson, 2010. ISBN 978-0-13-198842-2. Ch. 2. |
Discrete-time mean power as Parseval-consistent average; basis for |
Hoydis, J., Cammerer, S., et al. “Sionna: An Open-Source Library for Next-Generation Physical Layer Research.” arXiv:2203.11854, 2022. DOI: 10.48550/arXiv.2203.11854. |
Cross-implementation reference: |
Libraries¶
PyPI distribution |
Installed version |
Docs URL |
Role in validation |
|---|---|---|---|
|
2.12.1 |
Gaussian sampler ( |
|
|
2.4.6 |
|
|
|
1.18.0 |
Not directly invoked; |
|
|
3.11.0 |
Figure generation in |