From 7da0a5f7ecefbfc387e63190f34c0ffe2108cb83 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 4 Feb 2025 12:35:53 +1100 Subject: [PATCH] * new RX delays based on SNR --- examples/companion_radio/main.cpp | 4 +- examples/simple_repeater/main.cpp | 6 +++ examples/simple_room_server/main.cpp | 6 +++ examples/simple_secure_chat/main.cpp | 4 +- platformio.ini | 4 +- src/Dispatcher.cpp | 62 +++++++++++++++++++------ src/Dispatcher.h | 7 +++ src/helpers/CustomLLCC68Wrapper.h | 10 ++++ src/helpers/CustomSX1262Wrapper.h | 11 +++++ src/helpers/CustomSX1268Wrapper.h | 10 ++++ src/helpers/RadioLibWrappers.h | 2 + src/helpers/StaticPoolPacketManager.cpp | 9 +++- src/helpers/StaticPoolPacketManager.h | 4 +- 13 files changed, 116 insertions(+), 23 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 1ebec27e..9fd80bfd 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -46,8 +46,8 @@ #define SEND_TIMEOUT_BASE_MILLIS 300 #define FLOOD_SEND_TIMEOUT_FACTOR 16.0f -#define DIRECT_SEND_PERHOP_FACTOR 4.0f -#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 200 +#define DIRECT_SEND_PERHOP_FACTOR 5.0f +#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 #define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index af86837f..ec19d354 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -121,6 +121,7 @@ struct NodePrefs { // persisted to file float freq; uint8_t tx_power_dbm; uint8_t unused[3]; + float rx_delay_base; }; class MyMesh : public mesh::Mesh { @@ -193,6 +194,10 @@ protected: return true; // Yes, allow packet to be forwarded } + int calcRxDelay(float score, uint32_t air_time) const override { + return (int) ((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time); + } + void onAnonDataRecv(mesh::Packet* packet, uint8_t type, const mesh::Identity& sender, uint8_t* data, size_t len) override { if (type == PAYLOAD_TYPE_ANON_REQ) { // received an initial request by a possible admin client (unknown at this stage) uint32_t timestamp; @@ -371,6 +376,7 @@ public: // defaults _prefs.airtime_factor = 1.0; // one half + _prefs.rx_delay_base = 10.0; strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)-1); _prefs.node_name[sizeof(_prefs.node_name)-1] = 0; // truncate if necessary _prefs.node_lat = ADVERT_LAT; diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index b9880892..0dbe567f 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -138,6 +138,7 @@ struct NodePrefs { // persisted to file float freq; uint8_t tx_power_dbm; uint8_t unused[3]; + float rx_delay_base; }; class MyMesh : public mesh::Mesh { @@ -246,6 +247,10 @@ protected: return _prefs.airtime_factor; } + int calcRxDelay(float score, uint32_t air_time) const override { + return (int) ((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time); + } + #if ROOM_IS_ALSO_REPEATER bool allowPacketForward(const mesh::Packet* packet) override { return true; // Yes, allow packet to be forwarded @@ -484,6 +489,7 @@ public: // defaults _prefs.airtime_factor = 1.0; // one half + _prefs.rx_delay_base = 10.0; strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)-1); _prefs.node_name[sizeof(_prefs.node_name)-1] = 0; // truncate if necessary _prefs.node_lat = ADVERT_LAT; diff --git a/examples/simple_secure_chat/main.cpp b/examples/simple_secure_chat/main.cpp index 108db592..20bfbf19 100644 --- a/examples/simple_secure_chat/main.cpp +++ b/examples/simple_secure_chat/main.cpp @@ -44,8 +44,8 @@ #define SEND_TIMEOUT_BASE_MILLIS 300 #define FLOOD_SEND_TIMEOUT_FACTOR 16.0f -#define DIRECT_SEND_PERHOP_FACTOR 4.0f -#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 200 +#define DIRECT_SEND_PERHOP_FACTOR 5.0f +#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250 #define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg==" diff --git a/platformio.ini b/platformio.ini index 83f56fa5..ae984df7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,7 +16,7 @@ lib_deps = Wire jgromes/RadioLib @ ^6.3.0 rweather/Crypto @ ^0.4.0 -build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 +build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1 -D LORA_FREQ=867.5 -D LORA_BW=250 -D LORA_SF=10 @@ -69,7 +69,7 @@ build_flags = -D ADVERT_LAT=-37.0 -D ADVERT_LON=145.0 -D ADMIN_PASSWORD="\"password\"" -; -D MESH_PACKET_LOGGING=1 + -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/simple_repeater/main.cpp> lib_deps = diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index ab3fa356..be48c582 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -4,6 +4,8 @@ #include #endif +#include + namespace mesh { void Dispatcher::begin() { @@ -18,6 +20,10 @@ float Dispatcher::getAirtimeBudgetFactor() const { return 2.0; // default, 33.3% (1/3rd) } +int Dispatcher::calcRxDelay(float score, uint32_t air_time) const { + return (int) ((pow(10, 0.85f - score) - 1.0) * air_time); +} + void Dispatcher::loop() { if (outbound) { // waiting for outbound send to be completed if (_radio->isSendComplete()) { @@ -47,6 +53,13 @@ void Dispatcher::loop() { } } + // check inbound (delayed) queue + { + Packet* pkt = _mgr->getNextInbound(_ms->getMillis()); + if (pkt) { + processRecvPacket(pkt); + } + } checkRecv(); checkSend(); } @@ -57,6 +70,8 @@ void Dispatcher::onPacketSent(Packet* packet) { void Dispatcher::checkRecv() { Packet* pkt; + float score; + uint32_t air_time; { uint8_t raw[MAX_TRANS_UNIT]; int len = _radio->recvRaw(raw, MAX_TRANS_UNIT); @@ -87,6 +102,9 @@ void Dispatcher::checkRecv() { pkt->payload_len = len - i; // payload is remainder memcpy(pkt->payload, &raw[i], pkt->payload_len); + + score = _radio->packetScore(_radio->getLastSNR(), len); + air_time = _radio->getEstAirtimeFor(len); } } } else { @@ -94,27 +112,41 @@ void Dispatcher::checkRecv() { } } if (pkt) { + #if MESH_PACKET_LOGGING + Serial.printf("PACKET: recv, len=%d (type=%d, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d\n", + 2 + pkt->path_len + pkt->payload_len, pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len, + (int)_radio->getLastSNR(), (int)_radio->getLastRSSI(), (int)(score*1000)); + #endif + if (pkt->isRouteFlood()) { n_recv_flood++; + + int _delay = calcRxDelay(score, air_time); + if (_delay < 50) { + MESH_DEBUG_PRINTLN("Dispatcher::checkRecv(), score delay below threshold (%d)", _delay); + processRecvPacket(pkt); // is below the score delay threshold, so process immediately + } else { + MESH_DEBUG_PRINTLN("Dispatcher::checkRecv(), score delay is: %d millis", _delay); + _mgr->queueInbound(pkt, futureMillis(_delay)); // add to delayed inbound queue + } } else { n_recv_direct++; + processRecvPacket(pkt); } - #if MESH_PACKET_LOGGING - Serial.printf("PACKET: recv, len=%d (type=%d, route=%s, payload_len=%d) SNR=%d RSSI=%d\n", - 2 + pkt->path_len + pkt->payload_len, pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len, - (int)_radio->getLastSNR(), (int)_radio->getLastRSSI()); - #endif - DispatcherAction action = onRecvPacket(pkt); - if (action == ACTION_RELEASE) { - _mgr->free(pkt); - } else if (action == ACTION_MANUAL_HOLD) { - // sub-class is wanting to manually hold Packet instance, and call releasePacket() at appropriate time - } else { // ACTION_RETRANSMIT* - uint8_t priority = (action >> 24) - 1; - uint32_t _delay = action & 0xFFFFFF; + } +} - _mgr->queueOutbound(pkt, priority, futureMillis(_delay)); - } +void Dispatcher::processRecvPacket(Packet* pkt) { + DispatcherAction action = onRecvPacket(pkt); + if (action == ACTION_RELEASE) { + _mgr->free(pkt); + } else if (action == ACTION_MANUAL_HOLD) { + // sub-class is wanting to manually hold Packet instance, and call releasePacket() at appropriate time + } else { // ACTION_RETRANSMIT* + uint8_t priority = (action >> 24) - 1; + uint32_t _delay = action & 0xFFFFFF; + + _mgr->queueOutbound(pkt, priority, futureMillis(_delay)); } } diff --git a/src/Dispatcher.h b/src/Dispatcher.h index eea31460..d17fc51c 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -36,6 +36,8 @@ public: */ virtual uint32_t getEstAirtimeFor(int len_bytes) = 0; + virtual float packetScore(float snr, int packet_len) = 0; + /** * \brief starts the raw packet send. (no wait) * \param bytes the raw packet data @@ -77,6 +79,8 @@ public: virtual int getFreeCount() const = 0; virtual Packet* getOutboundByIdx(int i) = 0; virtual Packet* removeOutboundByIdx(int i) = 0; + virtual void queueInbound(Packet* packet, uint32_t scheduled_for) = 0; + virtual Packet* getNextInbound(uint32_t now) = 0; }; typedef uint32_t DispatcherAction; @@ -98,6 +102,8 @@ class Dispatcher { uint32_t n_recv_flood, n_recv_direct; uint32_t n_full_events; + void processRecvPacket(Packet* pkt); + protected: PacketManager* _mgr; Radio* _radio; @@ -112,6 +118,7 @@ protected: virtual DispatcherAction onRecvPacket(Packet* pkt) = 0; virtual void onPacketSent(Packet* packet); virtual float getAirtimeBudgetFactor() const; + virtual int calcRxDelay(float score, uint32_t air_time) const; public: void begin(); diff --git a/src/helpers/CustomLLCC68Wrapper.h b/src/helpers/CustomLLCC68Wrapper.h index 5647798c..b971fe34 100644 --- a/src/helpers/CustomLLCC68Wrapper.h +++ b/src/helpers/CustomLLCC68Wrapper.h @@ -18,4 +18,14 @@ public: } float getLastRSSI() const override { return ((CustomLLCC68 *)_radio)->getRSSI(); } float getLastSNR() const override { return ((CustomLLCC68 *)_radio)->getSNR(); } + + float packetScore(float snr, int packet_len) override { + int sf = ((CustomLLCC68 *)_radio)->spreadingFactor; + const float A = 0.7; + const float B = 0.4; + + float ber = exp(-pow(10, (snr / 10)) / (A * pow(10, (snr / 10)) + B * (1 << sf))); + + return pow(1 - ber, packet_len * 8); + } }; diff --git a/src/helpers/CustomSX1262Wrapper.h b/src/helpers/CustomSX1262Wrapper.h index a7708e8b..24676b4c 100644 --- a/src/helpers/CustomSX1262Wrapper.h +++ b/src/helpers/CustomSX1262Wrapper.h @@ -2,6 +2,7 @@ #include "CustomSX1262.h" #include "RadioLibWrappers.h" +#include class CustomSX1262Wrapper : public RadioLibWrapper { public: @@ -18,4 +19,14 @@ public: } float getLastRSSI() const override { return ((CustomSX1262 *)_radio)->getRSSI(); } float getLastSNR() const override { return ((CustomSX1262 *)_radio)->getSNR(); } + + float packetScore(float snr, int packet_len) override { + int sf = ((CustomSX1262 *)_radio)->spreadingFactor; + const float A = 0.7; + const float B = 0.4; + + float ber = exp(-pow(10, (snr / 10)) / (A * pow(10, (snr / 10)) + B * (1 << sf))); + + return pow(1 - ber, packet_len * 8); + } }; diff --git a/src/helpers/CustomSX1268Wrapper.h b/src/helpers/CustomSX1268Wrapper.h index 4c7a4ade..6120c4a1 100644 --- a/src/helpers/CustomSX1268Wrapper.h +++ b/src/helpers/CustomSX1268Wrapper.h @@ -18,4 +18,14 @@ public: } float getLastRSSI() const override { return ((CustomSX1268 *)_radio)->getRSSI(); } float getLastSNR() const override { return ((CustomSX1268 *)_radio)->getSNR(); } + + float packetScore(float snr, int packet_len) override { + int sf = ((CustomSX1268 *)_radio)->spreadingFactor; + const float A = 0.7; + const float B = 0.4; + + float ber = exp(-pow(10, (snr / 10)) / (A * pow(10, (snr / 10)) + B * (1 << sf))); + + return pow(1 - ber, packet_len * 8); + } }; diff --git a/src/helpers/RadioLibWrappers.h b/src/helpers/RadioLibWrappers.h index 4af18304..448af5a2 100644 --- a/src/helpers/RadioLibWrappers.h +++ b/src/helpers/RadioLibWrappers.h @@ -25,6 +25,8 @@ public: uint32_t getPacketsSent() const { return n_sent; } virtual float getLastRSSI() const override; virtual float getLastSNR() const override; + + float packetScore(float snr, int packet_len) override { return 0.85f; } // stub impl }; /** diff --git a/src/helpers/StaticPoolPacketManager.cpp b/src/helpers/StaticPoolPacketManager.cpp index ddf53232..3cd0906e 100644 --- a/src/helpers/StaticPoolPacketManager.cpp +++ b/src/helpers/StaticPoolPacketManager.cpp @@ -57,7 +57,7 @@ void PacketQueue::add(mesh::Packet* packet, uint8_t priority, uint32_t scheduled _num++; } -StaticPoolPacketManager::StaticPoolPacketManager(int pool_size): unused(pool_size), send_queue(pool_size) { +StaticPoolPacketManager::StaticPoolPacketManager(int pool_size): unused(pool_size), send_queue(pool_size), rx_queue(pool_size) { // load up our unusued Packet pool for (int i = 0; i < pool_size; i++) { unused.add(new mesh::Packet(), 0, 0); @@ -95,3 +95,10 @@ mesh::Packet* StaticPoolPacketManager::getOutboundByIdx(int i) { mesh::Packet* StaticPoolPacketManager::removeOutboundByIdx(int i) { return send_queue.removeByIdx(i); } + +void StaticPoolPacketManager::queueInbound(mesh::Packet* packet, uint32_t scheduled_for) { + // TODO +} +mesh::Packet* StaticPoolPacketManager::getNextInbound(uint32_t now) { + return NULL; // TODO +} diff --git a/src/helpers/StaticPoolPacketManager.h b/src/helpers/StaticPoolPacketManager.h index 7f6b1847..09c2fdec 100644 --- a/src/helpers/StaticPoolPacketManager.h +++ b/src/helpers/StaticPoolPacketManager.h @@ -18,7 +18,7 @@ public: }; class StaticPoolPacketManager : public mesh::PacketManager { - PacketQueue unused, send_queue; + PacketQueue unused, send_queue, rx_queue; public: StaticPoolPacketManager(int pool_size); @@ -31,4 +31,6 @@ public: int getFreeCount() const override; mesh::Packet* getOutboundByIdx(int i) override; mesh::Packet* removeOutboundByIdx(int i) override; + void queueInbound(mesh::Packet* packet, uint32_t scheduled_for) override; + mesh::Packet* getNextInbound(uint32_t now) override; }; \ No newline at end of file