A subscriber can move from system A to system B between calls while the
cache entry for system A is still valid. The cache update on voice header
must always overwrite the stored system, not just refresh the expiry, so
that routing follows the subscriber's current location.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Subscriber location cannot change mid-call. Updating the cache entry on
every frame is redundant and adds unnecessary work to the hot path. The
correct point is the voice header (first DMRD of a new stream_id), which
is already identified during new stream detection.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three areas updated:
- Talker alias: reframe the reason for LC overwrite as protocol interoperability, not
handling complexity. TA could be preserved in pure HBP-to-HBP paths but different
protocols (IPSC, OpenBridge, future adapters) each carry TA differently, making
uniform LC overwrite the safe and interoperable choice.
- Stale stream cleanup: a DMR superframe is ~360ms so 1-2 seconds is fast enough to
detect a call that ended without a terminator, given human PTT timescales. Remove the
overly conservative 30-second figure. Cleanup interval can be every few seconds; it
is a backstop, not a timing-critical path.
- Unit call routing: subscriber location cache must have a timeout/expiry or it grows
forever. Each transmission resets the expiry. Pruning runs at a minutes interval.
Cache miss behavior: flood to all systems that permit unit call flooding (per-system
config flag). Conceptually based on PIM Sparse Mode for IPv4 multicast. Reference
HBlink4's implementation for detailed behavior.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Four corrections from review:
- TO_TYPE OFF is the logical inverse of ON: member is ACTIVE by default, OFF TGID
deactivates it and starts the timer, timer expiry reactivates it. Previous description
was wrong about what OFF means.
- RESET is valid for TO_TYPE OFF (extends the deactivation period, keeping the member
INACTIVE longer).
- TIMER is application-relative, not necessarily Unix epoch. The distinction between
internal time source and the wire format (Unix epoch in telemetry) is now explicit.
- TIMEOUT is configured in minutes, not seconds. The 10-second rule timer loop interval
is appropriate because minute-resolution expiry needs no finer precision.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three design documents capturing what would otherwise require deciphering the Python source:
- telemetry-schema: NDJSON event types, field names, units, epoch conventions, and the
design principle that the emitter must never stall the routing hot path
- bridge-state-machine: ACTIVE/INACTIVE transitions, ON/OFF/RESET trigger semantics,
TO_TYPE timer behavior, and why activation fires at call START not call END
- packet-routing-flow: DMRD packet structure, arrival validation, new stream detection,
routing loop, LC rewriting policy, why talker alias is intentionally destroyed,
stream end handling, stale stream cleanup, and unit call subscriber map
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Captures the design rationale for keeping rules.py as the operator-facing
configuration format while using a Python helper to validate and export JSON
for consumption by a C protocol core. Documents live reload behavior, why
the single-threaded model makes the swap safe, and how the JSON schema as a
stable interface enables future configuration front-ends (web UI, REST, etc.).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>