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.9–2.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; 21–22 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(¤tModem);
|
|
|
|
|
|
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
|