From 8879d16705cf4ae289bc925e0bc657835c932fa7 Mon Sep 17 00:00:00 2001 From: jgromes Date: Sat, 18 Apr 2026 12:20:47 +0000 Subject: [PATCH 1/5] [LR2021] Initial PA optimization --- src/modules/LR2021/LR2021.h | 11 ++++ src/modules/LR2021/LR2021_cmds_radio.cpp | 2 +- src/modules/LR2021/LR2021_config.cpp | 80 ++++++++++++++++++++++-- 3 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/modules/LR2021/LR2021.h b/src/modules/LR2021/LR2021.h index 06c72992be..a33418818c 100644 --- a/src/modules/LR2021/LR2021.h +++ b/src/modules/LR2021/LR2021.h @@ -68,6 +68,17 @@ class LR2021: public LRxxxx { MODE_TX_HF, }; + /*! + \struct paTableEntry_t + \brief This structure is used as entry in the PA lookup table, + to optimize PA configuration for minimum power consumption. + */ + struct __attribute__((packed)) paTableEntry_t { + uint8_t paDutyCycle: 4; + uint8_t paSlices: 4; + int8_t paVal; + }; + // basic methods /*! diff --git a/src/modules/LR2021/LR2021_cmds_radio.cpp b/src/modules/LR2021/LR2021_cmds_radio.cpp index a953d1c80e..fdc9332311 100644 --- a/src/modules/LR2021/LR2021_cmds_radio.cpp +++ b/src/modules/LR2021/LR2021_cmds_radio.cpp @@ -103,7 +103,7 @@ int16_t LR2021::setPaConfig(uint8_t pa, uint8_t paLfMode, uint8_t paLfDutyCycle, } int16_t LR2021::setTxParams(int8_t txPower, uint8_t rampTime) { - uint8_t buff[] = { (uint8_t)(txPower * 2), rampTime }; + uint8_t buff[] = { txPower, rampTime }; return(this->SPIcommand(RADIOLIB_LR2021_CMD_SET_TX_PARAMS, true, buff, sizeof(buff))); } diff --git a/src/modules/LR2021/LR2021_config.cpp b/src/modules/LR2021/LR2021_config.cpp index b4a47d8098..66c67a926a 100644 --- a/src/modules/LR2021/LR2021_config.cpp +++ b/src/modules/LR2021/LR2021_config.cpp @@ -11,6 +11,76 @@ // maximum number of allowed frontend calibration attempts #define RADIOLIB_LR2021_MAX_CAL_ATTEMPTS (10) +static const LR2021::paTableEntry_t paOptTableLf[32] = { + { .paDutyCycle = 6, .paSlices = 2, .paVal = -18 }, // -9 + { .paDutyCycle = 6, .paSlices = 2, .paVal = -16 }, // -8 + { .paDutyCycle = 6, .paSlices = 2, .paVal = -14 }, // -7 + { .paDutyCycle = 6, .paSlices = 2, .paVal = -12 }, // -6 + { .paDutyCycle = 6, .paSlices = 2, .paVal = -10 }, // -5 + { .paDutyCycle = 6, .paSlices = 2, .paVal = -8 }, // -4 + { .paDutyCycle = 6, .paSlices = 2, .paVal = -6 }, // -3 + { .paDutyCycle = 6, .paSlices = 2, .paVal = -4 }, // -2 + { .paDutyCycle = 6, .paSlices = 2, .paVal = -2 }, // -1 + { .paDutyCycle = 6, .paSlices = 2, .paVal = 0 }, // 0 + { .paDutyCycle = 6, .paSlices = 2, .paVal = 2 }, // +1 + { .paDutyCycle = 6, .paSlices = 2, .paVal = 4 }, // +2 + { .paDutyCycle = 6, .paSlices = 2, .paVal = 6 }, // +3 + { .paDutyCycle = 6, .paSlices = 2, .paVal = 8 }, // +4 + { .paDutyCycle = 6, .paSlices = 2, .paVal = 10 }, // +5 + { .paDutyCycle = 6, .paSlices = 2, .paVal = 12 }, // +6 + { .paDutyCycle = 6, .paSlices = 2, .paVal = 14 }, // +7 + { .paDutyCycle = 6, .paSlices = 2, .paVal = 16 }, // +8 + { .paDutyCycle = 6, .paSlices = 2, .paVal = 18 }, // +9 + { .paDutyCycle = 2, .paSlices = 1, .paVal = 32 }, // +10 + { .paDutyCycle = 2, .paSlices = 2, .paVal = 32 }, // +11 + { .paDutyCycle = 5, .paSlices = 1, .paVal = 30 }, // +12 + { .paDutyCycle = 4, .paSlices = 3, .paVal = 31 }, // +13 + { .paDutyCycle = 4, .paSlices = 2, .paVal = 34 }, // +14 + { .paDutyCycle = 5, .paSlices = 4, .paVal = 33 }, // +15 + { .paDutyCycle = 4, .paSlices = 4, .paVal = 36 }, // +16 + { .paDutyCycle = 5, .paSlices = 6, .paVal = 36 }, // +17 + { .paDutyCycle = 5, .paSlices = 6, .paVal = 38 }, // +18 + { .paDutyCycle = 7, .paSlices = 4, .paVal = 38 }, // +19 + { .paDutyCycle = 6, .paSlices = 6, .paVal = 41 }, // +20 + { .paDutyCycle = 7, .paSlices = 7, .paVal = 42 }, // +21 + { .paDutyCycle = 7, .paSlices = 6, .paVal = 44 }, // +22 +}; + +static const LR2021::paTableEntry_t paOptTableHf[32] = { + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -38 }, // -19 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -36 }, // -18 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -34 }, // -17 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -32 }, // -16 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -30 }, // -15 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -28 }, // -14 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -26 }, // -13 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -24 }, // -12 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -22 }, // -11 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -20 }, // -10 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -18 }, // -9 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -16 }, // -8 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -14 }, // -7 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -12 }, // -6 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -10 }, // -5 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -8 }, // -4 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -6 }, // -3 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -4 }, // -2 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -2 }, // -1 + { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 4 }, // 0 + { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 6 }, // 1 + { .paDutyCycle = 12, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 7 }, // 2 + { .paDutyCycle = 9, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 8 }, // 3 + { .paDutyCycle = 9, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 10 }, // 4 + { .paDutyCycle = 15, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 15 }, // 5 + { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 16 }, // 6 + { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 18 }, // 7 + { .paDutyCycle = 15, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 21 }, // 8 + { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 22 }, // 9 + { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 24 }, // 10 + { .paDutyCycle = 10, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 24 }, // 11 + { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 24 }, // 12 +}; + int16_t LR2021::setFrequency(float freq) { return(this->setFrequency(freq, false)); } @@ -81,17 +151,17 @@ int16_t LR2021::setOutputPower(int8_t power, uint32_t rampTimeUs) { RADIOLIB_ASSERT(state); //! \TODO: [LR2021] how and when to configure OCP? - //! \TODO: [LR2021] Determine the optimal PA configuration // update PA config + LR2021::paTableEntry_t* paCfg = this->highFreq ? &paOptTableHf[power + 19] : &paOptTableLf[power + 9]; state = setPaConfig(this->highFreq, RADIOLIB_LR2021_PA_LF_MODE_FSM, - RADIOLIB_LR2021_PA_LF_DUTY_CYCLE_UNUSED, - RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, - RADIOLIB_LR2021_PA_HF_DUTY_CYCLE_UNUSED); + this->highFreq ? RADIOLIB_LR2021_PA_LF_DUTY_CYCLE_UNUSED : paCfg->paDutyCycle, + paCfg->paSlices, + this->highFreq ? (paCfg->paDutyCycle + RADIOLIB_LR2021_PA_HF_DUTY_CYCLE_UNUSED) : RADIOLIB_LR2021_PA_HF_DUTY_CYCLE_UNUSED); RADIOLIB_ASSERT(state); // set output power - state = setTxParams(power, roundRampTime(rampTimeUs)); + state = setTxParams(paCfg->paVal, roundRampTime(rampTimeUs)); return(state); } From 95f2e793b8c47736d481a7e5d022fcb348e34ec5 Mon Sep 17 00:00:00 2001 From: jgromes Date: Sat, 18 Apr 2026 15:06:27 +0000 Subject: [PATCH 2/5] [LR2021] Fix missing const --- src/modules/LR2021/LR2021_cmds_radio.cpp | 2 +- src/modules/LR2021/LR2021_config.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/LR2021/LR2021_cmds_radio.cpp b/src/modules/LR2021/LR2021_cmds_radio.cpp index fdc9332311..84af45ef44 100644 --- a/src/modules/LR2021/LR2021_cmds_radio.cpp +++ b/src/modules/LR2021/LR2021_cmds_radio.cpp @@ -103,7 +103,7 @@ int16_t LR2021::setPaConfig(uint8_t pa, uint8_t paLfMode, uint8_t paLfDutyCycle, } int16_t LR2021::setTxParams(int8_t txPower, uint8_t rampTime) { - uint8_t buff[] = { txPower, rampTime }; + uint8_t buff[] = { (uint8_t)txPower, rampTime }; return(this->SPIcommand(RADIOLIB_LR2021_CMD_SET_TX_PARAMS, true, buff, sizeof(buff))); } diff --git a/src/modules/LR2021/LR2021_config.cpp b/src/modules/LR2021/LR2021_config.cpp index 66c67a926a..3cd7e30d3f 100644 --- a/src/modules/LR2021/LR2021_config.cpp +++ b/src/modules/LR2021/LR2021_config.cpp @@ -152,7 +152,7 @@ int16_t LR2021::setOutputPower(int8_t power, uint32_t rampTimeUs) { //! \TODO: [LR2021] how and when to configure OCP? // update PA config - LR2021::paTableEntry_t* paCfg = this->highFreq ? &paOptTableHf[power + 19] : &paOptTableLf[power + 9]; + const LR2021::paTableEntry_t* paCfg = this->highFreq ? &paOptTableHf[power + 19] : &paOptTableLf[power + 9]; state = setPaConfig(this->highFreq, RADIOLIB_LR2021_PA_LF_MODE_FSM, this->highFreq ? RADIOLIB_LR2021_PA_LF_DUTY_CYCLE_UNUSED : paCfg->paDutyCycle, From 3ec0def3dee343fcb3208c6f20fbc458aec4b665 Mon Sep 17 00:00:00 2001 From: jgromes Date: Sun, 26 Apr 2026 19:31:15 +0200 Subject: [PATCH 3/5] [LR2021] Optimize LF PA configuration --- src/modules/LR2021/LR2021_config.cpp | 64 ++++++++++++++-------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/modules/LR2021/LR2021_config.cpp b/src/modules/LR2021/LR2021_config.cpp index 3cd7e30d3f..bd205a4adc 100644 --- a/src/modules/LR2021/LR2021_config.cpp +++ b/src/modules/LR2021/LR2021_config.cpp @@ -12,38 +12,38 @@ #define RADIOLIB_LR2021_MAX_CAL_ATTEMPTS (10) static const LR2021::paTableEntry_t paOptTableLf[32] = { - { .paDutyCycle = 6, .paSlices = 2, .paVal = -18 }, // -9 - { .paDutyCycle = 6, .paSlices = 2, .paVal = -16 }, // -8 - { .paDutyCycle = 6, .paSlices = 2, .paVal = -14 }, // -7 - { .paDutyCycle = 6, .paSlices = 2, .paVal = -12 }, // -6 - { .paDutyCycle = 6, .paSlices = 2, .paVal = -10 }, // -5 - { .paDutyCycle = 6, .paSlices = 2, .paVal = -8 }, // -4 - { .paDutyCycle = 6, .paSlices = 2, .paVal = -6 }, // -3 - { .paDutyCycle = 6, .paSlices = 2, .paVal = -4 }, // -2 - { .paDutyCycle = 6, .paSlices = 2, .paVal = -2 }, // -1 - { .paDutyCycle = 6, .paSlices = 2, .paVal = 0 }, // 0 - { .paDutyCycle = 6, .paSlices = 2, .paVal = 2 }, // +1 - { .paDutyCycle = 6, .paSlices = 2, .paVal = 4 }, // +2 - { .paDutyCycle = 6, .paSlices = 2, .paVal = 6 }, // +3 - { .paDutyCycle = 6, .paSlices = 2, .paVal = 8 }, // +4 - { .paDutyCycle = 6, .paSlices = 2, .paVal = 10 }, // +5 - { .paDutyCycle = 6, .paSlices = 2, .paVal = 12 }, // +6 - { .paDutyCycle = 6, .paSlices = 2, .paVal = 14 }, // +7 - { .paDutyCycle = 6, .paSlices = 2, .paVal = 16 }, // +8 - { .paDutyCycle = 6, .paSlices = 2, .paVal = 18 }, // +9 - { .paDutyCycle = 2, .paSlices = 1, .paVal = 32 }, // +10 - { .paDutyCycle = 2, .paSlices = 2, .paVal = 32 }, // +11 - { .paDutyCycle = 5, .paSlices = 1, .paVal = 30 }, // +12 - { .paDutyCycle = 4, .paSlices = 3, .paVal = 31 }, // +13 - { .paDutyCycle = 4, .paSlices = 2, .paVal = 34 }, // +14 - { .paDutyCycle = 5, .paSlices = 4, .paVal = 33 }, // +15 - { .paDutyCycle = 4, .paSlices = 4, .paVal = 36 }, // +16 - { .paDutyCycle = 5, .paSlices = 6, .paVal = 36 }, // +17 - { .paDutyCycle = 5, .paSlices = 6, .paVal = 38 }, // +18 - { .paDutyCycle = 7, .paSlices = 4, .paVal = 38 }, // +19 - { .paDutyCycle = 6, .paSlices = 6, .paVal = 41 }, // +20 - { .paDutyCycle = 7, .paSlices = 7, .paVal = 42 }, // +21 - { .paDutyCycle = 7, .paSlices = 6, .paVal = 44 }, // +22 + { .paDutyCycle = 1, .paSlices = 1, .paVal = 8 }, + { .paDutyCycle = 2, .paSlices = 2, .paVal = 1 }, + { .paDutyCycle = 2, .paSlices = 2, .paVal = 3 }, + { .paDutyCycle = 2, .paSlices = 2, .paVal = 5 }, + { .paDutyCycle = 1, .paSlices = 2, .paVal = 13 }, + { .paDutyCycle = 2, .paSlices = 1, .paVal = 13 }, + { .paDutyCycle = 2, .paSlices = 2, .paVal = 11 }, + { .paDutyCycle = 2, .paSlices = 2, .paVal = 13 }, + { .paDutyCycle = 3, .paSlices = 1, .paVal = 12 }, + { .paDutyCycle = 1, .paSlices = 1, .paVal = 18 }, + { .paDutyCycle = 1, .paSlices = 1, .paVal = 20 }, + { .paDutyCycle = 1, .paSlices = 1, .paVal = 23 }, + { .paDutyCycle = 1, .paSlices = 1, .paVal = 27 }, + { .paDutyCycle = 1, .paSlices = 1, .paVal = 33 }, + { .paDutyCycle = 1, .paSlices = 2, .paVal = 26 }, + { .paDutyCycle = 1, .paSlices = 2, .paVal = 31 }, + { .paDutyCycle = 1, .paSlices = 3, .paVal = 27 }, + { .paDutyCycle = 1, .paSlices = 1, .paVal = 37 }, + { .paDutyCycle = 1, .paSlices = 2, .paVal = 40 }, + { .paDutyCycle = 2, .paSlices = 1, .paVal = 38 }, + { .paDutyCycle = 2, .paSlices = 2, .paVal = 39 }, + { .paDutyCycle = 2, .paSlices = 4, .paVal = 40 }, + { .paDutyCycle = 2, .paSlices = 7, .paVal = 41 }, + { .paDutyCycle = 3, .paSlices = 2, .paVal = 39 }, + { .paDutyCycle = 3, .paSlices = 3, .paVal = 39 }, + { .paDutyCycle = 3, .paSlices = 6, .paVal = 38 }, + { .paDutyCycle = 4, .paSlices = 3, .paVal = 37 }, + { .paDutyCycle = 4, .paSlices = 5, .paVal = 37 }, + { .paDutyCycle = 4, .paSlices = 7, .paVal = 38 }, + { .paDutyCycle = 5, .paSlices = 3, .paVal = 37 }, + { .paDutyCycle = 5, .paSlices = 6, .paVal = 37 }, + { .paDutyCycle = 6, .paSlices = 7, .paVal = 35 }, }; static const LR2021::paTableEntry_t paOptTableHf[32] = { From 5247f9c8c8fa4999375dd93127a63e3cb4ae5b9f Mon Sep 17 00:00:00 2001 From: jgromes Date: Tue, 19 May 2026 19:47:29 +0100 Subject: [PATCH 4/5] [LR2021] Allow user to provide custom PA configuration --- src/modules/LR2021/LR2021.h | 25 ++++++++++++++----------- src/modules/LR2021/LR2021_commands.h | 1 + src/modules/LR2021/LR2021_config.cpp | 20 +++++++++++++++++--- src/modules/LR2021/LR2021_types.h | 11 +++++++++++ 4 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/modules/LR2021/LR2021.h b/src/modules/LR2021/LR2021.h index a33418818c..db169f3c45 100644 --- a/src/modules/LR2021/LR2021.h +++ b/src/modules/LR2021/LR2021.h @@ -68,17 +68,6 @@ class LR2021: public LRxxxx { MODE_TX_HF, }; - /*! - \struct paTableEntry_t - \brief This structure is used as entry in the PA lookup table, - to optimize PA configuration for minimum power consumption. - */ - struct __attribute__((packed)) paTableEntry_t { - uint8_t paDutyCycle: 4; - uint8_t paSlices: 4; - int8_t paVal; - }; - // basic methods /*! @@ -371,6 +360,17 @@ class LR2021: public LRxxxx { */ int16_t setOutputPower(int8_t power, uint32_t rampTimeUs); + /*! + \brief Sets custom PA configuration table. + \param table Pointer to user-provided PA configuration table. + The table MUST containt exactly RADIOLIB_LR2021_PA_TABLE_LEN entries, + one per each half-dBm step. The table is not copied, only reference to it is stored. + Set to NULL to return back to the default tables. + \param highFreq Whether this PA configuration is for the low-frequency sub-GHz PA (false), + or the high-frequency 2.4 GHz PA (true). + */ + void setPaTable(LR2021PaTableEntry_t* table, bool highFreq); + /*! \brief Check if output power is configurable. This method is needed for compatibility with PhysicalLayer::checkOutputPower. @@ -740,6 +740,9 @@ class LR2021: public LRxxxx { uint16_t bitRateFlrc = 0; uint8_t codingRateFlrc = 0; + // pointers to PA lookup tables - may be overridden by the user + LR2021PaTableEntry_t* paOptTable[2] = { nullptr, nullptr }; + int16_t modSetup(float freq, float tcxoVoltage, uint8_t modem); bool findChip(void); int16_t config(uint8_t modem); diff --git a/src/modules/LR2021/LR2021_commands.h b/src/modules/LR2021/LR2021_commands.h index 34b7c4d6f4..803fbf729d 100644 --- a/src/modules/LR2021/LR2021_commands.h +++ b/src/modules/LR2021/LR2021_commands.h @@ -344,6 +344,7 @@ #define RADIOLIB_LR2021_PA_HIGH_POWER (0x01UL << 0) // 1 0 high-power // RADIOLIB_LR2021_CMD_SET_PA_CONFIG +#define RADIOLIB_LR2021_PA_TABLE_LEN (32) #define RADIOLIB_LR2021_PA_LF_MODE_FSM (0x00UL << 0) // 1 0 PA LF mode: full single-ended mode #define RADIOLIB_LR2021_PA_LF_DUTY_CYCLE_UNUSED (0x06UL << 0) // 7 4 PA LF duty cycle: PA not used #define RADIOLIB_LR2021_PA_LF_SLICES_UNUSED (0x07UL << 0) // 3 0 PA LF slices: PA not used diff --git a/src/modules/LR2021/LR2021_config.cpp b/src/modules/LR2021/LR2021_config.cpp index bd205a4adc..fc956a44ea 100644 --- a/src/modules/LR2021/LR2021_config.cpp +++ b/src/modules/LR2021/LR2021_config.cpp @@ -11,7 +11,7 @@ // maximum number of allowed frontend calibration attempts #define RADIOLIB_LR2021_MAX_CAL_ATTEMPTS (10) -static const LR2021::paTableEntry_t paOptTableLf[32] = { +static const LR2021PaTableEntry_t paOptTableLf[RADIOLIB_LR2021_PA_TABLE_LEN] = { { .paDutyCycle = 1, .paSlices = 1, .paVal = 8 }, { .paDutyCycle = 2, .paSlices = 2, .paVal = 1 }, { .paDutyCycle = 2, .paSlices = 2, .paVal = 3 }, @@ -46,7 +46,7 @@ static const LR2021::paTableEntry_t paOptTableLf[32] = { { .paDutyCycle = 6, .paSlices = 7, .paVal = 35 }, }; -static const LR2021::paTableEntry_t paOptTableHf[32] = { +static const LR2021PaTableEntry_t paOptTableHf[RADIOLIB_LR2021_PA_TABLE_LEN] = { { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -38 }, // -19 { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -36 }, // -18 { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -34 }, // -17 @@ -151,8 +151,18 @@ int16_t LR2021::setOutputPower(int8_t power, uint32_t rampTimeUs) { RADIOLIB_ASSERT(state); //! \TODO: [LR2021] how and when to configure OCP? + + // poitners to default tables + LR2021PaTableEntry_t* defaultTables[] = { + const_cast(paOptTableLf), + const_cast(paOptTableHf), + }; + + // if the user pointers are not set, use the appropriate (LF/HF) default table + LR2021PaTableEntry_t* table = this->paOptTable[this->highFreq] ? this->paOptTable[this->highFreq] : defaultTables[this->highFreq]; + // update PA config - const LR2021::paTableEntry_t* paCfg = this->highFreq ? &paOptTableHf[power + 19] : &paOptTableLf[power + 9]; + LR2021PaTableEntry_t* paCfg = this->highFreq ? &table[power + 19] : &table[power + 9]; state = setPaConfig(this->highFreq, RADIOLIB_LR2021_PA_LF_MODE_FSM, this->highFreq ? RADIOLIB_LR2021_PA_LF_DUTY_CYCLE_UNUSED : paCfg->paDutyCycle, @@ -165,6 +175,10 @@ int16_t LR2021::setOutputPower(int8_t power, uint32_t rampTimeUs) { return(state); } +void LR2021::setPaTable(LR2021PaTableEntry_t* table, bool highFreq) { + this->paOptTable[highFreq] = table; +} + int16_t LR2021::checkOutputPower(int8_t power, int8_t* clipped) { if(this->highFreq) { if(clipped) { diff --git a/src/modules/LR2021/LR2021_types.h b/src/modules/LR2021/LR2021_types.h index c04aee3ecd..5ea11e9ec9 100644 --- a/src/modules/LR2021/LR2021_types.h +++ b/src/modules/LR2021/LR2021_types.h @@ -37,6 +37,17 @@ struct LR2021LoRaSideDetector_t { uint8_t syncWord; }; +/*! + \struct paTableEntry_t + \brief This structure is used as entry in the PA lookup table, + to optimize PA configuration for minimum power consumption. +*/ +struct __attribute__((packed)) LR2021PaTableEntry_t { + uint8_t paDutyCycle: 4; + uint8_t paSlices: 4; + int8_t paVal; +}; + #endif #endif From 5cf85a4a9ee61a83af392b7c1e9fc32959218fec Mon Sep 17 00:00:00 2001 From: jgromes Date: Tue, 19 May 2026 20:00:11 +0100 Subject: [PATCH 5/5] [LR2021] Fix cppcheck warning --- src/modules/LR2021/LR2021_config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/LR2021/LR2021_config.cpp b/src/modules/LR2021/LR2021_config.cpp index fc956a44ea..753487ce63 100644 --- a/src/modules/LR2021/LR2021_config.cpp +++ b/src/modules/LR2021/LR2021_config.cpp @@ -162,7 +162,7 @@ int16_t LR2021::setOutputPower(int8_t power, uint32_t rampTimeUs) { LR2021PaTableEntry_t* table = this->paOptTable[this->highFreq] ? this->paOptTable[this->highFreq] : defaultTables[this->highFreq]; // update PA config - LR2021PaTableEntry_t* paCfg = this->highFreq ? &table[power + 19] : &table[power + 9]; + const LR2021PaTableEntry_t* paCfg = this->highFreq ? &table[power + 19] : &table[power + 9]; state = setPaConfig(this->highFreq, RADIOLIB_LR2021_PA_LF_MODE_FSM, this->highFreq ? RADIOLIB_LR2021_PA_LF_DUTY_CYCLE_UNUSED : paCfg->paDutyCycle,