Browse Source

Have CAD be a separate toggle (set cad on/off)

pull/1727/head
Wessel Nieboer 2 weeks ago
parent
commit
7c8e092457
No known key found for this signature in database GPG Key ID: 929C8E45E33B5FD2
  1. 14
      docs/cli_commands.md
  2. 5
      examples/companion_radio/MyMesh.cpp
  3. 1
      examples/companion_radio/MyMesh.h
  4. 3
      examples/simple_repeater/MyMesh.cpp
  5. 3
      examples/simple_repeater/MyMesh.h
  6. 3
      examples/simple_room_server/MyMesh.cpp
  7. 3
      examples/simple_room_server/MyMesh.h
  8. 6
      examples/simple_sensor/SensorMesh.cpp
  9. 1
      examples/simple_sensor/SensorMesh.h
  10. 1
      src/Dispatcher.cpp
  11. 3
      src/Dispatcher.h
  12. 13
      src/helpers/CommonCLI.cpp
  13. 1
      src/helpers/CommonCLI.h
  14. 23
      src/helpers/radiolib/RadioLibWrappers.cpp
  15. 2
      src/helpers/radiolib/RadioLibWrappers.h

14
docs/cli_commands.md

@ -578,6 +578,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore
--- ---
#### Enable or disable hardware Channel Activity Detection (CAD)
**Usage:**
- `get cad`
- `set cad <on|off>`
**Description:** When enabled, the radio performs a hardware Channel Activity Detection scan before transmitting and defers if the channel is busy. Runs independently of `int.thresh` — either, both, or none may be active.
**Parameters:**
- `on|off`: Enable or disable hardware CAD
**Default:** `off`
---
#### View or change the AGC Reset Interval #### View or change the AGC Reset Interval
**Usage:** **Usage:**
- `get agc.reset.interval` - `get agc.reset.interval`

5
examples/companion_radio/MyMesh.cpp

@ -259,7 +259,10 @@ float MyMesh::getAirtimeBudgetFactor() const {
} }
int MyMesh::getInterferenceThreshold() const { int MyMesh::getInterferenceThreshold() const {
return 1; // non-zero enables hardware CAD (Channel Activity Detection) before TX return 0; // disabled for now, until currentRSSI() problem is resolved
}
bool MyMesh::getCADEnabled() const {
return true; // hardware CAD before TX (no CLI toggle on companion; enabled by default)
} }
int MyMesh::calcRxDelay(float score, uint32_t air_time) const { int MyMesh::calcRxDelay(float score, uint32_t air_time) const {

1
examples/companion_radio/MyMesh.h

@ -105,6 +105,7 @@ public:
protected: protected:
float getAirtimeBudgetFactor() const override; float getAirtimeBudgetFactor() const override;
int getInterferenceThreshold() const override; int getInterferenceThreshold() const override;
bool getCADEnabled() const override;
int calcRxDelay(float score, uint32_t air_time) const override; int calcRxDelay(float score, uint32_t air_time) const override;
uint32_t getRetransmitDelay(const mesh::Packet *packet) override; uint32_t getRetransmitDelay(const mesh::Packet *packet) override;
uint32_t getDirectRetransmitDelay(const mesh::Packet *packet) override; uint32_t getDirectRetransmitDelay(const mesh::Packet *packet) override;

3
examples/simple_repeater/MyMesh.cpp

@ -892,7 +892,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.flood_max = 64; _prefs.flood_max = 64;
_prefs.flood_max_unscoped = 64; _prefs.flood_max_unscoped = 64;
_prefs.flood_max_advert = 8; _prefs.flood_max_advert = 8;
_prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX _prefs.interference_threshold = 0; // disabled
_prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on')
// bridge defaults // bridge defaults
_prefs.bridge_enabled = 1; // enabled _prefs.bridge_enabled = 1; // enabled

3
examples/simple_repeater/MyMesh.h

@ -150,6 +150,9 @@ protected:
int getInterferenceThreshold() const override { int getInterferenceThreshold() const override {
return _prefs.interference_threshold; return _prefs.interference_threshold;
} }
bool getCADEnabled() const override {
return _prefs.cad_enabled;
}
int getAGCResetInterval() const override { int getAGCResetInterval() const override {
return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds
} }

3
examples/simple_room_server/MyMesh.cpp

@ -649,7 +649,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.flood_max = 64; _prefs.flood_max = 64;
_prefs.flood_max_unscoped = 64; _prefs.flood_max_unscoped = 64;
_prefs.flood_max_advert = 8; _prefs.flood_max_advert = 8;
_prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX _prefs.interference_threshold = 0; // disabled
_prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on')
#ifdef ROOM_PASSWORD #ifdef ROOM_PASSWORD
StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password)); StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password));
#endif #endif

3
examples/simple_room_server/MyMesh.h

@ -144,6 +144,9 @@ protected:
int getInterferenceThreshold() const override { int getInterferenceThreshold() const override {
return _prefs.interference_threshold; return _prefs.interference_threshold;
} }
bool getCADEnabled() const override {
return _prefs.cad_enabled;
}
int getAGCResetInterval() const override { int getAGCResetInterval() const override {
return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds
} }

6
examples/simple_sensor/SensorMesh.cpp

@ -323,6 +323,9 @@ uint32_t SensorMesh::getDirectRetransmitDelay(const mesh::Packet* packet) {
int SensorMesh::getInterferenceThreshold() const { int SensorMesh::getInterferenceThreshold() const {
return _prefs.interference_threshold; return _prefs.interference_threshold;
} }
bool SensorMesh::getCADEnabled() const {
return _prefs.cad_enabled;
}
int SensorMesh::getAGCResetInterval() const { int SensorMesh::getAGCResetInterval() const {
return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds
} }
@ -725,7 +728,8 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
_prefs.flood_advert_interval = 0; // disabled _prefs.flood_advert_interval = 0; // disabled
_prefs.disable_fwd = true; _prefs.disable_fwd = true;
_prefs.flood_max = 64; _prefs.flood_max = 64;
_prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX _prefs.interference_threshold = 0; // disabled
_prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on')
// GPS defaults // GPS defaults
_prefs.gps_enabled = 0; _prefs.gps_enabled = 0;

1
examples/simple_sensor/SensorMesh.h

@ -120,6 +120,7 @@ protected:
uint32_t getRetransmitDelay(const mesh::Packet* packet) override; uint32_t getRetransmitDelay(const mesh::Packet* packet) override;
uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override; uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override;
int getInterferenceThreshold() const override; int getInterferenceThreshold() const override;
bool getCADEnabled() const override;
int getAGCResetInterval() const override; int getAGCResetInterval() const override;
void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override; void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override;
int searchPeersByHash(const uint8_t* hash) override; int searchPeersByHash(const uint8_t* hash) override;

1
src/Dispatcher.cpp

@ -66,6 +66,7 @@ uint32_t Dispatcher::getCADFailMaxDuration() const {
void Dispatcher::loop() { void Dispatcher::loop() {
if (millisHasNowPassed(next_floor_calib_time)) { if (millisHasNowPassed(next_floor_calib_time)) {
_radio->triggerNoiseFloorCalibrate(getInterferenceThreshold()); _radio->triggerNoiseFloorCalibrate(getInterferenceThreshold());
_radio->setCADEnabled(getCADEnabled());
next_floor_calib_time = futureMillis(NOISE_FLOOR_CALIB_INTERVAL); next_floor_calib_time = futureMillis(NOISE_FLOOR_CALIB_INTERVAL);
} }
_radio->loop(); _radio->loop();

3
src/Dispatcher.h

@ -65,6 +65,8 @@ public:
virtual void triggerNoiseFloorCalibrate(int threshold) { } virtual void triggerNoiseFloorCalibrate(int threshold) { }
virtual void setCADEnabled(bool enable) { }
virtual void resetAGC() { } virtual void resetAGC() { }
virtual bool isInRecvMode() const = 0; virtual bool isInRecvMode() const = 0;
@ -166,6 +168,7 @@ protected:
virtual uint32_t getCADFailRetryDelay() const; virtual uint32_t getCADFailRetryDelay() const;
virtual uint32_t getCADFailMaxDuration() const; virtual uint32_t getCADFailMaxDuration() const;
virtual int getInterferenceThreshold() const { return 0; } // disabled by default virtual int getInterferenceThreshold() const { return 0; } // disabled by default
virtual bool getCADEnabled() const { return false; } // hardware CAD disabled by default
virtual int getAGCResetInterval() const { return 0; } // disabled by default virtual int getAGCResetInterval() const { return 0; } // disabled by default
virtual unsigned long getDutyCycleWindowMs() const { return 3600000; } virtual unsigned long getDutyCycleWindowMs() const { return 3600000; }

13
src/helpers/CommonCLI.cpp

@ -92,7 +92,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped)); // 291 file.read((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped)); // 291
file.read((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292 file.read((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292
file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293 file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293
// next: 294 file.read((uint8_t *)&_prefs->cad_enabled, sizeof(_prefs->cad_enabled)); // 294
// next: 295
// sanitise bad pref values // sanitise bad pref values
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
@ -123,6 +124,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
// sanitise settings // sanitise settings
_prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean
_prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean _prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean
_prefs->cad_enabled = constrain(_prefs->cad_enabled, 0, 1); // boolean
file.close(); file.close();
} }
@ -187,7 +189,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped)); // 291 file.write((uint8_t *)&_prefs->flood_max_unscoped, sizeof(_prefs->flood_max_unscoped)); // 291
file.write((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292 file.write((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292
file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293 file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293
// next: 294 file.write((uint8_t *)&_prefs->cad_enabled, sizeof(_prefs->cad_enabled)); // 294
// next: 295
file.close(); file.close();
} }
@ -503,6 +506,10 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->interference_threshold = atoi(&config[11]); _prefs->interference_threshold = atoi(&config[11]);
savePrefs(); savePrefs();
strcpy(reply, "OK"); strcpy(reply, "OK");
} else if (memcmp(config, "cad ", 4) == 0) {
_prefs->cad_enabled = memcmp(&config[4], "on", 2) == 0;
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "agc.reset.interval ", 19) == 0) { } else if (memcmp(config, "agc.reset.interval ", 19) == 0) {
_prefs->agc_reset_interval = atoi(&config[19]) / 4; _prefs->agc_reset_interval = atoi(&config[19]) / 4;
savePrefs(); savePrefs();
@ -801,6 +808,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->airtime_factor)); sprintf(reply, "> %s", StrHelper::ftoa(_prefs->airtime_factor));
} else if (memcmp(config, "int.thresh", 10) == 0) { } else if (memcmp(config, "int.thresh", 10) == 0) {
sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold); sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold);
} else if (memcmp(config, "cad", 3) == 0) {
sprintf(reply, "> %s", _prefs->cad_enabled ? "on" : "off");
} else if (memcmp(config, "agc.reset.interval", 18) == 0) { } else if (memcmp(config, "agc.reset.interval", 18) == 0) {
sprintf(reply, "> %d", ((uint32_t) _prefs->agc_reset_interval) * 4); sprintf(reply, "> %d", ((uint32_t) _prefs->agc_reset_interval) * 4);
} else if (memcmp(config, "multi.acks", 10) == 0) { } else if (memcmp(config, "multi.acks", 10) == 0) {

1
src/helpers/CommonCLI.h

@ -64,6 +64,7 @@ struct NodePrefs { // persisted to file
uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting uint8_t radio_fem_rxgain; // LoRa FEM RX gain setting
uint8_t path_hash_mode; // which path mode to use when sending uint8_t path_hash_mode; // which path mode to use when sending
uint8_t loop_detect; uint8_t loop_detect;
uint8_t cad_enabled; // hardware Channel Activity Detection before TX (boolean)
}; };
class CommonCLICallbacks { class CommonCLICallbacks {

23
src/helpers/radiolib/RadioLibWrappers.cpp

@ -36,6 +36,7 @@ void RadioLibWrapper::begin() {
_noise_floor = 0; _noise_floor = 0;
_threshold = 0; _threshold = 0;
_cad_enabled = false;
// start average out some samples // start average out some samples
_num_floor_samples = 0; _num_floor_samples = 0;
@ -183,15 +184,21 @@ int16_t RadioLibWrapper::performChannelScan() {
} }
bool RadioLibWrapper::isChannelActive() { bool RadioLibWrapper::isChannelActive() {
if (_threshold == 0) return false; // interference check is disabled // int.thresh: RSSI-based interference detection (relative to noise floor)
if (_threshold != 0 && getCurrentRSSI() > _noise_floor + _threshold) return true;
// cad: hardware channel activity detection
if (_cad_enabled) {
int16_t result = performChannelScan();
// scanChannel() triggers DIO interrupt (CAD done) which sets STATE_INT_READY
// via setFlag() ISR. Clear it before restarting RX so recvRaw() doesn't
// try to read a non-existent packet and count a spurious recv error.
state = STATE_IDLE;
startRecv();
if (result != RADIOLIB_CHANNEL_FREE) return true;
}
int16_t result = performChannelScan(); return false;
// scanChannel() triggers DIO interrupt (CAD done) which sets STATE_INT_READY
// via setFlag() ISR. Clear it before restarting RX so recvRaw() doesn't
// try to read a non-existent packet and count a spurious recv error.
state = STATE_IDLE;
startRecv();
return result != RADIOLIB_CHANNEL_FREE;
} }
float RadioLibWrapper::getLastRSSI() const { float RadioLibWrapper::getLastRSSI() const {

2
src/helpers/radiolib/RadioLibWrappers.h

@ -9,6 +9,7 @@ protected:
mesh::MainBoard* _board; mesh::MainBoard* _board;
uint32_t n_recv, n_sent, n_recv_errors; uint32_t n_recv, n_sent, n_recv_errors;
int16_t _noise_floor, _threshold; int16_t _noise_floor, _threshold;
bool _cad_enabled;
uint16_t _num_floor_samples; uint16_t _num_floor_samples;
int32_t _floor_sample_sum; int32_t _floor_sample_sum;
uint8_t _preamble_sf; uint8_t _preamble_sf;
@ -50,6 +51,7 @@ public:
int getNoiseFloor() const override { return _noise_floor; } int getNoiseFloor() const override { return _noise_floor; }
void triggerNoiseFloorCalibrate(int threshold) override; void triggerNoiseFloorCalibrate(int threshold) override;
void setCADEnabled(bool enable) override { _cad_enabled = enable; }
void resetAGC() override; void resetAGC() override;
void loop() override; void loop() override;

Loading…
Cancel
Save