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
This commit is contained in:
GUVWAF
2024-12-03 13:21:24 +01:00
committed by GitHub
parent d00e0f6911
commit 57ea6a265e
9 changed files with 121 additions and 57 deletions

View File

@@ -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,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;
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
#else
isReceiving = true;
receivingPacket = packetPool.allocCopy(*p);
handleReceiveInterrupt(); // Simulate receiving the packet immediately
startTransmitTimer();
#endif
}
meshtastic_QueueStatus SimRadio::getQueueStatus()
@@ -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;
LOG_DEBUG("HANDLE RECEIVE INTERRUPT");
rxGood++;
// 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);
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);
}
@@ -265,4 +326,4 @@ int16_t SimRadio::readData(uint8_t *data, size_t len)
}
return state;
}
}