Merge pull request #519 from geeksville/bug513

Bug513
This commit is contained in:
Kevin Hester 2020-11-14 10:21:44 +08:00 committed by GitHub
commit ac50b9544b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 148 additions and 82 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,6 +53,68 @@ 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
/**
* 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(uint32_t pl)
{
float bandwidthHz = bw * 1000.0f;
bool headDisable = false; // we currently always use the header
float tSym = (1 << sf) / bandwidthHz;
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.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;
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)
{
// 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()
{
/** 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)
{ {
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, 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,
@ -155,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
@ -171,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

@ -50,7 +50,16 @@ class RadioInterface
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;
@ -108,6 +117,22 @@ class RadioInterface
/// \return true if initialisation succeeded. /// \return true if initialisation succeeded.
virtual bool reconfigure() = 0; 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);
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,19 +116,6 @@ bool RadioLibInterface::canSleep()
return res; 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. /** 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
@ -226,8 +171,7 @@ void RadioLibInterface::startTransmitTimer(bool withDelay)
{ {
// If we have work to do and the timer wasn't already scheduled, schedule it now // If we have work to do and the timer wasn't already scheduled, schedule it now
if (!txQueue.isEmpty()) { if (!txQueue.isEmpty()) {
uint32_t delay = uint32_t delay = !withDelay ? 1 : getTxDelayMsec();
!withDelay ? 1 : random(MIN_TX_WAIT_MSEC, MAX_TX_WAIT_MSEC); // See documentation for loop() wrt these values
// DEBUG_MSG("xmit timer %d\n", delay); // DEBUG_MSG("xmit timer %d\n", delay);
notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable
} }
@ -238,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();
} }
@ -300,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();

View File

@ -111,7 +111,6 @@ PendingPacket::PendingPacket(MeshPacket *p)
{ {
packet = p; packet = p;
numRetransmissions = NUM_RETRANSMISSIONS - 1; // We subtract one, because we assume the user just did the first send numRetransmissions = NUM_RETRANSMISSIONS - 1; // We subtract one, because we assume the user just did the first send
setNextTx();
} }
PendingPacket *ReliableRouter::findPendingPacket(GlobalPacketId key) PendingPacket *ReliableRouter::findPendingPacket(GlobalPacketId key)
@ -151,6 +150,7 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p)
auto id = GlobalPacketId(p); auto id = GlobalPacketId(p);
auto rec = PendingPacket(p); auto rec = PendingPacket(p);
setNextTx(&rec);
stopRetransmission(p->from, p->id); stopRetransmission(p->from, p->id);
pending[id] = rec; pending[id] = rec;
@ -190,10 +190,9 @@ int32_t ReliableRouter::doRetransmissions()
// Queue again // Queue again
--p.numRetransmissions; --p.numRetransmissions;
p.setNextTx(); setNextTx(&p);
} }
} } else {
else {
// Not yet time // Not yet time
int32_t t = p.nextTxMsec - now; int32_t t = p.nextTxMsec - now;

View File

@ -46,8 +46,6 @@ struct PendingPacket {
PendingPacket() {} PendingPacket() {}
PendingPacket(MeshPacket *p); PendingPacket(MeshPacket *p);
void setNextTx() { nextTxMsec = millis() + random(20 * 1000L, 22 * 1000L); }
}; };
class GlobalPacketIdHashFunction class GlobalPacketIdHashFunction
@ -130,4 +128,8 @@ class ReliableRouter : public FloodingRouter
* @return the number of msecs until our next retransmission or MAXINT if none scheduled * @return the number of msecs until our next retransmission or MAXINT if none scheduled
*/ */
int32_t doRetransmissions(); int32_t doRetransmissions();
void setNextTx(PendingPacket *pending) {
assert(iface);
pending->nextTxMsec = millis() + iface->getRetransmissionMsec(pending->packet); }
}; };

View File

@ -14,12 +14,13 @@
class Router : protected concurrency::OSThread class Router : protected concurrency::OSThread
{ {
private: private:
RadioInterface *iface;
/// Packets which have just arrived from the radio, ready to be processed by this service and possibly /// Packets which have just arrived from the radio, ready to be processed by this service and possibly
/// forwarded to the phone. /// forwarded to the phone.
PointerQueue<MeshPacket> fromRadioQueue; PointerQueue<MeshPacket> fromRadioQueue;
protected:
RadioInterface *iface = NULL;
public: public:
/// Local services that want to see _every_ packet this node receives can observe this. /// 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 /// Observers should always return 0 and _copy_ any packets they want to keep for use later (this packet will be getting