mirror of
https://github.com/meshcore-dev/MeshCore.git
synced 2026-04-20 22:13:47 +00:00
Merge e01c6a5a29 into dee3e26ac0
This commit is contained in:
commit
58eef2e792
19 changed files with 143 additions and 17 deletions
|
|
@ -258,7 +258,7 @@ float MyMesh::getAirtimeBudgetFactor() const {
|
|||
}
|
||||
|
||||
int MyMesh::getInterferenceThreshold() const {
|
||||
return 0; // disabled for now, until currentRSSI() problem is resolved
|
||||
return 1; // non-zero enables hardware CAD (Channel Activity Detection) before TX
|
||||
}
|
||||
|
||||
int MyMesh::calcRxDelay(float score, uint32_t air_time) const {
|
||||
|
|
|
|||
|
|
@ -151,6 +151,10 @@ protected:
|
|||
|
||||
uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override;
|
||||
uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const override;
|
||||
uint32_t getCADFailMaxDuration() const override {
|
||||
if (_radio->isJapanMode()) return UINT32_MAX; // JP LBT: no forced TX — channel must be free per ARIB STD-T108
|
||||
return Dispatcher::getCADFailMaxDuration();
|
||||
}
|
||||
void onSendTimeout() override;
|
||||
|
||||
// DataStoreHost methods
|
||||
|
|
|
|||
|
|
@ -886,7 +886,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
|||
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
|
||||
_prefs.flood_advert_interval = 12; // 12 hours
|
||||
_prefs.flood_max = 64;
|
||||
_prefs.interference_threshold = 0; // disabled
|
||||
_prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX
|
||||
|
||||
// bridge defaults
|
||||
_prefs.bridge_enabled = 1; // enabled
|
||||
|
|
|
|||
|
|
@ -143,6 +143,10 @@ protected:
|
|||
void logTx(mesh::Packet* pkt, int len) override;
|
||||
void logTxFail(mesh::Packet* pkt, int len) override;
|
||||
int calcRxDelay(float score, uint32_t air_time) const override;
|
||||
uint32_t getCADFailMaxDuration() const override {
|
||||
if (_radio->isJapanMode()) return UINT32_MAX; // JP LBT: no forced TX — channel must be free per ARIB STD-T108
|
||||
return Dispatcher::getCADFailMaxDuration();
|
||||
}
|
||||
|
||||
uint32_t getRetransmitDelay(const mesh::Packet* packet) override;
|
||||
uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override;
|
||||
|
|
|
|||
|
|
@ -643,7 +643,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
|||
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
|
||||
_prefs.flood_advert_interval = 12; // 12 hours
|
||||
_prefs.flood_max = 64;
|
||||
_prefs.interference_threshold = 0; // disabled
|
||||
_prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX
|
||||
#ifdef ROOM_PASSWORD
|
||||
StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password));
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -137,6 +137,11 @@ protected:
|
|||
void logTxFail(mesh::Packet* pkt, int len) override;
|
||||
|
||||
int calcRxDelay(float score, uint32_t air_time) const override;
|
||||
uint32_t getCADFailMaxDuration() const override {
|
||||
if (_radio->isJapanMode()) return UINT32_MAX; // JP LBT: no forced TX — channel must be free per ARIB STD-T108
|
||||
return Dispatcher::getCADFailMaxDuration();
|
||||
}
|
||||
|
||||
const char* getLogDateTime() override;
|
||||
uint32_t getRetransmitDelay(const mesh::Packet* packet) override;
|
||||
uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override;
|
||||
|
|
|
|||
|
|
@ -725,7 +725,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
|
|||
_prefs.flood_advert_interval = 0; // disabled
|
||||
_prefs.disable_fwd = true;
|
||||
_prefs.flood_max = 64;
|
||||
_prefs.interference_threshold = 0; // disabled
|
||||
_prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX
|
||||
|
||||
// GPS defaults
|
||||
_prefs.gps_enabled = 0;
|
||||
|
|
|
|||
|
|
@ -117,6 +117,10 @@ protected:
|
|||
float getAirtimeBudgetFactor() const override;
|
||||
bool allowPacketForward(const mesh::Packet* packet) override;
|
||||
int calcRxDelay(float score, uint32_t air_time) const override;
|
||||
uint32_t getCADFailMaxDuration() const override {
|
||||
if (_radio->isJapanMode()) return UINT32_MAX; // JP LBT: no forced TX — channel must be free per ARIB STD-T108
|
||||
return Dispatcher::getCADFailMaxDuration();
|
||||
}
|
||||
uint32_t getRetransmitDelay(const mesh::Packet* packet) override;
|
||||
uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override;
|
||||
int getInterferenceThreshold() const override;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,10 @@ public:
|
|||
|
||||
virtual float packetScore(float snr, int packet_len) = 0;
|
||||
|
||||
virtual int getMaxTextLen() const { return 10 * 16; } // default: non-JP
|
||||
virtual int getMaxGroupTextLen() const { return 10 * 16; } // default: non-JP
|
||||
virtual bool isJapanMode() const { return false; } // default: non-JP
|
||||
|
||||
/**
|
||||
* \brief starts the raw packet send. (no wait)
|
||||
* \param bytes the raw packet data
|
||||
|
|
|
|||
|
|
@ -395,9 +395,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, ×tamp, 4); // mostly an extra blob to help make packet_hash unique
|
||||
temp[4] = (attempt & 3);
|
||||
|
|
@ -436,7 +436,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, ×tamp, 4); // mostly an extra blob to help make packet_hash unique
|
||||
|
|
@ -469,7 +470,9 @@ 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
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
#include <helpers/AdvertDataHelpers.h>
|
||||
#include <helpers/TxtDataHelpers.h>
|
||||
|
||||
#define MAX_TEXT_LEN (10*CIPHER_BLOCK_SIZE) // must be LESS than (MAX_PACKET_PAYLOAD - 4 - CIPHER_MAC_SIZE - 1)
|
||||
#define MAX_TEXT_LEN (10*CIPHER_BLOCK_SIZE)
|
||||
// must be LESS than (MAX_PACKET_PAYLOAD - 4 - CIPHER_MAC_SIZE - 1)
|
||||
|
||||
#include "ContactInfo.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -36,4 +36,8 @@ class CustomLR1110 : public LR1110 {
|
|||
bool detected = ((irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID) || (irq & RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED));
|
||||
return detected;
|
||||
}
|
||||
};
|
||||
|
||||
uint8_t getCodingRate() const {
|
||||
return this->codingRate + 4; // RadioLib stores 1-4, return 5-8
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,4 +31,11 @@ public:
|
|||
bool getRxBoostedGainMode() const override {
|
||||
return ((CustomLR1110 *)_radio)->getRxBoostedGainMode();
|
||||
}
|
||||
|
||||
uint8_t getCodingRate() const override {
|
||||
return ((CustomLR1110 *)_radio)->getCodingRate();
|
||||
}
|
||||
float getFreqMHz() const override {
|
||||
return ((CustomLR1110 *)_radio)->getFreqMHz();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,4 +23,11 @@ public:
|
|||
}
|
||||
|
||||
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
|
||||
|
||||
uint8_t getCodingRate() const override {
|
||||
return ((CustomSTM32WLx *)_radio)->codingRate + 4;
|
||||
}
|
||||
float getFreqMHz() const override {
|
||||
return ((CustomSTM32WLx *)_radio)->freqMHz;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -97,4 +97,4 @@ class CustomSX1262 : public SX1262 {
|
|||
readRegister(RADIOLIB_SX126X_REG_RX_GAIN, &rxGain, 1);
|
||||
return (rxGain == RADIOLIB_SX126X_RX_GAIN_BOOSTED);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -36,4 +36,11 @@ public:
|
|||
bool getRxBoostedGainMode() const override {
|
||||
return ((CustomSX1262 *)_radio)->getRxBoostedGainMode();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,13 @@
|
|||
#define RADIOLIB_STATIC_ONLY 1
|
||||
#include "RadioLibWrappers.h"
|
||||
|
||||
// Platform-safe yield for use in busy-wait loops
|
||||
#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
|
||||
|
|
@ -34,6 +41,7 @@ void RadioLibWrapper::begin() {
|
|||
|
||||
_noise_floor = 0;
|
||||
_threshold = 0;
|
||||
_busy_count = 0; // initialize exponential backoff counter
|
||||
|
||||
// start average out some samples
|
||||
_num_floor_samples = 0;
|
||||
|
|
@ -165,13 +173,51 @@ bool RadioLibWrapper::isSendComplete() {
|
|||
void RadioLibWrapper::onSendFinished() {
|
||||
_radio->finishTransmit();
|
||||
_board->onAfterTransmit();
|
||||
if (isJapanMode()) {
|
||||
// ARIB STD-T108: wait >= 50ms after TX before next transmission
|
||||
delay(50);
|
||||
}
|
||||
state = STATE_IDLE;
|
||||
}
|
||||
|
||||
int16_t RadioLibWrapper::performChannelScan() {
|
||||
return _radio->scanChannel();
|
||||
}
|
||||
|
||||
bool RadioLibWrapper::isChannelActive() {
|
||||
return _threshold == 0
|
||||
? false // interference check is disabled
|
||||
: getCurrentRSSI() > _noise_floor + _threshold;
|
||||
if (_threshold == 0) return false; // interference check is disabled
|
||||
|
||||
// Activate JP_STRICT LBT on Japan 920MHz band 3 channels only
|
||||
// CH25=920.800MHz, CH26=921.000MHz, CH27=921.200MHz (ARIB STD-T108)
|
||||
if (isJapanMode()) {
|
||||
// ARIB STD-T108 compliant LBT: continuous RSSI sensing for >= 5ms
|
||||
// Energy-based sensing required; LoRa CAD not used
|
||||
uint32_t sense_start = millis();
|
||||
while (millis() - sense_start < 5) {
|
||||
if (getCurrentRSSI() > -80.0f) {
|
||||
// Channel busy: exponential backoff (tuned for JP 4s airtime)
|
||||
_busy_count++;
|
||||
uint32_t base_ms = 2000;
|
||||
uint32_t max_backoff = min(base_ms * (1u << _busy_count), (uint32_t)16000);
|
||||
uint32_t backoff_until = millis() + random(max_backoff / 2, max_backoff);
|
||||
while (millis() < backoff_until) {
|
||||
YIELD_TASK();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
YIELD_TASK();
|
||||
}
|
||||
// Channel free: reset busy counter and add small jitter
|
||||
_busy_count = 0;
|
||||
uint32_t jitter_until = millis() + random(0, 500);
|
||||
while (millis() < jitter_until) {
|
||||
YIELD_TASK();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Non-JP: original behavior (RSSI threshold only)
|
||||
return getCurrentRSSI() > _noise_floor + _threshold;
|
||||
}
|
||||
|
||||
float RadioLibWrapper::getLastRSSI() const {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ protected:
|
|||
mesh::MainBoard* _board;
|
||||
uint32_t n_recv, n_sent, n_recv_errors;
|
||||
int16_t _noise_floor, _threshold;
|
||||
uint8_t _busy_count; // consecutive busy detections for exponential backoff
|
||||
uint16_t _num_floor_samples;
|
||||
int32_t _floor_sample_sum;
|
||||
|
||||
|
|
@ -31,14 +32,43 @@ public:
|
|||
bool isInRecvMode() const override;
|
||||
bool isChannelActive();
|
||||
|
||||
bool isReceiving() override {
|
||||
bool isReceiving() override {
|
||||
if (isReceivingPacket()) return true;
|
||||
|
||||
return isChannelActive();
|
||||
}
|
||||
|
||||
virtual float getCurrentRSSI() =0;
|
||||
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
|
||||
//
|
||||
bool isJapanMode() const {
|
||||
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 {
|
||||
if (!isJapanMode()) 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 {
|
||||
if (!isJapanMode()) 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; }
|
||||
void triggerNoiseFloorCalibrate(int threshold) override;
|
||||
void resetAGC() override;
|
||||
|
|
|
|||
|
|
@ -118,4 +118,4 @@ build_flags =
|
|||
; -D MESH_PACKET_LOGGING=1
|
||||
-D MESH_DEBUG=1
|
||||
build_src_filter = ${rak4631.build_src_filter}
|
||||
+<../examples/simple_sensor>
|
||||
+<../examples/simple_sensor>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue