Architecture¶
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.
The framework is six swappable layers, emitters, channels, scenes, labels, annotations, storage, wrapped by one configuration system. The framework owns the contracts between layers and delegates specialized work to mature libraries, backends, and adapters wherever practical.
Minimal Example¶
preset names a shipped Hydra recipe; see Scenario Presets for the full list. Each key in overrides is a Hydra dotted path targeting a nested config field (e.g. run.num_samples sets cfg.run.num_samples); the dot notation is the same convention Hydra uses on its CLI.
from rfgen.orchestration.local import LocalRunner
cfg = LocalRunner.build_config(
preset="narrowband_classifier_baseline",
overrides={"run.num_samples": 5, "storage.path": "./out/demo"},
)
runner = LocalRunner(cfg)
records = list(runner.run()) # returns list[Record]
# Each record has IQ, labels, and metadata
record = records[0]
print(record.iq.shape) # torch.Size([2, 1_000_000]) -> (n_rx_antennas=2, n_samples)
print(record.scene.scene_geometry_hash) # "sha256:…" (record.scene is SceneMetadata)
print(len(record.bboxes)) # number of emitter bounding boxes (record.bboxes is tuple[BBox, ...])
Pipeline¶
Generation runs in two phases. Phase 1 turns a config into IQ, metadata, and structured labels deterministically. Phase 2 attaches inference-generated text annotations asynchronously against already-stored records.
The Phase 1 channel chain is split into four ordered groups, each named for the operation family it owns. TX impairments model per-emitter transmitter-side effects: DAC quantization, power-amplifier (PA) nonlinearity, phase noise, IQ imbalance, and carrier frequency offset (CFO). Channel propagation is the over-the-air propagation step delegated to Sionna. RX capture models the receiver’s analog front-end as the signals are summed: RF-to-baseband mixing, IF filtering, fractional resampling to the receiver sample rate, summation across emitters, and low-noise-amplifier (LNA) thermal noise. RX hardware models the digital-side receiver effects: ADC quantization, receiver phase noise, receiver IQ imbalance, and automatic gain control (AGC). Each acronym is defined in the Glossary; the per-group transformations and their backends are catalogued in Concepts / Channels.
flowchart TD
cfg["<b>CONFIGURATION</b><br/>Hydra + Pydantic<br/>emitter · channel · scene<br/>label · annotator · storage"]
subgraph p1 ["Phase 1"]
direction TB
subgraph scene ["SCENE: planner + driver"]
direction TB
plan["<b>plan</b><br/>assign f_c, start_sample, tx_pos<br/>per emitter slot"]
loop["<b>for each emitter slot (pre-sum)</b><br/>EMITTER → TX impairments<br/>(DAC, PA, phase noise, IQ imb., CFO)<br/>→ channel propagation (Sionna)"]
sum["<b>per receiver (post-sum)</b><br/>RX capture (mix, filter,<br/>resample, sum, LNA noise)<br/>→ RX hardware (ADC, AGC, etc.)<br/>→ Signal w/ component Signals"]
plan --> loop --> sum
end
label["<b>LABEL</b><br/>bbox + segmentation<br/>+ per-emitter metadata"]
scene --> label
end
storage[("<b>STORAGE</b><br/>Zarr + WebDataset")]
annotation["<b>ANNOTATION</b><br/>Templater → inference client → Verifier<br/>(model IDs pinned outside concepts)"]
cfg --> p1
label --> storage
storage -. append .-> annotation
classDef phase1 fill:#e8f3ff,stroke:#3068a8,color:#1c3a66;
classDef phase2 fill:#fff1e8,stroke:#a86230,color:#663d1c;
classDef store fill:#f0f0f0,stroke:#666,color:#222;
class p1,scene,label phase1
class annotation phase2
class storage store
Phase 1 is deterministic and compute-bound: the same resolved config, global seed, shard ID, sample index, backend versions, and device determinism settings should reproduce the same IQ and metadata. Phase 2 is cost-bound, async, and separately resumable: re-annotating an existing dataset never requires re-generating IQ.
Config Distributions And Realized Records¶
In this page “realized” is the technical antonym of “the prior”: it is the concrete value the random draw produced for one record (the realized SNR, the realized emitter count, the realized bounding-box coordinates), as opposed to the distribution the config declared. The generator is stochastic by design, but reproducible. A fully-merged
GenerationConfig (the post-defaults-merge, post-validation config; “resolved” elsewhere in the docs)
and its scenario preset usually describe a dataset
distribution, not one exact recording. A run with num_samples=1000 creates
1000 concrete Record objects by drawing
from that distribution with deterministic seeds.
flowchart LR
cfg["GenerationConfig<br/>scenario preset<br/>fixed values + distributions"]
seed["Seed schedule<br/>global_seed<br/>shard_id<br/>sample_idx"]
composer["Scene composer<br/>realizes one scene plan"]
record1["Record 000001<br/>3 emitters<br/>Wi-Fi + BLE + radar"]
record2["Record 000002<br/>1 emitter<br/>LoRa"]
record3["Record 000003<br/>5 emitters<br/>dense ISM mix<br/>(2.4/5/24 GHz Wi-Fi/BLE)"]
audit["Dataset audit<br/>requested vs realized<br/>class balance, density, SNR"]
cfg --> composer
seed --> composer
composer --> record1
composer --> record2
composer --> record3
record1 --> audit
record2 --> audit
record3 --> audit
Some config fields are fixed values. Others are ranges, weights, or named distributions:
Config intent |
Example |
Realized record |
|---|---|---|
Fixed value |
|
Every scene has three emitter slots. |
Range |
|
Each scene records the actual emitter count drawn for that scene. |
Weighted choice |
Wi-Fi weight 0.7, BLE weight 0.2, radar weight 0.1 |
Each emitter slot records the chosen class and parameters. |
Continuous prior |
Signal-to-noise ratio (SNR) range |
Metadata stores the target and realized SNR after the channel chain (the LNA-noise transformation in |
Placement strategy |
iid_uniform, ism_realistic, forced_overlap (frequency); see Scene Composition Algorithm for the full set |
Metadata stores actual start samples, frequency offsets, and overlap outcomes. |
This distinction is central to the architecture:
Config describes what population of examples the dataset should contain.
The scene composer realizes one concrete scene at a time from that config.
Labels and storage persist realized metadata, not just the requested distribution.
Audits compare requested distributions against realized dataset statistics.
Fixed datasets are still supported: set fixed counts, fixed classes, fixed parameters, and deterministic placement. Most useful training datasets mix fixed structure with stochastic nuisance variables, such as fixed waveform families but random timing, frequency offset, SNR, payload, and channel realization.
Six Layers¶
Layer |
Input |
Output |
Contract |
|---|---|---|---|
emitter config and seeded RNG |
clean baseband Signal (a complex-valued IQ waveform centered at zero frequency, before any channel impairment or carrier shift; see Glossary) plus per-emitter metadata |
||
component Signal or summed receiver signal, depending on stage |
transformed Signal plus channel metadata |
||
emitter pool, channel chain, scene config |
receiver IQ plus preserved component Signal objects |
||
composed IQ, component metadata, scene metadata |
Record with structured labels |
||
stored Record metadata and labels |
append-only text fields |
||
persisted records and optional asset blobs |
Full API signatures live in Reference / API. The data objects that move between layers are summarized in Core Types.
Scene layer: placement strategy ABCs¶
The Scenes layer exposes three pluggable ABCs:
Substrate |
ABC |
Notes |
|---|---|---|
Composer |
Primary scenes ABC; orchestrates emitter selection, placement, channel routing, and receiver summation |
|
Time placement |
Strategy ABC for per-emitter start-sample scheduling (e.g. radar pulse trains, Wi-Fi beacons, frequency-hopping schedules) |
|
Frequency placement |
Strategy ABC for per-emitter carrier frequency selection |
BaseTimePlacement and BaseFrequencyPlacement are pluggable extension points. Custom strategies subclass the relevant ABC and are selected through validated configuration.
Annotations layer: substrate ABCs¶
The Annotations layer exposes three ABCs with distinct roles:
Substrate |
ABC |
Notes |
|---|---|---|
Annotator |
Primary annotations ABC; owns templating, validation, and persistence decisions |
|
Inference client |
Provider-agnostic inference call surface injected into annotators |
|
Batch orchestrator |
Phase 2 batch submission, polling, and result fetching |
BaseInferenceClient and BaseBatchAnnotationOrchestrator are not in the BaseAnnotator inheritance hierarchy. They are injected dependencies that allow swapping inference providers or batch backends without changing annotator code.
Cross-Cutting Contracts¶
The contracts below are framework-wide invariants every layer respects, regardless of which implementation is plugged in. They are what keeps the six layers composable: a backend swap stays mechanical only as long as these hold.
Contract |
Rule |
Detail |
|---|---|---|
Data model |
Layers pass Signal, component metadata, and Record, not naked tensors or ad hoc dicts. |
|
Asset storage |
Large Sionna RT geometry/material assets are content-addressed and stored once. |
|
Determinism |
|
|
Configuration |
Behavior changes through validated config, not hidden feature flags. |
|
Streaming |
The public contract is record/Signal iteration; batching is an implementation optimization. |
ABC pluggability¶
Every layer has one primary abstract base class (ABC). Per-slot ABCs are permitted under the layer ABC where multiple implementations exist (e.g. the channel layer’s 14 per-transformation ABCs sitting under BaseChannel). Concrete classes inherit from ABCs only; concrete-to-concrete inheritance is forbidden (an ABC may extend another ABC, but a concrete class never extends another concrete class). See Background / Design Decisions.
Two-ABC layers¶
Some layers require two ABCs to cover different lifecycles or roles:
Storage: BaseStore is the factory ABC; StoreHandle is the open-session ABC. The two ABCs exist because the factory and open session have different lifetimes. See Concepts / Storage.
Annotations: BaseAnnotator is the primary ABC. BaseInferenceClient and BaseBatchAnnotationOrchestrator are supporting ABCs injected as dependencies, not inherited by annotators.
Consumer Access¶
The six layers above produce and persist records. Downstream training pipelines consume them through a separate consumer-side toolkit (sampler, collator, PyTorch Dataset adapter) that wraps the storage layer’s read API. See Consumer Access for the consumer-side concept page and the ABCs it introduces.
Cross-Cutting Conventions¶
The topics below also span every layer, but are shared frames of reference, not rules. The contracts above operate inside the conventions below.
Convention |
What it covers |
Detail |
|---|---|---|
Frequency and time frames |
Each emitter outputs IQ samples as if it were the only signal at frequency 0 (its own local baseband); the scene composer shifts each emitter to its scene-level carrier frequency and start time before summation. |
|
Multi-RX records |
Default output is one record per receiver; joint tensors are opt-in. |
See Also¶
Core Types for Signal, Record, metadata, and
component_signals[].Coordinate Systems for scene center frequency, emitter carrier offsets, and scene-relative time.
Records, Receivers, and Assets for RecordAxis, multi-RX output, and content-addressed scene assets.
Background / Design Decisions for the rationale behind ABCs, enums, backend ownership, and wrapper choices.
Reference / Project Layout for the proposed source tree.