Scientific validation: CW tone emitter

Validated with documented limitations.

1. The component

TorchSigToneEmitter is a deterministic generator that produces a pure continuous-wave (CW) tone, a sinusoid of constant amplitude and constant frequency, at complex baseband. CW is the canonical test tone used to calibrate spectrum analysers and bench signal generators; in a foundation-model training corpus it is the simplest signal class and the zero-modulation reference against which all modulated waveforms are characterised.

class TorchSigToneEmitter(BaseEmitter):
    family: ClassVar[EmitterFamily] = EmitterFamily.COMMS
    supported_classes: ClassVar[tuple[str, ...]] = ("cw",)

    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 TorchSigToneParams(BaseModel):
    bandwidth_hz: float = Field(default=1.0, gt=0)

Parameter

Type

Units

Default

Purpose

class_label

str

n/a

required

Must equal "cw". One class is supported.

sample_rate

float

Hz

required

Samples per second on the output tensor. Must be finite and positive.

duration_s

float

s

required

Output duration. Total sample count is N = round(sample_rate * duration_s). Must yield N >= 1.

f_offset_hz

float

Hz

required

Baseband frequency offset of the tone from DC (zero frequency). Must satisfy &#124;f_offset_hz&#124; < sample_rate / 2 (the Nyquist constraint for complex baseband).

rng

torch.Generator

n/a

required

State advanced by one int64 draw per call to match the seed-stream contract of sibling emitters. The tone itself is deterministic and consumes no randomness.

device_id

str or None

n/a

None

Optional metadata label echoed into Signal.metadata. Never affects IQ.

params.bandwidth_hz

float

Hz

1.0

Symbolic occupied-bandwidth token. A CW tone has zero true bandwidth (a Dirac line in the frequency domain), but downstream channel-allocation logic requires a finite positive width. The default 1.0 Hz matches TorchSig’s ToneSignalGenerator convention. It is not a measured 3 dB bandwidth.

import torch
from rfgen.emitters.torchsig_tone import TorchSigToneEmitter

emitter = TorchSigToneEmitter()
signal = emitter.generate(
    class_label="cw",
    sample_rate=1_000_000.0,   # 1 MHz
    duration_s=0.05,            # 0.05 s
    f_offset_hz=100_000.0,      # 100 kHz offset
    rng=torch.Generator().manual_seed(0),
)
# signal.iq has shape (2, 50_000) and dtype torch.float32:
# channel 0 is the in-phase (I = real part) component,
# channel 1 is the quadrature (Q = imaginary part) component.
assert signal.iq.shape == (2, 50_000) and signal.iq.dtype == torch.float32

The taxonomy position is digital communications / tone: one class label (cw), family COMMS, taxonomy tuple ("comms", "tone", "cw"). The output represents the discrete-time complex exponential y[n] = exp(j * 2 * pi * f_offset_hz * n / sample_rate), the baseband (centred near DC) representation of a carrier offset by f_offset_hz Hz, where n is the sample index. No modulation, symbol stream, or pulse shape is applied.

One documented edge case: when f_offset_hz = 0, the all-ones TorchSig output is its own DC (zero-frequency, constant-amplitude) component. The wrapper preserves this unit-amplitude DC tone because the scene composer generates at baseband before applying frequency placement; subtracting the mean would erase the signal.

2. What we validated

This validation establishes 8 load-bearing claims. Each is restated and supported by evidence in §3.

  1. Shape and dtype contract (§3.1): the output tensor has shape (2, N) and dtype float32 for any in-envelope configuration.

  2. Frequency-shift identity (§3.2): the IQ samples implement the discrete-time complex-exponential formula from Oppenheim and Schafer, confirmed by constant-envelope and spectral-peak tests.

  3. Spectral peak accuracy (§3.3): the peak FFT bin lands within one 20 Hz bin of the requested offset across the full Nyquist band.

  4. Out-of-band rejection and harmonic absence (§3.4): spurious-free dynamic range exceeds 80 dBc, beating the R&S SMA100B bench-generator specification.

  5. DC carrier case (§3.5): f_offset_hz = 0 yields a unit-amplitude DC tone as documented.

  6. Input-validation envelope (§3.6): Nyquist guard, zero-sample guard, and class-label guard reject every out-of-envelope input before any arithmetic.

  7. Determinism and RNG-state advancement (§3.7): same seed reproduces bit-identical IQ, and the parent random-number generator (RNG) state advances by exactly one int64 draw per call.

  8. Robustness at boundary (§3.8): near-Nyquist inputs are accepted with finite output; negative-offset conjugate symmetry holds.

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 the cw class label. This is the structural contract every downstream consumer (data-loader, model input layer) depends on.

Measured at sample_rate = 1 MHz, duration_s = 0.01 s, N = 10 000: shape (2, 10000), dtype torch.float32. Test: test_shape_and_dtype in tests/validation/emitters/torchsig_tone/test_experiment_contract.py.

Metadata contract confirmed in the same pass: family = "comms", class_name = "cw", class_taxonomy = ("comms", "tone", "cw"), generator_name = "TorchSigToneEmitter", snr_db = inf, realized_carrier_hz == f_offset_hz, bandwidth_hz = 1.0 (default), start_sample = 0, duration_samples = N. Test: test_metadata_fields in tests/validation/emitters/torchsig_tone/test_experiment_contract.py.

3.2 Frequency-shift identity

The emitter synthesises the discrete-time complex exponential defined by Oppenheim and Schafer (2010), eq. 1.6 and §4.2:

y[n] = exp(j * 2 * pi * f * n / fs),   n = 0, 1, ..., N-1

where f = f_offset_hz and fs = sample_rate. The TorchSig tone_modulator returns a length-N all-ones array (constant amplitude), and the wrapper applies the digital heterodyne (frequency-shifting by mixing) exp(j * 2 * pi * f * t) where t = arange(N) / fs. The result is exactly the textbook formula.

The constant-envelope property follows directly: |exp(j * theta)| = 1 for any real theta. The empirical envelope |I + jQ| after DC subtraction equals 1.0 within absolute tolerance 1e-5 (float32 precision) for all four tested non-zero offsets {+25 kHz, +50 kHz, +100 kHz, -150 kHz} at N = 50 000, fs = 1 MHz.

Test: test_envelope_flat (parametrised) in tests/validation/emitters/torchsig_tone/test_experiment_contract.py.

Figure 2 shows the envelope and instantaneous frequency (rate of change of phase, the phase derivative divided by 2*pi) for all four offsets, confirming both properties over the first 200 samples.

Figure 2: Envelope and instantaneous frequency for four offsets, confirming constant envelope within 1e-5 and instantaneous frequency constant at the requested offset. Supports the frequency-shift identity claim in §3.2.

3.3 Spectral peak accuracy

A CW tone must place essentially all its energy in one FFT bin, at the bin nearest to the requested f_offset_hz. The spectral estimator is a Hann-windowed (a raised-cosine weighting applied to the samples before computing the FFT, which reduces spectral leakage at the cost of slightly wider mainlobe) FFT at N = 50 000, fs = 1 MHz, giving a bin resolution of fs / N = 20 Hz.

The peak FFT bin lands within one bin (±20 Hz) of the requested offset for all 7 offsets in {-400, -100, -10, +10, +100, +200, +400} kHz. Test: test_psd_peak_at_requested_offset (parametrised) in tests/validation/emitters/torchsig_tone/test_empirical_known_results.py.

A 19-point sweep from -450 kHz to +450 kHz (stepping through values non-coincident with exact bin boundaries) confirms the same alignment across the full Nyquist band. Figure 3 shows the peak locations and residual errors for all 19 points.

Figure 3: FFT peak bin vs requested f_offset_hz across 19 offsets spanning -450 kHz to +450 kHz. All measured peaks land within one 20 Hz bin of the request. Supports the spectral peak accuracy claim in §3.3.

3.4 Out-of-band rejection and harmonic absence

Real bench signal generators specify a spurious-free dynamic range (SFDR, the ratio in decibels relative to the carrier (dBc) between the carrier power and the strongest spurious output anywhere else in the spectrum, and harmonic levels. The R&S SMA100B specifies SFDR better than -80 dBc and harmonics below -30 dBc.

A numerically synthesised tone has no analog mixer to introduce spurs or a nonlinear power amplifier to produce harmonics. The following measurements confirm the digital floor is well below the hardware floor:

  • Out-of-band rejection: energy outside a ±200 Hz window around the carrier is at least 80 dB below the peak at offsets {+50 kHz, +200 kHz}. The 80 dB threshold is 20 dB above the Hann-window sidelobe floor at N = 50 000 (~-100 dB), leaving margin for float32 noise without admitting real spectral leakage. Test: test_out_of_band_rejection in tests/validation/emitters/torchsig_tone/test_empirical_known_results.py.

  • Second-harmonic suppression: energy in a ±1 kHz window around 2 * f is at least 80 dB below the fundamental for f = 50 kHz. This is 50 dB better than the -30 dBc hardware specification. Test: test_no_harmonic_at_2f in tests/validation/emitters/torchsig_tone/test_empirical_known_results.py.

  • Third-harmonic suppression: energy in a ±1 kHz window around 3 * f is at least 80 dB below the fundamental. Test: test_no_harmonic_at_3f in tests/validation/emitters/torchsig_tone/test_empirical_known_results.py.

  • Close-in phase-noise floor: energy in a 9–11 kHz offset window around a carrier at 100 kHz is at least 80 dB below the peak. The R&S SMA100B specifies approximately -145 dBc/Hz single-sideband (SSB) phase noise (noise power density at a given frequency offset from the carrier, expressed in dBc per Hz of bandwidth) at 10 kHz offset; scaled to a 1 kHz bin, that is approximately -115 dBc. The 80 dB threshold is therefore conservative. Test: test_phase_noise_floor_below_real_source in tests/validation/emitters/torchsig_tone/test_empirical_known_results.py.

Figure 1 shows the full Hann-windowed power spectral density (PSD; signal power per unit bandwidth, in dB) for the f = 100 kHz case, with the -80 dB threshold line.

Figure 1: Hann-windowed PSD of CW tone at 100 kHz offset. All bins outside the carrier are at least 80 dB below the peak, satisfying the SFDR threshold and confirming harmonic absence. Supports the out-of-band rejection claim in §3.4.

3.5 DC carrier case

When f_offset_hz = 0, the TorchSig tone_modulator returns an all-ones array. This array is its own DC (zero-frequency) component. The wrapper preserves it as the real component of a unit-amplitude CW signal, with a zero quadrature component; subtracting the empirical mean would emit no signal at all. This matters because the scene composer generates CW at baseband and applies its carrier placement later.

The output IQ tensor has an all-ones in-phase channel and an all-zero quadrature channel. Metadata records realized_carrier_hz = 0.0 and bandwidth_hz = 1.0. Test: test_dc_case_preserves_unit_amplitude_tone in tests/validation/emitters/torchsig_tone/test_robustness_envelope.py.

The DC residual for non-zero offsets was separately measured: mean(I) = -9e-11, mean(Q) = +6e-11 at fs = 1 MHz, N = 50 000, f = 50 kHz. This residual is at the float64 arithmetic limit before the float32 cast and well within the 1e-5 threshold. Test: test_dc_residual_below_float32_noise_for_nonzero_offset in tests/validation/emitters/torchsig_tone/test_robustness_envelope.py.

3.6 Input-validation envelope

Three guards reject out-of-envelope inputs before any arithmetic:

  • Nyquist guard: |f_offset_hz| >= sample_rate / 2 raises EmitterError. Verified at the exact boundary (f = fs/2), above it (f = 0.51 * fs), and on the negative side (f = -fs/2 - 1). Tests: test_nyquist_band_guard_at_half_rate, test_nyquist_band_guard_above_half_rate, test_nyquist_band_guard_negative_above_half_rate in tests/validation/emitters/torchsig_tone/test_experiment_contract.py.

  • Zero-sample guard: round(sample_rate * duration_s) <= 0 raises EmitterError. Verified at duration_s = 0.0 and duration_s = 1e-7 (which rounds to zero samples at fs = 1 MHz). Tests: test_zero_sample_record_raises, test_subsample_duration_raises in tests/validation/emitters/torchsig_tone/test_robustness_envelope.py.

  • Class-label guard: an unsupported class_label raises EmitterError with the supported class "cw" named in the message. Test: test_unsupported_class_label_raises in tests/validation/emitters/torchsig_tone/test_experiment_contract.py.

Pydantic validates bandwidth_hz > 0 before the emitter runs; invalid schema raises pydantic.ValidationError from the gt=0 constraint.

3.7 Determinism and random-number-generator (RNG) state advancement

The CW synthesis path is fully deterministic: tone_modulator returns the same all-ones array on every call, and the heterodyne exp(j * 2 * pi * f * t) is a closed-form function of f and the sample grid. Two calls with the same f_offset_hz and a torch.Generator seeded from the same value produce bit-identical IQ tensors. Test: test_determinism_same_seed in tests/validation/emitters/torchsig_tone/test_experiment_contract.py.

To preserve the seed-stream contract shared by sibling TorchSig emitters (so callers cannot detect the tone path by RNG consumption; RNG: random-number generator), the wrapper draws exactly one int64 from the parent generator per call. The generator state changes after each call even though the IQ is unchanged. Tests: test_seed_advances_torch_generator, test_emitter_advances_rng_state in tests/validation/emitters/torchsig_tone/test_experiment_contract.py and tests/validation/emitters/torchsig_tone/test_robustness_envelope.py.

3.8 Robustness at boundary

  • Near-Nyquist acceptance: f_offset_hz = sample_rate/2 - 100 Hz is accepted; the output is finite and |IQ|.max() <= 1.0 + 1e-5. Test: test_just_inside_nyquist_passes in tests/validation/emitters/torchsig_tone/test_robustness_envelope.py.

  • Conjugate symmetry: a tone at -f is the element-wise complex conjugate of a tone at +f: the I (in-phase) channel is even in frequency, and the Q (quadrature) channel is odd. Measured element-wise at tolerance 1e-6 for f = 75 kHz. Test: test_negative_offset_mirrors_positive in tests/validation/emitters/torchsig_tone/test_robustness_envelope.py.

  • Device-id passthrough: device_id is echoed verbatim into Signal.metadata.device_id and never affects IQ. Test: test_device_id_passthrough in tests/validation/emitters/torchsig_tone/test_robustness_envelope.py.

4. Limits and what’s not validated

Phase noise is not modelled. Real oscillators exhibit Lorentzian or composite phase-noise profiles ranging from -60 dBc/Hz at 10 kHz offset (free-running voltage-controlled oscillator) to -145 dBc/Hz (crystal-locked source). The synthesised tone has zero phase noise. A classifier that uses spectral-purity features trained on emitter output alone will not generalise to captured CW. Phase-noise injection is a channel-layer concern in the rfgen architecture.

Frequency drift is not modelled. Real oscillators drift with temperature (typical temperature-compensated crystal oscillator (TCXO) accuracy: 1–2 ppm over a commercial temperature range). The synthesised tone is locked exactly to the requested offset.

Harmonic distortion is not modelled. Real power amplifiers produce measurable harmonics at 2f and 3f due to their nonlinear transfer characteristic. The synthesised path is linear and produces none.

Amplitude variation from PA AM-AM compression is not modelled. The synthesised envelope is constant to float32 precision. Real PA (power amplifier) outputs exhibit envelope-dependent gain compression.

Spurious content (reference leakage, power-supply ripple) is not modelled. Real signal generators have spurs at predictable offsets (typically at integer multiples of the reference oscillator frequency). The synthesised tone has none.

Multi-tone scenes are not produced by one call. The emitter produces one tone per call. Two-tone intermodulation distortion (IMD3) measurements require composing two calls at the orchestration layer.

Very short records (N < ~10) are accepted but yield uninformative spectral measurements. The wrapper rejects only N <= 0; at N = 2 the FFT resolution is sample_rate / 2, comparable to the tone offset itself. This is a downstream-measurement concern, not an emitter-correctness concern.

bandwidth_hz = 1.0 Hz is a symbolic token. A CW tone has zero bandwidth in continuous time. The 1 Hz token gives downstream consumers a finite positive width for channel-allocation logic; it is not a measured or RF-standard-defined bandwidth.

5. References

Published works

Reference

Role in this validation

A. V. Oppenheim and R. W. Schafer, Discrete-Time Signal Processing, 3rd ed., Pearson, 2010, ISBN 978-0-13-198842-2, eq. 1.6 and §4.2

Defines the discrete-time complex exponential exp(j * omega * n) that the wrapper synthesises; cited in the module docstring and used in §3.2.

J. G. Proakis and M. Salehi, Digital Communications, 5th ed., McGraw-Hill, 2008, ISBN 978-0-07-295716-7, §3.2

Names CW as the “unmodulated carrier” reference signal in the digital-communications taxonomy; used in §1 for taxonomy placement.

Rohde & Schwarz, SMA100B RF and Microwave Signal Generator Data Sheet, document revision 09.2024

Reference SFDR (-80 dBc), harmonics (below -30 dBc), and SSB phase noise (below -145 dBc/Hz at 10 kHz offset) used as real-world benchmarks in §3.4.

Keysight Technologies, 8657B Synthesized Signal Generator Operating Manual, publication number 08657-90139

Provides secondary SSB phase-noise reference (-100 dBc/Hz at 10 kHz offset) and harmonics specification (below -30 dBc) for the §3.4 comparison.

S. Boegner, M. Vondrasek, et al., “Large Scale Radio Frequency Signal Classification,” arXiv:2207.09918 (2022), doi:10.48550/arXiv.2207.09918

Primary TorchSig citation; describes the signal library whose tone_modulator this emitter wraps.

Libraries

PyPI distribution

Installed version

Docs URL

Role

torchsig

2.1.1

https://torchsig.readthedocs.io/latest/

Provides torchsig.signals.builders.tone.tone_modulator, the all-ones baseband array; the wrapper adds only the heterodyne and Pydantic schema.

torch

2.12.1

https://pytorch.org/docs/stable/

Provides torch.Generator for the seed-stream contract and torch.from_numpy for the IQ tensor.

numpy

2.4.6

https://numpy.org/doc/stable/

Provides numpy.exp, numpy.arange, numpy.fft.* used in the heterodyne and in the validation spectral estimator.

pydantic

2.13.4

https://docs.pydantic.dev/latest/

Validates TorchSigToneParams at construction time; enforces bandwidth_hz > 0.

matplotlib

3.11.0

https://matplotlib.org/stable/

Produces the three validation figures via tests/validation/emitters/torchsig_tone/generate_figures.py.