Compare commits

...

3 Commits

Author SHA1 Message Date
Ben Meadors
901a91bd89 Merge remote-tracking branch 'origin/master' into adaptive-transmission 2025-09-28 17:46:56 -05:00
Ben Meadors
6249fc6917 Ugly. Need to clean this up 2025-09-27 07:17:19 -05:00
Ben Meadors
4be400fceb Set coding rate 2025-09-26 18:32:04 -05:00
13 changed files with 210 additions and 11 deletions

View File

@@ -303,4 +303,16 @@ template <typename T> bool LR11x0Interface<T>::sleep()
return true;
}
template <typename T> bool LR11x0Interface<T>::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

View File

@@ -65,5 +65,8 @@ template <class T> 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

View File

@@ -221,6 +221,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) {
@@ -280,6 +288,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);
@@ -292,6 +305,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)
@@ -312,7 +342,7 @@ int32_t NextHopRouter::doRetransmissions()
FloodingRouter::send(packetPool.allocCopy(*p.packet));
}
// Queue again
// Queue again for next retry
--p.numRetransmissions;
setNextTx(&p);
}

View File

@@ -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

View File

@@ -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);

View File

@@ -529,7 +529,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;
@@ -563,6 +563,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";
@@ -636,6 +637,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:
@@ -724,3 +729,65 @@ 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
// Calculate the target coding rate based on retry count
uint8_t newCr;
if (originalCr == 5) {
// Special progression for default CR 5: 5 -> 7 -> 8 -> 8 (skip 6)
switch (retryCount) {
case 0:
newCr = 7;
break; // Skip 6, go to 7
case 1:
newCr = 8;
break; // Maximum robustness
case 2:
newCr = 8;
break; // Stay at maximum
default:
newCr = 8;
break;
}
} else {
// Standard progression for other coding rates
newCr = originalCr + retryCount + 1;
// Clamp to maximum coding rate of 8
if (newCr > 8) {
newCr = 8;
}
}
// Apply the calculated coding rate for this retry
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);
}
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();
}

View File

@@ -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()

View File

@@ -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();
}

View File

@@ -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
*/

View File

@@ -371,4 +371,16 @@ template <typename T> void SX126xInterface<T>::setTransmitEnable(bool txon)
#endif
}
template <typename T> bool SX126xInterface<T>::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

View File

@@ -72,6 +72,9 @@ template <class T> 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);

View File

@@ -322,4 +322,17 @@ template <typename T> bool SX128xInterface<T>::sleep()
return true;
}
template <typename T> bool SX128xInterface<T>::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

View File

@@ -67,4 +67,7 @@ template <class T> 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;
};