/** / _____) _ | | ( (____ _____ ____ _| |_ _____ ____| |__ \____ \| ___ | (_ _) ___ |/ ___) _ \ _____) ) ____| | | || |_| ____( (___| | | | (______/|_____)_|_|_| \__)_____)\____)_| |_| (C)2013 Semtech ___ _____ _ ___ _ _____ ___ ___ ___ ___ / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| embedded.connectivity.solutions=============== License: Revised BSD License, see LICENSE.TXT file include in the project Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) Copyright (c) 2017, Arm Limited and affiliates. SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include "LoRaPHY.h" #define BACKOFF_DC_1_HOUR 100 #define BACKOFF_DC_10_HOURS 1000 #define BACKOFF_DC_24_HOURS 10000 #define CHANNELS_IN_MASK 16 LoRaPHY::LoRaPHY() : _radio(NULL) { memset(&phy_params, 0, sizeof(phy_params)); } LoRaPHY::~LoRaPHY() { _radio = NULL; } void LoRaPHY::initialize(LoRaWANTimeHandler *lora_time) { _lora_time = lora_time; } bool LoRaPHY::mask_bit_test(const uint16_t *mask, unsigned bit) { return mask[bit / 16] & (1U << (bit % 16)); } void LoRaPHY::mask_bit_set(uint16_t *mask, unsigned bit) { mask[bit / 16] |= (1U << (bit % 16)); } void LoRaPHY::mask_bit_clear(uint16_t *mask, unsigned bit) { mask[bit / 16] &= ~(1U << (bit % 16)); } void LoRaPHY::set_radio_instance(LoRaRadio &radio) { _radio = &radio; } void LoRaPHY::put_radio_to_sleep() { _radio->lock(); _radio->sleep(); _radio->unlock(); } void LoRaPHY::put_radio_to_standby() { _radio->lock(); _radio->standby(); _radio->unlock(); } void LoRaPHY::setup_public_network_mode(bool set) { _radio->lock(); _radio->set_public_network(set); _radio->unlock(); } void LoRaPHY::handle_receive(void) { _radio->lock(); _radio->receive(); _radio->unlock(); } // For DevNonce for example uint32_t LoRaPHY::get_radio_rng() { uint32_t rand; _radio->lock(); rand = _radio->random(); _radio->unlock(); return rand; } void LoRaPHY::handle_send(uint8_t *buf, uint8_t size) { _radio->lock(); _radio->send(buf, size); _radio->unlock(); } uint8_t LoRaPHY::request_new_channel(int8_t channel_id, channel_params_t *new_channel) { if (!phy_params.custom_channelplans_supported) { return 0; } uint8_t status = 0x03; if (new_channel->frequency == 0) { // Remove if (remove_channel(channel_id) == false) { status &= 0xFC; } } else { new_channel->band = lookup_band_for_frequency(new_channel->frequency); switch (add_channel(new_channel, channel_id)) { case LORAWAN_STATUS_OK: { break; } case LORAWAN_STATUS_FREQUENCY_INVALID: { status &= 0xFE; break; } case LORAWAN_STATUS_DATARATE_INVALID: { status &= 0xFD; break; } case LORAWAN_STATUS_FREQ_AND_DR_INVALID: { status &= 0xFC; break; } default: { status &= 0xFC; break; } } } return status; } int32_t LoRaPHY::get_random(int32_t min, int32_t max) { return (int32_t) rand() % (max - min + 1) + min; } bool LoRaPHY::verify_channel_DR(uint16_t *channel_mask, int8_t dr) { if (val_in_range(dr, phy_params.min_tx_datarate, phy_params.max_tx_datarate) == 0) { return false; } for (uint8_t i = 0; i < phy_params.max_channel_cnt; i++) { if (mask_bit_test(channel_mask, i)) { // Check datarate validity for enabled channels if (val_in_range(dr, (phy_params.channels.channel_list[i].dr_range.fields.min & 0x0F), (phy_params.channels.channel_list[i].dr_range.fields.max & 0x0F))) { // At least 1 channel has been found we can return OK. return true; } } } return false; } bool LoRaPHY::val_in_range(int8_t value, int8_t min, int8_t max) { if ((value >= min) && (value <= max)) { return true; } return false; } bool LoRaPHY::disable_channel(uint16_t *channel_mask, uint8_t id, uint8_t max_channels_num) { uint8_t index = id / 16; if ((index > phy_params.channels.mask_size) || (id >= max_channels_num)) { return false; } // Deactivate channel mask_bit_clear(channel_mask, id); return true; } uint8_t LoRaPHY::count_bits(uint16_t mask, uint8_t nbBits) { uint8_t nbActiveBits = 0; for (uint8_t j = 0; j < nbBits; j++) { if (mask_bit_test(&mask, j)) { nbActiveBits++; } } return nbActiveBits; } uint8_t LoRaPHY::num_active_channels(uint16_t *channel_mask, uint8_t start_idx, uint8_t stop_idx) { uint8_t nb_channels = 0; if (channel_mask == NULL) { return 0; } for (uint8_t i = start_idx; i < stop_idx; i++) { nb_channels += count_bits(channel_mask[i], 16); } return nb_channels; } void LoRaPHY::copy_channel_mask(uint16_t *dest_mask, uint16_t *src_mask, uint8_t len) { if ((dest_mask != NULL) && (src_mask != NULL)) { for (uint8_t i = 0; i < len; i++) { dest_mask[i] = src_mask[i]; } } } 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; channel_params_t *channel_list = phy_params.channels.channel_list; if (joined == true) { band_table[channel_list[channel].band].last_tx_time = last_tx_done_time; return; } band_table[channel_list[channel].band].last_tx_time = last_tx_done_time; band_table[channel_list[channel].band].last_join_tx_time = last_tx_done_time; } lorawan_time_t LoRaPHY::update_band_timeoff(bool joined, bool duty_cycle, band_t *bands, uint8_t nb_bands) { lorawan_time_t next_tx_delay = (lorawan_time_t)(-1); // Update bands Time OFF for (uint8_t i = 0; i < nb_bands; i++) { if (MBED_CONF_LORA_DUTY_CYCLE_ON_JOIN && joined == false) { uint32_t txDoneTime = MAX(_lora_time->get_elapsed_time(bands[i].last_join_tx_time), (duty_cycle == true) ? _lora_time->get_elapsed_time(bands[i].last_tx_time) : 0); if (bands[i].off_time <= txDoneTime) { bands[i].off_time = 0; } if (bands[i].off_time != 0) { next_tx_delay = MIN(bands[i].off_time - txDoneTime, next_tx_delay); } } else { // if network has been joined if (duty_cycle == true) { if (bands[i].off_time <= _lora_time->get_elapsed_time(bands[i].last_tx_time)) { bands[i].off_time = 0; } if (bands[i].off_time != 0) { next_tx_delay = MIN(bands[i].off_time - _lora_time->get_elapsed_time(bands[i].last_tx_time), next_tx_delay); } } else { // if duty cycle is not on next_tx_delay = 0; bands[i].off_time = 0; } } } return next_tx_delay; } uint8_t LoRaPHY::parse_link_ADR_req(const uint8_t *payload, link_adr_params_t *params) { uint8_t ret_index = 0; if (payload[0] == SRV_MAC_LINK_ADR_REQ) { // Parse datarate and tx power params->datarate = payload[1]; params->tx_power = params->datarate & 0x0F; params->datarate = (params->datarate >> 4) & 0x0F; // Parse ChMask params->channel_mask = (uint16_t) payload[2]; params->channel_mask |= (uint16_t) payload[3] << 8; // Parse ChMaskCtrl and nbRep params->nb_rep = payload[4]; params->ch_mask_ctrl = (params->nb_rep >> 4) & 0x07; params->nb_rep &= 0x0F; // LinkAdrReq has 4 bytes length + 1 byte CMD ret_index = 5; } return ret_index; } uint8_t LoRaPHY::verify_link_ADR_req(verify_adr_params_t *verify_params, int8_t *dr, int8_t *tx_pow, uint8_t *nb_rep) { uint8_t status = verify_params->status; int8_t datarate = verify_params->datarate; int8_t tx_power = verify_params->tx_power; int8_t nb_repetitions = verify_params->nb_rep; // Handle the case when ADR is off. if (verify_params->adr_enabled == false) { // When ADR is off, we are allowed to change the channels mask and the NbRep, // if the datarate and the TX power of the LinkAdrReq are set to 0x0F. if ((verify_params->datarate != 0x0F) || (verify_params->tx_power != 0x0F)) { status = 0; nb_repetitions = verify_params->current_nb_rep; } // Get the current datarate and tx power datarate = verify_params->current_datarate; tx_power = verify_params->current_tx_power; } if (status != 0) { // Verify channel datarate if (verify_channel_DR(verify_params->channel_mask, datarate) == false) { status &= 0xFD; // Datarate KO } // Verify tx power if (val_in_range(tx_power, phy_params.max_tx_power, phy_params.min_tx_power) == false) { // Verify if the maximum TX power is exceeded if (phy_params.max_tx_power > tx_power) { // Apply maximum TX power. Accept TX power. tx_power = phy_params.max_tx_power; } else { status &= 0xFB; // TxPower KO } } } // If the status is ok, verify the NbRep if (status == 0x07 && nb_repetitions == 0) { // Restore the default value according to the LoRaWAN specification nb_repetitions = 1; } // Apply changes *dr = datarate; *tx_pow = tx_power; *nb_rep = nb_repetitions; return status; } double LoRaPHY::compute_symb_timeout_lora(uint8_t phy_dr, uint32_t bandwidth) { return ((double)(1 << phy_dr) / (double) bandwidth) * 1000; } double LoRaPHY::compute_symb_timeout_fsk(uint8_t phy_dr) { return (8.0 / (double) phy_dr); // 1 symbol equals 1 byte } void LoRaPHY::get_rx_window_params(double t_symb, uint8_t min_rx_symb, uint32_t rx_error, uint32_t wakeup_time, uint32_t *window_timeout, int32_t *window_offset) { // Computed number of symbols *window_timeout = MAX((uint32_t) ceil(((2 * min_rx_symb - 8) * t_symb + 2 * rx_error) / t_symb), min_rx_symb); *window_offset = (int32_t) ceil((4.0 * t_symb) - ((*window_timeout * t_symb) / 2.0 ) - wakeup_time); } int8_t LoRaPHY::compute_tx_power(int8_t tx_power_idx, float max_eirp, float antenna_gain) { int8_t phy_tx_power = 0; phy_tx_power = (int8_t) floor((max_eirp - (tx_power_idx * 2U)) - antenna_gain); return phy_tx_power; } int8_t LoRaPHY::get_next_lower_dr(int8_t dr, int8_t min_dr) { uint8_t next_lower_dr = dr; do { if (next_lower_dr != min_dr) { next_lower_dr -= 1; } } while ((next_lower_dr != min_dr) && !is_datarate_supported(next_lower_dr)); return next_lower_dr; } uint8_t LoRaPHY::get_bandwidth(uint8_t dr) { uint32_t *bandwidths = (uint32_t *) phy_params.bandwidths.table; switch (bandwidths[dr]) { default: case 125000: return 0; case 250000: return 1; case 500000: return 2; } } uint8_t LoRaPHY::enabled_channel_count(uint8_t datarate, const uint16_t *channel_mask, uint8_t *channel_indices, uint8_t *delayTx) { uint8_t count = 0; uint8_t delay_transmission = 0; for (uint8_t i = 0; i < phy_params.max_channel_cnt; i++) { if (mask_bit_test(channel_mask, i)) { if (val_in_range(datarate, phy_params.channels.channel_list[i].dr_range.fields.min, phy_params.channels.channel_list[i].dr_range.fields.max) == 0) { // data rate range invalid for this channel continue; } band_t *band_table = (band_t *) phy_params.bands.table; if (band_table[phy_params.channels.channel_list[i].band].off_time > 0) { // Check if the band is available for transmission delay_transmission++; continue; } // otherwise count the channel as enabled channel_indices[count++] = i; } } *delayTx = delay_transmission; return count; } bool LoRaPHY::is_datarate_supported(const int8_t datarate) const { if (datarate < phy_params.datarates.size) { return (((uint8_t *)phy_params.datarates.table)[datarate] != 0) ? true : false; } else { return false; } } void LoRaPHY::reset_to_default_values(loramac_protocol_params *params, bool init) { if (init) { params->is_dutycycle_on = phy_params.duty_cycle_enabled; params->sys_params.max_rx_win_time = phy_params.max_rx_window; params->sys_params.recv_delay1 = phy_params.recv_delay1; params->sys_params.recv_delay2 = phy_params.recv_delay2; params->sys_params.join_accept_delay1 = phy_params.join_accept_delay1; params->sys_params.join_accept_delay2 = phy_params.join_accept_delay2; params->sys_params.downlink_dwell_time = phy_params.dl_dwell_time_setting; } params->sys_params.channel_tx_power = get_default_tx_power(); // We shall always start with highest achievable data rate. // Subsequent decrease in data rate will mean increase in range henceforth. params->sys_params.channel_data_rate = get_default_max_tx_datarate(); params->sys_params.rx1_dr_offset = phy_params.default_rx1_dr_offset; params->sys_params.rx2_channel.frequency = get_default_rx2_frequency(); params->sys_params.rx2_channel.datarate = get_default_rx2_datarate(); params->sys_params.uplink_dwell_time = phy_params.ul_dwell_time_setting; params->sys_params.max_eirp = phy_params.default_max_eirp; params->sys_params.antenna_gain = phy_params.default_antenna_gain; } int8_t LoRaPHY::get_next_lower_tx_datarate(int8_t datarate) { if (phy_params.ul_dwell_time_setting == 0) { return get_next_lower_dr(datarate, phy_params.min_tx_datarate); } return get_next_lower_dr(datarate, phy_params.dwell_limit_datarate); } uint8_t LoRaPHY::get_minimum_rx_datarate() { if (phy_params.dl_dwell_time_setting == 0) { return phy_params.min_rx_datarate; } return phy_params.dwell_limit_datarate; } uint8_t LoRaPHY::get_minimum_tx_datarate() { if (phy_params.ul_dwell_time_setting == 0) { return phy_params.min_tx_datarate; } return phy_params.dwell_limit_datarate; } uint8_t LoRaPHY::get_default_tx_datarate() { return phy_params.default_datarate; } uint8_t LoRaPHY::get_default_max_tx_datarate() { return phy_params.default_max_datarate; } uint8_t LoRaPHY::get_default_tx_power() { return phy_params.default_tx_power; } uint8_t LoRaPHY::get_max_payload(uint8_t datarate, bool use_repeater) { uint8_t *payload_table = NULL; if (use_repeater) { // if (datarate >= phy_params.payloads_with_repeater.size) { // //TODO: Can this ever happen? If yes, should we return 0? // } payload_table = (uint8_t *) phy_params.payloads_with_repeater.table; } else { payload_table = (uint8_t *) phy_params.payloads.table; } return payload_table[datarate]; } uint16_t LoRaPHY::get_maximum_frame_counter_gap() { return phy_params.max_fcnt_gap; } uint32_t LoRaPHY::get_ack_timeout() { uint16_t ack_timeout_rnd = phy_params.ack_timeout_rnd; return (phy_params.ack_timeout + get_random(-ack_timeout_rnd, ack_timeout_rnd)); } uint32_t LoRaPHY::get_default_rx2_frequency() { return phy_params.rx_window2_frequency; } uint8_t LoRaPHY::get_default_rx2_datarate() { return phy_params.rx_window2_datarate; } uint16_t *LoRaPHY::get_channel_mask(bool get_default) { if (get_default) { return phy_params.channels.default_mask; } return phy_params.channels.mask; } uint8_t LoRaPHY::get_max_nb_channels() { return phy_params.max_channel_cnt; } channel_params_t *LoRaPHY::get_phy_channels() { return phy_params.channels.channel_list; } bool LoRaPHY::is_custom_channel_plan_supported() { return phy_params.custom_channelplans_supported; } void LoRaPHY::restore_default_channels() { // Restore channels default mask for (uint8_t i = 0; i < phy_params.channels.mask_size; i++) { phy_params.channels.mask[i] |= phy_params.channels.default_mask[i]; } } bool LoRaPHY::verify_rx_datarate(uint8_t datarate) { if (is_datarate_supported(datarate)) { if (phy_params.dl_dwell_time_setting == 0) { //TODO: Check this! datarate must be same as minimum! Can be compared directly if OK return val_in_range(datarate, phy_params.min_rx_datarate, phy_params.max_rx_datarate); } else { return val_in_range(datarate, phy_params.dwell_limit_datarate, phy_params.max_rx_datarate); } } return false; } bool LoRaPHY::verify_tx_datarate(uint8_t datarate, bool use_default) { if (!is_datarate_supported(datarate)) { return false; } if (use_default) { return val_in_range(datarate, phy_params.default_datarate, phy_params.default_max_datarate); } else if (phy_params.ul_dwell_time_setting == 0) { return val_in_range(datarate, phy_params.min_tx_datarate, phy_params.max_tx_datarate); } else { return val_in_range(datarate, phy_params.dwell_limit_datarate, phy_params.max_tx_datarate); } } bool LoRaPHY::verify_tx_power(uint8_t tx_power) { return val_in_range(tx_power, phy_params.max_tx_power, phy_params.min_tx_power); } bool LoRaPHY::verify_duty_cycle(bool cycle) { if (cycle == phy_params.duty_cycle_enabled) { return true; } return false; } bool LoRaPHY::verify_nb_join_trials(uint8_t nb_join_trials) { if (nb_join_trials < MBED_CONF_LORA_NB_TRIALS) { return false; } return true; } void LoRaPHY::apply_cf_list(const uint8_t *payload, uint8_t size) { // if the underlying PHY doesn't support CF-List, ignore the request if (!phy_params.cflist_supported) { return; } channel_params_t new_channel; // Setup default datarate range new_channel.dr_range.value = (phy_params.default_max_datarate << 4) | phy_params.default_datarate; // Size of the optional CF list if (size != 16) { return; } // Last byte is RFU, don't take it into account // NOTE: Currently the PHY layers supported by LoRaWAN who accept a CF-List // define first 2 or 3 channels as default channels. this function is // written keeping that in mind. If there would be a PHY in the future that // accepts CF-list but have haphazard allocation of default channels, we // should override this function in the implementation of that particular // PHY. for (uint8_t i = 0, channel_id = phy_params.default_channel_cnt; channel_id < phy_params.max_channel_cnt; i += 3, channel_id++) { if (channel_id < (phy_params.cflist_channel_cnt + phy_params.default_channel_cnt)) { // Channel frequency new_channel.frequency = (uint32_t) payload[i]; new_channel.frequency |= ((uint32_t) payload[i + 1] << 8); new_channel.frequency |= ((uint32_t) payload[i + 2] << 16); new_channel.frequency *= 100; // Initialize alternative frequency to 0 new_channel.rx1_frequency = 0; } else { new_channel.frequency = 0; new_channel.dr_range.value = 0; new_channel.rx1_frequency = 0; } if (new_channel.frequency != 0) { //lookup for band new_channel.band = lookup_band_for_frequency(new_channel.frequency); // Try to add channel add_channel(&new_channel, channel_id); } else { remove_channel(channel_id); } } } bool LoRaPHY::get_next_ADR(bool restore_channel_mask, int8_t &dr_out, int8_t &tx_power_out, uint32_t &adr_ack_cnt) { bool set_adr_ack_bit = false; uint16_t ack_limit_plus_delay = phy_params.adr_ack_limit + phy_params.adr_ack_delay; if (dr_out == phy_params.min_tx_datarate) { adr_ack_cnt = 0; return set_adr_ack_bit; } if (adr_ack_cnt < phy_params.adr_ack_limit) { return set_adr_ack_bit; } // ADR ack counter is larger than ADR-ACK-LIMIT set_adr_ack_bit = true; tx_power_out = phy_params.max_tx_power; if (adr_ack_cnt >= ack_limit_plus_delay) { if ((adr_ack_cnt % phy_params.adr_ack_delay) == 1) { // Decrease the datarate dr_out = get_next_lower_tx_datarate(dr_out); if (dr_out == phy_params.min_tx_datarate) { // We must set adrAckReq to false as soon as we reach the lowest datarate set_adr_ack_bit = false; if (restore_channel_mask) { // Re-enable default channels restore_default_channels(); } } } } return set_adr_ack_bit; } void LoRaPHY::compute_rx_win_params(int8_t datarate, uint8_t min_rx_symbols, uint32_t rx_error, rx_config_params_t *rx_conf_params) { double t_symbol = 0.0; // Get the datarate, perform a boundary check rx_conf_params->datarate = MIN(datarate, phy_params.max_rx_datarate); rx_conf_params->bandwidth = get_bandwidth(rx_conf_params->datarate); if (phy_params.fsk_supported && rx_conf_params->datarate == phy_params.max_rx_datarate) { // FSK t_symbol = compute_symb_timeout_fsk(((uint8_t *)phy_params.datarates.table)[rx_conf_params->datarate]); } else { // LoRa t_symbol = compute_symb_timeout_lora(((uint8_t *)phy_params.datarates.table)[rx_conf_params->datarate], ((uint32_t *)phy_params.bandwidths.table)[rx_conf_params->datarate]); } get_rx_window_params(t_symbol, min_rx_symbols, rx_error, RADIO_WAKEUP_TIME, &rx_conf_params->window_timeout, &rx_conf_params->window_offset); } bool LoRaPHY::rx_config(rx_config_params_t *rx_conf) { radio_modems_t modem; uint8_t dr = rx_conf->datarate; uint8_t max_payload = 0; uint8_t phy_dr = 0; uint32_t frequency = rx_conf->frequency; if (rx_conf->rx_slot == RX_SLOT_WIN_1) { // Apply window 1 frequency frequency = phy_params.channels.channel_list[rx_conf->channel].frequency; // Apply the alternative RX 1 window frequency, if it is available if (phy_params.channels.channel_list[rx_conf->channel].rx1_frequency != 0) { frequency = phy_params.channels.channel_list[rx_conf->channel].rx1_frequency; } } // Read the physical datarate from the datarates table uint8_t *datarate_table = (uint8_t *) phy_params.datarates.table; uint8_t *payload_table = (uint8_t *) phy_params.payloads.table; uint8_t *payload_with_repeater_table = (uint8_t *) phy_params.payloads_with_repeater.table; phy_dr = datarate_table[dr]; _radio->lock(); _radio->set_channel(frequency); // Radio configuration if (dr == DR_7 && phy_params.fsk_supported) { modem = MODEM_FSK; _radio->set_rx_config(modem, 50000, phy_dr * 1000, 0, 83333, 5, rx_conf->window_timeout, false, 0, true, 0, 0, false, rx_conf->is_rx_continuous); } else { modem = MODEM_LORA; _radio->set_rx_config(modem, rx_conf->bandwidth, phy_dr, 1, 0, MBED_CONF_LORA_DOWNLINK_PREAMBLE_LENGTH, rx_conf->window_timeout, false, 0, false, 0, 0, true, rx_conf->is_rx_continuous); } if (rx_conf->is_repeater_supported) { max_payload = payload_with_repeater_table[dr]; } else { max_payload = payload_table[dr]; } _radio->set_max_payload_length(modem, max_payload + LORA_MAC_FRMPAYLOAD_OVERHEAD); _radio->unlock(); return true; } bool LoRaPHY::tx_config(tx_config_params_t *tx_conf, int8_t *tx_power, lorawan_time_t *tx_toa) { radio_modems_t modem; int8_t phy_dr = ((uint8_t *)phy_params.datarates.table)[tx_conf->datarate]; channel_params_t *list = phy_params.channels.channel_list; uint8_t band_idx = list[tx_conf->channel].band; band_t *bands = (band_t *)phy_params.bands.table; // limit TX power if set to too much tx_conf->tx_power = MAX(tx_conf->tx_power, bands[band_idx].max_tx_pwr); uint8_t bandwidth = get_bandwidth(tx_conf->datarate); int8_t phy_tx_power = 0; // Calculate physical TX power phy_tx_power = compute_tx_power(tx_conf->tx_power, tx_conf->max_eirp, tx_conf->antenna_gain); _radio->lock(); // Setup the radio frequency _radio->set_channel(list[tx_conf->channel].frequency); if (tx_conf->datarate == phy_params.max_tx_datarate) { // High Speed FSK channel modem = MODEM_FSK; _radio->set_tx_config(modem, phy_tx_power, 25000, bandwidth, phy_dr * 1000, 0, 5, false, true, 0, 0, false, 3000); } else { modem = MODEM_LORA; _radio->set_tx_config(modem, 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 _radio->set_max_payload_length(modem, tx_conf->pkt_len); // Get the time-on-air of the next tx frame *tx_toa = _radio->time_on_air(modem, tx_conf->pkt_len); _radio->unlock(); *tx_power = tx_conf->tx_power; return true; } uint8_t LoRaPHY::link_ADR_request(adr_req_params_t *link_adr_req, int8_t *dr_out, int8_t *tx_power_out, uint8_t *nb_rep_out, uint8_t *nb_bytes_processed) { uint8_t status = 0x07; link_adr_params_t adr_settings; uint8_t next_index = 0; uint8_t bytes_processed = 0; // rather than dynamically allocating memory, we choose to set // a channel mask list size of unity here as we know that all // the PHY layer implementations who have more than 16 channels, i.e., // have channel mask list size more than unity, override this method. uint16_t temp_channel_mask[1] = {0}; verify_adr_params_t verify_params; while (bytes_processed < link_adr_req->payload_size) { // Get ADR request parameters next_index = parse_link_ADR_req(&(link_adr_req->payload[bytes_processed]), &adr_settings); if (next_index == 0) { break; // break loop, since no more request has been found } // Update bytes processed bytes_processed += next_index; // Revert status, as we only check the last ADR request for the channel mask KO status = 0x07; // Setup temporary channels mask temp_channel_mask[0] = adr_settings.channel_mask; // Verify channels mask if (adr_settings.ch_mask_ctrl == 0 && temp_channel_mask[0] == 0) { status &= 0xFE; // Channel mask KO } // channel mask applies to first 16 channels if (adr_settings.ch_mask_ctrl == 0 || adr_settings.ch_mask_ctrl == 6) { for (uint8_t i = 0; i < phy_params.max_channel_cnt; i++) { // turn on all channels if channel mask control is 6 if (adr_settings.ch_mask_ctrl == 6) { if (phy_params.channels.channel_list[i].frequency != 0) { mask_bit_set(temp_channel_mask, i); } continue; } // if channel mask control is 0, we test the bits and // frequencies and change the status if we find a discrepancy if ((mask_bit_test(temp_channel_mask, i)) && (phy_params.channels.channel_list[i].frequency == 0)) { // Trying to enable an undefined channel status &= 0xFE; // Channel mask KO } } } else { // Channel mask control applies to RFUs status &= 0xFE; // Channel mask KO } } if (is_datarate_supported(adr_settings.datarate)) { verify_params.status = status; verify_params.adr_enabled = link_adr_req->adr_enabled; verify_params.current_datarate = link_adr_req->current_datarate; verify_params.current_tx_power = link_adr_req->current_tx_power; verify_params.current_nb_rep = link_adr_req->current_nb_rep; verify_params.datarate = adr_settings.datarate; verify_params.tx_power = adr_settings.tx_power; verify_params.nb_rep = adr_settings.nb_rep; verify_params.channel_mask = temp_channel_mask; // Verify the parameters and update, if necessary status = verify_link_ADR_req(&verify_params, &adr_settings.datarate, &adr_settings.tx_power, &adr_settings.nb_rep); } else { status &= 0xFD; // Datarate KO } // Update channelsMask if everything is correct if (status == 0x07) { // Set the channels mask to a default value memset(phy_params.channels.mask, 0, sizeof(uint16_t)*phy_params.channels.mask_size); // Update the channels mask copy_channel_mask(phy_params.channels.mask, temp_channel_mask, phy_params.channels.mask_size); } // Update status variables *dr_out = adr_settings.datarate; *tx_power_out = adr_settings.tx_power; *nb_rep_out = adr_settings.nb_rep; *nb_bytes_processed = bytes_processed; return status; } uint8_t LoRaPHY::accept_rx_param_setup_req(rx_param_setup_req_t *params) { uint8_t status = 0x07; if (lookup_band_for_frequency(params->frequency) < 0) { status &= 0xFE; } // Verify radio frequency if (_radio->check_rf_frequency(params->frequency) == false) { status &= 0xFE; // Channel frequency KO } // Verify datarate if (val_in_range(params->datarate, phy_params.min_rx_datarate, phy_params.max_rx_datarate) == 0) { status &= 0xFD; // Datarate KO } // Verify datarate offset if (val_in_range(params->dr_offset, phy_params.min_rx1_dr_offset, phy_params.max_rx1_dr_offset) == 0) { status &= 0xFB; // Rx1DrOffset range KO } return status; } bool LoRaPHY::accept_tx_param_setup_req(uint8_t ul_dwell_time, uint8_t dl_dwell_time) { if (phy_params.accept_tx_param_setup_req) { phy_params.ul_dwell_time_setting = ul_dwell_time; phy_params.dl_dwell_time_setting = dl_dwell_time; } return phy_params.accept_tx_param_setup_req; } int LoRaPHY::lookup_band_for_frequency(uint32_t freq) const { // check all sub bands (if there are sub-bands) to check if the given // frequency falls into any of the frequency ranges for (int band = 0; band < phy_params.bands.size; band++) { if (verify_frequency_for_band(freq, band)) { return band; } } return -1; } bool LoRaPHY::verify_frequency_for_band(uint32_t freq, uint8_t band) const { band_t *bands_table = (band_t *)phy_params.bands.table; if (freq <= bands_table[band].higher_band_freq && freq >= bands_table[band].lower_band_freq) { return true; } else { return false; } } uint8_t LoRaPHY::dl_channel_request(uint8_t channel_id, uint32_t rx1_frequency) { if (!phy_params.dl_channel_req_supported) { return 0; } uint8_t status = 0x03; // Verify if the frequency is supported int band = lookup_band_for_frequency(rx1_frequency); if (band < 0) { status &= 0xFE; } // Verify if an uplink frequency exists if (phy_params.channels.channel_list[channel_id].frequency == 0) { status &= 0xFD; } // Apply Rx1 frequency, if the status is OK if (status == 0x03) { phy_params.channels.channel_list[channel_id].rx1_frequency = rx1_frequency; } return status; } /** * Alternate datarate algorithm for join requests. * - We check from the PHY and take note of total * number of data rates available upto the default data rate for * default channels. * * - Application sets a total number of re-trials for a Join Request, i.e., * MBED_CONF_LORA_NB_TRIALS. So MAC layer will send us a counter * nb_trials < MBED_CONF_LORA_NB_TRIALS which is the current number of trial. * * - We roll over total available datarates and pick one according to the * number of trial sequentially. * * - We always start from the Default Data rate and and set the next lower * data rate for every iteration. * * - MAC layer will stop when maximum number of re-trials, i.e., * MBED_CONF_LORA_NB_TRIALS is achieved. * * So essentially MBED_CONF_LORA_NB_TRIALS should be a multiple of range of * data rates available. For example, in EU868 band, default max. data rate is * DR_5 and min. data rate is DR_0, so total data rates available are 6. * * Hence MBED_CONF_LORA_NB_TRIALS should be a multiple of 6. Setting, * MBED_CONF_LORA_NB_TRIALS = 6 would mean that every data rate will be tried * exactly once starting from the largest and finishing at the smallest. * * PHY layers which do not have datarates scheme similar to EU band will ofcourse * override this method. */ int8_t LoRaPHY::get_alternate_DR(uint8_t nb_trials) { int8_t datarate = 0; uint8_t total_nb_datrates = (phy_params.default_max_datarate - phy_params.min_tx_datarate) + 1; uint8_t res = nb_trials % total_nb_datrates; if (res == 0) { datarate = phy_params.min_tx_datarate; } else if (res == 1) { datarate = phy_params.default_max_datarate; } else { datarate = (phy_params.default_max_datarate - res) + 1; } return datarate; } void LoRaPHY::calculate_backoff(bool joined, bool last_tx_was_join_req, bool dc_enabled, uint8_t channel, lorawan_time_t elapsed_time, lorawan_time_t tx_toa) { band_t *band_table = (band_t *) phy_params.bands.table; channel_params_t *channel_list = phy_params.channels.channel_list; uint8_t band_idx = channel_list[channel].band; uint16_t duty_cycle = band_table[band_idx].duty_cycle; uint16_t join_duty_cycle = 0; // Reset time-off to initial value. band_table[band_idx].off_time = 0; if (MBED_CONF_LORA_DUTY_CYCLE_ON_JOIN && joined == false) { // Get the join duty cycle if (elapsed_time < 3600000) { join_duty_cycle = BACKOFF_DC_1_HOUR; } else if (elapsed_time < (3600000 + 36000000)) { join_duty_cycle = BACKOFF_DC_10_HOURS; } else { join_duty_cycle = BACKOFF_DC_24_HOURS; } // Apply the most restricting duty cycle duty_cycle = MAX(duty_cycle, join_duty_cycle); } // No back-off if the last frame was not a join request and when the // duty cycle is not enabled if (dc_enabled == false && last_tx_was_join_req == false) { band_table[band_idx].off_time = 0; } else { // Apply band time-off. band_table[band_idx].off_time = tx_toa * duty_cycle - tx_toa; } } lorawan_status_t LoRaPHY::set_next_channel(channel_selection_params_t *params, uint8_t *channel, lorawan_time_t *time, lorawan_time_t *aggregate_timeoff) { uint8_t channel_count = 0; uint8_t delay_tx = 0; // Note here that the PHY layer implementations which have more than // 16 channels at their disposal, override this function. That's why // it is safe to assume that we are dealing with a block of 16 channels // i.e., EU like implementations. So rather than dynamically allocating // memory we chose to use a magic number of 16 uint8_t enabled_channels[16]; memset(enabled_channels, 0xFF, sizeof(uint8_t) * 16); lorawan_time_t next_tx_delay = 0; band_t *band_table = (band_t *) phy_params.bands.table; if (num_active_channels(phy_params.channels.mask, 0, phy_params.channels.mask_size) == 0) { // Reactivate default channels copy_channel_mask(phy_params.channels.mask, phy_params.channels.default_mask, phy_params.channels.mask_size); } if (params->aggregate_timeoff <= _lora_time->get_elapsed_time(params->last_aggregate_tx_time)) { // Reset Aggregated time off *aggregate_timeoff = 0; // Update bands Time OFF next_tx_delay = update_band_timeoff(params->joined, params->dc_enabled, band_table, phy_params.bands.size); // Search how many channels are enabled channel_count = enabled_channel_count(params->current_datarate, phy_params.channels.mask, enabled_channels, &delay_tx); } else { delay_tx++; next_tx_delay = params->aggregate_timeoff - _lora_time->get_elapsed_time(params->last_aggregate_tx_time); } if (channel_count > 0) { // We found a valid channel *channel = enabled_channels[get_random(0, channel_count - 1)]; *time = 0; return LORAWAN_STATUS_OK; } if (delay_tx > 0) { // Delay transmission due to AggregatedTimeOff or to a band time off *time = next_tx_delay; return LORAWAN_STATUS_DUTYCYCLE_RESTRICTED; } // Datarate not supported by any channel, restore defaults copy_channel_mask(phy_params.channels.mask, phy_params.channels.default_mask, phy_params.channels.mask_size); *time = 0; return LORAWAN_STATUS_NO_CHANNEL_FOUND; } lorawan_status_t LoRaPHY::add_channel(const channel_params_t *new_channel, uint8_t id) { bool dr_invalid = false; bool freq_invalid = false; if (!phy_params.custom_channelplans_supported || id >= phy_params.max_channel_cnt) { return LORAWAN_STATUS_PARAMETER_INVALID; } // Validate the datarate range if (val_in_range(new_channel->dr_range.fields.min, phy_params.min_tx_datarate, phy_params.max_tx_datarate) == 0) { dr_invalid = true; } if (val_in_range(new_channel->dr_range.fields.max, phy_params.min_tx_datarate, phy_params.max_tx_datarate) == 0) { dr_invalid = true; } if (new_channel->dr_range.fields.min > new_channel->dr_range.fields.max) { dr_invalid = true; } // Default channels don't accept all values if (id < phy_params.default_channel_cnt) { // Validate the datarate range for min: must be DR_0 if (new_channel->dr_range.fields.min != DR_0) { dr_invalid = true; } // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE if (val_in_range(new_channel->dr_range.fields.max, phy_params.default_max_datarate, phy_params.max_tx_datarate) == 0) { dr_invalid = true; } // We are not allowed to change the frequency if (new_channel->frequency != phy_params.channels.channel_list[id].frequency) { freq_invalid = true; } } // Check frequency if (!freq_invalid) { if (new_channel->band >= phy_params.bands.size || verify_frequency_for_band(new_channel->frequency, new_channel->band) == false) { freq_invalid = true; } } // Check status if (dr_invalid && freq_invalid) { return LORAWAN_STATUS_FREQ_AND_DR_INVALID; } if (dr_invalid) { return LORAWAN_STATUS_DATARATE_INVALID; } if (freq_invalid) { return LORAWAN_STATUS_FREQUENCY_INVALID; } memcpy(&(phy_params.channels.channel_list[id]), new_channel, sizeof(channel_params_t)); phy_params.channels.channel_list[id].band = new_channel->band; mask_bit_set(phy_params.channels.mask, id); return LORAWAN_STATUS_OK; } bool LoRaPHY::remove_channel(uint8_t channel_id) { // upper layers are checking if the custom channel planning is supported or // not. So we don't need to worry about that if (mask_bit_test(phy_params.channels.default_mask, channel_id)) { return false; } // Remove the channel from the list of channels const channel_params_t empty_channel = { 0, 0, {0}, 0 }; phy_params.channels.channel_list[channel_id] = empty_channel; return disable_channel(phy_params.channels.mask, channel_id, phy_params.max_channel_cnt); } void LoRaPHY::set_tx_cont_mode(cw_mode_params_t *params, uint32_t given_frequency) { band_t *bands_table = (band_t *) phy_params.bands.table; channel_params_t *channels = phy_params.channels.channel_list; if (params->tx_power > bands_table[channels[params->channel].band].max_tx_pwr) { params->tx_power = bands_table[channels[params->channel].band].max_tx_pwr; } int8_t phy_tx_power = 0; uint32_t frequency = 0; if (given_frequency == 0) { frequency = channels[params->channel].frequency; } else { frequency = given_frequency; } // Calculate physical TX power if (params->max_eirp > 0 && params->antenna_gain > 0) { phy_tx_power = compute_tx_power(params->tx_power, params->max_eirp, params->antenna_gain); } else { phy_tx_power = params->tx_power; } _radio->lock(); _radio->set_tx_continuous_wave(frequency, phy_tx_power, params->timeout); _radio->unlock(); } uint8_t LoRaPHY::apply_DR_offset(int8_t dr, int8_t dr_offset) { int8_t datarate = dr - dr_offset; if (datarate < 0) { datarate = phy_params.min_tx_datarate; } return datarate; }