diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 5f0e5721..cfadcf64 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -118,20 +118,10 @@ ErrorCode RadioLibInterface::send(MeshPacket *p) return res; } - // We want all sending/receiving to be done by our daemon thread, 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 not 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); - } else { - // If there is a SNR, start a timer scaled based on that SNR. - DEBUG_MSG("rx_snr found. hop_limit:%d rx_snr:%f\n", p->hop_limit, p->rx_snr); - startTransmitTimerSNR(p->rx_snr); - } + // set random transmit delay to let others reconfigure their radio, + // to avoid collisions and implement timing-based flooding + // DEBUG_MSG("Set random delay before transmitting.\n"); + setRandomDelay(); return res; #else @@ -164,8 +154,8 @@ bool RadioLibInterface::cancelSending(NodeNum from, PacketId id) /** radio helper thread callback. We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and -wait a random delay of 50 to 200 ms to make sure we are not stomping on someone else. The 50ms delay at the beginning ensures all -possible listeners have had time to finish processing the previous packet and now have their radio in RX state. The up to 200ms +wait a random delay of 100ms to 100ms+shortPacketMsec to make sure we are not stomping on someone else. The 100ms delay at the beginning ensures all +possible listeners have had time to finish processing the previous packet and now have their radio in RX state. The up to 100ms+shortPacketMsec random delay gives a chance for all possible senders to have high odds of detecting that someone else started transmitting first and then they will wait until that packet finishes. @@ -192,20 +182,26 @@ void RadioLibInterface::onNotify(uint32_t notification) case TRANSMIT_DELAY_COMPLETED: // DEBUG_MSG("delay done\n"); - // If we are not currently in receive mode, then restart the timer and try again later (this can happen if the main thread + // If we are not currently in receive mode, then restart the random delay (this can happen if the main thread // has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode? if (!txQueue.empty()) { if (!canSendImmediately()) { - startTransmitTimer(); // try again in a little while + // DEBUG_MSG("Currently Rx/Tx-ing: set random delay\n"); + setRandomDelay(); // currently Rx/Tx-ing: reset random delay } else { - // Send any outgoing packets we have ready - MeshPacket *txp = txQueue.dequeue(); - assert(txp); - startSend(txp); + if (isChannelActive()) { // check if there is currently a LoRa packet on the channel + // DEBUG_MSG("Channel is active: set random delay\n"); + setRandomDelay(); // reset random delay + } else { + // Send any outgoing packets we have ready + MeshPacket *txp = txQueue.dequeue(); + assert(txp); + startSend(txp); - // Packet has been sent, count it toward our TX airtime utilization. - uint32_t xmitMsec = getPacketTime(txp); - airTime->logAirtime(TX_LOG, xmitMsec); + // Packet has been sent, count it toward our TX airtime utilization. + uint32_t xmitMsec = getPacketTime(txp); + airTime->logAirtime(TX_LOG, xmitMsec); + } } } else { // DEBUG_MSG("done with txqueue\n"); @@ -216,6 +212,26 @@ void RadioLibInterface::onNotify(uint32_t notification) } } +void RadioLibInterface::setRandomDelay() +{ + MeshPacket *p = txQueue.getFront(); + // We want all sending/receiving to be done by our daemon thread. + // 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); + } else { + // If there is a SNR, start a timer scaled based on that SNR. + DEBUG_MSG("rx_snr found. hop_limit:%d rx_snr:%f\n", p->hop_limit, p->rx_snr); + startTransmitTimerSNR(p->rx_snr); + } +} + void RadioLibInterface::startTransmitTimer(bool withDelay) { // If we have work to do and the timer wasn't already scheduled, schedule it now diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index 68dfe96d..2b342a68 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -132,6 +132,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified */ virtual void startReceive() = 0; + /** can we detect a LoRa preamble on the current channel? */ + virtual bool isChannelActive() = 0; + /** are we actively receiving a packet (only called during receiving state) * This method is only public to facilitate debugging. Do not call. */ @@ -142,17 +145,13 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified private: /** if we have something waiting to send, start a short random timer so we can come check for collision before actually doing - * the transmit - * - * If the timer was already running, we just wait for that one to occur. - * */ + * the transmit */ + void setRandomDelay(); + + /** random timer with certain min. and max. settings */ void startTransmitTimer(bool withDelay = true); - /** if we have something waiting to send, start a short scaled timer based on SNR so we can come check for collision before actually doing - * the transmit - * - * If the timer was already running, we just wait for that one to occur. - * */ + /** timer scaled to SNR of to be flooded packet */ void startTransmitTimerSNR(float snr); void handleTransmitInterrupt();