Scale CAD scan timeouts to radio settings

This commit is contained in:
mikecarper
2026-07-24 01:13:14 -07:00
parent c1752f2db1
commit 455b3f97e0
5 changed files with 61 additions and 4 deletions
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <stdint.h>
namespace mesh {
static constexpr uint32_t CAD_SCAN_MIN_TIMEOUT_MS = 100UL;
static constexpr uint32_t CAD_SCAN_MAX_TIMEOUT_MS = 3500UL;
static constexpr uint32_t CAD_SCAN_FALLBACK_TIMEOUT_MS = 500UL;
// RadioLib's SX126x CAD default examines four LoRa symbols. Allow six symbols
// plus fixed command/TCXO overhead, then clamp the result so normal profiles
// fail quickly without rejecting deliberately slow custom radio settings.
inline uint32_t calculateCadScanTimeoutMillis(uint8_t sf, float bandwidth_khz) {
if (sf < 5 || sf > 12 || bandwidth_khz <= 0.0f) {
return CAD_SCAN_FALLBACK_TIMEOUT_MS;
}
const float symbol_ms = static_cast<float>(1UL << sf) / bandwidth_khz;
const float padded_ms = symbol_ms * 6.0f + 20.0f;
uint32_t timeout_ms = static_cast<uint32_t>(padded_ms + 0.999f);
if (timeout_ms < CAD_SCAN_MIN_TIMEOUT_MS) return CAD_SCAN_MIN_TIMEOUT_MS;
if (timeout_ms > CAD_SCAN_MAX_TIMEOUT_MS) return CAD_SCAN_MAX_TIMEOUT_MS;
return timeout_ms;
}
} // namespace mesh
+1 -1
View File
@@ -42,7 +42,7 @@ public:
protected:
int16_t performChannelScan() override {
return ((CustomSX1276 *)_radio)->tryScanChannel(CAD_SCAN_TIMEOUT_MS, *_board);
return ((CustomSX1276 *)_radio)->tryScanChannel(cadScanTimeoutMillis(), *_board);
}
bool radioDeepInit() override {
+3 -2
View File
@@ -625,9 +625,10 @@ int16_t RadioLibWrapper::performChannelScan() {
int16_t result = _radio->startChannelScan();
if (result != RADIOLIB_ERR_NONE) return result;
const unsigned long timeout_ms = cadScanTimeoutMillis();
const unsigned long started = millis();
unsigned long last_watchdog_service = started;
while (millis() - started < CAD_SCAN_TIMEOUT_MS) {
while (millis() - started < timeout_ms) {
result = _radio->getChannelScanResult();
if (result != RADIOLIB_ERR_UNKNOWN) return result;
@@ -639,7 +640,7 @@ int16_t RadioLibWrapper::performChannelScan() {
yield();
}
MESH_DEBUG_PRINTLN("RadioLibWrapper: CAD scan timed out");
MESH_DEBUG_PRINTLN("RadioLibWrapper: CAD scan timed out after %lu ms", timeout_ms);
return RADIOLIB_ERR_RX_TIMEOUT;
}
+6 -1
View File
@@ -2,6 +2,7 @@
#include <Mesh.h>
#include <RadioLib.h>
#include "CadTiming.h"
// Fallback RX powersaving timings, only used until setRxPowerSaving() is called
// (begin() always applies the persisted values). The authoritative defaults live
@@ -12,7 +13,6 @@
class RadioLibWrapper : public mesh::Radio {
protected:
static constexpr unsigned long CAD_SCAN_TIMEOUT_MS = 10000UL;
PhysicalLayer* _radio;
mesh::MainBoard* _board;
uint32_t n_recv, n_sent, n_recv_errors;
@@ -72,6 +72,11 @@ protected:
void cacheParams(float freq, float bw, uint8_t sf, uint8_t cr) {
_cur_freq = freq; _cur_bw = bw; _cur_sf = sf; _cur_cr = cr; _params_valid = true;
}
unsigned long cadScanTimeoutMillis() const {
const uint8_t sf = _params_valid ? _cur_sf : getSpreadingFactor();
const float bw = _params_valid ? _cur_bw : static_cast<float>(LORA_BW);
return mesh::calculateCadScanTimeoutMillis(sf, bw);
}
virtual int startReceiveMode();
virtual void stopReceiveDutyCycle();
virtual bool isPacketReady();
+23
View File
@@ -0,0 +1,23 @@
#include <gtest/gtest.h>
#include <helpers/radiolib/CadTiming.h>
TEST(CadTiming, UsesShortDeadlineForCascadeProfile) {
EXPECT_EQ(mesh::calculateCadScanTimeoutMillis(7, 62.5f), 100UL);
}
TEST(CadTiming, ScalesForSlowRadioSettings) {
EXPECT_EQ(mesh::calculateCadScanTimeoutMillis(12, 62.5f), 414UL);
EXPECT_EQ(mesh::calculateCadScanTimeoutMillis(12, 7.8f), 3171UL);
}
TEST(CadTiming, BoundsInvalidAndExtremeInputs) {
EXPECT_EQ(mesh::calculateCadScanTimeoutMillis(0, 62.5f), 500UL);
EXPECT_EQ(mesh::calculateCadScanTimeoutMillis(7, 0.0f), 500UL);
EXPECT_EQ(mesh::calculateCadScanTimeoutMillis(12, 1.0f), 3500UL);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}