Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SimRadio: clean-up and emulate collisions #5487

Merged
merged 5 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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()) {
Expand Down
25 changes: 4 additions & 21 deletions src/mesh/MeshService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/mesh/MeshService.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -165,4 +165,4 @@ class MeshService
friend class RoutingModule;
};

extern MeshService *service;
extern MeshService *service;
15 changes: 8 additions & 7 deletions src/mesh/PhoneAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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
}
}
3 changes: 3 additions & 0 deletions src/mesh/Router.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions src/modules/Telemetry/DeviceTelemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
97 changes: 79 additions & 18 deletions src/platform/portduino/SimRadio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down Expand Up @@ -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 */
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -201,15 +214,64 @@ 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
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)
{
#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;
receivingPacket = packetPool.allocCopy(*p);
uint32_t airtimeMsec = getPacketTime(p);
notifyLater(airtimeMsec, ISR_RX, false); // Model the time it is busy receiving
#else
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);
handleReceiveInterrupt(); // Simulate receiving the packet immediately
startTransmitTimer();
#endif
}

meshtastic_QueueStatus SimRadio::getQueueStatus()
Expand All @@ -223,28 +285,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);
}
Expand All @@ -265,4 +326,4 @@ int16_t SimRadio::readData(uint8_t *data, size_t len)
}

return state;
}
}
19 changes: 12 additions & 7 deletions src/platform/portduino/SimRadio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -47,9 +42,17 @@ 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);

/**
* 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();
Expand All @@ -61,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);

Expand All @@ -73,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();
Expand Down
3 changes: 3 additions & 0 deletions src/platform/portduino/architecture.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading