diff --git a/src/helpers/radiolib/CustomLR1110.h b/src/helpers/radiolib/CustomLR1110.h index e4f9d06f..c1e6fb52 100644 --- a/src/helpers/radiolib/CustomLR1110.h +++ b/src/helpers/radiolib/CustomLR1110.h @@ -27,13 +27,26 @@ class CustomLR1110 : public LR1110 { int16_t startReceiveDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod, RadioLibIrqFlags_t irqFlags = RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RadioLibIrqFlags_t irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK) { - // RadioLib's LR11x0 duty-cycle path stages RX but does not call - // launchMode(), where the software RF switch is normally set to RX. + uint32_t symbolPeriod = (uint32_t)(((1000.0f * (float)(1UL << this->spreadingFactor)) / + this->bandwidthKhz) + 0.999f); uint32_t transitionTime = this->tcxoDelay + 1000; - sleepPeriod -= transitionTime; + if (sleepPeriod <= transitionTime) { + return RADIOLIB_ERR_INVALID_SLEEP_PERIOD; + } + uint32_t programmedSleepPeriod = sleepPeriod - transitionTime; - uint32_t rxPeriodRaw = (rxPeriod * 32768UL) / 1000000UL; - uint32_t sleepPeriodRaw = (sleepPeriod * 32768UL) / 1000000UL; + // PreambleDetected restarts the timeout at 2*rx + sleep. LR1110 testing + // established 78 ms RX / 26.851 ms sleep as the production minimum at + // SF8, BW 62.5 kHz. Preamble + 11 symbols plus 1 ms preserves that margin. + uint64_t requiredExtendedPeriod = + ((uint64_t)this->preambleLengthLoRa + 11ULL) * symbolPeriod + 1000ULL; + uint64_t extendedPeriod = 2ULL * rxPeriod + programmedSleepPeriod; + if (extendedPeriod < requiredExtendedPeriod) { + rxPeriod = (uint32_t)((requiredExtendedPeriod - programmedSleepPeriod + 1ULL) / 2ULL); + } + + uint32_t rxPeriodRaw = (uint32_t)(((uint64_t)rxPeriod * 32768UL) / 1000000UL); + uint32_t sleepPeriodRaw = (uint32_t)(((uint64_t)programmedSleepPeriod * 32768UL) / 1000000UL); if ((rxPeriodRaw & 0xFF000000) || (rxPeriodRaw == 0)) { return RADIOLIB_ERR_INVALID_RX_PERIOD; @@ -43,6 +56,13 @@ class CustomLR1110 : public LR1110 { return RADIOLIB_ERR_INVALID_SLEEP_PERIOD; } + // Semtech requires Standby RC and an explicitly configured RTC source + // before SetRxDutyCycle. RadioLib does neither in its LoRa RXPS path. + int16_t state = standby(RADIOLIB_LR11X0_STANDBY_RC); + RADIOLIB_ASSERT(state); + state = configLfClock(RADIOLIB_LR11X0_LF_CLK_RC | RADIOLIB_LR11X0_LF_BUSY_RELEASE_ENABLED); + RADIOLIB_ASSERT(state); + RadioModeConfig_t cfg = { .receive = { .timeout = RADIOLIB_LR11X0_RX_TIMEOUT_INF, @@ -51,10 +71,11 @@ class CustomLR1110 : public LR1110 { .len = 0, } }; - int16_t state = this->stageMode(RADIOLIB_RADIO_MODE_RX, &cfg); + state = this->stageMode(RADIOLIB_RADIO_MODE_RX, &cfg); RADIOLIB_ASSERT(state); - this->mod->setRfSwitchState(Module::MODE_RX); + // Send the already converted values. RadioLib 7.7.1 converts them again + // with 32-bit arithmetic, which overflows for periods above about 131 ms. return this->setRxDutyCycle(rxPeriodRaw, sleepPeriodRaw, RADIOLIB_LR11X0_RX_DUTY_CYCLE_MODE_RX); } diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index 86c71086..07e2136e 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -163,7 +163,7 @@ void RadioLibWrapper::rxPsWatchdogCheck() { } } -// Periodic noise-floor calibration, active only with RX duty-cycle powersaving: +// Initial and periodic noise-floor calibration, active only with RX duty-cycle powersaving: // a duty-cycled receiver can't be sampled reliably (the frontend is off in the // sleep windows and settling right after each wake), so at least once a minute // the receive mode is dropped to plain continuous RX, a fresh sample batch is @@ -178,7 +178,7 @@ void RadioLibWrapper::noiseFloorCalibCheck() { endNoiseFloorCalib(now); } } else if (_rx_ps_enabled && _rx_ps_armed && state == STATE_RX - && now - _nf_last_calib >= NF_CALIB_INTERVAL_MS + && (_nf_last_calib == 0 || now - _nf_last_calib >= NF_CALIB_INTERVAL_MS) && !isReceivingPacket()) { // never interrupt an ongoing reception to calibrate (a TX in flight is // already excluded by state == STATE_RX); retries next loop iteration diff --git a/src/helpers/radiolib/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h index 54b82479..e4eaaa99 100644 --- a/src/helpers/radiolib/RadioLibWrappers.h +++ b/src/helpers/radiolib/RadioLibWrappers.h @@ -48,11 +48,10 @@ protected: int8_t _cur_dbm; bool _params_valid, _dbm_valid; - // Periodic noise-floor calibration (only while RX duty-cycle powersaving is - // armed): a duty-cycled receiver can't be sampled reliably, so at least once - // a minute the wrapper drops to plain continuous RX, collects a fresh sample - // batch exactly like the non-powersaving path does, publishes the average - // into _noise_floor and re-arms the duty cycle. + // Initial and periodic noise-floor calibration (only while RX duty-cycle + // powersaving is armed): a duty-cycled receiver can't be sampled reliably, + // so at least once a minute the wrapper drops to plain continuous RX, + // collects a fresh sample batch, publishes it and re-arms the duty cycle. bool _nf_calib_active; unsigned long _nf_last_calib; // millis of last completed/attempted window unsigned long _nf_calib_deadline; // abort window if the batch can't complete