mirror of
https://github.com/ruvnet/RuView.git
synced 2026-10-18 21:00:22 -05:00
Merge pull request #1859 from clonea1/contrib/mesh-aligned-rate-gate
feat(firmware): align the CSI rate gate to mesh time
This commit is contained in:
2 files changed
+66
-2
No files matched your search
@@ -673,3 +673,20 @@ config UPLINK_WATCHDOG_TIMEOUT_S
|
||||
packet's health field before assuming the watchdog is responsible.
|
||||
|
||||
endmenu
|
||||
|
||||
config CSI_GATE_MESH_ALIGNED
|
||||
bool "Align the CSI rate gate to mesh time (enables cross-node pairing)"
|
||||
default y
|
||||
help
|
||||
The per-node elapsed gate ("20 ms since MY last accept") gives every node
|
||||
an independent phase, so two healthy nodes at 50 Hz accept disjoint
|
||||
frames and nothing can be paired across them. This aligns the 20 ms
|
||||
window to mesh time so nodes choose the same windows.
|
||||
|
||||
config CSI_SEQ_DIAG
|
||||
bool "Log (source MAC, rx_seq) for every CSI callback (ADR-345 phase 2 probe)"
|
||||
default n
|
||||
help
|
||||
Diagnostic. Emits the transmitter address and 802.11 sequence number for
|
||||
each CSI callback so the effect of the gate above can be measured rather
|
||||
than assumed. Verbose; leave off in normal operation.
|
||||
@@ -114,6 +114,9 @@ static int64_t s_last_send_us = 0;
|
||||
*/
|
||||
#define CSI_MIN_PROCESS_INTERVAL_US (20 * 1000) /* 50 Hz */
|
||||
static int64_t s_last_process_us = 0;
|
||||
/* Mesh-epoch window index of the last accepted frame. UINT64_MAX means "none
|
||||
* yet", which no real bucket can collide with. */
|
||||
static uint64_t s_last_gate_bucket = UINT64_MAX;
|
||||
static uint32_t s_early_drop = 0;
|
||||
|
||||
/* ---- ADR-029: Channel-hop state ---- */
|
||||
@@ -285,9 +288,53 @@ static void wifi_csi_callback(void *ctx, wifi_csi_info_t *info)
|
||||
(void)ctx;
|
||||
|
||||
/* Early rate gate: drop excess callbacks to ~50 Hz to prevent
|
||||
* SPI flash cache crash in WiFi ISR (wDev_ProcessFiq). */
|
||||
* SPI flash cache crash in WiFi ISR (wDev_ProcessFiq).
|
||||
*
|
||||
* HOW the rate is limited decides whether cross-node fusion is possible.
|
||||
*
|
||||
* The original gate is "at least 20 ms since MY last accept". Each node
|
||||
* therefore has an independent phase: node A can accept at t=0,20,40 ms
|
||||
* while node B accepts at 7,27,47. Both are perfectly healthy and both hit
|
||||
* 50 Hz, yet they accept disjoint frames and nothing can be paired.
|
||||
*
|
||||
* MEASURED 2026-08-30, two boards side by side: they HEARD the same
|
||||
* transmissions 72% of the time but only 25% of accepted frames were
|
||||
* common. The frames were there; the gate was throwing away the pairing.
|
||||
*
|
||||
* So gate on a bucket of the MESH-ALIGNED epoch instead: every node takes
|
||||
* the first frame it hears in the same absolute 20 ms window. Two nodes
|
||||
* that heard the same frame now both accept it. Mesh sync is good to
|
||||
* roughly 490 us against a 20 ms bucket -- about 2.5% of a window -- so
|
||||
* boundary disagreements are rare.
|
||||
*
|
||||
* Falls back to the original elapsed-time gate whenever mesh sync is not
|
||||
* valid (no leader heard yet, or a node that just booted), because an
|
||||
* unsynced epoch would otherwise gate on a meaningless number. A node
|
||||
* running unsynced simply gets the old behaviour rather than no gate.
|
||||
*
|
||||
* The half-interval floor below keeps the crash protection honest: bucket
|
||||
* gating alone could accept at the end of one window and the start of the
|
||||
* next, back to back. The floor bounds that to ~100 Hz for a single pair
|
||||
* while the average stays at 50 Hz. Do not remove it; the gate exists for
|
||||
* a crash, not for tidiness. */
|
||||
int64_t now_us = esp_timer_get_time();
|
||||
if ((now_us - s_last_process_us) < CSI_MIN_PROCESS_INTERVAL_US) {
|
||||
bool take;
|
||||
#ifdef CONFIG_CSI_GATE_MESH_ALIGNED
|
||||
if (c6_sync_espnow_is_valid()) {
|
||||
uint64_t bucket = c6_sync_espnow_get_epoch_us()
|
||||
/ (uint64_t)CSI_MIN_PROCESS_INTERVAL_US;
|
||||
take = (bucket != s_last_gate_bucket)
|
||||
&& ((now_us - s_last_process_us) >= CSI_MIN_PROCESS_INTERVAL_US / 2);
|
||||
if (take) {
|
||||
s_last_gate_bucket = bucket;
|
||||
}
|
||||
} else {
|
||||
take = (now_us - s_last_process_us) >= CSI_MIN_PROCESS_INTERVAL_US;
|
||||
}
|
||||
#else
|
||||
take = (now_us - s_last_process_us) >= CSI_MIN_PROCESS_INTERVAL_US;
|
||||
#endif
|
||||
if (!take) {
|
||||
s_early_drop++;
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user