ESPectre SDK 2.8.0-280-gac7af68
Wi-Fi CSI motion sensing for ESP32 firmware
Loading...
Searching...
No Matches
utils.h
Go to the documentation of this file.
1/*
2 * ESPectre - Utility Functions
3 *
4 * Shared statistical helpers (mean, median, variance, turbulence) used
5 * across multiple modules. CSI layout constants and payload helpers live
6 * in csi_format.h.
7 *
8 * Author: Francesco Pace <francesco.pace@gmail.com>
9 * SPDX-License-Identifier: GPL-3.0-only
10 * Commercial licensing available under separate agreement; see LICENSING.md.
11 */
12#pragma once
13
14#include <cstdint>
15#include <cmath>
16#include <algorithm>
17
18namespace espectre {
19
20// =============================================================================
21// Basic Statistical Functions
22// =============================================================================
23
24/**
25 * Calculate mean of an array
26 *
27 * @param values Array of float values
28 * @param n Number of values
29 * @return Mean (0.0 if n == 0)
30 */
31inline float calculate_mean(const float* values, size_t n) {
32 if (n == 0 || !values) return 0.0f;
33 float sum = 0.0f;
34 for (size_t i = 0; i < n; i++) {
35 sum += values[i];
36 }
37 return sum / n;
38}
39
40/**
41 * Calculate median of a float array (sorts array in-place)
42 *
43 * @param arr Array of float values (will be sorted)
44 * @param size Number of values
45 * @return Median value (0.0 if size == 0)
46 */
47inline float calculate_median_float(float* arr, size_t size) {
48 if (size == 0 || !arr) return 0.0f;
49 std::sort(arr, arr + size);
50 if (size % 2 == 0) {
51 return (arr[size / 2 - 1] + arr[size / 2]) / 2.0f;
52 }
53 return arr[size / 2];
54}
55
56/**
57 * Apply gain-invariant normalization to standard deviation
58 *
59 * CV (Coefficient of Variation) = std / mean
60 * Makes turbulence gain-invariant when AGC is not locked.
61 *
62 * @param std_dev Standard deviation
63 * @param mean Mean value
64 * @return Normalized turbulence
65 */
66inline float apply_cv_normalization(float std_dev, float mean) {
67 return (mean > 0.0f) ? std_dev / mean : 0.0f;
68}
69
70/**
71 * Calculate turbulence from variance with gain-invariant normalization
72 *
73 * Combines variance → std → normalization in one call.
74 *
75 * @param variance Pre-calculated variance
76 * @param values Array used for mean calculation
77 * @param count Number of values
78 * @return Turbulence value
79 */
80inline float calculate_turbulence_from_variance(float variance,
81 const float* values,
82 size_t count) {
83 float std_dev = std::sqrt(variance);
84 float mean = calculate_mean(values, count);
85 return apply_cv_normalization(std_dev, mean);
86}
87
89 float mean{0.0f};
90 float variance{0.0f};
91};
92
93/**
94 * Calculate mean and variance in one two-pass sweep (numerically stable)
95 *
96 * Two-pass algorithm: variance = sum((x - mean)^2) / n
97 * More stable than single-pass E[X²] - E[X]² for float32 arithmetic.
98 *
99 * The single definition matters beyond DRY. Both detectors feed these values
100 * into a threshold comparison, and the C++/Python parity gate requires the two
101 * runtimes to decide identically, so the accumulation order has to be fixed in
102 * exactly one place. Three hand-rolled copies of these loops used to exist.
103 *
104 * @param values Array of float values
105 * @param n Number of values
106 * @return Mean and variance (both 0.0 if n == 0)
107 */
108inline MeanVariance calculate_mean_variance_two_pass(const float *values, size_t n) {
109 MeanVariance result;
110 if (n == 0 || !values) {
111 return result;
112 }
113
114 // First pass: calculate mean
115 float mean = 0.0f;
116 for (size_t i = 0; i < n; i++) {
117 mean += values[i];
118 }
119 mean /= n;
120
121 // Second pass: calculate variance
122 float variance = 0.0f;
123 for (size_t i = 0; i < n; i++) {
124 float diff = values[i] - mean;
125 variance += diff * diff;
126 }
127 variance /= n;
128
129 result.mean = mean;
130 result.variance = variance;
131 return result;
132}
133
134/**
135 * Calculate variance using the two-pass algorithm
136 *
137 * @param values Array of float values
138 * @param n Number of values
139 * @return Variance (0.0 if n == 0)
140 */
141inline float calculate_variance_two_pass(const float *values, size_t n) {
143}
144
145/**
146 * Calculate magnitude (amplitude) from I/Q components
147 *
148 * @param i In-phase component
149 * @param q Quadrature component
150 * @return Magnitude = sqrt(I² + Q²)
151 */
152inline float calculate_magnitude(int8_t i, int8_t q) {
153 float fi = static_cast<float>(i);
154 float fq = static_cast<float>(q);
155 return std::sqrt(fi * fi + fq * fq);
156}
157
158/**
159 * Write the mean-normalized amplitude profile into `out`
160 *
161 * Shared numeric core for the C++ L1-delta tracker. The MicroPython tracker
162 * performs the same normalization directly in its packet loop.
163 *
164 * @param amplitudes Input amplitude values
165 * @param count Number of input values
166 * @param mean Precomputed arithmetic mean of the input values
167 * @param out Output buffer (at least `count` elements)
168 * @return Number of values written (0 when the profile is invalid)
169 */
170inline uint8_t normalize_amplitude_profile(const float* amplitudes,
171 uint8_t count,
172 float mean,
173 float* out) {
174 if (!amplitudes || !out || count < 2) {
175 return 0;
176 }
177 if (mean <= 0.0f) {
178 return 0;
179 }
180 for (uint8_t i = 0; i < count; i++) {
181 out[i] = amplitudes[i] / mean;
182 }
183 return count;
184}
185
186inline uint8_t normalize_amplitude_profile(const float* amplitudes,
187 uint8_t count,
188 float* out) {
189 if (!amplitudes || !out || count < 2) {
190 return 0;
191 }
193 amplitudes, count, calculate_mean(amplitudes, count), out);
194}
195
196} // namespace espectre
float calculate_median_float(float *arr, size_t size)
Calculate median of a float array (sorts array in-place).
Definition utils.h:47
MeanVariance calculate_mean_variance_two_pass(const float *values, size_t n)
Calculate mean and variance in one two-pass sweep (numerically stable).
Definition utils.h:108
float calculate_mean(const float *values, size_t n)
Calculate mean of an array.
Definition utils.h:31
float calculate_variance_two_pass(const float *values, size_t n)
Calculate variance using the two-pass algorithm.
Definition utils.h:141
float calculate_magnitude(int8_t i, int8_t q)
Calculate magnitude (amplitude) from I/Q components.
Definition utils.h:152
uint8_t normalize_amplitude_profile(const float *amplitudes, uint8_t count, float mean, float *out)
Write the mean-normalized amplitude profile into out.
Definition utils.h:170
float calculate_turbulence_from_variance(float variance, const float *values, size_t count)
Calculate turbulence from variance with gain-invariant normalization.
Definition utils.h:80
float apply_cv_normalization(float std_dev, float mean)
Apply gain-invariant normalization to standard deviation.
Definition utils.h:66