mirror of
https://github.com/meshtastic/firmware.git
synced 2025-09-15 15:05:48 +00:00
Merge pull request #1394 from GUVWAF/master
Implement listen-before-talk mechanism
This commit is contained in:
commit
e7a825d1ba
@ -72,6 +72,16 @@ MeshPacket *MeshPacketQueue::dequeue()
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MeshPacket *MeshPacketQueue::getFront()
|
||||||
|
{
|
||||||
|
if (empty()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *p = queue.front();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
/** Attempt to find and remove a packet from this queue. Returns a pointer to the removed packet, or NULL if not found */
|
/** Attempt to find and remove a packet from this queue. Returns a pointer to the removed packet, or NULL if not found */
|
||||||
MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id)
|
MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id)
|
||||||
{
|
{
|
||||||
|
@ -28,6 +28,8 @@ class MeshPacketQueue
|
|||||||
|
|
||||||
MeshPacket *dequeue();
|
MeshPacket *dequeue();
|
||||||
|
|
||||||
|
MeshPacket *getFront();
|
||||||
|
|
||||||
/** Attempt to find and remove a packet from this queue. Returns the packet which was removed from the queue */
|
/** Attempt to find and remove a packet from this queue. Returns the packet which was removed from the queue */
|
||||||
MeshPacket *remove(NodeNum from, PacketId id);
|
MeshPacket *remove(NodeNum from, PacketId id);
|
||||||
};
|
};
|
||||||
|
@ -176,6 +176,25 @@ void RF95Interface::startReceive()
|
|||||||
enableInterrupt(isrRxLevel0);
|
enableInterrupt(isrRxLevel0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RF95Interface::isChannelActive()
|
||||||
|
{
|
||||||
|
// check if we can detect a LoRa preamble on the current channel
|
||||||
|
int16_t result;
|
||||||
|
setTransmitEnable(false);
|
||||||
|
setStandby(); // needed for smooth transition
|
||||||
|
result = lora->scanChannel();
|
||||||
|
|
||||||
|
if (result == PREAMBLE_DETECTED) {
|
||||||
|
// DEBUG_MSG("Channel is busy!\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(result != ERR_WRONG_MODEM);
|
||||||
|
|
||||||
|
// DEBUG_MSG("Channel is free!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
||||||
bool RF95Interface::isActivelyReceiving()
|
bool RF95Interface::isActivelyReceiving()
|
||||||
{
|
{
|
||||||
|
@ -40,6 +40,9 @@ class RF95Interface : public RadioLibInterface
|
|||||||
*/
|
*/
|
||||||
virtual void enableInterrupt(void (*callback)()) { lora->setDio0Action(callback); }
|
virtual void enableInterrupt(void (*callback)()) { lora->setDio0Action(callback); }
|
||||||
|
|
||||||
|
/** can we detect a LoRa preamble on the current channel? */
|
||||||
|
virtual bool isChannelActive() override;
|
||||||
|
|
||||||
/** are we actively receiving a packet (only called during receiving state) */
|
/** are we actively receiving a packet (only called during receiving state) */
|
||||||
virtual bool isActivelyReceiving() override;
|
virtual bool isActivelyReceiving() override;
|
||||||
|
|
||||||
|
@ -118,20 +118,10 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want all sending/receiving to be done by our daemon thread, We use a delay here because this packet might have been sent
|
// set (random) transmit delay to let others reconfigure their radio,
|
||||||
// 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
|
// to avoid collisions and implement timing-based flooding
|
||||||
|
// DEBUG_MSG("Set random delay before transmitting.\n");
|
||||||
/* We assume if rx_snr = 0 and rx_rssi = 0, the packet was not generated locally.
|
setTransmitDelay();
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
#else
|
#else
|
||||||
@ -164,8 +154,8 @@ bool RadioLibInterface::cancelSending(NodeNum from, PacketId id)
|
|||||||
/** radio helper thread callback.
|
/** radio helper thread callback.
|
||||||
|
|
||||||
We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and
|
We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and
|
||||||
wait a random delay of 50 to 200 ms to make sure we are not stomping on someone else. The 50ms delay at the beginning ensures all
|
wait a random delay of 100ms to 100ms+shortPacketMsec to make sure we are not stomping on someone else. The 100ms delay at the beginning ensures all
|
||||||
possible listeners have had time to finish processing the previous packet and now have their radio in RX state. The up to 200ms
|
possible listeners have had time to finish processing the previous packet and now have their radio in RX state. The up to 100ms+shortPacketMsec
|
||||||
random delay gives a chance for all possible senders to have high odds of detecting that someone else started transmitting first
|
random delay gives a chance for all possible senders to have high odds of detecting that someone else started transmitting first
|
||||||
and then they will wait until that packet finishes.
|
and then they will wait until that packet finishes.
|
||||||
|
|
||||||
@ -192,20 +182,26 @@ void RadioLibInterface::onNotify(uint32_t notification)
|
|||||||
case TRANSMIT_DELAY_COMPLETED:
|
case TRANSMIT_DELAY_COMPLETED:
|
||||||
// DEBUG_MSG("delay done\n");
|
// DEBUG_MSG("delay done\n");
|
||||||
|
|
||||||
// If we are not currently in receive mode, then restart the timer and try again later (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
|
||||||
// has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode?
|
// has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode?
|
||||||
if (!txQueue.empty()) {
|
if (!txQueue.empty()) {
|
||||||
if (!canSendImmediately()) {
|
if (!canSendImmediately()) {
|
||||||
startTransmitTimer(); // try again in a little while
|
// DEBUG_MSG("Currently Rx/Tx-ing: set random delay\n");
|
||||||
|
setTransmitDelay(); // currently Rx/Tx-ing: reset random delay
|
||||||
} else {
|
} else {
|
||||||
// Send any outgoing packets we have ready
|
if (isChannelActive()) { // check if there is currently a LoRa packet on the channel
|
||||||
MeshPacket *txp = txQueue.dequeue();
|
// DEBUG_MSG("Channel is active: set random delay\n");
|
||||||
assert(txp);
|
setTransmitDelay(); // reset random delay
|
||||||
startSend(txp);
|
} 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.
|
// Packet has been sent, count it toward our TX airtime utilization.
|
||||||
uint32_t xmitMsec = getPacketTime(txp);
|
uint32_t xmitMsec = getPacketTime(txp);
|
||||||
airTime->logAirtime(TX_LOG, xmitMsec);
|
airTime->logAirtime(TX_LOG, xmitMsec);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// DEBUG_MSG("done with txqueue\n");
|
// DEBUG_MSG("done with txqueue\n");
|
||||||
@ -216,6 +212,26 @@ void RadioLibInterface::onNotify(uint32_t notification)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RadioLibInterface::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 RadioLibInterface::startTransmitTimer(bool withDelay)
|
void RadioLibInterface::startTransmitTimer(bool withDelay)
|
||||||
{
|
{
|
||||||
// If we have work to do and the timer wasn't already scheduled, schedule it now
|
// If we have work to do and the timer wasn't already scheduled, schedule it now
|
||||||
|
@ -132,6 +132,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
|||||||
*/
|
*/
|
||||||
virtual void startReceive() = 0;
|
virtual void startReceive() = 0;
|
||||||
|
|
||||||
|
/** can we detect a LoRa preamble on the current channel? */
|
||||||
|
virtual bool isChannelActive() = 0;
|
||||||
|
|
||||||
/** are we actively receiving a packet (only called during receiving state)
|
/** are we actively receiving a packet (only called during receiving state)
|
||||||
* This method is only public to facilitate debugging. Do not call.
|
* This method is only public to facilitate debugging. Do not call.
|
||||||
*/
|
*/
|
||||||
@ -141,18 +144,14 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
|||||||
virtual bool cancelSending(NodeNum from, PacketId id) override;
|
virtual bool cancelSending(NodeNum from, PacketId id) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** if we have something waiting to send, start a short random timer so we can come check for collision before actually doing
|
/** if we have something waiting to send, start a short (random) timer so we can come check for collision before actually doing
|
||||||
* the transmit
|
* the transmit */
|
||||||
*
|
void setTransmitDelay();
|
||||||
* If the timer was already running, we just wait for that one to occur.
|
|
||||||
* */
|
/** random timer with certain min. and max. settings */
|
||||||
void startTransmitTimer(bool withDelay = true);
|
void startTransmitTimer(bool withDelay = true);
|
||||||
|
|
||||||
/** if we have something waiting to send, start a short scaled timer based on SNR so we can come check for collision before actually doing
|
/** timer scaled to SNR of to be flooded packet */
|
||||||
* the transmit
|
|
||||||
*
|
|
||||||
* If the timer was already running, we just wait for that one to occur.
|
|
||||||
* */
|
|
||||||
void startTransmitTimerSNR(float snr);
|
void startTransmitTimerSNR(float snr);
|
||||||
|
|
||||||
void handleTransmitInterrupt();
|
void handleTransmitInterrupt();
|
||||||
|
@ -226,6 +226,23 @@ void SX126xInterface<T>::startReceive()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
||||||
|
template<typename T>
|
||||||
|
bool SX126xInterface<T>::isChannelActive()
|
||||||
|
{
|
||||||
|
// check if we can detect a LoRa preamble on the current channel
|
||||||
|
int16_t result;
|
||||||
|
|
||||||
|
setStandby();
|
||||||
|
result = lora.scanChannel();
|
||||||
|
if (result == PREAMBLE_DETECTED)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
assert(result != ERR_WRONG_MODEM);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
||||||
template<typename T>
|
template<typename T>
|
||||||
bool SX126xInterface<T>::isActivelyReceiving()
|
bool SX126xInterface<T>::isActivelyReceiving()
|
||||||
|
@ -46,6 +46,9 @@ class SX126xInterface : public RadioLibInterface
|
|||||||
*/
|
*/
|
||||||
virtual void enableInterrupt(void (*callback)()) { lora.setDio1Action(callback); }
|
virtual void enableInterrupt(void (*callback)()) { lora.setDio1Action(callback); }
|
||||||
|
|
||||||
|
/** can we detect a LoRa preamble on the current channel? */
|
||||||
|
virtual bool isChannelActive() override;
|
||||||
|
|
||||||
/** are we actively receiving a packet (only called during receiving state) */
|
/** are we actively receiving a packet (only called during receiving state) */
|
||||||
virtual bool isActivelyReceiving() override;
|
virtual bool isActivelyReceiving() override;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user