ESPectre SDK
2.8.0-280-gac7af68
Wi-Fi CSI motion sensing for ESP32 firmware
Toggle main menu visibility
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
18
namespace
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
*/
31
inline
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
*/
47
inline
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
*/
66
inline
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
*/
80
inline
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
88
struct
MeanVariance
{
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
*/
108
inline
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
*/
141
inline
float
calculate_variance_two_pass
(
const
float
*values,
size_t
n) {
142
return
calculate_mean_variance_two_pass
(values, n).
variance
;
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
*/
152
inline
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
*/
170
inline
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
186
inline
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
}
192
return
normalize_amplitude_profile
(
193
amplitudes, count,
calculate_mean
(amplitudes, count), out);
194
}
195
196
}
// namespace espectre
espectre
Definition
espectre_sdk_version.h:62
espectre::calculate_median_float
float calculate_median_float(float *arr, size_t size)
Calculate median of a float array (sorts array in-place).
Definition
utils.h:47
espectre::calculate_mean_variance_two_pass
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
espectre::calculate_mean
float calculate_mean(const float *values, size_t n)
Calculate mean of an array.
Definition
utils.h:31
espectre::calculate_variance_two_pass
float calculate_variance_two_pass(const float *values, size_t n)
Calculate variance using the two-pass algorithm.
Definition
utils.h:141
espectre::calculate_magnitude
float calculate_magnitude(int8_t i, int8_t q)
Calculate magnitude (amplitude) from I/Q components.
Definition
utils.h:152
espectre::normalize_amplitude_profile
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
espectre::calculate_turbulence_from_variance
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
espectre::apply_cv_normalization
float apply_cv_normalization(float std_dev, float mean)
Apply gain-invariant normalization to standard deviation.
Definition
utils.h:66
espectre::MeanVariance
Definition
utils.h:88
espectre::MeanVariance::variance
float variance
Definition
utils.h:90
espectre::MeanVariance::mean
float mean
Definition
utils.h:89
src
cpp
core
utils.h
Generated by
1.17.0