Core Types¶
Warning
Pre-implementation. This page describes proposed contracts. Class signatures, parameter types, config field names, and behavior are subject to change before code lands. Once implementation exists, content here will be regenerated from docstrings or sourced from running tests.
Core types are the stable data contracts between layers. Emitters, channels, scenes, labels, annotations, and storage should exchange these objects rather than raw tensors or layer-specific dictionaries.
What It Does¶
The core type layer provides:
Signal: in-phase/quadrature (IQ) sample stream plus signal metadata, used while generation is still in memory. IQ is the complex-valued RF waveform representation that the framework passes between layers.
SignalMetadata: per-emitter or per-signal ground truth.
SceneMetadata: scene-level ground truth for records.
Record: persisted dataset unit after labels are attached.
BBox: time-frequency target used by detectors and label exporters.
ChunkedSignal: streaming wrapper produced when a scene exceeds the in-memory threshold.
The module also defines tensor type aliases (IQ, Spectrogram) and scalar aliases (SampleRate, CenterHz) used as field types throughout. SampleRate is samples per second in hertz. CenterHz is an absolute carrier or receiver center frequency in hertz, not a scene-relative offset. Field-by-field definitions live in Reference / API / Core Types.
Minimal Shape¶
This is a minimal shape sketch. The full dataclass signatures, imports, and field tables live in Reference / API / Core Types.
from dataclasses import dataclass, field
import torch
@dataclass(frozen=True)
class Signal:
iq: torch.Tensor
metadata: SignalMetadata
component_signals: tuple["Signal", ...] = field(default_factory=tuple)
Signal.iq is the numeric payload. Signal.metadata explains the payload.
component_signals[] carries the nested per-emitter Signal objects after scene composition; the type is recursive.
Signal¶
Every generation layer speaks Signal:
Emitters return clean baseband Signal objects: the emitter’s complex IQ centered at 0 Hz before channel propagation, receiver capture, or receiver hardware transformations.
Channels transform
Signal -> Signal.Scene composition returns receiver IQ plus the per-emitter component Signal objects.
Labels read the scene-level Signal and its
component_signals[].
The key invariant is that IQ and metadata always move together during generation. A layer should not pass a naked tensor and rely on side channels to recover sample rate, bandwidth, class name, device identity, frequency offset, or placement. This invariant scopes to the in-memory generation pipeline; the persisted Record (below) flattens the structure for on-disk storage. The in-memory pipeline only ever sees Signal objects.
Component Signals¶
Each entry in component_signals[] is itself a Signal; the type is recursive. The “Component Signals” name refers to this list of nested Signal objects, not to a separate class. Multi-emitter scenes use component_signals[]:
scene_signal: Signal
├── iq # summed scene IQ
├── metadata # SceneMetadata
└── component_signals[]
├── emitter 0: Signal # metadata in scene reference frame
├── emitter 1: Signal
└── ...
The scene-level iq is the receiver waveform after each emitter’s contribution
is routed to each receiver, emitter contributions are summed per receiver, and
the post-sum Group.RX_CAPTURE and
Group.RX_HARDWARE channel-pipeline groups run.
Those receiver-side groups cover mixing to the receiver frame, intermediate-frequency
(IF) filtering, resampling, low-noise amplifier (LNA) noise, analog-to-digital
converter (ADC) quantization, receiver (RX) phase noise, RX IQ imbalance, and
automatic gain control (AGC). Component IQ may exist as in-memory composition
state, but each component Signal’s
metadata is the stable contract that labels and storage depend on.
Each component Signal carries metadata
after scene placement and channel routing. Placement realizes concrete time,
frequency, and power fields from the scene plan: scene-relative start_sample,
absolute realized_carrier_hz, bandwidth, realized signal-to-noise ratio
(SNR) or power fields, device identity, receiver association when applicable,
and any backend-specific channel state needed downstream. “Realized” means the
sampled value after scene planning, not the unresolved distribution in config.
Note: “component Signal objects” are not Record instances. Record is the persisted dataset unit defined below; components are in-memory Signal objects produced during scene composition. Per-component IQ is not part of the default storage contract (see the Record section).
This follows TorchSig’s nested-signal pattern where practical. TorchSig is an RF machine-learning dataset and signal-generation library; matching its nesting model lets RF machine-learning (RFML) tooling consume generated data with minimal conversion. See TorchSig Interop for the conversion contract.
Record¶
Record is the on-disk shape, not an in-memory Signal. It is the persisted dataset unit and corresponds to what a training job reads:
IQ or a reference to IQ,
scene metadata,
per-emitter metadata,
bounding boxes (bboxes) and segmentation masks for time-frequency labels,
optional text annotations,
references to external assets when the record depends on large geometry/material blobs.
Record.iq is the stored receiver IQ. Record.emitters is
tuple[SignalMetadata, ...], not tuple[Signal, ...]; per-emitter IQs are not
part of the default storage contract. The pairing of receiver IQ with scene
metadata happens through Record.iq plus Record.scene (a
SceneMetadata); per-emitter IQs
are intentionally not stored.
Labels are derived before the Record is
persisted, while in-memory component Signal
objects are still available to the labeler. Bounding boxes come from
per-emitter metadata such as start_sample, duration_samples,
realized_carrier_hz, and bandwidth_hz; segmentation masks are rasterized
onto the stored receiver IQ grid before storage. The record then stores the
derived labels plus per-emitter metadata, not the component IQ. See
Reference / API / Core Types for the full
field surface, Label Schema for box
and mask semantics, and Records, Receivers, and Assets for
why per-emitter IQ is not persisted.
The mapping between scenes and records is not always one-to-one. Multi-RX scenes can produce one record per receiver or one joint record. See Records, Receivers, and Assets.
Metadata Rules¶
Metadata is schema-versioned. Adding fields is non-breaking; removing or changing field meaning requires a schema bump.
Canonical fields stay small and stable.
Backend-specific fields live in
extras.Values that select a framework-owned closed choice should be enum members in Python and enum string values in YAML. See Reference / API / Enums.
See Also¶
Reference / API / Core Types for the full field surface.
Reference / IQ Layout Policy for the canonical
(2, N)float32 in-memory shape, storagecomplex64shape, and the conversion boundary.TorchSig Interop for round-trip conversion rules.
Tensor library policy for why
torch.Tensoris canonical and where NumPy is correct.