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
55 changes: 52 additions & 3 deletions src/helpers/radiolib/RadioLibWrappers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}

Expand All @@ -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 = 500;
uint32_t max_backoff = min(base_ms * (1u << _busy_count), (uint32_t)4000);
_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
Expand Down
46 changes: 35 additions & 11 deletions src/helpers/radiolib/RadioLibWrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
#include <Mesh.h>
#include <RadioLib.h>

#ifdef USE_CC310_HW_CRYPTO
#include <Adafruit_nRFCrypto.h>
#endif
struct PacketMillis {
uint32_t preambleMillis; // preamble-detect -> header-valid deadline
uint32_t payloadMillis; // header-valid -> rx-done deadline
Expand All @@ -18,18 +15,22 @@ class RadioLibWrapper : public mesh::Radio {
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);
virtual bool isReceivingPacket() =0;
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(); }
Expand All @@ -56,6 +57,36 @@ class RadioLibWrapper : public mesh::Radio {
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; }
Expand Down Expand Up @@ -89,15 +120,8 @@ class RadioNoiseListener : public mesh::RNG {
RadioNoiseListener(PhysicalLayer& radio): _radio(&radio) { }

void random(uint8_t* dest, size_t sz) override {
#ifdef USE_CC310_HW_CRYPTO
// CC310 TRNG is higher quality and environment-independent vs radio RSSI noise.
nRFCrypto.begin();
nRFCrypto.Random.generate(dest, (uint16_t)sz);
nRFCrypto.end();
#else
for (int i = 0; i < sz; i++) {
dest[i] = _radio->randomByte() ^ (::random(0, 256) & 0xFF);
}
#endif
}
};