From bf038cbebb46a80b15daf96827c284f217ebe22a Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Sun, 1 Dec 2024 14:19:59 +0100 Subject: [PATCH 1/4] Clean up SimRadio and don't let it use PKC --- src/main.cpp | 4 ++-- src/mesh/MeshService.cpp | 25 ++++--------------------- src/mesh/MeshService.h | 4 ++-- src/mesh/PhoneAPI.cpp | 15 ++++++++------- src/mesh/Router.cpp | 3 +++ src/platform/portduino/SimRadio.cpp | 25 ++++++++++++++++++++++++- src/platform/portduino/SimRadio.h | 3 +++ src/platform/portduino/architecture.h | 3 +++ 8 files changed, 49 insertions(+), 33 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 9036cd59ca..44a89a4a7e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -83,7 +83,7 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr; #include "STM32WLE5JCInterface.h" #endif -#if !HAS_RADIO && defined(ARCH_PORTDUINO) +#if defined(ARCH_PORTDUINO) #include "platform/portduino/SimRadio.h" #endif @@ -897,7 +897,7 @@ void setup() } #endif -#if !HAS_RADIO && defined(ARCH_PORTDUINO) +#if defined(ARCH_PORTDUINO) if (!rIf) { rIf = new SimRadio; if (!rIf->init()) { diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 8f7717585d..af3f134989 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -166,27 +166,10 @@ NodeNum MeshService::getNodenumFromRequestId(uint32_t request_id) */ void MeshService::handleToRadio(meshtastic_MeshPacket &p) { -#if defined(ARCH_PORTDUINO) && !HAS_RADIO - // Simulates device received a packet via the LoRa chip - if (p.decoded.portnum == meshtastic_PortNum_SIMULATOR_APP) { - // Simulator packet (=Compressed packet) is encapsulated in a MeshPacket, so need to unwrap first - meshtastic_Compressed scratch; - meshtastic_Compressed *decoded = NULL; - if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag) { - memset(&scratch, 0, sizeof(scratch)); - p.decoded.payload.size = - pb_decode_from_bytes(p.decoded.payload.bytes, p.decoded.payload.size, &meshtastic_Compressed_msg, &scratch); - if (p.decoded.payload.size) { - decoded = &scratch; - // Extract the original payload and replace - memcpy(&p.decoded.payload, &decoded->data, sizeof(decoded->data)); - // Switch the port from PortNum_SIMULATOR_APP back to the original PortNum - p.decoded.portnum = decoded->portnum; - } else - LOG_ERROR("Error decoding proto for simulator message!"); - } - // Let SimRadio receive as if it did via its LoRa chip - SimRadio::instance->startReceive(&p); +#if defined(ARCH_PORTDUINO) + if (SimRadio::instance && p.decoded.portnum == meshtastic_PortNum_SIMULATOR_APP) { + // Simulates device received a packet via the LoRa chip + SimRadio::instance->unPackAndReceive(p); return; } #endif diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index 1ccca4e6df..268c4308f7 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -10,7 +10,7 @@ #include "MeshTypes.h" #include "Observer.h" #include "PointerQueue.h" -#if defined(ARCH_PORTDUINO) && !HAS_RADIO +#if defined(ARCH_PORTDUINO) #include "../platform/portduino/SimRadio.h" #endif #if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) @@ -165,4 +165,4 @@ class MeshService friend class RoutingModule; }; -extern MeshService *service; +extern MeshService *service; \ No newline at end of file diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 20421e73e7..f49718c5e7 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -613,13 +613,14 @@ bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p) { printPacket("PACKET FROM PHONE", &p); -// For use with the simulator, we should not ignore duplicate packets -#if !(defined(ARCH_PORTDUINO) && !HAS_RADIO) - if (p.id > 0 && wasSeenRecently(p.id)) { - LOG_DEBUG("Ignore packet from phone, already seen recently"); - return false; - } +#if defined(ARCH_PORTDUINO) + // For use with the simulator, we should not ignore duplicate packets from the phone + if (SimRadio::instance == nullptr) #endif + if (p.id > 0 && wasSeenRecently(p.id)) { + LOG_DEBUG("Ignore packet from phone, already seen recently"); + return false; + } if (p.decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP && lastPortNumToRadio[p.decoded.portnum] && Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], THIRTY_SECONDS_MS)) { @@ -656,4 +657,4 @@ int PhoneAPI::onNotify(uint32_t newValue) } return timeout ? -1 : 0; // If we timed out, MeshService should stop iterating through observers as we just removed one -} +} \ No newline at end of file diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 7b792db308..d7c60dff4b 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -6,6 +6,7 @@ #include "NodeDB.h" #include "RTC.h" #include "configuration.h" +#include "detect/LoRaRadioType.h" #include "main.h" #include "mesh-pb-constants.h" #include "meshUtils.h" @@ -492,6 +493,8 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p) // is not in the local nodedb // First, only PKC encrypt packets we are originating if (isFromUs(p) && + // Don't use PKC with simulator + radioType != SIM_RADIO && // Don't use PKC with Ham mode !owner.is_licensed && // Don't use PKC if it's not explicitly requested and a non-primary channel is requested diff --git a/src/platform/portduino/SimRadio.cpp b/src/platform/portduino/SimRadio.cpp index 0a77b6088c..fd667d53a3 100644 --- a/src/platform/portduino/SimRadio.cpp +++ b/src/platform/portduino/SimRadio.cpp @@ -203,6 +203,29 @@ void SimRadio::startSend(meshtastic_MeshPacket *txp) service->sendToPhone(p); // Sending back to simulator } +// Simulates device received a packet via the LoRa chip +void SimRadio::unPackAndReceive(meshtastic_MeshPacket &p) +{ + // Simulator packet (=Compressed packet) is encapsulated in a MeshPacket, so need to unwrap first + meshtastic_Compressed scratch; + meshtastic_Compressed *decoded = NULL; + if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag) { + memset(&scratch, 0, sizeof(scratch)); + p.decoded.payload.size = + pb_decode_from_bytes(p.decoded.payload.bytes, p.decoded.payload.size, &meshtastic_Compressed_msg, &scratch); + if (p.decoded.payload.size) { + decoded = &scratch; + // Extract the original payload and replace + memcpy(&p.decoded.payload, &decoded->data, sizeof(decoded->data)); + // Switch the port from PortNum_SIMULATOR_APP back to the original PortNum + p.decoded.portnum = decoded->portnum; + } else + LOG_ERROR("Error decoding proto for simulator message!"); + } + // Let SimRadio receive as if it did via its LoRa chip + startReceive(&p); +} + void SimRadio::startReceive(meshtastic_MeshPacket *p) { isReceiving = true; @@ -265,4 +288,4 @@ int16_t SimRadio::readData(uint8_t *data, size_t len) } return state; -} +} \ No newline at end of file diff --git a/src/platform/portduino/SimRadio.h b/src/platform/portduino/SimRadio.h index 1edb4963b4..1aedd5b72c 100644 --- a/src/platform/portduino/SimRadio.h +++ b/src/platform/portduino/SimRadio.h @@ -47,6 +47,9 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr meshtastic_QueueStatus getQueueStatus() override; + // Convert Compressed_msg to normal msg and receive it + void unPackAndReceive(meshtastic_MeshPacket &p); + protected: /// are _trying_ to receive a packet currently (note - we might just be waiting for one) bool isReceiving = false; diff --git a/src/platform/portduino/architecture.h b/src/platform/portduino/architecture.h index 321949226e..3dde871998 100644 --- a/src/platform/portduino/architecture.h +++ b/src/platform/portduino/architecture.h @@ -11,6 +11,9 @@ #ifndef HAS_WIFI #define HAS_WIFI 1 #endif +#ifndef HAS_RADIO +#define HAS_RADIO 1 +#endif #ifndef HAS_RTC #define HAS_RTC 1 #endif From 61ecbbcc57a52164b6a18541e38003999d3f97d4 Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Sun, 1 Dec 2024 18:12:39 +0100 Subject: [PATCH 2/4] Add collision emulation for SimRadio --- src/platform/portduino/SimRadio.cpp | 60 +++++++++++++++++++++-------- src/platform/portduino/SimRadio.h | 16 ++++---- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/platform/portduino/SimRadio.cpp b/src/platform/portduino/SimRadio.cpp index fd667d53a3..f8bc199bab 100644 --- a/src/platform/portduino/SimRadio.cpp +++ b/src/platform/portduino/SimRadio.cpp @@ -73,6 +73,10 @@ void SimRadio::handleTransmitInterrupt() // ignore the transmit interrupt if (sendingPacket) completeSending(); + + isReceiving = true; + if (receivingPacket) // This happens when we don't consider something a collision if we weren't sending long enough + handleReceiveInterrupt(); } void SimRadio::completeSending() @@ -84,6 +88,8 @@ void SimRadio::completeSending() if (p) { txGood++; + if (!isFromUs(p)) + txRelay++; printPacket("Completed sending", p); // We are done sending that packet, release it @@ -113,12 +119,12 @@ bool SimRadio::canSendImmediately() bool SimRadio::isActivelyReceiving() { - return false; // TODO check how this should be simulated + return receivingPacket != nullptr; } bool SimRadio::isChannelActive() { - return false; // TODO ask simulator + return receivingPacket != nullptr; } /** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */ @@ -142,10 +148,16 @@ void SimRadio::onNotify(uint32_t notification) startTransmitTimer(); break; case ISR_RX: + handleReceiveInterrupt(); // LOG_DEBUG("rx complete - starting timer"); startTransmitTimer(); break; case TRANSMIT_DELAY_COMPLETED: + if (receivingPacket) { // This happens when we had a timer pending and we started receiving + handleReceiveInterrupt(); + startTransmitTimer(); + break; + } LOG_DEBUG("delay done"); // If we are not currently in receive mode, then restart the random delay (this can happen if the main thread @@ -183,6 +195,7 @@ void SimRadio::onNotify(uint32_t notification) void SimRadio::startSend(meshtastic_MeshPacket *txp) { printPacket("Start low level send", txp); + isReceiving = false; size_t numbytes = beginSending(txp); meshtastic_MeshPacket *p = packetPool.allocCopy(*txp); perhapsDecode(p); @@ -201,6 +214,7 @@ void SimRadio::startSend(meshtastic_MeshPacket *txp) service->sendQueueStatusToPhone(router->getQueueStatus(), 0, p->id); service->sendToPhone(p); // Sending back to simulator + service->loop(); // Process the send immediately } // Simulates device received a packet via the LoRa chip @@ -228,11 +242,24 @@ void SimRadio::unPackAndReceive(meshtastic_MeshPacket &p) void SimRadio::startReceive(meshtastic_MeshPacket *p) { + if (isActivelyReceiving()) { + LOG_WARN("Collision detected, dropping current and previous packet!"); + rxBad++; + airTime->logAirtime(RX_ALL_LOG, getPacketTime(receivingPacket)); + packetPool.release(receivingPacket); + receivingPacket = nullptr; + return; + } else if (sendingPacket && (interval - tillRun(millis()) > preambleTimeMsec)) { + // If not yet transmitting for longer than preamble, do as if not collided (channel should actually be detected as + // active) + LOG_WARN("Collision detected during transmission!"); + return; + } + isReceiving = true; - size_t length = getPacketLength(p); - uint32_t xmitMsec = getPacketTime(length); - delay(xmitMsec); // Model the time it is busy receiving - handleReceiveInterrupt(p); + receivingPacket = packetPool.allocCopy(*p); + uint32_t airtimeMsec = getPacketTime(p); + notifyLater(airtimeMsec, ISR_RX, false); // Model the time it is busy receiving } meshtastic_QueueStatus SimRadio::getQueueStatus() @@ -246,28 +273,27 @@ meshtastic_QueueStatus SimRadio::getQueueStatus() return qs; } -void SimRadio::handleReceiveInterrupt(meshtastic_MeshPacket *p) +void SimRadio::handleReceiveInterrupt() { - LOG_DEBUG("HANDLE RECEIVE INTERRUPT"); - uint32_t xmitMsec; + if (receivingPacket == nullptr) { + return; + } if (!isReceiving) { LOG_DEBUG("*** WAS_ASSERT *** handleReceiveInterrupt called when not in receive mode"); return; } - isReceiving = false; - - // read the number of actually received bytes - size_t length = getPacketLength(p); - xmitMsec = getPacketTime(length); - // LOG_DEBUG("Payload size %d vs length (includes header) %d", p->decoded.payload.size, length); + LOG_DEBUG("HANDLE RECEIVE INTERRUPT"); + rxGood++; - meshtastic_MeshPacket *mp = packetPool.allocCopy(*p); // keep a copy in packetPool + meshtastic_MeshPacket *mp = packetPool.allocCopy(*receivingPacket); // keep a copy in packetPool + packetPool.release(receivingPacket); // release the original + receivingPacket = nullptr; printPacket("Lora RX", mp); - airTime->logAirtime(RX_LOG, xmitMsec); + airTime->logAirtime(RX_LOG, getPacketTime(mp)); deliverToReceiver(mp); } diff --git a/src/platform/portduino/SimRadio.h b/src/platform/portduino/SimRadio.h index 1aedd5b72c..9ecddc44a7 100644 --- a/src/platform/portduino/SimRadio.h +++ b/src/platform/portduino/SimRadio.h @@ -11,11 +11,6 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr { enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED }; - /** - * Debugging counts - */ - uint32_t rxBad = 0, rxGood = 0, txGood = 0; - MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE); public: @@ -50,9 +45,14 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr // Convert Compressed_msg to normal msg and receive it void unPackAndReceive(meshtastic_MeshPacket &p); + /** + * Debugging counts + */ + uint32_t rxBad = 0, rxGood = 0, txGood = 0, txRelay = 0; + protected: /// are _trying_ to receive a packet currently (note - we might just be waiting for one) - bool isReceiving = false; + bool isReceiving = true; private: void setTransmitDelay(); @@ -64,7 +64,7 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr void startTransmitTimerSNR(float snr); void handleTransmitInterrupt(); - void handleReceiveInterrupt(meshtastic_MeshPacket *p); + void handleReceiveInterrupt(); void onNotify(uint32_t notification); @@ -76,6 +76,8 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr int16_t readData(uint8_t *str, size_t len); + meshtastic_MeshPacket *receivingPacket = nullptr; // The packet we are currently receiving + protected: /** Could we send right now (i.e. either not actively receiving or transmitting)? */ virtual bool canSendImmediately(); From cbbb16775fa8886a22aa130baa79400dbb1226ff Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Sun, 1 Dec 2024 19:19:03 +0100 Subject: [PATCH 3/4] Add stats from SimRadio to LocalStats --- src/modules/Telemetry/DeviceTelemetry.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index 4989b88e22..192754e09e 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -130,6 +130,14 @@ meshtastic_Telemetry DeviceTelemetryModule::getLocalStatsTelemetry() telemetry.variant.local_stats.num_packets_rx_bad = RadioLibInterface::instance->rxBad; telemetry.variant.local_stats.num_tx_relay = RadioLibInterface::instance->txRelay; } +#ifdef ARCH_PORTDUINO + if (SimRadio::instance) { + telemetry.variant.local_stats.num_packets_tx = SimRadio::instance->txGood; + telemetry.variant.local_stats.num_packets_rx = SimRadio::instance->rxGood + SimRadio::instance->rxBad; + telemetry.variant.local_stats.num_packets_rx_bad = SimRadio::instance->rxBad; + telemetry.variant.local_stats.num_tx_relay = SimRadio::instance->txRelay; + } +#endif if (router) { telemetry.variant.local_stats.num_rx_dupe = router->rxDupe; telemetry.variant.local_stats.num_tx_relay_canceled = router->txRelayCanceled; From a506010177d0e964dee22d59e55542726cfedce0 Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Mon, 2 Dec 2024 19:12:33 +0100 Subject: [PATCH 4/4] Make emulating collisions optional --- src/mesh/MeshService.cpp | 2 +- src/platform/portduino/SimRadio.cpp | 26 +++++++++++++++++++------- src/platform/portduino/SimRadio.h | 2 +- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index af3f134989..773ab70532 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -169,7 +169,7 @@ void MeshService::handleToRadio(meshtastic_MeshPacket &p) #if defined(ARCH_PORTDUINO) if (SimRadio::instance && p.decoded.portnum == meshtastic_PortNum_SIMULATOR_APP) { // Simulates device received a packet via the LoRa chip - SimRadio::instance->unPackAndReceive(p); + SimRadio::instance->unpackAndReceive(p); return; } #endif diff --git a/src/platform/portduino/SimRadio.cpp b/src/platform/portduino/SimRadio.cpp index f8bc199bab..7e63b995ea 100644 --- a/src/platform/portduino/SimRadio.cpp +++ b/src/platform/portduino/SimRadio.cpp @@ -218,7 +218,7 @@ void SimRadio::startSend(meshtastic_MeshPacket *txp) } // Simulates device received a packet via the LoRa chip -void SimRadio::unPackAndReceive(meshtastic_MeshPacket &p) +void SimRadio::unpackAndReceive(meshtastic_MeshPacket &p) { // Simulator packet (=Compressed packet) is encapsulated in a MeshPacket, so need to unwrap first meshtastic_Compressed scratch; @@ -242,6 +242,7 @@ void SimRadio::unPackAndReceive(meshtastic_MeshPacket &p) void SimRadio::startReceive(meshtastic_MeshPacket *p) { +#ifdef USERPREFS_SIMRADIO_EMULATE_COLLISIONS if (isActivelyReceiving()) { LOG_WARN("Collision detected, dropping current and previous packet!"); rxBad++; @@ -249,17 +250,28 @@ void SimRadio::startReceive(meshtastic_MeshPacket *p) packetPool.release(receivingPacket); receivingPacket = nullptr; return; - } else if (sendingPacket && (interval - tillRun(millis()) > preambleTimeMsec)) { - // If not yet transmitting for longer than preamble, do as if not collided (channel should actually be detected as - // active) - LOG_WARN("Collision detected during transmission!"); - return; + } else if (sendingPacket) { + uint32_t airtimeLeft = tillRun(millis()); + if (airtimeLeft <= 0) { + LOG_WARN("Transmitting packet was already done"); + handleTransmitInterrupt(); // Finish sending first + } else if ((interval - airtimeLeft) > preambleTimeMsec) { + // Only if transmitting for longer than preamble there is a collision + // (channel should actually be detected as active otherwise) + LOG_WARN("Collision detected during transmission!"); + return; + } } - isReceiving = true; receivingPacket = packetPool.allocCopy(*p); uint32_t airtimeMsec = getPacketTime(p); notifyLater(airtimeMsec, ISR_RX, false); // Model the time it is busy receiving +#else + isReceiving = true; + receivingPacket = packetPool.allocCopy(*p); + handleReceiveInterrupt(); // Simulate receiving the packet immediately + startTransmitTimer(); +#endif } meshtastic_QueueStatus SimRadio::getQueueStatus() diff --git a/src/platform/portduino/SimRadio.h b/src/platform/portduino/SimRadio.h index 9ecddc44a7..c082444e54 100644 --- a/src/platform/portduino/SimRadio.h +++ b/src/platform/portduino/SimRadio.h @@ -43,7 +43,7 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr meshtastic_QueueStatus getQueueStatus() override; // Convert Compressed_msg to normal msg and receive it - void unPackAndReceive(meshtastic_MeshPacket &p); + void unpackAndReceive(meshtastic_MeshPacket &p); /** * Debugging counts