* @file edge_processing.h
* @brief ADR-039 Edge Intelligence — dual-core CSI processing pipeline.
*
* Core 0 (WiFi): Produces CSI frames into a lock-free SPSC ring buffer.
* Core 1 (DSP): Consumes frames, runs signal processing, extracts vitals.
*
* Features:
* - Biquad IIR bandpass filters for breathing (0.1-0.5 Hz) and heart rate (0.8-2.0 Hz)
* - Phase unwrapping and Welford running statistics
* - Top-K subcarrier selection by variance
* - Presence detection with adaptive threshold calibration
* - Vital signs: breathing rate, heart rate (zero-crossing BPM)
* - Fall detection (phase acceleration exceeds threshold)
* - Delta compression (XOR + RLE) for bandwidth reduction
* - Multi-person vitals via subcarrier group clustering
* - 32-byte vitals packet (magic 0xC5110002) for server-side parsing
*/
#ifndef EDGE_PROCESSING_H
#define EDGE_PROCESSING_H
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#define EDGE_VITALS_MAGIC 0xC5110002
#define EDGE_COMPRESSED_MAGIC 0xC5110003
#define EDGE_RING_SLOTS 16
#define EDGE_MAX_IQ_BYTES 1024
#define EDGE_PHASE_HISTORY_LEN 256
#define EDGE_TOP_K 8
#define EDGE_MAX_SUBCARRIERS 128
#define EDGE_MAX_PERSONS 4
#define EDGE_CALIB_FRAMES 1200
#define EDGE_CALIB_SIGMA_MULT 3.0f
typedef struct {
uint8_t iq_data[EDGE_MAX_IQ_BYTES];
uint16_t iq_len;
int8_t rssi;
uint8_t channel;
uint32_t timestamp_us;
} edge_ring_slot_t;
typedef struct {
edge_ring_slot_t slots[EDGE_RING_SLOTS];
volatile uint32_t head;
volatile uint32_t tail;
} edge_ring_buf_t;
typedef struct {
float b0, b1, b2;
float a1, a2;
float x1, x2;
float y1, y2;
} edge_biquad_t;
typedef struct {
double mean;
double m2;
uint32_t count;
} edge_welford_t;
typedef struct {
float phase_history[EDGE_PHASE_HISTORY_LEN];
uint16_t history_len;
uint16_t history_idx;
float breathing_bpm;
float heartrate_bpm;
uint8_t subcarrier_idx;
bool active;
} edge_person_vitals_t;
typedef struct __attribute__((packed)) {
uint32_t magic;
uint8_t node_id;
uint8_t flags;
uint16_t breathing_rate;
uint32_t heartrate;
int8_t rssi;
uint8_t n_persons;
uint8_t reserved[2];
float motion_energy;
float presence_score;
uint32_t timestamp_ms;
uint32_t reserved2;
} edge_vitals_pkt_t;
_Static_assert(sizeof(edge_vitals_pkt_t) == 32, "vitals packet must be 32 bytes");
typedef struct {
uint8_t tier;
float presence_thresh;
float fall_thresh;
uint16_t vital_window;
uint16_t vital_interval_ms;
uint8_t top_k_count;
uint8_t power_duty;
} edge_config_t;
* Initialize the edge processing pipeline.
* Creates the SPSC ring buffer and starts the DSP task on Core 1.
*
* @param cfg Edge configuration (from NVS or defaults).
* @return ESP_OK on success.
*/
esp_err_t edge_processing_init(const edge_config_t *cfg);
* Enqueue a CSI frame from the WiFi callback (Core 0).
* Lock-free SPSC push — safe to call from ISR context.
*
* @param iq_data Raw I/Q data from wifi_csi_info_t.buf.
* @param iq_len Length of I/Q data in bytes.
* @param rssi RSSI from rx_ctrl.
* @param channel WiFi channel number.
* @return true if enqueued, false if ring buffer is full (frame dropped).
*/
bool edge_enqueue_csi(const uint8_t *iq_data, uint16_t iq_len,
int8_t rssi, uint8_t channel);
* Get the latest vitals packet (thread-safe copy).
*
* @param pkt Output vitals packet.
* @return true if valid vitals data is available.
*/
bool edge_get_vitals(edge_vitals_pkt_t *pkt);
* Get multi-person vitals array.
*
* @param persons Output array (must be EDGE_MAX_PERSONS elements).
* @param n_active Output: number of active persons.
*/
void edge_get_multi_person(edge_person_vitals_t *persons, uint8_t *n_active);
* Get pointer to the phase history ring buffer and its state.
* Used by WASM runtime (ADR-040) to expose phase history to modules.
*
* @param out_buf Output: pointer to phase history array.
* @param out_len Output: number of valid entries.
* @param out_idx Output: current write index.
*/
void edge_get_phase_history(const float **out_buf, uint16_t *out_len,
uint16_t *out_idx);
* Get per-subcarrier Welford variance array.
* Used by WASM runtime (ADR-040) to expose variances to modules.
*
* @param out_variances Output array (must be EDGE_MAX_SUBCARRIERS elements).
* @param n_subcarriers Number of subcarriers to fill.
*/
void edge_get_variances(float *out_variances, uint16_t n_subcarriers);
#endif