added functionality for LoRa Alliance TR-13 Enabling CSMA for LoRaWAN (#859)
* added functionality for LoRa Alliance TR-13 Enabling CSMA for LoRaWAN * Addressed feedback on CSMA implementation * symbolNumValues[6] array no longer needed as we will utilize only two symbol CAD operations for all SFs.
This commit is contained in:
parent
912333c408
commit
aca1d78a97
3 changed files with 120 additions and 14 deletions
|
@ -1702,27 +1702,41 @@ int16_t SX126x::setRx(uint32_t timeout) {
|
||||||
return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RX, data, 3, true, false));
|
return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RX, data, 3, true, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int16_t SX126x::setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) {
|
int16_t SX126x::setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) {
|
||||||
// default CAD parameters for assigned SF as per Semtech AN1200.48, Rev 2.1, Page 50
|
// default CAD parameters are shown in Semtech AN1200.48, page 41.
|
||||||
uint8_t detPeakValues[8] = { 22, 22, 22, 22, 23, 24, 25, 28};
|
uint8_t detPeakValues[6] = { 22, 22, 24, 25, 26, 30};
|
||||||
uint8_t symbolNumValues[8] = { RADIOLIB_SX126X_CAD_ON_2_SYMB,
|
|
||||||
RADIOLIB_SX126X_CAD_ON_2_SYMB,
|
// CAD parameters aren't available for SF-6. Just to be safe.
|
||||||
RADIOLIB_SX126X_CAD_ON_2_SYMB,
|
if(this->spreadingFactor < 7) {
|
||||||
RADIOLIB_SX126X_CAD_ON_2_SYMB,
|
this->spreadingFactor = 7;
|
||||||
RADIOLIB_SX126X_CAD_ON_4_SYMB,
|
} else if(this->spreadingFactor > 12) {
|
||||||
RADIOLIB_SX126X_CAD_ON_4_SYMB,
|
this->spreadingFactor = 12;
|
||||||
RADIOLIB_SX126X_CAD_ON_4_SYMB,
|
}
|
||||||
RADIOLIB_SX126X_CAD_ON_4_SYMB };
|
|
||||||
// build the packet
|
// build the packet
|
||||||
uint8_t data[7];
|
uint8_t data[7];
|
||||||
data[0] = symbolNumValues[this->spreadingFactor - 5];
|
data[0] = RADIOLIB_SX126X_CAD_ON_2_SYMB;
|
||||||
data[1] = detPeakValues[this->spreadingFactor - 5];
|
data[1] = detPeakValues[this->spreadingFactor - 7];
|
||||||
data[2] = RADIOLIB_SX126X_CAD_PARAM_DET_MIN;
|
data[2] = RADIOLIB_SX126X_CAD_PARAM_DET_MIN;
|
||||||
data[3] = RADIOLIB_SX126X_CAD_GOTO_STDBY;
|
data[3] = RADIOLIB_SX126X_CAD_GOTO_STDBY;
|
||||||
data[4] = 0x00;
|
data[4] = 0x00;
|
||||||
data[5] = 0x00;
|
data[5] = 0x00;
|
||||||
data[6] = 0x00;
|
data[6] = 0x00;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
CAD Configuration Note:
|
||||||
|
The default CAD configuration applied by `scanChannel` overrides the optimal SF-specific configurations, leading to suboptimal detection.
|
||||||
|
I.e., anything that is not RADIOLIB_SX126X_CAD_PARAM_DEFAULT is overridden. But CAD settings are SF specific.
|
||||||
|
To address this, the user override has been commented out, ensuring consistent application of the optimal CAD settings as
|
||||||
|
per Semtech's Application Note AN1200.48 (page 41) for the 125KHz setting. This approach significantly reduces false CAD occurrences.
|
||||||
|
Testing has shown that there is no reason for a user to change CAD settings for anything other than most optimal ones described in AN1200.48 .
|
||||||
|
However, this change deos not respect CAD configs from the LoRaWAN layer. Future considerations or use cases might require revisiting this decision.
|
||||||
|
Hence this note.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
// set user-provided values
|
// set user-provided values
|
||||||
if(symbolNum != RADIOLIB_SX126X_CAD_PARAM_DEFAULT) {
|
if(symbolNum != RADIOLIB_SX126X_CAD_PARAM_DEFAULT) {
|
||||||
data[0] = symbolNum;
|
data[0] = symbolNum;
|
||||||
|
@ -1736,6 +1750,9 @@ int16_t SX126x::setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) {
|
||||||
data[2] = detMin;
|
data[2] = detMin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
// configure parameters
|
// configure parameters
|
||||||
int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_CAD_PARAMS, data, 7);
|
int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_CAD_PARAMS, data, 7);
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
@ -2030,7 +2047,7 @@ int16_t SX126x::config(uint8_t modem) {
|
||||||
state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RX_TX_FALLBACK_MODE, data, 1);
|
state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RX_TX_FALLBACK_MODE, data, 1);
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// set some CAD parameters - will be overwritten whel calling CAD anyway
|
// set some CAD parameters - will be overwritten when calling CAD anyway
|
||||||
data[0] = RADIOLIB_SX126X_CAD_ON_8_SYMB;
|
data[0] = RADIOLIB_SX126X_CAD_ON_8_SYMB;
|
||||||
data[1] = this->spreadingFactor + 13;
|
data[1] = this->spreadingFactor + 13;
|
||||||
data[2] = RADIOLIB_SX126X_CAD_PARAM_DET_MIN;
|
data[2] = RADIOLIB_SX126X_CAD_PARAM_DET_MIN;
|
||||||
|
|
|
@ -37,6 +37,10 @@ LoRaWANNode::LoRaWANNode(PhysicalLayer* phy, const LoRaWANBand_t* band) {
|
||||||
this->startChannel = -1;
|
this->startChannel = -1;
|
||||||
this->numChannels = -1;
|
this->numChannels = -1;
|
||||||
this->backupFreq = this->band->backupChannel.freqStart;
|
this->backupFreq = this->band->backupChannel.freqStart;
|
||||||
|
this->difsSlots = 2;
|
||||||
|
this->backoffMax = 6;
|
||||||
|
this->enableCSMA = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoRaWANNode::wipe() {
|
void LoRaWANNode::wipe() {
|
||||||
|
@ -44,6 +48,13 @@ void LoRaWANNode::wipe() {
|
||||||
mod->hal->wipePersistentStorage();
|
mod->hal->wipePersistentStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LoRaWANNode::setCSMA(uint8_t backoffMax, uint8_t difsSlots, bool enableCSMA) {
|
||||||
|
this->backoffMax = backoffMax;
|
||||||
|
this->difsSlots = difsSlots;
|
||||||
|
this->enableCSMA = enableCSMA;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int16_t LoRaWANNode::restoreOTAA() {
|
int16_t LoRaWANNode::restoreOTAA() {
|
||||||
int16_t state = this->setPhyProperties();
|
int16_t state = this->setPhyProperties();
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
@ -637,6 +648,11 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
||||||
LoRaWANNode::hton<uint32_t>(&uplinkMsg[uplinkMsgLen - sizeof(uint32_t)], micF);
|
LoRaWANNode::hton<uint32_t>(&uplinkMsg[uplinkMsgLen - sizeof(uint32_t)], micF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// perform CSMA if enabled.
|
||||||
|
if (enableCSMA) {
|
||||||
|
performCSMA();
|
||||||
|
}
|
||||||
|
|
||||||
RADIOLIB_DEBUG_PRINTLN("uplinkMsg:");
|
RADIOLIB_DEBUG_PRINTLN("uplinkMsg:");
|
||||||
RADIOLIB_DEBUG_HEXDUMP(uplinkMsg, uplinkMsgLen);
|
RADIOLIB_DEBUG_HEXDUMP(uplinkMsg, uplinkMsgLen);
|
||||||
|
|
||||||
|
@ -1762,4 +1778,52 @@ void LoRaWANNode::hton(uint8_t* buff, T val, size_t size) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The following function enables LMAC, a CSMA scheme for LoRa as specified
|
||||||
|
// in the LoRa Alliance Technical Recommendation #13.
|
||||||
|
// A user may enable CSMA to provide frames an additional layer of protection from interference.
|
||||||
|
// https://resources.lora-alliance.org/technical-recommendations/tr013-1-0-0-csma
|
||||||
|
void LoRaWANNode::performCSMA() {
|
||||||
|
|
||||||
|
// Compute initial random back-off.
|
||||||
|
// When BO is reduced to zero, the function returns and the frame is transmitted.
|
||||||
|
uint32_t BO = this->phyLayer->random(1, this->backoffMax + 1);
|
||||||
|
|
||||||
|
while (BO > 0) {
|
||||||
|
// DIFS: Check channel for DIFS_slots
|
||||||
|
bool channelFreeDuringDIFS = true;
|
||||||
|
for (uint8_t i = 0; i < this->difsSlots; i++) {
|
||||||
|
if (performCAD()) {
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("OCCUPIED CHANNEL DURING DIFS");
|
||||||
|
channelFreeDuringDIFS = false;
|
||||||
|
// Channel is occupied during DIFS, hop to another.
|
||||||
|
this->setupChannels();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start reducing BO counter if DIFS slot was free.
|
||||||
|
if (channelFreeDuringDIFS) {
|
||||||
|
// Continue decrementing BO with per each CAD reporting free channel.
|
||||||
|
while (BO > 0) {
|
||||||
|
if (performCAD()) {
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("OCCUPIED CHANNEL DURING BO");
|
||||||
|
// Channel is busy during CAD, hop to another and return to DIFS state again.
|
||||||
|
this->setupChannels();
|
||||||
|
break; // Exit loop. Go back to DIFS state.
|
||||||
|
}
|
||||||
|
BO--; // Decrement BO by one if channel is free
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoRaWANNode::performCAD() {
|
||||||
|
int16_t state = this->phyLayer->scanChannel();
|
||||||
|
|
||||||
|
if ((state == RADIOLIB_PREAMBLE_DETECTED) || (state == RADIOLIB_LORA_DETECTED)) {
|
||||||
|
return true; // Channel is busy
|
||||||
|
}
|
||||||
|
return false; // Channel is free
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -296,6 +296,17 @@ class LoRaWANNode {
|
||||||
(e.g. 8 for US915 FSB2 used by TTN). By default -1 (no channel offset). */
|
(e.g. 8 for US915 FSB2 used by TTN). By default -1 (no channel offset). */
|
||||||
int8_t numChannels;
|
int8_t numChannels;
|
||||||
|
|
||||||
|
/*! \brief Num of Back Off(BO) slots to be decremented after DIFS phase. 0 to disable BO.
|
||||||
|
A random BO avoids collisions in the case where two or more nodes start the CSMA
|
||||||
|
process at the same time. */
|
||||||
|
uint8_t backoffMax;
|
||||||
|
|
||||||
|
/*! \brief Num of CADs to estimate a clear CH. */
|
||||||
|
uint8_t difsSlots;
|
||||||
|
|
||||||
|
/*! \brief enable/disable CSMA for LoRaWAN. */
|
||||||
|
bool enableCSMA;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Default constructor.
|
\brief Default constructor.
|
||||||
\param phy Pointer to the PhysicalLayer radio module.
|
\param phy Pointer to the PhysicalLayer radio module.
|
||||||
|
@ -309,6 +320,14 @@ class LoRaWANNode {
|
||||||
*/
|
*/
|
||||||
void wipe();
|
void wipe();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Configures CSMA for LoRaWAN as per TR-13, LoRa Alliance.
|
||||||
|
\param backoffMax Num of BO slots to be decremented after DIFS phase. 0 to disable BO.
|
||||||
|
\param difsSlots Num of CADs to estimate a clear CH.
|
||||||
|
\param enableCSMA enable/disable CSMA for LoRaWAN.
|
||||||
|
*/
|
||||||
|
void setCSMA(uint8_t backoffMax, uint8_t difsSlots, bool enableCSMA = false);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Restore OTAA session by loading information from persistent storage.
|
\brief Restore OTAA session by loading information from persistent storage.
|
||||||
\returns \ref status_codes
|
\returns \ref status_codes
|
||||||
|
@ -502,6 +521,12 @@ class LoRaWANNode {
|
||||||
// host-to-network conversion method - takes data from host variable and and converts it to network packet endians
|
// host-to-network conversion method - takes data from host variable and and converts it to network packet endians
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static void hton(uint8_t* buff, T val, size_t size = 0);
|
static void hton(uint8_t* buff, T val, size_t size = 0);
|
||||||
|
|
||||||
|
// perform a single CAD operation for the under SF/CH combination. Returns either busy or otherwise.
|
||||||
|
bool performCAD();
|
||||||
|
|
||||||
|
// Performs CSMA as per LoRa Alliance Technical Reccomendation 13 (TR-013).
|
||||||
|
void performCSMA();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue