SimRadio: clean-up and emulate collisions (#5487)

* Clean up SimRadio and don't let it use PKC

* Add collision emulation for SimRadio

* Add stats from SimRadio to LocalStats

* Make emulating collisions optional
pull/5499/head
GUVWAF 2024-12-03 13:21:24 +01:00 zatwierdzone przez GitHub
rodzic d00e0f6911
commit 57ea6a265e
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
9 zmienionych plików z 121 dodań i 57 usunięć

Wyświetl plik

@ -82,7 +82,7 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr;
#include "STM32WLE5JCInterface.h" #include "STM32WLE5JCInterface.h"
#endif #endif
#if !HAS_RADIO && defined(ARCH_PORTDUINO) #if defined(ARCH_PORTDUINO)
#include "platform/portduino/SimRadio.h" #include "platform/portduino/SimRadio.h"
#endif #endif
@ -896,7 +896,7 @@ void setup()
} }
#endif #endif
#if !HAS_RADIO && defined(ARCH_PORTDUINO) #if defined(ARCH_PORTDUINO)
if (!rIf) { if (!rIf) {
rIf = new SimRadio; rIf = new SimRadio;
if (!rIf->init()) { if (!rIf->init()) {

Wyświetl plik

@ -166,27 +166,10 @@ NodeNum MeshService::getNodenumFromRequestId(uint32_t request_id)
*/ */
void MeshService::handleToRadio(meshtastic_MeshPacket &p) void MeshService::handleToRadio(meshtastic_MeshPacket &p)
{ {
#if defined(ARCH_PORTDUINO) && !HAS_RADIO #if defined(ARCH_PORTDUINO)
// Simulates device received a packet via the LoRa chip if (SimRadio::instance && p.decoded.portnum == meshtastic_PortNum_SIMULATOR_APP) {
if (p.decoded.portnum == meshtastic_PortNum_SIMULATOR_APP) { // Simulates device received a packet via the LoRa chip
// Simulator packet (=Compressed packet) is encapsulated in a MeshPacket, so need to unwrap first SimRadio::instance->unpackAndReceive(p);
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);
return; return;
} }
#endif #endif

Wyświetl plik

@ -10,7 +10,7 @@
#include "MeshTypes.h" #include "MeshTypes.h"
#include "Observer.h" #include "Observer.h"
#include "PointerQueue.h" #include "PointerQueue.h"
#if defined(ARCH_PORTDUINO) && !HAS_RADIO #if defined(ARCH_PORTDUINO)
#include "../platform/portduino/SimRadio.h" #include "../platform/portduino/SimRadio.h"
#endif #endif
#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) #if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
@ -165,4 +165,4 @@ class MeshService
friend class RoutingModule; friend class RoutingModule;
}; };
extern MeshService *service; extern MeshService *service;

Wyświetl plik

@ -613,13 +613,14 @@ bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p)
{ {
printPacket("PACKET FROM PHONE", &p); printPacket("PACKET FROM PHONE", &p);
// For use with the simulator, we should not ignore duplicate packets #if defined(ARCH_PORTDUINO)
#if !(defined(ARCH_PORTDUINO) && !HAS_RADIO) // For use with the simulator, we should not ignore duplicate packets from the phone
if (p.id > 0 && wasSeenRecently(p.id)) { if (SimRadio::instance == nullptr)
LOG_DEBUG("Ignore packet from phone, already seen recently");
return false;
}
#endif #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] && if (p.decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP && lastPortNumToRadio[p.decoded.portnum] &&
Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], THIRTY_SECONDS_MS)) { 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 return timeout ? -1 : 0; // If we timed out, MeshService should stop iterating through observers as we just removed one
} }

Wyświetl plik

@ -6,6 +6,7 @@
#include "NodeDB.h" #include "NodeDB.h"
#include "RTC.h" #include "RTC.h"
#include "configuration.h" #include "configuration.h"
#include "detect/LoRaRadioType.h"
#include "main.h" #include "main.h"
#include "mesh-pb-constants.h" #include "mesh-pb-constants.h"
#include "meshUtils.h" #include "meshUtils.h"
@ -491,6 +492,8 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
// is not in the local nodedb // is not in the local nodedb
// First, only PKC encrypt packets we are originating // First, only PKC encrypt packets we are originating
if (isFromUs(p) && if (isFromUs(p) &&
// Don't use PKC with simulator
radioType != SIM_RADIO &&
// Don't use PKC with Ham mode // Don't use PKC with Ham mode
!owner.is_licensed && !owner.is_licensed &&
// Don't use PKC if it's not explicitly requested and a non-primary channel is requested // Don't use PKC if it's not explicitly requested and a non-primary channel is requested

Wyświetl plik

@ -130,6 +130,14 @@ meshtastic_Telemetry DeviceTelemetryModule::getLocalStatsTelemetry()
telemetry.variant.local_stats.num_packets_rx_bad = RadioLibInterface::instance->rxBad; telemetry.variant.local_stats.num_packets_rx_bad = RadioLibInterface::instance->rxBad;
telemetry.variant.local_stats.num_tx_relay = RadioLibInterface::instance->txRelay; 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) { if (router) {
telemetry.variant.local_stats.num_rx_dupe = router->rxDupe; telemetry.variant.local_stats.num_rx_dupe = router->rxDupe;
telemetry.variant.local_stats.num_tx_relay_canceled = router->txRelayCanceled; telemetry.variant.local_stats.num_tx_relay_canceled = router->txRelayCanceled;

Wyświetl plik

@ -73,6 +73,10 @@ void SimRadio::handleTransmitInterrupt()
// ignore the transmit interrupt // ignore the transmit interrupt
if (sendingPacket) if (sendingPacket)
completeSending(); 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() void SimRadio::completeSending()
@ -84,6 +88,8 @@ void SimRadio::completeSending()
if (p) { if (p) {
txGood++; txGood++;
if (!isFromUs(p))
txRelay++;
printPacket("Completed sending", p); printPacket("Completed sending", p);
// We are done sending that packet, release it // We are done sending that packet, release it
@ -113,12 +119,12 @@ bool SimRadio::canSendImmediately()
bool SimRadio::isActivelyReceiving() bool SimRadio::isActivelyReceiving()
{ {
return false; // TODO check how this should be simulated return receivingPacket != nullptr;
} }
bool SimRadio::isChannelActive() 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 */ /** 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(); startTransmitTimer();
break; break;
case ISR_RX: case ISR_RX:
handleReceiveInterrupt();
// LOG_DEBUG("rx complete - starting timer"); // LOG_DEBUG("rx complete - starting timer");
startTransmitTimer(); startTransmitTimer();
break; break;
case TRANSMIT_DELAY_COMPLETED: 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"); LOG_DEBUG("delay done");
// If we are not currently in receive mode, then restart the random delay (this can happen if the main thread // 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) void SimRadio::startSend(meshtastic_MeshPacket *txp)
{ {
printPacket("Start low level send", txp); printPacket("Start low level send", txp);
isReceiving = false;
size_t numbytes = beginSending(txp); size_t numbytes = beginSending(txp);
meshtastic_MeshPacket *p = packetPool.allocCopy(*txp); meshtastic_MeshPacket *p = packetPool.allocCopy(*txp);
perhapsDecode(p); perhapsDecode(p);
@ -201,15 +214,64 @@ void SimRadio::startSend(meshtastic_MeshPacket *txp)
service->sendQueueStatusToPhone(router->getQueueStatus(), 0, p->id); service->sendQueueStatusToPhone(router->getQueueStatus(), 0, p->id);
service->sendToPhone(p); // Sending back to simulator service->sendToPhone(p); // Sending back to simulator
service->loop(); // Process the send immediately
}
// 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) void SimRadio::startReceive(meshtastic_MeshPacket *p)
{ {
#ifdef USERPREFS_SIMRADIO_EMULATE_COLLISIONS
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) {
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; isReceiving = true;
size_t length = getPacketLength(p); receivingPacket = packetPool.allocCopy(*p);
uint32_t xmitMsec = getPacketTime(length); uint32_t airtimeMsec = getPacketTime(p);
delay(xmitMsec); // Model the time it is busy receiving notifyLater(airtimeMsec, ISR_RX, false); // Model the time it is busy receiving
handleReceiveInterrupt(p); #else
isReceiving = true;
receivingPacket = packetPool.allocCopy(*p);
handleReceiveInterrupt(); // Simulate receiving the packet immediately
startTransmitTimer();
#endif
} }
meshtastic_QueueStatus SimRadio::getQueueStatus() meshtastic_QueueStatus SimRadio::getQueueStatus()
@ -223,28 +285,27 @@ meshtastic_QueueStatus SimRadio::getQueueStatus()
return qs; return qs;
} }
void SimRadio::handleReceiveInterrupt(meshtastic_MeshPacket *p) void SimRadio::handleReceiveInterrupt()
{ {
LOG_DEBUG("HANDLE RECEIVE INTERRUPT"); if (receivingPacket == nullptr) {
uint32_t xmitMsec; return;
}
if (!isReceiving) { if (!isReceiving) {
LOG_DEBUG("*** WAS_ASSERT *** handleReceiveInterrupt called when not in receive mode"); LOG_DEBUG("*** WAS_ASSERT *** handleReceiveInterrupt called when not in receive mode");
return; return;
} }
isReceiving = false; LOG_DEBUG("HANDLE RECEIVE INTERRUPT");
rxGood++;
// read the number of actually received bytes meshtastic_MeshPacket *mp = packetPool.allocCopy(*receivingPacket); // keep a copy in packetPool
size_t length = getPacketLength(p); packetPool.release(receivingPacket); // release the original
xmitMsec = getPacketTime(length); receivingPacket = nullptr;
// LOG_DEBUG("Payload size %d vs length (includes header) %d", p->decoded.payload.size, length);
meshtastic_MeshPacket *mp = packetPool.allocCopy(*p); // keep a copy in packetPool
printPacket("Lora RX", mp); printPacket("Lora RX", mp);
airTime->logAirtime(RX_LOG, xmitMsec); airTime->logAirtime(RX_LOG, getPacketTime(mp));
deliverToReceiver(mp); deliverToReceiver(mp);
} }
@ -265,4 +326,4 @@ int16_t SimRadio::readData(uint8_t *data, size_t len)
} }
return state; return state;
} }

Wyświetl plik

@ -11,11 +11,6 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr
{ {
enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED }; 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); MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE);
public: public:
@ -47,9 +42,17 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr
meshtastic_QueueStatus getQueueStatus() override; meshtastic_QueueStatus getQueueStatus() override;
// 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: protected:
/// are _trying_ to receive a packet currently (note - we might just be waiting for one) /// are _trying_ to receive a packet currently (note - we might just be waiting for one)
bool isReceiving = false; bool isReceiving = true;
private: private:
void setTransmitDelay(); void setTransmitDelay();
@ -61,7 +64,7 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr
void startTransmitTimerSNR(float snr); void startTransmitTimerSNR(float snr);
void handleTransmitInterrupt(); void handleTransmitInterrupt();
void handleReceiveInterrupt(meshtastic_MeshPacket *p); void handleReceiveInterrupt();
void onNotify(uint32_t notification); void onNotify(uint32_t notification);
@ -73,6 +76,8 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr
int16_t readData(uint8_t *str, size_t len); int16_t readData(uint8_t *str, size_t len);
meshtastic_MeshPacket *receivingPacket = nullptr; // The packet we are currently receiving
protected: protected:
/** 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();

Wyświetl plik

@ -11,6 +11,9 @@
#ifndef HAS_WIFI #ifndef HAS_WIFI
#define HAS_WIFI 1 #define HAS_WIFI 1
#endif #endif
#ifndef HAS_RADIO
#define HAS_RADIO 1
#endif
#ifndef HAS_RTC #ifndef HAS_RTC
#define HAS_RTC 1 #define HAS_RTC 1
#endif #endif