2022-10-01 11:59:20 +02:00
|
|
|
#include "SimRadio.h"
|
|
|
|
|
#include "MeshService.h"
|
|
|
|
|
#include "Router.h"
|
|
|
|
|
|
2023-02-11 10:34:08 +01:00
|
|
|
SimRadio::SimRadio() : NotifiedWorkerThread("SimRadio")
|
2022-10-01 11:59:20 +02:00
|
|
|
{
|
|
|
|
|
instance = this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SimRadio *SimRadio::instance;
|
|
|
|
|
|
2023-01-21 18:39:58 +01:00
|
|
|
ErrorCode SimRadio::send(meshtastic_MeshPacket *p)
|
2022-10-01 11:59:20 +02:00
|
|
|
{
|
|
|
|
|
printPacket("enqueuing for send", p);
|
|
|
|
|
|
|
|
|
|
ErrorCode res = txQueue.enqueue(p) ? ERRNO_OK : ERRNO_UNKNOWN;
|
|
|
|
|
|
|
|
|
|
if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks
|
|
|
|
|
packetPool.release(p);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set (random) transmit delay to let others reconfigure their radio,
|
|
|
|
|
// to avoid collisions and implement timing-based flooding
|
2024-11-04 20:09:23 +08:00
|
|
|
LOG_DEBUG("Set random delay before tx");
|
2022-10-01 11:59:20 +02:00
|
|
|
setTransmitDelay();
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimRadio::setTransmitDelay()
|
|
|
|
|
{
|
2023-01-21 18:39:58 +01:00
|
|
|
meshtastic_MeshPacket *p = txQueue.getFront();
|
2022-10-01 11:59:20 +02:00
|
|
|
// We want all sending/receiving to be done by our daemon thread.
|
|
|
|
|
// We use a delay here because this packet might have been sent in response to a packet we just received.
|
|
|
|
|
// So we want to make sure the other side has had a chance to reconfigure its radio.
|
|
|
|
|
|
|
|
|
|
/* We assume if rx_snr = 0 and rx_rssi = 0, the packet was generated locally.
|
2023-01-21 14:34:29 +01:00
|
|
|
* This assumption is valid because of the offset generated by the radio to account for the noise
|
|
|
|
|
* floor.
|
|
|
|
|
*/
|
2022-10-01 11:59:20 +02:00
|
|
|
if (p->rx_snr == 0 && p->rx_rssi == 0) {
|
|
|
|
|
startTransmitTimer(true);
|
|
|
|
|
} else {
|
|
|
|
|
// If there is a SNR, start a timer scaled based on that SNR.
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_DEBUG("rx_snr found. hop_limit:%d rx_snr:%f", p->hop_limit, p->rx_snr);
|
2022-10-01 11:59:20 +02:00
|
|
|
startTransmitTimerSNR(p->rx_snr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimRadio::startTransmitTimer(bool withDelay)
|
|
|
|
|
{
|
|
|
|
|
// If we have work to do and the timer wasn't already scheduled, schedule it now
|
|
|
|
|
if (!txQueue.empty()) {
|
|
|
|
|
uint32_t delayMsec = !withDelay ? 1 : getTxDelayMsec();
|
2024-10-14 06:11:43 +02:00
|
|
|
// LOG_DEBUG("xmit timer %d", delay);
|
2023-02-11 10:34:08 +01:00
|
|
|
notifyLater(delayMsec, TRANSMIT_DELAY_COMPLETED, false);
|
2022-10-01 11:59:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimRadio::startTransmitTimerSNR(float snr)
|
|
|
|
|
{
|
|
|
|
|
// If we have work to do and the timer wasn't already scheduled, schedule it now
|
|
|
|
|
if (!txQueue.empty()) {
|
|
|
|
|
uint32_t delayMsec = getTxDelayMsecWeighted(snr);
|
2024-10-14 06:11:43 +02:00
|
|
|
// LOG_DEBUG("xmit timer %d", delay);
|
2023-02-11 10:34:08 +01:00
|
|
|
notifyLater(delayMsec, TRANSMIT_DELAY_COMPLETED, false);
|
2022-10-01 11:59:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimRadio::handleTransmitInterrupt()
|
|
|
|
|
{
|
|
|
|
|
// This can be null if we forced the device to enter standby mode. In that case
|
|
|
|
|
// ignore the transmit interrupt
|
|
|
|
|
if (sendingPacket)
|
|
|
|
|
completeSending();
|
2024-12-03 13:21:24 +01:00
|
|
|
|
|
|
|
|
isReceiving = true;
|
|
|
|
|
if (receivingPacket) // This happens when we don't consider something a collision if we weren't sending long enough
|
|
|
|
|
handleReceiveInterrupt();
|
2022-10-01 11:59:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimRadio::completeSending()
|
|
|
|
|
{
|
|
|
|
|
// We are careful to clear sending packet before calling printPacket because
|
|
|
|
|
// that can take a long time
|
|
|
|
|
auto p = sendingPacket;
|
|
|
|
|
sendingPacket = NULL;
|
|
|
|
|
|
|
|
|
|
if (p) {
|
|
|
|
|
txGood++;
|
2024-12-03 13:21:24 +01:00
|
|
|
if (!isFromUs(p))
|
|
|
|
|
txRelay++;
|
2022-10-01 11:59:20 +02:00
|
|
|
printPacket("Completed sending", p);
|
|
|
|
|
|
|
|
|
|
// We are done sending that packet, release it
|
|
|
|
|
packetPool.release(p);
|
2024-10-14 06:11:43 +02:00
|
|
|
// LOG_DEBUG("Done with send");
|
2022-10-01 11:59:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-14 17:25:20 -04:00
|
|
|
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
|
2022-10-01 11:59:20 +02:00
|
|
|
bool SimRadio::canSendImmediately()
|
|
|
|
|
{
|
|
|
|
|
// We wait _if_ we are partially though receiving a packet (rather than just merely waiting for one).
|
|
|
|
|
// To do otherwise would be doubly bad because not only would we drop the packet that was on the way in,
|
|
|
|
|
// we almost certainly guarantee no one outside will like the packet we are sending.
|
|
|
|
|
bool busyTx = sendingPacket != NULL;
|
|
|
|
|
bool busyRx = isReceiving && isActivelyReceiving();
|
|
|
|
|
|
|
|
|
|
if (busyTx || busyRx) {
|
|
|
|
|
if (busyTx)
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_WARN("Can not send yet, busyTx");
|
2022-10-01 11:59:20 +02:00
|
|
|
if (busyRx)
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_WARN("Can not send yet, busyRx");
|
2022-10-01 11:59:20 +02:00
|
|
|
return false;
|
|
|
|
|
} else
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
bool SimRadio::isActivelyReceiving()
|
2022-10-01 11:59:20 +02:00
|
|
|
{
|
2024-12-03 13:21:24 +01:00
|
|
|
return receivingPacket != nullptr;
|
2022-10-01 11:59:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SimRadio::isChannelActive()
|
|
|
|
|
{
|
2024-12-03 13:21:24 +01:00
|
|
|
return receivingPacket != nullptr;
|
2022-10-01 11:59:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
|
|
|
|
|
bool SimRadio::cancelSending(NodeNum from, PacketId id)
|
|
|
|
|
{
|
|
|
|
|
auto p = txQueue.remove(from, id);
|
|
|
|
|
if (p)
|
|
|
|
|
packetPool.release(p); // free the packet we just removed
|
|
|
|
|
|
|
|
|
|
bool result = (p != NULL);
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_DEBUG("cancelSending id=0x%x, removed=%d", id, result);
|
2022-10-01 11:59:20 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimRadio::onNotify(uint32_t notification)
|
|
|
|
|
{
|
|
|
|
|
switch (notification) {
|
|
|
|
|
case ISR_TX:
|
|
|
|
|
handleTransmitInterrupt();
|
2024-10-14 06:11:43 +02:00
|
|
|
// LOG_DEBUG("tx complete - starting timer");
|
2023-01-21 14:34:29 +01:00
|
|
|
startTransmitTimer();
|
2022-10-01 11:59:20 +02:00
|
|
|
break;
|
|
|
|
|
case ISR_RX:
|
2024-12-03 13:21:24 +01:00
|
|
|
handleReceiveInterrupt();
|
2024-10-14 06:11:43 +02:00
|
|
|
// LOG_DEBUG("rx complete - starting timer");
|
2023-02-11 10:34:08 +01:00
|
|
|
startTransmitTimer();
|
2022-10-01 11:59:20 +02:00
|
|
|
break;
|
|
|
|
|
case TRANSMIT_DELAY_COMPLETED:
|
2024-12-03 13:21:24 +01:00
|
|
|
if (receivingPacket) { // This happens when we had a timer pending and we started receiving
|
|
|
|
|
handleReceiveInterrupt();
|
|
|
|
|
startTransmitTimer();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_DEBUG("delay done");
|
2022-10-01 11:59:20 +02:00
|
|
|
|
|
|
|
|
// If we are not currently in receive mode, then restart the random delay (this can happen if the main thread
|
|
|
|
|
// has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode?
|
|
|
|
|
if (!txQueue.empty()) {
|
|
|
|
|
if (!canSendImmediately()) {
|
2024-10-14 06:11:43 +02:00
|
|
|
// LOG_DEBUG("Currently Rx/Tx-ing: set random delay");
|
2022-10-01 11:59:20 +02:00
|
|
|
setTransmitDelay(); // currently Rx/Tx-ing: reset random delay
|
|
|
|
|
} else {
|
|
|
|
|
if (isChannelActive()) { // check if there is currently a LoRa packet on the channel
|
2024-10-14 06:11:43 +02:00
|
|
|
// LOG_DEBUG("Channel is active: set random delay");
|
2022-10-01 11:59:20 +02:00
|
|
|
setTransmitDelay(); // reset random delay
|
|
|
|
|
} else {
|
|
|
|
|
// Send any outgoing packets we have ready
|
2023-01-21 18:39:58 +01:00
|
|
|
meshtastic_MeshPacket *txp = txQueue.dequeue();
|
2022-10-01 11:59:20 +02:00
|
|
|
assert(txp);
|
|
|
|
|
startSend(txp);
|
|
|
|
|
// Packet has been sent, count it toward our TX airtime utilization.
|
|
|
|
|
uint32_t xmitMsec = getPacketTime(txp);
|
|
|
|
|
airTime->logAirtime(TX_LOG, xmitMsec);
|
2022-10-08 19:10:50 +02:00
|
|
|
|
2023-02-11 10:34:08 +01:00
|
|
|
notifyLater(xmitMsec, ISR_TX, false); // Model the time it is busy sending
|
2022-10-01 11:59:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2024-10-14 06:11:43 +02:00
|
|
|
// LOG_DEBUG("done with txqueue");
|
2022-10-01 11:59:20 +02:00
|
|
|
}
|
2023-01-21 14:34:29 +01:00
|
|
|
break;
|
2022-10-01 11:59:20 +02:00
|
|
|
default:
|
|
|
|
|
assert(0); // We expected to receive a valid notification from the ISR
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** start an immediate transmit */
|
2023-01-21 18:52:52 +01:00
|
|
|
void SimRadio::startSend(meshtastic_MeshPacket *txp)
|
2022-10-01 11:59:20 +02:00
|
|
|
{
|
2024-11-04 12:16:25 -06:00
|
|
|
printPacket("Start low level send", txp);
|
2024-12-03 13:21:24 +01:00
|
|
|
isReceiving = false;
|
2022-10-01 11:59:20 +02:00
|
|
|
size_t numbytes = beginSending(txp);
|
2023-01-21 18:39:58 +01:00
|
|
|
meshtastic_MeshPacket *p = packetPool.allocCopy(*txp);
|
2022-10-01 11:59:20 +02:00
|
|
|
perhapsDecode(p);
|
2023-01-21 18:39:58 +01:00
|
|
|
meshtastic_Compressed c = meshtastic_Compressed_init_default;
|
2023-01-21 14:34:29 +01:00
|
|
|
c.portnum = p->decoded.portnum;
|
2024-11-04 12:16:25 -06:00
|
|
|
// LOG_DEBUG("Send back to simulator with portNum %d", p->decoded.portnum);
|
2022-10-01 11:59:20 +02:00
|
|
|
if (p->decoded.payload.size <= sizeof(c.data.bytes)) {
|
|
|
|
|
memcpy(&c.data.bytes, p->decoded.payload.bytes, p->decoded.payload.size);
|
2023-01-21 14:34:29 +01:00
|
|
|
c.data.size = p->decoded.payload.size;
|
2022-10-01 11:59:20 +02:00
|
|
|
} else {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_WARN("Payload size larger than compressed message allows! Send empty payload");
|
2022-10-01 11:59:20 +02:00
|
|
|
}
|
2023-01-21 18:39:58 +01:00
|
|
|
p->decoded.payload.size =
|
|
|
|
|
pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_Compressed_msg, &c);
|
|
|
|
|
p->decoded.portnum = meshtastic_PortNum_SIMULATOR_APP;
|
2023-12-27 15:16:04 +01:00
|
|
|
|
2024-08-01 19:29:49 -05:00
|
|
|
service->sendQueueStatusToPhone(router->getQueueStatus(), 0, p->id);
|
|
|
|
|
service->sendToPhone(p); // Sending back to simulator
|
2024-12-03 13:21:24 +01:00
|
|
|
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);
|
2022-10-01 11:59:20 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-21 18:52:52 +01:00
|
|
|
void SimRadio::startReceive(meshtastic_MeshPacket *p)
|
2023-01-21 14:34:29 +01:00
|
|
|
{
|
2024-12-03 13:21:24 +01:00
|
|
|
#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
|
2022-10-01 11:59:20 +02:00
|
|
|
isReceiving = true;
|
2024-12-03 13:21:24 +01:00
|
|
|
receivingPacket = packetPool.allocCopy(*p);
|
|
|
|
|
handleReceiveInterrupt(); // Simulate receiving the packet immediately
|
|
|
|
|
startTransmitTimer();
|
|
|
|
|
#endif
|
2022-10-01 11:59:20 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-21 18:39:58 +01:00
|
|
|
meshtastic_QueueStatus SimRadio::getQueueStatus()
|
2023-01-04 14:56:52 +02:00
|
|
|
{
|
2023-01-21 18:39:58 +01:00
|
|
|
meshtastic_QueueStatus qs;
|
2023-01-04 14:56:52 +02:00
|
|
|
|
|
|
|
|
qs.res = qs.mesh_packet_id = 0;
|
|
|
|
|
qs.free = txQueue.getFree();
|
|
|
|
|
qs.maxlen = txQueue.getMaxLen();
|
|
|
|
|
|
|
|
|
|
return qs;
|
|
|
|
|
}
|
2022-10-01 11:59:20 +02:00
|
|
|
|
2024-12-03 13:21:24 +01:00
|
|
|
void SimRadio::handleReceiveInterrupt()
|
2022-10-01 11:59:20 +02:00
|
|
|
{
|
2024-12-03 13:21:24 +01:00
|
|
|
if (receivingPacket == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-01-07 15:24:46 +01:00
|
|
|
|
|
|
|
|
if (!isReceiving) {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_DEBUG("*** WAS_ASSERT *** handleReceiveInterrupt called when not in receive mode");
|
2023-01-07 15:24:46 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-03 13:21:24 +01:00
|
|
|
LOG_DEBUG("HANDLE RECEIVE INTERRUPT");
|
|
|
|
|
rxGood++;
|
2022-10-01 11:59:20 +02:00
|
|
|
|
2024-12-03 13:21:24 +01:00
|
|
|
meshtastic_MeshPacket *mp = packetPool.allocCopy(*receivingPacket); // keep a copy in packetPool
|
|
|
|
|
packetPool.release(receivingPacket); // release the original
|
|
|
|
|
receivingPacket = nullptr;
|
2022-10-01 11:59:20 +02:00
|
|
|
|
|
|
|
|
printPacket("Lora RX", mp);
|
|
|
|
|
|
2024-12-03 13:21:24 +01:00
|
|
|
airTime->logAirtime(RX_LOG, getPacketTime(mp));
|
2022-10-01 11:59:20 +02:00
|
|
|
|
|
|
|
|
deliverToReceiver(mp);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-21 18:39:58 +01:00
|
|
|
size_t SimRadio::getPacketLength(meshtastic_MeshPacket *mp)
|
2023-01-21 14:34:29 +01:00
|
|
|
{
|
2022-10-01 11:59:20 +02:00
|
|
|
auto &p = mp->decoded;
|
2023-01-21 14:34:29 +01:00
|
|
|
return (size_t)p.payload.size + sizeof(PacketHeader);
|
2022-10-01 11:59:20 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
int16_t SimRadio::readData(uint8_t *data, size_t len)
|
|
|
|
|
{
|
2022-10-01 11:59:20 +02:00
|
|
|
int16_t state = RADIOLIB_ERR_NONE;
|
|
|
|
|
|
2023-01-21 14:34:29 +01:00
|
|
|
if (state == RADIOLIB_ERR_NONE) {
|
2022-10-01 11:59:20 +02:00
|
|
|
// add null terminator
|
|
|
|
|
data[len] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return state;
|
2024-12-03 13:21:24 +01:00
|
|
|
}
|