Coordinate Systems¶
A multi-emitter scene puts emitters at different RF carriers and receivers tuned to their own local oscillators (LOs) and sample rates; bounding-box labels live in time-frequency rectangles tied to a specific receiver. Without consistent rules for which time and frequency frame each value lives in, none of these pieces compose: an emitter generated at 2.4 GHz, a receiver tuned to 2.45 GHz, and a label drawn in receiver-relative coordinates would be ambiguous. This page names the frames the framework uses, says which object owns each, and points at where one frame becomes another in the pipeline.
What This Page Covers¶
The two named frequency frames and which entity owns each.
Sample rate frames per emitter and per receiver.
The scene-rate time coordinates used for placed events and receiver records.
Label coordinate conventions in the receiver frame.
TorchSig boundary adapter notes.
Orientation: From Samples to Records¶
Read the time axis in this order before following the frequency frames below:
A sample is one complex IQ value at a particular rate.
An event is one generated emitter transmission, represented by a contiguous run of scene-rate samples after it is accepted for placement.
A scene is the logical result of composing a fixed-duration receiver capture: its receiver IQ, scene metadata, and accepted event components.
A record is a persisted training or evaluation item derived from that scene after labels are attached. One scene publishes one record; a multi-receiver scene carries each receiver as its own named subtree inside it.
The high-level timing invariant is strict containment: an accepted event’s
scene-rate interval lies wholly within the fixed scene capture,
0 <= start <= scene_samples - event_samples. The detailed policy, including
full-scene defaults and channel-induced tails, belongs to Scene Capture
Boundaries. The sections below define which sample
rate and coordinate frame those quantities use; they do not add another
placement policy.
Frequency Frames¶
Two frequency frames coexist. rfgen stores one absolute carrier frequency per emitter and one tuned center frequency per receiver. Stored SceneMetadata does not carry a separate scene-wide RF anchor between them, though SceneConfig.center_hz still exists as a scene-construction default and fallback.
The two frames¶
Frame |
Owner |
Field |
Purpose |
|---|---|---|---|
Emitter (absolute RF) |
Composer-written SignalMetadata on the placed component |
|
Absolute carrier frequency in Hz where this emitter is placed (e.g., |
Receiver (RX baseband) |
|
Receiver’s local-oscillator (LO) frequency. Maps to 0 Hz in the receiver’s IQ buffer. |
SceneMetadata does not carry center_freq_hz or bandwidth_hz. Emitters and receivers express RF location independently, and frequency-frame transitions happen explicitly inside the receiver frontend at the BaseMixerStage step on the receiver’s capture plane (ReceiverStagePlane.CAPTURE) (see Frame transitions below).
Frame transitions¶
The frame transition happens at one place: BaseMixerStage at the head of the receiver’s capture plane. In-band means the receiver bandwidth overlaps the emitter bandwidth. The mixer multiplies the emitter IQ by a complex sinusoid so the emitter appears at the correct offset inside the receiver’s baseband, the complex IQ frame where 0 Hz is the receiver’s tuned center frequency:
delta_f_hz = emitter.realized_carrier_hz - rx.center_freq_hz
component_in_rx_frame(t) = component(t) * exp(j * 2 * pi * delta_f_hz * t)
At emitter generation, samples are requested-rate emitter-baseband IQ. The current
scene composer invokes emitters with sample_rate=scene_cfg.sample_rate_hz and
f_offset_hz=0.0, so protocol or capture metadata is pre-placement context
rather than the final scene carrier. After TX impairments, the composer resamples
to the scene/channel rate, draws a scene-relative frequency offset, shifts IQ by
it, and stamps realized_carrier_hz = SceneConfig.center_hz + f_offset_hz on
the emitted waveform. It prepares any per-emitter or geometry-backed propagation
for that waveform before drawing its time start. The eventual start remains a
scene-time coordinate: it controls where the already-prepared received result is
mixed, not the emitter or receiver frequency frame. The RX mixer then creates
receiver-baseband IQ at offset
delta_f_hz from 0 Hz so the following BaseIFFilterStage can apply its response in the correct
frequency range.
Source: GNU Radio’s Frequency Xlating FIR Filter documents the receiver-side frequency-translation plus FIR channel-selection pattern used by the RX mixer and IF filter. The detailed rfgen RX-capture contract is in Channels / RX Capture.
When each field is read¶
Emitter generation (BaseEmitter.generate): is requested at the scene/channel rate and may carry protocol/capture frequency metadata. A plugin may return another rate, which the composer normalizes. In the current scene composer it is invoked with
f_offset_hz=0.0; that metadata is not the final placed-scene carrier.Scene frequency placement: after TX transformations and resampling, the composer draws a relative
f_offset_hz, shifts IQ by it, and writesmetadata.realized_carrier_hz = scene_cfg.center_hz + f_offset_hz. This is the absolute carrier used by later stages.Channel (BaseChannel, Group.CHANNEL): reads
metadata.realized_carrier_hzfor carrier-dependent propagation effects such as wavelength scaling and Doppler. Per-emitter and geometry-backed propagation is prepared from this emitted waveform before the time planner draws a start; a statistical backend inscenemode instead runs after receiver summation.RX mixer (BaseMixerStage): computes the offset
(emitter.realized_carrier_hz - rx.center_freq_hz)and applies it.Labels: the label stage computes bounding boxes in the receiver’s frame. Bbox frequency limits are receiver-relative offsets from
rx.center_freq_hz.Out-of-band check: if
|emitter.realized_carrier_hz - rx.center_freq_hz| > rx.bandwidth_hz/2 + emitter.bandwidth_hz/2, this RX does not hear this emitter; the component is dropped from the RX’s chain and omitted from that receiver’s subtree.
Sample Rate Frames¶
Three rate frames exist; the framework uses each at one stage.
Rate |
Where |
Why |
|---|---|---|
Emitter-returned rate ( |
A plugin may return this in |
Supports a plugin whose waveform implementation has a distinct internal rate without changing placed-scene coordinates. |
RX rate ( |
Effective receiver rate: |
Each receiver’s ADC samples at its own rate. |
Channel rate ( |
|
One shared buffer rate makes frequency placement, propagation, and scene composition consistent. Sample-rate-dependent fading, discrete delay positions, and label timing must refer to the rate of the buffer they describe. |
The composer performs a plugin-returned R_emitter → R_channel transition,
when needed, before per-emitter propagation preparation and time-start drawing.
The BaseResamplerStage step
on the receiver’s capture plane performs the later
R_channel → R_rx transition for each receiver.
Stored scene metadata does not carry a scene-wide sample-rate field. SceneConfig.sample_rate_hz still exists at construction time: it sizes the scene/master buffer and supplies the fallback receiver rate when a receiver omits sample_rate_hz.
Source: Sionna’s TR 38.901 channel model APIs expose sampling frequency as a channel-model input, while SciPy’s signal.resample_poly documents the polyphase FIR resampling pattern used by the default RX resampler. The propagation handoff is described in Channels / Channel Propagation.
Time Frames¶
Time placement is in seconds at scene level; sample-indexed inside each receiver’s subtree of the record. This page owns the meaning and rate frame of those coordinates. The separate Scene Capture Boundaries page owns the rule that decides whether a generated event is allowed to occupy a particular interval of that coordinate system. For the per-field contract a published record carries — and the arithmetic that puts a radar pulse and a communications sample on one clock — see Time coordinates.
Quantity |
Frame |
Owner |
|---|---|---|
Emitter-local time |
Inside one emitter’s IQ buffer, starting at 0. |
Emitter before composition; a returned |
Scene time |
Seconds within the scene, shared across emitters and receivers. |
Scene composer ( |
Component placement index |
Absolute sample index in the placed scene/channel buffer at |
Scene composer ( |
Per-RX sample index |
Sample index within this RX’s IQ buffer at |
The receiver’s own subtree of the record. The scene composer sizes the initial pre-RX scene/master buffer with |
SignalMetadata.start_sample is the placed component’s absolute scene/channel-
rate coordinate, not an emitter-native sample index. The label stage maps the
placed component’s time extent into the receiver’s R_rx sample indexes as
needed.
Component-to-record time mapping¶
Component metadata is provenance for the placed event: its start_sample,
duration_samples, and sample_rate_hz remain in the scene/channel frame
R_channel. A receiver record can have a different active rate R_rx after
RX capture resampling, so a bbox cannot reuse those integer indexes directly.
For the component’s half-open channel-rate interval [start, stop), where
stop = start + duration, the labeler writes the enclosing receiver-record
interval:
scale = R_rx / R_channel
record_start = floor(start * scale)
record_stop = ceil(stop * scale)
bbox interval = [record_start, record_stop)
The outward rounding preserves every time point represented by the component when it is expressed on the receiver grid. The labeler rejects a converted interval that is empty or outside the fixed receiver capture; it does not crop it. Bounding boxes use that receiver-rate interval, and segmentation rasterizes the same bbox geometry. Thus label time coordinates always describe the IQ a model receives, while the per-event metadata remains audit provenance at the scene/channel rate. Equal rates preserve the same integer interval.
This conversion is intentionally separate from event placement. Strict
containment is decided first at R_channel from the pre-propagation emitted
footprint by Scene Capture Boundaries; conversion
to R_rx does not relax that rule or create a new event boundary policy. A
prepared propagation result can have a later channel tail, but only that tail
may be clipped by the fixed receiver window. Labels explains how
the mapped bbox then becomes segmentation cells, and Core Types
identifies which stored fields carry provenance versus receiver-record labels.
Swept-Frequency Emitters¶
Some waveforms intentionally move their energy across frequency over time. A chirp is a sweep with frequency changing continuously; a hop schedule jumps among discrete frequencies.
LoRa chirp spread spectrum (CSS),
frequency-modulated continuous-wave (FMCW) radar,
other chirp-like or hop-like waveforms whose slope or hop schedule is part of the modulation.
The sweep is intrinsic to the emitter’s IQ: its slope or hop
schedule is not re-synthesized by the scene composer. A direct emitter call
may report a provisional center-of-sweep carrier. In a composed scene, the
composer applies one whole-waveform scene-relative placement shift after TX
impairments and resampling, then stamps the final
realized_carrier_hz = SceneConfig.center_hz + f_offset_hz. The receiver then
hears whatever portion of that placed sweep falls within its capture window.
Source: TorchSig documents LFM and chirp spread spectrum signal builders in its signal-generation API, and SciPy’s signal.chirp documents frequency-swept cosine generation. LoRa and radar backend-source choices are catalogued in Emitter Library Landscape.
Label Implications¶
Labels live in the receiver’s frame, alongside the receiver subtree they describe.
Bbox frequency limits are receiver-relative:
low_freq_in_rx = (emitter.realized_carrier_hz - rx.center_freq_hz) - emitter.bandwidth_hz/2.Bbox time limits are sample-indexed at
rx.sample_rate_hz, using the half-open conversion in Component-to-record time mapping.Per-event metadata recorded on the label retains
realized_carrier_hz(absolute),sample_rate_hz,start_sample, andduration_samplesat the placed/channel rate, so the original placement and RF position are recoverable.
A detector trained on one receiver’s IQ sees the same coordinate system the labeler wrote: receiver baseband, receiver rate.
TorchSig Boundary Adapter¶
TorchSig, the RF machine-learning (RFML) signal-generation and dataset toolkit
rfgen interoperates with, enters this page only at the adapter boundary. See
Reference / TorchSig Interop for the
field-level mapping. Inside rfgen, carriers stay absolute on
SignalMetadata.realized_carrier_hz and bboxes stay in the active receiver’s
baseband frame.
Use this boundary rule:
Importing from TorchSig: if the TorchSig artifact already stores an absolute carrier, copy it into
realized_carrier_hz. If it stores a carrier relative to a documented dataset anchorF_anchor_hz, resolve it immediately asrealized_carrier_hz = F_anchor_hz + center_freq_relative_hz.Exporting to TorchSig: keep rfgen’s internal metadata unchanged, then convert only at the adapter boundary. If the target TorchSig format expects relative frequency coordinates, write
center_freq_relative_hz = realized_carrier_hz - F_anchor_hz. If it expects absolute frequency, passrealized_carrier_hzthrough unchanged.
The conditional anchor logic belongs only in the adapter. Core rfgen types and the channel pipeline never carry a dataset anchor. The exact field-level interop mapping lives in TorchSig Interop.
Source: TorchSig’s SignalMetadata documents center_freq, bandwidth, start_in_samples, duration_in_samples, and derived frequency-edge fields. rfgen’s exact import/export mapping is the local contract in Reference / TorchSig Interop.
Minimal Example¶
from rfgen.config import ReceiverConfig
from rfgen.core.types import SignalMetadata
# Wi-Fi 11g emitter on channel 6 (2.437 GHz)
wifi_meta = SignalMetadata(
realized_carrier_hz=2.437e9,
bandwidth_hz=20e6,
sample_rate_hz=20e6,
start_sample=0,
duration_samples=8_000_000,
# ... other fields
)
# Receiver tuned 5 MHz below the emitter's carrier, so the emitter lands
# off-center inside the receiver's baseband frame instead of at 0 Hz.
rx_cfg = ReceiverConfig(
rx_id="rx0",
center_freq_hz=2.432e9,
bandwidth_hz=20e6,
sample_rate_hz=20e6,
)
See Also¶
Emitters for the baseband-generation contract; the scene composer assigns the final carrier of a placed component.
Channels for the four-group pipeline that consumes these frames.
Channels / RX Capture for the BaseMixerStage step that bridges the two frequency frames.
Records, Receivers, and Assets for how receivers sit inside one record, and the sample-rate split.
Scene Capture Boundaries for the event-fit rule on the scene-rate time axis and the distinct treatment of propagation tails at the fixed receiver-capture boundary.
Reference / Core Types for SignalMetadata and SceneMetadata field definitions.
Background / Design Decisions § Channel pipeline for the rationale behind dropping the shared scene RF anchor and the shared scene sample rate.