mirror of
https://github.com/meshtastic/firmware.git
synced 2025-04-27 10:21:40 +00:00
Add content to SimRadio, similar to RadioInterface
This commit is contained in:
parent
067bde321b
commit
e66d9d0add
@ -0,0 +1,250 @@
|
|||||||
|
#include "SimRadio.h"
|
||||||
|
#include "MeshService.h"
|
||||||
|
#include "Router.h"
|
||||||
|
|
||||||
|
SimRadio::SimRadio()
|
||||||
|
{
|
||||||
|
instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SimRadio *SimRadio::instance;
|
||||||
|
|
||||||
|
ErrorCode SimRadio::send(MeshPacket *p)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
DEBUG_MSG("Set random delay before transmitting.\n");
|
||||||
|
setTransmitDelay();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimRadio::setTransmitDelay()
|
||||||
|
{
|
||||||
|
MeshPacket *p = txQueue.getFront();
|
||||||
|
// 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.
|
||||||
|
* This assumption is valid because of the offset generated by the radio to account for the noise
|
||||||
|
* floor.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
DEBUG_MSG("rx_snr found. hop_limit:%d rx_snr:%f\n", p->hop_limit, p->rx_snr);
|
||||||
|
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();
|
||||||
|
// DEBUG_MSG("xmit timer %d\n", delay);
|
||||||
|
delay(delayMsec);
|
||||||
|
onNotify(TRANSMIT_DELAY_COMPLETED);
|
||||||
|
} else {
|
||||||
|
DEBUG_MSG("TX QUEUE EMPTY!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
// DEBUG_MSG("xmit timer %d\n", delay);
|
||||||
|
delay(delayMsec);
|
||||||
|
onNotify(TRANSMIT_DELAY_COMPLETED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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++;
|
||||||
|
printPacket("Completed sending", p);
|
||||||
|
|
||||||
|
// We are done sending that packet, release it
|
||||||
|
packetPool.release(p);
|
||||||
|
// DEBUG_MSG("Done with send\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
||||||
|
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)
|
||||||
|
DEBUG_MSG("Can not send yet, busyTx\n");
|
||||||
|
if (busyRx)
|
||||||
|
DEBUG_MSG("Can not send yet, busyRx\n");
|
||||||
|
return false;
|
||||||
|
} else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SimRadio::isActivelyReceiving()
|
||||||
|
{
|
||||||
|
return false; // TODO check how this should be simulated
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SimRadio::isChannelActive()
|
||||||
|
{
|
||||||
|
return false; // TODO ask simulator
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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);
|
||||||
|
DEBUG_MSG("cancelSending id=0x%x, removed=%d\n", id, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SimRadio::onNotify(uint32_t notification)
|
||||||
|
{
|
||||||
|
switch (notification) {
|
||||||
|
case ISR_TX:
|
||||||
|
handleTransmitInterrupt();
|
||||||
|
DEBUG_MSG("tx complete - starting timer\n");
|
||||||
|
startTransmitTimer();
|
||||||
|
break;
|
||||||
|
case ISR_RX:
|
||||||
|
DEBUG_MSG("rx complete - starting timer\n");
|
||||||
|
break;
|
||||||
|
case TRANSMIT_DELAY_COMPLETED:
|
||||||
|
DEBUG_MSG("delay done\n");
|
||||||
|
|
||||||
|
// 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()) {
|
||||||
|
// DEBUG_MSG("Currently Rx/Tx-ing: set random delay\n");
|
||||||
|
setTransmitDelay(); // currently Rx/Tx-ing: reset random delay
|
||||||
|
} else {
|
||||||
|
if (isChannelActive()) { // check if there is currently a LoRa packet on the channel
|
||||||
|
// DEBUG_MSG("Channel is active: set random delay\n");
|
||||||
|
setTransmitDelay(); // reset random delay
|
||||||
|
} else {
|
||||||
|
// Send any outgoing packets we have ready
|
||||||
|
MeshPacket *txp = txQueue.dequeue();
|
||||||
|
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);
|
||||||
|
completeSending();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// DEBUG_MSG("done with txqueue\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0); // We expected to receive a valid notification from the ISR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** start an immediate transmit */
|
||||||
|
void SimRadio::startSend(MeshPacket * txp)
|
||||||
|
{
|
||||||
|
printPacket("Starting low level send", txp);
|
||||||
|
size_t numbytes = beginSending(txp);
|
||||||
|
MeshPacket* p = packetPool.allocCopy(*txp);
|
||||||
|
perhapsDecode(p);
|
||||||
|
Compressed c = Compressed_init_default;
|
||||||
|
c.portnum = p->decoded.portnum;
|
||||||
|
// DEBUG_MSG("Sending back to simulator with portNum %d\n", p->decoded.portnum);
|
||||||
|
if (p->decoded.payload.size <= sizeof(c.data.bytes)) {
|
||||||
|
memcpy(&c.data.bytes, p->decoded.payload.bytes, p->decoded.payload.size);
|
||||||
|
c.data.size = p->decoded.payload.size;
|
||||||
|
} else {
|
||||||
|
DEBUG_MSG("Payload size is larger than compressed message allows! Sending empty payload.\n");
|
||||||
|
}
|
||||||
|
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), Compressed_fields, &c);
|
||||||
|
p->decoded.portnum = PortNum_SIMULATOR_APP;
|
||||||
|
service.sendToPhone(p); // Sending back to simulator
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SimRadio::startReceive(MeshPacket *p) {
|
||||||
|
isReceiving = true;
|
||||||
|
handleReceiveInterrupt(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SimRadio::handleReceiveInterrupt(MeshPacket *p)
|
||||||
|
{
|
||||||
|
DEBUG_MSG("HANDLE RECEIVE INTERRUPT\n");
|
||||||
|
uint32_t xmitMsec;
|
||||||
|
assert(isReceiving);
|
||||||
|
isReceiving = false;
|
||||||
|
|
||||||
|
// read the number of actually received bytes
|
||||||
|
size_t length = getPacketLength(p);
|
||||||
|
xmitMsec = getPacketTime(length);
|
||||||
|
// DEBUG_MSG("Payload size %d vs length (includes header) %d\n", p->decoded.payload.size, length);
|
||||||
|
|
||||||
|
MeshPacket *mp = packetPool.allocCopy(*p); // keep a copy in packtPool
|
||||||
|
mp->which_payload_variant = MeshPacket_decoded_tag; // Mark that the payload is already decoded
|
||||||
|
|
||||||
|
printPacket("Lora RX", mp);
|
||||||
|
|
||||||
|
airTime->logAirtime(RX_LOG, xmitMsec);
|
||||||
|
|
||||||
|
deliverToReceiver(mp);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t SimRadio::getPacketLength(MeshPacket *mp) {
|
||||||
|
auto &p = mp->decoded;
|
||||||
|
return (size_t)p.payload.size+sizeof(PacketHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t SimRadio::readData(uint8_t* data, size_t len) {
|
||||||
|
int16_t state = RADIOLIB_ERR_NONE;
|
||||||
|
|
||||||
|
if(state == RADIOLIB_ERR_NONE) {
|
||||||
|
// add null terminator
|
||||||
|
data[len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "RadioInterface.h"
|
||||||
|
#include "MeshPacketQueue.h"
|
||||||
|
#include "wifi/WiFiServerAPI.h"
|
||||||
|
|
||||||
|
#define RADIOLIB_EXCLUDE_HTTP
|
||||||
|
#include <RadioLib.h>
|
||||||
|
|
||||||
|
class SimRadio : public RadioInterface
|
||||||
|
{
|
||||||
|
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:
|
||||||
|
SimRadio();
|
||||||
|
|
||||||
|
/** MeshService needs this to find our active instance
|
||||||
|
*/
|
||||||
|
static SimRadio *instance;
|
||||||
|
|
||||||
|
|
||||||
|
virtual ErrorCode send(MeshPacket *p) override;
|
||||||
|
|
||||||
|
/** can we detect a LoRa preamble on the current channel? */
|
||||||
|
virtual bool isChannelActive();
|
||||||
|
|
||||||
|
/** are we actively receiving a packet (only called during receiving state)
|
||||||
|
* This method is only public to facilitate debugging. Do not call.
|
||||||
|
*/
|
||||||
|
virtual bool isActivelyReceiving();
|
||||||
|
|
||||||
|
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
|
||||||
|
virtual bool cancelSending(NodeNum from, PacketId id) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start waiting to receive a message
|
||||||
|
*
|
||||||
|
* External functions can call this method to wake the device from sleep.
|
||||||
|
*/
|
||||||
|
virtual void startReceive(MeshPacket *p);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// are _trying_ to receive a packet currently (note - we might just be waiting for one)
|
||||||
|
bool isReceiving = false;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void setTransmitDelay();
|
||||||
|
|
||||||
|
/** random timer with certain min. and max. settings */
|
||||||
|
void startTransmitTimer(bool withDelay = true);
|
||||||
|
|
||||||
|
/** timer scaled to SNR of to be flooded packet */
|
||||||
|
void startTransmitTimerSNR(float snr);
|
||||||
|
|
||||||
|
void handleTransmitInterrupt();
|
||||||
|
void handleReceiveInterrupt(MeshPacket *p);
|
||||||
|
|
||||||
|
void onNotify(uint32_t notification);
|
||||||
|
|
||||||
|
// start an immediate transmit
|
||||||
|
virtual void startSend(MeshPacket *txp);
|
||||||
|
|
||||||
|
// derive packet length
|
||||||
|
size_t getPacketLength(MeshPacket *p);
|
||||||
|
|
||||||
|
int16_t readData(uint8_t* str, size_t len);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
|
||||||
|
virtual bool canSendImmediately();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a send was in progress finish it and return the buffer to the pool */
|
||||||
|
void completeSending();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
extern SimRadio *simRadio;
|
Loading…
Reference in New Issue
Block a user