Record RAK3401 mixed-PHY radio2 preamble384 test

This commit is contained in:
mikecarper committed 2026-10-23 00:54:59 -07:00
1 parent 3df901dce8
commit 90dba1c81d
2 files changed
+549

No files matched your search

@@ -0,0 +1,115 @@
<!-- meshcore-hosted-doc-link:start -->
<p class="meshcore-hosted-doc-link"><a href="https://mikecarper.github.io/MeshCore/test-results/rak3401_radio2_sf5_bw500_p384_2026-10-23/">View this page on MeshCore Docs</a>.</p>
<!-- meshcore-hosted-doc-link:end -->
# RAK3401 radio2 SF5/BW500/preamble384, 23 October 2026
Status: **COMPLETE, VALID MEASUREMENT, DELIVERY FAIL.** Radio2 received
**100/100** packets at SF5/BW500/preamble384, but the same scanning RAK missed
**3/100 primary packets** at SF7/BW62.5/preamble32. Both fixed receivers
received all their applicable packets. No firmware was changed or flashed.
The [sanitized receipt](rak3401_radio2_sf5_bw500_p384_2026-10-23.safe.json)
binds the counters, settings, independent audit and debugger capture hashes.
## Physical setup and scope
| Role | Installed firmware source | Frequency MHz | BW kHz | SF | CR | Preamble |
| --- | --- | ---: | ---: | ---: | ---: | ---: |
| RAK3401 Full unified Repeater, primary scanner | `adb11e7b` | 917.000 | 62.5 | 7 | 5 | 32 |
| Same RAK3401, secondary scanner, RX mode | `adb11e7b` | 918.000 | 500 | 5 | 5 | 384 |
| RAK WisMesh Tag Companion, sender | `08650f64` | Both tuples, one at a time | Both | Both | 5 | Both |
| T1000-E Companion, fixed primary witness | `08650f64` | 917.000 | 62.5 | 7 | 5 | 32 |
| Heltec T096 Companion, fixed secondary witness | `4773ac42` | 918.000 | 500 | 5 | 5 | 384 |
The sender used 2 dBm and sent 200 fresh 92-byte RAW_CUSTOM payloads, each
94 bytes on air including its direct, zero-hop route header. Ten seeded
pairs of ten-packet batches produced 100 packets per profile. Profile order
within each pair was shuffled. These are successive transmissions from one
sender, not concurrent traffic on two frequencies. The requested minimum gap
after physical TX_DONE was randomly 0.5-0.9 seconds; retunes increased actual
gaps to at most 3.416 seconds. The RF window lasted 201.569 seconds.
Only 917.000 and 918.000 MHz were commanded for the measured packet set.
Every transmission required one acceptance and exactly one advancing direct
physical TX counter. There were no retries, replacement packets or floods.
The scanner and both witnesses observed the same physical packet set.
RAK USB logging matched full raw packet bytes; Companion witnesses matched
both custom-payload pushes and independent raw-frame pushes. These two
Companion observations count as one delivery. No channel or contact changes
were needed. This test does not qualify message decryption or forwarding.
## Results
| Receiver | Primary received / 100 | Secondary received / 100 | Result |
| --- | ---: | ---: | --- |
| RAK3401 scanner | 97 | 100 | FAIL primary; PASS secondary |
| T1000-E fixed witness | 100 | Not applicable | PASS |
| T096 fixed witness | Not applicable | 100 | PASS |
The missing primary packets were **P022, P044 and P070**, corresponding to
global zero-based ordinals **51, 93 and 139**. Every one reached the primary
witness. There were no duplicates, opposite-profile witness deliveries, log
drops, framing losses or collector errors. All three receivers transmitted
zero packets during the RF measurement. All four radios accumulated zero new
receive/CRC errors. Thus the three scanner misses are supported by independent
same-packet witness evidence; their cause is not established by correlation.
RAK, Tag and T096 already had historical core flag `0x08` before this test;
T1000-E had zero. Those values were unchanged throughout the completed run.
No error flags were cleared. RAK soft/hard radio watchdog counters remained
`0/0`, queues were empty and uptimes remained continuous. This is not a claim
that the radios had zero historical errors.
## Switch timing and CPU observation
For the current pair, the firmware reported a **1,176 us guarded switch
budget**. This is the 1,069 us sampled maximum plus a rounded-up 10% margin.
nRF52 dual-profile timing uses RTC2 at 32,768 Hz, approximately 30.52 us per
tick. This measures the guarded RX-to-RX path, including activity checks,
standby/FS transition, frequency and changed modem/preamble parameters, and
restarting receive. It is not a bare PLL retune measurement or an average.
The derived visits were `9421,20995` us and the recommended preambles were
`32,200`. The requested radio2 preamble384 exceeds that estimate. Its 100/100
delivery does not explain the three primary misses despite primary meeting
the displayed recommendation. The separately displayed lifetime maximum
`3906us` is not reset with the pair's timing self-test and cannot be assigned
to a particular lost packet from these counters.
The attached debugger completed a **300.008-second nonhalting memory-AP
capture with 27,118 samples**. Before/after hardware identity matched. No
debug enable, halt, stepping, interrupt masking, lockup or reset was observed;
capture integrity passed. SWD did not set breakpoints, stop the CPU or flash
anything. It sampled CPU status, not per-hop instruction traces.
Only eight samples observed sleep, a weighted sampled fraction of **0.0307%**.
This is not a CPU sleep qualification: Full Repeater deliberately inhibits
CPU power saving during an active USB data session, which was needed here
for exact RF logging. A saved `powersaving on` preference does not bypass
that gate. The existing nRF52 scanner idle path requires more than 48 RTC2
ticks, approximately 1.465 ms, remaining before it admits a task delay; the
roughly 2 ms practical idle-budget consideration is separate from retune
time. An unplugged-USB, SWD-only sleep experiment would be a separate test.
Source inspection found concrete speed candidates, without changing this
baseline: RAK currently uses the default 2 MHz byte-at-a-time SPI path,
although a buffered nRF52 HAL exists; the retune path also performs modem
queries during setup and maximum-airtime calculation. Their individual costs
were not measured by this capture. Faster switching and any receiver repair
need fresh source-bound RF validation before a qualification claim.
## Controller correction and restoration
An earlier, separate attempt stopped after 50 transmissions because its
fixed 2.1-second wait ended while the sender still reported pending settings.
Those packets are excluded from every final count. Its settings and gateway
were restored. The corrected private controller polls for the exact active
tuple and preamble before any next transmission, retains partial evidence
on abort, and waits for live radio2 shutdown during cleanup. The firmware
was unchanged. The completed run used fresh nonces and a new receipt.
All four nodes passed final settings preservation. Temporary radio leases
were removed, RAK live radio2 status returned to off, original TX power and
USB logging were restored, and the gateway service reacquired its radio with
its configuration unchanged. The independent offline audit reproduced the
three missing primary packets and reported no measurement confounds.
@@ -0,0 +1,434 @@
{
"schema": 1,
"date": "2026-10-23",
"test": "RAK3401 radio2 SF5 BW500 preamble384 physical receive test",
"passed": false,
"complete": true,
"delivery_passed": false,
"firmware_writes": 0,
"packet_retries": 0,
"physical_packet_bytes": 94,
"application_payload_bytes": 92,
"frequency_profiles": {
"primary": {
"frequency_mhz": 917,
"bandwidth_khz": 62.5,
"sf": 7,
"cr": 5,
"preamble_symbols": 32
},
"secondary": {
"frequency_mhz": 918,
"bandwidth_khz": 500,
"sf": 5,
"cr": 5,
"preamble_symbols": 384
}
},
"sender_power_dbm": 2,
"traffic_pattern": "20 batches of10; ten batches/profile; seeded pair order; no per-packet interleaving claim",
"transmissions": 200,
"rf_duration_seconds": 201.56887236999864,
"gap_seconds": {
"minimum": 0.5018097600004694,
"maximum": 3.4158496399995784
},
"evidence": {
"private_report_sha256": "a6f39391895ca1ec4c163b446c83e43cb2ca84f1098a7f22efddd67eebe88dd2",
"controller_sha256": "a172870af04a36c0186f8d9289ca52f0c2bc416da26fc54decc553d940045299",
"debugger_log_sha256": "13545ca7ab5ac3b3934760c5452aff2919f42ef52916f1002c028b473774b12e"
},
"nodes": {
"rak": {
"board": "RAK 3401",
"firmware_version": "v1.18.0.1-halo-keymind-cascade-dev-adb11e7b (Build: 23-Oct-2026)",
"power_and_logging": {
"get powersaving": "on",
"get radio.rxps.config": "off,level=0,preamble=0,rx=65625,sleep=60000",
"get usb.logging": "on"
},
"core_before": {
"battery_mv": 4248,
"uptime_secs": 5423,
"errors": 8,
"queue_len": 0
},
"core_after": {
"battery_mv": 4182,
"uptime_secs": 5630,
"errors": 8,
"queue_len": 0
},
"packet_deltas": {
"sent": 0,
"sent_direct": 0,
"sent_flood": 0,
"recv": 197,
"recv_errors": 0
},
"rf_packet_counters_before": {
"sent": 24,
"sent_direct": 1,
"sent_flood": 23,
"recv": 73,
"recv_errors": 0
},
"rf_packet_counters_after": {
"sent": 24,
"sent_direct": 1,
"sent_flood": 23,
"recv": 270,
"recv_errors": 0
},
"configured": {
"get tempradio": "917.000,62.500,7,5,32",
"get tempradio2": "918.000,500.000,5,5,rx,384; 0d0h13m (13 min) left; WARN recommended preamble: radio2=200; switch=1176us",
"get radio2.status": "rx; RX=35,38 TX=24,0 switches=18181 errors=0 max=3906us preamble=32,384; test=4/4,4/4 ready budget=1176us; WARN recommended preamble: radio2=200",
"get radio2.scan": "slow=radio; listen_us=9421,20995; preamble=32,384; WARN recommended preamble: radio2=200; switch=1176us",
"get radio2.timing": "chirps=4.60,328.05; need=32,200; switch=1176us; loop=0us; test=4/4,4/4 ready; WARN recommended preamble: radio2=200"
},
"radio_diagnostics_before_configuration": "state=1(RX), recv=61, sent=12, errors=0, err_flags=8, outbound=no, last_rx=147s ago",
"radio_diagnostics_after": "state=1(RX), recv=270, sent=24, errors=0, err_flags=8, outbound=no, last_rx=5s ago",
"watchdog_before": "soft=0,hard=0",
"watchdog_after": "soft=0,hard=0",
"restoration_passed": true,
"restored_live_radio2_status": "off; RX=132,138 TX=24,0 switches=35454 errors=0 max=3906us preamble=32,32",
"collection": {
"expected": 200,
"received": 197,
"missing_ordinals": [
51,
93,
139
],
"duplicates": 0,
"wrong_profile_ordinals": [],
"drops": [],
"errors": [],
"framing": {
"ports_opened": 0,
"ports_closed": 0,
"request_frames_sent": 0,
"response_frames_received": 0,
"request_bytes_sent": 0,
"response_payload_bytes": 0,
"discarded_prefix_bytes": 0,
"invalid_length_candidates": 0,
"ignored_push_frames": 0
}
},
"profile_results": {
"primary": {
"expected": 100,
"received": 97,
"missing_ordinals": [
51,
93,
139
]
},
"secondary": {
"expected": 100,
"received": 100,
"missing_ordinals": []
}
}
},
"sender": {
"board": "RAK WisMesh Tag",
"firmware_version": "v1.18.0.1-halo-keymind-cascade-dev-08650f64 (Build: 20-Oct-2026)",
"power_and_logging": {
"get powersaving": "on",
"get radio.rxps.config": "on,level=8,preamble=16,rx=39657,sleep=15975",
"get usb.logging": "usb.logging off; port: dedicated USB CDC 1, interface 02 (Linux: *-if02; tty/COM name is host-assigned)"
},
"core_before": {
"battery_mv": 4121,
"uptime_secs": 11971,
"errors": 8,
"queue_len": 0
},
"core_after": {
"battery_mv": 4122,
"uptime_secs": 12177,
"errors": 8,
"queue_len": 0
},
"packet_deltas": {
"sent": 200,
"sent_direct": 200,
"sent_flood": 0,
"recv": 0,
"recv_errors": 0
},
"rf_packet_counters_before": {
"sent": 73,
"sent_direct": 62,
"sent_flood": 11,
"recv": 43,
"recv_errors": 4
},
"rf_packet_counters_after": {
"sent": 273,
"sent_direct": 262,
"sent_flood": 11,
"recv": 43,
"recv_errors": 4
},
"configured": {
"get tempradio": "TempRadio active: 917.000,62.50,7,5 780s left,preamble=32",
"get tempradio2": "off",
"get radio2.status": "off; RX=42,0 TX=73,0 switches=0 errors=0 max=0us preamble=32,32",
"get radio2.scan": "off",
"get radio2.timing": "off"
},
"radio_diagnostics_before_configuration": "state=1(RX), recv=27, sent=69, errors=4, err_flags=8, outbound=no, last_rx=254s ago",
"radio_diagnostics_after": "state=1(RX), recv=43, sent=273, errors=4, err_flags=8, outbound=no, last_rx=263s ago",
"watchdog_before": "Unknown command",
"watchdog_after": "Unknown command",
"restoration_passed": true,
"restored_live_radio2_status": "off; RX=42,0 TX=273,0 switches=0 errors=0 max=0us preamble=32,32"
},
"primary": {
"board": "Seeed Tracker T1000-E",
"firmware_version": "v1.18.0.1-halo-keymind-cascade-dev-08650f64 (Build: 20-Oct-2026)",
"power_and_logging": {
"get powersaving": "off",
"get radio.rxps.config": "off,level=3,preamble=16,rx=44079,sleep=5732",
"get usb.logging": "usb.logging off; port: primary USB serial port (tty/COM name is host-assigned)"
},
"core_before": {
"battery_mv": 4185,
"uptime_secs": 12406,
"errors": 0,
"queue_len": 0
},
"core_after": {
"battery_mv": 4193,
"uptime_secs": 12613,
"errors": 0,
"queue_len": 0
},
"packet_deltas": {
"sent": 0,
"sent_direct": 0,
"sent_flood": 0,
"recv": 100,
"recv_errors": 0
},
"rf_packet_counters_before": {
"sent": 8,
"sent_direct": 0,
"sent_flood": 8,
"recv": 6070,
"recv_errors": 290
},
"rf_packet_counters_after": {
"sent": 8,
"sent_direct": 0,
"sent_flood": 8,
"recv": 6170,
"recv_errors": 290
},
"configured": {
"get tempradio": "TempRadio active: 917.000,62.50,7,5 781s left,preamble=32",
"get tempradio2": "off",
"get radio2.status": "off; RX=6060,0 TX=8,0 switches=0 errors=0 max=0us preamble=32,32",
"get radio2.scan": "off",
"get radio2.timing": "off"
},
"radio_diagnostics_before_configuration": "state=1(RX), recv=6055, sent=4, errors=290, err_flags=0, outbound=no, last_rx=8s ago",
"radio_diagnostics_after": "state=1(RX), recv=6170, sent=8, errors=290, err_flags=0, outbound=no, last_rx=13s ago",
"watchdog_before": "Unknown command",
"watchdog_after": "Unknown command",
"restoration_passed": true,
"restored_live_radio2_status": "off; RX=6160,0 TX=8,0 switches=0 errors=0 max=0us preamble=32,32",
"collection": {
"expected": 100,
"received": 100,
"missing_ordinals": [],
"duplicates": 0,
"wrong_profile_ordinals": [],
"drops": [],
"errors": [],
"framing": {
"ports_opened": 0,
"ports_closed": 0,
"request_frames_sent": 0,
"response_frames_received": 328,
"request_bytes_sent": 0,
"response_payload_bytes": 24176,
"discarded_prefix_bytes": 0,
"invalid_length_candidates": 0,
"ignored_push_frames": 0
}
},
"profile_results": {
"primary": {
"expected": 100,
"received": 100,
"missing_ordinals": []
}
}
},
"secondary": {
"board": "Heltec T096",
"firmware_version": "v1.18.0.1-halo-keymind-cascade-dev-4773ac42 (Build: 20-Oct-2026)",
"power_and_logging": {
"get powersaving": "on",
"get radio.rxps.config": "on,level=8,preamble=16,rx=39657,sleep=15975",
"get usb.logging": "usb.logging off; port: dedicated USB CDC 1, interface 02 (Linux: *-if02; tty/COM name is host-assigned)"
},
"core_before": {
"battery_mv": 4184,
"uptime_secs": 13046,
"errors": 8,
"queue_len": 0
},
"core_after": {
"battery_mv": 4191,
"uptime_secs": 13253,
"errors": 8,
"queue_len": 0
},
"packet_deltas": {
"sent": 0,
"sent_direct": 0,
"sent_flood": 0,
"recv": 100,
"recv_errors": 0
},
"rf_packet_counters_before": {
"sent": 8,
"sent_direct": 0,
"sent_flood": 8,
"recv": 31,
"recv_errors": 111
},
"rf_packet_counters_after": {
"sent": 8,
"sent_direct": 0,
"sent_flood": 8,
"recv": 131,
"recv_errors": 111
},
"configured": {
"get tempradio": "TempRadio active: 918.000,500.00,5,5 782s left,preamble=384",
"get tempradio2": "off",
"get radio2.status": "off; RX=30,0 TX=8,0 switches=0 errors=0 max=0us preamble=384,32",
"get radio2.scan": "off",
"get radio2.timing": "off"
},
"radio_diagnostics_before_configuration": "state=1(RX), recv=31, sent=4, errors=111, err_flags=8, outbound=no, last_rx=149s ago",
"radio_diagnostics_after": "state=1(RX), recv=131, sent=8, errors=111, err_flags=8, outbound=no, last_rx=5s ago",
"watchdog_before": "Unknown command",
"watchdog_after": "Unknown command",
"restoration_passed": true,
"restored_live_radio2_status": "off; RX=130,0 TX=8,0 switches=0 errors=0 max=0us preamble=32,32",
"collection": {
"expected": 100,
"received": 100,
"missing_ordinals": [],
"duplicates": 0,
"wrong_profile_ordinals": [],
"drops": [],
"errors": [],
"framing": {
"ports_opened": 0,
"ports_closed": 0,
"request_frames_sent": 0,
"response_frames_received": 313,
"request_bytes_sent": 0,
"response_payload_bytes": 23986,
"discarded_prefix_bytes": 0,
"invalid_length_candidates": 0,
"ignored_push_frames": 0
}
},
"profile_results": {
"secondary": {
"expected": 100,
"received": 100,
"missing_ordinals": []
}
}
}
},
"debugger": {
"schema": 1,
"log_sha256": "13545ca7ab5ac3b3934760c5452aff2919f42ef52916f1002c028b473774b12e",
"parser_sha256": "0b4ed3ff71291954012424d493b8550b19c3da0f0e5b97f55e5f78d57d4fb76f",
"config_sha256": "1feb1f14a80cc0e9f1ad403d4c67d73866eea8c548c8df4c3c93b66b0be8ee6f",
"duration_ms": 300000,
"maximum_elapsed_ms": 300500,
"minimum_sample_count": 20000,
"sampled_cpu_integrity_passed": true,
"sampled_cpu_integrity_failures": [],
"physical_RF_delivery_qualified_by_this_parser": false,
"cpu_sampling": {
"passed": false,
"failures": [
"insufficient_sleeping_observations"
],
"sample_count": 27118,
"counts": {
"C_DEBUGEN": 0,
"C_HALT": 0,
"C_STEP": 0,
"C_MASKINTS": 0,
"S_HALT": 0,
"S_SLEEP": 8,
"S_LOCKUP": 0,
"S_RESET_ST": 0
},
"elapsed_ms": 300008,
"observed_ms": 300004.0,
"weighted_sleep_ms": 92.0,
"weighted_sleep_fraction": 0.00030666257783229555,
"unweighted_sleep_fraction": 0.00029500700641640237,
"max_gap_ms": 18.0,
"max_read_ms": 3,
"baseline_debug_ctrl_hex": "0000",
"baseline_C_DEBUGEN": false,
"initial_sticky_reset": false,
"final_DHCSR": "01010000",
"openocd_version": [
"Open On-Chip Debugger 0.12.0+dev-snapshot (2025-07-16-14:15)"
],
"qualification": "sampled CPU sleep observations, not exact residency or electrical current",
"firmware_identity_must_be_bound_separately": true,
"exact_hardware_identity_matched_before_and_after": true
},
"offline_analysis_of_physical_capture": true,
"cpu_sleep_qualified": false,
"cpu_sleep_scope": "Active USB logging inhibits repeater CPU sleep; this is an RF/debug-integrity capture."
},
"cleanup_passed": true,
"gateway_restored": true,
"errors": [],
"independent_audit": {
"audit_passed": false,
"failures": [
"missing_packets_rak"
],
"cautions": [],
"same_packet_witness_discrepancies": {
"primary": [
"P022",
"P044",
"P070"
],
"secondary": []
},
"auditor_sha256": "3333440a28445932beef62adab177301a492cb1306fb87e9a7080cb350422669"
},
"earlier_incomplete_attempt": {
"transmissions": 50,
"primary_transmissions": 20,
"secondary_transmissions": 30,
"reason": "Sender was still pending after the controller fixed 2.1-second retune wait.",
"included_in_final_counts": false,
"settings_and_gateway_restored": true,
"controller_only_repair": "Poll for exact active tuple and preamble before any next transmission."
}
}