Core Types¶
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: ground truth for one signal, including one accepted placed event in a composed scene.
SceneMetadata: scene-level ground truth for records.
LabeledScene: one composed scene plus the labels derived from it, carried from composition to projection.
StoredRecord: the same value with IQ stripped, used by read-only audit and annotation paths.
BBox: time-frequency target used by detectors and label exporters.
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 component Signal objects for accepted placed events.
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 LabeledScene (below) flattens the structure before a projection encodes it. Signal is the transmit-side and propagation container: past the receiver seam the in-memory container is ComplexCapture instead, in both domains. See Signal, Capture, Record for the handoff.
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[]
├── event 0: Signal # metadata in scene reference frame
├── event 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 receiver frontend runs, first its capture plane
(ReceiverStagePlane.CAPTURE) and then its hardware plane (ReceiverStagePlane.HARDWARE).
Those receiver-side planes 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.
component_signals[] is cardinal by accepted placement, not by planned emitter
slot: an allowed-empty time strategy can leave a selected slot with no
component, while a strategy that returns several valid starts yields one
component per accepted event. The event-fit rule for those starts is defined in
Scene Capture Boundaries; their scene-rate and
per-RX coordinate meanings are defined in Coordinate Systems.
Note: “component Signal objects” are not LabeledScene instances. LabeledScene is the composed-and-labeled scene defined below; components are in-memory Signal objects produced during scene composition. Per-component IQ is not part of the storage contract (see the LabeledScene 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.
LabeledScene¶
LabeledScene is the value that leaves composition, not an in-memory Signal. It carries what a projection needs in order to encode a record:
receiver IQ,
scene metadata,
per-event metadata,
a
LabelSet: bounding boxes (bboxes) and an optional segmentation raster,references to external assets, through scene metadata, when the scene depends on large geometry/material blobs.
LabeledScene.iq is the composed receiver IQ. LabeledScene.emitters is
tuple[SignalMetadata, ...], not tuple[Signal, ...]; per-event IQs are not
part of the storage contract. The pairing of receiver IQ with scene metadata
happens through LabeledScene.iq plus LabeledScene.scene (a
SceneMetadata); per-emitter IQs
are intentionally not stored.
LabeledScene is not itself the on-disk shape. A projection turns it into typed
observation fields namespaced under that projection’s id, and the store writes
them as one signal_dataset.Record. Read-only audit and annotation paths take
StoredRecord instead, built with
StoredRecord.from_labeled_scene(scene): the same scene, emitters, and boxes
with the IQ payload dropped. See Signal, Capture, Record.
Labels are derived while in-memory component
Signal objects are still available to the
labeler. Bounding boxes come from
per-event 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-event 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.
In particular, a component’s start_sample and duration_samples preserve
the scene/channel-rate placement, whereas a BBox and its segmentation mask
use the active receiver-record sample rate. The labeler maps the same half-open
event interval outward onto that receiver grid; it does not overwrite component
provenance or crop an invalid interval. Coordinate Systems
§ Component-to-record time mapping
is the single detailed conversion contract.
Scenes and records stand one to one. A multi-RX scene still publishes one record, carrying each receiver as its own named subtree. 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.