kopia lustrzana https://github.com/jgromes/RadioLib
Merge c3716c29f7
into 7fc83f732c
commit
1cc26f089e
|
@ -9,6 +9,7 @@ add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../../../../RadioLib" "${CMAKE_CUR
|
|||
file(GLOB_RECURSE TEST_SOURCES
|
||||
"tests/main.cpp"
|
||||
"tests/TestModule.cpp"
|
||||
"tests/TestCalculateTimeOnAir.cpp"
|
||||
)
|
||||
|
||||
# create the executable
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
#include "modules/SX126x/SX126x.h"
|
||||
#include "modules/SX127x/SX127x.h"
|
||||
#include "modules/SX128x/SX128x.h"
|
||||
#include "modules/LR11x0/LR11x0.h"
|
||||
|
||||
// Testable wrappers
|
||||
class SX127xTestable : public SX127x {
|
||||
public:
|
||||
explicit SX127xTestable(Module* mod) : SX127x(mod) {}
|
||||
void reset () {}
|
||||
void errataFix(bool rx) {(void)rx;}
|
||||
};
|
||||
|
||||
// --- Config structure ---
|
||||
struct RadioConfig {
|
||||
std::string name; // Radio name
|
||||
ModemType_t modem;
|
||||
DataRate_t dr;
|
||||
PacketConfig_t pc;
|
||||
std::vector<size_t> payload_len;
|
||||
std::vector<RadioLibTime_t> expected_toa; // Expected time on air in microseconds from Semtech calculators
|
||||
};
|
||||
|
||||
// --- Test configurations with golden values ---
|
||||
std::vector<RadioConfig> allConfigs = {
|
||||
{ "SX126x", RADIOLIB_MODEM_LORA, {.lora={7,125,5}}, {.lora={8,false,true,true}}, {1,10,50,255}, {30976,46336,128256,548096} }, // 30.97, 46.33, 128.25, 548.09
|
||||
{ "SX126x", RADIOLIB_MODEM_LORA, {.lora={11,250,8}}, {.lora={16,true,false,false}}, {5,15,100,200}, {296960,362496,1411072,2590720} }, // 296.96, 362.49, 1410, 2590
|
||||
{ "SX126x", RADIOLIB_MODEM_FSK, {.fsk={100,10}}, {.fsk={16,16,2}}, {1,16,64,200}, {560,1760,5600,16480} },
|
||||
{ "SX126x", RADIOLIB_MODEM_LRFHSS,{.lrFhss={RADIOLIB_LR11X0_LR_FHSS_BW_386_72,RADIOLIB_SX126X_LR_FHSS_CR_2_3,false}}, {.lrFhss={2}}, {1,20,100}, {3784697,4259832,6324212} },
|
||||
|
||||
{ "SX127x", RADIOLIB_MODEM_LORA, {.lora={6,125,6}}, {.lora={8,false,true,false}}, {7,23,98,156}, {23000,39000,115000,174000} }, // 20.61, 39.04, 115.84, 174.21
|
||||
{ "SX127x", RADIOLIB_MODEM_LORA, {.lora={8,250,8}}, {.lora={32,true,true,false}}, {10,20,80,160}, {70000,87000,210000,373000} }, // 69.89, 86.27, 209.15, 372.99
|
||||
{ "SX127x", RADIOLIB_MODEM_FSK, {.fsk={100,5}}, {.fsk={16,16,3}}, {1,16,32,61}, {640,1840,3120,5440} },
|
||||
|
||||
{ "SX128x", RADIOLIB_MODEM_LORA, {.lora={5,400,5}}, {.lora={8,false,true,false}}, {1,50,200}, {2580,10179,34180} }, // 2.54, 10.02, 33.65
|
||||
{ "SX128x", RADIOLIB_MODEM_LORA, {.lora={12,800,7}}, {.lora={16,false,true,true}}, {10,100,250}, {216319,861440,1936640} }, // 212.99, 848.19, 1910
|
||||
{ "SX128x", RADIOLIB_MODEM_FSK, {.fsk={250,100}}, {.fsk={16,16,2}}, {1,32,64,128}, {224,1216,2240,4288} },
|
||||
|
||||
{ "LR11x0", RADIOLIB_MODEM_LORA, {.lora={10,250,5}}, {.lora={8,false,true,true}}, {1,20,100}, {103424,205824,615424} }, // 103.42, 205.82, 615.42
|
||||
{ "LR11x0", RADIOLIB_MODEM_LORA, {.lora={11,500,6}}, {.lora={32,true,false,false}}, {10,25,200}, {205824,279552,1065984} }, // 205.82, 279.55, 1070
|
||||
{ "LR11x0", RADIOLIB_MODEM_FSK, {.fsk={200,50}}, {.fsk={16,32,2}}, {1,32,64,200}, {360,1600,2880,8320} },
|
||||
{ "LR11x0", RADIOLIB_MODEM_LRFHSS,{.lrFhss={RADIOLIB_LR11X0_LR_FHSS_BW_136_72,RADIOLIB_LR11X0_LR_FHSS_CR_1_3,true}}, {.lrFhss={1}}, {1,10,50}, {1949692,2392059,4456440} },
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(suite_TimeOnAir)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(TimeOnAir_AllRadios) {
|
||||
for (const auto& cfg : allConfigs) {
|
||||
BOOST_TEST_MESSAGE("--- Test calculateTimeOnAir " << cfg.name << ", modem=" << cfg.modem << " ---");
|
||||
|
||||
for (size_t i = 0; i < cfg.payload_len.size(); i++) {
|
||||
auto len = cfg.payload_len[i];
|
||||
RadioLibTime_t toa = 0;
|
||||
|
||||
if (cfg.name == "SX126x") {
|
||||
SX126x dummy(nullptr);
|
||||
toa = dummy.calculateTimeOnAir(cfg.modem, cfg.dr, cfg.pc, len);
|
||||
} else if (cfg.name == "SX127x") {
|
||||
SX127xTestable dummy(nullptr);
|
||||
toa = dummy.calculateTimeOnAir(cfg.modem, cfg.dr, cfg.pc, len);
|
||||
} else if (cfg.name == "SX128x") {
|
||||
SX128x dummy(nullptr);
|
||||
toa = dummy.calculateTimeOnAir(cfg.modem, cfg.dr, cfg.pc, len);
|
||||
} else if (cfg.name == "LR11x0") {
|
||||
LR11x0 dummy(nullptr);
|
||||
toa = dummy.calculateTimeOnAir(cfg.modem, cfg.dr, cfg.pc, len);
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(toa, cfg.expected_toa[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -230,6 +230,7 @@ beginFSK4 KEYWORD2
|
|||
setTCXO KEYWORD2
|
||||
setDio2AsRfSwitch KEYWORD2
|
||||
getTimeOnAir KEYWORD2
|
||||
calculateTimeOnAir KEYWORD2
|
||||
implicitHeader KEYWORD2
|
||||
explicitHeader KEYWORD2
|
||||
setSyncBits KEYWORD2
|
||||
|
|
|
@ -1203,71 +1203,44 @@ size_t LR11x0::getPacketLength(bool update, uint8_t* offset) {
|
|||
return((size_t)len);
|
||||
}
|
||||
|
||||
RadioLibTime_t LR11x0::getTimeOnAir(size_t len) {
|
||||
RadioLibTime_t LR11x0::calculateTimeOnAir(ModemType_t modem, DataRate_t dr, PacketConfig_t pc, size_t len) {
|
||||
// check active modem
|
||||
uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
|
||||
(void)getPacketType(&type);
|
||||
if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
|
||||
// calculate number of symbols
|
||||
float N_symbol = 0;
|
||||
if(this->codingRate <= RADIOLIB_LR11X0_LORA_CR_4_8_SHORT) {
|
||||
// legacy coding rate - nice and simple
|
||||
|
||||
// get SF coefficients
|
||||
float coeff1 = 0;
|
||||
int16_t coeff2 = 0;
|
||||
int16_t coeff3 = 0;
|
||||
if(this->spreadingFactor < 7) {
|
||||
// SF5, SF6
|
||||
coeff1 = 6.25;
|
||||
coeff2 = 4*this->spreadingFactor;
|
||||
coeff3 = 4*this->spreadingFactor;
|
||||
} else if(this->spreadingFactor < 11) {
|
||||
// SF7. SF8, SF9, SF10
|
||||
coeff1 = 4.25;
|
||||
coeff2 = 4*this->spreadingFactor + 8;
|
||||
coeff3 = 4*this->spreadingFactor;
|
||||
} else {
|
||||
// SF11, SF12
|
||||
coeff1 = 4.25;
|
||||
coeff2 = 4*this->spreadingFactor + 8;
|
||||
coeff3 = 4*(this->spreadingFactor - 2);
|
||||
}
|
||||
|
||||
// get CRC length
|
||||
int16_t N_bitCRC = 16;
|
||||
if(this->crcTypeLoRa == RADIOLIB_LR11X0_LORA_CRC_DISABLED) {
|
||||
N_bitCRC = 0;
|
||||
}
|
||||
|
||||
// get header length
|
||||
int16_t N_symbolHeader = 20;
|
||||
if(this->headerType == RADIOLIB_LR11X0_LORA_HEADER_IMPLICIT) {
|
||||
N_symbolHeader = 0;
|
||||
}
|
||||
|
||||
// calculate number of LoRa preamble symbols
|
||||
uint32_t N_symbolPreamble = (this->preambleLengthLoRa & 0x0F) * (uint32_t(1) << ((this->preambleLengthLoRa & 0xF0) >> 4));
|
||||
|
||||
// calculate the number of symbols
|
||||
N_symbol = (float)N_symbolPreamble + coeff1 + 8.0f + ceilf((float)RADIOLIB_MAX((int16_t)(8 * len + N_bitCRC - coeff2 + N_symbolHeader), (int16_t)0) / (float)coeff3) * (float)(this->codingRate + 4);
|
||||
|
||||
} else {
|
||||
// long interleaving - abandon hope all ye who enter here
|
||||
/// \todo implement this mess - SX1280 datasheet v3.0 section 7.4.4.2
|
||||
|
||||
if (modem == ModemType_t::RADIOLIB_MODEM_LORA) {
|
||||
uint32_t symbolLength_us = ((uint32_t)(1000 * 10) << dr.lora.spreadingFactor) / (dr.lora.bandwidth * 10) ;
|
||||
uint8_t sfCoeff1_x4 = 17; // (4.25 * 4)
|
||||
uint8_t sfCoeff2 = 8;
|
||||
if(dr.lora.spreadingFactor == 5 || dr.lora.spreadingFactor == 6) {
|
||||
sfCoeff1_x4 = 25; // 6.25 * 4
|
||||
sfCoeff2 = 0;
|
||||
}
|
||||
uint8_t sfDivisor = 4*dr.lora.spreadingFactor;
|
||||
if(pc.lora.ldrOptimize) {
|
||||
sfDivisor = 4*(dr.lora.spreadingFactor - 2);
|
||||
}
|
||||
const int8_t bitsPerCrc = 16;
|
||||
const int8_t N_symbol_header = pc.lora.implicitHeader ? 0 : 20;
|
||||
|
||||
// numerator of equation in section 6.1.4 of SX1268 datasheet v1.1 (might not actually be bitcount, but it has len * 8)
|
||||
int16_t bitCount = (int16_t) 8 * len + pc.lora.crcEnabled * bitsPerCrc - 4 * dr.lora.spreadingFactor + sfCoeff2 + N_symbol_header;
|
||||
if(bitCount < 0) {
|
||||
bitCount = 0;
|
||||
}
|
||||
// add (sfDivisor) - 1 to the numerator to give integer CEIL(...)
|
||||
uint16_t nPreCodedSymbols = (bitCount + (sfDivisor - 1)) / (sfDivisor);
|
||||
|
||||
// preamble can be 65k, therefore nSymbol_x4 needs to be 32 bit
|
||||
uint32_t nSymbol_x4 = (pc.lora.preambleLength + 8) * 4 + sfCoeff1_x4 + nPreCodedSymbols * dr.lora.codingRate * 4;
|
||||
|
||||
// get time-on-air in us
|
||||
return(((uint32_t(1) << this->spreadingFactor) / this->bandwidthKhz) * N_symbol * 1000.0f);
|
||||
return((symbolLength_us * nSymbol_x4) / 4);
|
||||
|
||||
} else if(type == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) {
|
||||
return(((uint32_t)len * 8 * 1000000UL) / this->bitRate);
|
||||
} else if(modem == ModemType_t::RADIOLIB_MODEM_FSK) {
|
||||
return((((float)(pc.fsk.crcLength * 8) + pc.fsk.syncWordLength + pc.fsk.preambleLength + (uint32_t)len * 8) / (dr.fsk.bitRate / 1000.0f)));
|
||||
|
||||
} else if(type == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) {
|
||||
} else if(modem == ModemType_t::RADIOLIB_MODEM_LRFHSS) {
|
||||
// calculate the number of bits based on coding rate
|
||||
uint16_t N_bits;
|
||||
switch(this->lrFhssCr) {
|
||||
switch(dr.lrFhss.cr) {
|
||||
case RADIOLIB_LR11X0_LR_FHSS_CR_5_6:
|
||||
N_bits = ((len * 6) + 4) / 5; // this is from the official LR11xx driver, but why the extra +4?
|
||||
break;
|
||||
|
@ -1292,14 +1265,74 @@ RadioLibTime_t LR11x0::getTimeOnAir(size_t len) {
|
|||
}
|
||||
|
||||
// add header bits
|
||||
uint16_t N_totalBits = (RADIOLIB_LR11X0_LR_FHSS_HEADER_BITS * this->lrFhssHdrCount) + N_payBits;
|
||||
uint16_t N_totalBits = (RADIOLIB_LR11X0_LR_FHSS_HEADER_BITS * pc.lrFhss.hdrCount) + N_payBits;
|
||||
return(((uint32_t)N_totalBits * 8 * 1000000UL) / RADIOLIB_LR11X0_LR_FHSS_BIT_RATE);
|
||||
|
||||
} else {
|
||||
return(RADIOLIB_ERR_WRONG_MODEM);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
RadioLibTime_t LR11x0::getTimeOnAir(size_t len) {
|
||||
ModemType_t modem;
|
||||
int32_t state = this->getModem(&modem);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
DataRate_t dr = {};
|
||||
PacketConfig_t pc = {};
|
||||
switch(modem) {
|
||||
case ModemType_t::RADIOLIB_MODEM_LORA: {
|
||||
uint8_t cr = this->codingRate;
|
||||
// We assume same calculation for short and long interleaving, so map CR values 0-4 and 5-7 to the same values
|
||||
if (cr < 5) {
|
||||
cr = cr + 4;
|
||||
} else if (cr == 7) {
|
||||
cr = cr + 1;
|
||||
}
|
||||
|
||||
dr.lora.spreadingFactor = this->spreadingFactor;
|
||||
dr.lora.bandwidth = this->bandwidthKhz;
|
||||
dr.lora.codingRate = cr;
|
||||
|
||||
pc.lora.preambleLength = this->preambleLengthLoRa;
|
||||
pc.lora.implicitHeader = (this->headerType == RADIOLIB_LR11X0_LORA_HEADER_IMPLICIT) ? true : false;
|
||||
pc.lora.crcEnabled = (this->crcTypeLoRa == RADIOLIB_LR11X0_LORA_CRC_ENABLED) ? true : false;
|
||||
pc.lora.ldrOptimize = (bool)this->ldrOptimize;
|
||||
break;
|
||||
}
|
||||
case ModemType_t::RADIOLIB_MODEM_FSK: {
|
||||
dr.fsk.bitRate = (float)this->bitRate / 1000.0f;
|
||||
dr.fsk.freqDev = (float)this->frequencyDev;
|
||||
|
||||
uint8_t crcLen = 0;
|
||||
if(this->crcTypeGFSK == RADIOLIB_LR11X0_GFSK_CRC_1_BYTE || this->crcTypeGFSK == RADIOLIB_LR11X0_GFSK_CRC_1_BYTE_INV) {
|
||||
crcLen = 1;
|
||||
} else if(this->crcTypeGFSK == RADIOLIB_LR11X0_GFSK_CRC_2_BYTE || this->crcTypeGFSK == RADIOLIB_LR11X0_GFSK_CRC_2_BYTE_INV) {
|
||||
crcLen = 2;
|
||||
}
|
||||
|
||||
pc.fsk.preambleLength = this->preambleLengthGFSK;
|
||||
pc.fsk.syncWordLength = this->syncWordLength;
|
||||
pc.fsk.crcLength = crcLen;
|
||||
break;
|
||||
}
|
||||
case ModemType_t::RADIOLIB_MODEM_LRFHSS: {
|
||||
dr.lrFhss.bw = this->lrFhssBw;
|
||||
dr.lrFhss.cr = this->lrFhssCr;
|
||||
dr.lrFhss.narrowGrid = (this->lrFhssGrid == RADIOLIB_LR11X0_LR_FHSS_GRID_STEP_NON_FCC) ? true : false;
|
||||
|
||||
pc.lrFhss.hdrCount = this->lrFhssHdrCount;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return(RADIOLIB_ERR_WRONG_MODEM);
|
||||
}
|
||||
|
||||
return(this->calculateTimeOnAir(modem, dr, pc, len));
|
||||
}
|
||||
|
||||
RadioLibTime_t LR11x0::calculateRxTimeout(RadioLibTime_t timeoutUs) {
|
||||
// the timeout value is given in units of 30.52 microseconds
|
||||
// the calling function should provide some extra width, as this number of units is truncated to integer
|
||||
|
|
|
@ -1372,6 +1372,16 @@ class LR11x0: public PhysicalLayer {
|
|||
*/
|
||||
int16_t getLoRaRxHeaderInfo(uint8_t* cr, bool* hasCRC);
|
||||
|
||||
/*!
|
||||
\brief Calculate the expected time-on-air for a given modem, data rate, packet configuration and payload size.
|
||||
\param modem Modem type.
|
||||
\param dr Data rate.
|
||||
\param pc Packet config.
|
||||
\param len Payload length in bytes.
|
||||
\returns Expected time-on-air in microseconds.
|
||||
*/
|
||||
RadioLibTime_t calculateTimeOnAir(ModemType_t modem, DataRate_t dr, PacketConfig_t pc, size_t len);
|
||||
|
||||
/*!
|
||||
\brief Get expected time-on-air for a given size of payload
|
||||
\param len Payload length in bytes.
|
||||
|
|
|
@ -1348,77 +1348,125 @@ int16_t SX126x::variablePacketLengthMode(uint8_t maxLen) {
|
|||
return(setPacketMode(RADIOLIB_SX126X_GFSK_PACKET_VARIABLE, maxLen));
|
||||
}
|
||||
|
||||
RadioLibTime_t SX126x::getTimeOnAir(size_t len) {
|
||||
RadioLibTime_t SX126x::calculateTimeOnAir(ModemType_t modem, DataRate_t dr, PacketConfig_t pc, size_t len) {
|
||||
// everything is in microseconds to allow integer arithmetic
|
||||
// some constants have .25, these are multiplied by 4, and have _x4 postfix to indicate that fact
|
||||
uint8_t modem = getPacketType();
|
||||
if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
||||
uint32_t symbolLength_us = ((uint32_t)(1000 * 10) << this->spreadingFactor) / (this->bandwidthKhz * 10) ;
|
||||
uint8_t sfCoeff1_x4 = 17; // (4.25 * 4)
|
||||
uint8_t sfCoeff2 = 8;
|
||||
if(this->spreadingFactor == 5 || this->spreadingFactor == 6) {
|
||||
sfCoeff1_x4 = 25; // 6.25 * 4
|
||||
sfCoeff2 = 0;
|
||||
switch (modem) {
|
||||
case RADIOLIB_MODEM_LORA: {
|
||||
uint32_t symbolLength_us = ((uint32_t)(1000 * 10) << dr.lora.spreadingFactor) / (dr.lora.bandwidth * 10) ;
|
||||
uint8_t sfCoeff1_x4 = 17; // (4.25 * 4)
|
||||
uint8_t sfCoeff2 = 8;
|
||||
if(dr.lora.spreadingFactor == 5 || dr.lora.spreadingFactor == 6) {
|
||||
sfCoeff1_x4 = 25; // 6.25 * 4
|
||||
sfCoeff2 = 0;
|
||||
}
|
||||
uint8_t sfDivisor = 4*dr.lora.spreadingFactor;
|
||||
if(pc.lora.ldrOptimize) {
|
||||
sfDivisor = 4*(dr.lora.spreadingFactor - 2);
|
||||
}
|
||||
const int8_t bitsPerCrc = 16;
|
||||
const int8_t N_symbol_header = pc.lora.implicitHeader ? 0 : 20;
|
||||
|
||||
// numerator of equation in section 6.1.4 of SX1268 datasheet v1.1 (might not actually be bitcount, but it has len * 8)
|
||||
int16_t bitCount = (int16_t) 8 * len + pc.lora.crcEnabled * bitsPerCrc - 4 * dr.lora.spreadingFactor + sfCoeff2 + N_symbol_header;
|
||||
if(bitCount < 0) {
|
||||
bitCount = 0;
|
||||
}
|
||||
// add (sfDivisor) - 1 to the numerator to give integer CEIL(...)
|
||||
uint16_t nPreCodedSymbols = (bitCount + (sfDivisor - 1)) / (sfDivisor);
|
||||
|
||||
// preamble can be 65k, therefore nSymbol_x4 needs to be 32 bit
|
||||
uint32_t nSymbol_x4 = (pc.lora.preambleLength + 8) * 4 + sfCoeff1_x4 + nPreCodedSymbols * dr.lora.codingRate * 4;
|
||||
|
||||
return((symbolLength_us * nSymbol_x4) / 4);
|
||||
}
|
||||
uint8_t sfDivisor = 4*this->spreadingFactor;
|
||||
if(symbolLength_us >= 16000) {
|
||||
sfDivisor = 4*(this->spreadingFactor - 2);
|
||||
case RADIOLIB_MODEM_FSK: {
|
||||
return((((float)(pc.fsk.crcLength * 8) + pc.fsk.syncWordLength + pc.fsk.preambleLength + (uint32_t)len * 8) / (dr.fsk.bitRate / 1000.0f)));
|
||||
}
|
||||
const int8_t bitsPerCrc = 16;
|
||||
const int8_t N_symbol_header = this->headerType == RADIOLIB_SX126X_LORA_HEADER_EXPLICIT ? 20 : 0;
|
||||
case RADIOLIB_MODEM_LRFHSS: {
|
||||
// calculate the number of bits based on coding rate
|
||||
uint16_t N_bits;
|
||||
switch(dr.lrFhss.cr) {
|
||||
case RADIOLIB_SX126X_LR_FHSS_CR_5_6:
|
||||
N_bits = ((len * 6) + 4) / 5; // this is from the official LR11xx driver, but why the extra +4?
|
||||
break;
|
||||
case RADIOLIB_SX126X_LR_FHSS_CR_2_3:
|
||||
N_bits = (len * 3) / 2;
|
||||
break;
|
||||
case RADIOLIB_SX126X_LR_FHSS_CR_1_2:
|
||||
N_bits = len * 2;
|
||||
break;
|
||||
case RADIOLIB_SX126X_LR_FHSS_CR_1_3:
|
||||
N_bits = len * 3;
|
||||
break;
|
||||
default:
|
||||
return(RADIOLIB_ERR_INVALID_CODING_RATE);
|
||||
}
|
||||
|
||||
// numerator of equation in section 6.1.4 of SX1268 datasheet v1.1 (might not actually be bitcount, but it has len * 8)
|
||||
int16_t bitCount = (int16_t) 8 * len + this->crcTypeLoRa * bitsPerCrc - 4 * this->spreadingFactor + sfCoeff2 + N_symbol_header;
|
||||
if(bitCount < 0) {
|
||||
bitCount = 0;
|
||||
// calculate number of bits when accounting for unaligned last block
|
||||
uint16_t N_payBits = (N_bits / RADIOLIB_SX126X_LR_FHSS_FRAG_BITS) * RADIOLIB_SX126X_LR_FHSS_BLOCK_BITS;
|
||||
uint16_t N_lastBlockBits = N_bits % RADIOLIB_SX126X_LR_FHSS_FRAG_BITS;
|
||||
if(N_lastBlockBits) {
|
||||
N_payBits += N_lastBlockBits + 2;
|
||||
}
|
||||
|
||||
// add header bits
|
||||
uint16_t N_totalBits = (RADIOLIB_SX126X_LR_FHSS_HEADER_BITS * pc.lrFhss.hdrCount) + N_payBits;
|
||||
return(((uint32_t)N_totalBits * 8 * 1000000UL) / 488.28215f);
|
||||
}
|
||||
// add (sfDivisor) - 1 to the numerator to give integer CEIL(...)
|
||||
uint16_t nPreCodedSymbols = (bitCount + (sfDivisor - 1)) / (sfDivisor);
|
||||
|
||||
// preamble can be 65k, therefore nSymbol_x4 needs to be 32 bit
|
||||
uint32_t nSymbol_x4 = (this->preambleLengthLoRa + 8) * 4 + sfCoeff1_x4 + nPreCodedSymbols * (this->codingRate + 4) * 4;
|
||||
|
||||
return((symbolLength_us * nSymbol_x4) / 4);
|
||||
|
||||
} else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
||||
return(((uint32_t)len * 8 * this->bitRate) / (RADIOLIB_SX126X_CRYSTAL_FREQ * 32));
|
||||
|
||||
} else if(modem == RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) {
|
||||
// calculate the number of bits based on coding rate
|
||||
uint16_t N_bits;
|
||||
switch(this->lrFhssCr) {
|
||||
case RADIOLIB_SX126X_LR_FHSS_CR_5_6:
|
||||
N_bits = ((len * 6) + 4) / 5; // this is from the official LR11xx driver, but why the extra +4?
|
||||
break;
|
||||
case RADIOLIB_SX126X_LR_FHSS_CR_2_3:
|
||||
N_bits = (len * 3) / 2;
|
||||
break;
|
||||
case RADIOLIB_SX126X_LR_FHSS_CR_1_2:
|
||||
N_bits = len * 2;
|
||||
break;
|
||||
case RADIOLIB_SX126X_LR_FHSS_CR_1_3:
|
||||
N_bits = len * 3;
|
||||
break;
|
||||
default:
|
||||
return(RADIOLIB_ERR_INVALID_CODING_RATE);
|
||||
}
|
||||
|
||||
// calculate number of bits when accounting for unaligned last block
|
||||
uint16_t N_payBits = (N_bits / RADIOLIB_SX126X_LR_FHSS_FRAG_BITS) * RADIOLIB_SX126X_LR_FHSS_BLOCK_BITS;
|
||||
uint16_t N_lastBlockBits = N_bits % RADIOLIB_SX126X_LR_FHSS_FRAG_BITS;
|
||||
if(N_lastBlockBits) {
|
||||
N_payBits += N_lastBlockBits + 2;
|
||||
}
|
||||
|
||||
// add header bits
|
||||
uint16_t N_totalBits = (RADIOLIB_SX126X_LR_FHSS_HEADER_BITS * this->lrFhssHdrCount) + N_payBits;
|
||||
return(((uint32_t)N_totalBits * 8 * 1000000UL) / 488.28215f);
|
||||
|
||||
default:
|
||||
return(RADIOLIB_ERR_WRONG_MODEM);
|
||||
}
|
||||
|
||||
return(RADIOLIB_ERR_UNKNOWN);
|
||||
}
|
||||
|
||||
RadioLibTime_t SX126x::getTimeOnAir(size_t len) {
|
||||
uint8_t type = getPacketType();
|
||||
ModemType_t modem = RADIOLIB_MODEM_LORA;
|
||||
DataRate_t dataRate = {};
|
||||
PacketConfig_t packetConfig = {};
|
||||
|
||||
if(type == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
||||
dataRate.lora.spreadingFactor = this->spreadingFactor;
|
||||
dataRate.lora.bandwidth = this->bandwidthKhz;
|
||||
dataRate.lora.codingRate = (uint8_t)(this->codingRate + 4);
|
||||
|
||||
packetConfig.lora.preambleLength = this->preambleLengthLoRa;
|
||||
packetConfig.lora.crcEnabled = (bool)this->crcTypeLoRa;
|
||||
packetConfig.lora.implicitHeader = this->headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT;
|
||||
packetConfig.lora.ldrOptimize = (bool)this->ldrOptimize;
|
||||
} else if(type == RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
||||
modem = RADIOLIB_MODEM_FSK;
|
||||
|
||||
dataRate.fsk.bitRate = RADIOLIB_SX126X_CRYSTAL_FREQ * 32.0f * 1000.0f / (float)this->bitRate;
|
||||
dataRate.fsk.freqDev = (float)this->frequencyDev;
|
||||
|
||||
uint8_t crcLen = 0;
|
||||
if(this->crcTypeFSK == RADIOLIB_SX126X_GFSK_CRC_1_BYTE || this->crcTypeFSK == RADIOLIB_SX126X_GFSK_CRC_1_BYTE_INV) {
|
||||
crcLen = 1;
|
||||
} else if(this->crcTypeFSK == RADIOLIB_SX126X_GFSK_CRC_2_BYTE || this->crcTypeFSK == RADIOLIB_SX126X_GFSK_CRC_2_BYTE_INV) {
|
||||
crcLen = 2;
|
||||
}
|
||||
|
||||
packetConfig.fsk.preambleLength = this->preambleLengthFSK;
|
||||
packetConfig.fsk.syncWordLength = this->syncWordLength;
|
||||
packetConfig.fsk.crcLength = crcLen;
|
||||
} else if(type == RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS) {
|
||||
modem = RADIOLIB_MODEM_LRFHSS;
|
||||
|
||||
dataRate.lrFhss.bw = this->lrFhssBw;
|
||||
dataRate.lrFhss.cr = this->lrFhssCr;
|
||||
dataRate.lrFhss.narrowGrid = this->lrFhssGridNonFcc;
|
||||
|
||||
packetConfig.lrFhss.hdrCount = this->lrFhssHdrCount;
|
||||
} else {
|
||||
return(RADIOLIB_ERR_WRONG_MODEM);
|
||||
}
|
||||
|
||||
return(calculateTimeOnAir(modem, dataRate, packetConfig, len));
|
||||
}
|
||||
|
||||
RadioLibTime_t SX126x::calculateRxTimeout(RadioLibTime_t timeoutUs) {
|
||||
// the timeout value is given in units of 15.625 microseconds
|
||||
// the calling function should provide some extra width, as this number of units is truncated to integer
|
||||
|
|
|
@ -1008,6 +1008,16 @@ class SX126x: public PhysicalLayer {
|
|||
*/
|
||||
int16_t variablePacketLengthMode(uint8_t maxLen = RADIOLIB_SX126X_MAX_PACKET_LENGTH);
|
||||
|
||||
/*!
|
||||
\brief Calculate the expected time-on-air for a given modem, data rate, packet configuration and payload size.
|
||||
\param modem Modem type.
|
||||
\param dr Data rate.
|
||||
\param pc Packet config.
|
||||
\param len Payload length in bytes.
|
||||
\returns Expected time-on-air in microseconds.
|
||||
*/
|
||||
RadioLibTime_t calculateTimeOnAir(ModemType_t modem, DataRate_t dr, PacketConfig_t pc, size_t len);
|
||||
|
||||
/*!
|
||||
\brief Get expected time-on-air for a given size of payload
|
||||
\param len Payload length in bytes.
|
||||
|
|
|
@ -122,8 +122,10 @@ int16_t SX1272::setBandwidth(float bw) {
|
|||
float symbolLength = (float)(uint32_t(1) << SX127x::spreadingFactor) / (float)SX127x::bandwidth;
|
||||
Module* mod = this->getMod();
|
||||
if(symbolLength >= 16.0f) {
|
||||
this->ldroEnabled = true;
|
||||
state = mod->SPIsetRegValue(RADIOLIB_SX127X_REG_MODEM_CONFIG_1, RADIOLIB_SX1272_LOW_DATA_RATE_OPT_ON, 0, 0);
|
||||
} else {
|
||||
this->ldroEnabled = false;
|
||||
state = mod->SPIsetRegValue(RADIOLIB_SX127X_REG_MODEM_CONFIG_1, RADIOLIB_SX1272_LOW_DATA_RATE_OPT_OFF, 0, 0);
|
||||
}
|
||||
}
|
||||
|
@ -483,6 +485,7 @@ int16_t SX1272::forceLDRO(bool enable) {
|
|||
}
|
||||
|
||||
this->ldroAuto = false;
|
||||
this->ldroEnabled = enable;
|
||||
Module* mod = this->getMod();
|
||||
if(enable) {
|
||||
return(mod->SPIsetRegValue(RADIOLIB_SX127X_REG_MODEM_CONFIG_1, RADIOLIB_SX1272_LOW_DATA_RATE_OPT_ON, 0, 0));
|
||||
|
|
|
@ -324,12 +324,6 @@ class SX1272: public SX127x {
|
|||
int16_t configFSK() override;
|
||||
void errataFix(bool rx) override;
|
||||
|
||||
#if !RADIOLIB_GODMODE
|
||||
private:
|
||||
#endif
|
||||
bool ldroAuto = true;
|
||||
bool ldroEnabled = false;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -136,8 +136,10 @@ int16_t SX1278::setBandwidth(float bw) {
|
|||
float symbolLength = (float)(uint32_t(1) << SX127x::spreadingFactor) / (float)SX127x::bandwidth;
|
||||
Module* mod = this->getMod();
|
||||
if(symbolLength >= 16.0f) {
|
||||
this->ldroEnabled = true;
|
||||
state = mod->SPIsetRegValue(RADIOLIB_SX1278_REG_MODEM_CONFIG_3, RADIOLIB_SX1278_LOW_DATA_RATE_OPT_ON, 3, 3);
|
||||
} else {
|
||||
this->ldroEnabled = false;
|
||||
state = mod->SPIsetRegValue(RADIOLIB_SX1278_REG_MODEM_CONFIG_3, RADIOLIB_SX1278_LOW_DATA_RATE_OPT_OFF, 3, 3);
|
||||
}
|
||||
}
|
||||
|
@ -522,6 +524,7 @@ int16_t SX1278::forceLDRO(bool enable) {
|
|||
|
||||
Module* mod = this->getMod();
|
||||
this->ldroAuto = false;
|
||||
this->ldroEnabled = enable;
|
||||
if(enable) {
|
||||
return(mod->SPIsetRegValue(RADIOLIB_SX1278_REG_MODEM_CONFIG_3, RADIOLIB_SX1278_LOW_DATA_RATE_OPT_ON, 3, 3));
|
||||
} else {
|
||||
|
|
|
@ -336,12 +336,6 @@ class SX1278: public SX127x {
|
|||
int16_t configFSK() override;
|
||||
void errataFix(bool rx) override;
|
||||
|
||||
#if !RADIOLIB_GODMODE
|
||||
private:
|
||||
#endif
|
||||
bool ldroAuto = true;
|
||||
bool ldroEnabled = false;
|
||||
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
|
@ -833,6 +833,8 @@ int16_t SX127x::setFrequencyDeviation(float freqDev) {
|
|||
return(RADIOLIB_ERR_INVALID_FREQUENCY_DEVIATION);
|
||||
}
|
||||
|
||||
this->frequencyDev = newFreqDev;
|
||||
|
||||
// set mode to STANDBY
|
||||
int16_t state = setMode(RADIOLIB_SX127X_STANDBY);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
@ -1127,68 +1129,99 @@ int16_t SX127x::variablePacketLengthMode(uint8_t maxLen) {
|
|||
return(SX127x::setPacketMode(RADIOLIB_SX127X_PACKET_VARIABLE, maxLen));
|
||||
}
|
||||
|
||||
float SX127x::getNumSymbols(size_t len) {
|
||||
// get symbol length in us
|
||||
float symbolLength = (float) (uint32_t(1) << this->spreadingFactor) / (float) this->bandwidth;
|
||||
|
||||
float SX127x::getNumSymbols(size_t len, DataRate_t dr, PacketConfig_t pc) {
|
||||
// get Low Data Rate optimization flag
|
||||
float de = 0;
|
||||
if (symbolLength >= 16.0f) {
|
||||
de = 1;
|
||||
}
|
||||
float de = pc.lora.ldrOptimize ? 1.0f : 0.0f;
|
||||
|
||||
// get explicit/implicit header enabled flag
|
||||
float ih = (float) this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_MODEM_CONFIG_1, 0, 0);
|
||||
float ih = (float) pc.lora.implicitHeader;
|
||||
|
||||
// get CRC enabled flag
|
||||
float crc = (float) (this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_MODEM_CONFIG_2, 2, 2) >> 2);
|
||||
float crc = (float) pc.lora.crcEnabled;
|
||||
|
||||
// get number of preamble symbols
|
||||
float n_pre = (float) ((this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_MSB) << 8) | this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_LSB));
|
||||
float n_pre = (float) pc.lora.preambleLength;
|
||||
|
||||
// get number of payload symbols
|
||||
float n_pay = 8.0f + RADIOLIB_MAX(ceilf((8.0f * (float) len - 4.0f * (float) this->spreadingFactor + 28.0f + 16.0f * crc - 20.0f * ih) / (4.0f * (float) this->spreadingFactor - 8.0f * de)) * (float) this->codingRate, 0.0f);
|
||||
float n_pay = 8.0f + RADIOLIB_MAX(ceilf((8.0f * (float) len - 4.0f * (float) dr.lora.spreadingFactor + 28.0f + 16.0f * crc - 20.0f * ih) / (4.0f * (float) dr.lora.spreadingFactor - 8.0f * de)) * (float) dr.lora.codingRate, 0.0f);
|
||||
|
||||
// add 4.25 symbols for the sync
|
||||
return(n_pre + n_pay + 4.25f);
|
||||
}
|
||||
|
||||
RadioLibTime_t SX127x::getTimeOnAir(size_t len) {
|
||||
// check active modem
|
||||
uint8_t modem = getActiveModem();
|
||||
if (modem == RADIOLIB_SX127X_LORA) {
|
||||
RadioLibTime_t SX127x::calculateTimeOnAir(ModemType_t modem, DataRate_t dr, PacketConfig_t pc, size_t len) {
|
||||
if (modem == RADIOLIB_MODEM_LORA) {
|
||||
// get symbol length in us
|
||||
float symbolLength = (float) (uint32_t(1) << this->spreadingFactor) / (float) this->bandwidth;
|
||||
float symbolLength = (float) (uint32_t(1) << dr.lora.spreadingFactor) / (float) dr.lora.bandwidth;
|
||||
|
||||
// get number of symbols
|
||||
float n_sym = getNumSymbols(len);
|
||||
float n_sym = getNumSymbols(len, dr, pc);
|
||||
|
||||
// get time-on-air in us
|
||||
return ceil((double)symbolLength * (double)n_sym) * 1000;
|
||||
|
||||
} else if(modem == RADIOLIB_SX127X_FSK_OOK) {
|
||||
// get number of bits preamble
|
||||
float n_pre = (float) ((this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_MSB_FSK) << 8) | this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_LSB_FSK)) * 8;
|
||||
// get the number of bits of the sync word
|
||||
float n_syncWord = (float) (this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_SYNC_CONFIG, 2, 0) + 1) * 8;
|
||||
// get CRC bits
|
||||
float crc = (this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PACKET_CONFIG_1, 4, 4) == RADIOLIB_SX127X_CRC_ON) * 16;
|
||||
|
||||
if (this->packetLengthConfig == RADIOLIB_SX127X_PACKET_FIXED) {
|
||||
// if packet size fixed -> len = fixed packet length
|
||||
len = this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PAYLOAD_LENGTH_FSK);
|
||||
} else {
|
||||
// if packet variable -> Add 1 extra byte for payload length
|
||||
len += 1;
|
||||
}
|
||||
} else if(modem == RADIOLIB_MODEM_FSK) {
|
||||
|
||||
// calculate time-on-air in us {[(length in bytes) * (8 bits / 1 byte)] / [(Bit Rate in kbps) * (1000 bps / 1 kbps)]} * (1000000 us in 1 sec)
|
||||
return((uint32_t) (((crc + n_syncWord + n_pre + (float) (len * 8)) / (this->bitRate * 1000.0f)) * 1000000.0f));
|
||||
return((uint32_t) ((((float)(pc.fsk.crcLength * 8) + pc.fsk.syncWordLength + pc.fsk.preambleLength + (float) (len * 8)) / (dr.fsk.bitRate * 1000.0f)) * 1000000.0f));
|
||||
} else {
|
||||
return(RADIOLIB_ERR_WRONG_MODEM);
|
||||
}
|
||||
|
||||
return(RADIOLIB_ERR_UNKNOWN);
|
||||
}
|
||||
|
||||
RadioLibTime_t SX127x::getTimeOnAir(size_t len) {
|
||||
uint8_t modem = getActiveModem();
|
||||
DataRate_t dr = {};
|
||||
PacketConfig_t pc = {};
|
||||
|
||||
switch (modem) {
|
||||
case(RADIOLIB_SX127X_LORA): {
|
||||
dr.lora.spreadingFactor = this->spreadingFactor;
|
||||
dr.lora.bandwidth = this->bandwidth;
|
||||
dr.lora.codingRate = this->codingRate;
|
||||
|
||||
// Get number of preamble symbols
|
||||
uint16_t n_pre = ((this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_MSB) << 8) | this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_LSB));
|
||||
|
||||
pc.lora.preambleLength = n_pre;
|
||||
pc.lora.implicitHeader = this->implicitHdr;
|
||||
pc.lora.crcEnabled = this->crcEnabled;
|
||||
pc.lora.ldrOptimize = this->ldroEnabled;
|
||||
|
||||
return(calculateTimeOnAir((ModemType_t)RADIOLIB_MODEM_LORA, dr, pc, len));
|
||||
}
|
||||
case(RADIOLIB_SX127X_FSK_OOK): {
|
||||
dr.fsk.bitRate = this->bitRate;
|
||||
dr.fsk.freqDev = this->frequencyDev;
|
||||
|
||||
// get number of bits preamble
|
||||
uint16_t n_pre = (uint16_t) ((this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_MSB_FSK) << 8) | this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_LSB_FSK)) * 8;
|
||||
// get the number of bits of the sync word
|
||||
uint8_t n_syncWord = (uint8_t) ((this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_SYNC_CONFIG, 2, 0) + 1) * 8);
|
||||
// get CRC enabled status
|
||||
bool crcEn = (this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PACKET_CONFIG_1, 4, 4) == RADIOLIB_SX127X_CRC_ON);
|
||||
|
||||
pc.fsk.preambleLength = n_pre;
|
||||
pc.fsk.syncWordLength = n_syncWord;
|
||||
pc.fsk.crcLength = (uint8_t)(crcEn * 2);
|
||||
|
||||
if (this->packetLengthConfig == RADIOLIB_SX127X_PACKET_FIXED) {
|
||||
// if packet size fixed -> len = fixed packet length
|
||||
len = this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PAYLOAD_LENGTH_FSK);
|
||||
} else {
|
||||
// if packet variable -> Add 1 extra byte for payload length
|
||||
len += 1;
|
||||
}
|
||||
|
||||
return(calculateTimeOnAir((ModemType_t)RADIOLIB_MODEM_FSK, dr, pc, len));
|
||||
}
|
||||
default:
|
||||
return(RADIOLIB_ERR_WRONG_MODEM);
|
||||
}
|
||||
}
|
||||
|
||||
RadioLibTime_t SX127x::calculateRxTimeout(RadioLibTime_t timeoutUs) {
|
||||
RadioLibTime_t timeout = 0;
|
||||
if(getActiveModem() == RADIOLIB_SX127X_LORA) {
|
||||
|
|
|
@ -1040,9 +1040,11 @@ class SX127x: public PhysicalLayer {
|
|||
/*!
|
||||
\brief Convert from bytes to LoRa symbols.
|
||||
\param len Payload length in bytes.
|
||||
\param dr Data rate.
|
||||
\param pc Packet configuration.
|
||||
\returns The total number of LoRa symbols, including preamble, sync and possible header.
|
||||
*/
|
||||
float getNumSymbols(size_t len);
|
||||
float getNumSymbols(size_t len, DataRate_t dr, PacketConfig_t pc);
|
||||
|
||||
/*!
|
||||
\brief Get expected time-on-air for a given size of payload.
|
||||
|
@ -1051,6 +1053,16 @@ class SX127x: public PhysicalLayer {
|
|||
*/
|
||||
RadioLibTime_t getTimeOnAir(size_t len) override;
|
||||
|
||||
/*!
|
||||
\brief Calculate the expected time-on-air for a given modem, data rate, packet configuration and payload size.
|
||||
\param modem Modem type.
|
||||
\param dr Data rate.
|
||||
\param pc Packet config.
|
||||
\param len Payload length in bytes.
|
||||
\returns Expected time-on-air in microseconds.
|
||||
*/
|
||||
RadioLibTime_t calculateTimeOnAir(ModemType_t modem, DataRate_t dr, PacketConfig_t pc, size_t len);
|
||||
|
||||
/*!
|
||||
\brief Calculate the timeout value for this specific module / series (in number of symbols or units of time)
|
||||
\param timeoutUs Timeout in microseconds to listen for
|
||||
|
@ -1251,6 +1263,8 @@ class SX127x: public PhysicalLayer {
|
|||
bool crcEnabled = false;
|
||||
bool ookEnabled = false;
|
||||
bool implicitHdr = false;
|
||||
bool ldroAuto = false;
|
||||
bool ldroEnabled = false;
|
||||
|
||||
virtual int16_t configFSK();
|
||||
int16_t getActiveModem();
|
||||
|
@ -1264,7 +1278,7 @@ class SX127x: public PhysicalLayer {
|
|||
#endif
|
||||
Module* mod;
|
||||
|
||||
float bitRate = 0;
|
||||
float bitRate = 0, frequencyDev = 0;
|
||||
bool crcOn = true; // default value used in FSK mode
|
||||
float dataRate = 0;
|
||||
bool packetLengthQueried = false; // FSK packet length is the first byte in FIFO, length can only be queried once
|
||||
|
|
|
@ -969,6 +969,7 @@ int16_t SX128x::setFrequencyDeviation(float freqDev) {
|
|||
}
|
||||
|
||||
// update modulation parameters
|
||||
this->frequencyDev = newFreqDev;
|
||||
this->modIndex = modInd;
|
||||
return(setModulationParams(this->bitRate, this->modIndex, this->shaping));
|
||||
}
|
||||
|
@ -1334,15 +1335,13 @@ int16_t SX128x::variablePacketLengthMode(uint8_t maxLen) {
|
|||
return(setPacketMode(RADIOLIB_SX128X_GFSK_FLRC_PACKET_VARIABLE, maxLen));
|
||||
}
|
||||
|
||||
RadioLibTime_t SX128x::getTimeOnAir(size_t len) {
|
||||
// check active modem
|
||||
uint8_t modem = getPacketType();
|
||||
if(modem == RADIOLIB_SX128X_PACKET_TYPE_LORA) {
|
||||
// calculate number of symbols
|
||||
float N_symbol = 0;
|
||||
uint8_t sf = this->spreadingFactor >> 4;
|
||||
if(this->codingRateLoRa <= RADIOLIB_SX128X_LORA_CR_4_8) {
|
||||
// legacy coding rate - nice and simple
|
||||
RadioLibTime_t SX128x::calculateTimeOnAir(ModemType_t modem, DataRate_t dr, PacketConfig_t pc, size_t len) {
|
||||
switch(modem) {
|
||||
case (ModemType_t::RADIOLIB_MODEM_LORA): {
|
||||
// calculate number of symbols
|
||||
float N_symbol = 0;
|
||||
uint8_t sf = dr.lora.spreadingFactor;
|
||||
float cr = (float)dr.lora.codingRate;
|
||||
|
||||
// get SF coefficients
|
||||
float coeff1 = 0;
|
||||
|
@ -1367,33 +1366,73 @@ RadioLibTime_t SX128x::getTimeOnAir(size_t len) {
|
|||
|
||||
// get CRC length
|
||||
int16_t N_bitCRC = 16;
|
||||
if(this->crcLoRa == RADIOLIB_SX128X_LORA_CRC_OFF) {
|
||||
if(!pc.lora.crcEnabled) {
|
||||
N_bitCRC = 0;
|
||||
}
|
||||
|
||||
// get header length
|
||||
int16_t N_symbolHeader = 20;
|
||||
if(this->headerType == RADIOLIB_SX128X_LORA_HEADER_IMPLICIT) {
|
||||
if(pc.lora.implicitHeader) {
|
||||
N_symbolHeader = 0;
|
||||
}
|
||||
|
||||
// calculate number of LoRa preamble symbols
|
||||
uint32_t N_symbolPreamble = (this->preambleLengthLoRa & 0x0F) * (uint32_t(1) << ((this->preambleLengthLoRa & 0xF0) >> 4));
|
||||
uint32_t N_symbolPreamble = pc.lora.preambleLength;
|
||||
|
||||
// calculate the number of symbols
|
||||
N_symbol = (float)N_symbolPreamble + coeff1 + 8.0f + ceilf((float)RADIOLIB_MAX((int16_t)(8 * len + N_bitCRC - coeff2 + N_symbolHeader), (int16_t)0) / (float)coeff3) * (float)(this->codingRateLoRa + 4);
|
||||
N_symbol = (float)N_symbolPreamble + coeff1 + 8.0f + ceilf((float)RADIOLIB_MAX((int16_t)(8 * len + N_bitCRC - coeff2 + N_symbolHeader), (int16_t)0) / (float)coeff3) * cr;
|
||||
|
||||
} else {
|
||||
// long interleaving - abandon hope all ye who enter here
|
||||
/// \todo implement this mess - SX1280 datasheet v3.0 section 7.4.4.2
|
||||
// get time-on-air in us
|
||||
return(((uint32_t(1) << sf) / dr.lora.bandwidth) * N_symbol * 1000.0f);
|
||||
}
|
||||
case (ModemType_t::RADIOLIB_MODEM_FSK):
|
||||
return((((float)(pc.fsk.crcLength * 8) + pc.fsk.syncWordLength + pc.fsk.preambleLength + (uint32_t)len * 8) / (dr.fsk.bitRate / 1000.0f)));
|
||||
|
||||
default:
|
||||
return(RADIOLIB_ERR_WRONG_MODEM);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RadioLibTime_t SX128x::getTimeOnAir(size_t len) {
|
||||
// check active modem
|
||||
uint8_t modem = getPacketType();
|
||||
DataRate_t dr = {};
|
||||
PacketConfig_t pc = {};
|
||||
|
||||
if(modem == RADIOLIB_SX128X_PACKET_TYPE_LORA) {
|
||||
uint8_t sf = this->spreadingFactor >> 4;
|
||||
uint8_t cr = this->codingRateLoRa;
|
||||
// We assume same calculation for short and long interleaving, so map CR values 0-4 and 5-7 to the same values
|
||||
if (cr < 5) {
|
||||
cr = cr + 4;
|
||||
} else if (cr == 7) {
|
||||
cr = cr + 1;
|
||||
}
|
||||
|
||||
// get time-on-air in us
|
||||
return(((uint32_t(1) << sf) / this->bandwidthKhz) * N_symbol * 1000.0f);
|
||||
dr.lora.spreadingFactor = sf;
|
||||
dr.lora.codingRate = cr;
|
||||
dr.lora.bandwidth = this->bandwidthKhz;
|
||||
|
||||
uint16_t preambleLength = (this->preambleLengthLoRa & 0x0F) * (uint32_t(1) << ((this->preambleLengthLoRa & 0xF0) >> 4));
|
||||
|
||||
pc.lora.preambleLength = preambleLength;
|
||||
pc.lora.implicitHeader = this->headerType == RADIOLIB_SX128X_LORA_HEADER_IMPLICIT;
|
||||
pc.lora.crcEnabled = this->crcLoRa == RADIOLIB_SX128X_LORA_CRC_ON;
|
||||
pc.lora.ldrOptimize = false;
|
||||
|
||||
return(calculateTimeOnAir(ModemType_t::RADIOLIB_MODEM_LORA, dr, pc, len));
|
||||
} else if (modem == RADIOLIB_SX128X_PACKET_TYPE_GFSK) {
|
||||
dr.fsk.bitRate = (float)this->bitRateKbps;
|
||||
dr.fsk.freqDev = this->frequencyDev;
|
||||
|
||||
pc.fsk.preambleLength = ((uint16_t)this->preambleLengthGFSK >> 2) + 4;
|
||||
pc.fsk.syncWordLength = ((this->syncWordLen >> 1) + 1) * 8;
|
||||
pc.fsk.crcLength = this->crcGFSK >> 4;
|
||||
|
||||
return(calculateTimeOnAir(ModemType_t::RADIOLIB_MODEM_FSK, dr, pc, len));
|
||||
} else {
|
||||
return(((uint32_t)len * 8 * 1000) / this->bitRateKbps);
|
||||
return(RADIOLIB_ERR_WRONG_MODEM);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -828,6 +828,16 @@ class SX128x: public PhysicalLayer {
|
|||
*/
|
||||
int16_t variablePacketLengthMode(uint8_t maxLen = RADIOLIB_SX128X_MAX_PACKET_LENGTH);
|
||||
|
||||
/*!
|
||||
\brief Calculate the expected time-on-air for a given modem, data rate, packet configuration and payload size.
|
||||
\param modem Modem type.
|
||||
\param dr Data rate.
|
||||
\param pc Packet config.
|
||||
\param len Payload length in bytes.
|
||||
\returns Expected time-on-air in microseconds.
|
||||
*/
|
||||
RadioLibTime_t calculateTimeOnAir(ModemType_t modem, DataRate_t dr, PacketConfig_t pc, size_t len);
|
||||
|
||||
/*!
|
||||
\brief Get expected time-on-air for a given size of payload.
|
||||
\param len Payload length in bytes.
|
||||
|
@ -942,7 +952,7 @@ class SX128x: public PhysicalLayer {
|
|||
uint8_t invertIQEnabled = RADIOLIB_SX128X_LORA_IQ_STANDARD;
|
||||
|
||||
// cached GFSK parameters
|
||||
float modIndexReal = 0;
|
||||
float modIndexReal = 0, frequencyDev = 0;
|
||||
uint16_t bitRateKbps = 0;
|
||||
uint8_t bitRate = 0, modIndex = 0, shaping = 0;
|
||||
uint8_t preambleLengthGFSK = 0, syncWordLen = 0, syncWordMatch = 0, crcGFSK = 0, whitening = 0;
|
||||
|
|
|
@ -36,7 +36,7 @@ struct LoRaRate_t {
|
|||
/*! \brief LoRa bandwidth in kHz */
|
||||
float bandwidth;
|
||||
|
||||
/*! \brief LoRa coding rate */
|
||||
/*! \brief LoRa coding rate denominator */
|
||||
uint8_t codingRate;
|
||||
};
|
||||
|
||||
|
@ -82,6 +82,46 @@ union DataRate_t {
|
|||
LrFhssRate_t lrFhss;
|
||||
};
|
||||
|
||||
struct LoRaPacketConfig_t {
|
||||
/*! \brief LoRa preamble length */
|
||||
uint16_t preambleLength;
|
||||
|
||||
/*! \brief LoRa implicit header mode */
|
||||
bool implicitHeader;
|
||||
|
||||
/*! \brief LoRa CRC mode */
|
||||
bool crcEnabled;
|
||||
|
||||
/*! \brief LoRa low data rate optimization */
|
||||
bool ldrOptimize;
|
||||
};
|
||||
|
||||
struct FSKPacketConfig_t {
|
||||
/*! \brief FSK preamble length in bits */
|
||||
uint16_t preambleLength;
|
||||
|
||||
/*! \brief Length of the sync word in bits */
|
||||
uint8_t syncWordLength;
|
||||
|
||||
/*! \brief FSK CRC length in bytes */
|
||||
uint8_t crcLength;
|
||||
};
|
||||
|
||||
struct LrFhssPacketConfig_t {
|
||||
/*! \brief LR-FHSS header count (1 - 4) */
|
||||
uint8_t hdrCount;
|
||||
};
|
||||
|
||||
/*!
|
||||
\union PacketConfig_t
|
||||
\brief Common packet configuration structure
|
||||
*/
|
||||
union PacketConfig_t {
|
||||
LoRaPacketConfig_t lora;
|
||||
FSKPacketConfig_t fsk;
|
||||
LrFhssPacketConfig_t lrFhss;
|
||||
};
|
||||
|
||||
/*!
|
||||
\struct CADScanConfig_t
|
||||
\brief Channel scan configuration interpretation in case LoRa CAD is used
|
||||
|
|
Ładowanie…
Reference in New Issue