MeshPRS

MeshPRS: test plan

How a MeshPRS implementation is shown to work, from byte-level vectors on a desk to a controlled field trial on the SEQ mesh and APRS-IS. The trial’s pass criteria are the proposed answer to OPEN-QUESTIONS Q12. Nothing here is decided until Alec signs it off through a decision record.

The models and numbers behind this plan are in reliability.md. Frame layouts belong to ../spec/MeshPRS-Spec.md. The frame fields this plan relies on (seq, retry, the gating field’s test value, the acked seq) are in spec §9.3; reliability.md §5 maps them (C1-C6).

Tests are run in stages, and each stage gates the next:

Stage What Where Gate to next stage
T1 Encode/decode and gate logic against shared vectors desk, no radio every vector passes in every implementation
T2 Simulated mesh, fault injection desk (Kelly mesh sim) retry/dedupe/fallback logic behaves
T3 RF bench, attenuated, 1-2 hops bench per-hop delivery ≥ 99 % at good SNR
T4 Hop chain 1-8 hops bench or controlled outdoor chain curve measured; ≤ 3-hop target met
T5 Multi-iGate and APRS-IS, private server bench + private aprsc 0 duplicates after the server’s dupe filter
T6 Field trial on the live mesh + private aprsc SEQ Q12 criteria (§7)
T7 Controlled live APRS-IS test SEQ + live APRS-IS short run, clean, Alec signs off

1. T1: shared JSON test vectors

Layout. The spec (§15) owns the file names and the drop-reason strings; this plan uses them as written there. Spec §15.1 names four files: frames.json (encode and decode, all subtypes), gateway.json (frame → APRS line, or drop reason; also the dedupe, retry and sequence cases below), inject.json (APRS-IS → mesh) and text.json (compact text profile).

The model is kelly-page/bridge/igate_vectors.json. Every implementation (js/igate.js via tests/igate-tests.html, bridge/kelly_igate.py via test_kelly_igate.py) loads the same file and must pass every entry. Its conventions, kept:

MeshPRS adds the record form already given in ../test-vectors/README.md: id, spec (section), about, input, config, expect ({ "out": … } or { "drop": "<reason>" }, with the reason exactly as spec §9.5 / §10.1 names it).

frames.json (byte layouts come from the spec; these are the kinds of vector, not the bytes):

gateway.json, reliability cases:

Pass: 100 % of vectors in every implementation. Any change to a vector file needs the spec section it tests to change in the same commit.

2. T2: simulated mesh

kelly-page/bridge/kelly_mesh_sim.py (and its JS twin js/meshcoresim.js) is a companion-protocol simulator with fault injection: ackloss=P, acklossfirst=N, acklatefirst=N, hang=S and others. Extend it with a MeshPRS channel and GRP_DATA frames, then run:

Pass: the expected sequence of sends, byte for byte, in each case. These are logic tests only; the sim has no RF model.

3. T3: RF bench, 1-2 hops

Kit:

Link design.

Tests:

Pass: ≥ 99 % per hop (95 % lower bound ≥ 98 %) at SNR ≥ +5 dB. The SNR-vs-delivery curve and the collision results are recorded for reliability.md, replacing the model’s values.

4. T4: hop chain, 1-8 hops

4.1 Building the chain

Three ways, in order of fidelity:

Method How Tests Limits
A. Forced direct path on an open bench All nodes in one room. The sender uses CMD_SEND_CHANNEL_DATA with an explicit path of 1-8 repeater hashes; only the named next hop forwards a direct packet [source §2.2] Hop-count logic, path growth, latency, ack return, direct-route fallback Every node hears every hop, so no hidden-node or weak-link effects, and floods short-circuit to 1 hop. Logic only, not RF reliability
B. Attenuated chain Nodes in boxes, adjacent pairs linked through attenuators/splitters, non-adjacent pairs isolated (§3). A flood can only travel the chain Real flood hop count, real per-hop loss, collision effects (add a parallel branch) Needs 8 repeaters + 2 endpoints and enough isolation; the most work
C. Outdoor chain Low-power repeaters spaced out along a line (a road or a park). Then the real mesh, using known 3-6 hop routes such as VK4AZ ↔ VK4AB Real terrain and real background traffic Not controlled; background traffic varies; uses the live channel (keep within the reliability.md §4.4 budget)

4.2 Verifying hop count

4.3 Runs

Output: a measured delivery-vs-hops table to set against the reliability.md §2.1 model, per frame class and route type.

Pass: the ≤ 3-hop one-way delivery target in §7 is met on method B or C.

5. Measurement methods

5.1 Packet loss: sequence numbers and sample size

True p ±5 % ±3 % ±2 %
0.5 (worst case) 385 1 068 2 401
0.8 246 683 1 537
0.9 139 385 865
0.95 73 203 457

5.2 Duplicates

5.3 Latency

5.4 Loops

5.5 Airtime

5.6 Background load

At each iGate and trial repeater, log the channel for ≥ 2 h before the trial, as on 2026-10-25 (meshcore_rx_logger.py). That measures G for reliability.md §2.3 and gives the baseline the trial’s added airtime is compared against.

6. T5 and the APRS-IS test path

Why it needs care:

T5 multi-iGate dedupe test:

6.2 T7: controlled live test

Only after T5 and T6 pass.

7. Pass criteria for the field trial (decision 0013)

Measured over T6 (the live mesh with a private aprsc), with at least 2 iGates and at least 1 mobile and 2 static originators, over ≥ 7 days.

# Metric Proposed criterion Sample
P1 Originator → any iGate, one-way, ≤ 3 hops (the received path) ≥ 90 % observed, 95 % lower bound ≥ 85 % ≥ 196 frames per originator
P2 Same, 4-6 hops Reported, not failed; compared with the model ≥ 140
P3 Message round trip (frame + ack, at most 2 retries at 30 s and 60 s), ≤ 3 hops ≥ 95 % observed, lower bound ≥ 90 % ≥ 139 messages
P4 Duplicates passed by APRS-IS per (src, seq) exactly 1, in 100 % of frames all
P5 Duplicate submissions by one iGate 0 all
P6 Loops 0 all
P7 MeshPRS airtime within the reliability.md §4.4 budget per node and per iGate; ≤ 2 % added occupancy at each logged repeater, from GET_STATUS deltas daily
P8 Latency, mesh → APRS-IS, ≤ 3 hops p95 ≤ 10 s all delivered
P9 No harm to other mesh traffic logged repeaters’ flood, dupe and error counters show no step change beyond normal day-to-day variation (baseline from §5.6) before vs during
P10 Vectors the trial build passes 100 % of the T1 vectors —

If P1 or P3 fails, the report states the hop count and per-hop SNR (from TRACE) of the failing originators before anyone changes the protocol. The usual cause in SEQ is a weak link: in the 2026-10-24 test it was a repeater’s power, not the protocol.