Signal Change Principles¶
Signal code defines facts reused by acquisition, tracking, simulation, navigation, infrastructure, and command workflows. Change it from the physical or computational contract outward, not from the first failing consumer inward.
Begin with Owned Meaning¶
flowchart TD
proposal["proposed change"]
meaning{"reusable signal<br/>meaning?"}
route["route to core, receiver,<br/>navigation, infrastructure,<br/>or command"]
family{"owned family"}
catalog["catalog and<br/>wavelength"]
codes["codes and<br/>modulation"]
samples["raw IQ and<br/>sample conversion"]
dsp["DSP, replica,<br/>and tracking math"]
validation["signal compatibility<br/>validation"]
proposal --> meaning
meaning -- no --> route
meaning -- yes --> family
family --> catalog
family --> codes
family --> samples
family --> dsp
family --> validation
The behavior belongs here only when it remains useful without receiver scheduling, navigation estimation, repository layout, or operator policy.
One Canonical Signal Fact¶
Carrier relationships, code assignments, component roles, rates, lengths, secondary sequences, modulation, quantization, and compatibility rules need one canonical implementation. Higher packages should consume that meaning rather than copy constants or rebuild lookup tables.
Before adding a helper, determine whether it:
- introduces new signal meaning
- presents an existing fact in a reusable form
- duplicates an existing computation with different naming or units
- encodes one receiver or command use case
Prefer extending the canonical family over creating a near-match. A generic helper is appropriate only when it preserves constellation-specific identity and does not erase unsupported cases.
Keep Authority Independent¶
flowchart LR
authority["specification, formula,<br/>or independent catalog"]
fixture["reviewed fixture<br/>or expected vector"]
implementation["production<br/>implementation"]
proof["exact, property,<br/>or numeric comparison"]
consumer["first affected<br/>consumer"]
authority --> fixture --> proof
authority --> implementation --> proof
proof --> consumer
Expected data must not be regenerated from the implementation under test. A reference update should identify source revision, constellation, signal, component, indexing, polarity, units, and any transformation applied to the source.
Use exact comparisons for identities, assignments, chip sequences, lengths, periods, enums, and metadata. Use justified tolerances for floating-point phase, spectra, filter response, correlation, and converted samples.
Preserve Continuity¶
Many APIs operate on chunks while modeling continuous code and carrier state. For time-evolving behavior:
- define phase origin and units
- state wrap and boundary conventions
- compare segmented and continuous execution
- exercise representative and awkward sample-rate ratios
- include long-duration accumulation when drift is possible
- verify that reset and resumed state have deliberate semantics
A single code period or short buffer cannot defend a long-duration claim.
Make Unsupported Meaning Explicit¶
Do not turn missing capability into zero, empty output, a default signal, or a plausible sample. Unsupported satellites, components, frequency channels, signal definitions, sequence shapes, and analysis configurations should return typed absence or failure according to the public contract.
Preserve the distinction between:
- no registered definition
- registered signal without a usable requested component
- invalid physical input
- unsupported operation
- malformed sequence or configuration
Downstream callers need that meaning to refuse or report honestly.
Protect Public and Serialized Boundaries¶
Treat these as compatibility-sensitive:
- curated API exports
- trait methods and associated error expectations
- raw-IQ metadata and schema fields
- quantization and sample-format enums
- validation report types and issue variants
- defaults and lookup behavior consumed by receiver code
An implementation-only edit can still change a public contract through output ordering, default selection, units, error variants, serialization, or numeric normalization.
Keep Effects Above the Signal Layer¶
Signal code may transform in-memory samples and retain computational state. It must not:
- discover or open capture files
- choose sidecars or repository paths
- schedule channels or declare receiver lock
- persist artifacts or run history
- accept or reject navigation solutions
- format operator reports
Source and sink traits define dependency-inversion seams; their implementations own I/O, buffering, retries, and lifecycle.
Structure the Change¶
One coherent signal change may contain:
- the canonical production implementation
- independent reference data or analytic expectations
- focused exact, property, and numeric proof
- public API or contract documentation when meaning moves
- first downstream integration evidence
Separate unrelated signal families, reference-generation tooling, broad consumer policy, and private reorganization when they carry independent review intent. Never leave an intermediate commit with an exported but unproven signal contract.
Reject Weak Justifications¶
Do not accept:
- “the receiver test passes” as sole proof of signal correctness
- “the formula is standard” without conventions and authority
- “the outputs look similar” without a metric and tolerance
- “the helper is reusable” when it embeds one runtime policy
- “the fixture changed with the implementation” without independent review
- “the diff is small” for a changed constant, default, or sign convention
Use the signal boundary, contract guide, and validation guide to locate ownership and proof.
A signal change is sound when one canonical implementation expresses an independently anchored fact, unsupported meaning stays explicit, continuity holds across relevant boundaries, and downstream evidence remains no broader than the contract proved.