55 uint8_t populated = 0;
56 for (uint8_t i = 0; i < count; ++i) {
57 const uint16_t byte_index =
static_cast<uint16_t
>(bins[i]) * 2U;
58 if (csi_data[byte_index] != 0 || csi_data[byte_index + 1] != 0) {
81 constexpr uint8_t kNullBinCount =
83 const uint8_t classic_energy =
85 const uint8_t centered_energy =
88 if (classic_energy == 0U && centered_energy == kNullBinCount) {
91 if (centered_energy == 0U && classic_energy == kNullBinCount) {
107 if (csi_data ==
nullptr || out ==
nullptr) {
111 std::memcpy(out, csi_data + kHalf, kHalf);
112 std::memcpy(out + kHalf, csi_data, kHalf);
129 const uint8_t* subcarriers,
130 uint8_t num_subcarriers,
131 uint16_t max_subcarrier = 64) {
132 if (num_subcarriers == 0 || !magnitudes || !subcarriers) {
137 float valid_mags[12];
138 uint8_t valid_count = 0;
140 for (uint8_t i = 0; i < num_subcarriers && valid_count < 12; i++) {
141 if (subcarriers[i] < max_subcarrier) {
142 valid_mags[valid_count++] = magnitudes[subcarriers[i]];
146 if (valid_count == 0) {
169 const uint8_t* subcarriers,
170 uint8_t num_subcarriers,
172 uint8_t out_capacity) {
173 if (!csi_data || csi_len < 2 || num_subcarriers == 0 || !subcarriers || !out) {
177 int total_subcarriers =
static_cast<int>(csi_len / 2);
178 uint8_t valid_count = 0;
180 for (
int i = 0; i < num_subcarriers && valid_count < out_capacity; i++) {
181 int sc_idx = subcarriers[i];
182 if (sc_idx >= total_subcarriers) {
188 csi_data[sc_idx * 2]);
197 uint8_t out_capacity) {
198 if (csi_data ==
nullptr || out ==
nullptr)
return 0U;
199 const uint8_t count =
static_cast<uint8_t
>(std::min<size_t>(
201 for (uint8_t i = 0U; i < count; ++i) {
211 uint8_t out_capacity) {
212 if (csi_data ==
nullptr || out ==
nullptr)
return 0U;
213 const uint8_t count =
static_cast<uint8_t
>(std::min<size_t>(
215 for (uint8_t i = 0U; i < count; ++i) {
216 const float imag =
static_cast<float>(csi_data[i * 2U]);
217 const float real =
static_cast<float>(csi_data[i * 2U + 1U]);
218 out[i] = real * real + imag * imag;
224 if (values ==
nullptr)
return;
225 for (uint8_t i = 0U; i < count; ++i) values[i] = std::sqrt(values[i]);
230 uint8_t packet_count,
231 const uint8_t* subcarriers,
232 uint8_t num_subcarriers,
234 uint8_t out_capacity) {
235 if (packet_amplitudes ==
nullptr || subcarriers ==
nullptr || out ==
nullptr)
return 0U;
236 uint8_t written = 0U;
237 for (uint8_t i = 0U; i < num_subcarriers && written < out_capacity; ++i) {
238 if (subcarriers[i] < packet_count) out[written++] = packet_amplitudes[subcarriers[i]];
245 const float* packet_amplitudes, uint8_t packet_count,
246 const uint8_t* subcarriers, uint8_t num_subcarriers, uint8_t width,
247 float* out, uint8_t out_capacity) {
248 if (packet_amplitudes ==
nullptr || subcarriers ==
nullptr || width == 0U || out ==
nullptr)
return 0U;
249 const int half =
static_cast<int>((width - 1U) / 2U);
250 uint8_t written = 0U;
251 for (uint8_t i = 0U; i < num_subcarriers && written < out_capacity; ++i) {
252 int low =
static_cast<int>(subcarriers[i]) - half;
253 int high =
static_cast<int>(subcarriers[i]) +
static_cast<int>(width - 1U) - half;
264 for (
int bin = low; bin <= high; ++bin) {
266 total += packet_amplitudes[bin];
269 if (count > 0U) out[written++] = total /
static_cast<float>(count);
282 const int8_t* csi_data,
284 const uint8_t* subcarriers,
285 uint8_t num_subcarriers,
288 uint8_t out_capacity) {
289 if (csi_data ==
nullptr || csi_len < 2U || subcarriers ==
nullptr ||
290 num_subcarriers == 0U || width == 0U || out ==
nullptr) {
294 const int total_subcarriers =
static_cast<int>(csi_len / 2U);
295 const int half =
static_cast<int>((width - 1U) / 2U);
296 uint8_t written = 0U;
297 for (uint8_t i = 0U; i < num_subcarriers && written < out_capacity; ++i) {
298 int low =
static_cast<int>(subcarriers[i]) - half;
299 int high =
static_cast<int>(subcarriers[i]) +
300 static_cast<int>(width - 1U) - half;
310 float magnitude_sum = 0.0f;
312 for (
int bin = low; bin <= high; ++bin) {
314 bin >= total_subcarriers) {
318 csi_data[bin * 2 + 1], csi_data[bin * 2]);
322 out[written++] = magnitude_sum /
static_cast<float>(count);
330 if (amplitudes ==
nullptr || count == 0) {
353 const uint8_t* subcarriers,
354 uint8_t num_subcarriers) {
357 csi_data, csi_len, subcarriers, num_subcarriers,
uint8_t select_adjacent_aggregated_subcarrier_amplitudes(const float *packet_amplitudes, uint8_t packet_count, const uint8_t *subcarriers, uint8_t num_subcarriers, uint8_t width, float *out, uint8_t out_capacity)
Select adjacent-bin mean amplitudes from a packet-wide amplitude frame.
constexpr uint16_t HT20_NUM_SUBCARRIERS
uint8_t ht20_bins_with_energy(const int8_t *csi_data, const uint8_t *bins, uint8_t count)
float calculate_spatial_turbulence(const float *magnitudes, const uint8_t *subcarriers, uint8_t num_subcarriers, uint16_t max_subcarrier=64)
Calculate spatial turbulence from pre-calculated magnitudes.
float calculate_spatial_turbulence_from_csi(const int8_t *csi_data, size_t csi_len, const uint8_t *subcarriers, uint8_t num_subcarriers)
Calculate spatial turbulence directly from raw CSI data (I/Q pairs).
MeanVariance calculate_mean_variance_two_pass(const float *values, size_t n)
Calculate mean and variance in one two-pass sweep (numerically stable).
constexpr uint8_t HT20_CENTERED_ONLY_NULL_BINS[]
float calculate_spatial_turbulence_from_amplitudes(const float *amplitudes, uint8_t count)
uint8_t fill_packet_subcarrier_energies(const int8_t *csi_data, size_t csi_len, float *out, uint8_t out_capacity)
Fill one packet-wide squared-magnitude frame for energy-domain consumers.
constexpr uint8_t HT20_SELECTED_BAND_SIZE
constexpr uint8_t HT20_CLASSIC_ONLY_NULL_BINS[]
uint8_t select_subcarrier_amplitudes(const float *packet_amplitudes, uint8_t packet_count, const uint8_t *subcarriers, uint8_t num_subcarriers, float *out, uint8_t out_capacity)
Select the configured tones from a packet-wide amplitude frame.
void rotate_ht20_classic_to_centered(const int8_t *csi_data, int8_t *out)
Rotate a classic-order HT20 payload into the centered convention.
void energies_to_amplitudes_in_place(float *values, uint8_t count)
float calculate_magnitude(int8_t i, int8_t q)
Calculate magnitude (amplitude) from I/Q components.
uint8_t extract_adjacent_aggregated_subcarrier_amplitudes(const int8_t *csi_data, size_t csi_len, const uint8_t *subcarriers, uint8_t num_subcarriers, uint8_t width, float *out, uint8_t out_capacity)
Extract one mean magnitude per selected tone from adjacent live HT20 bins.
constexpr uint8_t HT20_GUARD_BAND_HIGH
uint8_t extract_packet_subcarrier_amplitudes(const int8_t *csi_data, size_t csi_len, float *out, uint8_t out_capacity)
Extract one packet-wide amplitude frame for reuse by multiple feature paths.
constexpr uint8_t HT20_GUARD_BAND_LOW
constexpr uint16_t HT20_CSI_LEN
uint8_t extract_subcarrier_amplitudes(const int8_t *csi_data, size_t csi_len, const uint8_t *subcarriers, uint8_t num_subcarriers, float *out, uint8_t out_capacity)
Extract subcarrier amplitudes from raw CSI data (I/Q pairs).
float apply_cv_normalization(float std_dev, float mean)
Apply gain-invariant normalization to standard deviation.
constexpr uint8_t HT20_DC_SUBCARRIER
Ht20BinLayout detect_ht20_bin_layout(const int8_t *csi_data, size_t csi_len)
Identify which HT20 bin ordering a 64-subcarrier payload uses.