From d794b57e3109a1d01816ccd3c3041a3cd8ce9073 Mon Sep 17 00:00:00 2001 From: liquidraver <504870+liquidraver@users.noreply.github.com> Date: Sat, 14 Mar 2026 14:02:29 +0100 Subject: [PATCH] APC second test This reverts commit 4bd84ddf7e095b3e2579c55ed4217937e1e3279e. --- zephcore/Kconfig | 5 ++++- zephcore/app/CompanionMesh.cpp | 1 + zephcore/app/RepeaterMesh.h | 6 ++++++ zephcore/boards/example_board/README.md | 5 +++-- zephcore/helpers/CommonCLI.cpp | 15 +++++++++++++-- zephcore/helpers/CommonCLI.h | 2 ++ zephcore/include/mesh/PowerController.h | 10 +++++++--- zephcore/src/PowerController.cpp | 4 ++-- 8 files changed, 38 insertions(+), 10 deletions(-) diff --git a/zephcore/Kconfig b/zephcore/Kconfig index d8badff..8f04283 100644 --- a/zephcore/Kconfig +++ b/zephcore/Kconfig @@ -351,7 +351,7 @@ config ZEPHCORE_LORA_RX_DUTY_CYCLE config ZEPHCORE_APC bool "Adaptive Power Control (APC)" - default y + default n help Automatically reduce TX power when echo packets from neighbors indicate excess SNR margin. Saves battery and reduces channel @@ -361,6 +361,9 @@ config ZEPHCORE_APC Uses rogue-filtering: clusters echo SNRs to avoid one badly placed high-SNR neighbor from over-reducing power. + Off by default. Enable with CONFIG_ZEPHCORE_APC=y in board.conf + or via EXTRA_CONF_FILE. + endmenu menu "GPS Configuration" diff --git a/zephcore/app/CompanionMesh.cpp b/zephcore/app/CompanionMesh.cpp index f0d1ba4..e397507 100644 --- a/zephcore/app/CompanionMesh.cpp +++ b/zephcore/app/CompanionMesh.cpp @@ -185,6 +185,7 @@ void CompanionMesh::begin() BaseChatMesh::begin(); #ifdef CONFIG_ZEPHCORE_APC _power_ctrl.setSF(prefs.sf); + _power_ctrl.setTargetMargin(20); /* companions are mobile — more conservative */ #endif } diff --git a/zephcore/app/RepeaterMesh.h b/zephcore/app/RepeaterMesh.h index 2187f91..dd2b6d7 100644 --- a/zephcore/app/RepeaterMesh.h +++ b/zephcore/app/RepeaterMesh.h @@ -211,6 +211,12 @@ public: void setAPCEnabled(bool en) override { getPowerController().setEnabled(en); } + uint8_t getAPCTargetMargin() const override { + return getPowerController().getTargetMargin(); + } + void setAPCTargetMargin(uint8_t margin_db) override { + getPowerController().setTargetMargin(margin_db); + } #endif void handleCommand(uint32_t sender_timestamp, char* command, char* reply); diff --git a/zephcore/boards/example_board/README.md b/zephcore/boards/example_board/README.md index 3b7bd39..0b5d28a 100644 --- a/zephcore/boards/example_board/README.md +++ b/zephcore/boards/example_board/README.md @@ -178,6 +178,9 @@ should ONLY contain settings that can't be inferred from hardware: CONFIG_ESPTOOLPY_FLASHSIZE_16MB ESP32 boards with 16MB flash CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM Boards with external PA CONFIG_ZEPHCORE_MAX_TX_POWER_DBM Boards with external PA + CONFIG_ZEPHCORE_APC Adaptive Power Control — OFF by default. + Reduces TX power when echo SNR shows excess margin. + See apc.md for details on target margin tuning. AUTO-DETECTED (do NOT set in board.conf): CONFIG_PWM Auto from DT buzzer nodelabel @@ -186,8 +189,6 @@ should ONLY contain settings that can't be inferred from hardware: CONFIG_SPI Auto from ZEPHCORE_RADIO_LR1110 CONFIG_NORDIC_QSPI_NOR Auto from DT nordic,qspi-nor node CONFIG_ZEPHCORE_LORA_RX_DUTY_CYCLE Auto: ON for companion+SX1262, OFF for repeater/LR1110 - CONFIG_ZEPHCORE_APC Adaptive Power Control — ON by default for all boards/roles. - Set to n in board.conf to disable for a specific board. Config Inheritance diff --git a/zephcore/helpers/CommonCLI.cpp b/zephcore/helpers/CommonCLI.cpp index 1856312..16d01eb 100644 --- a/zephcore/helpers/CommonCLI.cpp +++ b/zephcore/helpers/CommonCLI.cpp @@ -428,13 +428,16 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch float ff = _callbacks->getFloodDelayFactor(); snprintf(reply, CLI_REPLY_SIZE, "> adaptive (est=%.1f flood=%.2f)", (double)est, (double)ff); + } else if (memcmp(config, "apc.margin", 10) == 0) { + snprintf(reply, CLI_REPLY_SIZE, "> %d dB", (int)_callbacks->getAPCTargetMargin()); } else if (memcmp(config, "txpower", 7) == 0) { if (_callbacks->isAPCEnabled()) { int8_t apc = _callbacks->getAPCReduction(); float margin = _callbacks->getAPCMargin(); int effective = (int)_prefs->tx_power_dbm - (int)apc; - snprintf(reply, CLI_REPLY_SIZE, "> %ddBm (max=%d apc=-%d margin=%.1f)", - effective, (int)_prefs->tx_power_dbm, (int)apc, (double)margin); + snprintf(reply, CLI_REPLY_SIZE, "> %ddBm (max=%d apc=-%d margin=%.1f target=%d)", + effective, (int)_prefs->tx_power_dbm, (int)apc, (double)margin, + (int)_callbacks->getAPCTargetMargin()); } else { snprintf(reply, CLI_REPLY_SIZE, "> %ddBm (apc=off)", (int)_prefs->tx_power_dbm); @@ -669,6 +672,14 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch savePrefs(); strcpy(reply, "OK"); } + } else if (memcmp(config, "apc.margin ", 11) == 0) { + int val = atoi(&config[11]); + if (val >= 6 && val <= 30) { + _callbacks->setAPCTargetMargin((uint8_t)val); + snprintf(reply, CLI_REPLY_SIZE, "OK - APC target margin=%d dB", val); + } else { + strcpy(reply, "Error, range 6-30 dB"); + } } else if (memcmp(config, "tx ", 3) == 0) { if (memcmp(&config[3], "apc", 3) == 0) { _callbacks->setAPCEnabled(true); diff --git a/zephcore/helpers/CommonCLI.h b/zephcore/helpers/CommonCLI.h index d085095..ffc33b2 100644 --- a/zephcore/helpers/CommonCLI.h +++ b/zephcore/helpers/CommonCLI.h @@ -57,6 +57,8 @@ public: virtual float getAPCMargin() const { return 0.0f; } virtual bool isAPCEnabled() const { return true; } virtual void setAPCEnabled(bool en) { (void)en; } + virtual uint8_t getAPCTargetMargin() const { return 16; } + virtual void setAPCTargetMargin(uint8_t margin_db) { (void)margin_db; } // Sensor manager interface (for GPS) virtual double getNodeLat() const { return 0.0; } diff --git a/zephcore/include/mesh/PowerController.h b/zephcore/include/mesh/PowerController.h index c388f92..98d93f9 100644 --- a/zephcore/include/mesh/PowerController.h +++ b/zephcore/include/mesh/PowerController.h @@ -31,6 +31,11 @@ public: /* Set current spreading factor (needed for margin calculation). */ void setSF(uint8_t sf) { _sf = sf; } + /* Set target link margin in dB. Higher = more conservative + * (better for networks with poor-RX hardware). Default 16 dB. */ + void setTargetMargin(uint8_t margin_db) { _target_margin_x4 = (int)margin_db * 4; } + uint8_t getTargetMargin() const { return (uint8_t)(_target_margin_x4 / 4); } + /* Called when we send or retransmit a flood packet. */ void trackTransmit(uint32_t hash32, uint32_t now_ms); @@ -69,9 +74,7 @@ private: static constexpr int8_t MAX_REDUCTION_DB = 12; static constexpr int8_t MIN_TX_POWER_DBM = -9; /* SX1262 hw min */ static constexpr int CLUSTER_WIDTH_X4 = 24; /* 6 dB in x4 */ - /* TARGET_MARGIN: 16 dB above SF threshold. - * For SF8 (threshold -10 dB): reduce at SNR > +7, increase at SNR < +5 */ - static constexpr int TARGET_MARGIN_X4 = 64; /* 16 dB * 4 */ + static constexpr int DEFAULT_TARGET_MARGIN_X4 = 64; /* 16 dB * 4 */ static constexpr int HYSTERESIS_X4 = 4; /* 1 dB * 4 */ struct Source { @@ -97,6 +100,7 @@ private: bool _enabled; uint8_t _sf; uint8_t _last_source_count; + int _target_margin_x4; void finalizeEntry(int idx); int findEntry(uint32_t hash32) const; diff --git a/zephcore/src/PowerController.cpp b/zephcore/src/PowerController.cpp index 35ac895..bb1b40e 100644 --- a/zephcore/src/PowerController.cpp +++ b/zephcore/src/PowerController.cpp @@ -25,7 +25,7 @@ namespace mesh { PowerController::PowerController() : _next_idx(0), _margin_ema_x256(0), _finalized_count(0), _last_echo_ms(0), _power_reduction_db(0), _enabled(true), - _sf(8), _last_source_count(0) + _sf(8), _last_source_count(0), _target_margin_x4(DEFAULT_TARGET_MARGIN_X4) { memset(_ring, 0, sizeof(_ring)); } @@ -203,7 +203,7 @@ void PowerController::tick(uint32_t now_ms) if (!isWarmedUp()) return; int32_t margin_x256 = _margin_ema_x256; - int32_t target_x256 = TARGET_MARGIN_X4 << 6; + int32_t target_x256 = _target_margin_x4 << 6; int32_t hyst_x256 = HYSTERESIS_X4 << 6; int8_t old_reduction = _power_reduction_db;