Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions examples/companion_radio/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ float MyMesh::getAirtimeBudgetFactor() const {
return _prefs.airtime_factor;
}

uint32_t MyMesh::estimateTxAirtimeFor(int len_bytes, uint8_t tx_cr) const {
return estimateLoRaAirtimeFor(len_bytes, _prefs.sf, _prefs.bw, tx_cr != 0 ? tx_cr : _prefs.cr);
}

int MyMesh::getInterferenceThreshold() const {
return 0; // disabled for now, until currentRSSI() problem is resolved
}
Expand All @@ -259,12 +263,12 @@ int MyMesh::calcRxDelay(float score, uint32_t air_time) const {
}

uint32_t MyMesh::getRetransmitDelay(const mesh::Packet *packet) {
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * 0.5f);
return getRNG()->nextInt(0, 5*t + 1);
uint32_t tx_airtime_ms = (estimateTxAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2, packet->_tx_cr) * 0.5f);
return getRNG()->nextInt(0, 5 * tx_airtime_ms + 1);
}
uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) {
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * 0.2f);
return getRNG()->nextInt(0, 5*t + 1);
uint32_t tx_airtime_ms = (estimateTxAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2, packet->_tx_cr) * 0.2f);
return getRNG()->nextInt(0, 5 * tx_airtime_ms + 1);
}

uint8_t MyMesh::getExtraAckTransmitCount() const {
Expand Down Expand Up @@ -886,6 +890,7 @@ void MyMesh::begin(bool has_display) {
_store->loadChannels(this);

radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
setDefaultCR(_prefs.cr);
radio_set_tx_power(_prefs.tx_power_dbm);
}

Expand Down Expand Up @@ -1264,6 +1269,7 @@ void MyMesh::handleCmdFrame(size_t len) {
savePrefs();

radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
setDefaultCR(_prefs.cr);
MESH_DEBUG_PRINTLN("OK: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf,
(uint32_t)cr);

Expand Down Expand Up @@ -1619,10 +1625,13 @@ void MyMesh::handleCmdFrame(size_t len) {
memcpy(&auth, &cmd_frame[5], 4);
auto pkt = createTrace(tag, auth, flags);
if (pkt) {
if ((path_len >> path_sz) > 0) {
pkt->_tx_cr = selectCodingRateForPeer(&cmd_frame[10], 1 << path_sz);
}
sendDirect(pkt, &cmd_frame[10], path_len);

uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2);
uint32_t est_timeout = calcDirectTimeoutMillisFor(t, path_len >> path_sz);
uint32_t tx_airtime_ms = estimateTxAirtimeFor(pkt->payload_len + pkt->path_len + 2, pkt->_tx_cr);
uint32_t est_timeout = calcDirectTimeoutMillisFor(tx_airtime_ms, path_len >> path_sz);

out_frame[0] = RESP_CODE_SENT;
out_frame[1] = 0;
Expand Down
1 change: 1 addition & 0 deletions examples/companion_radio/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class MyMesh : public BaseChatMesh, public DataStoreHost {

protected:
float getAirtimeBudgetFactor() const override;
uint32_t estimateTxAirtimeFor(int len_bytes, uint8_t tx_cr=0) const override;
int getInterferenceThreshold() const override;
int calcRxDelay(float score, uint32_t air_time) const override;
uint32_t getRetransmitDelay(const mesh::Packet *packet) override;
Expand Down
48 changes: 44 additions & 4 deletions examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "MyMesh.h"
#include <algorithm>
#include <climits>

/* ------------------------------ Config -------------------------------- */

Expand Down Expand Up @@ -87,6 +88,42 @@ void MyMesh::putNeighbour(const mesh::Identity &id, uint32_t timestamp, float sn
#endif
}

int8_t MyMesh::findNeighbourSNR(const uint8_t* hash, uint8_t hash_size) {
#if MAX_NEIGHBOURS
for (int i = 0; i < MAX_NEIGHBOURS; i++) {
if (neighbours[i].heard_timestamp != 0 && neighbours[i].id.isHashMatch(hash, hash_size)) {
return neighbours[i].snr;
}
}
#endif
return INT8_MAX;
}

// Approximate SNR demod floor per SF (same as RadioLibWrappers.cpp)
static const float cr_snr_thresholds[] = {
-7.5f, // SF7
-10.0f, // SF8
-12.5f, // SF9
-15.0f, // SF10
-17.5f, // SF11
-20.0f // SF12
};

uint8_t MyMesh::selectCodingRateForPeer(const uint8_t* hash, uint8_t hash_size) {
int8_t snr4 = findNeighbourSNR(hash, hash_size);
if (snr4 == INT8_MAX) return 0; // unknown neighbor, use default

float snr = snr4 / 4.0f;
float threshold = (_prefs.sf >= 7 && _prefs.sf <= 12)
? cr_snr_thresholds[_prefs.sf - 7] : -15.0f;
float margin = snr - threshold;

if (margin < 3.0f) return 8;
if (margin < 6.0f) return 7;
if (margin < 10.0f) return 6;
return 5; // good margin, use lightest CR
}

uint8_t MyMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood) {
ClientInfo* client = NULL;
if (data[0] == 0) { // blank password, just check if sender is in ACL
Expand Down Expand Up @@ -524,12 +561,12 @@ int MyMesh::calcRxDelay(float score, uint32_t air_time) const {
}

uint32_t MyMesh::getRetransmitDelay(const mesh::Packet *packet) {
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.tx_delay_factor);
return getRNG()->nextInt(0, 5*t + 1);
uint32_t tx_airtime_ms = (estimateTxAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2, packet->_tx_cr) * _prefs.tx_delay_factor);
return getRNG()->nextInt(0, 5 * tx_airtime_ms + 1);
}
uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) {
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.direct_tx_delay_factor);
return getRNG()->nextInt(0, 5*t + 1);
uint32_t tx_airtime_ms = (estimateTxAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2, packet->_tx_cr) * _prefs.direct_tx_delay_factor);
return getRNG()->nextInt(0, 5 * tx_airtime_ms + 1);
}

bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) {
Expand Down Expand Up @@ -910,6 +947,7 @@ void MyMesh::begin(FILESYSTEM *fs) {

radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
radio_set_tx_power(_prefs.tx_power_dbm);
setDefaultCR(_prefs.cr);

updateAdvertTimer();
updateFloodAdvertTimer();
Expand Down Expand Up @@ -1295,12 +1333,14 @@ void MyMesh::loop() {
if (set_radio_at && millisHasNowPassed(set_radio_at)) { // apply pending (temporary) radio params
set_radio_at = 0; // clear timer
radio_set_params(pending_freq, pending_bw, pending_sf, pending_cr);
setDefaultCR(pending_cr);
MESH_DEBUG_PRINTLN("Temp radio params");
}

if (revert_radio_at && millisHasNowPassed(revert_radio_at)) { // revert radio params to orig
revert_radio_at = 0; // clear timer
radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
setDefaultCR(_prefs.cr);
MESH_DEBUG_PRINTLN("Radio params restored");
}

Expand Down
5 changes: 5 additions & 0 deletions examples/simple_repeater/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
#endif

void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr);
int8_t findNeighbourSNR(const uint8_t* hash, uint8_t hash_size);
void sendNodeDiscoverReq();
uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood);
uint8_t handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data);
Expand All @@ -134,6 +135,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
float getAirtimeBudgetFactor() const override {
return _prefs.airtime_factor;
}
uint32_t estimateTxAirtimeFor(int len_bytes, uint8_t tx_cr=0) const override {
return estimateLoRaAirtimeFor(len_bytes, _prefs.sf, _prefs.bw, tx_cr != 0 ? tx_cr : _prefs.cr);
}

bool allowPacketForward(const mesh::Packet* packet) override;
const char* getLogDateTime() override;
Expand All @@ -156,6 +160,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
uint8_t getExtraAckTransmitCount() const override {
return _prefs.multi_acks;
}
uint8_t selectCodingRateForPeer(const uint8_t* hash, uint8_t hash_size) override;

#if ENV_INCLUDE_GPS == 1
void applyGpsPrefs() {
Expand Down
11 changes: 7 additions & 4 deletions examples/simple_room_server/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,12 +266,12 @@ const char *MyMesh::getLogDateTime() {
}

uint32_t MyMesh::getRetransmitDelay(const mesh::Packet *packet) {
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.tx_delay_factor);
return getRNG()->nextInt(0, 5*t + 1);
uint32_t tx_airtime_ms = (estimateTxAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2, packet->_tx_cr) * _prefs.tx_delay_factor);
return getRNG()->nextInt(0, 5 * tx_airtime_ms + 1);
}
uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) {
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.direct_tx_delay_factor);
return getRNG()->nextInt(0, 5*t + 1);
uint32_t tx_airtime_ms = (estimateTxAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2, packet->_tx_cr) * _prefs.direct_tx_delay_factor);
return getRNG()->nextInt(0, 5 * tx_airtime_ms + 1);
}

bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
Expand Down Expand Up @@ -642,6 +642,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
acl.load(_fs, self_id);

radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
setDefaultCR(_prefs.cr);
radio_set_tx_power(_prefs.tx_power_dbm);

updateAdvertTimer();
Expand Down Expand Up @@ -872,12 +873,14 @@ void MyMesh::loop() {
if (set_radio_at && millisHasNowPassed(set_radio_at)) { // apply pending (temporary) radio params
set_radio_at = 0; // clear timer
radio_set_params(pending_freq, pending_bw, pending_sf, pending_cr);
setDefaultCR(pending_cr);
MESH_DEBUG_PRINTLN("Temp radio params");
}

if (revert_radio_at && millisHasNowPassed(revert_radio_at)) { // revert radio params to orig
revert_radio_at = 0; // clear timer
radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
setDefaultCR(_prefs.cr);
MESH_DEBUG_PRINTLN("Radio params restored");
}

Expand Down
3 changes: 3 additions & 0 deletions examples/simple_room_server/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
float getAirtimeBudgetFactor() const override {
return _prefs.airtime_factor;
}
uint32_t estimateTxAirtimeFor(int len_bytes, uint8_t tx_cr=0) const override {
return estimateLoRaAirtimeFor(len_bytes, _prefs.sf, _prefs.bw, tx_cr != 0 ? tx_cr : _prefs.cr);
}

void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override;
void logRx(mesh::Packet* pkt, int len, float score) override;
Expand Down
4 changes: 4 additions & 0 deletions examples/simple_secure_chat/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ class MyMesh : public BaseChatMesh, ContactVisitor {
float getAirtimeBudgetFactor() const override {
return _prefs.airtime_factor;
}
uint32_t estimateTxAirtimeFor(int len_bytes, uint8_t tx_cr=0) const override {
return estimateLoRaAirtimeFor(len_bytes, LORA_SF, LORA_BW, tx_cr != 0 ? tx_cr : LORA_CR);
}

int calcRxDelay(float score, uint32_t air_time) const override {
return 0; // disable rxdelay
Expand Down Expand Up @@ -578,6 +581,7 @@ void setup() {
#endif

radio_set_params(the_mesh.getFreqPref(), LORA_BW, LORA_SF, LORA_CR);
the_mesh.setDefaultCR(LORA_CR);
radio_set_tx_power(the_mesh.getTxPowerPref());

the_mesh.showWelcome();
Expand Down
11 changes: 7 additions & 4 deletions examples/simple_sensor/SensorMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,12 +313,12 @@ int SensorMesh::calcRxDelay(float score, uint32_t air_time) const {
}

uint32_t SensorMesh::getRetransmitDelay(const mesh::Packet* packet) {
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.tx_delay_factor);
return getRNG()->nextInt(0, 6)*t;
uint32_t tx_airtime_ms = (estimateTxAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2, packet->_tx_cr) * _prefs.tx_delay_factor);
return getRNG()->nextInt(0, 6) * tx_airtime_ms;
}
uint32_t SensorMesh::getDirectRetransmitDelay(const mesh::Packet* packet) {
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.direct_tx_delay_factor);
return getRNG()->nextInt(0, 6)*t;
uint32_t tx_airtime_ms = (estimateTxAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2, packet->_tx_cr) * _prefs.direct_tx_delay_factor);
return getRNG()->nextInt(0, 6) * tx_airtime_ms;
}
int SensorMesh::getInterferenceThreshold() const {
return _prefs.interference_threshold;
Expand Down Expand Up @@ -740,6 +740,7 @@ void SensorMesh::begin(FILESYSTEM* fs) {
acl.load(_fs, self_id);

radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
setDefaultCR(_prefs.cr);
radio_set_tx_power(_prefs.tx_power_dbm);

updateAdvertTimer();
Expand Down Expand Up @@ -884,12 +885,14 @@ void SensorMesh::loop() {
if (set_radio_at && millisHasNowPassed(set_radio_at)) { // apply pending (temporary) radio params
set_radio_at = 0; // clear timer
radio_set_params(pending_freq, pending_bw, pending_sf, pending_cr);
setDefaultCR(pending_cr);
MESH_DEBUG_PRINTLN("Temp radio params");
}

if (revert_radio_at && millisHasNowPassed(revert_radio_at)) { // revert radio params to orig
revert_radio_at = 0; // clear timer
radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
setDefaultCR(_prefs.cr);
MESH_DEBUG_PRINTLN("Radio params restored");
}

Expand Down
3 changes: 3 additions & 0 deletions examples/simple_sensor/SensorMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ class SensorMesh : public mesh::Mesh, public CommonCLICallbacks {

// Mesh overrides
float getAirtimeBudgetFactor() const override;
uint32_t estimateTxAirtimeFor(int len_bytes, uint8_t tx_cr=0) const override {
return estimateLoRaAirtimeFor(len_bytes, _prefs.sf, _prefs.bw, tx_cr != 0 ? tx_cr : _prefs.cr);
}
bool allowPacketForward(const mesh::Packet* packet) override;
int calcRxDelay(float score, uint32_t air_time) const override;
uint32_t getRetransmitDelay(const mesh::Packet* packet) override;
Expand Down
38 changes: 35 additions & 3 deletions src/Dispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,25 @@ uint32_t Dispatcher::getCADFailMaxDuration() const {
return 4000; // 4 seconds
}

uint32_t Dispatcher::estimateTxAirtimeFor(int len_bytes, uint8_t tx_cr) const {
(void)tx_cr;
return _radio->getEstAirtimeFor(len_bytes);
}

// Matches the standard LoRa time-on-air equation for the configured modem settings.
uint32_t Dispatcher::estimateLoRaAirtimeFor(int len_bytes, uint8_t sf, float bw_khz, uint8_t cr) const {
bool ldro_enabled = (((float)(1UL << sf)) / bw_khz) > 16.0f;
float low_data_rate_optimize = ldro_enabled ? 1.0f : 0.0f;
float implicit_header = 0.0f;
float crc_enabled = 1.0f;
float preamble_symbols = 16.0f;
float payload_symbols = 8.0f + fmaxf(ceilf((8.0f * (float)len_bytes - 4.0f * (float)sf + 28.0f + 16.0f * crc_enabled - 20.0f * implicit_header) /
(4.0f * (float)sf - 8.0f * low_data_rate_optimize)) * (float)cr, 0.0f);
float total_symbols = preamble_symbols + payload_symbols + 4.25f;
float symbol_length_ms = ((float)(uint32_t(1) << sf) / bw_khz);
return (uint32_t)ceilf(symbol_length_ms * total_symbols);
}

void Dispatcher::loop() {
if (millisHasNowPassed(next_floor_calib_time)) {
_radio->triggerNoiseFloorCalibrate(getInterferenceThreshold());
Expand Down Expand Up @@ -105,6 +124,9 @@ void Dispatcher::loop() {
}

_radio->onSendFinished();
if (outbound->_tx_cr != 0 && outbound->_tx_cr != _default_cr) {
_radio->setCodingRate(_default_cr);
}
logTx(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len);
if (outbound->isRouteFlood()) {
n_sent_flood++;
Expand All @@ -117,6 +139,9 @@ void Dispatcher::loop() {
MESH_DEBUG_PRINTLN("%s Dispatcher::loop(): WARNING: outbound packed send timed out!", getLogDateTime());

_radio->onSendFinished();
if (outbound->_tx_cr != 0 && outbound->_tx_cr != _default_cr) {
_radio->setCodingRate(_default_cr);
}
logTxFail(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len);

releasePacket(outbound); // return to pool
Expand Down Expand Up @@ -323,14 +348,20 @@ void Dispatcher::checkSend() {
} else {
memcpy(&raw[len], outbound->payload, outbound->payload_len); len += outbound->payload_len;

uint32_t max_airtime = _radio->getEstAirtimeFor(len)*3/2;
if (outbound->_tx_cr != 0 && outbound->_tx_cr != _default_cr) {
_radio->setCodingRate(outbound->_tx_cr);
}
uint32_t max_airtime = estimateTxAirtimeFor(len, outbound->_tx_cr) * 3 / 2;
outbound_start = _ms->getMillis();
bool success = _radio->startSendRaw(raw, len);
if (!success) {
MESH_DEBUG_PRINTLN("%s Dispatcher::loop(): ERROR: send start failed!", getLogDateTime());
if (outbound->_tx_cr != 0 && outbound->_tx_cr != _default_cr) {
_radio->setCodingRate(_default_cr);
}

logTxFail(outbound, outbound->getRawLength());

releasePacket(outbound); // return to pool
outbound = NULL;
return;
Expand Down Expand Up @@ -359,6 +390,7 @@ Packet* Dispatcher::obtainNewPacket() {
} else {
pkt->payload_len = pkt->path_len = 0;
pkt->_snr = 0;
pkt->_tx_cr = 0;
}
return pkt;
}
Expand Down Expand Up @@ -386,4 +418,4 @@ unsigned long Dispatcher::futureMillis(int millis_from_now) const {
return _ms->getMillis() + millis_from_now;
}

}
}
Loading