mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-10-27 13:02:22 +00:00
Reset RX recovery deadline after successful transmissions
This commit is contained in:
1 parent
6db10c3986
commit
4bd4a77989
2 files changed
+110
-2
No files matched your search
+7
-2
@@ -429,10 +429,15 @@ void Dispatcher::loop() {
|
||||
}
|
||||
|
||||
_radio->onSendFinished();
|
||||
// RX can occur entirely between two loop observations during a TX
|
||||
// burst. Start a fresh RX recovery allowance after each successful TX;
|
||||
// otherwise a later CAD pause can trip the eight-second watchdog using
|
||||
// the start time of the whole healthy burst.
|
||||
radio_nonrx_start = _ms->getMillis();
|
||||
#ifdef RADIO_LIVENESS_SOFT_ONLY
|
||||
last_radio_activity_ms = _ms->getMillis(); // TX success -> radio is alive
|
||||
last_radio_activity_ms = radio_nonrx_start; // TX success -> radio is alive
|
||||
#else
|
||||
radio_liveness.noteActivity(_ms->getMillis()); // TX success -> radio is alive
|
||||
radio_liveness.noteActivity(radio_nonrx_start); // TX success -> radio is alive
|
||||
#endif
|
||||
restoreOutboundTxOverrides();
|
||||
logTx(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len);
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <helpers/RadioLivenessTracker.h>
|
||||
#include <helpers/radiolib/LR2021Band.h>
|
||||
#include <helpers/radiolib/RxBoostedGainDefaults.h>
|
||||
#include <Dispatcher.h>
|
||||
#include <helpers/StaticPoolPacketManager.h>
|
||||
|
||||
using mesh::RadioLivenessTracker;
|
||||
using mesh::RadioRecoveryAction;
|
||||
@@ -63,6 +65,107 @@ TEST(LR2021Band, SelectsTheCorrectFrontEnd) {
|
||||
EXPECT_TRUE(mesh::lr2021::isHighBand(2400.0f));
|
||||
}
|
||||
|
||||
namespace {
|
||||
class AdvancingClock : public mesh::MillisecondClock {
|
||||
public:
|
||||
unsigned long now = 1;
|
||||
unsigned long getMillis() override { return now++; }
|
||||
};
|
||||
|
||||
class BurstRadio : public mesh::Radio {
|
||||
public:
|
||||
bool receiving = true;
|
||||
bool cad_busy = false;
|
||||
bool broken_rx = false;
|
||||
int completed = 0;
|
||||
int recoveries = 0;
|
||||
int recvRaw(uint8_t*, int) override {
|
||||
if (!broken_rx) receiving = true;
|
||||
return 0;
|
||||
}
|
||||
uint32_t getEstAirtimeFor(int) override { return 1000; }
|
||||
float packetScore(float, int) override { return 1; }
|
||||
bool startSendRaw(const uint8_t*, int) override {
|
||||
receiving = false;
|
||||
return true;
|
||||
}
|
||||
bool isSendComplete() override { ++completed; return true; }
|
||||
void onSendFinished() override { receiving = false; }
|
||||
bool isInRecvMode() const override { return receiving; }
|
||||
bool isReceiving() override {
|
||||
// Active CAD may leave the radio outside RX until the next recvRaw().
|
||||
if (cad_busy) receiving = false;
|
||||
return cad_busy;
|
||||
}
|
||||
bool recoverRadio(bool) override {
|
||||
++recoveries;
|
||||
receiving = !broken_rx;
|
||||
return !broken_rx;
|
||||
}
|
||||
};
|
||||
|
||||
class WatchdogDispatcher : public mesh::Dispatcher {
|
||||
public:
|
||||
WatchdogDispatcher(BurstRadio& radio, AdvancingClock& clock,
|
||||
StaticPoolPacketManager& packets)
|
||||
: Dispatcher(radio, clock, packets) {}
|
||||
mesh::DispatcherAction onRecvPacket(mesh::Packet*) override { return ACTION_RELEASE; }
|
||||
uint16_t errors() const { return _err_flags; }
|
||||
};
|
||||
}
|
||||
|
||||
TEST(DispatcherRadioWatchdog, SuccessfulTxBurstDoesNotTriggerStuckRxRecovery) {
|
||||
AdvancingClock clock;
|
||||
BurstRadio radio;
|
||||
StaticPoolPacketManager packets(16);
|
||||
WatchdogDispatcher dispatcher(radio, clock, packets);
|
||||
dispatcher.begin();
|
||||
for (int i = 0; i < 12; ++i) {
|
||||
auto* packet = packets.allocNew();
|
||||
ASSERT_NE(packet, nullptr);
|
||||
packet->header = ROUTE_TYPE_DIRECT | (PAYLOAD_TYPE_TXT_MSG << 2);
|
||||
packet->path_len = 0;
|
||||
packet->payload_len = 1;
|
||||
packet->payload[0] = 0;
|
||||
ASSERT_TRUE(dispatcher.sendPacket(packet, 0));
|
||||
}
|
||||
dispatcher.loop();
|
||||
// RX happens inside each loop between completed TX and the next TX. The
|
||||
// watchdog at the top of the loop sees only TX, for longer than 8 seconds.
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
clock.now += 1000;
|
||||
if (i == 9) radio.cad_busy = true;
|
||||
dispatcher.loop();
|
||||
}
|
||||
ASSERT_EQ(radio.completed, 10);
|
||||
clock.now += 100;
|
||||
dispatcher.loop();
|
||||
EXPECT_EQ(radio.recoveries, 0);
|
||||
EXPECT_EQ(dispatcher.errors() & ERR_EVENT_STARTRX_TIMEOUT, 0);
|
||||
|
||||
// A subsequent genuine RX failure still gets its own recovery deadline.
|
||||
radio.broken_rx = true;
|
||||
clock.now += 8100;
|
||||
dispatcher.loop();
|
||||
EXPECT_EQ(radio.recoveries, 1);
|
||||
EXPECT_NE(dispatcher.errors() & ERR_EVENT_STARTRX_TIMEOUT, 0);
|
||||
}
|
||||
|
||||
TEST(DispatcherRadioWatchdog, StuckReceiverStillRecoversWithoutTransmitActivity) {
|
||||
AdvancingClock clock;
|
||||
BurstRadio radio;
|
||||
StaticPoolPacketManager packets(2);
|
||||
WatchdogDispatcher dispatcher(radio, clock, packets);
|
||||
dispatcher.begin();
|
||||
radio.broken_rx = true;
|
||||
radio.receiving = false;
|
||||
dispatcher.loop();
|
||||
clock.now += 8100;
|
||||
dispatcher.loop();
|
||||
EXPECT_EQ(radio.recoveries, 1);
|
||||
EXPECT_NE(dispatcher.errors() & ERR_EVENT_STARTRX_TIMEOUT, 0);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
|
||||
Reference in new issue
Block a user