LilyGo-LoRa-Series/lib/RadioLib/src/modules/LR2021/LR2021_config.cpp

1197 lines
41 KiB
C++
Raw Normal View History

2026-04-17 10:15:48 +08:00
#include "LR2021.h"
#include "../LR11x0/LR_common.h"
#include "LR2021_registers.h"
#include <math.h>
#include <string.h>
#if !RADIOLIB_EXCLUDE_LR2021
// maximum number of allowed frontend calibration attempts
#define RADIOLIB_LR2021_MAX_CAL_ATTEMPTS (10)
2026-04-20 18:00:37 +08:00
// Sub-GHz: below this RF frequency (MHz) use Table 7-17 (490 MHz ref.); at/above use Table 7-16 (915 MHz ref.).
#define RADIOLIB_LR2021_LF_PA_TABLE_490_MHZ_MAX (700.0f)
// Table 7-16: Optimal Values for 915 MHz Semtech Reference Design (LF PA).
// Integer targeted dBm 22..10 only: half-dBm rows (e.g. 21.5) need a future API that passes 0.5 dB steps.
// SetTxParams first byte = `txHalfDbm` (signed half-dBm); matches datasheet TX_PARAM column * 2.
static const struct {
int8_t txHalfDbm;
uint8_t paLfDutyCycle;
uint8_t paLfSlices;
} RADIOLIB_LR2021_TABLE_7_16_915_LF[] = {
/* 22 */ { 44, 7, 6 },
/* 21 */ { 42, 7, 7 },
/* 20 */ { 41, 6, 6 },
/* 19 */ { 39, 6, 6 },
/* 18 */ { 38, 5, 6 },
/* 17 */ { 36, 5, 6 },
/* 16 */ { 36, 4, 4 },
/* 15 */ { 33, 5, 4 },
/* 14 */ { 34, 4, 2 },
/* 13 */ { 31, 4, 3 },
/* 12 */ { 30, 5, 1 },
/* 11 */ { 32, 2, 2 },
/* 10 */ { 32, 2, 1 },
};
// `targetedDbm` integer 10..22 only (Table 7-16 rows).
static void lr2021Table716LfRow(int8_t targetedDbm, int8_t* txHalfDbm, uint8_t* duty, uint8_t* slices) {
if(targetedDbm < 10) { targetedDbm = 10; }
if(targetedDbm > 22) { targetedDbm = 22; }
const auto& e = RADIOLIB_LR2021_TABLE_7_16_915_LF[22 - targetedDbm];
*txHalfDbm = e.txHalfDbm;
*duty = e.paLfDutyCycle;
*slices = e.paLfSlices;
}
// Table 7-17: Optimal Values for 490 MHz Semtech Reference Design (LF PA). Integer targeted dBm 20..10.
static const struct {
int8_t txHalfDbm;
uint8_t paLfDutyCycle;
uint8_t paLfSlices;
} RADIOLIB_LR2021_TABLE_7_17_490_LF[] = {
/* 20 */ { 40, 7, 7 },
/* 19 */ { 38, 7, 7 },
/* 18 */ { 36, 7, 6 },
/* 17 */ { 34, 7, 6 },
/* 16 */ { 32, 7, 6 },
/* 15 */ { 31, 7, 4 },
/* 14 */ { 31, 6, 4 },
/* 13 */ { 29, 7, 2 },
/* 12 */ { 30, 5, 3 },
/* 11 */ { 29, 5, 2 },
/* 10 */ { 31, 4, 2 },
};
static void lr2021Table717LfRow(int8_t targetedDbm, int8_t* txHalfDbm, uint8_t* duty, uint8_t* slices) {
if(targetedDbm < 10) { targetedDbm = 10; }
if(targetedDbm > 20) { targetedDbm = 20; }
const auto& e = RADIOLIB_LR2021_TABLE_7_17_490_LF[20 - targetedDbm];
*txHalfDbm = e.txHalfDbm;
*duty = e.paLfDutyCycle;
*slices = e.paLfSlices;
}
// Table 7-18: Optimal Values for 2445 MHz Semtech Reference Design (HF PA). Integer targeted dBm 0..12.
static const struct {
int8_t txHalfDbm;
uint8_t paHfDutyCycle;
} RADIOLIB_LR2021_TABLE_7_18_2445_HF[] = {
/* 12 */ { 24, 16 },
/* 11 */ { 24, 26 },
/* 10 */ { 24, 30 },
/* 9 */ { 22, 30 },
/* 8 */ { 21, 31 },
/* 7 */ { 18, 30 },
/* 6 */ { 16, 30 },
/* 5 */ { 15, 31 },
/* 4 */ { 10, 25 },
/* 3 */ { 8, 25 },
/* 2 */ { 7, 28 },
/* 1 */ { 6, 30 },
/* 0 */ { 4, 30 },
};
static void lr2021Table718HfRow(int8_t targetedDbm, int8_t* txHalfDbm, uint8_t* hfDuty) {
if(targetedDbm < 0) { targetedDbm = 0; }
if(targetedDbm > 12) { targetedDbm = 12; }
const auto& e = RADIOLIB_LR2021_TABLE_7_18_2445_HF[12 - targetedDbm];
*txHalfDbm = e.txHalfDbm;
*hfDuty = e.paHfDutyCycle;
}
2026-04-17 10:15:48 +08:00
int16_t LR2021::setFrequency(float freq) {
return(this->setFrequency(freq, false));
}
int16_t LR2021::setFrequency(float freq, bool skipCalibration) {
#if RADIOLIB_CHECK_PARAMS
if(!(((freq >= 150.0f) && (freq <= 1090.0f)) ||
((freq >= 1900.0f) && (freq <= 2200.0f)) ||
((freq >= 2400.0f) && (freq <= 2500.0f)))) {
return(RADIOLIB_ERR_INVALID_FREQUENCY);
}
#endif
// check if we need to recalibrate image
int16_t state;
if(!skipCalibration && (fabsf(freq - this->freqMHz) >= RADIOLIB_LR2021_CAL_IMG_FREQ_TRIG_MHZ)) {
// calibration can fail if there is a strong interfering source
// run it several times until it passes
int i = 0;
for(; i < RADIOLIB_LR2021_MAX_CAL_ATTEMPTS; i++) {
// get the nearest multiple of 4 MHz
uint16_t frequencies[3] = { (uint16_t)((freq / 4.0f) + 0.5f), 0, 0 };
frequencies[0] |= (freq > RADIOLIB_LR2021_LF_CUTOFF_FREQ) ? RADIOLIB_LR2021_CALIBRATE_FE_HF_PATH : RADIOLIB_LR2021_CALIBRATE_FE_LF_PATH;
state = calibrateFrontEnd(const_cast<const uint16_t*>(frequencies));
// if something failed, check the device errors
if(state != RADIOLIB_ERR_NONE) {
uint16_t errors = 0;
getErrors(&errors);
RADIOLIB_DEBUG_BASIC_PRINTLN("Frontend calibration #%d failed, device errors: 0x%X", i, errors);
// if this is caused by something else than RSSI saturation, repeating will not help
if((errors & RADIOLIB_LR2021_SRC_SATURATION_CALIB_ERR) == 0) {
return(state);
}
// wait a little while before the next attempt
this->mod->hal->delay(5);
} else {
// calibration passed
break;
}
}
if(i == RADIOLIB_LR2021_MAX_CAL_ATTEMPTS) {
return(RADIOLIB_ERR_FRONTEND_CALIBRATION_FAILED);
}
}
// set frequency
state = setRfFrequency((uint32_t)(freq*1000000.0f));
RADIOLIB_ASSERT(state);
this->freqMHz = freq;
this->highFreq = (freq > RADIOLIB_LR2021_LF_CUTOFF_FREQ);
return(state);
}
int16_t LR2021::setOutputPower(int8_t power) {
return(this->setOutputPower(power, 48));
}
int16_t LR2021::setOutputPower(int8_t power, uint32_t rampTimeUs) {
int16_t state = this->checkOutputPower(power, NULL);
RADIOLIB_ASSERT(state);
2026-04-20 18:00:37 +08:00
// pa_sel: 0 = LF (Sub-GHz), 1 = HF (1.92.5 GHz). Same encoding as lr20xx reference driver.
uint8_t paSel = this->highFreq ? (uint8_t)1 : (uint8_t)0;
uint8_t paLfMode = RADIOLIB_LR2021_PA_LF_MODE_FSM;
uint8_t paLfDutyCycle = RADIOLIB_LR2021_PA_LF_DUTY_CYCLE_UNUSED;
uint8_t paLfSlices = RADIOLIB_LR2021_PA_LF_SLICES_UNUSED;
uint8_t paHfDutyCycle = RADIOLIB_LR2021_PA_HF_DUTY_CYCLE_UNUSED;
// TX_PARAM from tables is signed half-dBm (not always 2 * targeted dBm).
int8_t lfTxHalfDbm = (int8_t)(power * 2);
int8_t hfTxHalfDbm = (int8_t)(power * 2);
if(this->highFreq) {
// HF PA: Table 7-18 (2445 MHz); LF nibbles per Semtech reference (7/6).
paLfDutyCycle = (uint8_t)0x07;
paLfSlices = (uint8_t)0x06;
if(power >= 0) {
lr2021Table718HfRow(power, &hfTxHalfDbm, &paHfDutyCycle);
} else {
lr2021Table718HfRow(0, &hfTxHalfDbm, &paHfDutyCycle);
hfTxHalfDbm = (int8_t)(power * 2);
}
} else if(this->freqMHz < RADIOLIB_LR2021_LF_PA_TABLE_490_MHZ_MAX) {
// LF PA: Table 7-17 (490 MHz ref.), max +20 dBm in table; 2122 use row 20 PA + requested TX half-dBm.
if(power > 20) {
lr2021Table717LfRow(20, &lfTxHalfDbm, &paLfDutyCycle, &paLfSlices);
lfTxHalfDbm = (int8_t)(power * 2);
} else if(power >= 10) {
lr2021Table717LfRow(power, &lfTxHalfDbm, &paLfDutyCycle, &paLfSlices);
} else {
lr2021Table717LfRow(10, &lfTxHalfDbm, &paLfDutyCycle, &paLfSlices);
lfTxHalfDbm = (int8_t)(power * 2);
}
} else {
// LF PA: Table 7-16 (915 MHz ref.)
if(power >= 10) {
lr2021Table716LfRow(power, &lfTxHalfDbm, &paLfDutyCycle, &paLfSlices);
} else {
lr2021Table716LfRow(10, &lfTxHalfDbm, &paLfDutyCycle, &paLfSlices);
lfTxHalfDbm = (int8_t)(power * 2);
}
}
(void)clearErrors();
state = setPaConfig(paSel, paLfMode, paLfDutyCycle, paLfSlices, paHfDutyCycle);
RADIOLIB_ASSERT(state);
#if RADIOLIB_DEBUG_BASIC
RADIOLIB_DEBUG_BASIC_PRINTLN("LR2021 PA cfg: hf=%d lf_dc=0x%X lf_sl=0x%X hf_dc=0x%X",
(int)this->highFreq, (int)paLfDutyCycle, (int)paLfSlices, (int)paHfDutyCycle);
#endif
state = selPa(paSel);
2026-04-17 10:15:48 +08:00
RADIOLIB_ASSERT(state);
2026-04-20 18:00:37 +08:00
#if RADIOLIB_DEBUG_BASIC
RADIOLIB_DEBUG_BASIC_PRINTLN("LR2021 PA sel: %s", paSel ? "HF" : "LF");
#endif
if(this->highFreq) {
state = setTxParamsHalfDbm(hfTxHalfDbm, roundRampTime(rampTimeUs));
} else {
state = setTxParamsHalfDbm(lfTxHalfDbm, roundRampTime(rampTimeUs));
}
RADIOLIB_ASSERT(state);
#if RADIOLIB_DEBUG_BASIC
uint16_t err = 0;
if(getErrors(&err) == RADIOLIB_ERR_NONE) {
RADIOLIB_DEBUG_BASIC_PRINTLN("LR2021 device errors after PA/TX: 0x%X", err);
}
#endif
2026-04-17 10:15:48 +08:00
return(state);
}
int16_t LR2021::checkOutputPower(int8_t power, int8_t* clipped) {
if(this->highFreq) {
if(clipped) {
*clipped = RADIOLIB_MAX(-19, RADIOLIB_MIN(12, power));
}
RADIOLIB_CHECK_RANGE(power, -19, 12, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
} else {
if(clipped) {
*clipped = RADIOLIB_MAX(-9, RADIOLIB_MIN(22, power));
}
RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
}
return(RADIOLIB_ERR_NONE);
}
void LR2021::setRfSwitchTable(const uint32_t (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]) {
// find which pins are used
// on LR2021, modes are configured per DIO (exact opposite of LR11x0)
uint8_t dioConfigs[7] = { 0 };
for(size_t i = 0; i < Module::RFSWITCH_MAX_PINS; i++) {
// check if this pin is unused
if(pins[i] == RADIOLIB_NC) { continue; }
// only keep DIO pins, there may be some GPIOs in the switch tabke
if((pins[i] & RFSWITCH_PIN_FLAG) == 0) { continue; }
// find modes in which this pin is used
uint32_t dioNum = RADIOLIB_LRXXXX_DIOx_VAL(pins[i]);
size_t j = 0;
while(table[j].mode != LR2021::MODE_END_OF_TABLE) {
dioConfigs[dioNum] |= (table[j].values[i] == this->mod->hal->GpioLevelHigh) ? (1UL << (table[j].mode - 1)) : 0;
j++;
}
// For DIO = 5, only DIO_SLEEP_PULL_UP is accepted. Otherwise the SetDioFunction returns FAIL.
uint8_t pull = dioNum == 0 ? RADIOLIB_LR2021_DIO_SLEEP_PULL_UP : RADIOLIB_LR2021_DIO_SLEEP_PULL_AUTO;
// enable RF control for this pin and set the modes in which it is active
(void)this->setDioFunction(dioNum + 5, RADIOLIB_LR2021_DIO_FUNCTION_RF_SWITCH, pull);
(void)this->setDioRfSwitchConfig(dioNum + 5, dioConfigs[i]);
}
}
int16_t LR2021::setBandwidth(float bw) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
// convert to int to avoid bunch of math
int bw_div2 = bw / 2 + 0.01f;
// check allowed bandwidth values
switch (bw_div2) {
case 15:
this->bandwidth = RADIOLIB_LR2021_LORA_BW_31;
break;
case 20:
this->bandwidth = RADIOLIB_LR2021_LORA_BW_41;
break;
case 31:
this->bandwidth = RADIOLIB_LR2021_LORA_BW_62;
break;
case 41:
this->bandwidth = RADIOLIB_LR2021_LORA_BW_83;
break;
case 50:
this->bandwidth = RADIOLIB_LR2021_LORA_BW_101;
break;
case 101:
this->bandwidth = RADIOLIB_LR2021_LORA_BW_203;
break;
case 62:
this->bandwidth = RADIOLIB_LR2021_LORA_BW_125;
break;
case 125:
this->bandwidth = RADIOLIB_LR2021_LORA_BW_250;
break;
case 203:
this->bandwidth = RADIOLIB_LR2021_LORA_BW_406;
break;
case 250:
this->bandwidth = RADIOLIB_LR2021_LORA_BW_500;
break;
case 406:
this->bandwidth = RADIOLIB_LR2021_LORA_BW_812;
break;
case 500:
this->bandwidth = RADIOLIB_LR2021_LORA_BW_1000;
break;
default:
return(RADIOLIB_ERR_INVALID_BANDWIDTH);
}
// update modulation parameters
this->bandwidthKhz = bw;
return(setLoRaModulationParams(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
}
int16_t LR2021::setSpreadingFactor(uint8_t sf, bool legacy) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
RADIOLIB_CHECK_RANGE(sf, 5, 12, RADIOLIB_ERR_INVALID_SPREADING_FACTOR);
//! \TODO: [LR2021] enable SF6 legacy mode
if(legacy && (sf == 6)) {
//this->mod->SPIsetRegValue(RADIOLIB_LR11X0_REG_SF6_SX127X_COMPAT, RADIOLIB_LR11X0_SF6_SX127X, 18, 18);
}
// update modulation parameters
this->spreadingFactor = sf;
return(setLoRaModulationParams(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
}
int16_t LR2021::setCodingRate(uint8_t cr, bool longInterleave) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type == RADIOLIB_LR2021_PACKET_TYPE_LORA) {
RADIOLIB_CHECK_RANGE(cr, 4, 8, RADIOLIB_ERR_INVALID_CODING_RATE);
if(longInterleave) {
switch(cr) {
case 4:
this->codingRate = 0;
break;
case 5:
case 6:
this->codingRate = cr;
break;
case 8:
this->codingRate = cr - 1;
break;
default:
return(RADIOLIB_ERR_INVALID_CODING_RATE);
}
} else {
this->codingRate = cr - 4;
}
// update modulation parameters
return(setLoRaModulationParams(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize));
} else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
if(cr > RADIOLIB_LR2021_FLRC_CR_2_3) {
return(RADIOLIB_ERR_INVALID_CODING_RATE);
}
// update modulation parameters
this->codingRateFlrc = cr;
return(setFlrcModulationParams(this->bitRateFlrc, this->codingRateFlrc, this->pulseShape));
}
return(RADIOLIB_ERR_WRONG_MODEM);
}
int16_t LR2021::setSyncWord(uint8_t syncWord) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
return(setLoRaSyncword(syncWord));
}
int16_t LR2021::setPreambleLength(size_t preambleLength) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type == RADIOLIB_LR2021_PACKET_TYPE_LORA) {
this->preambleLengthLoRa = preambleLength;
return(setLoRaPacketParams(this->preambleLengthLoRa, this->headerType, this->implicitLen, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled));
} else if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
this->preambleLengthGFSK = preambleLength;
this->preambleDetLength = (preambleLength / 8) << 3;
return(setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType, RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
} else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
this->preambleLengthGFSK = preambleLength;
this->preambleDetLength = (preambleLength / 8) << 3;
return(setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType, RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
} else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
if((preambleLength % 4) != 0) {
return(RADIOLIB_ERR_INVALID_PREAMBLE_LENGTH);
}
this->preambleLengthGFSK = (preambleLength / 4) - 1;
return(setFlrcPacketParams(this->preambleLengthGFSK, this->syncWordLength, 1, 0x01, this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, this->crcLenGFSK, RADIOLIB_LR2021_MAX_PACKET_LENGTH));
}
return(RADIOLIB_ERR_WRONG_MODEM);
}
int16_t LR2021::setTCXO(float voltage, uint32_t delay) {
// check if TCXO is enabled at all
if(this->XTAL) {
return(RADIOLIB_ERR_INVALID_TCXO_VOLTAGE);
}
// set mode to standby
standby();
// check oscillator startup error flag and clear it
uint16_t errors = 0;
int16_t state = getErrors(&errors);
RADIOLIB_ASSERT(state);
if(errors & RADIOLIB_LR2021_HF_XOSC_START_ERR) {
clearErrors();
}
// check 0 V disable
if(fabsf(voltage - 0.0f) <= 0.001f) {
setTcxoMode(0, 0);
return(reset());
}
// check allowed voltage values
uint8_t tune = 0;
if(fabsf(voltage - 1.6f) <= 0.001f) {
tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_1_6;
} else if(fabsf(voltage - 1.7f) <= 0.001f) {
tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_1_7;
} else if(fabsf(voltage - 1.8f) <= 0.001f) {
tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_1_8;
} else if(fabsf(voltage - 2.2f) <= 0.001f) {
tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_2_2;
} else if(fabsf(voltage - 2.4f) <= 0.001f) {
tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_2_4;
} else if(fabsf(voltage - 2.7f) <= 0.001f) {
tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_2_7;
} else if(fabsf(voltage - 3.0f) <= 0.001f) {
tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_3_0;
} else if(fabsf(voltage - 3.3f) <= 0.001f) {
tune = RADIOLIB_LRXXXX_TCXO_VOLTAGE_3_3;
} else {
return(RADIOLIB_ERR_INVALID_TCXO_VOLTAGE);
}
// calculate delay value
uint32_t delayValue = (uint32_t)((float)delay / 30.52f);
if(delayValue == 0) {
delayValue = 1;
}
// enable TCXO control
return(setTcxoMode(tune, delayValue));
}
int16_t LR2021::setCRC(uint8_t len, uint32_t initial, uint32_t polynomial, bool inverted) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type == RADIOLIB_LR2021_PACKET_TYPE_LORA) {
// LoRa CRC doesn't allow to set CRC polynomial, initial value, or inversion
this->crcTypeLoRa = len > 0;
return(setLoRaPacketParams(this->preambleLengthLoRa, this->headerType, this->implicitLen, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled));
} else if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
if(len > 4) {
return(RADIOLIB_ERR_INVALID_CRC_CONFIGURATION);
}
this->crcTypeGFSK = len;
if((this->crcTypeGFSK != RADIOLIB_LR2021_GFSK_OOK_CRC_OFF) && inverted) {
this->crcTypeGFSK += 0x08;
}
state = setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType, RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
RADIOLIB_ASSERT(state);
return(setGfskCrcParams(polynomial, initial));
} else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
if(len > 4) {
return(RADIOLIB_ERR_INVALID_CRC_CONFIGURATION);
}
this->crcTypeGFSK = len;
if((this->crcTypeGFSK != RADIOLIB_LR2021_GFSK_OOK_CRC_OFF) && inverted) {
this->crcTypeGFSK += 0x08;
}
state = setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType, RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
RADIOLIB_ASSERT(state);
return(setOokCrcParams(polynomial, initial));
} else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
if((len == 1) || (len > 4)) {
return(RADIOLIB_ERR_INVALID_CRC_CONFIGURATION);
}
this->crcLenGFSK = len ? len - 1 : 0;
return(setFlrcPacketParams(this->preambleLengthGFSK, this->syncWordLength, 1, 0x01, this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, this->crcLenGFSK, RADIOLIB_LR2021_MAX_PACKET_LENGTH));
}
return(RADIOLIB_ERR_WRONG_MODEM);
}
int16_t LR2021::invertIQ(bool enable) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
this->invertIQEnabled = enable;
return(setLoRaPacketParams(this->preambleLengthLoRa, this->headerType, this->implicitLen, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled));
}
int16_t LR2021::setBitRate(float br) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
RADIOLIB_CHECK_RANGE(br, 0.5f, 2000.0f, RADIOLIB_ERR_INVALID_BIT_RATE);
//! \TODO: [LR2021] implement fractional bit rate configuration
this->bitRate = br * 1000.0f;
state = setGfskModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev);
return(state);
} else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
RADIOLIB_CHECK_RANGE(br, 0.5f, 2000.0f, RADIOLIB_ERR_INVALID_BIT_RATE);
//! \TODO: [LR2021] implement fractional bit rate configuration
this->bitRate = br * 1000.0f;
//! \TODO: [LR2021] implement OOK magnitude depth configuration
state = setOokModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, RADIOLIB_LR2021_OOK_DEPTH_FULL);
return(state);
} else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
if((uint16_t)br == 260) {
this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_260;
} else if((uint16_t)br == 325) {
this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_325;
} else if((uint16_t)br == 520) {
this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_520;
} else if((uint16_t)br == 650) {
this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_650;
} else if((uint16_t)br == 1040) {
this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_1040;
} else if((uint16_t)br == 1300) {
this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_1300;
} else if((uint16_t)br == 2080) {
this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_2080;
} else if((uint16_t)br == 2600) {
this->bitRateFlrc = RADIOLIB_LR2021_FLRC_BR_2600;
} else {
return(RADIOLIB_ERR_INVALID_BIT_RATE);
}
// it is slightly weird to reuse the GFSK bitrate variable in this way
// but if GFSK gets enabled it should get reset anyway ... I think
this->bitRate = br;
// update modulation parameters
return(setFlrcModulationParams(this->bitRateFlrc, this->codingRateFlrc, this->pulseShape));
}
return(RADIOLIB_ERR_WRONG_MODEM);
}
int16_t LR2021::setFrequencyDeviation(float freqDev) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type != RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
// set frequency deviation to lowest available setting (required for digimodes)
float newFreqDev = freqDev;
if(freqDev < 0.6f) {
newFreqDev = 0.6f;
}
RADIOLIB_CHECK_RANGE(newFreqDev, 0.6f, 500.0f, RADIOLIB_ERR_INVALID_FREQUENCY_DEVIATION);
this->frequencyDev = newFreqDev * 1000.0f;
state = setGfskModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev);
return(state);
}
int16_t LR2021::setRxBandwidth(float rxBw) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(!((type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) ||
(type == RADIOLIB_LR2021_PACKET_TYPE_OOK))) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
const uint8_t rxBwLut[] = {
RADIOLIB_LR2021_GFSK_OOK_RX_BW_4_8,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_5_8,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_7_4,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_9_7,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_12_0,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_14_9,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_19_2,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_23_1,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_29_8,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_38_5,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_46_3,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_59_5,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_76_9,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_92_6,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_119_0,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_153_8,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_185_2,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_238_1,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_307_7,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_370_4,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_476_2,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_555_6,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_666_7,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_769_2,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_1111,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_2222,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_2666,
RADIOLIB_LR2021_GFSK_OOK_RX_BW_3076,
};
state = findRxBw(rxBw, rxBwLut, sizeof(rxBwLut)/sizeof(rxBwLut[0]), 3076.0f, &this->rxBandwidth);
RADIOLIB_ASSERT(state);
// update modulation parameters
if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
state = setGfskModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev);
} else {
state = setOokModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, RADIOLIB_LR2021_OOK_DEPTH_FULL);
}
return(state);
}
int16_t LR2021::setSyncWord(uint8_t* syncWord, size_t len) {
if(len > RADIOLIB_LR2021_GFSK_SYNC_WORD_LEN) {
return(RADIOLIB_ERR_INVALID_SYNC_WORD);
}
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
uint32_t sync = 0;
switch(type) {
case(RADIOLIB_LR2021_PACKET_TYPE_GFSK):
// default to MSB-first
return(setGfskSyncword(const_cast<const uint8_t*>(syncWord), len, true));
case(RADIOLIB_LR2021_PACKET_TYPE_OOK):
// default to MSB-first
return(setOokSyncword(const_cast<const uint8_t*>(syncWord), len, true));
case(RADIOLIB_LR2021_PACKET_TYPE_LORA):
// with length set to 1 and LoRa modem active, assume it is the LoRa sync word
if(len > 1) {
return(RADIOLIB_ERR_INVALID_SYNC_WORD);
}
return(setSyncWord(syncWord[0]));
case(RADIOLIB_LR2021_PACKET_TYPE_LR_FHSS):
// with length set to 4 and LR-FHSS modem active, assume it is the LR-FHSS sync word
if(len != sizeof(uint32_t)) {
return(RADIOLIB_ERR_INVALID_SYNC_WORD);
}
memcpy(&sync, syncWord, sizeof(uint32_t));
return(lrFhssSetSyncword(sync));
case(RADIOLIB_LR2021_PACKET_TYPE_FLRC):
// FLRC requires 16 or 32-bit sync word
if(!((len == 0) || (len == 2) || (len == 4))) {
return(RADIOLIB_ERR_INVALID_SYNC_WORD);
}
// update sync word length
this->syncWordLength = len;
state = setFlrcPacketParams(this->preambleLengthGFSK, this->syncWordLength, 1, 0x01, this->packetType == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, this->crcLenGFSK, RADIOLIB_LR2021_MAX_PACKET_LENGTH);
RADIOLIB_ASSERT(state);
sync |= (uint32_t)syncWord[0] << 24;
sync |= (uint32_t)syncWord[1] << 16;
sync |= (uint32_t)syncWord[2] << 8;
sync |= (uint32_t)syncWord[3];
return(setFlrcSyncWord(1, sync));
}
return(RADIOLIB_ERR_WRONG_MODEM);
}
int16_t LR2021::setDataShaping(uint8_t sh) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
// set data shaping
switch(sh) {
case RADIOLIB_SHAPING_NONE:
this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_NONE;
break;
case RADIOLIB_SHAPING_0_3:
this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_GAUSS_BT_0_3;
break;
case RADIOLIB_SHAPING_0_5:
this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_GAUSS_BT_0_5;
break;
case RADIOLIB_SHAPING_0_7:
this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_GAUSS_BT_0_7;
break;
case RADIOLIB_SHAPING_1_0:
this->pulseShape = RADIOLIB_LR2021_GFSK_BPSK_FLRC_OOK_SHAPING_GAUSS_BT_1_0;
break;
default:
return(RADIOLIB_ERR_INVALID_DATA_SHAPING);
}
// update modulation parameters
if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
return(setFlrcModulationParams(this->bitRateFlrc, this->codingRateFlrc, this->pulseShape));
} else if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
return(setGfskModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev));
} else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
return(setOokModulationParams(this->bitRate, this->pulseShape, this->rxBandwidth, RADIOLIB_LR2021_OOK_DEPTH_FULL));
}
return(RADIOLIB_ERR_WRONG_MODEM);
}
int16_t LR2021::setEncoding(uint8_t encoding) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
switch(type) {
case(RADIOLIB_LR2021_PACKET_TYPE_GFSK):
return(setWhitening(encoding == RADIOLIB_ENCODING_WHITENING));
case(RADIOLIB_LR2021_PACKET_TYPE_OOK):
switch(encoding) {
case(RADIOLIB_ENCODING_NRZ):
case(RADIOLIB_ENCODING_WHITENING):
return(setWhitening(encoding == RADIOLIB_ENCODING_WHITENING));
case(RADIOLIB_ENCODING_MANCHESTER):
state = setWhitening(false);
RADIOLIB_ASSERT(state);
this->whitening = RADIOLIB_LR2021_OOK_MANCHESTER_ON;
return(setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType, RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
case(RADIOLIB_ENCODING_MANCHESTER_INV):
state = setWhitening(false);
RADIOLIB_ASSERT(state);
this->whitening = RADIOLIB_LR2021_OOK_MANCHESTER_ON_INV;
return(setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType, RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
default:
return(RADIOLIB_ERR_INVALID_ENCODING);
}
default:
return(RADIOLIB_ERR_WRONG_MODEM);
}
return(state);
}
int16_t LR2021::fixedPacketLengthMode(uint8_t len) {
return(setPacketMode(RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, len));
}
int16_t LR2021::variablePacketLengthMode(uint8_t maxLen) {
return(setPacketMode(RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_VARIABLE_8BIT, maxLen));
}
int16_t LR2021::setWhitening(bool enabled, uint16_t initial) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
switch(type) {
case(RADIOLIB_LR2021_PACKET_TYPE_GFSK):
//! \TODO: [LR2021] Implement SX128x-compatible whitening
if(enabled) {
state = setGfskWhiteningParams(RADIOLIB_LR2021_GFSK_WHITENING_TYPE_SX126X_LR11XX, initial);
RADIOLIB_ASSERT(state);
}
this->whitening = enabled;
return(setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType, RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
case(RADIOLIB_LR2021_PACKET_TYPE_OOK):
this->whitening = enabled;
if(enabled) {
//! \TODO: [LR2021] Implement configurable index and polynomial
state = setOokWhiteningParams(12, 0x01FF, initial);
} else {
state = setOokWhiteningParams(0, 0, 0);
}
RADIOLIB_ASSERT(state);
return(setOokPacketParams(this->preambleLengthGFSK, this->addrComp, this->packetType, RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
}
return(RADIOLIB_ERR_WRONG_MODEM);
}
int16_t LR2021::setDataRate(DataRate_t dr, ModemType_t modem ) {
// get the current modem
ModemType_t currentModem;
int16_t state = this->getModem(&currentModem);
RADIOLIB_ASSERT(state);
// switch over if the requested modem is different
if(modem != RADIOLIB_MODEM_NONE && modem != currentModem) {
state = this->standby();
RADIOLIB_ASSERT(state);
state = this->setModem(modem);
RADIOLIB_ASSERT(state);
}
if(modem == RADIOLIB_MODEM_NONE) {
modem = currentModem;
}
// select interpretation based on modem
if(modem == RADIOLIB_MODEM_FSK) {
// set the bit rate
state = this->setBitRate(dr.fsk.bitRate);
RADIOLIB_ASSERT(state);
// set the frequency deviation
state = this->setFrequencyDeviation(dr.fsk.freqDev);
} else if(modem == RADIOLIB_MODEM_LORA) {
// set the spreading factor
state = this->setSpreadingFactor(dr.lora.spreadingFactor);
RADIOLIB_ASSERT(state);
// set the bandwidth
state = this->setBandwidth(dr.lora.bandwidth);
RADIOLIB_ASSERT(state);
// set the coding rate
state = this->setCodingRate(dr.lora.codingRate);
} else if(modem == RADIOLIB_MODEM_LRFHSS) {
// set the basic config
state = this->setLrFhssConfig(dr.lrFhss.bw, dr.lrFhss.cr);
RADIOLIB_ASSERT(state);
// set hopping grid
this->lrFhssGrid = dr.lrFhss.narrowGrid ? RADIOLIB_LRXXXX_LR_FHSS_GRID_STEP_NON_FCC : RADIOLIB_LRXXXX_LR_FHSS_GRID_STEP_FCC;
}
return(state);
}
int16_t LR2021::checkDataRate(DataRate_t dr, ModemType_t modem) {
int16_t state = RADIOLIB_ERR_UNKNOWN;
// retrieve modem if not supplied
if(modem == RADIOLIB_MODEM_NONE) {
state = this->getModem(&modem);
RADIOLIB_ASSERT(state);
}
// select interpretation based on modem
if(modem == RADIOLIB_MODEM_FSK) {
RADIOLIB_CHECK_RANGE(dr.fsk.bitRate, 0.5f, 2000.0f, RADIOLIB_ERR_INVALID_BIT_RATE);
RADIOLIB_CHECK_RANGE(dr.fsk.freqDev, 0.6f, 500.0f, RADIOLIB_ERR_INVALID_FREQUENCY_DEVIATION);
return(RADIOLIB_ERR_NONE);
} else if(modem == RADIOLIB_MODEM_LORA) {
RADIOLIB_CHECK_RANGE(dr.lora.spreadingFactor, 5, 12, RADIOLIB_ERR_INVALID_SPREADING_FACTOR);
RADIOLIB_CHECK_RANGE(dr.lora.bandwidth, 0.0f, 510.0f, RADIOLIB_ERR_INVALID_BANDWIDTH);
RADIOLIB_CHECK_RANGE(dr.lora.codingRate, 4, 8, RADIOLIB_ERR_INVALID_CODING_RATE);
return(RADIOLIB_ERR_NONE);
}
return(state);
}
int16_t LR2021::setLrFhssConfig(uint8_t bw, uint8_t cr, uint8_t hdrCount, uint16_t hopSeed) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type != RADIOLIB_LR2021_PACKET_TYPE_LR_FHSS) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
// check and cache all parameters
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);
this->lrFhssCr = cr;
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);
this->lrFhssBw = bw;
RADIOLIB_CHECK_RANGE(hdrCount, 1, 4, RADIOLIB_ERR_INVALID_BIT_RANGE);
this->lrFhssHdrCount = hdrCount;
RADIOLIB_CHECK_RANGE((int16_t)hopSeed, (int16_t)0x000, (int16_t)0x1FF, RADIOLIB_ERR_INVALID_DATA_SHAPING);
this->lrFhssHopSeq = hopSeed;
return(RADIOLIB_ERR_NONE);
}
int16_t LR2021::setRxBoostedGainMode(uint8_t level) {
int16_t state = this->setRxPath(this->highFreq ? RADIOLIB_LR2021_RX_PATH_HF : RADIOLIB_LR2021_RX_PATH_LF, this->highFreq ? this->gainModeHf : this->gainModeLf);
RADIOLIB_ASSERT(state);
if(this->highFreq) {
this->gainModeHf = level;
} else {
this->gainModeLf = level;
}
return(state);
}
int16_t LR2021::setPacketMode(uint8_t mode, uint8_t len) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type == RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
// set requested packet mode
state = setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, mode, len, this->crcTypeGFSK, this->whitening);
RADIOLIB_ASSERT(state);
// update cached value
this->packetType = mode;
return(state);
} else if(type == RADIOLIB_LR2021_PACKET_TYPE_OOK) {
// set requested packet mode
state = setOokPacketParams(this->preambleLengthGFSK, this->addrComp, mode, len, this->crcTypeGFSK, this->whitening);
RADIOLIB_ASSERT(state);
// update cached value
this->packetType = mode;
return(state);
} else if(type == RADIOLIB_LR2021_PACKET_TYPE_FLRC) {
state = setFlrcPacketParams(this->preambleLengthGFSK, this->syncWordLength, 1, 0x01, mode == RADIOLIB_LR2021_GFSK_OOK_PACKET_FORMAT_FIXED, this->crcLenGFSK, len);
RADIOLIB_ASSERT(state);
this->packetType = mode;
return(state);
}
return(RADIOLIB_ERR_WRONG_MODEM);
}
int16_t LR2021::setLoRaHeaderType(uint8_t hdrType, size_t len) {
uint8_t modem = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&modem);
RADIOLIB_ASSERT(state);
if(modem != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
// set requested packet mode
state = setLoRaPacketParams(this->preambleLengthLoRa, hdrType, len, this->crcTypeLoRa, this->invertIQEnabled);
RADIOLIB_ASSERT(state);
// update cached value
this->headerType = hdrType;
this->implicitLen = len;
return(state);
}
int16_t LR2021::implicitHeader(size_t len) {
return(this->setLoRaHeaderType(RADIOLIB_LR2021_LORA_HEADER_IMPLICIT, len));
}
int16_t LR2021::explicitHeader() {
return(this->setLoRaHeaderType(RADIOLIB_LR2021_LORA_HEADER_EXPLICIT));
}
int16_t LR2021::setNodeAddress(uint8_t nodeAddr) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type != RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
// enable address filtering (node only)
this->addrComp = RADIOLIB_LR2021_GFSK_OOK_ADDR_FILT_NODE;
state = setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType, RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
RADIOLIB_ASSERT(state);
// set node address
this->node = nodeAddr;
return(setGfskAddress(this->node, 0));
}
int16_t LR2021::setBroadcastAddress(uint8_t broadAddr) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type != RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
// enable address filtering (node and broadcast)
this->addrComp = RADIOLIB_LR2021_GFSK_OOK_ADDR_FILT_NODE_BROADCAST;
state = setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType, RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening);
RADIOLIB_ASSERT(state);
// set node and broadcast address
return(setGfskAddress(this->node, broadAddr));
}
int16_t LR2021::disableAddressFiltering() {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type != RADIOLIB_LR2021_PACKET_TYPE_GFSK) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
// disable address filtering
this->addrComp = RADIOLIB_LR2021_GFSK_OOK_ADDR_FILT_DISABLED;
return(setGfskPacketParams(this->preambleLengthGFSK, this->preambleDetLength, false, false, this->addrComp, this->packetType, RADIOLIB_LR2021_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening));
}
int16_t LR2021::ookDetector(uint16_t pattern, uint8_t len, uint8_t repeats, bool syncRaw, bool rising, uint8_t sofLen) {
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type != RADIOLIB_LR2021_PACKET_TYPE_OOK) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
return(setOokDetector(pattern, len - 1, repeats, syncRaw, rising, sofLen));
}
int16_t LR2021::setOokDetectionThreshold(int16_t level) {
int16_t levelRaw = 64 + level;
return(this->writeRegMemMask32(RADIOLIB_LR2021_REG_OOK_DETECTION_THRESHOLD, (0x7FUL << 20), (uint32_t)levelRaw << 20));
}
int16_t LR2021::setSideDetector(const LR2021LoRaSideDetector_t* cfg, size_t numDetectors) {
// some basic sanity checks
if((cfg == nullptr) || (numDetectors == 0)) {
return(RADIOLIB_ERR_NONE);
}
if(numDetectors > 3) {
return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
}
// if bandwidth is higher than 500 kHz, at most 2 side detectors are allowed
if((this->bandwidthKhz > 500.0f) && (numDetectors > 2)) {
return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
}
// if the primary spreading factor is 10, 11 or 12, at most 2 side detectors are allowed
if((this->spreadingFactor >= 10) && (numDetectors > 2)) {
return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
}
// condition of the primary spreading factor being the smallest/largest is not checked
// this is intentional, because it depends on whether the user wants to start Rx or CAD
uint8_t detectors[3] = { 0 };
uint8_t syncWords[3] = { 0 };
uint8_t minSf = this->spreadingFactor;
for(size_t i = 0; i < numDetectors; i++) {
// all side-detector spreading factors must be higher than the primary one
//! \todo [LR2021] implement multi-SF for CAD (main SF must be smallest!)
if(this->spreadingFactor >= cfg[i].sf) {
return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
}
// the difference between maximum and minimum spreading factor used must be less than or equal to 4
if(cfg[i].sf - minSf > 4) {
return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
}
if(cfg[i].sf < minSf) { minSf = cfg[i].sf; }
detectors[i] = cfg[i].sf << 4 | cfg[i].ldro << 2 | cfg[i].invertIQ;
syncWords[i] = cfg[i].syncWord;
}
// all spreading factors must be different
if(numDetectors >= 2) {
if(cfg[0].sf == cfg[1].sf) {
return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
}
}
if(numDetectors == 3) {
if((cfg[1].sf == cfg[2].sf) || (cfg[0].sf == cfg[2].sf)) {
return(RADIOLIB_ERR_INVALID_SIDE_DETECT);
}
}
// check active modem
uint8_t type = RADIOLIB_LR2021_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type != RADIOLIB_LR2021_PACKET_TYPE_LORA) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
state = setLoRaSideDetConfig(detectors, numDetectors);
RADIOLIB_ASSERT(state);
return(setLoRaSideDetSyncword(syncWords, numDetectors));
}
int16_t LR2021::setGain(uint8_t gain) {
if(gain > 13) {
return(RADIOLIB_ERR_INVALID_GAIN);
}
return(this->SPIcommand(RADIOLIB_LR2021_CMD_SET_AGC_GAIN_MANUAL, true, &gain, sizeof(gain)));
}
#endif