From 1839f8f7ca476fa5b094bbd5544811f6532867bf Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 14 Nov 2020 10:07:25 +0800 Subject: [PATCH] fix #513 scale retransmission times based on true packet time on wire --- src/mesh/NodeDB.cpp | 8 ++- src/mesh/RadioInterface.cpp | 107 ++++++++++++++++++++++++--------- src/mesh/RadioInterface.h | 12 +++- src/mesh/RadioLibInterface.cpp | 53 ++-------------- src/mesh/RadioLibInterface.h | 11 ---- 5 files changed, 100 insertions(+), 91 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 3714f403..61be815e 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -143,11 +143,15 @@ bool NodeDB::resetRadioConfig() DEBUG_MSG("***** DEVELOPMENT MODE - DO NOT RELEASE *****\n"); // Sleep quite frequently to stress test the BLE comms, broadcast position every 6 mins - radioConfig.preferences.screen_on_secs = 30; - radioConfig.preferences.wait_bluetooth_secs = 30; + radioConfig.preferences.screen_on_secs = 10; + radioConfig.preferences.wait_bluetooth_secs = 10; radioConfig.preferences.position_broadcast_secs = 6 * 60; radioConfig.preferences.ls_secs = 60; radioConfig.preferences.region = RegionCode_TW; + + // Enter super deep sleep soon and stay there not very long + //radioConfig.preferences.mesh_sds_timeout_secs = 10; + //radioConfig.preferences.sds_secs = 60; } // Update the global myRegion diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 91dc38cf..a1bbc5c4 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -53,57 +53,66 @@ 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 + * 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) +uint32_t RadioInterface::getPacketTime(uint32_t pl) { - 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 + float bandwidthHz = bw * 1000.0f; 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; + + bool lowDataOptEn = tSym > 16e-3 ? true : false; // Needed if symbol time is >16ms + + float tPreamble = (preambleLength + 4.25f) * tSym; float numPayloadSym = - 8 + max(ceilf(((8 * pl - 4 * sf + 28 + 16 - 20 * headDisable) / (4 * (sf - 2 * lowDataOptEn))) * (cr + 4)), 0.0f); + 8 + max(ceilf(((8.0f * pl - 4 * sf + 28 + 16 - 20 * headDisable) / (4 * (sf - 2 * lowDataOptEn))) * cr), 0.0f); float tPayload = numPayloadSym * tSym; float tPacket = tPreamble + tPayload; - uint32_t msecs = tPacket / 1000; + uint32_t msecs = tPacket * 1000; + + DEBUG_MSG("(bw=%d, sf=%d, cr=4/%d) packet symLen=%d ms, payloadSize=%u, time %d ms\n", (int)bw, sf, cr, (int)(tSym * 1000), + pl, msecs); return msecs; } +uint32_t RadioInterface::getPacketTime(MeshPacket *p) +{ + assert(p->which_payload == MeshPacket_encrypted_tag); // It should have already been encoded by now + uint32_t pl = p->encrypted.size + sizeof(PacketHeader); + + return getPacketTime(pl); +} + /** The delay to use for retransmitting dropped packets */ uint32_t RadioInterface::getRetransmissionMsec(const MeshPacket *p) { - return random(20 * 1000L, 22 * 1000L); + // was 20 and 22 secs respectively, but now with shortPacketMsec as 2269, this should give the same range + return random(9 * shortPacketMsec, 10 * shortPacketMsec); } /** 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); + /** 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. + */ + const uint32_t 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. + */ + // const uint32_t MAX_TX_WAIT_MSEC = 2000; // stress test would still fail occasionally with 1000 + + return random(MIN_TX_WAIT_MSEC, shortPacketMsec); } void printPacket(const char *prefix, const MeshPacket *p) @@ -208,8 +217,47 @@ void RadioInterface::applyModemConfig() // Set up default configuration // No Sync Words in LORA mode + if (channelSettings.spread_factor == 0) { + switch (channelSettings.modem_config) { + case ChannelSettings_ModemConfig_Bw125Cr45Sf128: ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium + ///< range + bw = 125; + cr = 5; + sf = 7; + break; + case ChannelSettings_ModemConfig_Bw500Cr45Sf128: ///< Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short + ///< range + bw = 500; + cr = 5; + sf = 7; + break; + case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512: ///< Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long + ///< range + bw = 31.25; + cr = 8; + sf = 9; + break; + case ChannelSettings_ModemConfig_Bw125Cr48Sf4096: + bw = 125; + cr = 8; + sf = 12; + break; + default: + assert(0); // Unknown enum + } + } else { + sf = channelSettings.spread_factor; + cr = channelSettings.coding_rate; + bw = channelSettings.bandwidth; + + if (bw == 31) // This parameter is not an integer + bw = 31.25; + } + power = channelSettings.tx_power; + shortPacketMsec = getPacketTime(sizeof(PacketHeader)); + assert(myRegion); // Should have been found in init // If user has manually specified a channel num, then use that, otherwise generate one by hashing the name @@ -224,6 +272,7 @@ void RadioInterface::applyModemConfig() DEBUG_MSG("Radio myRegion->numChannels: %d\n", myRegion->numChannels); DEBUG_MSG("Radio channel_num: %d\n", channel_num); DEBUG_MSG("Radio frequency: %f\n", freq); + DEBUG_MSG("Short packet time: %u msec\n", shortPacketMsec); } /** diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index 49077b1f..62774a37 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 : public PacketTimes +class RadioInterface { friend class MeshRadio; // for debugging we let that class touch pool PointerQueue *rxDest = NULL; @@ -50,7 +50,16 @@ class RadioInterface : public PacketTimes CallbackObserver notifyDeepSleepObserver = CallbackObserver(this, &RadioInterface::notifyDeepSleepCb); + /// Number of msecs we expect our shortest actual packet to be over the wire (used in retry timeout calcs) + uint32_t shortPacketMsec; + protected: + float bw = 125; + uint8_t sf = 9; + uint8_t cr = 7; + + uint16_t preambleLength = 32; // 8 is default, but FIXME use longer to increase the amount of sleep time when receiving + MeshPacket *sendingPacket = NULL; // The packet we are currently sending uint32_t lastTxStart = 0L; @@ -122,6 +131,7 @@ class RadioInterface : public PacketTimes * @return num msecs for the packet */ uint32_t getPacketTime(MeshPacket *p); + uint32_t getPacketTime(uint32_t totalPacketLen); protected: int8_t power = 17; // Set by applyModemConfig() diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index a40178b5..0d8fbc00 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -58,50 +58,6 @@ void INTERRUPT_ATTR RadioLibInterface::isrTxLevel0() */ RadioLibInterface *RadioLibInterface::instance; -/** - * Convert our modemConfig enum into wf, sf, etc... - */ -void RadioLibInterface::applyModemConfig() -{ - RadioInterface::applyModemConfig(); - - if (channelSettings.spread_factor == 0) { - switch (channelSettings.modem_config) { - case ChannelSettings_ModemConfig_Bw125Cr45Sf128: ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium - ///< range - bw = 125; - cr = 5; - sf = 7; - break; - case ChannelSettings_ModemConfig_Bw500Cr45Sf128: ///< Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short - ///< range - bw = 500; - cr = 5; - sf = 7; - break; - case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512: ///< Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long - ///< range - bw = 31.25; - cr = 8; - sf = 9; - break; - case ChannelSettings_ModemConfig_Bw125Cr48Sf4096: - bw = 125; - cr = 8; - sf = 12; - break; - default: - assert(0); // Unknown enum - } - } else { - sf = channelSettings.spread_factor; - cr = channelSettings.coding_rate; - bw = channelSettings.bandwidth; - - if (bw == 31) // This parameter is not an integer - bw = 31.25; - } -} /** Could we send right now (i.e. either not actively receving or transmitting)? */ bool RadioLibInterface::canSendImmediately() @@ -130,6 +86,8 @@ ErrorCode RadioLibInterface::send(MeshPacket *p) // Sometimes when testing it is useful to be able to never turn on the xmitter #ifndef LORA_DISABLE_SENDING printPacket("enqueuing for send", p); + uint32_t xmitMsec = getPacketTime(p); + DEBUG_MSG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad); ErrorCode res = txQueue.enqueue(p, 0) ? ERRNO_OK : ERRNO_UNKNOWN; @@ -158,7 +116,6 @@ bool RadioLibInterface::canSleep() return res; } - /** radio helper thread callback. We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and @@ -214,7 +171,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 : getTxDelayMsec(); + uint32_t delay = !withDelay ? 1 : getTxDelayMsec(); // DEBUG_MSG("xmit timer %d\n", delay); notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable } @@ -225,7 +182,7 @@ void RadioLibInterface::handleTransmitInterrupt() // DEBUG_MSG("handling lora TX interrupt\n"); // This can be null if we forced the device to enter standby mode. In that case // ignore the transmit interrupt - if(sendingPacket) + if (sendingPacket) completeSending(); } @@ -287,7 +244,7 @@ void RadioLibInterface::handleReceiveInterrupt() addReceiveMetadata(mp); mp->which_payload = MeshPacket_encrypted_tag; // Mark that the payload is still encrypted at this point - assert(payloadLen <= sizeof(mp->encrypted.bytes)); + assert(((uint32_t) payloadLen) <= sizeof(mp->encrypted.bytes)); memcpy(mp->encrypted.bytes, payload, payloadLen); mp->encrypted.size = payloadLen; diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index 86dcb970..e762fdcd 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -77,9 +77,6 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified PointerQueue txQueue = PointerQueue(MAX_TX_QUEUE); protected: - float bw = 125; - uint8_t sf = 9; - uint8_t cr = 7; /** * FIXME, use a meshtastic sync word, but hashed with the Channel name. Currently picking the same default @@ -88,7 +85,6 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified uint8_t syncWord = SX126X_SYNC_WORD_PRIVATE; float currentLimit = 100; // FIXME - uint16_t preambleLength = 32; // 8 is default, but FIXME use longer to increase the amount of sleep time when receiving LockingModule module; // The HW interface to the radio @@ -165,13 +161,6 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified /** Do any hardware setup needed on entry into send configuration for the radio. Subclasses can customize */ virtual void configHardwareForSend() {} - /** - * Convert our modemConfig enum into wf, sf, etc... - * - * These paramaters will be pull from the channelSettings global - */ - virtual void applyModemConfig(); - /** Could we send right now (i.e. either not actively receiving or transmitting)? */ virtual bool canSendImmediately();