diff --git a/features/lorawan/LoRaWANInterface.h b/features/lorawan/LoRaWANInterface.h index f8d35b425fa..b181ca10465 100644 --- a/features/lorawan/LoRaWANInterface.h +++ b/features/lorawan/LoRaWANInterface.h @@ -34,6 +34,7 @@ class LoRaWANInterface: public LoRaWANBase { * */ LoRaWANInterface(LoRaRadio& radio); + virtual ~LoRaWANInterface(); /** Initialize the LoRa stack. @@ -44,7 +45,7 @@ class LoRaWANInterface: public LoRaWANBase { * * @return 0 on success, a negative error code on failure. */ - virtual lorawan_status_t initialize(events::EventQueue *ev_queue) ; + virtual lorawan_status_t initialize(events::EventQueue *ev_queue); /** Connect OTAA or ABP using Mbed-OS config system * diff --git a/features/lorawan/LoRaWANStack.cpp b/features/lorawan/LoRaWANStack.cpp index 418322ed141..e055d3b7c0a 100644 --- a/features/lorawan/LoRaWANStack.cpp +++ b/features/lorawan/LoRaWANStack.cpp @@ -87,21 +87,15 @@ lorawan_status_t LoRaWANStack::set_application_port(uint8_t port) LoRaWANStack::LoRaWANStack() : _loramac(_lora_time), _lora_phy(_lora_time), _device_current_state(DEVICE_STATE_NOT_INITIALIZED), _mac_handlers(NULL), - _num_retry(1), _queue(NULL), _duty_cycle_on(MBED_CONF_LORA_DUTY_CYCLE_ON) + _num_retry(1), _app_port(INVALID_PORT), _duty_cycle_on(MBED_CONF_LORA_DUTY_CYCLE_ON), + _queue(NULL) { #ifdef MBED_CONF_LORA_APP_PORT - // is_port_valid() is not virtual, so we can call it in constructor if (is_port_valid(MBED_CONF_LORA_APP_PORT)) { _app_port = MBED_CONF_LORA_APP_PORT; } else { tr_error("User defined port in .json is illegal."); - _app_port = INVALID_PORT; } - -#else - // initialize it to INVALID_PORT (255) an illegal port number. - // user should set the port - _app_port = INVALID_PORT; #endif memset(&_lw_session, 0, sizeof(_lw_session)); @@ -175,95 +169,6 @@ lorawan_status_t LoRaWANStack::initialize_mac_layer(EventQueue *queue) return lora_state_machine(); } -#if defined(LORAWAN_COMPLIANCE_TEST) -/** - * - * Prepares the upload message to reserved ports - * - * \param port Application port - */ -void LoRaWANStack::prepare_special_tx_frame(uint8_t port) -{ - if (port == 224) { - // Clear any normal message stuff before compliance test. - memset(&_tx_msg, 0, sizeof(_tx_msg)); - - if (_compliance_test.link_check == true) { - _compliance_test.link_check = false; - _compliance_test.state = 1; - _tx_msg.f_buffer_size = 3; - _tx_msg.f_buffer[0] = 5; - _tx_msg.f_buffer[1] = _compliance_test.demod_margin; - _tx_msg.f_buffer[2] = _compliance_test.nb_gateways; - } else { - switch (_compliance_test.state) { - case 4: - _compliance_test.state = 1; - _tx_msg.f_buffer_size = _compliance_test.app_data_size; - - _tx_msg.f_buffer[0] = _compliance_test.app_data_buffer[0]; - for(uint8_t i = 1; i < MIN(_compliance_test.app_data_size, MBED_CONF_LORA_TX_MAX_SIZE); ++i) { - _tx_msg.f_buffer[i] = _compliance_test.app_data_buffer[i]; - } - break; - case 1: - _tx_msg.f_buffer_size = 2; - _tx_msg.f_buffer[0] = _compliance_test.downlink_counter >> 8; - _tx_msg.f_buffer[1] = _compliance_test.downlink_counter; - break; - } - } - } -} - -/** Hands over the compliance test frame to MAC layer - * - * \return returns the state of the LoRa MAC - */ -lorawan_status_t LoRaWANStack::send_compliance_test_frame_to_mac() -{ - loramac_mcps_req_t mcps_req; - - get_phy_params_t phy_params; - phy_param_t default_datarate; - phy_params.attribute = PHY_DEF_TX_DR; - default_datarate = _lora_phy.get_phy_params(&phy_params); - - prepare_special_tx_frame(_compliance_test.app_port); - - if (!_compliance_test.is_tx_confirmed) { - mcps_req.type = MCPS_UNCONFIRMED; - mcps_req.req.unconfirmed.fport = _compliance_test.app_port; - mcps_req.f_buffer = _tx_msg.f_buffer; - mcps_req.f_buffer_size = _tx_msg.f_buffer_size; - mcps_req.req.unconfirmed.data_rate = default_datarate.value; - - tr_info("Transmit unconfirmed compliance test frame %d bytes.", mcps_req.f_buffer_size); - - for (uint8_t i = 0; i < mcps_req.f_buffer_size; ++i) { - tr_info("Byte %d, data is 0x%x", i+1, ((uint8_t*)mcps_req.f_buffer)[i]); - } - } else if (_compliance_test.is_tx_confirmed) { - mcps_req.type = MCPS_CONFIRMED; - mcps_req.req.confirmed.fport = _compliance_test.app_port; - mcps_req.f_buffer = _tx_msg.f_buffer; - mcps_req.f_buffer_size = _tx_msg.f_buffer_size; - mcps_req.req.confirmed.nb_trials = _num_retry; - mcps_req.req.confirmed.data_rate = default_datarate.value; - - tr_info("Transmit confirmed compliance test frame %d bytes.", mcps_req.f_buffer_size); - - for (uint8_t i = 0; i < mcps_req.f_buffer_size; ++i) { - tr_info("Byte %d, data is 0x%x", i+1, ((uint8_t*)mcps_req.f_buffer)[i]); - } - } else { - return LORAWAN_STATUS_SERVICE_UNKNOWN; - } - - return mcps_request_handler(&mcps_req); -} -#endif - uint16_t LoRaWANStack::check_possible_tx_size(uint16_t size) { loramac_tx_info_t tx_info; @@ -286,51 +191,48 @@ lorawan_status_t LoRaWANStack::send_frame_to_mac() lorawan_status_t status; loramac_mib_req_confirm_t mib_get_params; - get_phy_params_t phy_params; - phy_param_t default_datarate; - phy_params.attribute = PHY_DEF_TX_DR; - default_datarate = _lora_phy.get_phy_params(&phy_params); - mcps_req.type = _tx_msg.type; if (MCPS_UNCONFIRMED == mcps_req.type) { - mcps_req.req.unconfirmed.fport = _tx_msg.message_u.unconfirmed.fport; mcps_req.f_buffer = _tx_msg.f_buffer; - mcps_req.f_buffer_size = _tx_msg.f_buffer_size; + mcps_req.fport = _tx_msg.fport; + mcps_req.nb_trials = 1; mib_get_params.type = MIB_CHANNELS_DATARATE; if(mib_get_request(&mib_get_params) != LORAWAN_STATUS_OK) { tr_debug("Couldn't get MIB parameters: Using default data rate"); - mcps_req.req.unconfirmed.data_rate = default_datarate.value; + mcps_req.data_rate = _lora_phy.get_default_tx_datarate(); } else { - mcps_req.req.unconfirmed.data_rate = mib_get_params.param.channel_data_rate; + mcps_req.data_rate = mib_get_params.param.channel_data_rate; } } else if (mcps_req.type == MCPS_CONFIRMED) { - mcps_req.req.confirmed.fport = _tx_msg.message_u.confirmed.fport; mcps_req.f_buffer = _tx_msg.f_buffer; mcps_req.f_buffer_size = _tx_msg.f_buffer_size; - mcps_req.req.confirmed.nb_trials = _tx_msg.message_u.confirmed.nb_trials; + mcps_req.fport = _tx_msg.fport; + mcps_req.nb_trials = _tx_msg.nb_trials; mib_get_params.type = MIB_CHANNELS_DATARATE; if(mib_get_request(&mib_get_params) != LORAWAN_STATUS_OK) { tr_debug("Couldn't get MIB parameters: Using default data rate"); - mcps_req.req.confirmed.data_rate = default_datarate.value; + mcps_req.data_rate = _lora_phy.get_default_tx_datarate(); } else { - mcps_req.req.confirmed.data_rate = mib_get_params.param.channel_data_rate; + mcps_req.data_rate = mib_get_params.param.channel_data_rate; } } else if ( mcps_req.type == MCPS_PROPRIETARY) { mcps_req.f_buffer = _tx_msg.f_buffer; mcps_req.f_buffer_size = _tx_msg.f_buffer_size; + mcps_req.fport = 0; + mcps_req.nb_trials = 1; mib_get_params.type = MIB_CHANNELS_DATARATE; if(mib_get_request(&mib_get_params) != LORAWAN_STATUS_OK) { tr_debug("Couldn't get MIB parameters: Using default data rate"); - mcps_req.req.proprietary.data_rate = default_datarate.value; + mcps_req.data_rate = _lora_phy.get_default_tx_datarate(); } else { - mcps_req.req.proprietary.data_rate = mib_get_params.param.channel_data_rate; + mcps_req.data_rate = mib_get_params.param.channel_data_rate; } } else { @@ -661,7 +563,7 @@ int16_t LoRaWANStack::handle_tx(uint8_t port, const uint8_t* data, || (flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_PROPRIETARY) { _tx_msg.type = MCPS_UNCONFIRMED; - _tx_msg.message_u.unconfirmed.fport = _app_port; + _tx_msg.fport = _app_port; } // Handles all confirmed messages, including proprietary and multicast @@ -670,8 +572,8 @@ int16_t LoRaWANStack::handle_tx(uint8_t port, const uint8_t* data, || (flags & MSG_FLAG_MASK) == MSG_CONFIRMED_PROPRIETARY) { _tx_msg.type = MCPS_CONFIRMED; - _tx_msg.message_u.confirmed.fport = _app_port; - _tx_msg.message_u.confirmed.nb_trials = _num_retry; + _tx_msg.fport = _app_port; + _tx_msg.nb_trials = _num_retry; } tr_info("RTS = %u bytes, PEND = %u", _tx_msg.f_buffer_size, _tx_msg.pending_size); @@ -1367,3 +1269,89 @@ lorawan_status_t LoRaWANStack::lora_state_machine() return status; } + +#if defined(LORAWAN_COMPLIANCE_TEST) +/** + * + * Prepares the upload message to reserved ports + * + * \param port Application port + */ +void LoRaWANStack::prepare_special_tx_frame(uint8_t port) +{ + if (port == 224) { + // Clear any normal message stuff before compliance test. + memset(&_tx_msg, 0, sizeof(_tx_msg)); + + if (_compliance_test.link_check == true) { + _compliance_test.link_check = false; + _compliance_test.state = 1; + _tx_msg.f_buffer_size = 3; + _tx_msg.f_buffer[0] = 5; + _tx_msg.f_buffer[1] = _compliance_test.demod_margin; + _tx_msg.f_buffer[2] = _compliance_test.nb_gateways; + } else { + switch (_compliance_test.state) { + case 4: + _compliance_test.state = 1; + _tx_msg.f_buffer_size = _compliance_test.app_data_size; + + _tx_msg.f_buffer[0] = _compliance_test.app_data_buffer[0]; + for(uint8_t i = 1; i < MIN(_compliance_test.app_data_size, MBED_CONF_LORA_TX_MAX_SIZE); ++i) { + _tx_msg.f_buffer[i] = _compliance_test.app_data_buffer[i]; + } + break; + case 1: + _tx_msg.f_buffer_size = 2; + _tx_msg.f_buffer[0] = _compliance_test.downlink_counter >> 8; + _tx_msg.f_buffer[1] = _compliance_test.downlink_counter; + break; + } + } + } +} + +/** Hands over the compliance test frame to MAC layer + * + * \return returns the state of the LoRa MAC + */ +lorawan_status_t LoRaWANStack::send_compliance_test_frame_to_mac() +{ + loramac_mcps_req_t mcps_req; + + prepare_special_tx_frame(_compliance_test.app_port); + + if (!_compliance_test.is_tx_confirmed) { + mcps_req.type = MCPS_UNCONFIRMED; + mcps_req.f_buffer = _tx_msg.f_buffer; + mcps_req.f_buffer_size = _tx_msg.f_buffer_size; + mcps_req.fport = _compliance_test.app_port; + mcps_req.nb_trials = 1; + mcps_req.data_rate = _lora_phy.get_default_tx_datarate(); + + tr_info("Transmit unconfirmed compliance test frame %d bytes.", mcps_req.f_buffer_size); + + for (uint8_t i = 0; i < mcps_req.f_buffer_size; ++i) { + tr_info("Byte %d, data is 0x%x", i+1, ((uint8_t*)mcps_req.f_buffer)[i]); + } + } else if (_compliance_test.is_tx_confirmed) { + mcps_req.type = MCPS_CONFIRMED; + mcps_req.f_buffer = _tx_msg.f_buffer; + mcps_req.f_buffer_size = _tx_msg.f_buffer_size; + mcps_req.fport = _compliance_test.app_port; + mcps_req.nb_trials = _num_retry; + mcps_req.data_rate = _lora_phy.get_default_tx_datarate(); + + tr_info("Transmit confirmed compliance test frame %d bytes.", mcps_req.f_buffer_size); + + for (uint8_t i = 0; i < mcps_req.f_buffer_size; ++i) { + tr_info("Byte %d, data is 0x%x", i+1, ((uint8_t*)mcps_req.f_buffer)[i]); + } + } else { + return LORAWAN_STATUS_SERVICE_UNKNOWN; + } + + return mcps_request_handler(&mcps_req); +} +#endif + diff --git a/features/lorawan/LoRaWANStack.h b/features/lorawan/LoRaWANStack.h index 08daf98ca9a..6be13cc8381 100644 --- a/features/lorawan/LoRaWANStack.h +++ b/features/lorawan/LoRaWANStack.h @@ -77,6 +77,24 @@ SPDX-License-Identifier: BSD-3-Clause #define LORAWAN_NETWORK_ID_MASK ( uint32_t )0xFE000000 class LoRaWANStack: private mbed::NonCopyable { +private: + /** End-device states. + * + */ + typedef enum device_states { + DEVICE_STATE_NOT_INITIALIZED, + DEVICE_STATE_INIT, + DEVICE_STATE_JOINING, + DEVICE_STATE_ABP_CONNECTING, + DEVICE_STATE_JOINED, + DEVICE_STATE_SEND, + DEVICE_STATE_IDLE, +#if defined(LORAWAN_COMPLIANCE_TEST) + DEVICE_STATE_COMPLIANCE_TEST, +#endif + DEVICE_STATE_SHUTDOWN + } device_states_t; + public: static LoRaWANStack& get_lorawan_stack(); @@ -424,6 +442,24 @@ class LoRaWANStack: private mbed::NonCopyable { */ uint16_t check_possible_tx_size(uint16_t size); +private: + + LoRaWANTimeHandler _lora_time; + LoRaMac _loramac; + LoRaPHY_region _lora_phy; + loramac_primitives_t LoRaMacPrimitives; + + device_states_t _device_current_state; + lorawan_app_callbacks_t _callbacks; + radio_events_t *_mac_handlers; + lorawan_session_t _lw_session; + loramac_tx_message_t _tx_msg; + loramac_rx_message_t _rx_msg; + uint8_t _num_retry; + uint8_t _app_port; + bool _duty_cycle_on; + events::EventQueue *_queue; + #if defined(LORAWAN_COMPLIANCE_TEST) /** * This function is used only for compliance testing @@ -439,28 +475,10 @@ class LoRaWANStack: private mbed::NonCopyable { * Used only for compliance testing */ lorawan_status_t send_compliance_test_frame_to_mac(); -#endif - LoRaWANTimeHandler _lora_time; - LoRaMac _loramac; - LoRaPHY_region _lora_phy; - loramac_primitives_t LoRaMacPrimitives; - -#if defined(LORAWAN_COMPLIANCE_TEST) uint8_t compliance_test_buffer[MBED_CONF_LORA_TX_MAX_SIZE]; compliance_test_t _compliance_test; #endif - - device_states_t _device_current_state; - lorawan_app_callbacks_t _callbacks; - radio_events_t *_mac_handlers; - lorawan_session_t _lw_session; - loramac_tx_message_t _tx_msg; - loramac_rx_message_t _rx_msg; - uint8_t _app_port; - uint8_t _num_retry; - events::EventQueue *_queue; - bool _duty_cycle_on; }; #endif /* LORAWANSTACK_H_ */ diff --git a/features/lorawan/lorastack/mac/LoRaMac.cpp b/features/lorawan/lorastack/mac/LoRaMac.cpp index ab5c2b93587..bb82dcd7d76 100644 --- a/features/lorawan/lorastack/mac/LoRaMac.cpp +++ b/features/lorawan/lorastack/mac/LoRaMac.cpp @@ -78,7 +78,7 @@ using namespace events; LoRaMac::LoRaMac(LoRaWANTimeHandler &lora_time) - : mac_commands(*this), _lora_time(lora_time) + : mac_commands(), _lora_time(lora_time) { lora_phy = NULL; //radio_events_t RadioEvents; @@ -218,8 +218,6 @@ void LoRaMac::handle_rx2_timer_event(void) **************************************************************************/ void LoRaMac::on_radio_tx_done( void ) { - get_phy_params_t get_phy; - phy_param_t phy_param; set_band_txdone_params_t tx_done_params; lorawan_time_t cur_time = _lora_time.get_current_time( ); loramac_mlme_confirm_t mlme_confirm = mlme.get_confirmation(); @@ -240,10 +238,8 @@ void LoRaMac::on_radio_tx_done( void ) if ((_params.dev_class == CLASS_C ) || (_params.is_node_ack_requested == true)) { - get_phy.attribute = PHY_ACK_TIMEOUT; - phy_param = lora_phy->get_phy_params(&get_phy); _lora_time.start(_params.timers.ack_timeout_timer, - _params.rx_window2_delay + phy_param.value); + _params.rx_window2_delay + lora_phy->get_ack_timeout()); } } else { mcps.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_OK; @@ -303,8 +299,6 @@ void LoRaMac::on_radio_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, loramac_mhdr_t mac_hdr; loramac_frame_ctrl_t fctrl; cflist_params_t cflist; - get_phy_params_t get_phy; - phy_param_t phy_param; bool skip_indication = false; uint8_t pkt_header_len = 0; @@ -428,18 +422,9 @@ void LoRaMac::on_radio_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, case FRAME_TYPE_DATA_CONFIRMED_DOWN: case FRAME_TYPE_DATA_UNCONFIRMED_DOWN: { - // Check if the received payload size is valid - get_phy.datarate = mcps.get_indication().rx_datarate; - get_phy.attribute = PHY_MAX_PAYLOAD; + uint8_t value = lora_phy->get_max_payload(mcps.get_indication().rx_datarate, _params.is_repeater_supported); - // Get the maximum payload length - if (_params.is_repeater_supported == true) { - get_phy.attribute = PHY_MAX_PAYLOAD_REPEATER; - } - - phy_param = lora_phy->get_phy_params(&get_phy); - - if (MAX(0, (int16_t) ((int16_t)size - (int16_t)LORA_MAC_FRMPAYLOAD_OVERHEAD )) > (int32_t)phy_param.value) { + if (MAX(0, (int16_t) ((int16_t)size - (int16_t)LORA_MAC_FRMPAYLOAD_OVERHEAD )) > (int32_t)value) { mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_ERROR; prepare_rx_done_abort(); return; @@ -516,11 +501,7 @@ void LoRaMac::on_radio_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, } } - // Check for a the maximum allowed counter difference - get_phy.attribute = PHY_MAX_FCNT_GAP; - phy_param = lora_phy->get_phy_params(&get_phy); - - if (sequence_counter_diff >= phy_param.value) { + if (sequence_counter_diff >= lora_phy->get_maximum_frame_counter_gap()) { mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS; mcps.get_indication().dl_frame_counter = downlink_counter; prepare_rx_done_abort( ); @@ -621,10 +602,13 @@ void LoRaMac::on_radio_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, } // Decode frame payload MAC commands - if (mac_commands.process_mac_commands(_params.payload, 0, frame_len, snr, - mlme.get_confirmation(), - _params.sys_params, *lora_phy) != LORAWAN_STATUS_OK) { + if (LORAWAN_STATUS_OK != mac_commands.process_mac_commands( + _params.payload, 0, frame_len, snr, + mlme.get_confirmation(), _params.sys_params, *lora_phy)) { mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_ERROR; + } else if (mac_commands.has_sticky_mac_cmd()) { + set_mlme_schedule_ul_indication(); + mac_commands.clear_sticky_mac_cmd(); } } else { skip_indication = true; @@ -632,10 +616,13 @@ void LoRaMac::on_radio_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, } else { if (fctrl.bits.fopts_len > 0) { // Decode Options field MAC commands. Omit the fPort. - if (mac_commands.process_mac_commands(payload, 8, app_payload_start_index - 1, snr, - mlme.get_confirmation(), - _params.sys_params, *lora_phy ) != LORAWAN_STATUS_OK) { + if (LORAWAN_STATUS_OK != mac_commands.process_mac_commands( + payload, 8, app_payload_start_index - 1, snr, + mlme.get_confirmation(), _params.sys_params, *lora_phy )) { mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_ERROR; + } else if (mac_commands.has_sticky_mac_cmd()) { + set_mlme_schedule_ul_indication(); + mac_commands.clear_sticky_mac_cmd(); } } @@ -658,10 +645,14 @@ void LoRaMac::on_radio_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, } else { if (fctrl.bits.fopts_len > 0) { // Decode Options field MAC commands - if (mac_commands.process_mac_commands(payload, 8, app_payload_start_index, snr, - mlme.get_confirmation(), - _params.sys_params, *lora_phy) != LORAWAN_STATUS_OK) { + if (LORAWAN_STATUS_OK != mac_commands.process_mac_commands( + payload, 8, app_payload_start_index, snr, + mlme.get_confirmation(), + _params.sys_params, *lora_phy)) { mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_ERROR; + } else if (mac_commands.has_sticky_mac_cmd()) { + set_mlme_schedule_ul_indication(); + mac_commands.clear_sticky_mac_cmd(); } } } @@ -806,8 +797,6 @@ void LoRaMac::on_radio_rx_timeout(void) **************************************************************************/ void LoRaMac::on_mac_state_check_timer_event(void) { - get_phy_params_t get_phy; - phy_param_t phy_param; bool tx_timeout = false; _lora_time.stop(_params.timers.mac_state_check_timer); @@ -906,10 +895,8 @@ void LoRaMac::on_mac_state_check_timer_event(void) _params.ack_timeout_retry_counter++; if ((_params.ack_timeout_retry_counter % 2) == 1) { - get_phy.attribute = PHY_NEXT_LOWER_TX_DR; - get_phy.datarate = _params.sys_params.channel_data_rate; - phy_param = lora_phy->get_phy_params( &get_phy ); - _params.sys_params.channel_data_rate = phy_param.value; + _params.sys_params.channel_data_rate = lora_phy->get_next_lower_tx_datarate( + _params.sys_params.channel_data_rate); } // Try to send the frame again @@ -1106,21 +1093,10 @@ void LoRaMac::rx_window_setup(bool rx_continuous, uint32_t max_rx_window_time) bool LoRaMac::validate_payload_length(uint8_t length, int8_t datarate, uint8_t fopts_len) { - get_phy_params_t get_phy; - phy_param_t phy_param; uint16_t max_value = 0; uint16_t payloadSize = 0; - // Setup PHY request - get_phy.datarate = datarate; - get_phy.attribute = PHY_MAX_PAYLOAD; - - // Get the maximum payload length - if (_params.is_repeater_supported == true) { - get_phy.attribute = PHY_MAX_PAYLOAD_REPEATER; - } - phy_param = lora_phy->get_phy_params(&get_phy); - max_value = phy_param.value; + max_value = lora_phy->get_max_payload(datarate, _params.is_repeater_supported); // Calculate the resulting payload size payloadSize = (length + fopts_len); @@ -1176,8 +1152,6 @@ lorawan_status_t LoRaMac::schedule_tx(void) { lorawan_time_t dutyCycleTimeOff = 0; channel_selection_params_t nextChan; - get_phy_params_t getPhy; - phy_param_t phyParam; // Check if the device is off if (_params.sys_params.max_duty_cycle == 255) { @@ -1202,10 +1176,7 @@ lorawan_status_t LoRaMac::schedule_tx(void) while (lora_phy->set_next_channel(&nextChan, &_params.channel, &dutyCycleTimeOff, &_params.timers.aggregated_timeoff) == false) { - // Set the default datarate - getPhy.attribute = PHY_DEF_TX_DR; - phyParam = lora_phy->get_phy_params(&getPhy); - _params.sys_params.channel_data_rate = phyParam.value; + _params.sys_params.channel_data_rate = lora_phy->get_default_tx_datarate(); // Update datarate in the function parameters nextChan.current_datarate = _params.sys_params.channel_data_rate; } @@ -1281,9 +1252,6 @@ void LoRaMac::calculate_backOff(uint8_t channel) void LoRaMac::reset_mac_parameters(void) { - get_phy_params_t get_phy; - phy_param_t phy_param; - _params.is_nwk_joined = false; // Counters @@ -1306,37 +1274,7 @@ void LoRaMac::reset_mac_parameters(void) _params.is_rx_window_enabled = true; - get_phy.attribute = PHY_DEF_TX_POWER; - phy_param = lora_phy->get_phy_params(&get_phy); - - _params.sys_params.channel_tx_power = phy_param.value; - - get_phy.attribute = PHY_DEF_TX_DR; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.channel_data_rate = phy_param.value; - - get_phy.attribute = PHY_DEF_DR1_OFFSET; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.rx1_dr_offset = phy_param.value; - - get_phy.attribute = PHY_DEF_RX2_FREQUENCY; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.rx2_channel.frequency = phy_param.value; - get_phy.attribute = PHY_DEF_RX2_DR; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.rx2_channel.datarate = phy_param.value; - - get_phy.attribute = PHY_DEF_UPLINK_DWELL_TIME; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.uplink_dwell_time = phy_param.value; - - get_phy.attribute = PHY_DEF_MAX_EIRP; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.max_eirp = phy_param.value; - - get_phy.attribute = PHY_DEF_ANTENNA_GAIN; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.antenna_gain = phy_param.value; + lora_phy->reset_to_default_values(&_params, false); _params.is_node_ack_requested = false; _params.is_srv_ack_requested = false; @@ -1595,58 +1533,55 @@ lorawan_status_t LoRaMac::send_frame_on_channel(uint8_t channel) return LORAWAN_STATUS_OK; } -lorawan_status_t LoRaMac::set_tx_continuous_wave(uint16_t timeout) -{ - cw_mode_params_t continuous_wave; +//lorawan_status_t LoRaMac::set_tx_continuous_wave(uint16_t timeout) +//{ +// cw_mode_params_t continuous_wave; - continuous_wave.channel = _params.channel; - continuous_wave.datarate = _params.sys_params.channel_data_rate; - continuous_wave.tx_power = _params.sys_params.channel_tx_power; - continuous_wave.max_eirp = _params.sys_params.max_eirp; - continuous_wave.antenna_gain = _params.sys_params.antenna_gain; - continuous_wave.timeout = timeout; +// continuous_wave.channel = _params.channel; +// continuous_wave.datarate = _params.sys_params.channel_data_rate; +// continuous_wave.tx_power = _params.sys_params.channel_tx_power; +// continuous_wave.max_eirp = _params.sys_params.max_eirp; +// continuous_wave.antenna_gain = _params.sys_params.antenna_gain; +// continuous_wave.timeout = timeout; - lora_phy->set_tx_cont_mode(&continuous_wave); +// lora_phy->set_tx_cont_mode(&continuous_wave); - // Starts the MAC layer status check timer - _lora_time.start(_params.timers.mac_state_check_timer, - MAC_STATE_CHECK_TIMEOUT); +// // Starts the MAC layer status check timer +// _lora_time.start(_params.timers.mac_state_check_timer, +// MAC_STATE_CHECK_TIMEOUT); - _params.mac_state |= LORAMAC_TX_RUNNING; +// _params.mac_state |= LORAMAC_TX_RUNNING; - return LORAWAN_STATUS_OK; -} +// return LORAWAN_STATUS_OK; +//} -lorawan_status_t LoRaMac::set_tx_continuous_wave1(uint16_t timeout, - uint32_t frequency, - uint8_t power) -{ - cw_mode_params_t continuous_wave; +//lorawan_status_t LoRaMac::set_tx_continuous_wave1(uint16_t timeout, +// uint32_t frequency, +// uint8_t power) +//{ +// cw_mode_params_t continuous_wave; - continuous_wave.channel = 0; - continuous_wave.datarate = 0; - continuous_wave.tx_power = power; - continuous_wave.max_eirp = 0; - continuous_wave.antenna_gain = 0; - continuous_wave.timeout = timeout; +// continuous_wave.channel = 0; +// continuous_wave.datarate = 0; +// continuous_wave.tx_power = power; +// continuous_wave.max_eirp = 0; +// continuous_wave.antenna_gain = 0; +// continuous_wave.timeout = timeout; - lora_phy->set_tx_cont_mode(&continuous_wave); +// lora_phy->set_tx_cont_mode(&continuous_wave); - // Starts the MAC layer status check timer - _lora_time.start(_params.timers.mac_state_check_timer, - MAC_STATE_CHECK_TIMEOUT); +// // Starts the MAC layer status check timer +// _lora_time.start(_params.timers.mac_state_check_timer, +// MAC_STATE_CHECK_TIMEOUT); - _params.mac_state |= LORAMAC_TX_RUNNING; +// _params.mac_state |= LORAMAC_TX_RUNNING; - return LORAWAN_STATUS_OK; -} +// return LORAWAN_STATUS_OK; +//} lorawan_status_t LoRaMac::initialize(loramac_primitives_t *primitives, LoRaPHY *phy, EventQueue *queue) { - get_phy_params_t get_phy; - phy_param_t phy_param; - //store event queue pointer ev_queue = queue; @@ -1657,16 +1592,16 @@ lorawan_status_t LoRaMac::initialize(loramac_primitives_t *primitives, lora_phy = phy; // Activate MLME subsystem - mlme.activate_mlme_subsystem(this, lora_phy, &mac_commands); + mlme.activate_mlme_subsystem(lora_phy); // Activate MCPS subsystem - mcps.activate_mcps_subsystem(this, lora_phy); + mcps.activate_mcps_subsystem(); // Activate MIB subsystem - mib.activate_mib_subsystem(this, lora_phy); + mib.activate_mib_subsystem(lora_phy); // Activate channel planning subsystem - channel_plan.activate_channelplan_subsystem(lora_phy, &mib); + channel_plan.activate_channelplan_subsystem(lora_phy); mac_primitives = primitives; @@ -1683,67 +1618,7 @@ lorawan_status_t LoRaMac::initialize(loramac_primitives_t *primitives, _params.timers.aggregated_last_tx_time = 0; _params.timers.aggregated_timeoff = 0; - // Reset to defaults - get_phy.attribute = PHY_DUTY_CYCLE; - phy_param = lora_phy->get_phy_params(&get_phy); - // load default at this moment. Later can be changed using json - _params.is_dutycycle_on = (bool) phy_param.value; - - get_phy.attribute = PHY_DEF_TX_POWER; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.channel_tx_power = phy_param.value; - - get_phy.attribute = PHY_DEF_TX_DR; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.channel_data_rate = phy_param.value; - - get_phy.attribute = PHY_MAX_RX_WINDOW; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.max_rx_win_time = phy_param.value; - - get_phy.attribute = PHY_RECEIVE_DELAY1; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.recv_delay1 = phy_param.value; - - get_phy.attribute = PHY_RECEIVE_DELAY2; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.recv_delay2 = phy_param.value; - - get_phy.attribute = PHY_JOIN_ACCEPT_DELAY1; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.join_accept_delay1 = phy_param.value; - - get_phy.attribute = PHY_JOIN_ACCEPT_DELAY2; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.join_accept_delay2 = phy_param.value; - - get_phy.attribute = PHY_DEF_DR1_OFFSET; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.rx1_dr_offset = phy_param.value; - - get_phy.attribute = PHY_DEF_RX2_FREQUENCY; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.rx2_channel.frequency = phy_param.value; - - get_phy.attribute = PHY_DEF_RX2_DR; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.rx2_channel.datarate = phy_param.value; - - get_phy.attribute = PHY_DEF_UPLINK_DWELL_TIME; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.uplink_dwell_time = phy_param.value; - - get_phy.attribute = PHY_DEF_DOWNLINK_DWELL_TIME; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.downlink_dwell_time = phy_param.value; - - get_phy.attribute = PHY_DEF_MAX_EIRP; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.max_eirp = phy_param.f_value; - - get_phy.attribute = PHY_DEF_ANTENNA_GAIN; - phy_param = lora_phy->get_phy_params(&get_phy); - _params.sys_params.antenna_gain = phy_param.f_value; + lora_phy->reset_to_default_values(&_params, true); // Init parameters which are not set in function ResetMacParameters _params.sys_params.max_sys_rx_error = 10; @@ -1810,9 +1685,6 @@ void LoRaMac::disconnect() lorawan_status_t LoRaMac::query_tx_possible(uint8_t size, loramac_tx_info_t* tx_info) { - get_phy_params_t get_phy; - phy_param_t phy_param; - uint8_t fopt_len = mac_commands.get_mac_cmd_length() + mac_commands.get_repeat_commands_length(); @@ -1827,16 +1699,8 @@ lorawan_status_t LoRaMac::query_tx_possible(uint8_t size, _params.adr_ack_counter); } - // Setup PHY request - get_phy.datarate = _params.sys_params.channel_data_rate; - get_phy.attribute = PHY_MAX_PAYLOAD; + tx_info->current_payload_size = lora_phy->get_max_payload(_params.sys_params.channel_data_rate, _params.is_repeater_supported); - // Change request in case repeater is supported - if (_params.is_repeater_supported == true) { - get_phy.attribute = PHY_MAX_PAYLOAD_REPEATER; - } - phy_param = lora_phy->get_phy_params(&get_phy); - tx_info->current_payload_size = phy_param.value; // Verify if the fOpts fit into the maximum payload if (tx_info->current_payload_size >= fopt_len) { @@ -1884,7 +1748,18 @@ lorawan_status_t LoRaMac::remove_channel_plan() lorawan_status_t LoRaMac::get_channel_plan(lorawan_channelplan_t& plan) { - return channel_plan.get_plan(plan, &_params); + // Request Mib to get channels + loramac_mib_req_confirm_t mib_confirm; + memset(&mib_confirm, 0, sizeof(mib_confirm)); + mib_confirm.type = MIB_CHANNELS; + + lorawan_status_t status = mib.get_request(&mib_confirm, &_params); + + if (status != LORAWAN_STATUS_OK) { + return status; + } + + return channel_plan.get_plan(plan, &mib_confirm); } lorawan_status_t LoRaMac::remove_single_channel(uint8_t id) @@ -1961,7 +1836,75 @@ lorawan_status_t LoRaMac::multicast_channel_unlink( lorawan_status_t LoRaMac::mlme_request( loramac_mlme_req_t *mlmeRequest ) { - return mlme.set_request(mlmeRequest, &_params); + if (LORAMAC_IDLE != _params.mac_state) { + return LORAWAN_STATUS_BUSY; + } + + mlme.reset_confirmation(); + + mlme.get_confirmation().req_type = mlmeRequest->type; + _params.flags.bits.mlme_req = 1; + + lorawan_status_t status = LORAWAN_STATUS_SERVICE_UNKNOWN; + + if (MLME_LINK_CHECK == mlmeRequest->type) { + status = mac_commands.add_mac_command(MOTE_MAC_LINK_CHECK_REQ, 0, 0); + } else if (MLME_JOIN == mlmeRequest->type) { + if ((_params.mac_state & LORAMAC_TX_DELAYED) == LORAMAC_TX_DELAYED) { + return LORAWAN_STATUS_BUSY; + } + + if ((mlmeRequest->req.join.dev_eui == NULL) + || (mlmeRequest->req.join.app_eui == NULL) + || (mlmeRequest->req.join.app_key == NULL) + || (mlmeRequest->req.join.nb_trials == 0)) { + return LORAWAN_STATUS_PARAMETER_INVALID; + } + _params.keys.dev_eui = mlmeRequest->req.join.dev_eui; + _params.keys.app_eui = mlmeRequest->req.join.app_eui; + _params.keys.app_key = mlmeRequest->req.join.app_key; + _params.max_join_request_trials = mlmeRequest->req.join.nb_trials; + + if (!lora_phy->verify_nb_join_trials(mlmeRequest->req.join.nb_trials)) { + // Value not supported, get default + _params.max_join_request_trials = MBED_CONF_LORA_NB_TRIALS; + } + // Reset variable JoinRequestTrials + _params.join_request_trial_counter = 0; + + reset_mac_parameters(); + + _params.sys_params.channel_data_rate = + lora_phy->get_alternate_DR(_params.join_request_trial_counter + 1); + + loramac_mhdr_t machdr; + machdr.value = 0; + machdr.bits.mtype = FRAME_TYPE_JOIN_REQ; + status = send(&machdr, 0, NULL, 0); + } else if (MLME_TXCW == mlmeRequest->type) { + mlme.set_tx_continuous_wave(_params.channel, _params.sys_params.channel_data_rate, _params.sys_params.channel_tx_power, + _params.sys_params.max_eirp, _params.sys_params.antenna_gain, mlmeRequest->req.cw_tx_mode.timeout); + _lora_time.start(_params.timers.mac_state_check_timer, + MAC_STATE_CHECK_TIMEOUT); + + _params.mac_state |= LORAMAC_TX_RUNNING; + status = LORAWAN_STATUS_OK; + } else if (MLME_TXCW_1 == mlmeRequest->type) { + mlme.set_tx_continuous_wave(0, 0, mlmeRequest->req.cw_tx_mode.power, 0, 0, mlmeRequest->req.cw_tx_mode.timeout); + _lora_time.start(_params.timers.mac_state_check_timer, + MAC_STATE_CHECK_TIMEOUT); + + _params.mac_state |= LORAMAC_TX_RUNNING; + status = LORAWAN_STATUS_OK; + } + + if (status != LORAWAN_STATUS_OK) { + _params.is_node_ack_requested = false; + _params.flags.bits.mlme_req = 0; + } + + + return status; } lorawan_status_t LoRaMac::mcps_request( loramac_mcps_req_t *mcpsRequest ) @@ -1970,7 +1913,62 @@ lorawan_status_t LoRaMac::mcps_request( loramac_mcps_req_t *mcpsRequest ) return LORAWAN_STATUS_BUSY; } - return mcps.set_request(mcpsRequest, &_params); + loramac_mhdr_t machdr; + int8_t datarate = mcpsRequest->data_rate; + // TODO: The comment is different than the code??? + // Apply the minimum possible datarate. + // Some regions have limitations for the minimum datarate. + datarate = MAX(datarate, (int8_t)lora_phy->get_minimum_tx_datarate()); + + machdr.value = 0; + + mcps.reset_confirmation(); + + _params.ack_timeout_retry_counter = 1; + _params.max_ack_timeout_retries = 1; + + switch (mcpsRequest->type) { + case MCPS_UNCONFIRMED: { + machdr.bits.mtype = FRAME_TYPE_DATA_UNCONFIRMED_UP; + break; + } + case MCPS_CONFIRMED: { + machdr.bits.mtype = FRAME_TYPE_DATA_CONFIRMED_UP; + _params.max_ack_timeout_retries = mcpsRequest->nb_trials; + break; + } + case MCPS_PROPRIETARY: { + machdr.bits.mtype = FRAME_TYPE_PROPRIETARY; + break; + } + default: + return LORAWAN_STATUS_PARAMETER_INVALID; + } + +// Filter fPorts +// TODO: Does not work with PROPRIETARY messages +// if( IsFPortAllowed( mcpsRequest->fport ) == false ) { +// return LORAWAN_STATUS_PARAMETER_INVALID; +// } + + if (_params.sys_params.adr_on == false) { + if (lora_phy->verify_tx_datarate(datarate, false) == true) { + _params.sys_params.channel_data_rate = datarate; + } else { + return LORAWAN_STATUS_PARAMETER_INVALID; + } + } + + lorawan_status_t status = send(&machdr, mcpsRequest->fport, mcpsRequest->f_buffer, + mcpsRequest->f_buffer_size); + if (status == LORAWAN_STATUS_OK) { + mcps.get_confirmation().req_type = mcpsRequest->type; + _params.flags.bits.mcps_req = 1; + } else { + _params.is_node_ack_requested = false; + } + + return status; } lorawan_status_t LoRaMac::mib_get_request_confirm( loramac_mib_req_confirm_t *mibGet ) @@ -1980,7 +1978,12 @@ lorawan_status_t LoRaMac::mib_get_request_confirm( loramac_mib_req_confirm_t *mi lorawan_status_t LoRaMac::mib_set_request_confirm( loramac_mib_req_confirm_t *mibSet ) { - return mib.set_request(mibSet, &_params); + lorawan_status_t status = mib.set_request(mibSet, &_params); + if (LORAWAN_STATUS_OK == status && CLASS_C == _params.dev_class && (MIB_DEVICE_CLASS == mibSet->type || + (MIB_RX2_CHANNEL == mibSet->type && _params.is_nwk_joined))) { + open_continuous_rx2_window(); + } + return status; } radio_events_t *LoRaMac::get_phy_event_handlers() @@ -2024,11 +2027,7 @@ void LoRaMac::LoRaMacTestSetMic( uint16_t txPacketCounter ) void LoRaMac::LoRaMacTestSetDutyCycleOn( bool enable ) { - VerifyParams_t verify; - - verify.DutyCycle = enable; - - if(lora_phy->verify(&verify, PHY_DUTY_CYCLE) == true) + if(lora_phy->verify_duty_cycle(enable) == true) { _params.is_dutycycle_on = enable; } diff --git a/features/lorawan/lorastack/mac/LoRaMac.h b/features/lorawan/lorastack/mac/LoRaMac.h index b1676f84890..73d352c793d 100644 --- a/features/lorawan/lorastack/mac/LoRaMac.h +++ b/features/lorawan/lorastack/mac/LoRaMac.h @@ -334,9 +334,9 @@ class LoRaMac { * * loramac_mcps_req_t request; * request.type = MCPS_UNCONFIRMED; - * request.req.unconfirmed.fport = 1; - * request.req.unconfirmed.f_buffer = buffer; - * request.req.unconfirmed.f_buffer_size = sizeof(buffer); + * request.fport = 1; + * request.f_buffer = buffer; + * request.f_buffer_size = sizeof(buffer); * * if (mcps_request(&request) == LORAWAN_STATUS_OK) { * // Service started successfully. Waiting for the MCPS-Confirm event @@ -427,79 +427,7 @@ class LoRaMac { */ void open_continuous_rx2_window(void); -#if defined(LORAWAN_COMPLIANCE_TEST) -public: // Test interface - /** - * \brief LoRaMAC set tx timer. - * - * \details Sets up a timer for next transmission (application specific timers). - * - * \param [in] NextTxTime - Periodic time for next uplink. - - * \retval `lorawan_status_t` The status of the operation. The possible values are: - * \ref LORAWAN_STATUS_OK - * \ref LORAWAN_STATUS_PARAMETER_INVALID - */ - lorawan_status_t LoRaMacSetTxTimer( uint32_t NextTxTime ); - - /** - * \brief LoRaMAC stop tx timer. - * - * \details Stops the next tx timer. - * - * \retval `lorawan_status_t` The status of the operation. The possible values are: - * \ref LORAWAN_STATUS_OK - * \ref LORAWAN_STATUS_PARAMETER_INVALID - */ - lorawan_status_t LoRaMacStopTxTimer( ); - - /** - * \brief Enabled or disables the reception windows - * - * \details This is a test function. It shall be used for testing purposes only. - * Changing this attribute may lead to a non-conformance LoRaMac operation. - * - * \param [in] enable - Enabled or disables the reception windows - */ - void LoRaMacTestRxWindowsOn( bool enable ); - - /** - * \brief Enables the MIC field test - * - * \details This is a test function. It shall be used for testing purposes only. - * Changing this attribute may lead to a non-conformance LoRaMac operation. - * - * \param [in] txPacketCounter - Fixed Tx packet counter value - */ - void LoRaMacTestSetMic( uint16_t txPacketCounter ); - - /** - * \brief Enabled or disables the duty cycle - * - * \details This is a test function. It shall be used for testing purposes only. - * Changing this attribute may lead to a non-conformance LoRaMac operation. - * - * \param [in] enable - Enabled or disables the duty cycle - */ - void LoRaMacTestSetDutyCycleOn( bool enable ); - - /** - * \brief Sets the channel index - * - * \details This is a test function. It shall be used for testing purposes only. - * Changing this attribute may lead to a non-conformance LoRaMac operation. - * - * \param [in] channel - Channel index - */ - void LoRaMacTestSetChannel( uint8_t channel ); - -private: - /** - * Timer to handle the application data transmission duty cycle - */ - timer_event_t tx_next_packet_timer; -#endif private: /** @@ -673,6 +601,80 @@ class LoRaMac { * EventQueue object storage */ events::EventQueue *ev_queue; + +#if defined(LORAWAN_COMPLIANCE_TEST) +public: // Test interface + + /** + * \brief LoRaMAC set tx timer. + * + * \details Sets up a timer for next transmission (application specific timers). + * + * \param [in] NextTxTime - Periodic time for next uplink. + + * \retval `lorawan_status_t` The status of the operation. The possible values are: + * \ref LORAWAN_STATUS_OK + * \ref LORAWAN_STATUS_PARAMETER_INVALID + */ + lorawan_status_t LoRaMacSetTxTimer( uint32_t NextTxTime ); + + /** + * \brief LoRaMAC stop tx timer. + * + * \details Stops the next tx timer. + * + * \retval `lorawan_status_t` The status of the operation. The possible values are: + * \ref LORAWAN_STATUS_OK + * \ref LORAWAN_STATUS_PARAMETER_INVALID + */ + lorawan_status_t LoRaMacStopTxTimer( ); + + /** + * \brief Enabled or disables the reception windows + * + * \details This is a test function. It shall be used for testing purposes only. + * Changing this attribute may lead to a non-conformance LoRaMac operation. + * + * \param [in] enable - Enabled or disables the reception windows + */ + void LoRaMacTestRxWindowsOn( bool enable ); + + /** + * \brief Enables the MIC field test + * + * \details This is a test function. It shall be used for testing purposes only. + * Changing this attribute may lead to a non-conformance LoRaMac operation. + * + * \param [in] txPacketCounter - Fixed Tx packet counter value + */ + void LoRaMacTestSetMic( uint16_t txPacketCounter ); + + /** + * \brief Enabled or disables the duty cycle + * + * \details This is a test function. It shall be used for testing purposes only. + * Changing this attribute may lead to a non-conformance LoRaMac operation. + * + * \param [in] enable - Enabled or disables the duty cycle + */ + void LoRaMacTestSetDutyCycleOn( bool enable ); + + /** + * \brief Sets the channel index + * + * \details This is a test function. It shall be used for testing purposes only. + * Changing this attribute may lead to a non-conformance LoRaMac operation. + * + * \param [in] channel - Channel index + */ + void LoRaMacTestSetChannel( uint8_t channel ); + +private: + /** + * Timer to handle the application data transmission duty cycle + */ + timer_event_t tx_next_packet_timer; +#endif }; #endif // MBED_LORAWAN_MAC_H__ diff --git a/features/lorawan/lorastack/mac/LoRaMacChannelPlan.cpp b/features/lorawan/lorastack/mac/LoRaMacChannelPlan.cpp index 584207de445..4caac0f736d 100644 --- a/features/lorawan/lorastack/mac/LoRaMacChannelPlan.cpp +++ b/features/lorawan/lorastack/mac/LoRaMacChannelPlan.cpp @@ -25,7 +25,7 @@ SPDX-License-Identifier: BSD-3-Clause #include "lorastack/mac/LoRaMacChannelPlan.h" -LoRaMacChannelPlan::LoRaMacChannelPlan() : _lora_phy(NULL), _mib(NULL) +LoRaMacChannelPlan::LoRaMacChannelPlan() : _lora_phy(NULL) { } @@ -33,10 +33,9 @@ LoRaMacChannelPlan::~LoRaMacChannelPlan() { } -void LoRaMacChannelPlan::activate_channelplan_subsystem(LoRaPHY *phy, LoRaMacMib *mib) +void LoRaMacChannelPlan::activate_channelplan_subsystem(LoRaPHY *phy) { _lora_phy = phy; - _mib = mib; } lorawan_status_t LoRaMacChannelPlan::set_plan(const lorawan_channelplan_t& plan) @@ -44,22 +43,13 @@ lorawan_status_t LoRaMacChannelPlan::set_plan(const lorawan_channelplan_t& plan) channel_params_t mac_layer_ch_params; lorawan_status_t status; - get_phy_params_t get_phy; - phy_param_t phy_param; uint8_t max_num_channels; - // Check if the PHY layer supports custom channel plans or not. - get_phy.attribute = PHY_CUSTOM_CHANNEL_PLAN_SUPPORT; - phy_param = _lora_phy->get_phy_params(&get_phy); - - if (!phy_param.value) { + if (!_lora_phy->is_custom_channel_plan_supported()) { return LORAWAN_STATUS_SERVICE_UNKNOWN; } - // Check first how many channels the selected PHY layer supports - get_phy.attribute = PHY_MAX_NB_CHANNELS; - phy_param = _lora_phy->get_phy_params(&get_phy); - max_num_channels = (uint8_t) phy_param.value; + max_num_channels = _lora_phy->get_max_nb_channels(); // check if user is setting more channels than supported if (plan.nb_channels > max_num_channels) { @@ -87,48 +77,23 @@ lorawan_status_t LoRaMacChannelPlan::set_plan(const lorawan_channelplan_t& plan) } lorawan_status_t LoRaMacChannelPlan::get_plan(lorawan_channelplan_t& plan, - loramac_protocol_params *params) + const loramac_mib_req_confirm_t* mib_confirm) { - if (params == NULL) { + if (mib_confirm == NULL) { return LORAWAN_STATUS_PARAMETER_INVALID; } - loramac_mib_req_confirm_t mib_confirm; - lorawan_status_t status; - - get_phy_params_t get_phy; - phy_param_t phy_param; uint8_t max_num_channels; uint16_t *channel_mask; uint8_t count = 0; - // Check if the PHY layer supports custom channel plans or not. - get_phy.attribute = PHY_CUSTOM_CHANNEL_PLAN_SUPPORT; - phy_param = _lora_phy->get_phy_params(&get_phy); - - if (!phy_param.value) { + if (!_lora_phy->is_custom_channel_plan_supported()) { return LORAWAN_STATUS_SERVICE_UNKNOWN; } - // Check first how many channels the selected PHY layer supports - get_phy.attribute = PHY_MAX_NB_CHANNELS; - phy_param = _lora_phy->get_phy_params(&get_phy); - max_num_channels = (uint8_t) phy_param.value; - - // Now check the Default channel mask - get_phy.attribute = PHY_CHANNEL_MASK; - phy_param = _lora_phy->get_phy_params(&get_phy); - channel_mask = phy_param.channel_mask; + max_num_channels = _lora_phy->get_max_nb_channels(); - // Request Mib to get channels - memset(&mib_confirm, 0, sizeof(mib_confirm)); - mib_confirm.type = MIB_CHANNELS; - - status = _mib->get_request(&mib_confirm, params); - - if (status != LORAWAN_STATUS_OK) { - return status; - } + channel_mask = _lora_phy->get_channel_mask(false); for (uint8_t i = 0; i < max_num_channels; i++) { // skip the channels which are not enabled @@ -138,12 +103,12 @@ lorawan_status_t LoRaMacChannelPlan::get_plan(lorawan_channelplan_t& plan, // otherwise add them to the channel_plan struct plan.channels[count].id = i; - plan.channels[count].ch_param.frequency = mib_confirm.param.channel_list[i].frequency; - plan.channels[count].ch_param.dr_range.value = mib_confirm.param.channel_list[i].dr_range.value; - plan.channels[count].ch_param.dr_range.fields.min = mib_confirm.param.channel_list[i].dr_range.fields.min; - plan.channels[count].ch_param.dr_range.fields.max = mib_confirm.param.channel_list[i].dr_range.fields.max; - plan.channels[count].ch_param.band = mib_confirm.param.channel_list[i].band; - plan.channels[count].ch_param.rx1_frequency = mib_confirm.param.channel_list[i].rx1_frequency; + plan.channels[count].ch_param.frequency = mib_confirm->param.channel_list[i].frequency; + plan.channels[count].ch_param.dr_range.value = mib_confirm->param.channel_list[i].dr_range.value; + plan.channels[count].ch_param.dr_range.fields.min = mib_confirm->param.channel_list[i].dr_range.fields.min; + plan.channels[count].ch_param.dr_range.fields.max = mib_confirm->param.channel_list[i].dr_range.fields.max; + plan.channels[count].ch_param.band = mib_confirm->param.channel_list[i].band; + plan.channels[count].ch_param.rx1_frequency = mib_confirm->param.channel_list[i].rx1_frequency; count++; } @@ -156,34 +121,19 @@ lorawan_status_t LoRaMacChannelPlan::remove_plan() { lorawan_status_t status = LORAWAN_STATUS_OK; - get_phy_params_t get_phy; - phy_param_t phy_param; uint8_t max_num_channels; uint16_t *channel_mask; uint16_t *default_channel_mask; - // Check if the PHY layer supports custom channel plans or not. - get_phy.attribute = PHY_CUSTOM_CHANNEL_PLAN_SUPPORT; - phy_param = _lora_phy->get_phy_params(&get_phy); - - if (!phy_param.value) { + if (!_lora_phy->is_custom_channel_plan_supported()) { return LORAWAN_STATUS_SERVICE_UNKNOWN; } - // Check first how many channels the selected PHY layer supports - get_phy.attribute = PHY_MAX_NB_CHANNELS; - phy_param = _lora_phy->get_phy_params(&get_phy); - max_num_channels = (uint8_t) phy_param.value; + max_num_channels = _lora_phy->get_max_nb_channels(); - // Now check the channel mask for enabled channels - get_phy.attribute = PHY_CHANNEL_MASK; - phy_param = _lora_phy->get_phy_params(&get_phy); - channel_mask = phy_param.channel_mask; + channel_mask = _lora_phy->get_channel_mask(false); - // Now check the channel mask for default channels - get_phy.attribute = PHY_DEFAULT_CHANNEL_MASK; - phy_param = _lora_phy->get_phy_params(&get_phy); - default_channel_mask = phy_param.channel_mask; + default_channel_mask = _lora_phy->get_channel_mask(true); for (uint8_t i = 0; i < max_num_channels; i++) { // skip any default channels @@ -208,22 +158,13 @@ lorawan_status_t LoRaMacChannelPlan::remove_plan() lorawan_status_t LoRaMacChannelPlan::remove_single_channel(uint8_t channel_id) { - get_phy_params_t get_phy; - phy_param_t phy_param; uint8_t max_num_channels; - // Check if the PHY layer supports custom channel plans or not. - get_phy.attribute = PHY_CUSTOM_CHANNEL_PLAN_SUPPORT; - phy_param = _lora_phy->get_phy_params(&get_phy); - - if (!phy_param.value) { + if (!_lora_phy->is_custom_channel_plan_supported()) { return LORAWAN_STATUS_SERVICE_UNKNOWN; } - // Check first how many channels the selected PHY layer supports - get_phy.attribute = PHY_MAX_NB_CHANNELS; - phy_param = _lora_phy->get_phy_params(&get_phy); - max_num_channels = (uint8_t) phy_param.value; + max_num_channels = _lora_phy->get_max_nb_channels(); // According to specification channel IDs start from 0 and last valid // channel ID is N-1 where N=MAX_NUM_CHANNELS. diff --git a/features/lorawan/lorastack/mac/LoRaMacChannelPlan.h b/features/lorawan/lorastack/mac/LoRaMacChannelPlan.h index adf880de641..23a08cdc0ba 100644 --- a/features/lorawan/lorastack/mac/LoRaMacChannelPlan.h +++ b/features/lorawan/lorastack/mac/LoRaMacChannelPlan.h @@ -54,9 +54,8 @@ class LoRaMacChannelPlan { * Stores pointers to PHY layer MIB subsystem * * @param phy pointer to PHY layer - * @param mib pointer to MIB subsystem */ - void activate_channelplan_subsystem(LoRaPHY *phy,LoRaMacMib *mib); + void activate_channelplan_subsystem(LoRaPHY *phy); /** Set a given channel plan * @@ -75,15 +74,15 @@ class LoRaMacChannelPlan { * * Used to get active channel plan. * - * @param plan a reference to application provided channel plan structure - * which gets filled in with active channel plan data. + * @param plan a reference to application provided channel plan structure + * which gets filled in with active channel plan data. * - * @param params pointer to active MAC layer parameters. + * @param mib_confirm pointer to MIB request structure containing channel information * - * @return LORAWAN_STATUS_OK if everything goes well otherwise - * a negative error code is returned. + * @return LORAWAN_STATUS_OK if everything goes well otherwise + * a negative error code is returned. */ - lorawan_status_t get_plan(lorawan_channelplan_t& plan, loramac_protocol_params *params); + lorawan_status_t get_plan(lorawan_channelplan_t& plan, const loramac_mib_req_confirm_t *mib_confirm); /** Remove the active channel plan * @@ -109,7 +108,6 @@ class LoRaMacChannelPlan { * Local handles */ LoRaPHY *_lora_phy; - LoRaMacMib * _mib; }; diff --git a/features/lorawan/lorastack/mac/LoRaMacCommand.cpp b/features/lorawan/lorastack/mac/LoRaMacCommand.cpp index 0fc3356de36..c3c632d271a 100644 --- a/features/lorawan/lorastack/mac/LoRaMacCommand.cpp +++ b/features/lorawan/lorastack/mac/LoRaMacCommand.cpp @@ -39,10 +39,10 @@ SPDX-License-Identifier: BSD-3-Clause static const uint8_t max_eirp_table[] = { 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 }; -LoRaMacCommand::LoRaMacCommand(LoRaMac& lora_mac) - : _lora_mac(lora_mac) +LoRaMacCommand::LoRaMacCommand() { mac_cmd_in_next_tx = false; + sticky_mac_cmd = false; mac_cmd_buf_idx = 0; mac_cmd_buf_idx_to_repeat = 0; @@ -91,7 +91,8 @@ lorawan_status_t LoRaMacCommand::add_mac_command(uint8_t cmd, uint8_t p1, // Status: Datarate ACK, Channel ACK mac_cmd_buffer[mac_cmd_buf_idx++] = p1; // This is a sticky MAC command answer. Setup indication - _lora_mac.set_mlme_schedule_ul_indication(); +// _lora_mac.set_mlme_schedule_ul_indication(); + sticky_mac_cmd = true; status = LORAWAN_STATUS_OK; } break; @@ -118,7 +119,8 @@ lorawan_status_t LoRaMacCommand::add_mac_command(uint8_t cmd, uint8_t p1, mac_cmd_buffer[mac_cmd_buf_idx++] = cmd; // No payload for this answer // This is a sticky MAC command answer. Setup indication - _lora_mac.set_mlme_schedule_ul_indication(); +// _lora_mac.set_mlme_schedule_ul_indication(); + sticky_mac_cmd = true; status = LORAWAN_STATUS_OK; } break; @@ -135,7 +137,8 @@ lorawan_status_t LoRaMacCommand::add_mac_command(uint8_t cmd, uint8_t p1, // Status: Uplink frequency exists, Channel frequency OK mac_cmd_buffer[mac_cmd_buf_idx++] = p1; // This is a sticky MAC command answer. Setup indication - _lora_mac.set_mlme_schedule_ul_indication(); +// _lora_mac.set_mlme_schedule_ul_indication(); + sticky_mac_cmd = true; status = LORAWAN_STATUS_OK; } break; @@ -206,6 +209,7 @@ void LoRaMacCommand::parse_mac_commands_to_repeat() } else { mac_cmd_in_next_tx = false; } + mac_cmd_buf_idx_to_repeat = cmd_cnt; } @@ -216,7 +220,6 @@ void LoRaMacCommand::clear_repeat_buffer() void LoRaMacCommand::copy_repeat_commands_to_buffer() { - // Copy the MAC commands which must be re-send into the MAC command buffer memcpy(&mac_cmd_buffer[mac_cmd_buf_idx], mac_cmd_buffer_to_repeat, mac_cmd_buf_idx_to_repeat); mac_cmd_buf_idx += mac_cmd_buf_idx_to_repeat; } @@ -236,10 +239,18 @@ bool LoRaMacCommand::is_mac_command_in_next_tx() const return mac_cmd_in_next_tx; } -lorawan_status_t LoRaMacCommand::process_mac_commands(uint8_t *payload, - uint8_t mac_index, - uint8_t commands_size, - uint8_t snr, +void LoRaMacCommand::clear_sticky_mac_cmd() +{ + sticky_mac_cmd = false; +} + +bool LoRaMacCommand::has_sticky_mac_cmd() const +{ + return sticky_mac_cmd; +} + +lorawan_status_t LoRaMacCommand::process_mac_commands(uint8_t *payload, uint8_t mac_index, + uint8_t commands_size, uint8_t snr, loramac_mlme_confirm_t& mlme_conf, lora_mac_system_params_t &mac_sys_params, LoRaPHY &lora_phy) @@ -417,7 +428,6 @@ lorawan_status_t LoRaMacCommand::process_mac_commands(uint8_t *payload, bool LoRaMacCommand::is_sticky_mac_command_pending() { if (mac_cmd_buf_idx_to_repeat > 0) { - // Sticky MAC commands pending return true; } return false; diff --git a/features/lorawan/lorastack/mac/LoRaMacCommand.h b/features/lorawan/lorastack/mac/LoRaMacCommand.h index 3aa54c633d2..9d4a6e4873b 100644 --- a/features/lorawan/lorastack/mac/LoRaMacCommand.h +++ b/features/lorawan/lorastack/mac/LoRaMacCommand.h @@ -54,7 +54,7 @@ class LoRaMac; class LoRaMacCommand { public: - LoRaMacCommand(LoRaMac &lora_mac); + LoRaMacCommand(); ~LoRaMacCommand(); /** @@ -125,10 +125,22 @@ class LoRaMacCommand { /** * @brief Check if MAC command buffer has commands to be sent in next TX * - * @return status True: buffer has MAC commands to be sent, false: no commands in buffer] + * @return status True: buffer has MAC commands to be sent, false: no commands in buffer */ bool is_mac_command_in_next_tx() const; + /** + * @brief Clear sticky MAC commands. + */ + void clear_sticky_mac_cmd(); + + /** + * @brief Check if MAC command buffer contains sticky commands + * + * @return status True: buffer has sticky MAC commands in it, false: no sticky commands in buffer + */ + bool has_sticky_mac_cmd() const; + /** * @brief Decodes MAC commands in the fOpts field and in the payload * @@ -148,13 +160,16 @@ class LoRaMacCommand { bool is_sticky_mac_command_pending(); private: - LoRaMac& _lora_mac; - /** * Indicates if the MAC layer wants to send MAC commands */ bool mac_cmd_in_next_tx; + /** + * Indicates if there are any pending sticky MAC commands + */ + bool sticky_mac_cmd; + /** * Contains the current Mac command buffer index in 'mac_cmd_buffer' */ diff --git a/features/lorawan/lorastack/mac/LoRaMacMcps.cpp b/features/lorawan/lorastack/mac/LoRaMacMcps.cpp index c5331abd165..ab340e10d01 100644 --- a/features/lorawan/lorastack/mac/LoRaMacMcps.cpp +++ b/features/lorawan/lorastack/mac/LoRaMacMcps.cpp @@ -27,7 +27,6 @@ SPDX-License-Identifier: BSD-3-Clause #include "lorastack/mac/LoRaMacMcps.h" LoRaMacMcps::LoRaMacMcps() -: _lora_mac(NULL), _lora_phy(NULL) { } @@ -35,112 +34,24 @@ LoRaMacMcps::~LoRaMacMcps() { } -void LoRaMacMcps::activate_mcps_subsystem(LoRaMac *mac, LoRaPHY *phy) +void LoRaMacMcps::reset_confirmation() { - _lora_mac = mac; - _lora_phy = phy; -} - -lorawan_status_t LoRaMacMcps::set_request(loramac_mcps_req_t *mcpsRequest, - loramac_protocol_params *params) -{ - - if (mcpsRequest == NULL || _lora_phy == NULL || _lora_mac == NULL) { - return LORAWAN_STATUS_PARAMETER_INVALID; - } - - get_phy_params_t get_phy; - phy_param_t phyParam; - lorawan_status_t status = LORAWAN_STATUS_SERVICE_UNKNOWN; - loramac_mhdr_t machdr; - verification_params_t verify; - uint8_t fport = 0; - void *fbuffer; - uint16_t fbuffer_size; - int8_t datarate = DR_0; - bool ready_to_send = false; - - machdr.value = 0; - - // Before performing any MCPS request, clear the confirmation structure memset((uint8_t*) &confirmation, 0, sizeof(confirmation)); confirmation.status = LORAMAC_EVENT_INFO_STATUS_ERROR; +} - // ack_timeout_retry_counter must be reset every time a new request (unconfirmed or confirmed) is performed. - params->ack_timeout_retry_counter = 1; - - switch (mcpsRequest->type) { - case MCPS_UNCONFIRMED: { - ready_to_send = true; - params->max_ack_timeout_retries = 1; - - machdr.bits.mtype = FRAME_TYPE_DATA_UNCONFIRMED_UP; - fport = mcpsRequest->req.unconfirmed.fport; - fbuffer = mcpsRequest->f_buffer; - fbuffer_size = mcpsRequest->f_buffer_size; - datarate = mcpsRequest->req.unconfirmed.data_rate; - break; - } - case MCPS_CONFIRMED: { - ready_to_send = true; - params->max_ack_timeout_retries = mcpsRequest->req.confirmed.nb_trials; - - machdr.bits.mtype = FRAME_TYPE_DATA_CONFIRMED_UP; - fport = mcpsRequest->req.confirmed.fport; - fbuffer = mcpsRequest->f_buffer; - fbuffer_size = mcpsRequest->f_buffer_size; - datarate = mcpsRequest->req.confirmed.data_rate; - break; - } - case MCPS_PROPRIETARY: { - ready_to_send = true; - params->max_ack_timeout_retries = 1; - - machdr.bits.mtype = FRAME_TYPE_PROPRIETARY; - fbuffer = mcpsRequest->f_buffer; - fbuffer_size = mcpsRequest->f_buffer_size; - datarate = mcpsRequest->req.proprietary.data_rate; - break; - } - default: - break; - } - - // Filter fPorts - // TODO: Does not work with PROPRIETARY messages - // if( IsFPortAllowed( fPort ) == false ) - // { - // return LORAWAN_STATUS_PARAMETER_INVALID; - // } - - // Get the minimum possible datarate - get_phy.attribute = PHY_MIN_TX_DR; - phyParam = _lora_phy->get_phy_params(&get_phy); - - // Apply the minimum possible datarate. - // Some regions have limitations for the minimum datarate. - datarate = MAX(datarate, (int8_t)phyParam.value); - - if (ready_to_send == true) { - if (params->sys_params.adr_on == false) { - verify.datarate = datarate; - - if (_lora_phy->verify(&verify, PHY_TX_DR) == true) { - params->sys_params.channel_data_rate = verify.datarate; - } else { - return LORAWAN_STATUS_PARAMETER_INVALID; - } - } +loramac_mcps_confirm_t& LoRaMacMcps::get_confirmation() +{ + return confirmation; +} - status = _lora_mac->send(&machdr, fport, fbuffer, fbuffer_size); - if (status == LORAWAN_STATUS_OK) { - confirmation.req_type = mcpsRequest->type; - params->flags.bits.mcps_req = 1; - } else { - params->is_node_ack_requested = false; - } - } +loramac_mcps_indication_t& LoRaMacMcps::get_indication() +{ + return indication; +} - return status; +void LoRaMacMcps::activate_mcps_subsystem() +{ } + diff --git a/features/lorawan/lorastack/mac/LoRaMacMcps.h b/features/lorawan/lorastack/mac/LoRaMacMcps.h index 3989c66753c..84093511e3b 100644 --- a/features/lorawan/lorastack/mac/LoRaMacMcps.h +++ b/features/lorawan/lorastack/mac/LoRaMacMcps.h @@ -49,56 +49,31 @@ class LoRaMacMcps { */ ~LoRaMacMcps(); - /** Activating MCPS subsystem - * - * Stores pointers to MAC and PHY layer handles - * - * @param mac pointer to MAC layer - * @param phy pointer to PHY layer + /** + * @brief reset_confirmation Resets the confirmation struct */ - void activate_mcps_subsystem(LoRaMac *mac, LoRaPHY *phy); + void reset_confirmation(); - /** Sets up an MCPS Request - * - * Sets up an MCPS request and sends it through to the central MAC control. - * It also modifies or uses protocol information provided in the MAC - * protocol data structure. - * - * @param mcpsRequest pointer to MCPS request structure - * @param params pointer to MAC protocol parameters + /** Activating MCPS subsystem * - * @return LORAWAN_STATUS_OK if everything goes well otherwise - * a negative error code is returned. + * Stores pointers to MAC and PHY layer handles */ - lorawan_status_t set_request(loramac_mcps_req_t *mcpsRequest, loramac_protocol_params *params); + void activate_mcps_subsystem(); /** Grants access to MCPS confirmation data * * @return a reference to MCPS confirm data structure */ - inline loramac_mcps_confirm_t& get_confirmation() - { - return confirmation; - } + loramac_mcps_confirm_t& get_confirmation(); /** Grants access to MCPS indication data * * @return a reference to MCPS indication data structure */ - inline loramac_mcps_indication_t& get_indication() - { - return indication; - } - + loramac_mcps_indication_t& get_indication(); private: - /** - * Pointers to MAC and PHY handles - */ - LoRaMac *_lora_mac; - LoRaPHY *_lora_phy; - /** * Structure to hold MCPS indication data. */ diff --git a/features/lorawan/lorastack/mac/LoRaMacMib.cpp b/features/lorawan/lorastack/mac/LoRaMacMib.cpp index 39172ef5699..b80ddcf2d4e 100644 --- a/features/lorawan/lorastack/mac/LoRaMacMib.cpp +++ b/features/lorawan/lorastack/mac/LoRaMacMib.cpp @@ -27,7 +27,7 @@ SPDX-License-Identifier: BSD-3-Clause #include "lorastack/mac/LoRaMacMib.h" LoRaMacMib::LoRaMacMib() -: _lora_mac(NULL), _lora_phy(NULL) +: _lora_phy(NULL) { } @@ -35,22 +35,19 @@ LoRaMacMib::~LoRaMacMib() { } -void LoRaMacMib::activate_mib_subsystem(LoRaMac *mac, LoRaPHY *phy) +void LoRaMacMib::activate_mib_subsystem(LoRaPHY *phy) { - _lora_mac = mac; _lora_phy = phy; } lorawan_status_t LoRaMacMib::set_request(loramac_mib_req_confirm_t *mibSet, loramac_protocol_params *params) { - if (mibSet == NULL || _lora_phy == NULL || _lora_mac == NULL) { + if (mibSet == NULL || _lora_phy == NULL) { return LORAWAN_STATUS_PARAMETER_INVALID; } lorawan_status_t status = LORAWAN_STATUS_OK; - verification_params_t verify; - switch (mibSet->type) { case MIB_DEVICE_CLASS: { @@ -75,7 +72,6 @@ lorawan_status_t LoRaMacMib::set_request(loramac_mib_req_confirm_t *mibSet, params->sys_params.min_rx_symb, params->sys_params.max_sys_rx_error, ¶ms->rx_window2_config); - _lora_mac->open_continuous_rx2_window(); break; } } @@ -125,9 +121,7 @@ lorawan_status_t LoRaMacMib::set_request(loramac_mib_req_confirm_t *mibSet, break; } case MIB_RX2_CHANNEL: { - verify.datarate = mibSet->param.rx2_channel.datarate; - - if (_lora_phy->verify(&verify, PHY_RX_DR) == true) { + if (_lora_phy->verify_rx_datarate(mibSet->param.rx2_channel.datarate) == true) { params->sys_params.rx2_channel = mibSet->param.rx2_channel; if ((params->dev_class == CLASS_C) @@ -143,8 +137,6 @@ lorawan_status_t LoRaMacMib::set_request(loramac_mib_req_confirm_t *mibSet, params->sys_params.min_rx_symb, params->sys_params.max_sys_rx_error, ¶ms->rx_window2_config); - - _lora_mac->open_continuous_rx2_window(); } } else { status = LORAWAN_STATUS_PARAMETER_INVALID; @@ -152,9 +144,7 @@ lorawan_status_t LoRaMacMib::set_request(loramac_mib_req_confirm_t *mibSet, break; } case MIB_RX2_DEFAULT_CHANNEL: { - verify.datarate = mibSet->param.rx2_channel.datarate; - - if (_lora_phy->verify(&verify, PHY_RX_DR) == true) { + if (_lora_phy->verify_rx_datarate(mibSet->param.rx2_channel.datarate) == true) { params->sys_params.rx2_channel = mibSet->param.default_rx2_channel; } else { status = LORAWAN_STATUS_PARAMETER_INVALID; @@ -200,40 +190,32 @@ lorawan_status_t LoRaMacMib::set_request(loramac_mib_req_confirm_t *mibSet, break; } case MIB_CHANNELS_DEFAULT_DATARATE: { - verify.datarate = mibSet->param.default_channel_data_rate; - - if (_lora_phy->verify(&verify, PHY_DEF_TX_DR) == true) { - params->sys_params.channel_data_rate = verify.datarate; + if (_lora_phy->verify_tx_datarate(mibSet->param.default_channel_data_rate, true)) { + params->sys_params.channel_data_rate = mibSet->param.default_channel_data_rate; } else { status = LORAWAN_STATUS_PARAMETER_INVALID; } break; } case MIB_CHANNELS_DATARATE: { - verify.datarate = mibSet->param.channel_data_rate; - - if (_lora_phy->verify(&verify, PHY_TX_DR) == true) { - params->sys_params.channel_data_rate = verify.datarate; + if (_lora_phy->verify_tx_datarate(mibSet->param.channel_data_rate, false) == true) { + params->sys_params.channel_data_rate = mibSet->param.channel_data_rate; } else { status = LORAWAN_STATUS_PARAMETER_INVALID; } break; } case MIB_CHANNELS_DEFAULT_TX_POWER: { - verify.tx_power = mibSet->param.default_channel_tx_pwr; - - if (_lora_phy->verify(&verify, PHY_DEF_TX_POWER) == true) { - params->sys_params.channel_tx_power = verify.tx_power; + if (_lora_phy->verify_tx_power(mibSet->param.default_channel_tx_pwr)) { + params->sys_params.channel_tx_power = mibSet->param.default_channel_tx_pwr; } else { status = LORAWAN_STATUS_PARAMETER_INVALID; } break; } case MIB_CHANNELS_TX_POWER: { - verify.tx_power = mibSet->param.channel_tx_pwr; - - if (_lora_phy->verify(&verify, PHY_TX_POWER) == true) { - params->sys_params.channel_tx_power = verify.tx_power; + if (_lora_phy->verify_tx_power(mibSet->param.channel_tx_pwr)) { + params->sys_params.channel_tx_power = mibSet->param.channel_tx_pwr; } else { status = LORAWAN_STATUS_PARAMETER_INVALID; } @@ -271,8 +253,6 @@ lorawan_status_t LoRaMacMib::get_request(loramac_mib_req_confirm_t *mibGet, loramac_protocol_params *params) { lorawan_status_t status = LORAWAN_STATUS_OK; - get_phy_params_t get_phy; - phy_param_t phy_param; rx2_channel_params rx2_channel; if( mibGet == NULL ) @@ -329,10 +309,7 @@ lorawan_status_t LoRaMacMib::get_request(loramac_mib_req_confirm_t *mibGet, } case MIB_CHANNELS: { - get_phy.attribute = PHY_CHANNELS; - phy_param = _lora_phy->get_phy_params( &get_phy ); - - mibGet->param.channel_list = phy_param.channel_params; + mibGet->param.channel_list = _lora_phy->get_phy_channels(); break; } case MIB_RX2_CHANNEL: @@ -342,31 +319,19 @@ lorawan_status_t LoRaMacMib::get_request(loramac_mib_req_confirm_t *mibGet, } case MIB_RX2_DEFAULT_CHANNEL: { - get_phy.attribute = PHY_DEF_RX2_DR; - phy_param = _lora_phy->get_phy_params( &get_phy ); - rx2_channel.datarate = phy_param.value; - - get_phy.attribute = PHY_DEF_RX2_FREQUENCY; - phy_param = _lora_phy->get_phy_params( &get_phy ); - rx2_channel.frequency = phy_param.value; - + rx2_channel.datarate = _lora_phy->get_default_rx2_datarate(); + rx2_channel.frequency = _lora_phy->get_default_rx2_frequency(); mibGet->param.rx2_channel = rx2_channel; break; } case MIB_CHANNELS_DEFAULT_MASK: { - get_phy.attribute = PHY_DEFAULT_CHANNEL_MASK; - phy_param = _lora_phy->get_phy_params( &get_phy ); - - mibGet->param.default_channel_mask = phy_param.channel_mask; + mibGet->param.default_channel_mask = _lora_phy->get_channel_mask(true); break; } case MIB_CHANNELS_MASK: { - get_phy.attribute = PHY_CHANNEL_MASK; - phy_param = _lora_phy->get_phy_params( &get_phy ); - - mibGet->param.channel_mask = phy_param.channel_mask; + mibGet->param.channel_mask = _lora_phy->get_channel_mask(false); break; } case MIB_CHANNELS_NB_REP: @@ -401,9 +366,7 @@ lorawan_status_t LoRaMacMib::get_request(loramac_mib_req_confirm_t *mibGet, } case MIB_CHANNELS_DEFAULT_DATARATE: { - get_phy.attribute = PHY_DEF_TX_DR; - phy_param = _lora_phy->get_phy_params( &get_phy ); - mibGet->param.default_channel_data_rate = phy_param.value; + mibGet->param.default_channel_data_rate = _lora_phy->get_default_tx_datarate(); break; } case MIB_CHANNELS_DATARATE: @@ -413,9 +376,7 @@ lorawan_status_t LoRaMacMib::get_request(loramac_mib_req_confirm_t *mibGet, } case MIB_CHANNELS_DEFAULT_TX_POWER: { - get_phy.attribute = PHY_DEF_TX_POWER; - phy_param = _lora_phy->get_phy_params( &get_phy ); - mibGet->param.default_channel_tx_pwr = phy_param.value; + mibGet->param.default_channel_tx_pwr = _lora_phy->get_default_tx_power(); break; } case MIB_CHANNELS_TX_POWER: diff --git a/features/lorawan/lorastack/mac/LoRaMacMib.h b/features/lorawan/lorastack/mac/LoRaMacMib.h index 70cbaecfeb2..a442ad6ab47 100644 --- a/features/lorawan/lorastack/mac/LoRaMacMib.h +++ b/features/lorawan/lorastack/mac/LoRaMacMib.h @@ -53,10 +53,9 @@ class LoRaMacMib { * * Stores pointers to MAC and PHY layer handles * - * @param mac pointer to MAC layer * @param phy pointer to PHY layer */ - void activate_mib_subsystem(LoRaMac *mac, LoRaPHY *phy); + void activate_mib_subsystem(LoRaPHY *phy); /** Sets up a MIB Request * @@ -90,9 +89,8 @@ class LoRaMacMib { private: /** - * Pointers to MAC and PHY handles + * Pointer PHY handle */ - LoRaMac *_lora_mac; LoRaPHY *_lora_phy; }; diff --git a/features/lorawan/lorastack/mac/LoRaMacMlme.cpp b/features/lorawan/lorastack/mac/LoRaMacMlme.cpp index 35c0b623a8a..9153fa0d541 100644 --- a/features/lorawan/lorastack/mac/LoRaMacMlme.cpp +++ b/features/lorawan/lorastack/mac/LoRaMacMlme.cpp @@ -27,7 +27,7 @@ SPDX-License-Identifier: BSD-3-Clause #include "lorastack/mac/LoRaMacMlme.h" LoRaMacMlme::LoRaMacMlme() -: _lora_mac(NULL), _lora_phy(NULL), _mac_cmd(NULL) +: _lora_phy(NULL) { } @@ -35,117 +35,39 @@ LoRaMacMlme::~LoRaMacMlme() { } -void LoRaMacMlme::activate_mlme_subsystem(LoRaMac *mac, LoRaPHY *phy, - LoRaMacCommand *cmd) +void LoRaMacMlme::reset_confirmation() +{ + memset((uint8_t*) &confirmation, 0, sizeof(confirmation)); + + confirmation.status = LORAMAC_EVENT_INFO_STATUS_ERROR; +} + +loramac_mlme_confirm_t& LoRaMacMlme::get_confirmation() +{ + return confirmation; +} + +loramac_mlme_indication_t& LoRaMacMlme::get_indication() +{ + return indication; +} + +void LoRaMacMlme::activate_mlme_subsystem(LoRaPHY *phy) { - _lora_mac = mac; _lora_phy = phy; - _mac_cmd = cmd; } -lorawan_status_t LoRaMacMlme::set_request(loramac_mlme_req_t *request, - loramac_protocol_params *params) +void LoRaMacMlme::set_tx_continuous_wave(uint8_t channel, int8_t datarate, int8_t tx_power, + float max_eirp, float antenna_gain, uint16_t timeout) { - if (request && params && _lora_mac && _lora_phy && _mac_cmd) { - - lorawan_status_t status = LORAWAN_STATUS_SERVICE_UNKNOWN; - loramac_mhdr_t machdr; - - verification_params_t verify; - get_phy_params_t get_phy; - phy_param_t phy_param; - - - if (params->mac_state != LORAMAC_IDLE) { - return LORAWAN_STATUS_BUSY; - } - - // Before setting a new MLME request, clear the MLME confirmation - // structure - memset((uint8_t*) &confirmation, 0, sizeof(confirmation)); - - confirmation.status = LORAMAC_EVENT_INFO_STATUS_ERROR; - - switch (request->type) { - case MLME_JOIN: { - if ((params->mac_state & LORAMAC_TX_DELAYED) - == LORAMAC_TX_DELAYED) { - return LORAWAN_STATUS_BUSY; - } - - if ((request->req.join.dev_eui == NULL) - || (request->req.join.app_eui == NULL) - || (request->req.join.app_key == NULL) - || (request->req.join.nb_trials == 0)) { - return LORAWAN_STATUS_PARAMETER_INVALID; - } - - // Verify the parameter NbTrials for the join procedure - verify.nb_join_trials = request->req.join.nb_trials; - - if (_lora_phy->verify(&verify, PHY_NB_JOIN_TRIALS) == false) { - // Value not supported, get default - get_phy.attribute = PHY_DEF_NB_JOIN_TRIALS; - phy_param = _lora_phy->get_phy_params(&get_phy); - request->req.join.nb_trials = (uint8_t) phy_param.value; - } - - params->flags.bits.mlme_req = 1; - confirmation.req_type = request->type; - - params->keys.dev_eui = request->req.join.dev_eui; - params->keys.app_eui = request->req.join.app_eui; - params->keys.app_key = request->req.join.app_key; - params->max_join_request_trials = request->req.join.nb_trials; - - // Reset variable JoinRequestTrials - params->join_request_trial_counter = 0; - - // Setup header information - machdr.value = 0; - machdr.bits.mtype = FRAME_TYPE_JOIN_REQ; - - _lora_mac->reset_mac_parameters(); - - params->sys_params.channel_data_rate = - _lora_phy->get_alternate_DR(params->join_request_trial_counter + 1); - - status = _lora_mac->send(&machdr, 0, NULL, 0); - break; - } - case MLME_LINK_CHECK: { - params->flags.bits.mlme_req = 1; - // LoRaMac will send this command piggy-backed - confirmation.req_type = request->type; - - status = _mac_cmd->add_mac_command(MOTE_MAC_LINK_CHECK_REQ, 0, 0); - break; - } - case MLME_TXCW: { - confirmation.req_type = request->type; - params->flags.bits.mlme_req = 1; - status = _lora_mac->set_tx_continuous_wave(request->req.cw_tx_mode.timeout); - break; - } - case MLME_TXCW_1: { - confirmation.req_type = request->type; - params->flags.bits.mlme_req = 1; - status = _lora_mac->set_tx_continuous_wave1(request->req.cw_tx_mode.timeout, - request->req.cw_tx_mode.frequency, - request->req.cw_tx_mode.power); - break; - } - default: - break; - } - - if (status != LORAWAN_STATUS_OK) { - params->is_node_ack_requested = false; - params->flags.bits.mlme_req = 0; - } - - return status; - } - - return LORAWAN_STATUS_PARAMETER_INVALID; + cw_mode_params_t continuous_wave; + + continuous_wave.channel = channel; + continuous_wave.datarate = datarate; + continuous_wave.tx_power = tx_power; + continuous_wave.max_eirp = max_eirp; + continuous_wave.antenna_gain = antenna_gain; + continuous_wave.timeout = timeout; + + _lora_phy->set_tx_cont_mode(&continuous_wave); } diff --git a/features/lorawan/lorastack/mac/LoRaMacMlme.h b/features/lorawan/lorastack/mac/LoRaMacMlme.h index 2be3db0b71b..82089d19c72 100644 --- a/features/lorawan/lorastack/mac/LoRaMacMlme.h +++ b/features/lorawan/lorastack/mac/LoRaMacMlme.h @@ -28,7 +28,6 @@ SPDX-License-Identifier: BSD-3-Clause #include "lorawan/system/lorawan_data_structures.h" #include "lorastack/phy/LoRaPHY.h" -#include "lorastack/mac/LoRaMacCommand.h" // forward declaration class LoRaMac; @@ -50,56 +49,49 @@ class LoRaMacMlme { */ ~LoRaMacMlme(); + /** + * @brief reset_confirmation Resets the confirmation struct + */ + void reset_confirmation(); + /** Activating MLME subsystem * * Stores pointers to MAC and PHY layer handles * - * @param mac pointer to MAC layer * @param phy pointer to PHY layer - * @param cmd pointer to MAC commands */ - void activate_mlme_subsystem(LoRaMac *mac, LoRaPHY *phy, LoRaMacCommand *cmd); - - /** Sets up an MLME Request - * - * Sets up an MLME request, e.g., a Join Request and sends it through - * to the central MAC control. It also modifies or uses protocol information - * provided in the MAC protocol data structure. - * - * @param request pointer to MLME request structure - * @param params pointer to MAC protocol parameters - * - * @return LORAWAN_STATUS_OK if everything goes well otherwise - * a negative error code is returned. - */ - lorawan_status_t set_request(loramac_mlme_req_t *request, loramac_protocol_params *params); + void activate_mlme_subsystem(LoRaPHY *phy); /** Grants access to MLME confirmation data * * @return a reference to MLME confirm data structure */ - inline loramac_mlme_confirm_t& get_confirmation() - { - return confirmation; - } + loramac_mlme_confirm_t& get_confirmation(); /** Grants access to MLME indication data * * @return a reference to MLME indication data structure */ - inline loramac_mlme_indication_t& get_indication() - { - return indication; - } + loramac_mlme_indication_t& get_indication(); + + /** + * @brief set_tx_continuous_wave Puts the system in continuous transmission mode + * @param [in] channel A Channel to use + * @param [in] datarate A datarate to use + * @param [in] tx_power A RF output power to use + * @param [in] max_eirp A maximum possible EIRP to use + * @param [in] antenna_gain Antenna gain to use + * @param [in] timeout Time in seconds while the radio is kept in continuous wave mode + */ + void set_tx_continuous_wave(uint8_t channel, int8_t datarate, int8_t tx_power, + float max_eirp, float antenna_gain, uint16_t timeout); private: /** - * Pointers to MAC and PHY handles + * Pointer to PHY handle */ - LoRaMac *_lora_mac; LoRaPHY *_lora_phy; - LoRaMacCommand *_mac_cmd; /** * Structure to hold MLME indication data. diff --git a/features/lorawan/lorastack/phy/LoRaPHY.cpp b/features/lorawan/lorastack/phy/LoRaPHY.cpp index b134da70458..0461c6517e1 100644 --- a/features/lorawan/lorastack/phy/LoRaPHY.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHY.cpp @@ -46,6 +46,18 @@ LoRaPHY::~LoRaPHY() _radio = NULL; } +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; @@ -475,151 +487,136 @@ uint8_t LoRaPHY::enabled_channel_count(bool joined, uint8_t datarate, return count; } -phy_param_t LoRaPHY::get_phy_params(get_phy_params_t* getPhy) +void LoRaPHY::reset_to_default_values(loramac_protocol_params *params, bool init) { - phy_param_t phyParam = { 0 }; + if (init) { + params->is_dutycycle_on = phy_params.duty_cycle_enabled; - switch (getPhy->attribute) { - case PHY_MIN_RX_DR: { - if (phy_params.dl_dwell_time_setting == 0) { - phyParam.value = phy_params.min_rx_datarate; - } else { - phyParam.value = phy_params.dwell_limit_datarate; - } - break; - } - case PHY_MIN_TX_DR: { - if (phy_params.ul_dwell_time_setting == 0) { - phyParam.value = phy_params.min_tx_datarate; - } else { - phyParam.value = phy_params.dwell_limit_datarate; - } - break; - } - case PHY_DEF_TX_DR: { - phyParam.value = phy_params.default_datarate; - break; - } - case PHY_NEXT_LOWER_TX_DR: { - if (phy_params.ul_dwell_time_setting == 0) { - phyParam.value = get_next_lower_dr(getPhy->datarate, - phy_params.min_tx_datarate); - } else { - phyParam.value = get_next_lower_dr( - getPhy->datarate, phy_params.dwell_limit_datarate); - } - break; - } - case PHY_DEF_TX_POWER: { - phyParam.value = phy_params.default_tx_power; - break; - } - case PHY_MAX_PAYLOAD: { - uint8_t *payload_table = (uint8_t *) phy_params.payloads.table; - phyParam.value = payload_table[getPhy->datarate]; - break; - } - case PHY_MAX_PAYLOAD_REPEATER: { - uint8_t *payload_table = (uint8_t *) phy_params.payloads_with_repeater.table; - phyParam.value = payload_table[getPhy->datarate]; - break; - } - case PHY_DUTY_CYCLE: { - phyParam.value = phy_params.duty_cycle_enabled; - break; - } - case PHY_MAX_RX_WINDOW: { - phyParam.value = phy_params.max_rx_window; - break; - } - case PHY_RECEIVE_DELAY1: { - phyParam.value = phy_params.recv_delay1; - break; - } - case PHY_RECEIVE_DELAY2: { - phyParam.value = phy_params.recv_delay2; - break; - } - case PHY_JOIN_ACCEPT_DELAY1: { - phyParam.value = phy_params.join_accept_delay1; - break; - } - case PHY_JOIN_ACCEPT_DELAY2: { - phyParam.value = phy_params.join_accept_delay2; - break; - } - case PHY_MAX_FCNT_GAP: { - phyParam.value = phy_params.max_fcnt_gap; - break; - } - case PHY_ACK_TIMEOUT: { - uint16_t ack_timeout = phy_params.ack_timeout; - uint16_t ack_timeout_rnd = phy_params.ack_timeout_rnd; - phyParam.value = (ack_timeout - + get_random(-ack_timeout_rnd, ack_timeout_rnd)); - break; - } - case PHY_DEF_DR1_OFFSET: { - phyParam.value = phy_params.default_rx1_dr_offset; - break; - } - case PHY_DEF_RX2_FREQUENCY: { - phyParam.value = phy_params.rx_window2_frequency; - break; - } - case PHY_DEF_RX2_DR: { - phyParam.value = phy_params.rx_window2_datarate; - break; - } - case PHY_CHANNEL_MASK: { - phyParam.channel_mask = phy_params.channels.mask; - break; - } - case PHY_DEFAULT_CHANNEL_MASK: { - phyParam.channel_mask = phy_params.channels.default_mask; - break; - } - case PHY_MAX_NB_CHANNELS: { - phyParam.value = phy_params.max_channel_cnt; - break; - } - case PHY_CHANNELS: { - phyParam.channel_params = phy_params.channels.channel_list; - break; - } - case PHY_CUSTOM_CHANNEL_PLAN_SUPPORT: - // 0 if custom channel plans are not supported (in LoRaWAN terms - // the regions who do not support custom channels are called as - // regions with dynamic channel plans) - phyParam.value = (uint32_t) phy_params.custom_channelplans_supported; - break; - case PHY_DEF_UPLINK_DWELL_TIME: { - phyParam.value = phy_params.ul_dwell_time_setting; - break; - } - case PHY_DEF_DOWNLINK_DWELL_TIME: { - phyParam.value = phy_params.dl_dwell_time_setting; - break; - } - case PHY_DEF_MAX_EIRP: { - phyParam.f_value = phy_params.default_max_eirp; - break; - } - case PHY_DEF_ANTENNA_GAIN: { - phyParam.f_value = phy_params.default_antenna_gain; - break; - } - case PHY_NB_JOIN_TRIALS: - case PHY_DEF_NB_JOIN_TRIALS: { - phyParam.value = MBED_CONF_LORA_NB_TRIALS; - break; - } - default: { - break; - } + 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(); + + params->sys_params.channel_data_rate = get_default_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 phyParam; + 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_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() @@ -630,66 +627,53 @@ void LoRaPHY::restore_default_channels() } } -bool LoRaPHY::verify(verification_params_t* verify, phy_attributes_t phy_attribute) +bool LoRaPHY::verify_rx_datarate(uint8_t datarate) { - switch(phy_attribute) { - case PHY_TX_DR: - { - if (phy_params.ul_dwell_time_setting == 0) { - return val_in_range(verify->datarate, - phy_params.min_tx_datarate, - phy_params.max_tx_datarate); - } else { - return val_in_range(verify->datarate, - phy_params.dwell_limit_datarate, - phy_params.max_tx_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.min_rx_datarate); + } else { + return val_in_range(datarate, + phy_params.dwell_limit_datarate, + phy_params.min_rx_datarate ); + } +} - } - case PHY_DEF_TX_DR: - { - return val_in_range(verify->datarate, - phy_params.default_datarate, - phy_params.default_max_datarate); - } - case PHY_RX_DR: - { - if (phy_params.dl_dwell_time_setting == 0) { - return val_in_range(verify->datarate, - phy_params.min_rx_datarate, - phy_params.max_rx_datarate); - } else { - return val_in_range(verify->datarate, - phy_params.dwell_limit_datarate, - phy_params.max_rx_datarate); - } - } - case PHY_DEF_TX_POWER: - case PHY_TX_POWER: - { - // Remark: switched min and max! - return val_in_range(verify->tx_power, phy_params.max_tx_power, - phy_params.min_tx_power); - } - case PHY_DUTY_CYCLE: - { - if (verify->duty_cycle == phy_params.duty_cycle_enabled) { - return true; - } +bool LoRaPHY::verify_tx_datarate(uint8_t datarate, bool use_default) +{ + 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); + } +} - return false; - } - case PHY_NB_JOIN_TRIALS: - { - if (verify->nb_join_trials < MBED_CONF_LORA_NB_TRIALS) { - return false; - } - break; - } - default: - return false; +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; } @@ -750,8 +734,6 @@ bool LoRaPHY::get_next_ADR(bool restore_channel_mask, int8_t& dr_out, { bool set_adr_ack_bit = false; - get_phy_params_t get_phy; - phy_param_t phy_param; uint16_t ack_limit_plus_delay = phy_params.adr_ack_limit + phy_params.adr_ack_delay; if (dr_out == phy_params.min_tx_datarate) { @@ -770,10 +752,7 @@ bool LoRaPHY::get_next_ADR(bool restore_channel_mask, int8_t& dr_out, if (adr_ack_cnt >= ack_limit_plus_delay) { if ((adr_ack_cnt % phy_params.adr_ack_delay) == 1) { // Decrease the datarate - get_phy.attribute = PHY_NEXT_LOWER_TX_DR; - get_phy.datarate = dr_out; - phy_param = get_phy_params(&get_phy); - dr_out = phy_param.value; + 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 diff --git a/features/lorawan/lorastack/phy/LoRaPHY.h b/features/lorawan/lorastack/phy/LoRaPHY.h index df8f898e7dc..fe0ec358d33 100644 --- a/features/lorawan/lorastack/phy/LoRaPHY.h +++ b/features/lorawan/lorastack/phy/LoRaPHY.h @@ -115,23 +115,17 @@ class LoRaPHY : private mbed::NonCopyable { /** * Tests if a channel is on or off in the channel mask */ - inline bool mask_bit_test(const uint16_t *mask, unsigned bit) { - return mask[bit/16] & (1U << (bit % 16)); - } + bool mask_bit_test(const uint16_t *mask, unsigned bit); /** * Tests if a channel is on or off in the channel mask */ - inline void mask_bit_set(uint16_t *mask, unsigned bit) { - mask[bit/16] |= (1U << (bit % 16)); - } + void mask_bit_set(uint16_t *mask, unsigned bit); /** * Tests if a channel is on or off in the channel mask */ - inline void mask_bit_clear(uint16_t *mask, unsigned bit) { - mask[bit/16] &= ~(1U << (bit % 16)); - } + void mask_bit_clear(uint16_t *mask, unsigned bit); /** Entertain a new channel request MAC command. * @@ -145,18 +139,6 @@ class LoRaPHY : private mbed::NonCopyable { */ virtual uint8_t request_new_channel(new_channel_req_params_t* new_channel_req); - /** Grants access to PHY layer parameters. - * - * This is essentially a PHY layer parameter retrieval system. - * A request is made for a certain parameter by setting an appropriate - * attribute. - * - * @param [in] get_phy A pointer to get_phy_params_t - * - * @return A structure containing the requested PHY parameter value. - */ - virtual phy_param_t get_phy_params(get_phy_params_t* get_phy); - /** Process PHY layer state after a successful transmission. * * Updates times of the last transmission for the particular channel and @@ -173,17 +155,6 @@ class LoRaPHY : private mbed::NonCopyable { */ virtual void restore_default_channels(); - /** Verify if a parameter is eligible. - * - * @param verify A pointer to the verification_params_t that contains - * parameters which we need to check for validity. - * - * @param phy_attr The attribute for which the verification is needed. - * - * @return True, if the parameter is valid. - */ - virtual bool verify(verification_params_t* verify, phy_attributes_t phy_attr); - /** Processes the incoming CF-list. * * Handles the payload containing CF-list and enables channels defined @@ -406,6 +377,140 @@ class LoRaPHY : private mbed::NonCopyable { */ virtual uint8_t apply_DR_offset(int8_t dr, int8_t dr_offset); + /** + * @brief reset_to_default_values resets some parameters to default values + * @param params Pointer to MAC protocol parameters which will be reset + * @param init If true, most of the values will be modified + */ + void reset_to_default_values(loramac_protocol_params *params, bool init = false); + +public: + /** + * @brief get_next_lower_tx_datarate Gets the next lower datarate + * @param datarate Current TX datarate + * @return Lower datarate than given one or minimum if lower cannot be found anymore + */ + int8_t get_next_lower_tx_datarate(int8_t datarate); + + /** + * @brief get_minimum_rx_datarate Gets the minimum RX datarate supported by a device + * @return Minimum RX datarate + */ + uint8_t get_minimum_rx_datarate(); + + /** + * @brief get_minimum_tx_datarate Gets the minimum TX datarate supported by a device + * @return Minimum TX datarate + */ + uint8_t get_minimum_tx_datarate(); + + /** + * @brief get_default_tx_datarate Gets the default TX datarate + * @return default TX datarate + */ + uint8_t get_default_tx_datarate(); + + /** + * @brief get_default_tx_power Gets the default TX power + * @return Default TX power + */ + uint8_t get_default_tx_power(); + + /** + * @brief get_max_payload Gets maximum amount in bytes which device can send + * @param datarate A datarate to use + * @param use_repeater If true repeater table is used, otherwise payloads table is used + * @return Maximum number of bytes for payload + */ + uint8_t get_max_payload(uint8_t datarate, bool use_repeater = false); + + /** + * @brief get_maximum_frame_counter_gap Gets maximum frame counter gap + * @return Maximum frame counter gap + */ + uint16_t get_maximum_frame_counter_gap(); + + /** + * @brief get_ack_timeout Gets timeout value for ACK to be received + * @return ACK timeout + */ + uint32_t get_ack_timeout(); + + /** + * @brief get_default_rx2_frequency Gets default RX2 frequency + * @return RX2 frequency + */ + uint32_t get_default_rx2_frequency(); + + /** + * @brief get_default_rx2_datarate Gets default RX2 datarate + * @return RX2 datarate + */ + uint8_t get_default_rx2_datarate(); + + /** + * @brief get_channel_mask Gets the channel mask + * @param get_default If true the default mask is returned, otherwise the current mask is returned + * @return A channel mask + */ + uint16_t* get_channel_mask(bool get_default = false); + + /** + * @brief get_max_nb_channels Gets maximum number of channels supported + * @return Number of channels + */ + uint8_t get_max_nb_channels(); + + /** + * @brief get_phy_channels Gets PHY channels + * @return PHY channels + */ + channel_params_t* get_phy_channels(); + + /** + * @brief is_custom_channel_plan_supported Checks if custom channel plan is supported + * @return True if custom channel plan is supported, false otherwise + */ + bool is_custom_channel_plan_supported(); + +public: //Verifiers + + /** + * @brief verify_rx_datarate Verifies that given RX datarate is valid + * @param datarate Datarate to check + * @return true if given datarate is valid, false otherwise + */ + bool verify_rx_datarate(uint8_t datarate); + + /** + * @brief verify_tx_datarate Verifies that given TX datarate is valid + * @param datarate Datarate to check + * @param use_default If true validation is done against default value + * @return true if given datarate is valid, false otherwise + */ + bool verify_tx_datarate(uint8_t datarate, bool use_default = false); + + /** + * @brief verify_tx_power Verifies that given TX power is valid + * @param tx_power Power to check + * @return True if valid, false otherwise + */ + bool verify_tx_power(uint8_t tx_power); + + /** + * @brief verify_duty_cycle Verifies that given cycle is valid + * @param cycle Cycle to check + * @return True if valid, false otherwise + */ + bool verify_duty_cycle(bool cycle); + + /** + * @brief verify_nb_join_trials Verifies that given number of trials is valid + * @param nb_join_trials Number to check + * @return True if valid, false otherwise + */ + bool verify_nb_join_trials(uint8_t nb_join_trials); + protected: LoRaRadio *_radio; LoRaWANTimeHandler &_lora_time; diff --git a/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.cpp b/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.cpp index 2e628908b6e..babac4d70f9 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.cpp @@ -334,9 +334,6 @@ bool LoRaPHYUS915Hybrid::get_next_ADR(bool restore_channel_mask, int8_t& dr_out, { bool adrAckReq = false; - get_phy_params_t get_phy; - phy_param_t phy_param; - uint16_t ack_limit_plus_delay = phy_params.adr_ack_limit + phy_params.adr_ack_delay; if (dr_out == phy_params.min_tx_datarate) { @@ -356,10 +353,7 @@ bool LoRaPHYUS915Hybrid::get_next_ADR(bool restore_channel_mask, int8_t& dr_out, if (adr_ack_cnt >= ack_limit_plus_delay) { if ((adr_ack_cnt % phy_params.adr_ack_delay) == 1) { // Decrease the datarate - get_phy.attribute = PHY_NEXT_LOWER_TX_DR; - get_phy.datarate = dr_out; - phy_param = get_phy_params(&get_phy); - dr_out = phy_param.value; + 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 diff --git a/features/lorawan/lorastack/phy/lora_phy_ds.h b/features/lorawan/lorastack/phy/lora_phy_ds.h index ebcd0f6ae0d..3f00b95355d 100644 --- a/features/lorawan/lorastack/phy/lora_phy_ds.h +++ b/features/lorawan/lorastack/phy/lora_phy_ds.h @@ -524,194 +524,6 @@ */ #define TX_POWER_15 15 -/*! - * Enumeration of PHY attributes. - */ -typedef enum phy_attributes__e -{ - /*! - * The minimum RX datarate. - */ - PHY_MIN_RX_DR, - /*! - * The minimum TX datarate. - */ - PHY_MIN_TX_DR, - /*! - * The maximum RX datarate. - */ - PHY_MAX_RX_DR, - /*! - * The maximum TX datarate. - */ - PHY_MAX_TX_DR, - /*! - * The TX datarate. - */ - PHY_TX_DR, - /*! - * The default TX datarate. - */ - PHY_DEF_TX_DR, - /*! - * The RX datarate. - */ - PHY_RX_DR, - /*! - * The TX power. - */ - PHY_TX_POWER, - /*! - * The default TX power. - */ - PHY_DEF_TX_POWER, - /*! - * Maximum payload possible. - */ - PHY_MAX_PAYLOAD, - /*! - * Maximum payload possible when the repeater support is enabled. - */ - PHY_MAX_PAYLOAD_REPEATER, - /*! - * The duty cycle. - */ - PHY_DUTY_CYCLE, - /*! - * The maximum receive window duration. - */ - PHY_MAX_RX_WINDOW, - /*! - * The receive delay for window 1. - */ - PHY_RECEIVE_DELAY1, - /*! - * The receive delay for window 2. - */ - PHY_RECEIVE_DELAY2, - /*! - * The join accept delay for window 1. - */ - PHY_JOIN_ACCEPT_DELAY1, - /*! - * The join accept delay for window 2. - */ - PHY_JOIN_ACCEPT_DELAY2, - /*! - * The maximum frame counter gap. - */ - PHY_MAX_FCNT_GAP, - /*! - * The acknowledgement time out. - */ - PHY_ACK_TIMEOUT, - /*! - * The default datarate offset for window 1. - */ - PHY_DEF_DR1_OFFSET, - /*! - * The default receive window 2 frequency. - */ - PHY_DEF_RX2_FREQUENCY, - /*! - * The default receive window 2 datarate. - */ - PHY_DEF_RX2_DR, - /*! - * The channels mask. - */ - PHY_CHANNEL_MASK, - /*! - * The default channel mask. - */ - PHY_DEFAULT_CHANNEL_MASK, - /*! - * The maximum number of supported channels. - */ - PHY_MAX_NB_CHANNELS, - /*! - * The channels. - */ - PHY_CHANNELS, - /*! - * The PHYs that support dynamic channel plan (non-custom) - * support do not let the user add/remove to the channel plan. - * The network guides the device for the plan. So only - * EU like regions support custom channel planning. - */ - PHY_CUSTOM_CHANNEL_PLAN_SUPPORT, - /*! - * The default value of the uplink dwell time. - */ - PHY_DEF_UPLINK_DWELL_TIME, - /*! - * The default value of the downlink dwell time. - */ - PHY_DEF_DOWNLINK_DWELL_TIME, - /*! - * The default value of the MaxEIRP. - */ - PHY_DEF_MAX_EIRP, - /*! - * The default value of the antenna gain. - */ - PHY_DEF_ANTENNA_GAIN, - /*! - * The value for the number of join trials. - */ - PHY_NB_JOIN_TRIALS, - /*! - * The default value for the number of join trials. - */ - PHY_DEF_NB_JOIN_TRIALS, - /*! - * The next lower datarate. - */ - PHY_NEXT_LOWER_TX_DR -} phy_attributes_t; - -/*! - * Keeps value in response to a call to - * get_phy_params() API. - */ -typedef union phy_param_u -{ - /*! - * A parameter value. - */ - uint32_t value; - /*! - * A floating point value. - */ - float f_value; - /*! - * A pointer to the channels mask. - */ - uint16_t* channel_mask; - /*! - * A pointer to the channels. - */ - channel_params_t* channel_params; -} phy_param_t; - -/** - * The parameter structure for the function RegionGetPhyParam. - */ -typedef struct -{ - /** - * Set up the parameter to get. - */ - phy_attributes_t attribute; - - /** - * The parameter is needed for the following queries: - * PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_NEXT_LOWER_TX_DR. - */ - int8_t datarate; - -} get_phy_params_t; - /** * The parameter structure for the function RegionSetBandTxDone. */ @@ -731,30 +543,6 @@ typedef struct lorawan_time_t last_tx_done_time; } set_band_txdone_params_t; -/** - * The parameter verification structure. - */ -typedef union -{ - /** - * The TX power to verify. - */ - int8_t tx_power; - /** - * Set to true, if the duty cycle is enabled, otherwise false. - */ - bool duty_cycle; - /** - * The number of join trials. - */ - uint8_t nb_join_trials; - /** - * The datarate to verify. - */ - int8_t datarate; - -} verification_params_t; - /** * The parameter structure for the function RegionApplyCFList. */ diff --git a/features/lorawan/system/lorawan_data_structures.h b/features/lorawan/system/lorawan_data_structures.h index cc37cf01786..2c8d357f3f0 100644 --- a/features/lorawan/system/lorawan_data_structures.h +++ b/features/lorawan/system/lorawan_data_structures.h @@ -763,27 +763,14 @@ typedef enum { } mcps_type_t; /*! - * LoRaMAC MCPS-Request for an unconfirmed frame. + * LoRaMAC MCPS-Request structure. */ typedef struct { /*! - * Frame port field. Must be set if the payload is not empty. Use the - * application-specific frame port values: [1...223]. - * - * LoRaWAN Specification V1.0.2, chapter 4.3.2. - */ - uint8_t fport; - - /*! - * Uplink datarate, if ADR is off. + * MCPS-Request type. */ - int8_t data_rate; -} mcps_req_unconfirmed_t; + mcps_type_t type; -/*! - * LoRaMAC MCPS-Request for a confirmed frame. - */ -typedef struct { /*! * Frame port field. Must be set if the payload is not empty. Use the * application-specific frame port values: [1...223]. @@ -817,45 +804,6 @@ typedef struct { * the datarate, if the LoRaMAC layer did not receive an acknowledgment. */ uint8_t nb_trials; -} mcps_req_confirmed_t; - -/*! - * LoRaMAC MCPS-Request for a proprietary frame. - */ -typedef struct { - /*! - * Uplink datarate, if ADR is off. - */ - int8_t data_rate; -} mcps_req_proprietary_t; - -/*! - * LoRaMAC MCPS-Request structure. - */ -typedef struct { - /*! - * MCPS-Request type. - */ - mcps_type_t type; - - /*! - * MCPS-Request parameters. - */ - union - { - /*! - * MCPS-Request parameters for an unconfirmed frame. - */ - mcps_req_unconfirmed_t unconfirmed; - /*! - * MCPS-Request parameters for a confirmed frame. - */ - mcps_req_confirmed_t confirmed; - /*! - * MCPS-Request parameters for a proprietary frame. - */ - mcps_req_proprietary_t proprietary; - } req; /** Payload data * @@ -1663,23 +1611,6 @@ typedef struct { mbed::Callback mlme_indication; }loramac_primitives_t; -/** End-device states. - * - */ -typedef enum device_states { - DEVICE_STATE_NOT_INITIALIZED, - DEVICE_STATE_INIT, - DEVICE_STATE_JOINING, - DEVICE_STATE_ABP_CONNECTING, - DEVICE_STATE_JOINED, - DEVICE_STATE_SEND, - DEVICE_STATE_IDLE, -#if defined(LORAWAN_COMPLIANCE_TEST) - DEVICE_STATE_COMPLIANCE_TEST, -#endif - DEVICE_STATE_SHUTDOWN -} device_states_t; - /** Enum of LoRaWAN connection type. * * The LoRaWAN connection type specifies how an end-device connects to the gateway. @@ -1764,26 +1695,40 @@ typedef struct { * Message type */ mcps_type_t type; - /** Message parameters. + + /*! + * Frame port field. Must be set if the payload is not empty. Use the + * application-specific frame port values: [1...223]. * + * LoRaWAN Specification V1.0.2, chapter 4.3.2. */ - union message { - /** An unconfirmed frame. - * - * The message parameters for an unconfirmed frame. - */ - mcps_req_unconfirmed_t unconfirmed; - /** A confirmed frame. - * - * The message parameters for a confirmed frame. - */ - mcps_req_confirmed_t confirmed; - /** A proprietary frame. - * - * The message parameters for a proprietary frame. - */ - mcps_req_proprietary_t proprietary; - } message_u; + uint8_t fport; + + /*! + * Uplink datarate, if ADR is off. + */ + int8_t data_rate; + /*! + * The number of trials to transmit the frame, if the LoRaMAC layer did not + * receive an acknowledgment. The MAC performs a datarate adaptation + * according to the LoRaWAN Specification V1.0.2, chapter 18.4, as in + * the following table: + * + * Transmission nb | Data Rate + * ----------------|----------- + * 1 (first) | DR + * 2 | DR + * 3 | max(DR-1,0) + * 4 | max(DR-1,0) + * 5 | max(DR-2,0) + * 6 | max(DR-2,0) + * 7 | max(DR-3,0) + * 8 | max(DR-3,0) + * + * Note that if nb_trials is set to 1 or 2, the MAC will not decrease + * the datarate, if the LoRaMAC layer did not receive an acknowledgment. + */ + uint8_t nb_trials; /** Payload data * @@ -1918,54 +1863,6 @@ typedef struct { uint32_t downlink_counter; } lorawan_dev_commission_t; -#if defined(LORAWAN_COMPLIANCE_TEST) -/** LoRaWAN compliance tests support data - * - */ -typedef struct compliance_test { - /** Is test running - * - */ - bool running; - /** State of test - * - */ - uint8_t state; - /** Is TX confirmed - * - */ - bool is_tx_confirmed; - /** Port used by the application - * - */ - uint8_t app_port; - /** Maximum size of data used by application - * - */ - uint8_t app_data_size; - /** Data provided by application - * - */ - uint8_t *app_data_buffer; - /** Downlink counter - * - */ - uint16_t downlink_counter; - /** Is link check required - * - */ - bool link_check; - /** Demodulation margin - * - */ - uint8_t demod_margin; - /** Number of gateways - * - */ - uint8_t nb_gateways; -} compliance_test_t; -#endif - /** Structure containing the uplink status * */ @@ -2411,4 +2308,52 @@ typedef struct lora_channelplan { loramac_channel_t *channels; } lorawan_channelplan_t; +#if defined(LORAWAN_COMPLIANCE_TEST) +/** LoRaWAN compliance tests support data + * + */ +typedef struct compliance_test { + /** Is test running + * + */ + bool running; + /** State of test + * + */ + uint8_t state; + /** Is TX confirmed + * + */ + bool is_tx_confirmed; + /** Port used by the application + * + */ + uint8_t app_port; + /** Maximum size of data used by application + * + */ + uint8_t app_data_size; + /** Data provided by application + * + */ + uint8_t *app_data_buffer; + /** Downlink counter + * + */ + uint16_t downlink_counter; + /** Is link check required + * + */ + bool link_check; + /** Demodulation margin + * + */ + uint8_t demod_margin; + /** Number of gateways + * + */ + uint8_t nb_gateways; +} compliance_test_t; +#endif + #endif /* LORAWAN_SYSTEM_LORAWAN_DATA_STRUCTURES_H_ */