Signal Execution Model¶
Signal operations run when a caller invokes them. The crate does not own a background service, pipeline scheduler, channel registry, or artifact writer. Its state exists only to preserve mathematical continuity across calls.
Three Execution Shapes¶
flowchart TD
caller["caller"]
kind{"operation shape"}
lookup["deterministic lookup or conversion"]
transform["block or epoch transform"]
stateful["caller-driven state machine"]
result["typed value or SignalError"]
caller --> kind
kind --> lookup --> result
kind --> transform --> result
kind --> stateful --> result
stateful -->|"next explicit call"| caller
Deterministic Lookup¶
Catalog queries, code assignments, wavelength conversion, and many phase helpers return the same result for the same input. They do not consult clocks, files, environment variables, or process state.
Block Or Epoch Transform¶
Sample conversion, front-end filtering, spectral analysis, correlation, replica generation, and observation validation consume an explicit block or epoch. A transform may allocate output or mutate a caller-provided buffer, but it does not decide which block to process next.
Caller-Driven State¶
NCOs, front-end filters, analyzers, local-code models, and tracking adaptation carry state because signal evolution spans calls. The caller constructs the state, chooses call order, handles errors, and decides when to reset or discard it.
Continuity Across Blocks¶
Block boundaries are implementation boundaries, not physical discontinuities. For time-evolving operations, the caller must provide either absolute timing or the state produced by the preceding call.
sequenceDiagram
participant C as Caller
participant M as Signal model
C->>M: initialize(rate, phase origin)
C->>M: process block at start index
M-->>C: samples and advanced state
C->>M: process adjacent block
M-->>C: continuous samples and state
Resetting phase at each frame can create code jumps, carrier discontinuities, or incorrect Doppler even when every frame looks locally valid. Tests for replicas, NCOs, code timing, and tracking math should compare split and contiguous execution over representative long offsets.
Streaming Is Caller-Owned¶
The public source and sink traits do not turn this crate into an I/O runtime. They let a higher-level owner supply frames without coupling signal algorithms to one capture, device, or storage implementation.
flowchart LR
implementation["infrastructure or test implementation"]
trait["signal source/sink trait"]
runtime["receiver or test runtime"]
primitive["signal computation"]
policy["scheduling, retry, and persistence policy"]
implementation --> trait --> runtime --> primitive
runtime --> policy
primitive -. "does not own" .-> policy
The runtime decides frame length, backpressure, end-of-stream handling, retries, and where output goes. Signal code validates its own mathematical inputs and returns results.
Tracking Mathematics Versus Tracking Runtime¶
This crate owns correlators, discriminators, loop coefficients, loop updates, quality estimates, uncertainty calculations, and adaptation decisions. The receiver owns the channel state machine around them.
| signal layer | receiver layer |
|---|---|
| compute early, prompt, and late correlations | select a channel and frame |
| derive DLL, PLL, and FLL evidence | decide acquisition-to-tracking promotion |
| advance code and carrier estimates | retain lock history and degraded state |
| estimate C/N0 and uncertainty | decide when a channel is usable or refused |
| choose an adaptation result from explicit inputs | apply session-level policy and emit artifacts |
An API crosses the boundary when it needs hidden channel history, chooses when to run another stage, or emits receiver-owned status.
Error And Validation Flow¶
flowchart TD
input["explicit input"]
validate{"valid signal domain?"}
compute["perform computation"]
value["typed result"]
error["SignalError"]
caller["caller policy"]
input --> validate
validate -->|"yes"| compute --> value --> caller
validate -->|"no"| error --> caller
SignalError covers signal-owned invalidity such as unsupported PRNs, missing
GLONASS frequency channels, invalid rates or phases, empty code or symbol
sequences, correlation mismatch, and invalid front-end or spectrum
configuration. The caller decides whether an error rejects input, ends a
channel, or becomes operator-facing output.
Observation compatibility is reported as typed evidence where partial or per-pair results are useful. It does not silently promote a signal-level report into a navigation-quality decision.
Review Questions¶
- Are all time, frequency, phase, sample, chip, and distance units explicit?
- Does repeated execution depend only on explicit input and owned state?
- Does split-block execution preserve the same physical trajectory as contiguous execution?
- Can the operation run without a receiver session, repository layout, or process-global configuration?
- Is mutable state mathematically necessary, and is reset behavior visible?
- Does the caller retain scheduling, retry, persistence, and operational decisions?
For code ownership, continue to the module map. For state boundaries, see state and persistence. For downstream handoffs, see integration seams.