kopia lustrzana https://github.com/meshtastic/firmware
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 optionalpull/5499/head
rodzic
d00e0f6911
commit
57ea6a265e
|
@ -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()) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
Ładowanie…
Reference in New Issue