Skip to content

Commit

Permalink
Add new ROUTER_INFILL role
Browse files Browse the repository at this point in the history
Will always rebroadcast packets, but will do so after all other modes.
Intended for router nodes that are there to provide additional coverage
in areas not already covered by other routers, or to bridge around
problematic terrain, but should not be given priority over other routers
in order to avoid unnecessaraily consuming hops.

By default, this role will rebroadcast during the normal client window.
However, if another node is overheard rebroadcasting the packet, then it
will be moved to a second window *after* the normal client one, with the
same timing behaviour.
  • Loading branch information
erayd committed Dec 8, 2024
1 parent 59ed5c9 commit 5b4ae70
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 24 deletions.
6 changes: 5 additions & 1 deletion src/mesh/FloodingRouter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
printPacket("Ignore dupe incoming msg", p);
rxDupe++;
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) {
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER &&
config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER_INFILL) {
// cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater!
if (Router::cancelSending(p->from, p->id))
txRelayCanceled++;
}
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_INFILL && iface) {
iface->clampToLateRebroadcastWindow(getFrom(p), p->id);
}

/* If the original transmitter is doing retransmissions (hopStart equals hopLimit) for a reliable transmission, e.g., when
the ACK got lost, we will handle the packet again to make sure it gets an ACK to its packet. */
Expand Down
13 changes: 13 additions & 0 deletions src/mesh/MeshPacketQueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,19 @@ meshtastic_MeshPacket *MeshPacketQueue::getFront()
return p;
}

/** Attempt to find a packet in this queue. Returns a pointer to the found packet, or NULL if not found. */
meshtastic_MeshPacket *MeshPacketQueue::find(NodeNum from, PacketId id)
{
for (auto it = queue.begin(); it != queue.end(); it++) {
auto p = (*it);
if (getFrom(p) == from && p->id == id) {
return p;
}
}

return NULL;
}

/** Attempt to find and remove a packet from this queue. Returns a pointer to the removed packet, or NULL if not found */
meshtastic_MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id)
{
Expand Down
3 changes: 3 additions & 0 deletions src/mesh/MeshPacketQueue.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ class MeshPacketQueue

meshtastic_MeshPacket *getFront();

/** Attempt to find a packet in this queue. Returns a pointer to the found packet, or NULL if not found. */
meshtastic_MeshPacket *find(NodeNum from, PacketId id);

/** Attempt to find and remove a packet from this queue. Returns the packet which was removed from the queue */
meshtastic_MeshPacket *remove(NodeNum from, PacketId id);
};
20 changes: 17 additions & 3 deletions src/mesh/RadioInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,19 +254,33 @@ uint32_t RadioInterface::getTxDelayMsec()
return random(0, pow(2, CWsize)) * slotTimeMsec;
}

/** The delay to use when we want to flood a message */
uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
/** The CW size to use when calculating SNR_based delays */
uint8_t RadioInterface::getCWsize(float snr)
{
// The minimum value for a LoRa SNR
const uint32_t SNR_MIN = -20;

// The maximum value for a LoRa SNR
const uint32_t SNR_MAX = 15;

return map(snr, SNR_MIN, SNR_MAX, CWmin, CWmax);
}

/** The worst-case SNR_based packet delay */
uint32_t RadioInterface::getTxDelayMsecWeightedWorst(float snr)
{
uint8_t CWsize = getCWsize(snr);
// offset the maximum delay for routers: (2 * CWmax * slotTimeMsec)
return (2 * CWmax * slotTimeMsec) + pow(2, CWsize) * slotTimeMsec;
}

/** The delay to use when we want to flood a message */
uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
{
// high SNR = large CW size (Long Delay)
// low SNR = small CW size (Short Delay)
uint32_t delay = 0;
uint8_t CWsize = map(snr, SNR_MIN, SNR_MAX, CWmin, CWmax);
uint8_t CWsize = getCWsize(snr);
// LOG_DEBUG("rx_snr of %f so setting CWsize to:%d", snr, CWsize);
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
Expand Down
9 changes: 9 additions & 0 deletions src/mesh/RadioInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,18 @@ class RadioInterface
/** The delay to use when we want to send something */
uint32_t getTxDelayMsec();

/** The CW to use when calculating SNR_based delays */
uint8_t getCWsize(float snr);

/** The worst-case SNR_based packet delay */
uint32_t getTxDelayMsecWeightedWorst(float snr);

/** The delay to use when we want to flood a message. Use a weighted scale based on SNR */
uint32_t getTxDelayMsecWeighted(float snr);

/** If the packet is not already in the late rebroadcast window, move it there */
virtual void clampToLateRebroadcastWindow(NodeNum from, PacketId id) { return; }

/**
* Calculate airtime per
* https://www.rs-online.com/designspark/rel-assets/ds-assets/uploads/knowledge-items/application-notes-for-the-internet-of-things/LoRa%20Design%20Guide.pdf
Expand Down
62 changes: 46 additions & 16 deletions src/mesh/RadioLibInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,19 +278,28 @@ void RadioLibInterface::onNotify(uint32_t notification)
startReceive(); // try receiving this packet, afterwards we'll be trying to transmit again
setTransmitDelay();
} else {
// Send any outgoing packets we have ready as fast as possible to keep the time between channel scan and
// actual transmission as short as possible
meshtastic_MeshPacket *txp = txQueue.dequeue();
// check for any remaining delay, and abort if we're still supposed to be waiting
meshtastic_MeshPacket *txp = txQueue.getFront();
assert(txp);
bool sent = startSend(txp);
if (sent) {
// Packet has been sent, count it toward our TX airtime utilization.
uint32_t xmitMsec = getPacketTime(txp);
airTime->logAirtime(TX_LOG, xmitMsec);
if (txp->tx_after > millis()) {
// There's still some delay pending on this packet, so resume waiting for it to elapse
notifyLater(txp->tx_after - millis(), TRANSMIT_DELAY_COMPLETED, false);
} else {
// Send any outgoing packets we have ready as fast as possible to keep the time between channel scan and
// actual transmission as short as possible
txp = txQueue.dequeue();
assert(txp);
bool sent = startSend(txp);
if (sent) {
// Packet has been sent, count it toward our TX airtime utilization.
uint32_t xmitMsec = getPacketTime(txp);
airTime->logAirtime(TX_LOG, xmitMsec);
}
}
}
}
} else {
// Do nothing, because the queue is empty
}
break;
default:
Expand All @@ -305,34 +314,55 @@ void RadioLibInterface::setTransmitDelay()
// We use a delay here because this packet might have been sent in response to a packet we just received.
// So we want to make sure the other side has had a chance to reconfigure its radio.

/* We assume if rx_snr = 0 and rx_rssi = 0, the packet was generated locally.
* This assumption is valid because of the offset generated by the radio to account for the noise
* floor.
*/
if (p->rx_snr == 0 && p->rx_rssi == 0) {
startTransmitTimer(true);
/* We assume if rx_snr = 0 and rx_rssi = 0, the packet was generated locally.
* This assumption is valid because of the offset generated by the radio to account for the noise
* floor.
*/
p->tx_after = millis() + startTransmitTimer(true);
} else {
// If there is a SNR, start a timer scaled based on that SNR.
LOG_DEBUG("rx_snr found. hop_limit:%d rx_snr:%f", p->hop_limit, p->rx_snr);
startTransmitTimerSNR(p->rx_snr);
p->tx_after = millis() + startTransmitTimerSNR(p->rx_snr);
}
}

void RadioLibInterface::startTransmitTimer(bool withDelay)
uint32_t RadioLibInterface::startTransmitTimer(bool withDelay)
{
// If we have work to do and the timer wasn't already scheduled, schedule it now
if (!txQueue.empty()) {
uint32_t delay = !withDelay ? 1 : getTxDelayMsec();
notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable
return delay;
}
return 0;
}

void RadioLibInterface::startTransmitTimerSNR(float snr)
uint32_t RadioLibInterface::startTransmitTimerSNR(float snr)
{
// If we have work to do and the timer wasn't already scheduled, schedule it now
if (!txQueue.empty()) {
uint32_t delay = getTxDelayMsecWeighted(snr);
notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable
return delay;
}
return 0;
}

/**
* If the packet is not already in the late rebroadcast window, move it there
*/
void RadioLibInterface::clampToLateRebroadcastWindow(NodeNum from, PacketId id)
{
meshtastic_MeshPacket *p = txQueue.find(from, id);
if (p && p->delayed != meshtastic_MeshPacket_Delayed_DELAYED_REBROADCAST) {
p->tx_after += getTxDelayMsecWeightedWorst(p->rx_snr);
p->delayed = meshtastic_MeshPacket_Delayed_DELAYED_REBROADCAST;

// ensures that non-delayed packets are handled first
p->priority = meshtastic_MeshPacket_Priority_MIN;

LOG_DEBUG("Moved packet to the late rebroadcast window");
}
}

Expand Down
19 changes: 15 additions & 4 deletions src/mesh/RadioLibInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,17 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
* doing the transmit */
void setTransmitDelay();

/** random timer with certain min. and max. settings */
void startTransmitTimer(bool withDelay = true);
/**
* random timer with certain min. and max. settings
* @return Timestamp after which the packet may be sent
*/
uint32_t startTransmitTimer(bool withDelay = true);

/** timer scaled to SNR of to be flooded packet */
void startTransmitTimerSNR(float snr);
/**
* timer scaled to SNR of to be flooded packet
* @return Timestamp after which the packet may be sent
*/
uint32_t startTransmitTimerSNR(float snr);

void handleTransmitInterrupt();
void handleReceiveInterrupt();
Expand Down Expand Up @@ -200,4 +206,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
virtual void setStandby();

const char *radioLibErr = "RadioLib err=";

/**
* If the packet is not already in the late rebroadcast window, move it there
*/
void clampToLateRebroadcastWindow(NodeNum from, PacketId id);
};

0 comments on commit 5b4ae70

Please sign in to comment.