LCOV - code coverage report
Current view: top level - src/modules/LR2021 - LR2021_config.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 69 653 10.6 %
Date: 2026-11-08 19:43:08 Functions: 16 43 37.2 %

          Line data    Source code
       1             : #include "LR2021.h"
       2             : 
       3             : #include "../LR11x0/LR_common.h"
       4             : #include "LR2021_registers.h"
       5             : 
       6             : #include <math.h>
       7             : #include <string.h>
       8             : 
       9             : #if !RADIOLIB_EXCLUDE_LR2021
      10             : 
      11             : // maximum number of allowed frontend calibration attempts
      12             : #define RADIOLIB_LR2021_MAX_CAL_ATTEMPTS    (10)
      13             : 
      14             : static const LR2021PaTableEntry_t paOptTableLf[RADIOLIB_LR2021_PA_TABLE_LEN] = {
      15             :   { .paDutyCycle = 1, .paSlices = 1, .paVal = 8 },
      16             :   { .paDutyCycle = 2, .paSlices = 2, .paVal = 1 },
      17             :   { .paDutyCycle = 2, .paSlices = 2, .paVal = 3 },
      18             :   { .paDutyCycle = 2, .paSlices = 2, .paVal = 5 },
      19             :   { .paDutyCycle = 1, .paSlices = 2, .paVal = 13 },
      20             :   { .paDutyCycle = 2, .paSlices = 1, .paVal = 13 },
      21             :   { .paDutyCycle = 2, .paSlices = 2, .paVal = 11 },
      22             :   { .paDutyCycle = 2, .paSlices = 2, .paVal = 13 },
      23             :   { .paDutyCycle = 3, .paSlices = 1, .paVal = 12 },
      24             :   { .paDutyCycle = 1, .paSlices = 1, .paVal = 18 },
      25             :   { .paDutyCycle = 1, .paSlices = 1, .paVal = 20 },
      26             :   { .paDutyCycle = 1, .paSlices = 1, .paVal = 23 },
      27             :   { .paDutyCycle = 1, .paSlices = 1, .paVal = 27 },
      28             :   { .paDutyCycle = 1, .paSlices = 1, .paVal = 33 },
      29             :   { .paDutyCycle = 1, .paSlices = 2, .paVal = 26 },
      30             :   { .paDutyCycle = 1, .paSlices = 2, .paVal = 31 },
      31             :   { .paDutyCycle = 1, .paSlices = 3, .paVal = 27 },
      32             :   { .paDutyCycle = 1, .paSlices = 1, .paVal = 37 },
      33             :   { .paDutyCycle = 1, .paSlices = 2, .paVal = 40 },
      34             :   { .paDutyCycle = 2, .paSlices = 1, .paVal = 38 },
      35             :   { .paDutyCycle = 2, .paSlices = 2, .paVal = 39 },
      36             :   { .paDutyCycle = 2, .paSlices = 4, .paVal = 40 },
      37             :   { .paDutyCycle = 2, .paSlices = 7, .paVal = 41 },
      38             :   { .paDutyCycle = 3, .paSlices = 2, .paVal = 39 },
      39             :   { .paDutyCycle = 3, .paSlices = 3, .paVal = 39 },
      40             :   { .paDutyCycle = 3, .paSlices = 6, .paVal = 38 },
      41             :   { .paDutyCycle = 4, .paSlices = 3, .paVal = 37 },
      42             :   { .paDutyCycle = 4, .paSlices = 5, .paVal = 37 },
      43             :   { .paDutyCycle = 4, .paSlices = 7, .paVal = 38 },
      44             :   { .paDutyCycle = 5, .paSlices = 3, .paVal = 37 },
      45             :   { .paDutyCycle = 5, .paSlices = 6, .paVal = 37 },
      46             :   { .paDutyCycle = 6, .paSlices = 7, .paVal = 35 },
      47             : };
      48             : 
      49             : static const LR2021PaTableEntry_t paOptTableHf[RADIOLIB_LR2021_PA_TABLE_LEN] = {
      50             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -38 }, // -19
      51             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -36 }, // -18
      52             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -34 }, // -17
      53             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -32 }, // -16
      54             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -30 }, // -15
      55             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -28 }, // -14
      56             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -26 }, // -13
      57             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -24 }, // -12
      58             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -22 }, // -11
      59             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -20 }, // -10
      60             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -18 }, // -9
      61             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -16 }, // -8
      62             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -14 }, // -7
      63             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -12 }, // -6
      64             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -10 }, // -5
      65             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -8 },  // -4
      66             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -6 },  // -3
      67             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -4 },  // -2
      68             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = -2 },  // -1
      69             :   { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 4 },  // 0
      70             :   { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 6 },  // 1
      71             :   { .paDutyCycle = 12, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 7 },  // 2
      72             :   { .paDutyCycle = 9, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 8 },  // 3
      73             :   { .paDutyCycle = 9, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 10 },  // 4
      74             :   { .paDutyCycle = 15, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 15 },  // 5
      75             :   { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 16 },  // 6
      76             :   { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 18 },  // 7
      77             :   { .paDutyCycle = 15, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 21 },  // 8
      78             :   { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 22 },  // 9
      79             :   { .paDutyCycle = 14, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 24 },  // 10
      80             :   { .paDutyCycle = 10, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 24 },  // 11
      81             :   { .paDutyCycle = 0, .paSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED, .paVal = 24 },  // 12
      82             : };
      83             : 
      84           2 : int16_t LR2021::setFrequency(uint32_t freq) {
      85           2 :   return(this->setFrequency(freq, false));
      86             : }
      87             : 
      88           2 : int16_t LR2021::setFrequency(uint32_t freq, bool skipCalibration) {
      89             :   #if RADIOLIB_CHECK_PARAMS
      90           2 :   const bool lowBand = (freq >= RADIOLIB_UNIT_MEGA(150)) && (freq <= RADIOLIB_UNIT_MEGA(1090));
      91             :   // the sub-GHz-only parts have no high-frequency path, so only the low band is reachable there
      92           3 :   if(!(lowBand || (!this->subGhzOnly() &&
      93           1 :     (((freq >= RADIOLIB_UNIT_MEGA(1900)) && (freq <= RADIOLIB_UNIT_MEGA(2200))) ||
      94           0 :      ((freq >= RADIOLIB_UNIT_MEGA(2400)) && (freq <= RADIOLIB_UNIT_MEGA(2500))))))) {
      95           2 :       return(RADIOLIB_ERR_INVALID_FREQUENCY);
      96             :   }
      97             :   #endif
      98             : 
      99             :   // check if we need to recalibrate image
     100             :   int16_t state;
     101           0 :   if(!skipCalibration && (RADIOLIB_ABS_UNSIGNED(freq, this->freqHz) >= RADIOLIB_LR2021_CAL_IMG_FREQ_TRIG_MHZ)) {
     102             :     // calibration can fail if there is a strong interfering source
     103             :     // run it several times until it passes
     104           0 :     int i = 0;
     105           0 :     for(; i < RADIOLIB_LR2021_MAX_CAL_ATTEMPTS; i++) {
     106             :       // get the nearest multiple of 4 MHz
     107           0 :       uint16_t frequencies[3] = { (uint16_t)(freq / RADIOLIB_UNIT_MEGA(4)), 0, 0 };
     108           0 :       frequencies[0] |= (freq > RADIOLIB_LR2021_LF_CUTOFF_FREQ) ? RADIOLIB_LR2021_CALIBRATE_FE_HF_PATH : RADIOLIB_LR2021_CALIBRATE_FE_LF_PATH;
     109           0 :       state = calibrateFrontEnd(const_cast<const uint16_t*>(frequencies));
     110             : 
     111             :       // if something failed, check the device errors
     112           0 :       if(state != RADIOLIB_ERR_NONE) {
     113           0 :         uint16_t errors = 0;
     114           0 :         getErrors(&errors);
     115             :         RADIOLIB_DEBUG_BASIC_PRINTLN("Frontend calibration #%d failed, device errors: 0x%X", i, errors);
     116             : 
     117             :         // if this is caused by something else than RSSI saturation, repeating will not help
     118           0 :         if((errors & RADIOLIB_LR2021_SRC_SATURATION_CALIB_ERR) == 0) {
     119           0 :           return(state);
     120             :         }
     121             : 
     122             :         // wait a little while before the next attempt
     123           0 :         this->mod->hal->delay(5);
     124             :       
     125             :       } else {
     126             :         // calibration passed
     127           0 :         break;
     128             :       }
     129             : 
     130             :     }
     131             : 
     132           0 :     if(i == RADIOLIB_LR2021_MAX_CAL_ATTEMPTS) {
     133           0 :       return(RADIOLIB_ERR_FRONTEND_CALIBRATION_FAILED);
     134             :     }
     135             : 
     136             :   }
     137             : 
     138             :   // set frequency
     139           0 :   state = setRfFrequency((uint32_t)(freq));
     140           0 :   RADIOLIB_ASSERT(state);
     141           0 :   this->freqHz = freq;
     142             :   // a part with no high-frequency front end stays on the low path whatever it was handed
     143           0 :   this->highFreq = !this->subGhzOnly() && (freq > RADIOLIB_LR2021_LF_CUTOFF_FREQ);
     144             :   
     145             :   // power limits depend on the frequency band
     146           0 :   this->updatePowerLimits(this->highFreq);
     147             : 
     148           0 :   return(state);
     149             : }
     150             : 
     151           2 : int16_t LR2021::setOutputPower(int8_t power) {
     152           2 :   return(this->setOutputPower(power, 48));
     153             : }
     154             : 
     155           2 : int16_t LR2021::setOutputPower(int8_t power, uint32_t rampTimeUs) {
     156             :   // apply offset for external PA
     157           2 :   int8_t pwr = power;
     158           2 :   int16_t state = this->applyOutputPowerOffset(this->highFreq ? RADIOLIB_LR2021_HF_POUT_MIN : RADIOLIB_LR2021_LF_POUT_MIN, &power, &pwr);
     159           2 :   RADIOLIB_ASSERT(state);
     160             :   
     161             :   //! \TODO: [LR2021] how and when to configure OCP?
     162             : 
     163             :   // poitners to default tables
     164           2 :   LR2021PaTableEntry_t* defaultTables[] = { 
     165             :     const_cast<LR2021PaTableEntry_t*>(paOptTableLf),
     166             :     const_cast<LR2021PaTableEntry_t*>(paOptTableHf),
     167             :   };
     168             : 
     169             :   // if the user pointers are not set, use the appropriate (LF/HF) default table
     170           2 :   LR2021PaTableEntry_t* table = this->paOptTable[this->highFreq] ? this->paOptTable[this->highFreq] : defaultTables[this->highFreq];
     171             :   
     172             :   // update PA config
     173           2 :   const LR2021PaTableEntry_t* paCfg = this->highFreq ? &table[pwr + 19] : &table[pwr - RADIOLIB_LR2021_LF_POUT_MIN];
     174           4 :   state = setPaConfig(this->highFreq, 
     175             :     RADIOLIB_LR2021_PA_LF_MODE_FSM, 
     176           2 :     this->highFreq ? RADIOLIB_LR2021_PA_LF_DUTY_CYCLE_UNUSED : paCfg->paDutyCycle, 
     177           2 :     paCfg->paSlices, 
     178           2 :     this->highFreq ? (paCfg->paDutyCycle + RADIOLIB_LR2021_PA_HF_DUTY_CYCLE_UNUSED) : RADIOLIB_LR2021_PA_HF_DUTY_CYCLE_UNUSED);
     179           2 :   RADIOLIB_ASSERT(state);
     180             : 
     181             :   // set output power
     182           0 :   state = setTxParams(paCfg->paVal, roundRampTime(rampTimeUs));
     183           0 :   return(state);
     184             : }
     185             : 
     186           0 : void LR2021::setPaTable(LR2021PaTableEntry_t* table, bool highFreq) {
     187           0 :   this->paOptTable[highFreq] = table;
     188           0 : }
     189             : 
     190           0 : int16_t LR2021::setStandbyXOSC(bool enable) {
     191             :   // set the internal variable
     192           0 :   this->standbyXOSC = enable;
     193             : 
     194             :   // call standby to start the oscillator
     195           0 :   int16_t state = this->standbyXOSC ? standby(RADIOLIB_LR2021_STANDBY_XOSC) : standby(RADIOLIB_LR2021_STANDBY_RC);
     196           0 :   RADIOLIB_ASSERT(state);
     197             : 
     198             :   // update Rx/Tx fallback mode
     199           0 :   uint8_t mode = this->standbyXOSC ? RADIOLIB_LR2021_FALLBACK_MODE_STBY_XOSC : RADIOLIB_LR2021_FALLBACK_MODE_STBY_RC;
     200           0 :   return(setRxTxFallbackMode(mode));
     201             : }
     202             : 
     203           0 : void LR2021::setRfSwitchTable(const uint32_t (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]) {
     204             :   // find which pins are used
     205             :   // on LR2021, modes are configured per DIO (exact opposite of LR11x0)
     206           0 :   uint8_t dioConfigs[7] = { 0 };
     207           0 :   for(size_t i = 0; i < Module::RFSWITCH_MAX_PINS; i++) {
     208             :     // check if this pin is unused
     209           0 :     if(pins[i] == RADIOLIB_NC) { continue; }
     210             : 
     211             :     // only keep DIO pins, there may be some GPIOs in the switch tabke
     212           0 :     if((pins[i] & RFSWITCH_PIN_FLAG) == 0) { continue; }
     213             :     
     214             :     // find modes in which this pin is used
     215           0 :     uint32_t dioNum = RADIOLIB_LRXXXX_DIOx_VAL(pins[i]);
     216           0 :     size_t j = 0;
     217           0 :     while(table[j].mode != LR2021::MODE_END_OF_TABLE) {
     218           0 :       dioConfigs[dioNum] |= (table[j].values[i] == this->mod->hal->GpioLevelHigh) ? (1UL << (table[j].mode - 1)) : 0;
     219           0 :       j++;
     220             :     }
     221             : 
     222             :     // For DIO = 5, only DIO_SLEEP_PULL_UP is accepted. Otherwise the SetDioFunction returns FAIL.
     223           0 :     uint8_t pull = dioNum == 0 ? RADIOLIB_LR2021_DIO_SLEEP_PULL_UP : RADIOLIB_LR2021_DIO_SLEEP_PULL_AUTO;
     224             :     // enable RF control for this pin and set the modes in which it is active
     225           0 :     (void)this->setDioFunction(dioNum + 5, RADIOLIB_LR2021_DIO_FUNCTION_RF_SWITCH, pull);
     226           0 :     (void)this->setDioRfSwitchConfig(dioNum + 5, dioConfigs[dioNum]);
     227             :   }
     228             : 
     229             :   // also call the Module::setRfSwitchTable to cache the RF switch table
     230             :   // we may need to re-apply it after modem switch
     231           0 :   this->mod->setRfSwitchTable(pins, table);
     232           0 : }
     233             : 
     234           0 : int16_t LR2021::forceLDRO(bool enable) {
     235             :   // check packet type
     236           0 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     237           0 :   int16_t state = getPacketType(&type);
     238           0 :   RADIOLIB_ASSERT(state);
     239           0 :   if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
     240           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
     241             :   }
     242             : 
     243             :   // update modulation parameters
     244           0 :   this->ldroAuto = false;
     245           0 :   this->ldrOptimize = (uint8_t)enable;
     246           0 :   return(setLoRaModulationParams(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
     247             : }
     248             : 
     249           0 : int16_t LR2021::autoLDRO() {
     250           0 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     251           0 :   int16_t state = getPacketType(&type);
     252           0 :   RADIOLIB_ASSERT(state);
     253           0 :   if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
     254           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
     255             :   }
     256             : 
     257           0 :   this->ldroAuto = true;
     258           0 :   return(setLoRaModulationParams(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
     259             : }
     260             : 
     261           2 : int16_t LR2021::setBandwidth(uint32_t bw) {
     262             :   // check active modem
     263           2 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     264           2 :   int16_t state = getPacketType(&type);
     265           2 :   RADIOLIB_ASSERT(state);
     266           0 :   if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
     267           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
     268             :   }
     269             : 
     270             :   // check allowed bandwidth values
     271           0 :   switch(bw)  {
     272           0 :     case(7800):
     273           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_8;
     274           0 :       break;
     275           0 :     case(10400):
     276           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_10;
     277           0 :       break;
     278           0 :     case(15600):
     279           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_16;
     280           0 :       break;
     281           0 :     case(20800):
     282           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_21;
     283           0 :       break;
     284           0 :     case(31250):
     285           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_31;
     286           0 :       break;
     287           0 :     case(41670):
     288           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_41;
     289           0 :       break;
     290           0 :     case(62500):
     291           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_62;
     292           0 :       break;
     293           0 :     case(83340):
     294           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_83;
     295           0 :       break;
     296           0 :     case(RADIOLIB_UNIT_KILO(101)):
     297           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_101;
     298           0 :       break;
     299           0 :     case(203125):
     300             :     case(RADIOLIB_UNIT_KILO(203)):
     301           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_203;
     302           0 :       break;
     303           0 :     case(RADIOLIB_UNIT_KILO(125)):
     304           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_125;
     305           0 :       break;
     306           0 :     case(RADIOLIB_UNIT_KILO(250)):
     307           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_250;
     308           0 :       break;
     309           0 :     case(406250):
     310             :     case(RADIOLIB_UNIT_KILO(406)):
     311           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_406;
     312           0 :       break;
     313           0 :     case(RADIOLIB_UNIT_KILO(500)):
     314           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_500;
     315           0 :       break;
     316           0 :     case(812500):
     317             :     case(RADIOLIB_UNIT_KILO(812)):
     318           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_812;
     319           0 :       break;
     320           0 :     case(RADIOLIB_UNIT_KILO(1000)):
     321           0 :       this->bandwidth = RADIOLIB_LR2021_LORA_BW_1000;
     322           0 :       break;
     323           0 :     default:
     324           0 :       return(RADIOLIB_ERR_INVALID_BANDWIDTH);
     325             :   }
     326             : 
     327             :   // update modulation parameters
     328           0 :   this->bandwidthHz = bw;
     329           0 :   return(setLoRaModulationParams(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
     330             : }
     331             : 
     332           2 : int16_t LR2021::setSpreadingFactor(uint8_t sf) {
     333           2 :   return(this->setSpreadingFactor(sf, false));
     334             : }
     335             : 
     336           2 : int16_t LR2021::setSpreadingFactor(uint8_t sf, bool legacy) {
     337             :   // check active modem
     338           2 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     339           2 :   int16_t state = getPacketType(&type);
     340           2 :   RADIOLIB_ASSERT(state);
     341           0 :   if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
     342           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
     343             :   }
     344             : 
     345           0 :   RADIOLIB_CHECK_RANGE(sf, 5, 12, RADIOLIB_ERR_INVALID_SPREADING_FACTOR);
     346             : 
     347           0 :   if(legacy && (sf == 6)) {
     348             :     // enable SF6 legacy mode if requested
     349           0 :     state = this->writeRegMemMask32(RADIOLIB_LR2021_REG_LORA_MODEM_TXRX_CFG0, (3UL << 18), (1UL << 19));
     350             :   } else {
     351             :     // disable it in all other cases
     352           0 :     state = this->writeRegMemMask32(RADIOLIB_LR2021_REG_LORA_MODEM_TXRX_CFG0, (3UL << 18), 0);
     353             :   }
     354           0 :   RADIOLIB_ASSERT(state);
     355             : 
     356             :   // update modulation parameters
     357           0 :   this->spreadingFactor = sf;
     358           0 :   return(setLoRaModulationParams(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
     359             : }
     360             : 
     361           2 : int16_t LR2021::setCodingRate(uint8_t cr, bool longInterleave) {
     362             :   // check active modem
     363           2 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     364           2 :   int16_t state = getPacketType(&type);
     365           2 :   RADIOLIB_ASSERT(state);
     366           0 :   if(type == RADIOLIB_LR2021_PACKET_TYPE_LORA) {
     367           0 :     RADIOLIB_CHECK_RANGE(cr, 4, 8, RADIOLIB_ERR_INVALID_CODING_RATE);
     368             : 
     369           0 :     if(longInterleave) {
     370           0 :       switch(cr) {
     371           0 :         case 4:
     372           0 :           this->codingRate = 0;
     373           0 :           break;
     374           0 :         case 5:
     375             :         case 6:
     376           0 :           this->codingRate = cr;
     377           0 :           break;
     378           0 :         case 8: 
     379           0 :           this->codingRate = cr - 1;
     380           0 :           break;
     381           0 :         default:
     382           0 :           return(RADIOLIB_ERR_INVALID_CODING_RATE);
     383             :       }
     384             :     
     385             :     } else {
     386           0 :       this->codingRate = cr - 4;
     387             :     
     388             :     }
     389             : 
     390             :     // update modulation parameters
     391           0 :     return(setLoRaModulationParams(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
     392             : 
     393           0 :   } else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
     394           0 :     if(cr > RADIOLIB_LR2021_FLRC_CR_2_3) {
     395           0 :       return(RADIOLIB_ERR_INVALID_CODING_RATE);
     396             :     }
     397             : 
     398             :     // update modulation parameters
     399           0 :     this->codingRateFlrc = cr;
     400           0 :     return(setFlrcModulationParams(this->bitRateFlrc, this->codingRateFlrc, this->pulseShape));
     401             :   
     402             :   }
     403             :   
     404           0 :   return(RADIOLIB_ERR_WRONG_MODEM);
     405             : }
     406             : 
     407           0 : int16_t LR2021::setSyncWord(uint8_t syncWord) {
     408             :   // check active modem
     409           0 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     410           0 :   int16_t state = getPacketType(&type);
     411           0 :   RADIOLIB_ASSERT(state);
     412           0 :   if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
     413           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
     414             :   }
     415             :   
     416           0 :   return(setLoRaSyncword(syncWord));
     417             : }
     418             : 
     419           2 : int16_t LR2021::setPreambleLength(size_t preambleLength, size_t preambleDetLength) {
     420             :   // check active modem
     421           2 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     422           2 :   int16_t state = getPacketType(&type);
     423           2 :   RADIOLIB_ASSERT(state);
     424           0 :   if(type == RADIOLIB_LR2021_PACKET_TYPE_LORA) {
     425           0 :     this->preambleLengthLoRa = preambleLength;
     426           0 :     return(setLoRaPacketParams(this->preambleLengthLoRa, this->headerType, 
     427           0 :       (this->headerType == RADIOLIB_LRXXXX_LORA_HEADER_IMPLICIT) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled));
     428             :   
     429           0 :   } else if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
     430           0 :     this->preambleLengthGFSK = preambleLength;
     431           0 :     this->preambleDetLength = preambleDetLength;
     432           0 :     if(preambleDetLength == 0xFFFFFFFF) {
     433           0 :       this->preambleDetLength = (preambleLength / 8) << 3;
     434             :     }
     435           0 :     return(setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType, 
     436           0 :       (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
     437             :   
     438           0 :   } else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
     439           0 :     this->preambleLengthGFSK = preambleLength;
     440           0 :     this->preambleDetLength = preambleDetLength;
     441           0 :     if(preambleDetLength == 0xFFFFFFFF) {
     442           0 :       this->preambleDetLength = (preambleLength / 8) << 3;
     443             :     }
     444           0 :     return(setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType, 
     445           0 :       (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
     446             :     
     447           0 :   } else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
     448           0 :     if((preambleLength % 4) != 0) {
     449           0 :       return(RADIOLIB_ERR_INVALID_PREAMBLE_LENGTH);
     450             :     }
     451           0 :     this->preambleLengthGFSK = (preambleLength / 4) - 1;
     452           0 :     return(setFlrcPacketParams(this->preambleLengthGFSK, this->syncWordLength, 1, 0x01, this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, this->crcLenGFSK, 
     453           0 :       (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH));
     454             : 
     455             :   }
     456             : 
     457           0 :   return(RADIOLIB_ERR_WRONG_MODEM);
     458             : }
     459             : 
     460           0 : int16_t LR2021::setTCXO(RadioLibTCXOVoltage_t voltage, uint32_t delay) {
     461             :   // force RC oscillator - typically this is called on startup,
     462             :   // so we cannot rely on what the user may have provided
     463           0 :   (void)standby(RADIOLIB_LR2021_STANDBY_RC);
     464             : 
     465             :   // check oscillator startup error flag and clear it
     466           0 :   uint16_t errors = 0;
     467           0 :   int16_t state = getErrors(&errors);
     468           0 :   RADIOLIB_ASSERT(state);
     469           0 :   if(errors & RADIOLIB_LR2021_HF_XOSC_START_ERR) {
     470           0 :     clearErrors();
     471             :   }
     472             : 
     473             :   // check disable
     474           0 :   if(voltage == RadioLibTCXOVoltage_t::VoltageNone) {
     475           0 :     setTcxoMode(0, 0);
     476           0 :     return(reset());
     477             :   }
     478             : 
     479             :   // check allowed voltage values
     480           0 :   if(voltage > RadioLibTCXOVoltage_t::Voltage3V3) {
     481           0 :     return(RADIOLIB_ERR_INVALID_TCXO_VOLTAGE);
     482             :   }
     483             : 
     484             :   // calculate delay value
     485           0 :   uint32_t delayValue = (uint32_t)((float)delay * (1.0f / 30.52f));
     486           0 :   if(delayValue == 0) {
     487           0 :     delayValue = 1;
     488             :   }
     489             :  
     490             :   // enable TCXO control
     491           0 :   return(setTcxoMode((uint8_t)voltage, delayValue));
     492             : }
     493             : 
     494           0 : int16_t LR2021::setCRC(uint8_t len, uint32_t initial, uint32_t polynomial, bool inverted) {
     495             :   // check active modem
     496           0 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     497           0 :   int16_t state = getPacketType(&type);
     498           0 :   RADIOLIB_ASSERT(state);
     499           0 :   if(type == RADIOLIB_LR2021_PACKET_TYPE_LORA) {
     500             :     // LoRa CRC doesn't allow to set CRC polynomial, initial value, or inversion
     501           0 :     this->crcTypeLoRa = len > 0;
     502           0 :     return(setLoRaPacketParams(this->preambleLengthLoRa, this->headerType, 
     503           0 :       (this->headerType == RADIOLIB_LRXXXX_LORA_HEADER_IMPLICIT) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled));
     504             :   
     505           0 :   } else if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
     506           0 :     if(len > 4) {
     507           0 :       return(RADIOLIB_ERR_INVALID_CRC_CONFIGURATION);
     508             :     }
     509             : 
     510           0 :     this->crcTypeGFSK = len;
     511           0 :     if((this->crcTypeGFSK != RADIOLIB_LR2021_GFSK_OOK_CRC_OFF) && inverted) {
     512           0 :       this->crcTypeGFSK += 0x08;
     513             :     }
     514             : 
     515           0 :     this->crcLenGFSK = len;
     516           0 :     state = setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType, 
     517           0 :       (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
     518           0 :     RADIOLIB_ASSERT(state);
     519             : 
     520           0 :     return(setGfskCrcParams(polynomial, initial));
     521             : 
     522           0 :   } else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
     523           0 :     if(len > 4) {
     524           0 :       return(RADIOLIB_ERR_INVALID_CRC_CONFIGURATION);
     525             :     }
     526             : 
     527           0 :     this->crcTypeGFSK = len;
     528           0 :     if((this->crcTypeGFSK != RADIOLIB_LR2021_GFSK_OOK_CRC_OFF) && inverted) {
     529           0 :       this->crcTypeGFSK += 0x08;
     530             :     }
     531             : 
     532           0 :     state = setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType, 
     533           0 :       (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
     534           0 :     RADIOLIB_ASSERT(state);
     535             : 
     536           0 :     return(setOokCrcParams(polynomial, initial));
     537             :   
     538           0 :   } else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
     539           0 :     if((len == 1) || (len > 4)) {
     540           0 :       return(RADIOLIB_ERR_INVALID_CRC_CONFIGURATION);
     541             :     }
     542             :     
     543           0 :     this->crcLenGFSK = len ? len - 1 : 0;
     544           0 :     return(setFlrcPacketParams(this->preambleLengthGFSK, this->syncWordLength, 1, 0x01, this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, this->crcLenGFSK, 
     545           0 :       (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH));
     546             :       
     547             :   }
     548             : 
     549           0 :   return(RADIOLIB_ERR_WRONG_MODEM);
     550             : }
     551             : 
     552           2 : int16_t LR2021::invertIQ(bool enable) {
     553             :   // check active modem
     554           2 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     555           2 :   int16_t state = getPacketType(&type);
     556           2 :   RADIOLIB_ASSERT(state);
     557           0 :   if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
     558           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
     559             :   }
     560             : 
     561           0 :   this->invertIQEnabled = enable;
     562           0 :   return(setLoRaPacketParams(this->preambleLengthLoRa, this->headerType, 
     563           0 :     (this->headerType == RADIOLIB_LRXXXX_LORA_HEADER_IMPLICIT) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled));
     564             : }
     565             : 
     566           2 : int16_t LR2021::setBitRate(uint32_t br) {
     567             :   // check active modem
     568           2 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     569           2 :   int16_t state = getPacketType(&type);
     570           2 :   RADIOLIB_ASSERT(state);
     571           0 :   if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
     572           0 :     RADIOLIB_CHECK_RANGE(br, 500, RADIOLIB_UNIT_MEGA(2), RADIOLIB_ERR_INVALID_BIT_RATE);
     573             :     //! \TODO: [LR2021] implement fractional bit rate configuration
     574           0 :     this->bitRate = br;
     575           0 :     state = setGfskModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev);
     576           0 :     return(state);
     577             :   
     578           0 :   } else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
     579           0 :     RADIOLIB_CHECK_RANGE(br, 500, RADIOLIB_UNIT_MEGA(2), RADIOLIB_ERR_INVALID_BIT_RATE);
     580             :     //! \TODO: [LR2021] implement fractional bit rate configuration
     581           0 :     this->bitRate = br;
     582             :     //! \TODO: [LR2021] implement OOK magnitude depth configuration
     583           0 :     state = setOokModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, RADIOLIB_LR2021_OOK_DEPTH_FULL);
     584           0 :     return(state);
     585             :     
     586           0 :   } else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
     587           0 :     switch(br) {
     588           0 :       case(RADIOLIB_UNIT_KILO(260)):
     589           0 :         this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_260;
     590           0 :         break;
     591           0 :       case(RADIOLIB_UNIT_KILO(325)):
     592           0 :         this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_325;
     593           0 :         break;
     594           0 :       case(RADIOLIB_UNIT_KILO(520)):
     595           0 :         this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_520;
     596           0 :         break;
     597           0 :       case(RADIOLIB_UNIT_KILO(650)):
     598           0 :         this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_650;
     599           0 :         break;
     600           0 :       case(RADIOLIB_UNIT_KILO(1040)):
     601           0 :         this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_1040;
     602           0 :         break;
     603           0 :       case(RADIOLIB_UNIT_KILO(1300)):
     604           0 :         this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_1300;
     605           0 :         break;
     606           0 :       case(RADIOLIB_UNIT_KILO(2080)):
     607           0 :         this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_2080;
     608           0 :         break;
     609           0 :       case(RADIOLIB_UNIT_KILO(2600)):
     610           0 :         this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_2600;
     611           0 :         break;
     612           0 :       default:
     613           0 :         return(RADIOLIB_ERR_INVALID_BIT_RATE);
     614             :     }
     615             :     
     616             :     // it is slightly weird to reuse the GFSK bitrate variable in this way
     617             :     // but if GFSK gets enabled it should get reset anyway ... I think
     618           0 :     this->bitRate = br;
     619             : 
     620             :     // update modulation parameters
     621           0 :     return(setFlrcModulationParams(this->bitRateFlrc, this->codingRateFlrc, this->pulseShape));
     622             :   }
     623             : 
     624           0 :   return(RADIOLIB_ERR_WRONG_MODEM);  
     625             : }
     626             : 
     627           2 : int16_t LR2021::setFrequencyDeviation(uint32_t freqDev) {
     628             :   // check active modem
     629           2 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     630           2 :   int16_t state = getPacketType(&type);
     631           2 :   RADIOLIB_ASSERT(state);
     632           0 :   if(type != RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
     633           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
     634             :   }
     635             : 
     636             :   // set frequency deviation to lowest available setting (required for digimodes)
     637           0 :   uint32_t newFreqDev = freqDev ? freqDev : 600;
     638             : 
     639           0 :   RADIOLIB_CHECK_RANGE(newFreqDev, 600, RADIOLIB_UNIT_KILO(500), RADIOLIB_ERR_INVALID_FREQUENCY_DEVIATION);
     640           0 :   this->frequencyDev = newFreqDev;
     641           0 :   state = setGfskModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev);
     642           0 :   return(state);
     643             : }
     644             : 
     645           0 : int16_t LR2021::setRxBandwidth(uint32_t rxBw) {
     646             :   // check active modem
     647           0 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     648           0 :   int16_t state = getPacketType(&type);
     649           0 :   RADIOLIB_ASSERT(state);
     650           0 :   if(!((type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) || 
     651           0 :        (type == RADIOLIB_LR2021_PACKET_TYPE_OOK))) {
     652           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
     653             :   }
     654             : 
     655           0 :   const uint32_t rxBwAvg[] = {
     656             :     5300, 6600, 8500, 10800, 13400, 17000, 21100, 26400,
     657             :     34100, 42400, 52900, 68200, 84700, 105800, 136400,
     658             :     169500, 211600, 272900, 339000, 423300, 515900,
     659             :     611100, 717900, 940100, 1666500, 2444000, 2871000,
     660             :   };
     661             : 
     662           0 :   const uint8_t rxBwLut[] = {
     663             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_4_8,
     664             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_5_8,
     665             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_7_4,
     666             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_9_7,
     667             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_12_0,
     668             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_14_9,
     669             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_19_2,
     670             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_23_1,
     671             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_29_8,
     672             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_38_5,
     673             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_46_3,
     674             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_59_5,
     675             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_76_9,
     676             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_92_6,
     677             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_119_0,
     678             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_153_8,
     679             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_185_2,
     680             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_238_1,
     681             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_307_7,
     682             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_370_4,
     683             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_476_2,
     684             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_555_6,
     685             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_666_7,
     686             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_769_2,
     687             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_1111,
     688             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_2222,
     689             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_2666,
     690             :     RADIOLIB_LR2021_GFSK_OOK_RX_BW_3076,
     691             :   };
     692             : 
     693           0 :   state = findRxBw(rxBw, rxBwAvg, rxBwLut, sizeof(rxBwLut)/sizeof(rxBwLut[0]), RADIOLIB_UNIT_KILO(3076), &this->rxBandwidth);
     694           0 :   RADIOLIB_ASSERT(state);
     695             : 
     696             :   // update modulation parameters
     697           0 :   if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
     698           0 :     state = setGfskModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev);
     699             :   } else {
     700           0 :     state = setOokModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, RADIOLIB_LR2021_OOK_DEPTH_FULL);
     701             :   }
     702           0 :   return(state);
     703             : }
     704             : 
     705           2 : int16_t LR2021::setSyncWord(uint8_t* syncWord, size_t len) {
     706           2 :   if(len > RADIOLIB_LR2021_GFSK_SYNC_WORD_LEN) {
     707           0 :     return(RADIOLIB_ERR_INVALID_SYNC_WORD);
     708             :   }
     709             : 
     710             :   // check active modem
     711           2 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     712           2 :   int16_t state = getPacketType(&type);
     713           2 :   RADIOLIB_ASSERT(state);
     714             : 
     715           0 :   uint32_t sync = 0;
     716           0 :   switch(type) {
     717           0 :     case(RADIOLIB_LR2021_PACKET_TYPE_GFSK):
     718             :       // update sync word length
     719           0 :       this->syncWordLength = len*8;
     720             : 
     721             :       // default to MSB-first
     722           0 :       return(setGfskSyncword(const_cast<const uint8_t*>(syncWord), len, true));
     723             :     
     724           0 :     case(RADIOLIB_LR2021_PACKET_TYPE_OOK):
     725             :       // default to MSB-first
     726           0 :       return(setOokSyncword(const_cast<const uint8_t*>(syncWord), len, true));
     727             :     
     728           0 :     case(RADIOLIB_LR2021_PACKET_TYPE_LORA):
     729             :       // with length set to 1 and LoRa modem active, assume it is the LoRa sync word
     730           0 :       if(len > 1) {
     731           0 :         return(RADIOLIB_ERR_INVALID_SYNC_WORD);
     732             :       }
     733           0 :       return(setSyncWord(syncWord[0]));
     734             :     
     735           0 :     case(RADIOLIB_LR2021_PACKET_TYPE_LR_FHSS):
     736             :       // with length set to 4 and LR-FHSS modem active, assume it is the LR-FHSS sync word
     737           0 :       if(len != sizeof(uint32_t)) {
     738           0 :         return(RADIOLIB_ERR_INVALID_SYNC_WORD);
     739             :       }
     740           0 :       memcpy(&sync, syncWord, sizeof(uint32_t));
     741           0 :       return(lrFhssSetSyncword(sync));
     742             :     
     743           0 :     case(RADIOLIB_LR2021_PACKET_TYPE_FLRC):
     744             :       // FLRC requires 16 or 32-bit sync word
     745           0 :       if(!((len == 0) || (len == 2) || (len == 4))) {
     746           0 :         return(RADIOLIB_ERR_INVALID_SYNC_WORD);
     747             :       }
     748             : 
     749             :       // update sync word length
     750           0 :       this->syncWordLength = len;
     751           0 :       state = setFlrcPacketParams(this->preambleLengthGFSK, this->syncWordLength, 1, 0x01, this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, this->crcLenGFSK, 
     752           0 :         (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH);
     753           0 :       RADIOLIB_ASSERT(state);
     754             : 
     755           0 :       sync |= (uint32_t)syncWord[0] << 24;
     756           0 :       sync |= (uint32_t)syncWord[1] << 16;
     757           0 :       sync |= (uint32_t)syncWord[2] << 8;
     758           0 :       sync |= (uint32_t)syncWord[3];
     759           0 :       return(setFlrcSyncWord(1, sync));
     760             :   }
     761             : 
     762           0 :   return(RADIOLIB_ERR_WRONG_MODEM);
     763             : }
     764             : 
     765           2 : int16_t LR2021::setDataShaping(uint8_t sh) {
     766             :   // check active modem
     767           2 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     768           2 :   int16_t state = getPacketType(&type);
     769           2 :   RADIOLIB_ASSERT(state);
     770             : 
     771             :   // set data shaping
     772           0 :   switch(sh) {
     773           0 :     case RADIOLIB_SHAPING_NONE:
     774           0 :       this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_NONE;
     775           0 :       break;
     776           0 :     case RADIOLIB_SHAPING_0_3:
     777           0 :       this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_GAUSS_BT_0_3;
     778           0 :       break;
     779           0 :     case RADIOLIB_SHAPING_0_5:
     780           0 :       this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_GAUSS_BT_0_5;
     781           0 :       break;
     782           0 :     case RADIOLIB_SHAPING_0_7:
     783           0 :       this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_GAUSS_BT_0_7;
     784           0 :       break;
     785           0 :     case RADIOLIB_SHAPING_1_0:
     786           0 :       this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_GAUSS_BT_1_0;
     787           0 :       break;
     788           0 :     default:
     789           0 :       return(RADIOLIB_ERR_INVALID_DATA_SHAPING);
     790             :   }
     791             : 
     792             :   // update modulation parameters
     793           0 :   if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
     794           0 :     return(setFlrcModulationParams(this->bitRateFlrc, this->codingRateFlrc, this->pulseShape));
     795           0 :   } else if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
     796           0 :     return(setGfskModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev));
     797           0 :   } else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
     798           0 :     return(setOokModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, RADIOLIB_LR2021_OOK_DEPTH_FULL));
     799             :   }
     800           0 :   return(RADIOLIB_ERR_WRONG_MODEM);
     801             : }
     802             : 
     803           2 : int16_t LR2021::setEncoding(uint8_t encoding) {
     804             :   // check active modem
     805           2 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     806           2 :   int16_t state = getPacketType(&type);
     807           2 :   RADIOLIB_ASSERT(state);
     808             : 
     809           0 :   switch(type) {
     810           0 :     case(RADIOLIB_LR2021_PACKET_TYPE_GFSK):
     811           0 :       return(setWhitening(encoding == RADIOLIB_ENCODING_WHITENING));
     812             :     
     813           0 :     case(RADIOLIB_LR2021_PACKET_TYPE_OOK):
     814           0 :       switch(encoding) {
     815           0 :         case(RADIOLIB_ENCODING_NRZ):
     816             :         case(RADIOLIB_ENCODING_WHITENING):
     817           0 :           return(setWhitening(encoding == RADIOLIB_ENCODING_WHITENING));
     818             :         
     819           0 :         case(RADIOLIB_ENCODING_MANCHESTER):
     820           0 :           state = setWhitening(false);
     821           0 :           RADIOLIB_ASSERT(state);
     822           0 :           this->whitening = RADIOLIB_LR2021_OOK_MANCHESTER_ON;
     823           0 :           return(setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType, 
     824           0 :             (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
     825             :         
     826           0 :         case(RADIOLIB_ENCODING_MANCHESTER_INV):
     827           0 :           state = setWhitening(false);
     828           0 :           RADIOLIB_ASSERT(state);
     829           0 :           this->whitening = RADIOLIB_LR2021_OOK_MANCHESTER_ON_INV;
     830           0 :           return(setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType, 
     831           0 :             (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
     832             :         
     833           0 :         default:
     834           0 :           return(RADIOLIB_ERR_INVALID_ENCODING);
     835             :       }
     836             :     
     837           0 :     default:
     838           0 :       return(RADIOLIB_ERR_WRONG_MODEM);
     839             :   }
     840             : 
     841             :   return(state);
     842             : }
     843             : 
     844           0 : int16_t LR2021::fixedPacketLengthMode(uint8_t len) {
     845           0 :   return(setPacketMode(RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, len));
     846             : }
     847             : 
     848           0 : int16_t LR2021::variablePacketLengthMode(uint8_t maxLen) {
     849           0 :   return(setPacketMode(RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_VARIABLE_8BIT, maxLen));
     850             : }
     851             : 
     852           0 : int16_t LR2021::setWhitening(bool enabled, uint16_t initial) {
     853             :   // check active modem
     854           0 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     855           0 :   int16_t state = getPacketType(&type);
     856           0 :   RADIOLIB_ASSERT(state);
     857             :   
     858           0 :   switch(type) {
     859           0 :     case(RADIOLIB_LR2021_PACKET_TYPE_GFSK):
     860             :       //! \TODO: [LR2021] Implement SX128x-compatible whitening
     861           0 :       if(enabled) {
     862           0 :         state = setGfskWhiteningParams(RADIOLIB_LR2021_GFSK_WHITENING_TYPE_SX126X_LR11XX, initial);
     863           0 :         RADIOLIB_ASSERT(state);
     864             :       }
     865           0 :       this->whitening = enabled;
     866           0 :       return(setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType, 
     867           0 :         (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
     868             :     
     869           0 :     case(RADIOLIB_LR2021_PACKET_TYPE_OOK):
     870           0 :       this->whitening = enabled;
     871           0 :       if(enabled) {
     872             :         //! \TODO: [LR2021] Implement configurable index and polynomial
     873           0 :         state = setOokWhiteningParams(12, 0x01FF, initial);
     874             :       } else {
     875           0 :         state = setOokWhiteningParams(0, 0, 0);
     876             :       }
     877           0 :       RADIOLIB_ASSERT(state);
     878           0 :       return(setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType, 
     879           0 :         (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
     880             :   }
     881             : 
     882           0 :   return(RADIOLIB_ERR_WRONG_MODEM);
     883             : }
     884             : 
     885           2 : int16_t LR2021::checkDataRate(DataRate_t dr, ModemType_t modem) {
     886           2 :   int16_t state = RADIOLIB_ERR_UNKNOWN;
     887             : 
     888             :   // retrieve modem if not supplied
     889           2 :   if(modem == RADIOLIB_MODEM_NONE) {
     890           2 :     state = this->getModem(&modem);
     891           2 :     RADIOLIB_ASSERT(state);
     892             :   }
     893             : 
     894             :   // select interpretation based on modem
     895           0 :   if(modem == RADIOLIB_MODEM_FSK) {
     896           0 :     RADIOLIB_CHECK_RANGE(dr.fsk.bitRate, 500, RADIOLIB_UNIT_KILO(2000), RADIOLIB_ERR_INVALID_BIT_RATE);
     897           0 :     RADIOLIB_CHECK_RANGE(dr.fsk.freqDev, 600, RADIOLIB_UNIT_KILO(500), RADIOLIB_ERR_INVALID_FREQUENCY_DEVIATION);
     898           0 :     return(RADIOLIB_ERR_NONE);
     899             : 
     900           0 :   } else if(modem == RADIOLIB_MODEM_LORA) {
     901           0 :     RADIOLIB_CHECK_RANGE(dr.lora.spreadingFactor, 5, 12, RADIOLIB_ERR_INVALID_SPREADING_FACTOR);
     902           0 :     RADIOLIB_CHECK_RANGE(dr.lora.bandwidth, 31250, RADIOLIB_UNIT_KILO(500), RADIOLIB_ERR_INVALID_BANDWIDTH);
     903           0 :     RADIOLIB_CHECK_RANGE(dr.lora.codingRate, 4, 8, RADIOLIB_ERR_INVALID_CODING_RATE);
     904           0 :     return(RADIOLIB_ERR_NONE);
     905             :   
     906             :   }
     907             : 
     908           0 :   return(state);
     909             : }
     910             : 
     911           0 : int16_t LR2021::setLrFhssConfig(uint8_t bw, uint8_t cr, bool narrowGrid, uint8_t hdrCount, uint16_t hopSeed) {
     912             :   // check active modem
     913           0 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     914           0 :   int16_t state = getPacketType(&type);
     915           0 :   RADIOLIB_ASSERT(state);
     916           0 :   if(type != RADIOLIB_LR2021_PACKET_TYPE_LR_FHSS) {
     917           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
     918             :   }
     919             : 
     920             :   // check and cache all parameters
     921           0 :   this->lrFhssGrid = narrowGrid ? RADIOLIB_LRXXXX_LR_FHSS_GRID_STEP_NON_FCC : RADIOLIB_LRXXXX_LR_FHSS_GRID_STEP_FCC;
     922           0 :   RADIOLIB_CHECK_RANGE((int8_t)cr, (int8_t)RADIOLIB_LRXXXX_LR_FHSS_CR_5_6, (int8_t)RADIOLIB_LRXXXX_LR_FHSS_CR_1_3, RADIOLIB_ERR_INVALID_CODING_RATE);
     923           0 :   this->lrFhssCr = cr;
     924           0 :   RADIOLIB_CHECK_RANGE((int8_t)bw, (int8_t)RADIOLIB_LRXXXX_LR_FHSS_BW_39_06, (int8_t)RADIOLIB_LRXXXX_LR_FHSS_BW_1574_2, RADIOLIB_ERR_INVALID_BANDWIDTH);
     925           0 :   this->lrFhssBw = bw;
     926           0 :   RADIOLIB_CHECK_RANGE(hdrCount, 1, 4, RADIOLIB_ERR_INVALID_BIT_RANGE);
     927           0 :   this->lrFhssHdrCount = hdrCount;
     928           0 :   RADIOLIB_CHECK_RANGE((int16_t)hopSeed, (int16_t)0x000, (int16_t)0x1FF, RADIOLIB_ERR_INVALID_DATA_SHAPING);
     929           0 :   this->lrFhssHopSeq = hopSeed;
     930           0 :   return(RADIOLIB_ERR_NONE);
     931             : }
     932             : 
     933           0 : int16_t LR2021::setRxBoostedGainMode(uint8_t level) {
     934           0 :   if(this->highFreq) {
     935           0 :     this->gainModeHf = level;
     936             :   } else {
     937           0 :     this->gainModeLf = level;
     938             :   }
     939           0 :   return(this->setRxPath(this->highFreq ? RADIOLIB_LR2021_RX_PATH_HF : RADIOLIB_LR2021_RX_PATH_LF, this->highFreq ? this->gainModeHf : this->gainModeLf));
     940             : }
     941             : 
     942           0 : int16_t LR2021::setPacketMode(uint8_t mode, uint8_t len) {
     943             :   // check active modem
     944           0 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     945           0 :   int16_t state = getPacketType(&type);
     946           0 :   RADIOLIB_ASSERT(state);
     947           0 :   if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
     948             :     // set requested packet mode
     949           0 :     state = setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, mode, len, this->crcTypeGFSK, this->whitening);
     950           0 :     RADIOLIB_ASSERT(state);
     951             : 
     952             :     // update cached value
     953           0 :     this->packetType = mode;
     954           0 :     this->implicitLen = len;
     955           0 :     return(state);
     956             :   
     957           0 :   } else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
     958             :     // set requested packet mode
     959           0 :     state = setOokPacketParams(this->preambleLengthGFSK, this->addrComp, mode, len, this->crcTypeGFSK, this->whitening);
     960           0 :     RADIOLIB_ASSERT(state);
     961             : 
     962             :     // update cached value
     963           0 :     this->packetType = mode;
     964           0 :     this->implicitLen = len;
     965           0 :     return(state);
     966             :   
     967           0 :   } else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
     968           0 :     state = setFlrcPacketParams(this->preambleLengthGFSK, this->syncWordLength, 1, 0x01, mode == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, this->crcLenGFSK, len);
     969           0 :     RADIOLIB_ASSERT(state);
     970             : 
     971           0 :     this->packetType = mode;
     972           0 :     this->implicitLen = len;
     973           0 :     return(state);
     974             :   
     975           0 :   } else if(type == RADIOLIB_LR2021_PACKET_TYPE_LORA) {
     976             :     // LoRa mode does not have fixed/variable packet length mode, but it does have implicit/explicit header
     977             :     // in terms of packet length, that is equivalent with fixed/variable packets, so let's assume that's what the user meant
     978           0 :     if(mode == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) {
     979           0 :       return(this->implicitHeader(len));
     980             :     }
     981           0 :     return(this->explicitHeader());
     982             :   
     983             :   }
     984             : 
     985             : 
     986           0 :   return(RADIOLIB_ERR_WRONG_MODEM);
     987             : }
     988             : 
     989           0 : int16_t LR2021::setLoRaHeaderType(uint8_t hdrType, size_t len) {
     990           0 :   uint8_t modem = RADIOLIB_LR2021_PACKET_TYPE_NONE;
     991           0 :   int16_t state = getPacketType(&modem);
     992           0 :   RADIOLIB_ASSERT(state);
     993           0 :   if(modem != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
     994           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
     995             :   }
     996             : 
     997             :   // set requested packet mode
     998           0 :   state = setLoRaPacketParams(this->preambleLengthLoRa, hdrType, len, this->crcTypeLoRa, this->invertIQEnabled);
     999           0 :   RADIOLIB_ASSERT(state);
    1000             : 
    1001             :   // update cached value
    1002           0 :   this->headerType = hdrType;
    1003           0 :   this->implicitLen = len;
    1004             : 
    1005           0 :   return(state);
    1006             : }
    1007             : 
    1008           0 : int16_t LR2021::implicitHeader(size_t len) {
    1009           0 :   return(this->setLoRaHeaderType(RADIOLIB_LRXXXX_LORA_HEADER_IMPLICIT, len));
    1010             : }
    1011             : 
    1012           0 : int16_t LR2021::explicitHeader() {
    1013           0 :   return(this->setLoRaHeaderType(RADIOLIB_LRXXXX_LORA_HEADER_EXPLICIT));
    1014             : }
    1015             : 
    1016           0 : int16_t LR2021::setNodeAddress(uint8_t nodeAddr) {
    1017             :   // check active modem
    1018           0 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
    1019           0 :   int16_t state = getPacketType(&type);
    1020           0 :   RADIOLIB_ASSERT(state);
    1021           0 :   if(type != RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
    1022           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
    1023             :   }
    1024             : 
    1025             :   // enable address filtering (node only)
    1026           0 :   this->addrComp = RADIOLIB_LR2021_GFSK_OOK_ADDR_FILT_NODE;
    1027           0 :   state = setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType, 
    1028           0 :     (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
    1029           0 :   RADIOLIB_ASSERT(state);
    1030             : 
    1031             :   // set node address
    1032           0 :   this->node = nodeAddr;
    1033           0 :   return(setGfskAddress(this->node, 0));
    1034             : }
    1035             : 
    1036           0 : int16_t LR2021::setBroadcastAddress(uint8_t broadAddr) {
    1037             :   // check active modem
    1038           0 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
    1039           0 :   int16_t state = getPacketType(&type);
    1040           0 :   RADIOLIB_ASSERT(state);
    1041           0 :   if(type != RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
    1042           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
    1043             :   }
    1044             : 
    1045             :   // enable address filtering (node and broadcast)
    1046           0 :   this->addrComp = RADIOLIB_LR2021_GFSK_OOK_ADDR_FILT_NODE_BROADCAST;
    1047           0 :   state = setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType, 
    1048           0 :     (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
    1049           0 :   RADIOLIB_ASSERT(state);
    1050             : 
    1051             :   // set node and broadcast address
    1052           0 :   return(setGfskAddress(this->node, broadAddr));
    1053             : }
    1054             : 
    1055           0 : int16_t LR2021::disableAddressFiltering() {
    1056             :   // check active modem
    1057           0 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
    1058           0 :   int16_t state = getPacketType(&type);
    1059           0 :   RADIOLIB_ASSERT(state);
    1060           0 :   if(type != RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
    1061           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
    1062             :   }
    1063             : 
    1064             :   // disable address filtering
    1065           0 :   this->addrComp = RADIOLIB_LR2021_GFSK_OOK_ADDR_FILT_DISABLED;
    1066           0 :   return(setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType, 
    1067           0 :     (this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED) ? this->implicitLen : RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
    1068             : }
    1069             : 
    1070           0 : int16_t LR2021::ookDetector(uint16_t pattern, uint8_t len, uint8_t repeats, bool syncRaw, bool rising, uint8_t sofLen) {
    1071             :   // check active modem
    1072           0 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
    1073           0 :   int16_t state = getPacketType(&type);
    1074           0 :   RADIOLIB_ASSERT(state);
    1075           0 :   if(type != RADIOLIB_LR2021_PACKET_TYPE_OOK) {
    1076           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
    1077             :   }
    1078             : 
    1079           0 :   return(setOokDetector(pattern, len - 1, repeats, syncRaw, rising, sofLen));
    1080             : }
    1081             : 
    1082           0 : int16_t LR2021::setOokDetectionThreshold(int16_t level) {
    1083           0 :   int16_t levelRaw = 64 + level;
    1084           0 :   return(this->writeRegMemMask32(RADIOLIB_LR2021_REG_OOK_DETECTION_THRESHOLD, (0x7FUL << 20), (uint32_t)levelRaw << 20));
    1085             : }
    1086             : 
    1087           0 : int16_t LR2021::setSideDetector(const LR2021LoRaSideDetector_t* cfg, size_t numDetectors) {
    1088             :   // some basic sanity checks
    1089           0 :   if((cfg == nullptr) || (numDetectors == 0)) {
    1090           0 :     return(RADIOLIB_ERR_NONE);
    1091             :   }
    1092             : 
    1093           0 :   if(numDetectors > 3) {
    1094           0 :     return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
    1095             :   }
    1096             : 
    1097             :   // if bandwidth is higher than 500 kHz, at most 2 side detectors are allowed
    1098           0 :   if((this->bandwidthHz > RADIOLIB_UNIT_KILO(500)) && (numDetectors > 2)) {
    1099           0 :     return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
    1100             :   }
    1101             : 
    1102             :   // if the primary spreading factor is 10, 11 or 12, at most 2 side detectors are allowed
    1103           0 :   if((this->spreadingFactor >= 10) && (numDetectors > 2)) {
    1104           0 :     return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
    1105             :   }
    1106             : 
    1107             :   // condition of the primary spreading factor being the smallest/largest is not checked
    1108             :   // this is intentional, because it depends on whether the user wants to start Rx or CAD
    1109             : 
    1110           0 :   uint8_t detectors[3] = { 0 };
    1111           0 :   uint8_t syncWords[3] = { 0 };
    1112           0 :   uint8_t minSf = this->spreadingFactor;
    1113           0 :   uint32_t sumDetFactors = 0;
    1114           0 :   for(size_t i = 0; i < numDetectors; i++) {
    1115             :     // all side-detector spreading factors must be higher than the primary one
    1116             :     //! \todo [LR2021] implement multi-SF for CAD (main SF must be smallest!)
    1117           0 :     if(this->spreadingFactor >= cfg[i].sf) {
    1118           0 :       return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
    1119             :     }
    1120             : 
    1121             :     // the difference between maximum and minimum spreading factor used must be less than or equal to 4
    1122           0 :     if(cfg[i].sf - minSf > 4) {
    1123           0 :       return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
    1124             :     }
    1125             : 
    1126           0 :     if(cfg[i].sf < minSf) { minSf = cfg[i].sf; }
    1127             : 
    1128           0 :     detectors[i] = cfg[i].sf << 4 | cfg[i].ldro << 2 | cfg[i].invertIQ;
    1129           0 :     syncWords[i] = cfg[i].syncWord;
    1130           0 :     sumDetFactors += 10 + (((cfg[i].sf - 5) >> 1) << 1);
    1131             :   }
    1132             : 
    1133             :   // sum of detection factors multiplied by BW must be smaller than 32E6
    1134           0 :   if(sumDetFactors*(uint32_t)this->bandwidthHz >= RADIOLIB_UNIT_MEGA(32)) {
    1135           0 :     return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
    1136             :   }
    1137             : 
    1138             :   // all spreading factors must be different
    1139           0 :   if(numDetectors >= 2) {
    1140           0 :     if(cfg[0].sf == cfg[1].sf) { 
    1141           0 :       return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
    1142             :     }
    1143             :   }
    1144             : 
    1145           0 :   if(numDetectors == 3) {
    1146           0 :     if((cfg[1].sf == cfg[2].sf) || (cfg[0].sf == cfg[2].sf)) { 
    1147           0 :       return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
    1148             :     }
    1149             :   }
    1150             : 
    1151             :   // check active modem
    1152           0 :   uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
    1153           0 :   int16_t state = getPacketType(&type);
    1154           0 :   RADIOLIB_ASSERT(state);
    1155           0 :   if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
    1156           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
    1157             :   }
    1158             : 
    1159           0 :   state = setLoRaSideDetConfig(detectors, numDetectors);
    1160           0 :   RADIOLIB_ASSERT(state);
    1161             : 
    1162           0 :   return(setLoRaSideDetSyncword(syncWords, numDetectors));
    1163             : }
    1164             : 
    1165           0 : int16_t LR2021::setGain(uint8_t gain) {
    1166           0 :   if(gain > 13) {
    1167           0 :     return(RADIOLIB_ERR_INVALID_GAIN);
    1168             :   }
    1169           0 :   return(this->SPIcommand(RADIOLIB_LR2021_CMD_SET_AGC_GAIN_MANUAL, true, &gain, sizeof(gain)));
    1170             : }
    1171             : 
    1172           0 : int16_t LR2021::setTempCompensationInternal(uint8_t mode) {
    1173             :   // the chip rejects temperature compensation when TCXO is used
    1174           0 :   if(this->tcxoVoltage != RadioLibTCXOVoltage_t::VoltageNone) {
    1175           0 :     return(RADIOLIB_ERR_INVALID_TCXO_VOLTAGE);
    1176             :   }
    1177           0 :   if(mode > RADIOLIB_LR2021_TEMP_COMP_ABSOLUTE) {
    1178           0 :     return(RADIOLIB_ERR_UNSUPPORTED);
    1179             :   }
    1180           0 :   return(this->SPIcommand(RADIOLIB_LR2021_CMD_SET_TEMP_COMP_CFG, true, &mode, sizeof(mode)));
    1181             : }
    1182             : 
    1183           0 : int16_t LR2021::setTempCompensationNTC(uint8_t mode, uint16_t rRatio, uint16_t betaK, uint8_t delay) {
    1184             :   // the chip rejects temperature compensation when TCXO is used
    1185           0 :   if(this->tcxoVoltage != RadioLibTCXOVoltage_t::VoltageNone) {
    1186           0 :     return(RADIOLIB_ERR_INVALID_TCXO_VOLTAGE);
    1187             :   }
    1188             :   // the NTC only acts on top of relative or absolute compensation
    1189           0 :   if((mode != RADIOLIB_LR2021_TEMP_COMP_RELATIVE) && (mode != RADIOLIB_LR2021_TEMP_COMP_ABSOLUTE)) {
    1190           0 :     return(RADIOLIB_ERR_UNSUPPORTED);
    1191             :   }
    1192             : 
    1193             :   // ratio is 10 bits with 9 fractional bits, rounded from thousandths, beta is 12 bits in 2 K steps
    1194           0 :   uint32_t ratio = ((uint32_t)rRatio * 512UL + 500UL) / 1000UL;
    1195           0 :   if((ratio == 0) || (ratio > 1023) || (betaK < 2) || (betaK > 8190)) {
    1196           0 :     return(RADIOLIB_ERR_INVALID_NTC_PARAMETERS);
    1197             :   }
    1198           0 :   uint16_t beta = (betaK + 1) / 2;
    1199           0 :   uint8_t buff[] = { (uint8_t)((ratio >> 8) & 0x03), (uint8_t)(ratio & 0xFF), (uint8_t)((beta >> 8) & 0x0F), (uint8_t)(beta & 0xFF), delay };
    1200           0 :   int16_t state = this->SPIcommand(RADIOLIB_LR2021_CMD_SET_NTC_PARAMS, true, buff, sizeof(buff));
    1201           0 :   RADIOLIB_ASSERT(state);
    1202             : 
    1203           0 :   uint8_t cfg = mode | RADIOLIB_LR2021_TEMP_COMP_NTC_ENABLED;
    1204           0 :   return(this->SPIcommand(RADIOLIB_LR2021_CMD_SET_TEMP_COMP_CFG, true, &cfg, sizeof(cfg)));
    1205             : }
    1206             : 
    1207             : #endif

Generated by: LCOV version 1.14