mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-02 04:38:27 +00:00
tweak noise floor reading
This commit is contained in:
@@ -530,17 +530,26 @@ void LoRaRadioBase::triggerNoiseFloorCalibrate(int threshold)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Take multiple RSSI reads and use the minimum. The noise floor is
|
||||
* the lowest ambient energy — any higher sample contains signal or
|
||||
* interference. Min of N reads (~200 us) naturally rejects
|
||||
* interference-contaminated samples. */
|
||||
int16_t rssi = hwGetCurrentRSSI();
|
||||
for (int i = 1; i < NOISE_FLOOR_SAMPLES_PER_TICK; i++) {
|
||||
int16_t s = hwGetCurrentRSSI();
|
||||
if (s < rssi) {
|
||||
rssi = s;
|
||||
}
|
||||
/* Median of multiple RSSI reads (~200 us). Rejects up to N/2-1
|
||||
* outliers in either direction without the downward bias of min
|
||||
* or the spike sensitivity of average. Insertion sort is fine
|
||||
* for N=8 (28 comparisons worst case, all in registers). */
|
||||
int16_t samples[NOISE_FLOOR_SAMPLES_PER_TICK];
|
||||
for (int i = 0; i < NOISE_FLOOR_SAMPLES_PER_TICK; i++) {
|
||||
samples[i] = hwGetCurrentRSSI();
|
||||
}
|
||||
/* Insertion sort — tiny array, branch-friendly on Cortex-M */
|
||||
for (int i = 1; i < NOISE_FLOOR_SAMPLES_PER_TICK; i++) {
|
||||
int16_t key = samples[i];
|
||||
int j = i - 1;
|
||||
while (j >= 0 && samples[j] > key) {
|
||||
samples[j + 1] = samples[j];
|
||||
j--;
|
||||
}
|
||||
samples[j + 1] = key;
|
||||
}
|
||||
int16_t rssi = (samples[NOISE_FLOOR_SAMPLES_PER_TICK / 2 - 1] +
|
||||
samples[NOISE_FLOOR_SAMPLES_PER_TICK / 2]) / 2;
|
||||
|
||||
/* First sample after reset (DEFAULT_NOISE_FLOOR == 0): seed directly. */
|
||||
if (_noise_floor == DEFAULT_NOISE_FLOOR) {
|
||||
@@ -555,16 +564,17 @@ void LoRaRadioBase::triggerNoiseFloorCalibrate(int threshold)
|
||||
/* Threshold filter with warmup and periodic bypass.
|
||||
*
|
||||
* _ema_unguarded counts up from 0 on every tick.
|
||||
* Ticks 0..N-1 (warmup): all samples accepted for fast convergence
|
||||
* Ticks 0..W-1 (warmup): all samples accepted for fast convergence
|
||||
* after seed/reset — prevents a bad seed from locking out the
|
||||
* real noise floor via a too-tight threshold.
|
||||
* Ticks N+: threshold filter active. Every Nth tick (when the low
|
||||
* bits are zero) one sample bypasses the filter so the floor can
|
||||
* track sustained upward shifts (new interference, antenna change).
|
||||
* Ticks W+: threshold filter active. Every Pth tick one sample
|
||||
* bypasses the filter so the floor can track sustained upward
|
||||
* shifts (new interference, antenna change).
|
||||
* The EMA's 1/8 weight naturally dampens isolated spikes. */
|
||||
const int N = (1 << NOISE_FLOOR_EMA_SHIFT); /* 8 */
|
||||
bool warmup = (_ema_unguarded < N);
|
||||
bool periodic = (!warmup && (_ema_unguarded & (N - 1)) == 0);
|
||||
const int W = (1 << NOISE_FLOOR_EMA_SHIFT); /* 8 — warmup ticks */
|
||||
const int P = NOISE_FLOOR_UNGUARDED_INTERVAL; /* 16 — periodic interval */
|
||||
bool warmup = (_ema_unguarded < W);
|
||||
bool periodic = (!warmup && (_ema_unguarded & (P - 1)) == 0);
|
||||
_ema_unguarded++; /* wraps at 255 — harmless */
|
||||
|
||||
if (!warmup && !periodic &&
|
||||
@@ -572,14 +582,14 @@ void LoRaRadioBase::triggerNoiseFloorCalibrate(int threshold)
|
||||
return;
|
||||
}
|
||||
|
||||
/* EMA: floor += round_nearest((sample - floor) / N).
|
||||
/* EMA: floor += round_nearest((sample - floor) / W).
|
||||
* Plain >> has downward bias (-1>>3 == -1 but +1>>3 == 0).
|
||||
* Plain / has a ±7 dead zone (small drifts ignored).
|
||||
* Round-to-nearest: add half the divisor before dividing,
|
||||
* with sign-aware bias so both directions are symmetric. */
|
||||
int diff = rssi - _noise_floor;
|
||||
int half = N / 2; /* 4 */
|
||||
int step = (diff + (diff > 0 ? half : -half)) / N;
|
||||
int half = W / 2; /* 4 */
|
||||
int step = (diff + (diff > 0 ? half : -half)) / W;
|
||||
_noise_floor += step;
|
||||
if (_noise_floor < -120) _noise_floor = -120;
|
||||
if (_noise_floor > -50) _noise_floor = -50;
|
||||
|
||||
@@ -12,14 +12,15 @@
|
||||
#include <zephyr/drivers/lora.h>
|
||||
|
||||
/* --- Noise floor calibration (EMA) ---
|
||||
* Takes SAMPLES_PER_TICK RSSI reads (~100 us), feeds the minimum into an
|
||||
* exponential moving average. Using min naturally rejects interference
|
||||
* spikes — the noise floor is the lowest ambient energy in the band.
|
||||
* Median of SAMPLES_PER_TICK RSSI reads (~200 us), fed into an
|
||||
* exponential moving average. Median rejects up to N/2-1 outliers
|
||||
* without the downward bias of min or spike sensitivity of average.
|
||||
* alpha = 1/8: new_floor = floor + round((sample - floor) / 8)
|
||||
* Convergence: ~8 ticks (~40s at 5s housekeeping) to track a step change.
|
||||
* Samples above floor + SAMPLING_THRESHOLD are rejected (interference). */
|
||||
#define NOISE_FLOOR_EMA_SHIFT 3 /* alpha = 1 / (1 << 3) = 1/8 */
|
||||
#define NOISE_FLOOR_SAMPLES_PER_TICK 4 /* min of 4 RSSI reads per tick */
|
||||
#define NOISE_FLOOR_SAMPLES_PER_TICK 8 /* median of 8 RSSI reads per tick */
|
||||
#define NOISE_FLOOR_UNGUARDED_INTERVAL 16 /* ticks between unfiltered samples (must be power of 2) */
|
||||
#define NOISE_FLOOR_SAMPLING_THRESHOLD 14 /* only sample if rssi < floor + threshold */
|
||||
#define DEFAULT_NOISE_FLOOR 0 /* accept all samples until first update */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user