Skip to content

Commit b4d5e24

Browse files
authored
Merge pull request #7802 from hasnainvirk/pipeline_br
LoRaWAN: Custom FSB selection in US and Australian regions
2 parents 2f8e679 + 9b2507d commit b4d5e24

File tree

9 files changed

+152
-983
lines changed

9 files changed

+152
-983
lines changed

features/lorawan/FSB_Usage.txt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
Frequency sub-bands in US915/AU915:
2+
3+
US915/AU915 PHYs define channel structures which can support up-to 72 channels for upstream.
4+
First 64 channels (0-63), occupy 125 kHz and the last 8 channels (64-71) occupy 500 kHz.
5+
However, most of the base stations available in the market support 8 or 16 channels.
6+
Network acquisition can become costly if the device have no prior knowledge of active channel plan and it enables
7+
all 72 channels to begin with.
8+
9+
LoRaWAN 1.0.2 Regional parameters specification refers to a strategy of probing a set of nine channels (8 + 1) for
10+
joining process. According to that strategy the device is supposed to alternatively select a channel from a set of
11+
8, 125 kHz channels and a 500 kHz channel.
12+
For example send a join request alternatively on a randomly selected channel from a set of 0-7 channels and
13+
channel 64 which is the first 500 kHz channel.
14+
15+
Once the device has joined the network (in case of OTAA) or have sent the first uplink (in case of ABP), the network
16+
may send a LinkAdrReq mac command to set the channel mask to be used. Please note that these PHY layers do not
17+
support CFList so LinkAdrReq is the way the network tells you what channel plan to use.
18+
19+
Mbed LoRaWAN stack can be configured to use a particular frequency sub-band (FSB) which means that we don't have to
20+
probe all sets of channels. "fsb-mask" in lorawan/mbed_lib.json is the parameter which can be used to tell the
21+
system which FSB or a set of FSBs to use. By default the "fsb-mask" is set to "{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x00FF}"
22+
that means all channels are active, i.e., 64 125 kHz channels and 8 500 kHz channels are active. If the user wishes
23+
to use a custom FSB, he/she needs to set appropriate mask as the value of "fsb-mask". For example:
24+
If the user wishes to use the first FSB, i.e., first 8 125 kHz channels (0-7) and the first 500 kHz channel:
25+
"fsb-mask" = "{0x00FF, 0x0000, 0x0000, 0x0000, 0x0001}"
26+
Similarly, if the user wishes to use second FSB, i.e., 2nd set of 8 125 kHz channels (8-15) and the 2nd 500 kHz
27+
channel:
28+
"fsb-mask" = "{0xFF00, 0x0000, 0x0000, 0x0000, 0x0002}"
29+
30+
You can also combine FSBs if your base station supports more than 8 channels. For example:
31+
"fsb-mask" = "{0x00FF, 0x0000, 0x0000, 0xFF00, 0x0081}"
32+
will mean use channels 0-7(125 kHz) + channel 64 (500 KHz) and channels 56-63 (125 kHz) + channel 71 (500 kHz).
33+
34+
Please note that for Certification requirements, you need to alternate between 125 kHz and 500 kHz channels and that's
35+
why before joining you should not set a mask that enables only 500 kHz or only 125 kHz channels.

features/lorawan/lorastack/phy/LoRaPHY.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,45 @@ void LoRaPHY::copy_channel_mask(uint16_t *dest_mask, uint16_t *src_mask, uint8_t
248248
}
249249
}
250250

251+
void LoRaPHY::intersect_channel_mask(const uint16_t *source,
252+
uint16_t *destination, uint8_t size)
253+
{
254+
if (!source || !destination || size == 0) {
255+
return;
256+
}
257+
258+
for (uint8_t i = 0; i < size; i++) {
259+
destination[i] &= source[i];
260+
}
261+
}
262+
263+
void LoRaPHY::fill_channel_mask_with_fsb(const uint16_t *expectation,
264+
const uint16_t *fsb_mask,
265+
uint16_t *destination,
266+
uint8_t size)
267+
{
268+
if (!expectation || !fsb_mask || !destination || size == 0) {
269+
return;
270+
}
271+
272+
for (uint8_t i = 0; i < size; i++) {
273+
destination[i] = expectation[i] & fsb_mask[i];
274+
}
275+
276+
}
277+
278+
void LoRaPHY::fill_channel_mask_with_value(uint16_t *channel_mask,
279+
uint16_t value, uint8_t size)
280+
{
281+
if (!channel_mask || size == 0) {
282+
return;
283+
}
284+
285+
for (uint8_t i = 0; i < size; i++) {
286+
channel_mask[i] = value;
287+
}
288+
}
289+
251290
void LoRaPHY::set_last_tx_done(uint8_t channel, bool joined, lorawan_time_t last_tx_done_time)
252291
{
253292
band_t *band_table = (band_t *) phy_params.bands.table;

features/lorawan/lorastack/phy/LoRaPHY.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,26 @@ class LoRaPHY : private mbed::NonCopyable<LoRaPHY> {
535535
protected:
536536
LoRaPHY();
537537

538+
/**
539+
* Sets the intersection of source and destination channel masks
540+
* into the destination.
541+
*/
542+
void intersect_channel_mask(const uint16_t *source, uint16_t *destination,
543+
uint8_t size);
544+
545+
/**
546+
* Fills channel mask array based upon the provided FSB mask
547+
*/
548+
void fill_channel_mask_with_fsb(const uint16_t *expectation,
549+
const uint16_t *fsb_mask,
550+
uint16_t *channel_mask, uint8_t size);
551+
552+
/**
553+
* Fills channel mask array with a given value
554+
*/
555+
void fill_channel_mask_with_value(uint16_t *channel_mask,
556+
uint16_t value, uint8_t size);
557+
538558
/**
539559
* Looks up corresponding band for a frequency. Returns -1 if not in any band.
540560
*/

features/lorawan/lorastack/phy/LoRaPHYAU915.cpp

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@ static const uint8_t max_payload_AU915[] = { 51, 51, 51, 115, 242, 242,
220220
static const uint8_t max_payload_with_repeater_AU915[] = { 51, 51, 51, 115,
221221
222, 222, 222, 0, 33, 109, 222, 222, 222, 222, 0, 0 };
222222

223+
static const uint16_t fsb_mask[] = MBED_CONF_LORA_FSB_MASK;
224+
225+
static const uint16_t full_channel_mask [] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x00FF};
223226

224227
LoRaPHYAU915::LoRaPHYAU915()
225228
{
@@ -243,12 +246,10 @@ LoRaPHYAU915::LoRaPHYAU915()
243246
// Initialize channels default mask
244247
// All channels are default channels here
245248
// Join request needs to alternate between 125 KHz and 500 KHz channels
246-
// randomly.
247-
default_channel_mask[0] = 0xFFFF;
248-
default_channel_mask[1] = 0xFFFF;
249-
default_channel_mask[2] = 0xFFFF;
250-
default_channel_mask[3] = 0xFFFF;
251-
default_channel_mask[4] = 0x00FF;
249+
// randomly. Fill in the default channel mask depending upon the given
250+
// fsb_mask
251+
fill_channel_mask_with_fsb(full_channel_mask, fsb_mask,
252+
default_channel_mask, AU915_CHANNEL_MASK_SIZE);
252253

253254
memset(channel_mask, 0, sizeof(channel_mask));
254255
memset(current_channel_mask, 0, sizeof(current_channel_mask));
@@ -445,18 +446,16 @@ uint8_t LoRaPHYAU915::link_ADR_request(adr_req_params_t* params,
445446

446447
if (adr_settings.ch_mask_ctrl == 6) {
447448
// Enable all 125 kHz channels
448-
temp_channel_masks[0] = 0xFFFF;
449-
temp_channel_masks[1] = 0xFFFF;
450-
temp_channel_masks[2] = 0xFFFF;
451-
temp_channel_masks[3] = 0xFFFF;
449+
fill_channel_mask_with_value(temp_channel_masks, 0xFFFF,
450+
AU915_CHANNEL_MASK_SIZE - 1);
451+
452452
// Apply chMask to channels 64 to 71
453453
temp_channel_masks[4] = adr_settings.channel_mask;
454454
} else if (adr_settings.ch_mask_ctrl == 7) {
455455
// Disable all 125 kHz channels
456-
temp_channel_masks[0] = 0x0000;
457-
temp_channel_masks[1] = 0x0000;
458-
temp_channel_masks[2] = 0x0000;
459-
temp_channel_masks[3] = 0x0000;
456+
fill_channel_mask_with_value(temp_channel_masks, 0x0000,
457+
AU915_CHANNEL_MASK_SIZE - 1);
458+
460459
// Apply chMask to channels 64 to 71
461460
temp_channel_masks[4] = adr_settings.channel_mask;
462461
} else if (adr_settings.ch_mask_ctrl == 5) {
@@ -493,11 +492,8 @@ uint8_t LoRaPHYAU915::link_ADR_request(adr_req_params_t* params,
493492
// Copy Mask
494493
copy_channel_mask(channel_mask, temp_channel_masks, AU915_CHANNEL_MASK_SIZE);
495494

496-
current_channel_mask[0] &= channel_mask[0];
497-
current_channel_mask[1] &= channel_mask[1];
498-
current_channel_mask[2] &= channel_mask[2];
499-
current_channel_mask[3] &= channel_mask[3];
500-
current_channel_mask[4] = channel_mask[4];
495+
intersect_channel_mask(channel_mask, current_channel_mask,
496+
AU915_CHANNEL_MASK_SIZE);
501497
}
502498

503499
// Update status variables
@@ -548,9 +544,6 @@ int8_t LoRaPHYAU915::get_alternate_DR(uint8_t nb_trials)
548544
{
549545
int8_t datarate = 0;
550546

551-
// Re-enable 500 kHz default channels
552-
channel_mask[4] = 0x00FF;
553-
554547
if ((nb_trials & 0x01) == 0x01) {
555548
datarate = DR_6;
556549
} else {
@@ -576,11 +569,10 @@ lorawan_status_t LoRaPHYAU915::set_next_channel(channel_selection_params_t* next
576569
}
577570

578571
// Check other channels
579-
if (next_chan_params->current_datarate >= DR_6) {
580-
if ((current_channel_mask[4] & 0x00FF) == 0) {
581-
// fall back to 500 kHz default channels
582-
current_channel_mask[4] = channel_mask[4];
583-
}
572+
if ((next_chan_params->current_datarate >= DR_6)
573+
&& (current_channel_mask[4] & 0x00FF) == 0) {
574+
// fall back to 500 kHz default channels
575+
current_channel_mask[4] = channel_mask[4];
584576
}
585577

586578
if (next_chan_params->aggregate_timeoff <= _lora_time->get_elapsed_time(next_chan_params->last_aggregate_tx_time)) {
@@ -605,8 +597,7 @@ lorawan_status_t LoRaPHYAU915::set_next_channel(channel_selection_params_t* next
605597
// We found a valid channel
606598
*channel = enabled_channels[get_random(0, nb_enabled_channels - 1)];
607599
// Disable the channel in the mask
608-
disable_channel(current_channel_mask, *channel,
609-
AU915_MAX_NB_CHANNELS - 8);
600+
disable_channel(current_channel_mask, *channel, AU915_MAX_NB_CHANNELS);
610601

611602
*time = 0;
612603
return LORAWAN_STATUS_OK;

features/lorawan/lorastack/phy/LoRaPHYUS915.cpp

Lines changed: 33 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -204,45 +204,45 @@ static const int8_t datarate_offsets_US915[5][4] =
204204
/*!
205205
* Maximum payload with respect to the datarate index. Cannot operate with repeater.
206206
*/
207-
static const uint8_t max_payloads_US915[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 };
207+
static const uint8_t max_payloads_US915[] = {11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0};
208208

209209
/*!
210210
* Maximum payload with respect to the datarate index. Can operate with repeater.
211211
*/
212212
static const uint8_t max_payloads_with_repeater_US915[] = {11, 53, 125, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0};
213213

214+
static const uint16_t fsb_mask[] = MBED_CONF_LORA_FSB_MASK;
215+
static const uint16_t full_channel_mask [] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x00FF};
216+
214217
LoRaPHYUS915::LoRaPHYUS915()
215218
{
216219
bands[0] = US915_BAND0;
217220

218221
// Channels
219-
// 125 kHz channels
222+
// 125 kHz channels - Upstream
220223
for (uint8_t i = 0; i < US915_MAX_NB_CHANNELS - 8; i++) {
221224
channels[i].frequency = 902300000 + i * 200000;
222225
channels[i].dr_range.value = ( DR_3 << 4) | DR_0;
223226
channels[i].band = 0;
224227
}
225-
// 500 kHz channels
228+
// 500 kHz channels - Upstream
226229
for (uint8_t i = US915_MAX_NB_CHANNELS - 8; i < US915_MAX_NB_CHANNELS; i++) {
227230
channels[i].frequency = 903000000 + (i - ( US915_MAX_NB_CHANNELS - 8)) * 1600000;
228231
channels[i].dr_range.value = ( DR_4 << 4) | DR_4;
229232
channels[i].band = 0;
230233
}
231234

232-
// ChannelsMask
233-
default_channel_mask[0] = 0xFFFF;
234-
default_channel_mask[1] = 0xFFFF;
235-
default_channel_mask[2] = 0xFFFF;
236-
default_channel_mask[3] = 0xFFFF;
237-
default_channel_mask[4] = 0x00FF;
235+
// Fill-up default channel mask and apply FSB mask too
236+
fill_channel_mask_with_fsb(full_channel_mask, fsb_mask,
237+
default_channel_mask, US915_CHANNEL_MASK_SIZE);
238238

239239
memset(channel_mask, 0, sizeof(channel_mask));
240240
memset(current_channel_mask, 0, sizeof(current_channel_mask));
241241

242242
// Copy channels default mask
243243
copy_channel_mask(channel_mask, default_channel_mask, US915_CHANNEL_MASK_SIZE);
244244

245-
// current channel masks keep track what of the
245+
// current channel masks keep track of the
246246
// channels previously used, i.e., which channels should be avoided in
247247
// next transmission
248248
copy_channel_mask(current_channel_mask, channel_mask, US915_CHANNEL_MASK_SIZE);
@@ -346,10 +346,8 @@ void LoRaPHYUS915::restore_default_channels()
346346
// Copy channels default mask
347347
copy_channel_mask(channel_mask, default_channel_mask, US915_CHANNEL_MASK_SIZE);
348348

349-
for ( uint8_t i = 0; i < US915_CHANNEL_MASK_SIZE; i++ ) {
350-
// Copy-And the channels mask
351-
current_channel_mask[i] &= channel_mask[i];
352-
}
349+
// Update running channel mask
350+
intersect_channel_mask(channel_mask, current_channel_mask, US915_CHANNEL_MASK_SIZE);
353351
}
354352

355353
bool LoRaPHYUS915::rx_config(rx_config_params_t* config)
@@ -384,10 +382,11 @@ bool LoRaPHYUS915::rx_config(rx_config_params_t* config)
384382

385383
_radio->lock();
386384

387-
_radio->set_channel( frequency );
385+
_radio->set_channel(frequency);
388386

389387
// Radio configuration
390-
_radio->set_rx_config(MODEM_LORA, config->bandwidth, phy_dr, 1, 0, 8,
388+
_radio->set_rx_config(MODEM_LORA, config->bandwidth, phy_dr, 1, 0,
389+
MBED_CONF_LORA_DOWNLINK_PREAMBLE_LENGTH,
391390
config->window_timeout, false, 0, false, 0, 0, true,
392391
config->is_rx_continuous);
393392
_radio->unlock();
@@ -429,7 +428,8 @@ bool LoRaPHYUS915::tx_config(tx_config_params_t* config, int8_t* tx_power,
429428

430429
_radio->set_channel(channels[config->channel].frequency);
431430

432-
_radio->set_tx_config(MODEM_LORA, phy_tx_power, 0, bandwidth, phy_dr, 1, 8,
431+
_radio->set_tx_config(MODEM_LORA, phy_tx_power, 0, bandwidth, phy_dr, 1,
432+
MBED_CONF_LORA_UPLINK_PREAMBLE_LENGTH,
433433
false, true, 0, 0, false, 3000);
434434

435435
// Setup maximum payload lenght of the radio driver
@@ -478,20 +478,18 @@ uint8_t LoRaPHYUS915::link_ADR_request(adr_req_params_t* params,
478478
if (adr_settings.ch_mask_ctrl == 6) {
479479

480480
// Enable all 125 kHz channels
481-
temp_channel_masks[0] = 0xFFFF;
482-
temp_channel_masks[1] = 0xFFFF;
483-
temp_channel_masks[2] = 0xFFFF;
484-
temp_channel_masks[3] = 0xFFFF;
481+
fill_channel_mask_with_value(temp_channel_masks, 0xFFFF,
482+
US915_CHANNEL_MASK_SIZE - 1);
483+
485484
// Apply chMask to channels 64 to 71
486485
temp_channel_masks[4] = adr_settings.channel_mask;
487486

488487
} else if (adr_settings.ch_mask_ctrl == 7) {
489488

490489
// Disable all 125 kHz channels
491-
temp_channel_masks[0] = 0x0000;
492-
temp_channel_masks[1] = 0x0000;
493-
temp_channel_masks[2] = 0x0000;
494-
temp_channel_masks[3] = 0x0000;
490+
fill_channel_mask_with_value(temp_channel_masks, 0x0000,
491+
US915_CHANNEL_MASK_SIZE - 1);
492+
495493
// Apply chMask to channels 64 to 71
496494
temp_channel_masks[4] = adr_settings.channel_mask;
497495

@@ -531,11 +529,9 @@ uint8_t LoRaPHYUS915::link_ADR_request(adr_req_params_t* params,
531529
// Copy Mask
532530
copy_channel_mask(channel_mask, temp_channel_masks, US915_CHANNEL_MASK_SIZE);
533531

534-
current_channel_mask[0] &= channel_mask[0];
535-
current_channel_mask[1] &= channel_mask[1];
536-
current_channel_mask[2] &= channel_mask[2];
537-
current_channel_mask[3] &= channel_mask[3];
538-
current_channel_mask[4] = channel_mask[4];
532+
// update running channel mask
533+
intersect_channel_mask(channel_mask, current_channel_mask,
534+
US915_CHANNEL_MASK_SIZE);
539535
}
540536

541537
// Update status variables
@@ -553,7 +549,7 @@ uint8_t LoRaPHYUS915::accept_rx_param_setup_req(rx_param_setup_req_t* params)
553549
uint32_t freq = params->frequency;
554550

555551
// Verify radio frequency
556-
if ((_radio->check_rf_frequency( freq ) == false)
552+
if ((_radio->check_rf_frequency(freq) == false)
557553
|| (freq < US915_FIRST_RX1_CHANNEL)
558554
|| (freq > US915_LAST_RX1_CHANNEL)
559555
|| (((freq - (uint32_t) US915_FIRST_RX1_CHANNEL) % (uint32_t) US915_STEPWIDTH_RX1_CHANNEL) != 0)) {
@@ -588,9 +584,6 @@ int8_t LoRaPHYUS915::get_alternate_DR(uint8_t nb_trials)
588584
{
589585
int8_t datarate = 0;
590586

591-
// Re-enable 500 kHz default channels
592-
channel_mask[4] = 0x00FF;
593-
594587
if ((nb_trials & 0x01) == 0x01) {
595588
datarate = DR_4;
596589
} else {
@@ -616,11 +609,10 @@ lorawan_status_t LoRaPHYUS915::set_next_channel(channel_selection_params_t* para
616609
copy_channel_mask(current_channel_mask, channel_mask, 4);
617610
}
618611

619-
// Check other channels
620-
if (params->current_datarate >= DR_4) {
621-
if ((current_channel_mask[4] & 0x00FF ) == 0) {
622-
current_channel_mask[4] = channel_mask[4];
623-
}
612+
// Update the 500 kHz channels in the running mask
613+
if ((params->current_datarate >= DR_4)
614+
&& (current_channel_mask[4] & 0x00FF) == 0) {
615+
current_channel_mask[4] = channel_mask[4];
624616
}
625617

626618
if (params->aggregate_timeoff <= _lora_time->get_elapsed_time(params->last_aggregate_tx_time)) {
@@ -641,9 +633,9 @@ lorawan_status_t LoRaPHYUS915::set_next_channel(channel_selection_params_t* para
641633

642634
if (nb_enabled_channels > 0) {
643635
// We found a valid channel
644-
*channel = enabled_channels[get_random( 0, nb_enabled_channels - 1 )];
636+
*channel = enabled_channels[get_random(0, nb_enabled_channels - 1)];
645637
// Disable the channel in the mask
646-
disable_channel(current_channel_mask, *channel, US915_MAX_NB_CHANNELS - 8);
638+
disable_channel(current_channel_mask, *channel, US915_MAX_NB_CHANNELS);
647639

648640
*time = 0;
649641
return LORAWAN_STATUS_OK;

0 commit comments

Comments
 (0)