From 68937d52fe0ef0b09052957b3ce96ae91bafc79f Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 12 Nov 2020 17:49:04 +0800 Subject: [PATCH] bug #503 wip --- src/mesh/RadioInterface.cpp | 53 ++++++++++++++++++++++++++++++++++ src/mesh/RadioInterface.h | 17 ++++++++++- src/mesh/RadioLibInterface.cpp | 15 +--------- src/mesh/ReliableRouter.cpp | 7 ++--- src/mesh/ReliableRouter.h | 6 ++-- src/mesh/Router.h | 5 ++-- 6 files changed, 80 insertions(+), 23 deletions(-) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index b8ab4858..91dc38cf 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -53,6 +53,59 @@ separated by 2.16 MHz with respect to the adjacent channels. Channel zero starts // 1kb was too small #define RADIO_STACK_SIZE 4096 +/** At the low end we want to pick a delay large enough that anyone who just completed sending (some other node) + * has had enough time to switch their radio back into receive mode. + */ +#define MIN_TX_WAIT_MSEC 100 + +/** + * At the high end, this value is used to spread node attempts across time so when they are replying to a packet + * they don't both check that the airwaves are clear at the same moment. As long as they are off by some amount + * one of the two will be first to start transmitting and the other will see that. I bet 500ms is more than enough + * to guarantee this. + */ +#define MAX_TX_WAIT_MSEC 2000 // stress test would still fail occasionally with 1000 + +/** + * 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 + * section 4 + * + * @return num msecs for the packet + */ +uint32_t RadioInterface::getPacketTime(MeshPacket *p) +{ + assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now + + uint8_t sf = 12; // FIXME + uint8_t nPreamble = 32; // FIXME + uint32_t bandwidthHz = 125 * 1000; // FIXME + bool headDisable = false; // we currently always use the header + bool lowDataOptEn = false; // FIXME + uint8_t cr = 1; // from 1 to 4 + uint32_t pl = p->encrypted.size + sizeof(PacketHeader); + float tSym = (1 << sf) / bandwidthHz; + float tPreamble = (nPreamble + 4.25f) * tSym; + float numPayloadSym = + 8 + max(ceilf(((8 * pl - 4 * sf + 28 + 16 - 20 * headDisable) / (4 * (sf - 2 * lowDataOptEn))) * (cr + 4)), 0.0f); + float tPayload = numPayloadSym * tSym; + float tPacket = tPreamble + tPayload; + + uint32_t msecs = tPacket / 1000; + return msecs; +} + +/** The delay to use for retransmitting dropped packets */ +uint32_t RadioInterface::getRetransmissionMsec(const MeshPacket *p) +{ + return random(20 * 1000L, 22 * 1000L); +} + +/** The delay to use when we want to send something but the ether is busy */ +uint32_t RadioInterface::getTxDelayMsec() +{ + return random(MIN_TX_WAIT_MSEC, MAX_TX_WAIT_MSEC); +} + void printPacket(const char *prefix, const MeshPacket *p) { DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d", prefix, p->id, p->from & 0xff, p->to & 0xff, p->want_ack, diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index da717caa..49077b1f 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -36,7 +36,7 @@ typedef struct { * * This defines the SOLE API for talking to radios (because soon we will have alternate radio implementations) */ -class RadioInterface +class RadioInterface : public PacketTimes { friend class MeshRadio; // for debugging we let that class touch pool PointerQueue *rxDest = NULL; @@ -108,6 +108,21 @@ class RadioInterface /// \return true if initialisation succeeded. virtual bool reconfigure() = 0; + /** The delay to use for retransmitting dropped packets */ + uint32_t getRetransmissionMsec(const MeshPacket *p); + + /** The delay to use when we want to send something but the ether is busy */ + uint32_t getTxDelayMsec(); + + /** + * 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 + * section 4 + * + * @return num msecs for the packet + */ + uint32_t getPacketTime(MeshPacket *p); + protected: int8_t power = 17; // Set by applyModemConfig() diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index 63c2469f..a40178b5 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -158,18 +158,6 @@ bool RadioLibInterface::canSleep() return res; } -/** At the low end we want to pick a delay large enough that anyone who just completed sending (some other node) - * has had enough time to switch their radio back into receive mode. - */ -#define MIN_TX_WAIT_MSEC 100 - -/** - * At the high end, this value is used to spread node attempts across time so when they are replying to a packet - * they don't both check that the airwaves are clear at the same moment. As long as they are off by some amount - * one of the two will be first to start transmitting and the other will see that. I bet 500ms is more than enough - * to guarantee this. - */ -#define MAX_TX_WAIT_MSEC 2000 // stress test would still fail occasionally with 1000 /** radio helper thread callback. @@ -226,8 +214,7 @@ void RadioLibInterface::startTransmitTimer(bool withDelay) { // If we have work to do and the timer wasn't already scheduled, schedule it now if (!txQueue.isEmpty()) { - uint32_t delay = - !withDelay ? 1 : random(MIN_TX_WAIT_MSEC, MAX_TX_WAIT_MSEC); // See documentation for loop() wrt these values + uint32_t delay = !withDelay ? 1 : getTxDelayMsec(); // DEBUG_MSG("xmit timer %d\n", delay); notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable } diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index f3bf886b..bbbb5238 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -111,7 +111,6 @@ PendingPacket::PendingPacket(MeshPacket *p) { packet = p; numRetransmissions = NUM_RETRANSMISSIONS - 1; // We subtract one, because we assume the user just did the first send - setNextTx(); } PendingPacket *ReliableRouter::findPendingPacket(GlobalPacketId key) @@ -151,6 +150,7 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p) auto id = GlobalPacketId(p); auto rec = PendingPacket(p); + setNextTx(&rec); stopRetransmission(p->from, p->id); pending[id] = rec; @@ -190,10 +190,9 @@ int32_t ReliableRouter::doRetransmissions() // Queue again --p.numRetransmissions; - p.setNextTx(); + setNextTx(&p); } - } - else { + } else { // Not yet time int32_t t = p.nextTxMsec - now; diff --git a/src/mesh/ReliableRouter.h b/src/mesh/ReliableRouter.h index caa5f1a5..91dd248a 100644 --- a/src/mesh/ReliableRouter.h +++ b/src/mesh/ReliableRouter.h @@ -46,8 +46,6 @@ struct PendingPacket { PendingPacket() {} PendingPacket(MeshPacket *p); - - void setNextTx() { nextTxMsec = millis() + random(20 * 1000L, 22 * 1000L); } }; class GlobalPacketIdHashFunction @@ -130,4 +128,8 @@ class ReliableRouter : public FloodingRouter * @return the number of msecs until our next retransmission or MAXINT if none scheduled */ int32_t doRetransmissions(); + + void setNextTx(PendingPacket *pending) { + assert(iface); + pending->nextTxMsec = millis() + iface->getRetransmissionMsec(pending->packet); } }; diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 2bdb7231..4642a4da 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -14,12 +14,13 @@ class Router : protected concurrency::OSThread { private: - RadioInterface *iface; - /// Packets which have just arrived from the radio, ready to be processed by this service and possibly /// forwarded to the phone. PointerQueue fromRadioQueue; + protected: + RadioInterface *iface = NULL; + public: /// Local services that want to see _every_ packet this node receives can observe this. /// Observers should always return 0 and _copy_ any packets they want to keep for use later (this packet will be getting