ARQ retry-limit sweep + post-ARQ FEC dimensioning (#362 steps 1+4) - #367
Conversation
…measured curve in docs tests/arq_retry_sweep.sh runs the arq_e2e bench once per DEVOURER_TX_RETRY_LIMIT and tabulates delivered% / drops / mean retries (the airtime proxy), then hands each run to tests/arq_fec_dimension.py — the residual gap-length distribution a wfb-style (K,N) block FEC must cover, with the single-gap-per-window caveat and the conservative unreported-counts-as-lost accounting stated in the header. Measured (8812CU retrying TX -> 8812EU duplex ground airing PixelPilot-shaped bursts, ~1 k fps, near-field): limit 3 = 99.72% delivered with a 0.26% residual; 8 = 99.97%, residual 0.03% with gaps <= 3 (K8/N11 covers); 16 = 100.00% at mean 0.054 retries/frame; 32 = no gain for +17% more retries. Queue-time p99 flat across limits. docs/scheduled-mac.md carries the curve and the recommendation (16 on an ARQ link; 8 plus a light FEC floor where airtime is precious). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PR Summary by QodoAdd ARQ retry-limit sweep harness and post-ARQ FEC residual gap analyzer
AI Description
Diagram
High-Level Assessment
Files changed (3)
|
Code Review by Qodo
1.
|
…irs, ledger guard, nearest-rank percentiles - The sweep summary now breaks drops out per burst phase (drops@6M:10 / drops@6M:30) from each run's own report table — the limit-vs-burst-length interaction is the curve's point (smoke: 12 vs 36 at limit 3). - Each limit's run lands in a deterministic OUT=<sweep>/limit_<L> dir instead of inferring "newest /tmp/arq-e2e/*", which raced concurrent bench runs. - arq_fec_dimension.py refuses empty ledgers loudly (no tagged reports / no rx.seq would count every frame as undelivered — J1-format reports carry no tag) and computes nearest-rank percentiles (int(p*n) was one rank upward-biased); the published sweep extremes are unchanged under the fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Steps 1 and 4 of #362 (issue stays open for the rate-fallback and deadline research, steps 2–3).
What's added
tests/arq_retry_sweep.sh— runs the ARQ end-to-end delivery bench: three-ledger harness; count spsc-fat pool-exhaustion drops #359 arq_e2e bench once perDEVOURER_TX_RETRY_LIMIT, tabulates delivered% / drops / mean retries (the airtime proxy), and hands each run to the residual analyzer.tests/arq_fec_dimension.py— post-ARQ residual gap-length distribution per run: the quantity a wfb-style (K,N) block FEC must cover. Header states the caveats: single-gap-per-window mapping, near-field scope, and unreported-frames-count-as-lost (conservative — the safe direction for dimensioning).docs/scheduled-mac.md— the measured curve + recommendation next to the existing retry-knob paragraph.Measured (8812CU retrying TX → 8812EU duplex ground airing PixelPilot-shaped feedback bursts, ~1 k fps collision regime, near-field)
¹ single 62-frame gap in one run — one occurrence, P99 is still 3; not treated as a distribution feature.
Queue-time p99 is flat (~10–11 fw units) across all limits; only the rare worst case doubles (18 → 35) and then stops growing — deep retries are rare enough that latency does not enter the trade.
Recommendation carried in the docs:
DEVOURER_TX_RETRY_LIMIT=16on an ARQ link (zero bench loss at +5.4% retry airtime), or8plus a light FEC floor (K8/N11-class) where airtime is precious. Retries are backoff-spaced, so the field default of 3 can burn entirely inside one 2–3 ms ground-station feedback burst — that is where its 0.26% residual comes from.Also re-derived from the recorded #359/#366 ledgers (posted to #362): the stall-regime contrast — plain-async stalls produce ~110-frame contiguous gaps no sane FEC rate covers (rate 0.07), while the #366 backpressure policy bounds the same stalls to ≤13-frame gaps. FEC should be sized to the collision residual; stall regimes are a ring-policy problem.
Validation
Four full on-air bench runs (one per limit, ~129 k frames each, frame-exact three-ledger accounting from #359); analyzer parity-checked against the exploratory version on the recorded baseline.
ctest48/48 (no library changes in this PR).🤖 Generated with Claude Code