Skip to content

Commit

Permalink
added functionality for LoRa Alliance TR-13 Enabling CSMA for LoRaWAN (
Browse files Browse the repository at this point in the history
…#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.
  • Loading branch information
amalinda authored Oct 29, 2023
1 parent 912333c commit aca1d78
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 14 deletions.
43 changes: 30 additions & 13 deletions src/modules/SX126x/SX126x.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1702,27 +1702,41 @@ int16_t SX126x::setRx(uint32_t timeout) {
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) {
// default CAD parameters for assigned SF as per Semtech AN1200.48, Rev 2.1, Page 50
uint8_t detPeakValues[8] = { 22, 22, 22, 22, 23, 24, 25, 28};
uint8_t symbolNumValues[8] = { RADIOLIB_SX126X_CAD_ON_2_SYMB,
RADIOLIB_SX126X_CAD_ON_2_SYMB,
RADIOLIB_SX126X_CAD_ON_2_SYMB,
RADIOLIB_SX126X_CAD_ON_2_SYMB,
RADIOLIB_SX126X_CAD_ON_4_SYMB,
RADIOLIB_SX126X_CAD_ON_4_SYMB,
RADIOLIB_SX126X_CAD_ON_4_SYMB,
RADIOLIB_SX126X_CAD_ON_4_SYMB };
// default CAD parameters are shown in Semtech AN1200.48, page 41.
uint8_t detPeakValues[6] = { 22, 22, 24, 25, 26, 30};

// CAD parameters aren't available for SF-6. Just to be safe.
if(this->spreadingFactor < 7) {
this->spreadingFactor = 7;
} else if(this->spreadingFactor > 12) {
this->spreadingFactor = 12;
}

// build the packet
uint8_t data[7];
data[0] = symbolNumValues[this->spreadingFactor - 5];
data[1] = detPeakValues[this->spreadingFactor - 5];
data[0] = RADIOLIB_SX126X_CAD_ON_2_SYMB;
data[1] = detPeakValues[this->spreadingFactor - 7];
data[2] = RADIOLIB_SX126X_CAD_PARAM_DET_MIN;
data[3] = RADIOLIB_SX126X_CAD_GOTO_STDBY;
data[4] = 0x00;
data[5] = 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
if(symbolNum != RADIOLIB_SX126X_CAD_PARAM_DEFAULT) {
data[0] = symbolNum;
Expand All @@ -1736,6 +1750,9 @@ int16_t SX126x::setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) {
data[2] = detMin;
}
*/


// configure parameters
int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_CAD_PARAMS, data, 7);
RADIOLIB_ASSERT(state);
Expand Down Expand Up @@ -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);
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[1] = this->spreadingFactor + 13;
data[2] = RADIOLIB_SX126X_CAD_PARAM_DET_MIN;
Expand Down
66 changes: 65 additions & 1 deletion src/protocols/LoRaWAN/LoRaWAN.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,24 @@ LoRaWANNode::LoRaWANNode(PhysicalLayer* phy, const LoRaWANBand_t* band) {
this->startChannel = -1;
this->numChannels = -1;
this->backupFreq = this->band->backupChannel.freqStart;
this->difsSlots = 2;
this->backoffMax = 6;
this->enableCSMA = false;

}

void LoRaWANNode::wipe() {
Module* mod = this->phyLayer->getMod();
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 state = this->setPhyProperties();
RADIOLIB_ASSERT(state);
Expand Down Expand Up @@ -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);
}

// perform CSMA if enabled.
if (enableCSMA) {
performCSMA();
}

RADIOLIB_DEBUG_PRINTLN("uplinkMsg:");
RADIOLIB_DEBUG_HEXDUMP(uplinkMsg, uplinkMsgLen);

Expand Down Expand Up @@ -1762,4 +1778,52 @@ void LoRaWANNode::hton(uint8_t* buff, T val, size_t size) {
}
}

#endif
// 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
25 changes: 25 additions & 0 deletions src/protocols/LoRaWAN/LoRaWAN.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,17 @@ class LoRaWANNode {
(e.g. 8 for US915 FSB2 used by TTN). By default -1 (no channel offset). */
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.
\param phy Pointer to the PhysicalLayer radio module.
Expand All @@ -309,6 +320,14 @@ class LoRaWANNode {
*/
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.
\returns \ref status_codes
Expand Down Expand Up @@ -502,6 +521,12 @@ class LoRaWANNode {
// host-to-network conversion method - takes data from host variable and and converts it to network packet endians
template<typename T>
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

0 comments on commit aca1d78

Please sign in to comment.