Observation API¶
The rfgen.observation package defines the persistence-neutral aggregate
record, projection plugin, coordinate, and Signal Dataset adapter contracts.
Use these APIs when implementing a projection or consuming a unified scene
observation. Use Generation for the emitter and composer
contracts.
Module layout¶
Module |
Responsibility |
|---|---|
|
ABCs, immutable fields, coordinates, projection results, records, and compatibility predicates. |
|
Strict SDS encoding/decoding, metadata view, and aggregate tensor budget. |
|
Resolution, deterministic seeds, two-phase execution, and one-record assembly. |
|
Built-in communications and radar implementations. |
Record contract¶
BaseRFRecord is the abstract persistence boundary. Implementations provide a
stable record_type, a schema version, globally namespaced fields, and a
complete RFRecordEnvelope.
SceneObservationRecord is RFGen’s aggregate implementation. It binds:
one immutable
ScenePlanand its content-derived time reference;one or more unique, canonically ordered
ProjectionResultvalues;projection-owned fields under
projections/<projection_id>/<local_field_name>; andinvocation metadata used by observation identity.
Constructors reject an empty projection set, duplicate projection IDs, fields
whose clock differs from the plan, and descriptors that disagree with the
invocation. BaseRFRecord is not the external Signal Dataset class;
SDSObservationAdapter performs that conversion centrally.
Unless a narrower exception is named below, invalid constructor values and
failed coordinate, field, record, or compatibility predicates raise
rfgen.core.errors.ValidationError.
Field and coordinate contracts¶
BaseObservationField owns an immutable NumPy payload, semantic axes, a
MeasurementDescriptor, optional time coordinates, and JSON-safe metadata.
Concrete field types are ComplexCapture, RealValuedCapture, LabelField,
and RealValuedLabelField. A BaseCapture additionally requires a
CaptureAlignmentDescriptor; a label carries none, because it is derived from
declared metadata rather than measured.
Time coordinates implement BaseTimeCoordinates:
Type |
Use |
|---|---|
|
One uniformly sampled time axis. |
|
Pulse and fast-time axes with one interval per pulse. |
|
A finite sequence of uniform segments. |
|
Explicit offsets for a nonuniform schedule. |
Non-time axes may use UniformAxisCoordinates or
ExplicitAxisCoordinates. Axis cardinality, coordinate cardinality, sample
support, and plan-clock membership are validated before persistence.
Projection plugin contract¶
Subclass BaseObservationProjection and provide these public members:
Member |
Contract |
|---|---|
|
Portable namespaced selector; it must match the entry-point name. |
|
Positive wire-contract version, matched exactly by configuration. |
|
Strict Pydantic model for a frozen validated parameter snapshot. |
|
Interactions the plugin can classify. |
|
Capabilities added to aggregate store requirements. |
|
Retained instance of the declared |
|
Build only from the exact parameter model. |
|
Contribute plan-derived relevant interactions; default empty. |
|
Classify every relevant interaction before expensive work. |
|
Return one descriptor-bound result. |
|
Sealed lifecycle; subclasses are rejected if they override it. |
Do not override run. The base lifecycle validates context, capabilities,
interaction coverage, preflight output, and the returned descriptor. The
orchestrator preflights every configured projection before it renders any of
them. An unsupported interaction fails the complete observation.
Lifecycle contract violations and unsupported projection results raise
rfgen.core.errors.ValidationError. Defining a subclass that overrides the
sealed run method is rejected immediately by __init_subclass__ with
TypeError.
Register an out-of-tree implementation in the distribution’s
pyproject.toml:
[project.entry-points."rfgen.observation_projections"]
"example.passive_radar.receiver" = "example_rf.projection:PassiveRadarProjection"
Then configure that exact selector and contract version:
projections:
- projection_id: passive_radar_rx
selector: example.passive_radar.receiver
contract_version: 1
params: {}
Projection IDs are instance names, not plugin selectors. They must be unique and become field-namespace and seed-tree components. Configuration is an ordered list for explicit authorship, while runtime assembly canonicalizes by projection ID. Reordering the list therefore does not change projection streams or aggregate identity.
Persistence adapter¶
SDSObservationAdapter.encode(record) validates a BaseRFRecord and produces
exactly one content-addressed signal_dataset.Record. It stores the strict
rfgen.scene_observation.v1 envelope under metadata["rfgen"], including
field ownership, projection descriptors, interaction coverage, the ScenePlan,
and invocation metadata.
decode(record) reconstructs a typed SceneObservationRecord and rejects
unknown or malformed envelope shapes. metadata_view(record_or_metadata)
reads the common identity and field catalog without loading field tensors.
ObservationEncodingPolicy.max_tensor_bytes bounds one complete aggregate
record before allocation and again after encoding.
encode(observation: BaseRFRecord) -> signal_dataset.Record raises TypeError
for a non-RFGen record, ValidationError for an invalid record/envelope/field,
and RecordIntegrityError when estimated or encoded tensors exceed policy.
decode(source: signal_dataset.Record) -> SceneObservationRecord raises
ValidationError for unknown schema versions, malformed ownership, coordinates,
identity, or content. metadata_view(source: Record | RecordMetadata) -> ObservationMetadataView raises TypeError for another input type and
ValidationError for a malformed envelope; it does not load tensor values.
Signal Dataset reads full records. Consumers that only need catalog or
identity data should use store metadata access or metadata_view; consumers
that load a record must budget memory for all of its fields.
Complete public contract catalog¶
Records, fields, and descriptors¶
Object |
Constructor and contract |
|---|---|
Abstract properties |
|
|
|
|
|
|
|
|
Field ABC adding required |
|
Field/capture signature; |
|
Field/capture signature; |
|
Field signature; Boolean or signed/unsigned integer dtype. |
|
Field signature; |
|
|
|
|
|
Receiver, geometry, clock, carrier/LO, phase/oscillator, dechirp, calibration, noise, and state facts. Phase-bearing and noise-bearing captures must provide their references. |
The inherited concrete-field constructor is
(name, values, axes, descriptor, time_coordinates=None, metadata={}, alignment=None, acquisition_coordinates=None) for ComplexCapture and
RealValuedCapture; the two label types end after metadata. Captures require a
non-null alignment despite the dataclass inheritance default.
CaptureAlignmentDescriptor(receiver_id, receiver_geometry_id, clock_reference, phase_reference, oscillator_reference, carrier_hz, lo_hz, dechirp_reference=None, calibration_reference=None, phase_disposition=APPLICABLE, noise_disposition=NONE, noise_reference=None, state_policy=None) exposes all 13 persisted facts. IDs/references are nonblank
when present, carrier/LO are finite, applicable phase requires phase and
oscillator references, and non-NONE noise requires noise_reference.
AdditionDisposition is non_additive, coherent_amplitude, or
additive_measurement; PhaseDisposition is applicable or
not_applicable; NoiseDisposition is none, shared_component, or
independent_realization. These are declared contracts, not inference from a
field name.
Coordinates¶
Object |
Signature and validation |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Projection invocation values¶
Object |
Signature and validation |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dataproc’s runtime artifact identity is a control-plane attestation over the exact image and ordered package bytes. Workers verify it before rebuilding the same projection descriptors; it is not an extra persisted descriptor field.
Sealed lifecycle and extension checklist¶
BaseObservationProjection declares name, contract_version, ParamsModel,
interaction_selectors, optional required_store_capabilities, abstract
from_params, preflight, and project, plus optional
applicable_interactions. A subclass cannot override run.
Malformed lifecycle inputs, incomplete interaction coverage, drift from the
resolved descriptor, and an unsupported returned result raise
rfgen.core.errors.ValidationError; overriding run on a subclass raises
TypeError during class creation.
Resolve all selectors and strict parameter snapshots; bind implementation, distribution, parameters, and runtime into descriptors.
Validate store capabilities and plan-derived interaction contributions.
Build canonical-ID contexts with independently derived projection seeds.
Preflight every plugin; any unsupported interaction stops before rendering.
Render with the exact normalized coverage and reject any contract drift.
Assemble one canonical aggregate and encode exactly once.
A minimal compliant plugin is:
from __future__ import annotations
from typing import ClassVar
import numpy as np
from pydantic import BaseModel, ConfigDict
from rfgen.observation import (
BaseObservationProjection, InteractionStatement, LabelField, MeasurementDescriptor,
ObservationAxis, ProjectionContext, ProjectionResult,
)
from rfgen.scene.plan import ScenePlan
class Params(BaseModel):
model_config = ConfigDict(extra="forbid", frozen=True)
class_id: int
class PassiveProjection(BaseObservationProjection):
name: ClassVar[str] = "example.passive.receiver"
contract_version: ClassVar[int] = 1
ParamsModel: ClassVar[type[BaseModel]] = Params
interaction_selectors: ClassVar[frozenset[str]] = frozenset()
def __init__(self, params: Params) -> None:
self.params = params
@classmethod
def from_params(cls, params: BaseModel) -> PassiveProjection:
if not isinstance(params, Params):
raise TypeError("Params required")
return cls(params)
def preflight(
self, plan: ScenePlan, context: ProjectionContext
) -> tuple[InteractionStatement, ...]:
return ()
def project(
self,
plan: ScenePlan,
context: ProjectionContext,
interactions: tuple[InteractionStatement, ...],
) -> ProjectionResult:
field = LabelField(
"class_id", np.asarray([self.params.class_id], dtype=np.int64),
(ObservationAxis("class", 1, "example.axis.class"),),
MeasurementDescriptor("example.measurement.class", 1,
"example.quantity.class"),
)
return ProjectionResult(context.descriptor, {field.name: field}, interactions)
Built-ins and scientific boundary¶
Selector |
Parameters and output |
|---|---|
|
No authored params; resolver freezes communications generation config. Produces |
|
|
Both built-ins declare cross-domain RF coupling excluded; synchronization does not claim mutual interference. Radar scientific qualification remains bounded by Radar Response.
Compatibility predicates¶
Predicate |
Declared eligibility proved |
|---|---|
|
One equal qualified ScenePlan reference only. |
|
Same scene plus located axes, shape, and time-coordinate mapping. It does not compare dtype. |
|
Applicable equal phase and oscillator references. |
|
Grid, equal dtype/payload kind/descriptor, addition disposition, receiver/measurement coordinates, state/noise algebra, and phase eligibility. |
Passing the final predicate means declared contracts permit numeric summation; it is not empirical proof that the physical models are correct.