fix #513 scale retransmission times based on true packet time on wire

This commit is contained in:
Kevin Hester 2020-11-14 10:07:25 +08:00
parent f346b4f0f2
commit 1839f8f7ca
5 changed files with 100 additions and 91 deletions

View File

@ -143,11 +143,15 @@ bool NodeDB::resetRadioConfig()
DEBUG_MSG("***** DEVELOPMENT MODE - DO NOT RELEASE *****\n"); DEBUG_MSG("***** DEVELOPMENT MODE - DO NOT RELEASE *****\n");
// Sleep quite frequently to stress test the BLE comms, broadcast position every 6 mins // Sleep quite frequently to stress test the BLE comms, broadcast position every 6 mins
radioConfig.preferences.screen_on_secs = 30; radioConfig.preferences.screen_on_secs = 10;
radioConfig.preferences.wait_bluetooth_secs = 30; radioConfig.preferences.wait_bluetooth_secs = 10;
radioConfig.preferences.position_broadcast_secs = 6 * 60; radioConfig.preferences.position_broadcast_secs = 6 * 60;
radioConfig.preferences.ls_secs = 60; radioConfig.preferences.ls_secs = 60;
radioConfig.preferences.region = RegionCode_TW; 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 // Update the global myRegion

View File

@ -53,57 +53,66 @@ separated by 2.16 MHz with respect to the adjacent channels. Channel zero starts
// 1kb was too small // 1kb was too small
#define RADIO_STACK_SIZE 4096 #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 * Calculate airtime per
* they don't both check that the airwaves are clear at the same moment. As long as they are off by some amount * https://www.rs-online.com/designspark/rel-assets/ds-assets/uploads/knowledge-items/application-notes-for-the-internet-of-things/LoRa%20Design%20Guide.pdf
* 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 * section 4
* *
* @return num msecs for the packet * @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 float bandwidthHz = bw * 1000.0f;
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 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 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 = 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 tPayload = numPayloadSym * tSym;
float tPacket = tPreamble + tPayload; 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; 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 */ /** The delay to use for retransmitting dropped packets */
uint32_t RadioInterface::getRetransmissionMsec(const MeshPacket *p) 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 */ /** The delay to use when we want to send something but the ether is busy */
uint32_t RadioInterface::getTxDelayMsec() 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) void printPacket(const char *prefix, const MeshPacket *p)
@ -208,8 +217,47 @@ void RadioInterface::applyModemConfig()
// Set up default configuration // Set up default configuration
// No Sync Words in LORA mode // 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; power = channelSettings.tx_power;
shortPacketMsec = getPacketTime(sizeof(PacketHeader));
assert(myRegion); // Should have been found in init 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 // 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 myRegion->numChannels: %d\n", myRegion->numChannels);
DEBUG_MSG("Radio channel_num: %d\n", channel_num); DEBUG_MSG("Radio channel_num: %d\n", channel_num);
DEBUG_MSG("Radio frequency: %f\n", freq); DEBUG_MSG("Radio frequency: %f\n", freq);
DEBUG_MSG("Short packet time: %u msec\n", shortPacketMsec);
} }
/** /**

View File

@ -36,7 +36,7 @@ typedef struct {
* *
* This defines the SOLE API for talking to radios (because soon we will have alternate radio implementations) * 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 friend class MeshRadio; // for debugging we let that class touch pool
PointerQueue<MeshPacket> *rxDest = NULL; PointerQueue<MeshPacket> *rxDest = NULL;
@ -50,7 +50,16 @@ class RadioInterface : public PacketTimes
CallbackObserver<RadioInterface, void *> notifyDeepSleepObserver = CallbackObserver<RadioInterface, void *> notifyDeepSleepObserver =
CallbackObserver<RadioInterface, void *>(this, &RadioInterface::notifyDeepSleepCb); CallbackObserver<RadioInterface, void *>(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: 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 MeshPacket *sendingPacket = NULL; // The packet we are currently sending
uint32_t lastTxStart = 0L; uint32_t lastTxStart = 0L;
@ -122,6 +131,7 @@ class RadioInterface : public PacketTimes
* @return num msecs for the packet * @return num msecs for the packet
*/ */
uint32_t getPacketTime(MeshPacket *p); uint32_t getPacketTime(MeshPacket *p);
uint32_t getPacketTime(uint32_t totalPacketLen);
protected: protected:
int8_t power = 17; // Set by applyModemConfig() int8_t power = 17; // Set by applyModemConfig()

View File

@ -58,50 +58,6 @@ void INTERRUPT_ATTR RadioLibInterface::isrTxLevel0()
*/ */
RadioLibInterface *RadioLibInterface::instance; 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)? */ /** Could we send right now (i.e. either not actively receving or transmitting)? */
bool RadioLibInterface::canSendImmediately() 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 // Sometimes when testing it is useful to be able to never turn on the xmitter
#ifndef LORA_DISABLE_SENDING #ifndef LORA_DISABLE_SENDING
printPacket("enqueuing for send", p); printPacket("enqueuing for send", p);
uint32_t xmitMsec = getPacketTime(p);
DEBUG_MSG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad); DEBUG_MSG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad);
ErrorCode res = txQueue.enqueue(p, 0) ? ERRNO_OK : ERRNO_UNKNOWN; ErrorCode res = txQueue.enqueue(p, 0) ? ERRNO_OK : ERRNO_UNKNOWN;
@ -158,7 +116,6 @@ bool RadioLibInterface::canSleep()
return res; return res;
} }
/** radio helper thread callback. /** radio helper thread callback.
We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and
@ -225,7 +182,7 @@ void RadioLibInterface::handleTransmitInterrupt()
// DEBUG_MSG("handling lora TX interrupt\n"); // DEBUG_MSG("handling lora TX interrupt\n");
// This can be null if we forced the device to enter standby mode. In that case // This can be null if we forced the device to enter standby mode. In that case
// ignore the transmit interrupt // ignore the transmit interrupt
if(sendingPacket) if (sendingPacket)
completeSending(); completeSending();
} }
@ -287,7 +244,7 @@ void RadioLibInterface::handleReceiveInterrupt()
addReceiveMetadata(mp); addReceiveMetadata(mp);
mp->which_payload = MeshPacket_encrypted_tag; // Mark that the payload is still encrypted at this point 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); memcpy(mp->encrypted.bytes, payload, payloadLen);
mp->encrypted.size = payloadLen; mp->encrypted.size = payloadLen;

View File

@ -77,9 +77,6 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
PointerQueue<MeshPacket> txQueue = PointerQueue<MeshPacket>(MAX_TX_QUEUE); PointerQueue<MeshPacket> txQueue = PointerQueue<MeshPacket>(MAX_TX_QUEUE);
protected: 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 * 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; uint8_t syncWord = SX126X_SYNC_WORD_PRIVATE;
float currentLimit = 100; // FIXME 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 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 */ /** Do any hardware setup needed on entry into send configuration for the radio. Subclasses can customize */
virtual void configHardwareForSend() {} 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)? */ /** Could we send right now (i.e. either not actively receiving or transmitting)? */
virtual bool canSendImmediately(); virtual bool canSendImmediately();