Fixed RX duty-cycle rearming and profile timings
This commit is contained in:
1 parent
e97f0fecc6
commit
38034d2ee2
8 files changed
+122
-25
No files matched your search
@@ -744,12 +744,16 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
enable = 0;
|
||||
} else if (strcmp(value, "on") == 0 || strcmp(value, "conservative") == 0) {
|
||||
enable = 1;
|
||||
rx_us = RX_POWERSAVING_DEFAULT_RX_US;
|
||||
sleep_us = RX_POWERSAVING_DEFAULT_SLEEP_US;
|
||||
level = RX_POWERSAVING_CONSERVATIVE_LEVEL;
|
||||
preamble = RX_POWERSAVING_PROFILE_PREAMBLE;
|
||||
level_requested = true;
|
||||
preamble_overridden = true;
|
||||
} else if (strcmp(value, "balanced") == 0) {
|
||||
enable = 1;
|
||||
rx_us = RX_POWERSAVING_DEFAULT_RX_US;
|
||||
sleep_us = RX_POWERSAVING_BALANCED_SLEEP_US;
|
||||
level = RX_POWERSAVING_BALANCED_LEVEL;
|
||||
preamble = RX_POWERSAVING_PROFILE_PREAMBLE;
|
||||
level_requested = true;
|
||||
preamble_overridden = true;
|
||||
} else {
|
||||
StrHelper::strncpy(tmp, value, sizeof(tmp));
|
||||
const char *parts[4];
|
||||
@@ -777,11 +781,11 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
strcpy(reply, "ERROR: use off|on|conservative|balanced|level <1-10>|<rx_us> <sleep_us>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (level_requested && !calcRxPowerSavingLevel(level, _prefs->sf, _prefs->bw, preamble, &rx_us, &sleep_us)) {
|
||||
strcpy(reply, "ERROR: level range is 1-10; preamble is 16 or 32");
|
||||
return;
|
||||
}
|
||||
if (level_requested && !calcRxPowerSavingLevel(level, _prefs->sf, _prefs->bw, preamble, &rx_us, &sleep_us)) {
|
||||
strcpy(reply, "ERROR: level range is 1-10; preamble is 16 or 32");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isValidRxPowerSavingPeriod(rx_us) || !isValidRxPowerSavingPeriod(sleep_us)) {
|
||||
@@ -804,7 +808,8 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
_prefs->rx_ps_level = level;
|
||||
_prefs->rx_ps_preamble = preamble_overridden ? preamble : 0; // 0 = auto (derive from SF)
|
||||
} else if (strcmp(value, "off") != 0) {
|
||||
// on/conservative/balanced/manual timings are fixed, not level-derived.
|
||||
// manual <rx_us> <sleep_us> timings are fixed, not level-derived
|
||||
// (the named profiles set level_requested and are handled above)
|
||||
_prefs->rx_ps_level = 0;
|
||||
_prefs->rx_ps_preamble = 0;
|
||||
}
|
||||
|
||||
@@ -21,10 +21,16 @@
|
||||
|
||||
#define RX_POWERSAVING_DEFAULT_RX_US 65625UL
|
||||
#define RX_POWERSAVING_DEFAULT_SLEEP_US 60000UL
|
||||
#define RX_POWERSAVING_BALANCED_SLEEP_US 80000UL
|
||||
#define RX_POWERSAVING_MIN_PERIOD_US 1000UL
|
||||
#define RX_POWERSAVING_MAX_PERIOD_US 30000000UL
|
||||
|
||||
// The named profiles are level presets pinned to a 16-symbol preamble: most
|
||||
// deployed senders still transmit 16-symbol preambles regardless of the newer
|
||||
// SF-based rule (32 for SF <= 8). Revisit once the field has largely migrated.
|
||||
#define RX_POWERSAVING_CONSERVATIVE_LEVEL 1UL
|
||||
#define RX_POWERSAVING_BALANCED_LEVEL 5UL
|
||||
#define RX_POWERSAVING_PROFILE_PREAMBLE 16UL
|
||||
|
||||
struct NodePrefs { // persisted to file
|
||||
float airtime_factor;
|
||||
char node_name[32];
|
||||
|
||||
@@ -66,6 +66,12 @@ class CustomLR1110 : public LR1110 {
|
||||
bool getRxBoostedGainMode() const { return _rx_boosted; }
|
||||
|
||||
bool isReceiving() {
|
||||
// BUSY high means the chip is asleep (RX duty-cycle sleep window) or mid
|
||||
// command, so it cannot be mid-receive - and the SPI read below would
|
||||
// stall until the chip's next listen window.
|
||||
uint32_t busy = this->mod->getGpio();
|
||||
if (busy != RADIOLIB_NC && this->mod->hal->digitalRead(busy)) return false;
|
||||
|
||||
uint16_t irq = getIrqStatus();
|
||||
bool detected = ((irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID) || (irq & RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED));
|
||||
return detected;
|
||||
|
||||
@@ -35,15 +35,27 @@ public:
|
||||
|
||||
protected:
|
||||
int startReceiveMode() override {
|
||||
if (_rx_ps_armed) {
|
||||
// stop the previous duty-cycle sequence with an explicit standby before
|
||||
// reconfiguring the radio
|
||||
stopReceiveDutyCycle();
|
||||
}
|
||||
if (!_rx_ps_enabled) {
|
||||
return _radio->startReceive();
|
||||
}
|
||||
|
||||
const RadioLibIrqFlags_t irqFlags = RADIOLIB_IRQ_RX_DEFAULT_FLAGS;
|
||||
const RadioLibIrqFlags_t irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK;
|
||||
// route RX timeout (false preamble detect) and error IRQs to the IRQ pin
|
||||
// as well, so the wrapper re-arms RX instead of waiting forever
|
||||
const RadioLibIrqFlags_t irqMask =
|
||||
(1UL << RADIOLIB_IRQ_RX_DONE) |
|
||||
(1UL << RADIOLIB_IRQ_TIMEOUT) |
|
||||
(1UL << RADIOLIB_IRQ_CRC_ERR) |
|
||||
(1UL << RADIOLIB_IRQ_HEADER_ERR);
|
||||
|
||||
int err = ((CustomLR1110 *)_radio)->startReceiveDutyCycle(_rx_ps_rx_us, _rx_ps_sleep_us, irqFlags, irqMask);
|
||||
if (err == RADIOLIB_ERR_NONE) {
|
||||
_rx_ps_armed = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,11 +87,35 @@ class CustomSX1262 : public SX1262 {
|
||||
}
|
||||
|
||||
bool isReceiving() {
|
||||
// BUSY high means the chip is asleep (RX duty-cycle sleep window) or mid
|
||||
// command, so it cannot be mid-receive - and the SPI read below would
|
||||
// stall until the chip's next listen window.
|
||||
uint32_t busy = this->mod->getGpio();
|
||||
if (busy != RADIOLIB_NC && this->mod->hal->digitalRead(busy)) return false;
|
||||
|
||||
uint16_t irq = getIrqFlags();
|
||||
bool detected = (irq & SX126X_IRQ_HEADER_VALID) || (irq & SX126X_IRQ_PREAMBLE_DETECTED);
|
||||
return detected;
|
||||
}
|
||||
|
||||
// Port of Semtech's sx126x_stop_rtc() (same registers as RadioLib's
|
||||
// fixImplicitTimeout / datasheet errata 15.3): after duty-cycle RX ends via
|
||||
// RxDone or SetStandby, the internal RTC keeps running and its pending
|
||||
// event can silently knock a subsequently started RX back to standby with
|
||||
// no IRQ, leaving the node deaf. Must be called before re-arming RX.
|
||||
int16_t stopRTC() {
|
||||
uint8_t rtcStop = 0x00;
|
||||
int16_t state = writeRegister(RADIOLIB_SX126X_REG_RTC_CTRL, &rtcStop, 1);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
uint8_t rtcEvent = 0;
|
||||
state = readRegister(RADIOLIB_SX126X_REG_EVENT_MASK, &rtcEvent, 1);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
rtcEvent |= 0x02; // clear the RX timeout event
|
||||
return writeRegister(RADIOLIB_SX126X_REG_EVENT_MASK, &rtcEvent, 1);
|
||||
}
|
||||
|
||||
bool getRxBoostedGainMode() {
|
||||
uint8_t rxGain = 0;
|
||||
readRegister(RADIOLIB_SX126X_REG_RX_GAIN, &rxGain, 1);
|
||||
|
||||
@@ -42,6 +42,12 @@ public:
|
||||
|
||||
protected:
|
||||
int startReceiveMode() override {
|
||||
if (_rx_ps_armed) {
|
||||
// leaving duty-cycle mode (after RxDone or a reconfig): stop the
|
||||
// sequencer and the still-running RTC, or its pending event can
|
||||
// silently abort the RX we are about to start
|
||||
stopReceiveDutyCycle();
|
||||
}
|
||||
if (!_rx_ps_enabled) {
|
||||
return _radio->startReceive();
|
||||
}
|
||||
@@ -55,6 +61,7 @@ protected:
|
||||
|
||||
int err = ((CustomSX1262 *)_radio)->startReceiveDutyCycle(_rx_ps_rx_us, _rx_ps_sleep_us, irqFlags, irqMask);
|
||||
if (err == RADIOLIB_ERR_NONE) {
|
||||
_rx_ps_armed = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -62,6 +69,12 @@ protected:
|
||||
return _radio->startReceive();
|
||||
}
|
||||
|
||||
void stopReceiveDutyCycle() override {
|
||||
_radio->standby(); // also wakes the chip if it is in the sleep window
|
||||
((CustomSX1262 *)_radio)->stopRTC();
|
||||
_rx_ps_armed = false;
|
||||
}
|
||||
|
||||
public:
|
||||
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
|
||||
|
||||
|
||||
@@ -121,6 +121,24 @@ int RadioLibWrapper::startReceiveMode() {
|
||||
return _radio->startReceive();
|
||||
}
|
||||
|
||||
void RadioLibWrapper::stopReceiveDutyCycle() {
|
||||
// The duty-cycle sequencer only stops on RxDone or an explicit standby;
|
||||
// issuing other mode commands while it runs leads to undefined behaviour.
|
||||
_radio->standby();
|
||||
_rx_ps_armed = false;
|
||||
}
|
||||
|
||||
bool RadioLibWrapper::isPacketReady() {
|
||||
if (!_rx_ps_armed) return true; // continuous RX: DIO1 only fires for RxDone/TxDone here
|
||||
|
||||
// In duty-cycle RX the DIO1 interrupt also fires for RX timeout (false
|
||||
// preamble detect) and header errors. GetRxBufferStatus still reports the
|
||||
// *previous* packet's length then, so reading the buffer would re-deliver
|
||||
// stale bytes as a ghost packet. Only read when the radio reports RxDone.
|
||||
// (checkIrq errors are treated as ready, falling back to old behaviour.)
|
||||
return _radio->checkIrq(RADIOLIB_IRQ_RX_DONE) != 0;
|
||||
}
|
||||
|
||||
bool RadioLibWrapper::isInRecvMode() const {
|
||||
return (state & ~STATE_INT_READY) == STATE_RX;
|
||||
}
|
||||
@@ -146,17 +164,19 @@ bool RadioLibWrapper::setRxPowerSaving(bool enabled, uint32_t rx_us, uint32_t sl
|
||||
int RadioLibWrapper::recvRaw(uint8_t* bytes, int sz) {
|
||||
int len = 0;
|
||||
if (state & STATE_INT_READY) {
|
||||
len = _radio->getPacketLength();
|
||||
if (len > 0) {
|
||||
if (len > sz) { len = sz; }
|
||||
int err = _radio->readData(bytes, len);
|
||||
if (err != RADIOLIB_ERR_NONE) {
|
||||
MESH_DEBUG_PRINTLN("RadioLibWrapper: error: readData(%d)", err);
|
||||
len = 0;
|
||||
n_recv_errors++;
|
||||
} else {
|
||||
// Serial.print(" readData() -> "); Serial.println(len);
|
||||
n_recv++;
|
||||
if (isPacketReady()) {
|
||||
len = _radio->getPacketLength();
|
||||
if (len > 0) {
|
||||
if (len > sz) { len = sz; }
|
||||
int err = _radio->readData(bytes, len);
|
||||
if (err != RADIOLIB_ERR_NONE) {
|
||||
MESH_DEBUG_PRINTLN("RadioLibWrapper: error: readData(%d)", err);
|
||||
len = 0;
|
||||
n_recv_errors++;
|
||||
} else {
|
||||
// Serial.print(" readData() -> "); Serial.println(len);
|
||||
n_recv++;
|
||||
}
|
||||
}
|
||||
}
|
||||
state = STATE_IDLE; // need another startReceive()
|
||||
@@ -173,6 +193,11 @@ uint32_t RadioLibWrapper::getEstAirtimeFor(int len_bytes) {
|
||||
}
|
||||
|
||||
bool RadioLibWrapper::startSendRaw(const uint8_t* bytes, int len) {
|
||||
if (_rx_ps_armed) {
|
||||
// stop the duty-cycle sequencer before SetTx, otherwise its next RTC
|
||||
// event can fire mid-transmission and abort the TX
|
||||
stopReceiveDutyCycle();
|
||||
}
|
||||
_board->onBeforeTransmit();
|
||||
int err = _radio->startTransmit((uint8_t *) bytes, len);
|
||||
if (err == RADIOLIB_ERR_NONE) {
|
||||
@@ -205,8 +230,11 @@ int16_t RadioLibWrapper::performChannelScan() {
|
||||
}
|
||||
|
||||
bool RadioLibWrapper::isChannelActive() {
|
||||
// int.thresh: RSSI-based interference detection (relative to noise floor)
|
||||
if (_threshold != 0 && getCurrentRSSI() > _noise_floor + _threshold) return true;
|
||||
// int.thresh: RSSI-based interference detection (relative to noise floor).
|
||||
// Skipped in RX duty-cycle mode: the frontend sleeps part of the time, so an
|
||||
// instantaneous RSSI is meaningless and the SPI read would block until the
|
||||
// chip's next listen window.
|
||||
if (!_rx_ps_enabled && _threshold != 0 && getCurrentRSSI() > _noise_floor + _threshold) return true;
|
||||
|
||||
// cad: hardware channel activity detection
|
||||
if (_cad_enabled) {
|
||||
|
||||
@@ -21,19 +21,22 @@ protected:
|
||||
int32_t _floor_sample_sum;
|
||||
uint8_t _preamble_sf;
|
||||
bool _rx_ps_enabled;
|
||||
bool _rx_ps_armed; // radio is currently in RX duty-cycle mode
|
||||
uint32_t _rx_ps_rx_us;
|
||||
uint32_t _rx_ps_sleep_us;
|
||||
|
||||
void idle();
|
||||
void startRecv();
|
||||
virtual int startReceiveMode();
|
||||
virtual void stopReceiveDutyCycle();
|
||||
virtual bool isPacketReady();
|
||||
float packetScoreInt(float snr, int sf, int packet_len);
|
||||
virtual bool isReceivingPacket() =0;
|
||||
virtual void doResetAGC();
|
||||
|
||||
public:
|
||||
RadioLibWrapper(PhysicalLayer& radio, mesh::MainBoard& board)
|
||||
: _radio(&radio), _board(&board), _preamble_sf(0), _rx_ps_enabled(false),
|
||||
: _radio(&radio), _board(&board), _preamble_sf(0), _rx_ps_enabled(false), _rx_ps_armed(false),
|
||||
_rx_ps_rx_us(RX_PS_FALLBACK_RX_US), _rx_ps_sleep_us(RX_PS_FALLBACK_SLEEP_US) { n_recv = n_sent = n_recv_errors = 0; }
|
||||
|
||||
void begin() override;
|
||||
|
||||
Reference in new issue
Block a user