Scientific validation: TorchSig digital-constellation emitter¶
Validated with documented limitations.
1. The component¶
TorchSigCommsEmitter is a PyTorch-fronted generator that produces clean baseband in-phase/quadrature (IQ) tensors for 11 standard digital-modulation classes used in modulation-recognition training data. The in-phase (I) and quadrature (Q) channels are the real and imaginary parts of a complex-valued signal; together they form the two-channel representation (2, N) consumed by downstream models.
class TorchSigCommsEmitter(BaseEmitter):
family: ClassVar[EmitterFamily] = EmitterFamily.COMMS
supported_classes: ClassVar[tuple[str, ...]] = (
"bpsk", "qpsk", "8psk", "16psk",
"16qam", "32qam", "64qam", "256qam",
"ook", "4ask", "8ask",
)
def __init__(self) -> None: ...
def schema(self) -> type[BaseModel]: ...
def generate(
self,
*,
class_label: str,
sample_rate: float,
duration_s: float,
f_offset_hz: float,
rng: torch.Generator,
device_id: str | None = None,
params: BaseModel | None = None,
) -> Signal: ...
class TorchSigCommsParams(BaseModel):
bandwidth_hz: float = Field(default=200e3, gt=0)
pulse_shape_name: str = Field(default="srrc")
alpha_rolloff: float = Field(default=0.35, gt=0.0, lt=1.0)
Parameter |
Type |
Units |
Default |
Purpose |
|---|---|---|---|---|
|
|
n/a |
required |
One of the 11 supported constellation labels. A constellation is the discrete set of complex points the modulator picks from to encode bits. |
|
|
Hz |
required |
Samples per second on the output tensor. Must be finite and positive. |
|
|
s |
required |
Output duration. The total sample count is |
|
|
Hz |
required |
Baseband frequency shift in Hz. Mixes the signal up or down by this many Hz; real radio-frequency upconversion is a downstream step. May be negative or zero. |
|
|
n/a |
required |
Seeds the NumPy random-number generator used to draw symbols. State advances on each call. |
|
|
n/a |
|
Optional metadata label echoed into |
|
|
Hz |
|
Symbol-rate / 3 dB bandwidth. Default matches the GSM (Global System for Mobile Communications) cellular channel width of 200 kHz. |
|
|
n/a |
|
Pulse shape applied to each symbol. |
|
|
n/a |
|
Excess-bandwidth factor of the SRRC pulse, in |
import torch
from rfgen.emitters import TorchSigCommsEmitter
emitter = TorchSigCommsEmitter()
signal = emitter.generate(
class_label="qpsk",
sample_rate=1_000_000.0, # 1 MHz
duration_s=0.1, # 0.1 s
f_offset_hz=0.0,
rng=torch.Generator().manual_seed(0),
)
# signal.iq has shape (2, 100_000) and dtype torch.float32:
# channel 0 is the in-phase (I) component, channel 1 is the quadrature (Q).
assert signal.iq.shape == (2, 100_000) and signal.iq.dtype == torch.float32
The taxonomy position is digital linear modulation at the modulation-order level: one label per constellation shape, with no protocol framing. The 11 supported labels span three constellation families: phase-shift keying (PSK, where bits are encoded by the phase of the carrier: BPSK / QPSK / 8PSK / 16PSK with 2 / 4 / 8 / 16 phases), quadrature amplitude modulation (QAM, where bits are encoded jointly by amplitude and phase: 16QAM / 32QAM / 64QAM / 256QAM with 16 / 32 / 64 / 256 points on a grid), and amplitude-shift keying (ASK, where bits are encoded by amplitude alone: OOK / 4ASK / 8ASK with 2 / 4 / 8 levels, where OOK is “on-off keying”, a two-level amplitude code). Sibling families (frequency-shift keying, orthogonal-frequency-division multiplexing without a separate symbol grid, analog amplitude and frequency modulation, chirp, tone) are exposed as separate emitter classes; this report covers only the constellation-based subset.
Channel-side impairments (multipath fading, additive noise, power-amplifier nonlinearity, in-phase / quadrature imbalance, oscillator phase noise, direct-current or DC offset) are out of scope and assigned to a separate channel layer. One documented edge case applies to the on-off-keying (OOK) constellation: its symbol map has non-zero population mean by design (1/sqrt(2) ≈ 0.707 after unit-root-mean-square normalisation), so the conditional zero-mean step is skipped for that class.
2. What we validated¶
This validation establishes 13 load-bearing claims. Each is restated and supported by evidence in §3.
Shape and dtype contract (§3.1): the output is a
(2, N)float32tensor withNset bysample_rateandduration_sfor every supported class label.Unit average power (§3.2): the symmetric constellations have unit average power and OOK has the expected half-duty-cycle power.
Zero mean for symmetric classes, theoretical mean for OOK (§3.3): the population mean is zero for the symmetric classes and matches the OOK theoretical value.
Determinism and RNG advancement (§3.4): a fixed
torch.Generatorseed reproduces bit-identical IQ, and consecutive calls advance the generator state.Bandwidth scaling tracks
bandwidth_hz(§3.5): the empirical occupied bandwidth scales linearly with the configuredbandwidth_hz.alpha_rolloffcontrols transition-band steepness (§3.6): decreasingalpha_rolloffsteepens the PSD transition band as predicted by the SRRC pulse-shape formula.SRRC -3 dB frequency matches Proakis-Salehi (§3.7): the measured one-sided -3 dB frequency matches the closed-form textbook prediction.
Cross-class cumulant ordering follows Swami-Sadler (§3.8): waveform-level fourth-order cumulants preserve the literature-tabulated cross-class ordering.
Cumulant attenuation factor is constellation-invariant (§3.9): the waveform-to-symbol cumulant attenuation factor is shared across the symmetric constellations.
f_offset_hztranslates the spectral centroid (§3.10): the baseband frequency-shift identity translates the spectral centroid by the requested offset.Input-validation envelope (§3.11): the combined-occupancy guard rejects configurations exceeding the complex-baseband Nyquist budget, and non-finite or out-of-schema inputs raise a typed
EmitterErrorbefore any arithmetic.Robustness at the operating-envelope boundary (§3.12): very short records and near-boundary configurations do not silently produce garbage.
Statistical-estimator settings (§3.13): the PSD and cumulant estimator configurations have uncertainty well below the tolerances used elsewhere in §3.
Limits and scope-bounded items appear in §4; full citations are in §5.
3. Evidence per claim¶
3.1 Shape and dtype contract¶
The emitter must return a (2, N) float32 tensor with N = round(sample_rate * duration_s) for every supported class label. This is the structural contract every downstream consumer (the data-loader, the model input layer) depends on.
Measured at sample_rate = 1 MHz, duration_s = 0.1 s, N = 100000 across all 11 labels: shape (2, 100000), dtype torch.float32. Test: test_shape_and_dtype in tests/validation/emitters/torchsig_comms/test_experiment_contract.py.
3.2 Unit average power¶
For PSK (phase-shift keying), QAM (quadrature amplitude modulation), and ASK (amplitude-shift keying) constellations the symbol map is divided by the root-mean-square amplitude sqrt(mean(|map|^2)) before pulse shaping, so the mean instantaneous power E[I^2 + Q^2] is approximately 1. For OOK the half-duty-cycle structure gives a theoretical mean power of 0.5. Unit average power matters because every downstream signal-to-noise-ratio (SNR) calibration upstream of the channel layer assumes it.
Measured PSK / QAM / ASK power: 0.997. OOK power: 0.499. Test: test_power_normalisation in test_experiment_contract.py.
3.3 Zero mean for symmetric classes, theoretical mean for OOK¶
The 10 symmetric constellations (everything except OOK) have a population mean of zero by construction; finite-sample residuals are removed by a conditional mean-subtraction step. OOK retains its theoretical mean of 1/sqrt(2) ≈ 0.707 after unit-RMS (root-mean-square) normalisation. Zero mean for the symmetric classes matters because any residual DC bias would corrupt the IQ statistics a downstream classifier learns.
Measured for the 10 symmetric classes: |mean(I)| / max(|I|) < 3e-8. Measured for OOK at N = 200000: mean 0.683, deviating from theory by 0.024 (within tolerance; about 23 finite-sample standard errors). Tests: test_symmetric_classes_zero_mean, test_B_ook_mean_is_near_theoretical, and test_ook_mean_is_near_theoretical_at_200k_samples in test_experiment_contract.py.
3.4 Determinism and RNG advancement¶
The same torch.Generator seed must reproduce bit-identical IQ; consecutive generate() calls on a shared generator must produce different IQ (so callers do not accidentally generate duplicate samples in a sweep). Determinism is the foundation for reproducible training-data generation.
Measured: pass for all 11 labels. Tests: test_seed_level_determinism in test_experiment_contract.py and test_consecutive_calls_advance_rng in test_robustness_silent_gaps.py.
3.5 Bandwidth scaling tracks bandwidth_hz¶
Doubling the requested bandwidth_hz should double the empirical -3 dB occupied bandwidth, with linear tracking inside the operating envelope. A downstream training pipeline will sweep bandwidth_hz to vary symbol rate; if the realised bandwidth did not track the requested value, the trained model would learn the wrong rate-to-bandwidth mapping.
Measured doubling ratios at B0 in {50, 100, 200} kHz: 1.93, 1.94, 2.00, all inside the tolerance band [1.7, 2.3]. Test: test_bandwidth_doubling in test_experiment_psd_bandwidth.py. Figure 1 (below) overlays the per-operating-point Welch PSDs (power spectral density estimated by averaging windowed periodograms; see §3.13 for the estimator’s settings).

Figure 1 shows the three PSD curves shifting in proportion to the requested bandwidth, with -3 dB crossings at the predicted symbol-rate / 2 locations.
3.6 alpha_rolloff controls transition-band steepness¶
Smaller alpha_rolloff should produce a steeper SRRC transition between the passband and stopband; the closed-form prediction is that the -20 dB bandwidth scales with (1 + alpha), giving a ratio of about 1.81 between alpha = 0.99 and alpha = 0.10. This claim matters because a downstream classifier may treat alpha as a class-conditional feature; the empirical transition must follow the parameter monotonically.
Measured -20 dB bandwidth ratio alpha=0.99 / alpha=0.10: 1.72 (monotonic; the tolerance threshold for the assertion is > 1.3). Test: test_alpha_rolloff_controls_transition in test_experiment_psd_bandwidth.py. Figure 2 overlays Welch PSDs across the swept alpha values.

Figure 2 shows the PSDs steepening monotonically as alpha decreases, with the lowest-alpha curve having the narrowest passband-to-stopband transition.
3.7 SRRC -3 dB frequency matches Proakis-Salehi¶
For a square-root raised-cosine pulse at symbol rate f_sym, the one-sided -3 dB frequency of the resulting baseband PSD is f_sym / 2 (Proakis & Salehi 2008, §9.2). At bandwidth_hz = 200 kHz the prediction is 100 kHz. The closed-form prediction is the canonical anchor for SRRC fidelity.
Measured one-sided -3 dB frequency: 95.0 kHz. Relative error: -5.0%. Test: test_psd_3db_bandwidth in test_experiment_psd_bandwidth.py. Figure 3 overlays Welch PSDs for four constellations sharing the same SRRC shape, confirming the constellation-invariant spectral envelope.

Figure 3 shows all four PSDs overlapping within the per-bin variance, agreeing with the SRRC closed form to within 5% in the -3 dB neighbourhood. The 5% contraction originates from the polyphase resampler inside constellation_modulator (see §4).
3.8 Cross-class cumulant ordering follows Swami-Sadler¶
The fourth-order cumulant C42 (a kurtosis-like higher-order statistic that fingerprints constellation shape) takes characteristic absolute values per ideal constellation; Swami & Sadler 2000 tabulate the closed-form ordering BPSK > QPSK = 8PSK > 16QAM > 64QAM > 256QAM. Preserving this ordering on the waveform is the minimum bar for the emitter to be useful as training data for cumulant-feature-based classifiers.
Measured: same ordering on the waveform across all six tested constellations. The smallest cross-class gap is 16QAM - 64QAM = 0.054. The plug-in cumulant estimator has standard error of order 1/sqrt(N); at N = 500000 the standard error is about 0.0014, placing the smallest gap at about 39 standard errors and the QPSK to 16QAM gap at about 194 standard errors. Test: test_K_cross_class_discriminability in test_experiment_cumulants.py. Figure 4 plots the per-class |C42| measurements next to the Swami-Sadler reference.

Figure 4 shows the measured waveform bars tracking the reference shape with a uniform downward offset, and the relative ordering preserved across every adjacent pair.
3.9 Cumulant attenuation factor is constellation-invariant¶
Waveform-level |C42| measurements are systematically lower than the symbol-level reference. The cause is the SRRC filter’s inter-sample correlation: the oversampled waveform’s higher-order statistics are smoothed by the pulse-shape FIR. The empirical attenuation factor should be constellation-invariant (a single filter applied to every symbol stream cannot favour one constellation over another).
Measured per-class waveform-to-reference ratios at oversampling factor 5: QPSK 0.806, 16QAM 0.782, 64QAM 0.774, 256QAM 0.774, BPSK 0.818. The coefficient of variation across the five ratios is 2.2%, consistent with a constellation-invariant filter effect. Tests: test_F_cumulant_waveform_for_qpsk, test_F_cumulant_waveform_for_16qam, test_F_cumulant_waveform_for_64qam, test_F_cumulant_waveform_for_256qam, test_F_cumulant_waveform_for_bpsk in test_experiment_cumulants.py. Tests comparing the waveform-level |C42| directly against the symbol-level Swami-Sadler numbers are marked xfail(strict=True) with the inter-sample correlation cited as the cause.
The empirical constellation diagrams in Figure 5 corroborate the constellation-shape recovery: every label shows the expected point symmetry, ring structure, or on-axis ASK distribution.

Figure 5 shows clearly separated clusters at the expected positions for each constellation, with no degenerate collapses or missing points.
3.10 f_offset_hz translates the spectral centroid¶
The baseband frequency-shift identity iq_complex * exp(j * 2 * pi * f_offset_hz * t) (Oppenheim & Schafer 2010, §4.2) should translate the spectral centroid by exactly the requested f_offset_hz, and produce IQ measurably different from f_offset_hz = 0. The shift is the mechanism by which the channel layer composes multiple emitters at different carrier offsets onto a shared scene.
Measured spectral-centroid migration for a requested offset of 100 kHz: 99.4 kHz, within one Welch frequency-bin width. Test: test_f_offset_iq_is_upconverted in test_robustness_silent_gaps.py.
3.11 Input-validation envelope¶
Configurations that violate the complex-baseband Nyquist budget (the rfgen-specific occupied-bandwidth guard bandwidth_hz * (1 + alpha) + 2 * |f_offset_hz| < sample_rate) must be rejected before any arithmetic, with a typed EmitterError carrying enough context for the caller to debug. Schema violations (non-positive bandwidth_hz, out-of-range alpha_rolloff, unsupported class_label, extra fields, non-finite numeric inputs) must raise the appropriate typed error.
Measured: the combined-occupancy guard raises EmitterError at bandwidth_hz = 0.49 * sample_rate, alpha = 0.99, with a context dictionary naming bandwidth_hz, alpha_rolloff, f_offset_hz, sample_rate, occupied_bandwidth_hz, and shifted_extent_hz. All six NaN / +inf / -inf permutations of sample_rate, duration_s, and f_offset_hz raise EmitterError with a context dictionary naming the offending field. Schema bounds and extra-field rejection are enforced by Pydantic. Tests: test_combined_occupancy_guard and test_nan_inf_inputs_raise_emitter_error in test_robustness_envelope.py; test_alpha_schema_bounds and test_unsupported_class_label in test_experiment_contract.py. Figure 6 shows the well-formed PSD at both schema boundaries (alpha = 0.01 and alpha = 0.99), confirming the boundaries are reachable rather than degenerate.

Figure 6 shows two finite, structurally sound PSDs spanning the schema-permitted alpha range. The narrowest-alpha curve has the steepest transition band and the widest-alpha curve has the smoothest skirt, as the SRRC closed form predicts.
3.12 Robustness at the operating-envelope boundary¶
Two boundary regimes are exercised to confirm the implementation does not silently produce garbage: very short records (n_samples < 64), and configurations adjacent to the combined-occupancy limit. Short records produce output dominated by the SRRC filter transient (documented in the module docstring; no exception is raised). Near-boundary configurations either pass without spectral folding or are rejected by the guard.
Tests: test_short_n1_output_is_zero_mean in test_robustness_pathological.py; test_combined_occupancy_passes and test_combined_occupancy_guard in test_robustness_envelope.py. Figures 7 and 8 visualise the two regimes.

Figure 7 shows the transient-dominated waveform and its broadband PSD for n_samples < 64, with finite values throughout and no exception raised.

Figure 8 shows a well-formed accepted PSD that uses nearly the full Nyquist budget, alongside the rejected-configuration result where the guard fires before any sample is computed.
3.13 Statistical-estimator settings¶
The two main estimators in the validation suite are Welch’s method for PSD and a plug-in sample-moment estimator for C42. Welch’s method (a standard variance-reducing PSD estimator that averages periodograms over overlapping windowed segments) is invoked through scipy.signal.welch with nperseg = 4096, 50% overlap, and return_onesided = False. At sample_rate = 2 MHz, duration_s = 0.2 s (N = 400000) this yields about 194 averaged segments; per-bin coefficient of variation is approximately 1 / sqrt(194) ≈ 7%, equivalent to about 0.3 dB. The -3 dB crossing is locatable to within about +/- 1% in frequency at 50 kHz bandwidth, well inside the +/- 15% tolerance used for the bandwidth-scaling claim.
The cumulant estimator’s standard error scales as 1 / sqrt(N). At N = 500000 the standard error is about 0.0014, roughly 200 times smaller than the QPSK to 16QAM cross-class gap of 0.272, making the ordering claim statistically conclusive.
Sample-size choice: PSD-bandwidth tests use N = 400000 to keep the bandwidth-edge uncertainty below one-fifteenth of the +/- 15% tolerance. Cumulant tests use N = 500000 to push the standard error two orders of magnitude below the smallest measured cross-class gap. Shape and determinism tests use N = 100000, sufficient for those structural claims and small enough to keep the suite under 12 seconds. Welch averaging (over a full-record periodogram) is appropriate because the emitter’s output is statistically stationary; for non-stationary signals such as a chirp the full-record periodogram is preferred.
4. Limits and what’s not validated¶
The following items bound the scope of the validation. Each carries a one-sentence technical rationale.
No transmitter impairments. Power-amplifier AM-AM compression and AM-PM phase distortion (the gain and phase changes a real amplifier introduces near saturation), in-phase / quadrature gain and phase imbalance, oscillator phase noise, and local-oscillator DC offset are all absent. The synthesised waveform is the idealised transmitter-side baseband signal. A classifier trained on this emitter alone, without a realistic channel and hardware-impairment layer, will be brittle to real captures. Out of scope: impairments are the channel layer’s responsibility in the rfgen architecture.
No protocol framing. Real transmissions have preambles, sync words, headers, payloads, cyclic-redundancy-check fields, and inter-burst guard intervals. The emitter produces a continuous stationary burst of modulated symbols. Out of scope for a per-class waveform generator.
Non-Gray-coded bit mapping. TorchSig’s constellation maps use natural-binary indexing rather than Gray coding (a bit assignment where adjacent constellation points differ in only one bit, used by every standards-grade modem). Adjacent 8PSK index neighbours can differ by up to 3 bits. The physical waveform shape is unaffected; only the bit-to-symbol interpretation differs from the Gray-coded modems specified in ITU-T, DVB-S2, and 3GPP TS 38.211. Inherited from the upstream library.
32QAM is a 4 by 8 rectangular grid, not the cross-shape used in DVB-T2. TorchSig defines the
32qamlabel as a rectangular4 x 8grid; the cross-shape variant is available in TorchSig as32qam_crossbut is not exposed by this emitter. Documented in the module docstring.Symbol rate not exposed as a first-class parameter.
TorchSigCommsParamsexposesbandwidth_hzrather thansymbol_rateorsamples_per_symbol. A consumer who wants to sweep symbol rate at fixed pulse-shape rolloff must back-compute the bandwidth. The signal-catalog example configurations advertisesymbol_rateas a knob; aligning the schema and the catalog is a public-surface change outside this validation’s scope.n_samples < 64not hard-rejected. Short records produce output dominated by the SRRC pulse transient. A runtime warning or hard raise would break legitimate single-sample-probe tests. The minimum recommended value is documented in the module docstring and in thegenerate()parameter docstring.5% PSD-bandwidth contraction and -18.8 dB stopband floor. Both deviations from the ideal SRRC spectral mask originate from the polyphase resampler inside
constellation_modulator. The deviation is within real-world transmitter spectral-mask tolerances; regulatory limits typically specify -40 dB at twice the symbol rate, which the output meets. Treated as known upstream-library behaviour rather than a defect.No upper bound on
n_samplesat the emitter layer. Resource-limit policy belongs to the pipeline configuration layer, not to a single emitter.Mean-subtraction rather than an IIR DC-blocker. The zero-mean adjustment for symmetric classes removes the
O(1 / sqrt(N))finite-sample residual via plain mean subtraction. An infinite-impulse-response (IIR) DC-blocker (a one-pole high-pass filter often used to strip residual DC) has a phase response and filter-state question that would change the public behaviour; outside this component’s scope.
The executable validation suite is at tests/validation/emitters/torchsig_comms/, totalling 150 passing tests with 3 expected-failures across seven files, and runs in about 12 seconds on a developer workstation.
5. References¶
Published works¶
Citation |
Identifier |
Role |
|---|---|---|
J. G. Proakis and M. Salehi, Digital Communications, 5th ed., McGraw-Hill, 2008, §9.2 |
ISBN 978-0072957167 |
Canonical SRRC pulse formula; matched-filter and Nyquist-criterion derivation; closed-form -3 dB frequency |
A. V. Oppenheim and R. W. Schafer, Discrete-Time Signal Processing, 3rd ed., Pearson, 2010, §4.2 |
ISBN 978-0131988422 |
Complex-baseband frequency-shift identity |
A. Swami and B. M. Sadler, “Hierarchical digital modulation classification using cumulants,” IEEE Transactions on Communications, vol. 48, no. 3, pp. 416 to 429, March 2000 |
doi:10.1109/26.837045 |
Closed-form symbol-level |
L. Boegner et al., “Large Scale Radio Frequency Signal Classification,” 2022 |
arXiv:2207.09918, doi:10.48550/arXiv.2207.09918 |
TorchSig library citation |
ETSI EN 302 307-1 v1.4.1 (2014), §5.4 |
ETSI EN 302 307-1 |
DVB-S2 SRRC rolloff values including the default |
ETSI TS 145 005 v15.4.0 (2019), §2 |
ETSI TS 145 005 |
GSM channel raster of 200 kHz used as the default |
Libraries¶
Installed versions read via importlib.metadata.version at the time the validation suite ran. The PyPI distribution name is the string passed to pip install; the documentation URL is the library’s primary docs entry point.
Library |
PyPI distribution |
Installed version |
Documentation |
Role in validation |
|---|---|---|---|---|
TorchSig |
|
|
Wrapped backend providing |
|
PyTorch |
|
|
Tensor primitives, |
|
NumPy |
|
|
Frequency-shift arithmetic, sample-moment estimators in the cumulant tests, and array primitives |
|
SciPy |
|
|
|
|
Pydantic |
|
|
Schema validation for |
|
Matplotlib |
|
|
Renders the eight embedded figures (PSDs, constellation scatter plots, cumulant bars) |