diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index 0e23405e5..9898333ea 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -303,4 +303,16 @@ template bool LR11x0Interface::sleep() return true; } + +template bool LR11x0Interface::setRadioCodingRate(uint8_t cr) +{ + int state = lora.setCodingRate(cr); + if (state != RADIOLIB_ERR_NONE) { + LOG_WARN("Failed to set coding rate to 4/%d, error %d", cr, state); + return false; + } + LOG_DEBUG("Set coding rate to 4/%d", cr); + return true; +} + #endif \ No newline at end of file diff --git a/src/mesh/LR11x0Interface.h b/src/mesh/LR11x0Interface.h index 4829ddc1d..08c69cc6c 100644 --- a/src/mesh/LR11x0Interface.h +++ b/src/mesh/LR11x0Interface.h @@ -65,5 +65,8 @@ template class LR11x0Interface : public RadioLibInterface virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) override; virtual void setStandby() override; + + /// Efficient method to set coding rate without full reconfiguration + virtual bool setRadioCodingRate(uint8_t cr) override; }; #endif \ No newline at end of file diff --git a/src/mesh/NextHopRouter.cpp b/src/mesh/NextHopRouter.cpp index a90fe7ad2..226db9c06 100644 --- a/src/mesh/NextHopRouter.cpp +++ b/src/mesh/NextHopRouter.cpp @@ -215,6 +215,14 @@ bool NextHopRouter::stopRetransmission(GlobalPacketId key) auto old = findPendingPacket(key); if (old) { auto p = old->packet; + + // Restore original coding rate since retransmissions are no longer needed + if (iface) { + if (iface->restoreOriginalCodingRate()) { + LOG_DEBUG("Restored default CR after successful ACK for packet 0x%x", p->id); + } + } + /* Only when we already transmitted a packet via LoRa, we will cancel the packet in the Tx queue to avoid canceling a transmission if it was ACKed super fast via MQTT */ if (old->numRetransmissions < NUM_RELIABLE_RETX - 1) { @@ -274,6 +282,11 @@ int32_t NextHopRouter::doRetransmissions() // FIXME, handle 51 day rolloever here!!! if (p.nextTxMsec <= now) { if (p.numRetransmissions == 0) { + // Final failure - restore original coding rate before giving up + if (iface) { + iface->restoreOriginalCodingRate(); + } + if (isFromUs(p.packet)) { LOG_DEBUG("Reliable send failed, returning a nak for fr=0x%x,to=0x%x,id=0x%x", p.packet->from, p.packet->to, p.packet->id); @@ -286,6 +299,23 @@ int32_t NextHopRouter::doRetransmissions() LOG_DEBUG("Sending retransmission fr=0x%x,to=0x%x,id=0x%x, tries left=%d", p.packet->from, p.packet->to, p.packet->id, p.numRetransmissions); + // Calculate retry count (NUM_RELIABLE_RETX-1 = 2 initially, counts down) + uint8_t retryCount = (NUM_RELIABLE_RETX - 1) - p.numRetransmissions; + + LOG_DEBUG("Retransmit packet id=0x%x, numRetransmissions=%d, retryCount=%d", p.packet->id, p.numRetransmissions, + retryCount); + + // Apply adaptive coding rate for better reliability on retries + bool adaptiveCrApplied = false; + if (iface) { + adaptiveCrApplied = iface->setAdaptiveCodingRate(retryCount); + if (adaptiveCrApplied) { + LOG_INFO("Applied adaptive coding rate for retry %d", retryCount); + } else { + LOG_DEBUG("No adaptive coding rate change needed for retry %d", retryCount); + } + } + if (!isBroadcast(p.packet->to)) { if (p.numRetransmissions == 1) { // Last retransmission, reset next_hop (fallback to FloodingRouter) @@ -306,6 +336,13 @@ int32_t NextHopRouter::doRetransmissions() FloodingRouter::send(packetPool.allocCopy(*p.packet)); } + // Restore original coding rate after transmission + if (adaptiveCrApplied && iface) { + if (iface->restoreOriginalCodingRate()) { + LOG_DEBUG("Restored original coding rate after retransmission"); + } + } + // Queue again --p.numRetransmissions; setNextTx(&p); diff --git a/src/mesh/RF95Interface.cpp b/src/mesh/RF95Interface.cpp index 0f32f3427..0439dc20f 100644 --- a/src/mesh/RF95Interface.cpp +++ b/src/mesh/RF95Interface.cpp @@ -337,4 +337,17 @@ bool RF95Interface::sleep() return true; } + +bool RF95Interface::setRadioCodingRate(uint8_t cr) +{ + // Direct hardware call for efficient coding rate change + int state = lora->setCodingRate(cr); + if (state != RADIOLIB_ERR_NONE) { + LOG_WARN("Failed to set coding rate to 4/%d, error %d", cr, state); + return false; + } + LOG_DEBUG("Set coding rate to 4/%d", cr); + return true; +} + #endif \ No newline at end of file diff --git a/src/mesh/RF95Interface.h b/src/mesh/RF95Interface.h index 327e57900..98277f1fd 100644 --- a/src/mesh/RF95Interface.h +++ b/src/mesh/RF95Interface.h @@ -65,6 +65,9 @@ class RF95Interface : public RadioLibInterface */ virtual void configHardwareForSend() override; + /// Efficient method to set coding rate without full reconfiguration + virtual bool setRadioCodingRate(uint8_t cr) override; + private: /** Some boards require GPIO control of tx vs rx paths */ void setTransmitEnable(bool txon); diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 31c68c302..4b544b17d 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -530,7 +530,7 @@ void RadioInterface::applyModemConfig() cr = 5; sf = 10; break; - default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal. + default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default bw = (myRegion->wideLora) ? 812.5 : 250; cr = 5; sf = 11; @@ -564,6 +564,7 @@ void RadioInterface::applyModemConfig() if (bw == 1600) bw = 1625.0; } + originalCr = cr; if ((myRegion->freqEnd - myRegion->freqStart) < bw / 1000) { static const char *err_string = "Regional frequency range is smaller than bandwidth. Fall back to default preset"; @@ -637,6 +638,10 @@ void RadioInterface::applyModemConfig() LOG_INFO("channel_num: %d", channel_num + 1); LOG_INFO("frequency: %f", getFreq()); LOG_INFO("Slot time: %u msec", slotTimeMsec); + + // Save the original coding rate for potential adaptive retransmissions + originalCr = cr; + LOG_DEBUG("Default coding rate: 4/%d", originalCr); } /** Slottime is the time to detect a transmission has started, consisting of: @@ -725,3 +730,48 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) sendingPacket = p; return p->encrypted.size + sizeof(PacketHeader); } + +bool RadioInterface::setAdaptiveCodingRate(uint8_t retryCount) +{ + // Save original coding rate if this is the first retry + if (retryCount == 0) { + originalCr = cr; + } + + // Progressive coding rate adaptation for better reliability + // Always increase from the current/original coding rate, never decrease + uint8_t newCr = originalCr + retryCount + 1; + + // Clamp to maximum coding rate of 8 + if (newCr > 8) { + newCr = 8; + } + + if (newCr != cr) { + LOG_INFO("Adaptive coding rate: retry %d, changing CR from 4/%d to 4/%d", retryCount, cr, newCr); + cr = newCr; + // Use the more efficient direct coding rate change method if available + return setRadioCodingRate(cr); + } + + return false; // No change made +} + +bool RadioInterface::restoreOriginalCodingRate() +{ + if (cr != originalCr) { + LOG_INFO("Restoring original coding rate from 4/%d to 4/%d", cr, originalCr); + cr = originalCr; + // Use the more efficient direct coding rate change method if available + return setRadioCodingRate(cr); + } + + return false; // No change made +} + +bool RadioInterface::setRadioCodingRate(uint8_t cr) +{ + // Default implementation falls back to full reconfigure + // Subclasses should override this for more efficient direct coding rate changes + return reconfigure(); +} diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index 0c5b6cd1a..0e578c444 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -83,6 +83,7 @@ class RadioInterface float bw = 125; uint8_t sf = 9; uint8_t cr = 5; + uint8_t originalCr = cr; // Store original coding rate for restoration after adaptive retransmissions const uint8_t NUM_SYM_CAD = 2; // Number of symbols used for CAD, 2 is the default since RadioLib 6.3.0 as per AN1200.48 const uint8_t NUM_SYM_CAD_24GHZ = 4; // Number of symbols used for CAD in 2.4 GHz, 4 is recommended in AN1200.22 of SX1280 @@ -156,6 +157,14 @@ class RadioInterface /** Attempt to find a packet in the TxQueue. Returns true if the packet was found. */ virtual bool findInTxQueue(NodeNum from, PacketId id) { return false; } + /** + * Get the frequency we saved. + */ + virtual float getFreq(); + + // Whether we use the default frequency slot given our LoRa config (region and modem preset) + static bool uses_default_frequency_slot; + // methods from radiohead /// Initialise the Driver transport hardware and software. @@ -203,24 +212,39 @@ class RadioInterface * @return num msecs for the packet */ uint32_t getPacketTime(const meshtastic_MeshPacket *p); - uint32_t getPacketTime(uint32_t totalPacketLen); + uint32_t getPacketTime(uint32_t payloadSize); + + /** + * Adaptive coding rate for retransmissions + * Set a more robust coding rate for better reliability on retries + * @param retryCount How many retries this is (0 = first retry, 1 = second retry, etc.) + * @return true if coding rate was changed and radio was reconfigured + */ + virtual bool setAdaptiveCodingRate(uint8_t retryCount); + + /** + * Restore the original coding rate after adaptive transmission + * @return true if coding rate was restored and radio was reconfigured + */ + virtual bool restoreOriginalCodingRate(); + + /** + * Set the radio coding rate directly (more efficient than full reconfigure) + * @param cr Coding rate (5-8, representing 4/5 to 4/8) + * @return true if coding rate was set successfully + */ + virtual bool setRadioCodingRate(uint8_t cr); + + protected: /** * Get the channel we saved. */ uint32_t getChannelNum(); - /** - * Get the frequency we saved. - */ - virtual float getFreq(); - /// Some boards (1st gen Pinetab Lora module) have broken IRQ wires, so we need to poll via i2c registers virtual bool isIRQPending() { return false; } - // Whether we use the default frequency slot given our LoRa config (region and modem preset) - static bool uses_default_frequency_slot; - protected: int8_t power = 17; // Set by applyModemConfig() diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 3717e8780..a2071ab7d 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -552,4 +552,11 @@ bool RadioLibInterface::startSend(meshtastic_MeshPacket *txp) return res == RADIOLIB_ERR_NONE; } +} + +bool RadioLibInterface::setRadioCodingRate(uint8_t cr) +{ + // Default implementation just calls reconfigure() + // Subclasses should override this for more efficient direct coding rate changes + return reconfigure(); } \ No newline at end of file diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index 3444b1a2c..fc0f2f251 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -204,6 +204,15 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified */ virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) = 0; + /** + * Set the coding rate on the radio hardware directly + * Base implementation just calls reconfigure(), but subclasses should override + * for more efficient direct coding rate changes + * @param cr The coding rate to set (5-8 for 4/5 to 4/8) + * @return true if successful + */ + virtual bool setRadioCodingRate(uint8_t cr); + /** * Subclasses must override, implement and then call into this base class implementation */ diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 3fc2562b3..89b3b98c0 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -371,4 +371,16 @@ template void SX126xInterface::setTransmitEnable(bool txon) #endif } +template bool SX126xInterface::setRadioCodingRate(uint8_t cr) +{ + // Direct hardware call for efficient coding rate change + int state = lora.setCodingRate(cr); + if (state != RADIOLIB_ERR_NONE) { + LOG_WARN("Failed to set coding rate to 4/%d, error %d", cr, state); + return false; + } + LOG_DEBUG("Set coding rate to 4/%d", cr); + return true; +} + #endif \ No newline at end of file diff --git a/src/mesh/SX126xInterface.h b/src/mesh/SX126xInterface.h index dc7024daa..7a9a9275a 100644 --- a/src/mesh/SX126xInterface.h +++ b/src/mesh/SX126xInterface.h @@ -72,6 +72,9 @@ template class SX126xInterface : public RadioLibInterface virtual void setStandby() override; + /// Efficient method to set coding rate without full reconfiguration + virtual bool setRadioCodingRate(uint8_t cr) override; + private: /** Some boards require GPIO control of tx vs rx paths */ void setTransmitEnable(bool txon); diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index cbc98eeb1..f5a4d888a 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -322,4 +322,17 @@ template bool SX128xInterface::sleep() return true; } + +template bool SX128xInterface::setRadioCodingRate(uint8_t cr) +{ + // Direct hardware call for efficient coding rate change + int state = lora.setCodingRate(cr); + if (state != RADIOLIB_ERR_NONE) { + LOG_WARN("Failed to set coding rate to 4/%d, error %d", cr, state); + return false; + } + LOG_DEBUG("Set coding rate to 4/%d", cr); + return true; +} + #endif \ No newline at end of file diff --git a/src/mesh/SX128xInterface.h b/src/mesh/SX128xInterface.h index bba31dab4..4248ae513 100644 --- a/src/mesh/SX128xInterface.h +++ b/src/mesh/SX128xInterface.h @@ -67,4 +67,7 @@ template class SX128xInterface : public RadioLibInterface virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) override; virtual void setStandby() override; + + /// Efficient method to set coding rate without full reconfiguration + virtual bool setRadioCodingRate(uint8_t cr) override; };