Files
ZephCore/docs/adr/0007-adaptive-contention-window.md
liquidraverandClaude Opus 5.5 9fc1eaf18a docs: publish the system design and architecture decision records
DESIGN.md is the reconciled high-level design after the 2026-09 restructure:
system context, roles, build-time composition, layers and dependency rules
(with the known exceptions), threading and event loop, end-to-end flows,
cross-cutting concerns and the upstream alignment policy. docs/adr/ holds the
nine decision records (ESP32 boot layout, S3 native USB, overlay precedence,
BLE privacy, Zephyr main pin, SX126x duty cycle and RX-busy gating, adaptive
contention window, storage backend, WiFi companion boards).
ARCHITECTURE.md stays the detailed component reference and links both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 22:01:28 +02:00

33 lines
1.9 KiB
Markdown

# ADR 0007 — Adaptive contention window instead of Arduino's static tx/rx delays
Status: accepted (recorded 2026-09-25; ZephCore-only)
## Context
Replaces Arduino's static `txdelay`/`rxdelay` with three mechanisms:
- **EMA-based flood delay factor**: scales flood TX retransmit jitter adaptively based on local contention
history. Jitter is double-capped: `min(2000ms, 6·airtime)` for repeaters — the airtime-scaled ceiling keeps
SF7/narrow-BW configs from wasting time in oversized windows, while the absolute cap bounds per-hop latency in
dense areas. Ring buffer holds 24 concurrent tracked retransmits (sized for ~50-neighbor hilltops).
- **Per-dupe reactive backoff**: each heard duplicate of a pending TX packet delays transmission by
`backoff_multiplier * airtime` from NOW. Capped at `min(2000ms, 12·airtime)` total reactive extension per
packet. After the cap, CAD handles channel-busy detection like Arduino.
- **Initial-flood jitter (companion-only)**: companions don't forward floods (when `client_repeat=0`) and use a
fixed `20..150ms` window for originated TX — small enough to feel responsive, large enough to avoid lockstep
collisions with nearby repeaters. Passive flood tracking is disabled in that mode (no EMA warming needed). When
`client_repeat=1`, the EMA is warmed naturally via the forwarding path and `getRetransmitDelay()` falls back to
the same adaptive math as repeaters.
- **Direct packets**: use minimal fixed jitter only (no adaptive scaling).
## Decision
`set backoff.multiplier X` controls per-dupe delay (default 0.2; 0.0 disables reactive backoff). The old CLI
commands (`txdelay`, `rxdelay`, `direct.txdelay`) are accepted for prefs compatibility but ignored; `get txdelay`
shows the adaptive state.
## Consequences
A divergence from upstream by design (memory `acw-reactive-backoff`); ports touching `Mesh`/`Dispatcher` timing
must keep it.