Event planning¶
Use rfgen.planning after choosing event-start inputs and before any duration
policy is available. It records causal timing provenance in receiver-capture
sample coordinates; it does not choose a scheduler, infer duration, decide
containment, or generate a waveform.
Quick start¶
from rfgen.planning import EventPlanner
unresolved = EventPlanner.compile([
{"event_id": "e0", "anchor": "absolute", "start_sample": 0},
{"event_id": "e1", "anchor": "predecessor", "predecessor_id": "e0"},
])
EventPlanner.write_unresolved(unresolved, scene_id="demo", root="output")
# A later duration-policy owner supplies durations; this planner does not.
resolved = EventPlanner.resolve(
unresolved,
{"e0": 128, "e1": 64},
contained={"e0": True, "e1": True},
)
EventPlanner.write_resolved(resolved, scene_id="demo", root="output")
API¶
class EventPlanner:
@staticmethod
def compile(events: Iterable[EventRequest | Mapping[str, object]]) -> tuple[UnresolvedEvent, ...]: ...
@staticmethod
def from_time_placement(planner: BaseTimePlacement, signal: Signal, rng: torch.Generator, *, event_id_prefix: str, schedule_key: str | None = None, hop_state: Mapping[str, JsonValue] | None = None) -> tuple[UnresolvedEvent, ...]: ...
@staticmethod
def resolve(events: Iterable[UnresolvedEvent | Mapping[str, object]], durations: Mapping[str, int], *, contained: Mapping[str, bool]) -> tuple[ResolvedEvent, ...]: ...
@staticmethod
def write_unresolved(events: Iterable[UnresolvedEvent | Mapping[str, object]], *, scene_id: str, root: str | Path = ".") -> Path: ...
@staticmethod
def write_resolved(events: Iterable[ResolvedEvent | Mapping[str, object]], *, scene_id: str, root: str | Path = ".") -> Path: ...
@staticmethod
def read(path: str | Path) -> tuple[UnresolvedEvent, ...] | tuple[ResolvedEvent, ...]: ...
Record schemas¶
Every record uses strict integers and booleans: strings, floats, and booleans
are not coerced into sample counts. hop_state is either null or a JSON
object whose nested values are recursively JSON-compatible; it is retained
provenance, not an instruction to generate a hop. Arrays and scalar values are
rejected at the field boundary.
Field |
|
Resolved artifact |
|---|---|---|
|
Required, nonempty event identifier. |
Retained unchanged. |
|
Required: |
Retained unchanged. |
|
Required nonnegative receiver-capture sample for |
Required nonnegative concrete start. |
|
|
Retained unchanged. |
|
Nonnegative offset after a predecessor stop; defaults to |
Retained unchanged. |
|
Nullable reference to the later duration-policy owner. |
Retained unchanged. |
|
Nullable causal provenance. |
Retained unchanged. |
|
Forbidden. |
Required: nonnegative duration, |
EventRequest is the input model. UnresolvedEvent is the persisted form
before duration resolution. ResolvedEvent is the persisted form after an
external duration-policy owner supplies duration and containment results.
compile validates missing/cyclic predecessors and returns unresolved records
sorted by event_id. resolve is the only place predecessor timing becomes
concrete: start_sample = predecessor.stop_sample + requested_offset_samples;
each supplied nonnegative duration gives stop_sample = start_sample + duration_samples.
The duration-policy owner must also explicitly supply the per-event contained
result, so the planner does not invent a containment policy. Resolved rows sort
by (start_sample, event_id).
from_time_placement is the narrow adapter for an existing
BaseTimePlacement implementation, including EventPeriodicBeacon,
EventBurst, and EventFhssHop. It calls that strategy’s draw method and
stores only its returned absolute starts under deterministic prefix/index IDs;
it does not alter the strategy’s scheduling behavior or infer duration.
Both v1 artifacts contain schema_version, scene_id, and events.
events-unresolved.json is written at
artifacts/plans/<scene_id>/events-unresolved.json; it never has
duration_samples, stop_sample, or contained. events-resolved.json is
written beside it and requires all three fields. Both retain schedule_key and
hop_state as causal provenance. The retired events.json path is rejected.
Invalid anchors, missing predecessors, and cycles raise ValidationError with
event_anchor_invalid, event_predecessor_missing, or event_cycle and an
event_id; duplicate IDs use event_duplicate. offset_samples is forbidden;
use requested_offset_samples and handle the exact context
{code: "event_field_alias_forbidden", field: "offset_samples", use: "requested_offset_samples"}. Reading the retired events.json path raises
ValidationError with {code: "event_artifact_legacy_path"}. Other paths,
outer-object shapes, cross-artifact rows, and noncanonical ordering are
rejected rather than guessed.
Existing BaseTimePlacement, periodic-beacon, burst, and FHSS strategies may
provide start inputs. This component does not reimplement their scheduling
policies.