LCOV - code coverage report
Current view: top level - src/modules/SX126x - SX126x.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 151 785 19.2 %
Date: 2026-11-08 19:43:08 Functions: 30 61 49.2 %

          Line data    Source code
       1             : #include "SX126x.h"
       2             : #include <string.h>
       3             : #include <math.h>
       4             : #if !RADIOLIB_EXCLUDE_SX126X
       5             : 
       6          18 : SX126x::SX126x(Module* mod) : PhysicalLayer() {
       7          18 :   this->freqStep = RADIOLIB_SX126X_FREQUENCY_STEP_SIZE;
       8          18 :   this->maxPacketLength = RADIOLIB_SX126X_MAX_PACKET_LENGTH;
       9          18 :   this->mod = mod;
      10          18 :   this->standbyXOSC = false;
      11          18 :   this->irqMap[RADIOLIB_IRQ_TX_DONE] = RADIOLIB_SX126X_IRQ_TX_DONE;
      12          18 :   this->irqMap[RADIOLIB_IRQ_RX_DONE] = RADIOLIB_SX126X_IRQ_RX_DONE;
      13          18 :   this->irqMap[RADIOLIB_IRQ_PREAMBLE_DETECTED] = RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED;
      14          18 :   this->irqMap[RADIOLIB_IRQ_SYNC_WORD_VALID] = RADIOLIB_SX126X_IRQ_SYNC_WORD_VALID;
      15          18 :   this->irqMap[RADIOLIB_IRQ_HEADER_VALID] = RADIOLIB_SX126X_IRQ_HEADER_VALID;
      16          18 :   this->irqMap[RADIOLIB_IRQ_HEADER_ERR] = RADIOLIB_SX126X_IRQ_HEADER_ERR;
      17          18 :   this->irqMap[RADIOLIB_IRQ_CRC_ERR] = RADIOLIB_SX126X_IRQ_CRC_ERR;
      18          18 :   this->irqMap[RADIOLIB_IRQ_CAD_DONE] = RADIOLIB_SX126X_IRQ_CAD_DONE;
      19          18 :   this->irqMap[RADIOLIB_IRQ_CAD_DETECTED] = RADIOLIB_SX126X_IRQ_CAD_DETECTED;
      20          18 :   this->irqMap[RADIOLIB_IRQ_TIMEOUT] = RADIOLIB_SX126X_IRQ_TIMEOUT;
      21          18 : }
      22             : 
      23           0 : int16_t SX126x::begin(uint8_t cr, uint8_t syncWord, uint16_t preambleLength) {
      24             :   // BW in kHz and SF are required in order to calculate LDRO for setModulationParams
      25             :   // set the defaults, this will get overwritten later anyway
      26           0 :   this->bandwidthHz = RADIOLIB_UNIT_KILO(500);
      27           0 :   this->spreadingFactor = 9;
      28             : 
      29             :   // initialize configuration variables (will be overwritten during public settings configuration)
      30           0 :   this->bandwidth = RADIOLIB_SX126X_LORA_BW_500_0;  // initialized to 500 kHz, since lower values will interfere with LLCC68
      31           0 :   this->codingRate = RADIOLIB_SX126X_LORA_CR_4_7;
      32           0 :   this->ldrOptimize = 0x00;
      33           0 :   this->crcTypeLoRa = RADIOLIB_SX126X_LORA_CRC_ON;
      34           0 :   this->preambleLengthLoRa = preambleLength;
      35           0 :   this->tcxoDelay = 0;
      36           0 :   this->headerType = RADIOLIB_SX126X_LORA_HEADER_EXPLICIT;
      37           0 :   this->implicitLen = 0xFF;
      38             : 
      39             :   // set module properties and perform initial setup
      40           0 :   int16_t state = this->modSetup(RADIOLIB_SX126X_PACKET_TYPE_LORA);
      41           0 :   RADIOLIB_ASSERT(state);
      42             : 
      43             :   // configure publicly accessible settings
      44           0 :   state = setCodingRate(cr);
      45           0 :   RADIOLIB_ASSERT(state);
      46             : 
      47           0 :   state = setSyncWord(syncWord);
      48           0 :   RADIOLIB_ASSERT(state);
      49             : 
      50           0 :   state = setPreambleLength(preambleLength);
      51           0 :   RADIOLIB_ASSERT(state);
      52             : 
      53             :   // set publicly accessible settings that are not a part of begin method
      54           0 :   state = setCurrentLimit(60.0);
      55           0 :   RADIOLIB_ASSERT(state);
      56             : 
      57           0 :   state = setDio2AsRfSwitch(true);
      58           0 :   RADIOLIB_ASSERT(state);
      59             : 
      60           0 :   state = setCRC(2);
      61           0 :   RADIOLIB_ASSERT(state);
      62             : 
      63           0 :   state = invertIQ(false);
      64           0 :   RADIOLIB_ASSERT(state);
      65             : 
      66           0 :   return(state);
      67             : }
      68             : 
      69           0 : int16_t SX126x::beginFSK(uint32_t br, uint32_t freqDev, uint32_t rxBw, uint16_t preambleLength) {
      70             :   // initialize configuration variables (will be overwritten during public settings configuration)
      71           0 :   this->bitRateRaw = RADIOLIB_SX126X_BIT_RATE_RAW(48000);
      72           0 :   this->frequencyDev = 52428;                             // 50.0 kHz
      73           0 :   this->rxBandwidth = RADIOLIB_SX126X_GFSK_RX_BW_156_2;
      74           0 :   this->rxBandwidthHz = 156200;
      75           0 :   this->pulseShape = RADIOLIB_SX126X_GFSK_FILTER_GAUSS_0_5;
      76           0 :   this->crcTypeFSK = RADIOLIB_SX126X_GFSK_CRC_2_BYTE_INV;     // CCITT CRC configuration
      77           0 :   this->preambleLengthFSK = preambleLength;
      78             : 
      79             :   // set module properties and perform initial setup
      80           0 :   int16_t state = this->modSetup(RADIOLIB_SX126X_PACKET_TYPE_GFSK);
      81           0 :   RADIOLIB_ASSERT(state);
      82             : 
      83             :   // configure publicly accessible settings
      84           0 :   state = setBitRate(br);
      85           0 :   RADIOLIB_ASSERT(state);
      86             : 
      87           0 :   state = setFrequencyDeviation(freqDev);
      88           0 :   RADIOLIB_ASSERT(state);
      89             : 
      90           0 :   state = setRxBandwidth(rxBw);
      91           0 :   RADIOLIB_ASSERT(state);
      92             : 
      93           0 :   state = setCurrentLimit(60.0);
      94           0 :   RADIOLIB_ASSERT(state);
      95             : 
      96           0 :   state = setPreambleLength(preambleLength);
      97           0 :   RADIOLIB_ASSERT(state);
      98             : 
      99             :   // set publicly accessible settings that are not a part of begin method
     100           0 :   uint8_t sync[] = {0x12, 0xAD};
     101           0 :   state = setSyncWord(sync, 2);
     102           0 :   RADIOLIB_ASSERT(state);
     103             : 
     104           0 :   state = setDataShaping(RADIOLIB_SHAPING_NONE);
     105           0 :   RADIOLIB_ASSERT(state);
     106             : 
     107           0 :   state = setEncoding(RADIOLIB_ENCODING_NRZ);
     108           0 :   RADIOLIB_ASSERT(state);
     109             : 
     110           0 :   state = variablePacketLengthMode(RADIOLIB_SX126X_MAX_PACKET_LENGTH);
     111           0 :   RADIOLIB_ASSERT(state);
     112             : 
     113           0 :   state = setCRC(2);
     114           0 :   RADIOLIB_ASSERT(state);
     115             : 
     116           0 :   state = setDio2AsRfSwitch(true);
     117           0 :   RADIOLIB_ASSERT(state);
     118             : 
     119           0 :   return(state);
     120             : }
     121             : 
     122           0 : int16_t SX126x::beginBPSK(uint32_t br) {
     123             :   // set module properties and perform initial setup
     124           0 :   int16_t state = this->modSetup(RADIOLIB_SX126X_PACKET_TYPE_BPSK);
     125           0 :   RADIOLIB_ASSERT(state);
     126             : 
     127             :   // configure publicly accessible settings
     128           0 :   state = setBitRate(br);
     129           0 :   RADIOLIB_ASSERT(state);
     130             :   
     131             :   // set publicly accessible settings that are not a part of begin method
     132           0 :   state = setDio2AsRfSwitch(true);
     133           0 :   RADIOLIB_ASSERT(state);
     134             : 
     135           0 :   return(state);
     136             : }
     137             : 
     138           0 : int16_t SX126x::beginLRFHSS(uint8_t bw, uint8_t cr, bool narrowGrid) {
     139             :   // set module properties and perform initial setup
     140           0 :   int16_t state = this->modSetup(RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS);
     141           0 :   RADIOLIB_ASSERT(state);
     142             : 
     143             :   // set publicly accessible settings that are not a part of begin method
     144           0 :   state = setCurrentLimit(60.0);
     145           0 :   RADIOLIB_ASSERT(state);
     146             : 
     147           0 :   state = setDio2AsRfSwitch(true);
     148           0 :   RADIOLIB_ASSERT(state);
     149             : 
     150             :   // set all packet params to 0 (packet engine is disabled in LR-FHSS mode)
     151           0 :   state = setPacketParamsFSK(0, 0, 0, 0, 0, 0, 0, 0);
     152           0 :   RADIOLIB_ASSERT(state);
     153             : 
     154             :   // set bit rate
     155           0 :   this->rxBandwidth = 0;
     156           0 :   this->frequencyDev = 0;
     157           0 :   this->pulseShape = RADIOLIB_SX126X_GFSK_FILTER_GAUSS_1;
     158           0 :   state = setBitRate(488);
     159           0 :   RADIOLIB_ASSERT(state);
     160             : 
     161           0 :   return(setLrFhssConfig(bw, cr, narrowGrid));
     162             : }
     163             : 
     164           0 : int16_t SX126x::setLrFhssConfig(uint8_t bw, uint8_t cr, bool narrowGrid, uint8_t hdrCount, uint16_t hopSeqId) {
     165             :   // check and cache all parameters
     166           0 :   this->lrFhssGridNonFcc = narrowGrid;
     167           0 :   RADIOLIB_CHECK_RANGE((int8_t)cr, (int8_t)RADIOLIB_SX126X_LR_FHSS_CR_5_6, (int8_t)RADIOLIB_SX126X_LR_FHSS_CR_1_3, RADIOLIB_ERR_INVALID_CODING_RATE);
     168           0 :   this->lrFhssCr = cr;
     169           0 :   RADIOLIB_CHECK_RANGE((int8_t)bw, (int8_t)RADIOLIB_SX126X_LR_FHSS_BW_39_06, (int8_t)RADIOLIB_SX126X_LR_FHSS_BW_1574_2, RADIOLIB_ERR_INVALID_BANDWIDTH);
     170           0 :   this->lrFhssBw = bw;
     171           0 :   RADIOLIB_CHECK_RANGE(hdrCount, 1, 4, RADIOLIB_ERR_INVALID_BIT_RANGE);
     172           0 :   this->lrFhssHdrCount = hdrCount;
     173           0 :   RADIOLIB_CHECK_RANGE((int16_t)hopSeqId, (int16_t)0x000, (int16_t)0x1FF, RADIOLIB_ERR_INVALID_DATA_SHAPING);
     174           0 :   this->lrFhssHopSeqId = hopSeqId;
     175           0 :   return(RADIOLIB_ERR_NONE);
     176             : }
     177             : 
     178           0 : int16_t SX126x::reset(bool verify) {
     179             :   // run the reset sequence
     180           0 :   this->mod->hal->pinMode(this->mod->getRst(), this->mod->hal->GpioModeOutput);
     181           0 :   this->mod->hal->digitalWrite(this->mod->getRst(), this->mod->hal->GpioLevelLow);
     182           0 :   this->mod->hal->delay(1);
     183           0 :   this->mod->hal->digitalWrite(this->mod->getRst(), this->mod->hal->GpioLevelHigh);
     184             : 
     185             :   // return immediately when verification is disabled
     186           0 :   if(!verify) {
     187           0 :     return(RADIOLIB_ERR_NONE);
     188             :   }
     189             : 
     190             :   // set mode to standby - SX126x often refuses first few commands after reset
     191           0 :   RadioLibTime_t start = this->mod->hal->millis();
     192             :   while(true) {
     193             :     // try to set mode to standby
     194             :     // force usage of RC standby here - if this is being called during startup,
     195             :     // and user set standbyXOSC to true with TCXO, this would attempt to use the TCXO
     196           0 :     int16_t state = standby(RADIOLIB_SX126X_STANDBY_RC);
     197           0 :     if(state == RADIOLIB_ERR_NONE) {
     198             :       // standby command successful
     199           0 :       return(RADIOLIB_ERR_NONE);
     200             :     }
     201             : 
     202             :     // standby command failed, check timeout and try again
     203           0 :     if(this->mod->hal->millis() - start >= 1000) {
     204             :       // timed out, possibly incorrect wiring
     205           0 :       return(state);
     206             :     }
     207             : 
     208             :     // wait a bit to not spam the module
     209           0 :     this->mod->hal->delay(10);
     210           0 :   }
     211             : }
     212             : 
     213           3 : int16_t SX126x::transmit(const uint8_t* data, size_t len, uint8_t addr) {
     214             :   // set mode to standby
     215           3 :   int16_t state = standby();
     216           3 :   RADIOLIB_ASSERT(state);
     217             : 
     218             :   // check packet length
     219           0 :   if(this->codingRate > RADIOLIB_SX126X_LORA_CR_4_8) {
     220             :     // Long Interleaver needs at least 8 bytes
     221           0 :     if(len < 8) {
     222           0 :       return(RADIOLIB_ERR_PACKET_TOO_SHORT);
     223             :     }
     224             : 
     225             :     // Long Interleaver supports up to 253 bytes if CRC is enabled
     226           0 :     if(this->crcTypeLoRa == RADIOLIB_SX126X_LORA_CRC_ON && (len > RADIOLIB_SX126X_MAX_PACKET_LENGTH - 2)) {
     227           0 :       return(RADIOLIB_ERR_PACKET_TOO_LONG);
     228             :     }  
     229             :   } 
     230           0 :   if(len > RADIOLIB_SX126X_MAX_PACKET_LENGTH) {
     231           0 :     return(RADIOLIB_ERR_PACKET_TOO_LONG);
     232             :   }
     233             : 
     234             :   // calculate timeout in ms (5ms + 500 % of expected time-on-air)
     235           0 :   RadioLibTime_t timeout = 5 + (getTimeOnAir(len) * 5) / 1000;
     236             :   RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu ms", timeout);
     237             : 
     238             :   // start transmission
     239           0 :   state = startTransmit(data, len, addr);
     240           0 :   RADIOLIB_ASSERT(state);
     241             : 
     242             :   // wait for packet transmission or timeout
     243           0 :   uint8_t modem = getPacketType();
     244           0 :   RadioLibTime_t start = this->mod->hal->millis();
     245             :   while(true) {
     246             :     // yield for  multi-threaded platforms
     247           0 :     this->mod->hal->yield();
     248             : 
     249             :     // check timeout
     250           0 :     if(this->mod->hal->millis() - start > timeout) {
     251           0 :       finishTransmit();
     252           0 :       return(RADIOLIB_ERR_TX_TIMEOUT);
     253             :     }
     254             : 
     255             :     // poll the interrupt pin
     256           0 :     if(this->mod->hal->digitalRead(this->mod->getIrq())) {
     257             :       // in LoRa or GFSK, only Tx done interrupt is enabled
     258           0 :       if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) {
     259           0 :         break;
     260             :       }
     261             : 
     262             :       // in LR-FHSS, IRQ signals both Tx done as frequency hop request
     263           0 :       if(this->getIrqFlags() & RADIOLIB_SX126X_IRQ_TX_DONE) {
     264           0 :         break;
     265             :       } else {
     266             :         // handle frequency hop
     267           0 :         this->hopLRFHSS();
     268             :       }
     269             :     }
     270             :   }
     271             : 
     272           0 :   return(finishTransmit());
     273             : }
     274             : 
     275           3 : int16_t SX126x::receive(uint8_t* data, size_t len, RadioLibTime_t timeout) {
     276             :   // set mode to standby
     277           3 :   int16_t state = standby();
     278           3 :   RADIOLIB_ASSERT(state);
     279             : 
     280           0 :   RadioLibTime_t timeoutInternal = timeout;
     281           0 :   if(!timeoutInternal) {
     282             :     // calculate timeout (500 % of expected time-one-air)
     283           0 :     size_t maxLen = len;
     284           0 :     if(len == 0) { maxLen = RADIOLIB_SX126X_MAX_PACKET_LENGTH; }
     285           0 :     timeoutInternal = (getTimeOnAir(maxLen) * 5) / 1000;
     286             :   }
     287             :   RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu ms", timeoutInternal);
     288             : 
     289             :   // start reception
     290           0 :   uint32_t timeoutValue = (uint32_t)(((float)timeoutInternal * 1000.0f) / 15.625f);
     291           0 :   state = startReceive(timeoutValue);
     292           0 :   RADIOLIB_ASSERT(state);
     293             : 
     294             :   // wait for packet reception or timeout
     295           0 :   bool softTimeout = false;
     296           0 :   RadioLibTime_t start = this->mod->hal->millis();
     297           0 :   while(!this->mod->hal->digitalRead(this->mod->getIrq())) {
     298           0 :     this->mod->hal->yield();
     299             :     // safety check, the timeout should be done by the radio
     300           0 :     if(this->mod->hal->millis() - start > timeoutInternal) {
     301           0 :       softTimeout = true;
     302           0 :       break;
     303             :     }
     304             :   }
     305             : 
     306             :   // if it was a timeout, this will return an error code
     307           0 :   state = standby();
     308           0 :   if((state != RADIOLIB_ERR_NONE) && (state != RADIOLIB_ERR_SPI_CMD_TIMEOUT)) {
     309           0 :     return(state);
     310             :   }
     311             : 
     312             :   // check whether this was a timeout or not
     313           0 :   if(softTimeout || (getIrqFlags() & this->irqMap[RADIOLIB_IRQ_TIMEOUT])) {
     314           0 :     (void)finishReceive();
     315           0 :     return(RADIOLIB_ERR_RX_TIMEOUT);
     316             :   }
     317             : 
     318             :   // read the received data
     319           0 :   return(readData(data, len));
     320             : }
     321             : 
     322           3 : int16_t SX126x::transmitDirect(uint32_t frf) {
     323             :   // set RF switch (if present)
     324           3 :   this->mod->setRfSwitchState(this->txMode);
     325             : 
     326             :   // user requested to start transmitting immediately (required for RTTY)
     327           3 :   int16_t state = RADIOLIB_ERR_NONE;
     328           3 :   if(frf != 0) {
     329           0 :     state = setRfFrequency(frf);
     330             :   }
     331           3 :   RADIOLIB_ASSERT(state);
     332             : 
     333             :   // direct mode activation intentionally skipped here, as it seems to lead to much worse results
     334           3 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_TX_CONTINUOUS_WAVE, NULL, 0));
     335             : }
     336             : 
     337           3 : int16_t SX126x::receiveDirect() {
     338             :   // set RF switch (if present)
     339           3 :   this->mod->setRfSwitchState(Module::MODE_RX);
     340             : 
     341             :   // SX126x is unable to output received data directly
     342           3 :   return(RADIOLIB_ERR_UNKNOWN);
     343             : }
     344             : 
     345           0 : int16_t SX126x::directMode() {
     346             :   // check modem
     347           0 :   if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
     348           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
     349             :   }
     350             : 
     351             :   // set mode to standby
     352           0 :   int16_t state = standby();
     353           0 :   RADIOLIB_ASSERT(state);
     354             : 
     355             :   // disable DIO2 RF switch
     356           0 :   state = setDio2AsRfSwitch(false);
     357           0 :   RADIOLIB_ASSERT(state);
     358             : 
     359             :   // set DIO2 to clock output and DIO3 to data input
     360             :   // this is done exclusively by writing magic values to even more magic registers
     361           0 :   state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_1, RADIOLIB_SX126X_TX_BITBANG_1_ENABLED, 6, 4);
     362           0 :   RADIOLIB_ASSERT(state);
     363           0 :   state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_0, RADIOLIB_SX126X_TX_BITBANG_0_ENABLED, 3, 0);
     364           0 :   RADIOLIB_ASSERT(state);
     365           0 :   state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_DIOX_OUT_ENABLE, RADIOLIB_SX126X_DIO3_OUT_DISABLED, 3, 3);
     366           0 :   RADIOLIB_ASSERT(state);
     367           0 :   state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_DIOX_IN_ENABLE, RADIOLIB_SX126X_DIO3_IN_ENABLED, 3, 3);
     368           0 :   RADIOLIB_ASSERT(state);
     369             : 
     370             :   // enable TxDone interrupt
     371           0 :   state = setDioIrqParams(RADIOLIB_SX126X_IRQ_TX_DONE, RADIOLIB_SX126X_IRQ_TX_DONE);
     372           0 :   RADIOLIB_ASSERT(state);
     373             : 
     374             :   // set preamble length to the maximum to prevent SX126x from exiting Tx mode for a while
     375           0 :   state = setPreambleLength(0xFFFF);
     376           0 :   RADIOLIB_ASSERT(state);
     377             : 
     378           0 :   return(state);
     379             : }
     380             : 
     381           0 : int16_t SX126x::packetMode() {
     382             :   // set mode to standby
     383           0 :   int16_t state = standby();
     384           0 :   RADIOLIB_ASSERT(state);
     385             : 
     386             :   // set preamble length to the default
     387           0 :   state = setPreambleLength(16);
     388           0 :   RADIOLIB_ASSERT(state);
     389             : 
     390             :   // disable TxDone interrupt
     391           0 :   state = setDioIrqParams(RADIOLIB_SX126X_IRQ_NONE, RADIOLIB_SX126X_IRQ_NONE);
     392           0 :   RADIOLIB_ASSERT(state);
     393             : 
     394             :   // restore the magic registers
     395           0 :   state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_DIOX_IN_ENABLE, RADIOLIB_SX126X_DIO3_IN_DISABLED, 3, 3);
     396           0 :   RADIOLIB_ASSERT(state);
     397           0 :   state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_DIOX_OUT_ENABLE, RADIOLIB_SX126X_DIO3_OUT_ENABLED, 3, 3);
     398           0 :   RADIOLIB_ASSERT(state);
     399           0 :   state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_0, RADIOLIB_SX126X_TX_BITBANG_0_DISABLED, 3, 0);
     400           0 :   RADIOLIB_ASSERT(state);
     401           0 :   state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_1, RADIOLIB_SX126X_TX_BITBANG_1_DISABLED, 6, 4);
     402           0 :   RADIOLIB_ASSERT(state);
     403             : 
     404             :   // enable DIO2 RF switch
     405           0 :   state = setDio2AsRfSwitch(true);
     406           0 :   RADIOLIB_ASSERT(state);
     407             : 
     408           0 :   return(state);
     409             : }
     410             : 
     411           3 : int16_t SX126x::scanChannel() {
     412           3 :   ChannelScanConfig_t cfg = {
     413             :     .cad = {
     414             :       .symNum = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
     415             :       .detPeak = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
     416             :       .detMin = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
     417             :       .exitMode = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
     418             :       .rxTimeout = 0,
     419             :       .rxSymbols = 0,
     420             :       .irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS,
     421             :       .irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK,
     422             :     },
     423             :   };
     424           6 :   return(this->scanChannel(cfg));
     425             : }
     426             : 
     427           6 : int16_t SX126x::scanChannel(const ChannelScanConfig_t &cfg) {
     428             :   // set mode to CAD
     429           6 :   int state = startChannelScan(cfg);
     430           6 :   RADIOLIB_ASSERT(state);
     431             : 
     432             :   // wait for channel activity detected or timeout
     433           0 :   while(!this->mod->hal->digitalRead(this->mod->getIrq())) {
     434           0 :     this->mod->hal->yield();
     435             :   }
     436             : 
     437             :   // check CAD result
     438           0 :   return(getChannelScanResult());
     439             : }
     440             : 
     441           0 : int16_t SX126x::hopLRFHSS() {
     442           0 :   if(!(this->getIrqFlags() & RADIOLIB_SX126X_IRQ_LR_FHSS_HOP)) {
     443           0 :     return(RADIOLIB_ERR_TX_TIMEOUT);
     444             :   }
     445             : 
     446           0 :   int16_t state = this->setLRFHSSHop(this->lrFhssHopNum % 16);
     447           0 :   RADIOLIB_ASSERT(state);
     448           0 :   return(clearIrqStatus());
     449             : }
     450             : 
     451           3 : int16_t SX126x::finishReceive() {
     452             :   // set mode to standby to disable RF switch
     453           3 :   int16_t state = standby();
     454           3 :   RADIOLIB_ASSERT(state);
     455             : 
     456             :   // try to fix timeout error in implicit header mode
     457             :   // check for modem type and header mode is done in fixImplicitTimeout()
     458           0 :   state = fixImplicitTimeout();
     459           0 :   RADIOLIB_ASSERT(state);
     460             : 
     461             :   // clear interrupt flags
     462           0 :   return(clearIrqStatus());
     463             : }
     464             : 
     465           3 : int16_t SX126x::startReceive() {
     466           3 :   return(this->startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0));
     467             : }
     468             : 
     469           0 : int16_t SX126x::startReceiveDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask) {
     470             :   // datasheet claims time to go to sleep is ~500us, same to wake up, compensate for that with 1 ms + TCXO delay
     471           0 :   uint32_t transitionTime = this->tcxoDelay + 1000;
     472           0 :   sleepPeriod -= transitionTime;
     473             : 
     474             :   // divide by 15.625
     475           0 :   uint32_t rxPeriodRaw = (rxPeriod * 8) / 125;
     476           0 :   uint32_t sleepPeriodRaw = (sleepPeriod * 8) / 125;
     477             : 
     478             :   // check 24 bit limit and zero value (likely not intended)
     479           0 :   if((rxPeriodRaw & 0xFF000000) || (rxPeriodRaw == 0)) {
     480           0 :     return(RADIOLIB_ERR_INVALID_RX_PERIOD);
     481             :   }
     482             : 
     483             :   // this check of the high byte also catches underflow when we subtracted transitionTime
     484           0 :   if((sleepPeriodRaw & 0xFF000000) || (sleepPeriodRaw == 0)) {
     485           0 :     return(RADIOLIB_ERR_INVALID_SLEEP_PERIOD);
     486             :   }
     487             : 
     488             :   // set up Rx mode
     489           0 :   RadioModeConfig_t cfg = {
     490             :     .receive = {
     491             :       .timeout = RADIOLIB_SX126X_RX_TIMEOUT_INF,
     492             :       .syncSymbols = 0,
     493             :       .irqFlags = irqFlags,
     494             :       .irqMask = irqMask,
     495             :       .len = 0,
     496             :     }
     497           0 :   };
     498           0 :   int16_t state = this->stageMode(RADIOLIB_RADIO_MODE_RX, &cfg);
     499           0 :   RADIOLIB_ASSERT(state);
     500             : 
     501           0 :   const uint8_t data[6] = {(uint8_t)((rxPeriodRaw >> 16) & 0xFF), (uint8_t)((rxPeriodRaw >> 8) & 0xFF), (uint8_t)(rxPeriodRaw & 0xFF),
     502           0 :                      (uint8_t)((sleepPeriodRaw >> 16) & 0xFF), (uint8_t)((sleepPeriodRaw >> 8) & 0xFF), (uint8_t)(sleepPeriodRaw & 0xFF)};
     503           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RX_DUTY_CYCLE, data, 6));
     504             : }
     505             : 
     506           0 : int16_t SX126x::startReceiveDutyCycleAuto(uint16_t senderPreambleLength, uint16_t minSymbols, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask) {
     507             :   // calculate the sleep and wake periods
     508           0 :   uint32_t wakePeriod = 0;
     509           0 :   uint32_t sleepPeriod = 0;
     510             :   DataRate_t dr = {
     511             :     .lora = {
     512           0 :       .spreadingFactor = this->spreadingFactor,
     513           0 :       .bandwidth = this->bandwidthHz,
     514           0 :       .codingRate = this->codingRate,
     515             :     }
     516           0 :   };
     517           0 :   int16_t state = calculateRxDutyCycle(senderPreambleLength, this->preambleLengthLoRa, minSymbols, &dr, &wakePeriod, &sleepPeriod);
     518           0 :   RADIOLIB_ASSERT(state);
     519             : 
     520             :   // If our sleep period is shorter than our transition time, just use the standard startReceive
     521           0 :   if(sleepPeriod < this->tcxoDelay + 1016) {
     522           0 :     return(startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF, irqFlags, irqMask));
     523             :   }
     524             : 
     525           0 :   return(startReceiveDutyCycle(wakePeriod, sleepPeriod, irqFlags, irqMask));
     526             : }
     527             : 
     528           3 : int16_t SX126x::readData(uint8_t* data, size_t len) {
     529             :   // this method may get called from receive() after Rx timeout
     530             :   // if that's the case, the first call will return "SPI command timeout error"
     531             :   // check the IRQ to be sure this really originated from timeout event
     532           3 :   int16_t state = this->mod->SPIcheckStream();
     533           3 :   uint16_t irq = getIrqFlags();
     534           3 :   if((state == RADIOLIB_ERR_SPI_CMD_TIMEOUT) && (irq & RADIOLIB_SX126X_IRQ_TIMEOUT)) {
     535             :     // this is definitely Rx timeout
     536           0 :     return(RADIOLIB_ERR_RX_TIMEOUT);
     537             :   }
     538           3 :   RADIOLIB_ASSERT(state);
     539             : 
     540             :   // check integrity CRC
     541           0 :   int16_t crcState = RADIOLIB_ERR_NONE;
     542             :   // Report CRC mismatch when there's a payload CRC error, or a header error and no valid header (to avoid false alarm from previous packet)
     543           0 :   if((irq & RADIOLIB_SX126X_IRQ_CRC_ERR) || ((irq & RADIOLIB_SX126X_IRQ_HEADER_ERR) && !(irq & RADIOLIB_SX126X_IRQ_HEADER_VALID))) {
     544           0 :     crcState = RADIOLIB_ERR_CRC_MISMATCH;
     545             :   }
     546             :   
     547             :   // get packet length and Rx buffer offset
     548           0 :   uint8_t offset = 0;
     549           0 :   size_t length = getPacketLength(true, &offset);
     550           0 :   if((len != 0) && (len < length)) {
     551             :     // user requested less data than we got, only return what was requested
     552           0 :     length = len;
     553             :   }
     554             : 
     555             :   // read packet data starting at offset
     556           0 :   state = readBuffer(data, length, offset);
     557           0 :   RADIOLIB_ASSERT(state);
     558             : 
     559             :   // clear interrupt flags
     560           0 :   state = clearIrqStatus();
     561             : 
     562             :   // check if CRC failed - this is done after reading data to give user the option to keep them
     563           0 :   RADIOLIB_ASSERT(crcState);
     564             : 
     565           0 :   return(state);
     566             : }
     567             : 
     568           3 : int16_t SX126x::startChannelScan() {
     569           3 :   ChannelScanConfig_t cfg = {
     570             :     .cad = {
     571             :       .symNum = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
     572             :       .detPeak = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
     573             :       .detMin = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
     574             :       .exitMode = RADIOLIB_SX126X_CAD_PARAM_DEFAULT,
     575             :       .rxTimeout = 0,
     576             :       .rxSymbols = 0,
     577             :       .irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS,
     578             :       .irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK,
     579             :     },
     580             :   };
     581           6 :   return(this->startChannelScan(cfg));
     582             : }
     583             : 
     584          12 : int16_t SX126x::startChannelScan(const ChannelScanConfig_t &cfg) {
     585             :   // check active modem
     586          12 :   if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
     587          12 :     return(RADIOLIB_ERR_WRONG_MODEM);
     588             :   }
     589             : 
     590             :   // set mode to standby
     591           0 :   int16_t state = standby();
     592           0 :   RADIOLIB_ASSERT(state);
     593             : 
     594             :   // set RF switch (if present)
     595           0 :   this->mod->setRfSwitchState(Module::MODE_RX);
     596             : 
     597             :   // set DIO pin mapping
     598           0 :   state = setDioIrqParams(getIrqMapped(cfg.cad.irqFlags), getIrqMapped(cfg.cad.irqMask));
     599           0 :   RADIOLIB_ASSERT(state);
     600             : 
     601             :   // clear interrupt flags
     602           0 :   state = clearIrqStatus();
     603           0 :   RADIOLIB_ASSERT(state);
     604             : 
     605             :   // set LoRa symbol timeout
     606           0 :   state = setLoRaSymbNumTimeout(cfg.cad.rxSymbols);
     607           0 :   RADIOLIB_ASSERT(state);
     608             :   
     609             :   // set mode to CAD
     610           0 :   state = setCad(cfg.cad.symNum, cfg.cad.detPeak, cfg.cad.detMin, cfg.cad.exitMode, cfg.cad.rxTimeout);
     611           0 :   return(state);
     612             : }
     613             : 
     614           3 : int16_t SX126x::getChannelScanResult() {
     615             :   // check active modem
     616           3 :   if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
     617           3 :     return(RADIOLIB_ERR_WRONG_MODEM);
     618             :   }
     619             : 
     620             :   // check CAD result
     621           0 :   uint16_t cadResult = getIrqFlags();
     622           0 :   if(cadResult & RADIOLIB_SX126X_IRQ_CAD_DETECTED) {
     623             :     // detected some LoRa activity
     624           0 :     return(RADIOLIB_LORA_DETECTED);
     625           0 :   } else if(cadResult & RADIOLIB_SX126X_IRQ_CAD_DONE) {
     626             :     // channel is free
     627           0 :     return(RADIOLIB_CHANNEL_FREE);
     628             :   }
     629             : 
     630           0 :   return(RADIOLIB_ERR_UNKNOWN);
     631             : }
     632             : 
     633           3 : float SX126x::getRSSI() {
     634           3 :   return(this->getRSSI(true));
     635             : }
     636             : 
     637           3 : float SX126x::getRSSI(bool packet) {
     638           3 :   if(packet) { 
     639             :     // get last packet RSSI from packet status
     640           3 :     uint32_t packetStatus = getPacketStatus();
     641           3 :     uint8_t rssiPkt = (getPacketType() == RADIOLIB_SX126X_PACKET_TYPE_LORA) ?
     642           3 : +                      (uint8_t)((packetStatus >> 16) & 0xFF) : (uint8_t)(packetStatus & 0xFF);
     643           3 :     return(-0.5f * (float)rssiPkt);
     644             :   } else {
     645             :     // get instantaneous RSSI value
     646           0 :     uint8_t rssiRaw = 0;
     647           0 :     this->mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_RSSI_INST, &rssiRaw, 1);
     648           0 :     return(-0.5f * (float)rssiRaw);
     649             :   }
     650             : }
     651             : 
     652           3 : float SX126x::getSNR() {
     653             :   // check active modem
     654           3 :   if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
     655           3 :     return(RADIOLIB_ERR_WRONG_MODEM);
     656             :   }
     657             : 
     658             :   // get last packet SNR from packet status
     659           0 :   uint32_t packetStatus = getPacketStatus();
     660           0 :   uint8_t snrPkt = (packetStatus >> 8) & 0xFF;
     661           0 :   if(snrPkt < 128) {
     662           0 :     return((float)snrPkt * 0.25f);
     663             :   } else {
     664           0 :     return((float)(snrPkt - 256) * 0.25f);
     665             :   }
     666             : }
     667             : 
     668           0 : int32_t SX126x::getFrequencyError() {
     669             :   // check active modem
     670           0 :   uint8_t modem = getPacketType();
     671           0 :   if(modem != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
     672           0 :     return(0);
     673             :   }
     674             : 
     675             :   // read the raw frequency error register values
     676           0 :   uint8_t efeRaw[3] = {0};
     677           0 :   int16_t state = readRegister(RADIOLIB_SX126X_REG_FREQ_ERROR_RX_CRC, &efeRaw[0], 1);
     678           0 :   RADIOLIB_ASSERT(state);
     679           0 :   state = readRegister(RADIOLIB_SX126X_REG_FREQ_ERROR_RX_CRC + 1, &efeRaw[1], 1);
     680           0 :   RADIOLIB_ASSERT(state);
     681           0 :   state = readRegister(RADIOLIB_SX126X_REG_FREQ_ERROR_RX_CRC + 2, &efeRaw[2], 1);
     682           0 :   RADIOLIB_ASSERT(state);
     683           0 :   uint32_t raw = ((uint32_t) efeRaw[0] << 16) | ((uint32_t) efeRaw[1] << 8) | efeRaw[2];
     684           0 :   raw &= 0x0FFFFF;
     685             : 
     686             :   // check the first bit
     687             :   int32_t error;
     688           0 :   if (raw & 0x80000) {
     689             :     // frequency error is negative
     690           0 :     raw |= (uint32_t) 0xFFF00000;
     691           0 :     raw = ~raw + 1;
     692           0 :     error = raw * this->bandwidthHz * (155.0f / -160000000.0f);
     693             :   } else {
     694           0 :     error = raw * this->bandwidthHz * (155.0f / 160000000.0f);
     695             :   }
     696             : 
     697           0 :   return(error);
     698             : }
     699             : 
     700           3 : size_t SX126x::getPacketLength(bool update) {
     701           3 :   return(this->getPacketLength(update, NULL));
     702             : }
     703             : 
     704           3 : size_t SX126x::getPacketLength(bool update, uint8_t* offset) {
     705             :   (void)update;
     706             : 
     707             :   // in implicit mode, return the cached value if the offset was not requested
     708           3 :   if((getPacketType() == RADIOLIB_SX126X_PACKET_TYPE_LORA) && (this->headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT) && (!offset)) {
     709           0 :     return(this->implicitLen);
     710             :   }
     711             : 
     712             :   // if offset was requested, or in explicit mode, we always have to perform the SPI transaction
     713           3 :   uint8_t rxBufStatus[2] = {0, 0};
     714           3 :   this->mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_RX_BUFFER_STATUS, rxBufStatus, 2);
     715             : 
     716           3 :   if(offset) { *offset = rxBufStatus[1]; }
     717             : 
     718           3 :   return((size_t)rxBufStatus[0]);
     719             : }
     720             : 
     721           0 : int16_t SX126x::getLoRaRxHeaderInfo(uint8_t* cr, bool* hasCRC) {
     722           0 :   int16_t state = RADIOLIB_ERR_NONE;
     723             : 
     724             :   // check if in explicit header mode
     725           0 :   if(this->headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT) {
     726           0 :     return(RADIOLIB_ERR_WRONG_MODEM);
     727             :   }
     728             : 
     729           0 :   if(cr) { *cr = this->mod->SPIgetRegValue(RADIOLIB_SX126X_REG_LORA_RX_CODING_RATE, 6, 4) >> 4; }
     730           0 :   if(hasCRC) { *hasCRC = (this->mod->SPIgetRegValue(RADIOLIB_SX126X_REG_FREQ_ERROR_RX_CRC, 4, 4) != 0); }
     731             : 
     732           0 :   return(state);
     733             : }
     734             : 
     735           3 : RadioLibTime_t SX126x::getTimeOnAir(size_t len) {
     736           3 :   uint8_t type = getPacketType();
     737           3 :   ModemType_t modem = RADIOLIB_MODEM_LORA;
     738           3 :   DataRate_t dataRate = {};
     739           3 :   PacketConfig_t packetConfig = {};
     740             : 
     741           3 :   size_t packetLen = len;
     742           3 :   if(type == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
     743           0 :     uint8_t cr = this->codingRate;
     744             :     // We assume same calculation for short and long interleaving, so map CR values 0-4 and 5-7 to the same values
     745           0 :     if (cr < 5) {
     746           0 :       cr = cr + 4;
     747           0 :     } else if (cr == 7) {
     748           0 :       cr = cr + 1;
     749             :     }
     750             : 
     751           0 :     dataRate.lora.codingRate = cr;
     752           0 :     dataRate.lora.spreadingFactor = this->spreadingFactor;
     753           0 :     dataRate.lora.bandwidth = this->bandwidthHz;
     754             : 
     755           0 :     packetConfig.lora.preambleLength = this->preambleLengthLoRa;
     756           0 :     packetConfig.lora.crcEnabled = (bool)this->crcTypeLoRa;
     757           0 :     packetConfig.lora.implicitHeader = this->headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT;
     758           0 :     packetConfig.lora.ldrOptimize = (bool)this->ldrOptimize;
     759             :   
     760           3 :   } else if(type == RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
     761           0 :     modem = RADIOLIB_MODEM_FSK;
     762             : 
     763           0 :     dataRate.fsk.bitRate = (RADIOLIB_UNIT_MEGA(RADIOLIB_SX126X_CRYSTAL_FREQ) * 32UL) / this->bitRateRaw;
     764           0 :     dataRate.fsk.freqDev = (float)this->frequencyDev;
     765             : 
     766           0 :     uint8_t crcLen = 0;
     767           0 :     if(this->crcTypeFSK == RADIOLIB_SX126X_GFSK_CRC_1_BYTE || this->crcTypeFSK == RADIOLIB_SX126X_GFSK_CRC_1_BYTE_INV) {
     768           0 :       crcLen = 1;
     769           0 :     } else if(this->crcTypeFSK == RADIOLIB_SX126X_GFSK_CRC_2_BYTE || this->crcTypeFSK == RADIOLIB_SX126X_GFSK_CRC_2_BYTE_INV) {
     770           0 :       crcLen = 2;
     771             :     }
     772             : 
     773           0 :     packetConfig.fsk.preambleLength = this->preambleLengthFSK;
     774           0 :     packetConfig.fsk.syncWordLength = this->syncWordLength;
     775           0 :     packetConfig.fsk.crcLength = crcLen;
     776             :   }
     777           3 :    else if(type == RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) {
     778           0 :     modem = RADIOLIB_MODEM_LRFHSS;
     779             : 
     780           0 :     dataRate.lrFhss.bw = this->lrFhssBw;
     781           0 :     dataRate.lrFhss.cr = this->lrFhssCr;
     782           0 :     dataRate.lrFhss.narrowGrid = this->lrFhssGridNonFcc;
     783             :     
     784           0 :     packetConfig.lrFhss.hdrCount = this->lrFhssHdrCount;
     785             : 
     786           0 :     if(this->packetType != RADIOLIB_SX126X_GFSK_PACKET_FIXED) {
     787           0 :       packetLen++;
     788             :     }
     789             :   
     790           3 :   } else if(type == RADIOLIB_SX126X_PACKET_TYPE_BPSK) {
     791             :     // BPSK is so experimental it does not have a specific data rate structure
     792             :     // so just reuse FSK
     793           0 :     modem = RADIOLIB_MODEM_FSK;
     794             : 
     795           0 :     dataRate.fsk.bitRate = (RADIOLIB_UNIT_MEGA(RADIOLIB_SX126X_CRYSTAL_FREQ) * 32UL) / this->bitRateRaw;
     796           0 :     dataRate.fsk.freqDev = 0;
     797             : 
     798           0 :     packetConfig.fsk.preambleLength = 0;
     799           0 :     packetConfig.fsk.syncWordLength = 0;
     800           0 :     packetConfig.fsk.crcLength = 0;
     801             : 
     802             :   } else {
     803           3 :     return(RADIOLIB_ERR_WRONG_MODEM);
     804             :   
     805             :   }
     806             : 
     807           0 :   return(calculateTimeOnAir(modem, dataRate, packetConfig, packetLen));
     808             : }
     809             : 
     810           3 : RadioLibTime_t SX126x::calculateRxTimeout(RadioLibTime_t timeoutUs) {
     811             :   // the timeout value is given in units of 15.625 microseconds
     812             :   // the calling function should provide some extra width, as this number of units is truncated to integer
     813           3 :   RadioLibTime_t timeout = timeoutUs * (1.0f / 15.625f);
     814           3 :   return(timeout);
     815             : }
     816             : 
     817           6 : uint32_t SX126x::getIrqFlags() {
     818           6 :   uint8_t data[] = { 0x00, 0x00 };
     819           6 :   this->mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_IRQ_STATUS, data, 2);
     820           6 :   return(((uint32_t)(data[0]) << 8) | data[1]);
     821             : }
     822             : 
     823           3 : int16_t SX126x::setIrqFlags(uint32_t irq) {
     824           3 :   return(this->setDioIrqParams(irq, irq));
     825             : }
     826             : 
     827           6 : int16_t SX126x::clearIrqFlags(uint32_t irq) {
     828           6 :   return(this->clearIrqStatus(irq));
     829             : }
     830             : 
     831           3 : uint8_t SX126x::randomByte() {
     832             :   // set some magic registers
     833           3 :   this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_ANA_LNA, RADIOLIB_SX126X_LNA_RNG_ENABLED, 0, 0);
     834           3 :   this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_ANA_MIXER, RADIOLIB_SX126X_MIXER_RNG_ENABLED, 0, 0);
     835             : 
     836             :   // set mode to Rx
     837           3 :   setRx(RADIOLIB_SX126X_RX_TIMEOUT_INF);
     838             : 
     839             :   // wait a bit for the RSSI reading to stabilise
     840           3 :   this->mod->hal->delay(10);
     841             : 
     842             :   // read RSSI value 8 times, always keep just the least significant bit
     843           3 :   uint8_t randByte = 0x00;
     844          27 :   for(uint8_t i = 0; i < 8; i++) {
     845          24 :     uint8_t val = 0x00;
     846          24 :     readRegister(RADIOLIB_SX126X_REG_RANDOM_NUMBER_0, &val, sizeof(uint8_t));
     847          24 :     randByte |= ((val & 0x01) << i);
     848             :   }
     849             : 
     850             :   // set mode to standby
     851           3 :   standby();
     852             : 
     853             :   // restore the magic registers
     854           3 :   this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_ANA_LNA, RADIOLIB_SX126X_LNA_RNG_DISABLED, 0, 0);
     855           3 :   this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_ANA_MIXER, RADIOLIB_SX126X_MIXER_RNG_DISABLED, 0, 0);
     856             : 
     857           3 :   return(randByte);
     858             : }
     859             : 
     860           9 : int16_t SX126x::getModem(ModemType_t* modem) {
     861           9 :   RADIOLIB_ASSERT_PTR(modem);
     862             : 
     863           9 :   uint8_t type = getPacketType();
     864           9 :   switch(type) {
     865           0 :     case(RADIOLIB_SX126X_PACKET_TYPE_LORA):
     866           0 :       *modem = ModemType_t::RADIOLIB_MODEM_LORA;
     867           0 :       return(RADIOLIB_ERR_NONE);
     868           0 :     case(RADIOLIB_SX126X_PACKET_TYPE_GFSK):
     869           0 :       *modem = ModemType_t::RADIOLIB_MODEM_FSK;
     870           0 :       return(RADIOLIB_ERR_NONE);
     871           0 :     case(RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS):
     872           0 :       *modem = ModemType_t::RADIOLIB_MODEM_LRFHSS;
     873           0 :       return(RADIOLIB_ERR_NONE);
     874             :   }
     875             :   
     876           9 :   return(RADIOLIB_ERR_WRONG_MODEM);
     877             : }
     878             : 
     879           3 : int16_t SX126x::getMode(RadioModeType_t* mode) {
     880           3 :   RADIOLIB_ASSERT_PTR(mode);
     881             : 
     882           3 :   const uint8_t status = getStatus();
     883           3 :   if(status == RADIOLIB_SX126X_STATUS_SPI_FAILED) {
     884           3 :     return(RADIOLIB_ERR_SPI_CMD_FAILED);
     885             :   }
     886             : 
     887             :   // sleep is not among the modes this can report: the chip does not answer SPI while asleep
     888           0 :   switch(status & 0b01110000) {
     889           0 :     case(RADIOLIB_SX126X_STATUS_MODE_STDBY_RC):
     890             :     case(RADIOLIB_SX126X_STATUS_MODE_STDBY_XOSC):
     891           0 :       *mode = RADIOLIB_RADIO_MODE_STANDBY;
     892           0 :       break;
     893           0 :     case(RADIOLIB_SX126X_STATUS_MODE_RX):
     894           0 :       *mode = RADIOLIB_RADIO_MODE_RX;
     895           0 :       break;
     896           0 :     case(RADIOLIB_SX126X_STATUS_MODE_TX):
     897           0 :       *mode = RADIOLIB_RADIO_MODE_TX;
     898           0 :       break;
     899           0 :     default:
     900             :       // frequency synthesis, or a value the datasheet leaves unassigned
     901           0 :       *mode = RADIOLIB_RADIO_MODE_NONE;
     902           0 :       break;
     903             :   }
     904             : 
     905           0 :   return(RADIOLIB_ERR_NONE);
     906             : }
     907             : 
     908          12 : int16_t SX126x::stageMode(RadioModeType_t mode, RadioModeConfig_t* cfg) {
     909             :   int16_t state;
     910             : 
     911          12 :   switch(mode) {
     912           9 :     case(RADIOLIB_RADIO_MODE_RX): {
     913             :       // in implicit header mode, use the provided length if it is nonzero
     914             :       // otherwise we trust the user has previously set the payload length manually
     915           9 :       if((this->headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT) && (cfg->receive.len != 0)) {
     916           0 :         this->implicitLen = cfg->receive.len;
     917             :       }
     918             : 
     919             :       // ensure we are in standby
     920           9 :       state = standby();
     921           9 :       RADIOLIB_ASSERT(state);
     922             : 
     923             :       // set DIO mapping
     924           0 :       if(cfg->receive.timeout != RADIOLIB_SX126X_RX_TIMEOUT_INF) {
     925           0 :         cfg->receive.irqMask |= (1UL << RADIOLIB_IRQ_TIMEOUT);
     926             :       }
     927           0 :       state = setDioIrqParams(getIrqMapped(cfg->receive.irqFlags), getIrqMapped(cfg->receive.irqMask));
     928           0 :       RADIOLIB_ASSERT(state);
     929             : 
     930             :       // set buffer pointers
     931           0 :       state = setBufferBaseAddress();
     932           0 :       RADIOLIB_ASSERT(state);
     933             : 
     934             :       // clear interrupt flags
     935           0 :       state = clearIrqStatus();
     936           0 :       RADIOLIB_ASSERT(state);
     937             : 
     938             :       // restore original packet length
     939           0 :       uint8_t modem = getPacketType();
     940           0 :       if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
     941           0 :         state = setPacketParams(this->preambleLengthLoRa, this->crcTypeLoRa, this->implicitLen, this->headerType, this->invertIQEnabled);
     942           0 :         RADIOLIB_ASSERT(state);
     943             : 
     944             :         // set the LoRa symbol-number (sync) timeout
     945           0 :         state = setLoRaSymbNumTimeout((cfg->receive.syncSymbols > 0xFF) ? 0xFF : (uint8_t)cfg->receive.syncSymbols);
     946           0 :       } else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
     947           0 :         state = setPacketParamsFSK(this->preambleLengthFSK, this->preambleDetLength, this->crcTypeFSK, this->syncWordLength, RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF, this->whitening,
     948           0 :           this->packetType, (this->packetType == RADIOLIB_SX126X_GFSK_PACKET_FIXED) ? this->implicitLen : RADIOLIB_SX126X_MAX_PACKET_LENGTH);
     949             :       } else {
     950           0 :         return(RADIOLIB_ERR_UNKNOWN);
     951             :       }
     952             : 
     953             :       // if max(uint32_t) is used, revert to RxContinuous
     954           0 :       if(cfg->receive.timeout == 0xFFFFFFFF) {
     955           0 :         cfg->receive.timeout = 0xFFFFFF;
     956             :       }
     957           0 :       this->rxTimeout = cfg->receive.timeout;
     958           0 :     } break;
     959             :   
     960           3 :     case(RADIOLIB_RADIO_MODE_TX): {
     961             :       // check packet length
     962           3 :       if(cfg->transmit.len > RADIOLIB_SX126X_MAX_PACKET_LENGTH) {
     963           0 :         return(RADIOLIB_ERR_PACKET_TOO_LONG);
     964             :       }
     965             : 
     966             :       // maximum packet length is decreased by 1 when address filtering is active
     967             :       if((RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF != RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF) && 
     968             :         (cfg->transmit.len > RADIOLIB_SX126X_MAX_PACKET_LENGTH - 1)) {
     969             :         return(RADIOLIB_ERR_PACKET_TOO_LONG);
     970             :       }
     971             : 
     972             :       // set packet Length
     973           3 :       state = RADIOLIB_ERR_NONE;
     974           3 :       uint8_t modem = getPacketType();
     975           3 :       if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
     976           0 :         state = setPacketParams(this->preambleLengthLoRa, this->crcTypeLoRa, cfg->transmit.len, this->headerType, this->invertIQEnabled);
     977             :       
     978           3 :       } else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
     979           0 :         state = setPacketParamsFSK(this->preambleLengthFSK, this->preambleDetLength, this->crcTypeFSK, this->syncWordLength, RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF, this->whitening, this->packetType, cfg->transmit.len);
     980             :       
     981           3 :       } else if(modem == RADIOLIB_SX126X_PACKET_TYPE_BPSK) {
     982           0 :         uint16_t rampUp = RADIOLIB_SX126X_BPSK_RAMP_UP_TIME_600_BPS;
     983           0 :         uint16_t rampDown = RADIOLIB_SX126X_BPSK_RAMP_DOWN_TIME_600_BPS;
     984           0 :         if(this->bitRateRaw == RADIOLIB_SX126X_BIT_RATE_RAW(100)) {
     985           0 :           rampUp = RADIOLIB_SX126X_BPSK_RAMP_UP_TIME_100_BPS;
     986           0 :           rampDown = RADIOLIB_SX126X_BPSK_RAMP_DOWN_TIME_100_BPS;
     987             :         }
     988           0 :         state = setPacketParamsBPSK(cfg->transmit.len, rampUp, rampDown, 8*cfg->transmit.len);
     989             :       
     990           3 :       } else if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) {
     991           3 :         return(RADIOLIB_ERR_UNKNOWN);
     992             :       
     993             :       }
     994           0 :       RADIOLIB_ASSERT(state);
     995             : 
     996             :       // set DIO mapping
     997           0 :       if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) {
     998           0 :         state = setDioIrqParams(RADIOLIB_SX126X_IRQ_TX_DONE | RADIOLIB_SX126X_IRQ_TIMEOUT, RADIOLIB_SX126X_IRQ_TX_DONE);
     999             :       } else {
    1000           0 :         state = setDioIrqParams(RADIOLIB_SX126X_IRQ_TX_DONE | RADIOLIB_SX126X_IRQ_LR_FHSS_HOP, RADIOLIB_SX126X_IRQ_TX_DONE | RADIOLIB_SX126X_IRQ_LR_FHSS_HOP);
    1001             :       }
    1002           0 :       RADIOLIB_ASSERT(state);
    1003             : 
    1004             :       // set buffer pointers
    1005           0 :       state = setBufferBaseAddress();
    1006           0 :       RADIOLIB_ASSERT(state);
    1007             : 
    1008             :       // write packet to buffer - unless the caller already did that with writeTxBuffer,
    1009             :       // which it signals by leaving the payload pointer empty
    1010           0 :       if(modem != RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) {
    1011           0 :         state = (cfg->transmit.data != NULL) ? writeBuffer(cfg->transmit.data, cfg->transmit.len) : RADIOLIB_ERR_NONE;
    1012             :       
    1013             :       } else {
    1014             :         // an LR-FHSS frame is built from the payload in this step, so it cannot be written ahead
    1015           0 :         if(cfg->transmit.data == NULL) {
    1016           0 :           return(RADIOLIB_ERR_UNSUPPORTED);
    1017             :         }
    1018             : 
    1019             :         // first, reset the LR-FHSS state machine
    1020           0 :         state = resetLRFHSS();
    1021           0 :         RADIOLIB_ASSERT(state);
    1022             : 
    1023             :         // skip hopping for the first 4 - lrFhssHdrCount blocks
    1024           0 :         for(int i = 0; i < 4 - this->lrFhssHdrCount; ++i ) {
    1025           0 :           stepLRFHSS();
    1026             :         }
    1027             : 
    1028             :         // in LR-FHSS mode, we need to build the entire packet manually
    1029           0 :         uint8_t frame[RADIOLIB_SX126X_MAX_PACKET_LENGTH] = { 0 };
    1030           0 :         size_t frameLen = 0;
    1031           0 :         this->lrFhssFrameBitsRem = 0;
    1032           0 :         this->lrFhssFrameHopsRem = 0;
    1033           0 :         this->lrFhssHopNum = 0;
    1034           0 :         state = buildLRFHSSPacket(cfg->transmit.data, cfg->transmit.len, frame, &frameLen, &this->lrFhssFrameBitsRem, &this->lrFhssFrameHopsRem);
    1035           0 :         RADIOLIB_ASSERT(state);
    1036             : 
    1037             :         // FIXME check max len for FHSS
    1038           0 :         state = writeBuffer(frame, frameLen);
    1039           0 :         RADIOLIB_ASSERT(state);
    1040             : 
    1041             :         // activate hopping
    1042           0 :         const uint8_t hopCfg[] = { RADIOLIB_SX126X_HOPPING_ENABLED, (uint8_t)frameLen, (uint8_t)this->lrFhssFrameHopsRem };
    1043           0 :         state = writeRegister(RADIOLIB_SX126X_REG_HOPPING_ENABLE, hopCfg, 3);
    1044           0 :         RADIOLIB_ASSERT(state);
    1045             : 
    1046             :         // write the initial hopping table
    1047           0 :         uint8_t initHops = this->lrFhssFrameHopsRem;
    1048           0 :         if(initHops > 16) {
    1049           0 :           initHops = 16;
    1050             :         };
    1051           0 :         for(size_t i = 0; i < initHops; i++) {
    1052             :           // set the hop frequency and symbols
    1053           0 :           state = this->setLRFHSSHop(i);
    1054           0 :           RADIOLIB_ASSERT(state);
    1055             :         }
    1056             :       
    1057             :       }
    1058           0 :       RADIOLIB_ASSERT(state);
    1059             : 
    1060             :       // clear interrupt flags
    1061           0 :       state = clearIrqStatus();
    1062           0 :       RADIOLIB_ASSERT(state);
    1063             : 
    1064             :       // fix sensitivity
    1065           0 :       state = fixSensitivity();
    1066           0 :       RADIOLIB_ASSERT(state);
    1067           0 :     } break;
    1068             :     
    1069           0 :     default:
    1070           0 :       return(RADIOLIB_ERR_UNSUPPORTED);
    1071             :   }
    1072             : 
    1073           0 :   this->stagedMode = mode;
    1074           0 :   return(state);
    1075             : }
    1076             : 
    1077           3 : int16_t SX126x::writeTxBuffer(const uint8_t* data, size_t len) {
    1078           3 :   if(data == NULL) {
    1079           0 :     return(RADIOLIB_ERR_NULL_POINTER);
    1080             :   }
    1081           3 :   if(len > RADIOLIB_SX126X_MAX_PACKET_LENGTH) {
    1082           0 :     return(RADIOLIB_ERR_PACKET_TOO_LONG);
    1083             :   }
    1084             : 
    1085             :   // set buffer pointers
    1086           3 :   int16_t state = setBufferBaseAddress();
    1087           3 :   RADIOLIB_ASSERT(state);
    1088             : 
    1089           0 :   return(writeBuffer(data, (uint8_t)len));
    1090             : }
    1091             : 
    1092           3 : int16_t SX126x::clearTxBuffer() {
    1093             :   // the data buffer is overwritten by the next write, so there is nothing to discard
    1094           3 :   return(RADIOLIB_ERR_NONE);
    1095             : }
    1096             : 
    1097           3 : int16_t SX126x::launchMode() {
    1098             :   int16_t state;
    1099           3 :   switch(this->stagedMode) {
    1100           3 :     case(RADIOLIB_RADIO_MODE_RX): {
    1101           3 :       this->mod->setRfSwitchState(Module::MODE_RX);
    1102           3 :       state = setRx(this->rxTimeout);
    1103           3 :     } break;
    1104             :   
    1105           0 :     case(RADIOLIB_RADIO_MODE_TX): {
    1106           0 :       this->mod->setRfSwitchState(this->txMode);
    1107           0 :       state = setTx(RADIOLIB_SX126X_TX_TIMEOUT_NONE);
    1108           0 :       RADIOLIB_ASSERT(state);
    1109             : 
    1110             :       // wait for BUSY to go low (= PA ramp up done)
    1111           0 :       while(this->mod->hal->digitalRead(this->mod->getGpio())) {
    1112           0 :         this->mod->hal->yield();
    1113             :       }
    1114           0 :     } break;
    1115             :     
    1116           0 :     default:
    1117           0 :       return(RADIOLIB_ERR_UNSUPPORTED);
    1118             :   }
    1119             : 
    1120           3 :   this->stagedMode = RADIOLIB_RADIO_MODE_NONE;
    1121           3 :   return(state);
    1122             : }
    1123             : 
    1124             : #if !RADIOLIB_EXCLUDE_DIRECT_RECEIVE
    1125           0 : void SX126x::setDirectAction(void (*func)(void)) {
    1126           0 :   setDio1Action(func);
    1127           0 : }
    1128             : 
    1129           0 : void SX126x::readBit(uint32_t pin) {
    1130           0 :   updateDirectBuffer((uint8_t)this->mod->hal->digitalRead(pin));
    1131           0 : }
    1132             : #endif
    1133             : 
    1134           0 : int16_t SX126x::uploadPatch(const uint32_t* patch, size_t len, bool nonvolatile) {
    1135             :   // set to standby RC mode
    1136           0 :   int16_t state = standby(RADIOLIB_SX126X_STANDBY_RC);
    1137           0 :   RADIOLIB_ASSERT(state);
    1138             : 
    1139             :   // check the version
    1140             :   #if RADIOLIB_DEBUG_BASIC
    1141             :   char ver_pre[16];
    1142             :   this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, reinterpret_cast<uint8_t*>(ver_pre));
    1143             :   RADIOLIB_DEBUG_BASIC_PRINTLN("Pre-update version string: %s", ver_pre);
    1144             :   #endif
    1145             : 
    1146             :   // enable patch update
    1147           0 :   this->mod->SPIwriteRegister(RADIOLIB_SX126X_REG_PATCH_UPDATE_ENABLE, RADIOLIB_SX126X_PATCH_UPDATE_ENABLED);
    1148             :   
    1149             :   // upload the patch
    1150             :   uint8_t data[4];
    1151           0 :   for(uint32_t i = 0; i < len / sizeof(uint32_t); i++) {
    1152           0 :     uint32_t bin = 0;
    1153           0 :     if(nonvolatile) {
    1154           0 :       uint32_t* ptr = const_cast<uint32_t*>(patch) + i;
    1155           0 :       bin = RADIOLIB_NONVOLATILE_READ_DWORD(ptr);
    1156             :     } else {
    1157           0 :       bin = patch[i];
    1158             :     }
    1159           0 :     data[0] = (bin >> 24) & 0xFF;
    1160           0 :     data[1] = (bin >> 16) & 0xFF;
    1161           0 :     data[2] = (bin >> 8) & 0xFF;
    1162           0 :     data[3] = bin & 0xFF;
    1163           0 :     this->mod->SPIwriteRegisterBurst(RADIOLIB_SX126X_REG_PATCH_MEMORY_BASE + i*sizeof(uint32_t), data, sizeof(uint32_t));
    1164             :   }
    1165             : 
    1166             :   // disable patch update
    1167           0 :   this->mod->SPIwriteRegister(RADIOLIB_SX126X_REG_PATCH_UPDATE_ENABLE, RADIOLIB_SX126X_PATCH_UPDATE_DISABLED);
    1168             : 
    1169             :   // update
    1170           0 :   this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_PRAM_UPDATE, NULL, 0);
    1171             : 
    1172             :   // check the version again
    1173             :   #if RADIOLIB_DEBUG_BASIC
    1174             :   char ver_post[16];
    1175             :   this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, reinterpret_cast<uint8_t*>(ver_post));
    1176             :   RADIOLIB_DEBUG_BASIC_PRINTLN("Post-update version string: %s", ver_post);
    1177             :   #endif
    1178             : 
    1179           0 :   return(state);
    1180             : }
    1181             : 
    1182           0 : int16_t SX126x::spectralScanStart(uint16_t numSamples, uint8_t window, uint8_t interval) {
    1183             :   // abort first - not sure if this is strictly needed, but the example code does this
    1184           0 :   spectralScanAbort();
    1185             : 
    1186             :   // set the RSSI window size
    1187           0 :   this->mod->SPIwriteRegister(RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW, window);
    1188             : 
    1189             :   // start Rx with infinite timeout
    1190           0 :   int16_t state = setRx(RADIOLIB_SX126X_RX_TIMEOUT_INF);
    1191           0 :   RADIOLIB_ASSERT(state);
    1192             : 
    1193             :   // now set the actual spectral scan parameters
    1194           0 :   const uint8_t data[3] = { (uint8_t)((numSamples >> 8) & 0xFF), (uint8_t)(numSamples & 0xFF), interval };
    1195           0 :   return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_SPECTR_SCAN_PARAMS, data, 3));
    1196             : }
    1197             : 
    1198           0 : void SX126x::spectralScanAbort() {
    1199           0 :   this->mod->SPIwriteRegister(RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW, 0x00);
    1200           0 : }
    1201             : 
    1202           0 : int16_t SX126x::spectralScanGetStatus() {
    1203           0 :   uint8_t status = this->mod->SPIreadRegister(RADIOLIB_SX126X_REG_SPECTRAL_SCAN_STATUS);
    1204           0 :   if(status == RADIOLIB_SX126X_SPECTRAL_SCAN_COMPLETED) {
    1205           0 :     return(RADIOLIB_ERR_NONE);
    1206             :   }
    1207           0 :   return(RADIOLIB_ERR_RANGING_TIMEOUT);
    1208             : }
    1209             : 
    1210           0 : int16_t SX126x::spectralScanGetResult(uint16_t* results) {
    1211             :   // read the raw results
    1212             :   uint8_t data[2*RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE];
    1213           0 :   this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_SPECTRAL_SCAN_RESULT, 2*RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE, data);
    1214             : 
    1215             :   // convert it
    1216           0 :   for(uint8_t i = 0; i < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; i++) {
    1217           0 :     results[i] = ((uint16_t)data[i*2] << 8) | ((uint16_t)data[i*2 + 1]);
    1218             :   }
    1219           0 :   return(RADIOLIB_ERR_NONE);
    1220             : }
    1221             : 
    1222           0 : int16_t SX126x::calibrateImage(uint32_t freq) {
    1223           0 :   uint8_t data[2] = { 0, 0 };
    1224             : 
    1225             :   // try to match the frequency ranges
    1226           0 :   if((freq >= RADIOLIB_UNIT_MEGA(902)) && (freq <= RADIOLIB_UNIT_MEGA(928))) {
    1227           0 :     data[0] = RADIOLIB_SX126X_CAL_IMG_902_MHZ_1;
    1228           0 :     data[1] = RADIOLIB_SX126X_CAL_IMG_902_MHZ_2;
    1229           0 :   } else if((freq >= RADIOLIB_UNIT_MEGA(863)) && (freq <= RADIOLIB_UNIT_MEGA(870))) {
    1230           0 :     data[0] = RADIOLIB_SX126X_CAL_IMG_863_MHZ_1;
    1231           0 :     data[1] = RADIOLIB_SX126X_CAL_IMG_863_MHZ_2;
    1232           0 :   } else if((freq >= RADIOLIB_UNIT_MEGA(779)) && (freq <= RADIOLIB_UNIT_MEGA(787))) {
    1233           0 :     data[0] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_1;
    1234           0 :     data[1] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_2;
    1235           0 :   } else if((freq >= RADIOLIB_UNIT_MEGA(470)) && (freq <= RADIOLIB_UNIT_MEGA(510))) {
    1236           0 :     data[0] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_1;
    1237           0 :     data[1] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_2;
    1238           0 :   } else if((freq >= RADIOLIB_UNIT_MEGA(430)) && (freq <= RADIOLIB_UNIT_MEGA(440))) {
    1239           0 :     data[0] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_1;
    1240           0 :     data[1] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_2;
    1241             :   }
    1242             : 
    1243             :   int16_t state;
    1244           0 :   if(data[0]) {
    1245             :     // matched with predefined ranges, do the calibration
    1246           0 :     state = SX126x::calibrateImage(data);
    1247             :   
    1248             :   } else {
    1249             :     // if nothing matched, try custom calibration - the may or may not work
    1250             :     RADIOLIB_DEBUG_BASIC_PRINTLN("Failed to match predefined frequency range, trying custom");
    1251           0 :     state = SX126x::calibrateImageRejection(freq - RADIOLIB_UNIT_MEGA(4), freq + RADIOLIB_UNIT_MEGA(4));
    1252             :   
    1253             :   }
    1254             :   
    1255           0 :   return(state);
    1256             : }
    1257             : 
    1258           0 : int16_t SX126x::calibrateImageRejection(uint32_t freqMin, uint32_t freqMax) {
    1259             :   // calculate the calibration coefficients and calibrate image
    1260             :   uint8_t data[] = { 
    1261           0 :     (uint8_t)((freqMin - RADIOLIB_UNIT_MEGA(1)) / RADIOLIB_UNIT_MEGA(4)), 
    1262           0 :     (uint8_t)((freqMax + RADIOLIB_UNIT_MEGA(1)) / RADIOLIB_UNIT_MEGA(4)),
    1263           0 :   };
    1264           0 :   data[0] = (data[0] % 2) ? data[0] : data[0] - 1;
    1265           0 :   data[1] = (data[1] % 2) ? data[1] : data[1] + 1;
    1266           0 :   return(this->calibrateImage(data));
    1267             : }
    1268             : 
    1269           0 : int16_t SX126x::fixSensitivity() {
    1270             :   // fix receiver sensitivity for 500 kHz LoRa
    1271             :   // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.1 for details
    1272             : 
    1273             :   // read current sensitivity configuration
    1274           0 :   uint8_t sensitivityConfig = 0;
    1275           0 :   int16_t state = readRegister(RADIOLIB_SX126X_REG_SENSITIVITY_CONFIG, &sensitivityConfig, 1);
    1276           0 :   RADIOLIB_ASSERT(state);
    1277             : 
    1278             :   // fix the value for LoRa with 500 kHz bandwidth
    1279           0 :   if((getPacketType() == RADIOLIB_SX126X_PACKET_TYPE_LORA) && (this->bandwidthHz == RADIOLIB_UNIT_KILO(500))) {
    1280           0 :     sensitivityConfig &= 0xFB;
    1281             :   } else {
    1282           0 :     sensitivityConfig |= 0x04;
    1283             :   }
    1284           0 :   return(writeRegister(RADIOLIB_SX126X_REG_SENSITIVITY_CONFIG, &sensitivityConfig, 1));
    1285             : }
    1286             : 
    1287           0 : int16_t SX126x::fixPaClamping(bool enable) {
    1288             :   // fixes overly eager PA clamping
    1289             :   // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.2 for details
    1290             : 
    1291             :   // read current clamping configuration
    1292           0 :   uint8_t clampConfig = 0;
    1293           0 :   int16_t state = readRegister(RADIOLIB_SX126X_REG_TX_CLAMP_CONFIG, &clampConfig, 1);
    1294           0 :   RADIOLIB_ASSERT(state);
    1295             : 
    1296             :   // apply or undo workaround
    1297           0 :   clampConfig = enable ? (clampConfig | 0x1E) : ((clampConfig & ~0x1E) | 0x08);
    1298           0 :   return(writeRegister(RADIOLIB_SX126X_REG_TX_CLAMP_CONFIG, &clampConfig, 1));
    1299             : }
    1300             : 
    1301           0 : int16_t SX126x::fixImplicitTimeout() {
    1302             :   // fixes timeout in implicit header mode
    1303             :   // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.3 for details
    1304             : 
    1305             :   // check if we're in implicit LoRa mode
    1306           0 :   if(!((this->headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT) && (getPacketType() == RADIOLIB_SX126X_PACKET_TYPE_LORA))) {
    1307             :     // not in the correct mode, nothing to do here
    1308           0 :     return(RADIOLIB_ERR_NONE);
    1309             :   }
    1310             : 
    1311             :   // stop RTC counter
    1312           0 :   uint8_t rtcStop = 0x00;
    1313           0 :   int16_t state = writeRegister(RADIOLIB_SX126X_REG_RTC_CTRL, &rtcStop, 1);
    1314           0 :   RADIOLIB_ASSERT(state);
    1315             : 
    1316             :   // read currently active event
    1317           0 :   uint8_t rtcEvent = 0;
    1318           0 :   state = readRegister(RADIOLIB_SX126X_REG_EVENT_MASK, &rtcEvent, 1);
    1319           0 :   RADIOLIB_ASSERT(state);
    1320             : 
    1321             :   // clear events
    1322           0 :   rtcEvent |= 0x02;
    1323           0 :   return(writeRegister(RADIOLIB_SX126X_REG_EVENT_MASK, &rtcEvent, 1));
    1324             : }
    1325             : 
    1326           0 : int16_t SX126x::fixInvertedIQ(uint8_t iqConfig) {
    1327             :   // fixes IQ configuration for inverted IQ
    1328             :   // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.4 for details
    1329             : 
    1330             :   // read current IQ configuration
    1331           0 :   uint8_t iqConfigCurrent = 0;
    1332           0 :   int16_t state = readRegister(RADIOLIB_SX126X_REG_IQ_CONFIG, &iqConfigCurrent, 1);
    1333           0 :   RADIOLIB_ASSERT(state);
    1334             : 
    1335             :   // set correct IQ configuration
    1336           0 :   if(iqConfig == RADIOLIB_SX126X_LORA_IQ_INVERTED) {
    1337           0 :     iqConfigCurrent &= 0xFB;
    1338             :   } else {
    1339           0 :     iqConfigCurrent |= 0x04;
    1340             :   }
    1341             : 
    1342             :   // update with the new value
    1343           0 :   return(writeRegister(RADIOLIB_SX126X_REG_IQ_CONFIG, &iqConfigCurrent, 1));
    1344             : }
    1345             : 
    1346           0 : int16_t SX126x::fixGFSK() {
    1347             :   // method that applies some magic workaround for specific bitrate, frequency deviation,
    1348             :   // receiver bandwidth and carrier frequencies for GFSK (and resets it in all other cases)
    1349             :   // this is not documented in the datasheet, only in Semtech repositories for SX126x and LR11xx
    1350             : 
    1351             :   // first, check we are using GFSK modem
    1352           0 :   if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
    1353             :     // not in GFSK, nothing to do here
    1354           0 :     return(RADIOLIB_ERR_NONE);
    1355             :   }
    1356             : 
    1357             :   // next, decide what to change based on modulation properties
    1358           0 :   int16_t state = RADIOLIB_ERR_UNKNOWN;
    1359           0 :   if(this->bitRateRaw == RADIOLIB_SX126X_BIT_RATE_RAW(1200)) {
    1360             :     // workaround for 1.2 kbps
    1361           0 :     state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_GFSK_FIX_3, 0x00, 4, 4);
    1362             : 
    1363           0 :   } else if(this->bitRateRaw == RADIOLIB_SX126X_BIT_RATE_RAW(600))  {
    1364             :     // workaround for 0.6 kbps
    1365           0 :     state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_GFSK_FIX_1, 0x18, 4, 3);
    1366           0 :     RADIOLIB_ASSERT(state);
    1367           0 :     state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW, 0x04, 4, 2);
    1368           0 :     RADIOLIB_ASSERT(state);
    1369           0 :     state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_GFSK_FIX_3, 0x00, 4, 4);
    1370           0 :     RADIOLIB_ASSERT(state);
    1371           0 :     state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_GFSK_FIX_4, 0x50, 6, 4);
    1372             :   
    1373             :   } else {
    1374             :     // reset
    1375           0 :     state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_GFSK_FIX_1, 0x08, 4, 3);
    1376           0 :     RADIOLIB_ASSERT(state);
    1377           0 :     state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW, 0x00, 4, 2);
    1378           0 :     RADIOLIB_ASSERT(state);
    1379           0 :     state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_GFSK_FIX_3, 0x10, 4, 4);
    1380           0 :     RADIOLIB_ASSERT(state);
    1381           0 :     state = this->mod->SPIsetRegValue(RADIOLIB_SX126X_REG_GFSK_FIX_4, 0x00, 6, 4);
    1382             :   
    1383             :   }
    1384             : 
    1385           0 :   return(state);
    1386             : }
    1387             : 
    1388           0 : Module* SX126x::getMod() {
    1389           0 :   return(this->mod);
    1390             : }
    1391             : 
    1392           0 : int16_t SX126x::modSetup(uint8_t modem) {
    1393             :   // set module properties
    1394           0 :   int16_t state = this->mod->init();
    1395           0 :   RADIOLIB_ASSERT(state);
    1396           0 :   this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput);
    1397           0 :   this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput);
    1398           0 :   this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] = Module::BITS_16;
    1399           0 :   this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_8;
    1400           0 :   this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_8;
    1401           0 :   this->mod->spiConfig.statusPos = 1;
    1402           0 :   this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_SX126X_CMD_READ_REGISTER;
    1403           0 :   this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_SX126X_CMD_WRITE_REGISTER;
    1404           0 :   this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP] = RADIOLIB_SX126X_CMD_NOP;
    1405           0 :   this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] = RADIOLIB_SX126X_CMD_GET_STATUS;
    1406           0 :   this->mod->spiConfig.stream = true;
    1407           0 :   this->mod->spiConfig.parseStatusCb = SPIparseStatus;
    1408             : 
    1409             :   // find the SX126x chip - this will also reset the module and verify the module
    1410           0 :   if(!SX126x::findChip(this->chipType)) {
    1411             :     RADIOLIB_DEBUG_BASIC_PRINTLN("No SX126x found!");
    1412           0 :     state = this->mod->term();
    1413           0 :     RADIOLIB_ASSERT(state);
    1414           0 :     return(RADIOLIB_ERR_CHIP_NOT_FOUND);
    1415             :   }
    1416             :   RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX126x");
    1417             : 
    1418             :   // set TCXO control, if requested
    1419           0 :   if(this->tcxoVoltage != RadioLibTCXOVoltage_t::VoltageNone) {
    1420           0 :     state = setTCXO(this->tcxoVoltage);
    1421           0 :     RADIOLIB_ASSERT(state);
    1422             :   }
    1423             : 
    1424             :   // configure settings not accessible by API
    1425           0 :   state = config(modem);
    1426             : 
    1427             :   // if something failed, check the device errors
    1428           0 :   if(state != RADIOLIB_ERR_NONE) {
    1429             :     // unless mode is forced to standby, device errors will be 0
    1430           0 :     (void)standby();
    1431           0 :     uint16_t errors = getDeviceErrors();
    1432             :     RADIOLIB_DEBUG_BASIC_PRINTLN("Config failed, device errors: 0x%X", errors);
    1433             : 
    1434             :     // SPI command fail and oscillator start error flag indicate incorrectly set oscillator
    1435           0 :     if((state == RADIOLIB_ERR_SPI_CMD_FAILED) && (errors & RADIOLIB_SX126X_XOSC_START_ERR)) {
    1436             :       // typically users with XTAL devices will try to call the default begin method
    1437             :       // disable TCXO and try to run config again
    1438           0 :       this->tcxoVoltage = RadioLibTCXOVoltage_t::VoltageNone;
    1439             :       RADIOLIB_DEBUG_BASIC_PRINTLN("Bad oscillator selected, trying XTAL");
    1440             : 
    1441           0 :       state = setTCXO(RadioLibTCXOVoltage_t::VoltageNone);
    1442           0 :       RADIOLIB_ASSERT(state);
    1443             : 
    1444           0 :       state = config(modem);
    1445             :     }
    1446             :   }
    1447           0 :   RADIOLIB_ASSERT(state);
    1448             : 
    1449           0 :   if(this->useRegulatorLDO) {
    1450           0 :     state = setRegulatorLDO();
    1451             :   } else {
    1452           0 :     state = setRegulatorDCDC();
    1453             :   }
    1454           0 :   return(state);
    1455             : }
    1456             : 
    1457           0 : int16_t SX126x::SPIparseStatus(uint8_t in) {
    1458           0 :   if((in & 0b00001110) == RADIOLIB_SX126X_STATUS_CMD_TIMEOUT) {
    1459           0 :     return(RADIOLIB_ERR_SPI_CMD_TIMEOUT);
    1460           0 :   } else if((in & 0b00001110) == RADIOLIB_SX126X_STATUS_CMD_INVALID) {
    1461           0 :     return(RADIOLIB_ERR_SPI_CMD_INVALID);
    1462           0 :   } else if((in & 0b00001110) == RADIOLIB_SX126X_STATUS_CMD_FAILED) {
    1463           0 :     return(RADIOLIB_ERR_SPI_CMD_FAILED);
    1464           0 :   } else if((in == 0x00) || (in == 0xFF)) {
    1465           0 :     return(RADIOLIB_ERR_CHIP_NOT_FOUND);
    1466             :   }
    1467           0 :   return(RADIOLIB_ERR_NONE);
    1468             : }
    1469             : 
    1470           0 : bool SX126x::findChip(const char* verStr) {
    1471           0 :   uint8_t i = 0;
    1472           0 :   bool flagFound = false;
    1473           0 :   while((i < 10) && !flagFound) {
    1474             :     // reset the module
    1475           0 :     if(this->resetOnStartup) {
    1476           0 :       reset(true);
    1477             :     }
    1478             : 
    1479             :     // read the version string
    1480           0 :     char version[16] = { 0 };
    1481           0 :     this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, reinterpret_cast<uint8_t*>(version));
    1482             : 
    1483             :     // check version register
    1484           0 :     if(strncmp(verStr, version, 6) == 0) {
    1485             :       RADIOLIB_DEBUG_BASIC_PRINTLN("Found SX126x: RADIOLIB_SX126X_REG_VERSION_STRING:");
    1486             :       RADIOLIB_DEBUG_BASIC_HEXDUMP(reinterpret_cast<uint8_t*>(version), 16, RADIOLIB_SX126X_REG_VERSION_STRING);
    1487             :       RADIOLIB_DEBUG_BASIC_PRINTLN();
    1488           0 :       flagFound = true;
    1489             :     } else {
    1490             :       #if RADIOLIB_DEBUG_BASIC
    1491             :         RADIOLIB_DEBUG_BASIC_PRINTLN("SX126x not found! (%d of 10 tries) RADIOLIB_SX126X_REG_VERSION_STRING:", i + 1);
    1492             :         RADIOLIB_DEBUG_BASIC_HEXDUMP(reinterpret_cast<uint8_t*>(version), 16, RADIOLIB_SX126X_REG_VERSION_STRING);
    1493             :         RADIOLIB_DEBUG_BASIC_PRINTLN("Expected string: %s", verStr);
    1494             :       #endif
    1495           0 :       this->mod->hal->delay(10);
    1496           0 :       i++;
    1497             :     }
    1498             :   }
    1499             : 
    1500           0 :   return(flagFound);
    1501             : }
    1502             : 
    1503             : #endif

Generated by: LCOV version 1.14