mirror of
https://github.com/ruvnet/RuView
synced 2026-07-31 18:51:42 +00:00
fix: multistatic canonicalization, csi_fps burst inflation, control-packet starvation (#1170, #1180, #1183) (#1193)
#1170 — live multistatic bridge fed raw, un-canonicalized per-node CSI (64/128/192 bins) to MultistaticFuser, tripping DimensionMismatch every cycle and silently disabling fusion on mixed HT20/HT40 meshes. Add HardwareNormalizer::resample_to_canonical (resample-only, no z-score) and canonicalize every node frame onto the 56-tone grid before fusion. #1180 — update_csi_fps_ema only rejected dt<=0 or >=1s, so sub-ms UDP-burst arrivals (36us -> ~27kHz) inflated csi_fps_ema 40-840x. Add a 5ms plausibility floor and stop re-anchoring observe_csi_frame_arrival on burst deltas. #1183 — global ENOMEM backoff (CSI flood) starved <=48B/<=1Hz control packets. Add stream_sender_send_priority() bypassing the backoff gate without touching the streak; route feature_state/HEALTH/sync through it. Fix the misleading "HEALTH sent" log that printed even on rv_mesh_send failure. Verified: signal 501, sensing-server 677 tests (0 failed); firmware builds clean. Claude-Session: https://claude.ai/code/session_01AgpTcBLRJ32hUsKWxDXf36
This commit is contained in:
@@ -319,7 +319,9 @@ static void emit_feature_state(void)
|
||||
(uint64_t)esp_timer_get_time(),
|
||||
profile);
|
||||
|
||||
int sent = stream_sender_send((const uint8_t *)&pkt, sizeof(pkt));
|
||||
/* feature_state is ~1 Hz and small — priority path so the CSI ENOMEM
|
||||
* backoff can't starve it (#1183). */
|
||||
int sent = stream_sender_send_priority((const uint8_t *)&pkt, sizeof(pkt));
|
||||
if (sent < 0) {
|
||||
ESP_LOGW(TAG, "feature_state emit failed");
|
||||
}
|
||||
@@ -333,11 +335,14 @@ static void slow_loop_cb(TimerHandle_t t)
|
||||
* detect sync-error drift. */
|
||||
uint8_t nid[8];
|
||||
node_id_bytes(nid);
|
||||
rv_mesh_send_health(s_role, s_mesh_epoch, nid);
|
||||
/* #1183: report the actual send result — the old log printed "HEALTH sent"
|
||||
* unconditionally even when rv_mesh_send returned ESP_FAIL. */
|
||||
esp_err_t health_rc = rv_mesh_send_health(s_role, s_mesh_epoch, nid);
|
||||
|
||||
ESP_LOGI(TAG, "slow tick (state=%u, feature_state_seq=%u, role=%u, epoch=%u) HEALTH sent",
|
||||
ESP_LOGI(TAG, "slow tick (state=%u, feature_state_seq=%u, role=%u, epoch=%u) HEALTH %s",
|
||||
(unsigned)s_state, (unsigned)s_feature_state_seq,
|
||||
(unsigned)s_role, (unsigned)s_mesh_epoch);
|
||||
(unsigned)s_role, (unsigned)s_mesh_epoch,
|
||||
health_rc == ESP_OK ? "sent" : "FAILED");
|
||||
}
|
||||
|
||||
/* ---- Public API ---- */
|
||||
|
||||
@@ -341,7 +341,9 @@ static void wifi_csi_callback(void *ctx, wifi_csi_info_t *info)
|
||||
memcpy(&sync[24], &s_sequence, 4); /* high-water seq for pairing */
|
||||
uint32_t zero32 = 0;
|
||||
memcpy(&sync[28], &zero32, 4); /* reserved (room for leader_id low32) */
|
||||
int sr = stream_sender_send(sync, sizeof(sync));
|
||||
/* Sync packets are 32 B at ~0.5 Hz — priority path so the CSI
|
||||
* ENOMEM backoff can't starve cross-node time alignment (#1183). */
|
||||
int sr = stream_sender_send_priority(sync, sizeof(sync));
|
||||
static uint32_t s_sync_count = 0;
|
||||
s_sync_count++;
|
||||
if (s_sync_count <= 3 || (s_sync_count % 60) == 0) {
|
||||
|
||||
@@ -188,7 +188,9 @@ size_t rv_mesh_encode_calibration_start(uint8_t sender_role,
|
||||
esp_err_t rv_mesh_send(const uint8_t *frame, size_t len)
|
||||
{
|
||||
if (frame == NULL || len == 0) return ESP_ERR_INVALID_ARG;
|
||||
int sent = stream_sender_send(frame, len);
|
||||
/* Mesh control packets (HEALTH, anomaly) are low-rate and tiny — send them
|
||||
* on the priority path so the CSI ENOMEM backoff can't starve them (#1183). */
|
||||
int sent = stream_sender_send_priority(frame, len);
|
||||
if (sent < 0) {
|
||||
ESP_LOGW(TAG, "rv_mesh_send: stream_sender failed (len=%u)",
|
||||
(unsigned)len);
|
||||
|
||||
@@ -121,6 +121,34 @@ int stream_sender_send(const uint8_t *data, size_t len)
|
||||
return sent;
|
||||
}
|
||||
|
||||
int stream_sender_send_priority(const uint8_t *data, size_t len)
|
||||
{
|
||||
if (s_sock < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Priority path (#1183): low-rate control packets (feature_state, HEALTH,
|
||||
* mesh sync) bypass the global ENOMEM backoff gate so the high-rate CSI
|
||||
* stream cannot starve them. These are ≤48 B at ≤1 Hz — negligible pbuf
|
||||
* pressure, so they won't re-trigger the crash cascade that the backoff
|
||||
* (driven by the 50 Hz CSI flood) exists to prevent.
|
||||
*
|
||||
* Crucially, an ENOMEM here is reported quietly and does NOT extend the
|
||||
* global streak/backoff: a tiny control packet failing is a symptom of
|
||||
* the bulk-stream pressure, not a cause, so it must not feed the cooldown
|
||||
* that suppresses the next CSI frame. Likewise a success does not reset
|
||||
* the streak — the bulk path owns that signal. */
|
||||
int sent = sendto(s_sock, data, len, 0,
|
||||
(struct sockaddr *)&s_dest_addr, sizeof(s_dest_addr));
|
||||
if (sent < 0) {
|
||||
if (errno != ENOMEM) {
|
||||
ESP_LOGW(TAG, "priority sendto failed: errno %d", errno);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
void stream_sender_deinit(void)
|
||||
{
|
||||
if (s_sock >= 0) {
|
||||
|
||||
@@ -36,6 +36,20 @@ int stream_sender_init_with(const char *ip, uint16_t port);
|
||||
*/
|
||||
int stream_sender_send(const uint8_t *data, size_t len);
|
||||
|
||||
/**
|
||||
* Send a low-rate control packet, bypassing the ENOMEM backoff gate (#1183).
|
||||
*
|
||||
* Intended for ≤48 B, ≤1 Hz control traffic (feature_state, HEALTH, mesh
|
||||
* sync) that must not be starved by the global backoff the high-rate CSI
|
||||
* stream triggers. An ENOMEM on this path is reported quietly and does NOT
|
||||
* extend or reset the global backoff streak.
|
||||
*
|
||||
* @param data Frame data buffer.
|
||||
* @param len Length of data to send.
|
||||
* @return Number of bytes sent, or -1 on error.
|
||||
*/
|
||||
int stream_sender_send_priority(const uint8_t *data, size_t len);
|
||||
|
||||
/**
|
||||
* Close the UDP sender socket.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user