Skip to content

LoRaWAN: Custom FSB selection in US and Australian regions #7802

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions features/lorawan/FSB_Usage.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Frequency sub-bands in US915/AU915:

US915/AU915 PHYs define channel structures which can support up-to 72 channels for upstream.
First 64 channels (0-63), occupy 125 kHz and the last 8 channels (64-71) occupy 500 kHz.
However, most of the base stations available in the market support 8 or 16 channels.
Network acquisition can become costly if the device have no prior knowledge of active channel plan and it enables
all 72 channels to begin with.

LoRaWAN 1.0.2 Regional parameters specification refers to a strategy of probing a set of nine channels (8 + 1) for
joining process. According to that strategy the device is supposed to alternatively select a channel from a set of
8, 125 kHz channels and a 500 kHz channel.
For example send a join request alternatively on a randomly selected channel from a set of 0-7 channels and
channel 64 which is the first 500 kHz channel.

Once the device has joined the network (in case of OTAA) or have sent the first uplink (in case of ABP), the network
may send a LinkAdrReq mac command to set the channel mask to be used. Please note that these PHY layers do not
support CFList so LinkAdrReq is the way the network tells you what channel plan to use.

Mbed LoRaWAN stack can be configured to use a particular frequency sub-band (FSB) which means that we don't have to
probe all sets of channels. "fsb-mask" in lorawan/mbed_lib.json is the parameter which can be used to tell the
system which FSB or a set of FSBs to use. By default the "fsb-mask" is set to "{0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x00FF}"
that means all channels are active, i.e., 64 125 kHz channels and 8 500 kHz channels are active. If the user wishes
to use a custom FSB, he/she needs to set appropriate mask as the value of "fsb-mask". For example:
If the user wishes to use the first FSB, i.e., first 8 125 kHz channels (0-7) and the first 500 kHz channel:
"fsb-mask" = "{0x00FF, 0x0000, 0x0000, 0x0000, 0x0001}"
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
channel:
"fsb-mask" = "{0xFF00, 0x0000, 0x0000, 0x0000, 0x0002}"

You can also combine FSBs if your base station supports more than 8 channels. For example:
"fsb-mask" = "{0x00FF, 0x0000, 0x0000, 0xFF00, 0x0081}"
will mean use channels 0-7(125 kHz) + channel 64 (500 KHz) and channels 56-63 (125 kHz) + channel 71 (500 kHz).

Please note that for Certification requirements, you need to alternate between 125 kHz and 500 kHz channels and that's
why before joining you should not set a mask that enables only 500 kHz or only 125 kHz channels.
39 changes: 39 additions & 0 deletions features/lorawan/lorastack/phy/LoRaPHY.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,45 @@ void LoRaPHY::copy_channel_mask(uint16_t *dest_mask, uint16_t *src_mask, uint8_t
}
}

void LoRaPHY::intersect_channel_mask(const uint16_t *source,
uint16_t *destination, uint8_t size)
{
if (!source || !destination || size == 0) {
return;
}

for (uint8_t i = 0; i < size; i++) {
destination[i] &= source[i];
}
}

void LoRaPHY::fill_channel_mask_with_fsb(const uint16_t *expectation,
const uint16_t *fsb_mask,
uint16_t *destination,
uint8_t size)
{
if (!expectation || !fsb_mask || !destination || size == 0) {
return;
}

for (uint8_t i = 0; i < size; i++) {
destination[i] = expectation[i] & fsb_mask[i];
}

}

void LoRaPHY::fill_channel_mask_with_value(uint16_t *channel_mask,
uint16_t value, uint8_t size)
{
if (!channel_mask || size == 0) {
return;
}

for (uint8_t i = 0; i < size; i++) {
channel_mask[i] = value;
}
}

void LoRaPHY::set_last_tx_done(uint8_t channel, bool joined, lorawan_time_t last_tx_done_time)
{
band_t *band_table = (band_t *) phy_params.bands.table;
Expand Down
20 changes: 20 additions & 0 deletions features/lorawan/lorastack/phy/LoRaPHY.h
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,26 @@ class LoRaPHY : private mbed::NonCopyable<LoRaPHY> {
protected:
LoRaPHY();

/**
* Sets the intersection of source and destination channel masks
* into the destination.
*/
void intersect_channel_mask(const uint16_t *source, uint16_t *destination,
uint8_t size);

/**
* Fills channel mask array based upon the provided FSB mask
*/
void fill_channel_mask_with_fsb(const uint16_t *expectation,
const uint16_t *fsb_mask,
uint16_t *channel_mask, uint8_t size);

/**
* Fills channel mask array with a given value
*/
void fill_channel_mask_with_value(uint16_t *channel_mask,
uint16_t value, uint8_t size);

/**
* Looks up corresponding band for a frequency. Returns -1 if not in any band.
*/
Expand Down
49 changes: 20 additions & 29 deletions features/lorawan/lorastack/phy/LoRaPHYAU915.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ static const uint8_t max_payload_AU915[] = { 51, 51, 51, 115, 242, 242,
static const uint8_t max_payload_with_repeater_AU915[] = { 51, 51, 51, 115,
222, 222, 222, 0, 33, 109, 222, 222, 222, 222, 0, 0 };

static const uint16_t fsb_mask[] = MBED_CONF_LORA_FSB_MASK;

static const uint16_t full_channel_mask [] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x00FF};

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

memset(channel_mask, 0, sizeof(channel_mask));
memset(current_channel_mask, 0, sizeof(current_channel_mask));
Expand Down Expand Up @@ -445,18 +446,16 @@ uint8_t LoRaPHYAU915::link_ADR_request(adr_req_params_t* params,

if (adr_settings.ch_mask_ctrl == 6) {
// Enable all 125 kHz channels
temp_channel_masks[0] = 0xFFFF;
temp_channel_masks[1] = 0xFFFF;
temp_channel_masks[2] = 0xFFFF;
temp_channel_masks[3] = 0xFFFF;
fill_channel_mask_with_value(temp_channel_masks, 0xFFFF,
AU915_CHANNEL_MASK_SIZE - 1);

// Apply chMask to channels 64 to 71
temp_channel_masks[4] = adr_settings.channel_mask;
} else if (adr_settings.ch_mask_ctrl == 7) {
// Disable all 125 kHz channels
temp_channel_masks[0] = 0x0000;
temp_channel_masks[1] = 0x0000;
temp_channel_masks[2] = 0x0000;
temp_channel_masks[3] = 0x0000;
fill_channel_mask_with_value(temp_channel_masks, 0x0000,
AU915_CHANNEL_MASK_SIZE - 1);

// Apply chMask to channels 64 to 71
temp_channel_masks[4] = adr_settings.channel_mask;
} else if (adr_settings.ch_mask_ctrl == 5) {
Expand Down Expand Up @@ -493,11 +492,8 @@ uint8_t LoRaPHYAU915::link_ADR_request(adr_req_params_t* params,
// Copy Mask
copy_channel_mask(channel_mask, temp_channel_masks, AU915_CHANNEL_MASK_SIZE);

current_channel_mask[0] &= channel_mask[0];
current_channel_mask[1] &= channel_mask[1];
current_channel_mask[2] &= channel_mask[2];
current_channel_mask[3] &= channel_mask[3];
current_channel_mask[4] = channel_mask[4];
intersect_channel_mask(channel_mask, current_channel_mask,
AU915_CHANNEL_MASK_SIZE);
}

// Update status variables
Expand Down Expand Up @@ -548,9 +544,6 @@ int8_t LoRaPHYAU915::get_alternate_DR(uint8_t nb_trials)
{
int8_t datarate = 0;

// Re-enable 500 kHz default channels
channel_mask[4] = 0x00FF;

if ((nb_trials & 0x01) == 0x01) {
datarate = DR_6;
} else {
Expand All @@ -576,11 +569,10 @@ lorawan_status_t LoRaPHYAU915::set_next_channel(channel_selection_params_t* next
}

// Check other channels
if (next_chan_params->current_datarate >= DR_6) {
if ((current_channel_mask[4] & 0x00FF) == 0) {
// fall back to 500 kHz default channels
current_channel_mask[4] = channel_mask[4];
}
if ((next_chan_params->current_datarate >= DR_6)
&& (current_channel_mask[4] & 0x00FF) == 0) {
// fall back to 500 kHz default channels
current_channel_mask[4] = channel_mask[4];
}

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

*time = 0;
return LORAWAN_STATUS_OK;
Expand Down
74 changes: 33 additions & 41 deletions features/lorawan/lorastack/phy/LoRaPHYUS915.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,45 +204,45 @@ static const int8_t datarate_offsets_US915[5][4] =
/*!
* Maximum payload with respect to the datarate index. Cannot operate with repeater.
*/
static const uint8_t max_payloads_US915[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 };
static const uint8_t max_payloads_US915[] = {11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0};

/*!
* Maximum payload with respect to the datarate index. Can operate with repeater.
*/
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};

static const uint16_t fsb_mask[] = MBED_CONF_LORA_FSB_MASK;
static const uint16_t full_channel_mask [] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x00FF};

LoRaPHYUS915::LoRaPHYUS915()
{
bands[0] = US915_BAND0;

// Channels
// 125 kHz channels
// 125 kHz channels - Upstream
for (uint8_t i = 0; i < US915_MAX_NB_CHANNELS - 8; i++) {
channels[i].frequency = 902300000 + i * 200000;
channels[i].dr_range.value = ( DR_3 << 4) | DR_0;
channels[i].band = 0;
}
// 500 kHz channels
// 500 kHz channels - Upstream
for (uint8_t i = US915_MAX_NB_CHANNELS - 8; i < US915_MAX_NB_CHANNELS; i++) {
channels[i].frequency = 903000000 + (i - ( US915_MAX_NB_CHANNELS - 8)) * 1600000;
channels[i].dr_range.value = ( DR_4 << 4) | DR_4;
channels[i].band = 0;
}

// ChannelsMask
default_channel_mask[0] = 0xFFFF;
default_channel_mask[1] = 0xFFFF;
default_channel_mask[2] = 0xFFFF;
default_channel_mask[3] = 0xFFFF;
default_channel_mask[4] = 0x00FF;
// Fill-up default channel mask and apply FSB mask too
fill_channel_mask_with_fsb(full_channel_mask, fsb_mask,
default_channel_mask, US915_CHANNEL_MASK_SIZE);

memset(channel_mask, 0, sizeof(channel_mask));
memset(current_channel_mask, 0, sizeof(current_channel_mask));

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

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

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

bool LoRaPHYUS915::rx_config(rx_config_params_t* config)
Expand Down Expand Up @@ -384,10 +382,11 @@ bool LoRaPHYUS915::rx_config(rx_config_params_t* config)

_radio->lock();

_radio->set_channel( frequency );
_radio->set_channel(frequency);

// Radio configuration
_radio->set_rx_config(MODEM_LORA, config->bandwidth, phy_dr, 1, 0, 8,
_radio->set_rx_config(MODEM_LORA, config->bandwidth, phy_dr, 1, 0,
MBED_CONF_LORA_DOWNLINK_PREAMBLE_LENGTH,
config->window_timeout, false, 0, false, 0, 0, true,
config->is_rx_continuous);
_radio->unlock();
Expand Down Expand Up @@ -429,7 +428,8 @@ bool LoRaPHYUS915::tx_config(tx_config_params_t* config, int8_t* tx_power,

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

_radio->set_tx_config(MODEM_LORA, phy_tx_power, 0, bandwidth, phy_dr, 1, 8,
_radio->set_tx_config(MODEM_LORA, phy_tx_power, 0, bandwidth, phy_dr, 1,
MBED_CONF_LORA_UPLINK_PREAMBLE_LENGTH,
false, true, 0, 0, false, 3000);

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

// Enable all 125 kHz channels
temp_channel_masks[0] = 0xFFFF;
temp_channel_masks[1] = 0xFFFF;
temp_channel_masks[2] = 0xFFFF;
temp_channel_masks[3] = 0xFFFF;
fill_channel_mask_with_value(temp_channel_masks, 0xFFFF,
US915_CHANNEL_MASK_SIZE - 1);

// Apply chMask to channels 64 to 71
temp_channel_masks[4] = adr_settings.channel_mask;

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

// Disable all 125 kHz channels
temp_channel_masks[0] = 0x0000;
temp_channel_masks[1] = 0x0000;
temp_channel_masks[2] = 0x0000;
temp_channel_masks[3] = 0x0000;
fill_channel_mask_with_value(temp_channel_masks, 0x0000,
US915_CHANNEL_MASK_SIZE - 1);

// Apply chMask to channels 64 to 71
temp_channel_masks[4] = adr_settings.channel_mask;

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

current_channel_mask[0] &= channel_mask[0];
current_channel_mask[1] &= channel_mask[1];
current_channel_mask[2] &= channel_mask[2];
current_channel_mask[3] &= channel_mask[3];
current_channel_mask[4] = channel_mask[4];
// update running channel mask
intersect_channel_mask(channel_mask, current_channel_mask,
US915_CHANNEL_MASK_SIZE);
}

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

// Verify radio frequency
if ((_radio->check_rf_frequency( freq ) == false)
if ((_radio->check_rf_frequency(freq) == false)
|| (freq < US915_FIRST_RX1_CHANNEL)
|| (freq > US915_LAST_RX1_CHANNEL)
|| (((freq - (uint32_t) US915_FIRST_RX1_CHANNEL) % (uint32_t) US915_STEPWIDTH_RX1_CHANNEL) != 0)) {
Expand Down Expand Up @@ -588,9 +584,6 @@ int8_t LoRaPHYUS915::get_alternate_DR(uint8_t nb_trials)
{
int8_t datarate = 0;

// Re-enable 500 kHz default channels
channel_mask[4] = 0x00FF;

if ((nb_trials & 0x01) == 0x01) {
datarate = DR_4;
} else {
Expand All @@ -616,11 +609,10 @@ lorawan_status_t LoRaPHYUS915::set_next_channel(channel_selection_params_t* para
copy_channel_mask(current_channel_mask, channel_mask, 4);
}

// Check other channels
if (params->current_datarate >= DR_4) {
if ((current_channel_mask[4] & 0x00FF ) == 0) {
current_channel_mask[4] = channel_mask[4];
}
// Update the 500 kHz channels in the running mask
if ((params->current_datarate >= DR_4)
&& (current_channel_mask[4] & 0x00FF) == 0) {
current_channel_mask[4] = channel_mask[4];
}

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

if (nb_enabled_channels > 0) {
// We found a valid channel
*channel = enabled_channels[get_random( 0, nb_enabled_channels - 1 )];
*channel = enabled_channels[get_random(0, nb_enabled_channels - 1)];
// Disable the channel in the mask
disable_channel(current_channel_mask, *channel, US915_MAX_NB_CHANNELS - 8);
disable_channel(current_channel_mask, *channel, US915_MAX_NB_CHANNELS);

*time = 0;
return LORAWAN_STATUS_OK;
Expand Down
Loading