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.integrations.torchsig.emitters.torchsig_comms 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 −3 dB spectral width 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 is similar across the tested cohort (§3.9): the measured waveform-to-symbol ratios occupy a narrow range for the five tested 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 and non-finite runtime inputs raise
EmitterErrorbefore arithmetic; malformed parameter schemas raise PydanticValidationErrorduring validation.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 are recorded exactly, and their observed results are checked against the tolerances documented in the evidence sections below.
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 and remaining within the test’s stated tolerance. 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 spectral width, with linear tracking inside the operating envelope. A downstream training pipeline will sweep bandwidth_hz to vary symbol rate; if the realised width 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 scipy.signal.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 TorchSig’s constellation_modulator at v2.1.1 (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.
In the tested N = 500000 cohort, the measured waveform values preserve the reference ordering across the six tested constellations; the smallest observed cross-class gap is 16QAM - 64QAM = 0.054. This is an observed separation for the tested cohort, not a general sampling-uncertainty bound. 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 below the reference values, with the relative ordering preserved across every adjacent pair in the tested cohort.
3.9 Cumulant attenuation is similar across the tested cohort¶
Waveform-level |C42| measurements are lower than the symbol-level reference in the tested cohort. SRRC-filter inter-sample correlation is the documented mechanism under test: the oversampled waveform’s higher-order statistics are smoothed by the common pulse-shape FIR. The measurements below characterize this cohort; they do not prove a constellation-independent attenuation factor for untested configurations.
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. Their descriptive coefficient of variation is 2.2% for these five measurements. 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 combined spectral-extent 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, and extra fields) raise Pydantic ValidationError during parameter validation. Non-finite runtime inputs raise EmitterError before waveform arithmetic.
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 PSD estimator that averages overlapping windowed periodograms) 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), these are the exact settings exercised by the cited bandwidth tests. The measured crossings are evaluated against each test’s stated tolerance; this report does not assign an analytic uncertainty or coefficient of variation to those crossings.
The cumulant tests use N = 500000 and compare the observed class values and ordering directly with their checked assertions. The validation establishes the observed gaps for that tested cohort; it does not claim a numeric standard error or population-level statistical conclusion.
Sample-size choice: PSD-bandwidth tests use N = 400000, cumulant tests use N = 500000, and shape and determinism tests use N = 100000. Those sizes and the reported test tolerances define the verified configurations; no broader uncertainty guarantee is inferred from record length alone. Welch averaging is used for the stationary emitter outputs exercised here. This report does not prescribe an estimator for non-stationary signals.
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. Adjacent 8PSK index neighbours can differ by up to 3 bits. The physical waveform shape is unaffected; only the bit-to-symbol interpretation differs. 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-width contraction and −18.8 dB measured stopband floor. Both deviations from the ideal SRRC response originate from the polyphase resampler inside
constellation_modulator. They are retained as measured upstream-library behavior; this report does not compare them with a transmitter standard or claim spectral-mask compliance.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 151 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 |
|
|
PSD estimator used in the bandwidth and SRRC-shape tests |
|
Pydantic |
|
|
Schema validation for |
|
Matplotlib |
|
|
Renders the eight embedded figures (PSDs, constellation scatter plots, cumulant bars) |