|
|
|
@ -11,6 +11,15 @@ |
|
|
|
#define NUM_NOISE_FLOOR_SAMPLES 64 |
|
|
|
#define SAMPLING_THRESHOLD 14 |
|
|
|
|
|
|
|
// ambient CAD auto-calibration: probe CAD periodically while idle, and raise/lower detPeak
|
|
|
|
// so ambient activity (sub-decode-threshold distant traffic, interference) stops tripping LBT
|
|
|
|
#define CAD_PROBE_INTERVAL 4000 // millis between ambient CAD probes
|
|
|
|
#define CAD_PROBE_INTERVAL_QUIET 16000 // slower probing while offset is 0 and channel is clean
|
|
|
|
#define CAD_PROBE_WINDOW 16 // probes per evaluation window
|
|
|
|
#define CAD_PROBE_RAISE_HITS 5 // >= ~30% ambient detect rate -> desensitize
|
|
|
|
#define CAD_PROBE_LOWER_HITS 2 // <= ~12% ambient detect rate -> re-sensitize
|
|
|
|
#define CAD_PEAK_OFFSET_MAX 6 |
|
|
|
|
|
|
|
static volatile uint8_t state = STATE_IDLE; |
|
|
|
|
|
|
|
// this function is called when a complete packet
|
|
|
|
@ -37,6 +46,11 @@ void RadioLibWrapper::begin() { |
|
|
|
_noise_floor = 0; |
|
|
|
_threshold = 0; |
|
|
|
_cad_enabled = false; |
|
|
|
_cad_peak_offset = 0; |
|
|
|
_cad_probe_count = _cad_probe_hits = 0; |
|
|
|
_cad_last_count = _cad_last_hits = 0; |
|
|
|
_last_cad_probe = 0; |
|
|
|
_cad_probe_interval = CAD_PROBE_INTERVAL; |
|
|
|
|
|
|
|
// start average out some samples
|
|
|
|
_num_floor_samples = 0; |
|
|
|
@ -102,6 +116,49 @@ void RadioLibWrapper::loop() { |
|
|
|
|
|
|
|
MESH_DEBUG_PRINTLN("RadioLibWrapper: noise_floor = %d", (int)_noise_floor); |
|
|
|
} |
|
|
|
|
|
|
|
if (_cad_enabled && state == STATE_RX && getCADDetPeakBase() > 0 |
|
|
|
&& millis() - _last_cad_probe >= _cad_probe_interval) { |
|
|
|
_last_cad_probe = millis(); |
|
|
|
if (!isReceivingPacket()) { |
|
|
|
int16_t result = performChannelScan(); |
|
|
|
// CAD-done DIO interrupt sets STATE_INT_READY via setFlag() ISR. Clear it
|
|
|
|
// before restarting RX so recvRaw() doesn't try to read a non-existent packet.
|
|
|
|
state = STATE_IDLE; |
|
|
|
startRecv(); |
|
|
|
|
|
|
|
if (result == RADIOLIB_CHANNEL_FREE) { |
|
|
|
_cad_probe_count++; |
|
|
|
} else if (result == RADIOLIB_LORA_DETECTED || result == RADIOLIB_PREAMBLE_DETECTED) { |
|
|
|
_cad_probe_count++; |
|
|
|
_cad_probe_hits++; |
|
|
|
_cad_probe_interval = CAD_PROBE_INTERVAL; // activity -> resume fast probing
|
|
|
|
} |
|
|
|
// else: scan error, don't count the sample
|
|
|
|
|
|
|
|
if (_cad_probe_hits >= CAD_PROBE_RAISE_HITS) { // ambient rate too high, don't wait out the window
|
|
|
|
if (_cad_peak_offset < CAD_PEAK_OFFSET_MAX) { |
|
|
|
_cad_peak_offset++; |
|
|
|
MESH_DEBUG_PRINTLN("RadioLibWrapper: CAD ambient %d/%d, detPeak offset -> %d", |
|
|
|
(int)_cad_probe_hits, (int)_cad_probe_count, (int)_cad_peak_offset); |
|
|
|
} |
|
|
|
_cad_last_hits = _cad_probe_hits; _cad_last_count = _cad_probe_count; |
|
|
|
_cad_probe_count = _cad_probe_hits = 0; |
|
|
|
} else if (_cad_probe_count >= CAD_PROBE_WINDOW) { |
|
|
|
if (_cad_probe_hits <= CAD_PROBE_LOWER_HITS && _cad_peak_offset > 0) { |
|
|
|
// step down twice as fast when the window was completely clean
|
|
|
|
_cad_peak_offset -= (_cad_probe_hits == 0 && _cad_peak_offset >= 2) ? 2 : 1; |
|
|
|
MESH_DEBUG_PRINTLN("RadioLibWrapper: CAD ambient %d/%d, detPeak offset -> %d", |
|
|
|
(int)_cad_probe_hits, (int)_cad_probe_count, (int)_cad_peak_offset); |
|
|
|
} |
|
|
|
if (_cad_probe_hits == 0 && _cad_peak_offset == 0) { |
|
|
|
_cad_probe_interval = CAD_PROBE_INTERVAL_QUIET; // idle channel, probe less often
|
|
|
|
} |
|
|
|
_cad_last_hits = _cad_probe_hits; _cad_last_count = _cad_probe_count; |
|
|
|
_cad_probe_count = _cad_probe_hits = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void RadioLibWrapper::startRecv() { |
|
|
|
@ -180,6 +237,21 @@ void RadioLibWrapper::onSendFinished() { |
|
|
|
} |
|
|
|
|
|
|
|
int16_t RadioLibWrapper::performChannelScan() { |
|
|
|
uint8_t peak_base = getCADDetPeakBase(); |
|
|
|
if (_cad_peak_offset > 0 && peak_base > 0) { |
|
|
|
ChannelScanConfig_t cfg = { |
|
|
|
.cad = { |
|
|
|
.symNum = 0xFF, // 0xFF -> radio-specific default (RADIOLIB_*_CAD_PARAM_DEFAULT)
|
|
|
|
.detPeak = (uint8_t)(peak_base + _cad_peak_offset), |
|
|
|
.detMin = 0xFF, |
|
|
|
.exitMode = 0xFF, |
|
|
|
.timeout = 0, |
|
|
|
.irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS, |
|
|
|
.irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK, |
|
|
|
}, |
|
|
|
}; |
|
|
|
return _radio->scanChannel(cfg); |
|
|
|
} |
|
|
|
return _radio->scanChannel(); |
|
|
|
} |
|
|
|
|
|
|
|
|