Browse Source

Merge 25f65735df into db232808aa

pull/2218/merge
Jiro 18 hours ago
committed by GitHub
parent
commit
dedcb33710
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 12
      examples/simple_repeater/MyMesh.cpp
  2. 12
      examples/simple_room_server/MyMesh.cpp
  3. 16
      examples/simple_sensor/SensorMesh.cpp
  4. 1
      src/Dispatcher.cpp
  5. 5
      src/Dispatcher.h
  6. 11
      src/helpers/BaseChatMesh.cpp
  7. 1
      src/helpers/radiolib/CustomLR1110.h
  8. 2
      src/helpers/radiolib/CustomLR1110Wrapper.h
  9. 2
      src/helpers/radiolib/CustomSX1262Wrapper.h
  10. 55
      src/helpers/radiolib/RadioLibWrappers.cpp
  11. 36
      src/helpers/radiolib/RadioLibWrappers.h

12
examples/simple_repeater/MyMesh.cpp

@ -541,10 +541,22 @@ int MyMesh::calcRxDelay(float score, uint32_t air_time) const {
}
uint32_t MyMesh::getRetransmitDelay(const mesh::Packet *packet) {
if (_radio->isAS923_1_JP()) {
// JP LBT: suppress txdelay to jitter-scale to avoid adding unnecessary
// latency on top of LBT backoff. A window equal to jitter_max gives
// ~33% collision reduction vs zero, scales naturally with airtime as
// CR changes, and keeps average added delay to ~56ms at SF12/BW125.
uint32_t jitter_max = _radio->getEstAirtimeFor(MAX_TRANS_UNIT) / RadioLibWrapper::JP_LBT_JITTER_DIVISOR;
return getRNG()->nextInt(0, jitter_max + 1);
}
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.tx_delay_factor);
return getRNG()->nextInt(0, 5*t + 1);
}
uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) {
if (_radio->isAS923_1_JP()) {
uint32_t jitter_max = _radio->getEstAirtimeFor(MAX_TRANS_UNIT) / RadioLibWrapper::JP_LBT_JITTER_DIVISOR;
return getRNG()->nextInt(0, jitter_max + 1);
}
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.direct_tx_delay_factor);
return getRNG()->nextInt(0, 5*t + 1);
}

12
examples/simple_room_server/MyMesh.cpp

@ -289,10 +289,22 @@ const char *MyMesh::getLogDateTime() {
}
uint32_t MyMesh::getRetransmitDelay(const mesh::Packet *packet) {
if (_radio->isAS923_1_JP()) {
// JP LBT: suppress txdelay to jitter-scale to avoid adding unnecessary
// latency on top of LBT backoff. A window equal to jitter_max gives
// ~33% collision reduction vs zero, scales naturally with airtime as
// CR changes, and keeps average added delay to ~56ms at SF12/BW125.
uint32_t jitter_max = _radio->getEstAirtimeFor(MAX_TRANS_UNIT) / RadioLibWrapper::JP_LBT_JITTER_DIVISOR;
return getRNG()->nextInt(0, jitter_max + 1);
}
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.tx_delay_factor);
return getRNG()->nextInt(0, 5*t + 1);
}
uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) {
if (_radio->isAS923_1_JP()) {
uint32_t jitter_max = _radio->getEstAirtimeFor(MAX_TRANS_UNIT) / RadioLibWrapper::JP_LBT_JITTER_DIVISOR;
return getRNG()->nextInt(0, jitter_max + 1);
}
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.direct_tx_delay_factor);
return getRNG()->nextInt(0, 5*t + 1);
}

16
examples/simple_sensor/SensorMesh.cpp

@ -313,12 +313,24 @@ int SensorMesh::calcRxDelay(float score, uint32_t air_time) const {
}
uint32_t SensorMesh::getRetransmitDelay(const mesh::Packet* packet) {
if (_radio->isAS923_1_JP()) {
// JP LBT: suppress txdelay to jitter-scale to avoid adding unnecessary
// latency on top of LBT backoff. A window equal to jitter_max gives
// ~33% collision reduction vs zero, scales naturally with airtime as
// CR changes, and keeps average added delay to ~56ms at SF12/BW125.
uint32_t jitter_max = _radio->getEstAirtimeFor(MAX_TRANS_UNIT) / RadioLibWrapper::JP_LBT_JITTER_DIVISOR;
return getRNG()->nextInt(0, jitter_max + 1);
}
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.tx_delay_factor);
return getRNG()->nextInt(0, 6)*t;
return getRNG()->nextInt(0, 5*t + 1);
}
uint32_t SensorMesh::getDirectRetransmitDelay(const mesh::Packet* packet) {
if (_radio->isAS923_1_JP()) {
uint32_t jitter_max = _radio->getEstAirtimeFor(MAX_TRANS_UNIT) / RadioLibWrapper::JP_LBT_JITTER_DIVISOR;
return getRNG()->nextInt(0, jitter_max + 1);
}
uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.direct_tx_delay_factor);
return getRNG()->nextInt(0, 6)*t;
return getRNG()->nextInt(0, 5*t + 1);
}
int SensorMesh::getInterferenceThreshold() const {
return _prefs.interference_threshold;

1
src/Dispatcher.cpp

@ -60,6 +60,7 @@ uint32_t Dispatcher::getCADFailRetryDelay() const {
return 200;
}
uint32_t Dispatcher::getCADFailMaxDuration() const {
if (_radio->isAS923_1_JP()) return UINT32_MAX; // ARIB STD-T108: never force TX during LBT
return 4000; // 4 seconds
}

5
src/Dispatcher.h

@ -78,6 +78,11 @@ public:
virtual float getLastRSSI() const { return 0; }
virtual float getLastSNR() const { return 0; }
virtual bool isAS923_1_JP() const { return false; }
virtual int getMaxTextLen() const { return 10 * 16; } // default 160 bytes
virtual int getMaxGroupTextLen() const { return 10 * 16; } // default 160 bytes
};
/**

11
src/helpers/BaseChatMesh.cpp

@ -419,8 +419,9 @@ void BaseChatMesh::onGroupDataRecv(mesh::Packet* packet, uint8_t type, const mes
mesh::Packet* BaseChatMesh::composeMsgPacket(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char *text, uint32_t& expected_ack) {
int text_len = strlen(text);
if (text_len > MAX_TEXT_LEN) return NULL;
if (attempt > 3 && text_len > MAX_TEXT_LEN-2) return NULL;
int max_len = _radio->getMaxTextLen();
if (text_len > max_len) return NULL;
if (attempt > 3 && text_len > max_len - 2) return NULL;
uint8_t temp[5+MAX_TEXT_LEN+1];
memcpy(temp, &timestamp, 4); // mostly an extra blob to help make packet_hash unique
@ -460,7 +461,8 @@ int BaseChatMesh::sendMessage(const ContactInfo& recipient, uint32_t timestamp,
int BaseChatMesh::sendCommandData(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& est_timeout) {
int text_len = strlen(text);
if (text_len > MAX_TEXT_LEN) return MSG_SEND_FAILED;
int max_len = _radio->getMaxTextLen();
if (text_len > max_len) return MSG_SEND_FAILED;
uint8_t temp[5+MAX_TEXT_LEN+1];
memcpy(temp, &timestamp, 4); // mostly an extra blob to help make packet_hash unique
@ -493,7 +495,8 @@ bool BaseChatMesh::sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& chan
char *ep = strchr((char *) &temp[5], 0);
int prefix_len = ep - (char *) &temp[5];
if (text_len + prefix_len > MAX_TEXT_LEN) text_len = MAX_TEXT_LEN - prefix_len;
int max_len = _radio->getMaxGroupTextLen();
if (text_len + prefix_len > max_len) text_len = max_len - prefix_len;
memcpy(ep, text, text_len);
ep[text_len] = 0; // null terminator

1
src/helpers/radiolib/CustomLR1110.h

@ -92,4 +92,5 @@ class CustomLR1110 : public LR1110 {
}
uint8_t getSpreadingFactor() const { return spreadingFactor; }
uint8_t getCodingRate() const { return this->codingRate + 4; } // RadioLib stores 1-4, return 5-8
};

2
src/helpers/radiolib/CustomLR1110Wrapper.h

@ -43,6 +43,8 @@ public:
float getLastSNR() const override { return ((CustomLR1110 *)_radio)->getSNR(); }
uint8_t getSpreadingFactor() const override { return ((CustomLR1110 *)_radio)->getSpreadingFactor(); }
uint8_t getCodingRate() const override { return ((CustomLR1110 *)_radio)->getCodingRate(); }
float getFreqMHz() const override { return ((CustomLR1110 *)_radio)->getFreqMHz(); }
bool setRxBoostedGainMode(bool en) override {
return ((CustomLR1110 *)_radio)->setRxBoostedGainMode(en) == RADIOLIB_ERR_NONE;

2
src/helpers/radiolib/CustomSX1262Wrapper.h

@ -37,6 +37,8 @@ public:
return packetScoreInt(snr, sf, packet_len);
}
uint8_t getSpreadingFactor() const override { return ((CustomSX1262 *)_radio)->spreadingFactor; }
uint8_t getCodingRate() const override { return ((CustomSX1262 *)_radio)->codingRate + 4; } // RadioLib stores 1-4, return 5-8
float getFreqMHz() const override { return ((CustomSX1262 *)_radio)->freqMHz; }
virtual void powerOff() override {
((CustomSX1262 *)_radio)->sleep(false);
}

55
src/helpers/radiolib/RadioLibWrappers.cpp

@ -2,6 +2,12 @@
#define RADIOLIB_STATIC_ONLY 1
#include "RadioLibWrappers.h"
#ifdef NRF52_PLATFORM
#define YIELD_TASK() vTaskDelay(1)
#else
#define YIELD_TASK() delay(1)
#endif
#define STATE_IDLE 0
#define STATE_RX 1
#define STATE_TX_WAIT 3
@ -176,6 +182,9 @@ bool RadioLibWrapper::isSendComplete() {
void RadioLibWrapper::onSendFinished() {
_radio->finishTransmit();
_board->onAfterTransmit();
if (isAS923_1_JP()) {
delay(50); // ARIB STD-T108 §3.4.1: >= 50ms between transmissions
}
state = STATE_IDLE;
}
@ -184,10 +193,50 @@ int16_t RadioLibWrapper::performChannelScan() {
}
bool RadioLibWrapper::isChannelActive() {
// int.thresh: RSSI-based interference detection (relative to noise floor)
if (_threshold != 0 && getCurrentRSSI() > _noise_floor + _threshold) return true;
if (isAS923_1_JP()) {
// Non-blocking backoff: if a prior busy detection armed a backoff wait,
// report busy and let Dispatcher::checkSend() re-poll on the next loop()
// pass instead of spin-waiting here (which used to block the Dispatcher
// loop for up to 16s).
if (_lbt_backoff_active) {
if ((int32_t)(millis() - _lbt_deadline) < 0) {
return true; // still waiting; re-checked next call
}
_lbt_backoff_active = false;
return isChannelActive(); // backoff elapsed -- re-sense (bounded, single-level recursion)
}
// ARIB STD-T108: 5ms continuous RSSI sensing, -80dBm absolute threshold
uint32_t sense_start = millis();
while (millis() - sense_start < 5) {
if (getCurrentRSSI() > -80.0f) {
_busy_count++;
uint32_t base_ms = 2000;
uint32_t max_backoff = min(base_ms * (1u << _busy_count), (uint32_t)16000);
_lbt_deadline = millis() + random(max_backoff / 2, max_backoff);
_lbt_backoff_active = true;
return true;
}
YIELD_TASK();
}
// Channel free: reset busy counter and add airtime-scaled jitter.
// JP_LBT_JITTER_DIVISOR controls jitter upper bound:
// /8 -> SF12/BW125 ~975ms, SF7/BW62.5 ~50ms
// /16 -> SF12/BW125 ~490ms, SF7/BW62.5 ~25ms
// /32 -> SF12/BW125 ~245ms, SF7/BW62.5 ~12ms (default)
_busy_count = 0;
uint32_t airtime_ms = getEstAirtimeFor(MAX_TRANS_UNIT);
uint32_t jitter_until = millis() + random(0, airtime_ms / JP_LBT_JITTER_DIVISOR);
while (millis() < jitter_until) {
YIELD_TASK();
}
// JP RSSI sensing passed; fall through to CAD if enabled
} else {
// Non-JP: RSSI-based interference detection (relative to noise floor)
if (_threshold != 0 && getCurrentRSSI() > _noise_floor + _threshold) return true;
}
// cad: hardware channel activity detection
// hardware channel activity detection (JP and non-JP)
if (_cad_enabled) {
int16_t result = performChannelScan();
// scanChannel() triggers DIO interrupt (CAD done) which sets STATE_INT_READY

36
src/helpers/radiolib/RadioLibWrappers.h

@ -15,10 +15,14 @@ protected:
uint32_t n_recv, n_sent, n_recv_errors;
int16_t _noise_floor, _threshold;
bool _cad_enabled;
uint8_t _busy_count;
uint16_t _num_floor_samples;
int32_t _floor_sample_sum;
uint8_t _preamble_sf;
bool _lbt_backoff_active; // non-blocking wait after RSSI busy detection (up to 16s)
uint32_t _lbt_deadline;
void idle();
void startRecv();
float packetScoreInt(float snr, int sf, int packet_len);
@ -26,7 +30,7 @@ protected:
virtual void doResetAGC();
public:
RadioLibWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : _radio(&radio), _board(&board), _preamble_sf(0) { n_recv = n_sent = 0; }
RadioLibWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : _radio(&radio), _board(&board), _busy_count(0), _preamble_sf(0), _lbt_backoff_active(false), _lbt_deadline(0) { n_recv = n_sent = 0; }
void begin() override;
virtual void powerOff() { _radio->sleep(); }
@ -53,6 +57,36 @@ public:
static uint16_t preambleLengthForSF(uint8_t sf) { return sf <= 8 ? 32 : 16; }
void updatePreamble(uint8_t sf) { _preamble_sf = sf; _radio->setPreambleLength(preambleLengthForSF(sf)); }
PacketMillis calcMaxPacketMillis(uint8_t sf, float bw, uint8_t cr, uint8_t preambleSymbols);
virtual uint8_t getCodingRate() const { return 8; } // default CR4/8, override in subclass
virtual float getFreqMHz() const { return 0.0f; } // default unknown, override in subclass
static constexpr uint8_t JP_LBT_JITTER_DIVISOR = 32;
bool isAS923_1_JP() const override {
float freq = getFreqMHz();
return (fabsf(freq - 920.800f) < 0.05f ||
fabsf(freq - 921.000f) < 0.05f ||
fabsf(freq - 921.200f) < 0.05f);
}
int getMaxTextLen() const override {
if (!isAS923_1_JP()) return 10 * 16; // default 160 bytes
uint8_t cr = getCodingRate();
if (cr <= 5) return 64; // 3874ms @ SF12/BW125/CR4-5
if (cr == 6) return 48; // 3874ms @ SF12/BW125/CR4-6
if (cr == 7) return 32; // 3678ms @ SF12/BW125/CR4-7
return 24; // 3547ms @ SF12/BW125/CR4-8
}
int getMaxGroupTextLen() const override {
if (!isAS923_1_JP()) return 10 * 16; // default 160 bytes
uint8_t cr = getCodingRate();
if (cr <= 5) return 64; // 3710ms @ SF12/BW125/CR4-5
if (cr == 6) return 48; // 3678ms @ SF12/BW125/CR4-6
if (cr == 7) return 39; // 3907ms @ SF12/BW125/CR4-7
return 29; // 3809ms @ SF12/BW125/CR4-8
}
virtual int16_t performChannelScan();
int getNoiseFloor() const override { return _noise_floor; }

Loading…
Cancel
Save