Unified Scene Observations

Use a unified scene observation when one generated sample must preserve several views of the same RF scene. Communications receiver IQ, active-radar response, labels, and future domain outputs remain separate named fields, but they share one clock, one scene identity, and one persisted record.

Here IQ means in-phase/quadrature complex samples; baseband means the frequency-translated representation around 0 Hz. The RF glossary defines IQ, local oscillator (LO), phase, noise, and pulse repetition interval (PRI). On this page, an epoch is only SceneClock.time_origin_s, the numerical origin of a plan. It makes no UTC, TAI, Unix-time, or civil-timescale claim. In pulsed radar, fast time indexes samples within one pulse and the pulse axis is slow time across pulses. A measurement plane identifies where in the receive chain a sample is defined, including its phase, oscillator, dechirp, and noise meaning. Dechirping mixes a received frequency sweep with a reference copy of the transmitted sweep so its delay and Doppler appear as lower-frequency beat components.

        flowchart LR
  PLAN["ScenePlan<br/>one scene · one SceneClock"]
  COMM["Communications projection<br/>receiver IQ · labels"]
  RADAR["Radar projection<br/>baseband · response components"]
  FUTURE["Future projection plugin<br/>new fields · native axes"]
  OBS["SceneObservationRecord<br/>one aggregate observation"]
  ADAPTER["Central SDSObservationAdapter"]
  SDS["exactly one<br/>signal_dataset.Record"]

  PLAN --> COMM
  PLAN --> RADAR
  PLAN -. "rfgen.observation_projections" .-> FUTURE
  COMM --> OBS
  RADAR --> OBS
  FUTURE -.-> OBS
  OBS --> ADAPTER --> SDS
    

The projections do not pass samples to each other. Each reads the same immutable ScenePlan, renders fields with its own physics, and returns a ProjectionResult. The runtime validates all projections before expensive work, then assembles their results into one SceneObservationRecord. The central SDSObservationAdapter is the only observation-to-Signal-Dataset conversion boundary.

What each projection does between the plan and the record

The diagram above says the two projections share a plan and rejoin at one record. Between those points they run unrelated physics, and the shapes they produce reflect that.

        flowchart TD
    PLAN["Minted ScenePlan<br/>clock · systems · activities · entities · world"]
    SEED["derive_projection_seed(sample_seed, projection_id)<br/>one independent stream per projection"]

    subgraph COMMS["Communications projection"]
        CE["Emitters generate clean baseband<br/>TorchSig · one per planned emission"]
        CT["TX impairments per emitter<br/>DAC · PA · TX phase noise · TX IQ · CFO"]
        CP["Normalize any plugin-returned rate,<br/>then frequency placement and propagation<br/>Sionna or AWGN"]
        CS["Select emitters inside each receiver's band,<br/>then sum"]
        CR["Receiver chain per RX, post-sum<br/>capture plane, then hardware plane"]
        CL["Labelers derive boxes and segmentation<br/>from declared geometry, not from sample energy"]
        CE --> CT --> CP --> CS --> CR --> CL
    end

    subgraph RADAR["Active-radar projection"]
        RS["Build this sample's radar system from the plan"]
        RT["Read the plan's targets<br/>propagated to the event instant"]
        RB["Backend solves visibility and paths<br/>sionna_rt"]
        RC["Closed-form signal chain<br/>echo · Doppler · dechirp to beat frequency"]
        RF["Optional receiver frontend per component"]
        RS --> RT --> RB --> RC --> RF
    end

    REC["One SceneObservationRecord<br/>fields namespaced by projection_id"]

    PLAN --> SEED
    SEED --> CE
    SEED --> RS
    CL --> REC
    RF --> REC
    

Three things about that picture carry weight.

Neither branch can see the other. A projection receives the plan and its own seed, and returns a ProjectionResult. There is no channel between them, so a radar return cannot leak into a communications capture — and if a scene needs that coupling, it is a declared interaction the record must state, not something a projection may do quietly. Every record carries each projection’s disposition per interaction; cross_projection_rf_coupling is reported as not_applicable or excluded_by_configuration rather than left unsaid.

Communications sums, radar separates. The communications branch sums emitters into each receiver’s band, because that is what a receiver does. The radar branch keeps baseband and noise apart, because a consumer can add them and cannot subtract them.

Labels come from declared geometry, not from sample energy. A labeler reads what the plan and the realized metadata declare and rasterizes it. The segmentation labeler does take the IQ tensor, but only to fix the capture shape, the receiver axis, and the device the raster is built on — no label value is derived from a sample value. That is why a label is a statement about the scene rather than a measurement of the output.

Communications reception is one-way: an independent transmitter emits and a receiver captures the propagated signal. Active radar is coupled: the sensing system transmits its own waveform and observes target-reflected returns. The active-radar projection is available, but scientific qualification is bounded; consult Radar Response before making dataset claims.

Owner

Responsibility

Verification

RFGen

ScenePlan, projection lifecycle, typed contracts, deterministic identity, and orchestration.

Observation API and focused observation contract tests.

TorchSig

Built-in communications waveform generation selected by communications configuration.

TorchSig integration.

Sionna

Selected propagation/ray-tracing library boundary; RFGen adapts outputs rather than reimplementing its solver.

Sionna integration.

RFGen ClosedFormRadarSignalChain

Composes library-derived visibility/path facts into deterministic analytic point-target returns; custom because no selected library owns this exact cross-backend contract.

Radar Response / signal chain and its bounded qualification section.

RadarSimPy

Validation-only radar oracle, never the normal generation backend.

Radar Response.

Signal Dataset

Immutable shard/root format, record encoding, indexed and metadata-only reads.

Storage and storage layout.

One clock keeps domains synchronized

ScenePlan.clock is the shared SceneClock. Its time_origin_s is the scene epoch; its duration_s bounds the observation. Activities use offsets from that origin. Keeping the epoch and small, subsecond activity and sample offsets separate avoids losing those offsets when an epoch is large.

Every time-bearing field refers to the plan’s content-bound time_reference. Communications commonly uses uniform time coordinates. Pulsed radar commonly uses pulse and fast-time coordinates. Segmented and explicit coordinates cover nonuniform schedules. These coordinate contracts map native indices back to the same scene clock, so synchronization does not require flattening fields to one rate or shape.

For the stored contract — each kind’s time_composition, the arithmetic it names, how to read the block off a field, and a worked cross-domain alignment — see Time coordinates.

Measurement planes are load-bearing

Clock alignment is not permission to add arrays. A field declares its measurement meaning, axes, clock, receiver geometry, phase and oscillator references, and noise relationship. RFGen permits numeric addition only when the complete declared-contract compatibility predicate accepts it. This is summation eligibility, not proof of physical correctness. Otherwise, aligned fields coexist in the record without being summed.

Fields keep their native axes

There is no universal RF tensor shape. Each projection owns one or more BaseObservationField values:

  • ComplexCapture stores complex64 measurements such as communications IQ or radar baseband.

  • RealValuedCapture stores real-valued measurements.

  • LabelField stores integer or Boolean supervision.

Each field carries semantic ObservationAxis values whose lengths must match the array dimensions. An axis may also carry numeric coordinates. A communications field might have receiver and time axes; a radar field might have channel, pulse, and fast-time axes. The aggregate namespace is projections/<projection_id>/<local_field_name>. Generated coordinate arrays are auxiliary fields and remain associated with the same projection owner.

One envelope makes ownership and identity explicit

The Signal Dataset record stores the observation envelope under metadata["rfgen"]. It includes:

  • the complete canonical scene plan and scene_plan_reference;

  • one descriptor per projection, including its selector, contract version, validated-parameter identity, and implementation identity; the runtime identity is bound inside that implementation identity;

  • field_owners, primary_fields, and auxiliary_fields, so a consumer can distinguish payloads from generated coordinate fields;

  • interaction coverage and projection-owned metadata; and

  • invocation provenance: sample ordinal, sample seed, and the independently derived seed for each projection.

The observation identity covers the plan, projection descriptors, and invocation provenance. Projection seeds are derived from projection IDs, so adding or reordering another projection does not perturb an existing projection’s random stream. The final Signal Dataset record ID is derived from the complete encoded record. Retries therefore reproduce the same identity only when their persisted content and runtime contract agree.

One record is also a memory boundary

One scene observation becomes exactly one signal_dataset.Record; RFGen does not fan an aggregate observation out by receiver. Signal Dataset loads a record’s numerical fields as one record. It does not lazily page an individual field tensor from within that record.

Set observation.max_tensor_bytes to bound the aggregate primary fields and generated coordinate arrays. RFGen estimates the encoded size and checks the actual encoded size before publication. Metadata-only reads remain available through the store and SDSObservationAdapter.metadata_view(...), but a full record read must fit in the consumer’s memory.

Projection plugins are an open seam

Built-in selectors are rfgen.communications.receiver and rfgen.radar.receiver. A third-party projection subclasses BaseObservationProjection, declares a strict Pydantic ParamsModel, a contract version, interactions, and required store capabilities, then registers its selector in the rfgen.observation_projections Python entry-point group. See Observation API for the lifecycle and Generate one unified observation dataset for configuration and remote packaging.

The core does not branch on domain names. Domain facts live in strict, versioned ScenePlan extensions owned by systems, activities, or entities. A plugin must classify every relevant interaction before rendering. Unsupported interactions stop the complete observation before any projection publishes a partial result.

Compatibility and qualification boundaries

There is one architecture. A configuration without projections is refused before anything is composed — a run declares what it observes through projections and nothing else — so the second pipeline this section once contrasted against no longer exists, and neither does the exclusivity rule that kept the two apart. One ScenePlan produces one record, with every receiver held inside it as a named subtree. Records written by earlier releases are not rewritten and this build cannot read them.

Local and Dataproc Serverless execution both use the same projection resolver, preflight lifecycle, aggregate adapter, and identity rules. Dataproc adds an image/runtime handshake and verifies staged plugin distributions before cloud work. A projection-enabled submission fails before billing when its image is not explicitly qualified, its remote contract version is wrong, or its local plugin distribution does not match the staged package. Availability in a source checkout is not a claim that a particular image digest is qualified; see Dataproc Serverless.

RFGen’s declared-evidence annotation builder still targets the retained legacy record metadata. It does not yet interpret SceneObservationRecord fields or their projection envelope. Generate and inspect unified observations, but do not treat the legacy annotation path as aggregate-observation annotation; that integration is a separate milestone.