mirror of
https://github.com/meshtastic/firmware.git
synced 2025-05-04 04:47:25 +00:00
fix #513 scale retransmission times based on true packet time on wire
This commit is contained in:
parent
f346b4f0f2
commit
1839f8f7ca
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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()
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user