diff --git a/features/netsocket/LoRaRadio.h b/features/lorawan/LoRaRadio.h similarity index 93% rename from features/netsocket/LoRaRadio.h rename to features/lorawan/LoRaRadio.h index 4d5f48d597..e78bdb3fbf 100644 --- a/features/netsocket/LoRaRadio.h +++ b/features/lorawan/LoRaRadio.h @@ -18,6 +18,9 @@ #ifndef LORARADIO_H_ #define LORARADIO_H_ +#include "platform/Callback.h" +#include "PinNames.h" + /** * Structure to hold RF controls for LoRa Radio. * SX1276 have an extra control for the crystal (used in DOSCO-L072CZ) @@ -131,17 +134,18 @@ typedef struct radio_settings { * */ typedef struct radio_events { - /* Tx Done callback prototype. - * + /** + * Callback when Transmission is done */ - void (*tx_done) (void); + mbed::Callback tx_done; - /* Tx Timeout callback prototype. - * + /** + * Callback when Transmission is timed out */ - void (*tx_timeout) (void); + mbed::Callback tx_timeout; - /* Rx Done callback prototype. + /** + * Rx Done callback prototype. * * @param payload Received buffer pointer. * @param size Received buffer size. @@ -150,29 +154,31 @@ typedef struct radio_events { * FSK : N/A (set to 0) * LoRa: SNR value in dB */ - void (*rx_done) (uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr); + mbed::Callback rx_done; - /* Rx Timeout callback prototype. - * + /** + * Callback when Reception is timed out */ - void (*rx_timeout) (void); + mbed::Callback rx_timeout; - /* Rx Error callback prototype. - * + /** + * Callback when Reception ends up in error */ - void (*rx_error) (void); + mbed::Callback rx_error; - /* FHSS Change Channel callback prototype. - * - * @param current_channel The index number of the current channel. - */ - void (*fhss_change_channel) (uint8_t current_channel); + /** + * FHSS Change Channel callback prototype. + * + * @param current_channel The index number of the current channel. + */ + mbed::Callback fhss_change_channel; - /* CAD Done callback prototype. + /** + * CAD Done callback prototype. * - * @param channel_activity_detected Channel activity detected during the CAD. + * @param channel_busy True, if Channel activity detected. */ - void (*cad_done) (bool channel_activity_detected); + mbed::Callback cad_done; } radio_events_t; /** diff --git a/features/netsocket/LoRaWANBase.h b/features/lorawan/LoRaWANBase.h similarity index 77% rename from features/netsocket/LoRaWANBase.h rename to features/lorawan/LoRaWANBase.h index b4a6ca815a..f4b3354f5b 100644 --- a/features/netsocket/LoRaWANBase.h +++ b/features/lorawan/LoRaWANBase.h @@ -31,20 +31,20 @@ public: * * @param queue A pointer to EventQueue provided by the application. * - * @return LORA_MAC_STATUS_OK on success, a negative error code on + * @return LORAWAN_STATUS_OK on success, a negative error code on * failure. */ - virtual lora_mac_status_t initialize(events::EventQueue *queue) = 0; + virtual lorawan_status_t initialize(events::EventQueue *queue) = 0; /** Connect OTAA or ABP by setup. * * Connect by Over The Air Activation or Activation By Personalization. * The connection type is selected at the setup. * - * @return LORA_MAC_STATUS_OK on success, a negative error code on + * @return LORAWAN_STATUS_OK on success, a negative error code on * failure. */ - virtual lora_mac_status_t connect() = 0; + virtual lorawan_status_t connect() = 0; /** Connect OTAA or ABP by parameters * @@ -53,46 +53,78 @@ public: * You need to define the parameters in the main application. * * @param connect Options how end-device will connect to gateway - * @return LORA_MAC_STATUS_OK on success, negative error code + * @return LORAWAN_STATUS_OK on success, negative error code * on failure */ - virtual lora_mac_status_t connect(const lorawan_connect_t &connect) = 0; + virtual lorawan_status_t connect(const lorawan_connect_t &connect) = 0; /** Disconnects the current session. * - * @return LORA_MAC_STATUS_OK on success, a negative error code on failure. + * @return LORAWAN_STATUS_OK on success, a negative error code on failure. */ - virtual lora_mac_status_t disconnect() = 0; + virtual lorawan_status_t disconnect() = 0; + + /** Validate the connectivity with the network. + * + * Application may use this API to submit a request to the stack for + * validation of its connectivity to a Network Server. Under the hood, this + * API schedules a Link Check Request command (LinkCheckReq) for the network + * server and once the response, i.e., LinkCheckAns MAC command is received + * from the Network Server, user provided method is called. + * + * This API is usable only when the link check response is callback set by + * the application. See add_lora_app_callbacks API. If the above mentioned + * callback is not set, a LORAWAN_STATUS_PARAMETER_INVALID error is thrown. + * + * First parameter to callback function is the demodulation margin and + * the second parameter is the number of gateways that successfully received + * the last request. + * + * A 'Link Check Request' MAC command remains set for every subsequent + * transmission, until/unless application explicitly turns it off using + * remove_link_check_request() API. + * + * @return LORAWAN_STATUS_OK on successfully queuing a request, or + * a negative error code on failure. + * + */ + virtual lorawan_status_t add_link_check_request() = 0; + + /** Detaches Link Request MAC command. + * + * Removes sticky MAC command for link check request. + */ + virtual void remove_link_check_request() = 0; /** Sets up a particular data rate of choice * * @param data_rate Intended data rate e.g., DR_0, DR_1 etc. * Caution is advised as the macro DR_* can mean different * things while being in a different region. - * @return LORA_MAC_STATUS_OK if everything goes well, otherwise + * @return LORAWAN_STATUS_OK if everything goes well, otherwise * a negative error code. */ - virtual lora_mac_status_t set_datarate(uint8_t data_rate) = 0; + virtual lorawan_status_t set_datarate(uint8_t data_rate) = 0; /** Enables adaptive data rate (ADR) * * Underlying LoRaPHY and LoRaMac layers handle the data rate automatically * for the user based upon radio conditions (network congestion). * - * @return LORA_MAC_STATUS_OK on success, negative error code + * @return LORAWAN_STATUS_OK on success, negative error code * on failure. */ - virtual lora_mac_status_t enable_adaptive_datarate() = 0; + virtual lorawan_status_t enable_adaptive_datarate() = 0; /** Disables adaptive data rate * * When adaptive data rate (ADR) is disabled, user can either set a certain * data rate or the Mac layer will choose a default value. * - * @return LORA_MAC_STATUS_OK on success, negative error code + * @return LORAWAN_STATUS_OK on success, negative error code * on failure. */ - virtual lora_mac_status_t disable_adaptive_datarate() = 0; + virtual lorawan_status_t disable_adaptive_datarate() = 0; /** Sets up retry counter for confirmed messages * @@ -108,25 +140,25 @@ public: * * @param count number of retries for confirmed messages * - * @return LORA_MAC_STATUS_OK or a negative error code + * @return LORAWAN_STATUS_OK or a negative error code */ - virtual lora_mac_status_t set_confirmed_msg_retries(uint8_t count) = 0; + virtual lorawan_status_t set_confirmed_msg_retries(uint8_t count) = 0; /** Sets channel plan * * @param channel_plan The defined channel plans to be set. * @return 0 on success, a negative error code on failure. */ - virtual lora_mac_status_t set_channel_plan(const lora_channelplan_t &channel_plan) = 0; + virtual lorawan_status_t set_channel_plan(const lorawan_channelplan_t &channel_plan) = 0; /** Gets the current channel plan. * * @param channel_plan The current channel information. * - * @return LORA_MAC_STATUS_OK on success, a negative error + * @return LORAWAN_STATUS_OK on success, a negative error * code on failure. */ - virtual lora_mac_status_t get_channel_plan(lora_channelplan_t &channel_plan) = 0; + virtual lorawan_status_t get_channel_plan(lorawan_channelplan_t &channel_plan) = 0; /** Removes currently active channel plan * @@ -134,10 +166,10 @@ public: * allowed to be removed. So when a plan is abolished, only non-default * channels are removed. * - * @return LORA_MAC_STATUS_OK on success, negative error + * @return LORAWAN_STATUS_OK on success, negative error * code on failure */ - virtual lora_mac_status_t remove_channel_plan() = 0; + virtual lorawan_status_t remove_channel_plan() = 0; /** Removes a given single channel * @@ -146,10 +178,10 @@ public: * * @param index The channel index * - * @return LORA_MAC_STATUS_OK on success, negative error + * @return LORAWAN_STATUS_OK on success, negative error * code on failure */ - virtual lora_mac_status_t remove_channel(uint8_t index) = 0; + virtual lorawan_status_t remove_channel(uint8_t index) = 0; /** Send message to gateway * @@ -182,7 +214,7 @@ public: * * * @return The number of bytes sent, or - * LORA_MAC_STATUS_WOULD_BLOCK if another TX is + * LORAWAN_STATUS_WOULD_BLOCK if another TX is * ongoing, or a negative error code on failure. */ virtual int16_t send(uint8_t port, const uint8_t* data, @@ -223,7 +255,7 @@ public: * @return It could be one of these: * i) 0 if there is nothing else to read. * ii) Number of bytes written to user buffer. - * iii) LORA_MAC_STATUS_WOULD_BLOCK if there is + * iii) LORAWAN_STATUS_WOULD_BLOCK if there is * nothing available to read at the moment. * iv) A negative error code on failure. */ @@ -298,7 +330,7 @@ public: * @param callbacks A pointer to the structure containing application * callbacks. */ - virtual lora_mac_status_t add_app_callbacks(lorawan_app_callbacks_t *callbacks) = 0; + virtual lorawan_status_t add_app_callbacks(lorawan_app_callbacks_t *callbacks) = 0; }; #endif /* LORAWAN_BASE_H_ */ diff --git a/features/lorawan/LoRaWANInterface.cpp b/features/lorawan/LoRaWANInterface.cpp index df9529d993..8d45b0dee3 100644 --- a/features/lorawan/LoRaWANInterface.cpp +++ b/features/lorawan/LoRaWANInterface.cpp @@ -28,7 +28,7 @@ inline LoRaWANStack& stk_obj() return LoRaWANStack::get_lorawan_stack(); } -LoRaWANInterface::LoRaWANInterface(LoRaRadio& radio) +LoRaWANInterface::LoRaWANInterface(LoRaRadio& radio) : _link_check_requested(false) { // Pass mac_handlers to radio to the radio driver after // binding radio driver to PHY layer @@ -42,16 +42,16 @@ LoRaWANInterface::~LoRaWANInterface() { } -lora_mac_status_t LoRaWANInterface::initialize(EventQueue *queue) +lorawan_status_t LoRaWANInterface::initialize(EventQueue *queue) { if(!queue) { - return LORA_MAC_STATUS_PARAMETER_INVALID; + return LORAWAN_STATUS_PARAMETER_INVALID; } return stk_obj().initialize_mac_layer(queue); } -lora_mac_status_t LoRaWANInterface::connect() +lorawan_status_t LoRaWANInterface::connect() { // connection attempt without parameters. // System tries to look for configuration in mbed_lib.json that can be @@ -61,10 +61,10 @@ lora_mac_status_t LoRaWANInterface::connect() lorawan_connect_t connection_params; - if (OVER_THE_AIR_ACTIVATION != 0) { - static uint8_t dev_eui[] = LORAWAN_DEVICE_EUI; - static uint8_t app_eui[] = LORAWAN_APPLICATION_EUI; - static uint8_t app_key[] = LORAWAN_APPLICATION_KEY; + if (MBED_CONF_LORA_OVER_THE_AIR_ACTIVATION) { + static uint8_t dev_eui[] = MBED_CONF_LORA_DEVICE_EUI; + static uint8_t app_eui[] = MBED_CONF_LORA_APPLICATION_EUI; + static uint8_t app_key[] = MBED_CONF_LORA_APPLICATION_KEY; /** * * OTAA join @@ -73,14 +73,14 @@ lora_mac_status_t LoRaWANInterface::connect() connection_params.connection_u.otaa.app_eui = app_eui; connection_params.connection_u.otaa.dev_eui = dev_eui; connection_params.connection_u.otaa.app_key = app_key; - connection_params.connection_u.otaa.nb_trials = LORAWAN_NB_TRIALS; + connection_params.connection_u.otaa.nb_trials = MBED_CONF_LORA_NB_TRIALS; return connect(connection_params); } else { - static uint8_t nwk_skey[] = LORAWAN_NWKSKEY; - static uint8_t app_skey[] = LORAWAN_APPSKEY; - static uint32_t dev_addr = LORAWAN_DEVICE_ADDRESS; - static uint32_t nwk_id = (LORAWAN_DEVICE_ADDRESS & LORAWAN_NETWORK_ID_MASK); + static uint8_t nwk_skey[] = MBED_CONF_LORA_NWKSKEY; + static uint8_t app_skey[] = MBED_CONF_LORA_APPSKEY; + static uint32_t dev_addr = MBED_CONF_LORA_DEVICE_ADDRESS; + static uint32_t nwk_id = (MBED_CONF_LORA_DEVICE_ADDRESS & LORAWAN_NETWORK_ID_MASK); /** * @@ -96,63 +96,73 @@ lora_mac_status_t LoRaWANInterface::connect() } } -lora_mac_status_t LoRaWANInterface::connect(const lorawan_connect_t &connect) +lorawan_status_t LoRaWANInterface::connect(const lorawan_connect_t &connect) { - lora_mac_status_t mac_status; + lorawan_status_t mac_status; if (connect.connect_type == LORAWAN_CONNECTION_OTAA) { mac_status = stk_obj().join_request_by_otaa(connect); } else if (connect.connect_type == LORAWAN_CONNECTION_ABP) { mac_status = stk_obj().activation_by_personalization(connect); } else { - return LORA_MAC_STATUS_PARAMETER_INVALID; + return LORAWAN_STATUS_PARAMETER_INVALID; } return mac_status; } -lora_mac_status_t LoRaWANInterface::disconnect() +lorawan_status_t LoRaWANInterface::disconnect() { - stk_obj().shutdown(); - return LORA_MAC_STATUS_OK; + return stk_obj().shutdown(); } -lora_mac_status_t LoRaWANInterface::set_datarate(uint8_t data_rate) +lorawan_status_t LoRaWANInterface::add_link_check_request() +{ + _link_check_requested = true; + return stk_obj().set_link_check_request(); +} + +void LoRaWANInterface::remove_link_check_request() +{ + _link_check_requested = false; +} + +lorawan_status_t LoRaWANInterface::set_datarate(uint8_t data_rate) { return stk_obj().set_channel_data_rate(data_rate); } -lora_mac_status_t LoRaWANInterface::set_confirmed_msg_retries(uint8_t count) +lorawan_status_t LoRaWANInterface::set_confirmed_msg_retries(uint8_t count) { return stk_obj().set_confirmed_msg_retry(count); } -lora_mac_status_t LoRaWANInterface::enable_adaptive_datarate() +lorawan_status_t LoRaWANInterface::enable_adaptive_datarate() { return stk_obj().enable_adaptive_datarate(true); } -lora_mac_status_t LoRaWANInterface::disable_adaptive_datarate() +lorawan_status_t LoRaWANInterface::disable_adaptive_datarate() { return stk_obj().enable_adaptive_datarate(false); } -lora_mac_status_t LoRaWANInterface::set_channel_plan(const lora_channelplan_t &channel_plan) +lorawan_status_t LoRaWANInterface::set_channel_plan(const lorawan_channelplan_t &channel_plan) { return stk_obj().add_channels(channel_plan); } -lora_mac_status_t LoRaWANInterface::get_channel_plan(lora_channelplan_t &channel_plan) +lorawan_status_t LoRaWANInterface::get_channel_plan(lorawan_channelplan_t &channel_plan) { return stk_obj().get_enabled_channels(channel_plan); } -lora_mac_status_t LoRaWANInterface::remove_channel(uint8_t id) +lorawan_status_t LoRaWANInterface::remove_channel(uint8_t id) { return stk_obj().remove_a_channel(id); } -lora_mac_status_t LoRaWANInterface::remove_channel_plan() +lorawan_status_t LoRaWANInterface::remove_channel_plan() { return stk_obj().drop_channel_list(); } @@ -160,10 +170,16 @@ lora_mac_status_t LoRaWANInterface::remove_channel_plan() int16_t LoRaWANInterface::send(uint8_t port, const uint8_t* data, uint16_t length, int flags) { + if (_link_check_requested) { + // add a link check request with normal data, until the application + // explicitly removes it. + add_link_check_request(); + } + if (data) { return stk_obj().handle_tx(port, data, length, flags); } else { - return LORA_MAC_STATUS_PARAMETER_INVALID; + return LORAWAN_STATUS_PARAMETER_INVALID; } } @@ -173,19 +189,18 @@ int16_t LoRaWANInterface::receive(uint8_t port, uint8_t* data, uint16_t length, if (data && length > 0) { return stk_obj().handle_rx(port, data, length, flags); } else { - return LORA_MAC_STATUS_PARAMETER_INVALID; + return LORAWAN_STATUS_PARAMETER_INVALID; } } -lora_mac_status_t LoRaWANInterface::add_app_callbacks(lorawan_app_callbacks_t *callbacks) +lorawan_status_t LoRaWANInterface::add_app_callbacks(lorawan_app_callbacks_t *callbacks) { if (!callbacks || !callbacks->events) { // Event Callback is mandatory - return LORA_MAC_STATUS_PARAMETER_INVALID; + return LORAWAN_STATUS_PARAMETER_INVALID; } stk_obj().set_lora_callbacks(callbacks); - - return LORA_MAC_STATUS_OK; + return LORAWAN_STATUS_OK; } diff --git a/features/lorawan/LoRaWANInterface.h b/features/lorawan/LoRaWANInterface.h index 5ea9916554..f8d35b425f 100644 --- a/features/lorawan/LoRaWANInterface.h +++ b/features/lorawan/LoRaWANInterface.h @@ -19,9 +19,9 @@ #define LORAWANINTERFACE_H_ #include "platform/Callback.h" -#include "netsocket/LoRaWANBase.h" #include "lorawan/LoRaWANStack.h" -#include "netsocket/LoRaRadio.h" +#include "lorawan/LoRaRadio.h" +#include "lorawan/LoRaWANBase.h" class LoRaWANInterface: public LoRaWANBase { @@ -44,7 +44,7 @@ public: * * @return 0 on success, a negative error code on failure. */ - virtual lora_mac_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 * @@ -52,7 +52,7 @@ public: * You need to configure the connection properly via the Mbed OS configuration * system. * - * When connecting via OTAA, the return code for success (LORA_MAC_STATUS_CONNECT_IN_PROGRESS) is negative. + * When connecting via OTAA, the return code for success (LORAWAN_STATUS_CONNECT_IN_PROGRESS) is negative. * However, this is not a real error. It tells you that the connection is in progress and you will * be notified of the completion via an event. By default, after the Join Accept message * is received, base stations may provide the node with a CF-List that replaces @@ -78,17 +78,17 @@ public: * is important, at least for ABP. That's why we try to restore frame counters from * session information after a disconnection. * - * @return LORA_MAC_STATUS_OK or LORA_MAC_STATUS_CONNECT_IN_PROGRESS + * @return LORAWAN_STATUS_OK or LORAWAN_STATUS_CONNECT_IN_PROGRESS * on success, or a negative error code on failure. */ - virtual lora_mac_status_t connect(); + virtual lorawan_status_t connect(); /** Connect OTAA or ABP with parameters * * All connection parameters are chosen by the user and provided in the * data structure passed down. * - * When connecting via OTAA, the return code for success (LORA_MAC_STATUS_CONNECT_IN_PROGRESS) is negative. + * When connecting via OTAA, the return code for success (LORAWAN_STATUS_CONNECT_IN_PROGRESS) is negative. * However, this is not a real error. It tells you that connection is in progress and you will * be notified of completion via an event. By default, after Join Accept message * is received, base stations may provide the node with a CF-List which replaces @@ -118,17 +118,54 @@ public: * * @param connect Options for an end device connection to the gateway. * - * @return LORA_MAC_STATUS_OK or LORA_MAC_STATUS_CONNECT_IN_PROGRESS, + * @return LORAWAN_STATUS_OK or LORAWAN_STATUS_CONNECT_IN_PROGRESS, * a negative error code on failure. */ - virtual lora_mac_status_t connect(const lorawan_connect_t &connect); + virtual lorawan_status_t connect(const lorawan_connect_t &connect); /** Disconnect the current session. * - * @return LORA_MAC_STATUS_OK on success, a negative error code on - * failure. + * @return LORAWAN_STATUS_DEVICE_OFF on successfully shutdown. */ - virtual lora_mac_status_t disconnect(); + virtual lorawan_status_t disconnect(); + + /** Validate the connectivity with the network. + * + * Application may use this API to submit a request to the stack for + * validation of its connectivity to a Network Server. Under the hood, this + * API schedules a Link Check Request command (LinkCheckReq) for the network + * server and once the response, i.e., LinkCheckAns MAC command is received + * from the Network Server, user provided method is called. + * + * One way to use this API may be the validation of connectivity after a long + * deep sleep. Mbed LoRaWANStack piggy-backs the MAC commands with data + * frame payload so the application needs to try sending something and the Network + * Server may respond during the RX slots. + * + * This API is usable only when the 'link_check_resp' callback is set by + * the application. See add_lora_app_callbacks API. If the above mentioned + * callback is not set, a LORAWAN_STATUS_PARAMETER_INVALID error is thrown. + * + * First parameter to callback function is the demodulation margin and + * the second parameter is the number of gateways that successfully received + * the last request. + * + * A 'Link Check Request' MAC command remains set for every subsequent + * transmission, until/unless application explicitly turns it off using + * remove_link_check_request() API. + * + * @return LORAWAN_STATUS_OK on successfully queuing a request, or + * a negative error code on failure. + * + */ + virtual lorawan_status_t add_link_check_request(); + + /** Removes link check request sticky MAC command. + * + * Any already queued request may still get entertained. However, no new + * requests will be made. + */ + virtual void remove_link_check_request(); /** Sets up a particular data rate * @@ -138,28 +175,28 @@ public: * @param data_rate The intended data rate, for example DR_0 or DR_1. * Please note, that the macro DR_* can mean different * things in different regions. - * @return LORA_MAC_STATUS_OK if everything goes well, otherwise + * @return LORAWAN_STATUS_OK if everything goes well, otherwise * a negative error code. */ - virtual lora_mac_status_t set_datarate(uint8_t data_rate); + virtual lorawan_status_t set_datarate(uint8_t data_rate); /** Enables adaptive data rate (ADR). * * The underlying LoRaPHY and LoRaMac layers handle the data rate automatically * for the user, based upon the radio conditions (network congestion). * - * @return LORA_MAC_STATUS_OK or negative error code otherwise. + * @return LORAWAN_STATUS_OK or negative error code otherwise. */ - virtual lora_mac_status_t enable_adaptive_datarate(); + virtual lorawan_status_t enable_adaptive_datarate(); /** Disables adaptive data rate. * * When adaptive data rate (ADR) is disabled, you can either set a certain * data rate or the MAC layer selects a default value. * - * @return LORA_MAC_STATUS_OK or negative error code otherwise. + * @return LORAWAN_STATUS_OK or negative error code otherwise. */ - virtual lora_mac_status_t disable_adaptive_datarate(); + virtual lorawan_status_t disable_adaptive_datarate(); /** Sets up the retry counter for confirmed messages. * @@ -174,9 +211,9 @@ public: * * @param count The number of retries for confirmed messages. * - * @return LORA_MAC_STATUS_OK or a negative error code. + * @return LORAWAN_STATUS_OK or a negative error code. */ - virtual lora_mac_status_t set_confirmed_msg_retries(uint8_t count); + virtual lorawan_status_t set_confirmed_msg_retries(uint8_t count); /** Sets the channel plan. * @@ -188,6 +225,9 @@ public: * is already active, the request is silently ignored. A negative error * code is returned if there is any problem with parameters. * + * Please note that this API can also be used to add a single channel to the + * existing channel plan. + * * There is no reverse mechanism in the 1.0.2 specification for a node to request * a particular channel. Only the network server can initiate such a request. * You need to ensure that the corresponding base station supports the channel or channels being added. @@ -199,10 +239,10 @@ public: * * @param channel_plan The channel plan to set. * - * @return LORA_MAC_STATUS_OK on success, a negative error + * @return LORAWAN_STATUS_OK on success, a negative error * code on failure. */ - virtual lora_mac_status_t set_channel_plan(const lora_channelplan_t &channel_plan); + virtual lorawan_status_t set_channel_plan(const lorawan_channelplan_t &channel_plan); /** Gets the channel plans from the LoRa stack. * @@ -213,20 +253,20 @@ public: * * @param channel_plan The current channel plan information. * - * @return LORA_MAC_STATUS_OK on success, a negative error + * @return LORAWAN_STATUS_OK on success, a negative error * code on failure. */ - virtual lora_mac_status_t get_channel_plan(lora_channelplan_t &channel_plan); + virtual lorawan_status_t get_channel_plan(lorawan_channelplan_t &channel_plan); /** Removes an active channel plan. * * You cannot remove default channels (the channels the base stations are listening to). * When a plan is abolished, only the non-default channels are removed. * - * @return LORA_MAC_STATUS_OK on success, a negative error + * @return LORAWAN_STATUS_OK on success, a negative error * code on failure. */ - virtual lora_mac_status_t remove_channel_plan(); + virtual lorawan_status_t remove_channel_plan(); /** Removes a single channel. * @@ -234,10 +274,10 @@ public: * * @param index The channel index. * - * @return LORA_MAC_STATUS_OK on success, a negative error + * @return LORAWAN_STATUS_OK on success, a negative error * code on failure. */ - virtual lora_mac_status_t remove_channel(uint8_t index); + virtual lorawan_status_t remove_channel(uint8_t index); /** Send message to gateway * @@ -270,7 +310,7 @@ public: * * * @return The number of bytes sent, or - * LORA_MAC_STATUS_WOULD_BLOCK if another TX is + * LORAWAN_STATUS_WOULD_BLOCK if another TX is * ongoing, or a negative error code on failure. */ virtual int16_t send(uint8_t port, const uint8_t* data, uint16_t length, @@ -311,7 +351,7 @@ public: * @return It could be one of these: * i) 0 if there is nothing else to read. * ii) Number of bytes written to user buffer. - * iii) LORA_MAC_STATUS_WOULD_BLOCK if there is + * iii) LORAWAN_STATUS_WOULD_BLOCK if there is * nothing available to read at the moment. * iv) A negative error code on failure. */ @@ -361,7 +401,7 @@ public: * int main() * { * lorawan.initialize(&queue); - * cbs.lorawan_events = mbed::callback(my_event_handler); + * cbs.events = mbed::callback(my_event_handler); * lorawan.add_app_callbacks(&cbs); * lorawan.connect(); * } @@ -386,7 +426,10 @@ public: * @param callbacks A pointer to the structure containing application * callbacks. */ - virtual lora_mac_status_t add_app_callbacks(lorawan_app_callbacks_t *callbacks); + virtual lorawan_status_t add_app_callbacks(lorawan_app_callbacks_t *callbacks); + +private: + bool _link_check_requested; }; #endif /* LORAWANINTERFACE_H_ */ diff --git a/features/lorawan/LoRaWANStack.cpp b/features/lorawan/LoRaWANStack.cpp index ec0ef75b1e..418322ed14 100644 --- a/features/lorawan/LoRaWANStack.cpp +++ b/features/lorawan/LoRaWANStack.cpp @@ -41,52 +41,9 @@ SPDX-License-Identifier: BSD-3-Clause #define INVALID_PORT 0xFF #define MAX_CONFIRMED_MSG_RETRIES 255 -#ifdef MBED_CONF_LORA_PHY - #if MBED_CONF_LORA_PHY == 0 - #include "lorawan/lorastack/phy/LoRaPHYEU868.h" - static SingletonPtr lora_phy; - #elif MBED_CONF_LORA_PHY == 1 - #include "lorawan/lorastack/phy/LoRaPHYAS923.h" - static SingletonPtr lora_phy; - #elif MBED_CONF_LORA_PHY == 2 - #include "lorawan/lorastack/phy/LoRaPHYAU915.h" - static SingletonPtr lora_phy; - #elif MBED_CONF_LORA_PHY == 3 - #include "lorawan/lorastack/phy/LoRaPHYCN470.h" - static SingletonPtr lora_phy; - #elif MBED_CONF_LORA_PHY == 4 - #include "lorawan/lorastack/phy/LoRaPHYCN779.h" - static SingletonPtr lora_phy; - #elif MBED_CONF_LORA_PHY == 5 - #include "lorawan/lorastack/phy/LoRaPHYEU433.h" - static SingletonPtr lora_phy; - #elif MBED_CONF_LORA_PHY == 6 - #include "lorawan/lorastack/phy/LoRaPHYIN865.h" - static SingletonPtr lora_phy; - #elif MBED_CONF_LORA_PHY == 7 - #include "lorawan/lorastack/phy/LoRaPHYKR920.h" - static SingletonPtr lora_phy; - #elif MBED_CONF_LORA_PHY == 8 - #include "lorawan/lorastack/phy/LoRaPHYUS915.h" - static SingletonPtr lora_phy; - #elif MBED_CONF_LORA_PHY == 9 - #include "lorawan/lorastack/phy/LoRaPHYUS915Hybrid.h" - static SingletonPtr lora_phy; - #endif //MBED_CONF_LORA_PHY == VALUE -#else - #error "Must set LoRa PHY layer parameters." -#endif //MBED_CONF_LORA_PHY - using namespace mbed; using namespace events; -/** - * Helper function prototypes - */ -static Mcps_t interpret_mcps_confirm_type(const lora_mac_mcps_t& local); -static Mib_t interpret_mib_req_confirm_type(const lora_mac_mib_t& mib_local); -static lora_mac_event_info_status_t interpret_event_info_type(const LoRaMacEventInfoStatus_t& remote); - #if defined(LORAWAN_COMPLIANCE_TEST) /** * @@ -114,22 +71,23 @@ bool LoRaWANStack::is_port_valid(uint8_t port) } } -lora_mac_status_t LoRaWANStack::set_application_port(uint8_t port) +lorawan_status_t LoRaWANStack::set_application_port(uint8_t port) { if (is_port_valid(port)) { _app_port = port; - return LORA_MAC_STATUS_OK; + return LORAWAN_STATUS_OK; } - return LORA_MAC_STATUS_PORT_INVALID; + return LORAWAN_STATUS_PORT_INVALID; } /***************************************************************************** * Constructor and destructor * ****************************************************************************/ LoRaWANStack::LoRaWANStack() -: _device_current_state(DEVICE_STATE_NOT_INITIALIZED), _mac_handlers(NULL), - _num_retry(1), _queue(NULL), _duty_cycle_on(LORAWAN_DUTYCYCLE_ON) +: _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) { #ifdef MBED_CONF_LORA_APP_PORT // is_port_valid() is not virtual, so we can call it in constructor @@ -149,11 +107,15 @@ LoRaWANStack::LoRaWANStack() memset(&_lw_session, 0, sizeof(_lw_session)); memset(&_tx_msg, 0, sizeof(_tx_msg)); memset(&_rx_msg, 0, sizeof(_rx_msg)); + + LoRaMacPrimitives.mcps_confirm = callback(this, &LoRaWANStack::mcps_confirm_handler); + LoRaMacPrimitives.mcps_indication = callback(this, &LoRaWANStack::mcps_indication_handler); + LoRaMacPrimitives.mlme_confirm = callback(this, &LoRaWANStack::mlme_confirm_handler); + LoRaMacPrimitives.mlme_indication = callback(this, &LoRaWANStack::mlme_indication_handler); } LoRaWANStack::~LoRaWANStack() { - } /***************************************************************************** @@ -168,50 +130,40 @@ LoRaWANStack& LoRaWANStack::get_lorawan_stack() radio_events_t *LoRaWANStack::bind_radio_driver(LoRaRadio& radio) { // Store pointer to callback routines inside MAC layer (non-IRQ safe) - _mac_handlers = GetPhyEventHandlers(); + _mac_handlers = _loramac.get_phy_event_handlers(); // passes the reference to radio driver down to PHY layer - lora_phy.get()->set_radio_instance(radio); + _lora_phy.set_radio_instance(radio); return _mac_handlers; } -lora_mac_status_t LoRaWANStack::initialize_mac_layer(EventQueue *queue) +lorawan_status_t LoRaWANStack::initialize_mac_layer(EventQueue *queue) { if (DEVICE_STATE_NOT_INITIALIZED != _device_current_state) { tr_debug("Initialized already"); - return LORA_MAC_STATUS_OK; + return LORAWAN_STATUS_OK; } - static LoRaMacPrimitives_t LoRaMacPrimitives; - static LoRaMacCallback_t LoRaMacCallbacks; - static lora_mac_mib_request_confirm_t mib_req; - -#if defined(LORAWAN_COMPLIANCE_TEST) - static uint8_t compliance_test_buffer[LORAWAN_TX_MAX_SIZE]; -#endif - tr_debug("Initializing MAC layer"); //store a pointer to Event Queue _queue = queue; #if defined(LORAWAN_COMPLIANCE_TEST) - // Allocate memory for compliance test _compliance_test.app_data_buffer = compliance_test_buffer; #endif - TimerTimeCounterInit(queue); - LoRaMacPrimitives.MacMcpsConfirm = callback(this, &LoRaWANStack::mcps_confirm); - LoRaMacPrimitives.MacMcpsIndication = callback(this, &LoRaWANStack::mcps_indication); - LoRaMacPrimitives.MacMlmeConfirm = callback(this, &LoRaWANStack::mlme_confirm); - LoRaMacInitialization(&LoRaMacPrimitives, &LoRaMacCallbacks, lora_phy.get(), queue); + _lora_time.activate_timer_subsystem(queue); + _loramac.initialize(&LoRaMacPrimitives, &_lora_phy, queue); - mib_req.type = LORA_MIB_ADR; - mib_req.param.adr_enable = LORAWAN_ADR_ON; + loramac_mib_req_confirm_t mib_req; + + mib_req.type = MIB_ADR; + mib_req.param.is_adr_enable = MBED_CONF_LORA_ADR_ON; mib_set_request(&mib_req); - mib_req.type = LORA_MIB_PUBLIC_NETWORK; - mib_req.param.enable_public_network = LORAWAN_PUBLIC_NETWORK; + mib_req.type = MIB_PUBLIC_NETWORK; + mib_req.param.enable_public_nwk = MBED_CONF_LORA_PUBLIC_NETWORK; mib_set_request(&mib_req); // Reset counters to zero. Will change in future with 1.1 support. @@ -250,7 +202,7 @@ void LoRaWANStack::prepare_special_tx_frame(uint8_t port) _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, LORAWAN_TX_MAX_SIZE); ++i) { + 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; @@ -268,23 +220,23 @@ void LoRaWANStack::prepare_special_tx_frame(uint8_t port) * * \return returns the state of the LoRa MAC */ -lora_mac_status_t LoRaWANStack::send_compliance_test_frame_to_mac() +lorawan_status_t LoRaWANStack::send_compliance_test_frame_to_mac() { - lora_mac_mcps_req_t mcps_req; + loramac_mcps_req_t mcps_req; - GetPhyParams_t phy_params; - PhyParam_t default_datarate; - phy_params.Attribute = PHY_DEF_TX_DR; - default_datarate = lora_phy.get_phy_params(&phy_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); prepare_special_tx_frame(_compliance_test.app_port); if (!_compliance_test.is_tx_confirmed) { - mcps_req.type = LORA_MCPS_UNCONFIRMED; - mcps_req.req.unconfirmed.f_port = _compliance_test.app_port; + 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.datarate = default_datarate.Value; + mcps_req.req.unconfirmed.data_rate = default_datarate.value; tr_info("Transmit unconfirmed compliance test frame %d bytes.", mcps_req.f_buffer_size); @@ -292,12 +244,12 @@ lora_mac_status_t LoRaWANStack::send_compliance_test_frame_to_mac() 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 = LORA_MCPS_CONFIRMED; - mcps_req.req.confirmed.f_port = _compliance_test.app_port; + 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.datarate = default_datarate.Value; + mcps_req.req.confirmed.data_rate = default_datarate.value; tr_info("Transmit confirmed compliance test frame %d bytes.", mcps_req.f_buffer_size); @@ -305,7 +257,7 @@ lora_mac_status_t LoRaWANStack::send_compliance_test_frame_to_mac() tr_info("Byte %d, data is 0x%x", i+1, ((uint8_t*)mcps_req.f_buffer)[i]); } } else { - return LORA_MAC_STATUS_SERVICE_UNKNOWN; + return LORAWAN_STATUS_SERVICE_UNKNOWN; } return mcps_request_handler(&mcps_req); @@ -314,60 +266,75 @@ lora_mac_status_t LoRaWANStack::send_compliance_test_frame_to_mac() uint16_t LoRaWANStack::check_possible_tx_size(uint16_t size) { - LoRaMacTxInfo_t txInfo; - if (LoRaMacQueryTxPossible(size, &txInfo) == LORAMAC_STATUS_LENGTH_ERROR) { + loramac_tx_info_t tx_info; + if (_loramac.query_tx_possible(size, &tx_info) == LORAWAN_STATUS_LENGTH_ERROR) { // Cannot transmit this much. Return how much data can be sent // at the moment - return txInfo.MaxPossiblePayload; + return tx_info.max_possible_payload_size; } - return txInfo.CurrentPayloadSize; + return tx_info.current_payload_size; } /** Hands over the frame to MAC layer * * \return returns the state of the LoRa MAC */ -lora_mac_status_t LoRaWANStack::send_frame_to_mac() +lorawan_status_t LoRaWANStack::send_frame_to_mac() { - lora_mac_mcps_req_t mcps_req; - lora_mac_status_t status; - lora_mac_mib_request_confirm_t mib_get_params; + loramac_mcps_req_t mcps_req; + lorawan_status_t status; + loramac_mib_req_confirm_t mib_get_params; - GetPhyParams_t phy_params; - PhyParam_t default_datarate; - phy_params.Attribute = PHY_DEF_TX_DR; - default_datarate = lora_phy.get()->get_phy_params(&phy_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 (LORA_MCPS_UNCONFIRMED == mcps_req.type) { - mcps_req.req.unconfirmed.f_port = _tx_msg.message_u.unconfirmed.f_port; + 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.req.unconfirmed.datarate = default_datarate.Value; - } else if (LORA_MCPS_CONFIRMED == mcps_req.type) { - mcps_req.req.confirmed.f_port = _tx_msg.message_u.confirmed.f_port; + 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; + } else { + mcps_req.req.unconfirmed.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; - mib_get_params.type = LORA_MIB_CHANNELS_DATARATE; - if(mib_get_request(&mib_get_params) != LORA_MAC_STATUS_OK) { - tr_debug("Couldn't get MIB parameters: Using default data rate"); - mcps_req.req.confirmed.datarate = default_datarate.Value; - } else { - mcps_req.req.confirmed.datarate = mib_get_params.param.channels_datarate; - } + 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; + } else { + mcps_req.req.confirmed.data_rate = mib_get_params.param.channel_data_rate; + } - } else if (LORA_MCPS_PROPRIETARY == mcps_req.type) { + } 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.req.proprietary.datarate = default_datarate.Value; + + 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; + } else { + mcps_req.req.proprietary.data_rate = mib_get_params.param.channel_data_rate; + } + } else { - return LORA_MAC_STATUS_SERVICE_UNKNOWN; + return LORAWAN_STATUS_SERVICE_UNKNOWN; } status = mcps_request_handler(&mcps_req); @@ -375,15 +342,15 @@ lora_mac_status_t LoRaWANStack::send_frame_to_mac() return status; } -lora_mac_status_t LoRaWANStack::set_confirmed_msg_retry(uint8_t count) +lorawan_status_t LoRaWANStack::set_confirmed_msg_retry(uint8_t count) { if (count >= MAX_CONFIRMED_MSG_RETRIES) { - return LORA_MAC_STATUS_PARAMETER_INVALID; + return LORAWAN_STATUS_PARAMETER_INVALID; } _num_retry = count; - return LORA_MAC_STATUS_OK; + return LORAWAN_STATUS_OK; } void LoRaWANStack::set_device_state(device_states_t new_state) @@ -391,79 +358,29 @@ void LoRaWANStack::set_device_state(device_states_t new_state) _device_current_state = new_state; } -/** Wrapper function to MCPS-Confirm event function +/*! + * \brief MLME-Indication event function * - * \param mcps_confirm Pointer to the confirm structure, - * containing confirm attributes. + * \param [IN] mlmeIndication - Pointer to the indication structure. */ -void LoRaWANStack::mcps_confirm(McpsConfirm_t *mcps_confirm) +void LoRaWANStack::mlme_indication_handler(loramac_mlme_indication_t *mlmeIndication) { - lora_mac_mcps_confirm_t lora_mcps_confirm; - lora_mcps_confirm.ack_received = mcps_confirm->AckReceived; - lora_mcps_confirm.nb_retries = mcps_confirm->NbRetries; - lora_mcps_confirm.datarate = mcps_confirm->Datarate; - lora_mcps_confirm.tx_power = mcps_confirm->TxPower; - lora_mcps_confirm.uplink_counter = mcps_confirm->UpLinkCounter; - lora_mcps_confirm.uplink_frequency = mcps_confirm->UpLinkFrequency; - - // Interprets from Mcps_t to lora_mac_mcps_t - mcps_confirm->McpsRequest = interpret_mcps_confirm_type(lora_mcps_confirm.mcps_request); - // Interprets from LoRaMacEventInfoStatus_t to lora_mac_event_info_status_t - lora_mcps_confirm.status = interpret_event_info_type(mcps_confirm->Status); - lora_mcps_confirm.tx_time_on_air = mcps_confirm->TxTimeOnAir; - - mcps_confirm_handler(&lora_mcps_confirm); -} - -/** Wrapper function to MCPS-Indication event function - * - * \param mcps_indication Pointer to the indication structure, - * containing indication attributes. - */ -void LoRaWANStack::mcps_indication(McpsIndication_t *mcps_indication) -{ - lora_mac_mcps_indication_t lora_mcps_indication; - - lora_mcps_indication.ack_received = mcps_indication->AckReceived; - memcpy(lora_mcps_indication.buffer, mcps_indication->Buffer, mcps_indication->BufferSize); - lora_mcps_indication.buffer_size = mcps_indication->BufferSize; - lora_mcps_indication.downlink_counter = mcps_indication->DownLinkCounter; - lora_mcps_indication.frame_pending = mcps_indication->FramePending; - lora_mcps_indication.mcps_indication = (lora_mac_mcps_t)mcps_indication->McpsIndication; - lora_mcps_indication.multicast = mcps_indication->Multicast; - lora_mcps_indication.port = mcps_indication->Port; - lora_mcps_indication.rssi = mcps_indication->Rssi; - lora_mcps_indication.rx_data = mcps_indication->RxData; - lora_mcps_indication.rx_datarate = mcps_indication->RxDatarate; - lora_mcps_indication.rx_slot = mcps_indication->RxSlot; - lora_mcps_indication.snr = mcps_indication->Snr; - lora_mcps_indication.status = (lora_mac_event_info_status_t)mcps_indication->Status; - - mcps_indication_handler(&lora_mcps_indication); -} - -/** Wrapper function to MLME-Confirm event function - * - * \param mlme_confirm Pointer to the confirm structure, - * containing confirm attributes. - */ -void LoRaWANStack::mlme_confirm(MlmeConfirm_t *mlme_confirm) -{ - lora_mac_mlme_confirm_t lora_mlme_confirm; - - lora_mlme_confirm.demod_margin = mlme_confirm->DemodMargin; - lora_mlme_confirm.mlme_request = (lora_mac_mlme_t)mlme_confirm->MlmeRequest; - lora_mlme_confirm.nb_gateways = mlme_confirm->NbGateways; - lora_mlme_confirm.nb_retries = mlme_confirm->NbRetries; - lora_mlme_confirm.status = (lora_mac_event_info_status_t)mlme_confirm->Status; - lora_mlme_confirm.tx_time_on_air = mlme_confirm->TxTimeOnAir; - - mlme_confirm_handler(&lora_mlme_confirm); + switch( mlmeIndication->indication_type ) + { + case MLME_SCHEDULE_UPLINK: + {// The MAC signals that we shall provide an uplink as soon as possible + // TODO: Sending implementation missing and will be implemented using + // another task. + //OnTxNextPacketTimerEvent( ); + break; + } + default: + break; + } } void LoRaWANStack::set_lora_callbacks(lorawan_app_callbacks_t *cbs) { - if (cbs) { if (cbs->events) { _callbacks.events = cbs->events; @@ -472,239 +389,96 @@ void LoRaWANStack::set_lora_callbacks(lorawan_app_callbacks_t *cbs) if (cbs->link_check_resp) { _callbacks.link_check_resp = cbs->link_check_resp; } + + if (cbs->battery_level) { + _callbacks.battery_level = cbs->battery_level; + } } } -lora_mac_status_t LoRaWANStack::add_channels(const lora_channelplan_t &channel_plan) +lorawan_status_t LoRaWANStack::add_channels(const lorawan_channelplan_t &channel_plan) { // If device is not initialized, stop right away if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) { tr_error("Stack not initialized!"); - return LORA_MAC_STATUS_NOT_INITIALIZED; + return LORAWAN_STATUS_NOT_INITIALIZED; } - ChannelParams_t mac_layer_ch_params; - LoRaMacStatus_t status; - - GetPhyParams_t get_phy; - PhyParam_t phy_param; - uint8_t max_num_channels; - - // Check first how many channels the selected PHY layer supports - get_phy.Attribute = PHY_MAX_NB_CHANNELS; - phy_param = lora_phy.get()->get_phy_params(&get_phy); - max_num_channels = (uint8_t) phy_param.Value; - - // check if user is setting more channels than supported - if (channel_plan.nb_channels > max_num_channels) { - return LORA_MAC_STATUS_PARAMETER_INVALID; - } - - for (uint8_t i = 0; i < channel_plan.nb_channels; i++) { - mac_layer_ch_params.Band = channel_plan.channels[i].ch_param.band; - mac_layer_ch_params.DrRange.Fields.Max = channel_plan.channels[i].ch_param.dr_range.lora_mac_fields_s.max; - mac_layer_ch_params.DrRange.Fields.Min = channel_plan.channels[i].ch_param.dr_range.lora_mac_fields_s.min; - mac_layer_ch_params.DrRange.Value = channel_plan.channels[i].ch_param.dr_range.value; - mac_layer_ch_params.Frequency = channel_plan.channels[i].ch_param.frequency; - mac_layer_ch_params.Rx1Frequency =channel_plan.channels[i].ch_param.rx1_frequency; - - status = LoRaMacChannelAdd(channel_plan.channels[i].id, mac_layer_ch_params); - - if (status != LORAMAC_STATUS_OK) { - return error_type_converter(status); - } - } - - return LORA_MAC_STATUS_OK; + return _loramac.add_channel_plan(channel_plan); } -lora_mac_status_t LoRaWANStack::drop_channel_list() +lorawan_status_t LoRaWANStack::drop_channel_list() +{ + if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED) { + tr_error("Stack not initialized!"); + return LORAWAN_STATUS_NOT_INITIALIZED; + } + + return _loramac.remove_channel_plan(); +} + +lorawan_status_t LoRaWANStack::remove_a_channel(uint8_t channel_id) { if (_device_current_state == DEVICE_STATE_NOT_INITIALIZED ) { tr_error("Stack not initialized!"); - return LORA_MAC_STATUS_NOT_INITIALIZED; + return LORAWAN_STATUS_NOT_INITIALIZED; } - lora_mac_status_t status = LORA_MAC_STATUS_OK; - - GetPhyParams_t get_phy; - PhyParam_t phy_param; - uint8_t max_num_channels; - uint16_t *channel_masks; - uint16_t *default_channel_masks; - - // Check first how many channels the selected PHY layer supports - get_phy.Attribute = PHY_MAX_NB_CHANNELS; - phy_param = lora_phy.get()->get_phy_params(&get_phy); - max_num_channels = (uint8_t) phy_param.Value; - - // Now check the channel mask for enabled channels - get_phy.Attribute = PHY_CHANNELS_MASK; - phy_param = lora_phy.get()->get_phy_params(&get_phy); - channel_masks = phy_param.ChannelsMask; - - // Now check the channel mask for default channels - get_phy.Attribute = PHY_CHANNELS_DEFAULT_MASK; - phy_param = lora_phy.get()->get_phy_params(&get_phy); - default_channel_masks = phy_param.ChannelsMask; - - for (uint8_t i = 0; i < max_num_channels; i++) { - // skip any default channels - if ((default_channel_masks[0] & (1U<get_phy_params(&get_phy); - max_num_channels = (uint8_t) phy_param.Value; - - // According to specification channel IDs start from 0 and last valid - // channel ID is N-1 where N=MAX_NUM_CHANNELS. - // So any ID which is larger or equal to the Max number of channels is invalid - if (channel_id >= max_num_channels) { - return LORA_MAC_STATUS_PARAMETER_INVALID; - } - - // Now check the Default channel mask - get_phy.Attribute = PHY_CHANNELS_DEFAULT_MASK; - phy_param = lora_phy.get()->get_phy_params(&get_phy); - channel_masks = phy_param.ChannelsMask; - - // check if the channel ID give belongs to a default channel - // Mostly the default channels are in the first mask if the region - // have multiple channel masks for various sub-bands. So we check the first - // mask only and return an error code if user sent a default channel id - if ((channel_masks[0] & (1U << channel_id)) != 0) { - tr_error("Not allowed to remove a Default Channel."); - return LORA_MAC_STATUS_PARAMETER_INVALID; - } - - return error_type_converter(LoRaMacChannelRemove(channel_id)); -} - -lora_mac_status_t LoRaWANStack::get_enabled_channels(lora_channelplan_t& channel_plan) +lorawan_status_t LoRaWANStack::get_enabled_channels(lorawan_channelplan_t& channel_plan) { if (_device_current_state == DEVICE_STATE_JOINING || _device_current_state == DEVICE_STATE_NOT_INITIALIZED || _device_current_state == DEVICE_STATE_INIT) { tr_error("Cannot get channel plan until Joined!"); - return LORA_MAC_STATUS_BUSY; + return LORAWAN_STATUS_BUSY; } - lora_mac_mib_request_confirm_t mib_params; - - GetPhyParams_t get_phy; - PhyParam_t phy_param; - uint8_t max_num_channels; - uint16_t *channel_masks; - uint8_t count = 0; - - // Check first how many channels the selected PHY layer supports - get_phy.Attribute = PHY_MAX_NB_CHANNELS; - phy_param = lora_phy.get()->get_phy_params(&get_phy); - max_num_channels = (uint8_t) phy_param.Value; - - // Now check the Default channel mask - get_phy.Attribute = PHY_CHANNELS_MASK; - phy_param = lora_phy.get()->get_phy_params(&get_phy); - channel_masks = phy_param.ChannelsMask; - - // Request Mib to get channels - memset(&mib_params, 0, sizeof(mib_params)); - mib_params.type = LORA_MIB_CHANNELS; - mib_get_request(&mib_params); - - for (uint8_t i = 0; i < max_num_channels; i++) { - // skip the channels which are not enabled - if ((channel_masks[0] & (1U << i)) == 0) { - continue; - } - - // otherwise add them to the channel_plan struct - channel_plan.channels[count].id = i; - channel_plan.channels[count].ch_param.frequency = mib_params.param.channel_list[i].frequency; - channel_plan.channels[count].ch_param.dr_range.value = mib_params.param.channel_list[i].dr_range.value; - channel_plan.channels[count].ch_param.dr_range.lora_mac_fields_s.min = mib_params.param.channel_list[i].dr_range.lora_mac_fields_s.min; - channel_plan.channels[count].ch_param.dr_range.lora_mac_fields_s.max = mib_params.param.channel_list[i].dr_range.lora_mac_fields_s.max; - channel_plan.channels[count].ch_param.band = mib_params.param.channel_list[i].band; - channel_plan.channels[count].ch_param.rx1_frequency = mib_params.param.channel_list[i].rx1_frequency; - count++; - } - - channel_plan.nb_channels = count; - - return LORA_MAC_STATUS_OK; + return _loramac.get_channel_plan(channel_plan); } -lora_mac_status_t LoRaWANStack::enable_adaptive_datarate(bool adr_enabled) +lorawan_status_t LoRaWANStack::enable_adaptive_datarate(bool adr_enabled) { if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) { tr_error("Stack not initialized!"); - return LORA_MAC_STATUS_NOT_INITIALIZED; + return LORAWAN_STATUS_NOT_INITIALIZED; } - lora_mac_mib_request_confirm_t adr_mib_params; + loramac_mib_req_confirm_t adr_mib_params; - adr_mib_params.type = LORA_MIB_ADR; - adr_mib_params.param.adr_enable = adr_enabled; + adr_mib_params.type = MIB_ADR; + adr_mib_params.param.is_adr_enable = adr_enabled; return mib_set_request(&adr_mib_params); } -lora_mac_status_t LoRaWANStack::set_channel_data_rate(uint8_t data_rate) +lorawan_status_t LoRaWANStack::set_channel_data_rate(uint8_t data_rate) { if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) { tr_error("Stack not initialized!"); - return LORA_MAC_STATUS_NOT_INITIALIZED; + return LORAWAN_STATUS_NOT_INITIALIZED; } - lora_mac_mib_request_confirm_t mib_params; - mib_params.type = LORA_MIB_ADR; - if (mib_get_request(&mib_params) == true) { + loramac_mib_req_confirm_t mib_params; + mib_params.type = MIB_ADR; + if (mib_get_request(&mib_params) != LORAWAN_STATUS_OK) { tr_error("Cannot set data rate. Please turn off ADR first."); - return LORA_MAC_STATUS_PARAMETER_INVALID; + return LORAWAN_STATUS_PARAMETER_INVALID; } - mib_params.type = LORA_MIB_CHANNELS_DATARATE; - mib_params.param.channels_datarate = data_rate; + mib_params.type = MIB_CHANNELS_DATARATE; + mib_params.param.channel_data_rate = data_rate; return mib_set_request(&mib_params); } -void LoRaWANStack::commission_device(const lora_dev_commission_t &commission_data) +void LoRaWANStack::commission_device(const lorawan_dev_commission_t &commission_data) { _lw_session.connection.connect_type = commission_data.connection.connect_type; _lw_session.downlink_counter = commission_data.downlink_counter; @@ -733,14 +507,14 @@ void LoRaWANStack::commission_device(const lora_dev_commission_t &commission_dat * * Join OTAA */ -lora_mac_status_t LoRaWANStack::join_request_by_otaa(const lorawan_connect_t ¶ms) +lorawan_status_t LoRaWANStack::join_request_by_otaa(const lorawan_connect_t ¶ms) { - lora_dev_commission_t commission; + lorawan_dev_commission_t commission; if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) { tr_error("Stack not initialized!"); - return LORA_MAC_STATUS_NOT_INITIALIZED; + return LORAWAN_STATUS_NOT_INITIALIZED; } tr_debug("Initiating OTAA"); @@ -766,14 +540,14 @@ lora_mac_status_t LoRaWANStack::join_request_by_otaa(const lorawan_connect_t &pa * * Connect ABP */ -lora_mac_status_t LoRaWANStack::activation_by_personalization(const lorawan_connect_t ¶ms) +lorawan_status_t LoRaWANStack::activation_by_personalization(const lorawan_connect_t ¶ms) { - lora_mac_status_t status; - lora_dev_commission_t commission; + lorawan_status_t status; + lorawan_dev_commission_t commission; if (DEVICE_STATE_NOT_INITIALIZED == _device_current_state) { tr_error("Stack not initialized!"); - return LORA_MAC_STATUS_NOT_INITIALIZED; + return LORAWAN_STATUS_NOT_INITIALIZED; } tr_debug("Initiating ABP"); @@ -809,33 +583,37 @@ int16_t LoRaWANStack::handle_tx(uint8_t port, const uint8_t* data, uint16_t length, uint8_t flags) { if (!_lw_session.active) { - return LORA_MAC_STATUS_NO_ACTIVE_SESSIONS; + return LORAWAN_STATUS_NO_ACTIVE_SESSIONS; } if (_tx_msg.tx_ongoing) { - return LORA_MAC_STATUS_WOULD_BLOCK; + return LORAWAN_STATUS_WOULD_BLOCK; + } + + if (!data && length > 0) { + return LORAWAN_STATUS_PARAMETER_INVALID; } #if defined(LORAWAN_COMPLIANCE_TEST) if (_compliance_test.running) { - return LORA_MAC_STATUS_COMPLIANCE_TEST_ON; + return LORAWAN_STATUS_COMPLIANCE_TEST_ON; } #endif - lora_mac_mib_request_confirm_t mib_req; - lora_mac_status_t status; - mib_req.type = LORA_MIB_NETWORK_JOINED; + loramac_mib_req_confirm_t mib_req; + lorawan_status_t status; + mib_req.type = MIB_NETWORK_JOINED; status = mib_get_request(&mib_req); - if (status == LORA_MAC_STATUS_OK) { - if (mib_req.param.is_network_joined == false) { - return LORA_MAC_STATUS_NO_NETWORK_JOINED; + if (status == LORAWAN_STATUS_OK) { + if (mib_req.param.is_nwk_joined == false) { + return LORAWAN_STATUS_NO_NETWORK_JOINED; } } status = set_application_port(port); - if (status != LORA_MAC_STATUS_OK) { + if (status != LORAWAN_STATUS_OK) { tr_error("Illegal application port definition."); return status; } @@ -843,20 +621,20 @@ int16_t LoRaWANStack::handle_tx(uint8_t port, const uint8_t* data, if (flags == 0 || (flags & MSG_FLAG_MASK) == (MSG_CONFIRMED_FLAG|MSG_UNCONFIRMED_FLAG)) { tr_error("CONFIRMED and UNCONFIRMED are mutually exclusive for send()"); - return LORA_MAC_STATUS_PARAMETER_INVALID; + return LORAWAN_STATUS_PARAMETER_INVALID; } _tx_msg.port = port; uint16_t max_possible_size = check_possible_tx_size(length); - if (max_possible_size > LORAWAN_TX_MAX_SIZE) { + if (max_possible_size > MBED_CONF_LORA_TX_MAX_SIZE) { // LORAWAN_APP_DATA_MAX_SIZE should at least be // either equal to or bigger than maximum possible // tx size because our tx message buffer takes its // length from that macro. Force maximum possible tx unit // to be equal to the buffer size user chose. - max_possible_size = LORAWAN_TX_MAX_SIZE; + max_possible_size = MBED_CONF_LORA_TX_MAX_SIZE; } if (max_possible_size < length) { @@ -872,7 +650,9 @@ int16_t LoRaWANStack::handle_tx(uint8_t port, const uint8_t* data, _tx_msg.f_buffer_size = length; _tx_msg.pending_size = 0; // copy user buffer upto the max_possible_size - memcpy(_tx_msg.f_buffer, data, length); + if (length > 0) { + memcpy(_tx_msg.f_buffer, data, length); + } } // Handles all unconfirmed messages, including proprietary and multicast @@ -880,8 +660,8 @@ int16_t LoRaWANStack::handle_tx(uint8_t port, const uint8_t* data, || (flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_MULTICAST || (flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_PROPRIETARY) { - _tx_msg.type = LORA_MCPS_UNCONFIRMED; - _tx_msg.message_u.unconfirmed.f_port = _app_port; + _tx_msg.type = MCPS_UNCONFIRMED; + _tx_msg.message_u.unconfirmed.fport = _app_port; } // Handles all confirmed messages, including proprietary and multicast @@ -889,53 +669,53 @@ int16_t LoRaWANStack::handle_tx(uint8_t port, const uint8_t* data, || (flags & MSG_FLAG_MASK) == MSG_CONFIRMED_MULTICAST || (flags & MSG_FLAG_MASK) == MSG_CONFIRMED_PROPRIETARY) { - _tx_msg.type = LORA_MCPS_CONFIRMED; - _tx_msg.message_u.confirmed.f_port = _app_port; + _tx_msg.type = MCPS_CONFIRMED; + _tx_msg.message_u.confirmed.fport = _app_port; _tx_msg.message_u.confirmed.nb_trials = _num_retry; } tr_info("RTS = %u bytes, PEND = %u", _tx_msg.f_buffer_size, _tx_msg.pending_size); set_device_state(DEVICE_STATE_SEND); - lora_state_machine(); + status = lora_state_machine(); // send user the length of data which is scheduled now. // user should take care of the pending data. - return _tx_msg.f_buffer_size; + return (status == LORAWAN_STATUS_OK) ? _tx_msg.f_buffer_size : (int16_t) status; } int16_t LoRaWANStack::handle_rx(const uint8_t port, uint8_t* data, uint16_t length, uint8_t flags) { if (!_lw_session.active) { - return LORA_MAC_STATUS_NO_ACTIVE_SESSIONS; + return LORAWAN_STATUS_NO_ACTIVE_SESSIONS; } // No messages to read. if (!_rx_msg.receive_ready) { - return LORA_MAC_STATUS_WOULD_BLOCK; + return LORAWAN_STATUS_WOULD_BLOCK; } #if defined(LORAWAN_COMPLIANCE_TEST) if (_compliance_test.running) { - return LORA_MAC_STATUS_COMPLIANCE_TEST_ON; + return LORAWAN_STATUS_COMPLIANCE_TEST_ON; } #endif if (data == NULL) { - return LORA_MAC_STATUS_PARAMETER_INVALID; + return LORAWAN_STATUS_PARAMETER_INVALID; } - uint8_t *base_ptr = _rx_msg.rx_message.mcps_indication.buffer; - uint16_t base_size = _rx_msg.rx_message.mcps_indication.buffer_size; + uint8_t *base_ptr = _rx_msg.msg.mcps_indication.buffer; + uint16_t base_size = _rx_msg.msg.mcps_indication.buffer_size; bool read_complete = false; - if (_rx_msg.rx_message.mcps_indication.port != port) { + if (_rx_msg.msg.mcps_indication.port != port) { // Nothing yet received for this particular port - return LORA_MAC_STATUS_WOULD_BLOCK; + return LORAWAN_STATUS_WOULD_BLOCK; } // check if message received is a Confirmed message and user subscribed to it or not - if (_rx_msg.rx_message.mcps_indication.mcps_indication == LORA_MCPS_CONFIRMED + if (_rx_msg.msg.mcps_indication.type == MCPS_CONFIRMED && ((flags & MSG_FLAG_MASK) == MSG_CONFIRMED_FLAG || (flags & MSG_FLAG_MASK) == MSG_CONFIRMED_MULTICAST || (flags & MSG_FLAG_MASK) == MSG_CONFIRMED_PROPRIETARY)) { @@ -944,7 +724,7 @@ int16_t LoRaWANStack::handle_rx(const uint8_t port, uint8_t* data, } // check if message received is a Unconfirmed message and user subscribed to it or not - if (_rx_msg.rx_message.mcps_indication.mcps_indication == LORA_MCPS_UNCONFIRMED + if (_rx_msg.msg.mcps_indication.type == MCPS_UNCONFIRMED && ((flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_FLAG || (flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_MULTICAST || (flags & MSG_FLAG_MASK) == MSG_UNCONFIRMED_PROPRIETARY)) { @@ -953,10 +733,10 @@ int16_t LoRaWANStack::handle_rx(const uint8_t port, uint8_t* data, // check the length of received message whether we can fit into user // buffer completely or not - if (_rx_msg.rx_message.mcps_indication.buffer_size > length && + if (_rx_msg.msg.mcps_indication.buffer_size > length && _rx_msg.prev_read_size == 0) { // we can't fit into user buffer. Invoke counter measures - _rx_msg.pending_size = _rx_msg.rx_message.mcps_indication.buffer_size - length; + _rx_msg.pending_size = _rx_msg.msg.mcps_indication.buffer_size - length; base_size = length; _rx_msg.prev_read_size = base_size; memcpy(data, base_ptr, base_size); @@ -978,47 +758,20 @@ int16_t LoRaWANStack::handle_rx(const uint8_t port, uint8_t* data, // anything pending. If not, memset the buffer to zero and indicate // that no read is in progress if (read_complete) { - memset(_rx_msg.rx_message.mcps_indication.buffer, 0, LORAMAC_PHY_MAXPAYLOAD); + memset(_rx_msg.msg.mcps_indication.buffer, 0, LORAMAC_PHY_MAXPAYLOAD); _rx_msg.receive_ready = false; } return base_size; } -lora_mac_status_t LoRaWANStack::mlme_request_handler(lora_mac_mlme_req_t *mlme_request) +lorawan_status_t LoRaWANStack::mlme_request_handler(loramac_mlme_req_t *mlme_request) { - MlmeReq_t request; - - if (NULL == mlme_request) { - return LORA_MAC_STATUS_PARAMETER_INVALID; + if (mlme_request == NULL) { + return LORAWAN_STATUS_PARAMETER_INVALID; } - request.Type = (Mlme_t)mlme_request->type; - - switch (mlme_request->type) { - case LORA_MLME_JOIN: - request.Req.Join.AppEui = mlme_request->req.join.app_eui; - request.Req.Join.AppKey = mlme_request->req.join.app_key; - request.Req.Join.DevEui = mlme_request->req.join.dev_eui; - request.Req.Join.NbTrials = mlme_request->req.join.nb_trials; - break; - // This is handled in semtech stack. Only type value is needed. - case LORA_MLME_LINK_CHECK: - break; - case LORA_MLME_TXCW: - /* no break */ - /* Fall through */ - case LORA_MLME_TXCW_1: - request.Req.TxCw.Frequency = mlme_request->req.tx_cw.frequency; - request.Req.TxCw.Power = mlme_request->req.tx_cw.power; - request.Req.TxCw.Timeout = mlme_request->req.tx_cw.timeout; - break; - default: - return LORA_MAC_STATUS_SERVICE_UNKNOWN; - break; - } - - return error_type_converter(LoRaMacMlmeRequest(&request)); + return _loramac.mlme_request(mlme_request); } /** MLME-Confirm event function @@ -1026,30 +779,38 @@ lora_mac_status_t LoRaWANStack::mlme_request_handler(lora_mac_mlme_req_t *mlme_r * \param mlme_confirm Pointer to the confirm structure, * containing confirm attributes. */ -void LoRaWANStack::mlme_confirm_handler(lora_mac_mlme_confirm_t *mlme_confirm) +void LoRaWANStack::mlme_confirm_handler(loramac_mlme_confirm_t *mlme_confirm) { if (NULL == mlme_confirm) { + tr_error("mlme_confirm: struct [in] is null!"); + MBED_ASSERT(0); return; } - switch (mlme_confirm->mlme_request) { - case LORA_MLME_JOIN: - if (mlme_confirm->status == LORA_EVENT_INFO_STATUS_OK) { + switch (mlme_confirm->req_type) { + case MLME_JOIN: + if (mlme_confirm->status == LORAMAC_EVENT_INFO_STATUS_OK) { // Status is OK, node has joined the network set_device_state(DEVICE_STATE_JOINED); - lora_state_machine(); + if (lora_state_machine() != LORAWAN_STATUS_OK) { + tr_error("Lora state machine did not return LORAWAN_STATUS_OK"); + } } else { // Join attempt failed. set_device_state(DEVICE_STATE_IDLE); - lora_state_machine(); + if (lora_state_machine() != LORAWAN_STATUS_IDLE) { + tr_error("Lora state machine did not return DEVICE_STATE_IDLE !"); + } if (_callbacks.events) { - _queue->call(_callbacks.events, JOIN_FAILURE); + const int ret = _queue->call(_callbacks.events, JOIN_FAILURE); + MBED_ASSERT(ret != 0); + (void)ret; } } break; - case LORA_MLME_LINK_CHECK: - if (mlme_confirm->status == LORA_EVENT_INFO_STATUS_OK) { + case MLME_LINK_CHECK: + if (mlme_confirm->status == LORAMAC_EVENT_INFO_STATUS_OK) { // Check DemodMargin // Check NbGateways #if defined(LORAWAN_COMPLIANCE_TEST) @@ -1057,51 +818,32 @@ void LoRaWANStack::mlme_confirm_handler(lora_mac_mlme_confirm_t *mlme_confirm) _compliance_test.link_check = true; _compliance_test.demod_margin = mlme_confirm->demod_margin; _compliance_test.nb_gateways = mlme_confirm->nb_gateways; - } + } else #endif + { + // normal operation as oppose to compliance testing + if (_callbacks.link_check_resp) { + const int ret = _queue->call(_callbacks.link_check_resp, + mlme_confirm->demod_margin, + mlme_confirm->nb_gateways); + MBED_ASSERT(ret != 0); + (void)ret; + } + } } break; default: - return; break; } } -lora_mac_status_t LoRaWANStack::mcps_request_handler(lora_mac_mcps_req_t *mcps_request) +lorawan_status_t LoRaWANStack::mcps_request_handler(loramac_mcps_req_t *mcps_request) { - McpsReq_t request; - - if (NULL == mcps_request) { - return LORA_MAC_STATUS_PARAMETER_INVALID; + if (mcps_request == NULL) { + return LORAWAN_STATUS_PARAMETER_INVALID; } - request.Type = (Mcps_t)mcps_request->type; - - switch (mcps_request->type) { - case LORA_MCPS_UNCONFIRMED: - request.Req.Unconfirmed.Datarate = mcps_request->req.unconfirmed.datarate; - request.Req.Unconfirmed.fBuffer = mcps_request->f_buffer; - request.Req.Unconfirmed.fBufferSize = mcps_request->f_buffer_size; - request.Req.Unconfirmed.fPort = mcps_request->req.unconfirmed.f_port; - break; - case LORA_MCPS_CONFIRMED: - request.Req.Confirmed.Datarate = mcps_request->req.confirmed.datarate; - request.Req.Confirmed.fBuffer = mcps_request->f_buffer; - request.Req.Confirmed.fBufferSize = mcps_request->f_buffer_size; - request.Req.Confirmed.fPort = mcps_request->req.confirmed.f_port; - request.Req.Confirmed.NbTrials = mcps_request->req.confirmed.nb_trials; - break; - case LORA_MCPS_PROPRIETARY: - request.Req.Proprietary.Datarate = mcps_request->req.proprietary.datarate; - request.Req.Proprietary.fBuffer = mcps_request->f_buffer; - request.Req.Proprietary.fBufferSize = mcps_request->f_buffer_size; - break; - default: - return LORA_MAC_STATUS_SERVICE_UNKNOWN; - break; - } - - return error_type_converter(LoRaMacMcpsRequest(&request)); + return _loramac.mcps_request(mcps_request); } /** MCPS-Confirm event function @@ -1109,43 +851,48 @@ lora_mac_status_t LoRaWANStack::mcps_request_handler(lora_mac_mcps_req_t *mcps_r * \param mcps_confirm Pointer to the confirm structure, * containing confirm attributes. */ -void LoRaWANStack::mcps_confirm_handler(lora_mac_mcps_confirm_t *mcps_confirm) +void LoRaWANStack::mcps_confirm_handler(loramac_mcps_confirm_t *mcps_confirm) { if (mcps_confirm == NULL) { - tr_error("mcps_confirm: struct [in] is null."); + tr_error("mcps_confirm: struct [in] is null!"); + MBED_ASSERT(0); return; } - if (mcps_confirm->status != LORA_EVENT_INFO_STATUS_OK) { + if (mcps_confirm->status != LORAMAC_EVENT_INFO_STATUS_OK) { // Couldn't schedule packet, ack not recieved in CONFIRMED case // or some other error happened. Discard buffer, unset the tx-ongoing // flag and let the application know _tx_msg.tx_ongoing = false; - memset(_tx_msg.f_buffer, 0, LORAWAN_TX_MAX_SIZE); - _tx_msg.f_buffer_size = LORAWAN_TX_MAX_SIZE; + memset(_tx_msg.f_buffer, 0, MBED_CONF_LORA_TX_MAX_SIZE); + _tx_msg.f_buffer_size = MBED_CONF_LORA_TX_MAX_SIZE; tr_error("mcps_confirm_handler: Error code = %d", mcps_confirm->status); // If sending timed out, we have a special event for that - if (mcps_confirm->status == LORA_EVENT_INFO_STATUS_TX_TIMEOUT) { + if (mcps_confirm->status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT) { if (_callbacks.events) { - _queue->call(_callbacks.events, TX_TIMEOUT); + const int ret = _queue->call(_callbacks.events, TX_TIMEOUT); + MBED_ASSERT(ret != 0); + (void)ret; } return; - } if (mcps_confirm->status == LORA_EVENT_INFO_STATUS_RX2_TIMEOUT) { + } if (mcps_confirm->status == LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT) { tr_debug("Did not receive Ack"); } // Otherwise send a general TX_ERROR event if (_callbacks.events) { - _queue->call(_callbacks.events, TX_ERROR); + const int ret = _queue->call(_callbacks.events, TX_ERROR); + MBED_ASSERT(ret != 0); + (void)ret; } return; } // If No errors encountered, let's proceed with the status. // CONFIRMED needs special handling because of acks - if (mcps_confirm->mcps_request == LORA_MCPS_CONFIRMED) { + if (mcps_confirm->req_type == MCPS_CONFIRMED) { // In confirmed case, we need to check if we have received the Ack or not. // This is actually just being paranoid about ack because LoRaMac.cpp doesn't // call this callback until an ack is received. @@ -1157,10 +904,12 @@ void LoRaWANStack::mcps_confirm_handler(lora_mac_mcps_confirm_t *mcps_confirm) // This part is common to both CONFIRMED and UNCONFIRMED. // Tell the application about successful transmission and store // data rate plus frame counter. - _lw_session.uplink_counter = mcps_confirm->uplink_counter; + _lw_session.uplink_counter = mcps_confirm->ul_frame_counter; _tx_msg.tx_ongoing = false; if (_callbacks.events) { - _queue->call(_callbacks.events, TX_DONE); + const int ret = _queue->call(_callbacks.events, TX_DONE); + MBED_ASSERT(ret != 0); + (void)ret; } } @@ -1169,28 +918,30 @@ void LoRaWANStack::mcps_confirm_handler(lora_mac_mcps_confirm_t *mcps_confirm) * \param mcps_indication Pointer to the indication structure, * containing indication attributes. */ -void LoRaWANStack::mcps_indication_handler(lora_mac_mcps_indication_t *mcps_indication) +void LoRaWANStack::mcps_indication_handler(loramac_mcps_indication_t *mcps_indication) { if (mcps_indication == NULL) { tr_error("mcps_indication: struct [in] is null."); return; } - if (mcps_indication->status != LORA_EVENT_INFO_STATUS_OK) { + if (mcps_indication->status != LORAMAC_EVENT_INFO_STATUS_OK) { if (_callbacks.events) { - _queue->call(_callbacks.events, RX_ERROR); + const int ret = _queue->call(_callbacks.events, RX_ERROR); + MBED_ASSERT(ret != 0); + (void)ret; } return; } - switch (mcps_indication->mcps_indication) { - case LORA_MCPS_UNCONFIRMED: + switch (mcps_indication->type) { + case MCPS_UNCONFIRMED: break; - case LORA_MCPS_CONFIRMED: + case MCPS_CONFIRMED: break; - case LORA_MCPS_PROPRIETARY: + case MCPS_PROPRIETARY: break; - case LORA_MCPS_MULTICAST: + case MCPS_MULTICAST: break; default: break; @@ -1214,7 +965,7 @@ void LoRaWANStack::mcps_indication_handler(lora_mac_mcps_indication_t *mcps_indi } #endif - if (mcps_indication->rx_data == true) { + if (mcps_indication->is_data_recvd == true) { switch (mcps_indication->port) { case 224: #if defined(LORAWAN_COMPLIANCE_TEST) @@ -1226,32 +977,33 @@ void LoRaWANStack::mcps_indication_handler(lora_mac_mcps_indication_t *mcps_indi break; default: if (is_port_valid(mcps_indication->port) == true || - mcps_indication->mcps_indication == LORA_MCPS_PROPRIETARY) { + mcps_indication->type == MCPS_PROPRIETARY) { // Valid message arrived. - // Save message to buffer with session information. - if (_rx_msg.rx_message.mcps_indication.buffer_size > LORAMAC_PHY_MAXPAYLOAD) { - // This may never happen as both radio and MAC are limited - // to the size 255 bytes - tr_debug("Cannot receive more than buffer capacity!"); - if (_callbacks.events) { - _queue->call(_callbacks.events, RX_ERROR); - } - return; - } else { - _rx_msg.type = LORAMAC_RX_MCPS_INDICATION; - _rx_msg.rx_message.mcps_indication.buffer_size = mcps_indication->buffer_size; - _rx_msg.rx_message.mcps_indication.port = mcps_indication->port; - // Copy message for user - memcpy(_rx_msg.rx_message.mcps_indication.buffer, - mcps_indication->buffer, mcps_indication->buffer_size); - } + _rx_msg.type = LORAMAC_RX_MCPS_INDICATION; + _rx_msg.msg.mcps_indication.buffer_size = mcps_indication->buffer_size; + _rx_msg.msg.mcps_indication.port = mcps_indication->port; + + // no copy, just set the pointer for the user + _rx_msg.msg.mcps_indication.buffer = + mcps_indication->buffer; // Notify application about received frame.. - tr_debug("Received %d bytes", _rx_msg.rx_message.mcps_indication.buffer_size); + tr_debug("Received %d bytes", _rx_msg.msg.mcps_indication.buffer_size); _rx_msg.receive_ready = true; + if (_callbacks.events) { - _queue->call(_callbacks.events, RX_DONE); + const int ret = _queue->call(_callbacks.events, RX_DONE); + MBED_ASSERT(ret != 0); + (void)ret; + } + + // If fPending bit is set we try to generate an empty packet + // with CONFIRMED flag set. We always set a CONFIRMED flag so + // that we could retry a certain number of times if the uplink + // failed for some reason + if (mcps_indication->fpending_status) { + handle_tx(mcps_indication->port, NULL, 0, MSG_CONFIRMED_FLAG); } } else { // Invalid port, ports 0, 224 and 225-255 are reserved. @@ -1267,7 +1019,7 @@ void LoRaWANStack::mcps_indication_handler(lora_mac_mcps_indication_t *mcps_indi * \param mcps_indication Pointer to the indication structure, * containing indication attributes. */ -void LoRaWANStack::compliance_test_handler(lora_mac_mcps_indication_t *mcps_indication) +void LoRaWANStack::compliance_test_handler(loramac_mcps_indication_t *mcps_indication) { if (_compliance_test.running == false) { // Check compliance test enable command (i) @@ -1286,16 +1038,16 @@ void LoRaWANStack::compliance_test_handler(lora_mac_mcps_indication_t *mcps_indi _compliance_test.running = true; _compliance_test.state = 1; - lora_mac_mib_request_confirm_t mib_req; - mib_req.type = LORA_MIB_ADR; - mib_req.param.adr_enable = true; + loramac_mib_req_confirm_t mib_req; + mib_req.type = MIB_ADR; + mib_req.param.is_adr_enable = true; mib_set_request(&mib_req); #if MBED_CONF_LORA_PHY == 0 - LoRaMacTestSetDutyCycleOn(false); + _loramac.LoRaMacTestSetDutyCycleOn(false); #endif //5000ms - LoRaMacSetTxTimer(5000); + _loramac.LoRaMacSetTxTimer(5000); set_device_state(DEVICE_STATE_COMPLIANCE_TEST); tr_debug("Compliance test activated."); } @@ -1304,21 +1056,21 @@ void LoRaWANStack::compliance_test_handler(lora_mac_mcps_indication_t *mcps_indi switch (_compliance_test.state) { case 0: // Check compliance test disable command (ii) _compliance_test.is_tx_confirmed = true; - _compliance_test.app_port = LORAWAN_APP_PORT; + _compliance_test.app_port = MBED_CONF_LORA_APP_PORT; _compliance_test.app_data_size = LORAWAN_COMPLIANCE_TEST_DATA_SIZE; _compliance_test.downlink_counter = 0; _compliance_test.running = false; - lora_mac_mib_request_confirm_t mib_req; - mib_req.type = LORA_MIB_ADR; - mib_req.param.adr_enable = LORAWAN_ADR_ON; + loramac_mib_req_confirm_t mib_req; + mib_req.type = MIB_ADR; + mib_req.param.is_adr_enable = MBED_CONF_LORA_ADR_ON; mib_set_request(&mib_req); #if MBED_CONF_LORA_PHY == 0 - LoRaMacTestSetDutyCycleOn(LORAWAN_DUTYCYCLE_ON); + _loramac.LoRaMacTestSetDutyCycleOn(MBED_CONF_LORA_DUTY_CYCLE_ON); #endif // Go to idle state after compliance test mode. tr_debug("Compliance test disabled."); - LoRaMacStopTxTimer(); + _loramac.LoRaMacStopTxTimer(); // Clear any compliance test message stuff before going back to normal operation. memset(&_tx_msg, 0, sizeof(_tx_msg)); @@ -1347,28 +1099,28 @@ void LoRaWANStack::compliance_test_handler(lora_mac_mcps_indication_t *mcps_indi send_compliance_test_frame_to_mac(); break; case 5: // (viii) - lora_mac_mlme_req_t mlme_req; - mlme_req.type = LORA_MLME_LINK_CHECK; + loramac_mlme_req_t mlme_req; + mlme_req.type = MLME_LINK_CHECK; mlme_request_handler(&mlme_req); break; case 6: // (ix) - lora_mac_mlme_req_t mlme_request; - lora_mac_mib_request_confirm_t mib_request; + loramac_mlme_req_t mlme_request; + loramac_mib_req_confirm_t mib_request; // Disable TestMode and revert back to normal operation _compliance_test.is_tx_confirmed = true; - _compliance_test.app_port = LORAWAN_APP_PORT; + _compliance_test.app_port = MBED_CONF_LORA_APP_PORT; _compliance_test.app_data_size = LORAWAN_COMPLIANCE_TEST_DATA_SIZE; _compliance_test.downlink_counter = 0; _compliance_test.running = false; - mib_request.type = LORA_MIB_ADR; - mib_request.param.adr_enable = LORAWAN_ADR_ON; + mib_request.type = MIB_ADR; + mib_request.param.is_adr_enable = MBED_CONF_LORA_ADR_ON; mib_set_request(&mib_request); #if MBED_CONF_LORA_PHY == 0 - LoRaMacTestSetDutyCycleOn(LORAWAN_DUTYCYCLE_ON); + _loramac.LoRaMacTestSetDutyCycleOn(MBED_CONF_LORA_DUTY_CYCLE_ON); #endif - mlme_request.type = LORA_MLME_JOIN; + mlme_request.type = MLME_JOIN; mlme_request.req.join.dev_eui = _lw_session.connection.connection_u.otaa.dev_eui; mlme_request.req.join.app_eui = _lw_session.connection.connection_u.otaa.app_eui; mlme_request.req.join.app_key = _lw_session.connection.connection_u.otaa.app_key; @@ -1377,17 +1129,17 @@ void LoRaWANStack::compliance_test_handler(lora_mac_mcps_indication_t *mcps_indi break; case 7: // (x) if (mcps_indication->buffer_size == 3) { - lora_mac_mlme_req_t mlme_req; - mlme_req.type = LORA_MLME_TXCW; - mlme_req.req.tx_cw.timeout = (uint16_t)((mcps_indication->buffer[1] << 8) | mcps_indication->buffer[2]); + loramac_mlme_req_t mlme_req; + mlme_req.type = MLME_TXCW; + mlme_req.req.cw_tx_mode.timeout = (uint16_t)((mcps_indication->buffer[1] << 8) | mcps_indication->buffer[2]); mlme_request_handler(&mlme_req); } else if (mcps_indication->buffer_size == 7) { - lora_mac_mlme_req_t mlme_req; - mlme_req.type = LORA_MLME_TXCW_1; - mlme_req.req.tx_cw.timeout = (uint16_t)((mcps_indication->buffer[1] << 8) | mcps_indication->buffer[2]); - mlme_req.req.tx_cw.frequency = (uint32_t)((mcps_indication->buffer[3] << 16) | (mcps_indication->buffer[4] << 8) + loramac_mlme_req_t mlme_req; + mlme_req.type = MLME_TXCW_1; + mlme_req.req.cw_tx_mode.timeout = (uint16_t)((mcps_indication->buffer[1] << 8) | mcps_indication->buffer[2]); + mlme_req.req.cw_tx_mode.frequency = (uint32_t)((mcps_indication->buffer[3] << 16) | (mcps_indication->buffer[4] << 8) | mcps_indication->buffer[5]) * 100; - mlme_req.req.tx_cw.power = mcps_indication->buffer[6]; + mlme_req.req.cw_tx_mode.power = mcps_indication->buffer[6]; mlme_request_handler(&mlme_req); } _compliance_test.state = 1; @@ -1397,401 +1149,45 @@ void LoRaWANStack::compliance_test_handler(lora_mac_mcps_indication_t *mcps_indi } #endif -lora_mac_status_t LoRaWANStack::mib_set_request(lora_mac_mib_request_confirm_t *mib_set_params) +lorawan_status_t LoRaWANStack::mib_set_request(loramac_mib_req_confirm_t *mib_set_params) { - MibRequestConfirm_t stack_mib_set; - ChannelParams_t stack_channel_params; - MulticastParams_t *stack_multicast_params = NULL; - MulticastParams_t *head = NULL; - lora_mac_status_t status; - if (NULL == mib_set_params) { - return LORA_MAC_STATUS_PARAMETER_INVALID; + return LORAWAN_STATUS_PARAMETER_INVALID; } - - // Interpreting from lora_mac_mib_t to Mib_t - stack_mib_set.Type = interpret_mib_req_confirm_type(mib_set_params->type); - - switch (mib_set_params->type) { - case LORA_MIB_DEVICE_CLASS: - stack_mib_set.Param.Class = (DeviceClass_t)mib_set_params->param.lora_class; - break; - - case LORA_MIB_NETWORK_JOINED: - stack_mib_set.Param.IsNetworkJoined = mib_set_params->param.is_network_joined; - break; - - case LORA_MIB_ADR: - stack_mib_set.Param.AdrEnable = mib_set_params->param.adr_enable; - break; - - case LORA_MIB_NET_ID: - stack_mib_set.Param.NetID = mib_set_params->param.net_id; - break; - - case LORA_MIB_DEV_ADDR: - stack_mib_set.Param.DevAddr = mib_set_params->param.dev_addr; - break; - - case LORA_MIB_NWK_SKEY: - stack_mib_set.Param.NwkSKey = mib_set_params->param.nwk_skey; - break; - - case LORA_MIB_APP_SKEY: - stack_mib_set.Param.AppSKey = mib_set_params->param.app_skey; - break; - - case LORA_MIB_PUBLIC_NETWORK: - stack_mib_set.Param.EnablePublicNetwork = mib_set_params->param.enable_public_network; - break; - - case LORA_MIB_REPEATER_SUPPORT: - stack_mib_set.Param.EnableRepeaterSupport = mib_set_params->param.enable_repeater_support; - break; - - case LORA_MIB_CHANNELS: - stack_channel_params.Frequency = mib_set_params->param.channel_list->frequency; - stack_channel_params.DrRange.Value = mib_set_params->param.channel_list->dr_range.value; - stack_channel_params.DrRange.Fields.Min = mib_set_params->param.channel_list->dr_range.lora_mac_fields_s.min; - stack_channel_params.DrRange.Fields.Max = mib_set_params->param.channel_list->dr_range.lora_mac_fields_s.max; - stack_channel_params.Band = mib_set_params->param.channel_list->band; - stack_channel_params.Rx1Frequency = mib_set_params->param.channel_list->rx1_frequency; - stack_mib_set.Param.ChannelList = &stack_channel_params; - break; - - case LORA_MIB_RX2_CHANNEL: - stack_mib_set.Param.Rx2Channel.Frequency = mib_set_params->param.rx2_channel.frequency; - stack_mib_set.Param.Rx2Channel.Datarate = mib_set_params->param.rx2_channel.datarate; - break; - - case LORA_MIB_RX2_DEFAULT_CHANNEL: - stack_mib_set.Param.Rx2DefaultChannel.Frequency = mib_set_params->param.rx2_default_channel.frequency; - stack_mib_set.Param.Rx2DefaultChannel.Datarate = mib_set_params->param.rx2_default_channel.datarate; - break; - - case LORA_MIB_CHANNELS_MASK: - stack_mib_set.Param.ChannelsMask = mib_set_params->param.channels_mask; - break; - - case LORA_MIB_CHANNELS_DEFAULT_MASK: - stack_mib_set.Param.ChannelsDefaultMask = mib_set_params->param.channels_default_mask; - break; - - case LORA_MIB_CHANNELS_NB_REP: - stack_mib_set.Param.ChannelNbRep = mib_set_params->param.channel_nb_rep; - break; - - case LORA_MIB_MAX_RX_WINDOW_DURATION: - stack_mib_set.Param.MaxRxWindow = mib_set_params->param.max_rx_window; - break; - - case LORA_MIB_RECEIVE_DELAY_1: - stack_mib_set.Param.ReceiveDelay1 = mib_set_params->param.receive_delay1; - break; - - case LORA_MIB_RECEIVE_DELAY_2: - stack_mib_set.Param.ReceiveDelay2 = mib_set_params->param.receive_delay2; - break; - - case LORA_MIB_JOIN_ACCEPT_DELAY_1: - stack_mib_set.Param.JoinAcceptDelay1 = mib_set_params->param.join_accept_delay1; - break; - - case LORA_MIB_JOIN_ACCEPT_DELAY_2: - stack_mib_set.Param.JoinAcceptDelay2 = mib_set_params->param.join_accept_delay2; - break; - - case LORA_MIB_CHANNELS_DEFAULT_DATARATE: - stack_mib_set.Param.ChannelsDefaultDatarate = mib_set_params->param.channels_default_datarate; - break; - - case LORA_MIB_CHANNELS_DATARATE: - stack_mib_set.Param.ChannelsDatarate = mib_set_params->param.channels_datarate; - break; - - case LORA_MIB_CHANNELS_TX_POWER: - stack_mib_set.Param.ChannelsTxPower = mib_set_params->param.channels_tx_power; - break; - - case LORA_MIB_CHANNELS_DEFAULT_TX_POWER: - stack_mib_set.Param.ChannelsDefaultTxPower = mib_set_params->param.channels_default_tx_power; - break; - - case LORA_MIB_UPLINK_COUNTER: - stack_mib_set.Param.UpLinkCounter = mib_set_params->param.uplink_counter; - break; - - case LORA_MIB_DOWNLINK_COUNTER: - stack_mib_set.Param.DownLinkCounter = mib_set_params->param.downlink_counter; - break; - - case LORA_MIB_MULTICAST_CHANNEL: - /* - * Parse multicast list (C++ linked list) - */ - while (mib_set_params->param.multicast_list != NULL) { - if (stack_multicast_params == NULL) { - stack_multicast_params = new MulticastParams_t; - head = stack_multicast_params; - } else { - while (stack_multicast_params != NULL) { - stack_multicast_params = stack_multicast_params->Next; - } - - stack_multicast_params = new MulticastParams_t; - } - - stack_multicast_params->Address = mib_set_params->param.multicast_list->address; - for (int i = 0; i < 16; i++) { - stack_multicast_params->NwkSKey[i] = mib_set_params->param.multicast_list->nwk_skey[i]; - stack_multicast_params->AppSKey[i] = mib_set_params->param.multicast_list->app_skey[i]; - } - - stack_multicast_params->DownLinkCounter = mib_set_params->param.multicast_list->downlink_counter; - } - - stack_mib_set.Param.MulticastList = head; - break; - - case LORA_MIB_SYSTEM_MAX_RX_ERROR: - stack_mib_set.Param.SystemMaxRxError = mib_set_params->param.system_max_rx_error; - break; - - case LORA_MIB_MIN_RX_SYMBOLS: - stack_mib_set.Param.MinRxSymbols = mib_set_params->param.min_rx_symbols; - break; - - default: - return LORA_MAC_STATUS_SERVICE_UNKNOWN; - break; - } - - /* - * Set MIB data to LoRa stack - */ - status = error_type_converter(LoRaMacMibSetRequestConfirm(&stack_mib_set)); - /* - * Release memory if reserved by multicast list - */ - if (NULL != head) { - while (NULL != head) { - delete head; - head = NULL; - head = stack_mib_set.Param.MulticastList->Next; - } - stack_mib_set.Param.MulticastList = NULL; - } - - return status; + return _loramac.mib_set_request_confirm(mib_set_params); } -lora_mac_status_t LoRaWANStack::mib_get_request(lora_mac_mib_request_confirm_t *mib_get_params) +lorawan_status_t LoRaWANStack::mib_get_request(loramac_mib_req_confirm_t *mib_get_params) { - MibRequestConfirm_t stack_mib_get; - MulticastParams_t *origin_multicast_list = NULL; - lora_mac_multicast_params_t *new_multicast_list = NULL; - lora_mac_status_t mac_status = LORA_MAC_STATUS_OK; - if(NULL == mib_get_params) { - return LORA_MAC_STATUS_PARAMETER_INVALID; + return LORAWAN_STATUS_PARAMETER_INVALID; } - - // Interprets from lora_mac_mib_t to Mib_t - stack_mib_get.Type = interpret_mib_req_confirm_type(mib_get_params->type); - mac_status = error_type_converter(LoRaMacMibGetRequestConfirm(&stack_mib_get)); - - if (LORA_MAC_STATUS_OK != mac_status) { - return LORA_MAC_STATUS_SERVICE_UNKNOWN; - } - - switch(mib_get_params->type) { - case LORA_MIB_DEVICE_CLASS: - mib_get_params->param.lora_class = (lora_mac_device_class_t)stack_mib_get.Param.Class; - break; - case LORA_MIB_NETWORK_JOINED: - mib_get_params->param.is_network_joined = stack_mib_get.Param.IsNetworkJoined; - break; - case LORA_MIB_ADR: - mib_get_params->param.adr_enable = stack_mib_get.Param.AdrEnable; - break; - case LORA_MIB_NET_ID: - mib_get_params->param.net_id = stack_mib_get.Param.NetID; - break; - case LORA_MIB_DEV_ADDR: - mib_get_params->param.dev_addr = stack_mib_get.Param.DevAddr; - break; - case LORA_MIB_NWK_SKEY: - mib_get_params->param.nwk_skey = stack_mib_get.Param.NwkSKey; - break; - case LORA_MIB_APP_SKEY: - mib_get_params->param.app_skey = stack_mib_get.Param.AppSKey; - break; - case LORA_MIB_PUBLIC_NETWORK: - mib_get_params->param.enable_public_network = stack_mib_get.Param.EnablePublicNetwork; - break; - case LORA_MIB_REPEATER_SUPPORT: - mib_get_params->param.enable_repeater_support = stack_mib_get.Param.EnableRepeaterSupport; - break; - case LORA_MIB_CHANNELS: - mib_get_params->param.channel_list = (lora_mac_channel_params_t *) stack_mib_get.Param.ChannelList; - break; - case LORA_MIB_RX2_CHANNEL: - mib_get_params->param.rx2_channel.datarate = stack_mib_get.Param.Rx2Channel.Datarate; - mib_get_params->param.rx2_channel.frequency = stack_mib_get.Param.Rx2Channel.Frequency; - break; - case LORA_MIB_RX2_DEFAULT_CHANNEL: - mib_get_params->param.rx2_default_channel.datarate = stack_mib_get.Param.Rx2DefaultChannel.Datarate; - mib_get_params->param.rx2_default_channel.frequency = stack_mib_get.Param.Rx2DefaultChannel.Frequency; - break; - case LORA_MIB_CHANNELS_DEFAULT_MASK: - mib_get_params->param.channels_default_mask = stack_mib_get.Param.ChannelsDefaultMask; - break; - case LORA_MIB_CHANNELS_MASK: - mib_get_params->param.channels_mask = stack_mib_get.Param.ChannelsMask; - break; - case LORA_MIB_CHANNELS_NB_REP: - mib_get_params->param.channel_nb_rep = stack_mib_get.Param.ChannelNbRep; - break; - case LORA_MIB_MAX_RX_WINDOW_DURATION: - mib_get_params->param.max_rx_window = stack_mib_get.Param.MaxRxWindow; - break; - case LORA_MIB_RECEIVE_DELAY_1: - mib_get_params->param.receive_delay1 = stack_mib_get.Param.ReceiveDelay1; - break; - case LORA_MIB_RECEIVE_DELAY_2: - mib_get_params->param.receive_delay2 = stack_mib_get.Param.ReceiveDelay2; - break; - case LORA_MIB_JOIN_ACCEPT_DELAY_1: - mib_get_params->param.join_accept_delay1 = stack_mib_get.Param.JoinAcceptDelay1; - break; - case LORA_MIB_JOIN_ACCEPT_DELAY_2: - mib_get_params->param.join_accept_delay2 = stack_mib_get.Param.JoinAcceptDelay2; - break; - case LORA_MIB_CHANNELS_DEFAULT_DATARATE: - mib_get_params->param.channels_default_datarate = stack_mib_get.Param.ChannelsDefaultDatarate; - break; - case LORA_MIB_CHANNELS_DATARATE: - mib_get_params->param.channels_datarate = stack_mib_get.Param.ChannelsDatarate; - break; - case LORA_MIB_CHANNELS_DEFAULT_TX_POWER: - mib_get_params->param.channels_default_tx_power = stack_mib_get.Param.ChannelsDefaultTxPower; - break; - case LORA_MIB_CHANNELS_TX_POWER: - mib_get_params->param.channels_tx_power = stack_mib_get.Param.ChannelsTxPower; - break; - case LORA_MIB_UPLINK_COUNTER: - mib_get_params->param.uplink_counter = stack_mib_get.Param.UpLinkCounter; - break; - case LORA_MIB_DOWNLINK_COUNTER: - mib_get_params->param.downlink_counter = stack_mib_get.Param.DownLinkCounter; - break; - case LORA_MIB_MULTICAST_CHANNEL: - /* - * Parse multicast list (C++ linked list) - */ - origin_multicast_list = stack_mib_get.Param.MulticastList; - - while (NULL != origin_multicast_list) { - if (NULL == new_multicast_list) { - new_multicast_list = new lora_mac_multicast_params_t; - new_multicast_list->next = NULL; - - mib_get_params->param.multicast_list = new_multicast_list; - } else { - while (NULL != new_multicast_list) { - new_multicast_list = new_multicast_list->next; - } - new_multicast_list = new lora_mac_multicast_params_t; - new_multicast_list->next = NULL; - } - - new_multicast_list->address = origin_multicast_list->Address; - for (int i = 0; i < 16; ++i) { - new_multicast_list->nwk_skey[i] = origin_multicast_list->NwkSKey[i]; - new_multicast_list->app_skey[i] = origin_multicast_list->AppSKey[i]; - } - new_multicast_list->downlink_counter = origin_multicast_list->DownLinkCounter; - - origin_multicast_list = origin_multicast_list->Next; - } - break; - case LORA_MIB_SYSTEM_MAX_RX_ERROR: - mib_get_params->param.system_max_rx_error = stack_mib_get.Param.SystemMaxRxError; - break; - case LORA_MIB_MIN_RX_SYMBOLS: - mib_get_params->param.min_rx_symbols = stack_mib_get.Param.MinRxSymbols; - break; - default: - return LORA_MAC_STATUS_SERVICE_UNKNOWN; - break; - } - - return mac_status; + return _loramac.mib_get_request_confirm(mib_get_params); } -lora_mac_status_t LoRaWANStack::error_type_converter(LoRaMacStatus_t type) +lorawan_status_t LoRaWANStack::set_link_check_request() { - switch (type) { - case LORAMAC_STATUS_OK: - return LORA_MAC_STATUS_OK; - break; - - case LORAMAC_STATUS_BUSY: - return LORA_MAC_STATUS_BUSY; - break; - - case LORAMAC_STATUS_SERVICE_UNKNOWN: - return LORA_MAC_STATUS_SERVICE_UNKNOWN; - break; - - case LORAMAC_STATUS_PARAMETER_INVALID: - return LORA_MAC_STATUS_PARAMETER_INVALID; - break; - - case LORAMAC_STATUS_FREQUENCY_INVALID: - return LORA_MAC_STATUS_FREQUENCY_INVALID; - break; - - case LORAMAC_STATUS_DATARATE_INVALID: - return LORA_MAC_STATUS_DATARATE_INVALID; - break; - - case LORAMAC_STATUS_FREQ_AND_DR_INVALID: - return LORA_MAC_STATUS_FREQ_AND_DR_INVALID; - break; - - case LORAMAC_STATUS_NO_NETWORK_JOINED: - return LORA_MAC_STATUS_NO_NETWORK_JOINED; - break; - - case LORAMAC_STATUS_LENGTH_ERROR: - return LORA_MAC_STATUS_LENGTH_ERROR; - break; - - case LORAMAC_STATUS_DEVICE_OFF: - return LORA_MAC_STATUS_DEVICE_OFF; - break; - - case LORAMAC_STATUS_CRYPTO_FAIL: - return LORA_MAC_STATUS_CRYPTO_FAIL; - break; - - default: - return LORA_MAC_STATUS_SERVICE_UNKNOWN; - break; + if (!_callbacks.link_check_resp) { + tr_error("Must assign a callback function for link check request. "); + return LORAWAN_STATUS_PARAMETER_INVALID; } + + loramac_mlme_req_t mlme_req; + + mlme_req.type = MLME_LINK_CHECK; + return mlme_request_handler(&mlme_req); } -void LoRaWANStack::shutdown() +lorawan_status_t LoRaWANStack::shutdown() { set_device_state(DEVICE_STATE_SHUTDOWN); - lora_state_machine(); + return lora_state_machine(); } -lora_mac_status_t LoRaWANStack::lora_state_machine() +lorawan_status_t LoRaWANStack::lora_state_machine() { - lora_mac_mib_request_confirm_t mib_req; - lora_mac_status_t status = LORA_MAC_STATUS_DEVICE_OFF; + loramac_mib_req_confirm_t mib_req; + lorawan_status_t status = LORAWAN_STATUS_DEVICE_OFF; switch (_device_current_state) { case DEVICE_STATE_SHUTDOWN: @@ -1801,39 +1197,44 @@ lora_mac_status_t LoRaWANStack::lora_state_machine() */ drop_channel_list(); + // Shutdown LoRaMac + _loramac.disconnect(); + // Stop sending messages and set joined status to false. #if defined(LORAWAN_COMPLIANCE_TEST) _loramac.LoRaMacStopTxTimer(); #endif - mib_req.type = LORA_MIB_NETWORK_JOINED; - mib_req.param.is_network_joined = false; + mib_req.type = MIB_NETWORK_JOINED; + mib_req.param.is_nwk_joined = false; mib_set_request(&mib_req); // reset buffers to original state - memset(_tx_msg.f_buffer, 0, LORAWAN_TX_MAX_SIZE); + memset(_tx_msg.f_buffer, 0, MBED_CONF_LORA_TX_MAX_SIZE); _tx_msg.pending_size = 0; _tx_msg.f_buffer_size = 0; _tx_msg.tx_ongoing = false; - memset(_rx_msg.rx_message.mcps_indication.buffer, 0, LORAMAC_PHY_MAXPAYLOAD); + _rx_msg.msg.mcps_indication.buffer = NULL; _rx_msg.receive_ready = false; _rx_msg.prev_read_size = 0; - _rx_msg.rx_message.mcps_indication.buffer_size = 0; + _rx_msg.msg.mcps_indication.buffer_size = 0; // disable the session _lw_session.active = false; tr_debug("LoRaWAN protocol has been shut down."); if (_callbacks.events) { - _queue->call(_callbacks.events, DISCONNECTED); + const int ret = _queue->call(_callbacks.events, DISCONNECTED); + MBED_ASSERT(ret != 0); + (void)ret; } - status = LORA_MAC_STATUS_DEVICE_OFF; + status = LORAWAN_STATUS_DEVICE_OFF; break; case DEVICE_STATE_NOT_INITIALIZED: // Device is disconnected. - status = LORA_MAC_STATUS_DEVICE_OFF; + status = LORAWAN_STATUS_DEVICE_OFF; break; case DEVICE_STATE_INIT: - status = LORA_MAC_STATUS_OK; + status = LORAWAN_STATUS_OK; break; case DEVICE_STATE_JOINING: if (_lw_session.connection.connect_type == LORAWAN_CONNECTION_OTAA) { @@ -1841,8 +1242,8 @@ lora_mac_status_t LoRaWANStack::lora_state_machine() * OTAA join */ tr_debug("Send Join-request.."); - lora_mac_mlme_req_t mlme_req; - mlme_req.type = LORA_MLME_JOIN; + loramac_mlme_req_t mlme_req; + mlme_req.type = MLME_JOIN; mlme_req.req.join.dev_eui = _lw_session.connection.connection_u.otaa.dev_eui; mlme_req.req.join.app_eui = _lw_session.connection.connection_u.otaa.app_eui; @@ -1851,15 +1252,14 @@ lora_mac_status_t LoRaWANStack::lora_state_machine() // Send join request to server. status = mlme_request_handler(&mlme_req); - if (status != LORA_MAC_STATUS_OK) { + if (status != LORAWAN_STATUS_OK) { return status; } // Otherwise request was successful and OTAA connect is in //progress - return LORA_MAC_STATUS_CONNECT_IN_PROGRESS; + return LORAWAN_STATUS_CONNECT_IN_PROGRESS; } else { - status = LORA_MAC_STATUS_PARAMETER_INVALID; - break; + status = LORAWAN_STATUS_PARAMETER_INVALID; } break; case DEVICE_STATE_JOINED: @@ -1868,65 +1268,74 @@ lora_mac_status_t LoRaWANStack::lora_state_machine() _lw_session.active = true; // Tell the application that we are connected if (_callbacks.events) { - _queue->call(_callbacks.events, CONNECTED); + const int ret = _queue->call(_callbacks.events, CONNECTED); + MBED_ASSERT(ret != 0); + (void)ret; } + status = LORAWAN_STATUS_OK; break; case DEVICE_STATE_ABP_CONNECTING: /* * ABP connection */ - mib_req.type = LORA_MIB_NET_ID; + mib_req.type = MIB_NET_ID; mib_req.param.net_id = _lw_session.connection.connection_u.abp.nwk_id; mib_set_request(&mib_req); - mib_req.type = LORA_MIB_DEV_ADDR; + mib_req.type = MIB_DEV_ADDR; mib_req.param.dev_addr = _lw_session.connection.connection_u.abp.dev_addr; mib_set_request(&mib_req); - mib_req.type = LORA_MIB_NWK_SKEY; + mib_req.type = MIB_NWK_SKEY; mib_req.param.nwk_skey = _lw_session.connection.connection_u.abp.nwk_skey; mib_set_request(&mib_req); - mib_req.type = LORA_MIB_APP_SKEY; + mib_req.type = MIB_APP_SKEY; mib_req.param.app_skey = _lw_session.connection.connection_u.abp.app_skey; mib_set_request(&mib_req); - mib_req.type = LORA_MIB_NETWORK_JOINED; - mib_req.param.is_network_joined = true; + mib_req.type = MIB_NETWORK_JOINED; + mib_req.param.is_nwk_joined = true; mib_set_request(&mib_req); tr_debug("ABP Connection OK!"); // tell the application we are okay // if users provide wrong keys, it's their responsibility // there is no way to test ABP authentication success - status = LORA_MAC_STATUS_OK; + status = LORAWAN_STATUS_OK; // Session is now active _lw_session.active = true; if (_callbacks.events) { - _queue->call(_callbacks.events, CONNECTED); + const int ret = _queue->call(_callbacks.events, CONNECTED); + MBED_ASSERT(ret != 0); + (void)ret; } break; case DEVICE_STATE_SEND: // If a transmission is ongoing, don't interrupt if (_tx_msg.tx_ongoing) { - status = LORA_MAC_STATUS_OK; + status = LORAWAN_STATUS_OK; } else { _tx_msg.tx_ongoing = true; status = send_frame_to_mac(); switch (status) { - case LORA_MAC_STATUS_OK: + case LORAWAN_STATUS_OK: tr_debug("Frame scheduled to TX.."); break; - case LORA_MAC_STATUS_CRYPTO_FAIL: + case LORAWAN_STATUS_CRYPTO_FAIL: tr_error("Crypto failed. Clearing TX buffers"); if (_callbacks.events) { - _queue->call(_callbacks.events, TX_CRYPTO_ERROR); + const int ret = _queue->call(_callbacks.events, TX_CRYPTO_ERROR); + MBED_ASSERT(ret != 0); + (void)ret; } break; default: tr_error("Failure to schedule TX!"); if (_callbacks.events) { - _queue->call(_callbacks.events, TX_SCHEDULING_ERROR); + const int ret = _queue->call(_callbacks.events, TX_SCHEDULING_ERROR); + MBED_ASSERT(ret != 0); + (void)ret; } break; } @@ -1936,7 +1345,7 @@ lora_mac_status_t LoRaWANStack::lora_state_machine() break; case DEVICE_STATE_IDLE: //Do nothing - status = LORA_MAC_STATUS_IDLE; + status = LORAWAN_STATUS_IDLE; break; #if defined(LORAWAN_COMPLIANCE_TEST) case DEVICE_STATE_COMPLIANCE_TEST: @@ -1944,145 +1353,17 @@ lora_mac_status_t LoRaWANStack::lora_state_machine() tr_debug("Device is in compliance test mode."); //5000ms - LoRaMacSetTxTimer(5000); + _loramac.LoRaMacSetTxTimer(5000); if (_compliance_test.running == true) { send_compliance_test_frame_to_mac(); } - status = LORA_MAC_STATUS_COMPLIANCE_TEST_ON; + status = LORAWAN_STATUS_COMPLIANCE_TEST_ON; break; #endif default: - status = LORA_MAC_STATUS_SERVICE_UNKNOWN; + status = LORAWAN_STATUS_SERVICE_UNKNOWN; break; } return status; } - -static Mcps_t interpret_mcps_confirm_type(const lora_mac_mcps_t& local) -{ - switch (local) { - case LORA_MCPS_UNCONFIRMED: - return MCPS_UNCONFIRMED; - case LORA_MCPS_CONFIRMED: - return MCPS_CONFIRMED; - case LORA_MCPS_MULTICAST: - return MCPS_MULTICAST; - case LORA_MCPS_PROPRIETARY: - return MCPS_PROPRIETARY; - default: - MBED_ASSERT("Unknown Mcps_t"); - } - - // Never reaches here - return MCPS_UNCONFIRMED; -} - -static lora_mac_event_info_status_t interpret_event_info_type(const LoRaMacEventInfoStatus_t& remote) -{ - switch (remote) { - case LORAMAC_EVENT_INFO_STATUS_OK: - return LORA_EVENT_INFO_STATUS_OK; - case LORAMAC_EVENT_INFO_STATUS_ERROR: - return LORA_EVENT_INFO_STATUS_ERROR; - case LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT: - return LORA_EVENT_INFO_STATUS_TX_TIMEOUT; - case LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT: - return LORA_EVENT_INFO_STATUS_RX1_TIMEOUT; - case LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT: - return LORA_EVENT_INFO_STATUS_RX2_TIMEOUT; - case LORAMAC_EVENT_INFO_STATUS_RX1_ERROR: - return LORA_EVENT_INFO_STATUS_RX1_ERROR; - case LORAMAC_EVENT_INFO_STATUS_RX2_ERROR: - return LORA_EVENT_INFO_STATUS_RX2_ERROR; - case LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL: - return LORA_EVENT_INFO_STATUS_JOIN_FAIL; - case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED: - return LORA_EVENT_INFO_STATUS_DOWNLINK_REPEATED; - case LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR: - return LORA_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR; - case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS: - return LORA_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS; - case LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL: - return LORA_EVENT_INFO_STATUS_ADDRESS_FAIL; - case LORAMAC_EVENT_INFO_STATUS_MIC_FAIL: - return LORA_EVENT_INFO_STATUS_MIC_FAIL; - case LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL: - return LORA_EVENT_INFO_STATUS_CRYPTO_FAIL; - default: - MBED_ASSERT("Unknown LoRaMacEventInfoStatus_t"); - } - - // Never reaches here actually - return LORA_EVENT_INFO_STATUS_OK; -} - -static Mib_t interpret_mib_req_confirm_type(const lora_mac_mib_t& local) -{ - switch (local) { - case LORA_MIB_DEVICE_CLASS: - return MIB_DEVICE_CLASS; - case LORA_MIB_NETWORK_JOINED: - return MIB_NETWORK_JOINED; - case LORA_MIB_ADR: - return MIB_ADR; - case LORA_MIB_NET_ID: - return MIB_NET_ID; - case LORA_MIB_DEV_ADDR: - return MIB_DEV_ADDR; - case LORA_MIB_NWK_SKEY: - return MIB_NWK_SKEY; - case LORA_MIB_APP_SKEY: - return MIB_APP_SKEY; - case LORA_MIB_PUBLIC_NETWORK: - return MIB_PUBLIC_NETWORK; - case LORA_MIB_REPEATER_SUPPORT: - return MIB_REPEATER_SUPPORT; - case LORA_MIB_CHANNELS: - return MIB_CHANNELS; - case LORA_MIB_RX2_CHANNEL: - return MIB_RX2_CHANNEL; - case LORA_MIB_RX2_DEFAULT_CHANNEL: - return MIB_RX2_DEFAULT_CHANNEL; - case LORA_MIB_CHANNELS_MASK: - return MIB_CHANNELS_MASK; - case LORA_MIB_CHANNELS_DEFAULT_MASK: - return MIB_CHANNELS_DEFAULT_MASK; - case LORA_MIB_CHANNELS_NB_REP: - return MIB_CHANNELS_NB_REP; - case LORA_MIB_MAX_RX_WINDOW_DURATION: - return MIB_MAX_RX_WINDOW_DURATION; - case LORA_MIB_RECEIVE_DELAY_1: - return MIB_RECEIVE_DELAY_1; - case LORA_MIB_RECEIVE_DELAY_2: - return MIB_RECEIVE_DELAY_2; - case LORA_MIB_JOIN_ACCEPT_DELAY_1: - return MIB_JOIN_ACCEPT_DELAY_1; - case LORA_MIB_JOIN_ACCEPT_DELAY_2: - return MIB_JOIN_ACCEPT_DELAY_2; - case LORA_MIB_CHANNELS_DEFAULT_DATARATE: - return MIB_CHANNELS_DEFAULT_DATARATE; - case LORA_MIB_CHANNELS_DATARATE: - return MIB_CHANNELS_DATARATE; - case LORA_MIB_CHANNELS_TX_POWER: - return MIB_CHANNELS_TX_POWER; - case LORA_MIB_CHANNELS_DEFAULT_TX_POWER: - return MIB_CHANNELS_DEFAULT_TX_POWER; - case LORA_MIB_UPLINK_COUNTER: - return MIB_UPLINK_COUNTER; - case LORA_MIB_DOWNLINK_COUNTER: - return MIB_DOWNLINK_COUNTER; - case LORA_MIB_MULTICAST_CHANNEL: - return MIB_MULTICAST_CHANNEL; - case LORA_MIB_SYSTEM_MAX_RX_ERROR: - return MIB_SYSTEM_MAX_RX_ERROR; - case LORA_MIB_MIN_RX_SYMBOLS: - return MIB_MIN_RX_SYMBOLS; - default: - MBED_ASSERT("Cannot Interpret Mib_t"); - } - - // Never actually reaches here - return MIB_DEVICE_CLASS; -} - diff --git a/features/lorawan/LoRaWANStack.h b/features/lorawan/LoRaWANStack.h index edad86ee9d..08daf98ca9 100644 --- a/features/lorawan/LoRaWANStack.h +++ b/features/lorawan/LoRaWANStack.h @@ -35,6 +35,42 @@ SPDX-License-Identifier: BSD-3-Clause #include "lorawan/system/lorawan_data_structures.h" #include "LoRaRadio.h" +#ifdef MBED_CONF_LORA_PHY + #if MBED_CONF_LORA_PHY == 0 + #include "lorawan/lorastack/phy/LoRaPHYEU868.h" + #define LoRaPHY_region LoRaPHYEU868 + #elif MBED_CONF_LORA_PHY == 1 + #include "lorawan/lorastack/phy/LoRaPHYAS923.h" + #define LoRaPHY_region LoRaPHYAS923 + #elif MBED_CONF_LORA_PHY == 2 + #include "lorawan/lorastack/phy/LoRaPHYAU915.h" + #define LoRaPHY_region LoRaPHYAU915; + #elif MBED_CONF_LORA_PHY == 3 + #include "lorawan/lorastack/phy/LoRaPHYCN470.h" + #define LoRaPHY_region LoRaPHYCN470 + #elif MBED_CONF_LORA_PHY == 4 + #include "lorawan/lorastack/phy/LoRaPHYCN779.h" + #define LoRaPHY_region LoRaPHYCN779 + #elif MBED_CONF_LORA_PHY == 5 + #include "lorawan/lorastack/phy/LoRaPHYEU433.h" + #define LoRaPHY_region LoRaPHYEU433 + #elif MBED_CONF_LORA_PHY == 6 + #include "lorawan/lorastack/phy/LoRaPHYIN865.h" + #define LoRaPHY_region LoRaPHYIN865 + #elif MBED_CONF_LORA_PHY == 7 + #include "lorawan/lorastack/phy/LoRaPHYKR920.h" + #define LoRaPHY_region LoRaPHYKR920 + #elif MBED_CONF_LORA_PHY == 8 + #include "lorawan/lorastack/phy/LoRaPHYUS915.h" + #define LoRaPHY_region LoRaPHYUS915 + #elif MBED_CONF_LORA_PHY == 9 + #include "lorawan/lorastack/phy/LoRaPHYUS915Hybrid.h" + #define LoRaPHY_region LoRaPHYUS915Hybrid + #endif //MBED_CONF_LORA_PHY == VALUE +#else + #error "Must set LoRa PHY layer parameters." +#endif //MBED_CONF_LORA_PHY + /** * A mask for the network ID. */ @@ -61,13 +97,13 @@ public: /** End device initialization. * @param queue A pointer to an EventQueue passed from the application. - * @return LORA_MAC_STATUS_OK on success, a negative error code on failure. + * @return LORAWAN_STATUS_OK on success, a negative error code on failure. */ - lora_mac_status_t initialize_mac_layer(events::EventQueue *queue); + lorawan_status_t initialize_mac_layer(events::EventQueue *queue); /** Sets all callbacks for the application. * - * \param callbacks A pointer to the structure carrying callbacks. + * @param callbacks A pointer to the structure carrying callbacks. */ void set_lora_callbacks(lorawan_app_callbacks_t *callbacks); @@ -90,35 +126,35 @@ public: * * @param channel_plan A list of channels or a single channel. * - * @return LORA_MAC_STATUS_OK on success, a negative error + * @return LORAWAN_STATUS_OK on success, a negative error * code on failure. */ - lora_mac_status_t add_channels(const lora_channelplan_t &channel_plan); + lorawan_status_t add_channels(const lorawan_channelplan_t &channel_plan); /** Removes a channel from the list. * * @param channel_id Index of the channel being removed * - * @return LORA_MAC_STATUS_OK on success, a negative error + * @return LORAWAN_STATUS_OK on success, a negative error * code on failure. */ - lora_mac_status_t remove_a_channel(uint8_t channel_id); + lorawan_status_t remove_a_channel(uint8_t channel_id); /** Removes a previously set channel plan. * - * @return LORA_MAC_STATUS_OK on success, a negative error + * @return LORAWAN_STATUS_OK on success, a negative error * code on failure. */ - lora_mac_status_t drop_channel_list(); + lorawan_status_t drop_channel_list(); /** Gets a list of currently enabled channels . * * @param channel_plan The channel plan structure to store final result. * - * @return LORA_MAC_STATUS_OK on success, a negative error + * @return LORAWAN_STATUS_OK on success, a negative error * code on failure. */ - lora_mac_status_t get_enabled_channels(lora_channelplan_t &channel_plan); + lorawan_status_t get_enabled_channels(lorawan_channelplan_t &channel_plan); /** Sets up a retry counter for confirmed messages. * @@ -128,9 +164,9 @@ public: * * @param count The number of retries for confirmed messages. * - * @return LORA_MAC_STATUS_OK or a negative error code. + * @return LORAWAN_STATUS_OK or a negative error code. */ - lora_mac_status_t set_confirmed_msg_retry(uint8_t count); + lorawan_status_t set_confirmed_msg_retry(uint8_t count); /** Sets up the data rate. * @@ -141,26 +177,26 @@ public: * Note that the macro DR_* can mean different * things in different regions. * - * @return LORA_MAC_STATUS_OK if everything goes well, otherwise + * @return LORAWAN_STATUS_OK if everything goes well, otherwise * a negative error code. */ - lora_mac_status_t set_channel_data_rate(uint8_t data_rate); + lorawan_status_t set_channel_data_rate(uint8_t data_rate); /** Enables ADR. * * @param adr_enabled 0 ADR disabled, 1 ADR enabled. * - * @return LORA_MAC_STATUS_OK on success, a negative error + * @return LORAWAN_STATUS_OK on success, a negative error * code on failure. */ - lora_mac_status_t enable_adaptive_datarate(bool adr_enabled); + lorawan_status_t enable_adaptive_datarate(bool adr_enabled); /** Commissions a LoRa device. * * @param commission_data A structure representing all the commission * information. */ - void commission_device(const lora_dev_commission_t &commission_data); + void commission_device(const lorawan_dev_commission_t &commission_data); /** End device OTAA join. * @@ -169,11 +205,11 @@ public: * * @param params The `lorawan_connect_t` type structure. * - * @return LORA_MAC_STATUS_OK or - * LORA_MAC_STATUS_CONNECT_IN_PROGRESS on success, + * @return LORAWAN_STATUS_OK or + * LORAWAN_STATUS_CONNECT_IN_PROGRESS on success, * or a negative error code on failure. */ - lora_mac_status_t join_request_by_otaa(const lorawan_connect_t ¶ms); + lorawan_status_t join_request_by_otaa(const lorawan_connect_t ¶ms); /** End device ABP join. * @@ -182,11 +218,11 @@ public: * * @param params The `lorawan_connect_t` type structure. * - * @return LORA_MAC_STATUS_OK or - * LORA_MAC_STATUS_CONNECT_IN_PROGRESS on success, + * @return LORAWAN_STATUS_OK or + * LORAWAN_STATUS_CONNECT_IN_PROGRESS on success, * or a negative error code on failure. */ - lora_mac_status_t activation_by_personalization(const lorawan_connect_t ¶ms); + lorawan_status_t activation_by_personalization(const lorawan_connect_t ¶ms); /** Send message to gateway * @@ -219,7 +255,7 @@ public: * * * @return The number of bytes sent, or - * LORA_MAC_STATUS_WOULD_BLOCK if another TX is + * LORAWAN_STATUS_WOULD_BLOCK if another TX is * ongoing, or a negative error code on failure. */ int16_t handle_tx(uint8_t port, const uint8_t* data, @@ -260,18 +296,36 @@ public: * @return It could be one of these: * i) 0 if there is nothing else to read. * ii) Number of bytes written to user buffer. - * iii) LORA_MAC_STATUS_WOULD_BLOCK if there is + * iii) LORAWAN_STATUS_WOULD_BLOCK if there is * nothing available to read at the moment. * iv) A negative error code on failure. */ int16_t handle_rx(const uint8_t port, uint8_t* data, uint16_t length, uint8_t flags); + /** Send Link Check Request MAC command. + * + * + * This API schedules a Link Check Request command (LinkCheckReq) for the network + * server and once the response, i.e., LinkCheckAns MAC command is received + * from the Network Server, an event is generated. + * + * A callback function for the link check response must be set prior to using + * this API, otherwise a LORAWAN_STATUS_PARAMETER_INVALID error is thrown. + * + * @return LORAWAN_STATUS_OK on successfully queuing a request, or + * a negative error code on failure. + * + */ + lorawan_status_t set_link_check_request(); + /** Shuts down the LoRaWAN protocol. * * In response to the user call for disconnection, the stack shuts down itself. + * + * @return LORAWAN_STATUS_DEVICE_OFF on successfully shutdown. */ - void shutdown(); + lorawan_status_t shutdown(); private: LoRaWANStack(); @@ -286,7 +340,7 @@ private: * State machine for stack controller layer. * Needs to be wriggled for every state change */ - lora_mac_status_t lora_state_machine(); + lorawan_status_t lora_state_machine(); /** * Sets the current state of the device. @@ -296,57 +350,38 @@ private: */ void set_device_state(device_states_t new_state); - /** - * This function is used only for compliance testing - */ - void prepare_special_tx_frame(uint8_t port); - /** * Hands over the packet to Mac layer by posting an MCPS request. */ - lora_mac_status_t send_frame_to_mac(); + lorawan_status_t send_frame_to_mac(); /** - * Callback function for MCPS confirm. Mac layer calls this function once - * an MCPS confirmation is received. This method translates Mac layer data + * Callback function for MLME indication. Mac layer calls this function once + * an MLME indication is received. This method translates Mac layer data * structure into stack layer data structure. */ - void mcps_confirm(McpsConfirm_t *mcps_confirm); - - /** - * Callback function for MCPS indication. Mac layer calls this function once - * an MCPS indication is received. This method translates Mac layer data - * structure into stack layer data structure. - */ - void mcps_indication(McpsIndication_t *mcps_indication); - - /** - * Callback function for MLME confirm. Mac layer calls this function once - * an MLME confirmation is received. This method translates Mac layer data - * structure into stack layer data structure. - */ - void mlme_confirm(MlmeConfirm_t *mlme_confirm); + void mlme_indication_handler(loramac_mlme_indication_t *mlmeIndication); /** * Handles an MLME request coming from the upper layers and delegates * it to the Mac layer, for example, a Join request goes as an MLME request * to the Mac layer. */ - lora_mac_status_t mlme_request_handler(lora_mac_mlme_req_t *mlme_request); + lorawan_status_t mlme_request_handler(loramac_mlme_req_t *mlme_request); /** * Handles an MLME confirmation coming from the Mac layer and uses it to * update the state for example, a Join Accept triggers an MLME confirmation, * that eventually comes here and we take necessary steps accordingly. */ - void mlme_confirm_handler(lora_mac_mlme_confirm_t *mlme_confirm); + void mlme_confirm_handler(loramac_mlme_confirm_t *mlme_confirm); /** * Handles an MCPS request while attempting to hand over a packet from * upper layers to Mac layer. For example in response to send_frame_to_mac(), * an MCPS request is generated. */ - lora_mac_status_t mcps_request_handler(lora_mac_mcps_req_t *mcps_request); + lorawan_status_t mcps_request_handler(loramac_mcps_req_t *mcps_request); /** * Handles an MCPS confirmation coming from the Mac layer in response to an @@ -354,7 +389,7 @@ private: * e.g., letting the application know that ack was not received in case of * a CONFIRMED message or scheduling error etc. */ - void mcps_confirm_handler(lora_mac_mcps_confirm_t *mcps_confirm); + void mcps_confirm_handler(loramac_mcps_confirm_t *mcps_confirm); /** * Handles an MCPS indication coming from the Mac layer, e.g., once we @@ -362,22 +397,22 @@ private: * and consequently this handler posts an event to the application that * there is something available to read. */ - void mcps_indication_handler(lora_mac_mcps_indication_t *mcps_indication); + void mcps_indication_handler(loramac_mcps_indication_t *mcps_indication); /** * Sets a MIB request, i.e., update a particular parameter etc. */ - lora_mac_status_t mib_set_request(lora_mac_mib_request_confirm_t *mib_set_params); + lorawan_status_t mib_set_request(loramac_mib_req_confirm_t *mib_set_params); /** * Requests the MIB to inquire about a particular parameter. */ - lora_mac_status_t mib_get_request(lora_mac_mib_request_confirm_t *mib_get_params); + lorawan_status_t mib_get_request(loramac_mib_req_confirm_t *mib_get_params); /** * Sets up user application port */ - lora_mac_status_t set_application_port(uint8_t port); + lorawan_status_t set_application_port(uint8_t port); /** * Helper function to figure out if the user defined data size is possible @@ -389,22 +424,30 @@ private: */ uint16_t check_possible_tx_size(uint16_t size); +#if defined(LORAWAN_COMPLIANCE_TEST) /** - * Used only for compliance testing + * This function is used only for compliance testing */ - void compliance_test_handler(lora_mac_mcps_indication_t *mcps_indication); + void prepare_special_tx_frame(uint8_t port); /** * Used only for compliance testing */ - lora_mac_status_t send_compliance_test_frame_to_mac(); + void compliance_test_handler(loramac_mcps_indication_t *mcps_indication); /** - * converts error codes from Mac layer to controller layer + * Used only for compliance testing */ - lora_mac_status_t error_type_converter(LoRaMacStatus_t type); + 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 @@ -412,8 +455,8 @@ private: lorawan_app_callbacks_t _callbacks; radio_events_t *_mac_handlers; lorawan_session_t _lw_session; - lora_mac_tx_message_t _tx_msg; - lora_mac_rx_message_t _rx_msg; + loramac_tx_message_t _tx_msg; + loramac_rx_message_t _rx_msg; uint8_t _app_port; uint8_t _num_retry; events::EventQueue *_queue; diff --git a/features/lorawan/lorastack/mac/LoRaMac.cpp b/features/lorawan/lorastack/mac/LoRaMac.cpp index f50d68cdf6..ab5c2b9358 100644 --- a/features/lorawan/lorastack/mac/LoRaMac.cpp +++ b/features/lorawan/lorastack/mac/LoRaMac.cpp @@ -22,12 +22,9 @@ Copyright (c) 2017, Arm Limited and affiliates. SPDX-License-Identifier: BSD-3-Clause */ #include -#include "events/EventQueue.h" #include "LoRaMac.h" #include "LoRaMacCrypto.h" -#include "LoRaMacTest.h" -#include "netsocket/LoRaRadio.h" -#include "lorastack/phy/LoRaPHY.h" + #if defined(FEATURE_COMMON_PAL) #include "mbed_trace.h" #define TRACE_GROUP "LMAC" @@ -39,31 +36,6 @@ SPDX-License-Identifier: BSD-3-Clause using namespace events; -/** - * LoRa PHY layer object storage - */ -static LoRaPHY *lora_phy; - -/** - * EventQueue object storage - */ -static EventQueue *ev_queue; - -/** - * Radio event callback handlers for MAC - */ -radio_events_t RadioEvents; - -/*! - * Maximum PHY layer payload size - */ -#define LORAMAC_PHY_MAXPAYLOAD 255 - -/*! - * Maximum MAC commands buffer size - */ -#define LORA_MAC_COMMAND_MAX_LENGTH 128 - /*! * Maximum length of the fOpts field */ @@ -85,1236 +57,746 @@ radio_events_t RadioEvents; #define BACKOFF_DC_24_HOURS 10000 /*! - * Device IEEE EUI + * Check the MAC layer state every MAC_STATE_CHECK_TIMEOUT in ms. */ -static uint8_t *LoRaMacDevEui; +#define MAC_STATE_CHECK_TIMEOUT 1000 /*! - * Application IEEE EUI + * The maximum number of times the MAC layer tries to get an acknowledge. */ -static uint8_t *LoRaMacAppEui; +#define MAX_ACK_RETRIES 8 /*! - * AES encryption/decryption cipher application key + * The frame direction definition for uplink communications. */ -static uint8_t *LoRaMacAppKey; +#define UP_LINK 0 /*! - * AES encryption/decryption cipher network session key + * The frame direction definition for downlink communications. */ -static uint8_t LoRaMacNwkSKey[] = +#define DOWN_LINK 1 + + +LoRaMac::LoRaMac(LoRaWANTimeHandler &lora_time) + : mac_commands(*this), _lora_time(lora_time) { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; + lora_phy = NULL; + //radio_events_t RadioEvents; + _params.keys.dev_eui = NULL; + _params.keys.app_eui = NULL; + _params.keys.app_key = NULL; -/*! - * AES encryption/decryption cipher application session key - */ -static uint8_t LoRaMacAppSKey[] = + memset(_params.keys.nwk_skey, 0, sizeof(_params.keys.nwk_skey)); + memset(_params.keys.app_skey, 0, sizeof(_params.keys.app_skey)); + + _params.dev_nonce = 0; + _params.net_id = 0; + _params.dev_addr = 0; + _params.buffer_pkt_len = 0; + _params.payload_length = 0; + _params.ul_frame_counter = 0; + _params.dl_frame_counter = 0; + _params.is_ul_frame_counter_fixed = false; + _params.is_rx_window_enabled = true; + _params.is_nwk_joined = false; + _params.adr_ack_counter = 0; + _params.is_node_ack_requested = false; + _params.is_srv_ack_requested = false; + _params.ul_nb_rep_counter = 0; + _params.timers.mac_init_time = 0; + _params.mac_state = LORAMAC_IDLE; + _params.max_ack_timeout_retries = 1; + _params.ack_timeout_retry_counter = 1; + _params.is_ack_retry_timeout_expired = false; + _params.timers.tx_toa = 0; + + _params.multicast_channels = NULL; + + _params.sys_params.adr_on = false; + _params.sys_params.max_duty_cycle = 0; + + mac_primitives = NULL; + ev_queue = NULL; +} + +LoRaMac::~LoRaMac() { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; +} -/*! - * Device nonce is a random value extracted by issuing a sequence of RSSI - * measurements - */ -static uint16_t LoRaMacDevNonce; - -/*! - * Network ID ( 3 bytes ) - */ -static uint32_t LoRaMacNetID; - -/*! - * Mote Address - */ -static uint32_t LoRaMacDevAddr; - -/*! - * Multicast channels linked list - */ -static MulticastParams_t *MulticastChannels = NULL; - -/*! - * Actual device class - */ -static DeviceClass_t LoRaMacDeviceClass; - -/*! - * Indicates if the node is connected to a private or public network - */ -static bool PublicNetwork; - -/*! - * Indicates if the node supports repeaters - */ -static bool RepeaterSupport; - -/*! - * Buffer containing the data to be sent or received. - */ -static uint8_t LoRaMacBuffer[LORAMAC_PHY_MAXPAYLOAD]; - -/*! - * Length of packet in LoRaMacBuffer - */ -static uint16_t LoRaMacBufferPktLen = 0; - -/*! - * Length of the payload in LoRaMacBuffer - */ -static uint8_t LoRaMacTxPayloadLen = 0; - -/*! - * Buffer containing the upper layer data. - */ -static uint8_t LoRaMacRxPayload[LORAMAC_PHY_MAXPAYLOAD]; - -/*! - * LoRaMAC frame counter. Each time a packet is sent the counter is incremented. - * Only the 16 LSB bits are sent - */ -static uint32_t UpLinkCounter = 0; - -/*! - * LoRaMAC frame counter. Each time a packet is received the counter is incremented. - * Only the 16 LSB bits are received - */ -static uint32_t DownLinkCounter = 0; - -/*! - * IsPacketCounterFixed enables the MIC field tests by fixing the - * UpLinkCounter value - */ -static bool IsUpLinkCounterFixed = false; - -/*! - * Used for test purposes. Disables the opening of the reception windows. - */ -static bool IsRxWindowsEnabled = true; - -/*! - * Indicates if the MAC layer has already joined a network. - */ -static bool IsLoRaMacNetworkJoined = false; - -/*! - * LoRaMac ADR control status - */ -static bool AdrCtrlOn = false; - -/*! - * Counts the number of missed ADR acknowledgements - */ -static uint32_t AdrAckCounter = 0; - -/*! - * If the node has sent a FRAME_TYPE_DATA_CONFIRMED_UP this variable indicates - * if the nodes needs to manage the server acknowledgement. - */ -static bool NodeAckRequested = false; - -/*! - * If the server has sent a FRAME_TYPE_DATA_CONFIRMED_DOWN this variable indicates - * if the ACK bit must be set for the next transmission - */ -static bool SrvAckRequested = false; - -/*! - * Indicates if the MAC layer wants to send MAC commands - */ -static bool MacCommandsInNextTx = false; - -/*! - * Contains the current MacCommandsBuffer index - */ -static uint8_t MacCommandsBufferIndex = 0; - -/*! - * Contains the current MacCommandsBuffer index for MAC commands to repeat - */ -static uint8_t MacCommandsBufferToRepeatIndex = 0; - -/*! - * Buffer containing the MAC layer commands - */ -static uint8_t MacCommandsBuffer[LORA_MAC_COMMAND_MAX_LENGTH]; - -/*! - * Buffer containing the MAC layer commands which must be repeated - */ -static uint8_t MacCommandsBufferToRepeat[LORA_MAC_COMMAND_MAX_LENGTH]; - -/*! - * LoRaMac parameters - */ -LoRaMacParams_t LoRaMacParams; - -/*! - * LoRaMac default parameters - */ -LoRaMacParams_t LoRaMacParamsDefaults; - -/*! - * Uplink messages repetitions counter - */ -static uint8_t ChannelsNbRepCounter = 0; - -/*! - * Maximum duty cycle - * \remark Possibility to shutdown the device. - */ -static uint8_t MaxDCycle = 0; - -/*! - * Aggregated duty cycle management - */ -static uint16_t AggregatedDCycle; -static TimerTime_t AggregatedLastTxDoneTime; -static TimerTime_t AggregatedTimeOff; - -/*! - * Enables/Disables duty cycle management (Test only) - */ -static bool DutyCycleOn; - -/*! - * Current channel index - */ -static uint8_t Channel; - -/*! - * Current channel index - */ -static uint8_t LastTxChannel; - -/*! - * Set to true, if the last uplink was a join request - */ -static bool LastTxIsJoinRequest; - -/*! - * Stores the time at LoRaMac initialization. - * - * \remark Used for the BACKOFF_DC computation. - */ -static TimerTime_t LoRaMacInitializationTime = 0; - -/*! - * LoRaMac internal states - */ -enum eLoRaMacState -{ - LORAMAC_IDLE = 0x00000000, - LORAMAC_TX_RUNNING = 0x00000001, - LORAMAC_RX = 0x00000002, - LORAMAC_ACK_REQ = 0x00000004, - LORAMAC_ACK_RETRY = 0x00000008, - LORAMAC_TX_DELAYED = 0x00000010, - LORAMAC_TX_CONFIG = 0x00000020, - LORAMAC_RX_ABORT = 0x00000040, -}; - -/*! - * LoRaMac internal state - */ -uint32_t LoRaMacState = LORAMAC_IDLE; - -/*! - * LoRaMac timer used to check the LoRaMacState (runs every second) - */ -static TimerEvent_t MacStateCheckTimer; - -/*! - * LoRaMac upper layer event functions - */ -static LoRaMacPrimitives_t *LoRaMacPrimitives; - -/*! - * LoRaMac upper layer callback functions - */ -static LoRaMacCallback_t *LoRaMacCallbacks; - -/*! - * LoRaMac duty cycle delayed Tx timer - */ -static TimerEvent_t TxDelayedTimer; - -/*! - * LoRaMac reception windows timers - */ -static TimerEvent_t RxWindowTimer1; -static TimerEvent_t RxWindowTimer2; - -/*! - * LoRaMac reception windows delay - * \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME - * join frame : RxWindowXDelay = JoinAcceptDelayX - RADIO_WAKEUP_TIME - */ -static uint32_t RxWindow1Delay; -static uint32_t RxWindow2Delay; - -/*! - * LoRaMac Rx windows configuration - */ -static RxConfigParams_t RxWindow1Config; -static RxConfigParams_t RxWindow2Config; - -/*! - * Acknowledge timeout timer. Used for packet retransmissions. - */ -static TimerEvent_t AckTimeoutTimer; - -/*! - * Number of trials to get a frame acknowledged - */ -static uint8_t AckTimeoutRetries = 1; - -/*! - * Number of trials to get a frame acknowledged - */ -static uint8_t AckTimeoutRetriesCounter = 1; - -/*! - * Indicates if the AckTimeout timer has expired or not - */ -static bool AckTimeoutRetry = false; - -/*! - * Last transmission time on air - */ -TimerTime_t TxTimeOnAir = 0; - -/*! - * Number of trials for the Join Request - */ -static uint8_t JoinRequestTrials; - -/*! - * Maximum number of trials for the Join Request - */ -static uint8_t MaxJoinRequestTrials; - -/*! - * Structure to hold an MCPS indication data. - */ -static McpsIndication_t McpsIndication; - -/*! - * Structure to hold MCPS confirm data. - */ -static McpsConfirm_t McpsConfirm; - -/*! - * Structure to hold MLME confirm data. - */ -static MlmeConfirm_t MlmeConfirm; - -/*! - * Holds the current rx window slot - */ -static uint8_t RxSlot = 0; - -/*! - * LoRaMac tx/rx operation state - */ -LoRaMacFlags_t LoRaMacFlags; - -/*! - * \brief Function to be executed on Radio Tx Done event - */ -static void OnRadioTxDone( void ); - -/*! - * \brief This function prepares the MAC to abort the execution of function - * OnRadioRxDone in case of a reception error. - */ -static void PrepareRxDoneAbort( void ); - -/*! - * \brief Function to be executed on Radio Rx Done event - */ -static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); - -/*! - * \brief Function executed on Radio Tx Timeout event - */ -static void OnRadioTxTimeout( void ); - -/*! - * \brief Function executed on Radio Rx error event - */ -static void OnRadioRxError( void ); - -/*! - * \brief Function executed on Radio Rx Timeout event - */ -static void OnRadioRxTimeout( void ); - -/*! - * \brief Function executed on Resend Frame timer event. - */ -static void OnMacStateCheckTimerEvent( void ); - -/*! - * \brief Function executed on duty cycle delayed Tx timer event - */ -static void OnTxDelayedTimerEvent( void ); - -/*! - * \brief Function executed on first Rx window timer event - */ -static void OnRxWindow1TimerEvent( void ); - -/*! - * \brief Function executed on second Rx window timer event - */ -static void OnRxWindow2TimerEvent( void ); - -/*! - * \brief Function executed on AckTimeout timer event - */ -static void OnAckTimeoutTimerEvent( void ); - -/*! - * \brief Initializes and opens the reception window - * - * \param [IN] rxContinuous Set to true, if the RX is in continuous mode - * \param [IN] maxRxWindow Maximum RX window timeout - */ -static void RxWindowSetup( bool rxContinuous, uint32_t maxRxWindow ); - -/*! - * \brief Adds a new MAC command to be sent. - * - * \Remark MAC layer internal function - * - * \param [in] cmd MAC command to be added - * [MOTE_MAC_LINK_CHECK_REQ, - * MOTE_MAC_LINK_ADR_ANS, - * MOTE_MAC_DUTY_CYCLE_ANS, - * MOTE_MAC_RX2_PARAM_SET_ANS, - * MOTE_MAC_DEV_STATUS_ANS - * MOTE_MAC_NEW_CHANNEL_ANS] - * \param [in] p1 1st parameter ( optional depends on the command ) - * \param [in] p2 2nd parameter ( optional depends on the command ) - * - * \retval status Function status [0: OK, 1: Unknown command, 2: Buffer full] - */ -static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ); - -/*! - * \brief Parses the MAC commands which must be repeated. - * - * \Remark MAC layer internal function - * - * \param [IN] cmdBufIn Buffer which stores the MAC commands to send - * \param [IN] length Length of the input buffer to parse - * \param [OUT] cmdBufOut Buffer which stores the MAC commands which must be - * repeated. - * - * \retval Size of the MAC commands to repeat. - */ -static uint8_t ParseMacCommandsToRepeat( uint8_t* cmdBufIn, uint8_t length, uint8_t* cmdBufOut ); - -/*! - * \brief Validates if the payload fits into the frame, taking the datarate - * into account. - * - * \details Refer to chapter 4.3.2 of the LoRaWAN specification, v1.0 - * - * \param lenN Length of the application payload. The length depends on the - * datarate and is region specific - * - * \param datarate Current datarate - * - * \param fOptsLen Length of the fOpts field - * - * \retval [false: payload does not fit into the frame, true: payload fits into - * the frame] - */ -static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen ); - -/*! - * \brief Decodes MAC commands in the fOpts field and in the payload - */ -static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, uint8_t snr ); - -/*! - * \brief LoRaMAC layer generic send frame - * - * \param [IN] macHdr MAC header field - * \param [IN] fPort MAC payload port - * \param [IN] fBuffer MAC data buffer to be sent - * \param [IN] fBufferSize MAC data buffer size - * \retval status Status of the operation. - */ -LoRaMacStatus_t Send( LoRaMacHeader_t *macHdr, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ); - -/*! - * \brief LoRaMAC layer frame buffer initialization - * - * \param [IN] macHdr MAC header field - * \param [IN] fCtrl MAC frame control field - * \param [IN] fOpts MAC commands buffer - * \param [IN] fPort MAC payload port - * \param [IN] fBuffer MAC data buffer to be sent - * \param [IN] fBufferSize MAC data buffer size - * \retval status Status of the operation. - */ -LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ); - -/* - * \brief Schedules the frame according to the duty cycle - * - * \retval Status of the operation - */ -static LoRaMacStatus_t ScheduleTx( void ); - -/* - * \brief Calculates the back-off time for the band of a channel. - * - * \param [IN] channel The last Tx channel index - */ -static void CalculateBackOff( uint8_t channel ); - -/*! - * \brief LoRaMAC layer prepared frame buffer transmission with channel specification - * - * \remark PrepareFrame must be called at least once before calling this - * function. - * - * \param [IN] channel Channel to transmit on - * \retval status Status of the operation. - */ -LoRaMacStatus_t SendFrameOnChannel( uint8_t channel ); - -/*! - * \brief Sets the radio in continuous transmission mode - * - * \remark Uses the radio parameters set on the previous transmission. - * - * \param [IN] timeout Time in seconds while the radio is kept in continuous wave mode - * \retval status Status of the operation. - */ -LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout ); - -/*! - * \brief Sets the radio in continuous transmission mode - * - * \remark Uses the radio parameters set on the previous transmission. - * - * \param [IN] timeout Time in seconds while the radio is kept in continuous wave mode - * \param [IN] frequency RF frequency to be set. - * \param [IN] power RF output power to be set. - * \retval status Status of the operation. - */ -LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power ); - -/*! - * \brief Resets MAC specific parameters to default - */ -static void ResetMacParameters( void ); - - -/** - * Prototypes for ISR handlers - */ -static void handle_cad_done(bool cad); -static void handle_tx_done(void); -static void handle_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, - int8_t snr); -static void handle_rx_error(void); -static void handle_rx_timeout(void); -static void handle_tx_timeout(void); -static void handle_fhss_change_channel(uint8_t cur_channel); -static void handle_rx1_timer_event(void); -static void handle_rx2_timer_event(void); -static void handle_ack_timeout(void); -static void handle_delayed_tx_timer_event(void); -static void handle_mac_state_check_timer_event(void); /*************************************************************************** * ISRs - Handlers * **************************************************************************/ -static void handle_tx_done(void) +void LoRaMac::handle_tx_done(void) { - ev_queue->call(OnRadioTxDone); + const int ret = ev_queue->call(this, &LoRaMac::on_radio_tx_done); + MBED_ASSERT(ret != 0); + (void)ret; } -static void handle_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, - int8_t snr) +void LoRaMac::handle_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr) { - ev_queue->call(OnRadioRxDone, payload, size, rssi, snr); + const int ret = ev_queue->call(this, &LoRaMac::on_radio_rx_done, payload, size, rssi, snr); + MBED_ASSERT(ret != 0); + (void)ret; } -static void handle_rx_error(void) +void LoRaMac::handle_rx_error(void) { - ev_queue->call(OnRadioRxError); + const int ret = ev_queue->call(this, &LoRaMac::on_radio_rx_error); + MBED_ASSERT(ret != 0); + (void)ret; } -static void handle_rx_timeout(void) +void LoRaMac::handle_rx_timeout(void) { - ev_queue->call(OnRadioRxTimeout); + const int ret = ev_queue->call(this, &LoRaMac::on_radio_rx_timeout); + MBED_ASSERT(ret != 0); + (void)ret; } -static void handle_tx_timeout(void) +void LoRaMac::handle_tx_timeout(void) { - ev_queue->call(OnRadioTxTimeout); + const int ret = ev_queue->call(this, &LoRaMac::on_radio_tx_timeout); + MBED_ASSERT(ret != 0); + (void)ret; } -static void handle_cad_done(bool cad) +void LoRaMac::handle_cad_done(bool cad) { //TODO Not implemented yet - //ev_queue->call(OnRadioCadDone, cad); + //const int ret = ev_queue->call(this, &LoRaMac::OnRadioCadDone, cad); + //MBED_ASSERT(ret != 0); + //(void)ret; } -static void handle_fhss_change_channel(uint8_t cur_channel) +void LoRaMac::handle_fhss_change_channel(uint8_t cur_channel) { // TODO Not implemented yet - //ev_queue->call(OnRadioFHSSChangeChannel, cur_channel); -} -static void handle_mac_state_check_timer_event(void) -{ - ev_queue->call(OnMacStateCheckTimerEvent); + //const int ret = ev_queue->call(this, &LoRaMac::OnRadioFHSSChangeChannel, cur_channel); + //MBED_ASSERT(ret != 0); + //(void)ret; } -static void handle_delayed_tx_timer_event(void) +void LoRaMac::handle_mac_state_check_timer_event(void) { - ev_queue->call(OnTxDelayedTimerEvent); + const int ret = ev_queue->call(this, &LoRaMac::on_mac_state_check_timer_event); + MBED_ASSERT(ret != 0); + (void)ret; } -static void handle_ack_timeout() +void LoRaMac::handle_delayed_tx_timer_event(void) { - ev_queue->call(OnAckTimeoutTimerEvent); + const int ret = ev_queue->call(this, &LoRaMac::on_tx_delayed_timer_event); + MBED_ASSERT(ret != 0); + (void)ret; } -static void handle_rx1_timer_event(void) +void LoRaMac::handle_ack_timeout() { - ev_queue->call(OnRxWindow1TimerEvent); + const int ret = ev_queue->call(this, &LoRaMac::on_ack_timeout_timer_event); + MBED_ASSERT(ret != 0); + (void)ret; } -static void handle_rx2_timer_event(void) +void LoRaMac::handle_rx1_timer_event(void) { - ev_queue->call(OnRxWindow2TimerEvent); + const int ret = ev_queue->call(this, &LoRaMac::on_rx_window1_timer_event); + MBED_ASSERT(ret != 0); + (void)ret; +} + +void LoRaMac::handle_rx2_timer_event(void) +{ + const int ret = ev_queue->call(this, &LoRaMac::on_rx_window2_timer_event); + MBED_ASSERT(ret != 0); + (void)ret; } /*************************************************************************** * Radio event callbacks - delegated to Radio driver * **************************************************************************/ -static void OnRadioTxDone( void ) +void LoRaMac::on_radio_tx_done( void ) { - GetPhyParams_t getPhy; - PhyParam_t phyParam; - SetBandTxDoneParams_t txDone; - TimerTime_t curTime = TimerGetCurrentTime( ); + 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(); - if( LoRaMacDeviceClass != CLASS_C ) - { + if (_params.dev_class != CLASS_C) { lora_phy->put_radio_to_sleep(); - } - else - { - handle_rx2_timer_event(); + } else { + open_continuous_rx2_window(); } // Setup timers - if( IsRxWindowsEnabled == true ) - { - TimerSetValue( &RxWindowTimer1, RxWindow1Delay ); - TimerStart( &RxWindowTimer1 ); - if( LoRaMacDeviceClass != CLASS_C ) - { - TimerSetValue( &RxWindowTimer2, RxWindow2Delay ); - TimerStart( &RxWindowTimer2 ); - } - if( ( LoRaMacDeviceClass == CLASS_C ) || ( NodeAckRequested == true ) ) - { - getPhy.Attribute = PHY_ACK_TIMEOUT; - phyParam = lora_phy->get_phy_params(&getPhy); - TimerSetValue( &AckTimeoutTimer, RxWindow2Delay + phyParam.Value ); - TimerStart( &AckTimeoutTimer ); - } - } - else - { - McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; - MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; + if(_params.is_rx_window_enabled == true) { + _lora_time.start(_params.timers.rx_window1_timer, _params.rx_window1_delay); - if( LoRaMacFlags.Value == 0 ) - { - LoRaMacFlags.Bits.McpsReq = 1; + if (_params.dev_class != CLASS_C) { + _lora_time.start(_params.timers.rx_window2_timer, _params.rx_window2_delay); } - LoRaMacFlags.Bits.MacDone = 1; + + 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); + } + } else { + mcps.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_OK; + mlme_confirm.status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; + + if (_params.flags.value == 0) { + _params.flags.bits.mcps_req = 1; + } + + _params.flags.bits.mac_done = 1; } // Verify if the last uplink was a join request - if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) ) - { - LastTxIsJoinRequest = true; - } - else - { - LastTxIsJoinRequest = false; + if ((_params.flags.bits.mlme_req == 1) && + (mlme_confirm.req_type == MLME_JOIN)) { + _params.is_last_tx_join_request = true; + } else { + _params.is_last_tx_join_request = false; } // Store last Tx channel - LastTxChannel = Channel; - // Update last tx done time for the current channel - txDone.Channel = Channel; - txDone.Joined = IsLoRaMacNetworkJoined; - txDone.LastTxDoneTime = curTime; - lora_phy->set_band_tx_done(&txDone); - // Update Aggregated last tx done time - AggregatedLastTxDoneTime = curTime; + _params.last_channel_idx = _params.channel; - if( NodeAckRequested == false ) - { - McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; - ChannelsNbRepCounter++; + // Update last tx done time for the current channel + tx_done_params.channel = _params.channel; + tx_done_params.joined = _params.is_nwk_joined; + tx_done_params.last_tx_done_time = cur_time; + lora_phy->set_last_tx_done(&tx_done_params); + + // Update Aggregated last tx done time + _params.timers.aggregated_last_tx_time = cur_time; + + if (_params.is_node_ack_requested == false) { + mcps.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_OK; + _params.ul_nb_rep_counter++; } } -static void PrepareRxDoneAbort( void ) +void LoRaMac::prepare_rx_done_abort(void) { - LoRaMacState |= LORAMAC_RX_ABORT; + _params.mac_state |= LORAMAC_RX_ABORT; - if( NodeAckRequested ) - { + if (_params.is_node_ack_requested) { handle_ack_timeout(); } - LoRaMacFlags.Bits.McpsInd = 1; - LoRaMacFlags.Bits.MacDone = 1; + _params.flags.bits.mcps_ind = 1; + _params.flags.bits.mac_done = 1; - // Trig OnMacCheckTimerEvent call as soon as possible - TimerSetValue( &MacStateCheckTimer, 1 ); - TimerStart( &MacStateCheckTimer ); + // Trigger MAC state check event timer as soon as possible + _lora_time.start(_params.timers.mac_state_check_timer, 1); } -static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) +void LoRaMac::on_radio_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, + int8_t snr) { - LoRaMacHeader_t macHdr; - LoRaMacFrameCtrl_t fCtrl; - ApplyCFListParams_t applyCFList; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - bool skipIndication = false; + 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 pktHeaderLen = 0; + uint8_t pkt_header_len = 0; uint32_t address = 0; - uint8_t appPayloadStartIndex = 0; - uint8_t port = 0xFF; - uint8_t frameLen = 0; + uint8_t app_payload_start_index = 0; + uint8_t frame_len = 0; uint32_t mic = 0; - uint32_t micRx = 0; + uint32_t mic_rx = 0; - uint16_t sequenceCounter = 0; - uint16_t sequenceCounterPrev = 0; - uint16_t sequenceCounterDiff = 0; - uint32_t downLinkCounter = 0; + uint16_t sequence_counter = 0; + uint16_t sequence_counter_prev = 0; + uint16_t sequence_counter_diff = 0; + uint32_t downlink_counter = 0; - MulticastParams_t *curMulticastParams = NULL; - uint8_t *nwkSKey = LoRaMacNwkSKey; - uint8_t *appSKey = LoRaMacAppSKey; + multicast_params_t *cur_multicast_params = NULL; + uint8_t *nwk_skey = _params.keys.nwk_skey; + uint8_t *app_skey = _params.keys.app_skey; uint8_t multicast = 0; - bool isMicOk = false; + bool is_mic_ok = false; - McpsConfirm.AckReceived = false; - McpsIndication.Rssi = rssi; - McpsIndication.Snr = snr; - McpsIndication.RxSlot = RxSlot; - McpsIndication.Port = 0; - McpsIndication.Multicast = 0; - McpsIndication.FramePending = 0; - McpsIndication.Buffer = NULL; - McpsIndication.BufferSize = 0; - McpsIndication.RxData = false; - McpsIndication.AckReceived = false; - McpsIndication.DownLinkCounter = 0; - McpsIndication.McpsIndication = MCPS_UNCONFIRMED; + mcps.get_confirmation().ack_received = false; + mcps.get_indication().rssi = rssi; + mcps.get_indication().snr = snr; + mcps.get_indication().rx_slot = _params.rx_slot; + mcps.get_indication().port = 0; + mcps.get_indication().multicast = 0; + mcps.get_indication().fpending_status = 0; + mcps.get_indication().buffer = NULL; + mcps.get_indication().buffer_size = 0; + mcps.get_indication().is_data_recvd = false; + mcps.get_indication().is_ack_recvd = false; + mcps.get_indication().dl_frame_counter = 0; + mcps.get_indication().type = MCPS_UNCONFIRMED; lora_phy->put_radio_to_sleep(); - TimerStop( &RxWindowTimer2 ); + _lora_time.stop( _params.timers.rx_window2_timer ); - macHdr.Value = payload[pktHeaderLen++]; + mac_hdr.value = payload[pkt_header_len++]; + + switch (mac_hdr.bits.mtype) { - switch( macHdr.Bits.MType ) - { case FRAME_TYPE_JOIN_ACCEPT: - if( IsLoRaMacNetworkJoined == true ) - { - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; - PrepareRxDoneAbort( ); + if (_params.is_nwk_joined == true) { + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_ERROR; + prepare_rx_done_abort(); return; } - if (0 != LoRaMacJoinDecrypt( payload + 1, size - 1, LoRaMacAppKey, LoRaMacRxPayload + 1 )) { - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; + if (0 != decrypt_join_frame(payload + 1, size - 1, + _params.keys.app_key, + _params.payload + 1)) { + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; return; } - LoRaMacRxPayload[0] = macHdr.Value; + _params.payload[0] = mac_hdr.value; - if (0 != LoRaMacJoinComputeMic( LoRaMacRxPayload, size - LORAMAC_MFR_LEN, LoRaMacAppKey, &mic )) { - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; + if (0 != compute_join_frame_mic(_params.payload, size - LORAMAC_MFR_LEN, + _params.keys.app_key, &mic)) { + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; return; } - micRx |= ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN]; - micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 1] << 8 ); - micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 2] << 16 ); - micRx |= ( ( uint32_t )LoRaMacRxPayload[size - LORAMAC_MFR_LEN + 3] << 24 ); + mic_rx |= (uint32_t) _params.payload[size - LORAMAC_MFR_LEN]; + mic_rx |= ((uint32_t) _params.payload[size - LORAMAC_MFR_LEN + 1] << 8); + mic_rx |= ((uint32_t) _params.payload[size - LORAMAC_MFR_LEN + 2] << 16); + mic_rx |= ((uint32_t) _params.payload[size - LORAMAC_MFR_LEN + 3] << 24); - if( micRx == mic ) - { - if (0 != LoRaMacJoinComputeSKeys( LoRaMacAppKey, LoRaMacRxPayload + 1, LoRaMacDevNonce, LoRaMacNwkSKey, LoRaMacAppSKey )) { - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; + if (mic_rx == mic) { + + if (0 != compute_skeys_for_join_frame(_params.keys.app_key, + _params.payload + 1, + _params.dev_nonce, + _params.keys.nwk_skey, + _params.keys.app_skey)) { + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; return; } - LoRaMacNetID = ( uint32_t )LoRaMacRxPayload[4]; - LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[5] << 8 ); - LoRaMacNetID |= ( ( uint32_t )LoRaMacRxPayload[6] << 16 ); + _params.net_id = (uint32_t) _params.payload[4]; + _params.net_id |= ((uint32_t) _params.payload[5] << 8); + _params.net_id |= ((uint32_t) _params.payload[6] << 16); - LoRaMacDevAddr = ( uint32_t )LoRaMacRxPayload[7]; - LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[8] << 8 ); - LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[9] << 16 ); - LoRaMacDevAddr |= ( ( uint32_t )LoRaMacRxPayload[10] << 24 ); + _params.dev_addr = (uint32_t) _params.payload[7]; + _params.dev_addr |= ((uint32_t) _params.payload[8] << 8); + _params.dev_addr |= ((uint32_t) _params.payload[9] << 16); + _params.dev_addr |= ((uint32_t) _params.payload[10] << 24); // DLSettings - LoRaMacParams.Rx1DrOffset = ( LoRaMacRxPayload[11] >> 4 ) & 0x07; - LoRaMacParams.Rx2Channel.Datarate = LoRaMacRxPayload[11] & 0x0F; + _params.sys_params.rx1_dr_offset = (_params.payload[11] >> 4) & 0x07; + _params.sys_params.rx2_channel.datarate = _params.payload[11] & 0x0F; // RxDelay - LoRaMacParams.ReceiveDelay1 = ( LoRaMacRxPayload[12] & 0x0F ); - if( LoRaMacParams.ReceiveDelay1 == 0 ) - { - LoRaMacParams.ReceiveDelay1 = 1; + _params.sys_params.recv_delay1 = (_params.payload[12] & 0x0F); + + if (_params.sys_params.recv_delay1 == 0) { + _params.sys_params.recv_delay1 = 1; } - LoRaMacParams.ReceiveDelay1 *= 1000; - LoRaMacParams.ReceiveDelay2 = LoRaMacParams.ReceiveDelay1 + 1000; + + _params.sys_params.recv_delay1 *= 1000; + _params.sys_params.recv_delay2 = _params.sys_params.recv_delay1 + 1000; // Apply CF list - applyCFList.Payload = &LoRaMacRxPayload[13]; + cflist.payload = &_params.payload[13]; // Size of the regular payload is 12. Plus 1 byte MHDR and 4 bytes MIC - applyCFList.Size = size - 17; + cflist.size = size - 17; - lora_phy->apply_cf_list(&applyCFList); + lora_phy->apply_cf_list(&cflist); - MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; - IsLoRaMacNetworkJoined = true; - LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate; - } - else - { - MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL; + mlme.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_OK; + _params.is_nwk_joined = true; + } else { + mlme.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL; } + break; + case FRAME_TYPE_DATA_CONFIRMED_DOWN: case FRAME_TYPE_DATA_UNCONFIRMED_DOWN: { // Check if the received payload size is valid - getPhy.UplinkDwellTime = LoRaMacParams.DownlinkDwellTime; - getPhy.Datarate = McpsIndication.RxDatarate; - getPhy.Attribute = PHY_MAX_PAYLOAD; + get_phy.datarate = mcps.get_indication().rx_datarate; + get_phy.attribute = PHY_MAX_PAYLOAD; // Get the maximum payload length - if( RepeaterSupport == true ) - { - getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER; + if (_params.is_repeater_supported == true) { + get_phy.attribute = PHY_MAX_PAYLOAD_REPEATER; } - phyParam = lora_phy->get_phy_params(&getPhy); - if( MAX( 0, ( int16_t )( ( int16_t )size - ( int16_t )LORA_MAC_FRMPAYLOAD_OVERHEAD ) ) > phyParam.Value ) - { - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; - PrepareRxDoneAbort( ); + + 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) { + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_ERROR; + prepare_rx_done_abort(); return; } - address = payload[pktHeaderLen++]; - address |= ( (uint32_t)payload[pktHeaderLen++] << 8 ); - address |= ( (uint32_t)payload[pktHeaderLen++] << 16 ); - address |= ( (uint32_t)payload[pktHeaderLen++] << 24 ); + address = payload[pkt_header_len++]; + address |= ((uint32_t)payload[pkt_header_len++] << 8); + address |= ((uint32_t)payload[pkt_header_len++] << 16); + address |= ((uint32_t)payload[pkt_header_len++] << 24); - if( address != LoRaMacDevAddr ) - { - curMulticastParams = MulticastChannels; - while( curMulticastParams != NULL ) - { - if( address == curMulticastParams->Address ) - { + if (address != _params.dev_addr) { + + cur_multicast_params = _params.multicast_channels; + + while (cur_multicast_params != NULL) { + + if (address == cur_multicast_params->address) { multicast = 1; - nwkSKey = curMulticastParams->NwkSKey; - appSKey = curMulticastParams->AppSKey; - downLinkCounter = curMulticastParams->DownLinkCounter; + nwk_skey = cur_multicast_params->nwk_skey; + app_skey = cur_multicast_params->app_skey; + downlink_counter = cur_multicast_params->dl_frame_counter; break; } - curMulticastParams = curMulticastParams->Next; + + cur_multicast_params = cur_multicast_params->next; } - if( multicast == 0 ) - { + + if (multicast == 0) { // We are not the destination of this frame. - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL; - PrepareRxDoneAbort( ); + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL; + prepare_rx_done_abort(); return; } - } - else - { + } else { multicast = 0; - nwkSKey = LoRaMacNwkSKey; - appSKey = LoRaMacAppSKey; - downLinkCounter = DownLinkCounter; + nwk_skey = _params.keys.nwk_skey; + app_skey = _params.keys.app_skey; + downlink_counter = _params.dl_frame_counter; } - fCtrl.Value = payload[pktHeaderLen++]; + fctrl.value = payload[pkt_header_len++]; - sequenceCounter = ( uint16_t )payload[pktHeaderLen++]; - sequenceCounter |= ( uint16_t )payload[pktHeaderLen++] << 8; + sequence_counter = (uint16_t )payload[pkt_header_len++]; + sequence_counter |= (uint16_t)payload[pkt_header_len++] << 8; - appPayloadStartIndex = 8 + fCtrl.Bits.FOptsLen; + app_payload_start_index = 8 + fctrl.bits.fopts_len; - micRx |= ( uint32_t )payload[size - LORAMAC_MFR_LEN]; - micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 1] << 8 ); - micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 2] << 16 ); - micRx |= ( ( uint32_t )payload[size - LORAMAC_MFR_LEN + 3] << 24 ); + mic_rx |= (uint32_t)payload[size - LORAMAC_MFR_LEN]; + mic_rx |= ((uint32_t)payload[size - LORAMAC_MFR_LEN + 1] << 8); + mic_rx |= ((uint32_t)payload[size - LORAMAC_MFR_LEN + 2] << 16); + mic_rx |= ((uint32_t)payload[size - LORAMAC_MFR_LEN + 3] << 24); - sequenceCounterPrev = ( uint16_t )downLinkCounter; - sequenceCounterDiff = ( sequenceCounter - sequenceCounterPrev ); + sequence_counter_prev = (uint16_t)downlink_counter; + sequence_counter_diff = (sequence_counter - sequence_counter_prev); - if( sequenceCounterDiff < ( 1 << 15 ) ) - { - downLinkCounter += sequenceCounterDiff; - LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounter, &mic ); - if( micRx == mic ) - { - isMicOk = true; + if (sequence_counter_diff < (1 << 15)) { + + downlink_counter += sequence_counter_diff; + compute_mic(payload, size - LORAMAC_MFR_LEN, nwk_skey, + address, DOWN_LINK, downlink_counter, &mic); + if (mic_rx == mic) { + is_mic_ok = true; } - } - else - { + + } else { // check for sequence roll-over - uint32_t downLinkCounterTmp = downLinkCounter + 0x10000 + ( int16_t )sequenceCounterDiff; - LoRaMacComputeMic( payload, size - LORAMAC_MFR_LEN, nwkSKey, address, DOWN_LINK, downLinkCounterTmp, &mic ); - if( micRx == mic ) - { - isMicOk = true; - downLinkCounter = downLinkCounterTmp; + uint32_t downlink_counter_tmp = downlink_counter + 0x10000 + (int16_t)sequence_counter_diff; + compute_mic(payload, size - LORAMAC_MFR_LEN, nwk_skey, + address, DOWN_LINK, downlink_counter_tmp, &mic); + + if (mic_rx == mic ) { + is_mic_ok = true; + downlink_counter = downlink_counter_tmp; } } // Check for a the maximum allowed counter difference - getPhy.Attribute = PHY_MAX_FCNT_GAP; - phyParam = lora_phy->get_phy_params( &getPhy ); - if( sequenceCounterDiff >= phyParam.Value ) - { - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS; - McpsIndication.DownLinkCounter = downLinkCounter; - PrepareRxDoneAbort( ); + get_phy.attribute = PHY_MAX_FCNT_GAP; + phy_param = lora_phy->get_phy_params(&get_phy); + + if (sequence_counter_diff >= phy_param.value) { + 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( ); return; } - if( isMicOk == true ) - { - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK; - McpsIndication.Multicast = multicast; - McpsIndication.FramePending = fCtrl.Bits.FPending; - McpsIndication.Buffer = NULL; - McpsIndication.BufferSize = 0; - McpsIndication.DownLinkCounter = downLinkCounter; + if (is_mic_ok == true) { + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_OK; + mcps.get_indication().multicast = multicast; + mcps.get_indication().fpending_status = fctrl.bits.fpending; + mcps.get_indication().buffer = NULL; + mcps.get_indication().buffer_size = 0; + mcps.get_indication().dl_frame_counter = downlink_counter; - McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; + mcps.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_OK; - AdrAckCounter = 0; - MacCommandsBufferToRepeatIndex = 0; + _params.adr_ack_counter = 0; + mac_commands.clear_repeat_buffer(); // Update 32 bits downlink counter - if( multicast == 1 ) - { - McpsIndication.McpsIndication = MCPS_MULTICAST; + if (multicast == 1) { + mcps.get_indication().type = MCPS_MULTICAST; + + if ((cur_multicast_params->dl_frame_counter == downlink_counter) && + (cur_multicast_params->dl_frame_counter != 0)) { + + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; + mcps.get_indication().dl_frame_counter = downlink_counter; + prepare_rx_done_abort(); - if( ( curMulticastParams->DownLinkCounter == downLinkCounter ) && - ( curMulticastParams->DownLinkCounter != 0 ) ) - { - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; - McpsIndication.DownLinkCounter = downLinkCounter; - PrepareRxDoneAbort( ); return; } - curMulticastParams->DownLinkCounter = downLinkCounter; - } - else - { - if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN ) - { - SrvAckRequested = true; - McpsIndication.McpsIndication = MCPS_CONFIRMED; - if( ( DownLinkCounter == downLinkCounter ) && - ( DownLinkCounter != 0 ) ) - { + cur_multicast_params->dl_frame_counter = downlink_counter; + + } else { + + if (mac_hdr.bits.mtype == FRAME_TYPE_DATA_CONFIRMED_DOWN) { + _params.is_srv_ack_requested = true; + mcps.get_indication().type = MCPS_CONFIRMED; + + if ((_params.dl_frame_counter == downlink_counter ) && + (_params.dl_frame_counter != 0)) { // Duplicated confirmed downlink. Skip indication. // In this case, the MAC layer shall accept the MAC commands // which are included in the downlink retransmission. // It should not provide the same frame to the application // layer again. - skipIndication = true; + skip_indication = true; } - } - else - { - SrvAckRequested = false; - McpsIndication.McpsIndication = MCPS_UNCONFIRMED; + } else { + _params.is_srv_ack_requested = false; + mcps.get_indication().type = MCPS_UNCONFIRMED; - if( ( DownLinkCounter == downLinkCounter ) && - ( DownLinkCounter != 0 ) ) - { - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; - McpsIndication.DownLinkCounter = downLinkCounter; - PrepareRxDoneAbort( ); + if ((_params.dl_frame_counter == downlink_counter) && + (_params.dl_frame_counter != 0)) { + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED; + mcps.get_indication().dl_frame_counter = downlink_counter; + prepare_rx_done_abort(); return; } } - DownLinkCounter = downLinkCounter; + _params.dl_frame_counter = downlink_counter; } // This must be done before parsing the payload and the MAC commands. // We need to reset the MacCommandsBufferIndex here, since we need // to take retransmissions and repetitions into account. Error cases // will be handled in function OnMacStateCheckTimerEvent. - if( McpsConfirm.McpsRequest == MCPS_CONFIRMED ) - { - if( fCtrl.Bits.Ack == 1 ) - {// Reset MacCommandsBufferIndex when we have received an ACK. - MacCommandsBufferIndex = 0; + if (mcps.get_confirmation().req_type == MCPS_CONFIRMED) { + if (fctrl.bits.ack == 1) { + // Reset MacCommandsBufferIndex when we have received an ACK. + mac_commands.clear_command_buffer(); } - } - else - {// Reset the variable if we have received any valid frame. - MacCommandsBufferIndex = 0; + } else { + // Reset the variable if we have received any valid frame. + mac_commands.clear_command_buffer(); } // Process payload and MAC commands - if( ( ( size - 4 ) - appPayloadStartIndex ) > 0 ) - { - port = payload[appPayloadStartIndex++]; - frameLen = ( size - 4 ) - appPayloadStartIndex; + if (((size - 4) - app_payload_start_index) > 0) { + uint8_t port = payload[app_payload_start_index++]; + frame_len = (size - 4) - app_payload_start_index; - McpsIndication.Port = port; + mcps.get_indication().port = port; - if( port == 0 ) - { + if (port == 0) { // Only allow frames which do not have fOpts - if( fCtrl.Bits.FOptsLen == 0 ) - { - if (0 != LoRaMacPayloadDecrypt( payload + appPayloadStartIndex, - frameLen, - nwkSKey, - address, - DOWN_LINK, - downLinkCounter, - LoRaMacRxPayload )) { - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; + if (fctrl.bits.fopts_len == 0) { + if (0 != decrypt_payload(payload + app_payload_start_index, + frame_len, + nwk_skey, + address, + DOWN_LINK, + downlink_counter, + _params.payload)) { + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; } // Decode frame payload MAC commands - ProcessMacCommands( LoRaMacRxPayload, 0, frameLen, snr ); + if (mac_commands.process_mac_commands(_params.payload, 0, frame_len, snr, + mlme.get_confirmation(), + _params.sys_params, *lora_phy) != LORAWAN_STATUS_OK) { + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_ERROR; + } + } else { + skip_indication = true; } - else - { - skipIndication = true; - } - } - else - { - if( fCtrl.Bits.FOptsLen > 0 ) - { + } else { + if (fctrl.bits.fopts_len > 0) { // Decode Options field MAC commands. Omit the fPort. - ProcessMacCommands( payload, 8, appPayloadStartIndex - 1, snr ); + 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) { + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_ERROR; + } } - if (0 != LoRaMacPayloadDecrypt( payload + appPayloadStartIndex, - frameLen, - appSKey, - address, - DOWN_LINK, - downLinkCounter, - LoRaMacRxPayload )) { - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; + if (0 != decrypt_payload(payload + app_payload_start_index, + frame_len, + app_skey, + address, + DOWN_LINK, + downlink_counter, + _params.payload)) { + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL; } - if( skipIndication == false ) - { - McpsIndication.Buffer = LoRaMacRxPayload; - McpsIndication.BufferSize = frameLen; - McpsIndication.RxData = true; + if (skip_indication == false) { + mcps.get_indication().buffer = _params.payload; + mcps.get_indication().buffer_size = frame_len; + mcps.get_indication().is_data_recvd = true; } } - } - else - { - if( fCtrl.Bits.FOptsLen > 0 ) - { + } else { + if (fctrl.bits.fopts_len > 0) { // Decode Options field MAC commands - ProcessMacCommands( payload, 8, appPayloadStartIndex, snr ); + if (mac_commands.process_mac_commands(payload, 8, app_payload_start_index, snr, + mlme.get_confirmation(), + _params.sys_params, *lora_phy) != LORAWAN_STATUS_OK) { + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_ERROR; + } } } - if( skipIndication == false ) - { + if (skip_indication == false) { // Check if the frame is an acknowledgement - if( fCtrl.Bits.Ack == 1 ) - { - McpsConfirm.AckReceived = true; - McpsIndication.AckReceived = true; + if (fctrl.bits.ack == 1) { + mcps.get_confirmation().ack_received = true; + mcps.get_indication().is_ack_recvd = true; // Stop the AckTimeout timer as no more retransmissions // are needed. - TimerStop( &AckTimeoutTimer ); - } - else - { - McpsConfirm.AckReceived = false; + _lora_time.stop(_params.timers.ack_timeout_timer); + } else { + mcps.get_confirmation().ack_received = false; - if( AckTimeoutRetriesCounter > AckTimeoutRetries ) - { + if (_params.ack_timeout_retry_counter > _params.max_ack_timeout_retries) { // Stop the AckTimeout timer as no more retransmissions // are needed. - TimerStop( &AckTimeoutTimer ); + _lora_time.stop( _params.timers.ack_timeout_timer ); } } } // Provide always an indication, skip the callback to the user application, // in case of a confirmed downlink retransmission. - LoRaMacFlags.Bits.McpsInd = 1; - LoRaMacFlags.Bits.McpsIndSkip = skipIndication; - } - else - { - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL; + _params.flags.bits.mcps_ind = 1; + _params.flags.bits.mcps_ind_skip = skip_indication; + } else { + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL; - PrepareRxDoneAbort( ); + prepare_rx_done_abort( ); return; } } + break; + case FRAME_TYPE_PROPRIETARY: { - memcpy( LoRaMacRxPayload, &payload[pktHeaderLen], size ); + memcpy(_params.payload, &payload[pkt_header_len], size); - McpsIndication.McpsIndication = MCPS_PROPRIETARY; - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK; - McpsIndication.Buffer = LoRaMacRxPayload; - McpsIndication.BufferSize = size - pktHeaderLen; + mcps.get_indication().type = MCPS_PROPRIETARY; + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_OK; + mcps.get_indication().buffer = _params.payload; + mcps.get_indication().buffer_size = size - pkt_header_len; - LoRaMacFlags.Bits.McpsInd = 1; + _params.flags.bits.mcps_ind = 1; break; } default: - McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; - PrepareRxDoneAbort( ); + mcps.get_indication().status = LORAMAC_EVENT_INFO_STATUS_ERROR; + prepare_rx_done_abort(); break; } - LoRaMacFlags.Bits.MacDone = 1; + _params.flags.bits.mac_done = 1; - // Trig OnMacCheckTimerEvent call as soon as possible - TimerSetValue( &MacStateCheckTimer, 1 ); - TimerStart( &MacStateCheckTimer ); + _lora_time.start(_params.timers.mac_state_check_timer, 1); } -static void OnRadioTxTimeout( void ) +void LoRaMac::on_radio_tx_timeout( void ) { - if( LoRaMacDeviceClass != CLASS_C ) - { + if (_params.dev_class != CLASS_C) { lora_phy->put_radio_to_sleep(); - } - else - { - handle_rx2_timer_event(); + } else { + open_continuous_rx2_window(); } - McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; - MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; - LoRaMacFlags.Bits.MacDone = 1; + mcps.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; + + mlme.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT; + + _params.flags.bits.mac_done = 1; } -static void OnRadioRxError( void ) +void LoRaMac::on_radio_rx_error( void ) { - if( LoRaMacDeviceClass != CLASS_C ) - { + if (_params.dev_class != CLASS_C) { lora_phy->put_radio_to_sleep(); - } - else - { - handle_rx2_timer_event(); + } else { + open_continuous_rx2_window(); } - if( RxSlot == 0 ) - { - if( NodeAckRequested == true ) - { - McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR; + if (_params.rx_slot == RX_SLOT_WIN_1) { + if (_params.is_node_ack_requested == true) { + mcps.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR; } - MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR; - if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay ) - { - LoRaMacFlags.Bits.MacDone = 1; + mlme.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_RX1_ERROR; + + if (_lora_time.get_elapsed_time(_params.timers.aggregated_last_tx_time) >= _params.rx_window2_delay) { + _lora_time.stop(_params.timers.rx_window2_timer); + _params.flags.bits.mac_done = 1; } - } - else - { - if( NodeAckRequested == true ) - { - McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; + + } else { + + if (_params.is_node_ack_requested == true) { + mcps.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; } - MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; - LoRaMacFlags.Bits.MacDone = 1; + + mlme.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; + + _params.flags.bits.mac_done = 1; } } -static void OnRadioRxTimeout( void ) +void LoRaMac::on_radio_rx_timeout(void) { - if( LoRaMacDeviceClass != CLASS_C ) - { + if (_params.dev_class != CLASS_C) { lora_phy->put_radio_to_sleep(); - } - else - { - handle_rx2_timer_event(); + } else { + open_continuous_rx2_window(); } - if( RxSlot == 0 ) - { - if( NodeAckRequested == true ) - { - McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT; + if (_params.rx_slot == RX_SLOT_WIN_1) { + if (_params.is_node_ack_requested == true) { + mcps.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT; } - MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT; + mlme.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT; - if( TimerGetElapsedTime( AggregatedLastTxDoneTime ) >= RxWindow2Delay ) - { - LoRaMacFlags.Bits.MacDone = 1; + if (_lora_time.get_elapsed_time(_params.timers.aggregated_last_tx_time ) >= _params.rx_window2_delay) { + _lora_time.stop(_params.timers.rx_window2_timer); + _params.flags.bits.mac_done = 1; } - } - else - { - if( NodeAckRequested == true ) - { - McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; - } - MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; - if( LoRaMacDeviceClass != CLASS_C ) - { - LoRaMacFlags.Bits.MacDone = 1; + } else { + + if (_params.is_node_ack_requested == true) { + mcps.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; + } + + mlme.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; + + if (_params.dev_class != CLASS_C) { + _params.flags.bits.mac_done = 1; } } } @@ -1322,88 +804,75 @@ static void OnRadioRxTimeout( void ) /*************************************************************************** * Timer event callbacks - deliberated locally * **************************************************************************/ -static void OnMacStateCheckTimerEvent( void ) +void LoRaMac::on_mac_state_check_timer_event(void) { - GetPhyParams_t getPhy; - PhyParam_t phyParam; - bool txTimeout = false; + get_phy_params_t get_phy; + phy_param_t phy_param; + bool tx_timeout = false; - TimerStop( &MacStateCheckTimer ); + _lora_time.stop(_params.timers.mac_state_check_timer); - if( LoRaMacFlags.Bits.MacDone == 1 ) - { - if( ( LoRaMacState & LORAMAC_RX_ABORT ) == LORAMAC_RX_ABORT ) - { - LoRaMacState &= ~LORAMAC_RX_ABORT; - LoRaMacState &= ~LORAMAC_TX_RUNNING; + if (_params.flags.bits.mac_done == 1) { + + if ((_params.mac_state & LORAMAC_RX_ABORT) == LORAMAC_RX_ABORT) { + _params.mac_state &= ~LORAMAC_RX_ABORT; + _params.mac_state &= ~LORAMAC_TX_RUNNING; } - if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) || ( ( LoRaMacFlags.Bits.McpsReq == 1 ) ) ) - { - if( ( McpsConfirm.Status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ) || - ( MlmeConfirm.Status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT ) ) - { + if ((_params.flags.bits.mlme_req == 1) || (_params.flags.bits.mcps_req == 1)) { + + if ((mcps.get_confirmation().status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT) || + ( mlme.get_confirmation().status == LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT)) { // Stop transmit cycle due to tx timeout. - LoRaMacState &= ~LORAMAC_TX_RUNNING; - MacCommandsBufferIndex = 0; - McpsConfirm.NbRetries = AckTimeoutRetriesCounter; - McpsConfirm.AckReceived = false; - McpsConfirm.TxTimeOnAir = 0; - txTimeout = true; + _params.mac_state &= ~LORAMAC_TX_RUNNING; + mac_commands.clear_command_buffer(); + mcps.get_confirmation().nb_retries = _params.ack_timeout_retry_counter; + mcps.get_confirmation().ack_received = false; + mcps.get_confirmation().tx_toa = 0; + tx_timeout = true; } } - if( ( NodeAckRequested == false ) && ( txTimeout == false ) ) - { - if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) || ( ( LoRaMacFlags.Bits.McpsReq == 1 ) ) ) - { - if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) ) - {// Procedure for the join request - MlmeConfirm.NbRetries = JoinRequestTrials; + if ((_params.is_node_ack_requested == false) && (tx_timeout == false)) { + if ((_params.flags.bits.mlme_req == 1) || ((_params.flags.bits.mcps_req == 1))) { + if ((_params.flags.bits.mlme_req == 1) && (mlme.get_confirmation().req_type == MLME_JOIN)) { + // Procedure for the join request + mlme.get_confirmation().nb_retries = _params.join_request_trial_counter; - if( MlmeConfirm.Status == LORAMAC_EVENT_INFO_STATUS_OK ) - {// Node joined successfully - UpLinkCounter = 0; - ChannelsNbRepCounter = 0; - LoRaMacState &= ~LORAMAC_TX_RUNNING; - } - else - { - if( JoinRequestTrials >= MaxJoinRequestTrials ) - { - LoRaMacState &= ~LORAMAC_TX_RUNNING; - } - else - { - LoRaMacFlags.Bits.MacDone = 0; + if (mlme.get_confirmation().status == LORAMAC_EVENT_INFO_STATUS_OK) { + // Node joined successfully + _params.ul_frame_counter = 0; + _params.ul_nb_rep_counter = 0; + _params.mac_state &= ~LORAMAC_TX_RUNNING; + } else { + if (_params.join_request_trial_counter >= _params.max_join_request_trials) { + _params.mac_state &= ~LORAMAC_TX_RUNNING; + } else { + _params.flags.bits.mac_done = 0; // Sends the same frame again handle_delayed_tx_timer_event(); } } - } - else - {// Procedure for all other frames - if( ( ChannelsNbRepCounter >= LoRaMacParams.ChannelsNbRep ) || ( LoRaMacFlags.Bits.McpsInd == 1 ) ) - { - if( LoRaMacFlags.Bits.McpsInd == 0 ) - { // Maximum repetitions without downlink. Reset MacCommandsBufferIndex. Increase ADR Ack counter. + } else { + // Procedure for all other frames + if ((_params.ul_nb_rep_counter >= _params.sys_params.retry_num) || + (_params.flags.bits.mcps_ind == 1)) { + if (_params.flags.bits.mcps_ind == 0) { + // Maximum repetitions without downlink. Reset MacCommandsBufferIndex. Increase ADR Ack counter. // Only process the case when the MAC did not receive a downlink. - MacCommandsBufferIndex = 0; - AdrAckCounter++; + mac_commands.clear_command_buffer(); + _params.adr_ack_counter++; } - ChannelsNbRepCounter = 0; + _params.ul_nb_rep_counter = 0; - if( IsUpLinkCounterFixed == false ) - { - UpLinkCounter++; + if (_params.is_ul_frame_counter_fixed == false) { + _params.ul_frame_counter++; } - LoRaMacState &= ~LORAMAC_TX_RUNNING; - } - else - { - LoRaMacFlags.Bits.MacDone = 0; + _params.mac_state &= ~LORAMAC_TX_RUNNING; + } else { + _params.flags.bits.mac_done = 0; // Sends the same frame again handle_delayed_tx_timer_event(); } @@ -1411,2051 +880,1118 @@ static void OnMacStateCheckTimerEvent( void ) } } - if( LoRaMacFlags.Bits.McpsInd == 1 ) - {// Procedure if we received a frame - if( ( McpsConfirm.AckReceived == true ) || ( AckTimeoutRetriesCounter > AckTimeoutRetries ) ) - { - AckTimeoutRetry = false; - NodeAckRequested = false; - if( IsUpLinkCounterFixed == false ) - { - UpLinkCounter++; + if (_params.flags.bits.mcps_ind == 1) { + // Procedure if we received a frame + if ((mcps.get_confirmation().ack_received == true) || + (_params.ack_timeout_retry_counter > _params.max_ack_timeout_retries)) { + _params.is_ack_retry_timeout_expired = false; + _params.is_node_ack_requested = false; + if (_params.is_ul_frame_counter_fixed == false) { + _params.ul_frame_counter++; } - McpsConfirm.NbRetries = AckTimeoutRetriesCounter; + mcps.get_confirmation().nb_retries = _params.ack_timeout_retry_counter; - LoRaMacState &= ~LORAMAC_TX_RUNNING; + _params.mac_state &= ~LORAMAC_TX_RUNNING; } } - if( ( AckTimeoutRetry == true ) && ( ( LoRaMacState & LORAMAC_TX_DELAYED ) == 0 ) ) - {// Retransmissions procedure for confirmed uplinks - AckTimeoutRetry = false; - if( ( AckTimeoutRetriesCounter < AckTimeoutRetries ) && ( AckTimeoutRetriesCounter <= MAX_ACK_RETRIES ) ) - { - AckTimeoutRetriesCounter++; + if ((_params.is_ack_retry_timeout_expired == true) && + ((_params.mac_state & LORAMAC_TX_DELAYED) == 0)) { - if( ( AckTimeoutRetriesCounter % 2 ) == 1 ) - { - getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; - getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; - getPhy.Datarate = LoRaMacParams.ChannelsDatarate; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParams.ChannelsDatarate = phyParam.Value; + // Retransmissions procedure for confirmed uplinks + _params.is_ack_retry_timeout_expired = false; + if ((_params.ack_timeout_retry_counter < _params.max_ack_timeout_retries) && + (_params.ack_timeout_retry_counter <= MAX_ACK_RETRIES)) { + + _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; } + // Try to send the frame again - if( ScheduleTx( ) == LORAMAC_STATUS_OK ) - { - LoRaMacFlags.Bits.MacDone = 0; - } - else - { + if (schedule_tx() == LORAWAN_STATUS_OK) { + _params.flags.bits.mac_done = 0; + } else { // The DR is not applicable for the payload size - McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR; + mcps.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR; - MacCommandsBufferIndex = 0; - LoRaMacState &= ~LORAMAC_TX_RUNNING; - NodeAckRequested = false; - McpsConfirm.AckReceived = false; - McpsConfirm.NbRetries = AckTimeoutRetriesCounter; - McpsConfirm.Datarate = LoRaMacParams.ChannelsDatarate; - if( IsUpLinkCounterFixed == false ) - { - UpLinkCounter++; + mac_commands.clear_command_buffer(); + _params.mac_state &= ~LORAMAC_TX_RUNNING; + _params.is_node_ack_requested = false; + mcps.get_confirmation().ack_received = false; + mcps.get_confirmation().nb_retries = _params.ack_timeout_retry_counter; + mcps.get_confirmation().data_rate = _params.sys_params.channel_data_rate; + + if (_params.is_ul_frame_counter_fixed == false) { + _params.ul_frame_counter++; } } - } - else - { - lora_phy->load_defaults(INIT_TYPE_RESTORE); + } else { + lora_phy->restore_default_channels(); - LoRaMacState &= ~LORAMAC_TX_RUNNING; + _params.mac_state &= ~LORAMAC_TX_RUNNING; - MacCommandsBufferIndex = 0; - NodeAckRequested = false; - McpsConfirm.AckReceived = false; - McpsConfirm.NbRetries = AckTimeoutRetriesCounter; - if( IsUpLinkCounterFixed == false ) - { - UpLinkCounter++; + mac_commands.clear_command_buffer(); + _params.is_node_ack_requested = false; + mcps.get_confirmation().ack_received = false; + mcps.get_confirmation().nb_retries = _params.ack_timeout_retry_counter; + + if (_params.is_ul_frame_counter_fixed == false) { + _params.ul_frame_counter++; } } } } + // Handle reception for Class B and Class C - if( ( LoRaMacState & LORAMAC_RX ) == LORAMAC_RX ) - { - LoRaMacState &= ~LORAMAC_RX; + if ((_params.mac_state & LORAMAC_RX) == LORAMAC_RX) { + _params.mac_state &= ~LORAMAC_RX; } - if( LoRaMacState == LORAMAC_IDLE ) - { - if( LoRaMacFlags.Bits.McpsReq == 1 ) - { - LoRaMacPrimitives->MacMcpsConfirm( &McpsConfirm ); - LoRaMacFlags.Bits.McpsReq = 0; + + if (_params.mac_state == LORAMAC_IDLE) { + if (_params.flags.bits.mcps_req == 1) { + _params.flags.bits.mcps_req = 0; + mac_primitives->mcps_confirm(&mcps.get_confirmation()); } - if( LoRaMacFlags.Bits.MlmeReq == 1 ) - { - LoRaMacPrimitives->MacMlmeConfirm( &MlmeConfirm ); - LoRaMacFlags.Bits.MlmeReq = 0; + if (_params.flags.bits.mlme_req == 1) { + _params.flags.bits.mlme_req = 0; + mac_primitives->mlme_confirm(&mlme.get_confirmation()); + } + + // Verify if sticky MAC commands are pending or not + if (mac_commands.is_sticky_mac_command_pending() == true) { + // Setup MLME indication + set_mlme_schedule_ul_indication(); } // Procedure done. Reset variables. - LoRaMacFlags.Bits.MacDone = 0; - } - else - { + _params.flags.bits.mac_done = 0; + } else { // Operation not finished restart timer - TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT ); - TimerStart( &MacStateCheckTimer ); + _lora_time.start(_params.timers.mac_state_check_timer, MAC_STATE_CHECK_TIMEOUT); } - if( LoRaMacFlags.Bits.McpsInd == 1 ) - { - if( LoRaMacDeviceClass == CLASS_C ) - {// Activate RX2 window for Class C - handle_rx2_timer_event(); + // Handle MCPS indication + if (_params.flags.bits.mcps_ind == 1) { + _params.flags.bits.mcps_ind = 0; + + if (_params.dev_class== CLASS_C) { + // Activate RX2 window for Class C + open_continuous_rx2_window(); } - if( LoRaMacFlags.Bits.McpsIndSkip == 0 ) - { - LoRaMacPrimitives->MacMcpsIndication( &McpsIndication ); + + if (_params.flags.bits.mcps_ind_skip == 0) { + mac_primitives->mcps_indication(&mcps.get_indication()); } - LoRaMacFlags.Bits.McpsIndSkip = 0; - LoRaMacFlags.Bits.McpsInd = 0; + + _params.flags.bits.mcps_ind_skip = 0; + } + + // Handle MLME indication + if (_params.flags.bits.mlme_ind == 1) { + _params.flags.bits.mlme_ind = 0; + mac_primitives->mlme_indication(&mlme.get_indication()); } } -static void OnTxDelayedTimerEvent( void ) +void LoRaMac::on_tx_delayed_timer_event(void) { - LoRaMacHeader_t macHdr; - LoRaMacFrameCtrl_t fCtrl; - AlternateDrParams_t altDr; + loramac_mhdr_t mac_hdr; + loramac_frame_ctrl_t fctrl; - TimerStop( &TxDelayedTimer ); - LoRaMacState &= ~LORAMAC_TX_DELAYED; + lorawan_status_t status = LORAWAN_STATUS_OK; - if( ( LoRaMacFlags.Bits.MlmeReq == 1 ) && ( MlmeConfirm.MlmeRequest == MLME_JOIN ) ) - { - ResetMacParameters( ); + _lora_time.stop(_params.timers.tx_delayed_timer); + _params.mac_state &= ~LORAMAC_TX_DELAYED; - altDr.NbTrials = JoinRequestTrials + 1; - LoRaMacParams.ChannelsDatarate = lora_phy->get_alternate_DR(&altDr); + if ((_params.flags.bits.mlme_req == 1 ) && + (mlme.get_confirmation().req_type == MLME_JOIN)) { - macHdr.Value = 0; - macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; + reset_mac_parameters(); - fCtrl.Value = 0; - fCtrl.Bits.Adr = AdrCtrlOn; + _params.sys_params.channel_data_rate = lora_phy->get_alternate_DR(_params.join_request_trial_counter + 1); + + mac_hdr.value = 0; + mac_hdr.bits.mtype = FRAME_TYPE_JOIN_REQ; + + fctrl.value = 0; + fctrl.bits.adr = _params.sys_params.adr_on; /* In case of join request retransmissions, the stack must prepare * the frame again, because the network server keeps track of the random * LoRaMacDevNonce values to prevent reply attacks. */ - PrepareFrame( &macHdr, &fCtrl, 0, NULL, 0 ); + status = prepare_frame(&mac_hdr, &fctrl, 0, NULL, 0); } - ScheduleTx( ); + if (status == LORAWAN_STATUS_OK) { + schedule_tx(); + } else { + tr_error("Delayed TX: PrepareFrame returned error %d", status); + } } -static void OnRxWindow1TimerEvent( void ) +void LoRaMac::on_rx_window1_timer_event(void) { - TimerStop( &RxWindowTimer1 ); - RxSlot = 0; + _lora_time.stop(_params.timers.rx_window1_timer); + _params.rx_slot = RX_SLOT_WIN_1; - RxWindow1Config.Channel = Channel; - RxWindow1Config.DrOffset = LoRaMacParams.Rx1DrOffset; - RxWindow1Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; - RxWindow1Config.RepeaterSupport = RepeaterSupport; - RxWindow1Config.RxContinuous = false; - RxWindow1Config.Window = RxSlot; + _params.rx_window1_config.channel = _params.channel; + _params.rx_window1_config.dr_offset = _params.sys_params.rx1_dr_offset; + _params.rx_window1_config.dl_dwell_time = _params.sys_params.downlink_dwell_time; + _params.rx_window1_config.is_repeater_supported = _params.is_repeater_supported; + _params.rx_window1_config.is_rx_continuous = false; + _params.rx_window1_config.rx_slot = _params.rx_slot; - if( LoRaMacDeviceClass == CLASS_C ) - { + if (_params.dev_class == CLASS_C) { lora_phy->put_radio_to_standby(); } - lora_phy->rx_config(&RxWindow1Config, ( int8_t* )&McpsIndication.RxDatarate); - RxWindowSetup( RxWindow1Config.RxContinuous, LoRaMacParams.MaxRxWindow ); + lora_phy->rx_config(&_params.rx_window1_config, + (int8_t*) &mcps.get_indication().rx_datarate); + + rx_window_setup(_params.rx_window1_config.is_rx_continuous, + _params.sys_params.max_rx_win_time); } -static void OnRxWindow2TimerEvent( void ) +void LoRaMac::on_rx_window2_timer_event(void) { - TimerStop( &RxWindowTimer2 ); + _lora_time.stop(_params.timers.rx_window2_timer); - RxWindow2Config.Channel = Channel; - RxWindow2Config.Frequency = LoRaMacParams.Rx2Channel.Frequency; - RxWindow2Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; - RxWindow2Config.RepeaterSupport = RepeaterSupport; - RxWindow2Config.Window = 1; + _params.rx_window2_config.channel = _params.channel; + _params.rx_window2_config.frequency = _params.sys_params.rx2_channel.frequency; + _params.rx_window2_config.dl_dwell_time = _params.sys_params.downlink_dwell_time; + _params.rx_window2_config.is_repeater_supported = _params.is_repeater_supported; + _params.rx_window2_config.rx_slot = RX_SLOT_WIN_2; - if( LoRaMacDeviceClass != CLASS_C ) - { - RxWindow2Config.RxContinuous = false; - } - else - { - RxWindow2Config.RxContinuous = true; + if (_params.dev_class != CLASS_C) { + _params.rx_window2_config.is_rx_continuous = false; + } else { + // Setup continuous listening for class c + _params.rx_window2_config.is_rx_continuous = true; } - if(lora_phy->rx_config(&RxWindow2Config, ( int8_t* )&McpsIndication.RxDatarate) == true ) - { - RxWindowSetup( RxWindow2Config.RxContinuous, LoRaMacParams.MaxRxWindow ); - RxSlot = RxWindow2Config.Window; + if (lora_phy->rx_config(&_params.rx_window2_config, + (int8_t*) &mcps.get_indication().rx_datarate) == true) { + + rx_window_setup(_params.rx_window2_config.is_rx_continuous, + _params.sys_params.max_rx_win_time); + + _params.rx_slot = RX_SLOT_WIN_2; } } -static void OnAckTimeoutTimerEvent( void ) +void LoRaMac::on_ack_timeout_timer_event(void) { - TimerStop( &AckTimeoutTimer ); + _lora_time.stop(_params.timers.ack_timeout_timer); - if( NodeAckRequested == true ) - { - AckTimeoutRetry = true; - LoRaMacState &= ~LORAMAC_ACK_REQ; + if (_params.is_node_ack_requested == true) { + _params.is_ack_retry_timeout_expired = true; + _params.mac_state &= ~LORAMAC_ACK_REQ; } - if( LoRaMacDeviceClass == CLASS_C ) - { - LoRaMacFlags.Bits.MacDone = 1; + if (_params.dev_class == CLASS_C) { + _params.flags.bits.mac_done = 1; } } -static void RxWindowSetup( bool rxContinuous, uint32_t maxRxWindow ) +void LoRaMac::rx_window_setup(bool rx_continuous, uint32_t max_rx_window_time) { - lora_phy->setup_rx_window(rxContinuous, maxRxWindow); + lora_phy->setup_rx_window(rx_continuous, max_rx_window_time); } -static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen ) +bool LoRaMac::validate_payload_length(uint8_t length, int8_t datarate, + uint8_t fopts_len) { - GetPhyParams_t getPhy; - PhyParam_t phyParam; - uint16_t maxN = 0; + get_phy_params_t get_phy; + phy_param_t phy_param; + uint16_t max_value = 0; uint16_t payloadSize = 0; // Setup PHY request - getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; - getPhy.Datarate = datarate; - getPhy.Attribute = PHY_MAX_PAYLOAD; + get_phy.datarate = datarate; + get_phy.attribute = PHY_MAX_PAYLOAD; // Get the maximum payload length - if( RepeaterSupport == true ) - { - getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER; + if (_params.is_repeater_supported == true) { + get_phy.attribute = PHY_MAX_PAYLOAD_REPEATER; } - phyParam = lora_phy->get_phy_params(&getPhy); - maxN = phyParam.Value; + phy_param = lora_phy->get_phy_params(&get_phy); + max_value = phy_param.value; // Calculate the resulting payload size - payloadSize = ( lenN + fOptsLen ); + payloadSize = (length + fopts_len); // Validation of the application payload size - if( ( payloadSize <= maxN ) && ( payloadSize <= LORAMAC_PHY_MAXPAYLOAD ) ) - { + if ((payloadSize <= max_value) && + (payloadSize <= LORAMAC_PHY_MAXPAYLOAD)) { return true; } return false; } -static LoRaMacStatus_t AddMacCommand( uint8_t cmd, uint8_t p1, uint8_t p2 ) +void LoRaMac::set_mlme_schedule_ul_indication(void) { - LoRaMacStatus_t status = LORAMAC_STATUS_BUSY; - // The maximum buffer length must take MAC commands to re-send into account. - uint8_t bufLen = LORA_MAC_COMMAND_MAX_LENGTH - MacCommandsBufferToRepeatIndex; - - switch( cmd ) - { - case MOTE_MAC_LINK_CHECK_REQ: - if( MacCommandsBufferIndex < bufLen ) - { - MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; - // No payload for this command - status = LORAMAC_STATUS_OK; - } - break; - case MOTE_MAC_LINK_ADR_ANS: - if( MacCommandsBufferIndex < ( bufLen - 1 ) ) - { - MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; - // Margin - MacCommandsBuffer[MacCommandsBufferIndex++] = p1; - status = LORAMAC_STATUS_OK; - } - break; - case MOTE_MAC_DUTY_CYCLE_ANS: - if( MacCommandsBufferIndex < bufLen ) - { - MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; - // No payload for this answer - status = LORAMAC_STATUS_OK; - } - break; - case MOTE_MAC_RX_PARAM_SETUP_ANS: - if( MacCommandsBufferIndex < ( bufLen - 1 ) ) - { - MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; - // Status: Datarate ACK, Channel ACK - MacCommandsBuffer[MacCommandsBufferIndex++] = p1; - status = LORAMAC_STATUS_OK; - } - break; - case MOTE_MAC_DEV_STATUS_ANS: - if( MacCommandsBufferIndex < ( bufLen - 2 ) ) - { - MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; - // 1st byte Battery - // 2nd byte Margin - MacCommandsBuffer[MacCommandsBufferIndex++] = p1; - MacCommandsBuffer[MacCommandsBufferIndex++] = p2; - status = LORAMAC_STATUS_OK; - } - break; - case MOTE_MAC_NEW_CHANNEL_ANS: - if( MacCommandsBufferIndex < ( bufLen - 1 ) ) - { - MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; - // Status: Datarate range OK, Channel frequency OK - MacCommandsBuffer[MacCommandsBufferIndex++] = p1; - status = LORAMAC_STATUS_OK; - } - break; - case MOTE_MAC_RX_TIMING_SETUP_ANS: - if( MacCommandsBufferIndex < bufLen ) - { - MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; - // No payload for this answer - status = LORAMAC_STATUS_OK; - } - break; - case MOTE_MAC_TX_PARAM_SETUP_ANS: - if( MacCommandsBufferIndex < bufLen ) - { - MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; - // No payload for this answer - status = LORAMAC_STATUS_OK; - } - break; - case MOTE_MAC_DL_CHANNEL_ANS: - if( MacCommandsBufferIndex < bufLen ) - { - MacCommandsBuffer[MacCommandsBufferIndex++] = cmd; - // Status: Uplink frequency exists, Channel frequency OK - MacCommandsBuffer[MacCommandsBufferIndex++] = p1; - status = LORAMAC_STATUS_OK; - } - break; - default: - return LORAMAC_STATUS_SERVICE_UNKNOWN; - } - if( status == LORAMAC_STATUS_OK ) - { - MacCommandsInNextTx = true; - } - return status; -} - -static uint8_t ParseMacCommandsToRepeat( uint8_t* cmdBufIn, uint8_t length, uint8_t* cmdBufOut ) -{ - uint8_t i = 0; - uint8_t cmdCount = 0; - - if( ( cmdBufIn == NULL ) || ( cmdBufOut == NULL ) ) - { - return 0; - } - - for( i = 0; i < length; i++ ) - { - switch( cmdBufIn[i] ) - { - // STICKY - case MOTE_MAC_DL_CHANNEL_ANS: - case MOTE_MAC_RX_PARAM_SETUP_ANS: - { // 1 byte payload - cmdBufOut[cmdCount++] = cmdBufIn[i++]; - cmdBufOut[cmdCount++] = cmdBufIn[i]; - break; - } - case MOTE_MAC_RX_TIMING_SETUP_ANS: - { // 0 byte payload - cmdBufOut[cmdCount++] = cmdBufIn[i]; - break; - } - // NON-STICKY - case MOTE_MAC_DEV_STATUS_ANS: - { // 2 bytes payload - i += 2; - break; - } - case MOTE_MAC_LINK_ADR_ANS: - case MOTE_MAC_NEW_CHANNEL_ANS: - { // 1 byte payload - i++; - break; - } - case MOTE_MAC_TX_PARAM_SETUP_ANS: - case MOTE_MAC_DUTY_CYCLE_ANS: - case MOTE_MAC_LINK_CHECK_REQ: - { // 0 byte payload - break; - } - default: - break; - } - } - - return cmdCount; -} - -static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, uint8_t snr ) -{ - uint8_t status = 0; - - while( macIndex < commandsSize ) - { - // Decode Frame MAC commands - switch( payload[macIndex++] ) - { - case SRV_MAC_LINK_CHECK_ANS: - MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK; - MlmeConfirm.DemodMargin = payload[macIndex++]; - MlmeConfirm.NbGateways = payload[macIndex++]; - break; - case SRV_MAC_LINK_ADR_REQ: - { - LinkAdrReqParams_t linkAdrReq; - int8_t linkAdrDatarate = DR_0; - int8_t linkAdrTxPower = TX_POWER_0; - uint8_t linkAdrNbRep = 0; - uint8_t linkAdrNbBytesParsed = 0; - - // Fill parameter structure - linkAdrReq.Payload = &payload[macIndex - 1]; - linkAdrReq.PayloadSize = commandsSize - ( macIndex - 1 ); - linkAdrReq.AdrEnabled = AdrCtrlOn; - linkAdrReq.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; - linkAdrReq.CurrentDatarate = LoRaMacParams.ChannelsDatarate; - linkAdrReq.CurrentTxPower = LoRaMacParams.ChannelsTxPower; - linkAdrReq.CurrentNbRep = LoRaMacParams.ChannelsNbRep; - - // Process the ADR requests - status = lora_phy->link_ADR_request(&linkAdrReq, &linkAdrDatarate, - &linkAdrTxPower, &linkAdrNbRep, - &linkAdrNbBytesParsed); - - if( ( status & 0x07 ) == 0x07 ) - { - LoRaMacParams.ChannelsDatarate = linkAdrDatarate; - LoRaMacParams.ChannelsTxPower = linkAdrTxPower; - LoRaMacParams.ChannelsNbRep = linkAdrNbRep; - } - - // Add the answers to the buffer - for( uint8_t i = 0; i < ( linkAdrNbBytesParsed / 5 ); i++ ) - { - AddMacCommand( MOTE_MAC_LINK_ADR_ANS, status, 0 ); - } - // Update MAC index - macIndex += linkAdrNbBytesParsed - 1; - } - break; - case SRV_MAC_DUTY_CYCLE_REQ: - MaxDCycle = payload[macIndex++]; - AggregatedDCycle = 1 << MaxDCycle; - AddMacCommand( MOTE_MAC_DUTY_CYCLE_ANS, 0, 0 ); - break; - case SRV_MAC_RX_PARAM_SETUP_REQ: - { - RxParamSetupReqParams_t rxParamSetupReq; - status = 0x07; - - rxParamSetupReq.DrOffset = ( payload[macIndex] >> 4 ) & 0x07; - rxParamSetupReq.Datarate = payload[macIndex] & 0x0F; - macIndex++; - - rxParamSetupReq.Frequency = ( uint32_t )payload[macIndex++]; - rxParamSetupReq.Frequency |= ( uint32_t )payload[macIndex++] << 8; - rxParamSetupReq.Frequency |= ( uint32_t )payload[macIndex++] << 16; - rxParamSetupReq.Frequency *= 100; - - // Perform request on region - status = lora_phy->setup_rx_params(&rxParamSetupReq); - - if( ( status & 0x07 ) == 0x07 ) - { - LoRaMacParams.Rx2Channel.Datarate = rxParamSetupReq.Datarate; - LoRaMacParams.Rx2Channel.Frequency = rxParamSetupReq.Frequency; - LoRaMacParams.Rx1DrOffset = rxParamSetupReq.DrOffset; - } - AddMacCommand( MOTE_MAC_RX_PARAM_SETUP_ANS, status, 0 ); - } - break; - case SRV_MAC_DEV_STATUS_REQ: - { - uint8_t batteryLevel = BAT_LEVEL_NO_MEASURE; - if( ( LoRaMacCallbacks != NULL ) && ( LoRaMacCallbacks->GetBatteryLevel != NULL ) ) - { - batteryLevel = LoRaMacCallbacks->GetBatteryLevel( ); - } - AddMacCommand( MOTE_MAC_DEV_STATUS_ANS, batteryLevel, snr ); - break; - } - case SRV_MAC_NEW_CHANNEL_REQ: - { - NewChannelReqParams_t newChannelReq; - ChannelParams_t chParam; - status = 0x03; - - newChannelReq.ChannelId = payload[macIndex++]; - newChannelReq.NewChannel = &chParam; - - chParam.Frequency = ( uint32_t )payload[macIndex++]; - chParam.Frequency |= ( uint32_t )payload[macIndex++] << 8; - chParam.Frequency |= ( uint32_t )payload[macIndex++] << 16; - chParam.Frequency *= 100; - chParam.Rx1Frequency = 0; - chParam.DrRange.Value = payload[macIndex++]; - - status = lora_phy->request_new_channel(&newChannelReq); - - AddMacCommand( MOTE_MAC_NEW_CHANNEL_ANS, status, 0 ); - } - break; - case SRV_MAC_RX_TIMING_SETUP_REQ: - { - uint8_t delay = payload[macIndex++] & 0x0F; - - if( delay == 0 ) - { - delay++; - } - LoRaMacParams.ReceiveDelay1 = delay * 1000; - LoRaMacParams.ReceiveDelay2 = LoRaMacParams.ReceiveDelay1 + 1000; - AddMacCommand( MOTE_MAC_RX_TIMING_SETUP_ANS, 0, 0 ); - } - break; - case SRV_MAC_TX_PARAM_SETUP_REQ: - { - TxParamSetupReqParams_t txParamSetupReq; - uint8_t eirpDwellTime = payload[macIndex++]; - - txParamSetupReq.UplinkDwellTime = 0; - txParamSetupReq.DownlinkDwellTime = 0; - - if( ( eirpDwellTime & 0x20 ) == 0x20 ) - { - txParamSetupReq.DownlinkDwellTime = 1; - } - if( ( eirpDwellTime & 0x10 ) == 0x10 ) - { - txParamSetupReq.UplinkDwellTime = 1; - } - txParamSetupReq.MaxEirp = eirpDwellTime & 0x0F; - - // Check the status for correctness - if( lora_phy->setup_tx_params(&txParamSetupReq ) != -1 ) - { - // Accept command - LoRaMacParams.UplinkDwellTime = txParamSetupReq.UplinkDwellTime; - LoRaMacParams.DownlinkDwellTime = txParamSetupReq.DownlinkDwellTime; - LoRaMacParams.MaxEirp = LoRaMacMaxEirpTable[txParamSetupReq.MaxEirp]; - // Add command response - AddMacCommand( MOTE_MAC_TX_PARAM_SETUP_ANS, 0, 0 ); - } - } - break; - case SRV_MAC_DL_CHANNEL_REQ: - { - DlChannelReqParams_t dlChannelReq; - status = 0x03; - - dlChannelReq.ChannelId = payload[macIndex++]; - dlChannelReq.Rx1Frequency = ( uint32_t )payload[macIndex++]; - dlChannelReq.Rx1Frequency |= ( uint32_t )payload[macIndex++] << 8; - dlChannelReq.Rx1Frequency |= ( uint32_t )payload[macIndex++] << 16; - dlChannelReq.Rx1Frequency *= 100; - - status = lora_phy->dl_channel_request(&dlChannelReq); - - AddMacCommand( MOTE_MAC_DL_CHANNEL_ANS, status, 0 ); - } - break; - default: - // Unknown command. ABORT MAC commands processing - return; - } - } + mlme.get_indication().indication_type = MLME_SCHEDULE_UPLINK; + _params.flags.bits.mlme_ind = 1; } // This is not actual transmission. It just schedules a message in response // to MCPS request -LoRaMacStatus_t Send( LoRaMacHeader_t *macHdr, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) +lorawan_status_t LoRaMac::send(loramac_mhdr_t *machdr, uint8_t fport, + void *fbuffer, uint16_t fbuffer_size) { - LoRaMacFrameCtrl_t fCtrl; - LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID; + loramac_frame_ctrl_t fctrl; - fCtrl.Value = 0; - fCtrl.Bits.FOptsLen = 0; - fCtrl.Bits.FPending = 0; - fCtrl.Bits.Ack = false; - fCtrl.Bits.AdrAckReq = false; - fCtrl.Bits.Adr = AdrCtrlOn; + fctrl.value = 0; + fctrl.bits.fopts_len = 0; + fctrl.bits.fpending = 0; + fctrl.bits.ack = false; + fctrl.bits.adr_ack_req = false; + fctrl.bits.adr = _params.sys_params.adr_on; // Prepare the frame - status = PrepareFrame( macHdr, &fCtrl, fPort, fBuffer, fBufferSize ); + lorawan_status_t status = prepare_frame(machdr, &fctrl, fport, fbuffer, + fbuffer_size); // Validate status - if( status != LORAMAC_STATUS_OK ) - { + if (status != LORAWAN_STATUS_OK) { return status; } // Reset confirm parameters - McpsConfirm.NbRetries = 0; - McpsConfirm.AckReceived = false; - McpsConfirm.UpLinkCounter = UpLinkCounter; + mcps.get_confirmation().nb_retries = 0; + mcps.get_confirmation().ack_received = false; + mcps.get_confirmation().ul_frame_counter = _params.ul_frame_counter; - status = ScheduleTx( ); + status = schedule_tx(); return status; } -static LoRaMacStatus_t ScheduleTx( void ) +lorawan_status_t LoRaMac::schedule_tx(void) { - TimerTime_t dutyCycleTimeOff = 0; - NextChanParams_t nextChan; + 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( MaxDCycle == 255 ) - { - return LORAMAC_STATUS_DEVICE_OFF; + if (_params.sys_params.max_duty_cycle == 255) { + return LORAWAN_STATUS_DEVICE_OFF; } - if( MaxDCycle == 0 ) - { - AggregatedTimeOff = 0; + + if (_params.sys_params.max_duty_cycle == 0) { + _params.timers.aggregated_timeoff = 0; } // Update Backoff - CalculateBackOff( LastTxChannel ); + calculate_backOff(_params.last_channel_idx); - nextChan.AggrTimeOff = AggregatedTimeOff; - nextChan.Datarate = LoRaMacParams.ChannelsDatarate; - DutyCycleOn = LORAWAN_DUTYCYCLE_ON; - nextChan.DutyCycleEnabled = DutyCycleOn; - nextChan.Joined = IsLoRaMacNetworkJoined; - nextChan.LastAggrTx = AggregatedLastTxDoneTime; + nextChan.aggregate_timeoff = _params.timers.aggregated_timeoff; + nextChan.current_datarate = _params.sys_params.channel_data_rate; + _params.is_dutycycle_on = MBED_CONF_LORA_DUTY_CYCLE_ON; + nextChan.dc_enabled = _params.is_dutycycle_on; + nextChan.joined = _params.is_nwk_joined; + nextChan.last_aggregate_tx_time = _params.timers.aggregated_last_tx_time; // Select channel - while( lora_phy->set_next_channel(&nextChan, &Channel, &dutyCycleTimeOff, &AggregatedTimeOff ) == false ) - { + while (lora_phy->set_next_channel(&nextChan, &_params.channel, + &dutyCycleTimeOff, + &_params.timers.aggregated_timeoff) == false) { // Set the default datarate - LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate; + getPhy.attribute = PHY_DEF_TX_DR; + phyParam = lora_phy->get_phy_params(&getPhy); + _params.sys_params.channel_data_rate = phyParam.value; // Update datarate in the function parameters - nextChan.Datarate = LoRaMacParams.ChannelsDatarate; + nextChan.current_datarate = _params.sys_params.channel_data_rate; } - tr_debug("Next Channel Idx=%d, DR=%d", Channel, nextChan.Datarate); + tr_debug("Next Channel Idx=%d, DR=%d", _params.channel, nextChan.current_datarate); // Compute Rx1 windows parameters - uint8_t dr_offset = lora_phy->apply_DR_offset(LoRaMacParams.DownlinkDwellTime, - LoRaMacParams.ChannelsDatarate, - LoRaMacParams.Rx1DrOffset); + uint8_t dr_offset = lora_phy->apply_DR_offset(_params.sys_params.channel_data_rate, + _params.sys_params.rx1_dr_offset); + + lora_phy->compute_rx_win_params(dr_offset, _params.sys_params.min_rx_symb, + _params.sys_params.max_sys_rx_error, + &_params.rx_window1_config); - lora_phy->compute_rx_win_params(dr_offset, LoRaMacParams.MinRxSymbols, - LoRaMacParams.SystemMaxRxError, - &RxWindow1Config ); // Compute Rx2 windows parameters - lora_phy->compute_rx_win_params(LoRaMacParams.Rx2Channel.Datarate, - LoRaMacParams.MinRxSymbols, - LoRaMacParams.SystemMaxRxError, - &RxWindow2Config ); + lora_phy->compute_rx_win_params(_params.sys_params.rx2_channel.datarate, + _params.sys_params.min_rx_symb, + _params.sys_params.max_sys_rx_error, + &_params.rx_window2_config); - if( IsLoRaMacNetworkJoined == false ) - { - RxWindow1Delay = LoRaMacParams.JoinAcceptDelay1 + RxWindow1Config.WindowOffset; - RxWindow2Delay = LoRaMacParams.JoinAcceptDelay2 + RxWindow2Config.WindowOffset; - } - else - { - if( ValidatePayloadLength( LoRaMacTxPayloadLen, LoRaMacParams.ChannelsDatarate, MacCommandsBufferIndex ) == false ) - { - return LORAMAC_STATUS_LENGTH_ERROR; + if (_params.is_nwk_joined == false) { + _params.rx_window1_delay = _params.sys_params.join_accept_delay1 + + _params.rx_window1_config.window_offset; + _params.rx_window2_delay = _params.sys_params.join_accept_delay2 + + _params.rx_window2_config.window_offset; + } else { + if (validate_payload_length(_params.payload_length, + _params.sys_params.channel_data_rate, + mac_commands.get_mac_cmd_length()) == false) { + return LORAWAN_STATUS_LENGTH_ERROR; } - RxWindow1Delay = LoRaMacParams.ReceiveDelay1 + RxWindow1Config.WindowOffset; - RxWindow2Delay = LoRaMacParams.ReceiveDelay2 + RxWindow2Config.WindowOffset; + _params.rx_window1_delay = _params.sys_params.recv_delay1 + + _params.rx_window1_config.window_offset; + _params.rx_window2_delay = _params.sys_params.recv_delay2 + + _params.rx_window2_config.window_offset; } // Schedule transmission of frame - if( dutyCycleTimeOff == 0 ) - { + if (dutyCycleTimeOff == 0) { // Try to send now - return SendFrameOnChannel( Channel ); - } - else - { + return send_frame_on_channel(_params.channel); + } else { // Send later - prepare timer - LoRaMacState |= LORAMAC_TX_DELAYED; + _params.mac_state |= LORAMAC_TX_DELAYED; tr_debug("Next Transmission in %lu ms", dutyCycleTimeOff); - TimerSetValue( &TxDelayedTimer, dutyCycleTimeOff ); - TimerStart( &TxDelayedTimer ); + _lora_time.start(_params.timers.tx_delayed_timer, dutyCycleTimeOff); - return LORAMAC_STATUS_OK; + return LORAWAN_STATUS_OK; } } -static void CalculateBackOff( uint8_t channel ) +void LoRaMac::calculate_backOff(uint8_t channel) { - CalcBackOffParams_t calcBackOff; + backoff_params_t backoff_params; - calcBackOff.Joined = IsLoRaMacNetworkJoined; - DutyCycleOn = LORAWAN_DUTYCYCLE_ON; - calcBackOff.DutyCycleEnabled = DutyCycleOn; - calcBackOff.Channel = channel; - calcBackOff.ElapsedTime = TimerGetElapsedTime( LoRaMacInitializationTime ); - calcBackOff.TxTimeOnAir = TxTimeOnAir; - calcBackOff.LastTxIsJoinRequest = LastTxIsJoinRequest; + backoff_params.joined = _params.is_nwk_joined; + _params.is_dutycycle_on = MBED_CONF_LORA_DUTY_CYCLE_ON; + backoff_params.dc_enabled = _params.is_dutycycle_on; + backoff_params.channel = channel; + backoff_params.elapsed_time = _lora_time.get_elapsed_time(_params.timers.mac_init_time); + backoff_params.tx_toa = _params.timers.tx_toa; + backoff_params.last_tx_was_join_req = _params.is_last_tx_join_request; // Update regional back-off - lora_phy->calculate_backoff(&calcBackOff); + lora_phy->calculate_backoff(&backoff_params); // Update aggregated time-off - AggregatedTimeOff = AggregatedTimeOff + ( TxTimeOnAir * AggregatedDCycle - TxTimeOnAir ); + _params.timers.aggregated_timeoff = _params.timers.aggregated_timeoff + + (_params.timers.tx_toa * _params.sys_params.aggregated_duty_cycle + - _params.timers.tx_toa); } -static void ResetMacParameters( void ) +void LoRaMac::reset_mac_parameters(void) { - IsLoRaMacNetworkJoined = false; + get_phy_params_t get_phy; + phy_param_t phy_param; + + _params.is_nwk_joined = false; // Counters - UpLinkCounter = 0; - DownLinkCounter = 0; - AdrAckCounter = 0; + _params.ul_frame_counter = 0; + _params.dl_frame_counter = 0; + _params.adr_ack_counter = 0; - ChannelsNbRepCounter = 0; + _params.ul_nb_rep_counter = 0; - AckTimeoutRetries = 1; - AckTimeoutRetriesCounter = 1; - AckTimeoutRetry = false; + _params.max_ack_timeout_retries = 1; + _params.ack_timeout_retry_counter = 1; + _params.is_ack_retry_timeout_expired = false; - MaxDCycle = 0; - AggregatedDCycle = 1; + _params.sys_params.max_duty_cycle = 0; + _params.sys_params.aggregated_duty_cycle = 1; - MacCommandsBufferIndex = 0; - MacCommandsBufferToRepeatIndex = 0; + mac_commands.clear_command_buffer(); + mac_commands.clear_repeat_buffer(); + mac_commands.clear_mac_commands_in_next_tx(); - IsRxWindowsEnabled = true; + _params.is_rx_window_enabled = true; - LoRaMacParams.ChannelsTxPower = LoRaMacParamsDefaults.ChannelsTxPower; - LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate; - LoRaMacParams.Rx1DrOffset = LoRaMacParamsDefaults.Rx1DrOffset; - LoRaMacParams.Rx2Channel = LoRaMacParamsDefaults.Rx2Channel; - LoRaMacParams.UplinkDwellTime = LoRaMacParamsDefaults.UplinkDwellTime; - LoRaMacParams.DownlinkDwellTime = LoRaMacParamsDefaults.DownlinkDwellTime; - LoRaMacParams.MaxEirp = LoRaMacParamsDefaults.MaxEirp; - LoRaMacParams.AntennaGain = LoRaMacParamsDefaults.AntennaGain; + get_phy.attribute = PHY_DEF_TX_POWER; + phy_param = lora_phy->get_phy_params(&get_phy); - NodeAckRequested = false; - SrvAckRequested = false; - MacCommandsInNextTx = false; + _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; + + _params.is_node_ack_requested = false; + _params.is_srv_ack_requested = false; // Reset Multicast downlink counters - MulticastParams_t *cur = MulticastChannels; - while( cur != NULL ) - { - cur->DownLinkCounter = 0; - cur = cur->Next; + multicast_params_t *cur = _params.multicast_channels; + while (cur != NULL) { + cur->dl_frame_counter = 0; + cur = cur->next; } // Initialize channel index. - Channel = 0; - LastTxChannel = Channel; + _params.channel = 0; + _params.last_channel_idx = _params.channel; } -void memcpy_convert_endianess( uint8_t *dst, const uint8_t *src, uint16_t size ) +bool LoRaMac::is_fPort_allowed (uint8_t fPort) { - dst = dst + ( size - 1 ); - while( size-- ) - { + if ((fPort == 0) || (fPort > 224)) { + return false; + } + return true; +} + +void LoRaMac::open_continuous_rx2_window (void) +{ + handle_rx2_timer_event(); + _params.rx_slot = RX_SLOT_WIN_CLASS_C; +} + +static void memcpy_convert_endianess(uint8_t *dst, const uint8_t *src, + uint16_t size) +{ + dst = dst + (size - 1); + while (size--) { *dst-- = *src++; } } -LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t *macHdr, LoRaMacFrameCtrl_t *fCtrl, uint8_t fPort, void *fBuffer, uint16_t fBufferSize ) +lorawan_status_t LoRaMac::prepare_frame(loramac_mhdr_t *machdr, + loramac_frame_ctrl_t *fctrl, + uint8_t fport, void *fbuffer, + uint16_t fbuffer_size) { - AdrNextParams_t adrNext; uint16_t i; - uint8_t pktHeaderLen = 0; + uint8_t pkt_header_len = 0; uint32_t mic = 0; - const void* payload = fBuffer; - uint8_t framePort = fPort; - LoRaMacStatus_t status = LORAMAC_STATUS_OK; + const void* payload = fbuffer; + uint8_t frame_port = fport; + lorawan_status_t status = LORAWAN_STATUS_OK; - LoRaMacBufferPktLen = 0; + _params.buffer_pkt_len = 0; - NodeAckRequested = false; + _params.is_node_ack_requested = false; - if( fBuffer == NULL ) - { - fBufferSize = 0; + if (fbuffer == NULL) { + fbuffer_size = 0; } - LoRaMacTxPayloadLen = fBufferSize; + _params.payload_length = fbuffer_size; - LoRaMacBuffer[pktHeaderLen++] = macHdr->Value; + _params.buffer[pkt_header_len++] = machdr->value; + + switch (machdr->bits.mtype) { - switch( macHdr->Bits.MType ) - { case FRAME_TYPE_JOIN_REQ: - LoRaMacBufferPktLen = pktHeaderLen; - memcpy_convert_endianess( LoRaMacBuffer + LoRaMacBufferPktLen, LoRaMacAppEui, 8 ); - LoRaMacBufferPktLen += 8; - memcpy_convert_endianess( LoRaMacBuffer + LoRaMacBufferPktLen, LoRaMacDevEui, 8 ); - LoRaMacBufferPktLen += 8; + _params.buffer_pkt_len = pkt_header_len; + memcpy_convert_endianess(_params.buffer + _params.buffer_pkt_len, + _params.keys.app_eui, 8); + _params.buffer_pkt_len += 8; + memcpy_convert_endianess(_params.buffer + _params.buffer_pkt_len, + _params.keys.dev_eui, 8); + _params.buffer_pkt_len += 8; - LoRaMacDevNonce = lora_phy->get_radio_rng(); + _params.dev_nonce = lora_phy->get_radio_rng(); - LoRaMacBuffer[LoRaMacBufferPktLen++] = LoRaMacDevNonce & 0xFF; - LoRaMacBuffer[LoRaMacBufferPktLen++] = ( LoRaMacDevNonce >> 8 ) & 0xFF; + _params.buffer[_params.buffer_pkt_len++] = _params.dev_nonce & 0xFF; + _params.buffer[_params.buffer_pkt_len++] = (_params.dev_nonce >> 8) & 0xFF; - if (0 != LoRaMacJoinComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen & 0xFF, LoRaMacAppKey, &mic )) { - return LORAMAC_STATUS_CRYPTO_FAIL; + if (0 != compute_join_frame_mic(_params.buffer, + _params.buffer_pkt_len & 0xFF, + _params.keys.app_key, &mic)) { + return LORAWAN_STATUS_CRYPTO_FAIL; } - LoRaMacBuffer[LoRaMacBufferPktLen++] = mic & 0xFF; - LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 8 ) & 0xFF; - LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 16 ) & 0xFF; - LoRaMacBuffer[LoRaMacBufferPktLen++] = ( mic >> 24 ) & 0xFF; + _params.buffer[_params.buffer_pkt_len++] = mic & 0xFF; + _params.buffer[_params.buffer_pkt_len++] = (mic >> 8) & 0xFF; + _params.buffer[_params.buffer_pkt_len++] = (mic >> 16) & 0xFF; + _params.buffer[_params.buffer_pkt_len++] = (mic >> 24) & 0xFF; break; case FRAME_TYPE_DATA_CONFIRMED_UP: - NodeAckRequested = true; + _params.is_node_ack_requested = true; //Intentional fallthrough - case FRAME_TYPE_DATA_UNCONFIRMED_UP: - if( IsLoRaMacNetworkJoined == false ) - { - return LORAMAC_STATUS_NO_NETWORK_JOINED; // No network has been joined yet + case FRAME_TYPE_DATA_UNCONFIRMED_UP: { + if (_params.is_nwk_joined == false) { + // No network has been joined yet + return LORAWAN_STATUS_NO_NETWORK_JOINED; } - // Adr next request - adrNext.UpdateChanMask = true; - adrNext.AdrEnabled = fCtrl->Bits.Adr; - adrNext.AdrAckCounter = AdrAckCounter; - adrNext.Datarate = LoRaMacParams.ChannelsDatarate; - adrNext.TxPower = LoRaMacParams.ChannelsTxPower; - adrNext.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; - - fCtrl->Bits.AdrAckReq = lora_phy->get_next_ADR(&adrNext, - &LoRaMacParams.ChannelsDatarate, - &LoRaMacParams.ChannelsTxPower, - &AdrAckCounter); - - if( SrvAckRequested == true ) - { - SrvAckRequested = false; - fCtrl->Bits.Ack = 1; + if (_params.sys_params.adr_on) { + if (lora_phy->get_next_ADR(true, + _params.sys_params.channel_data_rate, + _params.sys_params.channel_tx_power, + _params.adr_ack_counter)) { + fctrl->bits.adr_ack_req = 1; + } } - LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr ) & 0xFF; - LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 8 ) & 0xFF; - LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 16 ) & 0xFF; - LoRaMacBuffer[pktHeaderLen++] = ( LoRaMacDevAddr >> 24 ) & 0xFF; + if (_params.is_srv_ack_requested == true) { + _params.is_srv_ack_requested = false; + fctrl->bits.ack = 1; + } - LoRaMacBuffer[pktHeaderLen++] = fCtrl->Value; + _params.buffer[pkt_header_len++] = (_params.dev_addr) & 0xFF; + _params.buffer[pkt_header_len++] = (_params.dev_addr >> 8) & 0xFF; + _params.buffer[pkt_header_len++] = (_params.dev_addr >> 16) & 0xFF; + _params.buffer[pkt_header_len++] = (_params.dev_addr >> 24) & 0xFF; - LoRaMacBuffer[pktHeaderLen++] = UpLinkCounter & 0xFF; - LoRaMacBuffer[pktHeaderLen++] = ( UpLinkCounter >> 8 ) & 0xFF; + _params.buffer[pkt_header_len++] = fctrl->value; + + _params.buffer[pkt_header_len++] = _params.ul_frame_counter & 0xFF; + _params.buffer[pkt_header_len++] = (_params.ul_frame_counter >> 8) + & 0xFF; // Copy the MAC commands which must be re-send into the MAC command buffer - memcpy( &MacCommandsBuffer[MacCommandsBufferIndex], MacCommandsBufferToRepeat, MacCommandsBufferToRepeatIndex ); - MacCommandsBufferIndex += MacCommandsBufferToRepeatIndex; + mac_commands.copy_repeat_commands_to_buffer(); - if( ( payload != NULL ) && ( LoRaMacTxPayloadLen > 0 ) ) - { - if( MacCommandsInNextTx == true ) - { - if( MacCommandsBufferIndex <= LORA_MAC_COMMAND_MAX_FOPTS_LENGTH ) - { - fCtrl->Bits.FOptsLen += MacCommandsBufferIndex; + const uint8_t mac_commands_len = mac_commands.get_mac_cmd_length(); + + if ((payload != NULL) && (_params.payload_length > 0)) { + if (mac_commands.is_mac_command_in_next_tx() == true) { + if (mac_commands_len <= LORA_MAC_COMMAND_MAX_FOPTS_LENGTH) { + fctrl->bits.fopts_len += mac_commands_len; // Update FCtrl field with new value of OptionsLength - LoRaMacBuffer[0x05] = fCtrl->Value; - for( i = 0; i < MacCommandsBufferIndex; i++ ) - { - LoRaMacBuffer[pktHeaderLen++] = MacCommandsBuffer[i]; + _params.buffer[0x05] = fctrl->value; + + const uint8_t *buffer = + mac_commands.get_mac_commands_buffer(); + for (i = 0; i < mac_commands_len; i++) { + _params.buffer[pkt_header_len++] = buffer[i]; } - } - else - { - LoRaMacTxPayloadLen = MacCommandsBufferIndex; - payload = MacCommandsBuffer; - framePort = 0; + } else { + _params.payload_length = mac_commands_len; + payload = mac_commands.get_mac_commands_buffer(); + frame_port = 0; } } - } - else - { - if( ( MacCommandsBufferIndex > 0 ) && ( MacCommandsInNextTx == true ) ) - { - LoRaMacTxPayloadLen = MacCommandsBufferIndex; - payload = MacCommandsBuffer; - framePort = 0; + } else { + if ((mac_commands_len > 0) + && (mac_commands.is_mac_command_in_next_tx() == true)) { + _params.payload_length = mac_commands_len; + payload = mac_commands.get_mac_commands_buffer(); + frame_port = 0; } } - MacCommandsInNextTx = false; + // Store MAC commands which must be re-send in case the device does not receive a downlink anymore - MacCommandsBufferToRepeatIndex = ParseMacCommandsToRepeat( MacCommandsBuffer, MacCommandsBufferIndex, MacCommandsBufferToRepeat ); - if( MacCommandsBufferToRepeatIndex > 0 ) - { - MacCommandsInNextTx = true; - } + mac_commands.parse_mac_commands_to_repeat(); - if( ( payload != NULL ) && ( LoRaMacTxPayloadLen > 0 ) ) - { - LoRaMacBuffer[pktHeaderLen++] = framePort; + if ((payload != NULL) && (_params.payload_length > 0)) { + _params.buffer[pkt_header_len++] = frame_port; - if( framePort == 0 ) - { + if (frame_port == 0) { // Reset buffer index as the mac commands are being sent on port 0 - MacCommandsBufferIndex = 0; - if (0 != LoRaMacPayloadEncrypt( (uint8_t* ) payload, LoRaMacTxPayloadLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &LoRaMacBuffer[pktHeaderLen] )) { - status = LORAMAC_STATUS_CRYPTO_FAIL; + mac_commands.clear_command_buffer(); + if (0 != encrypt_payload((uint8_t*) payload, _params.payload_length, + _params.keys.nwk_skey, _params.dev_addr, + UP_LINK, + _params.ul_frame_counter, + &_params.buffer[pkt_header_len])) { + status = LORAWAN_STATUS_CRYPTO_FAIL; } - - } - else - { - if (0 != LoRaMacPayloadEncrypt( (uint8_t* ) payload, LoRaMacTxPayloadLen, LoRaMacAppSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &LoRaMacBuffer[pktHeaderLen] )) { - status = LORAMAC_STATUS_CRYPTO_FAIL; + } else { + if (0 != encrypt_payload((uint8_t*) payload, _params.payload_length, + _params.keys.app_skey, _params.dev_addr, + UP_LINK, + _params.ul_frame_counter, + &_params.buffer[pkt_header_len])) { + status = LORAWAN_STATUS_CRYPTO_FAIL; } } } - LoRaMacBufferPktLen = pktHeaderLen + LoRaMacTxPayloadLen; - if (0 != LoRaMacComputeMic( LoRaMacBuffer, LoRaMacBufferPktLen, LoRaMacNwkSKey, LoRaMacDevAddr, UP_LINK, UpLinkCounter, &mic )) { - status = LORAMAC_STATUS_CRYPTO_FAIL; + _params.buffer_pkt_len = pkt_header_len + _params.payload_length; + + if (0 != compute_mic(_params.buffer, _params.buffer_pkt_len, + _params.keys.nwk_skey, + _params.dev_addr, + UP_LINK, + _params.ul_frame_counter, &mic)) { + status = LORAWAN_STATUS_CRYPTO_FAIL; } - LoRaMacBuffer[LoRaMacBufferPktLen + 0] = mic & 0xFF; - LoRaMacBuffer[LoRaMacBufferPktLen + 1] = ( mic >> 8 ) & 0xFF; - LoRaMacBuffer[LoRaMacBufferPktLen + 2] = ( mic >> 16 ) & 0xFF; - LoRaMacBuffer[LoRaMacBufferPktLen + 3] = ( mic >> 24 ) & 0xFF; - - LoRaMacBufferPktLen += LORAMAC_MFR_LEN; + _params.buffer[_params.buffer_pkt_len + 0] = mic & 0xFF; + _params.buffer[_params.buffer_pkt_len + 1] = (mic >> 8) & 0xFF; + _params.buffer[_params.buffer_pkt_len + 2] = (mic >> 16) & 0xFF; + _params.buffer[_params.buffer_pkt_len + 3] = (mic >> 24) & 0xFF; + _params.buffer_pkt_len += LORAMAC_MFR_LEN; + } break; case FRAME_TYPE_PROPRIETARY: - if( ( fBuffer != NULL ) && ( LoRaMacTxPayloadLen > 0 ) ) - { - memcpy( LoRaMacBuffer + pktHeaderLen, ( uint8_t* ) fBuffer, LoRaMacTxPayloadLen ); - LoRaMacBufferPktLen = pktHeaderLen + LoRaMacTxPayloadLen; + if ((fbuffer != NULL) && (_params.payload_length > 0)) { + memcpy(_params.buffer + pkt_header_len, (uint8_t*) fbuffer, + _params.payload_length); + _params.buffer_pkt_len = pkt_header_len + _params.payload_length; } break; default: - status = LORAMAC_STATUS_SERVICE_UNKNOWN; + status = LORAWAN_STATUS_SERVICE_UNKNOWN; } return status; } -LoRaMacStatus_t SendFrameOnChannel( uint8_t channel ) +lorawan_status_t LoRaMac::send_frame_on_channel(uint8_t channel) { - TxConfigParams_t txConfig; - int8_t txPower = 0; + tx_config_params_t tx_config; + int8_t tx_power = 0; - txConfig.Channel = channel; - txConfig.Datarate = LoRaMacParams.ChannelsDatarate; - txConfig.TxPower = LoRaMacParams.ChannelsTxPower; - txConfig.MaxEirp = LoRaMacParams.MaxEirp; - txConfig.AntennaGain = LoRaMacParams.AntennaGain; - txConfig.PktLen = LoRaMacBufferPktLen; + tx_config.channel = channel; + tx_config.datarate = _params.sys_params.channel_data_rate; + tx_config.tx_power = _params.sys_params.channel_tx_power; + tx_config.max_eirp = _params.sys_params.max_eirp; + tx_config.antenna_gain = _params.sys_params.antenna_gain; + tx_config.pkt_len = _params.buffer_pkt_len; - lora_phy->tx_config(&txConfig, &txPower, &TxTimeOnAir); + lora_phy->tx_config(&tx_config, &tx_power, &_params.timers.tx_toa); - MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; - McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; - McpsConfirm.Datarate = LoRaMacParams.ChannelsDatarate; - McpsConfirm.TxPower = txPower; + mlme.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_ERROR; + + mcps.get_confirmation().status = LORAMAC_EVENT_INFO_STATUS_ERROR; + mcps.get_confirmation().data_rate = _params.sys_params.channel_data_rate; + mcps.get_confirmation().tx_power = tx_power; // Store the time on air - McpsConfirm.TxTimeOnAir = TxTimeOnAir; - MlmeConfirm.TxTimeOnAir = TxTimeOnAir; + mcps.get_confirmation().tx_toa = _params.timers.tx_toa; + mlme.get_confirmation().tx_toa = _params.timers.tx_toa; // Starts the MAC layer status check timer - TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT ); - TimerStart( &MacStateCheckTimer ); + _lora_time.start(_params.timers.mac_state_check_timer, + MAC_STATE_CHECK_TIMEOUT); - if( IsLoRaMacNetworkJoined == false ) - { - JoinRequestTrials++; + if (_params.is_nwk_joined == false) { + _params.join_request_trial_counter++; } // Send now - lora_phy->handle_send(LoRaMacBuffer, LoRaMacBufferPktLen); + lora_phy->handle_send(_params.buffer, _params.buffer_pkt_len); - LoRaMacState |= LORAMAC_TX_RUNNING; + _params.mac_state |= LORAMAC_TX_RUNNING; - return LORAMAC_STATUS_OK; + return LORAWAN_STATUS_OK; } -LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout ) +lorawan_status_t LoRaMac::set_tx_continuous_wave(uint16_t timeout) { - ContinuousWaveParams_t continuousWave; + cw_mode_params_t continuous_wave; - continuousWave.Channel = Channel; - continuousWave.Datarate = LoRaMacParams.ChannelsDatarate; - continuousWave.TxPower = LoRaMacParams.ChannelsTxPower; - continuousWave.MaxEirp = LoRaMacParams.MaxEirp; - continuousWave.AntennaGain = LoRaMacParams.AntennaGain; - continuousWave.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(&continuousWave); + lora_phy->set_tx_cont_mode(&continuous_wave); // Starts the MAC layer status check timer - TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT ); - TimerStart( &MacStateCheckTimer ); + _lora_time.start(_params.timers.mac_state_check_timer, + MAC_STATE_CHECK_TIMEOUT); - LoRaMacState |= LORAMAC_TX_RUNNING; + _params.mac_state |= LORAMAC_TX_RUNNING; - return LORAMAC_STATUS_OK; + return LORAWAN_STATUS_OK; } -LoRaMacStatus_t SetTxContinuousWave1( uint16_t timeout, uint32_t frequency, uint8_t power ) +lorawan_status_t LoRaMac::set_tx_continuous_wave1(uint16_t timeout, + uint32_t frequency, + uint8_t power) { - lora_phy->setup_tx_cont_wave_mode(frequency, power, timeout); + 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; + + lora_phy->set_tx_cont_mode(&continuous_wave); // Starts the MAC layer status check timer - TimerSetValue( &MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT ); - TimerStart( &MacStateCheckTimer ); + _lora_time.start(_params.timers.mac_state_check_timer, + MAC_STATE_CHECK_TIMEOUT); - LoRaMacState |= LORAMAC_TX_RUNNING; + _params.mac_state |= LORAMAC_TX_RUNNING; - return LORAMAC_STATUS_OK; + return LORAWAN_STATUS_OK; } -LoRaMacStatus_t LoRaMacInitialization(LoRaMacPrimitives_t *primitives, - LoRaMacCallback_t *callbacks, - LoRaPHY *phy, - EventQueue *queue) +lorawan_status_t LoRaMac::initialize(loramac_primitives_t *primitives, + LoRaPHY *phy, EventQueue *queue) { - GetPhyParams_t getPhy; - PhyParam_t phyParam; + get_phy_params_t get_phy; + phy_param_t phy_param; //store event queue pointer ev_queue = queue; - if(!primitives || !callbacks) - { - return LORAMAC_STATUS_PARAMETER_INVALID; + if (!primitives) { + return LORAWAN_STATUS_PARAMETER_INVALID; } lora_phy = phy; - LoRaMacPrimitives = primitives; - LoRaMacCallbacks = callbacks; + // Activate MLME subsystem + mlme.activate_mlme_subsystem(this, lora_phy, &mac_commands); - LoRaMacFlags.Value = 0; + // Activate MCPS subsystem + mcps.activate_mcps_subsystem(this, lora_phy); - LoRaMacDeviceClass = CLASS_A; - LoRaMacState = LORAMAC_IDLE; + // Activate MIB subsystem + mib.activate_mib_subsystem(this, lora_phy); - JoinRequestTrials = 0; - MaxJoinRequestTrials = 1; - RepeaterSupport = false; + // Activate channel planning subsystem + channel_plan.activate_channelplan_subsystem(lora_phy, &mib); + + mac_primitives = primitives; + + _params.flags.value = 0; + + _params.dev_class = CLASS_A; + _params.mac_state = LORAMAC_IDLE; + + _params.join_request_trial_counter = 0; + _params.max_join_request_trials = 1; + _params.is_repeater_supported = false; // Reset duty cycle times - AggregatedLastTxDoneTime = 0; - AggregatedTimeOff = 0; + _params.timers.aggregated_last_tx_time = 0; + _params.timers.aggregated_timeoff = 0; // Reset to defaults - getPhy.Attribute = PHY_DUTY_CYCLE; - phyParam = lora_phy->get_phy_params(&getPhy); + 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 - DutyCycleOn = ( bool ) phyParam.Value; + _params.is_dutycycle_on = (bool) phy_param.value; - getPhy.Attribute = PHY_DEF_TX_POWER; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParamsDefaults.ChannelsTxPower = phyParam.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; - getPhy.Attribute = PHY_DEF_TX_DR; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParamsDefaults.ChannelsDatarate = phyParam.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; - getPhy.Attribute = PHY_MAX_RX_WINDOW; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParamsDefaults.MaxRxWindow = phyParam.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; - getPhy.Attribute = PHY_RECEIVE_DELAY1; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParamsDefaults.ReceiveDelay1 = phyParam.Value; + get_phy.attribute = PHY_RECEIVE_DELAY1; + phy_param = lora_phy->get_phy_params(&get_phy); + _params.sys_params.recv_delay1 = phy_param.value; - getPhy.Attribute = PHY_RECEIVE_DELAY2; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParamsDefaults.ReceiveDelay2 = phyParam.Value; + get_phy.attribute = PHY_RECEIVE_DELAY2; + phy_param = lora_phy->get_phy_params(&get_phy); + _params.sys_params.recv_delay2 = phy_param.value; - getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY1; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParamsDefaults.JoinAcceptDelay1 = phyParam.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; - getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY2; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParamsDefaults.JoinAcceptDelay2 = phyParam.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; - getPhy.Attribute = PHY_DEF_DR1_OFFSET; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParamsDefaults.Rx1DrOffset = phyParam.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; - getPhy.Attribute = PHY_DEF_RX2_FREQUENCY; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParamsDefaults.Rx2Channel.Frequency = phyParam.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; - getPhy.Attribute = PHY_DEF_RX2_DR; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParamsDefaults.Rx2Channel.Datarate = phyParam.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; - getPhy.Attribute = PHY_DEF_UPLINK_DWELL_TIME; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParamsDefaults.UplinkDwellTime = phyParam.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; - getPhy.Attribute = PHY_DEF_DOWNLINK_DWELL_TIME; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParamsDefaults.DownlinkDwellTime = phyParam.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; - getPhy.Attribute = PHY_DEF_MAX_EIRP; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParamsDefaults.MaxEirp = phyParam.fValue; + 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; - getPhy.Attribute = PHY_DEF_ANTENNA_GAIN; - phyParam = lora_phy->get_phy_params( &getPhy ); - LoRaMacParamsDefaults.AntennaGain = phyParam.fValue; - - lora_phy->load_defaults(INIT_TYPE_INIT); + 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; // Init parameters which are not set in function ResetMacParameters - LoRaMacParamsDefaults.ChannelsNbRep = 1; - LoRaMacParamsDefaults.SystemMaxRxError = 10; - LoRaMacParamsDefaults.MinRxSymbols = 6; + _params.sys_params.max_sys_rx_error = 10; + _params.sys_params.min_rx_symb = 6; + _params.sys_params.retry_num = 1; - LoRaMacParams.SystemMaxRxError = LoRaMacParamsDefaults.SystemMaxRxError; - LoRaMacParams.MinRxSymbols = LoRaMacParamsDefaults.MinRxSymbols; - LoRaMacParams.MaxRxWindow = LoRaMacParamsDefaults.MaxRxWindow; - LoRaMacParams.ReceiveDelay1 = LoRaMacParamsDefaults.ReceiveDelay1; - LoRaMacParams.ReceiveDelay2 = LoRaMacParamsDefaults.ReceiveDelay2; - LoRaMacParams.JoinAcceptDelay1 = LoRaMacParamsDefaults.JoinAcceptDelay1; - LoRaMacParams.JoinAcceptDelay2 = LoRaMacParamsDefaults.JoinAcceptDelay2; - LoRaMacParams.ChannelsNbRep = LoRaMacParamsDefaults.ChannelsNbRep; - - ResetMacParameters( ); + reset_mac_parameters(); // Random seed initialization srand(lora_phy->get_radio_rng()); - PublicNetwork = LORAWAN_PUBLIC_NETWORK; - lora_phy->setup_public_network_mode(PublicNetwork); + _params.is_nwk_public = MBED_CONF_LORA_PUBLIC_NETWORK; + lora_phy->setup_public_network_mode(_params.is_nwk_public); lora_phy->put_radio_to_sleep(); // Initialize timers - TimerInit(&MacStateCheckTimer, handle_mac_state_check_timer_event); - TimerSetValue(&MacStateCheckTimer, MAC_STATE_CHECK_TIMEOUT); - - TimerInit(&TxDelayedTimer, handle_delayed_tx_timer_event); - TimerInit(&RxWindowTimer1, handle_rx1_timer_event); - TimerInit(&RxWindowTimer2, handle_rx2_timer_event); - TimerInit(&AckTimeoutTimer, handle_ack_timeout); + _lora_time.init(_params.timers.mac_state_check_timer, + mbed::callback(this, &LoRaMac::handle_mac_state_check_timer_event)); + _lora_time.init(_params.timers.tx_delayed_timer, + mbed::callback(this, &LoRaMac::handle_delayed_tx_timer_event)); + _lora_time.init(_params.timers.rx_window1_timer, + mbed::callback(this, &LoRaMac::handle_rx1_timer_event)); + _lora_time.init(_params.timers.rx_window2_timer, + mbed::callback(this, &LoRaMac::handle_rx2_timer_event)); + _lora_time.init(_params.timers.ack_timeout_timer, + mbed::callback(this, &LoRaMac::handle_ack_timeout)); // Store the current initialization time - LoRaMacInitializationTime = TimerGetCurrentTime(); + _params.timers.mac_init_time = _lora_time.get_current_time(); - return LORAMAC_STATUS_OK; + return LORAWAN_STATUS_OK; } -LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo ) +void LoRaMac::disconnect() { - AdrNextParams_t adrNext; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - int8_t datarate = LoRaMacParamsDefaults.ChannelsDatarate; - int8_t txPower = LoRaMacParamsDefaults.ChannelsTxPower; - uint8_t fOptLen = MacCommandsBufferIndex + MacCommandsBufferToRepeatIndex; + // Cancel all timers + _lora_time.stop(_params.timers.mac_state_check_timer); + _lora_time.stop(_params.timers.tx_delayed_timer); + _lora_time.stop(_params.timers.rx_window1_timer); + _lora_time.stop(_params.timers.rx_window2_timer); + _lora_time.stop(_params.timers.ack_timeout_timer); - if( txInfo == NULL ) - { - return LORAMAC_STATUS_PARAMETER_INVALID; + // Put radio to sleep + lora_phy->put_radio_to_sleep(); + + // Reset internal state + _params.is_nwk_joined = false; + _params.is_ack_retry_timeout_expired = false; + _params.is_rx_window_enabled = true; + _params.is_node_ack_requested = false; + _params.is_srv_ack_requested = false; + _params.flags.value = 0; + _params.mac_state = 0; + + // Clear MAC commands + mac_commands.clear_command_buffer(); + mac_commands.clear_repeat_buffer(); + mac_commands.clear_mac_commands_in_next_tx(); + + // Set internal state to idle. + _params.mac_state = LORAMAC_IDLE; +} + +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(); + + if (tx_info == NULL) { + return LORAWAN_STATUS_PARAMETER_INVALID; } - // Setup ADR request - adrNext.UpdateChanMask = false; - adrNext.AdrEnabled = AdrCtrlOn; - adrNext.AdrAckCounter = AdrAckCounter; - adrNext.Datarate = LoRaMacParams.ChannelsDatarate; - adrNext.TxPower = LoRaMacParams.ChannelsTxPower; - adrNext.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; - - // We call the function for information purposes only. We don't want to - // apply the datarate, the tx power and the ADR ack counter. - lora_phy->get_next_ADR(&adrNext, &datarate, &txPower, &AdrAckCounter); + // if applicaion has turned on ADR, we want to opt it out + if (_params.sys_params.adr_on) { + lora_phy->get_next_ADR(false, _params.sys_params.channel_data_rate, + _params.sys_params.channel_tx_power, + _params.adr_ack_counter); + } // Setup PHY request - getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; - getPhy.Datarate = datarate; - getPhy.Attribute = PHY_MAX_PAYLOAD; + get_phy.datarate = _params.sys_params.channel_data_rate; + get_phy.attribute = PHY_MAX_PAYLOAD; // Change request in case repeater is supported - if( RepeaterSupport == true ) - { - getPhy.Attribute = PHY_MAX_PAYLOAD_REPEATER; + if (_params.is_repeater_supported == true) { + get_phy.attribute = PHY_MAX_PAYLOAD_REPEATER; } - phyParam = lora_phy->get_phy_params( &getPhy ); - txInfo->CurrentPayloadSize = phyParam.Value; + 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( txInfo->CurrentPayloadSize >= fOptLen ) - { - txInfo->MaxPossiblePayload = txInfo->CurrentPayloadSize - fOptLen; - } - else - { - txInfo->MaxPossiblePayload = txInfo->CurrentPayloadSize; + if (tx_info->current_payload_size >= fopt_len) { + tx_info->max_possible_payload_size = tx_info->current_payload_size - fopt_len; + } else { + tx_info->max_possible_payload_size = tx_info->current_payload_size; // The fOpts don't fit into the maximum payload. Omit the MAC commands to // ensure that another uplink is possible. - fOptLen = 0; - MacCommandsBufferIndex = 0; - MacCommandsBufferToRepeatIndex = 0; + fopt_len = 0; + mac_commands.clear_command_buffer(); + mac_commands.clear_repeat_buffer(); } // Verify if the fOpts and the payload fit into the maximum payload - if( ValidatePayloadLength( size, datarate, fOptLen ) == false ) - { - return LORAMAC_STATUS_LENGTH_ERROR; + if (validate_payload_length(size, _params.sys_params.channel_data_rate, + fopt_len) == false) { + return LORAWAN_STATUS_LENGTH_ERROR; } - return LORAMAC_STATUS_OK; + return LORAWAN_STATUS_OK; } -LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t *mibGet ) +lorawan_status_t LoRaMac::add_channel_plan(const lorawan_channelplan_t& plan) { - LoRaMacStatus_t status = LORAMAC_STATUS_OK; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - - if( mibGet == NULL ) - { - return LORAMAC_STATUS_PARAMETER_INVALID; - } - - switch( mibGet->Type ) - { - case MIB_DEVICE_CLASS: - { - mibGet->Param.Class = LoRaMacDeviceClass; - break; - } - case MIB_NETWORK_JOINED: - { - mibGet->Param.IsNetworkJoined = IsLoRaMacNetworkJoined; - break; - } - case MIB_ADR: - { - mibGet->Param.AdrEnable = AdrCtrlOn; - break; - } - case MIB_NET_ID: - { - mibGet->Param.NetID = LoRaMacNetID; - break; - } - case MIB_DEV_ADDR: - { - mibGet->Param.DevAddr = LoRaMacDevAddr; - break; - } - case MIB_NWK_SKEY: - { - mibGet->Param.NwkSKey = LoRaMacNwkSKey; - break; - } - case MIB_APP_SKEY: - { - mibGet->Param.AppSKey = LoRaMacAppSKey; - break; - } - case MIB_PUBLIC_NETWORK: - { - mibGet->Param.EnablePublicNetwork = PublicNetwork; - break; - } - case MIB_REPEATER_SUPPORT: - { - mibGet->Param.EnableRepeaterSupport = RepeaterSupport; - break; - } - case MIB_CHANNELS: - { - getPhy.Attribute = PHY_CHANNELS; - phyParam = lora_phy->get_phy_params( &getPhy ); - - mibGet->Param.ChannelList = phyParam.Channels; - break; - } - case MIB_RX2_CHANNEL: - { - mibGet->Param.Rx2Channel = LoRaMacParams.Rx2Channel; - break; - } - case MIB_RX2_DEFAULT_CHANNEL: - { - mibGet->Param.Rx2Channel = LoRaMacParamsDefaults.Rx2Channel; - break; - } - case MIB_CHANNELS_DEFAULT_MASK: - { - getPhy.Attribute = PHY_CHANNELS_DEFAULT_MASK; - phyParam = lora_phy->get_phy_params( &getPhy ); - - mibGet->Param.ChannelsDefaultMask = phyParam.ChannelsMask; - break; - } - case MIB_CHANNELS_MASK: - { - getPhy.Attribute = PHY_CHANNELS_MASK; - phyParam = lora_phy->get_phy_params( &getPhy ); - - mibGet->Param.ChannelsMask = phyParam.ChannelsMask; - break; - } - case MIB_CHANNELS_NB_REP: - { - mibGet->Param.ChannelNbRep = LoRaMacParams.ChannelsNbRep; - break; - } - case MIB_MAX_RX_WINDOW_DURATION: - { - mibGet->Param.MaxRxWindow = LoRaMacParams.MaxRxWindow; - break; - } - case MIB_RECEIVE_DELAY_1: - { - mibGet->Param.ReceiveDelay1 = LoRaMacParams.ReceiveDelay1; - break; - } - case MIB_RECEIVE_DELAY_2: - { - mibGet->Param.ReceiveDelay2 = LoRaMacParams.ReceiveDelay2; - break; - } - case MIB_JOIN_ACCEPT_DELAY_1: - { - mibGet->Param.JoinAcceptDelay1 = LoRaMacParams.JoinAcceptDelay1; - break; - } - case MIB_JOIN_ACCEPT_DELAY_2: - { - mibGet->Param.JoinAcceptDelay2 = LoRaMacParams.JoinAcceptDelay2; - break; - } - case MIB_CHANNELS_DEFAULT_DATARATE: - { - mibGet->Param.ChannelsDefaultDatarate = LoRaMacParamsDefaults.ChannelsDatarate; - break; - } - case MIB_CHANNELS_DATARATE: - { - mibGet->Param.ChannelsDatarate = LoRaMacParams.ChannelsDatarate; - break; - } - case MIB_CHANNELS_DEFAULT_TX_POWER: - { - mibGet->Param.ChannelsDefaultTxPower = LoRaMacParamsDefaults.ChannelsTxPower; - break; - } - case MIB_CHANNELS_TX_POWER: - { - mibGet->Param.ChannelsTxPower = LoRaMacParams.ChannelsTxPower; - break; - } - case MIB_UPLINK_COUNTER: - { - mibGet->Param.UpLinkCounter = UpLinkCounter; - break; - } - case MIB_DOWNLINK_COUNTER: - { - mibGet->Param.DownLinkCounter = DownLinkCounter; - break; - } - case MIB_MULTICAST_CHANNEL: - { - mibGet->Param.MulticastList = MulticastChannels; - break; - } - case MIB_SYSTEM_MAX_RX_ERROR: - { - mibGet->Param.SystemMaxRxError = LoRaMacParams.SystemMaxRxError; - break; - } - case MIB_MIN_RX_SYMBOLS: - { - mibGet->Param.MinRxSymbols = LoRaMacParams.MinRxSymbols; - break; - } - case MIB_ANTENNA_GAIN: - { - mibGet->Param.AntennaGain = LoRaMacParams.AntennaGain; - break; - } - default: - status = LORAMAC_STATUS_SERVICE_UNKNOWN; - break; - } - - return status; -} - -LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ) -{ - LoRaMacStatus_t status = LORAMAC_STATUS_OK; - ChanMaskSetParams_t chanMaskSet; - VerifyParams_t verify; - - if( mibSet == NULL ) - { - return LORAMAC_STATUS_PARAMETER_INVALID; - } - - switch( mibSet->Type ) - { - case MIB_DEVICE_CLASS: - { - LoRaMacDeviceClass = mibSet->Param.Class; - switch( LoRaMacDeviceClass ) - { - case CLASS_A: - { - // Set the radio into sleep to setup a defined state - lora_phy->put_radio_to_sleep(); - break; - } - case CLASS_B: - { - break; - } - case CLASS_C: - { - // Set the NodeAckRequested indicator to default - NodeAckRequested = false; - handle_rx2_timer_event(); - break; - } - } - break; - } - case MIB_NETWORK_JOINED: - { - IsLoRaMacNetworkJoined = mibSet->Param.IsNetworkJoined; - break; - } - case MIB_ADR: - { - AdrCtrlOn = mibSet->Param.AdrEnable; - break; - } - case MIB_NET_ID: - { - LoRaMacNetID = mibSet->Param.NetID; - break; - } - case MIB_DEV_ADDR: - { - LoRaMacDevAddr = mibSet->Param.DevAddr; - break; - } - case MIB_NWK_SKEY: - { - if( mibSet->Param.NwkSKey != NULL ) - { - memcpy( LoRaMacNwkSKey, mibSet->Param.NwkSKey, - sizeof( LoRaMacNwkSKey ) ); - } - else - { - status = LORAMAC_STATUS_PARAMETER_INVALID; - } - break; - } - case MIB_APP_SKEY: - { - if( mibSet->Param.AppSKey != NULL ) - { - memcpy( LoRaMacAppSKey, mibSet->Param.AppSKey, - sizeof( LoRaMacAppSKey ) ); - } - else - { - status = LORAMAC_STATUS_PARAMETER_INVALID; - } - break; - } - case MIB_PUBLIC_NETWORK: - { - PublicNetwork = mibSet->Param.EnablePublicNetwork; - lora_phy->setup_public_network_mode(PublicNetwork); - break; - } - case MIB_REPEATER_SUPPORT: - { - RepeaterSupport = mibSet->Param.EnableRepeaterSupport; - break; - } - case MIB_RX2_CHANNEL: - { - verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate; - verify.DatarateParams.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; - - if( lora_phy->verify(&verify, PHY_RX_DR) == true ) - { - LoRaMacParams.Rx2Channel = mibSet->Param.Rx2Channel; - - if( ( LoRaMacDeviceClass == CLASS_C ) && ( IsLoRaMacNetworkJoined == true ) ) - { - // Compute Rx2 windows parameters - lora_phy->compute_rx_win_params(LoRaMacParams.Rx2Channel.Datarate, - LoRaMacParams.MinRxSymbols, - LoRaMacParams.SystemMaxRxError, - &RxWindow2Config); - - RxWindow2Config.Channel = Channel; - RxWindow2Config.Frequency = LoRaMacParams.Rx2Channel.Frequency; - RxWindow2Config.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; - RxWindow2Config.RepeaterSupport = RepeaterSupport; - RxWindow2Config.Window = 1; - RxWindow2Config.RxContinuous = true; - - if(lora_phy->rx_config(&RxWindow2Config, ( int8_t* )&McpsIndication.RxDatarate) == true ) - { - RxWindowSetup( RxWindow2Config.RxContinuous, LoRaMacParams.MaxRxWindow ); - RxSlot = RxWindow2Config.Window; - } - else - { - status = LORAMAC_STATUS_PARAMETER_INVALID; - } - } - } - else - { - status = LORAMAC_STATUS_PARAMETER_INVALID; - } - break; - } - case MIB_RX2_DEFAULT_CHANNEL: - { - verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate; - verify.DatarateParams.DownlinkDwellTime = LoRaMacParams.DownlinkDwellTime; - - if( lora_phy->verify(&verify, PHY_RX_DR) == true ) - { - LoRaMacParamsDefaults.Rx2Channel = mibSet->Param.Rx2DefaultChannel; - } - else - { - status = LORAMAC_STATUS_PARAMETER_INVALID; - } - break; - } - case MIB_CHANNELS_DEFAULT_MASK: - { - chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask; - chanMaskSet.ChannelsMaskType = CHANNELS_DEFAULT_MASK; - - if(lora_phy->set_channel_mask(&chanMaskSet) == false ) - { - status = LORAMAC_STATUS_PARAMETER_INVALID; - } - break; - } - case MIB_CHANNELS_MASK: - { - chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask; - chanMaskSet.ChannelsMaskType = CHANNELS_MASK; - - if(lora_phy->set_channel_mask(&chanMaskSet) == false ) - { - status = LORAMAC_STATUS_PARAMETER_INVALID; - } - break; - } - case MIB_CHANNELS_NB_REP: - { - if( ( mibSet->Param.ChannelNbRep >= 1 ) && - ( mibSet->Param.ChannelNbRep <= 15 ) ) - { - LoRaMacParams.ChannelsNbRep = mibSet->Param.ChannelNbRep; - } - else - { - status = LORAMAC_STATUS_PARAMETER_INVALID; - } - break; - } - case MIB_MAX_RX_WINDOW_DURATION: - { - LoRaMacParams.MaxRxWindow = mibSet->Param.MaxRxWindow; - break; - } - case MIB_RECEIVE_DELAY_1: - { - LoRaMacParams.ReceiveDelay1 = mibSet->Param.ReceiveDelay1; - break; - } - case MIB_RECEIVE_DELAY_2: - { - LoRaMacParams.ReceiveDelay2 = mibSet->Param.ReceiveDelay2; - break; - } - case MIB_JOIN_ACCEPT_DELAY_1: - { - LoRaMacParams.JoinAcceptDelay1 = mibSet->Param.JoinAcceptDelay1; - break; - } - case MIB_JOIN_ACCEPT_DELAY_2: - { - LoRaMacParams.JoinAcceptDelay2 = mibSet->Param.JoinAcceptDelay2; - break; - } - case MIB_CHANNELS_DEFAULT_DATARATE: - { - verify.DatarateParams.Datarate = mibSet->Param.ChannelsDefaultDatarate; - - if(lora_phy->verify(&verify, PHY_DEF_TX_DR) == true) - { - LoRaMacParamsDefaults.ChannelsDatarate = verify.DatarateParams.Datarate; - } - else - { - status = LORAMAC_STATUS_PARAMETER_INVALID; - } - break; - } - case MIB_CHANNELS_DATARATE: - { - verify.DatarateParams.Datarate = mibSet->Param.ChannelsDatarate; - - if(lora_phy->verify(&verify, PHY_TX_DR) == true) - { - LoRaMacParams.ChannelsDatarate = verify.DatarateParams.Datarate; - } - else - { - status = LORAMAC_STATUS_PARAMETER_INVALID; - } - break; - } - case MIB_CHANNELS_DEFAULT_TX_POWER: - { - verify.TxPower = mibSet->Param.ChannelsDefaultTxPower; - - if(lora_phy->verify(&verify, PHY_DEF_TX_POWER) == true) - { - LoRaMacParamsDefaults.ChannelsTxPower = verify.TxPower; - } - else - { - status = LORAMAC_STATUS_PARAMETER_INVALID; - } - break; - } - case MIB_CHANNELS_TX_POWER: - { - verify.TxPower = mibSet->Param.ChannelsTxPower; - - if(lora_phy->verify(&verify, PHY_TX_POWER) == true) - { - LoRaMacParams.ChannelsTxPower = verify.TxPower; - } - else - { - status = LORAMAC_STATUS_PARAMETER_INVALID; - } - break; - } - case MIB_UPLINK_COUNTER: - { - UpLinkCounter = mibSet->Param.UpLinkCounter; - break; - } - case MIB_DOWNLINK_COUNTER: - { - DownLinkCounter = mibSet->Param.DownLinkCounter; - break; - } - case MIB_SYSTEM_MAX_RX_ERROR: - { - LoRaMacParams.SystemMaxRxError = LoRaMacParamsDefaults.SystemMaxRxError = mibSet->Param.SystemMaxRxError; - break; - } - case MIB_MIN_RX_SYMBOLS: - { - LoRaMacParams.MinRxSymbols = LoRaMacParamsDefaults.MinRxSymbols = mibSet->Param.MinRxSymbols; - break; - } - case MIB_ANTENNA_GAIN: - { - LoRaMacParams.AntennaGain = mibSet->Param.AntennaGain; - break; - } - default: - status = LORAMAC_STATUS_SERVICE_UNKNOWN; - break; - } - - return status; -} - -LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params ) -{ - ChannelAddParams_t channelAdd; - // Validate if the MAC is in a correct state - if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) - { - if( ( LoRaMacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG ) - { - return LORAMAC_STATUS_BUSY; + if ((_params.mac_state & LORAMAC_TX_RUNNING) == LORAMAC_TX_RUNNING) { + if ((_params.mac_state & LORAMAC_TX_CONFIG) != LORAMAC_TX_CONFIG) { + return LORAWAN_STATUS_BUSY; } } - channelAdd.NewChannel = ¶ms; - channelAdd.ChannelId = id; - - return lora_phy->add_channel(&channelAdd); + return channel_plan.set_plan(plan); } -LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id ) +lorawan_status_t LoRaMac::remove_channel_plan() { - ChannelRemoveParams_t channelRemove; - - if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) - { - if( ( LoRaMacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG ) - { - return LORAMAC_STATUS_BUSY; + if ((_params.mac_state & LORAMAC_TX_RUNNING) == LORAMAC_TX_RUNNING) { + if ((_params.mac_state & LORAMAC_TX_CONFIG) != LORAMAC_TX_CONFIG) { + return LORAWAN_STATUS_BUSY; } } - channelRemove.ChannelId = id; + return channel_plan.remove_plan(); - if(lora_phy->remove_channel(&channelRemove) == false) - { - return LORAMAC_STATUS_PARAMETER_INVALID; - } - - lora_phy->put_radio_to_sleep(); - - return LORAMAC_STATUS_OK; } -LoRaMacStatus_t LoRaMacMulticastChannelLink( MulticastParams_t *channelParam ) +lorawan_status_t LoRaMac::get_channel_plan(lorawan_channelplan_t& plan) { - if( channelParam == NULL ) - { - return LORAMAC_STATUS_PARAMETER_INVALID; + return channel_plan.get_plan(plan, &_params); +} + +lorawan_status_t LoRaMac::remove_single_channel(uint8_t id) +{ + if ((_params.mac_state & LORAMAC_TX_RUNNING) == LORAMAC_TX_RUNNING) { + if ((_params.mac_state & LORAMAC_TX_CONFIG) != LORAMAC_TX_CONFIG) { + return LORAWAN_STATUS_BUSY; + } } - if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) - { - return LORAMAC_STATUS_BUSY; + + return channel_plan.remove_single_channel(id); +} + +lorawan_status_t LoRaMac::multicast_channel_link(multicast_params_t *channel_param) +{ + if (channel_param == NULL) { + return LORAWAN_STATUS_PARAMETER_INVALID; + } + if ((_params.mac_state & LORAMAC_TX_RUNNING) == LORAMAC_TX_RUNNING) { + return LORAWAN_STATUS_BUSY; } // Reset downlink counter - channelParam->DownLinkCounter = 0; + channel_param->dl_frame_counter = 0; - if( MulticastChannels == NULL ) - { + if (_params.multicast_channels == NULL) { // New node is the fist element - MulticastChannels = channelParam; - } - else - { - MulticastParams_t *cur = MulticastChannels; + _params.multicast_channels = channel_param; + } else { + multicast_params_t *cur = _params.multicast_channels; // Search the last node in the list - while( cur->Next != NULL ) - { - cur = cur->Next; + while (cur->next != NULL) { + cur = cur->next; } // This function always finds the last node - cur->Next = channelParam; + cur->next = channel_param; } - return LORAMAC_STATUS_OK; + return LORAWAN_STATUS_OK; } -LoRaMacStatus_t LoRaMacMulticastChannelUnlink( MulticastParams_t *channelParam ) +lorawan_status_t LoRaMac::multicast_channel_unlink( + multicast_params_t *channel_param) { - if( channelParam == NULL ) - { - return LORAMAC_STATUS_PARAMETER_INVALID; + if (channel_param == NULL) { + return LORAWAN_STATUS_PARAMETER_INVALID; } - if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) - { - return LORAMAC_STATUS_BUSY; + if ((_params.mac_state & LORAMAC_TX_RUNNING) == LORAMAC_TX_RUNNING) { + return LORAWAN_STATUS_BUSY; } - if( MulticastChannels != NULL ) - { - if( MulticastChannels == channelParam ) - { - // First element - MulticastChannels = channelParam->Next; - } - else - { - MulticastParams_t *cur = MulticastChannels; + if (_params.multicast_channels != NULL) { + if (_params.multicast_channels == channel_param) { + // First element + _params.multicast_channels = channel_param->next; + } else { + multicast_params_t *cur = _params.multicast_channels; // Search the node in the list - while( cur->Next && cur->Next != channelParam ) - { - cur = cur->Next; + while (cur->next && cur->next != channel_param) { + cur = cur->next; } // If we found the node, remove it - if( cur->Next ) - { - cur->Next = channelParam->Next; + if (cur->next) { + cur->next = channel_param->next; } } - channelParam->Next = NULL; + channel_param->next = NULL; } - return LORAMAC_STATUS_OK; + return LORAWAN_STATUS_OK; } -LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ) +lorawan_status_t LoRaMac::mlme_request( loramac_mlme_req_t *mlmeRequest ) { - LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN; - LoRaMacHeader_t macHdr; - AlternateDrParams_t altDr; - VerifyParams_t verify; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - - if( mlmeRequest == NULL ) - { - return LORAMAC_STATUS_PARAMETER_INVALID; - } - if( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) - { - return LORAMAC_STATUS_BUSY; - } - - memset( ( uint8_t* ) &MlmeConfirm, 0, sizeof( MlmeConfirm ) ); - - MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; - - switch( mlmeRequest->Type ) - { - case MLME_JOIN: - { - if( ( LoRaMacState & LORAMAC_TX_DELAYED ) == LORAMAC_TX_DELAYED ) - { - return LORAMAC_STATUS_BUSY; - } - - if( ( mlmeRequest->Req.Join.DevEui == NULL ) || - ( mlmeRequest->Req.Join.AppEui == NULL ) || - ( mlmeRequest->Req.Join.AppKey == NULL ) || - ( mlmeRequest->Req.Join.NbTrials == 0 ) ) - { - return LORAMAC_STATUS_PARAMETER_INVALID; - } - - // Verify the parameter NbTrials for the join procedure - verify.NbJoinTrials = mlmeRequest->Req.Join.NbTrials; - - if(lora_phy->verify(&verify, PHY_NB_JOIN_TRIALS) == false) - { - // Value not supported, get default - getPhy.Attribute = PHY_DEF_NB_JOIN_TRIALS; - phyParam = lora_phy->get_phy_params( &getPhy ); - mlmeRequest->Req.Join.NbTrials = ( uint8_t ) phyParam.Value; - } - - LoRaMacFlags.Bits.MlmeReq = 1; - MlmeConfirm.MlmeRequest = mlmeRequest->Type; - - LoRaMacDevEui = mlmeRequest->Req.Join.DevEui; - LoRaMacAppEui = mlmeRequest->Req.Join.AppEui; - LoRaMacAppKey = mlmeRequest->Req.Join.AppKey; - MaxJoinRequestTrials = mlmeRequest->Req.Join.NbTrials; - - // Reset variable JoinRequestTrials - JoinRequestTrials = 0; - - // Setup header information - macHdr.Value = 0; - macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; - - ResetMacParameters( ); - - altDr.NbTrials = JoinRequestTrials + 1; - - LoRaMacParams.ChannelsDatarate = lora_phy->get_alternate_DR(&altDr); - - status = Send( &macHdr, 0, NULL, 0 ); - break; - } - case MLME_LINK_CHECK: - { - LoRaMacFlags.Bits.MlmeReq = 1; - // LoRaMac will send this command piggy-pack - MlmeConfirm.MlmeRequest = mlmeRequest->Type; - - status = AddMacCommand( MOTE_MAC_LINK_CHECK_REQ, 0, 0 ); - break; - } - case MLME_TXCW: - { - MlmeConfirm.MlmeRequest = mlmeRequest->Type; - LoRaMacFlags.Bits.MlmeReq = 1; - status = SetTxContinuousWave( mlmeRequest->Req.TxCw.Timeout ); - break; - } - case MLME_TXCW_1: - { - MlmeConfirm.MlmeRequest = mlmeRequest->Type; - LoRaMacFlags.Bits.MlmeReq = 1; - status = SetTxContinuousWave1( mlmeRequest->Req.TxCw.Timeout, mlmeRequest->Req.TxCw.Frequency, mlmeRequest->Req.TxCw.Power ); - break; - } - default: - break; - } - - if( status != LORAMAC_STATUS_OK ) - { - NodeAckRequested = false; - LoRaMacFlags.Bits.MlmeReq = 0; - } - - return status; + return mlme.set_request(mlmeRequest, &_params); } -LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t *mcpsRequest ) +lorawan_status_t LoRaMac::mcps_request( loramac_mcps_req_t *mcpsRequest ) { - GetPhyParams_t getPhy; - PhyParam_t phyParam; - LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN; - LoRaMacHeader_t macHdr; - VerifyParams_t verify; - uint8_t fPort = 0; - void *fBuffer; - uint16_t fBufferSize; - int8_t datarate; - bool readyToSend = false; - - if( mcpsRequest == NULL ) - { - return LORAMAC_STATUS_PARAMETER_INVALID; - } - if( ( ( LoRaMacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING ) || - ( ( LoRaMacState & LORAMAC_TX_DELAYED ) == LORAMAC_TX_DELAYED ) ) - { - return LORAMAC_STATUS_BUSY; + if (_params.mac_state != LORAMAC_IDLE) { + return LORAWAN_STATUS_BUSY; } - macHdr.Value = 0; - memset ( ( uint8_t* ) &McpsConfirm, 0, sizeof( McpsConfirm ) ); - McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR; - - // AckTimeoutRetriesCounter must be reset every time a new request (unconfirmed or confirmed) is performed. - AckTimeoutRetriesCounter = 1; - - switch( mcpsRequest->Type ) - { - case MCPS_UNCONFIRMED: - { - readyToSend = true; - AckTimeoutRetries = 1; - - macHdr.Bits.MType = FRAME_TYPE_DATA_UNCONFIRMED_UP; - fPort = mcpsRequest->Req.Unconfirmed.fPort; - fBuffer = mcpsRequest->Req.Unconfirmed.fBuffer; - fBufferSize = mcpsRequest->Req.Unconfirmed.fBufferSize; - datarate = mcpsRequest->Req.Unconfirmed.Datarate; - break; - } - case MCPS_CONFIRMED: - { - readyToSend = true; - AckTimeoutRetries = mcpsRequest->Req.Confirmed.NbTrials; - - macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP; - fPort = mcpsRequest->Req.Confirmed.fPort; - fBuffer = mcpsRequest->Req.Confirmed.fBuffer; - fBufferSize = mcpsRequest->Req.Confirmed.fBufferSize; - datarate = mcpsRequest->Req.Confirmed.Datarate; - break; - } - case MCPS_PROPRIETARY: - { - readyToSend = true; - AckTimeoutRetries = 1; - - macHdr.Bits.MType = FRAME_TYPE_PROPRIETARY; - fBuffer = mcpsRequest->Req.Proprietary.fBuffer; - fBufferSize = mcpsRequest->Req.Proprietary.fBufferSize; - datarate = mcpsRequest->Req.Proprietary.Datarate; - break; - } - default: - break; - } - - // Get the minimum possible datarate - getPhy.Attribute = PHY_MIN_TX_DR; - getPhy.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; - phyParam = lora_phy->get_phy_params( &getPhy ); - // Apply the minimum possible datarate. - // Some regions have limitations for the minimum datarate. - datarate = MAX( datarate, phyParam.Value ); - - if( readyToSend == true ) - { - if( AdrCtrlOn == false ) - { - verify.DatarateParams.Datarate = datarate; - verify.DatarateParams.UplinkDwellTime = LoRaMacParams.UplinkDwellTime; - - if(lora_phy->verify(&verify, PHY_TX_DR) == true) - { - LoRaMacParams.ChannelsDatarate = verify.DatarateParams.Datarate; - } - else - { - return LORAMAC_STATUS_PARAMETER_INVALID; - } - } - - status = Send( &macHdr, fPort, fBuffer, fBufferSize ); - if( status == LORAMAC_STATUS_OK ) - { - McpsConfirm.McpsRequest = mcpsRequest->Type; - LoRaMacFlags.Bits.McpsReq = 1; - } - else - { - NodeAckRequested = false; - } - } - - return status; + return mcps.set_request(mcpsRequest, &_params); } -radio_events_t *GetPhyEventHandlers() +lorawan_status_t LoRaMac::mib_get_request_confirm( loramac_mib_req_confirm_t *mibGet ) { - RadioEvents.tx_done = handle_tx_done; - RadioEvents.rx_done = handle_rx_done; - RadioEvents.rx_error = handle_rx_error; - RadioEvents.tx_timeout = handle_tx_timeout; - RadioEvents.rx_timeout = handle_rx_timeout; + return mib.get_request(mibGet, &_params); +} - return &RadioEvents; +lorawan_status_t LoRaMac::mib_set_request_confirm( loramac_mib_req_confirm_t *mibSet ) +{ + return mib.set_request(mibSet, &_params); +} + +radio_events_t *LoRaMac::get_phy_event_handlers() +{ + radio_events.tx_done = mbed::callback(this, &LoRaMac::handle_tx_done); + radio_events.rx_done = mbed::callback(this, &LoRaMac::handle_rx_done); + radio_events.rx_error = mbed::callback(this, &LoRaMac::handle_rx_error); + radio_events.tx_timeout = mbed::callback(this, &LoRaMac::handle_tx_timeout); + radio_events.rx_timeout = mbed::callback(this, &LoRaMac::handle_rx_timeout); + + return &radio_events; } #if defined(LORAWAN_COMPLIANCE_TEST) @@ -3463,34 +1999,30 @@ radio_events_t *GetPhyEventHandlers() * Compliance testing * **************************************************************************/ -LoRaMacStatus_t LoRaMac::LoRaMacSetTxTimer( uint32_t TxDutyCycleTime ) +lorawan_status_t LoRaMac::LoRaMacSetTxTimer( uint32_t TxDutyCycleTime ) { - TimerSetValue(&TxNextPacketTimer, TxDutyCycleTime); - TimerStart(&TxNextPacketTimer); - - return LORAMAC_STATUS_OK; + _lora_time.start(tx_next_packet_timer, TxDutyCycleTime); + return LORAWAN_STATUS_OK; } -LoRaMacStatus_t LoRaMac::LoRaMacStopTxTimer( ) - + lorawan_status_t LoRaMac::LoRaMacStopTxTimer( ) { - TimerStop(&TxNextPacketTimer); - - return LORAMAC_STATUS_OK; + _lora_time.stop(tx_next_packet_timer); + return LORAWAN_STATUS_OK; } -void LoRaMacTestRxWindowsOn( bool enable ) +void LoRaMac::LoRaMacTestRxWindowsOn( bool enable ) { - IsRxWindowsEnabled = enable; + _params.is_rx_window_enabled = enable; } -void LoRaMacTestSetMic( uint16_t txPacketCounter ) +void LoRaMac::LoRaMacTestSetMic( uint16_t txPacketCounter ) { - UpLinkCounter = txPacketCounter; - IsUpLinkCounterFixed = true; + _params.ul_frame_counter = txPacketCounter; + _params.is_ul_frame_counter_fixed = true; } -void LoRaMacTestSetDutyCycleOn( bool enable ) +void LoRaMac::LoRaMacTestSetDutyCycleOn( bool enable ) { VerifyParams_t verify; @@ -3498,12 +2030,12 @@ void LoRaMacTestSetDutyCycleOn( bool enable ) if(lora_phy->verify(&verify, PHY_DUTY_CYCLE) == true) { - DutyCycleOn = enable; + _params.is_dutycycle_on = enable; } } -void LoRaMacTestSetChannel( uint8_t channel ) +void LoRaMac::LoRaMacTestSetChannel( uint8_t channel ) { - Channel = channel; + _params.channel = channel; } #endif diff --git a/features/lorawan/lorastack/mac/LoRaMac.h b/features/lorawan/lorastack/mac/LoRaMac.h index 534e2930f4..b1676f8489 100644 --- a/features/lorawan/lorastack/mac/LoRaMac.h +++ b/features/lorawan/lorastack/mac/LoRaMac.h @@ -37,324 +37,642 @@ * SPDX-License-Identifier: BSD-3-Clause * */ -#ifndef __LORAMAC_H__ -#define __LORAMAC_H__ +#ifndef MBED_LORAWAN_MAC_H__ +#define MBED_LORAWAN_MAC_H__ #include "lorawan/system/LoRaWANTimer.h" -#include "netsocket/LoRaRadio.h" #include "lorastack/phy/LoRaPHY.h" #include "lorawan/system/lorawan_data_structures.h" +#include "lorastack/mac/LoRaMacCommand.h" +#include "events/EventQueue.h" +#include "lorastack/mac/LoRaMacMlme.h" +#include "lorastack/mac/LoRaMacMcps.h" +#include "lorastack/mac/LoRaMacMib.h" +#include "lorastack/mac/LoRaMacChannelPlan.h" -/*! - * Check the MAC layer state every MAC_STATE_CHECK_TIMEOUT in ms. - */ -#define MAC_STATE_CHECK_TIMEOUT 1000 +class LoRaMac { -/*! - * The maximum number of times the MAC layer tries to get an acknowledge. - */ -#define MAX_ACK_RETRIES 8 +public: -/*! - * The frame direction definition for uplink communications. - */ -#define UP_LINK 0 + /** + * Constructor + */ + LoRaMac(LoRaWANTimeHandler &lora_time); -/*! - * The frame direction definition for downlink communications. - */ -#define DOWN_LINK 1 + /** + * Destructor + */ + ~LoRaMac(); + /** + * @brief LoRaMAC layer initialization + * + * @details In addition to the initialization of the LoRaMAC layer, this + * function initializes the callback primitives of the MCPS and + * MLME services. Every data field of \ref loramac_primitives_t must be + * set to a valid callback function. + * + * @param primitives [in] A pointer to the structure defining the LoRaMAC + * event functions. Refer to \ref loramac_primitives_t. + * + * @param phy [in] A pointer to the selected PHY layer. + * + * @param queue [in] A pointer to the application provided EventQueue. + * + * @return `lorawan_status_t` The status of the operation. The possible values are: + * \ref LORAWAN_STATUS_OK + * \ref LORAWAN_STATUS_PARAMETER_INVALID + */ + lorawan_status_t initialize(loramac_primitives_t *primitives, LoRaPHY *phy, + events::EventQueue *queue); -/*! - * LoRaMAC max EIRP (dBm) table. - */ -static const uint8_t LoRaMacMaxEirpTable[] = { 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 }; + /** + * @brief Disconnect LoRaMac layer + * + * @details Cancels all outstanding requests and sets LoRaMac's + * internal state to idle. + */ + void disconnect(void); + /** + * @brief Queries the LoRaMAC whether it is possible to send the next frame with + * a given payload size. The LoRaMAC takes the scheduled MAC commands into + * account and reports when the frame can be sent. + * + * @param size [in] The size of the applicable payload to be sent next. + * @param tx_info [out] The structure \ref loramac_tx_info_t contains + * information on the actual maximum payload possible + * (according to the configured datarate or the next + * datarate according to ADR), and the maximum frame + * size, taking the scheduled MAC commands into account. + * + * @return `lorawan_status_t` The status of the operation. When the parameters are + * not valid, the function returns \ref LORAWAN_STATUS_PARAMETER_INVALID. + * In case of a length error caused by the applicable payload in combination + * with the MAC commands, the function returns \ref LORAWAN_STATUS_LENGTH_ERROR. + * Please note that if the size of the MAC commands in the queue do + * not fit into the payload size on the related datarate, the LoRaMAC will + * omit the MAC commands. + * If the query is valid, and the LoRaMAC is able to send the frame, + * the function returns \ref LORAWAN_STATUS_OK. + */ + lorawan_status_t query_tx_possible(uint8_t size, loramac_tx_info_t* tx_info); -/*! - * \brief LoRaMAC layer initialization - * - * \details In addition to the initialization of the LoRaMAC layer, this - * function initializes the callback primitives of the MCPS and - * MLME services. Every data field of \ref LoRaMacPrimitives_t must be - * set to a valid callback function. - * - * \param primitives [in] - A pointer to the structure defining the LoRaMAC - * event functions. Refer to \ref LoRaMacPrimitives_t. - * - * \param callbacks [in] - A pointer to the structure defining the LoRaMAC - * callback functions. Refer to \ref LoRaMacCallback_t. - * - * \param phy [in]- A pointer to the selected PHY layer. - * - * \param queue [in]- A pointer to the application provided EventQueue. - * - * \retval `LoRaMacStatus_t` The status of the operation. The possible values are: - * \ref LORAMAC_STATUS_OK - * \ref LORAMAC_STATUS_PARAMETER_INVALID - * \ref LORAMAC_STATUS_REGION_NOT_SUPPORTED. - */ -LoRaMacStatus_t LoRaMacInitialization(LoRaMacPrimitives_t *primitives, - LoRaMacCallback_t *callbacks, - LoRaPHY *phy, - events::EventQueue *queue); + /** + * @brief Adds a channel plan to the system. + * + * @details Adds a whole channel plan or a single new channel if the plan + * contains only one channel and 'plan.nb_channels' is set to 1. + * Please note that this functionality is not available in all regions. + * Information on the allowed ranges is available at the + * LoRaWAN Regional Parameters V1.0.2rB. + * + * @param plan [in] A reference to application provided channel plan. + * + * @return `lorawan_status_t` The status of the operation. The possible values are: + * \ref LORAWAN_STATUS_OK + * \ref LORAWAN_STATUS_BUSY + * \ref LORAWAN_STATUS_PARAMETER_INVALID + */ + lorawan_status_t add_channel_plan(const lorawan_channelplan_t& plan); -/*! - * \brief Queries the LoRaMAC whether it is possible to send the next frame with - * a given payload size. The LoRaMAC takes the scheduled MAC commands into - * account and reports when the frame can be sent. - * - * \param size [in]- The size of the applicable payload to be sent next. - * \param txInfo [out] - The structure \ref LoRaMacTxInfo_t contains - * information on the actual maximum payload possible - * (according to the configured datarate or the next - * datarate according to ADR), and the maximum frame - * size, taking the scheduled MAC commands into account. - * - * \retval `LoRaMacStatus_t` The status of the operation. When the parameters are - * not valid, the function returns \ref LORAMAC_STATUS_PARAMETER_INVALID. - * In case of a length error caused by the applicable payload in combination - * with the MAC commands, the function returns \ref LORAMAC_STATUS_LENGTH_ERROR. - * Please note that if the size of the MAC commands in the queue do - * not fit into the payload size on the related datarate, the LoRaMAC will - * omit the MAC commands. - * If the query is valid, and the LoRaMAC is able to send the frame, - * the function returns \ref LORAMAC_STATUS_OK. - */ -LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo ); + /** + * @brief Removes a channel plan from the system. + * + * @details Removes the whole active channel plan except the 'Default Channels'. + * Please note that this functionality is not available in all regions. + * Information on the allowed ranges is available at the + * LoRaWAN Regional Parameters V1.0.2rB. + * + * @return `lorawan_status_t` The status of the operation. The possible values are: + * \ref LORAWAN_STATUS_OK + * \ref LORAWAN_STATUS_BUSY + * \ref LORAWAN_STATUS_PARAMETER_INVALID + */ + lorawan_status_t remove_channel_plan(); -/*! - * \brief LoRaMAC channel add service. - * - * \details Adds a new channel to the channel list and activates the ID in - * the channel mask. Please note that this functionality is not available - * in all regions. Information on the allowed ranges is available at the LoRaWAN Regional Parameters V1.0.2rB. - * - * \param id [in] - The ID of the channel. - * - * \param params [in] - The channel parameters to set. - * - * \retval `LoRaMacStatus_t` The status of the operation. The possible values are: - * \ref LORAMAC_STATUS_OK - * \ref LORAMAC_STATUS_BUSY - * \ref LORAMAC_STATUS_PARAMETER_INVALID - */ -LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params ); + /** + * @brief Access active channel plan. + * + * @details Provides access to the current active channel plan. + * + * @param plan [out] A reference to application provided channel plan data + * structure which will be filled in with active channel + * plan. + * + * @return `lorawan_status_t` The status of the operation. The possible values are: + * \ref LORAWAN_STATUS_OK + * \ref LORAWAN_STATUS_BUSY + * \ref LORAWAN_STATUS_PARAMETER_INVALID + */ + lorawan_status_t get_channel_plan(lorawan_channelplan_t& plan); -/*! - * \brief LoRaMAC channel remove service. - * - * \details Deactivates the ID in the channel mask. - * - * \param id - Id of the channel. - * - * \retval `LoRaMacStatus_t` The status of the operation. The possible values are: - * \ref LORAMAC_STATUS_OK - * \ref LORAMAC_STATUS_BUSY - * \ref LORAMAC_STATUS_PARAMETER_INVALID - */ -LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id ); + /** + * @brief Remove a given channel from the active plan. + * + * @details Deactivates the given channel. + * + * @param id Id of the channel. + * + * @return `lorawan_status_t` The status of the operation. The possible values are: + * \ref LORAWAN_STATUS_OK + * \ref LORAWAN_STATUS_BUSY + * \ref LORAWAN_STATUS_PARAMETER_INVALID + */ + lorawan_status_t remove_single_channel(uint8_t id); -/*! - * \brief LoRaMAC multicast channel link service. - * - * \details Links a multicast channel into the linked list. - * - * \param [in] channelParam - The multicast channel parameters to link. - * - * \retval `LoRaMacStatus_t` The status of the operation. The possible values are: - * \ref LORAMAC_STATUS_OK - * \ref LORAMAC_STATUS_BUSY - * \ref LORAMAC_STATUS_PARAMETER_INVALID - */ -LoRaMacStatus_t LoRaMacMulticastChannelLink( MulticastParams_t *channelParam ); + /** + * @brief LoRaMAC multicast channel link service. + * + * @details Links a multicast channel into the linked list. + * + * @param [in] channel_param The multicast channel parameters to link. + * + * @return `lorawan_status_t` The status of the operation. The possible values are: + * \ref LORAWAN_STATUS_OK + * \ref LORAWAN_STATUS_BUSY + * \ref LORAWAN_STATUS_PARAMETER_INVALID + */ + lorawan_status_t multicast_channel_link(multicast_params_t *channel_param); -/*! - * \brief LoRaMAC multicast channel unlink service. - * - * \details Unlinks a multicast channel from the linked list. - * - * \param [in] channelParam - The multicast channel parameters to unlink. - * - * \retval `LoRaMacStatus_t` The status of the operation. The possible values are: - * \ref LORAMAC_STATUS_OK - * \ref LORAMAC_STATUS_BUSY - * \ref LORAMAC_STATUS_PARAMETER_INVALID - */ -LoRaMacStatus_t LoRaMacMulticastChannelUnlink( MulticastParams_t *channelParam ); + /** + * @brief LoRaMAC multicast channel unlink service. + * + * @details Unlinks a multicast channel from the linked list. + * + * @param [in] channel_param The multicast channel parameters to unlink. + * + * @return `lorawan_status_t` The status of the operation. The possible values are: + * \ref LORAWAN_STATUS_OK + * \ref LORAWAN_STATUS_BUSY + * \ref LORAWAN_STATUS_PARAMETER_INVALID + */ + lorawan_status_t multicast_channel_unlink(multicast_params_t *channel_param); -/*! - * \brief LoRaMAC MIB-GET. - * - * \details The MAC information base service to get the attributes of the LoRaMac layer. - * - * The following code-snippet shows how to use the API to get the - * parameter `AdrEnable`, defined by the enumeration type - * \ref MIB_ADR. - * \code - * MibRequestConfirm_t mibReq; - * mibReq.Type = MIB_ADR; - * - * if( LoRaMacMibGetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) - * { - * // LoRaMAC updated the parameter mibParam.AdrEnable - * } - * \endcode - * - * \param [in] mibGet - The MIB-GET request to perform. Refer to \ref MibRequestConfirm_t. - * - * \retval `LoRaMacStatus_t` The status of the operation. The possible values are: - * \ref LORAMAC_STATUS_OK - * \ref LORAMAC_STATUS_SERVICE_UNKNOWN - * \ref LORAMAC_STATUS_PARAMETER_INVALID - */ -LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t *mibGet ); + /** + * @brief Get parameter values from MIB service. + * + * @details The MAC information base service to get the attributes of the LoRaMac layer. + * + * The following code-snippet shows how to use the API to get the + * parameter `AdrEnable`, defined by the enumeration type + * \ref MIB_ADR. + * + * @code + * + * loramac_mib_req_confirm_t mib_get; + * mib_get.type = MIB_ADR; + * + * if (mib_get_request_confirm(&mib_get) == LORAWAN_STATUS_OK) { + * // LoRaMAC updated the parameter mibParam.AdrEnable + * } + * + * @endcode + * + * @param [in] mib_get The MIB-GET request to perform. Refer to + * \ref loramac_mib_req_confirm_t. + * + * @return `lorawan_status_t` The status of the operation. + * The possible values are: + * \ref LORAWAN_STATUS_OK + * \ref LORAWAN_STATUS_SERVICE_UNKNOWN + * \ref LORAWAN_STATUS_PARAMETER_INVALID + */ + lorawan_status_t mib_get_request_confirm(loramac_mib_req_confirm_t *mib_get); -/*! - * \brief LoRaMAC MIB-SET. - * - * \details The MAC information base service to set the attributes of the LoRaMac layer. - * - * The following code-snippet shows how to use the API to set the - * parameter `AdrEnable`, defined by the enumeration type - * \ref MIB_ADR. - * - * \code - * MibRequestConfirm_t mibReq; - * mibReq.Type = MIB_ADR; - * mibReq.Param.AdrEnable = true; - * - * if( LoRaMacMibGetRequestConfirm( &mibReq ) == LORAMAC_STATUS_OK ) - * { - * // LoRaMAC updated the parameter - * } - * \endcode - * - * \param [in] mibSet - The MIB-SET request to perform. Refer to \ref MibRequestConfirm_t. - * - * \retval `LoRaMacStatus_t` The status of the operation. The possible values are: - * \ref LORAMAC_STATUS_OK - * \ref LORAMAC_STATUS_BUSY - * \ref LORAMAC_STATUS_SERVICE_UNKNOWN - * \ref LORAMAC_STATUS_PARAMETER_INVALID - */ -LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet ); + /** + * @brief Set attributes for MAC layer using MIB service. + * + * @details The MAC information base service to set the attributes of the LoRaMac layer. + * + * The following code-snippet shows how to use the API to set the + * parameter `adr_enable`, defined by the enumeration type + * \ref MIB_ADR. + * + * @code + * + * loramac_mib_req_confirm_t mib_set; + * mib_set.Type = MIB_ADR; + * mib_set.param.adr_enable = true; + * + * if (mib_set_request_confirm(&mib_set) == LORAWAN_STATUS_OK) { + * // LoRaMAC updated the parameter + * } + * + * @endcode + * + * @param [in] mib_set The MIB-SET request to perform. Refer to + * \ref loramac_mib_req_confirm_t. + * + * @return `lorawan_status_t` The status of the operation. The possible values are: + * \ref LORAWAN_STATUS_OK + * \ref LORAWAN_STATUS_BUSY + * \ref LORAWAN_STATUS_SERVICE_UNKNOWN + * \ref LORAWAN_STATUS_PARAMETER_INVALID + */ + lorawan_status_t mib_set_request_confirm(loramac_mib_req_confirm_t *mib_set); -/*! - * \brief LoRaMAC MLME request - * - * \details The MAC layer management entity handles the management services. The - * following code-snippet shows how to use the API to perform a - * network join request. - * - * \code - * static uint8_t DevEui[] = - * { - * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - * }; - * static uint8_t AppEui[] = - * { - * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - * }; - * static uint8_t AppKey[] = - * { - * 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, - * 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C - * }; - * - * MlmeReq_t mlmeReq; - * mlmeReq.Type = MLME_JOIN; - * mlmeReq.Req.Join.DevEui = DevEui; - * mlmeReq.Req.Join.AppEui = AppEui; - * mlmeReq.Req.Join.AppKey = AppKey; - * - * if( LoRaMacMlmeRequest( &mlmeReq ) == LORAMAC_STATUS_OK ) - * { - * // Service started successfully. Waiting for the Mlme-Confirm event - * } - * \endcode - * - * \param [in] mlmeRequest - The MLME request to perform. Refer to \ref MlmeReq_t. - * - * \retval `LoRaMacStatus_t` The status of the operation. The possible values are: - * \ref LORAMAC_STATUS_OK - * \ref LORAMAC_STATUS_BUSY - * \ref LORAMAC_STATUS_SERVICE_UNKNOWN - * \ref LORAMAC_STATUS_PARAMETER_INVALID - * \ref LORAMAC_STATUS_NO_NETWORK_JOINED - * \ref LORAMAC_STATUS_LENGTH_ERROR - * \ref LORAMAC_STATUS_DEVICE_OFF - */ -LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t *mlmeRequest ); + /** + * @brief Set forth an MLME request. + * + * @details The MAC layer management entity handles the management services. The + * following code-snippet shows how to use the API to perform a + * network join request. + * + * @code + * + * static uint8_t dev_eui[] = + * { + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + * }; + * static uint8_t app_eui[] = + * { + * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + * }; + * static uint8_t app_key[] = + * { + * 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + * 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C + * }; + * + * loramac_mlme_req_t mlme_req; + * mlme_req.Type = MLME_JOIN; + * mlme_req.req.join.dev_eui = dev_eui; + * mlme_req.req.join.app_eui = app_eui; + * mlme_req.req.join.app_key = app_key; + * + * if (LoRaMacMlmeRequest(&mlme_req) == LORAWAN_STATUS_OK) { + * // Service started successfully. Waiting for the Mlme-Confirm event + * } + * + * @endcode + * + * @param [in] request The MLME request to perform. + * Refer to \ref loramac_mlme_req_t. + * + * @return `lorawan_status_t` The status of the operation. The possible values are: + * \ref LORAWAN_STATUS_OK + * \ref LORAWAN_STATUS_BUSY + * \ref LORAWAN_STATUS_SERVICE_UNKNOWN + * \ref LORAWAN_STATUS_PARAMETER_INVALID + * \ref LORAWAN_STATUS_NO_NETWORK_JOINED + * \ref LORAWAN_STATUS_LENGTH_ERROR + * \ref LORAWAN_STATUS_DEVICE_OFF + */ + lorawan_status_t mlme_request(loramac_mlme_req_t *request); -/*! - * \brief LoRaMAC MCPS request - * - * \details The MAC Common Part Sublayer handles the data services. The following - * code-snippet shows how to use the API to send an unconfirmed - * LoRaMAC frame. - * - * \code - * uint8_t myBuffer[] = { 1, 2, 3 }; - * - * McpsReq_t mcpsReq; - * mcpsReq.Type = MCPS_UNCONFIRMED; - * mcpsReq.Req.Unconfirmed.fPort = 1; - * mcpsReq.Req.Unconfirmed.fBuffer = myBuffer; - * mcpsReq.Req.Unconfirmed.fBufferSize = sizeof( myBuffer ); - * - * if( LoRaMacMcpsRequest( &mcpsReq ) == LORAMAC_STATUS_OK ) - * { - * // Service started successfully. Waiting for the MCPS-Confirm event - * } - * \endcode - * - * \param [in] mcpsRequest - The MCPS request to perform. Refer to \ref McpsReq_t. - * - * \retval `LoRaMacStatus_t` The status of the operation. The possible values are: - * \ref LORAMAC_STATUS_OK - * \ref LORAMAC_STATUS_BUSY - * \ref LORAMAC_STATUS_SERVICE_UNKNOWN - * \ref LORAMAC_STATUS_PARAMETER_INVALID - * \ref LORAMAC_STATUS_NO_NETWORK_JOINED - * \ref LORAMAC_STATUS_LENGTH_ERROR - * \ref LORAMAC_STATUS_DEVICE_OFF - */ -LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t *mcpsRequest ); + /** + * @brief Set forth an MCPS request. + * + * @details The MAC Common Part Sublayer handles the data services. The following + * code-snippet shows how to use the API to send an unconfirmed + * LoRaMAC frame. + * + * @code + * + * uint8_t buffer[] = {1, 2, 3}; + * + * 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); + * + * if (mcps_request(&request) == LORAWAN_STATUS_OK) { + * // Service started successfully. Waiting for the MCPS-Confirm event + * } + * + * @endcode + * + * @param [in] request The MCPS request to perform. + * Refer to \ref loramac_mcps_req_t. + * + * @return `lorawan_status_t` The status of the operation. The possible values are: + * \ref LORAWAN_STATUS_OK + * \ref LORAWAN_STATUS_BUSY + * \ref LORAWAN_STATUS_SERVICE_UNKNOWN + * \ref LORAWAN_STATUS_PARAMETER_INVALID + * \ref LORAWAN_STATUS_NO_NETWORK_JOINED + * \ref LORAWAN_STATUS_LENGTH_ERROR + * \ref LORAWAN_STATUS_DEVICE_OFF + */ + lorawan_status_t mcps_request(loramac_mcps_req_t *request); -/** - * \brief LoRaMAC layer provides its callback functions for - * PHY layer - * - * \return Pointer to callback functions for radio events - */ -radio_events_t *GetPhyEventHandlers(); + /** + * @brief LoRaMAC layer provides its callback functions for + * PHY layer. + * + * @return Pointer to callback functions for radio events + */ + radio_events_t *get_phy_event_handlers(); + + /** + * @brief Configures the events to trigger an MLME-Indication with + * a MLME type of MLME_SCHEDULE_UPLINK. + */ + void set_mlme_schedule_ul_indication(void); + + /** + * @brief Schedules the frame for sending. + * + * @details Prepares a full MAC frame and schedules it for physical + * transmission. + * + * @param [in] mac_hdr MAC frame header field + * @param [in] fport Payload port + * @param [in] fbuffer MAC frame data buffer to be sent + * @param [in] fbuffer_size MAC frame data buffer size + * + * @return status Status of the operation. LORAWAN_STATUS_OK in case + * of success and a negative error code in case of + * failure. + */ + lorawan_status_t send(loramac_mhdr_t *mac_hdr, uint8_t fport, void *fbuffer, + uint16_t fbuffer_size); + + /** + * @brief Puts the system in continuous transmission mode + * + * @remark Uses the radio parameters set on the previous transmission. + * + * @param [in] timeout Time in seconds while the radio is kept in continuous wave mode + * + * @return status Status of the operation. LORAWAN_STATUS_OK in case + * of success and a negative error code in case of + * failure. + */ + lorawan_status_t set_tx_continuous_wave(uint16_t timeout); + + /** + * @brief Puts the system in continuous transmission mode + * + * @param [in] timeout Time in seconds while the radio is kept in continuous wave mode + * @param [in] frequency RF frequency to be set. + * @param [in] power RF output power to be set. + * + * @return status Status of the operation. LORAWAN_STATUS_OK in case + * of success and a negative error code in case of + * failure. + */ + lorawan_status_t set_tx_continuous_wave1(uint16_t timeout, uint32_t frequency, uint8_t power); + + /** + * @brief Resets MAC specific parameters to default + */ + void reset_mac_parameters(void); + + /** + * @brief Opens up a continuous RX 2 window. This is used for + * class c devices. + */ + void open_continuous_rx2_window(void); #if defined(LORAWAN_COMPLIANCE_TEST) -/** - * \brief LoRaMAC set tx timer. - * - * \details Sets up a timer for next transmission (application specific timers). - * - * \param [in] NextTxTime - Periodic time for next uplink. +public: // Test interface - * \retval `LoRaMacStatus_t` The status of the operation. The possible values are: - * \ref LORAMAC_STATUS_OK - * \ref LORAMAC_STATUS_PARAMETER_INVALID - */ -LoRaMacStatus_t LoRaMacSetTxTimer( uint32_t NextTxTime ); + /** + * \brief LoRaMAC set tx timer. + * + * \details Sets up a timer for next transmission (application specific timers). + * + * \param [in] NextTxTime - Periodic time for next uplink. -/** - * \brief LoRaMAC stop tx timer. - * - * \details Stops the next tx timer. - * - * \retval `LoRaMacStatus_t` The status of the operation. The possible values are: - * \ref LORAMAC_STATUS_OK - * \ref LORAMAC_STATUS_PARAMETER_INVALID - */ -LoRaMacStatus_t LoRaMacStopTxTimer( ); + * \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 // __LORAMAC_H__ +private: + /** + * Function to be executed on Radio Tx Done event + */ + void on_radio_tx_done(void); + + /** + * This function prepares the MAC to abort the execution of function + * on_radio_rx_done() in case of a reception error. + */ + void prepare_rx_done_abort(void); + + /** + * Function to be executed on Radio Rx Done event + */ + void on_radio_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, + int8_t snr); + + /** + * Function executed on Radio Tx Timeout event + */ + void on_radio_tx_timeout(void); + + /** + * Function executed on Radio Rx error event + */ + void on_radio_rx_error(void); + + /** + * Function executed on Radio Rx Timeout event + */ + void on_radio_rx_timeout(void); + + /** + *Function executed on Resend Frame timer event. + */ + void on_mac_state_check_timer_event(void); + + /** + * Function executed on duty cycle delayed Tx timer event + */ + void on_tx_delayed_timer_event(void); + + /** + * Function executed on first Rx window timer event + */ + void on_rx_window1_timer_event(void); + + /** + * Function executed on second Rx window timer event + */ + void on_rx_window2_timer_event(void); + + /** + * Function executed on AckTimeout timer event + */ + void on_ack_timeout_timer_event(void); + + /** + * Initializes and opens the reception window + */ + void rx_window_setup(bool rx_continuous, uint32_t max_rx_window_time); + + /** + * Validates if the payload fits into the frame, taking the datarate + * into account. + * + * Please Refer to chapter 4.3.2 of the LoRaWAN specification, v1.0.2 + */ + bool validate_payload_length(uint8_t length, int8_t datarate, uint8_t fopts_len); + + /** + * Prepares MAC frame on the behest of send() API. + */ + lorawan_status_t prepare_frame(loramac_mhdr_t *mac_hdr, + loramac_frame_ctrl_t *fctrl, uint8_t fport, + void *fbuffer, uint16_t fbuffer_size); + + /** + * Schedules a transmission on the behest of send() API. + */ + lorawan_status_t schedule_tx(void); + + /** + * Calculates the back-off time for the band of a channel. + * Takes in the last used channel id as a parameter. + */ + void calculate_backOff(uint8_t channel_id); + + /** + * Hands over the MAC frame to PHY layer. + */ + lorawan_status_t send_frame_on_channel(uint8_t channel); + + /** + * Checks for Port validity. + */ + bool is_fPort_allowed(uint8_t fPort); + + /** + * Prototypes for ISR handlers + */ + void handle_cad_done(bool cad); + void handle_tx_done(void); + void handle_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, + int8_t snr); + void handle_rx_error(void); + void handle_rx_timeout(void); + void handle_tx_timeout(void); + void handle_fhss_change_channel(uint8_t cur_channel); + void handle_rx1_timer_event(void); + void handle_rx2_timer_event(void); + void handle_ack_timeout(void); + void handle_delayed_tx_timer_event(void); + void handle_mac_state_check_timer_event(void); + void handle_next_tx_timer_event(void); + +private: + /** + * LoRa PHY layer object storage + */ + LoRaPHY *lora_phy; + + /** + * MAC command handle + */ + LoRaMacCommand mac_commands; + + /** + * MLME subsystem handle + */ + LoRaMacMlme mlme; + + /** + * MCPS subsystem handle + */ + LoRaMacMcps mcps; + + /** + * MCPS subsystem handle + */ + LoRaMacMib mib; + + /** + * Channel planning subsystem + */ + LoRaMacChannelPlan channel_plan; + + /** + * Timer subsystem handle + */ + LoRaWANTimeHandler &_lora_time; + + /** + * Central MAC layer data storage + */ + loramac_protocol_params _params; + + /** + * Radio event callback handlers for MAC + */ + radio_events_t radio_events; + + /** + * LoRaMac upper layer event functions + */ + loramac_primitives_t *mac_primitives; + + /** + * EventQueue object storage + */ + events::EventQueue *ev_queue; +}; + +#endif // MBED_LORAWAN_MAC_H__ diff --git a/features/lorawan/lorastack/mac/LoRaMacChannelPlan.cpp b/features/lorawan/lorastack/mac/LoRaMacChannelPlan.cpp new file mode 100644 index 0000000000..584207de44 --- /dev/null +++ b/features/lorawan/lorastack/mac/LoRaMacChannelPlan.cpp @@ -0,0 +1,243 @@ +/** + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRaWAN stack layer that controls both MAC and PHY underneath + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) + + +Copyright (c) 2017, Arm Limited and affiliates. + +SPDX-License-Identifier: BSD-3-Clause +*/ + +#include "lorastack/mac/LoRaMacChannelPlan.h" + +LoRaMacChannelPlan::LoRaMacChannelPlan() : _lora_phy(NULL), _mib(NULL) +{ +} + +LoRaMacChannelPlan::~LoRaMacChannelPlan() +{ +} + +void LoRaMacChannelPlan::activate_channelplan_subsystem(LoRaPHY *phy, LoRaMacMib *mib) +{ + _lora_phy = phy; + _mib = mib; +} + +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) { + 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; + + // check if user is setting more channels than supported + if (plan.nb_channels > max_num_channels) { + return LORAWAN_STATUS_PARAMETER_INVALID; + } + + for (uint8_t i = 0; i < plan.nb_channels; i++) { + + mac_layer_ch_params.band = plan.channels[i].ch_param.band; + + mac_layer_ch_params.dr_range.fields.max = plan.channels[i].ch_param.dr_range.fields.max; + mac_layer_ch_params.dr_range.fields.min = plan.channels[i].ch_param.dr_range.fields.min; + mac_layer_ch_params.dr_range.value = plan.channels[i].ch_param.dr_range.value; + mac_layer_ch_params.frequency = plan.channels[i].ch_param.frequency; + mac_layer_ch_params.rx1_frequency = plan.channels[i].ch_param.rx1_frequency; + + status = _lora_phy->add_channel(&mac_layer_ch_params, plan.channels[i].id); + + if (status != LORAWAN_STATUS_OK) { + return status; + } + } + + return LORAWAN_STATUS_OK; +} + +lorawan_status_t LoRaMacChannelPlan::get_plan(lorawan_channelplan_t& plan, + loramac_protocol_params *params) +{ + if (params == 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) { + 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; + + // 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; + } + + for (uint8_t i = 0; i < max_num_channels; i++) { + // skip the channels which are not enabled + if (_lora_phy->mask_bit_test(channel_mask, i) == 0) { + continue; + } + + // 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; + count++; + } + + plan.nb_channels = count; + + return LORAWAN_STATUS_OK; +} + +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) { + 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 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; + + // 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; + + for (uint8_t i = 0; i < max_num_channels; i++) { + // skip any default channels + if (_lora_phy->mask_bit_test(default_channel_mask, i) != 0) { + continue; + } + + // skip any channels which are not currently enabled + if (_lora_phy->mask_bit_test(channel_mask, i) == 0) { + continue; + } + + status = remove_single_channel(i); + + if (status != LORAWAN_STATUS_OK) { + return status; + } + } + + return status; +} + +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) { + 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; + + // According to specification channel IDs start from 0 and last valid + // channel ID is N-1 where N=MAX_NUM_CHANNELS. + // So any ID which is larger or equal to the Max number of channels is invalid + if (channel_id >= max_num_channels) { + return LORAWAN_STATUS_PARAMETER_INVALID; + } + + if (_lora_phy->remove_channel(channel_id) == false) { + return LORAWAN_STATUS_PARAMETER_INVALID; + } + + _lora_phy->put_radio_to_sleep(); + + return LORAWAN_STATUS_OK; +} + diff --git a/features/lorawan/lorastack/mac/LoRaMacChannelPlan.h b/features/lorawan/lorastack/mac/LoRaMacChannelPlan.h new file mode 100644 index 0000000000..adf880de64 --- /dev/null +++ b/features/lorawan/lorastack/mac/LoRaMacChannelPlan.h @@ -0,0 +1,117 @@ +/** + \code + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== +\endcode + +Description: LoRaWAN stack layer that controls both MAC and PHY underneath + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) + + +Copyright (c) 2017, Arm Limited and affiliates. + +SPDX-License-Identifier: BSD-3-Clause +*/ + +#ifndef MBED_LORAWAN_LORAMACCHANNELPLAN_H_ +#define MBED_LORAWAN_LORAMACCHANNELPLAN_H_ + +#include "lorawan/system/lorawan_data_structures.h" +#include "lorastack/phy/LoRaPHY.h" +#include "lorastack/mac/LoRaMacMib.h" + +class LoRaMacChannelPlan { + +public: + + /** Constructor + * + * Sets local handles to NULL. These handles will be set when the subsystem + * is activated by the MAC layer. + */ + LoRaMacChannelPlan(); + + /** Destructor + * + * Does nothing + */ + ~LoRaMacChannelPlan(); + + /** Activates Channel Planning subsystem + * + * 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); + + /** Set a given channel plan + * + * Used to set application provided channel plan. This API can be used to + * set a single channel as well to the existing channel plan. + * + * @param plan a reference to application channel plan. PHY layer takes a + * copy of the channel parameters provided within. + * + * @return LORAWAN_STATUS_OK if everything goes well otherwise + * a negative error code is returned. + */ + lorawan_status_t set_plan(const lorawan_channelplan_t& plan); + + /** Access the active channel plan + * + * 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 params pointer to active MAC layer parameters. + * + * @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); + + /** Remove the active channel plan + * + * Drops the whole channel list except the 'Default Channels' ofcourse. + * + * @return LORAWAN_STATUS_OK if everything goes well otherwise + * a negative error code is returned. + */ + lorawan_status_t remove_plan(); + + /** Remove a single channel from the plan + * + * @param id the channel id which needs to be removed + * + * @return LORAWAN_STATUS_OK if everything goes well otherwise + * a negative error code is returned. + */ + lorawan_status_t remove_single_channel(uint8_t id); + +private: + + /** + * Local handles + */ + LoRaPHY *_lora_phy; + LoRaMacMib * _mib; +}; + + + +#endif /* MBED_LORAWAN_LORAMACCHANNELPLAN_H_ */ diff --git a/features/lorawan/lorastack/mac/LoRaMacCommand.cpp b/features/lorawan/lorastack/mac/LoRaMacCommand.cpp new file mode 100644 index 0000000000..0fc3356de3 --- /dev/null +++ b/features/lorawan/lorastack/mac/LoRaMacCommand.cpp @@ -0,0 +1,424 @@ +/** + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRa MAC layer implementation + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) + +Copyright (c) 2017, Arm Limited and affiliates. + +SPDX-License-Identifier: BSD-3-Clause +*/ +#include "LoRaMacCommand.h" +#include "LoRaMac.h" + +#if defined(FEATURE_COMMON_PAL) +#include "mbed_trace.h" +#define TRACE_GROUP "LMACC" +#else +#define tr_debug(...) (void(0)) //dummies if feature common pal is not added +#define tr_info(...) (void(0)) //dummies if feature common pal is not added +#define tr_error(...) (void(0)) //dummies if feature common pal is not added +#endif //defined(FEATURE_COMMON_PAL) + +/** + * LoRaMAC max EIRP (dBm) table. + */ +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) +{ + mac_cmd_in_next_tx = false; + mac_cmd_buf_idx = 0; + mac_cmd_buf_idx_to_repeat = 0; + + memset(mac_cmd_buffer, 0, sizeof(mac_cmd_buffer)); + memset(mac_cmd_buffer_to_repeat, 0, sizeof(mac_cmd_buffer_to_repeat)); +} + +LoRaMacCommand::~LoRaMacCommand() +{ +} + +lorawan_status_t LoRaMacCommand::add_mac_command(uint8_t cmd, uint8_t p1, + uint8_t p2) +{ + lorawan_status_t status = LORAWAN_STATUS_BUSY; + // The maximum buffer length must take MAC commands to re-send into account. + const uint8_t bufLen = LORA_MAC_COMMAND_MAX_LENGTH + - mac_cmd_buf_idx_to_repeat; + + switch (cmd) { + case MOTE_MAC_LINK_CHECK_REQ: + if (mac_cmd_buf_idx < bufLen) { + mac_cmd_buffer[mac_cmd_buf_idx++] = cmd; + // No payload for this command + status = LORAWAN_STATUS_OK; + } + break; + case MOTE_MAC_LINK_ADR_ANS: + if (mac_cmd_buf_idx < (bufLen - 1)) { + mac_cmd_buffer[mac_cmd_buf_idx++] = cmd; + // Margin + mac_cmd_buffer[mac_cmd_buf_idx++] = p1; + status = LORAWAN_STATUS_OK; + } + break; + case MOTE_MAC_DUTY_CYCLE_ANS: + if (mac_cmd_buf_idx < bufLen) { + mac_cmd_buffer[mac_cmd_buf_idx++] = cmd; + // No payload for this answer + status = LORAWAN_STATUS_OK; + } + break; + case MOTE_MAC_RX_PARAM_SETUP_ANS: + if (mac_cmd_buf_idx < (bufLen - 1)) { + mac_cmd_buffer[mac_cmd_buf_idx++] = cmd; + // 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(); + status = LORAWAN_STATUS_OK; + } + break; + case MOTE_MAC_DEV_STATUS_ANS: + if (mac_cmd_buf_idx < (bufLen - 2)) { + mac_cmd_buffer[mac_cmd_buf_idx++] = cmd; + // 1st byte Battery + // 2nd byte Margin + mac_cmd_buffer[mac_cmd_buf_idx++] = p1; + mac_cmd_buffer[mac_cmd_buf_idx++] = p2; + status = LORAWAN_STATUS_OK; + } + break; + case MOTE_MAC_NEW_CHANNEL_ANS: + if (mac_cmd_buf_idx < (bufLen - 1)) { + mac_cmd_buffer[mac_cmd_buf_idx++] = cmd; + // Status: Datarate range OK, Channel frequency OK + mac_cmd_buffer[mac_cmd_buf_idx++] = p1; + status = LORAWAN_STATUS_OK; + } + break; + case MOTE_MAC_RX_TIMING_SETUP_ANS: + if (mac_cmd_buf_idx < bufLen) { + 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(); + status = LORAWAN_STATUS_OK; + } + break; + case MOTE_MAC_TX_PARAM_SETUP_ANS: + if (mac_cmd_buf_idx < bufLen) { + mac_cmd_buffer[mac_cmd_buf_idx++] = cmd; + // No payload for this answer + status = LORAWAN_STATUS_OK; + } + break; + case MOTE_MAC_DL_CHANNEL_ANS: + if (mac_cmd_buf_idx < bufLen) { + mac_cmd_buffer[mac_cmd_buf_idx++] = cmd; + // 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(); + status = LORAWAN_STATUS_OK; + } + break; + default: + return LORAWAN_STATUS_SERVICE_UNKNOWN; + } + if (status == LORAWAN_STATUS_OK) { + mac_cmd_in_next_tx = true; + } + return status; +} + +void LoRaMacCommand::clear_command_buffer() +{ + mac_cmd_buf_idx = 0; +} + +uint8_t LoRaMacCommand::get_mac_cmd_length() const +{ + return mac_cmd_buf_idx; +} + +uint8_t *LoRaMacCommand::get_mac_commands_buffer() +{ + return mac_cmd_buffer; +} + +void LoRaMacCommand::parse_mac_commands_to_repeat() +{ + uint8_t i = 0; + uint8_t cmd_cnt = 0; + + for (i = 0; i < mac_cmd_buf_idx; i++) { + switch (mac_cmd_buffer[i]) { + // STICKY + case MOTE_MAC_DL_CHANNEL_ANS: + case MOTE_MAC_RX_PARAM_SETUP_ANS: { // 1 byte payload + mac_cmd_buffer_to_repeat[cmd_cnt++] = mac_cmd_buffer[i++]; + mac_cmd_buffer_to_repeat[cmd_cnt++] = mac_cmd_buffer[i]; + break; + } + case MOTE_MAC_RX_TIMING_SETUP_ANS: { // 0 byte payload + mac_cmd_buffer_to_repeat[cmd_cnt++] = mac_cmd_buffer[i]; + break; + } + // NON-STICKY + case MOTE_MAC_DEV_STATUS_ANS: { // 2 bytes payload + i += 2; + break; + } + case MOTE_MAC_LINK_ADR_ANS: + case MOTE_MAC_NEW_CHANNEL_ANS: { // 1 byte payload + i++; + break; + } + case MOTE_MAC_TX_PARAM_SETUP_ANS: + case MOTE_MAC_DUTY_CYCLE_ANS: + case MOTE_MAC_LINK_CHECK_REQ: { // 0 byte payload + break; + } + default: + break; + } + } + + if (cmd_cnt > 0) { + mac_cmd_in_next_tx = true; + } else { + mac_cmd_in_next_tx = false; + } +} + + +void LoRaMacCommand::clear_repeat_buffer() +{ + mac_cmd_buf_idx_to_repeat = 0; +} + +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; +} + +uint8_t LoRaMacCommand::get_repeat_commands_length() const +{ + return mac_cmd_buf_idx_to_repeat; +} + +void LoRaMacCommand::clear_mac_commands_in_next_tx() +{ + mac_cmd_in_next_tx = false; +} + +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, + loramac_mlme_confirm_t& mlme_conf, + lora_mac_system_params_t &mac_sys_params, + LoRaPHY &lora_phy) +{ + uint8_t status = 0; + lorawan_status_t ret_value = LORAWAN_STATUS_OK; + + while (mac_index < commands_size) { + // Decode Frame MAC commands + switch (payload[mac_index++]) { + case SRV_MAC_LINK_CHECK_ANS: + mlme_conf.status = LORAMAC_EVENT_INFO_STATUS_OK; + mlme_conf.demod_margin = payload[mac_index++]; + mlme_conf.nb_gateways = payload[mac_index++]; + break; + case SRV_MAC_LINK_ADR_REQ: { + adr_req_params_t linkAdrReq; + int8_t linkAdrDatarate = DR_0; + int8_t linkAdrTxPower = TX_POWER_0; + uint8_t linkAdrNbRep = 0; + uint8_t linkAdrNbBytesParsed = 0; + + // Fill parameter structure + linkAdrReq.payload = &payload[mac_index - 1]; + linkAdrReq.payload_size = commands_size - (mac_index - 1); + linkAdrReq.adr_enabled = mac_sys_params.adr_on; + linkAdrReq.ul_dwell_time = mac_sys_params.uplink_dwell_time; + linkAdrReq.current_datarate = mac_sys_params.channel_data_rate; + linkAdrReq.current_tx_power = mac_sys_params.channel_tx_power; + linkAdrReq.current_nb_rep = mac_sys_params.retry_num; + + // Process the ADR requests + status = lora_phy.link_ADR_request(&linkAdrReq, + &linkAdrDatarate, + &linkAdrTxPower, + &linkAdrNbRep, + &linkAdrNbBytesParsed); + + if ((status & 0x07) == 0x07) { + mac_sys_params.channel_data_rate = linkAdrDatarate; + mac_sys_params.channel_tx_power = linkAdrTxPower; + mac_sys_params.retry_num = linkAdrNbRep; + } + + // Add the answers to the buffer + for (uint8_t i = 0; i < (linkAdrNbBytesParsed / 5); i++) { + ret_value = add_mac_command(MOTE_MAC_LINK_ADR_ANS, status, 0); + } + // Update MAC index + mac_index += linkAdrNbBytesParsed - 1; + } + break; + case SRV_MAC_DUTY_CYCLE_REQ: + mac_sys_params.max_duty_cycle = payload[mac_index++]; + mac_sys_params.aggregated_duty_cycle = 1 << mac_sys_params.max_duty_cycle; + ret_value = add_mac_command(MOTE_MAC_DUTY_CYCLE_ANS, 0, 0); + break; + case SRV_MAC_RX_PARAM_SETUP_REQ: { + rx_param_setup_req_t rxParamSetupReq; + + rxParamSetupReq.dr_offset = (payload[mac_index] >> 4) & 0x07; + rxParamSetupReq.datarate = payload[mac_index] & 0x0F; + mac_index++; + + rxParamSetupReq.frequency = (uint32_t) payload[mac_index++]; + rxParamSetupReq.frequency |= (uint32_t) payload[mac_index++] + << 8; + rxParamSetupReq.frequency |= (uint32_t) payload[mac_index++] + << 16; + rxParamSetupReq.frequency *= 100; + + // Perform request on region + status = lora_phy.accept_rx_param_setup_req(&rxParamSetupReq); + + if ((status & 0x07) == 0x07) { + mac_sys_params.rx2_channel.datarate = + rxParamSetupReq.datarate; + mac_sys_params.rx2_channel.frequency = + rxParamSetupReq.frequency; + mac_sys_params.rx1_dr_offset = rxParamSetupReq.dr_offset; + } + ret_value = add_mac_command(MOTE_MAC_RX_PARAM_SETUP_ANS, status, + 0); + } + break; + case SRV_MAC_DEV_STATUS_REQ: { + uint8_t batteryLevel = BAT_LEVEL_NO_MEASURE; + // we don't have a mechanism at the moment to measure + // battery levels + ret_value = add_mac_command(MOTE_MAC_DEV_STATUS_ANS, + batteryLevel, snr); + break; + } + case SRV_MAC_NEW_CHANNEL_REQ: { + new_channel_req_params_t newChannelReq; + channel_params_t chParam; + + newChannelReq.channel_id = payload[mac_index++]; + newChannelReq.new_channel = &chParam; + + chParam.frequency = (uint32_t) payload[mac_index++]; + chParam.frequency |= (uint32_t) payload[mac_index++] << 8; + chParam.frequency |= (uint32_t) payload[mac_index++] << 16; + chParam.frequency *= 100; + chParam.rx1_frequency = 0; + chParam.dr_range.value = payload[mac_index++]; + + status = lora_phy.request_new_channel(&newChannelReq); + + ret_value = add_mac_command(MOTE_MAC_NEW_CHANNEL_ANS, status, 0); + } + break; + case SRV_MAC_RX_TIMING_SETUP_REQ: { + uint8_t delay = payload[mac_index++] & 0x0F; + + if (delay == 0) { + delay++; + } + mac_sys_params.recv_delay1 = delay * 1000; + mac_sys_params.recv_delay2 = mac_sys_params.recv_delay1 + 1000; + ret_value = add_mac_command(MOTE_MAC_RX_TIMING_SETUP_ANS, 0, 0); + } + break; + case SRV_MAC_TX_PARAM_SETUP_REQ: { + tx_param_setup_req_t txParamSetupReq; + uint8_t eirpDwellTime = payload[mac_index++]; + + txParamSetupReq.ul_dwell_time = 0; + txParamSetupReq.dl_dwell_time = 0; + + if ((eirpDwellTime & 0x20) == 0x20) { + txParamSetupReq.dl_dwell_time = 1; + } + if ((eirpDwellTime & 0x10) == 0x10) { + txParamSetupReq.ul_dwell_time = 1; + } + txParamSetupReq.max_eirp = eirpDwellTime & 0x0F; + + // Check the status for correctness + if (lora_phy.accept_tx_param_setup_req(&txParamSetupReq)) { + // Accept command + mac_sys_params.uplink_dwell_time = + txParamSetupReq.ul_dwell_time; + mac_sys_params.downlink_dwell_time = + txParamSetupReq.dl_dwell_time; + mac_sys_params.max_eirp = + max_eirp_table[txParamSetupReq.max_eirp]; + // Add command response + ret_value = add_mac_command(MOTE_MAC_TX_PARAM_SETUP_ANS, 0, 0); + } + } + break; + case SRV_MAC_DL_CHANNEL_REQ: { + dl_channel_req_params_t dlChannelReq; + + dlChannelReq.channel_id = payload[mac_index++]; + dlChannelReq.rx1_frequency = (uint32_t) payload[mac_index++]; + dlChannelReq.rx1_frequency |= (uint32_t) payload[mac_index++] << 8; + dlChannelReq.rx1_frequency |= (uint32_t) payload[mac_index++] << 16; + dlChannelReq.rx1_frequency *= 100; + + status = lora_phy.dl_channel_request(&dlChannelReq); + + ret_value = add_mac_command(MOTE_MAC_DL_CHANNEL_ANS, status, 0); + } + break; + default: + // Unknown command. ABORT MAC commands processing + ret_value = LORAWAN_STATUS_UNSUPPORTED; + } + } + return ret_value; +} + +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 new file mode 100644 index 0000000000..3aa54c633d --- /dev/null +++ b/features/lorawan/lorastack/mac/LoRaMacCommand.h @@ -0,0 +1,180 @@ +/** + * \file LoRaMacCommand.h + * + * \brief LoRa MAC layer implementation + * + * \copyright Revised BSD License, see LICENSE.TXT file include in the project + * + * \code + * ______ _ + * / _____) _ | | + * ( (____ _____ ____ _| |_ _____ ____| |__ + * \____ \| ___ | (_ _) ___ |/ ___) _ \ + * _____) ) ____| | | || |_| ____( (___| | | | + * (______/|_____)_|_|_| \__)_____)\____)_| |_| + * (C)2013 Semtech + * + * ___ _____ _ ___ _ _____ ___ ___ ___ ___ + * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| + * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| + * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| + * embedded.connectivity.solutions=============== + * + * \endcode + * + * \author Miguel Luis ( Semtech ) + * + * \author Gregory Cristian ( Semtech ) + * + * \author Daniel Jaeckle ( STACKFORCE ) + * + * \defgroup LORAMAC LoRa MAC layer implementation + * This module specifies the API implementation of the LoRaMAC layer. + * This is a placeholder for a detailed description of the LoRaMac + * layer and the supported features. + * + * Copyright (c) 2017, Arm Limited and affiliates. + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#ifndef __LORAMACCOMMAND_H__ +#define __LORAMACCOMMAND_H__ + +#include +#include "lorawan/system/lorawan_data_structures.h" +#include "lorawan/lorastack/phy/LoRaPHY.h" + +/*! + * Maximum MAC commands buffer size + */ +#define LORA_MAC_COMMAND_MAX_LENGTH 128 + +class LoRaMac; + +class LoRaMacCommand { + +public: + LoRaMacCommand(LoRaMac &lora_mac); + ~LoRaMacCommand(); + + /** + * @brief Adds a new MAC command to be sent. + * + * @remark MAC layer internal function + * + * @param [in] cmd MAC command to be added + * [MOTE_MAC_LINK_CHECK_REQ, + * MOTE_MAC_LINK_ADR_ANS, + * MOTE_MAC_DUTY_CYCLE_ANS, + * MOTE_MAC_RX2_PARAM_SET_ANS, + * MOTE_MAC_DEV_STATUS_ANS + * MOTE_MAC_NEW_CHANNEL_ANS] + * @param [in] p1 1st parameter (optional depends on the command) + * @param [in] p2 2nd parameter (optional depends on the command) + * + * @return status Function status [0: OK, 1: Unknown command, 2: Buffer full] + */ + lorawan_status_t add_mac_command(uint8_t cmd, uint8_t p1, uint8_t p2); + + /** + * @brief Clear MAC command buffer. + */ + void clear_command_buffer(void); + + /** + * @brief Get the length of MAC commands + * + * @return status Length of used MAC buffer (bytes) + */ + uint8_t get_mac_cmd_length() const; + + /** + * @brief Get MAC command buffer + * + * @return Pointer to MAC command buffer + */ + uint8_t *get_mac_commands_buffer(); + + /** + * @brief Parses the MAC commands which must be resent. + */ + void parse_mac_commands_to_repeat(); + + /** + * @brief Clear MAC command repeat buffer. + */ + void clear_repeat_buffer(); + + /** + * @brief Copy MAC commands from repeat buffer to actual MAC command buffer. + */ + void copy_repeat_commands_to_buffer(); + + /** + * @brief Get the length of MAC commands in repeat buffer + * + * @return status Length of used MAC Repeat buffer (bytes) + */ + uint8_t get_repeat_commands_length() const; + + /** + * @brief Clear MAC commands in next TX. + */ + void clear_mac_commands_in_next_tx(); + + /** + * @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] + */ + bool is_mac_command_in_next_tx() const; + + /** + * @brief Decodes MAC commands in the fOpts field and in the payload + * + * @return status Function status. LORAWAN_STATUS_OK if command successful. + */ + lorawan_status_t 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_params, + LoRaPHY& lora_phy); + + /** + * @brief Verifies if sticky MAC commands are pending. + * + * @return [true: sticky MAC commands pending, false: No MAC commands pending] + */ + 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; + + /** + * Contains the current Mac command buffer index in 'mac_cmd_buffer' + */ + uint8_t mac_cmd_buf_idx; + + /** + * Contains the current Mac command buffer index for MAC commands to repeat in + * 'mac_cmd_buffer_to_repeat' + */ + uint8_t mac_cmd_buf_idx_to_repeat; + + /** + * Buffer containing the MAC layer commands + */ + uint8_t mac_cmd_buffer[LORA_MAC_COMMAND_MAX_LENGTH]; + + /** + * Buffer containing the MAC layer commands which must be repeated + */ + uint8_t mac_cmd_buffer_to_repeat[LORA_MAC_COMMAND_MAX_LENGTH]; +}; + +#endif //__LORAMACCOMMAND_H__ diff --git a/features/lorawan/lorastack/mac/LoRaMacCrypto.cpp b/features/lorawan/lorastack/mac/LoRaMacCrypto.cpp index 56998c36a9..75deb84197 100644 --- a/features/lorawan/lorastack/mac/LoRaMacCrypto.cpp +++ b/features/lorawan/lorastack/mac/LoRaMacCrypto.cpp @@ -41,251 +41,253 @@ /** * MIC field computation initial data */ -static uint8_t MicBlockB0[] = { 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; +static uint8_t mic_block_b0[] = {0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; /** * Contains the computed MIC field. * * \remark Only the 4 first bytes are used */ -static uint8_t Mic[16]; +static uint8_t computed_mic[16]; /** * Encryption aBlock and sBlock */ -static uint8_t aBlock[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; -static uint8_t sBlock[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; +static uint8_t a_block[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static uint8_t s_block[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; /** * AES computation context variable */ -static mbedtls_aes_context AesContext; +static mbedtls_aes_context aes_ctx; /** * CMAC computation context variable */ -static mbedtls_cipher_context_t AesCmacCtx[1]; +static mbedtls_cipher_context_t aes_cmac_ctx[1]; #define AES_CMAC_KEY_LENGTH 16 -/** - * \brief Computes the LoRaMAC frame MIC field - * - * \param [in] buffer Data buffer - * \param [in] size Data buffer size - * \param [in] key AES key to be used - * \param [in] address Frame address - * \param [in] dir Frame direction [0: uplink, 1: downlink] - * \param [in] sequenceCounter Frame sequence counter - * \param [out] mic Computed MIC field - */ -int LoRaMacComputeMic( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint32_t *mic ) +int compute_mic(const uint8_t *buffer, uint16_t size, const uint8_t *key, + uint32_t address, uint8_t dir, uint32_t seq_counter, + uint32_t *mic) { int ret = 0; - MicBlockB0[5] = dir; + mic_block_b0[5] = dir; - MicBlockB0[6] = ( address ) & 0xFF; - MicBlockB0[7] = ( address >> 8 ) & 0xFF; - MicBlockB0[8] = ( address >> 16 ) & 0xFF; - MicBlockB0[9] = ( address >> 24 ) & 0xFF; + mic_block_b0[6] = (address) & 0xFF; + mic_block_b0[7] = (address >> 8) & 0xFF; + mic_block_b0[8] = (address >> 16) & 0xFF; + mic_block_b0[9] = (address >> 24) & 0xFF; - MicBlockB0[10] = ( sequenceCounter ) & 0xFF; - MicBlockB0[11] = ( sequenceCounter >> 8 ) & 0xFF; - MicBlockB0[12] = ( sequenceCounter >> 16 ) & 0xFF; - MicBlockB0[13] = ( sequenceCounter >> 24 ) & 0xFF; + mic_block_b0[10] = (seq_counter) & 0xFF; + mic_block_b0[11] = (seq_counter >> 8) & 0xFF; + mic_block_b0[12] = (seq_counter >> 16) & 0xFF; + mic_block_b0[13] = (seq_counter >> 24) & 0xFF; - MicBlockB0[15] = size & 0xFF; + mic_block_b0[15] = size & 0xFF; + + mbedtls_cipher_init(aes_cmac_ctx); - mbedtls_cipher_init(AesCmacCtx); const mbedtls_cipher_info_t* cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB); + if (NULL != cipher_info) { - ret = mbedtls_cipher_setup(AesCmacCtx, cipher_info); + ret = mbedtls_cipher_setup(aes_cmac_ctx, cipher_info); if (0 != ret) goto exit; - ret = mbedtls_cipher_cmac_starts(AesCmacCtx, key, AES_CMAC_KEY_LENGTH*8); + ret = mbedtls_cipher_cmac_starts(aes_cmac_ctx, key, + AES_CMAC_KEY_LENGTH * 8); if (0 != ret) goto exit; - ret = mbedtls_cipher_cmac_update(AesCmacCtx, MicBlockB0, LORAMAC_MIC_BLOCK_B0_SIZE); + ret = mbedtls_cipher_cmac_update(aes_cmac_ctx, mic_block_b0, + LORAMAC_MIC_BLOCK_B0_SIZE); if (0 != ret) goto exit; - ret = mbedtls_cipher_cmac_update(AesCmacCtx, buffer, size & 0xFF); + ret = mbedtls_cipher_cmac_update(aes_cmac_ctx, buffer, size & 0xFF); if (0 != ret) goto exit; - ret = mbedtls_cipher_cmac_finish(AesCmacCtx, Mic); + ret = mbedtls_cipher_cmac_finish(aes_cmac_ctx, computed_mic); if (0 != ret) goto exit; - *mic = ( uint32_t )( ( uint32_t )Mic[3] << 24 | ( uint32_t )Mic[2] << 16 | ( uint32_t )Mic[1] << 8 | ( uint32_t )Mic[0] ); + *mic = (uint32_t) ((uint32_t) computed_mic[3] << 24 + | (uint32_t) computed_mic[2] << 16 + | (uint32_t) computed_mic[1] << 8 | (uint32_t) computed_mic[0]); } else { ret = MBEDTLS_ERR_CIPHER_ALLOC_FAILED; } -exit: - mbedtls_cipher_free( AesCmacCtx ); +exit: mbedtls_cipher_free(aes_cmac_ctx); return ret; } -int LoRaMacPayloadEncrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint8_t *encBuffer ) +int encrypt_payload(const uint8_t *buffer, uint16_t size, const uint8_t *key, + uint32_t address, uint8_t dir, uint32_t seq_counter, + uint8_t *enc_buffer) { uint16_t i; uint8_t bufferIndex = 0; uint16_t ctr = 1; int ret = 0; - mbedtls_aes_init(&AesContext); - ret = mbedtls_aes_setkey_enc(&AesContext, key, 16*8); + mbedtls_aes_init(&aes_ctx); + ret = mbedtls_aes_setkey_enc(&aes_ctx, key, 16 * 8); if (0 != ret) goto exit; - aBlock[5] = dir; + a_block[5] = dir; - aBlock[6] = ( address ) & 0xFF; - aBlock[7] = ( address >> 8 ) & 0xFF; - aBlock[8] = ( address >> 16 ) & 0xFF; - aBlock[9] = ( address >> 24 ) & 0xFF; + a_block[6] = (address) & 0xFF; + a_block[7] = (address >> 8) & 0xFF; + a_block[8] = (address >> 16) & 0xFF; + a_block[9] = (address >> 24) & 0xFF; - aBlock[10] = ( sequenceCounter ) & 0xFF; - aBlock[11] = ( sequenceCounter >> 8 ) & 0xFF; - aBlock[12] = ( sequenceCounter >> 16 ) & 0xFF; - aBlock[13] = ( sequenceCounter >> 24 ) & 0xFF; + a_block[10] = (seq_counter) & 0xFF; + a_block[11] = (seq_counter >> 8) & 0xFF; + a_block[12] = (seq_counter >> 16) & 0xFF; + a_block[13] = (seq_counter >> 24) & 0xFF; - while( size >= 16 ) - { - aBlock[15] = ( ( ctr ) & 0xFF ); + while (size >= 16) { + a_block[15] = ((ctr) & 0xFF); ctr++; - ret = mbedtls_aes_crypt_ecb(&AesContext, MBEDTLS_AES_ENCRYPT, aBlock, sBlock); + ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, a_block, + s_block); if (0 != ret) goto exit; - for( i = 0; i < 16; i++ ) - { - encBuffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i]; + for (i = 0; i < 16; i++) { + enc_buffer[bufferIndex + i] = buffer[bufferIndex + i] ^ s_block[i]; } size -= 16; bufferIndex += 16; } - if( size > 0 ) - { - aBlock[15] = ( ( ctr ) & 0xFF ); - ret = mbedtls_aes_crypt_ecb(&AesContext, MBEDTLS_AES_ENCRYPT, aBlock, sBlock); + if (size > 0) { + a_block[15] = ((ctr) & 0xFF); + ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, a_block, + s_block); if (0 != ret) goto exit; - for( i = 0; i < size; i++ ) - { - encBuffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i]; + for (i = 0; i < size; i++) { + enc_buffer[bufferIndex + i] = buffer[bufferIndex + i] ^ s_block[i]; } } -exit: - mbedtls_aes_free(&AesContext); +exit: mbedtls_aes_free(&aes_ctx); return ret; } -int LoRaMacPayloadDecrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint8_t *decBuffer ) +int decrypt_payload(const uint8_t *buffer, uint16_t size, const uint8_t *key, + uint32_t address, uint8_t dir, uint32_t seq_counter, + uint8_t *dec_buffer) { - return LoRaMacPayloadEncrypt( buffer, size, key, address, dir, sequenceCounter, decBuffer ); + return encrypt_payload(buffer, size, key, address, dir, seq_counter, + dec_buffer); } -int LoRaMacJoinComputeMic( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t *mic ) +int compute_join_frame_mic(const uint8_t *buffer, uint16_t size, + const uint8_t *key, uint32_t *mic) { int ret = 0; - mbedtls_cipher_init(AesCmacCtx); + mbedtls_cipher_init(aes_cmac_ctx); const mbedtls_cipher_info_t* cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB); + if (NULL != cipher_info) { - ret = mbedtls_cipher_setup(AesCmacCtx, cipher_info); + ret = mbedtls_cipher_setup(aes_cmac_ctx, cipher_info); if (0 != ret) goto exit; - ret = mbedtls_cipher_cmac_starts(AesCmacCtx, key, AES_CMAC_KEY_LENGTH*8); + ret = mbedtls_cipher_cmac_starts(aes_cmac_ctx, key, + AES_CMAC_KEY_LENGTH * 8); if (0 != ret) goto exit; - ret = mbedtls_cipher_cmac_update(AesCmacCtx, buffer, size & 0xFF); + ret = mbedtls_cipher_cmac_update(aes_cmac_ctx, buffer, size & 0xFF); if (0 != ret) goto exit; - ret = mbedtls_cipher_cmac_finish(AesCmacCtx, Mic); + ret = mbedtls_cipher_cmac_finish(aes_cmac_ctx, computed_mic); if (0 != ret) goto exit; - *mic = ( uint32_t )( ( uint32_t )Mic[3] << 24 | ( uint32_t )Mic[2] << 16 | ( uint32_t )Mic[1] << 8 | ( uint32_t )Mic[0] ); + *mic = (uint32_t) ((uint32_t) computed_mic[3] << 24 + | (uint32_t) computed_mic[2] << 16 + | (uint32_t) computed_mic[1] << 8 | (uint32_t) computed_mic[0]); } else { ret = MBEDTLS_ERR_CIPHER_ALLOC_FAILED; } -exit: - mbedtls_cipher_free(AesCmacCtx); +exit: mbedtls_cipher_free(aes_cmac_ctx); return ret; } -int LoRaMacJoinDecrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint8_t *decBuffer ) +int decrypt_join_frame(const uint8_t *buffer, uint16_t size, const uint8_t *key, + uint8_t *dec_buffer) { int ret = 0; - mbedtls_aes_init(&AesContext); + mbedtls_aes_init(&aes_ctx); - ret = mbedtls_aes_setkey_enc(&AesContext, key, 16*8); + ret = mbedtls_aes_setkey_enc(&aes_ctx, key, 16 * 8); if (0 != ret) goto exit; - ret = mbedtls_aes_crypt_ecb(&AesContext, MBEDTLS_AES_ENCRYPT, buffer, decBuffer); + ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, buffer, + dec_buffer); if (0 != ret) goto exit; // Check if optional CFList is included - if( size >= 16 ) - { - ret = mbedtls_aes_crypt_ecb(&AesContext, MBEDTLS_AES_ENCRYPT, buffer + 16, decBuffer + 16); + if (size >= 16) { + ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, buffer + 16, + dec_buffer + 16); } -exit: - mbedtls_aes_free(&AesContext); +exit: mbedtls_aes_free(&aes_ctx); return ret; } -int LoRaMacJoinComputeSKeys( const uint8_t *key, const uint8_t *appNonce, uint16_t devNonce, uint8_t *nwkSKey, uint8_t *appSKey ) +int compute_skeys_for_join_frame(const uint8_t *key, const uint8_t *app_nonce, + uint16_t dev_nonce, uint8_t *nwk_skey, + uint8_t *app_skey) { uint8_t nonce[16]; - uint8_t *pDevNonce = ( uint8_t * )&devNonce; + uint8_t *p_dev_nonce = (uint8_t *) &dev_nonce; int ret = 0; - mbedtls_aes_init(&AesContext); + mbedtls_aes_init(&aes_ctx); - ret = mbedtls_aes_setkey_enc(&AesContext, key, 16*8); + ret = mbedtls_aes_setkey_enc(&aes_ctx, key, 16 * 8); if (0 != ret) goto exit; - memset( nonce, 0, sizeof( nonce ) ); + memset(nonce, 0, sizeof(nonce)); nonce[0] = 0x01; - memcpy( nonce + 1, appNonce, 6 ); - memcpy( nonce + 7, pDevNonce, 2 ); - ret = mbedtls_aes_crypt_ecb(&AesContext, MBEDTLS_AES_ENCRYPT, nonce, nwkSKey); + memcpy(nonce + 1, app_nonce, 6); + memcpy(nonce + 7, p_dev_nonce, 2); + ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, nonce, nwk_skey); if (0 != ret) goto exit; - memset( nonce, 0, sizeof( nonce ) ); + memset(nonce, 0, sizeof(nonce)); nonce[0] = 0x02; - memcpy( nonce + 1, appNonce, 6 ); - memcpy( nonce + 7, pDevNonce, 2 ); - ret = mbedtls_aes_crypt_ecb(&AesContext, MBEDTLS_AES_ENCRYPT, nonce, appSKey); + memcpy(nonce + 1, app_nonce, 6); + memcpy(nonce + 7, p_dev_nonce, 2); + ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, nonce, app_skey); -exit: - mbedtls_aes_free(&AesContext); + exit: mbedtls_aes_free(&aes_ctx); return ret; } #else @@ -294,54 +296,56 @@ exit: // user knows what is wrong and in addition to that these ensure that // Mbed-OS compiles properly under normal conditions where LoRaWAN in conjunction // with mbedTLS is not being used. -int LoRaMacComputeMic( const uint8_t *, uint16_t , const uint8_t *, uint32_t, - uint8_t dir, uint32_t, uint32_t * ) +int compute_mic(const uint8_t *, uint16_t , const uint8_t *, uint32_t, + uint8_t dir, uint32_t, uint32_t *) { MBED_ASSERT("[LoRaCrypto] Must enable AES, CMAC & CIPHER from mbedTLS"); // Never actually reaches here - return LORA_MAC_STATUS_CRYPTO_FAIL; + return LORAWAN_STATUS_CRYPTO_FAIL; } -int LoRaMacPayloadEncrypt( const uint8_t *, uint16_t , const uint8_t *, uint32_t, - uint8_t , uint32_t , uint8_t * ) +int encrypt_payload(const uint8_t *, uint16_t , const uint8_t *, uint32_t, + uint8_t , uint32_t , uint8_t *) { MBED_ASSERT("[LoRaCrypto] Must enable AES, CMAC & CIPHER from mbedTLS"); // Never actually reaches here - return LORA_MAC_STATUS_CRYPTO_FAIL; + return LORAWAN_STATUS_CRYPTO_FAIL; } -int LoRaMacPayloadDecrypt( const uint8_t *, uint16_t , const uint8_t *, uint32_t, - uint8_t , uint32_t , uint8_t * ) +int decrypt_payload(const uint8_t *, uint16_t , const uint8_t *, uint32_t, + uint8_t , uint32_t , uint8_t *) { MBED_ASSERT("[LoRaCrypto] Must enable AES, CMAC & CIPHER from mbedTLS"); // Never actually reaches here - return LORA_MAC_STATUS_CRYPTO_FAIL; -} -int LoRaMacJoinComputeMic( const uint8_t *, uint16_t , const uint8_t *, uint32_t * ) -{ - MBED_ASSERT("[LoRaCrypto] Must enable AES, CMAC & CIPHER from mbedTLS"); - - // Never actually reaches here - return LORA_MAC_STATUS_CRYPTO_FAIL; + return LORAWAN_STATUS_CRYPTO_FAIL; } -int LoRaMacJoinDecrypt( const uint8_t *, uint16_t , const uint8_t *, uint8_t * ) +int compute_join_frame_mic(const uint8_t *, uint16_t , const uint8_t *, uint32_t *) { MBED_ASSERT("[LoRaCrypto] Must enable AES, CMAC & CIPHER from mbedTLS"); // Never actually reaches here - return LORA_MAC_STATUS_CRYPTO_FAIL; + return LORAWAN_STATUS_CRYPTO_FAIL; } -int LoRaMacJoinComputeSKeys( const uint8_t *, const uint8_t *, uint16_t , uint8_t *, uint8_t * ) +int decrypt_join_frame(const uint8_t *, uint16_t , const uint8_t *, uint8_t *) { MBED_ASSERT("[LoRaCrypto] Must enable AES, CMAC & CIPHER from mbedTLS"); // Never actually reaches here - return LORA_MAC_STATUS_CRYPTO_FAIL; + return LORAWAN_STATUS_CRYPTO_FAIL; +} + +int compute_skeys_for_join_frame(const uint8_t *, const uint8_t *, uint16_t , + uint8_t *, uint8_t *) +{ + MBED_ASSERT("[LoRaCrypto] Must enable AES, CMAC & CIPHER from mbedTLS"); + + // Never actually reaches here + return LORAWAN_STATUS_CRYPTO_FAIL; } #endif diff --git a/features/lorawan/lorastack/mac/LoRaMacCrypto.h b/features/lorawan/lorastack/mac/LoRaMacCrypto.h index cf09becb3e..af3fa75cff 100644 --- a/features/lorawan/lorastack/mac/LoRaMacCrypto.h +++ b/features/lorawan/lorastack/mac/LoRaMacCrypto.h @@ -29,84 +29,94 @@ SPDX-License-Identifier: BSD-3-Clause /** * Computes the LoRaMAC frame MIC field * - * \param [in] buffer - Data buffer - * \param [in] size - Data buffer size - * \param [in] key - AES key to be used - * \param [in] address - Frame address - * \param [in] dir - Frame direction [0: uplink, 1: downlink] - * \param [in] sequenceCounter - Frame sequence counter - * \param [out] mic - Computed MIC field + * @param [in] buffer - Data buffer + * @param [in] size - Data buffer size + * @param [in] key - AES key to be used + * @param [in] address - Frame address + * @param [in] dir - Frame direction [0: uplink, 1: downlink] + * @param [in] seq_counter - Frame sequence counter + * @param [out] mic - Computed MIC field * - * \return 0 if successful, or a cipher specific error code + * @return 0 if successful, or a cipher specific error code */ -int LoRaMacComputeMic( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint32_t *mic ); +int compute_mic(const uint8_t *buffer, uint16_t size, const uint8_t *key, + uint32_t address, uint8_t dir, uint32_t seq_counter, + uint32_t *mic); /** - * Computes the LoRaMAC payload encryption + * Performs payload encryption * - * \param [in] buffer - Data buffer - * \param [in] size - Data buffer size - * \param [in] key - AES key to be used - * \param [in] address - Frame address - * \param [in] dir - Frame direction [0: uplink, 1: downlink] - * \param [in] sequenceCounter - Frame sequence counter - * \param [out] encBuffer - Encrypted buffer + * @param [in] buffer - Data buffer + * @param [in] size - Data buffer size + * @param [in] key - AES key to be used + * @param [in] address - Frame address + * @param [in] dir - Frame direction [0: uplink, 1: downlink] + * @param [in] seq_counter - Frame sequence counter + * @param [out] enc_buffer - Encrypted buffer * - * \return 0 if successful, or a cipher specific error code + * @return 0 if successful, or a cipher specific error code */ -int LoRaMacPayloadEncrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint8_t *encBuffer ); +int encrypt_payload(const uint8_t *buffer, uint16_t size, const uint8_t *key, + uint32_t address, uint8_t dir, uint32_t seq_counter, + uint8_t *enc_buffer); /** - * Computes the LoRaMAC payload decryption + * Performs payload decryption * - * \param [in] buffer - Data buffer - * \param [in] size - Data buffer size - * \param [in] key - AES key to be used - * \param [in] address - Frame address - * \param [in] dir - Frame direction [0: uplink, 1: downlink] - * \param [in] sequenceCounter - Frame sequence counter - * \param [out] decBuffer - Decrypted buffer + * @param [in] buffer - Data buffer + * @param [in] size - Data buffer size + * @param [in] key - AES key to be used + * @param [in] address - Frame address + * @param [in] dir - Frame direction [0: uplink, 1: downlink] + * @param [in] seq_counter - Frame sequence counter + * @param [out] dec_buffer - Decrypted buffer * - * \return 0 if successful, or a cipher specific error code + * @return 0 if successful, or a cipher specific error code */ -int LoRaMacPayloadDecrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t address, uint8_t dir, uint32_t sequenceCounter, uint8_t *decBuffer ); +int decrypt_payload(const uint8_t *buffer, uint16_t size, const uint8_t *key, + uint32_t address, uint8_t dir, uint32_t seq_counter, + uint8_t *dec_buffer); /** * Computes the LoRaMAC Join Request frame MIC field * - * \param [in] buffer - Data buffer - * \param [in] size - Data buffer size - * \param [in] key - AES key to be used - * \param [out] mic - Computed MIC field + * @param [in] buffer - Data buffer + * @param [in] size - Data buffer size + * @param [in] key - AES key to be used + * @param [out] mic - Computed MIC field * - * \return 0 if successful, or a cipher specific error code + * @return 0 if successful, or a cipher specific error code * */ -int LoRaMacJoinComputeMic( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint32_t *mic ); +int compute_join_frame_mic(const uint8_t *buffer, uint16_t size, + const uint8_t *key, uint32_t *mic); /** * Computes the LoRaMAC join frame decryption * - * \param [in] buffer - Data buffer - * \param [in] size - Data buffer size - * \param [in] key - AES key to be used - * \param [out] decBuffer - Decrypted buffer + * @param [in] buffer - Data buffer + * @param [in] size - Data buffer size + * @param [in] key - AES key to be used + * @param [out] dec_buffer - Decrypted buffer * - * \return 0 if successful, or a cipher specific error code + * @return 0 if successful, or a cipher specific error code */ -int LoRaMacJoinDecrypt( const uint8_t *buffer, uint16_t size, const uint8_t *key, uint8_t *decBuffer ); +int decrypt_join_frame(const uint8_t *buffer, uint16_t size, + const uint8_t *key, uint8_t *dec_buffer); /** * Computes the LoRaMAC join frame decryption * - * \param [in] key - AES key to be used - * \param [in] appNonce - Application nonce - * \param [in] devNonce - Device nonce - * \param [out] nwkSKey - Network session key - * \param [out] appSKey - Application session key + * @param [in] key - AES key to be used + * @param [in] app_nonce - Application nonce + * @param [in] dev_nonce - Device nonce + * @param [out] nwk_skey - Network session key + * @param [out] app_skey - Application session key * - * \return 0 if successful, or a cipher specific error code + * @return 0 if successful, or a cipher specific error code */ -int LoRaMacJoinComputeSKeys( const uint8_t *key, const uint8_t *appNonce, uint16_t devNonce, uint8_t *nwkSKey, uint8_t *appSKey ); +int compute_skeys_for_join_frame(const uint8_t *key, const uint8_t *app_nonce, + uint16_t dev_nonce, uint8_t *nwk_skey, + uint8_t *app_skey ); #endif // MBED_LORAWAN_MAC_LORAMAC_CRYPTO_H__ diff --git a/features/lorawan/lorastack/mac/LoRaMacMcps.cpp b/features/lorawan/lorastack/mac/LoRaMacMcps.cpp new file mode 100644 index 0000000000..c5331abd16 --- /dev/null +++ b/features/lorawan/lorastack/mac/LoRaMacMcps.cpp @@ -0,0 +1,146 @@ +/** + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRaWAN stack layer that controls both MAC and PHY underneath + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) + + +Copyright (c) 2017, Arm Limited and affiliates. + +SPDX-License-Identifier: BSD-3-Clause +*/ + +#include "LoRaMac.h" +#include "lorastack/mac/LoRaMacMcps.h" + +LoRaMacMcps::LoRaMacMcps() +: _lora_mac(NULL), _lora_phy(NULL) +{ +} + +LoRaMacMcps::~LoRaMacMcps() +{ +} + +void LoRaMacMcps::activate_mcps_subsystem(LoRaMac *mac, LoRaPHY *phy) +{ + _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; + } + } + + 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; + } + } + + return status; +} diff --git a/features/lorawan/lorastack/mac/LoRaMacMcps.h b/features/lorawan/lorastack/mac/LoRaMacMcps.h new file mode 100644 index 0000000000..3989c66753 --- /dev/null +++ b/features/lorawan/lorastack/mac/LoRaMacMcps.h @@ -0,0 +1,113 @@ +/** + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRaWAN stack layer that controls both MAC and PHY underneath + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) + + +Copyright (c) 2017, Arm Limited and affiliates. + +SPDX-License-Identifier: BSD-3-Clause +*/ + +#ifndef MBED_OS_LORAWAN_MAC_MCPS_H_ +#define MBED_OS_LORAWAN_MAC_MCPS_H_ + +#include "lorawan/system/lorawan_data_structures.h" +#include "lorastack/phy/LoRaPHY.h" + +// forward declaration +class LoRaMac; + +class LoRaMacMcps { + +public: + + /** Constructor + * + * Sets local handles to NULL. These handles will be set when the subsystem + * is activated by the MAC layer. + */ + LoRaMacMcps(); + + /** Destructor + * + * Does nothing + */ + ~LoRaMacMcps(); + + /** Activating MCPS subsystem + * + * Stores pointers to MAC and PHY layer handles + * + * @param mac pointer to MAC layer + * @param phy pointer to PHY layer + */ + void activate_mcps_subsystem(LoRaMac *mac, LoRaPHY *phy); + + /** 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 + * + * @return LORAWAN_STATUS_OK if everything goes well otherwise + * a negative error code is returned. + */ + lorawan_status_t set_request(loramac_mcps_req_t *mcpsRequest, loramac_protocol_params *params); + + /** Grants access to MCPS confirmation data + * + * @return a reference to MCPS confirm data structure + */ + inline loramac_mcps_confirm_t& get_confirmation() + { + return confirmation; + } + + /** Grants access to MCPS indication data + * + * @return a reference to MCPS indication data structure + */ + inline loramac_mcps_indication_t& get_indication() + { + return indication; + } + + +private: + + /** + * Pointers to MAC and PHY handles + */ + LoRaMac *_lora_mac; + LoRaPHY *_lora_phy; + + /** + * Structure to hold MCPS indication data. + */ + loramac_mcps_indication_t indication; + + /** + * Structure to hold MCPS confirm data. + */ + loramac_mcps_confirm_t confirmation; +}; + +#endif /* MBED_OS_LORAWAN_MAC_MCPS_H_ */ diff --git a/features/lorawan/lorastack/mac/LoRaMacMib.cpp b/features/lorawan/lorastack/mac/LoRaMacMib.cpp new file mode 100644 index 0000000000..39172ef569 --- /dev/null +++ b/features/lorawan/lorastack/mac/LoRaMacMib.cpp @@ -0,0 +1,462 @@ +/** + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRaWAN stack layer that controls both MAC and PHY underneath + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) + + +Copyright (c) 2017, Arm Limited and affiliates. + +SPDX-License-Identifier: BSD-3-Clause +*/ + +#include "lorastack/mac/LoRaMac.h" +#include "lorastack/mac/LoRaMacMib.h" + +LoRaMacMib::LoRaMacMib() +: _lora_mac(NULL), _lora_phy(NULL) +{ +} + +LoRaMacMib::~LoRaMacMib() +{ +} + +void LoRaMacMib::activate_mib_subsystem(LoRaMac *mac, 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) { + return LORAWAN_STATUS_PARAMETER_INVALID; + } + + lorawan_status_t status = LORAWAN_STATUS_OK; + verification_params_t verify; + + + switch (mibSet->type) { + case MIB_DEVICE_CLASS: { + params->dev_class = mibSet->param.dev_class; + switch (params->dev_class) { + case CLASS_A: { + // Set the radio into sleep to setup a defined state + _lora_phy->put_radio_to_sleep(); + break; + } + case CLASS_B: { + break; + } + case CLASS_C: { + // Set the is_node_ack_requested indicator to default + params->is_node_ack_requested = false; + // Set the radio into sleep mode in case we are still in RX mode + _lora_phy->put_radio_to_sleep(); + // Compute Rx2 windows parameters in case the RX2 datarate has changed + _lora_phy->compute_rx_win_params( + params->sys_params.rx2_channel.datarate, + params->sys_params.min_rx_symb, + params->sys_params.max_sys_rx_error, + ¶ms->rx_window2_config); + _lora_mac->open_continuous_rx2_window(); + break; + } + } + break; + } + case MIB_NETWORK_JOINED: { + params->is_nwk_joined = mibSet->param.is_nwk_joined; + break; + } + case MIB_ADR: { + params->sys_params.adr_on = mibSet->param.is_adr_enable; + break; + } + case MIB_NET_ID: { + params->net_id = mibSet->param.net_id; + break; + } + case MIB_DEV_ADDR: { + params->dev_addr = mibSet->param.dev_addr; + break; + } + case MIB_NWK_SKEY: { + if (mibSet->param.nwk_skey != NULL) { + memcpy(params->keys.nwk_skey, mibSet->param.nwk_skey, + sizeof(params->keys.nwk_skey)); + } else { + status = LORAWAN_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_APP_SKEY: { + if (mibSet->param.app_skey != NULL) { + memcpy(params->keys.app_skey, mibSet->param.app_skey, + sizeof(params->keys.app_skey)); + } else { + status = LORAWAN_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_PUBLIC_NETWORK: { + params->is_nwk_public = mibSet->param.enable_public_nwk; + _lora_phy->setup_public_network_mode(params->is_nwk_public); + break; + } + case MIB_REPEATER_SUPPORT: { + params->is_repeater_supported = mibSet->param.enable_repeater_support; + break; + } + case MIB_RX2_CHANNEL: { + verify.datarate = mibSet->param.rx2_channel.datarate; + + if (_lora_phy->verify(&verify, PHY_RX_DR) == true) { + params->sys_params.rx2_channel = mibSet->param.rx2_channel; + + if ((params->dev_class == CLASS_C) + && (params->is_nwk_joined == true)) { + // We can only compute the RX window parameters directly, if we are already + // in class c mode and joined. We cannot setup an RX window in case of any other + // class type. + // Set the radio into sleep mode in case we are still in RX mode + _lora_phy->put_radio_to_sleep(); + // Compute Rx2 windows parameters + _lora_phy->compute_rx_win_params( + params->sys_params.rx2_channel.datarate, + 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; + } + break; + } + case MIB_RX2_DEFAULT_CHANNEL: { + verify.datarate = mibSet->param.rx2_channel.datarate; + + if (_lora_phy->verify(&verify, PHY_RX_DR) == true) { + params->sys_params.rx2_channel = mibSet->param.default_rx2_channel; + } else { + status = LORAWAN_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_CHANNELS_DEFAULT_MASK: + case MIB_CHANNELS_MASK: { + // channel masks must not be tempered with. + // They should be manipulated only on request with certain + // APIs like add_channel() and remove_channel() + // You should be able to get these MIB parameters, not set + status = LORAWAN_STATUS_SERVICE_UNKNOWN; + break; + } + case MIB_CHANNELS_NB_REP: { + if ((mibSet->param.channel_nb_rep >= 1) + && (mibSet->param.channel_nb_rep <= 15)) { + params->sys_params.retry_num = mibSet->param.channel_nb_rep; + } else { + status = LORAWAN_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_MAX_RX_WINDOW_DURATION: { + params->sys_params.max_rx_win_time = mibSet->param.max_rx_window; + break; + } + case MIB_RECEIVE_DELAY_1: { + params->sys_params.recv_delay1 = mibSet->param.recv_delay1; + break; + } + case MIB_RECEIVE_DELAY_2: { + params->sys_params.recv_delay2 = mibSet->param.recv_delay2; + break; + } + case MIB_JOIN_ACCEPT_DELAY_1: { + params->sys_params.join_accept_delay1 = mibSet->param.join_accept_delay1; + break; + } + case MIB_JOIN_ACCEPT_DELAY_2: { + params->sys_params.join_accept_delay2 = mibSet->param.join_accept_delay2; + 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; + } 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; + } 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; + } 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; + } else { + status = LORAWAN_STATUS_PARAMETER_INVALID; + } + break; + } + case MIB_UPLINK_COUNTER: { + params->ul_frame_counter = mibSet->param.ul_frame_counter; + break; + } + case MIB_DOWNLINK_COUNTER: { + params->dl_frame_counter = mibSet->param.dl_frame_counter; + break; + } + case MIB_SYSTEM_MAX_RX_ERROR: { + params->sys_params.max_sys_rx_error = mibSet->param.max_rx_sys_error; + break; + } + case MIB_MIN_RX_SYMBOLS: { + params->sys_params.min_rx_symb = mibSet->param.min_rx_symb; + break; + } + case MIB_ANTENNA_GAIN: { + params->sys_params.antenna_gain = mibSet->param.antenna_gain; + break; + } + default: + status = LORAWAN_STATUS_SERVICE_UNKNOWN; + break; + } + + return status; +} + +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 ) + { + return LORAWAN_STATUS_PARAMETER_INVALID; + } + + switch( mibGet->type ) + { + case MIB_DEVICE_CLASS: + { + mibGet->param.dev_class = params->dev_class; + break; + } + case MIB_NETWORK_JOINED: + { + mibGet->param.is_nwk_joined = params->is_nwk_joined; + break; + } + case MIB_ADR: + { + mibGet->param.is_adr_enable = params->sys_params.adr_on; + break; + } + case MIB_NET_ID: + { + mibGet->param.net_id = params->net_id; + break; + } + case MIB_DEV_ADDR: + { + mibGet->param.dev_addr = params->dev_addr; + break; + } + case MIB_NWK_SKEY: + { + mibGet->param.nwk_skey =params->keys.nwk_skey; + break; + } + case MIB_APP_SKEY: + { + mibGet->param.app_skey = params->keys.app_skey; + break; + } + case MIB_PUBLIC_NETWORK: + { + mibGet->param.enable_public_nwk = params->is_nwk_public; + break; + } + case MIB_REPEATER_SUPPORT: + { + mibGet->param.enable_repeater_support = params->is_repeater_supported; + break; + } + 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; + break; + } + case MIB_RX2_CHANNEL: + { + mibGet->param.rx2_channel = params->sys_params.rx2_channel; + break; + } + 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; + + 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; + 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; + break; + } + case MIB_CHANNELS_NB_REP: + { + mibGet->param.channel_nb_rep = params->sys_params.retry_num; + break; + } + case MIB_MAX_RX_WINDOW_DURATION: + { + mibGet->param.max_rx_window = params->sys_params.max_rx_win_time; + break; + } + case MIB_RECEIVE_DELAY_1: + { + mibGet->param.recv_delay1 = params->sys_params.recv_delay1; + break; + } + case MIB_RECEIVE_DELAY_2: + { + mibGet->param.recv_delay2 = params->sys_params.recv_delay2; + break; + } + case MIB_JOIN_ACCEPT_DELAY_1: + { + mibGet->param.join_accept_delay1 = params->sys_params.join_accept_delay1; + break; + } + case MIB_JOIN_ACCEPT_DELAY_2: + { + mibGet->param.join_accept_delay2 = params->sys_params.join_accept_delay2; + break; + } + 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; + break; + } + case MIB_CHANNELS_DATARATE: + { + mibGet->param.channel_data_rate = params->sys_params.channel_data_rate; + break; + } + 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; + break; + } + case MIB_CHANNELS_TX_POWER: + { + mibGet->param.channel_tx_pwr = params->sys_params.channel_tx_power; + break; + } + case MIB_UPLINK_COUNTER: + { + mibGet->param.ul_frame_counter = params->ul_frame_counter; + break; + } + case MIB_DOWNLINK_COUNTER: + { + mibGet->param.dl_frame_counter = params->dl_frame_counter; + break; + } + case MIB_MULTICAST_CHANNEL: + { + mibGet->param.multicast_list = params->multicast_channels; + break; + } + case MIB_SYSTEM_MAX_RX_ERROR: + { + mibGet->param.max_rx_sys_error = params->sys_params.max_sys_rx_error; + break; + } + case MIB_MIN_RX_SYMBOLS: + { + mibGet->param.min_rx_symb = params->sys_params.min_rx_symb; + break; + } + case MIB_ANTENNA_GAIN: + { + mibGet->param.antenna_gain = params->sys_params.antenna_gain; + break; + } + default: + status = LORAWAN_STATUS_SERVICE_UNKNOWN; + break; + } + + return status; +} diff --git a/features/lorawan/lorastack/mac/LoRaMacMib.h b/features/lorawan/lorastack/mac/LoRaMacMib.h new file mode 100644 index 0000000000..70cbaecfeb --- /dev/null +++ b/features/lorawan/lorastack/mac/LoRaMacMib.h @@ -0,0 +1,102 @@ +/** + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRaWAN stack layer that controls both MAC and PHY underneath + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) + + +Copyright (c) 2017, Arm Limited and affiliates. + +SPDX-License-Identifier: BSD-3-Clause +*/ + +#ifndef MBED_OS_LORAWAN_MAC_MIB_H_ +#define MBED_OS_LORAWAN_MAC_MIB_H_ + +#include "lorawan/system/lorawan_data_structures.h" +#include "lorastack/phy/LoRaPHY.h" + +// forward declaration +class LoRaMac; + +class LoRaMacMib { + +public: + + /** Constructor + * + * Sets local handles to NULL. These handles will be set when the subsystem + * is activated by the MAC layer. + */ + LoRaMacMib(); + + /** Destructor + * + * Does nothing + */ + ~LoRaMacMib(); + + /** Activating MLME subsystem + * + * 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); + + /** Sets up a MIB Request + * + * Used to configure MAC protocol parameters using appropriate + * key/value pair in the MIB request structure. Use this API to set + * any system wide configurable parameter exposed by MIB service. + * + * @param mibSet [in] pointer to MIB request structure + * @param params pointer to MAC protocol parameters which will be modified + * + * @return LORAWAN_STATUS_OK if everything goes well otherwise + * a negative error code is returned. + */ + lorawan_status_t set_request(loramac_mib_req_confirm_t *mibSet, + loramac_protocol_params *params); + + /** Provides access to the given MIB parameter + * + * Used to extract information about system wide MAC protocol parameters + * which are exposed by MIB service. + * + * @param mibGet [out] pointer to MIB request structure which will be filled in + * @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 get_request(loramac_mib_req_confirm_t *mibGet, + loramac_protocol_params *params); + +private: + + /** + * Pointers to MAC and PHY handles + */ + LoRaMac *_lora_mac; + LoRaPHY *_lora_phy; +}; + + + + +#endif /* MBED_OS_LORAWAN_MAC_MIB_H_ */ diff --git a/features/lorawan/lorastack/mac/LoRaMacMlme.cpp b/features/lorawan/lorastack/mac/LoRaMacMlme.cpp new file mode 100644 index 0000000000..35c0b623a8 --- /dev/null +++ b/features/lorawan/lorastack/mac/LoRaMacMlme.cpp @@ -0,0 +1,151 @@ +/** + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRaWAN stack layer that controls both MAC and PHY underneath + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) + + +Copyright (c) 2017, Arm Limited and affiliates. + +SPDX-License-Identifier: BSD-3-Clause +*/ + +#include "LoRaMac.h" +#include "lorastack/mac/LoRaMacMlme.h" + +LoRaMacMlme::LoRaMacMlme() +: _lora_mac(NULL), _lora_phy(NULL), _mac_cmd(NULL) +{ +} + +LoRaMacMlme::~LoRaMacMlme() +{ +} + +void LoRaMacMlme::activate_mlme_subsystem(LoRaMac *mac, LoRaPHY *phy, + LoRaMacCommand *cmd) +{ + _lora_mac = mac; + _lora_phy = phy; + _mac_cmd = cmd; +} + +lorawan_status_t LoRaMacMlme::set_request(loramac_mlme_req_t *request, + loramac_protocol_params *params) +{ + 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; +} diff --git a/features/lorawan/lorastack/mac/LoRaMacMlme.h b/features/lorawan/lorastack/mac/LoRaMacMlme.h new file mode 100644 index 0000000000..2be3db0b71 --- /dev/null +++ b/features/lorawan/lorastack/mac/LoRaMacMlme.h @@ -0,0 +1,115 @@ +/** + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C)2013 Semtech + ___ _____ _ ___ _ _____ ___ ___ ___ ___ +/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| +\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| +|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| +embedded.connectivity.solutions=============== + +Description: LoRaWAN stack layer that controls both MAC and PHY underneath + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) + + +Copyright (c) 2017, Arm Limited and affiliates. + +SPDX-License-Identifier: BSD-3-Clause +*/ + +#ifndef MBED_OS_LORAWAN_MAC_MLME_H_ +#define MBED_OS_LORAWAN_MAC_MLME_H_ + +#include "lorawan/system/lorawan_data_structures.h" +#include "lorastack/phy/LoRaPHY.h" +#include "lorastack/mac/LoRaMacCommand.h" + +// forward declaration +class LoRaMac; + +class LoRaMacMlme { + +public: + + /** Constructor + * + * Sets local handles to NULL. These handles will be set when the subsystem + * is activated by the MAC layer. + */ + LoRaMacMlme(); + + /** Destructor + * + * Does nothing + */ + ~LoRaMacMlme(); + + /** 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); + + /** Grants access to MLME confirmation data + * + * @return a reference to MLME confirm data structure + */ + inline loramac_mlme_confirm_t& get_confirmation() + { + return confirmation; + } + + /** Grants access to MLME indication data + * + * @return a reference to MLME indication data structure + */ + inline loramac_mlme_indication_t& get_indication() + { + return indication; + } + +private: + + /** + * Pointers to MAC and PHY handles + */ + LoRaMac *_lora_mac; + LoRaPHY *_lora_phy; + LoRaMacCommand *_mac_cmd; + + /** + * Structure to hold MLME indication data. + */ + loramac_mlme_indication_t indication; + + /** + * Structure to hold MLME confirm data. + */ + loramac_mlme_confirm_t confirmation; +}; + +#endif /* MBED_OS_LORAWAN_MAC_MLME_H_ */ diff --git a/features/lorawan/lorastack/mac/LoRaMacTest.h b/features/lorawan/lorastack/mac/LoRaMacTest.h deleted file mode 100644 index cf51a1c29b..0000000000 --- a/features/lorawan/lorastack/mac/LoRaMacTest.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - / _____) _ | | -( (____ _____ ____ _| |_ _____ ____| |__ - \____ \| ___ | (_ _) ___ |/ ___) _ \ - _____) ) ____| | | || |_| ____( (___| | | | -(______/|_____)_|_|_| \__)_____)\____)_| |_| - (C)2013 Semtech - ___ _____ _ ___ _ _____ ___ ___ ___ ___ -/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| -\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| -|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| -embedded.connectivity.solutions=============== - -Description: LoRa MAC layer test function implementation - -License: Revised BSD License, see LICENSE.TXT file include in the project - -Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE ) - - -Copyright (c) 2017, Arm Limited and affiliates. - -SPDX-License-Identifier: BSD-3-Clause -*/ -#ifndef __LORAMACTEST_H__ -#define __LORAMACTEST_H__ - -/** - * \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 ); - -#endif // __LORAMACTEST_H__ diff --git a/features/lorawan/lorastack/phy/LoRaPHY.cpp b/features/lorawan/lorastack/phy/LoRaPHY.cpp index 7a7c43b724..4b1c502a45 100644 --- a/features/lorawan/lorastack/phy/LoRaPHY.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHY.cpp @@ -27,28 +27,18 @@ SPDX-License-Identifier: BSD-3-Clause #include #include #include "lorawan/lorastack/phy/LoRaPHY.h" -#include "lorawan/system/LoRaWANTimer.h" #define BACKOFF_DC_1_HOUR 100 #define BACKOFF_DC_10_HOURS 1000 #define BACKOFF_DC_24_HOURS 10000 -static uint8_t CountChannels( uint16_t mask, uint8_t nbBits ) -{ - uint8_t nbActiveBits = 0; +#define CHANNELS_IN_MASK 16 - for( uint8_t j = 0; j < nbBits; j++ ) - { - if( ( mask & ( 1 << j ) ) == ( 1 << j ) ) - { - nbActiveBits++; - } - } - return nbActiveBits; -} - -LoRaPHY::LoRaPHY() +LoRaPHY::LoRaPHY(LoRaWANTimeHandler &lora_time) + : _radio(NULL), + _lora_time(lora_time) { + memset(&phy_params, 0, sizeof(phy_params)); } LoRaPHY::~LoRaPHY() @@ -73,14 +63,6 @@ void LoRaPHY::put_radio_to_standby() { _radio->unlock(); } -void LoRaPHY::setup_tx_cont_wave_mode(uint16_t timeout, uint32_t frequency, - uint8_t power) -{ - _radio->lock(); - _radio->set_tx_continuous_wave(frequency, power, timeout); - _radio->unlock(); -} - void LoRaPHY::setup_public_network_mode(bool set) { _radio->lock(); @@ -91,15 +73,11 @@ void LoRaPHY::setup_public_network_mode(bool set) void LoRaPHY::setup_rx_window(bool rx_continuous, uint32_t max_rx_window) { _radio->lock(); - if (!rx_continuous) { _radio->receive(max_rx_window); - _radio->unlock(); - return; + } else { + _radio->receive(0); // Continuous mode } - - _radio->receive(0); // Continuous mode - _radio->unlock(); } @@ -122,316 +100,1305 @@ void LoRaPHY::handle_send(uint8_t *buf, uint8_t size) _radio->unlock(); } +uint8_t LoRaPHY::request_new_channel(new_channel_req_params_t* params) +{ + if (!phy_params.custom_channelplans_supported) { + return 0; + } + + uint8_t status = 0x03; + + if (params->new_channel->frequency == 0) { + // Remove + if (remove_channel(params->channel_id) == false) { + status &= 0xFC; + } + } else { + + switch (add_channel(params->new_channel, params->channel_id)) { + case LORAWAN_STATUS_OK: + { + break; + } + case LORAWAN_STATUS_FREQUENCY_INVALID: + { + status &= 0xFE; + break; + } + case LORAWAN_STATUS_DATARATE_INVALID: + { + status &= 0xFD; + break; + } + case LORAWAN_STATUS_FREQ_AND_DR_INVALID: + { + status &= 0xFC; + break; + } + default: + { + status &= 0xFC; + break; + } + } + } + + return status; +} + int32_t LoRaPHY::get_random(int32_t min, int32_t max) { return (int32_t) rand() % (max - min + 1) + min; } -uint16_t LoRaPHY::get_join_DC( TimerTime_t elapsedTime ) +bool LoRaPHY::verify_channel_DR(uint8_t nb_channels, uint16_t* channel_mask, + int8_t dr, int8_t min_dr, int8_t max_dr, + channel_params_t* channels) { - uint16_t dutyCycle = 0; - - if( elapsedTime < 3600000 ) - { - dutyCycle = BACKOFF_DC_1_HOUR; - } - else if( elapsedTime < ( 3600000 + 36000000 ) ) - { - dutyCycle = BACKOFF_DC_10_HOURS; - } - else - { - dutyCycle = BACKOFF_DC_24_HOURS; - } - return dutyCycle; -} - -bool LoRaPHY::verify_channel_DR( uint8_t nbChannels, uint16_t* channelsMask, int8_t dr, int8_t minDr, int8_t maxDr, ChannelParams_t* channels ) -{ - if( val_in_range( dr, minDr, maxDr ) == 0 ) - { + if (val_in_range(dr, min_dr, max_dr) == 0) { return false; } - for( uint8_t i = 0, k = 0; i < nbChannels; i += 16, k++ ) - { - for( uint8_t j = 0; j < 16; j++ ) - { - if( ( ( channelsMask[k] & ( 1 << j ) ) != 0 ) ) - {// Check datarate validity for enabled channels - if( val_in_range( dr, ( channels[i + j].DrRange.Fields.Min & 0x0F ), - ( channels[i + j].DrRange.Fields.Max & 0x0F ) ) == 1 ) - { - // At least 1 channel has been found we can return OK. - return true; - } + for (uint8_t i; i < phy_params.max_channel_cnt; i++) { + if (mask_bit_test(channel_mask, i)) { + // Check datarate validity for enabled channels + if (val_in_range(dr, (channels[i].dr_range.fields.min & 0x0F), + (channels[i].dr_range.fields.max & 0x0F))) { + // At least 1 channel has been found we can return OK. + return true; } } } + return false; } uint8_t LoRaPHY::val_in_range( int8_t value, int8_t min, int8_t max ) { - if( ( value >= min ) && ( value <= max ) ) - { + if ((value >= min) && (value <= max)) { return 1; } + return 0; } -bool LoRaPHY::disable_channel( uint16_t* channelsMask, uint8_t id, uint8_t maxChannels ) +bool LoRaPHY::disable_channel(uint16_t* channel_mask, uint8_t id, + uint8_t max_channels_num) { uint8_t index = id / 16; - if( ( index > ( maxChannels / 16 ) ) || ( id >= maxChannels ) ) - { + if ((index > phy_params.channels.mask_size) || (id >= max_channels_num)) { return false; } // Deactivate channel - channelsMask[index] &= ~( 1 << ( id % 16 ) ); + mask_bit_clear(channel_mask, id); return true; } -uint8_t LoRaPHY::num_active_channels( uint16_t* channelsMask, uint8_t startIdx, uint8_t stopIdx ) +uint8_t LoRaPHY::count_bits(uint16_t mask, uint8_t nbBits) { - uint8_t nbChannels = 0; + uint8_t nbActiveBits = 0; - if( channelsMask == NULL ) - { + for(uint8_t j = 0; j < nbBits; j++) { + if (mask_bit_test(&mask, j)) { + nbActiveBits++; + } + } + + return nbActiveBits; +} + +uint8_t LoRaPHY::num_active_channels(uint16_t* channel_mask, uint8_t start_idx, + uint8_t stop_idx) +{ + uint8_t nb_channels = 0; + + if (channel_mask == NULL) { return 0; } - for( uint8_t i = startIdx; i < stopIdx; i++ ) - { - nbChannels += CountChannels( channelsMask[i], 16 ); + for (uint8_t i = start_idx; i < stop_idx; i++) { + nb_channels += count_bits(channel_mask[i], 16); } - return nbChannels; + return nb_channels; } -void LoRaPHY::copy_channel_mask( uint16_t* channelsMaskDest, uint16_t* channelsMaskSrc, uint8_t len ) +void LoRaPHY::copy_channel_mask(uint16_t* dest_mask, uint16_t* src_mask, uint8_t len) { - if( ( channelsMaskDest != NULL ) && ( channelsMaskSrc != NULL ) ) - { - for( uint8_t i = 0; i < len; i++ ) - { - channelsMaskDest[i] = channelsMaskSrc[i]; + if ((dest_mask != NULL) && (src_mask != NULL)) { + for( uint8_t i = 0; i < len; i++ ) { + dest_mask[i] = src_mask[i]; } } } -void LoRaPHY::set_last_tx_done( bool joined, Band_t* band, TimerTime_t lastTxDone ) +void LoRaPHY::set_last_tx_done(set_band_txdone_params_t* last_tx_params) { - if( joined == true ) - { - band->LastTxDoneTime = lastTxDone; + if (!last_tx_params) { + return; } - else - { - band->LastTxDoneTime = lastTxDone; - band->LastJoinTxDoneTime = lastTxDone; + + band_t *band_table = (band_t *) phy_params.bands.table; + channel_params_t *channel_list = phy_params.channels.channel_list; + + if (last_tx_params->joined == true) { + band_table[channel_list[last_tx_params->channel].band].last_tx_time = last_tx_params->last_tx_done_time; + return; } + + band_table[channel_list[last_tx_params->channel].band].last_tx_time = last_tx_params->last_tx_done_time; + band_table[channel_list[last_tx_params->channel].band].last_join_tx_time = last_tx_params->last_tx_done_time; + } -TimerTime_t LoRaPHY::update_band_timeoff( bool joined, bool dutyCycle, Band_t* bands, uint8_t nbBands ) +lorawan_time_t LoRaPHY::update_band_timeoff(bool joined, bool duty_cycle, + band_t* bands, uint8_t nb_bands) { - TimerTime_t nextTxDelay = ( TimerTime_t )( -1 ); + lorawan_time_t next_tx_delay = (lorawan_time_t) (-1); // Update bands Time OFF - for( uint8_t i = 0; i < nbBands; i++ ) - { - if( joined == false ) - { - uint32_t txDoneTime = MAX( TimerGetElapsedTime( bands[i].LastJoinTxDoneTime ), - ( dutyCycle == true ) ? TimerGetElapsedTime( bands[i].LastTxDoneTime ) : 0 ); + for (uint8_t i = 0; i < nb_bands; i++) { - if( bands[i].TimeOff <= txDoneTime ) - { - bands[i].TimeOff = 0; + if (joined == false) { + uint32_t txDoneTime = MAX(_lora_time.get_elapsed_time(bands[i].last_join_tx_time), + (duty_cycle == true) ? + _lora_time.get_elapsed_time(bands[i].last_tx_time) : 0); + + if (bands[i].off_time <= txDoneTime) { + bands[i].off_time = 0; } - if( bands[i].TimeOff != 0 ) - { - nextTxDelay = MIN( bands[i].TimeOff - txDoneTime, nextTxDelay ); + + if (bands[i].off_time != 0) { + next_tx_delay = MIN( bands[i].off_time - txDoneTime, next_tx_delay ); } - } - else - { - if( dutyCycle == true ) - { - if( bands[i].TimeOff <= TimerGetElapsedTime( bands[i].LastTxDoneTime ) ) - { - bands[i].TimeOff = 0; + + } else { + // if network has been joined + if (duty_cycle == true) { + + if( bands[i].off_time <= _lora_time.get_elapsed_time(bands[i].last_tx_time)) { + bands[i].off_time = 0; } - if( bands[i].TimeOff != 0 ) - { - nextTxDelay = MIN( bands[i].TimeOff - TimerGetElapsedTime( bands[i].LastTxDoneTime ), - nextTxDelay ); + + if(bands[i].off_time != 0 ) { + next_tx_delay = MIN(bands[i].off_time - _lora_time.get_elapsed_time(bands[i].last_tx_time), + next_tx_delay); } - } - else - { - nextTxDelay = 0; - bands[i].TimeOff = 0; + } else { + // if duty cycle is not on + next_tx_delay = 0; + bands[i].off_time = 0; } } } - return nextTxDelay; + + return next_tx_delay; } -uint8_t LoRaPHY::parse_link_ADR_req( uint8_t* payload, RegionCommonLinkAdrParams_t* linkAdrParams ) +uint8_t LoRaPHY::parse_link_ADR_req(uint8_t* payload, link_adr_params_t* params) { - uint8_t retIndex = 0; + uint8_t ret_index = 0; + + if (payload[0] == SRV_MAC_LINK_ADR_REQ) { - if( payload[0] == SRV_MAC_LINK_ADR_REQ ) - { // Parse datarate and tx power - linkAdrParams->Datarate = payload[1]; - linkAdrParams->TxPower = linkAdrParams->Datarate & 0x0F; - linkAdrParams->Datarate = ( linkAdrParams->Datarate >> 4 ) & 0x0F; + params->datarate = payload[1]; + params->tx_power = params->datarate & 0x0F; + params->datarate = (params->datarate >> 4) & 0x0F; + // Parse ChMask - linkAdrParams->ChMask = ( uint16_t )payload[2]; - linkAdrParams->ChMask |= ( uint16_t )payload[3] << 8; + params->channel_mask = (uint16_t) payload[2]; + params->channel_mask |= (uint16_t) payload[3] << 8; + // Parse ChMaskCtrl and nbRep - linkAdrParams->NbRep = payload[4]; - linkAdrParams->ChMaskCtrl = ( linkAdrParams->NbRep >> 4 ) & 0x07; - linkAdrParams->NbRep &= 0x0F; + params->nb_rep = payload[4]; + params->ch_mask_ctrl = ( params->nb_rep >> 4 ) & 0x07; + params->nb_rep &= 0x0F; // LinkAdrReq has 4 bytes length + 1 byte CMD - retIndex = 5; + ret_index = 5; } - return retIndex; + + return ret_index; } -uint8_t LoRaPHY::verify_link_ADR_req( RegionCommonLinkAdrReqVerifyParams_t* verifyParams, int8_t* dr, int8_t* txPow, uint8_t* nbRep ) +uint8_t LoRaPHY::verify_link_ADR_req(verify_adr_params_t* verify_params, + int8_t* dr, int8_t* tx_pow, uint8_t* nb_rep) { - uint8_t status = verifyParams->Status; - int8_t datarate = verifyParams->Datarate; - int8_t txPower = verifyParams->TxPower; - int8_t nbRepetitions = verifyParams->NbRep; + uint8_t status = verify_params->status; + int8_t datarate = verify_params->datarate; + int8_t tx_power = verify_params->tx_power; + int8_t nb_repetitions = verify_params->nb_rep; // Handle the case when ADR is off. - if( verifyParams->AdrEnabled == false ) - { + if (verify_params->adr_enabled == false) { // When ADR is off, we are allowed to change the channels mask and the NbRep, // if the datarate and the TX power of the LinkAdrReq are set to 0x0F. - if( ( verifyParams->Datarate != 0x0F ) || ( verifyParams->TxPower != 0x0F ) ) - { + if ((verify_params->datarate != 0x0F) || (verify_params->tx_power != 0x0F)) { status = 0; - nbRepetitions = verifyParams->CurrentNbRep; + nb_repetitions = verify_params->current_nb_rep; } + // Get the current datarate and tx power - datarate = verifyParams->CurrentDatarate; - txPower = verifyParams->CurrentTxPower; + datarate = verify_params->current_datarate; + tx_power = verify_params->current_tx_power; } - if( status != 0 ) - { - // Verify datarate. The variable phyParam. Value contains the minimum allowed datarate. - if( verify_channel_DR( verifyParams->NbChannels, verifyParams->ChannelsMask, datarate, - verifyParams->MinDatarate, verifyParams->MaxDatarate, verifyParams->Channels ) == false ) - { + if (status != 0) { + // Verify channel datarate + if (verify_channel_DR(phy_params.max_channel_cnt, verify_params->channel_mask, + datarate, phy_params.min_tx_datarate, + phy_params.max_tx_datarate, phy_params.channels.channel_list) + == false) { status &= 0xFD; // Datarate KO } // Verify tx power - if( val_in_range( txPower, verifyParams->MaxTxPower, verifyParams->MinTxPower ) == 0 ) - { + if (val_in_range(tx_power, phy_params.max_tx_power, + phy_params.min_tx_power) == 0) { // Verify if the maximum TX power is exceeded - if( verifyParams->MaxTxPower > txPower ) - { // Apply maximum TX power. Accept TX power. - txPower = verifyParams->MaxTxPower; - } - else - { + if (phy_params.max_tx_power > tx_power) { + // Apply maximum TX power. Accept TX power. + tx_power = phy_params.max_tx_power; + } else { status &= 0xFB; // TxPower KO } } } // If the status is ok, verify the NbRep - if( status == 0x07 ) - { - if( nbRepetitions == 0 ) - { // Keep the current one - nbRepetitions = verifyParams->CurrentNbRep; - } + if (status == 0x07 && nb_repetitions == 0) { + // Restore the default value according to the LoRaWAN specification + nb_repetitions = 1; } // Apply changes *dr = datarate; - *txPow = txPower; - *nbRep = nbRepetitions; + *tx_pow = tx_power; + *nb_rep = nb_repetitions; return status; } -double LoRaPHY::compute_symb_timeout_lora( uint8_t phyDr, uint32_t bandwidth ) +double LoRaPHY::compute_symb_timeout_lora(uint8_t phy_dr, uint32_t bandwidth) { - return ( ( double )( 1 << phyDr ) / ( double )bandwidth ) * 1000; + return ((double)(1 << phy_dr) / (double) bandwidth) * 1000; } -double LoRaPHY::compute_symb_timeout_fsk( uint8_t phyDr ) +double LoRaPHY::compute_symb_timeout_fsk(uint8_t phy_dr) { - return ( 8.0 / ( double )phyDr ); // 1 symbol equals 1 byte + return (8.0 / (double) phy_dr); // 1 symbol equals 1 byte } -void LoRaPHY::get_rx_window_params( double tSymbol, uint8_t minRxSymbols, uint32_t rxError, uint32_t wakeUpTime, uint32_t* windowTimeout, int32_t* windowOffset ) +void LoRaPHY::get_rx_window_params(double t_symb, uint8_t min_rx_symb, + uint32_t rx_error, uint32_t wakeup_time, + uint32_t* window_timeout, int32_t* window_offset) { - *windowTimeout = MAX( ( uint32_t )ceil( ( ( 2 * minRxSymbols - 8 ) * tSymbol + 2 * rxError ) / tSymbol ), minRxSymbols ); // Computed number of symbols - *windowOffset = ( int32_t )ceil( ( 4.0 * tSymbol ) - ( ( *windowTimeout * tSymbol ) / 2.0 ) - wakeUpTime ); + // Computed number of symbols + *window_timeout = MAX ((uint32_t) ceil(((2 * min_rx_symb - 8) * t_symb + 2 * rx_error) / t_symb), min_rx_symb ); + *window_offset = (int32_t) ceil((4.0 * t_symb) - ((*window_timeout * t_symb) / 2.0 ) - wakeup_time); } -int8_t LoRaPHY::compute_tx_power( int8_t txPowerIndex, float maxEirp, float antennaGain ) +int8_t LoRaPHY::compute_tx_power(int8_t tx_power_idx, float max_eirp, + float antenna_gain) { - int8_t phyTxPower = 0; + int8_t phy_tx_power = 0; - phyTxPower = ( int8_t )floor( ( maxEirp - ( txPowerIndex * 2U ) ) - antennaGain ); + phy_tx_power = (int8_t) floor((max_eirp - (tx_power_idx * 2U)) - antenna_gain); - return phyTxPower; + return phy_tx_power; } -void LoRaPHY::get_DC_backoff( RegionCommonCalcBackOffParams_t* calcBackOffParams ) + +int8_t LoRaPHY::get_next_lower_dr(int8_t dr, int8_t min_dr) { - uint8_t bandIdx = calcBackOffParams->Channels[calcBackOffParams->Channel].Band; - uint16_t dutyCycle = calcBackOffParams->Bands[bandIdx].DCycle; - uint16_t joinDutyCycle = 0; + uint8_t next_lower_dr = 0; + + if (dr == min_dr) { + next_lower_dr = min_dr; + } else { + next_lower_dr = dr - 1; + } + + return next_lower_dr; +} + +uint8_t LoRaPHY::get_bandwidth(uint8_t dr) +{ + uint32_t *bandwidths = (uint32_t *) phy_params.bandwidths.table; + + switch(bandwidths[dr]) { + default: + case 125000: + return 0; + case 250000: + return 1; + case 500000: + return 2; + } +} + +uint8_t LoRaPHY::enabled_channel_count(bool joined, uint8_t datarate, + const uint16_t *channel_mask, + uint8_t *channel_indices, + uint8_t *delayTx) +{ + uint8_t count = 0; + uint8_t delay_transmission = 0; + + for (uint8_t i = 0; i < phy_params.max_channel_cnt; i++) { + if (mask_bit_test(channel_mask, i)) { + + if (val_in_range(datarate, phy_params.channels.channel_list[i].dr_range.fields.min, + phy_params.channels.channel_list[i].dr_range.fields.max ) == 0) { + // data rate range invalid for this channel + continue; + } + + band_t *band_table = (band_t *) phy_params.bands.table; + if (band_table[phy_params.channels.channel_list[i].band].off_time > 0) { + // Check if the band is available for transmission + delay_transmission++; + continue; + } + + // otherwise count the channel as enabled + channel_indices[count++] = i; + } + } + + *delayTx = delay_transmission; + + return count; +} + +phy_param_t LoRaPHY::get_phy_params(get_phy_params_t* getPhy) +{ + phy_param_t phyParam = { 0 }; + + 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; + } + } + + return phyParam; +} + +void LoRaPHY::restore_default_channels() +{ + // Restore channels default mask + for (uint8_t i=0; i < phy_params.channels.mask_size; i++) { + phy_params.channels.mask[i] |= phy_params.channels.default_mask[i]; + } +} + +bool LoRaPHY::verify(verification_params_t* verify, phy_attributes_t phy_attribute) +{ + 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); + } + + } + 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.min_rx_datarate); + } else { + return val_in_range(verify->datarate, + phy_params.dwell_limit_datarate, + phy_params.min_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; + } + + return false; + } + case PHY_NB_JOIN_TRIALS: + { + if (verify->nb_join_trials < MBED_CONF_LORA_NB_TRIALS) { + return false; + } + break; + } + default: + return false; + } + + return true; +} + +void LoRaPHY::apply_cf_list(cflist_params_t* cf_list) +{ + // if the underlying PHY doesn't support CF-List, ignore the request + if (!phy_params.cflist_supported) { + return; + } + + channel_params_t new_channel; + + // Setup default datarate range + new_channel.dr_range.value = (phy_params.default_max_datarate << 4) + | phy_params.default_datarate; + + // Size of the optional CF list + if (cf_list->size != 16) { + return; + } + + // Last byte is RFU, don't take it into account + // NOTE: Currently the PHY layers supported by LoRaWAN who accept a CF-List + // define first 2 or 3 channels as default channels. this function is + // written keeping that in mind. If there would be a PHY in the future that + // accepts CF-list but have haphazard allocation of default channels, we + // should override this function in the implementation of that particular + // PHY. + for (uint8_t i = 0, channel_id = phy_params.default_channel_cnt; + channel_id < phy_params.max_channel_cnt; i+=phy_params.default_channel_cnt, channel_id++) { + if (channel_id < (phy_params.cflist_channel_cnt + phy_params.default_channel_cnt)) { + // Channel frequency + new_channel.frequency = (uint32_t) cf_list->payload[i]; + new_channel.frequency |= ((uint32_t) cf_list->payload[i + 1] << 8); + new_channel.frequency |= ((uint32_t) cf_list->payload[i + 2] << 16); + new_channel.frequency *= 100; + + // Initialize alternative frequency to 0 + new_channel.rx1_frequency = 0; + } else { + new_channel.frequency = 0; + new_channel.dr_range.value = 0; + new_channel.rx1_frequency = 0; + } + + if (new_channel.frequency != 0) { + // Try to add channel + add_channel(&new_channel, channel_id); + } else { + remove_channel(channel_id); + } + } +} + + +bool LoRaPHY::get_next_ADR(bool restore_channel_mask, int8_t& dr_out, + int8_t& tx_power_out, uint32_t& adr_ack_cnt) +{ + bool set_adr_ack_bit = false; + + 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) { + adr_ack_cnt = 0; + return set_adr_ack_bit; + } + + if (adr_ack_cnt < phy_params.adr_ack_limit) { + return set_adr_ack_bit; + } + + // ADR ack counter is larger than ADR-ACK-LIMIT + set_adr_ack_bit = true; + tx_power_out = phy_params.max_tx_power; + + if (adr_ack_cnt >= ack_limit_plus_delay) { + if ((adr_ack_cnt % phy_params.adr_ack_delay) == 1) { + // Decrease the datarate + 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; + + if (dr_out == phy_params.min_tx_datarate) { + // We must set adrAckReq to false as soon as we reach the lowest datarate + set_adr_ack_bit = false; + if (restore_channel_mask) { + // Re-enable default channels + restore_default_channels(); + } + } + } + } + + return set_adr_ack_bit; +} + +void LoRaPHY::compute_rx_win_params(int8_t datarate, uint8_t min_rx_symbols, + uint32_t rx_error, + rx_config_params_t *rx_conf_params) +{ + double t_symbol = 0.0; + + // Get the datarate, perform a boundary check + rx_conf_params->datarate = MIN( datarate, phy_params.max_rx_datarate); + + rx_conf_params->bandwidth = get_bandwidth(rx_conf_params->datarate); + + if (phy_params.fsk_supported && rx_conf_params->datarate == phy_params.max_rx_datarate) { + // FSK + t_symbol = compute_symb_timeout_fsk(((uint8_t *)phy_params.datarates.table)[rx_conf_params->datarate]); + } else { + // LoRa + t_symbol = compute_symb_timeout_lora(((uint8_t *)phy_params.datarates.table)[rx_conf_params->datarate], + ((uint32_t *)phy_params.bandwidths.table)[rx_conf_params->datarate]); + } + + get_rx_window_params(t_symbol, min_rx_symbols, rx_error, RADIO_WAKEUP_TIME, + &rx_conf_params->window_timeout, &rx_conf_params->window_offset); +} + +bool LoRaPHY::rx_config(rx_config_params_t* rx_conf, int8_t* datarate) +{ + radio_modems_t modem; + uint8_t dr = rx_conf->datarate; + uint8_t max_payload = 0; + uint8_t phy_dr = 0; + uint32_t frequency = rx_conf->frequency; + + _radio->lock(); + + if (_radio->get_status() != RF_IDLE) { + _radio->unlock(); + return false; + } + + _radio->unlock(); + + if (rx_conf->rx_slot == RX_SLOT_WIN_1) { + // Apply window 1 frequency + frequency = phy_params.channels.channel_list[rx_conf->channel].frequency; + // Apply the alternative RX 1 window frequency, if it is available + if (phy_params.channels.channel_list[rx_conf->channel].rx1_frequency != 0) { + frequency = phy_params.channels.channel_list[rx_conf->channel].rx1_frequency; + } + } + + // Read the physical datarate from the datarates table + uint8_t *datarate_table = (uint8_t *) phy_params.datarates.table; + uint8_t *payload_table = (uint8_t *) phy_params.payloads.table; + uint8_t *payload_with_repeater_table = (uint8_t *) phy_params.payloads_with_repeater.table; + + phy_dr = datarate_table[dr]; + + _radio->lock(); + + _radio->set_channel(frequency); + + // Radio configuration + if (dr == DR_7 && phy_params.fsk_supported) { + modem = MODEM_FSK; + _radio->set_rx_config(modem, 50000, phy_dr * 1000, 0, 83333, 5, + rx_conf->window_timeout, false, 0, true, 0, 0, + false, rx_conf->is_rx_continuous); + } else { + modem = MODEM_LORA; + _radio->set_rx_config(modem, rx_conf->bandwidth, phy_dr, 1, 0, 8, + rx_conf->window_timeout, false, 0, false, 0, 0, + true, rx_conf->is_rx_continuous); + } + + if (rx_conf->is_repeater_supported) { + max_payload = payload_with_repeater_table[dr]; + } else { + max_payload = payload_table[dr]; + } + + _radio->set_max_payload_length(modem, max_payload + LORA_MAC_FRMPAYLOAD_OVERHEAD); + + _radio->unlock(); + + *datarate = phy_dr; + + return true; +} + +bool LoRaPHY::tx_config(tx_config_params_t* tx_conf, int8_t* tx_power, + lorawan_time_t* tx_toa) +{ + radio_modems_t modem; + int8_t phy_dr = ((uint8_t *)phy_params.datarates.table)[tx_conf->datarate]; + channel_params_t *list = phy_params.channels.channel_list; + uint8_t band_idx = list[tx_conf->channel].band; + band_t *bands = (band_t *)phy_params.bands.table; + + // limit TX power if set to too much + if (tx_conf->tx_power > bands[band_idx].max_tx_pwr) { + tx_conf->tx_power = bands[band_idx].max_tx_pwr; + } + + uint8_t bandwidth = get_bandwidth(tx_conf->datarate); + int8_t phy_tx_power = 0; + + // Calculate physical TX power + phy_tx_power = compute_tx_power(tx_conf->tx_power, tx_conf->max_eirp, + tx_conf->antenna_gain); + + _radio->lock(); + + // Setup the radio frequency + _radio->set_channel(list[tx_conf->channel].frequency); + + if( tx_conf->datarate == phy_params.max_tx_datarate ) { + // High Speed FSK channel + modem = MODEM_FSK; + _radio->set_tx_config(modem, phy_tx_power, 25000, bandwidth, + phy_dr * 1000, 0, 5, false, true, 0, 0, false, + 3000); + } else { + modem = MODEM_LORA; + _radio->set_tx_config(modem, phy_tx_power, 0, bandwidth, phy_dr, 1, 8, + false, true, 0, 0, false, 3000 ); + } + + // Setup maximum payload lenght of the radio driver + _radio->set_max_payload_length( modem, tx_conf->pkt_len); + // Get the time-on-air of the next tx frame + *tx_toa = _radio->time_on_air(modem, tx_conf->pkt_len); + + _radio->unlock(); + + *tx_power = tx_conf->tx_power; + + return true; +} + +uint8_t LoRaPHY::link_ADR_request(adr_req_params_t* link_adr_req, + int8_t* dr_out, int8_t* tx_power_out, + uint8_t* nb_rep_out, uint8_t* nb_bytes_processed) +{ + uint8_t status = 0x07; + link_adr_params_t adr_settings; + uint8_t next_index = 0; + uint8_t bytes_processed = 0; + + // rather than dynamically allocating memory, we choose to set + // a channel mask list size of unity here as we know that all + // the PHY layer implementations who have more than 16 channels, i.e., + // have channel mask list size more than unity, override this method. + uint16_t temp_channel_mask[1] = {0}; + + verify_adr_params_t verify_params; + + while (bytes_processed < link_adr_req->payload_size) { + // Get ADR request parameters + next_index = parse_link_ADR_req(&(link_adr_req->payload[bytes_processed]), + &adr_settings); + + if (next_index == 0) { + break; // break loop, since no more request has been found + } + + // Update bytes processed + bytes_processed += next_index; + + // Revert status, as we only check the last ADR request for the channel mask KO + status = 0x07; + + // Setup temporary channels mask + temp_channel_mask[0] = adr_settings.channel_mask; + + // Verify channels mask + if (adr_settings.ch_mask_ctrl == 0 && temp_channel_mask[0] == 0) { + status &= 0xFE; // Channel mask KO + } + + // channel mask applies to first 16 channels + if (adr_settings.ch_mask_ctrl == 0 || + adr_settings.ch_mask_ctrl == 6) { + + for (uint8_t i = 0; i < phy_params.max_channel_cnt; i++) { + + // turn on all channels if channel mask control is 6 + if (adr_settings.ch_mask_ctrl == 6) { + if (phy_params.channels.channel_list[i].frequency != 0) { + mask_bit_set(temp_channel_mask, i); + } + + continue; + } + + // if channel mask control is 0, we test the bits and + // frequencies and change the status if we find a discrepancy + if ((mask_bit_test(temp_channel_mask, i)) && + (phy_params.channels.channel_list[i].frequency == 0)) { + // Trying to enable an undefined channel + status &= 0xFE; // Channel mask KO + } + } + } else { + // Channel mask control applies to RFUs + status &= 0xFE; // Channel mask KO + } + } + + verify_params.status = status; + + verify_params.adr_enabled = link_adr_req->adr_enabled; + verify_params.current_datarate = link_adr_req->current_datarate; + verify_params.current_tx_power = link_adr_req->current_tx_power; + verify_params.current_nb_rep = link_adr_req->current_nb_rep; + + verify_params.datarate = adr_settings.datarate; + verify_params.tx_power = adr_settings.tx_power; + verify_params.nb_rep = adr_settings.nb_rep; + + + verify_params.channel_mask = temp_channel_mask; + + // Verify the parameters and update, if necessary + status = verify_link_ADR_req(&verify_params, &adr_settings.datarate, + &adr_settings.tx_power, &adr_settings.nb_rep); + + // Update channelsMask if everything is correct + if (status == 0x07) { + // Set the channels mask to a default value + memset(phy_params.channels.mask, 0, + sizeof(uint16_t)*phy_params.channels.mask_size); + + // Update the channels mask + copy_channel_mask(phy_params.channels.mask, temp_channel_mask, + phy_params.channels.mask_size); + } + + // Update status variables + *dr_out = adr_settings.datarate; + *tx_power_out = adr_settings.tx_power; + *nb_rep_out = adr_settings.nb_rep; + *nb_bytes_processed = bytes_processed; + + return status; +} + +uint8_t LoRaPHY::accept_rx_param_setup_req(rx_param_setup_req_t* params) +{ + uint8_t status = 0x07; + + // Verify radio frequency + if (_radio->check_rf_frequency(params->frequency) == false) { + status &= 0xFE; // Channel frequency KO + } + + // Verify datarate + if (val_in_range(params->datarate, phy_params.min_rx_datarate, + phy_params.max_rx_datarate) == 0) { + status &= 0xFD; // Datarate KO + } + + // Verify datarate offset + if (val_in_range(params->dr_offset, phy_params.min_rx1_dr_offset, + phy_params.max_rx1_dr_offset) == 0) { + status &= 0xFB; // Rx1DrOffset range KO + } + + return status; +} + +bool LoRaPHY::accept_tx_param_setup_req(tx_param_setup_req_t *params) +{ + if (phy_params.accept_tx_param_setup_req) { + phy_params.ul_dwell_time_setting = params->ul_dwell_time; + phy_params.dl_dwell_time_setting = params->dl_dwell_time; + } + + return phy_params.accept_tx_param_setup_req; +} + +bool LoRaPHY::verify_frequency(uint32_t freq) +{ + band_t *bands_table = (band_t *)phy_params.bands.table; + + // check all sub bands (if there are sub-bands) to check if the given + // frequency falls into any of the frequency ranges + + for (uint8_t i=0; i= bands_table[i].lower_band_freq) { + return true; + } + } + + return false; +} + +uint8_t LoRaPHY::dl_channel_request(dl_channel_req_params_t* params) +{ + if (!phy_params.dl_channel_req_supported) { + return 0; + } + + uint8_t status = 0x03; + + // Verify if the frequency is supported + if (verify_frequency(params->rx1_frequency) == false) { + status &= 0xFE; + } + + // Verify if an uplink frequency exists + if (phy_params.channels.channel_list[params->channel_id].frequency == 0) { + status &= 0xFD; + } + + // Apply Rx1 frequency, if the status is OK + if (status == 0x03) { + phy_params.channels.channel_list[params->channel_id].rx1_frequency = params->rx1_frequency; + } + + return status; +} + +/** + * Alternate datarate algorithm for join requests. + * - We check from the PHY and take note of total + * number of data rates available upto the default data rate for + * default channels. + * + * - Application sets a total number of re-trials for a Join Request, i.e., + * MBED_CONF_LORA_NB_TRIALS. So MAC layer will send us a counter + * nb_trials < MBED_CONF_LORA_NB_TRIALS which is the current number of trial. + * + * - We roll over total available datarates and pick one according to the + * number of trial sequentially. + * + * - We always start from the Default Data rate and and set the next lower + * data rate for every iteration. + * + * - MAC layer will stop when maximum number of re-trials, i.e., + * MBED_CONF_LORA_NB_TRIALS is achieved. + * + * So essentially MBED_CONF_LORA_NB_TRIALS should be a multiple of range of + * data rates available. For example, in EU868 band, default max. data rate is + * DR_5 and min. data rate is DR_0, so total data rates available are 6. + * + * Hence MBED_CONF_LORA_NB_TRIALS should be a multiple of 6. Setting, + * MBED_CONF_LORA_NB_TRIALS = 6 would mean that every data rate will be tried + * exactly once starting from the largest and finishing at the smallest. + * + * PHY layers which do not have datarates scheme similar to EU band will ofcourse + * override this method. + */ +int8_t LoRaPHY::get_alternate_DR(uint8_t nb_trials) +{ + int8_t datarate = 0; + uint8_t total_nb_datrates = (phy_params.default_max_datarate - phy_params.min_tx_datarate) + 1; + + uint8_t res = nb_trials % total_nb_datrates; + + if (res == 0) { + datarate = phy_params.min_tx_datarate; + } else if (res == 1) { + datarate = phy_params.default_max_datarate; + } else { + datarate = (phy_params.default_max_datarate - res) + 1; + } + + return datarate; +} + +void LoRaPHY::calculate_backoff(backoff_params_t* calc_backoff) +{ + band_t *band_table = (band_t *) phy_params.bands.table; + channel_params_t *channel_list = phy_params.channels.channel_list; + + uint8_t band_idx = channel_list[calc_backoff->channel].band; + uint16_t duty_cycle = band_table[band_idx].duty_cycle; + uint16_t join_duty_cycle = 0; // Reset time-off to initial value. - calcBackOffParams->Bands[bandIdx].TimeOff = 0; + band_table[band_idx].off_time = 0; - if( calcBackOffParams->Joined == false ) - { + if (calc_backoff->joined == false) { // Get the join duty cycle - joinDutyCycle = get_join_DC( calcBackOffParams->ElapsedTime ); + if (calc_backoff->elapsed_time < 3600000) { + join_duty_cycle = BACKOFF_DC_1_HOUR; + } else if (calc_backoff->elapsed_time < (3600000 + 36000000)) { + join_duty_cycle = BACKOFF_DC_10_HOURS; + } else { + join_duty_cycle = BACKOFF_DC_24_HOURS; + } + // Apply the most restricting duty cycle - dutyCycle = MAX( dutyCycle, joinDutyCycle ); - // Reset the timeoff if the last frame was not a join request and when the duty cycle is not enabled - if( ( calcBackOffParams->DutyCycleEnabled == false ) && ( calcBackOffParams->LastTxIsJoinRequest == false ) ) - { - // This is the case when the duty cycle is off and the last uplink frame was not a join. - // This could happen in case of a rejoin, e.g. in compliance test mode. - // In this special case we have to set the time off to 0, since the join duty cycle shall only - // be applied after the first join request. - calcBackOffParams->Bands[bandIdx].TimeOff = 0; - } - else - { - // Apply band time-off. - calcBackOffParams->Bands[bandIdx].TimeOff = calcBackOffParams->TxTimeOnAir * dutyCycle - calcBackOffParams->TxTimeOnAir; - } + duty_cycle = MAX(duty_cycle, join_duty_cycle); } - else - { - if( calcBackOffParams->DutyCycleEnabled == true ) - { - calcBackOffParams->Bands[bandIdx].TimeOff = calcBackOffParams->TxTimeOnAir * dutyCycle - calcBackOffParams->TxTimeOnAir; - } - else - { - calcBackOffParams->Bands[bandIdx].TimeOff = 0; - } + + // No back-off if the last frame was not a join request and when the + // duty cycle is not enabled + if (calc_backoff->dc_enabled == false && + calc_backoff->last_tx_was_join_req == false) { + band_table[band_idx].off_time = 0; + } else { + // Apply band time-off. + band_table[band_idx].off_time = calc_backoff->tx_toa * duty_cycle - calc_backoff->tx_toa; } } + +bool LoRaPHY::set_next_channel(channel_selection_params_t* params, + uint8_t* channel, lorawan_time_t* time, + lorawan_time_t* aggregate_timeoff) +{ + uint8_t channel_count = 0; + uint8_t delay_tx = 0; + + // Note here that the PHY layer implementations which have more than + // 16 channels at their disposal, override this function. That's why + // it is safe to assume that we are dealing with a block of 16 channels + // i.e., EU like implementations. So rather than dynamically allocating + // memory we chose to use a magic number of 16 + uint8_t enabled_channels[16]; + + memset(enabled_channels, 0xFF, sizeof(uint8_t)*16); + + lorawan_time_t next_tx_delay = 0; + band_t *band_table = (band_t *) phy_params.bands.table; + + if (num_active_channels(phy_params.channels.mask, 0, + phy_params.channels.mask_size) == 0) { + + // Reactivate default channels + copy_channel_mask(phy_params.channels.mask, + phy_params.channels.default_mask, + phy_params.channels.mask_size); + } + + if (params->aggregate_timeoff + <= _lora_time.get_elapsed_time(params->last_aggregate_tx_time)) { + // Reset Aggregated time off + *aggregate_timeoff = 0; + + // Update bands Time OFF + next_tx_delay = update_band_timeoff(params->joined, + params->dc_enabled, + band_table, phy_params.bands.size); + + // Search how many channels are enabled + channel_count = enabled_channel_count(params->joined, params->current_datarate, + phy_params.channels.mask, + enabled_channels, &delay_tx); + } else { + delay_tx++; + next_tx_delay = params->aggregate_timeoff + - _lora_time.get_elapsed_time(params->last_aggregate_tx_time); + } + + if (channel_count > 0) { + // We found a valid channel + *channel = enabled_channels[get_random(0, channel_count - 1)]; + *time = 0; + return true; + } + + if (delay_tx > 0) { + // Delay transmission due to AggregatedTimeOff or to a band time off + *time = next_tx_delay; + return true; + } + + // Datarate not supported by any channel, restore defaults + copy_channel_mask(phy_params.channels.mask, + phy_params.channels.default_mask, + phy_params.channels.mask_size); + *time = 0; + return false; +} + +lorawan_status_t LoRaPHY::add_channel(channel_params_t* new_channel, uint8_t id) +{ + bool dr_invalid = false; + bool freq_invalid = false; + + if (!phy_params.custom_channelplans_supported + || id >= phy_params.max_channel_cnt) { + + return LORAWAN_STATUS_PARAMETER_INVALID; + } + + // Validate the datarate range + if (val_in_range(new_channel->dr_range.fields.min, + phy_params.min_tx_datarate, + phy_params.max_tx_datarate) == 0) { + dr_invalid = true; + } + + if (val_in_range(new_channel->dr_range.fields.max, phy_params.min_tx_datarate, + phy_params.max_tx_datarate) == 0) { + dr_invalid = true; + } + + if (new_channel->dr_range.fields.min > new_channel->dr_range.fields.max) { + dr_invalid = true; + } + + // Default channels don't accept all values + if (id < phy_params.default_channel_cnt) { + // Validate the datarate range for min: must be DR_0 + if (new_channel->dr_range.fields.min > phy_params.min_tx_datarate) { + dr_invalid = true; + } + + // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE + if (val_in_range(new_channel->dr_range.fields.max, + phy_params.default_max_datarate, + phy_params.max_tx_datarate) == 0) { + dr_invalid = true; + } + + // We are not allowed to change the frequency + if (new_channel->frequency != phy_params.channels.channel_list[id].frequency) { + freq_invalid = true; + } + } + + // Check frequency + if (!freq_invalid) { + if (verify_frequency(new_channel->frequency) == false) { + freq_invalid = true; + } + } + + // Check status + if (dr_invalid && freq_invalid) { + return LORAWAN_STATUS_FREQ_AND_DR_INVALID; + } + + if (dr_invalid) { + return LORAWAN_STATUS_DATARATE_INVALID; + } + + if (freq_invalid) { + return LORAWAN_STATUS_FREQUENCY_INVALID; + } + + memcpy(&(phy_params.channels.channel_list[id]), new_channel, sizeof(channel_params_t)); + + phy_params.channels.channel_list[id].band = new_channel->band; + + mask_bit_set(phy_params.channels.mask, id); + + return LORAWAN_STATUS_OK; +} + +bool LoRaPHY::remove_channel(uint8_t channel_id) +{ + // upper layers are checking if the custom channel planning is supported or + // not. So we don't need to worry about that + if (mask_bit_test(phy_params.channels.default_mask, channel_id)) { + return false; + } + + + // Remove the channel from the list of channels + const channel_params_t empty_channel = { 0, 0, { 0 }, 0 }; + phy_params.channels.channel_list[channel_id] = empty_channel; + + return disable_channel(phy_params.channels.mask, channel_id, + phy_params.max_channel_cnt); +} + +void LoRaPHY::set_tx_cont_mode(cw_mode_params_t* params, uint32_t given_frequency) +{ + band_t *bands_table = (band_t *) phy_params.bands.table; + channel_params_t *channels = phy_params.channels.channel_list; + + if (params->tx_power > bands_table[channels[params->channel].band].max_tx_pwr) { + params->tx_power = bands_table[channels[params->channel].band].max_tx_pwr; + } + + int8_t phy_tx_power = 0; + uint32_t frequency = 0; + + if (given_frequency == 0) { + frequency = channels[params->channel].frequency; + } else { + frequency = given_frequency; + } + + // Calculate physical TX power + if (params->max_eirp > 0 && params->antenna_gain > 0) { + phy_tx_power = compute_tx_power(params->tx_power, params->max_eirp, + params->antenna_gain ); + } else { + phy_tx_power = params->tx_power; + } + + _radio->lock(); + _radio->set_tx_continuous_wave(frequency, phy_tx_power, params->timeout); + _radio->unlock(); +} + +uint8_t LoRaPHY::apply_DR_offset(int8_t dr, int8_t dr_offset) +{ + int8_t datarate = dr - dr_offset; + + if (datarate < 0) { + datarate = phy_params.min_tx_datarate; + } + + return datarate; +} + + diff --git a/features/lorawan/lorastack/phy/LoRaPHY.h b/features/lorawan/lorastack/phy/LoRaPHY.h index 2ab7eb7acf..df8f898e7d 100644 --- a/features/lorawan/lorastack/phy/LoRaPHY.h +++ b/features/lorawan/lorastack/phy/LoRaPHY.h @@ -34,112 +34,192 @@ #ifndef MBED_OS_LORAPHY_BASE_ #define MBED_OS_LORAPHY_BASE_ +#include "lorawan/LoRaRadio.h" #include "lorawan/system/LoRaWANTimer.h" #include "lorawan/lorastack/phy/lora_phy_ds.h" -#include "netsocket/LoRaRadio.h" +#include "platform/NonCopyable.h" -class LoRaPHY { +class LoRaPHY : private mbed::NonCopyable { public: - LoRaPHY(); virtual ~LoRaPHY(); + /** Stores a reference to Radio object. + * + * Application is responsible for constructing a 'LoRaRadio' object + * which is passed down to the PHY layer. + * + * @param radio a reference to radio driver object + */ void set_radio_instance(LoRaRadio& radio); + /** Puts radio in sleep mode. + * + * Requests the radio driver to enter sleep mode. + */ void put_radio_to_sleep(void); + /** Puts radio in standby mode. + * + * Requests the radio driver to enter standby mode. + */ void put_radio_to_standby(void); + /** Puts radio in receive mode. + * + * Requests the radio driver to enter receive mode for a given time or to + * enter continuous reception mode. + * + * @param is_rx_continuous if true, sets the radio to enter continuous + * reception mode. + * + * @param max_rx_window duration of receive window + */ void setup_rx_window(bool is_rx_continuous, uint32_t max_rx_window); - void setup_tx_cont_wave_mode(uint16_t timeout, uint32_t frequency, - uint8_t power); - + /** Delegates MAC layer request to transmit packet. + * + * @param buf a pointer to the data which needs to be transmitted + * + * @param size size of the data in bytes + */ void handle_send(uint8_t *buf, uint8_t size); + /** Enables/Disables public network mode. + * + * Public and private LoRaWAN network constitute different preambles and + * Net IDs. This API isused to tell the radio which network mode is in use. + * + * @param set true or false + */ void setup_public_network_mode(bool set); + /** Provides a random number from radio. + * + * Returns a 32-bit random unsigned integer value based upon RSSI + * measurements. + * + * @return a 32-bit long random number + * + */ uint32_t get_radio_rng(); - /*! - * \brief The function gets a value of a specific PHY attribute. + /** Calculates and applies duty cycle back-off time. * - * \param [in] getPhy A pointer to the function parameters. + * Explicitly updates the band time-off. * - * \retval A structure containing the PHY parameter. + * @param [in] backoff_params A pointer to backoff parameters. */ - virtual PhyParam_t get_phy_params(GetPhyParams_t* getPhy ) = 0; + void calculate_backoff(backoff_params_t* backoff_params); - /*! - * \brief Updates the last TX done parameters of the current channel. + /** + * 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)); + } + + /** + * 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)); + } + + /** + * 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)); + } + + /** Entertain a new channel request MAC command. * - * \param [in] txDone A pointer to the function parameters. + * MAC command subsystem processes the new channel request coming form + * the network server and then MAC layer asks the PHY layer to entertain + * the request. + * + * @param [in] new_channel_req A pointer to the new_channel_req_params_t. + * + * @return bit mask, according to the LoRaWAN spec 1.0.2. */ - virtual void set_band_tx_done(SetBandTxDoneParams_t* txDone ) = 0; + virtual uint8_t request_new_channel(new_channel_req_params_t* new_channel_req); - /*! - * \brief Initializes the channels masks and the channels. + /** Grants access to PHY layer parameters. * - * \param [in] type Sets the initialization type. + * 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 void load_defaults(InitType_t type ) = 0; + virtual phy_param_t get_phy_params(get_phy_params_t* get_phy); - /*! - * \brief Verifies a parameter. + /** Process PHY layer state after a successful transmission. * - * \param [in] verify A pointer to the function parameters. + * Updates times of the last transmission for the particular channel and + * band upon which last transmission took place. * - * \param [in] phyAttribute The attribute for which the verification is needed. - * - * \retval True, if the parameter is valid. + * @param [in] tx_done A pointer to set_band_txdone_params_t */ - virtual bool verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute ) = 0; + virtual void set_last_tx_done(set_band_txdone_params_t* tx_done); - /*! - * \brief The function parses the input buffer and sets up the channels of the CF list. + /** Enables default channels only. * - * \param [in] applyCFList A pointer to the function parameters. + * Falls back to a channel mask where only default channels are enabled, all + * other channels are disabled. */ - virtual void apply_cf_list(ApplyCFListParams_t* applyCFList ) = 0; + virtual void restore_default_channels(); - /*! - * \brief Sets a channels mask. + /** Verify if a parameter is eligible. * - * \param [in] chanMaskSet A pointer to the function parameters. + * @param verify A pointer to the verification_params_t that contains + * parameters which we need to check for validity. * - * \retval True, if the channels mask could be set. + * @param phy_attr The attribute for which the verification is needed. + * + * @return True, if the parameter is valid. */ - virtual bool set_channel_mask(ChanMaskSetParams_t* chanMaskSet ) = 0; + virtual bool verify(verification_params_t* verify, phy_attributes_t phy_attr); - /*! - * \brief Calculates the next datarate to set, when ADR is on or off. + /** Processes the incoming CF-list. * - * \param [in] adrNext A pointer to the function parameters. + * Handles the payload containing CF-list and enables channels defined + * therein. * - * \param [out] drOut The calculated datarate for the next TX. - * - * \param [out] txPowOut The TX power for the next TX. - * - * \param [out] adrAckCounter The calculated ADR acknowledgement counter. - * - * \retval True, if an ADR request should be performed. + * @param cflist_params A pointer to cflist_params_t. */ - virtual bool get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter ) = 0; + virtual void apply_cf_list(cflist_params_t* cflist_params); - /*! - * \brief Configuration of the RX windows. + /** Calculates the next datarate to set, when ADR is on or off. * - * \param [in] rxConfig A pointer to the function parameters. + * @param restore_channel_mask A boolean set restore channel mask in case + * of failure. * - * \param [out] datarate The datarate index set. + * @param dr_out The calculated datarate for the next TX. * - * \retval True, if the configuration was applied successfully. + * @param tx_power_out The TX power for the next TX. + * + * @param adr_ack_counter The calculated ADR acknowledgement counter. + * + * @return True, if an ADR request should be performed. */ - virtual bool rx_config(RxConfigParams_t* rxConfig, int8_t* datarate ) = 0; + bool get_next_ADR(bool restore_channel_mask, int8_t& dr_out, + int8_t& tx_power_out, uint32_t& adr_ack_counter); - /* - * RX window precise timing + /** Configure radio reception. + * + * @param [in] config A pointer to the RX configuration. + * + * @param [out] datarate The datarate index set. + * + * @return True, if the configuration was applied successfully. + */ + virtual bool rx_config(rx_config_params_t* config, int8_t* datarate); + + /** Computing Receive Windows * * For more details please consult the following document, chapter 3.1.2. * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf @@ -179,499 +259,258 @@ public: /*! * Computes the RX window timeout and offset. * - * \param [in] datarate The RX window datarate index to be used. + * @param [in] datarate The RX window datarate index to be used. * - * \param [in] minRxSymbols The minimum number of symbols required to detect an RX frame. + * @param [in] min_rx_symbols The minimum number of symbols required to + * detect an RX frame. * - * \param [in] rxError The maximum timing error of the receiver in milliseconds. - * The receiver will turn on in a [-rxError : +rxError] ms - * interval around RxOffset. + * @param [in] rx_error The maximum timing error of the receiver + * in milliseconds. The receiver will turn on + * in a [-rxError : +rxError] ms interval around + * RxOffset. * - * \param [out] rxConfigParams Returns the updated WindowTimeout and WindowOffset fields. + * @param [out] rx_conf_params Pointer to the structure that needs to be + * filled with receive window parameters. * */ - virtual void compute_rx_win_params(int8_t datarate, - uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams) = 0; - /*! - * \brief TX configuration. - * - * \param [in] txConfig A pointer to the function parameters. - * - * \param [out] txPower The TX power index set. - * - * \param [out] txTimeOnAir The time-on-air of the frame. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir ) = 0; + virtual void compute_rx_win_params(int8_t datarate, uint8_t min_rx_symbols, + uint32_t rx_error, + rx_config_params_t *rx_conf_params); - /*! - * \brief The function processes a Link ADR Request. + /** Configure radio transmission. * - * \param [in] linkAdrReq A pointer to the function parameters. + * @param [in] tx_config Structure containing tx parameters. * - * \param [out] drOut The datarate applied. + * @param [out] tx_power The TX power which will be set. * - * \param [out] txPowOut The TX power applied. + * @param [out] tx_toa The time-on-air of the frame. * - * \param [out] nbRepOut The number of repetitions to apply. - * - * \param [out] nbBytesParsed The number of bytes parsed. - * - * \retval The status of the operation, according to the LoRaMAC specification. + * @return True, if the configuration was applied successfully. */ - virtual uint8_t link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, - uint8_t* nbBytesParsed ) = 0; + virtual bool tx_config(tx_config_params_t* tx_config, int8_t* tx_power, + lorawan_time_t* tx_toa); - /*! - * \brief The function processes a RX Parameter Setup Request. + /** Processes a Link ADR Request. * - * \param [in] rxParamSetupReq A pointer to the function parameters. + * @param [in] params A pointer ADR request parameters. * - * \retval The status of the operation, according to the LoRaMAC specification. + * @param [out] dr_out The datarate applied. + * + * @param [out] tx_power_out The TX power applied. + * + * @param [out] nb_rep_out The number of repetitions to apply. + * + * @param [out] nb_bytes_parsed The number of bytes parsed. + * + * @return The status of the operation, according to the LoRaMAC specification. */ - virtual uint8_t setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq ) = 0; + virtual uint8_t link_ADR_request(adr_req_params_t* params, + int8_t* dr_out, int8_t* tx_power_out, + uint8_t* nb_rep_out, + uint8_t* nb_bytes_parsed); - /*! - * \brief The function processes a New Channel Request. + /** Accept or rejects RxParamSetupReq MAC command * - * \param [in] newChannelReq A pointer to the function parameters. + * The function processes a RX parameter setup request in response to + * server MAC command for RX setup. * - * \retval The status of the operation, according to the LoRaMAC specification. + * @param [in] params A pointer to rx parameter setup request. + * + * @return The status of the operation, according to the LoRaWAN specification. */ - virtual uint8_t request_new_channel(NewChannelReqParams_t* newChannelReq ) = 0; + virtual uint8_t accept_rx_param_setup_req(rx_param_setup_req_t* params); - /*! - * \brief The function processes a TX ParamSetup Request. + /** Makes decision whether to accept or reject TxParamSetupReq MAC command * - * \param [in] txParamSetupReq A pointer to the function parameters. + * @param [in] params A pointer to tx parameter setup request. * - * \retval The status of the operation, according to the LoRaMAC specification. - * Returns -1, if the functionality is not implemented. In this case, the end node - * shall ignore the command. + * @return True to let the MAC know that the request is + * accepted and MAC can apply TX parameters received + * form Network Server. Otherwise false is returned. */ - virtual int8_t setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq ) = 0; + virtual bool accept_tx_param_setup_req(tx_param_setup_req_t* params); - /*! - * \brief The function processes a DlChannel Request. + /** Processes a DlChannelReq MAC command. * - * \param [in] dlChannelReq A pointer to the function parameters. + * @param [in] params A pointer to downlink channel request. * - * \retval The status of the operation, according to the LoRaMAC specification. + * @return The status of the operation, according to the LoRaWAN specification. */ - virtual uint8_t dl_channel_request(DlChannelReqParams_t* dlChannelReq ) = 0; + virtual uint8_t dl_channel_request(dl_channel_req_params_t* params); - /*! - * \brief Alternates the datarate of the channel for the join request. + /** Alternates the datarate of the channel for the join request. * - * \param [in] alternateDr A pointer to the function parameters. + * @param nb_trials Number of trials to be made on one given data rate. * - * \retval The datarate to apply. + * @return The datarate to apply . */ - virtual int8_t get_alternate_DR(AlternateDrParams_t* alternateDr ) = 0; + virtual int8_t get_alternate_DR(uint8_t nb_trials); - /*! - * \brief Calculates the back-off time. + /** Searches and sets the next available channel. * - * \param [in] calcBackOff A pointer to the function parameters. + * If there are multiple channels found available, one of them is selected + * randomly. + * + * @param [in] nextChanParams Parameters for the next channel. + * + * @param [out] channel The next channel to use for TX. + * + * @param [out] time The time to wait for the next transmission according to the duty cycle. + * + * @param [out] aggregatedTimeOff Updates the aggregated time off. + * + * @return Function status [1: OK, 0: Unable to find a channel on the current datarate]. */ - virtual void calculate_backoff(CalcBackOffParams_t* calcBackOff ) = 0; + virtual bool set_next_channel(channel_selection_params_t* nextChanParams, + uint8_t* channel, lorawan_time_t* time, + lorawan_time_t* aggregatedTimeOff); - /*! - * \brief Searches and sets the next random available channel. + /** Adds a channel to the channel list. * - * \param [in] nextChanParams Parameters for the next channel. + * Verifies the channel parameters and if everything is found legitimate, + * adds that particular channel to the channel list and updates the channel + * mask. * - * \param [out] channel The next channel to use for TX. + * @param [in] new_channel A pointer to the parameters for the new channel. + * @param [in] id Channel ID * - * \param [out] time The time to wait for the next transmission according to the duty cycle. - * - * \param [out] aggregatedTimeOff Updates the aggregated time off. - * - * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]. + * @return LORAWAN_STATUS_OK if everything goes fine, negative error code + * otherwise. */ - virtual bool set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff ) = 0; + virtual lorawan_status_t add_channel(channel_params_t* new_channel, uint8_t id); - /*! - * \brief Adds a channel. + /** Removes a channel from the channel list. * - * \param [in] channelAdd A pointer to the function parameters. + * @param [in] channel_id Index of the channel to be removed * - * \retval The status of the operation. + * @return True, if the channel was removed successfully. */ - virtual LoRaMacStatus_t add_channel(ChannelAddParams_t* channelAdd ) = 0; + virtual bool remove_channel(uint8_t channel_id); - /*! - * \brief Removes a channel. + /** Puts the radio into continuous wave mode. * - * \param [in] channelRemove A pointer to the function parameters. + * @param [in] continuous_wave A pointer to the function parameters. * - * \retval True, if the channel was removed successfully. + * @param [in] frequency Frequency to transmit at */ - virtual bool remove_channel(ChannelRemoveParams_t* channelRemove ) = 0; + virtual void set_tx_cont_mode(cw_mode_params_t* continuous_wave, + uint32_t frequency = 0); - /*! - * \brief Sets the radio into continuous wave mode. + /** Computes new data rate according to the given offset * - * \param [in] continuousWave A pointer to the function parameters. + * @param [in] dr The current datarate. + * + * @param [in] dr_offset The offset to be applied. + * + * @return The computed datarate. */ - virtual void set_tx_cont_mode(ContinuousWaveParams_t* continuousWave ) = 0; - - /*! - * \brief Computes new datarate according to the given offset - * - * \param [in] downlinkDwellTime The downlink dwell time configuration. 0: No limit, 1: 400ms - * - * \param [in] dr The current datarate. - * - * \param [in] drOffset The offset to be applied. - * - * \retval newDr The computed datarate. - */ - virtual uint8_t apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ) = 0; + virtual uint8_t apply_DR_offset(int8_t dr, int8_t dr_offset); protected: LoRaRadio *_radio; + LoRaWANTimeHandler &_lora_time; + loraphy_params_t phy_params; - typedef struct sRegionCommonLinkAdrParams - { - /*! - * The number of repetitions. - */ - uint8_t NbRep; - /*! - * Datarate. - */ - int8_t Datarate; - /*! - * TX power. - */ - int8_t TxPower; - /*! - * Channels mask control field. - */ - uint8_t ChMaskCtrl; - /*! - * Channels mask field. - */ - uint16_t ChMask; - }RegionCommonLinkAdrParams_t; + LoRaPHY(LoRaWANTimeHandler &lora_time); - typedef struct sRegionCommonLinkAdrReqVerifyParams - { - /*! - * The current status of the AdrLinkRequest. - */ - uint8_t Status; - /*! - * Set to true, if ADR is enabled. - */ - bool AdrEnabled; - /*! - * The datarate the AdrLinkRequest wants to set. - */ - int8_t Datarate; - /*! - * The TX power the AdrLinkRequest wants to set. - */ - int8_t TxPower; - /*! - * The number of repetitions the AdrLinkRequest wants to set. - */ - uint8_t NbRep; - /*! - * The current datarate the node is using. - */ - int8_t CurrentDatarate; - /*! - * The current TX power the node is using. - */ - int8_t CurrentTxPower; - /*! - * The current number of repetitions the node is using. - */ - int8_t CurrentNbRep; - /*! - * The number of channels. - */ - uint8_t NbChannels; - /*! - * A pointer to the first element of the channels mask. - */ - uint16_t* ChannelsMask; - /*! - * The minimum possible datarate. - */ - int8_t MinDatarate; - /*! - * The maximum possible datarate. - */ - int8_t MaxDatarate; - /*! - * A pointer to the channels. - */ - ChannelParams_t* Channels; - /*! - * The minimum possible TX power. - */ - int8_t MinTxPower; - /*! - * The maximum possible TX power. - */ - int8_t MaxTxPower; - }RegionCommonLinkAdrReqVerifyParams_t; - - typedef struct sRegionCommonCalcBackOffParams - { - /*! - * A pointer to region specific channels. - */ - ChannelParams_t* Channels; - /*! - * A pointer to region specific bands. - */ - Band_t* Bands; - /*! - * Set to true, if the last uplink was a join request. - */ - bool LastTxIsJoinRequest; - /*! - * Set to true, if the node is joined. - */ - bool Joined; - /*! - * Set to true, if the duty cycle is enabled. - */ - bool DutyCycleEnabled; - /*! - * The current channel. - */ - uint8_t Channel; - /*! - * The elapsed time since initialization. - */ - TimerTime_t ElapsedTime; - /*! - * The time on air of the last TX frame. - */ - TimerTime_t TxTimeOnAir; - }RegionCommonCalcBackOffParams_t; - - /*! - * \brief Calculates the join duty cycle. - * This is a generic function and valid for all regions. - * - * \param [in] elapsedTime The time elapsed since starting the device. - * - * \retval Duty cycle restriction. + /** + * Verifies the given frequency. */ - uint16_t get_join_DC( TimerTime_t elapsedTime ); + virtual bool verify_frequency(uint32_t freq); - /*! - * \brief Verifies, if a value is in a given range. - * This is a generic function and valid for all regions. - * - * \param [in] value The value to verify, if it is in range. - * - * \param [in] min The minimum possible value. - * - * \param [in] max The maximum possible value. - * - * \retval 1 if the value is in range, otherwise 0. + + /** + * Verifies, if a value is in a given range. */ - uint8_t val_in_range( int8_t value, int8_t min, int8_t max ); + uint8_t val_in_range(int8_t value, int8_t min, int8_t max); - /*! - * \brief Verifies, if a datarate is available on an active channel. - * This is a generic function and valid for all regions. - * - * \param [in] nbChannels The number of channels. - * - * \param [in] channelsMask The channels mask of the region. - * - * \param [in] dr The datarate to verify. - * - * \param [in] minDr The minimum datarate. - * - * \param [in] maxDr The maximum datarate. - * - * \param [in] channels The channels of the region. - * - * \retval True if the datarate is supported, false if not. + /** + * Verifies, if a datarate is available on an active channel. */ - bool verify_channel_DR( uint8_t nbChannels, uint16_t* channelsMask, int8_t dr, - int8_t minDr, int8_t maxDr, ChannelParams_t* channels ); + bool verify_channel_DR(uint8_t nbChannels, uint16_t* channelsMask, int8_t dr, + int8_t minDr, int8_t maxDr, channel_params_t* channels); - /*! - * \brief Disables a channel in a given channels mask. - * This is a generic function and valid for all regions. - * - * \param [in] channelsMask The channels mask of the region. - * - * \param [in] id The ID of the channels mask to disable. - * - * \param [in] maxChannels The maximum number of channels. - * - * \retval True if the channel could be disabled, false if not. + /** + * Disables a channel in a given channels mask. */ - bool disable_channel( uint16_t* channelsMask, uint8_t id, uint8_t maxChannels ); + bool disable_channel(uint16_t* channel_mask, uint8_t id, uint8_t max_channels); - /*! - * \brief Counts the number of active channels in a given channels mask. - * This is a generic function and valid for all regions. - * - * \param [in] channelsMask The channels mask of the region. - * - * \param [in] startIdx The start index. - * - * \param [in] stopIdx The stop index (the channels of this index will not be counted). - * - * \retval The number of active channels. + /** + * Counts number of bits on in a given mask */ - uint8_t num_active_channels( uint16_t* channelsMask, uint8_t startIdx, uint8_t stopIdx ); + uint8_t count_bits(uint16_t mask, uint8_t nb_bits); - /*! - * \brief Copy a channels mask. - * This is a generic function and valid for all regions. - * - * \param [in] channelsMaskDest The destination channels mask. - * - * \param [in] channelsMaskSrc The source channels mask. - * - * \param [in] len The index length to copy. + /** + * Counts the number of active channels in a given channels mask. */ - void copy_channel_mask( uint16_t* channelsMaskDest, uint16_t* channelsMaskSrc, uint8_t len ); + uint8_t num_active_channels(uint16_t* channel_mask, uint8_t start_idx, + uint8_t stop_idx); - /*! - * \brief Sets the last TX done property. - * This is a generic function and valid for all regions. - * - * \param [in] joined Set to true, if the node has joined the network - * - * \param [in] band The band to be updated. - * - * \param [in] lastTxDone The time of the last TX done. + /** + * Copy channel masks. */ - void set_last_tx_done( bool joined, Band_t* band, TimerTime_t lastTxDone ); + void copy_channel_mask(uint16_t* dest_mask, uint16_t* src_mask, uint8_t len); - /*! - * \brief Updates the time-offs of the bands. - * This is a generic function and valid for all regions. - * - * \param [in] joined Set to true, if the node has joined the network - * - * \param [in] dutyCycle Set to true, if the duty cycle is enabled. - * - * \param [in] bands A pointer to the bands. - * - * \param [in] nbBands The number of bands available. - * - * \retval The time which must be waited to perform the next uplink. + /** + * Updates the time-offs of the bands. */ - TimerTime_t update_band_timeoff( bool joined, bool dutyCycle, Band_t* bands, uint8_t nbBands ); + lorawan_time_t update_band_timeoff(bool joined, bool dutyCycle, band_t* bands, + uint8_t nb_bands); - /*! - * \brief Parses the parameter of an LinkAdrRequest. - * This is a generic function and valid for all regions. - * - * \param [in] payload A pointer to the payload containing the MAC commands. The payload - * must contain the CMD identifier, followed by the parameters. - * - * \param [out] parseLinkAdr The function fills the structure with the ADR parameters. - * - * \retval The length of the ADR request, if a request was found. Otherwise, the - * function returns 0. + /** + * Parses the parameter of an LinkAdrRequest. */ - uint8_t parse_link_ADR_req( uint8_t* payload, RegionCommonLinkAdrParams_t* parseLinkAdr ); + uint8_t parse_link_ADR_req(uint8_t* payload, link_adr_params_t* adr_params); - /*! - * \brief Verifies and updates the datarate, the TX power and the number of repetitions - * of a LinkAdrRequest. This also depends on the ADR configuration. - * - * \param [in] verifyParams A pointer to a structure containing the input parameters. - * - * \param [out] dr The updated datarate. - * - * \param [out] txPow The updated TX power. - * - * \param [out] nbRep The updated number of repetitions. - * - * \retval The status according to the LinkAdrRequest definition. + /** + * Verifies and updates the datarate, the TX power and the number of repetitions + * of a LinkAdrRequest. */ - uint8_t verify_link_ADR_req( RegionCommonLinkAdrReqVerifyParams_t* verifyParams, int8_t* dr, int8_t* txPow, uint8_t* nbRep ); + uint8_t verify_link_ADR_req(verify_adr_params_t* verify_params, int8_t* dr, + int8_t* tx_pow, uint8_t* nb_rep); - /*! - * \brief Computes the symbol time for LoRa modulation. - * - * \param [in] phyDr The physical datarate to use. - * - * \param [in] bandwidth The bandwidth to use. - * - * \retval The symbol time. + /** + * Computes the symbol time for LoRa modulation. */ - double compute_symb_timeout_lora( uint8_t phyDr, uint32_t bandwidth ); + double compute_symb_timeout_lora(uint8_t phy_dr, uint32_t bandwidth ); - /*! - * \brief Computes the symbol time for FSK modulation. - * - * \param [in] phyDr The physical datarate to use. - * - * \retval The symbol time. + /** + * Computes the symbol time for FSK modulation. */ - double compute_symb_timeout_fsk( uint8_t phyDr ); + double compute_symb_timeout_fsk(uint8_t phy_dr); - /*! - * \brief Computes the RX window timeout and the RX window offset. - * - * \param [in] tSymbol The symbol timeout. - * - * \param [in] minRxSymbols The minimum required number of symbols to detect an RX frame. - * - * \param [in] rxError The system maximum timing error of the receiver in milliseconds - * The receiver will turn on in a [-rxError : +rxError] ms interval around RxOffset. - * - * \param [in] wakeUpTime The wakeup time of the system. - * - * \param [out] windowTimeout The RX window timeout. - * - * \param [out] windowOffset The RX window time offset to be applied to the RX delay. + /** + * Computes the RX window timeout and the RX window offset. */ - void get_rx_window_params( double tSymbol, uint8_t minRxSymbols, uint32_t rxError, uint32_t wakeUpTime, uint32_t* windowTimeout, int32_t* windowOffset ); + void get_rx_window_params(double t_symbol, uint8_t min_rx_symbols, + uint32_t rx_error, uint32_t wakeup_time, + uint32_t* window_timeout, int32_t* window_offset); - /*! - * \brief Computes the txPower, based on the max EIRP and the antenna gain. - * - * \param [in] txPowerIndex The TX power index. - * - * \param [in] maxEirp The maximum EIRP. - * - * \param [in] antennaGain The antenna gain. - * - * \retval The physical TX power. + /** + * Computes the txPower, based on the max EIRP and the antenna gain. */ - int8_t compute_tx_power( int8_t txPowerIndex, float maxEirp, float antennaGain ); + int8_t compute_tx_power(int8_t txPowerIndex, float maxEirp, float antennaGain); - /*! - * \brief Provides a random number in the range provided. - * - * \param [in] min lower boundary - * \param [in] max upper boundary + /** + * Provides a random number in the range provided. */ int32_t get_random(int32_t min, int32_t max); - /*! - * \brief Calculates the duty cycle for the current band. - * - * \param [in] calcBackOffParams A pointer to the input parameters. + /** + * Get next lower data rate */ - void get_DC_backoff( RegionCommonCalcBackOffParams_t* calcBackOffParams ); + int8_t get_next_lower_dr(int8_t dr, int8_t min_dr); + + /** + * Get channel bandwidth depending upon data rate table index + */ + uint8_t get_bandwidth(uint8_t dr_index); + + uint8_t enabled_channel_count(bool joined, uint8_t datarate, + const uint16_t *mask, uint8_t* enabledChannels, + uint8_t* delayTx); }; + + #endif /* MBED_OS_LORAPHY_BASE_ */ diff --git a/features/lorawan/lorastack/phy/LoRaPHYAS923.cpp b/features/lorawan/lorastack/phy/LoRaPHYAS923.cpp index 69ec6ee94c..5a10bca24b 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYAS923.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYAS923.cpp @@ -31,8 +31,6 @@ #include "LoRaPHYAS923.h" #include "lora_phy_ds.h" -#include "LoRaRadio.h" - /*! * Number of default channels @@ -69,6 +67,8 @@ */ #define AS923_DEFAULT_DATARATE DR_2 +#define AS923_DEFAULT_MAX_DATARATE DR_7 + /*! * The minimum datarate which is used when the * dwell time is limited. @@ -105,16 +105,6 @@ */ #define AS923_DEFAULT_TX_POWER TX_POWER_0 -/*! - * Default uplink dwell time configuration - */ -#define AS923_DEFAULT_UPLINK_DWELL_TIME 1 - -/*! - * Default downlink dwell time configuration - */ -#define AS923_DEFAULT_DOWNLINK_DWELL_TIME 1 - /*! * Default Max EIRP */ @@ -196,21 +186,21 @@ /*! * Band 0 definition - * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } */ -#define AS923_BAND0 { 100, AS923_MAX_TX_POWER, 0, 0 } // 1.0 % +static const band_t AS923_BAND0 = {100, AS923_MAX_TX_POWER, 0, 0, 0, 923000000, 928000000}; // 1.0 % /*! * LoRaMac default channel 1 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define AS923_LC1 { 923200000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +static const channel_params_t AS923_LC1 = { 923200000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }; /*! * LoRaMac default channel 2 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define AS923_LC2 { 923400000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +static const channel_params_t AS923_LC2 = { 923400000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }; /*! * LoRaMac channels which are allowed for the join procedure @@ -230,1109 +220,205 @@ /*! * Data rates table definition */ -static const uint8_t DataratesAS923[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; +static const uint8_t datarates_AS923[] = {12, 11, 10, 9, 8, 7, 7, 50}; /*! * Bandwidths table definition in Hz */ -static const uint32_t BandwidthsAS923[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; +static const uint32_t bandwidths_AS923[] = {125000, 125000, 125000, 125000, 125000, 125000, 250000, 0}; -/*! - * Maximum payload with respect to the datarate index. Cannot operate with repeater. - * The table is valid for the dwell time configuration of 0 for uplinks and downlinks. - */ -static const uint8_t MaxPayloadOfDatarateDwell0AS923[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; - -/*! - * Maximum payload with respect to the datarate index. Can operate with repeater. - * The table is valid for the dwell time configuration of 0 for uplinks and downlinks. The table provides - * repeater support. - */ -static const uint8_t MaxPayloadOfDatarateRepeaterDwell0AS923[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; - -/*! - * Maximum payload with respect to the datarate index. Can operate with and without repeater. - * The table proides repeater support. The table is only valid for uplinks. - */ -static const uint8_t MaxPayloadOfDatarateDwell1UpAS923[] = { 0, 0, 11, 53, 125, 242, 242, 242 }; - -/*! - * Maximum payload with respect to the datarate index. Can operate with and without repeater. - * The table proides repeater support. The table is only valid for downlinks. - */ -static const uint8_t MaxPayloadOfDatarateDwell1DownAS923[] = { 0, 0, 11, 53, 126, 242, 242, 242 }; +#if (MBED_CONF_LORA_DWELL_TIME == 0) + static const uint8_t max_payload_table[] = {51, 51, 51, 115, 242, 242, 242, 242}; + static const uint8_t max_payload_table_with_repeater[] = {51, 51, 51, 115, 222, 222, 222, 222}; +#else + // this is the default, i.e., + static const uint8_t max_payload_table[] = {0, 0, 11, 53, 125, 242, 242, 242}; + static const uint8_t max_payload_table_with_repeater[] = {0, 0, 11, 53, 125, 242, 242, 242}; +#endif /*! * Effective datarate offsets for receive window 1. */ -static const int8_t EffectiveRx1DrOffsetAS923[] = { 0, 1, 2, 3, 4, 5, -1, -2 }; +static const int8_t rx1_dr_offset_AS923[] = {0, 1, 2, 3, 4, 5, -1, -2}; - -// Static functions -static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +LoRaPHYAS923::LoRaPHYAS923(LoRaWANTimeHandler &lora_time) + : LoRaPHY(lora_time) { - uint8_t nextLowerDr = 0; + bands[0] = AS923_BAND0; - if( dr == minDr ) - { - nextLowerDr = minDr; - } - else - { - nextLowerDr = dr - 1; - } - return nextLowerDr; -} + // Default Channels are always enabled in the channel list, + // rest will be added later + channels[0] = AS923_LC1; + channels[1] = AS923_LC2; -static uint32_t GetBandwidth( uint32_t drIndex ) -{ - switch( BandwidthsAS923[drIndex] ) - { - default: - case 125000: - return 0; - case 250000: - return 1; - case 500000: - return 2; - } -} + // Initialize the default channel mask + default_channel_mask[0] = LC(1) + LC(2); -static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) -{ - int8_t txPowerResult = txPower; + // Update the channel mask list + copy_channel_mask(channel_mask, default_channel_mask, AS923_CHANNEL_MASK_SIZE); - // Limit tx power to the band max - txPowerResult = MAX( txPower, maxBandTxPower ); + // set default channels + phy_params.channels.channel_list = channels; + phy_params.channels.channel_list_size = AS923_MAX_NB_CHANNELS; + phy_params.channels.mask = channel_mask; + phy_params.channels.default_mask = default_channel_mask; + phy_params.channels.mask_size = AS923_CHANNEL_MASK_SIZE; - return txPowerResult; -} + // set bands for AS923 spectrum + phy_params.bands.table = (void *) bands; + phy_params.bands.size = AS923_MAX_NB_BANDS; -static bool VerifyTxFreq( uint32_t freq ) -{ - // Not checking the radio drivers here as all radio drivers - // always return true. Just check for Asia-Pacific band here - if( ( freq < 915000000 ) || ( freq > 928000000 ) ) - { - return false; - } - return true; -} + // set bandwidths available in AS923 spectrum + phy_params.bandwidths.table = (void *) bandwidths_AS923; + phy_params.bandwidths.size = 8; -uint8_t LoRaPHYAS923::CountNbOfEnabledChannels(bool joined, uint8_t datarate, - uint16_t* channelsMask, - ChannelParams_t* channels, Band_t* bands, - uint8_t* enabledChannels, uint8_t* delayTx) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTransmission = 0; + // set data rates available in AS923 spectrum + phy_params.datarates.table = (void *) datarates_AS923; + phy_params.datarates.size = 8; - for( uint8_t i = 0, k = 0; i < AS923_MAX_NB_CHANNELS; i += 16, k++ ) - { - for( uint8_t j = 0; j < 16; j++ ) - { - if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) - { - if( channels[i + j].Frequency == 0 ) - { // Check if the channel is enabled - continue; - } - if( joined == false ) - { - if( ( AS923_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) - { - continue; - } - } - if( val_in_range( datarate, channels[i + j].DrRange.Fields.Min, - channels[i + j].DrRange.Fields.Max ) == 0 ) - { // Check if the current channel selection supports the given datarate - continue; - } - if( bands[channels[i + j].Band].TimeOff > 0 ) - { // Check if the band is available for transmission - delayTransmission++; - continue; - } - enabledChannels[nbEnabledChannels++] = i + j; - } - } - } + // set payload sizes with respect to data rates + phy_params.payloads.table = (void *) max_payload_table; + phy_params.payloads.size = 8; + phy_params.payloads_with_repeater.table = (void *) max_payload_table_with_repeater; + phy_params.payloads.size = 8; - *delayTx = delayTransmission; - return nbEnabledChannels; -} + // dwell time setting, 400 ms + phy_params.ul_dwell_time_setting = 1; + phy_params.dl_dwell_time_setting = 1; + phy_params.dwell_limit_datarate = AS923_DWELL_LIMIT_DATARATE; -LoRaPHYAS923::LoRaPHYAS923() -{ - const Band_t band0 = AS923_BAND0; - Bands[0] = band0; + phy_params.duty_cycle_enabled = AS923_DUTY_CYCLE_ENABLED; + phy_params.accept_tx_param_setup_req = true; + phy_params.fsk_supported = true; + phy_params.cflist_supported = true; + + phy_params.default_channel_cnt = AS923_NUMB_DEFAULT_CHANNELS; + phy_params.max_channel_cnt = AS923_MAX_NB_CHANNELS; + phy_params.cflist_channel_cnt = AS923_NUMB_CHANNELS_CF_LIST; + phy_params.min_tx_datarate = AS923_TX_MIN_DATARATE; + phy_params.max_tx_datarate = AS923_TX_MAX_DATARATE; + phy_params.min_rx_datarate = AS923_RX_MIN_DATARATE; + phy_params.max_rx_datarate = AS923_RX_MAX_DATARATE; + phy_params.default_datarate = AS923_DEFAULT_DATARATE; + phy_params.default_max_datarate = AS923_DEFAULT_MAX_DATARATE; + phy_params.min_rx1_dr_offset = AS923_MIN_RX1_DR_OFFSET; + phy_params.max_rx1_dr_offset = AS923_MAX_RX1_DR_OFFSET; + phy_params.default_rx1_dr_offset = AS923_DEFAULT_RX1_DR_OFFSET; + phy_params.min_tx_power = AS923_MIN_TX_POWER; + phy_params.max_tx_power = AS923_MAX_TX_POWER; + phy_params.default_tx_power = AS923_DEFAULT_TX_POWER; + phy_params.default_max_eirp = AS923_DEFAULT_MAX_EIRP; + phy_params.default_antenna_gain = AS923_DEFAULT_ANTENNA_GAIN; + phy_params.adr_ack_limit = AS923_ADR_ACK_LIMIT; + phy_params.adr_ack_delay = AS923_ADR_ACK_DELAY; + phy_params.max_rx_window = AS923_MAX_RX_WINDOW; + phy_params.recv_delay1 = AS923_RECEIVE_DELAY1; + phy_params.recv_delay2 = AS923_RECEIVE_DELAY2; + phy_params.join_channel_mask = AS923_JOIN_CHANNELS; + phy_params.join_accept_delay1 = AS923_JOIN_ACCEPT_DELAY1; + phy_params.join_accept_delay2 = AS923_JOIN_ACCEPT_DELAY2; + phy_params.max_fcnt_gap = AS923_MAX_FCNT_GAP; + phy_params.ack_timeout = AS923_ACKTIMEOUT; + phy_params.ack_timeout_rnd = AS923_ACK_TIMEOUT_RND; + phy_params.rx_window2_datarate = AS923_RX_WND_2_DR; + phy_params.rx_window2_frequency = AS923_RX_WND_2_FREQ; } LoRaPHYAS923::~LoRaPHYAS923() { } -PhyParam_t LoRaPHYAS923::get_phy_params(GetPhyParams_t* getPhy) -{ - PhyParam_t phyParam = { 0 }; - - switch( getPhy->Attribute ) - { - case PHY_MIN_RX_DR: - { - if( getPhy->DownlinkDwellTime == 0 ) - { - phyParam.Value = AS923_RX_MIN_DATARATE; - } - else - { - phyParam.Value = AS923_DWELL_LIMIT_DATARATE; - } - break; - } - case PHY_MIN_TX_DR: - { - if( getPhy->UplinkDwellTime == 0 ) - { - phyParam.Value = AS923_TX_MIN_DATARATE; - } - else - { - phyParam.Value = AS923_DWELL_LIMIT_DATARATE; - } - break; - } - case PHY_DEF_TX_DR: - { - phyParam.Value = AS923_DEFAULT_DATARATE; - break; - } - case PHY_NEXT_LOWER_TX_DR: - { - if( getPhy->UplinkDwellTime == 0 ) - { - phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AS923_TX_MIN_DATARATE ); - } - else - { - phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, AS923_DWELL_LIMIT_DATARATE ); - } - break; - } - case PHY_DEF_TX_POWER: - { - phyParam.Value = AS923_DEFAULT_TX_POWER; - break; - } - case PHY_MAX_PAYLOAD: - { - if( getPhy->UplinkDwellTime == 0 ) - { - phyParam.Value = MaxPayloadOfDatarateDwell0AS923[getPhy->Datarate]; - } - else - { - phyParam.Value = MaxPayloadOfDatarateDwell1UpAS923[getPhy->Datarate]; - } - break; - } - case PHY_MAX_PAYLOAD_REPEATER: - { - if( getPhy->UplinkDwellTime == 0 ) - { - phyParam.Value = MaxPayloadOfDatarateRepeaterDwell0AS923[getPhy->Datarate]; - } - else - { - phyParam.Value = MaxPayloadOfDatarateDwell1UpAS923[getPhy->Datarate]; - } - break; - } - case PHY_DUTY_CYCLE: - { - phyParam.Value = AS923_DUTY_CYCLE_ENABLED; - break; - } - case PHY_MAX_RX_WINDOW: - { - phyParam.Value = AS923_MAX_RX_WINDOW; - break; - } - case PHY_RECEIVE_DELAY1: - { - phyParam.Value = AS923_RECEIVE_DELAY1; - break; - } - case PHY_RECEIVE_DELAY2: - { - phyParam.Value = AS923_RECEIVE_DELAY2; - break; - } - case PHY_JOIN_ACCEPT_DELAY1: - { - phyParam.Value = AS923_JOIN_ACCEPT_DELAY1; - break; - } - case PHY_JOIN_ACCEPT_DELAY2: - { - phyParam.Value = AS923_JOIN_ACCEPT_DELAY2; - break; - } - case PHY_MAX_FCNT_GAP: - { - phyParam.Value = AS923_MAX_FCNT_GAP; - break; - } - case PHY_ACK_TIMEOUT: - { - phyParam.Value = ( AS923_ACKTIMEOUT + get_random( -AS923_ACK_TIMEOUT_RND, AS923_ACK_TIMEOUT_RND ) ); - break; - } - case PHY_DEF_DR1_OFFSET: - { - phyParam.Value = AS923_DEFAULT_RX1_DR_OFFSET; - break; - } - case PHY_DEF_RX2_FREQUENCY: - { - phyParam.Value = AS923_RX_WND_2_FREQ; - break; - } - case PHY_DEF_RX2_DR: - { - phyParam.Value = AS923_RX_WND_2_DR; - break; - } - case PHY_CHANNELS_MASK: - { - phyParam.ChannelsMask = ChannelsMask; - break; - } - case PHY_CHANNELS_DEFAULT_MASK: - { - phyParam.ChannelsMask = ChannelsDefaultMask; - break; - } - case PHY_MAX_NB_CHANNELS: - { - phyParam.Value = AS923_MAX_NB_CHANNELS; - break; - } - case PHY_CHANNELS: - { - phyParam.Channels = Channels; - break; - } - case PHY_DEF_UPLINK_DWELL_TIME: - { - phyParam.Value = AS923_DEFAULT_UPLINK_DWELL_TIME; - break; - } - case PHY_DEF_DOWNLINK_DWELL_TIME: - { - phyParam.Value = AS923_DEFAULT_DOWNLINK_DWELL_TIME; - break; - } - case PHY_DEF_MAX_EIRP: - { - phyParam.fValue = AS923_DEFAULT_MAX_EIRP; - break; - } - case PHY_DEF_ANTENNA_GAIN: - { - phyParam.fValue = AS923_DEFAULT_ANTENNA_GAIN; - break; - } - case PHY_NB_JOIN_TRIALS: - case PHY_DEF_NB_JOIN_TRIALS: - { - phyParam.Value = 1; - break; - } - default: - { - break; - } - } - - return phyParam; -} - -void LoRaPHYAS923::set_band_tx_done(SetBandTxDoneParams_t* txDone) -{ - set_last_tx_done( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); -} - -void LoRaPHYAS923::load_defaults(InitType_t type) -{ - switch( type ) - { - case INIT_TYPE_INIT: - { - // Channels - const ChannelParams_t channel1 = AS923_LC1; - const ChannelParams_t channel2 = AS923_LC2; - Channels[0] = channel1; - Channels[1] = channel2; - - // Initialize the channels default mask - ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ); - // Update the channels mask - copy_channel_mask( ChannelsMask, ChannelsDefaultMask, 1 ); - break; - } - case INIT_TYPE_RESTORE: - { - // Restore channels default mask - ChannelsMask[0] |= ChannelsDefaultMask[0]; - break; - } - default: - { - break; - } - } -} - -bool LoRaPHYAS923::verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute) -{ - switch( phyAttribute ) - { - case PHY_TX_DR: - { - if( verify->DatarateParams.UplinkDwellTime == 0 ) - { - return val_in_range( verify->DatarateParams.Datarate, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ); - } - else - { - return val_in_range( verify->DatarateParams.Datarate, AS923_DWELL_LIMIT_DATARATE, AS923_TX_MAX_DATARATE ); - } - } - case PHY_DEF_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, DR_0, DR_5 ); - } - case PHY_RX_DR: - { - if( verify->DatarateParams.DownlinkDwellTime == 0 ) - { - return val_in_range( verify->DatarateParams.Datarate, AS923_RX_MIN_DATARATE, AS923_RX_MAX_DATARATE ); - } - else - { - return val_in_range( verify->DatarateParams.Datarate, AS923_DWELL_LIMIT_DATARATE, AS923_RX_MAX_DATARATE ); - } - } - case PHY_DEF_TX_POWER: - case PHY_TX_POWER: - { - // Remark: switched min and max! - return val_in_range( verify->TxPower, AS923_MAX_TX_POWER, AS923_MIN_TX_POWER ); - } - case PHY_DUTY_CYCLE: - { - return AS923_DUTY_CYCLE_ENABLED; - } - case PHY_NB_JOIN_TRIALS: - { - return true; - } - default: - return false; - } -} - -void LoRaPHYAS923::apply_cf_list(ApplyCFListParams_t* applyCFList) -{ - ChannelParams_t newChannel; - ChannelAddParams_t channelAdd; - ChannelRemoveParams_t channelRemove; - - // Setup default datarate range - newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; - - // Size of the optional CF list - if( applyCFList->Size != 16 ) - { - return; - } - - // Last byte is RFU, don't take it into account - for( uint8_t i = 0, chanIdx = AS923_NUMB_DEFAULT_CHANNELS; chanIdx < AS923_MAX_NB_CHANNELS; i+=3, chanIdx++ ) - { - if( chanIdx < ( AS923_NUMB_CHANNELS_CF_LIST + AS923_NUMB_DEFAULT_CHANNELS ) ) - { - // Channel frequency - newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; - newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); - newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); - newChannel.Frequency *= 100; - - // Initialize alternative frequency to 0 - newChannel.Rx1Frequency = 0; - } - else - { - newChannel.Frequency = 0; - newChannel.DrRange.Value = 0; - newChannel.Rx1Frequency = 0; - } - - if( newChannel.Frequency != 0 ) - { - channelAdd.NewChannel = &newChannel; - channelAdd.ChannelId = chanIdx; - - // Try to add all channels - add_channel(&channelAdd); - } - else - { - channelRemove.ChannelId = chanIdx; - - remove_channel(&channelRemove); - } - } -} - -bool LoRaPHYAS923::set_channel_mask(ChanMaskSetParams_t* chanMaskSet) -{ - switch( chanMaskSet->ChannelsMaskType ) - { - case CHANNELS_MASK: - { - copy_channel_mask( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); - break; - } - case CHANNELS_DEFAULT_MASK: - { - copy_channel_mask( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); - break; - } - default: - return false; - } - return true; -} - -bool LoRaPHYAS923::get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter) -{ - bool adrAckReq = false; - int8_t datarate = adrNext->Datarate; - int8_t minTxDatarate = 0; - int8_t txPower = adrNext->TxPower; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - - // Get the minimum possible datarate - getPhy.Attribute = PHY_MIN_TX_DR; - getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; - phyParam = get_phy_params(&getPhy); - minTxDatarate = phyParam.Value; - - // Report back the adr ack counter - *adrAckCounter = adrNext->AdrAckCounter; - - // Apply the minimum possible datarate. - datarate = MAX( datarate, minTxDatarate ); - - if( adrNext->AdrEnabled == true ) - { - if( datarate == minTxDatarate ) - { - *adrAckCounter = 0; - adrAckReq = false; - } - else - { - if( adrNext->AdrAckCounter >= AS923_ADR_ACK_LIMIT ) - { - adrAckReq = true; - txPower = AS923_MAX_TX_POWER; - } - else - { - adrAckReq = false; - } - if( adrNext->AdrAckCounter >= ( AS923_ADR_ACK_LIMIT + AS923_ADR_ACK_DELAY ) ) - { - if( ( adrNext->AdrAckCounter % AS923_ADR_ACK_DELAY ) == 1 ) - { - // Decrease the datarate - getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; - getPhy.Datarate = datarate; - getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; - phyParam = get_phy_params(&getPhy); - datarate = phyParam.Value; - - if( datarate == minTxDatarate ) - { - // We must set adrAckReq to false as soon as we reach the lowest datarate - adrAckReq = false; - if( adrNext->UpdateChanMask == true ) - { - // Re-enable default channels - ChannelsMask[0] |= LC( 1 ) + LC( 2 ); - } - } - } - } - } - } - - *drOut = datarate; - *txPowOut = txPower; - return adrAckReq; -} - -void LoRaPHYAS923::compute_rx_win_params(int8_t datarate, uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams) -{ - double tSymbol = 0.0; - - // Get the datarate, perform a boundary check - rxConfigParams->Datarate = MIN( datarate, AS923_RX_MAX_DATARATE ); - rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); - - if( rxConfigParams->Datarate == DR_7 ) - { // FSK - tSymbol = compute_symb_timeout_fsk( DataratesAS923[rxConfigParams->Datarate] ); - } - else - { // LoRa - tSymbol = compute_symb_timeout_lora( DataratesAS923[rxConfigParams->Datarate], BandwidthsAS923[rxConfigParams->Datarate] ); - } - - get_rx_window_params( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); -} - -bool LoRaPHYAS923::rx_config(RxConfigParams_t* rxConfig, int8_t* datarate) -{ - radio_modems_t modem; - int8_t dr = rxConfig->Datarate; - uint8_t maxPayload = 0; - int8_t phyDr = 0; - uint32_t frequency = rxConfig->Frequency; - - if( _radio->get_status() != RF_IDLE ) - { - return false; - } - - if( rxConfig->Window == 0 ) - { - // Apply window 1 frequency - frequency = Channels[rxConfig->Channel].Frequency; - // Apply the alternative RX 1 window frequency, if it is available - if( Channels[rxConfig->Channel].Rx1Frequency != 0 ) - { - frequency = Channels[rxConfig->Channel].Rx1Frequency; - } - } - - // Read the physical datarate from the datarates table - phyDr = DataratesAS923[dr]; - - _radio->set_channel(frequency); - - // Radio configuration - if( dr == DR_7 ) - { - modem = MODEM_FSK; - _radio->set_rx_config(modem, 50000, phyDr * 1000, 0, 83333, 5, - rxConfig->WindowTimeout, false, 0, true, 0, - 0, false, rxConfig->RxContinuous); - } - else - { - modem = MODEM_LORA; - _radio->set_rx_config(modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, - rxConfig->WindowTimeout, false, 0, false, 0, 0, - true, rxConfig->RxContinuous); - } - - // Check for repeater support - if( rxConfig->RepeaterSupport == true ) - { - maxPayload = MaxPayloadOfDatarateRepeaterDwell0AS923[dr]; - } - else - { - maxPayload = MaxPayloadOfDatarateDwell0AS923[dr]; - } - - _radio->set_max_payload_length(modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD); - - *datarate = (uint8_t) dr; - return true; -} - -bool LoRaPHYAS923::tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir) -{ - radio_modems_t modem; - int8_t phyDr = DataratesAS923[txConfig->Datarate]; - int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); - uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); - int8_t phyTxPower = 0; - - // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); - - // Setup the radio frequency - _radio->set_channel(Channels[txConfig->Channel].Frequency); - - if( txConfig->Datarate == DR_7 ) - { // High Speed FSK channel - modem = MODEM_FSK; - _radio->set_tx_config(modem, phyTxPower, 25000, bandwidth, phyDr * 1000, - 0, 5, false, true, 0, 0, false, 3000 ); - } - else - { - modem = MODEM_LORA; - _radio->set_tx_config(modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, - false, true, 0, 0, false, 3000); - } - - // Setup maximum payload lenght of the radio driver - _radio->set_max_payload_length(modem, txConfig->PktLen); - // Get the time-on-air of the next tx frame - *txTimeOnAir = _radio->time_on_air(modem, txConfig->PktLen); - - *txPower = txPowerLimited; - return true; -} - -uint8_t LoRaPHYAS923::link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, uint8_t* nbBytesParsed) -{ - uint8_t status = 0x07; - RegionCommonLinkAdrParams_t linkAdrParams; - uint8_t nextIndex = 0; - uint8_t bytesProcessed = 0; - uint16_t chMask = 0; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; - - while( bytesProcessed < linkAdrReq->PayloadSize ) - { - // Get ADR request parameters - nextIndex = parse_link_ADR_req( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); - - if( nextIndex == 0 ) - break; // break loop, since no more request has been found - - // Update bytes processed - bytesProcessed += nextIndex; - - // Revert status, as we only check the last ADR request for the channel mask KO - status = 0x07; - - // Setup temporary channels mask - chMask = linkAdrParams.ChMask; - - // Verify channels mask - if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) - { - status &= 0xFE; // Channel mask KO - } - else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || - ( linkAdrParams.ChMaskCtrl >= 7 ) ) - { - // RFU - status &= 0xFE; // Channel mask KO - } - else - { - for( uint8_t i = 0; i < AS923_MAX_NB_CHANNELS; i++ ) - { - if( linkAdrParams.ChMaskCtrl == 6 ) - { - if( Channels[i].Frequency != 0 ) - { - chMask |= 1 << i; - } - } - else - { - if( ( ( chMask & ( 1 << i ) ) != 0 ) && - ( Channels[i].Frequency == 0 ) ) - {// Trying to enable an undefined channel - status &= 0xFE; // Channel mask KO - } - } - } - } - } - - // Get the minimum possible datarate - getPhy.Attribute = PHY_MIN_TX_DR; - getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; - phyParam = get_phy_params(&getPhy); - - linkAdrVerifyParams.Status = status; - linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; - linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; - linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; - linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; - linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; - linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; - linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; - linkAdrVerifyParams.NbChannels = AS923_MAX_NB_CHANNELS; - linkAdrVerifyParams.ChannelsMask = &chMask; - linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; - linkAdrVerifyParams.MaxDatarate = AS923_TX_MAX_DATARATE; - linkAdrVerifyParams.Channels = Channels; - linkAdrVerifyParams.MinTxPower = AS923_MIN_TX_POWER; - linkAdrVerifyParams.MaxTxPower = AS923_MAX_TX_POWER; - - // Verify the parameters and update, if necessary - status = verify_link_ADR_req( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); - - // Update channelsMask if everything is correct - if( status == 0x07 ) - { - // Set the channels mask to a default value - memset( ChannelsMask, 0, sizeof( ChannelsMask ) ); - // Update the channels mask - ChannelsMask[0] = chMask; - } - - // Update status variables - *drOut = linkAdrParams.Datarate; - *txPowOut = linkAdrParams.TxPower; - *nbRepOut = linkAdrParams.NbRep; - *nbBytesParsed = bytesProcessed; - - return status; -} - -uint8_t LoRaPHYAS923::setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq) -{ - uint8_t status = 0x07; - - // Verify radio frequency - if(_radio->check_rf_frequency(rxParamSetupReq->Frequency) == false ) - { - status &= 0xFE; // Channel frequency KO - } - - // Verify datarate - if( val_in_range( rxParamSetupReq->Datarate, AS923_RX_MIN_DATARATE, AS923_RX_MAX_DATARATE ) == 0 ) - { - status &= 0xFD; // Datarate KO - } - - // Verify datarate offset - if( val_in_range( rxParamSetupReq->DrOffset, AS923_MIN_RX1_DR_OFFSET, AS923_MAX_RX1_DR_OFFSET ) == 0 ) - { - status &= 0xFB; // Rx1DrOffset range KO - } - - return status; -} - -uint8_t LoRaPHYAS923::request_new_channel(NewChannelReqParams_t* newChannelReq) -{ - uint8_t status = 0x03; - ChannelAddParams_t channelAdd; - ChannelRemoveParams_t channelRemove; - - if( newChannelReq->NewChannel->Frequency == 0 ) - { - channelRemove.ChannelId = newChannelReq->ChannelId; - - // Remove - if( remove_channel(&channelRemove) == false ) - { - status &= 0xFC; - } - } - else - { - channelAdd.NewChannel = newChannelReq->NewChannel; - channelAdd.ChannelId = newChannelReq->ChannelId; - - switch( add_channel (&channelAdd )) - { - case LORAMAC_STATUS_OK: - { - break; - } - case LORAMAC_STATUS_FREQUENCY_INVALID: - { - status &= 0xFE; - break; - } - case LORAMAC_STATUS_DATARATE_INVALID: - { - status &= 0xFD; - break; - } - case LORAMAC_STATUS_FREQ_AND_DR_INVALID: - { - status &= 0xFC; - break; - } - default: - { - status &= 0xFC; - break; - } - } - } - - return status; -} - -int8_t LoRaPHYAS923::setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq) -{ - // Accept the request - return 0; -} - -uint8_t LoRaPHYAS923::dl_channel_request(DlChannelReqParams_t* dlChannelReq) -{ - uint8_t status = 0x03; - - // Verify if the frequency is supported - if( VerifyTxFreq( dlChannelReq->Rx1Frequency ) == false ) - { - status &= 0xFE; - } - - // Verify if an uplink frequency exists - if( Channels[dlChannelReq->ChannelId].Frequency == 0 ) - { - status &= 0xFD; - } - - // Apply Rx1 frequency, if the status is OK - if( status == 0x03 ) - { - Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; - } - - return status; -} - -int8_t LoRaPHYAS923::get_alternate_DR(AlternateDrParams_t* alternateDr) +int8_t LoRaPHYAS923::get_alternate_DR(uint8_t nb_trials) { // Only AS923_DWELL_LIMIT_DATARATE is supported return AS923_DWELL_LIMIT_DATARATE; } -void LoRaPHYAS923::calculate_backoff(CalcBackOffParams_t* calcBackOff) +bool LoRaPHYAS923::set_next_channel(channel_selection_params_t* next_channel_prams, + uint8_t* channel, lorawan_time_t* time, + lorawan_time_t* aggregate_timeoff) { - RegionCommonCalcBackOffParams_t calcBackOffParams; + uint8_t next_channel_idx = 0; + uint8_t nb_enabled_channels = 0; + uint8_t delay_tx = 0; + uint8_t enabled_channels[AS923_MAX_NB_CHANNELS] = { 0 }; + lorawan_time_t next_tx_delay = 0; - calcBackOffParams.Channels = Channels; - calcBackOffParams.Bands = Bands; - calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; - calcBackOffParams.Joined = calcBackOff->Joined; - calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; - calcBackOffParams.Channel = calcBackOff->Channel; - calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; - calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; - - get_DC_backoff( &calcBackOffParams ); -} - -bool LoRaPHYAS923::set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff) -{ - uint8_t channelNext = 0; - uint8_t nbEnabledChannels = 0; - uint8_t delayTx = 0; - uint8_t enabledChannels[AS923_MAX_NB_CHANNELS] = { 0 }; - TimerTime_t nextTxDelay = 0; - - if( num_active_channels( ChannelsMask, 0, 1 ) == 0 ) - { // Reactivate default channels - ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + if (num_active_channels(channel_mask, 0, 1) == 0) { + // Reactivate default channels + channel_mask[0] |= LC(1) + LC(2); } - if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) - { + if (next_channel_prams->aggregate_timeoff <= _lora_time.get_elapsed_time(next_channel_prams->last_aggregate_tx_time)) { // Reset Aggregated time off - *aggregatedTimeOff = 0; + *aggregate_timeoff = 0; // Update bands Time OFF - nextTxDelay = update_band_timeoff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, AS923_MAX_NB_BANDS ); + next_tx_delay = update_band_timeoff(next_channel_prams->joined, + next_channel_prams->dc_enabled, + bands, AS923_MAX_NB_BANDS); // Search how many channels are enabled - nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, - ChannelsMask, Channels, - Bands, enabledChannels, &delayTx ); - } - else - { - delayTx++; - nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); + nb_enabled_channels = enabled_channel_count(next_channel_prams->joined, + next_channel_prams->current_datarate, + channel_mask, + enabled_channels, &delay_tx); + } else { + delay_tx++; + next_tx_delay = next_channel_prams->aggregate_timeoff - _lora_time.get_elapsed_time(next_channel_prams->last_aggregate_tx_time); } - if( nbEnabledChannels > 0 ) - { - for( uint8_t i = 0, j = get_random( 0, nbEnabledChannels - 1 ); i < AS923_MAX_NB_CHANNELS; i++ ) - { - channelNext = enabledChannels[j]; - j = ( j + 1 ) % nbEnabledChannels; + if (nb_enabled_channels > 0) { + + _radio->lock(); + + for (uint8_t i = 0, j = get_random(0, nb_enabled_channels - 1); i < AS923_MAX_NB_CHANNELS; i++) { + next_channel_idx = enabled_channels[j]; + j = ( j + 1 ) % nb_enabled_channels; // Perform carrier sense for AS923_CARRIER_SENSE_TIME // If the channel is free, we can stop the LBT mechanism - if( _radio->perform_carrier_sense(MODEM_LORA, - Channels[channelNext].Frequency, + + if (_radio->perform_carrier_sense(MODEM_LORA, + channels[next_channel_idx].frequency, AS923_RSSI_FREE_TH, - AS923_CARRIER_SENSE_TIME ) == true) - { + AS923_CARRIER_SENSE_TIME) == true) { // Free channel found - *channel = channelNext; + _radio->unlock(); + *channel = next_channel_idx; *time = 0; return true; } } + _radio->unlock(); return false; - } - else - { - if( delayTx > 0 ) - { + } else { + + if (delay_tx > 0) { // Delay transmission due to AggregatedTimeOff or to a band time off - *time = nextTxDelay; + *time = next_tx_delay; return true; } + // Datarate not supported by any channel, restore defaults - ChannelsMask[0] |= LC( 1 ) + LC( 2 ); + channel_mask[0] |= LC( 1 ) + LC( 2 ); *time = 0; return false; } } -LoRaMacStatus_t LoRaPHYAS923::add_channel(ChannelAddParams_t* channelAdd) -{ - uint8_t band = 0; - bool drInvalid = false; - bool freqInvalid = false; - uint8_t id = channelAdd->ChannelId; - - if( id >= AS923_MAX_NB_CHANNELS ) - { - return LORAMAC_STATUS_PARAMETER_INVALID; - } - - // Validate the datarate range - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Min, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Max, AS923_TX_MIN_DATARATE, AS923_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) - { - drInvalid = true; - } - - // Default channels don't accept all values - if( id < AS923_NUMB_DEFAULT_CHANNELS ) - { - // Validate the datarate range for min: must be DR_0 - if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 ) - { - drInvalid = true; - } - // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, AS923_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - // We are not allowed to change the frequency - if( channelAdd->NewChannel->Frequency != Channels[id].Frequency ) - { - freqInvalid = true; - } - } - - // Check frequency - if( freqInvalid == false ) - { - if( VerifyTxFreq( channelAdd->NewChannel->Frequency ) == false ) - { - freqInvalid = true; - } - } - - // Check status - if( ( drInvalid == true ) && ( freqInvalid == true ) ) - { - return LORAMAC_STATUS_FREQ_AND_DR_INVALID; - } - if( drInvalid == true ) - { - return LORAMAC_STATUS_DATARATE_INVALID; - } - if( freqInvalid == true ) - { - return LORAMAC_STATUS_FREQUENCY_INVALID; - } - - memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) ); - Channels[id].Band = band; - ChannelsMask[0] |= ( 1 << id ); - return LORAMAC_STATUS_OK; -} - -bool LoRaPHYAS923::remove_channel(ChannelRemoveParams_t* channelRemove) -{ - uint8_t id = channelRemove->ChannelId; - - if( id < AS923_NUMB_DEFAULT_CHANNELS ) - { - return false; - } - - // Remove the channel from the list of channels - const ChannelParams_t empty_channel = { 0, 0, { 0 }, 0 }; - Channels[id] = empty_channel; - - return disable_channel( ChannelsMask, id, AS923_MAX_NB_CHANNELS ); -} - -void LoRaPHYAS923::set_tx_cont_mode(ContinuousWaveParams_t* continuousWave) -{ - int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); - int8_t phyTxPower = 0; - uint32_t frequency = Channels[continuousWave->Channel].Frequency; - - // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); - - _radio->set_tx_continuous_wave(frequency, phyTxPower, continuousWave->Timeout); -} - -uint8_t LoRaPHYAS923::apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, - int8_t drOffset) +uint8_t LoRaPHYAS923::apply_DR_offset(int8_t dr, int8_t dr_offset) { // Initialize minDr for a downlink dwell time configuration of 0 - int8_t minDr = DR_0; + int8_t min_dr = DR_0; // Update the minDR for a downlink dwell time configuration of 1 - if( downlinkDwellTime == 1 ) - { - minDr = AS923_DWELL_LIMIT_DATARATE; + if (phy_params.dl_dwell_time_setting == 1) { + min_dr = AS923_DWELL_LIMIT_DATARATE; } // Apply offset formula - return MIN( DR_5, MAX( minDr, dr - EffectiveRx1DrOffsetAS923[drOffset] ) ); + return MIN(DR_5, MAX(min_dr, dr - rx1_dr_offset_AS923[dr_offset])); } diff --git a/features/lorawan/lorastack/phy/LoRaPHYAS923.h b/features/lorawan/lorastack/phy/LoRaPHYAS923.h index 2dabdafccc..17f943dbce 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYAS923.h +++ b/features/lorawan/lorastack/phy/LoRaPHYAS923.h @@ -33,7 +33,6 @@ #define MBED_OS_LORAPHY_AS923_H_ #include "LoRaPHY.h" -#include "netsocket/LoRaRadio.h" /*! * LoRaMac maximum number of channels @@ -45,319 +44,28 @@ */ #define AS923_MAX_NB_BANDS 1 -#define AS923_CHANNELS_MASK_SIZE 1 +#define AS923_CHANNEL_MASK_SIZE 1 class LoRaPHYAS923 : public LoRaPHY { public: - - LoRaPHYAS923(); + LoRaPHYAS923(LoRaWANTimeHandler &lora_time); virtual ~LoRaPHYAS923(); - /*! - * \brief The function gets a value of a specific PHY attribute. - * - * \param [in] getPhy A pointer to the function parameters. - * - * \retval A structure containing the PHY parameter. - */ - virtual PhyParam_t get_phy_params(GetPhyParams_t* getPhy ); + virtual int8_t get_alternate_DR(uint8_t nb_trials); - /*! - * \brief Updates the last TX done parameters of the current channel. - * - * \param [in] txDone A pointer to the function parameters. - */ - virtual void set_band_tx_done(SetBandTxDoneParams_t* txDone ); + virtual bool set_next_channel(channel_selection_params_t* nextChanParams, + uint8_t* channel, lorawan_time_t* time, + lorawan_time_t* aggregatedTimeOff ); - /*! - * \brief Initializes the channels masks and the channels. - * - * \param [in] type Sets the initialization type. - */ - virtual void load_defaults(InitType_t type ); - - /*! - * \brief Verifies a parameter. - * - * \param [in] verify A pointer to the function parameters. - * - * \param [in] phyAttribute The attribute to be verified. - * - * \retval True, if the parameter is valid. - */ - virtual bool verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute ); - - /*! - * \brief The function parses the input buffer and sets up the channels of the - * CF list. - * - * \param [in] applyCFList A pointer to the function parameters. - */ - virtual void apply_cf_list(ApplyCFListParams_t* applyCFList ); - - /*! - * \brief Sets a channels mask. - * - * \param [in] chanMaskSet A pointer to the function parameters. - * - * \retval True, if the channels mask could be set. - */ - virtual bool set_channel_mask(ChanMaskSetParams_t* chanMaskSet ); - - /*! - * \brief Calculates the next datarate to set, when ADR is on or off. - * - * \param [in] adrNext A pointer to the function parameters. - * - * \param [out] drOut The calculated datarate for the next TX. - * - * \param [out] txPowOut The TX power for the next TX. - * - * \param [out] adrAckCounter The calculated ADR acknowledgement counter. - * - * \retval True, if an ADR request should be performed. - */ - virtual bool get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter ); - - /*! - * \brief Configuration of the RX windows. - * - * \param [in] rxConfig A pointer to the function parameters. - * - * \param [out] datarate The datarate index set. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool rx_config(RxConfigParams_t* rxConfig, int8_t* datarate ); - - /* - * RX window precise timing. - * - * For more details please consult the following document, chapter 3.1.2. - * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf - * or - * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf - * - * Downlink start: T = Tx + 1s (+/- 20 us) - * | - * TRxEarly | TRxLate - * | | | - * | | +---+---+---+---+---+---+---+---+ - * | | | Latest Rx window | - * | | +---+---+---+---+---+---+---+---+ - * | | | - * +---+---+---+---+---+---+---+---+ - * | Earliest Rx window | - * +---+---+---+---+---+---+---+---+ - * | - * +---+---+---+---+---+---+---+---+ - *Downlink preamble 8 symbols | | | | | | | | | - * +---+---+---+---+---+---+---+---+ - * - * Worst case Rx window timings - * - * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME - * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME - * - * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * - * RxOffset = ( TRxLate + TRxEarly ) / 2 - * - * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME - * - * The minimum value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol - */ - /*! - * Computes the RX window timeout and offset. - * - * \param [in] datarate The RX window datarate index to be used. - * - * \param [in] minRxSymbols The minimum required number of symbols to detect an RX frame. - * - * \param [in] rxError The system maximum timing error of the receiver in milliseconds. - * The receiver will turn on in a [-rxError : +rxError] ms - * interval around RxOffset. - * - * \param [out] rxConfigParams The updated WindowTimeout and WindowOffset fields. - */ - virtual void compute_rx_win_params(int8_t datarate, - uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams); - - /*! - * \brief TX configuration. - * - * \param [in] txConfig A pointer to the function parameters. - * - * \param [out] txPower The TX power index set. - * - * \param [out] txTimeOnAir The time-on-air of the frame. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir ); - - /*! - * \brief The function processes a Link ADR Request. - * - * \param [in] linkAdrReq A pointer to the function parameters. - * - * \param [out] drOut The datarate applied. - * - * \param [out] txPowOut The TX power applied. - * - * \param [out] nbRepOut The number of repetitions to apply. - * - * \param [out] nbBytesParsed The number of bytes parsed. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, - uint8_t* nbBytesParsed ); - - /*! - * \brief The function processes a RX parameter setup request. - * - * \param [in] rxParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq ); - - /*! - * \brief The function processes a new channel request. - * - * \param [in] newChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t request_new_channel(NewChannelReqParams_t* newChannelReq ); - - /*! - * \brief The function processes a TX ParamSetup request. - * - * \param [in] txParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - * Returns -1, if the functionality is not implemented. In this case, the end node - * shall ignore the command. - */ - virtual int8_t setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq ); - - /*! - * \brief The function processes a DlChannel request. - * - * \param [in] dlChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t dl_channel_request(DlChannelReqParams_t* dlChannelReq ); - - /*! - * \brief Alternates the datarate of the channel for the join request. - * - * \param [in] alternateDr A pointer to the function parameters. - * - * \retval The datarate to apply. - */ - virtual int8_t get_alternate_DR(AlternateDrParams_t* alternateDr ); - - /*! - * \brief Calculates the back-off time. - * - * \param [in] calcBackOff A pointer to the function parameters. - */ - virtual void calculate_backoff(CalcBackOffParams_t* calcBackOff ); - - /*! - * \brief Searches and sets the next random available channel. - * - * \param [in] nextChanParams The parameters for the next channel - * - * \param [out] channel The next channel to use for TX. - * - * \param [out] time The time to wait for the next transmission according to the duty cycle. - * - * \param [out] aggregatedTimeOff Updates the aggregated time off. - * - * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]. - */ - virtual bool set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff ); - - /*! - * \brief Adds a channel. - * - * \param [in] channelAdd A pointer to the function parameters. - * - * \retval The status of the operation. - */ - virtual LoRaMacStatus_t add_channel(ChannelAddParams_t* channelAdd ); - - /*! - * \brief Removes a channel. - * - * \param [in] channelRemove A pointer to the function parameters. - * - * \retval True, if the channel was removed successfully. - */ - virtual bool remove_channel(ChannelRemoveParams_t* channelRemove ); - - /*! - * \brief Sets the radio into continuous wave mode. - * - * \param [in] continuousWave A pointer to the function parameters. - */ - virtual void set_tx_cont_mode(ContinuousWaveParams_t* continuousWave ); - - /*! - * \brief Computes a new datarate according to the given offset. - * - * \param [in] downlinkDwellTime The downlink dwell time configuration. 0: No limit, 1: 400ms - * - * \param [in] dr The current datarate. - * - * \param [in] drOffset The offset to be applied. - * - * \retval newDr The computed datarate. - */ - virtual uint8_t apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + virtual uint8_t apply_DR_offset(int8_t dr, int8_t drOffset ); private: - uint8_t CountNbOfEnabledChannels(bool joined, uint8_t datarate, - uint16_t* channelsMask, - ChannelParams_t* channels, Band_t* bands, - uint8_t* enabledChannels, uint8_t* delayTx); - - // Global attributes - /*! - * LoRaMAC channels - */ - ChannelParams_t Channels[AS923_MAX_NB_CHANNELS]; - - /*! - * LoRaMac bands - */ - Band_t Bands[AS923_MAX_NB_BANDS]; - - /*! - * LoRaMac channels mask - */ - uint16_t ChannelsMask[AS923_CHANNELS_MASK_SIZE]; - - /*! - * LoRaMac channels default mask - */ - uint16_t ChannelsDefaultMask[AS923_CHANNELS_MASK_SIZE]; + channel_params_t channels[AS923_MAX_NB_CHANNELS]; + band_t bands[AS923_MAX_NB_BANDS]; + uint16_t channel_mask[AS923_CHANNEL_MASK_SIZE]; + uint16_t default_channel_mask[AS923_CHANNEL_MASK_SIZE]; }; #endif /* MBED_OS_LORAPHY_AS923_H_ */ diff --git a/features/lorawan/lorastack/phy/LoRaPHYAU915.cpp b/features/lorawan/lorastack/phy/LoRaPHYAU915.cpp index 370b9fa4b0..c1e1ecd2f9 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYAU915.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYAU915.cpp @@ -31,7 +31,6 @@ #include "LoRaPHYAU915.h" #include "lora_phy_ds.h" -#include "LoRaRadio.h" /*! * Minimal datarate that can be used by the node @@ -163,46 +162,44 @@ */ #define AU915_RX_WND_2_DR DR_8 - /*! * Band 0 definition - * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } */ -#define AU915_BAND0 { 1, AU915_MAX_TX_POWER, 0, 0 } // 100.0 % +static const band_t AU915_BAND0 = {1, AU915_MAX_TX_POWER, 0, 0, 0, 915200000, 927800000}; // 100.0 % /*! * Defines the first channel for RX window 1 for US band */ -#define AU915_FIRST_RX1_CHANNEL ( (uint32_t) 923300000 ) +#define AU915_FIRST_RX1_CHANNEL ((uint32_t) 923300000) /*! * Defines the last channel for RX window 1 for US band */ -#define AU915_LAST_RX1_CHANNEL ( (uint32_t) 927500000 ) +#define AU915_LAST_RX1_CHANNEL ((uint32_t) 927500000) /*! * Defines the step width of the channels for RX window 1 */ -#define AU915_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 600000 ) +#define AU915_STEPWIDTH_RX1_CHANNEL ((uint32_t) 600000) /*! * Data rates table definition */ -static const uint8_t DataratesAU915[] = { 12, 11, 10, 9, 8, 7, 8, 0, 12, 11, 10, - 9, 8, 7, 0, 0 }; +static const uint8_t datarates_AU915[] = {12, 11, 10, 9, 8, 7, 8, 0, 12, 11, 10, 9, 8, 7, 0, 0}; /*! * Bandwidths table definition in Hz */ -static const uint32_t BandwidthsAU915[] = { 125000, 125000, 125000, 125000, +static const uint32_t bandwidths_AU915[] = { 125000, 125000, 125000, 125000, 125000, 125000, 500000, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 }; /*! * Up/Down link data rates offset definition */ -static const int8_t DatarateOffsetsAU915[7][6] = { { DR_8, DR_8, DR_8, DR_8, - DR_8, DR_8 }, // DR_0 +static const int8_t datarate_offsets_AU915[7][6] = { { DR_8, DR_8, DR_8, DR_8, +DR_8, DR_8 }, // DR_0 { DR_9, DR_8, DR_8, DR_8, DR_8, DR_8 }, // DR_1 { DR_10, DR_9, DR_8, DR_8, DR_8, DR_8 }, // DR_2 { DR_11, DR_10, DR_9, DR_8, DR_8, DR_8 }, // DR_3 @@ -214,601 +211,307 @@ static const int8_t DatarateOffsetsAU915[7][6] = { { DR_8, DR_8, DR_8, DR_8, /*! * Maximum payload with respect to the datarate index. Cannot operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateAU915[] = { 51, 51, 51, 115, 242, 242, +static const uint8_t max_payload_AU915[] = { 51, 51, 51, 115, 242, 242, 242, 0, 53, 129, 242, 242, 242, 242, 0, 0 }; /*! * Maximum payload with respect to the datarate index. Can operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateRepeaterAU915[] = { 51, 51, 51, 115, +static const uint8_t max_payload_with_repeater_AU915[] = { 51, 51, 51, 115, 222, 222, 222, 0, 33, 109, 222, 222, 222, 222, 0, 0 }; -// Static functions -static int8_t GetNextLowerTxDr(int8_t dr, int8_t minDr) +LoRaPHYAU915::LoRaPHYAU915(LoRaWANTimeHandler &lora_time) + : LoRaPHY(lora_time) { - uint8_t nextLowerDr = 0; + bands[0] = AU915_BAND0; - if (dr == minDr) { - nextLowerDr = minDr; - } else { - nextLowerDr = dr - 1; + // Activate Channels + // 125 kHz channels Upstream only + for (uint8_t i = 0; i < AU915_MAX_NB_CHANNELS - 8; i++) { + channels[i].frequency = 915200000 + i * 200000; + channels[i].dr_range.value = ( DR_5 << 4) | DR_0; + channels[i].band = 0; } - return nextLowerDr; -} - -static uint32_t GetBandwidth(uint32_t drIndex) -{ - switch (BandwidthsAU915[drIndex]) { - default: - case 125000: - return 0; - case 250000: - return 1; - case 500000: - return 2; - } -} - -static int8_t LimitTxPower(int8_t txPower, int8_t maxBandTxPower, - int8_t datarate, uint16_t* channelsMask) -{ - int8_t txPowerResult = txPower; - - // Limit tx power to the band max - txPowerResult = MAX(txPower, maxBandTxPower); - - return txPowerResult; -} - -uint8_t LoRaPHYAU915::CountNbOfEnabledChannels(uint8_t datarate, - uint16_t* channelsMask, - ChannelParams_t* channels, - Band_t* bands, uint8_t* enabledChannels, - uint8_t* delayTx) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTransmission = 0; - - for (uint8_t i = 0, k = 0; i < AU915_MAX_NB_CHANNELS; i += 16, k++) { - for (uint8_t j = 0; j < 16; j++) { - if ((channelsMask[k] & (1 << j)) != 0) { - if (channels[i + j].Frequency == 0) { // Check if the channel is enabled - continue; - } - if (val_in_range(datarate, channels[i + j].DrRange.Fields.Min, - channels[i + j].DrRange.Fields.Max) == 0) { // Check if the current channel selection supports the given datarate - continue; - } - if (bands[channels[i + j].Band].TimeOff > 0) { // Check if the band is available for transmission - delayTransmission++; - continue; - } - enabledChannels[nbEnabledChannels++] = i + j; - } - } + // 500 kHz channels + // Upstream and downstream both + for (uint8_t i = AU915_MAX_NB_CHANNELS - 8; i < AU915_MAX_NB_CHANNELS; i++) { + channels[i].frequency = 915900000 + (i - ( AU915_MAX_NB_CHANNELS - 8)) * 1600000; + channels[i].dr_range.value = ( DR_6 << 4) | DR_6; + channels[i].band = 0; } - *delayTx = delayTransmission; - return nbEnabledChannels; -} + // Initialize channels default mask + // All channels are default channels here + // Join request needs to alternate between 125 KHz and 500 KHz channels + // randomly. + default_channel_mask[0] = 0xFFFF; + default_channel_mask[1] = 0xFFFF; + default_channel_mask[2] = 0xFFFF; + default_channel_mask[3] = 0xFFFF; + default_channel_mask[4] = 0x00FF; -LoRaPHYAU915::LoRaPHYAU915() -{ - const Band_t band0 = AU915_BAND0; - Bands[0] = band0; + memset(channel_mask, 0, sizeof(channel_mask)); + memset(current_channel_mask, 0, sizeof(current_channel_mask)); + + // Copy channels default mask + copy_channel_mask(channel_mask, default_channel_mask, AU915_CHANNEL_MASK_SIZE); + + // Copy into current channels mask + // This mask is used to keep track of the channels which were used in + // previous transmissions as the AU915 band doesn't allow concurrent + // transmission on the same channel + copy_channel_mask(current_channel_mask, channel_mask, AU915_CHANNEL_MASK_SIZE); + + // set bands for EU868 spectrum + phy_params.bands.table = (void *) bands; + phy_params.bands.size = AU915_MAX_NB_BANDS; + + // set bandwidths available in EU868 spectrum + phy_params.bandwidths.table = (void *) bandwidths_AU915; + phy_params.bandwidths.size = 16; + + // set data rates available in EU868 spectrum + phy_params.datarates.table = (void *) datarates_AU915; + phy_params.datarates.size = 16; + + // set payload sizes with respect to data rates + phy_params.payloads.table = (void *) max_payload_AU915; + phy_params.payloads.size = 16; + phy_params.payloads_with_repeater.table = (void *) max_payload_with_repeater_AU915; + phy_params.payloads.size = 16; + + // dwell time setting + phy_params.ul_dwell_time_setting = 0; + phy_params.dl_dwell_time_setting = 0; + phy_params.dwell_limit_datarate = AU915_DEFAULT_DATARATE; + + phy_params.duty_cycle_enabled = AU915_DUTY_CYCLE_ENABLED; + phy_params.accept_tx_param_setup_req = false; + phy_params.custom_channelplans_supported = false; + phy_params.cflist_supported = false; + phy_params.fsk_supported = false; + + phy_params.default_channel_cnt = AU915_MAX_NB_CHANNELS; + phy_params.max_channel_cnt = AU915_MAX_NB_CHANNELS; + phy_params.cflist_channel_cnt = 0; + phy_params.min_tx_datarate = AU915_TX_MIN_DATARATE; + phy_params.max_tx_datarate = AU915_TX_MAX_DATARATE; + phy_params.min_rx_datarate = AU915_RX_MIN_DATARATE; + phy_params.max_rx_datarate = AU915_RX_MAX_DATARATE; + phy_params.default_datarate = AU915_DEFAULT_DATARATE; + phy_params.default_max_datarate = AU915_TX_MAX_DATARATE; + phy_params.min_rx1_dr_offset = AU915_MIN_RX1_DR_OFFSET; + phy_params.max_rx1_dr_offset = AU915_MAX_RX1_DR_OFFSET; + phy_params.default_rx1_dr_offset = AU915_DEFAULT_RX1_DR_OFFSET; + phy_params.min_tx_power = AU915_MIN_TX_POWER; + phy_params.max_tx_power = AU915_MAX_TX_POWER; + phy_params.default_tx_power = AU915_DEFAULT_TX_POWER; + phy_params.default_max_eirp = AU915_DEFAULT_MAX_EIRP; + phy_params.default_antenna_gain = AU915_DEFAULT_ANTENNA_GAIN; + phy_params.adr_ack_limit = AU915_ADR_ACK_LIMIT; + phy_params.adr_ack_delay = AU915_ADR_ACK_DELAY; + phy_params.max_rx_window = AU915_MAX_RX_WINDOW; + phy_params.recv_delay1 = AU915_RECEIVE_DELAY1; + phy_params.recv_delay2 = AU915_RECEIVE_DELAY2; + + phy_params.join_accept_delay1 = AU915_JOIN_ACCEPT_DELAY1; + phy_params.join_accept_delay2 = AU915_JOIN_ACCEPT_DELAY2; + phy_params.max_fcnt_gap = AU915_MAX_FCNT_GAP; + phy_params.ack_timeout = AU915_ACKTIMEOUT; + phy_params.ack_timeout_rnd = AU915_ACK_TIMEOUT_RND; + phy_params.rx_window2_datarate = AU915_RX_WND_2_DR; + phy_params.rx_window2_frequency = AU915_RX_WND_2_FREQ; } LoRaPHYAU915::~LoRaPHYAU915() { } -PhyParam_t LoRaPHYAU915::get_phy_params(GetPhyParams_t* getPhy) +bool LoRaPHYAU915::rx_config(rx_config_params_t* params, int8_t* datarate) { - PhyParam_t phyParam = { 0 }; - - switch (getPhy->Attribute) { - case PHY_MIN_RX_DR: { - phyParam.Value = AU915_RX_MIN_DATARATE; - break; - } - case PHY_MIN_TX_DR: { - phyParam.Value = AU915_TX_MIN_DATARATE; - break; - } - case PHY_DEF_TX_DR: { - phyParam.Value = AU915_DEFAULT_DATARATE; - break; - } - case PHY_NEXT_LOWER_TX_DR: { - phyParam.Value = GetNextLowerTxDr(getPhy->Datarate, - AU915_TX_MIN_DATARATE); - break; - } - case PHY_DEF_TX_POWER: { - phyParam.Value = AU915_DEFAULT_TX_POWER; - break; - } - case PHY_MAX_PAYLOAD: { - phyParam.Value = MaxPayloadOfDatarateAU915[getPhy->Datarate]; - break; - } - case PHY_MAX_PAYLOAD_REPEATER: { - phyParam.Value = - MaxPayloadOfDatarateRepeaterAU915[getPhy->Datarate]; - break; - } - case PHY_DUTY_CYCLE: { - phyParam.Value = AU915_DUTY_CYCLE_ENABLED; - break; - } - case PHY_MAX_RX_WINDOW: { - phyParam.Value = AU915_MAX_RX_WINDOW; - break; - } - case PHY_RECEIVE_DELAY1: { - phyParam.Value = AU915_RECEIVE_DELAY1; - break; - } - case PHY_RECEIVE_DELAY2: { - phyParam.Value = AU915_RECEIVE_DELAY2; - break; - } - case PHY_JOIN_ACCEPT_DELAY1: { - phyParam.Value = AU915_JOIN_ACCEPT_DELAY1; - break; - } - case PHY_JOIN_ACCEPT_DELAY2: { - phyParam.Value = AU915_JOIN_ACCEPT_DELAY2; - break; - } - case PHY_MAX_FCNT_GAP: { - phyParam.Value = AU915_MAX_FCNT_GAP; - break; - } - case PHY_ACK_TIMEOUT: { - phyParam.Value = - ( AU915_ACKTIMEOUT - + get_random(-AU915_ACK_TIMEOUT_RND, - AU915_ACK_TIMEOUT_RND)); - break; - } - case PHY_DEF_DR1_OFFSET: { - phyParam.Value = AU915_DEFAULT_RX1_DR_OFFSET; - break; - } - case PHY_DEF_RX2_FREQUENCY: { - phyParam.Value = AU915_RX_WND_2_FREQ; - break; - } - case PHY_DEF_RX2_DR: { - phyParam.Value = AU915_RX_WND_2_DR; - break; - } - case PHY_CHANNELS_MASK: { - phyParam.ChannelsMask = ChannelsMask; - break; - } - case PHY_CHANNELS_DEFAULT_MASK: { - phyParam.ChannelsMask = ChannelsDefaultMask; - break; - } - case PHY_MAX_NB_CHANNELS: { - phyParam.Value = AU915_MAX_NB_CHANNELS; - break; - } - case PHY_CHANNELS: { - phyParam.Channels = Channels; - break; - } - case PHY_DEF_UPLINK_DWELL_TIME: - case PHY_DEF_DOWNLINK_DWELL_TIME: { - phyParam.Value = 0; - break; - } - case PHY_DEF_MAX_EIRP: { - phyParam.fValue = AU915_DEFAULT_MAX_EIRP; - break; - } - case PHY_DEF_ANTENNA_GAIN: { - phyParam.fValue = AU915_DEFAULT_ANTENNA_GAIN; - break; - } - case PHY_NB_JOIN_TRIALS: - case PHY_DEF_NB_JOIN_TRIALS: { - phyParam.Value = 2; - break; - } - default: { - break; - } - } - - return phyParam; -} - -void LoRaPHYAU915::set_band_tx_done(SetBandTxDoneParams_t* txDone) -{ - set_last_tx_done(txDone->Joined, &Bands[Channels[txDone->Channel].Band], - txDone->LastTxDoneTime); -} - -void LoRaPHYAU915::load_defaults(InitType_t type) -{ - switch (type) { - case INIT_TYPE_INIT: { - // Channels - // 125 kHz channels - for (uint8_t i = 0; i < AU915_MAX_NB_CHANNELS - 8; i++) { - Channels[i].Frequency = 915200000 + i * 200000; - Channels[i].DrRange.Value = ( DR_5 << 4) | DR_0; - Channels[i].Band = 0; - } - // 500 kHz channels - for (uint8_t i = AU915_MAX_NB_CHANNELS - 8; - i < AU915_MAX_NB_CHANNELS; i++) { - Channels[i].Frequency = 915900000 - + (i - ( AU915_MAX_NB_CHANNELS - 8)) * 1600000; - Channels[i].DrRange.Value = ( DR_6 << 4) | DR_6; - Channels[i].Band = 0; - } - - // Initialize channels default mask - ChannelsDefaultMask[0] = 0xFFFF; - ChannelsDefaultMask[1] = 0xFFFF; - ChannelsDefaultMask[2] = 0xFFFF; - ChannelsDefaultMask[3] = 0xFFFF; - ChannelsDefaultMask[4] = 0x00FF; - ChannelsDefaultMask[5] = 0x0000; - - // Copy channels default mask - copy_channel_mask(ChannelsMask, ChannelsDefaultMask, 6); - - // Copy into channels mask remaining - copy_channel_mask(ChannelsMaskRemaining, ChannelsMask, 6); - break; - } - case INIT_TYPE_RESTORE: { - // Copy channels default mask - copy_channel_mask(ChannelsMask, ChannelsDefaultMask, 6); - - for (uint8_t i = 0; i < 6; i++) { // Copy-And the channels mask - ChannelsMaskRemaining[i] &= ChannelsMask[i]; - } - break; - } - default: { - break; - } - } -} - -bool LoRaPHYAU915::verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute) -{ - switch (phyAttribute) { - case PHY_TX_DR: - case PHY_DEF_TX_DR: { - return val_in_range(verify->DatarateParams.Datarate, - AU915_TX_MIN_DATARATE, AU915_TX_MAX_DATARATE); - } - case PHY_RX_DR: { - return val_in_range(verify->DatarateParams.Datarate, - AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE); - } - case PHY_DEF_TX_POWER: - case PHY_TX_POWER: { - // Remark: switched min and max! - return val_in_range(verify->TxPower, AU915_MAX_TX_POWER, - AU915_MIN_TX_POWER); - } - case PHY_DUTY_CYCLE: { - return AU915_DUTY_CYCLE_ENABLED; - } - case PHY_NB_JOIN_TRIALS: { - if (verify->NbJoinTrials < 2) { - return false; - } - break; - } - default: - return false; - } - return true; -} - -void LoRaPHYAU915::apply_cf_list(ApplyCFListParams_t* applyCFList) -{ - return; -} - -bool LoRaPHYAU915::set_channel_mask(ChanMaskSetParams_t* chanMaskSet) -{ - uint8_t nbChannels = num_active_channels(chanMaskSet->ChannelsMaskIn, 0, 4); - - // Check the number of active channels - // According to ACMA regulation, we require at least 20 125KHz channels, if - // the node shall utilize 125KHz channels. - if ((nbChannels < 20) && (nbChannels > 0)) { - return false; - } - - switch (chanMaskSet->ChannelsMaskType) { - case CHANNELS_MASK: { - copy_channel_mask(ChannelsMask, chanMaskSet->ChannelsMaskIn, 6); - - for (uint8_t i = 0; i < 6; i++) { // Copy-And the channels mask - ChannelsMaskRemaining[i] &= ChannelsMask[i]; - } - break; - } - case CHANNELS_DEFAULT_MASK: { - copy_channel_mask(ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, - 6); - break; - } - default: - return false; - } - return true; -} - -bool LoRaPHYAU915::get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter) -{ - bool adrAckReq = false; - int8_t datarate = adrNext->Datarate; - int8_t txPower = adrNext->TxPower; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - - // Report back the adr ack counter - *adrAckCounter = adrNext->AdrAckCounter; - - if (adrNext->AdrEnabled == true) { - if (datarate == AU915_TX_MIN_DATARATE) { - *adrAckCounter = 0; - adrAckReq = false; - } else { - if (adrNext->AdrAckCounter >= AU915_ADR_ACK_LIMIT) { - adrAckReq = true; - txPower = AU915_MAX_TX_POWER; - } else { - adrAckReq = false; - } - if (adrNext->AdrAckCounter - >= ( AU915_ADR_ACK_LIMIT + AU915_ADR_ACK_DELAY)) { - if ((adrNext->AdrAckCounter % AU915_ADR_ACK_DELAY) == 1) { - // Decrease the datarate - getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; - getPhy.Datarate = datarate; - getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; - phyParam = get_phy_params(&getPhy); - datarate = phyParam.Value; - - if (datarate == AU915_TX_MIN_DATARATE) { - // We must set adrAckReq to false as soon as we reach the lowest datarate - adrAckReq = false; - if (adrNext->UpdateChanMask == true) { - // Re-enable default channels - ChannelsMask[0] = 0xFFFF; - ChannelsMask[1] = 0xFFFF; - ChannelsMask[2] = 0xFFFF; - ChannelsMask[3] = 0xFFFF; - ChannelsMask[4] = 0x00FF; - ChannelsMask[5] = 0x0000; - } - } - } - } - } - } - - *drOut = datarate; - *txPowOut = txPower; - return adrAckReq; -} - -void LoRaPHYAU915::compute_rx_win_params(int8_t datarate, uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams) -{ - double tSymbol = 0.0; - - // Get the datarate, perform a boundary check - rxConfigParams->Datarate = MIN(datarate, AU915_RX_MAX_DATARATE); - rxConfigParams->Bandwidth = GetBandwidth(rxConfigParams->Datarate); - - tSymbol = compute_symb_timeout_lora( - DataratesAU915[rxConfigParams->Datarate], - BandwidthsAU915[rxConfigParams->Datarate]); - - get_rx_window_params(tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, - &rxConfigParams->WindowTimeout, - &rxConfigParams->WindowOffset); -} - -bool LoRaPHYAU915::rx_config(RxConfigParams_t* rxConfig, int8_t* datarate) -{ - int8_t dr = rxConfig->Datarate; - uint8_t maxPayload = 0; - int8_t phyDr = 0; - uint32_t frequency = rxConfig->Frequency; + int8_t dr = params->datarate; + uint8_t max_payload = 0; + int8_t phy_dr = 0; + uint32_t frequency = params->frequency; if (_radio->get_status() != RF_IDLE) { return false; } - if (rxConfig->Window == 0) { + if (params->rx_slot == RX_SLOT_WIN_1) { // Apply window 1 frequency frequency = AU915_FIRST_RX1_CHANNEL - + (rxConfig->Channel % 8) * AU915_STEPWIDTH_RX1_CHANNEL; + + (params->channel % 8) * AU915_STEPWIDTH_RX1_CHANNEL; } // Read the physical datarate from the datarates table - phyDr = DataratesAU915[dr]; + phy_dr = datarates_AU915[dr]; + + _radio->lock(); _radio->set_channel(frequency); // Radio configuration - _radio->set_rx_config(MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, - rxConfig->WindowTimeout, false, 0, false, 0, 0, true, - rxConfig->RxContinuous); + _radio->set_rx_config(MODEM_LORA, params->bandwidth, phy_dr, 1, 0, 8, + params->window_timeout, false, 0, false, 0, 0, true, + params->is_rx_continuous); - if (rxConfig->RepeaterSupport == true) { - maxPayload = MaxPayloadOfDatarateRepeaterAU915[dr]; + if (params->is_repeater_supported == true) { + max_payload = max_payload_with_repeater_AU915[dr]; } else { - maxPayload = MaxPayloadOfDatarateAU915[dr]; + max_payload = max_payload_AU915[dr]; } _radio->set_max_payload_length(MODEM_LORA, - maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD); + max_payload + LORA_MAC_FRMPAYLOAD_OVERHEAD); + + _radio->unlock(); *datarate = (uint8_t) dr; return true; } -bool LoRaPHYAU915::tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir) +bool LoRaPHYAU915::tx_config(tx_config_params_t* params, int8_t* tx_power, + lorawan_time_t* tx_toa) { - int8_t phyDr = DataratesAU915[txConfig->Datarate]; - int8_t txPowerLimited = LimitTxPower( - txConfig->TxPower, - Bands[Channels[txConfig->Channel].Band].TxMaxPower, - txConfig->Datarate, ChannelsMask); - uint32_t bandwidth = GetBandwidth(txConfig->Datarate); - int8_t phyTxPower = 0; + int8_t phy_dr = datarates_AU915[params->datarate]; + + if (params->tx_power > bands[channels[params->channel].band].max_tx_pwr) { + params->tx_power = bands[channels[params->channel].band].max_tx_pwr; + } + + uint32_t bandwidth = get_bandwidth(params->datarate); + int8_t phy_tx_power = 0; // Calculate physical TX power - phyTxPower = compute_tx_power(txPowerLimited, txConfig->MaxEirp, - txConfig->AntennaGain); + phy_tx_power = compute_tx_power(params->tx_power, params->max_eirp, + params->antenna_gain); - // Setup the radio frequency - _radio->set_channel(Channels[txConfig->Channel].Frequency); + // setting up radio tx configurations - _radio->set_tx_config(MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, + _radio->lock(); + + _radio->set_channel(channels[params->channel].frequency); + + _radio->set_tx_config(MODEM_LORA, phy_tx_power, 0, bandwidth, phy_dr, 1, 8, false, true, 0, 0, false, 3000); // Setup maximum payload lenght of the radio driver - _radio->set_max_payload_length(MODEM_LORA, txConfig->PktLen); + _radio->set_max_payload_length(MODEM_LORA, params->pkt_len); - *txTimeOnAir = _radio->time_on_air(MODEM_LORA, txConfig->PktLen); - *txPower = txPowerLimited; + *tx_toa = _radio->time_on_air(MODEM_LORA, params->pkt_len); + + _radio->unlock(); + + *tx_power = params->tx_power; return true; } -uint8_t LoRaPHYAU915::link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, - uint8_t* nbBytesParsed) +uint8_t LoRaPHYAU915::link_ADR_request(adr_req_params_t* params, + int8_t* dr_out, int8_t* tx_power_out, + uint8_t* nb_rep_out, + uint8_t* nb_bytes_parsed) { uint8_t status = 0x07; - RegionCommonLinkAdrParams_t linkAdrParams; - uint8_t nextIndex = 0; - uint8_t bytesProcessed = 0; - uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + link_adr_params_t adr_settings; + uint8_t next_index = 0; + uint8_t bytes_processed = 0; + uint16_t temp_channel_masks[AU915_CHANNEL_MASK_SIZE] = { 0, 0, 0, 0, 0}; + + verify_adr_params_t verify_params; // Initialize local copy of channels mask - copy_channel_mask(channelsMask, ChannelsMask, 6); + copy_channel_mask(temp_channel_masks, channel_mask, AU915_CHANNEL_MASK_SIZE); - while (bytesProcessed < linkAdrReq->PayloadSize) { - nextIndex = parse_link_ADR_req(&(linkAdrReq->Payload[bytesProcessed]), - &linkAdrParams); + while (bytes_processed < params->payload_size) { + next_index = parse_link_ADR_req(&(params->payload[bytes_processed]), + &adr_settings); - if (nextIndex == 0) + if (next_index == 0) { break; // break loop, since no more request has been found + } // Update bytes processed - bytesProcessed += nextIndex; + bytes_processed += next_index; // Revert status, as we only check the last ADR request for the channel mask KO status = 0x07; - if (linkAdrParams.ChMaskCtrl == 6) { + if (adr_settings.ch_mask_ctrl == 6) { // Enable all 125 kHz channels - channelsMask[0] = 0xFFFF; - channelsMask[1] = 0xFFFF; - channelsMask[2] = 0xFFFF; - channelsMask[3] = 0xFFFF; + temp_channel_masks[0] = 0xFFFF; + temp_channel_masks[1] = 0xFFFF; + temp_channel_masks[2] = 0xFFFF; + temp_channel_masks[3] = 0xFFFF; // Apply chMask to channels 64 to 71 - channelsMask[4] = linkAdrParams.ChMask; - } else if (linkAdrParams.ChMaskCtrl == 7) { + temp_channel_masks[4] = adr_settings.channel_mask; + } else if (adr_settings.ch_mask_ctrl == 7) { // Disable all 125 kHz channels - channelsMask[0] = 0x0000; - channelsMask[1] = 0x0000; - channelsMask[2] = 0x0000; - channelsMask[3] = 0x0000; + temp_channel_masks[0] = 0x0000; + temp_channel_masks[1] = 0x0000; + temp_channel_masks[2] = 0x0000; + temp_channel_masks[3] = 0x0000; // Apply chMask to channels 64 to 71 - channelsMask[4] = linkAdrParams.ChMask; - } else if (linkAdrParams.ChMaskCtrl == 5) { + temp_channel_masks[4] = adr_settings.channel_mask; + } else if (adr_settings.ch_mask_ctrl == 5) { // RFU status &= 0xFE; // Channel mask KO } else { - channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + temp_channel_masks[adr_settings.ch_mask_ctrl] = adr_settings.channel_mask; } } // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels - if ((linkAdrParams.Datarate < DR_6) - && (num_active_channels(channelsMask, 0, 4) < 2)) { + if ((adr_settings.datarate < DR_6) + && (num_active_channels(temp_channel_masks, 0, 4) < 2)) { status &= 0xFE; // Channel mask KO } - // Get the minimum possible datarate - getPhy.Attribute = PHY_MIN_TX_DR; - getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; - phyParam = get_phy_params(&getPhy); + verify_params.status = status; + verify_params.adr_enabled = params->adr_enabled; + verify_params.datarate = adr_settings.datarate; + verify_params.tx_power = adr_settings.tx_power; + verify_params.nb_rep = adr_settings.nb_rep; + verify_params.current_datarate = params->current_datarate; + verify_params.current_tx_power = params->current_tx_power; + verify_params.current_nb_rep = params->current_nb_rep; + verify_params.channel_mask = temp_channel_masks; - linkAdrVerifyParams.Status = status; - linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; - linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; - linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; - linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; - linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; - linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; - linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; - linkAdrVerifyParams.NbChannels = AU915_MAX_NB_CHANNELS; - linkAdrVerifyParams.ChannelsMask = channelsMask; - linkAdrVerifyParams.MinDatarate = (int8_t) phyParam.Value; - linkAdrVerifyParams.MaxDatarate = AU915_TX_MAX_DATARATE; - linkAdrVerifyParams.Channels = Channels; - linkAdrVerifyParams.MinTxPower = AU915_MIN_TX_POWER; - linkAdrVerifyParams.MaxTxPower = AU915_MAX_TX_POWER; // Verify the parameters and update, if necessary - status = verify_link_ADR_req(&linkAdrVerifyParams, &linkAdrParams.Datarate, - &linkAdrParams.TxPower, &linkAdrParams.NbRep); + status = verify_link_ADR_req(&verify_params, &adr_settings.datarate, + &adr_settings.tx_power, &adr_settings.nb_rep); - // Update channelsMask if everything is correct + // Update cchannel mask if everything is correct if (status == 0x07) { // Copy Mask - copy_channel_mask(ChannelsMask, channelsMask, 6); + copy_channel_mask(channel_mask, temp_channel_masks, AU915_CHANNEL_MASK_SIZE); - ChannelsMaskRemaining[0] &= ChannelsMask[0]; - ChannelsMaskRemaining[1] &= ChannelsMask[1]; - ChannelsMaskRemaining[2] &= ChannelsMask[2]; - ChannelsMaskRemaining[3] &= ChannelsMask[3]; - ChannelsMaskRemaining[4] = ChannelsMask[4]; - ChannelsMaskRemaining[5] = ChannelsMask[5]; + current_channel_mask[0] &= channel_mask[0]; + current_channel_mask[1] &= channel_mask[1]; + current_channel_mask[2] &= channel_mask[2]; + current_channel_mask[3] &= channel_mask[3]; + current_channel_mask[4] = channel_mask[4]; } // Update status variables - *drOut = linkAdrParams.Datarate; - *txPowOut = linkAdrParams.TxPower; - *nbRepOut = linkAdrParams.NbRep; - *nbBytesParsed = bytesProcessed; + *dr_out = adr_settings.datarate; + *tx_power_out = adr_settings.tx_power; + *nb_rep_out = adr_settings.nb_rep; + *nb_bytes_parsed = bytes_processed; return status; } -uint8_t LoRaPHYAU915::setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq) +uint8_t LoRaPHYAU915::accept_rx_param_setup_req(rx_param_setup_req_t* params) { uint8_t status = 0x07; - uint32_t freq = rxParamSetupReq->Frequency; + uint32_t freq = params->frequency; // Verify radio frequency + _radio->lock(); + if ((_radio->check_rf_frequency(freq) == false) || (freq < AU915_FIRST_RX1_CHANNEL) || (freq > AU915_LAST_RX1_CHANNEL) @@ -817,126 +520,96 @@ uint8_t LoRaPHYAU915::setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq) status &= 0xFE; // Channel frequency KO } + _radio->unlock(); + // Verify datarate - if (val_in_range(rxParamSetupReq->Datarate, AU915_RX_MIN_DATARATE, - AU915_RX_MAX_DATARATE) == 0) { + if (val_in_range(params->datarate, AU915_RX_MIN_DATARATE, AU915_RX_MAX_DATARATE) == 0) { status &= 0xFD; // Datarate KO } - if ((rxParamSetupReq->Datarate == DR_7) - || (rxParamSetupReq->Datarate > DR_13)) { + + if ((params->datarate == DR_7) || (params->datarate > DR_13)) { status &= 0xFD; // Datarate KO } // Verify datarate offset - if (val_in_range(rxParamSetupReq->DrOffset, AU915_MIN_RX1_DR_OFFSET, - AU915_MAX_RX1_DR_OFFSET) == 0) { + if (val_in_range(params->dr_offset, AU915_MIN_RX1_DR_OFFSET, AU915_MAX_RX1_DR_OFFSET) == 0) { status &= 0xFB; // Rx1DrOffset range KO } return status; } -uint8_t LoRaPHYAU915::request_new_channel(NewChannelReqParams_t* newChannelReq) -{ - // Datarate and frequency KO - return 0; -} - -int8_t LoRaPHYAU915::setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq) -{ - return -1; -} - -uint8_t LoRaPHYAU915::dl_channel_request(DlChannelReqParams_t* dlChannelReq) -{ - return 0; -} - -int8_t LoRaPHYAU915::get_alternate_DR(AlternateDrParams_t* alternateDr) +int8_t LoRaPHYAU915::get_alternate_DR(uint8_t nb_trials) { int8_t datarate = 0; // Re-enable 500 kHz default channels - ChannelsMask[4] = 0x00FF; + channel_mask[4] = 0x00FF; - if ((alternateDr->NbTrials & 0x01) == 0x01) { + if ((nb_trials & 0x01) == 0x01) { datarate = DR_6; } else { datarate = DR_0; } + return datarate; } -void LoRaPHYAU915::calculate_backoff(CalcBackOffParams_t* calcBackOff) +bool LoRaPHYAU915::set_next_channel(channel_selection_params_t* next_chan_params, + uint8_t* channel, lorawan_time_t* time, + lorawan_time_t* aggregated_timeOff) { - RegionCommonCalcBackOffParams_t calcBackOffParams; - - calcBackOffParams.Channels = Channels; - calcBackOffParams.Bands = Bands; - calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; - calcBackOffParams.Joined = calcBackOff->Joined; - calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; - calcBackOffParams.Channel = calcBackOff->Channel; - calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; - calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; - - get_DC_backoff(&calcBackOffParams); -} - -bool LoRaPHYAU915::set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTx = 0; - uint8_t enabledChannels[AU915_MAX_NB_CHANNELS] = { 0 }; - TimerTime_t nextTxDelay = 0; + uint8_t nb_enabled_channels = 0; + uint8_t delay_tx = 0; + uint8_t enabled_channels[AU915_MAX_NB_CHANNELS] = { 0 }; + lorawan_time_t next_tx_delay = 0; // Count 125kHz channels - if (num_active_channels(ChannelsMaskRemaining, 0, 4) == 0) { // Reactivate default channels - copy_channel_mask(ChannelsMaskRemaining, ChannelsMask, 4); + if (num_active_channels(current_channel_mask, 0, 4) == 0) { + // Reactivate 125 kHz default channels + copy_channel_mask(current_channel_mask, channel_mask, 4); } + // Check other channels - if (nextChanParams->Datarate >= DR_6) { - if ((ChannelsMaskRemaining[4] & 0x00FF) == 0) { - ChannelsMaskRemaining[4] = ChannelsMask[4]; + if (next_chan_params->current_datarate >= DR_6) { + if ((current_channel_mask[4] & 0x00FF) == 0) { + // fall back to 500 kHz default channels + current_channel_mask[4] = channel_mask[4]; } } - if (nextChanParams->AggrTimeOff - <= TimerGetElapsedTime(nextChanParams->LastAggrTx)) { + if (next_chan_params->aggregate_timeoff <= _lora_time.get_elapsed_time(next_chan_params->last_aggregate_tx_time)) { // Reset Aggregated time off - *aggregatedTimeOff = 0; + *aggregated_timeOff = 0; // Update bands Time OFF - nextTxDelay = update_band_timeoff(nextChanParams->Joined, - nextChanParams->DutyCycleEnabled, - Bands, AU915_MAX_NB_BANDS); + next_tx_delay = update_band_timeoff(next_chan_params->joined, + next_chan_params->dc_enabled, + bands, AU915_MAX_NB_BANDS); // Search how many channels are enabled - nbEnabledChannels = CountNbOfEnabledChannels(nextChanParams->Datarate, - ChannelsMaskRemaining, - Channels, Bands, - enabledChannels, &delayTx); + nb_enabled_channels = enabled_channel_count(next_chan_params->joined, + next_chan_params->current_datarate, + current_channel_mask, + enabled_channels, &delay_tx); } else { - delayTx++; - nextTxDelay = nextChanParams->AggrTimeOff - - TimerGetElapsedTime(nextChanParams->LastAggrTx); + delay_tx++; + next_tx_delay = next_chan_params->aggregate_timeoff - _lora_time.get_elapsed_time(next_chan_params->last_aggregate_tx_time); } - if (nbEnabledChannels > 0) { + if (nb_enabled_channels > 0) { // We found a valid channel - *channel = enabledChannels[get_random(0, nbEnabledChannels - 1)]; + *channel = enabled_channels[get_random(0, nb_enabled_channels - 1)]; // Disable the channel in the mask - disable_channel(ChannelsMaskRemaining, *channel, - AU915_MAX_NB_CHANNELS - 8); + disable_channel(current_channel_mask, *channel, + AU915_MAX_NB_CHANNELS - 8); *time = 0; return true; } else { - if (delayTx > 0) { + if (delay_tx > 0) { // Delay transmission due to AggregatedTimeOff or to a band time off - *time = nextTxDelay; + *time = next_tx_delay; return true; } // Datarate not supported by any channel @@ -945,37 +618,9 @@ bool LoRaPHYAU915::set_next_channel(NextChanParams_t* nextChanParams, } } -LoRaMacStatus_t LoRaPHYAU915::add_channel(ChannelAddParams_t* channelAdd) +uint8_t LoRaPHYAU915::apply_DR_offset(int8_t dr, int8_t dr_offset) { - return LORAMAC_STATUS_PARAMETER_INVALID; -} - -bool LoRaPHYAU915::remove_channel(ChannelRemoveParams_t* channelRemove) -{ - return LORAMAC_STATUS_PARAMETER_INVALID; -} - -void LoRaPHYAU915::set_tx_cont_mode(ContinuousWaveParams_t* continuousWave) -{ - int8_t txPowerLimited = LimitTxPower( - continuousWave->TxPower, - Bands[Channels[continuousWave->Channel].Band].TxMaxPower, - continuousWave->Datarate, ChannelsMask); - int8_t phyTxPower = 0; - uint32_t frequency = Channels[continuousWave->Channel].Frequency; - - // Calculate physical TX power - phyTxPower = compute_tx_power(txPowerLimited, continuousWave->MaxEirp, - continuousWave->AntennaGain); - - _radio->set_tx_continuous_wave(frequency, phyTxPower, - continuousWave->Timeout); -} - -uint8_t LoRaPHYAU915::apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, - int8_t drOffset) -{ - int8_t datarate = DatarateOffsetsAU915[dr][drOffset]; + int8_t datarate = datarate_offsets_AU915[dr][dr_offset]; if (datarate < 0) { datarate = DR_0; diff --git a/features/lorawan/lorastack/phy/LoRaPHYAU915.h b/features/lorawan/lorastack/phy/LoRaPHYAU915.h index ab0651db0a..5c7c358f61 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYAU915.h +++ b/features/lorawan/lorastack/phy/LoRaPHYAU915.h @@ -34,7 +34,6 @@ #define MBED_OS_LORAPHY_AU915_H_ #include "LoRaPHY.h" -#include "netsocket/LoRaRadio.h" // Definitions /*! @@ -47,327 +46,62 @@ */ #define AU915_MAX_NB_BANDS 1 -#define AU915_CHANNELS_MASK_SIZE 6 +#define AU915_CHANNEL_MASK_SIZE 5 class LoRaPHYAU915 : public LoRaPHY{ public: - LoRaPHYAU915(); + LoRaPHYAU915(LoRaWANTimeHandler &lora_time); virtual ~LoRaPHYAU915(); - /*! - * \brief The function gets a value of a specific PHY attribute. - * - * \param [in] getPhy A pointer to the function parameters. - * - * \retval A structure containing the PHY parameter. - */ - virtual PhyParam_t get_phy_params(GetPhyParams_t* getPhy ); + virtual bool rx_config(rx_config_params_t* config, int8_t* datarate); - /*! - * \brief Updates the last TX done parameters of the current channel. - * - * \param [in] txDone A pointer to the function parameters. - */ - virtual void set_band_tx_done(SetBandTxDoneParams_t* txDone ); + virtual bool tx_config(tx_config_params_t* config, int8_t* txPower, + lorawan_time_t* txTimeOnAir); - /*! - * \brief Initializes the channels masks and the channels. - * - * \param [in] type Sets the initialization type. - */ - virtual void load_defaults(InitType_t type ); - - /*! - * \brief Verifies a parameter. - * - * \param [in] verify A pointer to the function parameters. - * - * \param [in] phyAttribute The attribute to be verified. - * - * \retval True, if the parameter is valid. - */ - virtual bool verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute ); - - /*! - * \brief The function parses the input buffer and sets up the channels of the - * CF list. - * - * \param [in] applyCFList A pointer to the function parameters. - */ - virtual void apply_cf_list(ApplyCFListParams_t* applyCFList ); - - /*! - * \brief Sets a channels mask. - * - * \param [in] chanMaskSet A pointer to the function parameters. - * - * \retval True, if the channels mask could be set. - */ - virtual bool set_channel_mask(ChanMaskSetParams_t* chanMaskSet ); - - /*! - * \brief Calculates the next datarate to set, when ADR is on or off. - * - * \param [in] adrNext A pointer to the function parameters. - * - * \param [out] drOut The calculated datarate for the next TX. - * - * \param [out] txPowOut The TX power for the next TX. - * - * \param [out] adrAckCounter The calculated ADR acknowledgement counter. - * - * \retval True, if an ADR request should be performed. - */ - virtual bool get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter ); - - /*! - * \brief Configuration of the RX windows. - * - * \param [in] rxConfig A pointer to the function parameters. - * - * \param [out] datarate The datarate index set. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool rx_config(RxConfigParams_t* rxConfig, int8_t* datarate ); - - /* - * RX window precise timing - * - * For more details please consult the following document, chapter 3.1.2. - * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf - * or - * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf - * - * Downlink start: T = Tx + 1s (+/- 20 us) - * | - * TRxEarly | TRxLate - * | | | - * | | +---+---+---+---+---+---+---+---+ - * | | | Latest Rx window | - * | | +---+---+---+---+---+---+---+---+ - * | | | - * +---+---+---+---+---+---+---+---+ - * | Earliest Rx window | - * +---+---+---+---+---+---+---+---+ - * | - * +---+---+---+---+---+---+---+---+ - *Downlink preamble 8 symbols | | | | | | | | | - * +---+---+---+---+---+---+---+---+ - * - * Worst case Rx window timings - * - * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME - * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME - * - * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * - * RxOffset = ( TRxLate + TRxEarly ) / 2 - * - * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME - * - * The minimum value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol - */ - /*! - * Computes the Rx window timeout and offset. - * - * \param [in] datarate The RX window datarate index to be used. - * - * \param [in] minRxSymbols The minimum number of symbols required to detect an RX frame. - * - * \param [in] rxError The system maximum timing error of the receiver in milliseconds. - * The receiver will turn on in a [-rxError : +rxError] ms - * interval around RxOffset. - * - * \param [out] rxConfigParams The updated WindowTimeout and WindowOffset fields. - */ - virtual void compute_rx_win_params(int8_t datarate, - uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams); - - /*! - * \brief TX configuration. - * - * \param [in] txConfig A pointer to the function parameters. - * - * \param [out] txPower The TX power index set. - * - * \param [out] txTimeOnAir The time-on-air of the frame. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir ); - - /*! - * \brief The function processes a Link ADR Request. - * - * \param [in] linkAdrReq A pointer to the function parameters. - * - * \param [out] drOut The datarate applied. - * - * \param [out] txPowOut The TX power applied. - * - * \param [out] nbRepOut The number of repetitions to apply. - * - * \param [out] nbBytesParsed The number bytes parsed. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t link_ADR_request(LinkAdrReqParams_t* linkAdrReq, + virtual uint8_t link_ADR_request(adr_req_params_t* params, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, - uint8_t* nbBytesParsed ); + uint8_t* nbBytesParsed); - /*! - * \brief The function processes a RX parameter setup request. - * - * \param [in] rxParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq ); + virtual uint8_t accept_rx_param_setup_req(rx_param_setup_req_t* params); - /*! - * \brief The function processes a new channel request. - * - * \param [in] newChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t request_new_channel(NewChannelReqParams_t* newChannelReq ); + virtual int8_t get_alternate_DR(uint8_t nb_trials); - /*! - * \brief The function processes a TX ParamSetup request. - * - * \param [in] txParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - * Returns -1, if the functionality is not implemented. In this case, the end node - * shall ignore the command. - */ - virtual int8_t setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq ); + virtual bool set_next_channel(channel_selection_params_t* next_chan_params, + uint8_t* channel, lorawan_time_t* time, + lorawan_time_t* aggregate_timeoff); - /*! - * \brief The function processes a DlChannel request. - * - * \param [in] dlChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t dl_channel_request(DlChannelReqParams_t* dlChannelReq ); - - /*! - * \brief Alternates the datarate of the channel for the join request. - * - * \param [in] alternateDr A pointer to the function parameters. - * - * \retval The datarate to apply. - */ - virtual int8_t get_alternate_DR(AlternateDrParams_t* alternateDr ); - - /*! - * \brief Calculates the back-off time. - * - * \param [in] calcBackOff A pointer to the function parameters. - */ - virtual void calculate_backoff(CalcBackOffParams_t* calcBackOff ); - - /*! - * \brief Searches and sets the next random available channel. - * - * \param [in] nextChanParams The parameters for the next channel. - * - * \param [out] channel The next channel to use for TX. - * - * \param [out] time The time to wait for the next transmission according to the duty - * cycle. - * - * \param [out] aggregatedTimeOff Updates the aggregated time off. - * - * \retval The function status [1: OK, 0: Unable to find a channel on the current datarate]. - */ - virtual bool set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff ); - - /*! - * \brief Adds a channel. - * - * \param [in] channelAdd A pointer to the function parameters. - * - * \retval The status of the operation. - */ - virtual LoRaMacStatus_t add_channel(ChannelAddParams_t* channelAdd ); - - /*! - * \brief Removes a channel. - * - * \param [in] channelRemove A pointer to the function parameters. - * - * \retval True, if the channel was removed successfully. - */ - virtual bool remove_channel(ChannelRemoveParams_t* channelRemove ); - - /*! - * \brief Sets the radio into continuous wave mode. - * - * \param [in] continuousWave A pointer to the function parameters. - */ - virtual void set_tx_cont_mode(ContinuousWaveParams_t* continuousWave ); - - /*! - * \brief Computes a new datarate according to the given offset. - * - * \param [in] downlinkDwellTime The downlink dwell time configuration. 0: No limit, 1: 400ms - * - * \param [in] dr The current datarate. - * - * \param [in] drOffset The offset to be applied. - * - * \retval newDr The computed datarate. - */ - virtual uint8_t apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + virtual uint8_t apply_DR_offset(int8_t dr, int8_t dr_offset); private: - uint8_t CountNbOfEnabledChannels(uint8_t datarate, - uint16_t* channelsMask, - ChannelParams_t* channels, - Band_t* bands, uint8_t* enabledChannels, - uint8_t* delayTx); - - // Global attributes /*! * LoRaMAC channels */ - ChannelParams_t Channels[AU915_MAX_NB_CHANNELS]; + channel_params_t channels[AU915_MAX_NB_CHANNELS]; /*! * LoRaMac bands */ - Band_t Bands[AU915_MAX_NB_BANDS]; + band_t bands[AU915_MAX_NB_BANDS]; /*! - * LoRaMac channels mask + * LoRaMac channel mask */ - uint16_t ChannelsMask[AU915_CHANNELS_MASK_SIZE]; + uint16_t channel_mask[AU915_CHANNEL_MASK_SIZE]; /*! - * LoRaMac channels remaining + * Previously used channel mask */ - uint16_t ChannelsMaskRemaining[AU915_CHANNELS_MASK_SIZE]; + uint16_t current_channel_mask[AU915_CHANNEL_MASK_SIZE]; /*! * LoRaMac channels default mask */ - uint16_t ChannelsDefaultMask[AU915_CHANNELS_MASK_SIZE]; + uint16_t default_channel_mask[AU915_CHANNEL_MASK_SIZE]; }; #endif /* MBED_OS_LORAPHY_AU915_H_ */ diff --git a/features/lorawan/lorastack/phy/LoRaPHYCN470.cpp b/features/lorawan/lorastack/phy/LoRaPHYCN470.cpp index 61031fb923..68ff3f5525 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYCN470.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYCN470.cpp @@ -30,9 +30,7 @@ */ #include "LoRaPHYCN470.h" - #include "lora_phy_ds.h" -#include "LoRaRadio.h" /*! * Minimal datarate that can be used by the node @@ -166,814 +164,349 @@ /*! * Band 0 definition - * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } */ -#define CN470_BAND0 { 1, CN470_MAX_TX_POWER, 0, 0 } // 100.0 % +static const band_t CN470_BAND0 = {1, CN470_MAX_TX_POWER, 0, 0, 0}; // 100.0 % /*! * Defines the first channel for RX window 1 for CN470 band */ -#define CN470_FIRST_RX1_CHANNEL ( (uint32_t) 500300000 ) +#define CN470_FIRST_RX1_CHANNEL ((uint32_t) 500300000) /*! * Defines the last channel for RX window 1 for CN470 band */ -#define CN470_LAST_RX1_CHANNEL ( (uint32_t) 509700000 ) +#define CN470_LAST_RX1_CHANNEL ((uint32_t) 509700000) /*! * Defines the step width of the channels for RX window 1 */ -#define CN470_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 200000 ) +#define CN470_STEPWIDTH_RX1_CHANNEL ((uint32_t) 200000) /*! * Data rates table definition */ -static const uint8_t DataratesCN470[] = { 12, 11, 10, 9, 8, 7 }; +static const uint8_t datarates_CN470[] = {12, 11, 10, 9, 8, 7}; /*! * Bandwidths table definition in Hz */ -static const uint32_t BandwidthsCN470[] = { 125000, 125000, 125000, 125000, 125000, 125000 }; +static const uint32_t bandwidths_CN470[] = {125000, 125000, 125000, 125000, 125000, 125000}; /*! * Maximum payload with respect to the datarate index. Cannot operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateCN470[] = { 51, 51, 51, 115, 222, 222 }; +static const uint8_t max_payloads_CN470[] = {51, 51, 51, 115, 222, 222}; /*! * Maximum payload with respect to the datarate index. Can operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateRepeaterCN470[] = { 51, 51, 51, 115, 222, 222 }; +static const uint8_t max_payloads_with_repeater_CN470[] = {51, 51, 51, 115, 222, 222}; -// Static functions -static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) + +LoRaPHYCN470::LoRaPHYCN470(LoRaWANTimeHandler &lora_time) + : LoRaPHY(lora_time) { - uint8_t nextLowerDr = 0; + bands[0] = CN470_BAND0; - if( dr == minDr ) + // Channels + // 125 kHz channels + for( uint8_t i = 0; i < CN470_MAX_NB_CHANNELS; i++ ) { - nextLowerDr = minDr; - } - else - { - nextLowerDr = dr - 1; - } - return nextLowerDr; -} - -static uint32_t GetBandwidth( uint32_t drIndex ) -{ - switch( BandwidthsCN470[drIndex] ) - { - default: - case 125000: - return 0; - case 250000: - return 1; - case 500000: - return 2; - } -} - -static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) -{ - int8_t txPowerResult = txPower; - - // Limit tx power to the band max - txPowerResult = MAX( txPower, maxBandTxPower ); - - return txPowerResult; -} - -uint8_t LoRaPHYCN470::CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTransmission = 0; - - for( uint8_t i = 0, k = 0; i < CN470_MAX_NB_CHANNELS; i += 16, k++ ) - { - for( uint8_t j = 0; j < 16; j++ ) - { - if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) - { - if( channels[i + j].Frequency == 0 ) - { // Check if the channel is enabled - continue; - } - if( val_in_range( datarate, channels[i + j].DrRange.Fields.Min, - channels[i + j].DrRange.Fields.Max ) == 0 ) - { // Check if the current channel selection supports the given datarate - continue; - } - if( bands[channels[i + j].Band].TimeOff > 0 ) - { // Check if the band is available for transmission - delayTransmission++; - continue; - } - enabledChannels[nbEnabledChannels++] = i + j; - } - } + channels[i].frequency = 470300000 + i * 200000; + channels[i].dr_range.value = ( DR_5 << 4 ) | DR_0; + channels[i].band = 0; } - *delayTx = delayTransmission; - return nbEnabledChannels; -} + // Initialize the channels default mask + default_channel_mask[0] = 0xFFFF; + default_channel_mask[1] = 0xFFFF; + default_channel_mask[2] = 0xFFFF; + default_channel_mask[3] = 0xFFFF; + default_channel_mask[4] = 0xFFFF; + default_channel_mask[5] = 0xFFFF; -LoRaPHYCN470::LoRaPHYCN470() -{ - const Band_t band0 = CN470_BAND0; - Bands[0] = band0; + // Update the channels mask + copy_channel_mask(channel_mask, default_channel_mask, CN470_CHANNEL_MASK_SIZE); + + // set default channels + phy_params.channels.channel_list = channels; + phy_params.channels.channel_list_size = CN470_MAX_NB_CHANNELS; + phy_params.channels.mask = channel_mask; + phy_params.channels.default_mask = default_channel_mask; + phy_params.channels.mask_size = CN470_CHANNEL_MASK_SIZE; + + // set bands for CN470 spectrum + phy_params.bands.table = (void *) bands; + phy_params.bands.size = CN470_MAX_NB_BANDS; + + // set bandwidths available in CN470 spectrum + phy_params.bandwidths.table = (void *) bandwidths_CN470; + phy_params.bandwidths.size = 6; + + // set data rates available in CN470 spectrum + phy_params.datarates.table = (void *) datarates_CN470; + phy_params.datarates.size = 6; + + // set payload sizes with respect to data rates + phy_params.payloads.table = (void *) max_payloads_CN470; + phy_params.payloads.size = 6; + phy_params.payloads_with_repeater.table = (void *)max_payloads_with_repeater_CN470; + phy_params.payloads.size = 6; + + // dwell time setting + phy_params.ul_dwell_time_setting = 0; + phy_params.dl_dwell_time_setting = 0; + + // set initial and default parameters + phy_params.duty_cycle_enabled = CN470_DUTY_CYCLE_ENABLED; + + phy_params.accept_tx_param_setup_req = false; + phy_params.fsk_supported = false; + phy_params.cflist_supported = false; + phy_params.dl_channel_req_supported = false; + phy_params.custom_channelplans_supported = false; + + phy_params.default_channel_cnt = CN470_MAX_NB_CHANNELS; + phy_params.max_channel_cnt = CN470_MAX_NB_CHANNELS; + phy_params.cflist_channel_cnt = 0; + phy_params.min_tx_datarate = CN470_TX_MIN_DATARATE; + phy_params.max_tx_datarate = CN470_TX_MAX_DATARATE; + phy_params.min_rx_datarate = CN470_RX_MIN_DATARATE; + phy_params.max_rx_datarate = CN470_RX_MAX_DATARATE; + phy_params.default_datarate = CN470_DEFAULT_DATARATE; + phy_params.default_max_datarate = CN470_TX_MAX_DATARATE; + phy_params.min_rx1_dr_offset = CN470_MIN_RX1_DR_OFFSET; + phy_params.max_rx1_dr_offset = CN470_MAX_RX1_DR_OFFSET; + phy_params.default_rx1_dr_offset = CN470_DEFAULT_RX1_DR_OFFSET; + phy_params.min_tx_power = CN470_MIN_TX_POWER; + phy_params.max_tx_power = CN470_MAX_TX_POWER; + phy_params.default_tx_power = CN470_DEFAULT_TX_POWER; + phy_params.default_max_eirp = CN470_DEFAULT_MAX_EIRP; + phy_params.default_antenna_gain = CN470_DEFAULT_ANTENNA_GAIN; + phy_params.adr_ack_limit = CN470_ADR_ACK_LIMIT; + phy_params.adr_ack_delay = CN470_ADR_ACK_DELAY; + phy_params.max_rx_window = CN470_MAX_RX_WINDOW; + phy_params.recv_delay1 = CN470_RECEIVE_DELAY1; + phy_params.recv_delay2 = CN470_RECEIVE_DELAY2; + + phy_params.join_accept_delay1 = CN470_JOIN_ACCEPT_DELAY1; + phy_params.join_accept_delay2 = CN470_JOIN_ACCEPT_DELAY2; + phy_params.max_fcnt_gap = CN470_MAX_FCNT_GAP; + phy_params.ack_timeout = CN470_ACKTIMEOUT; + phy_params.ack_timeout_rnd = CN470_ACK_TIMEOUT_RND; + phy_params.rx_window2_datarate = CN470_RX_WND_2_DR; + phy_params.rx_window2_frequency = CN470_RX_WND_2_FREQ; } LoRaPHYCN470::~LoRaPHYCN470() { } -PhyParam_t LoRaPHYCN470::get_phy_params(GetPhyParams_t* getPhy) +bool LoRaPHYCN470::rx_config(rx_config_params_t* config, int8_t* datarate) { - PhyParam_t phyParam = { 0 }; + int8_t dr = config->datarate; + uint8_t max_payload = 0; + int8_t phy_dr = 0; + uint32_t frequency = config->frequency; - switch( getPhy->Attribute ) - { - case PHY_MIN_RX_DR: - { - phyParam.Value = CN470_RX_MIN_DATARATE; - break; - } - case PHY_MIN_TX_DR: - { - phyParam.Value = CN470_TX_MIN_DATARATE; - break; - } - case PHY_DEF_TX_DR: - { - phyParam.Value = CN470_DEFAULT_DATARATE; - break; - } - case PHY_NEXT_LOWER_TX_DR: - { - phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, CN470_TX_MIN_DATARATE ); - break; - } - case PHY_DEF_TX_POWER: - { - phyParam.Value = CN470_DEFAULT_TX_POWER; - break; - } - case PHY_MAX_PAYLOAD: - { - phyParam.Value = MaxPayloadOfDatarateCN470[getPhy->Datarate]; - break; - } - case PHY_MAX_PAYLOAD_REPEATER: - { - phyParam.Value = MaxPayloadOfDatarateRepeaterCN470[getPhy->Datarate]; - break; - } - case PHY_DUTY_CYCLE: - { - phyParam.Value = CN470_DUTY_CYCLE_ENABLED; - break; - } - case PHY_MAX_RX_WINDOW: - { - phyParam.Value = CN470_MAX_RX_WINDOW; - break; - } - case PHY_RECEIVE_DELAY1: - { - phyParam.Value = CN470_RECEIVE_DELAY1; - break; - } - case PHY_RECEIVE_DELAY2: - { - phyParam.Value = CN470_RECEIVE_DELAY2; - break; - } - case PHY_JOIN_ACCEPT_DELAY1: - { - phyParam.Value = CN470_JOIN_ACCEPT_DELAY1; - break; - } - case PHY_JOIN_ACCEPT_DELAY2: - { - phyParam.Value = CN470_JOIN_ACCEPT_DELAY2; - break; - } - case PHY_MAX_FCNT_GAP: - { - phyParam.Value = CN470_MAX_FCNT_GAP; - break; - } - case PHY_ACK_TIMEOUT: - { - phyParam.Value = ( CN470_ACKTIMEOUT + get_random( -CN470_ACK_TIMEOUT_RND, CN470_ACK_TIMEOUT_RND ) ); - break; - } - case PHY_DEF_DR1_OFFSET: - { - phyParam.Value = CN470_DEFAULT_RX1_DR_OFFSET; - break; - } - case PHY_DEF_RX2_FREQUENCY: - { - phyParam.Value = CN470_RX_WND_2_FREQ; - break; - } - case PHY_DEF_RX2_DR: - { - phyParam.Value = CN470_RX_WND_2_DR; - break; - } - case PHY_CHANNELS_MASK: - { - phyParam.ChannelsMask = ChannelsMask; - break; - } - case PHY_CHANNELS_DEFAULT_MASK: - { - phyParam.ChannelsMask = ChannelsDefaultMask; - break; - } - case PHY_MAX_NB_CHANNELS: - { - phyParam.Value = CN470_MAX_NB_CHANNELS; - break; - } - case PHY_CHANNELS: - { - phyParam.Channels = Channels; - break; - } - case PHY_DEF_UPLINK_DWELL_TIME: - case PHY_DEF_DOWNLINK_DWELL_TIME: - { - phyParam.Value = 0; - break; - } - case PHY_DEF_MAX_EIRP: - { - phyParam.fValue = CN470_DEFAULT_MAX_EIRP; - break; - } - case PHY_DEF_ANTENNA_GAIN: - { - phyParam.fValue = CN470_DEFAULT_ANTENNA_GAIN; - break; - } - case PHY_NB_JOIN_TRIALS: - case PHY_DEF_NB_JOIN_TRIALS: - { - phyParam.Value = 48; - break; - } - default: - { - break; - } - } + _radio->lock(); - return phyParam; -} - -void LoRaPHYCN470::set_band_tx_done(SetBandTxDoneParams_t* txDone) -{ - set_last_tx_done( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); -} - -void LoRaPHYCN470::load_defaults(InitType_t type) -{ - switch( type ) - { - case INIT_TYPE_INIT: - { - // Channels - // 125 kHz channels - for( uint8_t i = 0; i < CN470_MAX_NB_CHANNELS; i++ ) - { - Channels[i].Frequency = 470300000 + i * 200000; - Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0; - Channels[i].Band = 0; - } - - // Initialize the channels default mask - ChannelsDefaultMask[0] = 0xFFFF; - ChannelsDefaultMask[1] = 0xFFFF; - ChannelsDefaultMask[2] = 0xFFFF; - ChannelsDefaultMask[3] = 0xFFFF; - ChannelsDefaultMask[4] = 0xFFFF; - ChannelsDefaultMask[5] = 0xFFFF; - - // Update the channels mask - copy_channel_mask( ChannelsMask, ChannelsDefaultMask, 6 ); - break; - } - case INIT_TYPE_RESTORE: - { - // Restore channels default mask - copy_channel_mask( ChannelsMask, ChannelsDefaultMask, 6 ); - break; - } - default: - { - break; - } - } -} - -bool LoRaPHYCN470::verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) -{ - switch( phyAttribute ) - { - case PHY_TX_DR: - case PHY_DEF_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, CN470_TX_MIN_DATARATE, CN470_TX_MAX_DATARATE ); - } - case PHY_RX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE ); - } - case PHY_DEF_TX_POWER: - case PHY_TX_POWER: - { - // Remark: switched min and max! - return val_in_range( verify->TxPower, CN470_MAX_TX_POWER, CN470_MIN_TX_POWER ); - } - case PHY_DUTY_CYCLE: - { - return CN470_DUTY_CYCLE_ENABLED; - } - case PHY_NB_JOIN_TRIALS: - { - if( verify->NbJoinTrials < 48 ) - { - return false; - } - break; - } - default: - return false; - } - return true; -} - -void LoRaPHYCN470::apply_cf_list(ApplyCFListParams_t* applyCFList) -{ - return; -} - -bool LoRaPHYCN470::set_channel_mask( ChanMaskSetParams_t* chanMaskSet ) -{ - switch( chanMaskSet->ChannelsMaskType ) - { - case CHANNELS_MASK: - { - copy_channel_mask( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 ); - break; - } - case CHANNELS_DEFAULT_MASK: - { - copy_channel_mask( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 ); - break; - } - default: - return false; - } - return true; -} - -bool LoRaPHYCN470::get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter) -{ - bool adrAckReq = false; - int8_t datarate = adrNext->Datarate; - int8_t txPower = adrNext->TxPower; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - - // Report back the adr ack counter - *adrAckCounter = adrNext->AdrAckCounter; - - if( adrNext->AdrEnabled == true ) - { - if( datarate == CN470_TX_MIN_DATARATE ) - { - *adrAckCounter = 0; - adrAckReq = false; - } - else - { - if( adrNext->AdrAckCounter >= CN470_ADR_ACK_LIMIT ) - { - adrAckReq = true; - txPower = CN470_MAX_TX_POWER; - } - else - { - adrAckReq = false; - } - if( adrNext->AdrAckCounter >= ( CN470_ADR_ACK_LIMIT + CN470_ADR_ACK_DELAY ) ) - { - if( ( adrNext->AdrAckCounter % CN470_ADR_ACK_DELAY ) == 1 ) - { - // Decrease the datarate - getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; - getPhy.Datarate = datarate; - getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; - phyParam = get_phy_params(&getPhy); - datarate = phyParam.Value; - - if( datarate == CN470_TX_MIN_DATARATE ) - { - // We must set adrAckReq to false as soon as we reach the lowest datarate - adrAckReq = false; - if( adrNext->UpdateChanMask == true ) - { - // Re-enable default channels - ChannelsMask[0] = 0xFFFF; - ChannelsMask[1] = 0xFFFF; - ChannelsMask[2] = 0xFFFF; - ChannelsMask[3] = 0xFFFF; - ChannelsMask[4] = 0xFFFF; - ChannelsMask[5] = 0xFFFF; - } - } - } - } - } - } - - *drOut = datarate; - *txPowOut = txPower; - return adrAckReq; -} - -void LoRaPHYCN470::compute_rx_win_params(int8_t datarate, uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams) -{ - double tSymbol = 0.0; - - // Get the datarate, perform a boundary check - rxConfigParams->Datarate = MIN( datarate, CN470_RX_MAX_DATARATE ); - rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); - - tSymbol = compute_symb_timeout_lora( DataratesCN470[rxConfigParams->Datarate], BandwidthsCN470[rxConfigParams->Datarate] ); - - get_rx_window_params( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); -} - -bool LoRaPHYCN470::rx_config(RxConfigParams_t* rxConfig, int8_t* datarate) -{ - int8_t dr = rxConfig->Datarate; - uint8_t maxPayload = 0; - int8_t phyDr = 0; - uint32_t frequency = rxConfig->Frequency; - - if(_radio->get_status() != RF_IDLE ) - { + if (_radio->get_status() != RF_IDLE) { + _radio->unlock(); return false; } - if( rxConfig->Window == 0 ) + _radio->unlock(); + + if( config->rx_slot == RX_SLOT_WIN_1 ) { // Apply window 1 frequency - frequency = CN470_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 48 ) * CN470_STEPWIDTH_RX1_CHANNEL; + frequency = CN470_FIRST_RX1_CHANNEL + (config->channel % 48) * CN470_STEPWIDTH_RX1_CHANNEL; } // Read the physical datarate from the datarates table - phyDr = DataratesCN470[dr]; + phy_dr = datarates_CN470[dr]; + + _radio->lock(); _radio->set_channel(frequency); // Radio configuration - _radio->set_rx_config(MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, - rxConfig->WindowTimeout, false, 0, false, 0, 0, true, - rxConfig->RxContinuous); + _radio->set_rx_config(MODEM_LORA, config->bandwidth, phy_dr, 1, 0, 8, + config->window_timeout, false, 0, false, 0, 0, true, + config->is_rx_continuous); - if( rxConfig->RepeaterSupport == true ) - { - maxPayload = MaxPayloadOfDatarateRepeaterCN470[dr]; + _radio->unlock(); + + if (config->is_repeater_supported == true) { + max_payload = max_payloads_with_repeater_CN470[dr]; + } else { + max_payload = max_payloads_CN470[dr]; } - else - { - maxPayload = MaxPayloadOfDatarateCN470[dr]; - } - _radio->set_max_payload_length(MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD); + + _radio->lock(); + _radio->set_max_payload_length(MODEM_LORA, max_payload + LORA_MAC_FRMPAYLOAD_OVERHEAD); + _radio->unlock(); *datarate = (uint8_t) dr; return true; } -bool LoRaPHYCN470::tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir) +bool LoRaPHYCN470::tx_config(tx_config_params_t* config, int8_t* tx_power, + lorawan_time_t* tx_toa) { - int8_t phyDr = DataratesCN470[txConfig->Datarate]; - int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); - int8_t phyTxPower = 0; + int8_t phy_dr = datarates_CN470[config->datarate]; + + if (config->tx_power > bands[channels[config->channel].band].max_tx_pwr) { + config->tx_power = bands[channels[config->channel].band].max_tx_pwr; + } + + int8_t phy_tx_power = 0; // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); + phy_tx_power = compute_tx_power(config->tx_power, config->max_eirp, + config->antenna_gain); - // Setup the radio frequency - _radio->set_channel(Channels[txConfig->Channel].Frequency); + // acquire lock to radio + _radio->lock(); - _radio->set_tx_config(MODEM_LORA, phyTxPower, 0, 0, phyDr, 1, 8, false, true, + _radio->set_channel(channels[config->channel].frequency); + + _radio->set_tx_config(MODEM_LORA, phy_tx_power, 0, 0, phy_dr, 1, 8, false, true, 0, 0, false, 3000); // Setup maximum payload lenght of the radio driver - _radio->set_max_payload_length(MODEM_LORA, txConfig->PktLen); + _radio->set_max_payload_length(MODEM_LORA, config->pkt_len); + // Get the time-on-air of the next tx frame - *txTimeOnAir = _radio->time_on_air(MODEM_LORA, txConfig->PktLen); - *txPower = txPowerLimited; + *tx_toa = _radio->time_on_air(MODEM_LORA, config->pkt_len); + + // release lock to radio + _radio->unlock(); + + *tx_power = config->tx_power; return true; } -uint8_t LoRaPHYCN470::link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, uint8_t* nbBytesParsed) +uint8_t LoRaPHYCN470::link_ADR_request(adr_req_params_t* params, + int8_t* dr_out, int8_t* tx_power_out, + uint8_t* nb_rep_out, + uint8_t* nb_bytes_parsed) { uint8_t status = 0x07; - RegionCommonLinkAdrParams_t linkAdrParams; - uint8_t nextIndex = 0; - uint8_t bytesProcessed = 0; - uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + link_adr_params_t adr_settings; + uint8_t next_index = 0; + uint8_t bytes_processed = 0; + uint16_t temp_channel_masks[CN470_CHANNEL_MASK_SIZE] = {0, 0, 0, 0, 0, 0}; + + verify_adr_params_t verify_params; // Initialize local copy of channels mask - copy_channel_mask( channelsMask, ChannelsMask, 6 ); + copy_channel_mask(temp_channel_masks, channel_mask, CN470_CHANNEL_MASK_SIZE); + + while(bytes_processed < params->payload_size) { - while( bytesProcessed < linkAdrReq->PayloadSize ) - { // Get ADR request parameters - nextIndex = parse_link_ADR_req( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + next_index = parse_link_ADR_req(&(params->payload[bytes_processed]), &adr_settings); - if( nextIndex == 0 ) + if (next_index == 0) { break; // break loop, since no more request has been found + } // Update bytes processed - bytesProcessed += nextIndex; + bytes_processed += next_index; // Revert status, as we only check the last ADR request for the channel mask KO status = 0x07; - if( linkAdrParams.ChMaskCtrl == 6 ) - { + if (adr_settings.ch_mask_ctrl == 6) { + // Enable all 125 kHz channels - channelsMask[0] = 0xFFFF; - channelsMask[1] = 0xFFFF; - channelsMask[2] = 0xFFFF; - channelsMask[3] = 0xFFFF; - channelsMask[4] = 0xFFFF; - channelsMask[5] = 0xFFFF; - } - else if( linkAdrParams.ChMaskCtrl == 7 ) - { + temp_channel_masks[0] = 0xFFFF; + temp_channel_masks[1] = 0xFFFF; + temp_channel_masks[2] = 0xFFFF; + temp_channel_masks[3] = 0xFFFF; + temp_channel_masks[4] = 0xFFFF; + temp_channel_masks[5] = 0xFFFF; + + } else if( adr_settings.ch_mask_ctrl == 7 ) { + status &= 0xFE; // Channel mask KO - } - else - { - for( uint8_t i = 0; i < 16; i++ ) - { - if( ( ( linkAdrParams.ChMask & ( 1 << i ) ) != 0 ) && - ( Channels[linkAdrParams.ChMaskCtrl * 16 + i].Frequency == 0 ) ) - {// Trying to enable an undefined channel + + } else { + + for (uint8_t i = 0; i < 16; i++) { + + if (((adr_settings.channel_mask & (1 << i)) != 0 ) && + (channels[adr_settings.ch_mask_ctrl * 16 + i].frequency == 0)) { + // Trying to enable an undefined channel status &= 0xFE; // Channel mask KO } } - channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + + temp_channel_masks[adr_settings.ch_mask_ctrl] = adr_settings.channel_mask; } } - // Get the minimum possible datarate - getPhy.Attribute = PHY_MIN_TX_DR; - getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; - phyParam = get_phy_params(&getPhy); + verify_params.status = status; + verify_params.adr_enabled = params->adr_enabled; + verify_params.datarate = adr_settings.datarate; + verify_params.tx_power = adr_settings.tx_power; + verify_params.nb_rep = adr_settings.nb_rep; + verify_params.current_datarate = params->current_datarate; + verify_params.current_tx_power = params->current_tx_power; + verify_params.current_nb_rep = params->current_nb_rep; + verify_params.channel_mask = temp_channel_masks; - linkAdrVerifyParams.Status = status; - linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; - linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; - linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; - linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; - linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; - linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; - linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; - linkAdrVerifyParams.NbChannels = CN470_MAX_NB_CHANNELS; - linkAdrVerifyParams.ChannelsMask = channelsMask; - linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; - linkAdrVerifyParams.MaxDatarate = CN470_TX_MAX_DATARATE; - linkAdrVerifyParams.Channels = Channels; - linkAdrVerifyParams.MinTxPower = CN470_MIN_TX_POWER; - linkAdrVerifyParams.MaxTxPower = CN470_MAX_TX_POWER; // Verify the parameters and update, if necessary - status = verify_link_ADR_req( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + status = verify_link_ADR_req(&verify_params, &adr_settings.datarate, + &adr_settings.tx_power, &adr_settings.nb_rep); // Update channelsMask if everything is correct - if( status == 0x07 ) - { + if (status == 0x07) { // Copy Mask - copy_channel_mask( ChannelsMask, channelsMask, 6 ); + copy_channel_mask(channel_mask, temp_channel_masks, CN470_CHANNEL_MASK_SIZE); } // Update status variables - *drOut = linkAdrParams.Datarate; - *txPowOut = linkAdrParams.TxPower; - *nbRepOut = linkAdrParams.NbRep; - *nbBytesParsed = bytesProcessed; + *dr_out = adr_settings.datarate; + *tx_power_out = adr_settings.tx_power; + *nb_rep_out = adr_settings.nb_rep; + *nb_bytes_parsed = bytes_processed; return status; } -uint8_t LoRaPHYCN470::setup_rx_params( RxParamSetupReqParams_t* rxParamSetupReq) +uint8_t LoRaPHYCN470::accept_rx_param_setup_req(rx_param_setup_req_t* params) { uint8_t status = 0x07; - uint32_t freq = rxParamSetupReq->Frequency; + uint32_t freq = params->frequency; + + // acquire radio lock + _radio->lock(); + + if ((_radio->check_rf_frequency(freq) == false) + || (freq < CN470_FIRST_RX1_CHANNEL) + || (freq > CN470_LAST_RX1_CHANNEL) + || (((freq - (uint32_t) CN470_FIRST_RX1_CHANNEL) % (uint32_t) CN470_STEPWIDTH_RX1_CHANNEL) != 0)) { - // Verify radio frequency - if( (_radio->check_rf_frequency(freq) == false ) || - ( freq < CN470_FIRST_RX1_CHANNEL ) || - ( freq > CN470_LAST_RX1_CHANNEL ) || - ( ( ( freq - ( uint32_t ) CN470_FIRST_RX1_CHANNEL ) % ( uint32_t ) CN470_STEPWIDTH_RX1_CHANNEL ) != 0 ) ) - { status &= 0xFE; // Channel frequency KO } + // release radio lock + _radio->unlock(); + // Verify datarate - if( val_in_range( rxParamSetupReq->Datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE ) == 0 ) - { + if (val_in_range(params->datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE) == 0) { status &= 0xFD; // Datarate KO } // Verify datarate offset - if( val_in_range( rxParamSetupReq->DrOffset, CN470_MIN_RX1_DR_OFFSET, CN470_MAX_RX1_DR_OFFSET ) == 0 ) - { + if (val_in_range(params->dr_offset, CN470_MIN_RX1_DR_OFFSET, CN470_MAX_RX1_DR_OFFSET) == 0) { status &= 0xFB; // Rx1DrOffset range KO } return status; } - -uint8_t LoRaPHYCN470::request_new_channel(NewChannelReqParams_t* newChannelReq) -{ - // Datarate and frequency KO - return 0; -} - -int8_t LoRaPHYCN470::setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq) -{ - return -1; -} - -uint8_t LoRaPHYCN470::dl_channel_request(DlChannelReqParams_t* dlChannelReq) -{ - return 0; -} - -int8_t LoRaPHYCN470::get_alternate_DR(AlternateDrParams_t* alternateDr) -{ - int8_t datarate = 0; - - if( ( alternateDr->NbTrials % 48 ) == 0 ) - { - datarate = DR_0; - } - else if( ( alternateDr->NbTrials % 32 ) == 0 ) - { - datarate = DR_1; - } - else if( ( alternateDr->NbTrials % 24 ) == 0 ) - { - datarate = DR_2; - } - else if( ( alternateDr->NbTrials % 16 ) == 0 ) - { - datarate = DR_3; - } - else if( ( alternateDr->NbTrials % 8 ) == 0 ) - { - datarate = DR_4; - } - else - { - datarate = DR_5; - } - return datarate; -} - -void LoRaPHYCN470::calculate_backoff(CalcBackOffParams_t* calcBackOff) -{ - RegionCommonCalcBackOffParams_t calcBackOffParams; - - calcBackOffParams.Channels = Channels; - calcBackOffParams.Bands = Bands; - calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; - calcBackOffParams.Joined = calcBackOff->Joined; - calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; - calcBackOffParams.Channel = calcBackOff->Channel; - calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; - calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; - - get_DC_backoff( &calcBackOffParams ); -} - -bool LoRaPHYCN470::set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTx = 0; - uint8_t enabledChannels[CN470_MAX_NB_CHANNELS] = { 0 }; - TimerTime_t nextTxDelay = 0; - - // Count 125kHz channels - if( num_active_channels( ChannelsMask, 0, 6 ) == 0 ) - { // Reactivate default channels - ChannelsMask[0] = 0xFFFF; - ChannelsMask[1] = 0xFFFF; - ChannelsMask[2] = 0xFFFF; - ChannelsMask[3] = 0xFFFF; - ChannelsMask[4] = 0xFFFF; - ChannelsMask[5] = 0xFFFF; - } - - if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) - { - // Reset Aggregated time off - *aggregatedTimeOff = 0; - - // Update bands Time OFF - nextTxDelay = update_band_timeoff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, CN470_MAX_NB_BANDS ); - - // Search how many channels are enabled - nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate, - ChannelsMask, Channels, - Bands, enabledChannels, &delayTx ); - } - else - { - delayTx++; - nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); - } - - if( nbEnabledChannels > 0 ) - { - // We found a valid channel - *channel = enabledChannels[get_random( 0, nbEnabledChannels - 1 )]; - - *time = 0; - return true; - } - else - { - if( delayTx > 0 ) - { - // Delay transmission due to AggregatedTimeOff or to a band time off - *time = nextTxDelay; - return true; - } - // Datarate not supported by any channel - *time = 0; - return false; - } -} - -LoRaMacStatus_t LoRaPHYCN470::add_channel(ChannelAddParams_t* channelAdd) -{ - return LORAMAC_STATUS_PARAMETER_INVALID; -} - -bool LoRaPHYCN470::remove_channel(ChannelRemoveParams_t* channelRemove) -{ - return LORAMAC_STATUS_PARAMETER_INVALID; -} - -void LoRaPHYCN470::set_tx_cont_mode(ContinuousWaveParams_t* continuousWave) -{ - int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); - int8_t phyTxPower = 0; - uint32_t frequency = Channels[continuousWave->Channel].Frequency; - - // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); - - _radio->set_tx_continuous_wave(frequency, phyTxPower, continuousWave->Timeout); -} - -uint8_t LoRaPHYCN470::apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, - int8_t drOffset) -{ - int8_t datarate = dr - drOffset; - - if( datarate < 0 ) - { - datarate = DR_0; - } - return datarate; -} - - - diff --git a/features/lorawan/lorastack/phy/LoRaPHYCN470.h b/features/lorawan/lorastack/phy/LoRaPHYCN470.h index 4c5bcd09bb..846c849acb 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYCN470.h +++ b/features/lorawan/lorastack/phy/LoRaPHYCN470.h @@ -33,7 +33,6 @@ #define MBED_OS_LORAPHY_CN470_H_ #include "LoRaPHY.h" -#include "netsocket/LoRaRadio.h" // Definitions /*! @@ -47,319 +46,48 @@ #define CN470_MAX_NB_BANDS 1 -#define CN470_CHANNELS_MASK_SIZE 6 +#define CN470_CHANNEL_MASK_SIZE 6 class LoRaPHYCN470 : public LoRaPHY { public: - LoRaPHYCN470(); + LoRaPHYCN470(LoRaWANTimeHandler &lora_time); virtual ~LoRaPHYCN470(); - /*! - * \brief The function gets a value of a specific PHY attribute. - * - * \param [in] getPhy A pointer to the function parameters. - * - * \retval A structure containing the PHY parameter. - */ - virtual PhyParam_t get_phy_params(GetPhyParams_t* getPhy ); + virtual bool rx_config(rx_config_params_t* config, int8_t* datarate ); - /*! - * \brief Updates the last TX done parameters of the current channel. - * - * \param [in] txDone A pointer to the function parameters. - */ - virtual void set_band_tx_done(SetBandTxDoneParams_t* txDone ); + virtual bool tx_config(tx_config_params_t* config, int8_t* tx_power, + lorawan_time_t* tx_toa); - /*! - * \brief Initializes the channels masks and the channels. - * - * \param [in] type Sets the initialization type. - */ - virtual void load_defaults(InitType_t type ); + virtual uint8_t link_ADR_request(adr_req_params_t* params, int8_t* dr_out, + int8_t* tx_power_out, uint8_t* nb_rep_out, + uint8_t* nb_bytes_parsed); - /*! - * \brief Verifies a parameter. - * - * \param [in] verify A pointer to the function parameters. - * - * \param [in] phyAttribute The attribute to be verified. - * - * \retval True, if the parameter is valid. - */ - virtual bool verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute ); - - /*! - * \brief The function parses the input buffer and sets up the channels of the - * CF list. - * - * \param [in] applyCFList A pointer to the function parameters. - */ - virtual void apply_cf_list(ApplyCFListParams_t* applyCFList ); - - /*! - * \brief Sets a channels mask. - * - * \param [in] chanMaskSet A pointer to the function parameters. - * - * \retval True, if the channels mask could be set. - */ - virtual bool set_channel_mask(ChanMaskSetParams_t* chanMaskSet ); - - /*! - * \brief Calculates the next datarate to set, when ADR is on or off. - * - * \param [in] adrNext A pointer to the function parameters. - * - * \param [out] drOut The calculated datarate for the next TX. - * - * \param [out] txPowOut The TX power for the next TX. - * - * \param [out] adrAckCounter The calculated ADR acknowledgement counter. - * - * \retval True, if an ADR request should be performed. - */ - virtual bool get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter ); - - /*! - * \brief Configuration of the RX windows. - * - * \param [in] rxConfig A pointer to the function parameters. - * - * \param [out] datarate The datarate index set. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool rx_config(RxConfigParams_t* rxConfig, int8_t* datarate ); - - /* - * RX window precise timing - * - * For more details please consult the following document, chapter 3.1.2. - * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf - * or - * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf - * - * Downlink start: T = Tx + 1s (+/- 20 us) - * | - * TRxEarly | TRxLate - * | | | - * | | +---+---+---+---+---+---+---+---+ - * | | | Latest Rx window | - * | | +---+---+---+---+---+---+---+---+ - * | | | - * +---+---+---+---+---+---+---+---+ - * | Earliest Rx window | - * +---+---+---+---+---+---+---+---+ - * | - * +---+---+---+---+---+---+---+---+ - *Downlink preamble 8 symbols | | | | | | | | | - * +---+---+---+---+---+---+---+---+ - * - * Worst case Rx window timings - * - * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME - * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME - * - * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * - * RxOffset = ( TRxLate + TRxEarly ) / 2 - * - * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME - * - * The minimum value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol - */ - /*! - * Computes the RX window timeout and offset. - * - * \param [in] datarate The RX window datarate index to be used. - * - * \param [in] minRxSymbols The minimum number of symbols required to detect an RX frame. - * - * \param [in] rxError The system maximum timing error of the receiver in milliseconds. - * The receiver will turn on in a [-rxError : +rxError] ms - * interval around RxOffset. - * - * \param [out] rxConfigParams The updated WindowTimeout and WindowOffset fields. - */ - virtual void compute_rx_win_params(int8_t datarate, - uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams); - - /*! - * \brief TX configuration. - * - * \param [in] txConfig A pointer to the function parameters. - * - * \param [out] txPower The TX power index set. - * - * \param [out] txTimeOnAir The time-on-air of the frame. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir ); - - /*! - * \brief The function processes a Link ADR request. - * - * \param [in] linkAdrReq A pointer to the function parameters. - * - * \param [out] drOut The datarate applied. - * - * \param [out] txPowOut The TX power applied. - * - * \param [out] nbRepOut The number of repetitions to apply. - * - * \param [out] nbBytesParsed The number of bytes parsed. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, - uint8_t* nbBytesParsed ); - - /*! - * \brief The function processes a RX parameter setup request. - * - * \param [in] rxParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq ); - - /*! - * \brief The function processes a new channel request. - * - * \param [in] newChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t request_new_channel(NewChannelReqParams_t* newChannelReq ); - - /*! - * \brief The function processes a TX ParamSetup request. - * - * \param [in] txParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - * Returns -1, if the functionality is not implemented. In this case, the end node - * shall ignore the command. - */ - virtual int8_t setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq ); - - /*! - * \brief The function processes a DlChannel request. - * - * \param [in] dlChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t dl_channel_request(DlChannelReqParams_t* dlChannelReq ); - - /*! - * \brief Alternates the datarate of the channel for the join request. - * - * \param [in] alternateDr A pointer to the function parameters. - * - * \retval The datarate to apply. - */ - virtual int8_t get_alternate_DR(AlternateDrParams_t* alternateDr ); - - /*! - * \brief Calculates the back-off time. - * - * \param [in] calcBackOff A pointer to the function parameters. - */ - virtual void calculate_backoff(CalcBackOffParams_t* calcBackOff ); - - /*! - * \brief Searches and sets the next random available channel. - * - * \param [in] nextChanParams The parameters for the next channel - * - * \param [out] channel The next channel to use for TX. - * - * \param [out] time The time to wait for the next transmission according to the duty - * cycle. - * - * \param [out] aggregatedTimeOff Updates the aggregated time off. - * - * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]. - */ - virtual bool set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff ); - - /*! - * \brief Adds a channel. - * - * \param [in] channelAdd A pointer to the function parameters. - * - * \retval The status of the operation. - */ - virtual LoRaMacStatus_t add_channel(ChannelAddParams_t* channelAdd ); - - /*! - * \brief Removes a channel. - * - * \param [in] channelRemove A pointer to the function parameters. - * - * \retval True, if the channel was removed successfully. - */ - virtual bool remove_channel(ChannelRemoveParams_t* channelRemove ); - - /*! - * \brief Sets the radio into continuous wave mode. - * - * \param [in] continuousWave A pointer to the function parameters. - */ - virtual void set_tx_cont_mode(ContinuousWaveParams_t* continuousWave ); - - /*! - * \brief Computes a new datarate according to the given offset. - * - * \param [in] downlinkDwellTime The downlink dwell time configuration. 0: No limit, 1: 400ms - * - * \param [in] dr The current datarate. - * - * \param [in] drOffset The offset to be applied. - * - * \retval newDr The computed datarate. - */ - virtual uint8_t apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + virtual uint8_t accept_rx_param_setup_req(rx_param_setup_req_t* params); private: - uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ); - bool RegionCN470ChanMaskSet( ChanMaskSetParams_t* chanMaskSet ); - - // Global attributes /*! * LoRaMAC channels */ - ChannelParams_t Channels[CN470_MAX_NB_CHANNELS]; + channel_params_t channels[CN470_MAX_NB_CHANNELS]; /*! * LoRaMac bands */ - Band_t Bands[CN470_MAX_NB_BANDS]; + band_t bands[CN470_MAX_NB_BANDS]; /*! - * LoRaMac channels mask + * LoRaMac channel mask */ - uint16_t ChannelsMask[CN470_CHANNELS_MASK_SIZE]; + uint16_t channel_mask[CN470_CHANNEL_MASK_SIZE]; /*! - * LoRaMac channels default mask + * LoRaMac default channel mask */ - uint16_t ChannelsDefaultMask[CN470_CHANNELS_MASK_SIZE]; + uint16_t default_channel_mask[CN470_CHANNEL_MASK_SIZE]; }; #endif /* MBED_OS_LORAPHY_CN470_H_ */ diff --git a/features/lorawan/lorastack/phy/LoRaPHYCN779.cpp b/features/lorawan/lorastack/phy/LoRaPHYCN779.cpp index 9e527cc147..66783d32a2 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYCN779.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYCN779.cpp @@ -30,10 +30,7 @@ */ #include "LoRaPHYCN779.h" - #include "lora_phy_ds.h" -#include "LoRaRadio.h" - /*! * Number of default channels @@ -65,6 +62,8 @@ */ #define CN779_RX_MAX_DATARATE DR_7 +#define CN779_DEFAULT_MAX_DATARATE DR_5 + /*! * Default datarate used by the node */ @@ -184,1056 +183,137 @@ /*! * Band 0 definition - * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } */ -#define CN779_BAND0 { 100, CN779_MAX_TX_POWER, 0, 0 } // 1.0 % +static const band_t CN779_BAND0 = {100, CN779_MAX_TX_POWER, 0, 0, 0, 779500000, 786500000}; // 1.0 % /*! * LoRaMac default channel 1 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define CN779_LC1 { 779500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +static const channel_params_t CN779_LC1 = {779500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0}; /*! * LoRaMac default channel 2 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define CN779_LC2 { 779700000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +static const channel_params_t CN779_LC2 = {779700000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0}; /*! * LoRaMac default channel 3 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define CN779_LC3 { 779900000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +static const channel_params_t CN779_LC3 = {779900000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0}; /*! * LoRaMac channels which are allowed for the join procedure */ -#define CN779_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) +#define CN779_JOIN_CHANNELS (uint16_t) (LC(1) | LC(2) | LC(3)) /*! * Data rates table definition */ -static const uint8_t DataratesCN779[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; +static const uint8_t datarates_CN779[] = {12, 11, 10, 9, 8, 7, 7, 50}; /*! * Bandwidths table definition in Hz */ -static const uint32_t BandwidthsCN779[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; +static const uint32_t bandwidths_CN779[] = {125000, 125000, 125000, 125000, 125000, 125000, 250000, 0}; /*! * Maximum payload with respect to the datarate index. Cannot operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateCN779[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; +static const uint8_t max_payloads_CN779[] = {51, 51, 51, 115, 242, 242, 242, 242}; /*! * Maximum payload with respect to the datarate index. Can operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateRepeaterCN779[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; +static const uint8_t max_payloads_with_repeater_CN779[] = {51, 51, 51, 115, 222, 222, 222, 222}; -// Static functions -static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) + +LoRaPHYCN779::LoRaPHYCN779(LoRaWANTimeHandler &lora_time) + : LoRaPHY(lora_time) { - uint8_t nextLowerDr = 0; + bands[0] = CN779_BAND0; - if( dr == minDr ) - { - nextLowerDr = minDr; - } - else - { - nextLowerDr = dr - 1; - } - return nextLowerDr; -} + // Channels + channels[0] = CN779_LC1; + channels[1] = CN779_LC2; + channels[2] = CN779_LC3; -static uint32_t GetBandwidth( uint32_t drIndex ) -{ - switch( BandwidthsCN779[drIndex] ) - { - default: - case 125000: - return 0; - case 250000: - return 1; - case 500000: - return 2; - } -} + // Initialize the channels default mask + default_channel_mask[0] = LC(1) + LC(2) + LC(3); + // Update the channels mask + copy_channel_mask(channel_mask, default_channel_mask, CN779_CHANNEL_MASK_SIZE); -static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) -{ - int8_t txPowerResult = txPower; + // set default channels + phy_params.channels.channel_list = channels; + phy_params.channels.channel_list_size = CN779_MAX_NB_CHANNELS; + phy_params.channels.mask = channel_mask; + phy_params.channels.default_mask = default_channel_mask; + phy_params.channels.mask_size = CN779_CHANNEL_MASK_SIZE; - // Limit tx power to the band max - txPowerResult = MAX( txPower, maxBandTxPower ); + // set bands for CN779 spectrum + phy_params.bands.table = bands; + phy_params.bands.size = CN779_MAX_NB_BANDS; - return txPowerResult; -} + // set bandwidths available in CN779 spectrum + phy_params.bandwidths.table = (void *) bandwidths_CN779; + phy_params.bandwidths.size = 8; -static bool VerifyTxFreq( uint32_t freq, LoRaRadio *radio) -{ - // Check radio driver support - if(radio->check_rf_frequency(freq) == false) - { - return false; - } + // set data rates available in CN779 spectrum + phy_params.datarates.table = (void *) datarates_CN779; + phy_params.datarates.size = 8; - if( ( freq < 779500000 ) || ( freq > 786500000 ) ) - { - return false; - } - return true; -} + // set payload sizes with respect to data rates + phy_params.payloads.table = (void *) max_payloads_CN779; + phy_params.payloads.size = 8; + phy_params.payloads_with_repeater.table = (void *) max_payloads_with_repeater_CN779; + phy_params.payloads.size = 8; -uint8_t LoRaPHYCN779::CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTransmission = 0; + // dwell time setting + phy_params.ul_dwell_time_setting = 0; + phy_params.dl_dwell_time_setting = 0; - for( uint8_t i = 0, k = 0; i < CN779_MAX_NB_CHANNELS; i += 16, k++ ) - { - for( uint8_t j = 0; j < 16; j++ ) - { - if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) - { - if( channels[i + j].Frequency == 0 ) - { // Check if the channel is enabled - continue; - } - if( joined == false ) - { - if( ( CN779_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) - { - continue; - } - } - if( val_in_range( datarate, channels[i + j].DrRange.Fields.Min, - channels[i + j].DrRange.Fields.Max ) == 0 ) - { // Check if the current channel selection supports the given datarate - continue; - } - if( bands[channels[i + j].Band].TimeOff > 0 ) - { // Check if the band is available for transmission - delayTransmission++; - continue; - } - enabledChannels[nbEnabledChannels++] = i + j; - } - } - } - - *delayTx = delayTransmission; - return nbEnabledChannels; -} - -LoRaPHYCN779::LoRaPHYCN779() -{ - const Band_t band0 = CN779_BAND0; - Bands[0] = band0; + // set initial and default parameters + phy_params.duty_cycle_enabled = CN779_DUTY_CYCLE_ENABLED; + phy_params.accept_tx_param_setup_req = false; + phy_params.fsk_supported = true; + phy_params.cflist_supported = true; + phy_params.dl_channel_req_supported = true; + phy_params.custom_channelplans_supported = true; + phy_params.default_channel_cnt = CN779_NUMB_DEFAULT_CHANNELS; + phy_params.max_channel_cnt = CN779_MAX_NB_CHANNELS; + phy_params.cflist_channel_cnt = CN779_NUMB_CHANNELS_CF_LIST; + phy_params.min_tx_datarate = CN779_TX_MIN_DATARATE; + phy_params.max_tx_datarate = CN779_TX_MAX_DATARATE; + phy_params.min_rx_datarate = CN779_RX_MIN_DATARATE; + phy_params.max_rx_datarate = CN779_RX_MAX_DATARATE; + phy_params.default_datarate = CN779_DEFAULT_DATARATE; + phy_params.default_max_datarate = CN779_DEFAULT_MAX_DATARATE; + phy_params.min_rx1_dr_offset = CN779_MIN_RX1_DR_OFFSET; + phy_params.max_rx1_dr_offset = CN779_MAX_RX1_DR_OFFSET; + phy_params.default_rx1_dr_offset = CN779_DEFAULT_RX1_DR_OFFSET; + phy_params.min_tx_power = CN779_MIN_TX_POWER; + phy_params.max_tx_power = CN779_MAX_TX_POWER; + phy_params.default_tx_power = CN779_DEFAULT_TX_POWER; + phy_params.default_max_eirp = CN779_DEFAULT_MAX_EIRP; + phy_params.default_antenna_gain = CN779_DEFAULT_ANTENNA_GAIN; + phy_params.adr_ack_limit = CN779_ADR_ACK_LIMIT; + phy_params.adr_ack_delay = CN779_ADR_ACK_DELAY; + phy_params.max_rx_window = CN779_MAX_RX_WINDOW; + phy_params.recv_delay1 = CN779_RECEIVE_DELAY1; + phy_params.recv_delay2 = CN779_RECEIVE_DELAY2; + phy_params.join_channel_mask = CN779_JOIN_CHANNELS; + phy_params.join_accept_delay1 = CN779_JOIN_ACCEPT_DELAY1; + phy_params.join_accept_delay2 = CN779_JOIN_ACCEPT_DELAY2; + phy_params.max_fcnt_gap = CN779_MAX_FCNT_GAP; + phy_params.ack_timeout = CN779_ACKTIMEOUT; + phy_params.ack_timeout_rnd = CN779_ACK_TIMEOUT_RND; + phy_params.rx_window2_datarate = CN779_RX_WND_2_DR; + phy_params.rx_window2_frequency = CN779_RX_WND_2_FREQ; } LoRaPHYCN779::~LoRaPHYCN779() { } -PhyParam_t LoRaPHYCN779::get_phy_params(GetPhyParams_t* getPhy) -{ - PhyParam_t phyParam = { 0 }; - - switch( getPhy->Attribute ) - { - case PHY_MIN_RX_DR: - { - phyParam.Value = CN779_RX_MIN_DATARATE; - break; - } - case PHY_MIN_TX_DR: - { - phyParam.Value = CN779_TX_MIN_DATARATE; - break; - } - case PHY_DEF_TX_DR: - { - phyParam.Value = CN779_DEFAULT_DATARATE; - break; - } - case PHY_NEXT_LOWER_TX_DR: - { - phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, CN779_TX_MIN_DATARATE ); - break; - } - case PHY_DEF_TX_POWER: - { - phyParam.Value = CN779_DEFAULT_TX_POWER; - break; - } - case PHY_MAX_PAYLOAD: - { - phyParam.Value = MaxPayloadOfDatarateCN779[getPhy->Datarate]; - break; - } - case PHY_MAX_PAYLOAD_REPEATER: - { - phyParam.Value = MaxPayloadOfDatarateRepeaterCN779[getPhy->Datarate]; - break; - } - case PHY_DUTY_CYCLE: - { - phyParam.Value = CN779_DUTY_CYCLE_ENABLED; - break; - } - case PHY_MAX_RX_WINDOW: - { - phyParam.Value = CN779_MAX_RX_WINDOW; - break; - } - case PHY_RECEIVE_DELAY1: - { - phyParam.Value = CN779_RECEIVE_DELAY1; - break; - } - case PHY_RECEIVE_DELAY2: - { - phyParam.Value = CN779_RECEIVE_DELAY2; - break; - } - case PHY_JOIN_ACCEPT_DELAY1: - { - phyParam.Value = CN779_JOIN_ACCEPT_DELAY1; - break; - } - case PHY_JOIN_ACCEPT_DELAY2: - { - phyParam.Value = CN779_JOIN_ACCEPT_DELAY2; - break; - } - case PHY_MAX_FCNT_GAP: - { - phyParam.Value = CN779_MAX_FCNT_GAP; - break; - } - case PHY_ACK_TIMEOUT: - { - phyParam.Value = (CN779_ACKTIMEOUT + get_random(-CN779_ACK_TIMEOUT_RND, CN779_ACK_TIMEOUT_RND)); - break; - } - case PHY_DEF_DR1_OFFSET: - { - phyParam.Value = CN779_DEFAULT_RX1_DR_OFFSET; - break; - } - case PHY_DEF_RX2_FREQUENCY: - { - phyParam.Value = CN779_RX_WND_2_FREQ; - break; - } - case PHY_DEF_RX2_DR: - { - phyParam.Value = CN779_RX_WND_2_DR; - break; - } - case PHY_CHANNELS_MASK: - { - phyParam.ChannelsMask = ChannelsMask; - break; - } - case PHY_CHANNELS_DEFAULT_MASK: - { - phyParam.ChannelsMask = ChannelsDefaultMask; - break; - } - case PHY_MAX_NB_CHANNELS: - { - phyParam.Value = CN779_MAX_NB_CHANNELS; - break; - } - case PHY_CHANNELS: - { - phyParam.Channels = Channels; - break; - } - case PHY_DEF_UPLINK_DWELL_TIME: - case PHY_DEF_DOWNLINK_DWELL_TIME: - { - phyParam.Value = 0; - break; - } - case PHY_DEF_MAX_EIRP: - { - phyParam.fValue = CN779_DEFAULT_MAX_EIRP; - break; - } - case PHY_DEF_ANTENNA_GAIN: - { - phyParam.fValue = CN779_DEFAULT_ANTENNA_GAIN; - break; - } - case PHY_NB_JOIN_TRIALS: - case PHY_DEF_NB_JOIN_TRIALS: - { - phyParam.Value = 48; - break; - } - default: - { - break; - } - } - - return phyParam; -} - -void LoRaPHYCN779::set_band_tx_done(SetBandTxDoneParams_t* txDone) -{ - set_last_tx_done( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); -} - -void LoRaPHYCN779::load_defaults(InitType_t type) -{ - switch( type ) - { - case INIT_TYPE_INIT: - { - // Channels - const ChannelParams_t channel1 = CN779_LC1; - const ChannelParams_t channel2 = CN779_LC2; - const ChannelParams_t channel3 = CN779_LC3; - Channels[0] = channel1; - Channels[1] = channel2; - Channels[2] = channel3; - - // Initialize the channels default mask - ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); - // Update the channels mask - copy_channel_mask( ChannelsMask, ChannelsDefaultMask, 1 ); - break; - } - case INIT_TYPE_RESTORE: - { - // Restore channels default mask - ChannelsMask[0] |= ChannelsDefaultMask[0]; - break; - } - default: - { - break; - } - } -} - -bool LoRaPHYCN779::verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute) -{ - switch( phyAttribute ) - { - case PHY_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ); - } - case PHY_DEF_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, DR_0, DR_5 ); - } - case PHY_RX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, CN779_RX_MIN_DATARATE, CN779_RX_MAX_DATARATE ); - } - case PHY_DEF_TX_POWER: - case PHY_TX_POWER: - { - // Remark: switched min and max! - return val_in_range( verify->TxPower, CN779_MAX_TX_POWER, CN779_MIN_TX_POWER ); - } - case PHY_DUTY_CYCLE: - { - return CN779_DUTY_CYCLE_ENABLED; - } - case PHY_NB_JOIN_TRIALS: - { - if( verify->NbJoinTrials < 48 ) - { - return false; - } - break; - } - default: - return false; - } - return true; -} - -void LoRaPHYCN779::apply_cf_list(ApplyCFListParams_t* applyCFList) -{ - ChannelParams_t newChannel; - ChannelAddParams_t channelAdd; - ChannelRemoveParams_t channelRemove; - - // Setup default datarate range - newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; - - // Size of the optional CF list - if( applyCFList->Size != 16 ) - { - return; - } - - // Last byte is RFU, don't take it into account - for( uint8_t i = 0, chanIdx = CN779_NUMB_DEFAULT_CHANNELS; chanIdx < CN779_MAX_NB_CHANNELS; i+=3, chanIdx++ ) - { - if( chanIdx < ( CN779_NUMB_CHANNELS_CF_LIST + CN779_NUMB_DEFAULT_CHANNELS ) ) - { - // Channel frequency - newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; - newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); - newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); - newChannel.Frequency *= 100; - - // Initialize alternative frequency to 0 - newChannel.Rx1Frequency = 0; - } - else - { - newChannel.Frequency = 0; - newChannel.DrRange.Value = 0; - newChannel.Rx1Frequency = 0; - } - - if( newChannel.Frequency != 0 ) - { - channelAdd.NewChannel = &newChannel; - channelAdd.ChannelId = chanIdx; - - // Try to add all channels - add_channel(&channelAdd); - } - else - { - channelRemove.ChannelId = chanIdx; - - remove_channel(&channelRemove); - } - } -} - -bool LoRaPHYCN779::set_channel_mask(ChanMaskSetParams_t* chanMaskSet) -{ - switch( chanMaskSet->ChannelsMaskType ) - { - case CHANNELS_MASK: - { - copy_channel_mask( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); - break; - } - case CHANNELS_DEFAULT_MASK: - { - copy_channel_mask( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); - break; - } - default: - return false; - } - return true; -} - -bool LoRaPHYCN779::get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter) -{ - bool adrAckReq = false; - int8_t datarate = adrNext->Datarate; - int8_t txPower = adrNext->TxPower; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - - // Report back the adr ack counter - *adrAckCounter = adrNext->AdrAckCounter; - - if( adrNext->AdrEnabled == true ) - { - if( datarate == CN779_TX_MIN_DATARATE ) - { - *adrAckCounter = 0; - adrAckReq = false; - } - else - { - if( adrNext->AdrAckCounter >= CN779_ADR_ACK_LIMIT ) - { - adrAckReq = true; - txPower = CN779_MAX_TX_POWER; - } - else - { - adrAckReq = false; - } - if( adrNext->AdrAckCounter >= ( CN779_ADR_ACK_LIMIT + CN779_ADR_ACK_DELAY ) ) - { - if( ( adrNext->AdrAckCounter % CN779_ADR_ACK_DELAY ) == 1 ) - { - // Decrease the datarate - getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; - getPhy.Datarate = datarate; - getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; - phyParam = get_phy_params(&getPhy); - datarate = phyParam.Value; - - if( datarate == CN779_TX_MIN_DATARATE ) - { - // We must set adrAckReq to false as soon as we reach the lowest datarate - adrAckReq = false; - if( adrNext->UpdateChanMask == true ) - { - // Re-enable default channels - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); - } - } - } - } - } - } - - *drOut = datarate; - *txPowOut = txPower; - return adrAckReq; -} - -void LoRaPHYCN779::compute_rx_win_params(int8_t datarate, uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams) -{ - double tSymbol = 0.0; - - // Get the datarate, perform a boundary check - rxConfigParams->Datarate = MIN( datarate, CN779_RX_MAX_DATARATE ); - rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); - - if( rxConfigParams->Datarate == DR_7 ) - { // FSK - tSymbol = compute_symb_timeout_fsk( DataratesCN779[rxConfigParams->Datarate] ); - } - else - { // LoRa - tSymbol = compute_symb_timeout_lora( DataratesCN779[rxConfigParams->Datarate], BandwidthsCN779[rxConfigParams->Datarate] ); - } - - get_rx_window_params( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); -} - -bool LoRaPHYCN779::rx_config(RxConfigParams_t* rxConfig, int8_t* datarate) -{ - radio_modems_t modem; - int8_t dr = rxConfig->Datarate; - uint8_t maxPayload = 0; - int8_t phyDr = 0; - uint32_t frequency = rxConfig->Frequency; - - if(_radio->get_status() != RF_IDLE ) - { - return false; - } - - if( rxConfig->Window == 0 ) - { - // Apply window 1 frequency - frequency = Channels[rxConfig->Channel].Frequency; - // Apply the alternative RX 1 window frequency, if it is available - if( Channels[rxConfig->Channel].Rx1Frequency != 0 ) - { - frequency = Channels[rxConfig->Channel].Rx1Frequency; - } - } - - // Read the physical datarate from the datarates table - phyDr = DataratesCN779[dr]; - - _radio->set_channel(frequency); - - // Radio configuration - if( dr == DR_7 ) - { - modem = MODEM_FSK; - _radio->set_rx_config(modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous); - } - else - { - modem = MODEM_LORA; - _radio->set_rx_config(modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous); - } - - if( rxConfig->RepeaterSupport == true ) - { - maxPayload = MaxPayloadOfDatarateRepeaterCN779[dr]; - } - else - { - maxPayload = MaxPayloadOfDatarateCN779[dr]; - } - _radio->set_max_payload_length(modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD); - - *datarate = (uint8_t) dr; - return true; -} - -bool LoRaPHYCN779::tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir) -{ - radio_modems_t modem; - int8_t phyDr = DataratesCN779[txConfig->Datarate]; - int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); - uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); - int8_t phyTxPower = 0; - - // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); - - // Setup the radio frequency - _radio->set_channel(Channels[txConfig->Channel].Frequency); - - if( txConfig->Datarate == DR_7 ) - { // High Speed FSK channel - modem = MODEM_FSK; - _radio->set_tx_config(modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 3000); - } - else - { - modem = MODEM_LORA; - _radio->set_tx_config(modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000); - } - - // Setup maximum payload lenght of the radio driver - _radio->set_max_payload_length(modem, txConfig->PktLen); - // Get the time-on-air of the next tx frame - *txTimeOnAir = _radio->time_on_air(modem, txConfig->PktLen); - - *txPower = txPowerLimited; - return true; -} - -uint8_t LoRaPHYCN779::link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, uint8_t* nbBytesParsed) -{ - uint8_t status = 0x07; - RegionCommonLinkAdrParams_t linkAdrParams; - uint8_t nextIndex = 0; - uint8_t bytesProcessed = 0; - uint16_t chMask = 0; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; - - while( bytesProcessed < linkAdrReq->PayloadSize ) - { - // Get ADR request parameters - nextIndex = parse_link_ADR_req( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); - - if( nextIndex == 0 ) - break; // break loop, since no more request has been found - - // Update bytes processed - bytesProcessed += nextIndex; - - // Revert status, as we only check the last ADR request for the channel mask KO - status = 0x07; - - // Setup temporary channels mask - chMask = linkAdrParams.ChMask; - - // Verify channels mask - if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) - { - status &= 0xFE; // Channel mask KO - } - else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || - ( linkAdrParams.ChMaskCtrl >= 7 ) ) - { - // RFU - status &= 0xFE; // Channel mask KO - } - else - { - for( uint8_t i = 0; i < CN779_MAX_NB_CHANNELS; i++ ) - { - if( linkAdrParams.ChMaskCtrl == 6 ) - { - if( Channels[i].Frequency != 0 ) - { - chMask |= 1 << i; - } - } - else - { - if( ( ( chMask & ( 1 << i ) ) != 0 ) && - ( Channels[i].Frequency == 0 ) ) - {// Trying to enable an undefined channel - status &= 0xFE; // Channel mask KO - } - } - } - } - } - - // Get the minimum possible datarate - getPhy.Attribute = PHY_MIN_TX_DR; - getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; - phyParam = get_phy_params(&getPhy); - - linkAdrVerifyParams.Status = status; - linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; - linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; - linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; - linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; - linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; - linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; - linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; - linkAdrVerifyParams.NbChannels = CN779_MAX_NB_CHANNELS; - linkAdrVerifyParams.ChannelsMask = &chMask; - linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; - linkAdrVerifyParams.MaxDatarate = CN779_TX_MAX_DATARATE; - linkAdrVerifyParams.Channels = Channels; - linkAdrVerifyParams.MinTxPower = CN779_MIN_TX_POWER; - linkAdrVerifyParams.MaxTxPower = CN779_MAX_TX_POWER; - - // Verify the parameters and update, if necessary - status = verify_link_ADR_req( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); - - // Update channelsMask if everything is correct - if( status == 0x07 ) - { - // Set the channels mask to a default value - memset( ChannelsMask, 0, sizeof( ChannelsMask ) ); - // Update the channels mask - ChannelsMask[0] = chMask; - } - - // Update status variables - *drOut = linkAdrParams.Datarate; - *txPowOut = linkAdrParams.TxPower; - *nbRepOut = linkAdrParams.NbRep; - *nbBytesParsed = bytesProcessed; - - return status; -} - -uint8_t LoRaPHYCN779::setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq) -{ - uint8_t status = 0x07; - - // Verify radio frequency - if(_radio->check_rf_frequency(rxParamSetupReq->Frequency) == false ) - { - status &= 0xFE; // Channel frequency KO - } - - // Verify datarate - if( val_in_range( rxParamSetupReq->Datarate, CN779_RX_MIN_DATARATE, CN779_RX_MAX_DATARATE ) == 0 ) - { - status &= 0xFD; // Datarate KO - } - - // Verify datarate offset - if( val_in_range( rxParamSetupReq->DrOffset, CN779_MIN_RX1_DR_OFFSET, CN779_MAX_RX1_DR_OFFSET ) == 0 ) - { - status &= 0xFB; // Rx1DrOffset range KO - } - - return status; -} - -uint8_t LoRaPHYCN779::request_new_channel(NewChannelReqParams_t* newChannelReq) -{ - uint8_t status = 0x03; - ChannelAddParams_t channelAdd; - ChannelRemoveParams_t channelRemove; - - if( newChannelReq->NewChannel->Frequency == 0 ) - { - channelRemove.ChannelId = newChannelReq->ChannelId; - - // Remove - if(remove_channel(&channelRemove) == false ) - { - status &= 0xFC; - } - } - else - { - channelAdd.NewChannel = newChannelReq->NewChannel; - channelAdd.ChannelId = newChannelReq->ChannelId; - - switch (add_channel(&channelAdd)) - { - case LORAMAC_STATUS_OK: - { - break; - } - case LORAMAC_STATUS_FREQUENCY_INVALID: - { - status &= 0xFE; - break; - } - case LORAMAC_STATUS_DATARATE_INVALID: - { - status &= 0xFD; - break; - } - case LORAMAC_STATUS_FREQ_AND_DR_INVALID: - { - status &= 0xFC; - break; - } - default: - { - status &= 0xFC; - break; - } - } - } - - return status; -} - -int8_t LoRaPHYCN779::setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq) -{ - return -1; -} - -uint8_t LoRaPHYCN779::dl_channel_request(DlChannelReqParams_t* dlChannelReq) -{ - uint8_t status = 0x03; - - // Verify if the frequency is supported - if( VerifyTxFreq(dlChannelReq->Rx1Frequency, _radio) == false ) - { - status &= 0xFE; - } - - // Verify if an uplink frequency exists - if( Channels[dlChannelReq->ChannelId].Frequency == 0 ) - { - status &= 0xFD; - } - - // Apply Rx1 frequency, if the status is OK - if( status == 0x03 ) - { - Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; - } - - return status; -} - -int8_t LoRaPHYCN779::get_alternate_DR(AlternateDrParams_t* alternateDr) -{ - int8_t datarate = 0; - - if( ( alternateDr->NbTrials % 48 ) == 0 ) - { - datarate = DR_0; - } - else if( ( alternateDr->NbTrials % 32 ) == 0 ) - { - datarate = DR_1; - } - else if( ( alternateDr->NbTrials % 24 ) == 0 ) - { - datarate = DR_2; - } - else if( ( alternateDr->NbTrials % 16 ) == 0 ) - { - datarate = DR_3; - } - else if( ( alternateDr->NbTrials % 8 ) == 0 ) - { - datarate = DR_4; - } - else - { - datarate = DR_5; - } - return datarate; -} - -void LoRaPHYCN779::calculate_backoff(CalcBackOffParams_t* calcBackOff) -{ - RegionCommonCalcBackOffParams_t calcBackOffParams; - - calcBackOffParams.Channels = Channels; - calcBackOffParams.Bands = Bands; - calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; - calcBackOffParams.Joined = calcBackOff->Joined; - calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; - calcBackOffParams.Channel = calcBackOff->Channel; - calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; - calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; - - get_DC_backoff( &calcBackOffParams ); -} - -bool LoRaPHYCN779::set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTx = 0; - uint8_t enabledChannels[CN779_MAX_NB_CHANNELS] = { 0 }; - TimerTime_t nextTxDelay = 0; - - if( num_active_channels( ChannelsMask, 0, 1 ) == 0 ) - { // Reactivate default channels - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); - } - - if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) - { - // Reset Aggregated time off - *aggregatedTimeOff = 0; - - // Update bands Time OFF - nextTxDelay = update_band_timeoff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, CN779_MAX_NB_BANDS ); - - // Search how many channels are enabled - nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, - ChannelsMask, Channels, - Bands, enabledChannels, &delayTx ); - } - else - { - delayTx++; - nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); - } - - if( nbEnabledChannels > 0 ) - { - // We found a valid channel - *channel = enabledChannels[get_random( 0, nbEnabledChannels - 1 )]; - - *time = 0; - return true; - } - else - { - if( delayTx > 0 ) - { - // Delay transmission due to AggregatedTimeOff or to a band time off - *time = nextTxDelay; - return true; - } - // Datarate not supported by any channel, restore defaults - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); - *time = 0; - return false; - } -} - -LoRaMacStatus_t LoRaPHYCN779::add_channel(ChannelAddParams_t* channelAdd) -{ - uint8_t band = 0; - bool drInvalid = false; - bool freqInvalid = false; - uint8_t id = channelAdd->ChannelId; - - if( id >= CN779_MAX_NB_CHANNELS ) - { - return LORAMAC_STATUS_PARAMETER_INVALID; - } - - // Validate the datarate range - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Min, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Max, CN779_TX_MIN_DATARATE, CN779_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) - { - drInvalid = true; - } - - // Default channels don't accept all values - if( id < CN779_NUMB_DEFAULT_CHANNELS ) - { - // Validate the datarate range for min: must be DR_0 - if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 ) - { - drInvalid = true; - } - // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, CN779_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - // We are not allowed to change the frequency - if( channelAdd->NewChannel->Frequency != Channels[id].Frequency ) - { - freqInvalid = true; - } - } - - // Check frequency - if( freqInvalid == false ) - { - if( VerifyTxFreq(channelAdd->NewChannel->Frequency, _radio) == false ) - { - freqInvalid = true; - } - } - - // Check status - if( ( drInvalid == true ) && ( freqInvalid == true ) ) - { - return LORAMAC_STATUS_FREQ_AND_DR_INVALID; - } - if( drInvalid == true ) - { - return LORAMAC_STATUS_DATARATE_INVALID; - } - if( freqInvalid == true ) - { - return LORAMAC_STATUS_FREQUENCY_INVALID; - } - - memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) ); - Channels[id].Band = band; - ChannelsMask[0] |= ( 1 << id ); - return LORAMAC_STATUS_OK; -} - -bool LoRaPHYCN779::remove_channel(ChannelRemoveParams_t* channelRemove) -{ - uint8_t id = channelRemove->ChannelId; - - if( id < CN779_NUMB_DEFAULT_CHANNELS ) - { - return false; - } - - // Remove the channel from the list of channels - const ChannelParams_t empty_channel = { 0, 0, { 0 }, 0 }; - Channels[id] = empty_channel; - - return disable_channel( ChannelsMask, id, CN779_MAX_NB_CHANNELS ); -} - -void LoRaPHYCN779::set_tx_cont_mode(ContinuousWaveParams_t* continuousWave) -{ - int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); - int8_t phyTxPower = 0; - uint32_t frequency = Channels[continuousWave->Channel].Frequency; - - // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); - - _radio->set_tx_continuous_wave(frequency, phyTxPower, continuousWave->Timeout); -} - -uint8_t LoRaPHYCN779::apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset) -{ - int8_t datarate = dr - drOffset; - - if( datarate < 0 ) - { - datarate = DR_0; - } - return datarate; -} diff --git a/features/lorawan/lorastack/phy/LoRaPHYCN779.h b/features/lorawan/lorastack/phy/LoRaPHYCN779.h index 8be486db5f..b931c43f84 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYCN779.h +++ b/features/lorawan/lorastack/phy/LoRaPHYCN779.h @@ -33,322 +33,41 @@ #define MBED_OS_LORAPHY_CN779_H_ #include "LoRaPHY.h" -#include "netsocket/LoRaRadio.h" #define CN779_MAX_NB_CHANNELS 16 #define CN779_MAX_NB_BANDS 1 -#define CN779_CHANNELS_MASK_SIZE 1 +#define CN779_CHANNEL_MASK_SIZE 1 class LoRaPHYCN779 : public LoRaPHY { public: - LoRaPHYCN779(); + LoRaPHYCN779(LoRaWANTimeHandler &lora_time); virtual ~LoRaPHYCN779(); - /*! - * \brief The function gets a value of a specific PHY attribute. - * - * \param [in] getPhy A pointer to the function parameters. - * - * \retval The structure containing the PHY parameter. - */ - virtual PhyParam_t get_phy_params(GetPhyParams_t* getPhy ); - - /*! - * \brief Updates the last TX done parameters of the current channel. - * - * \param [in] txDone A pointer to the function parameters. - */ - virtual void set_band_tx_done(SetBandTxDoneParams_t* txDone ); - - /*! - * \brief Initializes the channels masks and the channels. - * - * \param [in] type Sets the initialization type. - */ - virtual void load_defaults(InitType_t type ); - - /*! - * \brief Verifies a parameter. - * - * \param [in] verify A pointer to the function parameters. - * - * \param [in] phyAttribute The attribute to verify. - * - * \retval True, if the parameter is valid. - */ - virtual bool verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute ); - - /*! - * \brief The function parses the input buffer and sets up the channels of the CF list. - * - * \param [in] applyCFList A pointer to the function parameters. - */ - virtual void apply_cf_list(ApplyCFListParams_t* applyCFList ); - - /*! - * \brief Sets a channels mask. - * - * \param [in] chanMaskSet A pointer to the function parameters. - * - * \retval True, if the channels mask could be set. - */ - virtual bool set_channel_mask(ChanMaskSetParams_t* chanMaskSet ); - - /*! - * \brief Calculates the next datarate to set, when ADR is on or off. - * - * \param [in] adrNext A pointer to the function parameters. - * - * \param [out] drOut The calculated datarate for the next TX. - * - * \param [out] txPowOut The TX power for the next TX. - * - * \param [out] adrAckCounter The calculated ADR acknowledgement counter. - * - * \retval True, if an ADR request should be performed. - */ - virtual bool get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter ); - - /*! - * \brief Configuration of the RX windows. - * - * \param [in] rxConfig A pointer to the function parameters. - * - * \param [out] datarate The datarate index set. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool rx_config(RxConfigParams_t* rxConfig, int8_t* datarate ); - - /* - * RX window precise timing - * - * For more details please consult the following document, chapter 3.1.2. - * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf - * or - * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf - * - * Downlink start: T = Tx + 1s (+/- 20 us) - * | - * TRxEarly | TRxLate - * | | | - * | | +---+---+---+---+---+---+---+---+ - * | | | Latest Rx window | - * | | +---+---+---+---+---+---+---+---+ - * | | | - * +---+---+---+---+---+---+---+---+ - * | Earliest Rx window | - * +---+---+---+---+---+---+---+---+ - * | - * +---+---+---+---+---+---+---+---+ - *Downlink preamble 8 symbols | | | | | | | | | - * +---+---+---+---+---+---+---+---+ - * - * Worst case Rx window timings - * - * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME - * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME - * - * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * - * RxOffset = ( TRxLate + TRxEarly ) / 2 - * - * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME - * - * The minimum value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol - */ - /*! - * Computes the RX window timeout and offset. - * - * \param [in] datarate The RX window datarate index to be used. - * - * \param [in] minRxSymbols The minimum required number of symbols to detect an RX frame. - * - * \param [in] rxError The system maximum timing error of the receiver in milliseconds. - * The receiver will turn on in a [-rxError : +rxError] ms - * interval around RxOffset. - * - * \param [out] rxConfigParams Returns the updated WindowTimeout and WindowOffset fields. - */ - virtual void compute_rx_win_params(int8_t datarate, - uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams); - - /*! - * \brief TX configuration. - * - * \param [in] txConfig A pointer to the function parameters. - * - * \param [out] txPower The TX power index set. - * - * \param [out] txTimeOnAir The time-on-air of the frame. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir ); - - /*! - * \brief The function processes a Link ADR request. - * - * \param [in] linkAdrReq A pointer to the function parameters. - * - * \param [out] drOut The datarate applied. - * - * \param [out] txPowOut The TX power applied. - * - * \param [out] nbRepOut The number of repetitions to apply. - * - * \param [out] nbBytesParsed The number of bytes parsed. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, - uint8_t* nbBytesParsed ); - - /*! - * \brief The function processes a RX parameter setup request. - * - * \param [in] rxParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq ); - - /*! - * \brief The function processes a new channel request. - * - * \param [in] newChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t request_new_channel(NewChannelReqParams_t* newChannelReq ); - - /*! - * \brief The function processes a TX ParamSetup request. - * - * \param [in] txParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - * Returns -1, if the functionality is not implemented. In this case, the end node - * shall ignore the command. - */ - virtual int8_t setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq ); - - /*! - * \brief The function processes a DlChannel request. - * - * \param [in] dlChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t dl_channel_request(DlChannelReqParams_t* dlChannelReq ); - - /*! - * \brief Alternates the datarate of the channel for the join request. - * - * \param [in] alternateDr A pointer to the function parameters. - * - * \retval The datarate to apply. - */ - virtual int8_t get_alternate_DR(AlternateDrParams_t* alternateDr ); - - /*! - * \brief Calculates the back-off time. - * - * \param [in] calcBackOff A pointer to the function parameters. - */ - virtual void calculate_backoff(CalcBackOffParams_t* calcBackOff ); - - /*! - * \brief Searches and sets the next random available channel. - * - * \param [in] nextChanParams The parameters for the next channel. - * - * \param [out] channel The next channel to use for TX. - * - * \param [out] time The time to wait for the next transmission according to the duty - * cycle. - * - * \param [out] aggregatedTimeOff Updates the aggregated time off. - * - * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]. - */ - virtual bool set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff ); - - /*! - * \brief Adds a channel. - * - * \param [in] channelAdd A pointer to the function parameters. - * - * \retval The status of the operation. - */ - virtual LoRaMacStatus_t add_channel(ChannelAddParams_t* channelAdd ); - - /*! - * \brief Removes a channel. - * - * \param [in] channelRemove A pointer to the function parameters. - * - * \retval True, if the channel was removed successfully. - */ - virtual bool remove_channel(ChannelRemoveParams_t* channelRemove ); - - /*! - * \brief Sets the radio into continuous wave mode. - * - * \param [in] continuousWave A pointer to the function parameters. - */ - virtual void set_tx_cont_mode(ContinuousWaveParams_t* continuousWave ); - - /*! - * \brief Computes a new datarate according to the given offset. - * - * \param [in] downlinkDwellTime The downlink dwell time configuration. 0: No limit, 1: 400ms - * - * \param [in] dr The current datarate. - * - * \param [in] drOffset The offset to be applied. - * - * \retval newDr The computed datarate. - */ - virtual uint8_t apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); - private: - uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ); - - // Global attributes /*! * LoRaMAC channels */ - ChannelParams_t Channels[CN779_MAX_NB_CHANNELS]; + channel_params_t channels[CN779_MAX_NB_CHANNELS]; /*! * LoRaMac bands */ - Band_t Bands[CN779_MAX_NB_BANDS]; + band_t bands[CN779_MAX_NB_BANDS]; /*! - * LoRaMac channels mask + * LoRaMac channel mask */ - uint16_t ChannelsMask[CN779_CHANNELS_MASK_SIZE]; + uint16_t channel_mask[CN779_CHANNEL_MASK_SIZE]; /*! - * LoRaMac channels default mask + * LoRaMac default channel mask */ - uint16_t ChannelsDefaultMask[CN779_CHANNELS_MASK_SIZE]; + uint16_t default_channel_mask[CN779_CHANNEL_MASK_SIZE]; }; #endif /* MBED_OS_LORAPHY_CN779_H_ */ diff --git a/features/lorawan/lorastack/phy/LoRaPHYEU433.cpp b/features/lorawan/lorastack/phy/LoRaPHYEU433.cpp index d68d0d5bdb..6345b5395c 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYEU433.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYEU433.cpp @@ -30,9 +30,7 @@ */ #include "LoRaPHYEU433.h" - #include "lora_phy_ds.h" -#include "LoRaRadio.h" /*! * Number of default channels @@ -69,6 +67,8 @@ */ #define EU433_DEFAULT_DATARATE DR_0 +#define EU433_DEFAULT_MAX_DATARATE DR_5 + /*! * Minimal Rx1 receive datarate offset */ @@ -183,1062 +183,137 @@ /*! * Band 0 definition - * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } */ -#define EU433_BAND0 { 100, EU433_MAX_TX_POWER, 0, 0 } // 1.0 % +static const band_t EU433_BAND0 = {100, EU433_MAX_TX_POWER, 0, 0, 0, 433175000, 434665000}; // 1.0 % /*! * LoRaMac default channel 1 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define EU433_LC1 { 433175000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +static const channel_params_t EU433_LC1 = {433175000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0}; /*! * LoRaMac default channel 2 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define EU433_LC2 { 433375000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +static const channel_params_t EU433_LC2 = {433375000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0}; /*! * LoRaMac default channel 3 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define EU433_LC3 { 433575000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +static const channel_params_t EU433_LC3 = {433575000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0}; /*! * LoRaMac channels which are allowed for the join procedure */ -#define EU433_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) +#define EU433_JOIN_CHANNELS (uint16_t) (LC(1) | LC(2) | LC(3)) /*! * Data rates table definition */ -static const uint8_t DataratesEU433[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; +static const uint8_t datarates_EU433[] = {12, 11, 10, 9, 8, 7, 7, 50}; /*! * Bandwidths table definition in Hz */ -static const uint32_t BandwidthsEU433[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; +static const uint32_t bandwidths_EU433[] = {125000, 125000, 125000, 125000, 125000, 125000, 250000, 0}; /*! * Maximum payload with respect to the datarate index. Cannot operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateEU433[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; +static const uint8_t max_payloads_EU433[] = {51, 51, 51, 115, 242, 242, 242, 242}; /*! * Maximum payload with respect to the datarate index. Can operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateRepeaterEU433[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; +static const uint8_t max_payloads_with_repeater_EU433[] = {51, 51, 51, 115, 222, 222, 222, 222}; -// Static functions -static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +LoRaPHYEU433::LoRaPHYEU433(LoRaWANTimeHandler &lora_time) + : LoRaPHY(lora_time) { - uint8_t nextLowerDr = 0; + bands[0] = EU433_BAND0; - if( dr == minDr ) - { - nextLowerDr = minDr; - } - else - { - nextLowerDr = dr - 1; - } - return nextLowerDr; -} + // Channels + channels[0] = EU433_LC1; + channels[1] = EU433_LC2;; + channels[2] = EU433_LC3;; -static uint32_t GetBandwidth( uint32_t drIndex ) -{ - switch( BandwidthsEU433[drIndex] ) - { - default: - case 125000: - return 0; - case 250000: - return 1; - case 500000: - return 2; - } -} + // Initialize the channels default mask + default_channel_mask[0] = LC(1) + LC(2) + LC(3); + // Update the channels mask + copy_channel_mask(channel_mask, default_channel_mask, EU433_CHANNEL_MASK_SIZE); -static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) -{ - int8_t txPowerResult = txPower; + // set default channels + phy_params.channels.channel_list = channels; + phy_params.channels.channel_list_size = EU433_MAX_NB_CHANNELS; + phy_params.channels.mask = channel_mask; + phy_params.channels.default_mask = default_channel_mask; + phy_params.channels.mask_size = EU433_CHANNEL_MASK_SIZE; - // Limit tx power to the band max - txPowerResult = MAX( txPower, maxBandTxPower ); + // set bands for EU433 spectrum + phy_params.bands.table = bands; + phy_params.bands.size = EU433_MAX_NB_BANDS; - return txPowerResult; -} + // set bandwidths available in EU433 spectrum + phy_params.bandwidths.table = (void *) bandwidths_EU433; + phy_params.bandwidths.size = 8; -static bool VerifyTxFreq( uint32_t freq, LoRaRadio *radio ) -{ - // Check radio driver support - if(radio->check_rf_frequency(freq) == false ) - { - return false; - } + // set data rates available in EU433 spectrum + phy_params.datarates.table = (void *) datarates_EU433; + phy_params.datarates.size = 8; - if( ( freq < 433175000 ) || ( freq > 434665000 ) ) - { - return false; - } - return true; -} + // set payload sizes with respect to data rates + phy_params.payloads.table = (void *) max_payloads_EU433; + phy_params.payloads.size = 8; + phy_params.payloads_with_repeater.table = (void *) max_payloads_with_repeater_EU433; + phy_params.payloads.size = 8; -uint8_t LoRaPHYEU433::CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTransmission = 0; + // dwell time setting + phy_params.ul_dwell_time_setting = 0; + phy_params.dl_dwell_time_setting = 0; - for( uint8_t i = 0, k = 0; i < EU433_MAX_NB_CHANNELS; i += 16, k++ ) - { - for( uint8_t j = 0; j < 16; j++ ) - { - if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) - { - if( channels[i + j].Frequency == 0 ) - { // Check if the channel is enabled - continue; - } - if( joined == false ) - { - if( ( EU433_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) - { - continue; - } - } - if( val_in_range( datarate, channels[i + j].DrRange.Fields.Min, - channels[i + j].DrRange.Fields.Max ) == 0 ) - { // Check if the current channel selection supports the given datarate - continue; - } - if( bands[channels[i + j].Band].TimeOff > 0 ) - { // Check if the band is available for transmission - delayTransmission++; - continue; - } - enabledChannels[nbEnabledChannels++] = i + j; - } - } - } - - *delayTx = delayTransmission; - return nbEnabledChannels; -} - -LoRaPHYEU433::LoRaPHYEU433() -{ - const Band_t band0 = EU433_BAND0; - Bands[0] = band0; + // set initial and default parameters + phy_params.duty_cycle_enabled = EU433_DUTY_CYCLE_ENABLED; + phy_params.accept_tx_param_setup_req = false; + phy_params.fsk_supported = true; + phy_params.cflist_supported = true; + phy_params.dl_channel_req_supported = true; + phy_params.custom_channelplans_supported = true; + phy_params.default_channel_cnt = EU433_NUMB_DEFAULT_CHANNELS; + phy_params.max_channel_cnt = EU433_MAX_NB_CHANNELS; + phy_params.cflist_channel_cnt = EU433_NUMB_CHANNELS_CF_LIST; + phy_params.min_tx_datarate = EU433_TX_MIN_DATARATE; + phy_params.max_tx_datarate = EU433_TX_MAX_DATARATE; + phy_params.min_rx_datarate = EU433_RX_MIN_DATARATE; + phy_params.max_rx_datarate = EU433_RX_MAX_DATARATE; + phy_params.default_datarate = EU433_DEFAULT_DATARATE; + phy_params.default_max_datarate = EU433_DEFAULT_MAX_DATARATE; + phy_params.min_rx1_dr_offset = EU433_MIN_RX1_DR_OFFSET; + phy_params.max_rx1_dr_offset = EU433_MAX_RX1_DR_OFFSET; + phy_params.default_rx1_dr_offset = EU433_DEFAULT_RX1_DR_OFFSET; + phy_params.min_tx_power = EU433_MIN_TX_POWER; + phy_params.max_tx_power = EU433_MAX_TX_POWER; + phy_params.default_tx_power = EU433_DEFAULT_TX_POWER; + phy_params.default_max_eirp = EU433_DEFAULT_MAX_EIRP; + phy_params.default_antenna_gain = EU433_DEFAULT_ANTENNA_GAIN; + phy_params.adr_ack_limit = EU433_ADR_ACK_LIMIT; + phy_params.adr_ack_delay = EU433_ADR_ACK_DELAY; + phy_params.max_rx_window = EU433_MAX_RX_WINDOW; + phy_params.recv_delay1 = EU433_RECEIVE_DELAY1; + phy_params.recv_delay2 = EU433_RECEIVE_DELAY2; + phy_params.join_channel_mask = EU433_JOIN_CHANNELS; + phy_params.join_accept_delay1 = EU433_JOIN_ACCEPT_DELAY1; + phy_params.join_accept_delay2 = EU433_JOIN_ACCEPT_DELAY2; + phy_params.max_fcnt_gap = EU433_MAX_FCNT_GAP; + phy_params.ack_timeout = EU433_ACKTIMEOUT; + phy_params.ack_timeout_rnd = EU433_ACK_TIMEOUT_RND; + phy_params.rx_window2_datarate = EU433_RX_WND_2_DR; + phy_params.rx_window2_frequency = EU433_RX_WND_2_FREQ; } LoRaPHYEU433::~LoRaPHYEU433() { } - -PhyParam_t LoRaPHYEU433::get_phy_params(GetPhyParams_t* getPhy) -{ - PhyParam_t phyParam = { 0 }; - - switch( getPhy->Attribute ) - { - case PHY_MIN_RX_DR: - { - phyParam.Value = EU433_RX_MIN_DATARATE; - break; - } - case PHY_MIN_TX_DR: - { - phyParam.Value = EU433_TX_MIN_DATARATE; - break; - } - case PHY_DEF_TX_DR: - { - phyParam.Value = EU433_DEFAULT_DATARATE; - break; - } - case PHY_NEXT_LOWER_TX_DR: - { - phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, EU433_TX_MIN_DATARATE ); - break; - } - case PHY_DEF_TX_POWER: - { - phyParam.Value = EU433_DEFAULT_TX_POWER; - break; - } - case PHY_MAX_PAYLOAD: - { - phyParam.Value = MaxPayloadOfDatarateEU433[getPhy->Datarate]; - break; - } - case PHY_MAX_PAYLOAD_REPEATER: - { - phyParam.Value = MaxPayloadOfDatarateRepeaterEU433[getPhy->Datarate]; - break; - } - case PHY_DUTY_CYCLE: - { - phyParam.Value = EU433_DUTY_CYCLE_ENABLED; - break; - } - case PHY_MAX_RX_WINDOW: - { - phyParam.Value = EU433_MAX_RX_WINDOW; - break; - } - case PHY_RECEIVE_DELAY1: - { - phyParam.Value = EU433_RECEIVE_DELAY1; - break; - } - case PHY_RECEIVE_DELAY2: - { - phyParam.Value = EU433_RECEIVE_DELAY2; - break; - } - case PHY_JOIN_ACCEPT_DELAY1: - { - phyParam.Value = EU433_JOIN_ACCEPT_DELAY1; - break; - } - case PHY_JOIN_ACCEPT_DELAY2: - { - phyParam.Value = EU433_JOIN_ACCEPT_DELAY2; - break; - } - case PHY_MAX_FCNT_GAP: - { - phyParam.Value = EU433_MAX_FCNT_GAP; - break; - } - case PHY_ACK_TIMEOUT: - { - phyParam.Value = ( EU433_ACKTIMEOUT + get_random( -EU433_ACK_TIMEOUT_RND, EU433_ACK_TIMEOUT_RND ) ); - break; - } - case PHY_DEF_DR1_OFFSET: - { - phyParam.Value = EU433_DEFAULT_RX1_DR_OFFSET; - break; - } - case PHY_DEF_RX2_FREQUENCY: - { - phyParam.Value = EU433_RX_WND_2_FREQ; - break; - } - case PHY_DEF_RX2_DR: - { - phyParam.Value = EU433_RX_WND_2_DR; - break; - } - case PHY_CHANNELS_MASK: - { - phyParam.ChannelsMask = ChannelsMask; - break; - } - case PHY_CHANNELS_DEFAULT_MASK: - { - phyParam.ChannelsMask = ChannelsDefaultMask; - break; - } - case PHY_MAX_NB_CHANNELS: - { - phyParam.Value = EU433_MAX_NB_CHANNELS; - break; - } - case PHY_CHANNELS: - { - phyParam.Channels = Channels; - break; - } - case PHY_DEF_UPLINK_DWELL_TIME: - case PHY_DEF_DOWNLINK_DWELL_TIME: - { - phyParam.Value = 0; - break; - } - case PHY_DEF_MAX_EIRP: - { - phyParam.fValue = EU433_DEFAULT_MAX_EIRP; - break; - } - case PHY_DEF_ANTENNA_GAIN: - { - phyParam.fValue = EU433_DEFAULT_ANTENNA_GAIN; - break; - } - case PHY_NB_JOIN_TRIALS: - case PHY_DEF_NB_JOIN_TRIALS: - { - phyParam.Value = 48; - break; - } - default: - { - break; - } - } - - return phyParam; -} - -void LoRaPHYEU433::set_band_tx_done(SetBandTxDoneParams_t* txDone) -{ - set_last_tx_done( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); -} - -void LoRaPHYEU433::load_defaults(InitType_t type) -{ - switch( type ) - { - case INIT_TYPE_INIT: - { - // Channels - const ChannelParams_t channel1 = EU433_LC1; - const ChannelParams_t channel2 = EU433_LC2; - const ChannelParams_t channel3 = EU433_LC3; - Channels[0] = channel1; - Channels[1] = channel2; - Channels[2] = channel3; - - // Initialize the channels default mask - ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); - // Update the channels mask - copy_channel_mask( ChannelsMask, ChannelsDefaultMask, 1 ); - break; - } - case INIT_TYPE_RESTORE: - { - // Restore channels default mask - ChannelsMask[0] |= ChannelsDefaultMask[0]; - break; - } - default: - { - break; - } - } -} - -bool LoRaPHYEU433::verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute) -{ - switch( phyAttribute ) - { - case PHY_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ); - } - case PHY_DEF_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, DR_0, DR_5 ); - } - case PHY_RX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, EU433_RX_MIN_DATARATE, EU433_RX_MAX_DATARATE ); - } - case PHY_DEF_TX_POWER: - case PHY_TX_POWER: - { - // Remark: switched min and max! - return val_in_range( verify->TxPower, EU433_MAX_TX_POWER, EU433_MIN_TX_POWER ); - } - case PHY_DUTY_CYCLE: - { - return EU433_DUTY_CYCLE_ENABLED; - } - case PHY_NB_JOIN_TRIALS: - { - if( verify->NbJoinTrials < 48 ) - { - return false; - } - break; - } - default: - return false; - } - return true; -} - -void LoRaPHYEU433::apply_cf_list(ApplyCFListParams_t* applyCFList) -{ - ChannelParams_t newChannel; - ChannelAddParams_t channelAdd; - ChannelRemoveParams_t channelRemove; - - // Setup default datarate range - newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; - - // Size of the optional CF list - if( applyCFList->Size != 16 ) - { - return; - } - - // Last byte is RFU, don't take it into account - for( uint8_t i = 0, chanIdx = EU433_NUMB_DEFAULT_CHANNELS; chanIdx < EU433_MAX_NB_CHANNELS; i+=3, chanIdx++ ) - { - if( chanIdx < ( EU433_NUMB_CHANNELS_CF_LIST + EU433_NUMB_DEFAULT_CHANNELS ) ) - { - // Channel frequency - newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; - newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); - newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); - newChannel.Frequency *= 100; - - // Initialize alternative frequency to 0 - newChannel.Rx1Frequency = 0; - } - else - { - newChannel.Frequency = 0; - newChannel.DrRange.Value = 0; - newChannel.Rx1Frequency = 0; - } - - if( newChannel.Frequency != 0 ) - { - channelAdd.NewChannel = &newChannel; - channelAdd.ChannelId = chanIdx; - - // Try to add all channels - add_channel( &channelAdd ); - } - else - { - channelRemove.ChannelId = chanIdx; - - remove_channel( &channelRemove ); - } - } -} - -bool LoRaPHYEU433::set_channel_mask(ChanMaskSetParams_t* chanMaskSet) -{ - switch( chanMaskSet->ChannelsMaskType ) - { - case CHANNELS_MASK: - { - copy_channel_mask( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); - break; - } - case CHANNELS_DEFAULT_MASK: - { - copy_channel_mask( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); - break; - } - default: - return false; - } - return true; -} - -bool LoRaPHYEU433::get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter) -{ - bool adrAckReq = false; - int8_t datarate = adrNext->Datarate; - int8_t txPower = adrNext->TxPower; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - - // Report back the adr ack counter - *adrAckCounter = adrNext->AdrAckCounter; - - if( adrNext->AdrEnabled == true ) - { - if( datarate == EU433_TX_MIN_DATARATE ) - { - *adrAckCounter = 0; - adrAckReq = false; - } - else - { - if( adrNext->AdrAckCounter >= EU433_ADR_ACK_LIMIT ) - { - adrAckReq = true; - txPower = EU433_MAX_TX_POWER; - } - else - { - adrAckReq = false; - } - if( adrNext->AdrAckCounter >= ( EU433_ADR_ACK_LIMIT + EU433_ADR_ACK_DELAY ) ) - { - if( ( adrNext->AdrAckCounter % EU433_ADR_ACK_DELAY ) == 1 ) - { - // Decrease the datarate - getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; - getPhy.Datarate = datarate; - getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; - phyParam = get_phy_params( &getPhy ); - datarate = phyParam.Value; - - if( datarate == EU433_TX_MIN_DATARATE ) - { - // We must set adrAckReq to false as soon as we reach the lowest datarate - adrAckReq = false; - if( adrNext->UpdateChanMask == true ) - { - // Re-enable default channels - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); - } - } - } - } - } - } - - *drOut = datarate; - *txPowOut = txPower; - return adrAckReq; -} - -void LoRaPHYEU433::compute_rx_win_params(int8_t datarate, uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams) -{ - double tSymbol = 0.0; - - // Get the datarate, perform a boundary check - rxConfigParams->Datarate = MIN( datarate, EU433_RX_MAX_DATARATE ); - rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); - - if( rxConfigParams->Datarate == DR_7 ) - { // FSK - tSymbol = compute_symb_timeout_fsk( DataratesEU433[rxConfigParams->Datarate] ); - } - else - { // LoRa - tSymbol = compute_symb_timeout_lora( DataratesEU433[rxConfigParams->Datarate], BandwidthsEU433[rxConfigParams->Datarate] ); - } - - get_rx_window_params( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); -} - -bool LoRaPHYEU433::rx_config(RxConfigParams_t* rxConfig, int8_t* datarate) -{ - radio_modems_t modem; - int8_t dr = rxConfig->Datarate; - uint8_t maxPayload = 0; - int8_t phyDr = 0; - uint32_t frequency = rxConfig->Frequency; - - if( _radio->get_status() != RF_IDLE ) - { - return false; - } - - if( rxConfig->Window == 0 ) - { - // Apply window 1 frequency - frequency = Channels[rxConfig->Channel].Frequency; - // Apply the alternative RX 1 window frequency, if it is available - if( Channels[rxConfig->Channel].Rx1Frequency != 0 ) - { - frequency = Channels[rxConfig->Channel].Rx1Frequency; - } - } - - // Read the physical datarate from the datarates table - phyDr = DataratesEU433[dr]; - - _radio->set_channel( frequency ); - - // Radio configuration - if( dr == DR_7 ) - { - modem = MODEM_FSK; - _radio->set_rx_config( modem, 50000, phyDr * 1000, 0, 83333, 5, - rxConfig->WindowTimeout, false, 0, true, 0, 0, - false, rxConfig->RxContinuous ); - } - else - { - modem = MODEM_LORA; - _radio->set_rx_config( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, - rxConfig->WindowTimeout, false, 0, false, 0, 0, - true, rxConfig->RxContinuous ); - } - - if( rxConfig->RepeaterSupport == true ) - { - maxPayload = MaxPayloadOfDatarateRepeaterEU433[dr]; - } - else - { - maxPayload = MaxPayloadOfDatarateEU433[dr]; - } - _radio->set_max_payload_length( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); - - *datarate = (uint8_t) dr; - return true; -} - -bool LoRaPHYEU433::tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir) -{ - radio_modems_t modem; - int8_t phyDr = DataratesEU433[txConfig->Datarate]; - int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); - uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); - int8_t phyTxPower = 0; - - // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); - - // Setup the radio frequency - _radio->set_channel( Channels[txConfig->Channel].Frequency ); - - if( txConfig->Datarate == DR_7 ) - { // High Speed FSK channel - modem = MODEM_FSK; - _radio->set_tx_config( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 3000 ); - } - else - { - modem = MODEM_LORA; - _radio->set_tx_config( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 ); - } - - // Setup maximum payload lenght of the radio driver - _radio->set_max_payload_length( modem, txConfig->PktLen ); - // Get the time-on-air of the next tx frame - *txTimeOnAir = _radio->time_on_air( modem, txConfig->PktLen ); - - *txPower = txPowerLimited; - return true; -} - -uint8_t LoRaPHYEU433::link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, uint8_t* nbBytesParsed) -{ - uint8_t status = 0x07; - RegionCommonLinkAdrParams_t linkAdrParams; - uint8_t nextIndex = 0; - uint8_t bytesProcessed = 0; - uint16_t chMask = 0; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; - - while( bytesProcessed < linkAdrReq->PayloadSize ) - { - // Get ADR request parameters - nextIndex = parse_link_ADR_req( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); - - if( nextIndex == 0 ) - break; // break loop, since no more request has been found - - // Update bytes processed - bytesProcessed += nextIndex; - - // Revert status, as we only check the last ADR request for the channel mask KO - status = 0x07; - - // Setup temporary channels mask - chMask = linkAdrParams.ChMask; - - // Verify channels mask - if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) - { - status &= 0xFE; // Channel mask KO - } - else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || - ( linkAdrParams.ChMaskCtrl >= 7 ) ) - { - // RFU - status &= 0xFE; // Channel mask KO - } - else - { - for( uint8_t i = 0; i < EU433_MAX_NB_CHANNELS; i++ ) - { - if( linkAdrParams.ChMaskCtrl == 6 ) - { - if( Channels[i].Frequency != 0 ) - { - chMask |= 1 << i; - } - } - else - { - if( ( ( chMask & ( 1 << i ) ) != 0 ) && - ( Channels[i].Frequency == 0 ) ) - {// Trying to enable an undefined channel - status &= 0xFE; // Channel mask KO - } - } - } - } - } - - // Get the minimum possible datarate - getPhy.Attribute = PHY_MIN_TX_DR; - getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; - phyParam = get_phy_params( &getPhy ); - - linkAdrVerifyParams.Status = status; - linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; - linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; - linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; - linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; - linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; - linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; - linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; - linkAdrVerifyParams.NbChannels = EU433_MAX_NB_CHANNELS; - linkAdrVerifyParams.ChannelsMask = &chMask; - linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; - linkAdrVerifyParams.MaxDatarate = EU433_TX_MAX_DATARATE; - linkAdrVerifyParams.Channels = Channels; - linkAdrVerifyParams.MinTxPower = EU433_MIN_TX_POWER; - linkAdrVerifyParams.MaxTxPower = EU433_MAX_TX_POWER; - - // Verify the parameters and update, if necessary - status = verify_link_ADR_req( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); - - // Update channelsMask if everything is correct - if( status == 0x07 ) - { - // Set the channels mask to a default value - memset( ChannelsMask, 0, sizeof( ChannelsMask ) ); - // Update the channels mask - ChannelsMask[0] = chMask; - } - - // Update status variables - *drOut = linkAdrParams.Datarate; - *txPowOut = linkAdrParams.TxPower; - *nbRepOut = linkAdrParams.NbRep; - *nbBytesParsed = bytesProcessed; - - return status; -} - -uint8_t LoRaPHYEU433::setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq) -{ - uint8_t status = 0x07; - - // Verify radio frequency - if( _radio->check_rf_frequency( rxParamSetupReq->Frequency ) == false ) - { - status &= 0xFE; // Channel frequency KO - } - - // Verify datarate - if( val_in_range( rxParamSetupReq->Datarate, EU433_RX_MIN_DATARATE, EU433_RX_MAX_DATARATE ) == 0 ) - { - status &= 0xFD; // Datarate KO - } - - // Verify datarate offset - if( val_in_range( rxParamSetupReq->DrOffset, EU433_MIN_RX1_DR_OFFSET, EU433_MAX_RX1_DR_OFFSET ) == 0 ) - { - status &= 0xFB; // Rx1DrOffset range KO - } - - return status; -} - -uint8_t LoRaPHYEU433::request_new_channel(NewChannelReqParams_t* newChannelReq) -{ - uint8_t status = 0x03; - ChannelAddParams_t channelAdd; - ChannelRemoveParams_t channelRemove; - - if( newChannelReq->NewChannel->Frequency == 0 ) - { - channelRemove.ChannelId = newChannelReq->ChannelId; - - // Remove - if( remove_channel( &channelRemove ) == false ) - { - status &= 0xFC; - } - } - else - { - channelAdd.NewChannel = newChannelReq->NewChannel; - channelAdd.ChannelId = newChannelReq->ChannelId; - - switch( add_channel( &channelAdd ) ) - { - case LORAMAC_STATUS_OK: - { - break; - } - case LORAMAC_STATUS_FREQUENCY_INVALID: - { - status &= 0xFE; - break; - } - case LORAMAC_STATUS_DATARATE_INVALID: - { - status &= 0xFD; - break; - } - case LORAMAC_STATUS_FREQ_AND_DR_INVALID: - { - status &= 0xFC; - break; - } - default: - { - status &= 0xFC; - break; - } - } - } - - return status; -} - -int8_t LoRaPHYEU433::setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq) -{ - return -1; -} - -uint8_t LoRaPHYEU433::dl_channel_request(DlChannelReqParams_t* dlChannelReq) -{ - uint8_t status = 0x03; - - // Verify if the frequency is supported - if( VerifyTxFreq( dlChannelReq->Rx1Frequency, _radio ) == false ) - { - status &= 0xFE; - } - - // Verify if an uplink frequency exists - if( Channels[dlChannelReq->ChannelId].Frequency == 0 ) - { - status &= 0xFD; - } - - // Apply Rx1 frequency, if the status is OK - if( status == 0x03 ) - { - Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; - } - - return status; -} - -int8_t LoRaPHYEU433::get_alternate_DR(AlternateDrParams_t* alternateDr) -{ - int8_t datarate = 0; - - if( ( alternateDr->NbTrials % 48 ) == 0 ) - { - datarate = DR_0; - } - else if( ( alternateDr->NbTrials % 32 ) == 0 ) - { - datarate = DR_1; - } - else if( ( alternateDr->NbTrials % 24 ) == 0 ) - { - datarate = DR_2; - } - else if( ( alternateDr->NbTrials % 16 ) == 0 ) - { - datarate = DR_3; - } - else if( ( alternateDr->NbTrials % 8 ) == 0 ) - { - datarate = DR_4; - } - else - { - datarate = DR_5; - } - return datarate; -} - -void LoRaPHYEU433::calculate_backoff(CalcBackOffParams_t* calcBackOff) -{ - RegionCommonCalcBackOffParams_t calcBackOffParams; - - calcBackOffParams.Channels = Channels; - calcBackOffParams.Bands = Bands; - calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; - calcBackOffParams.Joined = calcBackOff->Joined; - calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; - calcBackOffParams.Channel = calcBackOff->Channel; - calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; - calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; - - get_DC_backoff( &calcBackOffParams ); -} - -bool LoRaPHYEU433::set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTx = 0; - uint8_t enabledChannels[EU433_MAX_NB_CHANNELS] = { 0 }; - TimerTime_t nextTxDelay = 0; - - if( num_active_channels( ChannelsMask, 0, 1 ) == 0 ) - { // Reactivate default channels - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); - } - - if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) - { - // Reset Aggregated time off - *aggregatedTimeOff = 0; - - // Update bands Time OFF - nextTxDelay = update_band_timeoff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, EU433_MAX_NB_BANDS ); - - // Search how many channels are enabled - nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, - ChannelsMask, Channels, - Bands, enabledChannels, &delayTx ); - } - else - { - delayTx++; - nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); - } - - if( nbEnabledChannels > 0 ) - { - // We found a valid channel - *channel = enabledChannels[get_random( 0, nbEnabledChannels - 1 )]; - - *time = 0; - return true; - } - else - { - if( delayTx > 0 ) - { - // Delay transmission due to AggregatedTimeOff or to a band time off - *time = nextTxDelay; - return true; - } - // Datarate not supported by any channel, restore defaults - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); - *time = 0; - return false; - } -} - -LoRaMacStatus_t LoRaPHYEU433::add_channel(ChannelAddParams_t* channelAdd) -{ - uint8_t band = 0; - bool drInvalid = false; - bool freqInvalid = false; - uint8_t id = channelAdd->ChannelId; - - if( id >= EU433_MAX_NB_CHANNELS ) - { - return LORAMAC_STATUS_PARAMETER_INVALID; - } - - // Validate the datarate range - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Min, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Max, EU433_TX_MIN_DATARATE, EU433_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) - { - drInvalid = true; - } - - // Default channels don't accept all values - if( id < EU433_NUMB_DEFAULT_CHANNELS ) - { - // Validate the datarate range for min: must be DR_0 - if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 ) - { - drInvalid = true; - } - // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, EU433_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - // We are not allowed to change the frequency - if( channelAdd->NewChannel->Frequency != Channels[id].Frequency ) - { - freqInvalid = true; - } - } - - // Check frequency - if( freqInvalid == false ) - { - if( VerifyTxFreq( channelAdd->NewChannel->Frequency, _radio ) == false ) - { - freqInvalid = true; - } - } - - // Check status - if( ( drInvalid == true ) && ( freqInvalid == true ) ) - { - return LORAMAC_STATUS_FREQ_AND_DR_INVALID; - } - if( drInvalid == true ) - { - return LORAMAC_STATUS_DATARATE_INVALID; - } - if( freqInvalid == true ) - { - return LORAMAC_STATUS_FREQUENCY_INVALID; - } - - memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) ); - Channels[id].Band = band; - ChannelsMask[0] |= ( 1 << id ); - return LORAMAC_STATUS_OK; -} - -bool LoRaPHYEU433::remove_channel(ChannelRemoveParams_t* channelRemove) -{ - uint8_t id = channelRemove->ChannelId; - - if( id < EU433_NUMB_DEFAULT_CHANNELS ) - { - return false; - } - - // Remove the channel from the list of channels - const ChannelParams_t empty_channel = { 0, 0, { 0 }, 0 }; - Channels[id] = empty_channel; - - return disable_channel( ChannelsMask, id, EU433_MAX_NB_CHANNELS ); -} - -void LoRaPHYEU433::set_tx_cont_mode(ContinuousWaveParams_t* continuousWave) -{ - int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); - int8_t phyTxPower = 0; - uint32_t frequency = Channels[continuousWave->Channel].Frequency; - - // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); - - _radio->set_tx_continuous_wave( frequency, phyTxPower, continuousWave->Timeout ); -} - -uint8_t LoRaPHYEU433::apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset) -{ - int8_t datarate = dr - drOffset; - - if( datarate < 0 ) - { - datarate = DR_0; - } - return datarate; -} diff --git a/features/lorawan/lorastack/phy/LoRaPHYEU433.h b/features/lorawan/lorastack/phy/LoRaPHYEU433.h index 8090b42c4d..ccffc9d207 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYEU433.h +++ b/features/lorawan/lorastack/phy/LoRaPHYEU433.h @@ -33,7 +33,6 @@ #define MBED_OS_LORAPHY_EU433_H_ #include "LoRaPHY.h" -#include "netsocket/LoRaRadio.h" /*! * LoRaMac maximum number of channels @@ -45,316 +44,36 @@ */ #define EU433_MAX_NB_BANDS 1 -#define EU433_CHANNELS_MASK_SIZE 1 +#define EU433_CHANNEL_MASK_SIZE 1 class LoRaPHYEU433 : public LoRaPHY { public: - LoRaPHYEU433(); + LoRaPHYEU433(LoRaWANTimeHandler &lora_time); virtual ~LoRaPHYEU433(); - /*! - * \brief The function gets a value of a specific PHY attribute. - * - * \param [in] getPhy A pointer to the function parameters. - * - * \retval The structure containing the PHY parameter. - */ - virtual PhyParam_t get_phy_params(GetPhyParams_t* getPhy ); - - /*! - * \brief Updates the last TX done parameters of the current channel. - * - * \param [in] txDone A pointer to the function parameters. - */ - virtual void set_band_tx_done(SetBandTxDoneParams_t* txDone ); - - /*! - * \brief Initializes the channels masks and the channels. - * - * \param [in] type Sets the initialization type. - */ - virtual void load_defaults(InitType_t type ); - - /*! - * \brief Verifies a parameter. - * - * \param [in] verify A pointer to the function parameters. - * - * \param [in] phyAttribute The attribute to verify. - * - * \retval True, if the parameter is valid. - */ - virtual bool verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute ); - - /*! - * \brief The function parses the input buffer and sets up the channels of the CF list. - * - * \param [in] applyCFList A pointer to the function parameters. - */ - virtual void apply_cf_list(ApplyCFListParams_t* applyCFList ); - - /*! - * \brief Sets a channels mask. - * - * \param [in] chanMaskSet A pointer to the function parameters. - * - * \retval True, if the channels mask could be set. - */ - virtual bool set_channel_mask(ChanMaskSetParams_t* chanMaskSet ); - - /*! - * \brief Calculates the next datarate to set, when ADR is on or off. - * - * \param [in] adrNext A pointer to the function parameters. - * - * \param [out] drOut The calculated datarate for the next TX. - * - * \param [out] txPowOut The TX power for the next TX. - * - * \param [out] adrAckCounter The calculated ADR acknowledgement counter. - * - * \retval True, if an ADR request should be performed. - */ - virtual bool get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter ); - - /*! - * \brief Configuration of the RX windows. - * - * \param [in] rxConfig A pointer to the function parameters. - * - * \param [out] datarate The datarate index set. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool rx_config(RxConfigParams_t* rxConfig, int8_t* datarate ); - - /* - * RX window precise timing - * - * For more details please consult the following document, chapter 3.1.2. - * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf - * or - * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf - * - * Downlink start: T = Tx + 1s (+/- 20 us) - * | - * TRxEarly | TRxLate - * | | | - * | | +---+---+---+---+---+---+---+---+ - * | | | Latest Rx window | - * | | +---+---+---+---+---+---+---+---+ - * | | | - * +---+---+---+---+---+---+---+---+ - * | Earliest Rx window | - * +---+---+---+---+---+---+---+---+ - * | - * +---+---+---+---+---+---+---+---+ - *Downlink preamble 8 symbols | | | | | | | | | - * +---+---+---+---+---+---+---+---+ - * - * Worst case Rx window timings - * - * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME - * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME - * - * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * - * RxOffset = ( TRxLate + TRxEarly ) / 2 - * - * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME - * - * The minimum value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol - */ - /*! - * Computes the RX window timeout and offset. - * - * \param [in] datarate The RX window datarate index to be used. - * - * \param [in] minRxSymbols The minimum number of symbols required to detect an RX frame. - * - * \param [in] rxError The system maximum timing error of the receiver in milliseconds. - * The receiver will turn on in a [-rxError : +rxError] ms - * interval around RxOffset. - * - * \param [out] rxConfigParams Returns the updated WindowTimeout and WindowOffset fields. - */ - virtual void compute_rx_win_params(int8_t datarate, - uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams); - - /*! - * \brief TX configuration. - * - * \param [in] txConfig A pointer to the function parameters. - * - * \param [out] txPower The TX power index set. - * - * \param [out] txTimeOnAir The time-on-air of the frame. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir ); - - /*! - * \brief The function processes a Link ADR request. - * - * \param [in] linkAdrReq A pointer to the function parameters. - * - * \param [out] drOut The datarate applied. - * - * \param [out] txPowOut The TX power applied. - * - * \param [out] nbRepOut The number of repetitions to apply. - * - * \param [out] nbBytesParsed The number of bytes parsed. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, - uint8_t* nbBytesParsed ); - - /*! - * \brief The function processes a RX parameter setup request. - * - * \param [in] rxParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq ); - - /*! - * \brief The function processes a new channel request. - * - * \param [in] newChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t request_new_channel(NewChannelReqParams_t* newChannelReq ); - - /*! - * \brief The function processes a TX ParamSetup request. - * - * \param [in] txParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - * Returns -1, if the functionality is not implemented. In this case, the end node - * shall ignore the command. - */ - virtual int8_t setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq ); - - /*! - * \brief The function processes a DlChannel request. - * - * \param [in] dlChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t dl_channel_request(DlChannelReqParams_t* dlChannelReq ); - - /*! - * \brief Alternates the datarate of the channel for the join request. - * - * \param [in] alternateDr A pointer to the function parameters. - * - * \retval The datarate to apply. - */ - virtual int8_t get_alternate_DR(AlternateDrParams_t* alternateDr ); - - /*! - * \brief Calculates the back-off time. - * - * \param [in] calcBackOff A pointer to the function parameters. - */ - virtual void calculate_backoff(CalcBackOffParams_t* calcBackOff ); - - /*! - * \brief Searches and sets the next random available channel. - * - * \param [in] nextChanParams The parameters for the next channel - * - * \param [out] channel The next channel to use for TX. - * - * \param [out] time The time to wait for the next transmission according to the duty - * cycle. - * - * \param [out] aggregatedTimeOff Updates the aggregated time off. - * - * \retval Function status [1: OK, 0: Unable to find a channel on the current datarate]. - */ - virtual bool set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff ); - - /*! - * \brief Adds a channel. - * - * \param [in] channelAdd A pointer to the function parameters. - * - * \retval The status of the operation. - */ - virtual LoRaMacStatus_t add_channel(ChannelAddParams_t* channelAdd ); - - /*! - * \brief Removes a channel. - * - * \param [in] channelRemove A pointer to the function parameters. - * - * \retval True, if the channel was removed successfully. - */ - virtual bool remove_channel(ChannelRemoveParams_t* channelRemove ); - - /*! - * \brief Sets the radio into continuous wave mode. - * - * \param [in] continuousWave A pointer to the function parameters. - */ - virtual void set_tx_cont_mode(ContinuousWaveParams_t* continuousWave ); - - /*! - * \brief Computes new datarate according to the given offset. - * - * \param [in] downlinkDwellTime The downlink dwell time configuration. 0: No limit, 1: 400ms - * - * \param [in] dr The current datarate. - * - * \param [in] drOffset The offset to be applied. - * - * \retval newDr The computed datarate. - */ - virtual uint8_t apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); - private: - uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ); - - // Global attributes /*! * LoRaMAC channels */ - ChannelParams_t Channels[EU433_MAX_NB_CHANNELS]; + channel_params_t channels[EU433_MAX_NB_CHANNELS]; /*! * LoRaMac bands */ - Band_t Bands[EU433_MAX_NB_BANDS]; + band_t bands[EU433_MAX_NB_BANDS]; /*! - * LoRaMac channels mask + * LoRaMac channel mask */ - uint16_t ChannelsMask[EU433_CHANNELS_MASK_SIZE]; + uint16_t channel_mask[EU433_CHANNEL_MASK_SIZE]; /*! - * LoRaMac channels default mask + * LoRaMac default channel mask */ - uint16_t ChannelsDefaultMask[EU433_CHANNELS_MASK_SIZE]; + uint16_t default_channel_mask[EU433_CHANNEL_MASK_SIZE]; }; diff --git a/features/lorawan/lorastack/phy/LoRaPHYEU868.cpp b/features/lorawan/lorastack/phy/LoRaPHYEU868.cpp index d89054bb6a..fcf8c4ce7f 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYEU868.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYEU868.cpp @@ -31,7 +31,6 @@ #include "LoRaPHYEU868.h" #include "lora_phy_ds.h" -#include "LoRaRadio.h" /*! * Number of default channels @@ -68,6 +67,8 @@ */ #define EU868_DEFAULT_DATARATE DR_0 +#define EU868_DEFAULT_MAX_DATARATE DR_5 + /*! * Minimal Rx1 receive datarate offset */ @@ -161,7 +162,7 @@ /*! * Random ack timeout limits */ -#define EU868_ACK_TIMEOUT_RND 1000 +#define EU868_ACK_TIMEOUT_RND 1000 #if ( EU868_DEFAULT_DATARATE > DR_5 ) #error "A default DR higher than DR_5 may lead to connectivity loss." @@ -170,1127 +171,180 @@ /*! * Second reception window channel frequency definition. */ -#define EU868_RX_WND_2_FREQ 869525000 +#define EU868_RX_WND_2_FREQ 869525000 /*! * Second reception window channel datarate definition. */ -#define EU868_RX_WND_2_DR DR_0 +#define EU868_RX_WND_2_DR DR_0 /*! * Band 0 definition - * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } */ -#define EU868_BAND0 { 100 , EU868_MAX_TX_POWER, 0, 0 } // 1.0 % - +static const band_t EU868_BAND0 = {100 , EU868_MAX_TX_POWER, 0, 0, 0,865000000, 868000000}; // 1.0 % /*! * Band 1 definition - * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } */ -#define EU868_BAND1 { 100 , EU868_MAX_TX_POWER, 0, 0 } // 1.0 % +static const band_t EU868_BAND1 = {100 , EU868_MAX_TX_POWER, 0, 0, 0, 868100000, 868600000}; // 1.0 % /*! * Band 2 definition - * Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + * Band = { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } */ -#define EU868_BAND2 { 1000, EU868_MAX_TX_POWER, 0, 0 } // 0.1 % +static const band_t EU868_BAND2 = {1000, EU868_MAX_TX_POWER, 0, 0, 0, 868700000, 869200000}; // 0.1 % /*! - * Band 2 definition - * Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + * Band 3 definition + * Band = { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } */ -#define EU868_BAND3 { 10 , EU868_MAX_TX_POWER, 0, 0 } // 10.0 % +static const band_t EU868_BAND3 = {10 , EU868_MAX_TX_POWER, 0, 0, 0, 869400000, 869650000}; // 10.0 % /*! - * Band 2 definition - * Band = { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + * Band 4 definition + * Band = { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } */ -#define EU868_BAND4 { 100 , EU868_MAX_TX_POWER, 0, 0 } // 1.0 % +static const band_t EU868_BAND4 = {100 , EU868_MAX_TX_POWER, 0, 0, 0, 869700000, 870000000}; // 1.0 % + +/*! + * Band 5 definition - It's actually a sub part of Band 2 + * Band = { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } + */ +static const band_t EU868_BAND5 = {1000, EU868_MAX_TX_POWER, 0, 0, 0, 863000000, 865000000}; // 0.1 % /*! * LoRaMac default channel 1 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define EU868_LC1 { 868100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } +static const channel_params_t EU868_LC1 = {868100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1}; /*! * LoRaMac default channel 2 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define EU868_LC2 { 868300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } +static const channel_params_t EU868_LC2 = {868300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1}; /*! * LoRaMac default channel 3 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define EU868_LC3 { 868500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1 } +static const channel_params_t EU868_LC3 = {868500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 1}; /*! * LoRaMac channels which are allowed for the join procedure */ -#define EU868_JOIN_CHANNELS ( uint16_t )( LC( 1 ) | LC( 2 ) | LC( 3 ) ) +#define EU868_JOIN_CHANNELS (uint16_t)(LC(1) | LC(2) | LC(3)) /*! * Data rates table definition */ -static const uint8_t DataratesEU868[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; +static const uint8_t datarates_EU868[] = {12, 11, 10, 9, 8, 7, 7, 50}; /*! * Bandwidths table definition in Hz */ -static const uint32_t BandwidthsEU868[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; +static const uint32_t bandwidths_EU868[] = {125000, 125000, 125000, 125000, 125000, 125000, 250000, 0}; /*! * Maximum payload with respect to the datarate index. Cannot operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateEU868[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; +static const uint8_t max_payloads_EU868[] = {51, 51, 51, 115, 242, 242, 242, 242}; /*! * Maximum payload with respect to the datarate index. Can operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateRepeaterEU868[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; +static const uint8_t max_payloads_repeater_EU868[] = {51, 51, 51, 115, 222, 222, 222, 222}; - -// Static functions -static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +LoRaPHYEU868::LoRaPHYEU868(LoRaWANTimeHandler &lora_time) + : LoRaPHY(lora_time) { - uint8_t nextLowerDr = 0; + bands[0] = EU868_BAND0; + bands[1] = EU868_BAND1; + bands[2] = EU868_BAND2; + bands[3] = EU868_BAND3; + bands[4] = EU868_BAND4; + bands[5] = EU868_BAND5; - if( dr == minDr ) - { - nextLowerDr = minDr; - } - else - { - nextLowerDr = dr - 1; - } - return nextLowerDr; -} + // Default Channels are always enabled, rest will be added later + channels[0] = EU868_LC1; + channels[1] = EU868_LC2; + channels[2] = EU868_LC3; -static uint32_t GetBandwidth( uint32_t drIndex ) -{ - switch( BandwidthsEU868[drIndex] ) - { - default: - case 125000: - return 0; - case 250000: - return 1; - case 500000: - return 2; - } -} + // Initialize the channels default mask + default_channel_mask[0] = LC(1) + LC(2) + LC(3); + // Update the channels mask + copy_channel_mask(channel_mask, default_channel_mask, 1); -static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) -{ - int8_t txPowerResult = txPower; + // set default channels + phy_params.channels.channel_list = channels; + phy_params.channels.channel_list_size = EU868_MAX_NB_CHANNELS; + phy_params.channels.mask = channel_mask; + phy_params.channels.default_mask = default_channel_mask; + phy_params.channels.mask_size = EU868_CHANNEL_MASK_SIZE; - // Limit tx power to the band max - txPowerResult = MAX( txPower, maxBandTxPower ); + // set bands for EU868 spectrum + phy_params.bands.table = (void *) bands; + phy_params.bands.size = EU868_MAX_NB_BANDS; - return txPowerResult; -} + // set bandwidths available in EU868 spectrum + phy_params.bandwidths.table = (void *) bandwidths_EU868; + phy_params.bandwidths.size = 8; -static bool VerifyTxFreq( uint32_t freq, uint8_t *band, LoRaRadio *radio ) -{ - // Check radio driver support - if( radio->check_rf_frequency( freq ) == false ) - { - return false; - } + // set data rates available in EU868 spectrum + phy_params.datarates.table = (void *) datarates_EU868; + phy_params.datarates.size = 8; - // Check frequency bands - if( ( freq >= 863000000 ) && ( freq < 865000000 ) ) - { - *band = 2; - } - else if( ( freq >= 865000000 ) && ( freq <= 868000000 ) ) - { - *band = 0; - } - else if( ( freq > 868000000 ) && ( freq <= 868600000 ) ) - { - *band = 1; - } - else if( ( freq >= 868700000 ) && ( freq <= 869200000 ) ) - { - *band = 2; - } - else if( ( freq >= 869400000 ) && ( freq <= 869650000 ) ) - { - *band = 3; - } - else if( ( freq >= 869700000 ) && ( freq <= 870000000 ) ) - { - *band = 4; - } - else - { - return false; - } - return true; -} + // set payload sizes with respect to data rates + phy_params.payloads.table = (void *) max_payloads_EU868; + phy_params.payloads.size = 8; + phy_params.payloads_with_repeater.table = (void *) max_payloads_repeater_EU868; + phy_params.payloads.size = 8; -uint8_t LoRaPHYEU868::CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTransmission = 0; + // dwell time setting + phy_params.ul_dwell_time_setting = 0; + phy_params.dl_dwell_time_setting = 0; - for( uint8_t i = 0, k = 0; i < EU868_MAX_NB_CHANNELS; i += 16, k++ ) - { - for( uint8_t j = 0; j < 16; j++ ) - { - if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) - { - if( channels[i + j].Frequency == 0 ) - { // Check if the channel is enabled - continue; - } - if( joined == false ) - { - if( ( EU868_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) - { - continue; - } - } - if( val_in_range( datarate, channels[i + j].DrRange.Fields.Min, - channels[i + j].DrRange.Fields.Max ) == 0 ) - { // Check if the current channel selection supports the given datarate - continue; - } - if( bands[channels[i + j].Band].TimeOff > 0 ) - { // Check if the band is available for transmission - delayTransmission++; - continue; - } - enabledChannels[nbEnabledChannels++] = i + j; - } - } - } - - *delayTx = delayTransmission; - return nbEnabledChannels; -} - -LoRaPHYEU868::LoRaPHYEU868() -{ - const Band_t band0 = EU868_BAND0; - const Band_t band1 = EU868_BAND1; - const Band_t band2 = EU868_BAND2; - const Band_t band3 = EU868_BAND3; - const Band_t band4 = EU868_BAND4; - - Bands[0] = band0; - Bands[1] = band1; - Bands[2] = band2; - Bands[3] = band3; - Bands[4] = band4; + // set initial and default parameters + phy_params.duty_cycle_enabled = EU868_DUTY_CYCLE_ENABLED; + phy_params.accept_tx_param_setup_req = true; + phy_params.fsk_supported = true; + phy_params.cflist_supported = true; + phy_params.dl_channel_req_supported = true; + phy_params.custom_channelplans_supported = true; + phy_params.default_channel_cnt = EU868_NUMB_DEFAULT_CHANNELS; + phy_params.max_channel_cnt = EU868_MAX_NB_CHANNELS; + phy_params.cflist_channel_cnt = EU868_NUMB_CHANNELS_CF_LIST; + phy_params.min_tx_datarate = EU868_TX_MIN_DATARATE; + phy_params.max_tx_datarate = EU868_TX_MAX_DATARATE; + phy_params.min_rx_datarate = EU868_RX_MIN_DATARATE; + phy_params.max_rx_datarate = EU868_RX_MAX_DATARATE; + phy_params.default_datarate = EU868_DEFAULT_DATARATE; + phy_params.default_max_datarate = EU868_DEFAULT_MAX_DATARATE; + phy_params.min_rx1_dr_offset = EU868_MIN_RX1_DR_OFFSET; + phy_params.max_rx1_dr_offset = EU868_MAX_RX1_DR_OFFSET; + phy_params.default_rx1_dr_offset = EU868_DEFAULT_RX1_DR_OFFSET; + phy_params.min_tx_power = EU868_MIN_TX_POWER; + phy_params.max_tx_power = EU868_MAX_TX_POWER; + phy_params.default_tx_power = EU868_DEFAULT_TX_POWER; + phy_params.default_max_eirp = EU868_DEFAULT_MAX_EIRP; + phy_params.default_antenna_gain = EU868_DEFAULT_ANTENNA_GAIN; + phy_params.adr_ack_limit = EU868_ADR_ACK_LIMIT; + phy_params.adr_ack_delay = EU868_ADR_ACK_DELAY; + phy_params.max_rx_window = EU868_MAX_RX_WINDOW; + phy_params.recv_delay1 = EU868_RECEIVE_DELAY1; + phy_params.recv_delay2 = EU868_RECEIVE_DELAY2; + phy_params.join_channel_mask = EU868_JOIN_CHANNELS; + phy_params.join_accept_delay1 = EU868_JOIN_ACCEPT_DELAY1; + phy_params.join_accept_delay2 = EU868_JOIN_ACCEPT_DELAY2; + phy_params.max_fcnt_gap = EU868_MAX_FCNT_GAP; + phy_params.ack_timeout = EU868_ACKTIMEOUT; + phy_params.ack_timeout_rnd = EU868_ACK_TIMEOUT_RND; + phy_params.rx_window2_datarate = EU868_RX_WND_2_DR; + phy_params.rx_window2_frequency = EU868_RX_WND_2_FREQ; } LoRaPHYEU868::~LoRaPHYEU868() { } -PhyParam_t LoRaPHYEU868::get_phy_params(GetPhyParams_t* getPhy) -{ - PhyParam_t phyParam = { 0 }; - - switch( getPhy->Attribute ) - { - case PHY_MIN_RX_DR: - { - phyParam.Value = EU868_RX_MIN_DATARATE; - break; - } - case PHY_MIN_TX_DR: - { - phyParam.Value = EU868_TX_MIN_DATARATE; - break; - } - case PHY_DEF_TX_DR: - { - phyParam.Value = EU868_DEFAULT_DATARATE; - break; - } - case PHY_NEXT_LOWER_TX_DR: - { - phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, EU868_TX_MIN_DATARATE ); - break; - } - case PHY_DEF_TX_POWER: - { - phyParam.Value = EU868_DEFAULT_TX_POWER; - break; - } - case PHY_MAX_PAYLOAD: - { - phyParam.Value = MaxPayloadOfDatarateEU868[getPhy->Datarate]; - break; - } - case PHY_MAX_PAYLOAD_REPEATER: - { - phyParam.Value = MaxPayloadOfDatarateRepeaterEU868[getPhy->Datarate]; - break; - } - case PHY_DUTY_CYCLE: - { - phyParam.Value = EU868_DUTY_CYCLE_ENABLED; - break; - } - case PHY_MAX_RX_WINDOW: - { - phyParam.Value = EU868_MAX_RX_WINDOW; - break; - } - case PHY_RECEIVE_DELAY1: - { - phyParam.Value = EU868_RECEIVE_DELAY1; - break; - } - case PHY_RECEIVE_DELAY2: - { - phyParam.Value = EU868_RECEIVE_DELAY2; - break; - } - case PHY_JOIN_ACCEPT_DELAY1: - { - phyParam.Value = EU868_JOIN_ACCEPT_DELAY1; - break; - } - case PHY_JOIN_ACCEPT_DELAY2: - { - phyParam.Value = EU868_JOIN_ACCEPT_DELAY2; - break; - } - case PHY_MAX_FCNT_GAP: - { - phyParam.Value = EU868_MAX_FCNT_GAP; - break; - } - case PHY_ACK_TIMEOUT: - { - phyParam.Value = ( EU868_ACKTIMEOUT + get_random( -EU868_ACK_TIMEOUT_RND, EU868_ACK_TIMEOUT_RND ) ); - break; - } - case PHY_DEF_DR1_OFFSET: - { - phyParam.Value = EU868_DEFAULT_RX1_DR_OFFSET; - break; - } - case PHY_DEF_RX2_FREQUENCY: - { - phyParam.Value = EU868_RX_WND_2_FREQ; - break; - } - case PHY_DEF_RX2_DR: - { - phyParam.Value = EU868_RX_WND_2_DR; - break; - } - case PHY_CHANNELS_MASK: - { - phyParam.ChannelsMask = ChannelsMask; - break; - } - case PHY_CHANNELS_DEFAULT_MASK: - { - phyParam.ChannelsMask = ChannelsDefaultMask; - break; - } - case PHY_MAX_NB_CHANNELS: - { - phyParam.Value = EU868_MAX_NB_CHANNELS; - break; - } - case PHY_CHANNELS: - { - phyParam.Channels = Channels; - break; - } - case PHY_DEF_UPLINK_DWELL_TIME: - case PHY_DEF_DOWNLINK_DWELL_TIME: - { - phyParam.Value = 0; - break; - } - case PHY_DEF_MAX_EIRP: - { - phyParam.fValue = EU868_DEFAULT_MAX_EIRP; - break; - } - case PHY_DEF_ANTENNA_GAIN: - { - phyParam.fValue = EU868_DEFAULT_ANTENNA_GAIN; - break; - } - case PHY_NB_JOIN_TRIALS: - case PHY_DEF_NB_JOIN_TRIALS: - { - phyParam.Value = 48; - break; - } - default: - { - break; - } - } - - return phyParam; -} - -void LoRaPHYEU868::set_band_tx_done(SetBandTxDoneParams_t* txDone) -{ - set_last_tx_done( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); -} - -void LoRaPHYEU868::load_defaults(InitType_t type) -{ - switch( type ) - { - case INIT_TYPE_INIT: - { - // Channels - const ChannelParams_t channel1 = EU868_LC1; - const ChannelParams_t channel2 = EU868_LC2; - const ChannelParams_t channel3 = EU868_LC3; - Channels[0] = channel1; - Channels[1] = channel2; - Channels[2] = channel3; - - // Initialize the channels default mask - ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); - // Update the channels mask - copy_channel_mask( ChannelsMask, ChannelsDefaultMask, 1 ); - break; - } - case INIT_TYPE_RESTORE: - { - // Restore channels default mask - ChannelsMask[0] |= ChannelsDefaultMask[0]; - break; - } - default: - { - break; - } - } -} - -bool LoRaPHYEU868::verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute) -{ - switch( phyAttribute ) - { - case PHY_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ); - } - case PHY_DEF_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, DR_0, DR_5 ); - } - case PHY_RX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, EU868_RX_MIN_DATARATE, EU868_RX_MAX_DATARATE ); - } - case PHY_DEF_TX_POWER: - case PHY_TX_POWER: - { - // Remark: switched min and max! - return val_in_range( verify->TxPower, EU868_MAX_TX_POWER, EU868_MIN_TX_POWER ); - } - case PHY_DUTY_CYCLE: - { - return EU868_DUTY_CYCLE_ENABLED; - } - case PHY_NB_JOIN_TRIALS: - { - if( verify->NbJoinTrials < 48 ) - { - return false; - } - break; - } - default: - return false; - } - return true; -} - -void LoRaPHYEU868::apply_cf_list(ApplyCFListParams_t* applyCFList) -{ - ChannelParams_t newChannel; - ChannelAddParams_t channelAdd; - ChannelRemoveParams_t channelRemove; - - // Setup default datarate range - newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; - - // Size of the optional CF list - if( applyCFList->Size != 16 ) - { - return; - } - - // Last byte is RFU, don't take it into account - for( uint8_t i = 0, chanIdx = EU868_NUMB_DEFAULT_CHANNELS; chanIdx < EU868_MAX_NB_CHANNELS; i+=3, chanIdx++ ) - { - if( chanIdx < ( EU868_NUMB_CHANNELS_CF_LIST + EU868_NUMB_DEFAULT_CHANNELS ) ) - { - // Channel frequency - newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; - newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); - newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); - newChannel.Frequency *= 100; - - // Initialize alternative frequency to 0 - newChannel.Rx1Frequency = 0; - } - else - { - newChannel.Frequency = 0; - newChannel.DrRange.Value = 0; - newChannel.Rx1Frequency = 0; - } - - if( newChannel.Frequency != 0 ) - { - channelAdd.NewChannel = &newChannel; - channelAdd.ChannelId = chanIdx; - - // Try to add all channels - add_channel( &channelAdd ); - } - else - { - channelRemove.ChannelId = chanIdx; - - remove_channel( &channelRemove ); - } - } -} - -bool LoRaPHYEU868::set_channel_mask(ChanMaskSetParams_t* chanMaskSet) -{ - switch( chanMaskSet->ChannelsMaskType ) - { - case CHANNELS_MASK: - { - copy_channel_mask( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); - break; - } - case CHANNELS_DEFAULT_MASK: - { - copy_channel_mask( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); - break; - } - default: - return false; - } - return true; -} - -bool LoRaPHYEU868::get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter) -{ - bool adrAckReq = false; - int8_t datarate = adrNext->Datarate; - int8_t txPower = adrNext->TxPower; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - - // Report back the adr ack counter - *adrAckCounter = adrNext->AdrAckCounter; - - if( adrNext->AdrEnabled == true ) - { - if( datarate == EU868_TX_MIN_DATARATE ) - { - *adrAckCounter = 0; - adrAckReq = false; - } - else - { - if( adrNext->AdrAckCounter >= EU868_ADR_ACK_LIMIT ) - { - adrAckReq = true; - txPower = EU868_MAX_TX_POWER; - } - else - { - adrAckReq = false; - } - if( adrNext->AdrAckCounter >= ( EU868_ADR_ACK_LIMIT + EU868_ADR_ACK_DELAY ) ) - { - if( ( adrNext->AdrAckCounter % EU868_ADR_ACK_DELAY ) == 1 ) - { - // Decrease the datarate - getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; - getPhy.Datarate = datarate; - getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; - phyParam = get_phy_params( &getPhy ); - datarate = phyParam.Value; - - if( datarate == EU868_TX_MIN_DATARATE ) - { - // We must set adrAckReq to false as soon as we reach the lowest datarate - adrAckReq = false; - if( adrNext->UpdateChanMask == true ) - { - // Re-enable default channels - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); - } - } - } - } - } - } - - *drOut = datarate; - *txPowOut = txPower; - return adrAckReq; -} - -void LoRaPHYEU868::compute_rx_win_params(int8_t datarate, uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams) -{ - double tSymbol = 0.0; - - // Get the datarate, perform a boundary check - rxConfigParams->Datarate = MIN( datarate, EU868_RX_MAX_DATARATE ); - rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); - - if( rxConfigParams->Datarate == DR_7 ) - { // FSK - tSymbol = compute_symb_timeout_fsk( DataratesEU868[rxConfigParams->Datarate] ); - } - else - { // LoRa - tSymbol = compute_symb_timeout_lora( DataratesEU868[rxConfigParams->Datarate], BandwidthsEU868[rxConfigParams->Datarate] ); - } - - get_rx_window_params( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); -} - -bool LoRaPHYEU868::rx_config(RxConfigParams_t* rxConfig, int8_t* datarate) -{ - radio_modems_t modem; - int8_t dr = rxConfig->Datarate; - uint8_t maxPayload = 0; - int8_t phyDr = 0; - uint32_t frequency = rxConfig->Frequency; - - if( _radio->get_status() != RF_IDLE ) - { - return false; - } - - if( rxConfig->Window == 0 ) - { - // Apply window 1 frequency - frequency = Channels[rxConfig->Channel].Frequency; - // Apply the alternative RX 1 window frequency, if it is available - if( Channels[rxConfig->Channel].Rx1Frequency != 0 ) - { - frequency = Channels[rxConfig->Channel].Rx1Frequency; - } - } - - // Read the physical datarate from the datarates table - phyDr = DataratesEU868[dr]; - - _radio->set_channel( frequency ); - - // Radio configuration - if( dr == DR_7 ) - { - modem = MODEM_FSK; - _radio->set_rx_config( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); - } - else - { - modem = MODEM_LORA; - _radio->set_rx_config( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); - } - - if( rxConfig->RepeaterSupport == true ) - { - maxPayload = MaxPayloadOfDatarateRepeaterEU868[dr]; - } - else - { - maxPayload = MaxPayloadOfDatarateEU868[dr]; - } - - _radio->set_max_payload_length( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); - - *datarate = (uint8_t) dr; - return true; -} - -bool LoRaPHYEU868::tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir) -{ - radio_modems_t modem; - int8_t phyDr = DataratesEU868[txConfig->Datarate]; - int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); - uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); - int8_t phyTxPower = 0; - - // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); - - // Setup the radio frequency - _radio->set_channel( Channels[txConfig->Channel].Frequency ); - - if( txConfig->Datarate == DR_7 ) - { // High Speed FSK channel - modem = MODEM_FSK; - _radio->set_tx_config( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 3000 ); - } - else - { - modem = MODEM_LORA; - _radio->set_tx_config( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 ); - } - - // Setup maximum payload lenght of the radio driver - _radio->set_max_payload_length( modem, txConfig->PktLen ); - // Get the time-on-air of the next tx frame - *txTimeOnAir = _radio->time_on_air( modem, txConfig->PktLen ); - - *txPower = txPowerLimited; - return true; -} - -uint8_t LoRaPHYEU868::link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, uint8_t* nbBytesParsed) -{ - uint8_t status = 0x07; - RegionCommonLinkAdrParams_t linkAdrParams; - uint8_t nextIndex = 0; - uint8_t bytesProcessed = 0; - uint16_t chMask = 0; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; - - while( bytesProcessed < linkAdrReq->PayloadSize ) - { - // Get ADR request parameters - nextIndex = parse_link_ADR_req( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); - - if( nextIndex == 0 ) - break; // break loop, since no more request has been found - - // Update bytes processed - bytesProcessed += nextIndex; - - // Revert status, as we only check the last ADR request for the channel mask KO - status = 0x07; - - // Setup temporary channels mask - chMask = linkAdrParams.ChMask; - - // Verify channels mask - if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) - { - status &= 0xFE; // Channel mask KO - } - else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || - ( linkAdrParams.ChMaskCtrl >= 7 ) ) - { - // RFU - status &= 0xFE; // Channel mask KO - } - else - { - for( uint8_t i = 0; i < EU868_MAX_NB_CHANNELS; i++ ) - { - if( linkAdrParams.ChMaskCtrl == 6 ) - { - if( Channels[i].Frequency != 0 ) - { - chMask |= 1 << i; - } - } - else - { - if( ( ( chMask & ( 1 << i ) ) != 0 ) && - ( Channels[i].Frequency == 0 ) ) - {// Trying to enable an undefined channel - status &= 0xFE; // Channel mask KO - } - } - } - } - } - - // Get the minimum possible datarate - getPhy.Attribute = PHY_MIN_TX_DR; - getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; - phyParam = get_phy_params( &getPhy ); - - linkAdrVerifyParams.Status = status; - linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; - linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; - linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; - linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; - linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; - linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; - linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; - linkAdrVerifyParams.NbChannels = EU868_MAX_NB_CHANNELS; - linkAdrVerifyParams.ChannelsMask = &chMask; - linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; - linkAdrVerifyParams.MaxDatarate = EU868_TX_MAX_DATARATE; - linkAdrVerifyParams.Channels = Channels; - linkAdrVerifyParams.MinTxPower = EU868_MIN_TX_POWER; - linkAdrVerifyParams.MaxTxPower = EU868_MAX_TX_POWER; - - // Verify the parameters and update, if necessary - status = verify_link_ADR_req( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); - - // Update channelsMask if everything is correct - if( status == 0x07 ) - { - // Set the channels mask to a default value - memset( ChannelsMask, 0, sizeof( ChannelsMask ) ); - // Update the channels mask - ChannelsMask[0] = chMask; - } - - // Update status variables - *drOut = linkAdrParams.Datarate; - *txPowOut = linkAdrParams.TxPower; - *nbRepOut = linkAdrParams.NbRep; - *nbBytesParsed = bytesProcessed; - - return status; -} - -uint8_t LoRaPHYEU868::setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq) -{ - uint8_t status = 0x07; - - // Verify radio frequency - if( _radio->check_rf_frequency( rxParamSetupReq->Frequency ) == false ) - { - status &= 0xFE; // Channel frequency KO - } - - // Verify datarate - if( val_in_range( rxParamSetupReq->Datarate, EU868_RX_MIN_DATARATE, EU868_RX_MAX_DATARATE ) == 0 ) - { - status &= 0xFD; // Datarate KO - } - - // Verify datarate offset - if( val_in_range( rxParamSetupReq->DrOffset, EU868_MIN_RX1_DR_OFFSET, EU868_MAX_RX1_DR_OFFSET ) == 0 ) - { - status &= 0xFB; // Rx1DrOffset range KO - } - - return status; -} - -uint8_t LoRaPHYEU868::request_new_channel(NewChannelReqParams_t* newChannelReq) -{ - uint8_t status = 0x03; - ChannelAddParams_t channelAdd; - ChannelRemoveParams_t channelRemove; - - if( newChannelReq->NewChannel->Frequency == 0 ) - { - channelRemove.ChannelId = newChannelReq->ChannelId; - - // Remove - if( remove_channel( &channelRemove ) == false ) - { - status &= 0xFC; - } - } - else - { - channelAdd.NewChannel = newChannelReq->NewChannel; - channelAdd.ChannelId = newChannelReq->ChannelId; - - switch( add_channel( &channelAdd ) ) - { - case LORAMAC_STATUS_OK: - { - break; - } - case LORAMAC_STATUS_FREQUENCY_INVALID: - { - status &= 0xFE; - break; - } - case LORAMAC_STATUS_DATARATE_INVALID: - { - status &= 0xFD; - break; - } - case LORAMAC_STATUS_FREQ_AND_DR_INVALID: - { - status &= 0xFC; - break; - } - default: - { - status &= 0xFC; - break; - } - } - } - - return status; -} - -int8_t LoRaPHYEU868::setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq) -{ - return -1; -} - -uint8_t LoRaPHYEU868::dl_channel_request(DlChannelReqParams_t* dlChannelReq) -{ - uint8_t status = 0x03; - uint8_t band = 0; - - // Verify if the frequency is supported - if( VerifyTxFreq( dlChannelReq->Rx1Frequency, &band, _radio ) == false ) - { - status &= 0xFE; - } - - // Verify if an uplink frequency exists - if( Channels[dlChannelReq->ChannelId].Frequency == 0 ) - { - status &= 0xFD; - } - - // Apply Rx1 frequency, if the status is OK - if( status == 0x03 ) - { - Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; - } - - return status; -} - -int8_t LoRaPHYEU868::get_alternate_DR(AlternateDrParams_t* alternateDr) -{ - int8_t datarate = 0; - - if( ( alternateDr->NbTrials % 48 ) == 0 ) - { - datarate = DR_0; - } - else if( ( alternateDr->NbTrials % 32 ) == 0 ) - { - datarate = DR_1; - } - else if( ( alternateDr->NbTrials % 24 ) == 0 ) - { - datarate = DR_2; - } - else if( ( alternateDr->NbTrials % 16 ) == 0 ) - { - datarate = DR_3; - } - else if( ( alternateDr->NbTrials % 8 ) == 0 ) - { - datarate = DR_4; - } - else - { - datarate = DR_5; - } - return datarate; -} - -void LoRaPHYEU868::calculate_backoff(CalcBackOffParams_t* calcBackOff) -{ - RegionCommonCalcBackOffParams_t calcBackOffParams; - - calcBackOffParams.Channels = Channels; - calcBackOffParams.Bands = Bands; - calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; - calcBackOffParams.Joined = calcBackOff->Joined; - calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; - calcBackOffParams.Channel = calcBackOff->Channel; - calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; - calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; - - get_DC_backoff( &calcBackOffParams ); -} - -bool LoRaPHYEU868::set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTx = 0; - uint8_t enabledChannels[EU868_MAX_NB_CHANNELS] = { 0 }; - TimerTime_t nextTxDelay = 0; - - if( num_active_channels( ChannelsMask, 0, 1 ) == 0 ) - { // Reactivate default channels - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); - } - - if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) - { - // Reset Aggregated time off - *aggregatedTimeOff = 0; - - // Update bands Time OFF - nextTxDelay = update_band_timeoff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, EU868_MAX_NB_BANDS ); - - // Search how many channels are enabled - nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, - ChannelsMask, Channels, - Bands, enabledChannels, &delayTx ); - } - else - { - delayTx++; - nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); - } - - if( nbEnabledChannels > 0 ) - { - // We found a valid channel - *channel = enabledChannels[get_random( 0, nbEnabledChannels - 1 )]; - - *time = 0; - return true; - } - else - { - if( delayTx > 0 ) - { - // Delay transmission due to AggregatedTimeOff or to a band time off - *time = nextTxDelay; - return true; - } - // Datarate not supported by any channel, restore defaults - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); - *time = 0; - return false; - } -} - -LoRaMacStatus_t LoRaPHYEU868::add_channel(ChannelAddParams_t* channelAdd) -{ - uint8_t band = 0; - bool drInvalid = false; - bool freqInvalid = false; - uint8_t id = channelAdd->ChannelId; - - if( id >= EU868_MAX_NB_CHANNELS ) - { - return LORAMAC_STATUS_PARAMETER_INVALID; - } - - // Validate the datarate range - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Min, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Max, EU868_TX_MIN_DATARATE, EU868_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) - { - drInvalid = true; - } - - // Default channels don't accept all values - if( id < EU868_NUMB_DEFAULT_CHANNELS ) - { - // Validate the datarate range for min: must be DR_0 - if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 ) - { - drInvalid = true; - } - // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, EU868_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - // We are not allowed to change the frequency - if( channelAdd->NewChannel->Frequency != Channels[id].Frequency ) - { - freqInvalid = true; - } - } - - // Check frequency - if( freqInvalid == false ) - { - if( VerifyTxFreq( channelAdd->NewChannel->Frequency, &band, _radio ) == false ) - { - freqInvalid = true; - } - } - - // Check status - if( ( drInvalid == true ) && ( freqInvalid == true ) ) - { - return LORAMAC_STATUS_FREQ_AND_DR_INVALID; - } - if( drInvalid == true ) - { - return LORAMAC_STATUS_DATARATE_INVALID; - } - if( freqInvalid == true ) - { - return LORAMAC_STATUS_FREQUENCY_INVALID; - } - - memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) ); - Channels[id].Band = band; - ChannelsMask[0] |= ( 1 << id ); - return LORAMAC_STATUS_OK; -} - -bool LoRaPHYEU868::remove_channel(ChannelRemoveParams_t* channelRemove) -{ - uint8_t id = channelRemove->ChannelId; - - if( id < EU868_NUMB_DEFAULT_CHANNELS ) - { - return false; - } - - // Remove the channel from the list of channels - const ChannelParams_t empty_channel = { 0, 0, { 0 }, 0 }; - Channels[id] = empty_channel; - - return disable_channel( ChannelsMask, id, EU868_MAX_NB_CHANNELS ); -} - -void LoRaPHYEU868::set_tx_cont_mode(ContinuousWaveParams_t* continuousWave) -{ - int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); - int8_t phyTxPower = 0; - uint32_t frequency = Channels[continuousWave->Channel].Frequency; - - // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); - - _radio->set_tx_continuous_wave( frequency, phyTxPower, continuousWave->Timeout ); -} - -uint8_t LoRaPHYEU868::apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset) -{ - int8_t datarate = dr - drOffset; - - if( datarate < 0 ) - { - datarate = DR_0; - } - return datarate; -} diff --git a/features/lorawan/lorastack/phy/LoRaPHYEU868.h b/features/lorawan/lorastack/phy/LoRaPHYEU868.h index 0c588c5417..404a9f11a6 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYEU868.h +++ b/features/lorawan/lorastack/phy/LoRaPHYEU868.h @@ -33,7 +33,6 @@ #define MBED_OS_LORAPHY_EU868_H_ #include "LoRaPHY.h" -#include "netsocket/LoRaRadio.h" /*! * LoRaMac maximum number of channels @@ -42,317 +41,42 @@ /*! * Maximum number of bands + * + * We have broken down EU-868 MHz BAND 2 into two parts. That's why + * total number of sub-bands is 6. + * from 863 MHz to 865 MHz region is part of BAND 2, however + * we call it Band-5 here. Duty cycle limit is 0.1 % in this sub band. */ -#define EU868_MAX_NB_BANDS 5 - -#define EU868_CHANNELS_MASK_SIZE 1 +#define EU868_MAX_NB_BANDS 6 +#define EU868_CHANNEL_MASK_SIZE 1 class LoRaPHYEU868 : public LoRaPHY { public: - LoRaPHYEU868(); + LoRaPHYEU868(LoRaWANTimeHandler &lora_time); virtual ~LoRaPHYEU868(); - /*! - * \brief The function gets a value of a specific PHY attribute. - * - * \param [in] getPhy A pointer to the function parameters. - * - * \retval The structure containing the PHY parameter. - */ - virtual PhyParam_t get_phy_params(GetPhyParams_t* getPhy ); - - /*! - * \brief Updates the last TX done parameters of the current channel. - * - * \param [in] txDone A pointer to the function parameters. - */ - virtual void set_band_tx_done(SetBandTxDoneParams_t* txDone ); - - /*! - * \brief Initializes the channels masks and the channels. - * - * \param [in] type Sets the initialization type. - */ - virtual void load_defaults(InitType_t type ); - - /*! - * \brief Verifies a parameter. - * - * \param [in] verify A pointer to the function parameters. - * - * \param [in] phyAttribute The attribute to verify. - * - * \retval True, if the parameter is valid. - */ - virtual bool verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute ); - - /*! - * \brief The function parses the input buffer and sets up the channels of the CF list. - * - * \param [in] applyCFList A pointer to the function parameters. - */ - virtual void apply_cf_list(ApplyCFListParams_t* applyCFList ); - - /*! - * \brief Sets a channels mask. - * - * \param [in] chanMaskSet A pointer to the function parameters. - * - * \retval True, if the channels mask could be set. - */ - virtual bool set_channel_mask(ChanMaskSetParams_t* chanMaskSet ); - - /*! - * \brief Calculates the next datarate to set, when ADR is on or off. - * - * \param [in] adrNext A pointer to the function parameters. - * - * \param [out] drOut The calculated datarate for the next TX. - * - * \param [out] txPowOut The TX power for the next TX. - * - * \param [out] adrAckCounter The calculated ADR acknowledgement counter. - * - * \retval True, if an ADR request should be performed. - */ - virtual bool get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter ); - - /*! - * \brief Configuration of the RX windows. - * - * \param [in] rxConfig A pointer to the function parameters. - * - * \param [out] datarate The datarate index set. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool rx_config(RxConfigParams_t* rxConfig, int8_t* datarate ); - - /* - * RX window precise timing - * - * For more details please consult the following document, chapter 3.1.2. - * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf - * or - * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf - * - * Downlink start: T = Tx + 1s (+/- 20 us) - * | - * TRxEarly | TRxLate - * | | | - * | | +---+---+---+---+---+---+---+---+ - * | | | Latest Rx window | - * | | +---+---+---+---+---+---+---+---+ - * | | | - * +---+---+---+---+---+---+---+---+ - * | Earliest Rx window | - * +---+---+---+---+---+---+---+---+ - * | - * +---+---+---+---+---+---+---+---+ - *Downlink preamble 8 symbols | | | | | | | | | - * +---+---+---+---+---+---+---+---+ - * - * Worst case Rx window timings - * - * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME - * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME - * - * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * - * RxOffset = ( TRxLate + TRxEarly ) / 2 - * - * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME - * - * The minimum value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol - */ - /*! - * Computes the RX window timeout and offset. - * - * \param [in] datarate The RX window datarate index to be used. - * - * \param [in] minRxSymbols The minimum number of symbols required to detect an RX frame. - * - * \param [in] rxError The system maximum timing error of the receiver in milliseconds. - * The receiver will turn on in a [-rxError : +rxError] ms - * interval around RxOffset. - * - * \param [out] rxConfigParams Returns the updated WindowTimeout and WindowOffset fields. - */ - virtual void compute_rx_win_params(int8_t datarate, - uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams); - - /*! - * \brief TX configuration. - * - * \param [in] txConfig A pointer to the function parameters. - * - * \param [out] txPower The TX power index set. - * - * \param [out] txTimeOnAir The time-on-air of the frame. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir ); - - /*! - * \brief The function processes a Link ADR request. - * - * \param [in] linkAdrReq A pointer to the function parameters. - * - * \param [out] drOut The datarate applied. - * - * \param [out] txPowOut The TX power applied. - * - * \param [out] nbRepOut The number of repetitions to apply. - * - * \param [out] nbBytesParsed The number of bytes parsed. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, - uint8_t* nbBytesParsed ); - - /*! - * \brief The function processes a RX parameter setup request. - * - * \param [in] rxParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq ); - - /*! - * \brief The function processes a new channel request. - * - * \param [in] newChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t request_new_channel(NewChannelReqParams_t* newChannelReq ); - - /*! - * \brief The function processes a TX ParamSetup request. - * - * \param [in] txParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - * Returns -1, if the functionality is not implemented. In this case, the end node - * shall ignore the command. - */ - virtual int8_t setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq ); - - /*! - * \brief The function processes a DlChannel request. - * - * \param [in] dlChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t dl_channel_request(DlChannelReqParams_t* dlChannelReq ); - - /*! - * \brief Alternates the datarate of the channel for the join request. - * - * \param [in] alternateDr A pointer to the function parameters. - * - * \retval The datarate to apply. - */ - virtual int8_t get_alternate_DR(AlternateDrParams_t* alternateDr ); - - /*! - * \brief Calculates the back-off time. - * - * \param [in] calcBackOff A pointer to the function parameters. - */ - virtual void calculate_backoff(CalcBackOffParams_t* calcBackOff ); - - /*! - * \brief Searches and sets the next random available channel. - * - * \param [in] nextChanParams The parameters for the next channel. - * - * \param [out] channel The next channel to use for TX. - * - * \param [out] time The time to wait for the next transmission according to the duty cycle. - * - * \param [out] aggregatedTimeOff Updates the aggregated time off. - * - * \retval The function status [1: OK, 0: Unable to find a channel on the current datarate]. - */ - virtual bool set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff ); - - /*! - * \brief Adds a channel. - * - * \param [in] channelAdd A pointer to the function parameters. - * - * \retval The status of the operation. - */ - virtual LoRaMacStatus_t add_channel(ChannelAddParams_t* channelAdd ); - - /*! - * \brief Removes a channel. - * - * \param [in] channelRemove A pointer to the function parameters. - * - * \retval True, if the channel was removed successfully. - */ - virtual bool remove_channel(ChannelRemoveParams_t* channelRemove ); - - /*! - * \brief Sets the radio into continuous wave mode. - * - * \param [in] continuousWave A pointer to the function parameters. - */ - virtual void set_tx_cont_mode(ContinuousWaveParams_t* continuousWave ); - - /*! - * \brief Computes a new datarate according to the given offset. - * - * \param [in] downlinkDwellTime The downlink dwell time configuration. 0: No limit, 1: 400ms - * - * \param [in] dr The current datarate. - * - * \param [in] drOffset The offset to be applied. - * - * \retval newDr The computed datarate. - */ - virtual uint8_t apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); - private: - uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ); - - // Global attributes /*! * LoRaMAC channels */ - ChannelParams_t Channels[EU868_MAX_NB_CHANNELS]; + channel_params_t channels[EU868_MAX_NB_CHANNELS]; /*! * LoRaMac bands */ - Band_t Bands[EU868_MAX_NB_BANDS]; + band_t bands[EU868_MAX_NB_BANDS]; /*! * LoRaMac channels mask */ - uint16_t ChannelsMask[EU868_CHANNELS_MASK_SIZE]; + uint16_t channel_mask[EU868_CHANNEL_MASK_SIZE]; /*! - * LoRaMac channels default mask + * LoRaMac default channel mask */ - uint16_t ChannelsDefaultMask[EU868_CHANNELS_MASK_SIZE]; + uint16_t default_channel_mask[EU868_CHANNEL_MASK_SIZE]; }; #endif /* MBED_OS_LORAPHY_EU868_H_ */ diff --git a/features/lorawan/lorastack/phy/LoRaPHYIN865.cpp b/features/lorawan/lorastack/phy/LoRaPHYIN865.cpp index 26196569c5..5ab3bc8f35 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYIN865.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYIN865.cpp @@ -30,9 +30,7 @@ */ #include "LoRaPHYIN865.h" - #include "lora_phy_ds.h" -#include "LoRaRadio.h" /*! @@ -69,6 +67,7 @@ * Default datarate used by the node */ #define IN865_DEFAULT_DATARATE DR_0 +#define IN865_DEFAULT_MAX_DATARATE DR_5 /*! * Minimal Rx1 receive datarate offset @@ -181,27 +180,27 @@ /*! * Band 0 definition - * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } */ -#define IN865_BAND0 { 1 , IN865_MAX_TX_POWER, 0, 0 } // 100.0 % +static const band_t IN865_BAND0 = { 1 , IN865_MAX_TX_POWER, 0, 0, 0, 865000000, 867000000 }; // 100.0 % /*! * LoRaMac default channel 1 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define IN865_LC1 { 865062500, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +static const channel_params_t IN865_LC1 = { 865062500, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }; /*! * LoRaMac default channel 2 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define IN865_LC2 { 865402500, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +static const channel_params_t IN865_LC2 = { 865402500, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }; /*! * LoRaMac default channel 3 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define IN865_LC3 { 865985000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +static const channel_params_t IN865_LC3 = { 865985000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }; /*! * LoRaMac channels which are allowed for the join procedure @@ -211,1033 +210,117 @@ /*! * Data rates table definition */ -static const uint8_t DataratesIN865[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; +static const uint8_t datarates_IN865[] = { 12, 11, 10, 9, 8, 7, 7, 50 }; /*! * Bandwidths table definition in Hz */ -static const uint32_t BandwidthsIN865[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; +static const uint32_t bandwidths_IN865[] = { 125000, 125000, 125000, 125000, 125000, 125000, 250000, 0 }; /*! * Maximum payload with respect to the datarate index. Cannot operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateIN865[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; +static const uint8_t max_payloads_IN865[] = { 51, 51, 51, 115, 242, 242, 242, 242 }; /*! * Maximum payload with respect to the datarate index. Can operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateRepeaterIN865[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; +static const uint8_t max_payloads_with_repeater[] = { 51, 51, 51, 115, 222, 222, 222, 222 }; /*! * Effective datarate offsets for receive window 1. */ -static const int8_t EffectiveRx1DrOffsetIN865[] = { 0, 1, 2, 3, 4, 5, -1, -2 }; +static const int8_t rx1_dr_offset_IN865[] = { 0, 1, 2, 3, 4, 5, -1, -2 }; - -// Static functions -static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +LoRaPHYIN865::LoRaPHYIN865(LoRaWANTimeHandler &lora_time) + : LoRaPHY(lora_time) { - uint8_t nextLowerDr = 0; + bands[0] = IN865_BAND0; - if( dr == minDr ) - { - nextLowerDr = minDr; - } - else if( dr == DR_7 ) - { - nextLowerDr = DR_5; - } - else - { - nextLowerDr = dr - 1; - } - return nextLowerDr; -} + // Default Channels are always enabled, rest will be added later + channels[0] = IN865_LC1; + channels[1] = IN865_LC2; + channels[2] = IN865_LC3; -static uint32_t GetBandwidth( uint32_t drIndex ) -{ - switch( BandwidthsIN865[drIndex] ) - { - default: - case 125000: - return 0; - case 250000: - return 1; - case 500000: - return 2; - } -} + // Initialize the channels default mask + default_channel_mask[0] = LC(1) + LC(2) + LC(3); + // Update the channels mask + copy_channel_mask(channel_mask, default_channel_mask, 1); -static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) -{ - int8_t txPowerResult = txPower; + // set default channels + phy_params.channels.channel_list = channels; + phy_params.channels.channel_list_size = IN865_MAX_NB_CHANNELS; + phy_params.channels.mask = channel_mask; + phy_params.channels.default_mask = default_channel_mask; + phy_params.channels.mask_size = IN865_CHANNEL_MASK_SIZE; - // Limit tx power to the band max - txPowerResult = MAX( txPower, maxBandTxPower ); + // set bands for IN865 spectrum + phy_params.bands.table = (void *) bands; + phy_params.bands.size = IN865_MAX_NB_BANDS; - return txPowerResult; -} + // set bandwidths available in IN865 spectrum + phy_params.bandwidths.table = (void *) bandwidths_IN865; + phy_params.bandwidths.size = 8; -static bool VerifyTxFreq( uint32_t freq, uint8_t *band, LoRaRadio *radio ) -{ - // Check radio driver support - if( radio->check_rf_frequency( freq ) == false ) - { - return false; - } + // set data rates available in IN865 spectrum + phy_params.datarates.table = (void *) datarates_IN865; + phy_params.datarates.size = 8; - if( ( freq < 865000000 ) || ( freq > 867000000 ) ) - { - return false; - } - return true; -} + // set payload sizes with respect to data rates + phy_params.payloads.table = (void *) max_payloads_IN865; + phy_params.payloads.size = 8; + phy_params.payloads_with_repeater.table = (void *) max_payloads_with_repeater; + phy_params.payloads.size = 8; -uint8_t LoRaPHYIN865::CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTransmission = 0; + // dwell time setting + phy_params.ul_dwell_time_setting = 0; + phy_params.dl_dwell_time_setting = 0; - for( uint8_t i = 0, k = 0; i < IN865_MAX_NB_CHANNELS; i += 16, k++ ) - { - for( uint8_t j = 0; j < 16; j++ ) - { - if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) - { - if( channels[i + j].Frequency == 0 ) - { // Check if the channel is enabled - continue; - } - if( joined == false ) - { - if( ( IN865_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) - { - continue; - } - } - if( val_in_range( datarate, channels[i + j].DrRange.Fields.Min, - channels[i + j].DrRange.Fields.Max ) == 0 ) - { // Check if the current channel selection supports the given datarate - continue; - } - if( bands[channels[i + j].Band].TimeOff > 0 ) - { // Check if the band is available for transmission - delayTransmission++; - continue; - } - enabledChannels[nbEnabledChannels++] = i + j; - } - } - } - - *delayTx = delayTransmission; - return nbEnabledChannels; -} - -LoRaPHYIN865::LoRaPHYIN865() -{ - const Band_t band0 = IN865_BAND0; - Bands[0] = band0; + // set initial and default parameters + phy_params.duty_cycle_enabled = IN865_DUTY_CYCLE_ENABLED; + phy_params.accept_tx_param_setup_req = false; + phy_params.fsk_supported = true; + phy_params.cflist_supported = true; + phy_params.dl_channel_req_supported = true; + phy_params.custom_channelplans_supported = true; + phy_params.default_channel_cnt = IN865_NUMB_DEFAULT_CHANNELS; + phy_params.max_channel_cnt = IN865_MAX_NB_CHANNELS; + phy_params.cflist_channel_cnt = IN865_NUMB_CHANNELS_CF_LIST; + phy_params.min_tx_datarate = IN865_TX_MIN_DATARATE; + phy_params.max_tx_datarate = IN865_TX_MAX_DATARATE; + phy_params.min_rx_datarate = IN865_RX_MIN_DATARATE; + phy_params.max_rx_datarate = IN865_RX_MAX_DATARATE; + phy_params.default_datarate = IN865_DEFAULT_DATARATE; + phy_params.default_max_datarate = IN865_DEFAULT_MAX_DATARATE; + phy_params.min_rx1_dr_offset = IN865_MIN_RX1_DR_OFFSET; + phy_params.max_rx1_dr_offset = IN865_MAX_RX1_DR_OFFSET; + phy_params.default_rx1_dr_offset = IN865_DEFAULT_RX1_DR_OFFSET; + phy_params.min_tx_power = IN865_MIN_TX_POWER; + phy_params.max_tx_power = IN865_MAX_TX_POWER; + phy_params.default_tx_power = IN865_DEFAULT_TX_POWER; + phy_params.default_max_eirp = IN865_DEFAULT_MAX_EIRP; + phy_params.default_antenna_gain = IN865_DEFAULT_ANTENNA_GAIN; + phy_params.adr_ack_limit = IN865_ADR_ACK_LIMIT; + phy_params.adr_ack_delay = IN865_ADR_ACK_DELAY; + phy_params.max_rx_window = IN865_MAX_RX_WINDOW; + phy_params.recv_delay1 = IN865_RECEIVE_DELAY1; + phy_params.recv_delay2 = IN865_RECEIVE_DELAY2; + phy_params.join_channel_mask = IN865_JOIN_CHANNELS; + phy_params.join_accept_delay1 = IN865_JOIN_ACCEPT_DELAY1; + phy_params.join_accept_delay2 = IN865_JOIN_ACCEPT_DELAY2; + phy_params.max_fcnt_gap = IN865_MAX_FCNT_GAP; + phy_params.ack_timeout = IN865_ACKTIMEOUT; + phy_params.ack_timeout_rnd = IN865_ACK_TIMEOUT_RND; + phy_params.rx_window2_datarate = IN865_RX_WND_2_DR; + phy_params.rx_window2_frequency = IN865_RX_WND_2_FREQ; } LoRaPHYIN865::~LoRaPHYIN865() { } -PhyParam_t LoRaPHYIN865::get_phy_params(GetPhyParams_t* getPhy) -{ - PhyParam_t phyParam = { 0 }; - - switch( getPhy->Attribute ) - { - case PHY_MIN_RX_DR: - { - phyParam.Value = IN865_RX_MIN_DATARATE; - break; - } - case PHY_MIN_TX_DR: - { - phyParam.Value = IN865_TX_MIN_DATARATE; - break; - } - case PHY_DEF_TX_DR: - { - phyParam.Value = IN865_DEFAULT_DATARATE; - break; - } - case PHY_NEXT_LOWER_TX_DR: - { - phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, IN865_TX_MIN_DATARATE ); - break; - } - case PHY_DEF_TX_POWER: - { - phyParam.Value = IN865_DEFAULT_TX_POWER; - break; - } - case PHY_MAX_PAYLOAD: - { - phyParam.Value = MaxPayloadOfDatarateIN865[getPhy->Datarate]; - break; - } - case PHY_MAX_PAYLOAD_REPEATER: - { - phyParam.Value = MaxPayloadOfDatarateRepeaterIN865[getPhy->Datarate]; - break; - } - case PHY_DUTY_CYCLE: - { - phyParam.Value = IN865_DUTY_CYCLE_ENABLED; - break; - } - case PHY_MAX_RX_WINDOW: - { - phyParam.Value = IN865_MAX_RX_WINDOW; - break; - } - case PHY_RECEIVE_DELAY1: - { - phyParam.Value = IN865_RECEIVE_DELAY1; - break; - } - case PHY_RECEIVE_DELAY2: - { - phyParam.Value = IN865_RECEIVE_DELAY2; - break; - } - case PHY_JOIN_ACCEPT_DELAY1: - { - phyParam.Value = IN865_JOIN_ACCEPT_DELAY1; - break; - } - case PHY_JOIN_ACCEPT_DELAY2: - { - phyParam.Value = IN865_JOIN_ACCEPT_DELAY2; - break; - } - case PHY_MAX_FCNT_GAP: - { - phyParam.Value = IN865_MAX_FCNT_GAP; - break; - } - case PHY_ACK_TIMEOUT: - { - phyParam.Value = ( IN865_ACKTIMEOUT + get_random( -IN865_ACK_TIMEOUT_RND, IN865_ACK_TIMEOUT_RND ) ); - break; - } - case PHY_DEF_DR1_OFFSET: - { - phyParam.Value = IN865_DEFAULT_RX1_DR_OFFSET; - break; - } - case PHY_DEF_RX2_FREQUENCY: - { - phyParam.Value = IN865_RX_WND_2_FREQ; - break; - } - case PHY_DEF_RX2_DR: - { - phyParam.Value = IN865_RX_WND_2_DR; - break; - } - case PHY_CHANNELS_MASK: - { - phyParam.ChannelsMask = ChannelsMask; - break; - } - case PHY_CHANNELS_DEFAULT_MASK: - { - phyParam.ChannelsMask = ChannelsDefaultMask; - break; - } - case PHY_MAX_NB_CHANNELS: - { - phyParam.Value = IN865_MAX_NB_CHANNELS; - break; - } - case PHY_CHANNELS: - { - phyParam.Channels = Channels; - break; - } - case PHY_DEF_UPLINK_DWELL_TIME: - case PHY_DEF_DOWNLINK_DWELL_TIME: - { - phyParam.Value = 0; - break; - } - case PHY_DEF_MAX_EIRP: - { - phyParam.fValue = IN865_DEFAULT_MAX_EIRP; - break; - } - case PHY_DEF_ANTENNA_GAIN: - { - phyParam.fValue = IN865_DEFAULT_ANTENNA_GAIN; - break; - } - case PHY_NB_JOIN_TRIALS: - case PHY_DEF_NB_JOIN_TRIALS: - { - phyParam.Value = 48; - break; - } - default: - { - break; - } - } - - return phyParam; -} - -void LoRaPHYIN865::set_band_tx_done(SetBandTxDoneParams_t* txDone) -{ - set_last_tx_done( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); -} - -void LoRaPHYIN865::load_defaults(InitType_t type) -{ - switch( type ) - { - case INIT_TYPE_INIT: - { - // Channels - const ChannelParams_t channel1 = IN865_LC1; - const ChannelParams_t channel2 = IN865_LC2; - const ChannelParams_t channel3 = IN865_LC3; - Channels[0] = channel1; - Channels[1] = channel2; - Channels[2] = channel3; - - // Initialize the channels default mask - ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); - // Update the channels mask - copy_channel_mask( ChannelsMask, ChannelsDefaultMask, 1 ); - break; - } - case INIT_TYPE_RESTORE: - { - // Restore channels default mask - ChannelsMask[0] |= ChannelsDefaultMask[0]; - break; - } - default: - { - break; - } - } -} - -bool LoRaPHYIN865::verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute) -{ - switch( phyAttribute ) - { - case PHY_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ); - } - case PHY_DEF_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, DR_0, DR_5 ); - } - case PHY_RX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ); - } - case PHY_DEF_TX_POWER: - case PHY_TX_POWER: - { - // Remark: switched min and max! - return val_in_range( verify->TxPower, IN865_MAX_TX_POWER, IN865_MIN_TX_POWER ); - } - case PHY_DUTY_CYCLE: - { - return IN865_DUTY_CYCLE_ENABLED; - } - case PHY_NB_JOIN_TRIALS: - { - if( verify->NbJoinTrials < 48 ) - { - return false; - } - break; - } - default: - return false; - } - return true; -} - -void LoRaPHYIN865::apply_cf_list(ApplyCFListParams_t* applyCFList) -{ - ChannelParams_t newChannel; - ChannelAddParams_t channelAdd; - ChannelRemoveParams_t channelRemove; - - // Setup default datarate range - newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; - - // Size of the optional CF list - if( applyCFList->Size != 16 ) - { - return; - } - - // Last byte is RFU, don't take it into account - for( uint8_t i = 0, chanIdx = IN865_NUMB_DEFAULT_CHANNELS; chanIdx < IN865_MAX_NB_CHANNELS; i+=3, chanIdx++ ) - { - if( chanIdx < ( IN865_NUMB_CHANNELS_CF_LIST + IN865_NUMB_DEFAULT_CHANNELS ) ) - { - // Channel frequency - newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; - newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); - newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); - newChannel.Frequency *= 100; - - // Initialize alternative frequency to 0 - newChannel.Rx1Frequency = 0; - } - else - { - newChannel.Frequency = 0; - newChannel.DrRange.Value = 0; - newChannel.Rx1Frequency = 0; - } - - if( newChannel.Frequency != 0 ) - { - channelAdd.NewChannel = &newChannel; - channelAdd.ChannelId = chanIdx; - - // Try to add all channels - add_channel( &channelAdd ); - } - else - { - channelRemove.ChannelId = chanIdx; - - remove_channel( &channelRemove ); - } - } -} - -bool LoRaPHYIN865::set_channel_mask(ChanMaskSetParams_t* chanMaskSet) -{ - switch( chanMaskSet->ChannelsMaskType ) - { - case CHANNELS_MASK: - { - copy_channel_mask( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); - break; - } - case CHANNELS_DEFAULT_MASK: - { - copy_channel_mask( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); - break; - } - default: - return false; - } - return true; -} - -bool LoRaPHYIN865::get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter) -{ - bool adrAckReq = false; - int8_t datarate = adrNext->Datarate; - int8_t txPower = adrNext->TxPower; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - - // Report back the adr ack counter - *adrAckCounter = adrNext->AdrAckCounter; - - if( adrNext->AdrEnabled == true ) - { - if( datarate == IN865_TX_MIN_DATARATE ) - { - *adrAckCounter = 0; - adrAckReq = false; - } - else - { - if( adrNext->AdrAckCounter >= IN865_ADR_ACK_LIMIT ) - { - adrAckReq = true; - txPower = IN865_MAX_TX_POWER; - } - else - { - adrAckReq = false; - } - if( adrNext->AdrAckCounter >= ( IN865_ADR_ACK_LIMIT + IN865_ADR_ACK_DELAY ) ) - { - if( ( adrNext->AdrAckCounter % IN865_ADR_ACK_DELAY ) == 1 ) - { - // Decrease the datarate - getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; - getPhy.Datarate = datarate; - getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; - phyParam = get_phy_params( &getPhy ); - datarate = phyParam.Value; - - if( datarate == IN865_TX_MIN_DATARATE ) - { - // We must set adrAckReq to false as soon as we reach the lowest datarate - adrAckReq = false; - if( adrNext->UpdateChanMask == true ) - { - // Re-enable default channels - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); - } - } - } - } - } - } - - *drOut = datarate; - *txPowOut = txPower; - return adrAckReq; -} - -void LoRaPHYIN865::compute_rx_win_params(int8_t datarate, uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams) -{ - double tSymbol = 0.0; - - // Get the datarate, perform a boundary check - rxConfigParams->Datarate = MIN( datarate, IN865_RX_MAX_DATARATE ); - rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); - - if( rxConfigParams->Datarate == DR_7 ) - { // FSK - tSymbol = compute_symb_timeout_fsk( DataratesIN865[rxConfigParams->Datarate] ); - } - else - { // LoRa - tSymbol = compute_symb_timeout_lora( DataratesIN865[rxConfigParams->Datarate], BandwidthsIN865[rxConfigParams->Datarate] ); - } - - get_rx_window_params( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); -} - -bool LoRaPHYIN865::rx_config(RxConfigParams_t* rxConfig, int8_t* datarate) -{ - radio_modems_t modem; - int8_t dr = rxConfig->Datarate; - uint8_t maxPayload = 0; - int8_t phyDr = 0; - uint32_t frequency = rxConfig->Frequency; - - if( _radio->get_status() != RF_IDLE ) - { - return false; - } - - if( rxConfig->Window == 0 ) - { - // Apply window 1 frequency - frequency = Channels[rxConfig->Channel].Frequency; - // Apply the alternative RX 1 window frequency, if it is available - if( Channels[rxConfig->Channel].Rx1Frequency != 0 ) - { - frequency = Channels[rxConfig->Channel].Rx1Frequency; - } - } - - // Read the physical datarate from the datarates table - phyDr = DataratesIN865[dr]; - - _radio->set_channel( frequency ); - - // Radio configuration - if( dr == DR_7 ) - { - modem = MODEM_FSK; - _radio->set_rx_config( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous ); - } - else - { - modem = MODEM_LORA; - _radio->set_rx_config( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); - } - - if( rxConfig->RepeaterSupport == true ) - { - maxPayload = MaxPayloadOfDatarateRepeaterIN865[dr]; - } - else - { - maxPayload = MaxPayloadOfDatarateIN865[dr]; - } - _radio->set_max_payload_length( modem, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); - - *datarate = (uint8_t) dr; - return true; -} - -bool LoRaPHYIN865::tx_config(TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir) -{ - radio_modems_t modem; - int8_t phyDr = DataratesIN865[txConfig->Datarate]; - int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); - uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); - int8_t phyTxPower = 0; - - // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain ); - - // Setup the radio frequency - _radio->set_channel( Channels[txConfig->Channel].Frequency ); - - if( txConfig->Datarate == DR_7 ) - { // High Speed FSK channel - modem = MODEM_FSK; - _radio->set_tx_config( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 3000 ); - } - else - { - modem = MODEM_LORA; - _radio->set_tx_config( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 ); - } - - // Setup maximum payload lenght of the radio driver - _radio->set_max_payload_length( modem, txConfig->PktLen ); - // Get the time-on-air of the next tx frame - *txTimeOnAir = _radio->time_on_air( modem, txConfig->PktLen ); - - *txPower = txPowerLimited; - return true; -} - -uint8_t LoRaPHYIN865::link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, uint8_t* nbBytesParsed) -{ - uint8_t status = 0x07; - RegionCommonLinkAdrParams_t linkAdrParams; - uint8_t nextIndex = 0; - uint8_t bytesProcessed = 0; - uint16_t chMask = 0; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; - - while( bytesProcessed < linkAdrReq->PayloadSize ) - { - // Get ADR request parameters - nextIndex = parse_link_ADR_req( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); - - if( nextIndex == 0 ) - break; // break loop, since no more request has been found - - // Update bytes processed - bytesProcessed += nextIndex; - - // Revert status, as we only check the last ADR request for the channel mask KO - status = 0x07; - - // Setup temporary channels mask - chMask = linkAdrParams.ChMask; - - // Verify channels mask - if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) - { - status &= 0xFE; // Channel mask KO - } - else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || - ( linkAdrParams.ChMaskCtrl >= 7 ) ) - { - // RFU - status &= 0xFE; // Channel mask KO - } - else - { - for( uint8_t i = 0; i < IN865_MAX_NB_CHANNELS; i++ ) - { - if( linkAdrParams.ChMaskCtrl == 6 ) - { - if( Channels[i].Frequency != 0 ) - { - chMask |= 1 << i; - } - } - else - { - if( ( ( chMask & ( 1 << i ) ) != 0 ) && - ( Channels[i].Frequency == 0 ) ) - {// Trying to enable an undefined channel - status &= 0xFE; // Channel mask KO - } - } - } - } - } - - // Get the minimum possible datarate - getPhy.Attribute = PHY_MIN_TX_DR; - getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; - phyParam = get_phy_params( &getPhy ); - - linkAdrVerifyParams.Status = status; - linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; - linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; - linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; - linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; - linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; - linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; - linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; - linkAdrVerifyParams.NbChannels = IN865_MAX_NB_CHANNELS; - linkAdrVerifyParams.ChannelsMask = &chMask; - linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; - linkAdrVerifyParams.MaxDatarate = IN865_TX_MAX_DATARATE; - linkAdrVerifyParams.Channels = Channels; - linkAdrVerifyParams.MinTxPower = IN865_MIN_TX_POWER; - linkAdrVerifyParams.MaxTxPower = IN865_MAX_TX_POWER; - - // Verify the parameters and update, if necessary - status = verify_link_ADR_req( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); - - // Update channelsMask if everything is correct - if( status == 0x07 ) - { - // Set the channels mask to a default value - memset( ChannelsMask, 0, sizeof( ChannelsMask ) ); - // Update the channels mask - ChannelsMask[0] = chMask; - } - - // Update status variables - *drOut = linkAdrParams.Datarate; - *txPowOut = linkAdrParams.TxPower; - *nbRepOut = linkAdrParams.NbRep; - *nbBytesParsed = bytesProcessed; - - return status; -} - -uint8_t LoRaPHYIN865::setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq) -{ - uint8_t status = 0x07; - - // Verify radio frequency - if( _radio->check_rf_frequency( rxParamSetupReq->Frequency ) == false ) - { - status &= 0xFE; // Channel frequency KO - } - - // Verify datarate - if( val_in_range( rxParamSetupReq->Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ) == 0 ) - { - status &= 0xFD; // Datarate KO - } - - // Verify datarate offset - if( val_in_range( rxParamSetupReq->DrOffset, IN865_MIN_RX1_DR_OFFSET, IN865_MAX_RX1_DR_OFFSET ) == 0 ) - { - status &= 0xFB; // Rx1DrOffset range KO - } - - return status; -} - -uint8_t LoRaPHYIN865::request_new_channel(NewChannelReqParams_t* newChannelReq) -{ - uint8_t status = 0x03; - ChannelAddParams_t channelAdd; - ChannelRemoveParams_t channelRemove; - - if( newChannelReq->NewChannel->Frequency == 0 ) - { - channelRemove.ChannelId = newChannelReq->ChannelId; - - // Remove - if( remove_channel( &channelRemove ) == false ) - { - status &= 0xFC; - } - } - else - { - channelAdd.NewChannel = newChannelReq->NewChannel; - channelAdd.ChannelId = newChannelReq->ChannelId; - - switch( add_channel( &channelAdd ) ) - { - case LORAMAC_STATUS_OK: - { - break; - } - case LORAMAC_STATUS_FREQUENCY_INVALID: - { - status &= 0xFE; - break; - } - case LORAMAC_STATUS_DATARATE_INVALID: - { - status &= 0xFD; - break; - } - case LORAMAC_STATUS_FREQ_AND_DR_INVALID: - { - status &= 0xFC; - break; - } - default: - { - status &= 0xFC; - break; - } - } - } - - return status; -} - -int8_t LoRaPHYIN865::setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq) -{ - return -1; -} - -uint8_t LoRaPHYIN865::dl_channel_request(DlChannelReqParams_t* dlChannelReq) -{ - uint8_t status = 0x03; - uint8_t band = 0; - - // Verify if the frequency is supported - if( VerifyTxFreq( dlChannelReq->Rx1Frequency, &band, _radio ) == false ) - { - status &= 0xFE; - } - - // Verify if an uplink frequency exists - if( Channels[dlChannelReq->ChannelId].Frequency == 0 ) - { - status &= 0xFD; - } - - // Apply Rx1 frequency, if the status is OK - if( status == 0x03 ) - { - Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; - } - - return status; -} - -int8_t LoRaPHYIN865::get_alternate_DR(AlternateDrParams_t* alternateDr) -{ - int8_t datarate = 0; - - if( ( alternateDr->NbTrials % 48 ) == 0 ) - { - datarate = DR_0; - } - else if( ( alternateDr->NbTrials % 32 ) == 0 ) - { - datarate = DR_1; - } - else if( ( alternateDr->NbTrials % 24 ) == 0 ) - { - datarate = DR_2; - } - else if( ( alternateDr->NbTrials % 16 ) == 0 ) - { - datarate = DR_3; - } - else if( ( alternateDr->NbTrials % 8 ) == 0 ) - { - datarate = DR_4; - } - else - { - datarate = DR_5; - } - return datarate; -} - -void LoRaPHYIN865::calculate_backoff(CalcBackOffParams_t* calcBackOff) -{ - RegionCommonCalcBackOffParams_t calcBackOffParams; - - calcBackOffParams.Channels = Channels; - calcBackOffParams.Bands = Bands; - calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; - calcBackOffParams.Joined = calcBackOff->Joined; - calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; - calcBackOffParams.Channel = calcBackOff->Channel; - calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; - calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; - - get_DC_backoff( &calcBackOffParams ); -} - -bool LoRaPHYIN865::set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTx = 0; - uint8_t enabledChannels[IN865_MAX_NB_CHANNELS] = { 0 }; - TimerTime_t nextTxDelay = 0; - - if( num_active_channels( ChannelsMask, 0, 1 ) == 0 ) - { // Reactivate default channels - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); - } - - if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) - { - // Reset Aggregated time off - *aggregatedTimeOff = 0; - - // Update bands Time OFF - nextTxDelay = update_band_timeoff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, IN865_MAX_NB_BANDS ); - - // Search how many channels are enabled - nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, - ChannelsMask, Channels, - Bands, enabledChannels, &delayTx ); - } - else - { - delayTx++; - nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); - } - - if( nbEnabledChannels > 0 ) - { - // We found a valid channel - *channel = enabledChannels[get_random( 0, nbEnabledChannels - 1 )]; - - *time = 0; - return true; - } - else - { - if( delayTx > 0 ) - { - // Delay transmission due to AggregatedTimeOff or to a band time off - *time = nextTxDelay; - return true; - } - // Datarate not supported by any channel, restore defaults - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); - *time = 0; - return false; - } -} - -LoRaMacStatus_t LoRaPHYIN865::add_channel(ChannelAddParams_t* channelAdd) -{ - uint8_t band = 0; - bool drInvalid = false; - bool freqInvalid = false; - uint8_t id = channelAdd->ChannelId; - - if( id >= IN865_MAX_NB_CHANNELS ) - { - return LORAMAC_STATUS_PARAMETER_INVALID; - } - - // Validate the datarate range - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Min, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Max, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) - { - drInvalid = true; - } - - // Default channels don't accept all values - if( id < IN865_NUMB_DEFAULT_CHANNELS ) - { - // Validate the datarate range for min: must be DR_0 - if( channelAdd->NewChannel->DrRange.Fields.Min > DR_0 ) - { - drInvalid = true; - } - // Validate the datarate range for max: must be DR_5 <= Max <= TX_MAX_DATARATE - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Max, DR_5, IN865_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - // We are not allowed to change the frequency - if( channelAdd->NewChannel->Frequency != Channels[id].Frequency ) - { - freqInvalid = true; - } - } - - // Check frequency - if( freqInvalid == false ) - { - if( VerifyTxFreq( channelAdd->NewChannel->Frequency, &band, _radio ) == false ) - { - freqInvalid = true; - } - } - - // Check status - if( ( drInvalid == true ) && ( freqInvalid == true ) ) - { - return LORAMAC_STATUS_FREQ_AND_DR_INVALID; - } - if( drInvalid == true ) - { - return LORAMAC_STATUS_DATARATE_INVALID; - } - if( freqInvalid == true ) - { - return LORAMAC_STATUS_FREQUENCY_INVALID; - } - - memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) ); - Channels[id].Band = band; - ChannelsMask[0] |= ( 1 << id ); - return LORAMAC_STATUS_OK; -} - -bool LoRaPHYIN865::remove_channel(ChannelRemoveParams_t* channelRemove) -{ - uint8_t id = channelRemove->ChannelId; - - if( id < IN865_NUMB_DEFAULT_CHANNELS ) - { - return false; - } - - // Remove the channel from the list of channels - const ChannelParams_t empty_channel = { 0, 0, { 0 }, 0 }; - Channels[id] = empty_channel; - - return disable_channel( ChannelsMask, id, IN865_MAX_NB_CHANNELS ); -} - -void LoRaPHYIN865::set_tx_cont_mode(ContinuousWaveParams_t* continuousWave) -{ - int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); - int8_t phyTxPower = 0; - uint32_t frequency = Channels[continuousWave->Channel].Frequency; - - // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, continuousWave->MaxEirp, continuousWave->AntennaGain ); - - _radio->set_tx_continuous_wave( frequency, phyTxPower, continuousWave->Timeout ); -} - -uint8_t LoRaPHYIN865::apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, - int8_t drOffset) +uint8_t LoRaPHYIN865::apply_DR_offset(int8_t dr, int8_t dr_offset) { // Apply offset formula - return MIN( DR_5, MAX( DR_0, dr - EffectiveRx1DrOffsetIN865[drOffset] ) ); + return MIN(DR_5, MAX(DR_0, dr - rx1_dr_offset_IN865[dr_offset])); } diff --git a/features/lorawan/lorastack/phy/LoRaPHYIN865.h b/features/lorawan/lorastack/phy/LoRaPHYIN865.h index d1be5f5b62..5d47f67a7a 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYIN865.h +++ b/features/lorawan/lorastack/phy/LoRaPHYIN865.h @@ -33,7 +33,6 @@ #define MBED_OS_LORAPHY_IN865_H_ #include "LoRaPHY.h" -#include "netsocket/LoRaRadio.h" /*! @@ -46,316 +45,38 @@ */ #define IN865_MAX_NB_BANDS 1 - -#define IN865_CHANNELS_MASK_SIZE 1 +#define IN865_CHANNEL_MASK_SIZE 1 class LoRaPHYIN865 : public LoRaPHY { public: - LoRaPHYIN865(); + LoRaPHYIN865(LoRaWANTimeHandler &lora_time); virtual ~LoRaPHYIN865(); - /*! - * \brief The function gets a value of a specific PHY attribute. - * - * \param [in] getPhy A pointer to the function parameters. - * - * \retval The structure containing the PHY parameter. - */ - virtual PhyParam_t get_phy_params(GetPhyParams_t* getPhy ); - - /*! - * \brief Updates the last TX done parameters of the current channel. - * - * \param [in] txDone A pointer to the function parameters. - */ - virtual void set_band_tx_done(SetBandTxDoneParams_t* txDone ); - - /*! - * \brief Initializes the channels masks and the channels. - * - * \param [in] type Sets the initialization type. - */ - virtual void load_defaults(InitType_t type ); - - /*! - * \brief Verifies a parameter. - * - * \param [in] verify A pointer to the function parameters. - * - * \param [in] phyAttribute The attribute to verify. - * - * \retval True, if the parameter is valid. - */ - virtual bool verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute ); - - /*! - * \brief The function parses the input buffer and sets up the channels of the CF list. - * - * \param [in] applyCFList A pointer to the function parameters. - */ - virtual void apply_cf_list(ApplyCFListParams_t* applyCFList ); - - /*! - * \brief Sets a channels mask. - * - * \param [in] chanMaskSet A pointer to the function parameters. - * - * \retval True, if the channels mask could be set. - */ - virtual bool set_channel_mask(ChanMaskSetParams_t* chanMaskSet ); - - /*! - * \brief Calculates the next datarate to set, when ADR is on or off. - * - * \param [in] adrNext A pointer to the function parameters. - * - * \param [out] drOut The calculated datarate for the next TX. - * - * \param [out] txPowOut The TX power for the next TX. - * - * \param [out] adrAckCounter The calculated ADR acknowledgement counter. - * - * \retval True, if an ADR request should be performed. - */ - virtual bool get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter ); - - /*! - * \brief Configuration of the RX windows. - * - * \param [in] rxConfig A pointer to the function parameters. - * - * \param [out] datarate The datarate index set. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool rx_config(RxConfigParams_t* rxConfig, int8_t* datarate ); - - /* - * RX window precise timing - * - * For more details, please consult the following document, chapter 3.1.2. - * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf - * or - * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf - * - * Downlink start: T = Tx + 1s (+/- 20 us) - * | - * TRxEarly | TRxLate - * | | | - * | | +---+---+---+---+---+---+---+---+ - * | | | Latest Rx window | - * | | +---+---+---+---+---+---+---+---+ - * | | | - * +---+---+---+---+---+---+---+---+ - * | Earliest Rx window | - * +---+---+---+---+---+---+---+---+ - * | - * +---+---+---+---+---+---+---+---+ - *Downlink preamble 8 symbols | | | | | | | | | - * +---+---+---+---+---+---+---+---+ - * - * Worst case Rx window timings - * - * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME - * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME - * - * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * - * RxOffset = ( TRxLate + TRxEarly ) / 2 - * - * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME - * - * The minimum value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol - */ - /*! - * Computes the RX window timeout and offset. - * - * \param [in] datarate The RX window datarate index to be used. - * - * \param [in] minRxSymbols The minimum number of symbols required to detect an RX frame. - * - * \param [in] rxError The system maximum timing error of the receiver in milliseconds. - * The receiver will turn on in a [-rxError : +rxError] ms - * interval around RxOffset. - * - * \param [out] rxConfigParams Returns the updated WindowTimeout and WindowOffset fields. - */ - virtual void compute_rx_win_params(int8_t datarate, - uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams); - - /*! - * \brief TX configuration. - * - * \param [in] txConfig A pointer to the function parameters. - * - * \param [out] txPower The TX power index set. - * - * \param [out] txTimeOnAir The time-on-air of the frame. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir ); - - /*! - * \brief The function processes a Link ADR request. - * - * \param [in] linkAdrReq A pointer to the function parameters. - * - * \param [out] drOut The datarate applied. - * - * \param [out] txPowOut The TX power applied. - * - * \param [out] nbRepOut The number of repetitions to apply. - * - * \param [out] nbBytesParsed The number of bytes parsed. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, - uint8_t* nbBytesParsed ); - - /*! - * \brief The function processes a RX parameter setup request. - * - * \param [in] rxParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq ); - - /*! - * \brief The function processes a new channel request. - * - * \param [in] newChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t request_new_channel(NewChannelReqParams_t* newChannelReq ); - - /*! - * \brief The function processes a TX ParamSetup request. - * - * \param [in] txParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - * Returns -1, if the functionality is not implemented. In this case, the end node - * shall ignore the command. - */ - virtual int8_t setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq ); - - /*! - * \brief The function processes a DlChannel request. - * - * \param [in] dlChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t dl_channel_request(DlChannelReqParams_t* dlChannelReq ); - - /*! - * \brief Alternates the datarate of the channel for the join request. - * - * \param [in] alternateDr A pointer to the function parameters. - * - * \retval The datarate to apply. - */ - virtual int8_t get_alternate_DR(AlternateDrParams_t* alternateDr ); - - /*! - * \brief Calculates the back-off time. - * - * \param [in] calcBackOff A pointer to the function parameters. - */ - virtual void calculate_backoff(CalcBackOffParams_t* calcBackOff ); - - /*! - * \brief Searches and sets the next random available channel. - * - * \param [in] nextChanParams The parameters for the next channel. - * - * \param [out] channel The next channel to use for TX. - * - * \param [out] time The time to wait for the next transmission according to the duty cycle. - * - * \param [out] aggregatedTimeOff Updates the aggregated time off. - * - * \retval The function status [1: OK, 0: Unable to find a channel on the current datarate]. - */ - virtual bool set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff ); - - /*! - * \brief Adds a channel. - * - * \param [in] channelAdd A pPointer to the function parameters. - * - * \retval The status of the operation. - */ - virtual LoRaMacStatus_t add_channel(ChannelAddParams_t* channelAdd ); - - /*! - * \brief Removes a channel. - * - * \param [in] channelRemove A pointer to the function parameters. - * - * \retval True, if the channel was removed successfully. - */ - virtual bool remove_channel(ChannelRemoveParams_t* channelRemove ); - - /*! - * \brief Sets the radio into continuous wave mode. - * - * \param [in] continuousWave A pointer to the function parameters. - */ - virtual void set_tx_cont_mode(ContinuousWaveParams_t* continuousWave ); - - /*! - * \brief Computes a new datarate according to the given offset. - * - * \param [in] downlinkDwellTime The downlink dwell time configuration. 0: No limit, 1: 400ms - * - * \param [in] dr The current datarate. - * - * \param [in] drOffset The offset to be applied. - * - * \retval newDr The computed datarate. - */ - virtual uint8_t apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + virtual uint8_t apply_DR_offset(int8_t dr, int8_t dr_offset ); private: - uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ); - - // Global attributes /*! * LoRaMAC channels */ - ChannelParams_t Channels[IN865_MAX_NB_CHANNELS]; + channel_params_t channels[IN865_MAX_NB_CHANNELS]; /*! * LoRaMac bands */ - Band_t Bands[IN865_MAX_NB_BANDS]; + band_t bands[IN865_MAX_NB_BANDS]; /*! - * LoRaMac channels mask + * LoRaMac channel mask */ - uint16_t ChannelsMask[IN865_CHANNELS_MASK_SIZE]; + uint16_t channel_mask[IN865_CHANNEL_MASK_SIZE]; /*! - * LoRaMac channels default mask + * LoRaMac default channel mask */ - uint16_t ChannelsDefaultMask[IN865_CHANNELS_MASK_SIZE]; + uint16_t default_channel_mask[IN865_CHANNEL_MASK_SIZE]; }; #endif /* MBED_OS_LORAPHY_IN865_H_ */ diff --git a/features/lorawan/lorastack/phy/LoRaPHYKR920.cpp b/features/lorawan/lorastack/phy/LoRaPHYKR920.cpp index b0229a2e8b..74f67c5cc8 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYKR920.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYKR920.cpp @@ -30,9 +30,7 @@ */ #include "LoRaPHYKR920.h" - #include "lora_phy_ds.h" -#include "LoRaRadio.h" /*! @@ -186,27 +184,27 @@ /*! * Band 0 definition - * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } */ -#define KR920_BAND0 { 1 , KR920_MAX_TX_POWER, 0, 0 } // 100.0 % +static const band_t KR920_BAND0 = { 1 , KR920_MAX_TX_POWER, 0, 0, 0 }; // 100.0 % /*! * LoRaMac default channel 1 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define KR920_LC1 { 922100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +static const channel_params_t KR920_LC1 = { 922100000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }; /*! * LoRaMac default channel 2 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define KR920_LC2 { 922300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +static const channel_params_t KR920_LC2 = { 922300000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }; /*! * LoRaMac default channel 3 * Channel = { Frequency [Hz], RX1 Frequency [Hz], { ( ( DrMax << 4 ) | DrMin ) }, Band } */ -#define KR920_LC3 { 922500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 } +static const channel_params_t KR920_LC3 = { 922500000, 0, { ( ( DR_5 << 4 ) | DR_0 ) }, 0 }; /*! * LoRaMac channels which are allowed for the join procedure @@ -226,1033 +224,282 @@ /*! * Data rates table definition */ -static const uint8_t DataratesKR920[] = { 12, 11, 10, 9, 8, 7 }; +static const uint8_t datarates_KR920[] = { 12, 11, 10, 9, 8, 7 }; /*! * Bandwidths table definition in Hz */ -static const uint32_t BandwidthsKR920[] = { 125000, 125000, 125000, 125000, 125000, 125000 }; +static const uint32_t bandwidths_KR920[] = { 125000, 125000, 125000, 125000, 125000, 125000 }; /*! * Maximum payload with respect to the datarate index. Can operate with and without a repeater. */ -static const uint8_t MaxPayloadOfDatarateKR920[] = { 51, 51, 51, 115, 242, 242 }; +static const uint8_t max_payloads_KR920[] = { 51, 51, 51, 115, 242, 242 }; /*! * Maximum payload with respect to the datarate index. Can operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateRepeaterKR920[] = { 51, 51, 51, 115, 222, 222 }; +static const uint8_t max_payloads_with_repeater_KR920[] = { 51, 51, 51, 115, 222, 222 }; - -// Static functions -static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +LoRaPHYKR920::LoRaPHYKR920(LoRaWANTimeHandler &lora_time) + : LoRaPHY(lora_time) { - uint8_t nextLowerDr = 0; + bands[0] = KR920_BAND0; - if( dr == minDr ) - { - nextLowerDr = minDr; - } - else - { - nextLowerDr = dr - 1; - } - return nextLowerDr; -} + // Channels + channels[0] = KR920_LC1; + channels[1] = KR920_LC2; + channels[2] = KR920_LC3; -static int8_t GetMaxEIRP( uint32_t freq ) -{ - if( freq >= 922100000 ) - {// Limit to 14dBm - return KR920_DEFAULT_MAX_EIRP_HIGH; - } - // Limit to 10dBm - return KR920_DEFAULT_MAX_EIRP_LOW; -} + // Initialize the channels default mask + default_channel_mask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); + // Update the channels mask + copy_channel_mask(channel_mask, default_channel_mask, KR920_CHANNEL_MASK_SIZE); -static uint32_t GetBandwidth( uint32_t drIndex ) -{ - switch( BandwidthsKR920[drIndex] ) - { - default: - case 125000: - return 0; - case 250000: - return 1; - case 500000: - return 2; - } -} + // set default channels + phy_params.channels.channel_list = channels; + phy_params.channels.channel_list_size = KR920_MAX_NB_CHANNELS; + phy_params.channels.mask = channel_mask; + phy_params.channels.default_mask = default_channel_mask; + phy_params.channels.mask_size = KR920_CHANNEL_MASK_SIZE; -static int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) -{ - int8_t txPowerResult = txPower; + // set bands for KR920 spectrum + phy_params.bands.table = (void *) bands; + phy_params.bands.size = KR920_MAX_NB_BANDS; - // Limit tx power to the band max - txPowerResult = MAX( txPower, maxBandTxPower ); + // set bandwidths available in KR920 spectrum + phy_params.bandwidths.table = (void *) bandwidths_KR920; + phy_params.bandwidths.size = 6; - return txPowerResult; -} + // set data rates available in KR920 spectrum + phy_params.datarates.table = (void *) datarates_KR920; + phy_params.datarates.size = 6; -static bool VerifyTxFreq( uint32_t freq, LoRaRadio *radio ) -{ - uint32_t tmpFreq = freq; + // set payload sizes with respect to data rates + phy_params.payloads.table = (void *) max_payloads_KR920; + phy_params.payloads.size = 6; + phy_params.payloads_with_repeater.table = (void *) max_payloads_with_repeater_KR920; + phy_params.payloads.size = 6; - // Check radio driver support - if( radio->check_rf_frequency( tmpFreq ) == false ) - { - return false; - } + // dwell time setting + phy_params.ul_dwell_time_setting = 0; + phy_params.dl_dwell_time_setting = 0; - // Verify if the frequency is valid. The frequency must be in a specified - // range and can be set to specific values. - if( ( tmpFreq >= 920900000 ) && ( tmpFreq <=923300000 ) ) - { - // Range ok, check for specific value - tmpFreq -= 920900000; - if( ( tmpFreq % 200000 ) == 0 ) - { - return true; - } - } - return false; -} - -uint8_t LoRaPHYKR920::CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTransmission = 0; - - for( uint8_t i = 0, k = 0; i < KR920_MAX_NB_CHANNELS; i += 16, k++ ) - { - for( uint8_t j = 0; j < 16; j++ ) - { - if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) - { - if( channels[i + j].Frequency == 0 ) - { // Check if the channel is enabled - continue; - } - if( joined == false ) - { - if( ( KR920_JOIN_CHANNELS & ( 1 << j ) ) == 0 ) - { - continue; - } - } - if( val_in_range( datarate, channels[i + j].DrRange.Fields.Min, - channels[i + j].DrRange.Fields.Max ) == 0 ) - { // Check if the current channel selection supports the given datarate - continue; - } - if( bands[channels[i + j].Band].TimeOff > 0 ) - { // Check if the band is available for transmission - delayTransmission++; - continue; - } - enabledChannels[nbEnabledChannels++] = i + j; - } - } - } - - *delayTx = delayTransmission; - return nbEnabledChannels; -} - -LoRaPHYKR920::LoRaPHYKR920() -{ - const Band_t band0 = KR920_BAND0; - Bands[0] = band0; + // set initial and default parameters + phy_params.duty_cycle_enabled = KR920_DUTY_CYCLE_ENABLED; + phy_params.accept_tx_param_setup_req = false; + phy_params.fsk_supported = false; + phy_params.cflist_supported = true; + phy_params.dl_channel_req_supported = true; + phy_params.custom_channelplans_supported = true; + phy_params.default_channel_cnt = KR920_NUMB_DEFAULT_CHANNELS; + phy_params.max_channel_cnt = KR920_MAX_NB_CHANNELS; + phy_params.cflist_channel_cnt = KR920_NUMB_CHANNELS_CF_LIST; + phy_params.min_tx_datarate = KR920_TX_MIN_DATARATE; + phy_params.max_tx_datarate = KR920_TX_MAX_DATARATE; + phy_params.min_rx_datarate = KR920_RX_MIN_DATARATE; + phy_params.max_rx_datarate = KR920_RX_MAX_DATARATE; + phy_params.default_datarate = KR920_DEFAULT_DATARATE; + phy_params.default_max_datarate = KR920_TX_MAX_DATARATE; + phy_params.min_rx1_dr_offset = KR920_MIN_RX1_DR_OFFSET; + phy_params.max_rx1_dr_offset = KR920_MAX_RX1_DR_OFFSET; + phy_params.default_rx1_dr_offset = KR920_DEFAULT_RX1_DR_OFFSET; + phy_params.min_tx_power = KR920_MIN_TX_POWER; + phy_params.max_tx_power = KR920_MAX_TX_POWER; + phy_params.default_tx_power = KR920_DEFAULT_TX_POWER; + phy_params.default_max_eirp = KR920_DEFAULT_MAX_EIRP_HIGH; + phy_params.default_antenna_gain = KR920_DEFAULT_ANTENNA_GAIN; + phy_params.adr_ack_limit = KR920_ADR_ACK_LIMIT; + phy_params.adr_ack_delay = KR920_ADR_ACK_DELAY; + phy_params.max_rx_window = KR920_MAX_RX_WINDOW; + phy_params.recv_delay1 = KR920_RECEIVE_DELAY1; + phy_params.recv_delay2 = KR920_RECEIVE_DELAY2; + phy_params.join_channel_mask = KR920_JOIN_CHANNELS; + phy_params.join_accept_delay1 = KR920_JOIN_ACCEPT_DELAY1; + phy_params.join_accept_delay2 = KR920_JOIN_ACCEPT_DELAY2; + phy_params.max_fcnt_gap = KR920_MAX_FCNT_GAP; + phy_params.ack_timeout = KR920_ACKTIMEOUT; + phy_params.ack_timeout_rnd = KR920_ACK_TIMEOUT_RND; + phy_params.rx_window2_datarate = KR920_RX_WND_2_DR; + phy_params.rx_window2_frequency = KR920_RX_WND_2_FREQ; } LoRaPHYKR920::~LoRaPHYKR920() { } -PhyParam_t LoRaPHYKR920::get_phy_params(GetPhyParams_t* getPhy) +int8_t LoRaPHYKR920::get_max_eirp(uint32_t freq) { - PhyParam_t phyParam = { 0 }; - - switch( getPhy->Attribute ) - { - case PHY_MIN_RX_DR: - { - phyParam.Value = KR920_RX_MIN_DATARATE; - break; - } - case PHY_MIN_TX_DR: - { - phyParam.Value = KR920_TX_MIN_DATARATE; - break; - } - case PHY_DEF_TX_DR: - { - phyParam.Value = KR920_DEFAULT_DATARATE; - break; - } - case PHY_NEXT_LOWER_TX_DR: - { - phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, KR920_TX_MIN_DATARATE ); - break; - } - case PHY_DEF_TX_POWER: - { - phyParam.Value = KR920_DEFAULT_TX_POWER; - break; - } - case PHY_MAX_PAYLOAD: - { - phyParam.Value = MaxPayloadOfDatarateKR920[getPhy->Datarate]; - break; - } - case PHY_MAX_PAYLOAD_REPEATER: - { - phyParam.Value = MaxPayloadOfDatarateRepeaterKR920[getPhy->Datarate]; - break; - } - case PHY_DUTY_CYCLE: - { - phyParam.Value = KR920_DUTY_CYCLE_ENABLED; - break; - } - case PHY_MAX_RX_WINDOW: - { - phyParam.Value = KR920_MAX_RX_WINDOW; - break; - } - case PHY_RECEIVE_DELAY1: - { - phyParam.Value = KR920_RECEIVE_DELAY1; - break; - } - case PHY_RECEIVE_DELAY2: - { - phyParam.Value = KR920_RECEIVE_DELAY2; - break; - } - case PHY_JOIN_ACCEPT_DELAY1: - { - phyParam.Value = KR920_JOIN_ACCEPT_DELAY1; - break; - } - case PHY_JOIN_ACCEPT_DELAY2: - { - phyParam.Value = KR920_JOIN_ACCEPT_DELAY2; - break; - } - case PHY_MAX_FCNT_GAP: - { - phyParam.Value = KR920_MAX_FCNT_GAP; - break; - } - case PHY_ACK_TIMEOUT: - { - phyParam.Value = ( KR920_ACKTIMEOUT + get_random( -KR920_ACK_TIMEOUT_RND, KR920_ACK_TIMEOUT_RND ) ); - break; - } - case PHY_DEF_DR1_OFFSET: - { - phyParam.Value = KR920_DEFAULT_RX1_DR_OFFSET; - break; - } - case PHY_DEF_RX2_FREQUENCY: - { - phyParam.Value = KR920_RX_WND_2_FREQ; - break; - } - case PHY_DEF_RX2_DR: - { - phyParam.Value = KR920_RX_WND_2_DR; - break; - } - case PHY_CHANNELS_MASK: - { - phyParam.ChannelsMask = ChannelsMask; - break; - } - case PHY_CHANNELS_DEFAULT_MASK: - { - phyParam.ChannelsMask = ChannelsDefaultMask; - break; - } - case PHY_MAX_NB_CHANNELS: - { - phyParam.Value = KR920_MAX_NB_CHANNELS; - break; - } - case PHY_CHANNELS: - { - phyParam.Channels = Channels; - break; - } - case PHY_DEF_UPLINK_DWELL_TIME: - case PHY_DEF_DOWNLINK_DWELL_TIME: - { - phyParam.Value = 0; - break; - } - case PHY_DEF_MAX_EIRP: - { - // We set the higher maximum EIRP as default value. - // The reason for this is, that the frequency may - // change during a channel selection for the next uplink. - // The value has to be recalculated in the TX configuration. - phyParam.fValue = KR920_DEFAULT_MAX_EIRP_HIGH; - break; - } - case PHY_DEF_ANTENNA_GAIN: - { - phyParam.fValue = KR920_DEFAULT_ANTENNA_GAIN; - break; - } - case PHY_NB_JOIN_TRIALS: - case PHY_DEF_NB_JOIN_TRIALS: - { - phyParam.Value = 48; - break; - } - default: - { - break; - } + if (freq >= 922100000) {// Limit to 14dBm + return KR920_DEFAULT_MAX_EIRP_HIGH; } - return phyParam; + // Limit to 10dBm + return KR920_DEFAULT_MAX_EIRP_LOW; } -void LoRaPHYKR920::set_band_tx_done(SetBandTxDoneParams_t* txDone) + +bool LoRaPHYKR920::verify_frequency(uint32_t freq) { - set_last_tx_done( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); -} + uint32_t tmp_freq = freq; -void LoRaPHYKR920::load_defaults(InitType_t type) -{ - switch( type ) - { - case INIT_TYPE_INIT: - { - // Channels - const ChannelParams_t channel1 = KR920_LC1; - const ChannelParams_t channel2 = KR920_LC2; - const ChannelParams_t channel3 = KR920_LC3; - Channels[0] = channel1; - Channels[1] = channel2; - Channels[2] = channel3; + _radio->lock(); - // Initialize the channels default mask - ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 ); - // Update the channels mask - copy_channel_mask( ChannelsMask, ChannelsDefaultMask, 1 ); - break; - } - case INIT_TYPE_RESTORE: - { - // Restore channels default mask - ChannelsMask[0] |= ChannelsDefaultMask[0]; - break; - } - default: - { - break; - } - } -} - -bool LoRaPHYKR920::verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute ) -{ - switch( phyAttribute ) - { - case PHY_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ); - } - case PHY_DEF_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, DR_0, DR_5 ); - } - case PHY_RX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, KR920_RX_MIN_DATARATE, KR920_RX_MAX_DATARATE ); - } - case PHY_DEF_TX_POWER: - case PHY_TX_POWER: - { - // Remark: switched min and max! - return val_in_range( verify->TxPower, KR920_MAX_TX_POWER, KR920_MIN_TX_POWER ); - } - case PHY_DUTY_CYCLE: - { - return KR920_DUTY_CYCLE_ENABLED; - } - case PHY_NB_JOIN_TRIALS: - { - if( verify->NbJoinTrials < 48 ) - { - return false; - } - break; - } - default: - return false; - } - return true; -} - -void LoRaPHYKR920::apply_cf_list(ApplyCFListParams_t* applyCFList) -{ - ChannelParams_t newChannel; - ChannelAddParams_t channelAdd; - ChannelRemoveParams_t channelRemove; - - // Setup default datarate range - newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0; - - // Size of the optional CF list - if( applyCFList->Size != 16 ) - { - return; - } - - // Last byte is RFU, don't take it into account - for( uint8_t i = 0, chanIdx = KR920_NUMB_DEFAULT_CHANNELS; chanIdx < KR920_MAX_NB_CHANNELS; i+=3, chanIdx++ ) - { - if( chanIdx < ( KR920_NUMB_CHANNELS_CF_LIST + KR920_NUMB_DEFAULT_CHANNELS ) ) - { - // Channel frequency - newChannel.Frequency = (uint32_t) applyCFList->Payload[i]; - newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 ); - newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 ); - newChannel.Frequency *= 100; - - // Initialize alternative frequency to 0 - newChannel.Rx1Frequency = 0; - } - else - { - newChannel.Frequency = 0; - newChannel.DrRange.Value = 0; - newChannel.Rx1Frequency = 0; - } - - if( newChannel.Frequency != 0 ) - { - channelAdd.NewChannel = &newChannel; - channelAdd.ChannelId = chanIdx; - - // Try to add all channels - add_channel( &channelAdd ); - } - else - { - channelRemove.ChannelId = chanIdx; - - remove_channel( &channelRemove ); - } - } -} - -bool LoRaPHYKR920::set_channel_mask(ChanMaskSetParams_t* chanMaskSet) -{ - switch( chanMaskSet->ChannelsMaskType ) - { - case CHANNELS_MASK: - { - copy_channel_mask( ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 ); - break; - } - case CHANNELS_DEFAULT_MASK: - { - copy_channel_mask( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 ); - break; - } - default: - return false; - } - return true; -} - -bool LoRaPHYKR920::get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter) -{ - bool adrAckReq = false; - int8_t datarate = adrNext->Datarate; - int8_t txPower = adrNext->TxPower; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - - // Report back the adr ack counter - *adrAckCounter = adrNext->AdrAckCounter; - - if( adrNext->AdrEnabled == true ) - { - if( datarate == KR920_TX_MIN_DATARATE ) - { - *adrAckCounter = 0; - adrAckReq = false; - } - else - { - if( adrNext->AdrAckCounter >= KR920_ADR_ACK_LIMIT ) - { - adrAckReq = true; - txPower = KR920_MAX_TX_POWER; - } - else - { - adrAckReq = false; - } - if( adrNext->AdrAckCounter >= ( KR920_ADR_ACK_LIMIT + KR920_ADR_ACK_DELAY ) ) - { - if( ( adrNext->AdrAckCounter % KR920_ADR_ACK_DELAY ) == 1 ) - { - // Decrease the datarate - getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; - getPhy.Datarate = datarate; - getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; - phyParam = get_phy_params( &getPhy ); - datarate = phyParam.Value; - - if( datarate == KR920_TX_MIN_DATARATE ) - { - // We must set adrAckReq to false as soon as we reach the lowest datarate - adrAckReq = false; - if( adrNext->UpdateChanMask == true ) - { - // Re-enable default channels - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); - } - } - } - } - } - } - - *drOut = datarate; - *txPowOut = txPower; - return adrAckReq; -} - -void LoRaPHYKR920::compute_rx_win_params(int8_t datarate, uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams) -{ - double tSymbol = 0.0; - - // Get the datarate, perform a boundary check - rxConfigParams->Datarate = MIN( datarate, KR920_RX_MAX_DATARATE ); - rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); - - tSymbol = compute_symb_timeout_lora( DataratesKR920[rxConfigParams->Datarate], BandwidthsKR920[rxConfigParams->Datarate] ); - - get_rx_window_params( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); -} - -bool LoRaPHYKR920::rx_config(RxConfigParams_t* rxConfig, int8_t* datarate) -{ - int8_t dr = rxConfig->Datarate; - uint8_t maxPayload = 0; - int8_t phyDr = 0; - uint32_t frequency = rxConfig->Frequency; - - if( _radio->get_status() != RF_IDLE ) - { + if (_radio->check_rf_frequency(tmp_freq) == false) { + _radio->unlock(); return false; } - if( rxConfig->Window == 0 ) - { - // Apply window 1 frequency - frequency = Channels[rxConfig->Channel].Frequency; - // Apply the alternative RX 1 window frequency, if it is available - if( Channels[rxConfig->Channel].Rx1Frequency != 0 ) - { - frequency = Channels[rxConfig->Channel].Rx1Frequency; + _radio->unlock(); + + // Verify if the frequency is valid. The frequency must be in a specified + // range and can be set to specific values. + if ((tmp_freq >= 920900000) && (tmp_freq <=923300000)) { + // Range ok, check for specific value + tmp_freq -= 920900000; + if ((tmp_freq % 200000) == 0) { + return true; } } - // Read the physical datarate from the datarates table - phyDr = DataratesKR920[dr]; - - _radio->set_channel( frequency ); - - // Radio configuration - _radio->set_rx_config( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, - rxConfig->WindowTimeout, false, 0, false, 0, 0, true, - rxConfig->RxContinuous ); - maxPayload = MaxPayloadOfDatarateKR920[dr]; - _radio->set_max_payload_length( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); - - *datarate = (uint8_t) dr; - return true; + return false; } -bool LoRaPHYKR920::tx_config(TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir) +bool LoRaPHYKR920::tx_config(tx_config_params_t* config, int8_t* tx_power, + lorawan_time_t* tx_toa) { - int8_t phyDr = DataratesKR920[txConfig->Datarate]; - int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); - uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); - float maxEIRP = GetMaxEIRP( Channels[txConfig->Channel].Frequency ); - int8_t phyTxPower = 0; + int8_t phy_dr = datarates_KR920[config->datarate]; - // Take the minimum between the maxEIRP and txConfig->MaxEirp. + if (config->tx_power > bands[channels[config->channel].band].max_tx_pwr) { + config->tx_power = bands[channels[config->channel].band].max_tx_pwr; + } + + uint32_t bandwidth = get_bandwidth(config->datarate); + float max_eirp = get_max_eirp(channels[config->channel].frequency); + int8_t phy_tx_power = 0; + + // Take the minimum between the max_eirp and txConfig->MaxEirp. // The value of txConfig->MaxEirp could have changed during runtime, e.g. due to a MAC command. - maxEIRP = MIN( txConfig->MaxEirp, maxEIRP ); + max_eirp = MIN(config->max_eirp, max_eirp); // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, maxEIRP, txConfig->AntennaGain ); + phy_tx_power = compute_tx_power(config->tx_power, max_eirp, config->antenna_gain); // Setup the radio frequency - _radio->set_channel( Channels[txConfig->Channel].Frequency ); + _radio->lock(); - _radio->set_tx_config( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 ); + _radio->set_channel(channels[config->channel].frequency); + + _radio->set_tx_config(MODEM_LORA, phy_tx_power, 0, bandwidth, phy_dr, 1, 8, + false, true, 0, 0, false, 3000 ); // Setup maximum payload lenght of the radio driver - _radio->set_max_payload_length( MODEM_LORA, txConfig->PktLen ); + _radio->set_max_payload_length(MODEM_LORA, config->pkt_len); // Get the time-on-air of the next tx frame - *txTimeOnAir =_radio->time_on_air( MODEM_LORA, txConfig->PktLen ); + *tx_toa =_radio->time_on_air(MODEM_LORA, config->pkt_len); - *txPower = txPowerLimited; + _radio->unlock(); + + *tx_power = config->tx_power; return true; } -uint8_t LoRaPHYKR920::link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, uint8_t* nbBytesParsed) +bool LoRaPHYKR920::set_next_channel(channel_selection_params_t* params, + uint8_t* channel, lorawan_time_t* time, + lorawan_time_t* aggregate_timeoff) { - uint8_t status = 0x07; - RegionCommonLinkAdrParams_t linkAdrParams; - uint8_t nextIndex = 0; - uint8_t bytesProcessed = 0; - uint16_t chMask = 0; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + uint8_t next_channel_idx = 0; + uint8_t nb_enabled_channels = 0; + uint8_t delay_tx = 0; + uint8_t enabled_channels[KR920_MAX_NB_CHANNELS] = {0}; + lorawan_time_t nextTxDelay = 0; - while( bytesProcessed < linkAdrReq->PayloadSize ) - { - // Get ADR request parameters - nextIndex = parse_link_ADR_req( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); - - if( nextIndex == 0 ) - break; // break loop, since no more request has been found - - // Update bytes processed - bytesProcessed += nextIndex; - - // Revert status, as we only check the last ADR request for the channel mask KO - status = 0x07; - - // Setup temporary channels mask - chMask = linkAdrParams.ChMask; - - // Verify channels mask - if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) ) - { - status &= 0xFE; // Channel mask KO - } - else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) || - ( linkAdrParams.ChMaskCtrl >= 7 ) ) - { - // RFU - status &= 0xFE; // Channel mask KO - } - else - { - for( uint8_t i = 0; i < KR920_MAX_NB_CHANNELS; i++ ) - { - if( linkAdrParams.ChMaskCtrl == 6 ) - { - if( Channels[i].Frequency != 0 ) - { - chMask |= 1 << i; - } - } - else - { - if( ( ( chMask & ( 1 << i ) ) != 0 ) && - ( Channels[i].Frequency == 0 ) ) - {// Trying to enable an undefined channel - status &= 0xFE; // Channel mask KO - } - } - } - } + if (num_active_channels(channel_mask, 0, 1) == 0) { + // Reactivate default channels + channel_mask[0] |= LC(1) + LC(2) + LC(3); } - // Get the minimum possible datarate - getPhy.Attribute = PHY_MIN_TX_DR; - getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; - phyParam = get_phy_params( &getPhy ); - - linkAdrVerifyParams.Status = status; - linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; - linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; - linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; - linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; - linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; - linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; - linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; - linkAdrVerifyParams.NbChannels = KR920_MAX_NB_CHANNELS; - linkAdrVerifyParams.ChannelsMask = &chMask; - linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; - linkAdrVerifyParams.MaxDatarate = KR920_TX_MAX_DATARATE; - linkAdrVerifyParams.Channels = Channels; - linkAdrVerifyParams.MinTxPower = KR920_MIN_TX_POWER; - linkAdrVerifyParams.MaxTxPower = KR920_MAX_TX_POWER; - - // Verify the parameters and update, if necessary - status = verify_link_ADR_req( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); - - // Update channelsMask if everything is correct - if( status == 0x07 ) - { - // Set the channels mask to a default value - memset( ChannelsMask, 0, sizeof( ChannelsMask ) ); - // Update the channels mask - ChannelsMask[0] = chMask; - } - - // Update status variables - *drOut = linkAdrParams.Datarate; - *txPowOut = linkAdrParams.TxPower; - *nbRepOut = linkAdrParams.NbRep; - *nbBytesParsed = bytesProcessed; - - return status; -} - -uint8_t LoRaPHYKR920::setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq) -{ - uint8_t status = 0x07; - - // Verify radio frequency - if(_radio->check_rf_frequency( rxParamSetupReq->Frequency ) == false ) - { - status &= 0xFE; // Channel frequency KO - } - - // Verify datarate - if( val_in_range( rxParamSetupReq->Datarate, KR920_RX_MIN_DATARATE, KR920_RX_MAX_DATARATE ) == 0 ) - { - status &= 0xFD; // Datarate KO - } - - // Verify datarate offset - if( val_in_range( rxParamSetupReq->DrOffset, KR920_MIN_RX1_DR_OFFSET, KR920_MAX_RX1_DR_OFFSET ) == 0 ) - { - status &= 0xFB; // Rx1DrOffset range KO - } - - return status; -} - -uint8_t LoRaPHYKR920::request_new_channel(NewChannelReqParams_t* newChannelReq) -{ - uint8_t status = 0x03; - ChannelAddParams_t channelAdd; - ChannelRemoveParams_t channelRemove; - - if( newChannelReq->NewChannel->Frequency == 0 ) - { - channelRemove.ChannelId = newChannelReq->ChannelId; - - // Remove - if( remove_channel( &channelRemove ) == false ) - { - status &= 0xFC; - } - } - else - { - channelAdd.NewChannel = newChannelReq->NewChannel; - channelAdd.ChannelId = newChannelReq->ChannelId; - - switch( add_channel( &channelAdd ) ) - { - case LORAMAC_STATUS_OK: - { - break; - } - case LORAMAC_STATUS_FREQUENCY_INVALID: - { - status &= 0xFE; - break; - } - case LORAMAC_STATUS_DATARATE_INVALID: - { - status &= 0xFD; - break; - } - case LORAMAC_STATUS_FREQ_AND_DR_INVALID: - { - status &= 0xFC; - break; - } - default: - { - status &= 0xFC; - break; - } - } - } - - return status; -} - -int8_t LoRaPHYKR920::setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq) -{ - return -1; -} - -uint8_t LoRaPHYKR920::dl_channel_request(DlChannelReqParams_t* dlChannelReq) -{ - uint8_t status = 0x03; - - // Verify if the frequency is supported - if( VerifyTxFreq( dlChannelReq->Rx1Frequency, _radio ) == false ) - { - status &= 0xFE; - } - - // Verify if an uplink frequency exists - if( Channels[dlChannelReq->ChannelId].Frequency == 0 ) - { - status &= 0xFD; - } - - // Apply Rx1 frequency, if the status is OK - if( status == 0x03 ) - { - Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency; - } - - return status; -} - -int8_t LoRaPHYKR920::get_alternate_DR(AlternateDrParams_t* alternateDr) -{ - int8_t datarate = 0; - - if( ( alternateDr->NbTrials % 48 ) == 0 ) - { - datarate = DR_0; - } - else if( ( alternateDr->NbTrials % 32 ) == 0 ) - { - datarate = DR_1; - } - else if( ( alternateDr->NbTrials % 24 ) == 0 ) - { - datarate = DR_2; - } - else if( ( alternateDr->NbTrials % 16 ) == 0 ) - { - datarate = DR_3; - } - else if( ( alternateDr->NbTrials % 8 ) == 0 ) - { - datarate = DR_4; - } - else - { - datarate = DR_5; - } - return datarate; -} - -void LoRaPHYKR920::calculate_backoff(CalcBackOffParams_t* calcBackOff) -{ - RegionCommonCalcBackOffParams_t calcBackOffParams; - - calcBackOffParams.Channels = Channels; - calcBackOffParams.Bands = Bands; - calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; - calcBackOffParams.Joined = calcBackOff->Joined; - calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; - calcBackOffParams.Channel = calcBackOff->Channel; - calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; - calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; - - get_DC_backoff( &calcBackOffParams ); -} - -bool LoRaPHYKR920::set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff) -{ - uint8_t channelNext = 0; - uint8_t nbEnabledChannels = 0; - uint8_t delayTx = 0; - uint8_t enabledChannels[KR920_MAX_NB_CHANNELS] = { 0 }; - TimerTime_t nextTxDelay = 0; - - if( num_active_channels( ChannelsMask, 0, 1 ) == 0 ) - { // Reactivate default channels - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); - } - - if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) - { + if (params->aggregate_timeoff <= _lora_time.get_elapsed_time(params->last_aggregate_tx_time)) { // Reset Aggregated time off - *aggregatedTimeOff = 0; + *aggregate_timeoff = 0; // Update bands Time OFF - nextTxDelay = update_band_timeoff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, KR920_MAX_NB_BANDS ); + nextTxDelay = update_band_timeoff(params->joined, params->dc_enabled, + bands, KR920_MAX_NB_BANDS); // Search how many channels are enabled - nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Joined, nextChanParams->Datarate, - ChannelsMask, Channels, - Bands, enabledChannels, &delayTx ); - } - else - { - delayTx++; - nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); + nb_enabled_channels = enabled_channel_count(params->joined, params->current_datarate, + channel_mask, + enabled_channels, &delay_tx); + } else { + delay_tx++; + nextTxDelay = params->aggregate_timeoff - _lora_time.get_elapsed_time(params->last_aggregate_tx_time); } - if( nbEnabledChannels > 0 ) - { - for( uint8_t i = 0, j = get_random( 0, nbEnabledChannels - 1 ); i < KR920_MAX_NB_CHANNELS; i++ ) - { - channelNext = enabledChannels[j]; - j = ( j + 1 ) % nbEnabledChannels; + if (nb_enabled_channels > 0) { + + for (uint8_t i = 0, j = get_random(0, nb_enabled_channels - 1); + i < KR920_MAX_NB_CHANNELS; i++) { + next_channel_idx = enabled_channels[j]; + j = ( j + 1 ) % nb_enabled_channels; // Perform carrier sense for KR920_CARRIER_SENSE_TIME // If the channel is free, we can stop the LBT mechanism - if( _radio->perform_carrier_sense( MODEM_LORA, - Channels[channelNext].Frequency, - KR920_RSSI_FREE_TH, - KR920_CARRIER_SENSE_TIME ) == true ) - { + _radio->lock(); + + if (_radio->perform_carrier_sense(MODEM_LORA, + channels[next_channel_idx].frequency, + KR920_RSSI_FREE_TH, + KR920_CARRIER_SENSE_TIME ) == true) { // Free channel found - *channel = channelNext; + *channel = next_channel_idx; *time = 0; + _radio->unlock(); return true; } + + _radio->unlock(); } + return false; - } - else - { - if( delayTx > 0 ) - { + + } else { + + if (delay_tx > 0) { // Delay transmission due to AggregatedTimeOff or to a band time off *time = nextTxDelay; return true; } + // Datarate not supported by any channel, restore defaults - ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 ); + channel_mask[0] |= LC(1) + LC(2) + LC(3); *time = 0; return false; } } -LoRaMacStatus_t LoRaPHYKR920::add_channel(ChannelAddParams_t* channelAdd) +void LoRaPHYKR920::set_tx_cont_mode(cw_mode_params_t* params, uint32_t given_frequency) { - uint8_t band = 0; - bool drInvalid = false; - bool freqInvalid = false; - uint8_t id = channelAdd->ChannelId; + (void)given_frequency; - if( id >= KR920_MAX_NB_CHANNELS ) - { - return LORAMAC_STATUS_PARAMETER_INVALID; + if (params->tx_power > bands[channels[params->channel].band].max_tx_pwr) { + params->tx_power = bands[channels[params->channel].band].max_tx_pwr; } - // Validate the datarate range - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Min, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - if( val_in_range( channelAdd->NewChannel->DrRange.Fields.Max, KR920_TX_MIN_DATARATE, KR920_TX_MAX_DATARATE ) == 0 ) - { - drInvalid = true; - } - if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max ) - { - drInvalid = true; - } + float max_eirp = get_max_eirp(channels[params->channel].frequency); + int8_t phy_tx_power = 0; + uint32_t frequency = channels[params->channel].frequency; - // Default channels don't accept all values - if( id < KR920_NUMB_DEFAULT_CHANNELS ) - { - // All datarates are supported - // We are not allowed to change the frequency - if( channelAdd->NewChannel->Frequency != Channels[id].Frequency ) - { - freqInvalid = true; - } - } - - // Check frequency - if( freqInvalid == false ) - { - if( VerifyTxFreq( channelAdd->NewChannel->Frequency, _radio ) == false ) - { - freqInvalid = true; - } - } - - // Check status - if( ( drInvalid == true ) && ( freqInvalid == true ) ) - { - return LORAMAC_STATUS_FREQ_AND_DR_INVALID; - } - if( drInvalid == true ) - { - return LORAMAC_STATUS_DATARATE_INVALID; - } - if( freqInvalid == true ) - { - return LORAMAC_STATUS_FREQUENCY_INVALID; - } - - memcpy( &(Channels[id]), channelAdd->NewChannel, sizeof( Channels[id] ) ); - Channels[id].Band = band; - ChannelsMask[0] |= ( 1 << id ); - return LORAMAC_STATUS_OK; -} - -bool LoRaPHYKR920::remove_channel(ChannelRemoveParams_t* channelRemove) -{ - uint8_t id = channelRemove->ChannelId; - - if( id < KR920_NUMB_DEFAULT_CHANNELS ) - { - return false; - } - - // Remove the channel from the list of channels - const ChannelParams_t empty_channel = { 0, 0, { 0 }, 0 }; - Channels[id] = empty_channel; - - return disable_channel( ChannelsMask, id, KR920_MAX_NB_CHANNELS ); -} - -void LoRaPHYKR920::set_tx_cont_mode(ContinuousWaveParams_t* continuousWave) -{ - int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); - float maxEIRP = GetMaxEIRP( Channels[continuousWave->Channel].Frequency ); - int8_t phyTxPower = 0; - uint32_t frequency = Channels[continuousWave->Channel].Frequency; - - // Take the minimum between the maxEIRP and continuousWave->MaxEirp. - // The value of continuousWave->MaxEirp could have changed during runtime, e.g. due to a MAC command. - maxEIRP = MIN( continuousWave->MaxEirp, maxEIRP ); + // Take the minimum between the max_eirp and params->max_eirp. + // The value of params->max_eirp could have changed during runtime, + // e.g. due to a MAC command. + max_eirp = MIN (params->max_eirp, max_eirp); // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, maxEIRP, continuousWave->AntennaGain ); + phy_tx_power = compute_tx_power(params->tx_power, max_eirp, params->antenna_gain); - _radio->set_tx_continuous_wave( frequency, phyTxPower, continuousWave->Timeout ); + _radio->lock(); + _radio->set_tx_continuous_wave(frequency, phy_tx_power, params->timeout); + _radio->unlock(); } -uint8_t LoRaPHYKR920::apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset) -{ - int8_t datarate = dr - drOffset; - - if( datarate < 0 ) - { - datarate = DR_0; - } - return datarate; -} diff --git a/features/lorawan/lorastack/phy/LoRaPHYKR920.h b/features/lorawan/lorastack/phy/LoRaPHYKR920.h index 329bbd1511..b95c902a14 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYKR920.h +++ b/features/lorawan/lorastack/phy/LoRaPHYKR920.h @@ -33,7 +33,6 @@ #define MBED_OS_LORAPHY_KR920_H_ #include "LoRaPHY.h" -#include "netsocket/LoRaRadio.h" /*! * LoRaMac maximum number of channels @@ -45,314 +44,52 @@ */ #define KR920_MAX_NB_BANDS 1 -#define KR920_CHANNELS_MASK_SIZE 1 +#define KR920_CHANNEL_MASK_SIZE 1 class LoRaPHYKR920 : public LoRaPHY { public: - LoRaPHYKR920(); + LoRaPHYKR920(LoRaWANTimeHandler &lora_time); virtual ~LoRaPHYKR920(); - /*! - * \brief The function gets a value of a specific PHY attribute. - * - * \param [in] getPhy A pointer to the function parameters. - * - * \retval The structure containing the PHY parameter. - */ - virtual PhyParam_t get_phy_params(GetPhyParams_t* getPhy ); + virtual bool verify_frequency(uint32_t freq); - /*! - * \brief Updates the last TX done parameters of the current channel. - * - * \param [in] txDone A pointer to the function parameters. - */ - virtual void set_band_tx_done(SetBandTxDoneParams_t* txDone ); + virtual bool tx_config(tx_config_params_t* config, int8_t* tx_power, + lorawan_time_t* tx_toa); - /*! - * \brief Initializes the channels masks and the channels. - * - * \param [in] type Sets the initialization type. - */ - virtual void load_defaults(InitType_t type ); + virtual bool set_next_channel(channel_selection_params_t* params, uint8_t* channel, + lorawan_time_t* time, + lorawan_time_t* aggregate_timeOff); - /*! - * \brief Verifies a parameter. - * - * \param [in] verify A pointer to the function parameters. - * - * \param [in] phyAttribute The attribute to verify. - * - * \retval True, if the parameter is valid. - */ - virtual bool verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + virtual void set_tx_cont_mode(cw_mode_params_t* continuousWave, + uint32_t frequency = 0); - /*! - * \brief The function parses the input buffer and sets up the channels of the CF list. - * - * \param [in] applyCFList A pointer to the function parameters. - */ - virtual void apply_cf_list(ApplyCFListParams_t* applyCFList ); - /*! - * \brief Sets a channels mask. - * - * \param [in] chanMaskSet A pointer to the function parameters. - * - * \retval True, if the channels mask could be set. - */ - virtual bool set_channel_mask(ChanMaskSetParams_t* chanMaskSet ); +private: - /*! - * \brief Calculates the next datarate to set, when ADR is on or off. - * - * \param [in] adrNext A pointer to the function parameters. - * - * \param [out] drOut The calculated datarate for the next TX. - * - * \param [out] txPowOut The TX power for the next TX. - * - * \param [out] adrAckCounter The calculated ADR acknowledgement counter. - * - * \retval True, if an ADR request should be performed. - */ - virtual bool get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter ); + int8_t get_max_eirp(uint32_t freq); - /*! - * \brief Configuration of the RX windows. - * - * \param [in] rxConfig A pointer to the function parameters. - * - * \param [out] datarate The datarate index set. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool rx_config(RxConfigParams_t* rxConfig, int8_t* datarate ); - - /* - * RX window precise timing - * - * For more details, please consult the following document, chapter 3.1.2. - * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf - * or - * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf - * - * Downlink start: T = Tx + 1s (+/- 20 us) - * | - * TRxEarly | TRxLate - * | | | - * | | +---+---+---+---+---+---+---+---+ - * | | | Latest Rx window | - * | | +---+---+---+---+---+---+---+---+ - * | | | - * +---+---+---+---+---+---+---+---+ - * | Earliest Rx window | - * +---+---+---+---+---+---+---+---+ - * | - * +---+---+---+---+---+---+---+---+ - *Downlink preamble 8 symbols | | | | | | | | | - * +---+---+---+---+---+---+---+---+ - * - * Worst case Rx window timings - * - * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME - * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME - * - * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * - * RxOffset = ( TRxLate + TRxEarly ) / 2 - * - * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME - * - * The minimum value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol - */ - /*! - * Computes the RX window timeout and offset. - * - * \param [in] datarate The RX window datarate index to be used. - * - * \param [in] minRxSymbols The minimum number of symbols required to detect an RX frame. - * - * \param [in] rxError The system maximum timing error of the receiver in milliseconds. - * The receiver will turn on in a [-rxError : +rxError] ms - * interval around RxOffset. - * - * \param [out] rxConfigParams Returns the updated WindowTimeout and WindowOffset fields. - */ - virtual void compute_rx_win_params(int8_t datarate, - uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams); - - /*! - * \brief TX configuration. - * - * \param [in] txConfig A pointer to the function parameters. - * - * \param [out] txPower The TX power index set. - * - * \param [out] txTimeOnAir The time-on-air of the frame. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir ); - - /*! - * \brief The function processes a Link ADR request. - * - * \param [in] linkAdrReq A pointer to the function parameters. - * - * \param [out] drOut The datarate applied. - * - * \param [out] txPowOut The TX power applied. - * - * \param [out] nbRepOut The number of repetitions to apply. - * - * \param [out] nbBytesParsed The number of bytes parsed. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, - uint8_t* nbBytesParsed ); - - /*! - * \brief The function processes a RX parameter setup request. - * - * \param [in] rxParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq ); - - /*! - * \brief The function processes a new channel request. - * - * \param [in] newChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t request_new_channel(NewChannelReqParams_t* newChannelReq ); - - /*! - * \brief The function processes a TX ParamSetup request. - * - * \param [in] txParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - * Returns -1, if the functionality is not implemented. In this case, the end node - * shall ignore the command. - */ - virtual int8_t setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq ); - - /*! - * \brief The function processes a DlChannel request. - * - * \param [in] dlChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t dl_channel_request(DlChannelReqParams_t* dlChannelReq ); - - /*! - * \brief Alternates the datarate of the channel for the join request. - * - * \param [in] alternateDr A pointer to the function parameters. - * - * \retval The datarate to apply. - */ - virtual int8_t get_alternate_DR(AlternateDrParams_t* alternateDr ); - - /*! - * \brief Calculates the back-off time. - * - * \param [in] calcBackOff A pointer to the function parameters. - */ - virtual void calculate_backoff(CalcBackOffParams_t* calcBackOff ); - - /*! - * \brief Searches and sets the next random available channel. - * - * \param [in] nextChanParams The parameters for the next channel. - * - * \param [out] channel The next channel to use for TX. - * - * \param [out] time The time to wait for the next transmission according to the duty cycle. - * - * \param [out] aggregatedTimeOff Updates the aggregated time off. - * - * \retval The function status [1: OK, 0: Unable to find a channel on the current datarate]. - */ - virtual bool set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff ); - - /*! - * \brief Adds a channel. - * - * \param [in] channelAdd A pointer to the function parameters. - * - * \retval The status of the operation. - */ - virtual LoRaMacStatus_t add_channel(ChannelAddParams_t* channelAdd ); - - /*! - * \brief Removes a channel. - * - * \param [in] channelRemove A pointer to the function parameters. - * - * \retval True, if the channel was removed successfully. - */ - virtual bool remove_channel(ChannelRemoveParams_t* channelRemove ); - - /*! - * \brief Sets the radio into continuous wave mode. - * - * \param [in] continuousWave A pointer to the function parameters. - */ - virtual void set_tx_cont_mode(ContinuousWaveParams_t* continuousWave ); - - /*! - * \brief Computes new datarate according to the given offset. - * - * \param [in] downlinkDwellTime The downlink dwell time configuration. 0: No limit, 1: 400ms - * - * \param [in] dr The current datarate. - * - * \param [in] drOffset The offset to be applied. - * - * \retval newDr The computed datarate. - */ - virtual uint8_t apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); - - uint8_t CountNbOfEnabledChannels( bool joined, uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ); - - // Global attributes - /*! + /** * LoRaMAC channels */ - ChannelParams_t Channels[KR920_MAX_NB_CHANNELS]; + channel_params_t channels[KR920_MAX_NB_CHANNELS]; - /*! + /** * LoRaMac bands */ - Band_t Bands[KR920_MAX_NB_BANDS]; + band_t bands[KR920_MAX_NB_BANDS]; - /*! - * LoRaMac channels mask + /** + * LoRaMac channel mask */ - uint16_t ChannelsMask[KR920_CHANNELS_MASK_SIZE]; + uint16_t channel_mask[KR920_CHANNEL_MASK_SIZE]; - /*! - * LoRaMac channels default mask + /** + * LoRaMac default channel mask */ - uint16_t ChannelsDefaultMask[KR920_CHANNELS_MASK_SIZE]; + uint16_t default_channel_mask[KR920_CHANNEL_MASK_SIZE]; }; #endif // MBED_OS_LORAPHY_KR920_H_ diff --git a/features/lorawan/lorastack/phy/LoRaPHYUS915.cpp b/features/lorawan/lorastack/phy/LoRaPHYUS915.cpp index 145d079de1..b89d3550eb 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYUS915.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYUS915.cpp @@ -31,7 +31,6 @@ #include "LoRaPHYUS915.h" #include "lora_phy_ds.h" -#include "LoRaRadio.h" /*! @@ -161,9 +160,9 @@ /*! * Band 0 definition - * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } */ -#define US915_BAND0 { 1, US915_MAX_TX_POWER, 0, 0 } // 100.0 % +static const band_t US915_BAND0 = { 1, US915_MAX_TX_POWER, 0, 0, 0 }; // 100.0 % /*! * Defines the first channel for RX window 1 for US band @@ -183,17 +182,17 @@ /*! * Data rates table definition */ -static const uint8_t DataratesUS915[] = { 10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0 }; +static const uint8_t datarates_US915[] = {10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0}; /*! * Bandwidths table definition in Hz */ -static const uint32_t BandwidthsUS915[] = { 125000, 125000, 125000, 125000, 500000, 0, 0, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 }; +static const uint32_t bandwidths_US915[] = {125000, 125000, 125000, 125000, 500000, 0, 0, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0}; /*! * Up/Down link data rates offset definition */ -static const int8_t DatarateOffsetsUS915[5][4] = +static const int8_t datarate_offsets_US915[5][4] = { { DR_10, DR_9 , DR_8 , DR_8 }, // DR_0 { DR_11, DR_10, DR_9 , DR_8 }, // DR_1 @@ -205,680 +204,381 @@ static const int8_t DatarateOffsetsUS915[5][4] = /*! * Maximum payload with respect to the datarate index. Cannot operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateUS915[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 }; +static const uint8_t max_payloads_US915[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 }; /*! * Maximum payload with respect to the datarate index. Can operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateRepeaterUS915[] = { 11, 53, 125, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0 }; +static const uint8_t max_payloads_with_repeater_US915[] = {11, 53, 125, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0}; - -// Static functions -static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +LoRaPHYUS915::LoRaPHYUS915(LoRaWANTimeHandler &lora_time) + : LoRaPHY(lora_time) { - uint8_t nextLowerDr = 0; + bands[0] = US915_BAND0; - if( dr == minDr ) - { - nextLowerDr = minDr; + // Channels + // 125 kHz channels + for (uint8_t i = 0; i < US915_MAX_NB_CHANNELS - 8; i++) { + channels[i].frequency = 902300000 + i * 200000; + channels[i].dr_range.value = ( DR_3 << 4) | DR_0; + channels[i].band = 0; } - else - { - nextLowerDr = dr - 1; - } - return nextLowerDr; -} - -static uint32_t GetBandwidth( uint32_t drIndex ) -{ - switch( BandwidthsUS915[drIndex] ) - { - default: - case 125000: - return 0; - case 250000: - return 1; - case 500000: - return 2; - } -} - -int8_t LoRaPHYUS915::LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) -{ - int8_t txPowerResult = txPower; - - // Limit tx power to the band max - txPowerResult = MAX( txPower, maxBandTxPower ); - - if( datarate == DR_4 ) - {// Limit tx power to max 26dBm - txPowerResult = MAX( txPower, TX_POWER_2 ); - } - else - { - if( num_active_channels( channelsMask, 0, 4 ) < 50 ) - {// Limit tx power to max 21dBm - txPowerResult = MAX( txPower, TX_POWER_5 ); - } - } - return txPowerResult; -} - -uint8_t LoRaPHYUS915::CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTransmission = 0; - - for( uint8_t i = 0, k = 0; i < US915_MAX_NB_CHANNELS; i += 16, k++ ) - { - for( uint8_t j = 0; j < 16; j++ ) - { - if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) - { - if( channels[i + j].Frequency == 0 ) - { // Check if the channel is enabled - continue; - } - if( val_in_range( datarate, channels[i + j].DrRange.Fields.Min, - channels[i + j].DrRange.Fields.Max ) == 0 ) - { // Check if the current channel selection supports the given datarate - continue; - } - if( bands[channels[i + j].Band].TimeOff > 0 ) - { // Check if the band is available for transmission - delayTransmission++; - continue; - } - enabledChannels[nbEnabledChannels++] = i + j; - } - } + // 500 kHz channels + for (uint8_t i = US915_MAX_NB_CHANNELS - 8; i < US915_MAX_NB_CHANNELS; i++) { + channels[i].frequency = 903000000 + (i - ( US915_MAX_NB_CHANNELS - 8)) * 1600000; + channels[i].dr_range.value = ( DR_4 << 4) | DR_4; + channels[i].band = 0; } - *delayTx = delayTransmission; - return nbEnabledChannels; -} + // ChannelsMask + default_channel_mask[0] = 0xFFFF; + default_channel_mask[1] = 0xFFFF; + default_channel_mask[2] = 0xFFFF; + default_channel_mask[3] = 0xFFFF; + default_channel_mask[4] = 0x00FF; -LoRaPHYUS915::LoRaPHYUS915() -{ - const Band_t band0 = US915_BAND0; - Bands[0] = band0; + memset(channel_mask, 0, sizeof(channel_mask)); + memset(current_channel_mask, 0, sizeof(current_channel_mask)); + + // Copy channels default mask + copy_channel_mask(channel_mask, default_channel_mask, US915_CHANNEL_MASK_SIZE); + + // current channel masks keep track what of the + // channels previously used, i.e., which channels should be avoided in + // next transmission + copy_channel_mask(current_channel_mask, channel_mask, US915_CHANNEL_MASK_SIZE); + + // set default channels + phy_params.channels.channel_list = channels; + phy_params.channels.channel_list_size = US915_MAX_NB_CHANNELS; + phy_params.channels.mask = channel_mask; + phy_params.channels.default_mask = default_channel_mask; + phy_params.channels.mask_size = US915_CHANNEL_MASK_SIZE; + + // set bands for US915 spectrum + phy_params.bands.table = (void *) bands; + phy_params.bands.size = US915_MAX_NB_BANDS; + + // set bandwidths available in US915 spectrum + phy_params.bandwidths.table = (void *) bandwidths_US915; + phy_params.bandwidths.size = 16; + + // set data rates available in US915 spectrum + phy_params.datarates.table = (void *) datarates_US915; + phy_params.datarates.size = 16; + + // set payload sizes with respect to data rates + phy_params.payloads.table = (void *) max_payloads_US915; + phy_params.payloads.size = 16; + phy_params.payloads_with_repeater.table = (void *) max_payloads_with_repeater_US915; + phy_params.payloads.size = 16; + + // dwell time setting + phy_params.ul_dwell_time_setting = 0; + phy_params.dl_dwell_time_setting = 0; + + // set initial and default parameters + phy_params.duty_cycle_enabled = US915_DUTY_CYCLE_ENABLED; + phy_params.accept_tx_param_setup_req = false; + phy_params.fsk_supported = false; + phy_params.cflist_supported = false; + phy_params.dl_channel_req_supported = false; + phy_params.custom_channelplans_supported = false; + phy_params.default_channel_cnt = US915_MAX_NB_CHANNELS; + phy_params.max_channel_cnt = US915_MAX_NB_CHANNELS; + phy_params.cflist_channel_cnt = 0; + phy_params.min_tx_datarate = US915_TX_MIN_DATARATE; + phy_params.max_tx_datarate = US915_TX_MAX_DATARATE; + phy_params.min_rx_datarate = US915_RX_MIN_DATARATE; + phy_params.max_rx_datarate = US915_RX_MAX_DATARATE; + phy_params.default_datarate = US915_DEFAULT_DATARATE; + phy_params.default_max_datarate = US915_TX_MAX_DATARATE; + phy_params.min_rx1_dr_offset = US915_MIN_RX1_DR_OFFSET; + phy_params.max_rx1_dr_offset = US915_MAX_RX1_DR_OFFSET; + phy_params.default_rx1_dr_offset = US915_DEFAULT_RX1_DR_OFFSET; + phy_params.min_tx_power = US915_MIN_TX_POWER; + phy_params.max_tx_power = US915_MAX_TX_POWER; + phy_params.default_tx_power = US915_DEFAULT_TX_POWER; + phy_params.default_max_eirp = 0; + phy_params.default_antenna_gain = 0; + phy_params.adr_ack_limit = US915_ADR_ACK_LIMIT; + phy_params.adr_ack_delay = US915_ADR_ACK_DELAY; + phy_params.max_rx_window = US915_MAX_RX_WINDOW; + phy_params.recv_delay1 = US915_RECEIVE_DELAY1; + phy_params.recv_delay2 = US915_RECEIVE_DELAY2; + + phy_params.join_accept_delay1 = US915_JOIN_ACCEPT_DELAY1; + phy_params.join_accept_delay2 = US915_JOIN_ACCEPT_DELAY2; + phy_params.max_fcnt_gap = US915_MAX_FCNT_GAP; + phy_params.ack_timeout = US915_ACKTIMEOUT; + phy_params.ack_timeout_rnd = US915_ACK_TIMEOUT_RND; + phy_params.rx_window2_datarate = US915_RX_WND_2_DR; + phy_params.rx_window2_frequency = US915_RX_WND_2_FREQ; } LoRaPHYUS915::~LoRaPHYUS915() { } -PhyParam_t LoRaPHYUS915::get_phy_params(GetPhyParams_t* getPhy) +int8_t LoRaPHYUS915::limit_tx_power(int8_t tx_power, int8_t max_band_tx_power, + int8_t datarate) { - PhyParam_t phyParam = { 0 }; + int8_t tx_power_out = tx_power; - switch( getPhy->Attribute ) - { - case PHY_MIN_RX_DR: - { - phyParam.Value = US915_RX_MIN_DATARATE; - break; - } - case PHY_MIN_TX_DR: - { - phyParam.Value = US915_TX_MIN_DATARATE; - break; - } - case PHY_DEF_TX_DR: - { - phyParam.Value = US915_DEFAULT_DATARATE; - break; - } - case PHY_NEXT_LOWER_TX_DR: - { - phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, US915_TX_MIN_DATARATE ); - break; - } - case PHY_DEF_TX_POWER: - { - phyParam.Value = US915_DEFAULT_TX_POWER; - break; - } - case PHY_MAX_PAYLOAD: - { - phyParam.Value = MaxPayloadOfDatarateUS915[getPhy->Datarate]; - break; - } - case PHY_MAX_PAYLOAD_REPEATER: - { - phyParam.Value = MaxPayloadOfDatarateRepeaterUS915[getPhy->Datarate]; - break; - } - case PHY_DUTY_CYCLE: - { - phyParam.Value = US915_DUTY_CYCLE_ENABLED; - break; - } - case PHY_MAX_RX_WINDOW: - { - phyParam.Value = US915_MAX_RX_WINDOW; - break; - } - case PHY_RECEIVE_DELAY1: - { - phyParam.Value = US915_RECEIVE_DELAY1; - break; - } - case PHY_RECEIVE_DELAY2: - { - phyParam.Value = US915_RECEIVE_DELAY2; - break; - } - case PHY_JOIN_ACCEPT_DELAY1: - { - phyParam.Value = US915_JOIN_ACCEPT_DELAY1; - break; - } - case PHY_JOIN_ACCEPT_DELAY2: - { - phyParam.Value = US915_JOIN_ACCEPT_DELAY2; - break; - } - case PHY_MAX_FCNT_GAP: - { - phyParam.Value = US915_MAX_FCNT_GAP; - break; - } - case PHY_ACK_TIMEOUT: - { - phyParam.Value = ( US915_ACKTIMEOUT + get_random( -US915_ACK_TIMEOUT_RND, US915_ACK_TIMEOUT_RND ) ); - break; - } - case PHY_DEF_DR1_OFFSET: - { - phyParam.Value = US915_DEFAULT_RX1_DR_OFFSET; - break; - } - case PHY_DEF_RX2_FREQUENCY: - { - phyParam.Value = US915_RX_WND_2_FREQ; - break; - } - case PHY_DEF_RX2_DR: - { - phyParam.Value = US915_RX_WND_2_DR; - break; - } - case PHY_CHANNELS_MASK: - { - phyParam.ChannelsMask = ChannelsMask; - break; - } - case PHY_CHANNELS_DEFAULT_MASK: - { - phyParam.ChannelsMask = ChannelsDefaultMask; - break; - } - case PHY_MAX_NB_CHANNELS: - { - phyParam.Value = US915_MAX_NB_CHANNELS; - break; - } - case PHY_CHANNELS: - { - phyParam.Channels = Channels; - break; - } - case PHY_DEF_UPLINK_DWELL_TIME: - case PHY_DEF_DOWNLINK_DWELL_TIME: - { - phyParam.Value = 0; - break; - } - case PHY_DEF_MAX_EIRP: - case PHY_DEF_ANTENNA_GAIN: - { - phyParam.fValue = 0; - break; - } - case PHY_NB_JOIN_TRIALS: - case PHY_DEF_NB_JOIN_TRIALS: - { - phyParam.Value = 2; - break; - } - default: - { - break; + // Limit tx power to the band max + tx_power_out = MAX (tx_power, max_band_tx_power); + + if (datarate == DR_4) { + // Limit tx power to max 26dBm + tx_power_out = MAX (tx_power, TX_POWER_2); + } else { + + if (num_active_channels(channel_mask, 0, 4) < 50) { + // Limit tx power to max 21dBm + tx_power_out = MAX (tx_power, TX_POWER_5); } } - return phyParam; + return tx_power_out; } -void LoRaPHYUS915::set_band_tx_done(SetBandTxDoneParams_t* txDone) +void LoRaPHYUS915::restore_default_channels() { - set_last_tx_done( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); -} + // Copy channels default mask + copy_channel_mask(channel_mask, default_channel_mask, US915_CHANNEL_MASK_SIZE); -void LoRaPHYUS915::load_defaults(InitType_t type) -{ - switch( type ) - { - case INIT_TYPE_INIT: - { - // Channels - // 125 kHz channels - for( uint8_t i = 0; i < US915_MAX_NB_CHANNELS - 8; i++ ) - { - Channels[i].Frequency = 902300000 + i * 200000; - Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0; - Channels[i].Band = 0; - } - // 500 kHz channels - for( uint8_t i = US915_MAX_NB_CHANNELS - 8; i < US915_MAX_NB_CHANNELS; i++ ) - { - Channels[i].Frequency = 903000000 + ( i - ( US915_MAX_NB_CHANNELS - 8 ) ) * 1600000; - Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4; - Channels[i].Band = 0; - } - - // ChannelsMask - ChannelsDefaultMask[0] = 0xFFFF; - ChannelsDefaultMask[1] = 0xFFFF; - ChannelsDefaultMask[2] = 0xFFFF; - ChannelsDefaultMask[3] = 0xFFFF; - ChannelsDefaultMask[4] = 0x00FF; - ChannelsDefaultMask[5] = 0x0000; - - // Copy channels default mask - copy_channel_mask( ChannelsMask, ChannelsDefaultMask, 6 ); - - // Copy into channels mask remaining - copy_channel_mask( ChannelsMaskRemaining, ChannelsMask, 6 ); - break; - } - case INIT_TYPE_RESTORE: - { - // Copy channels default mask - copy_channel_mask( ChannelsMask, ChannelsDefaultMask, 6 ); - - for( uint8_t i = 0; i < 6; i++ ) - { // Copy-And the channels mask - ChannelsMaskRemaining[i] &= ChannelsMask[i]; - } - break; - } - default: - { - break; - } + for ( uint8_t i = 0; i < US915_CHANNEL_MASK_SIZE; i++ ) { + // Copy-And the channels mask + current_channel_mask[i] &= channel_mask[i]; } } -bool LoRaPHYUS915::verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute) +bool LoRaPHYUS915::rx_config(rx_config_params_t* config, int8_t* datarate) { - switch( phyAttribute ) - { - case PHY_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, US915_TX_MIN_DATARATE, US915_TX_MAX_DATARATE ); - } - case PHY_DEF_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, DR_0, DR_5 ); - } - case PHY_RX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, US915_RX_MIN_DATARATE, US915_RX_MAX_DATARATE ); - } - case PHY_DEF_TX_POWER: - case PHY_TX_POWER: - { - // Remark: switched min and max! - return val_in_range( verify->TxPower, US915_MAX_TX_POWER, US915_MIN_TX_POWER ); - } - case PHY_DUTY_CYCLE: - { - return US915_DUTY_CYCLE_ENABLED; - } - case PHY_NB_JOIN_TRIALS: - { - if( verify->NbJoinTrials < 2 ) - { - return false; - } - break; - } - default: - return false; - } - return true; -} + int8_t dr = config->datarate; + uint8_t max_payload = 0; + int8_t phy_dr = 0; + uint32_t frequency = config->frequency; -void LoRaPHYUS915::apply_cf_list(ApplyCFListParams_t* applyCFList) -{ - return; -} + _radio->lock(); -bool LoRaPHYUS915::set_channel_mask(ChanMaskSetParams_t* chanMaskSet) -{ - uint8_t nbChannels = num_active_channels( chanMaskSet->ChannelsMaskIn, 0, 4 ); + if (_radio->get_status() != RF_IDLE) { - // Check the number of active channels - if( ( nbChannels < 2 ) && - ( nbChannels > 0 ) ) - { + _radio->unlock(); return false; + } - switch( chanMaskSet->ChannelsMaskType ) - { - case CHANNELS_MASK: - { - copy_channel_mask( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 ); + _radio->unlock(); - for( uint8_t i = 0; i < 6; i++ ) - { // Copy-And the channels mask - ChannelsMaskRemaining[i] &= ChannelsMask[i]; - } - break; - } - case CHANNELS_DEFAULT_MASK: - { - copy_channel_mask( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 ); - break; - } - default: - return false; - } - return true; -} - -bool LoRaPHYUS915::get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter) -{ - bool adrAckReq = false; - int8_t datarate = adrNext->Datarate; - int8_t txPower = adrNext->TxPower; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - - // Report back the adr ack counter - *adrAckCounter = adrNext->AdrAckCounter; - - if( adrNext->AdrEnabled == true ) - { - if( datarate == US915_TX_MIN_DATARATE ) - { - *adrAckCounter = 0; - adrAckReq = false; - } - else - { - if( adrNext->AdrAckCounter >= US915_ADR_ACK_LIMIT ) - { - adrAckReq = true; - txPower = US915_MAX_TX_POWER; - } - else - { - adrAckReq = false; - } - if( adrNext->AdrAckCounter >= ( US915_ADR_ACK_LIMIT + US915_ADR_ACK_DELAY ) ) - { - if( ( adrNext->AdrAckCounter % US915_ADR_ACK_DELAY ) == 1 ) - { - // Decrease the datarate - getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; - getPhy.Datarate = datarate; - getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; - phyParam = get_phy_params( &getPhy ); - datarate = phyParam.Value; - - if( datarate == US915_TX_MIN_DATARATE ) - { - // We must set adrAckReq to false as soon as we reach the lowest datarate - adrAckReq = false; - if( adrNext->UpdateChanMask == true ) - { - // Re-enable default channels - ChannelsMask[0] = 0xFFFF; - ChannelsMask[1] = 0xFFFF; - ChannelsMask[2] = 0xFFFF; - ChannelsMask[3] = 0xFFFF; - ChannelsMask[4] = 0x00FF; - ChannelsMask[5] = 0x0000; - } - } - } - } - } - } - - *drOut = datarate; - *txPowOut = txPower; - return adrAckReq; -} - -void LoRaPHYUS915::compute_rx_win_params(int8_t datarate, uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams) -{ - double tSymbol = 0.0; - - // Get the datarate, perform a boundary check - rxConfigParams->Datarate = MIN( datarate, US915_RX_MAX_DATARATE ); - rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); - - tSymbol = compute_symb_timeout_lora( DataratesUS915[rxConfigParams->Datarate], BandwidthsUS915[rxConfigParams->Datarate] ); - - get_rx_window_params( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); -} - -bool LoRaPHYUS915::rx_config(RxConfigParams_t* rxConfig, int8_t* datarate) -{ - int8_t dr = rxConfig->Datarate; - uint8_t maxPayload = 0; - int8_t phyDr = 0; - uint32_t frequency = rxConfig->Frequency; - - if(_radio->get_status() != RF_IDLE ) - { - return false; - } - - if( rxConfig->Window == 0 ) - { + // For US915 spectrum, we have 8 Downstream channels, MAC would have + // selected a channel randomly from 72 Upstream channels, that index is + // passed in rx_config_params_t. Based on that channel index, we choose the + // frequency for first RX slot + if (config->rx_slot == RX_SLOT_WIN_1) { // Apply window 1 frequency - frequency = US915_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * US915_STEPWIDTH_RX1_CHANNEL; + frequency = US915_FIRST_RX1_CHANNEL + (config->channel % 8) * US915_STEPWIDTH_RX1_CHANNEL; } // Read the physical datarate from the datarates table - phyDr = DataratesUS915[dr]; + phy_dr = datarates_US915[dr]; + + _radio->lock(); _radio->set_channel( frequency ); // Radio configuration - _radio->set_rx_config( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, - rxConfig->WindowTimeout, false, 0, false, 0, 0, true, - rxConfig->RxContinuous ); + _radio->set_rx_config(MODEM_LORA, config->bandwidth, phy_dr, 1, 0, 8, + config->window_timeout, false, 0, false, 0, 0, true, + config->is_rx_continuous); + _radio->unlock(); + + if (config->is_repeater_supported == true) { + + max_payload = max_payloads_with_repeater_US915[dr]; + + } else { + + max_payload = max_payloads_US915[dr]; - if( rxConfig->RepeaterSupport == true ) - { - maxPayload = MaxPayloadOfDatarateRepeaterUS915[dr]; } - else - { - maxPayload = MaxPayloadOfDatarateUS915[dr]; - } - _radio->set_max_payload_length( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + + _radio->lock(); + + _radio->set_max_payload_length(MODEM_LORA, max_payload + LORA_MAC_FRMPAYLOAD_OVERHEAD); + + _radio->unlock(); *datarate = (uint8_t) dr; return true; } -bool LoRaPHYUS915::tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir) +bool LoRaPHYUS915::tx_config(tx_config_params_t* config, int8_t* tx_power, + lorawan_time_t* tx_toa) { - int8_t phyDr = DataratesUS915[txConfig->Datarate]; - int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); - uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); - int8_t phyTxPower = 0; + int8_t phy_dr = datarates_US915[config->datarate]; + int8_t tx_power_limited = limit_tx_power(config->tx_power, + bands[channels[config->channel].band].max_tx_pwr, + config->datarate); + + uint32_t bandwidth = get_bandwidth(config->datarate); + int8_t phy_tx_power = 0; // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, US915_DEFAULT_MAX_ERP, 0 ); + phy_tx_power = compute_tx_power( tx_power_limited, US915_DEFAULT_MAX_ERP, 0 ); - // Setup the radio frequency - _radio->set_channel( Channels[txConfig->Channel].Frequency ); + _radio->lock(); - _radio->set_tx_config( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, - false, true, 0, 0, false, 3000 ); + _radio->set_channel(channels[config->channel].frequency); + + _radio->set_tx_config(MODEM_LORA, phy_tx_power, 0, bandwidth, phy_dr, 1, 8, + false, true, 0, 0, false, 3000); // Setup maximum payload lenght of the radio driver - _radio->set_max_payload_length( MODEM_LORA, txConfig->PktLen ); + _radio->set_max_payload_length(MODEM_LORA, config->pkt_len); + // Get the time-on-air of the next tx frame - *txTimeOnAir = _radio->time_on_air( MODEM_LORA, txConfig->PktLen ); - *txPower = txPowerLimited; + *tx_toa = _radio->time_on_air(MODEM_LORA, config->pkt_len); + + _radio->unlock(); + + *tx_power = tx_power_limited; return true; } -uint8_t LoRaPHYUS915::link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, uint8_t* nbBytesParsed) +uint8_t LoRaPHYUS915::link_ADR_request(adr_req_params_t* params, + int8_t* dr_out, int8_t* tx_power_out, + uint8_t* nb_rep_out, uint8_t* nb_bytes_parsed) { uint8_t status = 0x07; - RegionCommonLinkAdrParams_t linkAdrParams; - uint8_t nextIndex = 0; - uint8_t bytesProcessed = 0; - uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + + link_adr_params_t adr_settings; + uint8_t next_idx = 0; + uint8_t bytes_processed = 0; + uint16_t temp_channel_masks[US915_CHANNEL_MASK_SIZE] = {0, 0, 0, 0, 0}; + + verify_adr_params_t verify_params; // Initialize local copy of channels mask - copy_channel_mask( channelsMask, ChannelsMask, 6 ); + copy_channel_mask(temp_channel_masks, channel_mask, US915_CHANNEL_MASK_SIZE); - while( bytesProcessed < linkAdrReq->PayloadSize ) - { - nextIndex = parse_link_ADR_req( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + while (bytes_processed < params->payload_size) { + next_idx = parse_link_ADR_req(&(params->payload[bytes_processed]), + &adr_settings); - if( nextIndex == 0 ) + if (next_idx == 0) { break; // break loop, since no more request has been found + } // Update bytes processed - bytesProcessed += nextIndex; + bytes_processed += next_idx; // Revert status, as we only check the last ADR request for the channel mask KO status = 0x07; - if( linkAdrParams.ChMaskCtrl == 6 ) - { + if (adr_settings.ch_mask_ctrl == 6) { + // Enable all 125 kHz channels - channelsMask[0] = 0xFFFF; - channelsMask[1] = 0xFFFF; - channelsMask[2] = 0xFFFF; - channelsMask[3] = 0xFFFF; + temp_channel_masks[0] = 0xFFFF; + temp_channel_masks[1] = 0xFFFF; + temp_channel_masks[2] = 0xFFFF; + temp_channel_masks[3] = 0xFFFF; // Apply chMask to channels 64 to 71 - channelsMask[4] = linkAdrParams.ChMask; - } - else if( linkAdrParams.ChMaskCtrl == 7 ) - { + temp_channel_masks[4] = adr_settings.channel_mask; + + } else if (adr_settings.ch_mask_ctrl == 7) { + // Disable all 125 kHz channels - channelsMask[0] = 0x0000; - channelsMask[1] = 0x0000; - channelsMask[2] = 0x0000; - channelsMask[3] = 0x0000; + temp_channel_masks[0] = 0x0000; + temp_channel_masks[1] = 0x0000; + temp_channel_masks[2] = 0x0000; + temp_channel_masks[3] = 0x0000; // Apply chMask to channels 64 to 71 - channelsMask[4] = linkAdrParams.ChMask; - } - else if( linkAdrParams.ChMaskCtrl == 5 ) - { + temp_channel_masks[4] = adr_settings.channel_mask; + + } else if (adr_settings.ch_mask_ctrl == 5) { // RFU status &= 0xFE; // Channel mask KO - } - else - { - channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + + } else { + temp_channel_masks[adr_settings.ch_mask_ctrl] = adr_settings.channel_mask; } } // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels - if( ( linkAdrParams.Datarate < DR_4 ) && ( num_active_channels( channelsMask, 0, 4 ) < 2 ) ) - { + if ((adr_settings.datarate < DR_4) && + (num_active_channels(temp_channel_masks, 0, 4) < 2)) { + status &= 0xFE; // Channel mask KO + } - // Get the minimum possible datarate - getPhy.Attribute = PHY_MIN_TX_DR; - getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; - phyParam = get_phy_params( &getPhy ); - - linkAdrVerifyParams.Status = status; - linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; - linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; - linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; - linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; - linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; - linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; - linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; - linkAdrVerifyParams.NbChannels = US915_MAX_NB_CHANNELS; - linkAdrVerifyParams.ChannelsMask = channelsMask; - linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; - linkAdrVerifyParams.MaxDatarate = US915_TX_MAX_DATARATE; - linkAdrVerifyParams.Channels = Channels; - linkAdrVerifyParams.MinTxPower = US915_MIN_TX_POWER; - linkAdrVerifyParams.MaxTxPower = US915_MAX_TX_POWER; + verify_params.status = status; + verify_params.adr_enabled = params->adr_enabled; + verify_params.datarate = adr_settings.datarate; + verify_params.tx_power = adr_settings.tx_power; + verify_params.nb_rep = adr_settings.nb_rep; + verify_params.current_datarate = params->current_datarate; + verify_params.current_tx_power = params->current_tx_power; + verify_params.current_nb_rep = params->current_nb_rep; + verify_params.channel_mask = temp_channel_masks; // Verify the parameters and update, if necessary - status = verify_link_ADR_req( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + status = verify_link_ADR_req(&verify_params, &adr_settings.datarate, + &adr_settings.tx_power, &adr_settings.nb_rep); // Update channelsMask if everything is correct - if( status == 0x07 ) - { + if (status == 0x07) { // Copy Mask - copy_channel_mask( ChannelsMask, channelsMask, 6 ); + copy_channel_mask(channel_mask, temp_channel_masks, US915_CHANNEL_MASK_SIZE); - ChannelsMaskRemaining[0] &= ChannelsMask[0]; - ChannelsMaskRemaining[1] &= ChannelsMask[1]; - ChannelsMaskRemaining[2] &= ChannelsMask[2]; - ChannelsMaskRemaining[3] &= ChannelsMask[3]; - ChannelsMaskRemaining[4] = ChannelsMask[4]; - ChannelsMaskRemaining[5] = ChannelsMask[5]; + current_channel_mask[0] &= channel_mask[0]; + current_channel_mask[1] &= channel_mask[1]; + current_channel_mask[2] &= channel_mask[2]; + current_channel_mask[3] &= channel_mask[3]; + current_channel_mask[4] = channel_mask[4]; } // Update status variables - *drOut = linkAdrParams.Datarate; - *txPowOut = linkAdrParams.TxPower; - *nbRepOut = linkAdrParams.NbRep; - *nbBytesParsed = bytesProcessed; + *dr_out = adr_settings.datarate; + *tx_power_out = adr_settings.tx_power; + *nb_rep_out = adr_settings.nb_rep; + *nb_bytes_parsed = bytes_processed; return status; } -uint8_t LoRaPHYUS915::setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq) +uint8_t LoRaPHYUS915::accept_rx_param_setup_req(rx_param_setup_req_t* params) { uint8_t status = 0x07; - uint32_t freq = rxParamSetupReq->Frequency; + uint32_t freq = params->frequency; // Verify radio frequency - if( ( _radio->check_rf_frequency( freq ) == false ) || - ( freq < US915_FIRST_RX1_CHANNEL ) || - ( freq > US915_LAST_RX1_CHANNEL ) || - ( ( ( freq - ( uint32_t ) US915_FIRST_RX1_CHANNEL ) % ( uint32_t ) US915_STEPWIDTH_RX1_CHANNEL ) != 0 ) ) - { + if ((_radio->check_rf_frequency( freq ) == false) + || (freq < US915_FIRST_RX1_CHANNEL) + || (freq > US915_LAST_RX1_CHANNEL) + || (((freq - (uint32_t) US915_FIRST_RX1_CHANNEL) % (uint32_t) US915_STEPWIDTH_RX1_CHANNEL) != 0)) { + status &= 0xFE; // Channel frequency KO + } // Verify datarate - if( val_in_range( rxParamSetupReq->Datarate, US915_RX_MIN_DATARATE, US915_RX_MAX_DATARATE ) == 0 ) - { + if (val_in_range(params->datarate, US915_RX_MIN_DATARATE, US915_RX_MAX_DATARATE) == 0) { + status &= 0xFD; // Datarate KO + } - if( ( val_in_range( rxParamSetupReq->Datarate, DR_5, DR_7 ) == 1 ) || - ( rxParamSetupReq->Datarate > DR_13 ) ) - { + + if ((val_in_range(params->datarate, DR_5, DR_7)) || (params->datarate > DR_13)) { + status &= 0xFD; // Datarate KO + } // Verify datarate offset - if( val_in_range( rxParamSetupReq->DrOffset, US915_MIN_RX1_DR_OFFSET, US915_MAX_RX1_DR_OFFSET ) == 0 ) + if (val_in_range( params->dr_offset, US915_MIN_RX1_DR_OFFSET, US915_MAX_RX1_DR_OFFSET ) == 0 ) { status &= 0xFB; // Rx1DrOffset range KO } @@ -886,152 +586,112 @@ uint8_t LoRaPHYUS915::setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq) return status; } -uint8_t LoRaPHYUS915::request_new_channel(NewChannelReqParams_t* newChannelReq) -{ - // Datarate and frequency KO - return 0; -} - -int8_t LoRaPHYUS915::setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq) -{ - return -1; -} - -uint8_t LoRaPHYUS915::dl_channel_request(DlChannelReqParams_t* dlChannelReq) -{ - return 0; -} - -int8_t LoRaPHYUS915::get_alternate_DR(AlternateDrParams_t* alternateDr) +int8_t LoRaPHYUS915::get_alternate_DR(uint8_t nb_trials) { int8_t datarate = 0; // Re-enable 500 kHz default channels - ChannelsMask[4] = 0x00FF; + channel_mask[4] = 0x00FF; - if( ( alternateDr->NbTrials & 0x01 ) == 0x01 ) - { + if ((nb_trials & 0x01) == 0x01) { datarate = DR_4; - } - else - { + } else { datarate = DR_0; } + return datarate; } -void LoRaPHYUS915::calculate_backoff(CalcBackOffParams_t* calcBackOff) +bool LoRaPHYUS915::set_next_channel(channel_selection_params_t* params, + uint8_t* channel, lorawan_time_t* time, + lorawan_time_t* aggregate_timeOff) { - RegionCommonCalcBackOffParams_t calcBackOffParams; - - calcBackOffParams.Channels = Channels; - calcBackOffParams.Bands = Bands; - calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; - calcBackOffParams.Joined = calcBackOff->Joined; - calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; - calcBackOffParams.Channel = calcBackOff->Channel; - calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; - calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; - - get_DC_backoff( &calcBackOffParams ); -} - -bool LoRaPHYUS915::set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTx = 0; - uint8_t enabledChannels[US915_MAX_NB_CHANNELS] = { 0 }; - TimerTime_t nextTxDelay = 0; + uint8_t nb_enabled_channels = 0; + uint8_t delay_tx = 0; + uint8_t enabled_channels[US915_MAX_NB_CHANNELS] = {0}; + lorawan_time_t next_tx_delay = 0; // Count 125kHz channels - if( num_active_channels( ChannelsMaskRemaining, 0, 4 ) == 0 ) - { // Reactivate default channels - copy_channel_mask( ChannelsMaskRemaining, ChannelsMask, 4 ); + if (num_active_channels(current_channel_mask, 0, 4) == 0) { + // If none of the 125 kHz Upstream channel found, + // Reactivate default channels + copy_channel_mask(current_channel_mask, channel_mask, 4); } + // Check other channels - if( nextChanParams->Datarate >= DR_4 ) - { - if( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 ) - { - ChannelsMaskRemaining[4] = ChannelsMask[4]; + if (params->current_datarate >= DR_4) { + if ((current_channel_mask[4] & 0x00FF ) == 0) { + current_channel_mask[4] = channel_mask[4]; } } - if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) - { + if (params->aggregate_timeoff <= _lora_time.get_elapsed_time(params->last_aggregate_tx_time)) { // Reset Aggregated time off - *aggregatedTimeOff = 0; + *aggregate_timeOff = 0; // Update bands Time OFF - nextTxDelay = update_band_timeoff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, US915_MAX_NB_BANDS ); + next_tx_delay = update_band_timeoff(params->joined, params->dc_enabled, bands, US915_MAX_NB_BANDS); // Search how many channels are enabled - nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate, - ChannelsMaskRemaining, Channels, - Bands, enabledChannels, &delayTx ); - } - else - { - delayTx++; - nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); + nb_enabled_channels = enabled_channel_count(params->joined, + params->current_datarate, + current_channel_mask, + enabled_channels, &delay_tx); + } else { + delay_tx++; + next_tx_delay = params->aggregate_timeoff - _lora_time.get_elapsed_time(params->last_aggregate_tx_time); } - if( nbEnabledChannels > 0 ) - { + if (nb_enabled_channels > 0) { // We found a valid channel - *channel = enabledChannels[get_random( 0, nbEnabledChannels - 1 )]; + *channel = enabled_channels[get_random( 0, nb_enabled_channels - 1 )]; // Disable the channel in the mask - disable_channel( ChannelsMaskRemaining, *channel, US915_MAX_NB_CHANNELS - 8 ); + disable_channel(current_channel_mask, *channel, US915_MAX_NB_CHANNELS - 8); *time = 0; return true; - } - else - { - if( delayTx > 0 ) - { + + } else { + + if (delay_tx > 0) { // Delay transmission due to AggregatedTimeOff or to a band time off - *time = nextTxDelay; + *time = next_tx_delay; return true; } + // Datarate not supported by any channel *time = 0; return false; } } -LoRaMacStatus_t LoRaPHYUS915::add_channel(ChannelAddParams_t* channelAdd) +void LoRaPHYUS915::set_tx_cont_mode(cw_mode_params_t* params, uint32_t given_frequency) { - return LORAMAC_STATUS_PARAMETER_INVALID; -} + (void)given_frequency; -bool LoRaPHYUS915::remove_channel(ChannelRemoveParams_t* channelRemove) -{ - return LORAMAC_STATUS_PARAMETER_INVALID; -} - -void LoRaPHYUS915::set_tx_cont_mode(ContinuousWaveParams_t* continuousWave) -{ - int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); + int8_t tx_power_limited = limit_tx_power(params->tx_power, + bands[channels[params->channel].band].max_tx_pwr, + params->datarate); int8_t phyTxPower = 0; - uint32_t frequency = Channels[continuousWave->Channel].Frequency; + uint32_t frequency = channels[params->channel].frequency; // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, US915_DEFAULT_MAX_ERP, 0 ); + phyTxPower = compute_tx_power(tx_power_limited, US915_DEFAULT_MAX_ERP, 0); - _radio->set_tx_continuous_wave( frequency, phyTxPower, continuousWave->Timeout ); + _radio->lock(); + + _radio->set_tx_continuous_wave(frequency, phyTxPower, params->timeout); + + _radio->unlock(); } -uint8_t LoRaPHYUS915::apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, - int8_t drOffset) +uint8_t LoRaPHYUS915::apply_DR_offset(int8_t dr, int8_t dr_offset) { - int8_t datarate = DatarateOffsetsUS915[dr][drOffset]; + int8_t datarate = datarate_offsets_US915[dr][dr_offset]; - if( datarate < 0 ) - { + if (datarate < 0) { datarate = DR_0; } + return datarate; } diff --git a/features/lorawan/lorastack/phy/LoRaPHYUS915.h b/features/lorawan/lorastack/phy/LoRaPHYUS915.h index 3c97014c4e..f08944e6e7 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYUS915.h +++ b/features/lorawan/lorastack/phy/LoRaPHYUS915.h @@ -33,7 +33,6 @@ #define MBED_OS_LORAPHYUS_915_H_ #include "LoRaPHY.h" -#include "netsocket/LoRaRadio.h" /*! * LoRaMac maximum number of channels @@ -45,321 +44,69 @@ */ #define US915_MAX_NB_BANDS 1 -#define US915_CHANNELS_MASK_SIZE 6 +#define US915_CHANNEL_MASK_SIZE 5 class LoRaPHYUS915 : public LoRaPHY { public: - LoRaPHYUS915(); + LoRaPHYUS915(LoRaWANTimeHandler &lora_time); virtual ~LoRaPHYUS915(); - /*! - * \brief The function gets a value of a specific PHY attribute. - * - * \param [in] getPhy A pointer to the function parameters. - * - * \retval The structure containing the PHY parameter. - */ - virtual PhyParam_t get_phy_params(GetPhyParams_t* getPhy ); + virtual void restore_default_channels(); - /*! - * \brief Updates the last TX done parameters of the current channel. - * - * \param [in] txDone A pointer to the function parameters. - */ - virtual void set_band_tx_done(SetBandTxDoneParams_t* txDone ); + virtual bool rx_config(rx_config_params_t* config, int8_t* datarate); - /*! - * \brief Initializes the channels masks and the channels. - * - * \param [in] type Sets the initialization type. - */ - virtual void load_defaults(InitType_t type ); + virtual bool tx_config(tx_config_params_t* config, int8_t* tx_power, + lorawan_time_t* tx_toa); - /*! - * \brief Verifies a parameter. - * - * \param [in] verify A pointer to the function parameters. - * - * \param [in] phyAttribute The attribute to verify. - * - * \retval True, if the parameter is valid. - */ - virtual bool verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + virtual uint8_t link_ADR_request(adr_req_params_t* params, + int8_t* dr_out, int8_t* tx_power_out, + uint8_t* nb_rep_out, + uint8_t* nb_bytes_parsed); - /*! - * \brief The function parses the input buffer and sets up the channels of the CF list. - * - * \param [in] applyCFList A pointer to the function parameters. - */ - virtual void apply_cf_list(ApplyCFListParams_t* applyCFList ); + virtual uint8_t accept_rx_param_setup_req(rx_param_setup_req_t* params); - /*! - * \brief Sets a channels mask. - * - * \param [in] chanMaskSet A pointer to the function parameters. - * - * \retval True, if the channels mask could be set. - */ - virtual bool set_channel_mask(ChanMaskSetParams_t* chanMaskSet ); + virtual int8_t get_alternate_DR(uint8_t nb_trials); - /*! - * \brief Calculates the next datarate to set, when ADR is on or off. - * - * \param [in] adrNext A pointer to the function parameters. - * - * \param [out] drOut The calculated datarate for the next TX. - * - * \param [out] txPowOut The TX power for the next TX. - * - * \param [out] adrAckCounter The calculated ADR acknowledgement counter. - * - * \retval True, if an ADR request should be performed. - */ - virtual bool get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter ); + virtual bool set_next_channel(channel_selection_params_t* params, uint8_t* channel, + lorawan_time_t* time, lorawan_time_t* aggregate_timeOff); - /*! - * \brief Configuration of the RX windows. - * - * \param [in] rxConfig A pointer to the function parameters. - * - * \param [out] datarate The datarate index set. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool rx_config(RxConfigParams_t* rxConfig, int8_t* datarate ); + virtual void set_tx_cont_mode(cw_mode_params_t* continuousWave, + uint32_t frequency = 0); - /* - * RX window precise timing - * - * For more details, please consult the following document, chapter 3.1.2. - * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf - * or - * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf - * - * Downlink start: T = Tx + 1s (+/- 20 us) - * | - * TRxEarly | TRxLate - * | | | - * | | +---+---+---+---+---+---+---+---+ - * | | | Latest Rx window | - * | | +---+---+---+---+---+---+---+---+ - * | | | - * +---+---+---+---+---+---+---+---+ - * | Earliest Rx window | - * +---+---+---+---+---+---+---+---+ - * | - * +---+---+---+---+---+---+---+---+ - *Downlink preamble 8 symbols | | | | | | | | | - * +---+---+---+---+---+---+---+---+ - * - * Worst case Rx window timings - * - * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME - * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME - * - * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * - * RxOffset = ( TRxLate + TRxEarly ) / 2 - * - * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME - * - * The minimum value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol - */ - /*! - * Computes the RX window timeout and offset. - * - * \param [in] datarate The RX window datarate index to be used. - * - * \param [in] minRxSymbols The minimum number of symbols required to detect an RX frame. - * - * \param [in] rxError The system maximum timing error of the receiver in milliseconds. - * The receiver will turn on in a [-rxError : +rxError] ms - * interval around RxOffset. - * - * \param [out] rxConfigParams Returns the updated WindowTimeout and WindowOffset fields. - */ - virtual void compute_rx_win_params(int8_t datarate, - uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams); - - /*! - * \brief TX configuration. - * - * \param [in] txConfig A pointer to the function parameters. - * - * \param [out] txPower The TX power index set. - * - * \param [out] txTimeOnAir The time-on-air of the frame. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir ); - - /*! - * \brief The function processes a Link ADR request. - * - * \param [in] linkAdrReq A pointer to the function parameters. - * - * \param [out] drOut The datarate applied. - * - * \param [out] txPowOut The TX power applied. - * - * \param [out] nbRepOut The number of repetitions to apply. - * - * \param [out] nbBytesParsed The number of bytes parsed. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, - uint8_t* nbBytesParsed ); - - /*! - * \brief The function processes a RX parameter setup request. - * - * \param [in] rxParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq ); - - /*! - * \brief The function processes a new channel request. - * - * \param [in] newChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t request_new_channel(NewChannelReqParams_t* newChannelReq ); - - /*! - * \brief The function processes a TX ParamSetup request. - * - * \param [in] txParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - * Returns -1, if the functionality is not implemented. In this case, the end node - * shall ignore the command. - */ - virtual int8_t setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq ); - - /*! - * \brief The function processes a DlChannel request. - * - * \param [in] dlChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t dl_channel_request(DlChannelReqParams_t* dlChannelReq ); - - /*! - * \brief Alternates the datarate of the channel for the join request. - * - * \param [in] alternateDr A pointer to the function parameters. - * - * \retval The datarate to apply. - */ - virtual int8_t get_alternate_DR(AlternateDrParams_t* alternateDr ); - - /*! - * \brief Calculates the back-off time. - * - * \param [in] calcBackOff A pointer to the function parameters. - */ - virtual void calculate_backoff(CalcBackOffParams_t* calcBackOff ); - - /*! - * \brief Searches and sets the next random available channel. - * - * \param [in] nextChanParams The parameters for the next channel. - * - * \param [out] channel The next channel to use for TX. - * - * \param [out] time The time to wait for the next transmission according to the duty cycle. - * - * \param [out] aggregatedTimeOff Updates the aggregated time off. - * - * \retval The function status [1: OK, 0: Unable to find a channel on the current datarate]. - */ - virtual bool set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff ); - - /*! - * \brief Adds a channel. - * - * \param [in] channelAdd A pointer to the function parameters. - * - * \retval The status of the operation. - */ - virtual LoRaMacStatus_t add_channel(ChannelAddParams_t* channelAdd ); - - /*! - * \brief Removes a channel. - * - * \param [in] channelRemove A pointer to the function parameters. - * - * \retval True, if the channel was removed successfully. - */ - virtual bool remove_channel(ChannelRemoveParams_t* channelRemove ); - - /*! - * \brief Sets the radio into continuous wave mode. - * - * \param [in] continuousWave A pointer to the function parameters. - */ - virtual void set_tx_cont_mode(ContinuousWaveParams_t* continuousWave ); - - /*! - * \brief Computes a new datarate according to the given offset - * - * \param [in] downlinkDwellTime The downlink dwell time configuration. 0: No limit, 1: 400ms - * - * \param [in] dr The current datarate. - * - * \param [in] drOffset The offset to be applied. - * - * \retval newDr The computed datarate. - */ - virtual uint8_t apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + virtual uint8_t apply_DR_offset(int8_t dr, int8_t dr_offset); private: - int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ); - uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ); - // Global attributes + int8_t limit_tx_power(int8_t tx_power, int8_t max_band_tx_power, + int8_t datarate); + /*! * LoRaMAC channels */ - ChannelParams_t Channels[US915_MAX_NB_CHANNELS]; + channel_params_t channels[US915_MAX_NB_CHANNELS]; /*! * LoRaMac bands */ - Band_t Bands[US915_MAX_NB_BANDS]; + band_t bands[US915_MAX_NB_BANDS]; /*! - * LoRaMac channels mask + * LoRaMac channel mask */ - uint16_t ChannelsMask[US915_CHANNELS_MASK_SIZE]; + uint16_t channel_mask[US915_CHANNEL_MASK_SIZE]; /*! - * LoRaMac channels remaining + * Previously used channel mask */ - uint16_t ChannelsMaskRemaining[US915_CHANNELS_MASK_SIZE]; + uint16_t current_channel_mask[US915_CHANNEL_MASK_SIZE]; /*! - * LoRaMac channels default mask + * LoRaMac default channel mask */ - uint16_t ChannelsDefaultMask[US915_CHANNELS_MASK_SIZE]; + uint16_t default_channel_mask[US915_CHANNEL_MASK_SIZE]; }; #endif /* MBED_OS_LORAPHY_US915_H_ */ diff --git a/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.cpp b/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.cpp index 4137f224e9..9ab99e30fb 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.cpp +++ b/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.cpp @@ -30,10 +30,7 @@ */ #include "LoRaPHYUS915Hybrid.h" - #include "lora_phy_ds.h" -#include "LoRaRadio.h" - /*! * Minimal datarate that can be used by the node @@ -162,9 +159,9 @@ /*! * Band 0 definition - * { DutyCycle, TxMaxPower, LastTxDoneTime, TimeOff } + * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff } */ -#define US915_HYBRID_BAND0 { 1, US915_HYBRID_MAX_TX_POWER, 0, 0 } // 100.0 % +static const band_t US915_HYBRID_BAND0 = { 1, US915_HYBRID_MAX_TX_POWER, 0, 0, 0 }; // 100.0 % /*! * Defines the first channel for RX window 1 for US band @@ -184,17 +181,17 @@ /*! * Data rates table definition */ -static const uint8_t DataratesUS915_HYBRID[] = { 10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0 }; +static const uint8_t datarates_US915_HYBRID[] = { 10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0 }; /*! * Bandwidths table definition in Hz */ -static const uint32_t BandwidthsUS915_HYBRID[] = { 125000, 125000, 125000, 125000, 500000, 0, 0, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 }; +static const uint32_t bandwidths_US915_HYBRID[] = { 125000, 125000, 125000, 125000, 500000, 0, 0, 0, 500000, 500000, 500000, 500000, 500000, 500000, 0, 0 }; /*! * Up/Down link data rates offset definition */ -static const int8_t DatarateOffsetsUS915_HYBRID[5][4] = +static const int8_t datarate_offsets_US915_HYBRID[5][4] = { { DR_10, DR_9 , DR_8 , DR_8 }, // DR_0 { DR_11, DR_10, DR_9 , DR_8 }, // DR_1 @@ -206,922 +203,598 @@ static const int8_t DatarateOffsetsUS915_HYBRID[5][4] = /*! * Maximum payload with respect to the datarate index. Cannot operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateUS915_HYBRID[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 }; +static const uint8_t max_payloads_US915_HYBRID[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 }; /*! * Maximum payload with respect to the datarate index. Can operate with repeater. */ -static const uint8_t MaxPayloadOfDatarateRepeaterUS915_HYBRID[] = { 11, 53, 125, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0 }; +static const uint8_t max_payloads_with_repeater_US915_HYBRID[] = { 11, 53, 125, 242, 242, 0, 0, 0, 33, 109, 222, 222, 222, 222, 0, 0 }; - -// Static functions -static int8_t GetNextLowerTxDr( int8_t dr, int8_t minDr ) +LoRaPHYUS915Hybrid::LoRaPHYUS915Hybrid(LoRaWANTimeHandler &lora_time) + : LoRaPHY(lora_time) { - uint8_t nextLowerDr = 0; + bands[0] = US915_HYBRID_BAND0; - if( dr == minDr ) - { - nextLowerDr = minDr; - } - else - { - nextLowerDr = dr - 1; - } - return nextLowerDr; -} - -static uint32_t GetBandwidth( uint32_t drIndex ) -{ - switch( BandwidthsUS915_HYBRID[drIndex] ) - { - default: - case 125000: - return 0; - case 250000: - return 1; - case 500000: - return 2; - } -} - -static void ReenableChannels( uint16_t mask, uint16_t* channelsMask ) -{ - uint16_t blockMask = mask; - - for( uint8_t i = 0, j = 0; i < 4; i++, j += 2 ) - { - channelsMask[i] = 0; - if( ( blockMask & ( 1 << j ) ) != 0 ) - { - channelsMask[i] |= 0x00FF; - } - if( ( blockMask & ( 1 << ( j + 1 ) ) ) != 0 ) - { - channelsMask[i] |= 0xFF00; - } - } - channelsMask[4] = blockMask; - channelsMask[5] = 0x0000; -} - -static uint8_t CountBits( uint16_t mask, uint8_t nbBits ) -{ - uint8_t nbActiveBits = 0; - - for( uint8_t j = 0; j < nbBits; j++ ) - { - if( ( mask & ( 1 << j ) ) == ( 1 << j ) ) - { - nbActiveBits++; - } - } - return nbActiveBits; -} - -int8_t LoRaPHYUS915Hybrid::LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ) -{ - int8_t txPowerResult = txPower; - - // Limit tx power to the band max - txPowerResult = MAX( txPower, maxBandTxPower ); - - if( datarate == DR_4 ) - {// Limit tx power to max 26dBm - txPowerResult = MAX( txPower, TX_POWER_2 ); - } - else - { - if( num_active_channels( channelsMask, 0, 4 ) < 50 ) - {// Limit tx power to max 21dBm - txPowerResult = MAX( txPower, TX_POWER_5 ); - } - } - return txPowerResult; -} - -static bool ValidateChannelsMask( uint16_t* channelsMask ) -{ - bool chanMaskState = false; - uint16_t block1 = 0; - uint16_t block2 = 0; - uint8_t index = 0; - uint16_t channelsMaskCpy[6]; - - // Copy channels mask to not change the input - for( uint8_t i = 0; i < 4; i++ ) - { - channelsMaskCpy[i] = channelsMask[i]; + // Channels + // 125 kHz channels + for (uint8_t i = 0; i < US915_HYBRID_MAX_NB_CHANNELS - 8; i++) { + channels[i].frequency = 902300000 + i * 200000; + channels[i].dr_range.value = ( DR_3 << 4 ) | DR_0; + channels[i].band = 0; } - for( uint8_t i = 0; i < 4; i++ ) - { - block1 = channelsMaskCpy[i] & 0x00FF; - block2 = channelsMaskCpy[i] & 0xFF00; - - if( CountBits( block1, 16 ) > 5 ) - { - channelsMaskCpy[i] &= block1; - channelsMaskCpy[4] = 1 << ( i * 2 ); - chanMaskState = true; - index = i; - break; - } - else if( CountBits( block2, 16 ) > 5 ) - { - channelsMaskCpy[i] &= block2; - channelsMaskCpy[4] = 1 << ( i * 2 + 1 ); - chanMaskState = true; - index = i; - break; - } + // 500 kHz channels + for (uint8_t i = US915_HYBRID_MAX_NB_CHANNELS - 8; i < US915_HYBRID_MAX_NB_CHANNELS; i++) { + channels[i].frequency = 903000000 + (i - (US915_HYBRID_MAX_NB_CHANNELS - 8)) * 1600000; + channels[i].dr_range.value = ( DR_4 << 4 ) | DR_4; + channels[i].band = 0; } - // Do only change the channel mask, if we have found a valid block. - if( chanMaskState == true ) - { - // Copy channels mask back again - for( uint8_t i = 0; i < 4; i++ ) - { - channelsMask[i] = channelsMaskCpy[i]; + // ChannelsMask + default_channel_mask[0] = 0x00FF; + default_channel_mask[1] = 0x0000; + default_channel_mask[2] = 0x0000; + default_channel_mask[3] = 0x0000; + default_channel_mask[4] = 0x0001; - if( i != index ) - { - channelsMask[i] = 0; - } - } - channelsMask[4] = channelsMaskCpy[4]; - } - return chanMaskState; -} + memset(channel_mask, 0, sizeof(channel_mask)); + memset(current_channel_mask, 0, sizeof(current_channel_mask)); -uint8_t LoRaPHYUS915Hybrid::CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTransmission = 0; + // Copy channels default mask + copy_channel_mask(channel_mask, default_channel_mask, US915_HYBRID_CHANNEL_MASK_SIZE); - for( uint8_t i = 0, k = 0; i < US915_HYBRID_MAX_NB_CHANNELS; i += 16, k++ ) - { - for( uint8_t j = 0; j < 16; j++ ) - { - if( ( channelsMask[k] & ( 1 << j ) ) != 0 ) - { - if( channels[i + j].Frequency == 0 ) - { // Check if the channel is enabled - continue; - } - if( val_in_range( datarate, channels[i + j].DrRange.Fields.Min, - channels[i + j].DrRange.Fields.Max ) == 0 ) - { // Check if the current channel selection supports the given datarate - continue; - } - if( bands[channels[i + j].Band].TimeOff > 0 ) - { // Check if the band is available for transmission - delayTransmission++; - continue; - } - enabledChannels[nbEnabledChannels++] = i + j; - } - } - } + // Copy into channels mask remaining + copy_channel_mask(current_channel_mask, channel_mask, US915_HYBRID_CHANNEL_MASK_SIZE); - *delayTx = delayTransmission; - return nbEnabledChannels; -} + // set default channels + phy_params.channels.channel_list = channels; + phy_params.channels.channel_list_size = US915_HYBRID_MAX_NB_CHANNELS; + phy_params.channels.mask = channel_mask; + phy_params.channels.default_mask = default_channel_mask; + phy_params.channels.mask_size = US915_HYBRID_CHANNEL_MASK_SIZE; -LoRaPHYUS915Hybrid::LoRaPHYUS915Hybrid() -{ - const Band_t band0 = US915_HYBRID_BAND0; - Bands[0] = band0; + // set bands for US915_HYBRID spectrum + phy_params.bands.table = (void *) bands; + phy_params.bands.size = US915_HYBRID_MAX_NB_BANDS; + + // set bandwidths available in US915_HYBRID spectrum + phy_params.bandwidths.table = (void *) bandwidths_US915_HYBRID; + phy_params.bandwidths.size = 16; + + // set data rates available in US915_HYBRID spectrum + phy_params.datarates.table = (void *) datarates_US915_HYBRID; + phy_params.datarates.size = 16; + + // set payload sizes with respect to data rates + phy_params.payloads.table = (void *) max_payloads_US915_HYBRID; + phy_params.payloads.size = 16; + phy_params.payloads_with_repeater.table = (void *) max_payloads_with_repeater_US915_HYBRID; + phy_params.payloads.size = 16; + + // dwell time setting + phy_params.ul_dwell_time_setting = 0; + phy_params.dl_dwell_time_setting = 0; + + // set initial and default parameters + phy_params.duty_cycle_enabled = US915_HYBRID_DUTY_CYCLE_ENABLED; + phy_params.accept_tx_param_setup_req = false; + phy_params.fsk_supported = false; + phy_params.cflist_supported = false; + phy_params.dl_channel_req_supported = false; + phy_params.custom_channelplans_supported = false; + phy_params.default_channel_cnt = US915_HYBRID_MAX_NB_CHANNELS; + phy_params.max_channel_cnt = US915_HYBRID_MAX_NB_CHANNELS; + phy_params.cflist_channel_cnt = 0; + phy_params.min_tx_datarate = US915_HYBRID_TX_MIN_DATARATE; + phy_params.max_tx_datarate = US915_HYBRID_TX_MAX_DATARATE; + phy_params.min_rx_datarate = US915_HYBRID_RX_MIN_DATARATE; + phy_params.max_rx_datarate = US915_HYBRID_RX_MAX_DATARATE; + phy_params.default_datarate = US915_HYBRID_DEFAULT_DATARATE; + phy_params.default_max_datarate = US915_HYBRID_TX_MAX_DATARATE; + phy_params.min_rx1_dr_offset = US915_HYBRID_MIN_RX1_DR_OFFSET; + phy_params.max_rx1_dr_offset = US915_HYBRID_MAX_RX1_DR_OFFSET; + phy_params.default_rx1_dr_offset = US915_HYBRID_DEFAULT_RX1_DR_OFFSET; + phy_params.min_tx_power = US915_HYBRID_MIN_TX_POWER; + phy_params.max_tx_power = US915_HYBRID_MAX_TX_POWER; + phy_params.default_tx_power = US915_HYBRID_DEFAULT_TX_POWER; + phy_params.default_max_eirp = 0; + phy_params.default_antenna_gain = 0; + phy_params.adr_ack_limit = US915_HYBRID_ADR_ACK_LIMIT; + phy_params.adr_ack_delay = US915_HYBRID_ADR_ACK_DELAY; + phy_params.max_rx_window = US915_HYBRID_MAX_RX_WINDOW; + phy_params.recv_delay1 = US915_HYBRID_RECEIVE_DELAY1; + phy_params.recv_delay2 = US915_HYBRID_RECEIVE_DELAY2; + + phy_params.join_accept_delay1 = US915_HYBRID_JOIN_ACCEPT_DELAY1; + phy_params.join_accept_delay2 = US915_HYBRID_JOIN_ACCEPT_DELAY2; + phy_params.max_fcnt_gap = US915_HYBRID_MAX_FCNT_GAP; + phy_params.ack_timeout = US915_HYBRID_ACKTIMEOUT; + phy_params.ack_timeout_rnd = US915_HYBRID_ACK_TIMEOUT_RND; + phy_params.rx_window2_datarate = US915_HYBRID_RX_WND_2_DR; + phy_params.rx_window2_frequency = US915_HYBRID_RX_WND_2_FREQ; } LoRaPHYUS915Hybrid::~LoRaPHYUS915Hybrid() { } -PhyParam_t LoRaPHYUS915Hybrid::get_phy_params(GetPhyParams_t* getPhy) +void LoRaPHYUS915Hybrid::restore_default_channels() { - PhyParam_t phyParam = { 0 }; + // Copy channels default mask + copy_channel_mask(channel_mask, default_channel_mask, US915_HYBRID_CHANNEL_MASK_SIZE); - switch( getPhy->Attribute ) - { - case PHY_MIN_RX_DR: - { - phyParam.Value = US915_HYBRID_RX_MIN_DATARATE; - break; - } - case PHY_MIN_TX_DR: - { - phyParam.Value = US915_HYBRID_TX_MIN_DATARATE; - break; - } - case PHY_DEF_TX_DR: - { - phyParam.Value = US915_HYBRID_DEFAULT_DATARATE; - break; - } - case PHY_NEXT_LOWER_TX_DR: - { - phyParam.Value = GetNextLowerTxDr( getPhy->Datarate, US915_HYBRID_TX_MIN_DATARATE ); - break; - } - case PHY_DEF_TX_POWER: - { - phyParam.Value = US915_HYBRID_DEFAULT_TX_POWER; - break; - } - case PHY_MAX_PAYLOAD: - { - phyParam.Value = MaxPayloadOfDatarateUS915_HYBRID[getPhy->Datarate]; - break; - } - case PHY_MAX_PAYLOAD_REPEATER: - { - phyParam.Value = MaxPayloadOfDatarateRepeaterUS915_HYBRID[getPhy->Datarate]; - break; - } - case PHY_DUTY_CYCLE: - { - phyParam.Value = US915_HYBRID_DUTY_CYCLE_ENABLED; - break; - } - case PHY_MAX_RX_WINDOW: - { - phyParam.Value = US915_HYBRID_MAX_RX_WINDOW; - break; - } - case PHY_RECEIVE_DELAY1: - { - phyParam.Value = US915_HYBRID_RECEIVE_DELAY1; - break; - } - case PHY_RECEIVE_DELAY2: - { - phyParam.Value = US915_HYBRID_RECEIVE_DELAY2; - break; - } - case PHY_JOIN_ACCEPT_DELAY1: - { - phyParam.Value = US915_HYBRID_JOIN_ACCEPT_DELAY1; - break; - } - case PHY_JOIN_ACCEPT_DELAY2: - { - phyParam.Value = US915_HYBRID_JOIN_ACCEPT_DELAY2; - break; - } - case PHY_MAX_FCNT_GAP: - { - phyParam.Value = US915_HYBRID_MAX_FCNT_GAP; - break; - } - case PHY_ACK_TIMEOUT: - { - phyParam.Value = ( US915_HYBRID_ACKTIMEOUT + get_random( -US915_HYBRID_ACK_TIMEOUT_RND, US915_HYBRID_ACK_TIMEOUT_RND ) ); - break; - } - case PHY_DEF_DR1_OFFSET: - { - phyParam.Value = US915_HYBRID_DEFAULT_RX1_DR_OFFSET; - break; - } - case PHY_DEF_RX2_FREQUENCY: - { - phyParam.Value = US915_HYBRID_RX_WND_2_FREQ; - break; - } - case PHY_DEF_RX2_DR: - { - phyParam.Value = US915_HYBRID_RX_WND_2_DR; - break; - } - case PHY_CHANNELS_MASK: - { - phyParam.ChannelsMask = ChannelsMask; - break; - } - case PHY_CHANNELS_DEFAULT_MASK: - { - phyParam.ChannelsMask = ChannelsDefaultMask; - break; - } - case PHY_MAX_NB_CHANNELS: - { - phyParam.Value = US915_HYBRID_MAX_NB_CHANNELS; - break; - } - case PHY_CHANNELS: - { - phyParam.Channels = Channels; - break; - } - case PHY_DEF_UPLINK_DWELL_TIME: - case PHY_DEF_DOWNLINK_DWELL_TIME: - { - phyParam.Value = 0; - break; - } - case PHY_DEF_MAX_EIRP: - case PHY_DEF_ANTENNA_GAIN: - { - phyParam.fValue = 0; - break; - } - case PHY_NB_JOIN_TRIALS: - case PHY_DEF_NB_JOIN_TRIALS: - { - phyParam.Value = 2; - break; - } - default: - { - break; - } - } - - return phyParam; -} - -void LoRaPHYUS915Hybrid::set_band_tx_done(SetBandTxDoneParams_t* txDone) -{ - set_last_tx_done( txDone->Joined, &Bands[Channels[txDone->Channel].Band], txDone->LastTxDoneTime ); -} - -void LoRaPHYUS915Hybrid::load_defaults(InitType_t type) -{ - switch( type ) - { - case INIT_TYPE_INIT: - { - // Channels - // 125 kHz channels - for( uint8_t i = 0; i < US915_HYBRID_MAX_NB_CHANNELS - 8; i++ ) - { - Channels[i].Frequency = 902300000 + i * 200000; - Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0; - Channels[i].Band = 0; - } - // 500 kHz channels - for( uint8_t i = US915_HYBRID_MAX_NB_CHANNELS - 8; i < US915_HYBRID_MAX_NB_CHANNELS; i++ ) - { - Channels[i].Frequency = 903000000 + ( i - ( US915_HYBRID_MAX_NB_CHANNELS - 8 ) ) * 1600000; - Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4; - Channels[i].Band = 0; - } - - // ChannelsMask - ChannelsDefaultMask[0] = 0x00FF; - ChannelsDefaultMask[1] = 0x0000; - ChannelsDefaultMask[2] = 0x0000; - ChannelsDefaultMask[3] = 0x0000; - ChannelsDefaultMask[4] = 0x0001; - ChannelsDefaultMask[5] = 0x0000; - - // Copy channels default mask - copy_channel_mask( ChannelsMask, ChannelsDefaultMask, 6 ); - - // Copy into channels mask remaining - copy_channel_mask( ChannelsMaskRemaining, ChannelsMask, 6 ); - break; - } - case INIT_TYPE_RESTORE: - { - ReenableChannels( ChannelsDefaultMask[4], ChannelsMask ); - - for( uint8_t i = 0; i < 6; i++ ) - { // Copy-And the channels mask - ChannelsMaskRemaining[i] &= ChannelsMask[i]; - } - } - default: - { - break; - } + for (uint8_t i = 0; i < US915_HYBRID_CHANNEL_MASK_SIZE; i++) { + // Copy-And the channels mask + current_channel_mask[i] &= channel_mask[i]; } } -bool LoRaPHYUS915Hybrid::verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute) -{ - switch( phyAttribute ) - { - case PHY_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, US915_HYBRID_TX_MIN_DATARATE, US915_HYBRID_TX_MAX_DATARATE ); - } - case PHY_DEF_TX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, DR_0, DR_5 ); - } - case PHY_RX_DR: - { - return val_in_range( verify->DatarateParams.Datarate, US915_HYBRID_RX_MIN_DATARATE, US915_HYBRID_RX_MAX_DATARATE ); - } - case PHY_DEF_TX_POWER: - case PHY_TX_POWER: - { - // Remark: switched min and max! - return val_in_range( verify->TxPower, US915_HYBRID_MAX_TX_POWER, US915_HYBRID_MIN_TX_POWER ); - } - case PHY_DUTY_CYCLE: - { - return US915_HYBRID_DUTY_CYCLE_ENABLED; - } - case PHY_NB_JOIN_TRIALS: - { - if( verify->NbJoinTrials < 2 ) - { - return false; - } - break; - } - default: - return false; - } - return true; -} - -void LoRaPHYUS915Hybrid::apply_cf_list(ApplyCFListParams_t* applyCFList) -{ - return; -} - -bool LoRaPHYUS915Hybrid::set_channel_mask(ChanMaskSetParams_t* chanMaskSet) -{ - uint8_t nbChannels = num_active_channels( chanMaskSet->ChannelsMaskIn, 0, 4 ); - - // Check the number of active channels - if( ( nbChannels < 2 ) && - ( nbChannels > 0 ) ) - { - return false; - } - - // Validate the channels mask - if( ValidateChannelsMask( chanMaskSet->ChannelsMaskIn ) == false ) - { - return false; - } - - switch( chanMaskSet->ChannelsMaskType ) - { - case CHANNELS_MASK: - { - copy_channel_mask( ChannelsMask, chanMaskSet->ChannelsMaskIn, 6 ); - - for( uint8_t i = 0; i < 6; i++ ) - { // Copy-And the channels mask - ChannelsMaskRemaining[i] &= ChannelsMask[i]; - } - break; - } - case CHANNELS_DEFAULT_MASK: - { - copy_channel_mask( ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 6 ); - break; - } - default: - return false; - } - return true; -} - -bool LoRaPHYUS915Hybrid::get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter) +bool LoRaPHYUS915Hybrid::get_next_ADR(bool restore_channel_mask, int8_t& dr_out, + int8_t& tx_power_out, uint32_t& adr_ack_cnt) { bool adrAckReq = false; - int8_t datarate = adrNext->Datarate; - int8_t txPower = adrNext->TxPower; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - // Report back the adr ack counter - *adrAckCounter = adrNext->AdrAckCounter; + get_phy_params_t get_phy; + phy_param_t phy_param; - if( adrNext->AdrEnabled == true ) - { - if( datarate == US915_HYBRID_TX_MIN_DATARATE ) - { - *adrAckCounter = 0; - adrAckReq = false; - } - else - { - if( adrNext->AdrAckCounter >= US915_HYBRID_ADR_ACK_LIMIT ) - { - adrAckReq = true; - txPower = US915_HYBRID_MAX_TX_POWER; - } - else - { + uint16_t ack_limit_plus_delay = phy_params.adr_ack_limit + phy_params.adr_ack_delay; + + if (dr_out == phy_params.min_tx_datarate) { + adr_ack_cnt = 0; + return adrAckReq; + } + + if (adr_ack_cnt < phy_params.adr_ack_limit) { + return adrAckReq; + } + + // ADR ack counter is larger than ADR-ACK-LIMIT + adrAckReq = true; + tx_power_out = phy_params.max_tx_power; + + + if (adr_ack_cnt >= ack_limit_plus_delay) { + if ((adr_ack_cnt % phy_params.adr_ack_delay) == 1) { + // Decrease the datarate + 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; + + if (dr_out == phy_params.min_tx_datarate) { + // We must set adrAckReq to false as soon as we reach the lowest datarate adrAckReq = false; - } - if( adrNext->AdrAckCounter >= ( US915_HYBRID_ADR_ACK_LIMIT + US915_HYBRID_ADR_ACK_DELAY ) ) - { - if( ( adrNext->AdrAckCounter % US915_HYBRID_ADR_ACK_DELAY ) == 1 ) - { - // Decrease the datarate - getPhy.Attribute = PHY_NEXT_LOWER_TX_DR; - getPhy.Datarate = datarate; - getPhy.UplinkDwellTime = adrNext->UplinkDwellTime; - phyParam = get_phy_params( &getPhy ); - datarate = phyParam.Value; - - if( datarate == US915_HYBRID_TX_MIN_DATARATE ) - { - // We must set adrAckReq to false as soon as we reach the lowest datarate - adrAckReq = false; - if( adrNext->UpdateChanMask == true ) - { - // Re-enable default channels - ReenableChannels( ChannelsMask[4], ChannelsMask ); - } - } + if (restore_channel_mask) { + // Re-enable default channels + reenable_500khz_channels(channel_mask[4], channel_mask); } } } } - *drOut = datarate; - *txPowOut = txPower; return adrAckReq; } -void LoRaPHYUS915Hybrid::compute_rx_win_params(int8_t datarate, uint8_t minRxSymbols, - uint32_t rxError, RxConfigParams_t *rxConfigParams) +bool LoRaPHYUS915Hybrid::rx_config(rx_config_params_t* config, int8_t* datarate) { - double tSymbol = 0.0; + int8_t dr = config->datarate; + uint8_t max_payload = 0; + int8_t phy_dr = 0; + uint32_t frequency = config->frequency; - // Get the datarate, perform a boundary check - rxConfigParams->Datarate = MIN( datarate, US915_HYBRID_RX_MAX_DATARATE ); - rxConfigParams->Bandwidth = GetBandwidth( rxConfigParams->Datarate ); + _radio->lock(); - tSymbol = compute_symb_timeout_lora( DataratesUS915_HYBRID[rxConfigParams->Datarate], BandwidthsUS915_HYBRID[rxConfigParams->Datarate] ); + if (_radio->get_status() != RF_IDLE) { - get_rx_window_params( tSymbol, minRxSymbols, rxError, RADIO_WAKEUP_TIME, &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset ); -} - -bool LoRaPHYUS915Hybrid::rx_config(RxConfigParams_t* rxConfig, int8_t* datarate) -{ - int8_t dr = rxConfig->Datarate; - uint8_t maxPayload = 0; - int8_t phyDr = 0; - uint32_t frequency = rxConfig->Frequency; - - if( _radio->get_status() != RF_IDLE ) - { + _radio->unlock(); return false; + } - if( rxConfig->Window == 0 ) - { + _radio->unlock(); + + if (config->rx_slot == RX_SLOT_WIN_1) { // Apply window 1 frequency - frequency = US915_HYBRID_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 8 ) * US915_HYBRID_STEPWIDTH_RX1_CHANNEL; + frequency = US915_HYBRID_FIRST_RX1_CHANNEL + (config->channel % 8) * US915_HYBRID_STEPWIDTH_RX1_CHANNEL; } // Read the physical datarate from the datarates table - phyDr = DataratesUS915_HYBRID[dr]; + phy_dr = datarates_US915_HYBRID[dr]; + + _radio->lock(); _radio->set_channel( frequency ); // Radio configuration - _radio->set_rx_config( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous ); + _radio->set_rx_config(MODEM_LORA, config->bandwidth, phy_dr, 1, 0, 8, + config->window_timeout, false, 0, false, 0, 0, true, + config->is_rx_continuous); - if( rxConfig->RepeaterSupport == true ) - { - maxPayload = MaxPayloadOfDatarateRepeaterUS915_HYBRID[dr]; + _radio->unlock(); + + if (config->is_repeater_supported == true) { + max_payload = max_payloads_with_repeater_US915_HYBRID[dr]; + } else { + max_payload = max_payloads_US915_HYBRID[dr]; } - else - { - maxPayload = MaxPayloadOfDatarateUS915_HYBRID[dr]; - } - _radio->set_max_payload_length( MODEM_LORA, maxPayload + LORA_MAC_FRMPAYLOAD_OVERHEAD ); + + _radio->lock(); + _radio->set_max_payload_length(MODEM_LORA, max_payload + LORA_MAC_FRMPAYLOAD_OVERHEAD); + _radio->unlock(); *datarate = (uint8_t) dr; return true; } -bool LoRaPHYUS915Hybrid::tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir) +bool LoRaPHYUS915Hybrid::tx_config(tx_config_params_t* config, int8_t* tx_power, + lorawan_time_t* tx_toa) { - int8_t phyDr = DataratesUS915_HYBRID[txConfig->Datarate]; - int8_t txPowerLimited = LimitTxPower( txConfig->TxPower, Bands[Channels[txConfig->Channel].Band].TxMaxPower, txConfig->Datarate, ChannelsMask ); - uint32_t bandwidth = GetBandwidth( txConfig->Datarate ); - int8_t phyTxPower = 0; + int8_t phy_dr = datarates_US915_HYBRID[config->datarate]; + + int8_t tx_power_limited = limit_tx_power(config->tx_power, + bands[channels[config->channel].band].max_tx_pwr, + config->datarate); + + uint32_t bandwidth = get_bandwidth (config->datarate); + int8_t phy_tx_power = 0; // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, US915_HYBRID_DEFAULT_MAX_ERP, 0 ); + phy_tx_power = compute_tx_power(tx_power_limited, US915_HYBRID_DEFAULT_MAX_ERP, 0); - // Setup the radio frequency - _radio->set_channel( Channels[txConfig->Channel].Frequency ); + _radio->lock(); - _radio->set_tx_config( MODEM_LORA, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 3000 ); + _radio->set_channel( channels[config->channel].frequency ); + + _radio->set_tx_config(MODEM_LORA, phy_tx_power, 0, bandwidth, phy_dr, 1, 8, + false, true, 0, 0, false, 3000); // Setup maximum payload lenght of the radio driver - _radio->set_max_payload_length( MODEM_LORA, txConfig->PktLen ); + _radio->set_max_payload_length(MODEM_LORA, config->pkt_len); + // Get the time-on-air of the next tx frame - *txTimeOnAir = _radio->time_on_air( MODEM_LORA, txConfig->PktLen ); - *txPower = txPowerLimited; + *tx_toa = _radio->time_on_air(MODEM_LORA, config->pkt_len); + + _radio->unlock(); + *tx_power = tx_power_limited; return true; } -uint8_t LoRaPHYUS915Hybrid::link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, - uint8_t* nbBytesParsed) +uint8_t LoRaPHYUS915Hybrid::link_ADR_request(adr_req_params_t* params, + int8_t* dr_out, int8_t* tx_power_out, + uint8_t* nb_rep_out, + uint8_t* nb_bytes_parsed) { uint8_t status = 0x07; - RegionCommonLinkAdrParams_t linkAdrParams; - uint8_t nextIndex = 0; - uint8_t bytesProcessed = 0; - uint16_t channelsMask[6] = { 0, 0, 0, 0, 0, 0 }; - GetPhyParams_t getPhy; - PhyParam_t phyParam; - RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams; + link_adr_params_t adr_settings; + uint8_t next_idx = 0; + uint8_t bytes_processed = 0; + uint16_t temp_channel_mask[US915_HYBRID_CHANNEL_MASK_SIZE] = {0, 0, 0, 0, 0}; + + verify_adr_params_t verify_params; // Initialize local copy of channels mask - copy_channel_mask( channelsMask, ChannelsMask, 6 ); + copy_channel_mask(temp_channel_mask, channel_mask, US915_HYBRID_CHANNEL_MASK_SIZE); - while( bytesProcessed < linkAdrReq->PayloadSize ) - { - nextIndex = parse_link_ADR_req( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams ); + while (bytes_processed < params->payload_size) { + next_idx = parse_link_ADR_req(&(params->payload[bytes_processed]), + &adr_settings); - if( nextIndex == 0 ) + if (next_idx == 0) { break; // break loop, since no more request has been found + } // Update bytes processed - bytesProcessed += nextIndex; + bytes_processed += next_idx; // Revert status, as we only check the last ADR request for the channel mask KO status = 0x07; - if( linkAdrParams.ChMaskCtrl == 6 ) - { + if (adr_settings.ch_mask_ctrl == 6) { // Enable all 125 kHz channels - channelsMask[0] = 0xFFFF; - channelsMask[1] = 0xFFFF; - channelsMask[2] = 0xFFFF; - channelsMask[3] = 0xFFFF; + temp_channel_mask[0] = 0xFFFF; + temp_channel_mask[1] = 0xFFFF; + temp_channel_mask[2] = 0xFFFF; + temp_channel_mask[3] = 0xFFFF; // Apply chMask to channels 64 to 71 - channelsMask[4] = linkAdrParams.ChMask; - } - else if( linkAdrParams.ChMaskCtrl == 7 ) - { + temp_channel_mask[4] = adr_settings.channel_mask; + } else if( adr_settings.ch_mask_ctrl == 7 ) { // Disable all 125 kHz channels - channelsMask[0] = 0x0000; - channelsMask[1] = 0x0000; - channelsMask[2] = 0x0000; - channelsMask[3] = 0x0000; + temp_channel_mask[0] = 0x0000; + temp_channel_mask[1] = 0x0000; + temp_channel_mask[2] = 0x0000; + temp_channel_mask[3] = 0x0000; // Apply chMask to channels 64 to 71 - channelsMask[4] = linkAdrParams.ChMask; - } - else if( linkAdrParams.ChMaskCtrl == 5 ) - { + temp_channel_mask[4] = adr_settings.channel_mask; + } else if( adr_settings.ch_mask_ctrl == 5 ) { // RFU status &= 0xFE; // Channel mask KO - } - else - { - channelsMask[linkAdrParams.ChMaskCtrl] = linkAdrParams.ChMask; + } else { + temp_channel_mask[adr_settings.ch_mask_ctrl] = adr_settings.channel_mask; } } // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels - if( ( linkAdrParams.Datarate < DR_4 ) && ( num_active_channels( channelsMask, 0, 4 ) < 2 ) ) - { + if ((adr_settings.datarate < DR_4) && + (num_active_channels( temp_channel_mask, 0, 4 ) < 2)) { status &= 0xFE; // Channel mask KO } - if( ValidateChannelsMask( channelsMask ) == false ) - { + if( validate_channel_mask(temp_channel_mask ) == false) { status &= 0xFE; // Channel mask KO } - // Get the minimum possible datarate - getPhy.Attribute = PHY_MIN_TX_DR; - getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime; - phyParam = get_phy_params( &getPhy ); + verify_params.status = status; + verify_params.adr_enabled = params->adr_enabled; + verify_params.datarate = adr_settings.datarate; + verify_params.tx_power = adr_settings.tx_power; + verify_params.nb_rep = adr_settings.nb_rep; + verify_params.current_datarate = params->current_datarate; + verify_params.current_tx_power = params->current_tx_power; + verify_params.current_nb_rep = params->current_nb_rep; + verify_params.channel_mask = temp_channel_mask; - linkAdrVerifyParams.Status = status; - linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled; - linkAdrVerifyParams.Datarate = linkAdrParams.Datarate; - linkAdrVerifyParams.TxPower = linkAdrParams.TxPower; - linkAdrVerifyParams.NbRep = linkAdrParams.NbRep; - linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate; - linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower; - linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep; - linkAdrVerifyParams.NbChannels = US915_HYBRID_MAX_NB_CHANNELS; - linkAdrVerifyParams.ChannelsMask = channelsMask; - linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value; - linkAdrVerifyParams.MaxDatarate = US915_HYBRID_TX_MAX_DATARATE; - linkAdrVerifyParams.Channels = Channels; - linkAdrVerifyParams.MinTxPower = US915_HYBRID_MIN_TX_POWER; - linkAdrVerifyParams.MaxTxPower = US915_HYBRID_MAX_TX_POWER; // Verify the parameters and update, if necessary - status = verify_link_ADR_req( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep ); + status = verify_link_ADR_req(&verify_params, &adr_settings.datarate, + &adr_settings.tx_power, &adr_settings.nb_rep); // Update channelsMask if everything is correct - if( status == 0x07 ) - { + if (status == 0x07) { // Copy Mask - copy_channel_mask( ChannelsMask, channelsMask, 6 ); + copy_channel_mask(channel_mask, temp_channel_mask, US915_HYBRID_CHANNEL_MASK_SIZE); - ChannelsMaskRemaining[0] &= ChannelsMask[0]; - ChannelsMaskRemaining[1] &= ChannelsMask[1]; - ChannelsMaskRemaining[2] &= ChannelsMask[2]; - ChannelsMaskRemaining[3] &= ChannelsMask[3]; - ChannelsMaskRemaining[4] = ChannelsMask[4]; - ChannelsMaskRemaining[5] = ChannelsMask[5]; + current_channel_mask[0] &= channel_mask[0]; + current_channel_mask[1] &= channel_mask[1]; + current_channel_mask[2] &= channel_mask[2]; + current_channel_mask[3] &= channel_mask[3]; + current_channel_mask[4] = channel_mask[4]; } // Update status variables - *drOut = linkAdrParams.Datarate; - *txPowOut = linkAdrParams.TxPower; - *nbRepOut = linkAdrParams.NbRep; - *nbBytesParsed = bytesProcessed; + *dr_out = adr_settings.datarate; + *tx_power_out = adr_settings.tx_power; + *nb_rep_out = adr_settings.nb_rep; + *nb_bytes_parsed = bytes_processed; return status; } -uint8_t LoRaPHYUS915Hybrid::setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq) +uint8_t LoRaPHYUS915Hybrid::accept_rx_param_setup_req(rx_param_setup_req_t* params) { uint8_t status = 0x07; - uint32_t freq = rxParamSetupReq->Frequency; + uint32_t freq = params->frequency; // Verify radio frequency - if( ( _radio->check_rf_frequency( freq ) == false ) || - ( freq < US915_HYBRID_FIRST_RX1_CHANNEL ) || - ( freq > US915_HYBRID_LAST_RX1_CHANNEL ) || - ( ( ( freq - ( uint32_t ) US915_HYBRID_FIRST_RX1_CHANNEL ) % ( uint32_t ) US915_HYBRID_STEPWIDTH_RX1_CHANNEL ) != 0 ) ) - { + if ((_radio->check_rf_frequency(freq) == false) + || (freq < US915_HYBRID_FIRST_RX1_CHANNEL) + || (freq > US915_HYBRID_LAST_RX1_CHANNEL) + || (((freq - ( uint32_t ) US915_HYBRID_FIRST_RX1_CHANNEL) % (uint32_t) US915_HYBRID_STEPWIDTH_RX1_CHANNEL) != 0)) { status &= 0xFE; // Channel frequency KO } // Verify datarate - if( val_in_range( rxParamSetupReq->Datarate, US915_HYBRID_RX_MIN_DATARATE, US915_HYBRID_RX_MAX_DATARATE ) == 0 ) - { + if (val_in_range(params->datarate, US915_HYBRID_RX_MIN_DATARATE, US915_HYBRID_RX_MAX_DATARATE) == 0) { status &= 0xFD; // Datarate KO } - if( ( val_in_range( rxParamSetupReq->Datarate, DR_5, DR_7 ) == 1 ) || - ( rxParamSetupReq->Datarate > DR_13 ) ) - { + + if ((val_in_range(params->datarate, DR_5, DR_7) == 1) + || (params->datarate > DR_13)) { status &= 0xFD; // Datarate KO } // Verify datarate offset - if( val_in_range( rxParamSetupReq->DrOffset, US915_HYBRID_MIN_RX1_DR_OFFSET, US915_HYBRID_MAX_RX1_DR_OFFSET ) == 0 ) - { + if (val_in_range(params->dr_offset, US915_HYBRID_MIN_RX1_DR_OFFSET, US915_HYBRID_MAX_RX1_DR_OFFSET) == 0) { status &= 0xFB; // Rx1DrOffset range KO } return status; } -uint8_t LoRaPHYUS915Hybrid::request_new_channel(NewChannelReqParams_t* newChannelReq) -{ - // Datarate and frequency KO - return 0; -} - -int8_t LoRaPHYUS915Hybrid::setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq) -{ - return -1; -} - -uint8_t LoRaPHYUS915Hybrid::dl_channel_request(DlChannelReqParams_t* dlChannelReq) -{ - return 0; -} - -int8_t LoRaPHYUS915Hybrid::get_alternate_DR(AlternateDrParams_t* alternateDr) +int8_t LoRaPHYUS915Hybrid::get_alternate_DR(uint8_t nb_trials) { int8_t datarate = 0; // Re-enable 500 kHz default channels - ReenableChannels( ChannelsMask[4], ChannelsMask ); + reenable_500khz_channels(channel_mask[4], channel_mask); - if( ( alternateDr->NbTrials & 0x01 ) == 0x01 ) - { + if ((nb_trials & 0x01) == 0x01) { datarate = DR_4; - } - else - { + } else { datarate = DR_0; } + return datarate; } -void LoRaPHYUS915Hybrid::calculate_backoff(CalcBackOffParams_t* calcBackOff) +bool LoRaPHYUS915Hybrid::set_next_channel(channel_selection_params_t* params, + uint8_t* channel, lorawan_time_t* time, + lorawan_time_t* aggregate_timeOff) { - RegionCommonCalcBackOffParams_t calcBackOffParams; - - calcBackOffParams.Channels = Channels; - calcBackOffParams.Bands = Bands; - calcBackOffParams.LastTxIsJoinRequest = calcBackOff->LastTxIsJoinRequest; - calcBackOffParams.Joined = calcBackOff->Joined; - calcBackOffParams.DutyCycleEnabled = calcBackOff->DutyCycleEnabled; - calcBackOffParams.Channel = calcBackOff->Channel; - calcBackOffParams.ElapsedTime = calcBackOff->ElapsedTime; - calcBackOffParams.TxTimeOnAir = calcBackOff->TxTimeOnAir; - - get_DC_backoff( &calcBackOffParams ); -} - -bool LoRaPHYUS915Hybrid::set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff) -{ - uint8_t nbEnabledChannels = 0; - uint8_t delayTx = 0; - uint8_t enabledChannels[US915_HYBRID_MAX_NB_CHANNELS] = { 0 }; - TimerTime_t nextTxDelay = 0; + uint8_t nb_enabled_channels = 0; + uint8_t delay_tx = 0; + uint8_t enabled_channels[US915_HYBRID_MAX_NB_CHANNELS] = {0}; + lorawan_time_t next_tx_delay = 0; // Count 125kHz channels - if( num_active_channels( ChannelsMaskRemaining, 0, 4 ) == 0 ) - { // Reactivate default channels - copy_channel_mask( ChannelsMaskRemaining, ChannelsMask, 4 ); + if (num_active_channels(current_channel_mask, 0, 4) == 0) { + // Reactivate default channels + copy_channel_mask(current_channel_mask, channel_mask, 4); } + // Check other channels - if( nextChanParams->Datarate >= DR_4 ) - { - if( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 ) - { - ChannelsMaskRemaining[4] = ChannelsMask[4]; + if (params->current_datarate >= DR_4) { + if ((current_channel_mask[4] & 0x00FF ) == 0) { + current_channel_mask[4] = channel_mask[4]; } } - if( nextChanParams->AggrTimeOff <= TimerGetElapsedTime( nextChanParams->LastAggrTx ) ) - { + if (params->aggregate_timeoff <= _lora_time.get_elapsed_time( params->last_aggregate_tx_time)) { // Reset Aggregated time off - *aggregatedTimeOff = 0; + *aggregate_timeOff = 0; // Update bands Time OFF - nextTxDelay = update_band_timeoff( nextChanParams->Joined, nextChanParams->DutyCycleEnabled, Bands, US915_HYBRID_MAX_NB_BANDS ); + next_tx_delay = update_band_timeoff(params->joined, + params->dc_enabled, bands, + US915_HYBRID_MAX_NB_BANDS); // Search how many channels are enabled - nbEnabledChannels = CountNbOfEnabledChannels( nextChanParams->Datarate, - ChannelsMaskRemaining, Channels, - Bands, enabledChannels, &delayTx ); - } - else - { - delayTx++; - nextTxDelay = nextChanParams->AggrTimeOff - TimerGetElapsedTime( nextChanParams->LastAggrTx ); + nb_enabled_channels = enabled_channel_count(params->joined, + params->current_datarate, + current_channel_mask, + enabled_channels, &delay_tx); + } else { + delay_tx++; + next_tx_delay = params->aggregate_timeoff - _lora_time.get_elapsed_time(params->last_aggregate_tx_time); } - if( nbEnabledChannels > 0 ) - { + if (nb_enabled_channels > 0) { + // We found a valid channel - *channel = enabledChannels[get_random( 0, nbEnabledChannels - 1 )]; + *channel = enabled_channels[get_random(0, nb_enabled_channels - 1)]; // Disable the channel in the mask - disable_channel( ChannelsMaskRemaining, *channel, US915_HYBRID_MAX_NB_CHANNELS - 8 ); + disable_channel(current_channel_mask, *channel, US915_HYBRID_MAX_NB_CHANNELS - 8); *time = 0; return true; - } - else - { - if( delayTx > 0 ) - { + + } else { + + if (delay_tx > 0) { // Delay transmission due to AggregatedTimeOff or to a band time off - *time = nextTxDelay; + *time = next_tx_delay; return true; } + // Datarate not supported by any channel *time = 0; return false; } } -LoRaMacStatus_t LoRaPHYUS915Hybrid::add_channel(ChannelAddParams_t* channelAdd) +void LoRaPHYUS915Hybrid::set_tx_cont_mode(cw_mode_params_t* params, uint32_t given_frequency) { - return LORAMAC_STATUS_PARAMETER_INVALID; -} + (void)given_frequency; -bool LoRaPHYUS915Hybrid::remove_channel(ChannelRemoveParams_t* channelRemove) -{ - return LORAMAC_STATUS_PARAMETER_INVALID; -} + int8_t tx_power_limited = limit_tx_power(params->tx_power, + bands[channels[params->channel].band].max_tx_pwr, + params->datarate); -void LoRaPHYUS915Hybrid::set_tx_cont_mode(ContinuousWaveParams_t* continuousWave) -{ - int8_t txPowerLimited = LimitTxPower( continuousWave->TxPower, Bands[Channels[continuousWave->Channel].Band].TxMaxPower, continuousWave->Datarate, ChannelsMask ); - int8_t phyTxPower = 0; - uint32_t frequency = Channels[continuousWave->Channel].Frequency; + int8_t phy_tx_power = 0; + uint32_t frequency = channels[params->channel].frequency; // Calculate physical TX power - phyTxPower = compute_tx_power( txPowerLimited, US915_HYBRID_DEFAULT_MAX_ERP, 0 ); + phy_tx_power = compute_tx_power(tx_power_limited, US915_HYBRID_DEFAULT_MAX_ERP, 0); - _radio->set_tx_continuous_wave( frequency, phyTxPower, continuousWave->Timeout ); + _radio->lock(); + _radio->set_tx_continuous_wave(frequency, phy_tx_power, params->timeout); + _radio->unlock(); } -uint8_t LoRaPHYUS915Hybrid::apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset) +uint8_t LoRaPHYUS915Hybrid::apply_DR_offset(int8_t dr, int8_t drOffset) { - int8_t datarate = DatarateOffsetsUS915_HYBRID[dr][drOffset]; + int8_t datarate = datarate_offsets_US915_HYBRID[dr][drOffset]; - if( datarate < 0 ) - { + if (datarate < 0) { datarate = DR_0; } + return datarate; } + + +void LoRaPHYUS915Hybrid::reenable_500khz_channels(uint16_t mask, uint16_t* channelsMask) +{ + uint16_t blockMask = mask; + + for (uint8_t i = 0, j = 0; i < 4; i++, j += 2) { + channelsMask[i] = 0; + if ((blockMask & (1 << j)) != 0) { + channelsMask[i] |= 0x00FF; + } + + if ((blockMask & (1 << (j + 1))) != 0) { + channelsMask[i] |= 0xFF00; + } + } + + channelsMask[4] = blockMask; +} + +int8_t LoRaPHYUS915Hybrid::limit_tx_power(int8_t txPower, int8_t maxBandTxPower, + int8_t datarate) +{ + int8_t txPowerResult = txPower; + + // Limit tx power to the band max + txPowerResult = MAX(txPower, maxBandTxPower); + + if (datarate == DR_4) { + + // Limit tx power to max 26dBm + txPowerResult = MAX(txPower, TX_POWER_2); + + } else { + + if (num_active_channels(channel_mask, 0, 4) < 50) { + // Limit tx power to max 21dBm + txPowerResult = MAX(txPower, TX_POWER_5); + } + } + + return txPowerResult; +} + +bool LoRaPHYUS915Hybrid::validate_channel_mask(uint16_t* channel_masks) +{ + bool mask_state = false; + + uint16_t block1 = 0; + uint16_t block2 = 0; + uint8_t index = 0; + uint16_t temp_channel_masks[US915_HYBRID_CHANNEL_MASK_SIZE]; + + // Copy channels mask to not change the input + for (uint8_t i = 0; i < 4; i++) { + temp_channel_masks[i] = channel_masks[i]; + } + + for(uint8_t i = 0; i < 4; i++) { + block1 = temp_channel_masks[i] & 0x00FF; + block2 = temp_channel_masks[i] & 0xFF00; + + if (count_bits(block1, 16) > 5) { + + temp_channel_masks[i] &= block1; + temp_channel_masks[4] = 1 << ( i * 2 ); + mask_state = true; + index = i; + break; + + } else if( count_bits( block2, 16 ) > 5 ) { + + temp_channel_masks[i] &= block2; + temp_channel_masks[4] = 1 << ( i * 2 + 1 ); + mask_state = true; + index = i; + break; + + } + } + + // Do change the channel mask, if we have found a valid block. + if (mask_state == true) { + // Copy channels mask back again + for (uint8_t i = 0; i < 4; i++) { + channel_masks[i] = temp_channel_masks[i]; + + if (i != index) { + channel_masks[i] = 0; + } + } + + channel_masks[4] = temp_channel_masks[4]; + } + + return mask_state; +} diff --git a/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.h b/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.h index b84f571770..08f1b58a71 100644 --- a/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.h +++ b/features/lorawan/lorastack/phy/LoRaPHYUS915Hybrid.h @@ -29,11 +29,10 @@ * */ -#ifndef MBED_OS_LORAPHY_US915HYBRID_H_ +#ifndef MBED_OS_LORAPHY_US915_HYBRID_H_ #define MBED_OS_LORAPHY_US915_HYBRID_H_ #include "LoRaPHY.h" -#include "netsocket/LoRaRadio.h" /*! @@ -46,321 +45,73 @@ */ #define US915_HYBRID_MAX_NB_BANDS 1 -#define US915_HYBRID_CHANNELS_MASK_SIZE 6 +#define US915_HYBRID_CHANNEL_MASK_SIZE 5 class LoRaPHYUS915Hybrid : public LoRaPHY { public: - LoRaPHYUS915Hybrid(); + LoRaPHYUS915Hybrid(LoRaWANTimeHandler &lora_time); virtual ~LoRaPHYUS915Hybrid(); - /*! - * \brief The function gets a value of a specific PHY attribute. - * - * \param [in] getPhy A pointer to the function parameters. - * - * \retval The structure containing the PHY parameter. - */ - virtual PhyParam_t get_phy_params(GetPhyParams_t* getPhy ); + virtual void restore_default_channels(); - /*! - * \brief Updates the last TX done parameters of the current channel. - * - * \param [in] txDone A pointer to the function parameters. - */ - virtual void set_band_tx_done(SetBandTxDoneParams_t* txDone ); + virtual bool get_next_ADR(bool restore_channel_mask, int8_t& dr_out, + int8_t& tx_power_out, uint32_t& adr_ack_cnt); - /*! - * \brief Initializes the channels masks and the channels. - * - * \param [in] type Sets the initialization type. - */ - virtual void load_defaults(InitType_t type ); + virtual bool rx_config(rx_config_params_t* rxConfig, int8_t* datarate); - /*! - * \brief Verifies a parameter. - * - * \param [in] verify A pointer to the function parameters. - * - * \param [in] phyAttribute The attribute to verify. - * - * \retval True, if the parameter is valid. - */ - virtual bool verify(VerifyParams_t* verify, PhyAttribute_t phyAttribute ); + virtual bool tx_config(tx_config_params_t* tx_config, int8_t* tx_power, + lorawan_time_t* tx_toa); - /*! - * \brief The function parses the input buffer and sets up the channels of the CF list. - * - * \param [in] applyCFList A pointer to the function parameters. - */ - virtual void apply_cf_list(ApplyCFListParams_t* applyCFList ); + virtual uint8_t link_ADR_request(adr_req_params_t* params, + int8_t* dr_out, int8_t* tx_power_out, + uint8_t* nb_rep_out, + uint8_t* nb_bytes_parsed); - /*! - * \brief Sets a channels mask. - * - * \param [in] chanMaskSet A pointer to the function parameters. - * - * \retval True, if the channels mask could be set. - */ - virtual bool set_channel_mask(ChanMaskSetParams_t* chanMaskSet ); + virtual uint8_t accept_rx_param_setup_req(rx_param_setup_req_t* params); - /*! - * \brief Calculates the next datarate to set, when ADR is on or off. - * - * \param [in] adrNext A pointer to the function parameters. - * - * \param [out] drOut The calculated datarate for the next TX. - * - * \param [out] txPowOut The TX power for the next TX. - * - * \param [out] adrAckCounter The calculated ADR acknowledgement counter. - * - * \retval True, if an ADR request should be performed. - */ - virtual bool get_next_ADR(AdrNextParams_t* adrNext, int8_t* drOut, - int8_t* txPowOut, uint32_t* adrAckCounter ); + virtual int8_t get_alternate_DR(uint8_t nb_trials); - /*! - * \brief Configuration of the RX windows. - * - * \param [in] rxConfig A pointer to the function parameters. - * - * \param [out] datarate The datarate index set. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool rx_config(RxConfigParams_t* rxConfig, int8_t* datarate ); + virtual bool set_next_channel(channel_selection_params_t* params, + uint8_t* channel, lorawan_time_t* time, + lorawan_time_t* aggregate_timeoff); - /* - * RX window precise timing - * - * For more details, please consult the following document, chapter 3.1.2. - * http://www.semtech.com/images/datasheet/SX1272_settings_for_LoRaWAN_v2.0.pdf - * or - * http://www.semtech.com/images/datasheet/SX1276_settings_for_LoRaWAN_v2.0.pdf - * - * Downlink start: T = Tx + 1s (+/- 20 us) - * | - * TRxEarly | TRxLate - * | | | - * | | +---+---+---+---+---+---+---+---+ - * | | | Latest Rx window | - * | | +---+---+---+---+---+---+---+---+ - * | | | - * +---+---+---+---+---+---+---+---+ - * | Earliest Rx window | - * +---+---+---+---+---+---+---+---+ - * | - * +---+---+---+---+---+---+---+---+ - *Downlink preamble 8 symbols | | | | | | | | | - * +---+---+---+---+---+---+---+---+ - * - * Worst case Rx window timings - * - * TRxLate = DEFAULT_MIN_RX_SYMBOLS * tSymbol - RADIO_WAKEUP_TIME - * TRxEarly = 8 - DEFAULT_MIN_RX_SYMBOLS * tSymbol - RxWindowTimeout - RADIO_WAKEUP_TIME - * - * TRxLate - TRxEarly = 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * - * RxOffset = ( TRxLate + TRxEarly ) / 2 - * - * RxWindowTimeout = ( 2 * DEFAULT_MIN_RX_SYMBOLS - 8 ) * tSymbol + 2 * DEFAULT_SYSTEM_MAX_RX_ERROR - * RxOffset = 4 * tSymbol - RxWindowTimeout / 2 - RADIO_WAKE_UP_TIME - * - * The minimum value of RxWindowTimeout must be 5 symbols which implies that the system always tolerates at least an error of 1.5 * tSymbol - */ - /*! - * Computes the RX window timeout and offset. - * - * \param [in] datarate The RX window datarate index to be used. - * - * \param [in] minRxSymbols The minimum number of symbols required to detect an RX frame. - * - * \param [in] rxError The system maximum timing error of the receiver in milliseconds. - * The receiver will turn on in a [-rxError : +rxError] ms - * interval around RxOffset. - * - * \param [out] rxConfigParams Returns the updated WindowTimeout and WindowOffset fields. - */ - virtual void compute_rx_win_params(int8_t datarate, - uint8_t minRxSymbols, - uint32_t rxError, - RxConfigParams_t *rxConfigParams); + virtual void set_tx_cont_mode(cw_mode_params_t* continuousWave, + uint32_t frequency = 0); - /*! - * \brief TX configuration. - * - * \param [in] txConfig A pointer to the function parameters. - * - * \param [out] txPower The TX power index set. - * - * \param [out] txTimeOnAir The time-on-air of the frame. - * - * \retval True, if the configuration was applied successfully. - */ - virtual bool tx_config(TxConfigParams_t* txConfig, int8_t* txPower, - TimerTime_t* txTimeOnAir ); - - /*! - * \brief The function processes a Link ADR request. - * - * \param [in] linkAdrReq A pointer to the function parameters. - * - * \param [out] drOut The datarate applied. - * - * \param [out] txPowOut The TX power applied. - * - * \param [out] nbRepOut The number of repetitions to apply. - * - * \param [out] nbBytesParsed The number of bytes parsed. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t link_ADR_request(LinkAdrReqParams_t* linkAdrReq, - int8_t* drOut, int8_t* txPowOut, - uint8_t* nbRepOut, - uint8_t* nbBytesParsed ); - - /*! - * \brief The function processes a RX parameter setup request. - * - * \param [in] rxParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t setup_rx_params(RxParamSetupReqParams_t* rxParamSetupReq ); - - /*! - * \brief The function processes a new channel request. - * - * \param [in] newChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t request_new_channel(NewChannelReqParams_t* newChannelReq ); - - /*! - * \brief The function processes a TX ParamSetup request. - * - * \param [in] txParamSetupReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - * Returns -1, if the functionality is not implemented. In this case, the end node - * shall ignore the command. - */ - virtual int8_t setup_tx_params(TxParamSetupReqParams_t* txParamSetupReq ); - - /*! - * \brief The function processes a DlChannel request. - * - * \param [in] dlChannelReq A pointer to the function parameters. - * - * \retval The status of the operation, according to the LoRaMAC specification. - */ - virtual uint8_t dl_channel_request(DlChannelReqParams_t* dlChannelReq ); - - /*! - * \brief Alternates the datarate of the channel for the join request. - * - * \param [in] alternateDr A pointer to the function parameters. - * - * \retval The datarate to apply. - */ - virtual int8_t get_alternate_DR(AlternateDrParams_t* alternateDr ); - - /*! - * \brief Calculates the back-off time. - * - * \param [in] calcBackOff A pointer to the function parameters. - */ - virtual void calculate_backoff(CalcBackOffParams_t* calcBackOff ); - - /*! - * \brief Searches and sets the next random available channel. - * - * \param [in] nextChanParams The parameters for the next channel. - * - * \param [out] channel The next channel to use for TX. - * - * \param [out] time The time to wait for the next transmission according to the duty cycle. - * - * \param [out] aggregatedTimeOff Updates the aggregated time off. - * - * \retval The function status [1: OK, 0: Unable to find a channel on the current datarate]. - */ - virtual bool set_next_channel(NextChanParams_t* nextChanParams, - uint8_t* channel, TimerTime_t* time, - TimerTime_t* aggregatedTimeOff ); - - /*! - * \brief Adds a channel. - * - * \param [in] channelAdd A pointer to the function parameters. - * - * \retval The status of the operation. - */ - virtual LoRaMacStatus_t add_channel(ChannelAddParams_t* channelAdd ); - - /*! - * \brief Removes a channel. - * - * \param [in] channelRemove A pointer to the function parameters. - * - * \retval True, if the channel was removed successfully. - */ - virtual bool remove_channel(ChannelRemoveParams_t* channelRemove ); - - /*! - * \brief Sets the radio into continuous wave mode. - * - * \param [in] continuousWave A pointer to the function parameters. - */ - virtual void set_tx_cont_mode(ContinuousWaveParams_t* continuousWave ); - - /*! - * \brief Computes a new datarate according to the given offset. - * - * \param [in] downlinkDwellTime The downlink dwell time configuration. 0: No limit, 1: 400ms - * - * \param [in] dr The current datarate. - * - * \param [in] drOffset The offset to be applied. - * - * \retval newDr The computed datarate. - */ - virtual uint8_t apply_DR_offset(uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset ); + virtual uint8_t apply_DR_offset(int8_t dr, int8_t dr_offset); private: - int8_t LimitTxPower( int8_t txPower, int8_t maxBandTxPower, int8_t datarate, uint16_t* channelsMask ); - uint8_t CountNbOfEnabledChannels( uint8_t datarate, uint16_t* channelsMask, ChannelParams_t* channels, Band_t* bands, uint8_t* enabledChannels, uint8_t* delayTx ); + int8_t limit_tx_power(int8_t tx_power, int8_t max_band_tx_power, int8_t datarate); + bool validate_channel_mask(uint16_t* channel_mask); + void reenable_500khz_channels(uint16_t mask, uint16_t* channel_mask); - // Global attributes /*! * LoRaMAC channels */ - ChannelParams_t Channels[US915_HYBRID_MAX_NB_CHANNELS]; + channel_params_t channels[US915_HYBRID_MAX_NB_CHANNELS]; /*! * LoRaMac bands */ - Band_t Bands[US915_HYBRID_MAX_NB_BANDS]; + band_t bands[US915_HYBRID_MAX_NB_BANDS]; /*! * LoRaMac channels mask */ - uint16_t ChannelsMask[US915_HYBRID_CHANNELS_MASK_SIZE]; + uint16_t channel_mask[US915_HYBRID_CHANNEL_MASK_SIZE]; /*! - * LoRaMac channels remaining + * Previously used channel mask */ - uint16_t ChannelsMaskRemaining[US915_HYBRID_CHANNELS_MASK_SIZE]; + uint16_t current_channel_mask[US915_HYBRID_CHANNEL_MASK_SIZE]; /*! - * LoRaMac channels default mask + * LoRaMac default channel mask */ - uint16_t ChannelsDefaultMask[US915_HYBRID_CHANNELS_MASK_SIZE]; + uint16_t default_channel_mask[US915_HYBRID_CHANNEL_MASK_SIZE]; }; #endif /* MBED_OS_LORAPHY_US915HYBRID_H_ */ diff --git a/features/lorawan/lorastack/phy/lora_phy_ds.h b/features/lorawan/lorastack/phy/lora_phy_ds.h index a364a5ef42..ebcd0f6ae0 100644 --- a/features/lorawan/lorastack/phy/lora_phy_ds.h +++ b/features/lorawan/lorastack/phy/lora_phy_ds.h @@ -527,7 +527,7 @@ /*! * Enumeration of PHY attributes. */ -typedef enum ePhyAttribute +typedef enum phy_attributes__e { /*! * The minimum RX datarate. @@ -620,11 +620,11 @@ typedef enum ePhyAttribute /*! * The channels mask. */ - PHY_CHANNELS_MASK, + PHY_CHANNEL_MASK, /*! - * The channels default mask. + * The default channel mask. */ - PHY_CHANNELS_DEFAULT_MASK, + PHY_DEFAULT_CHANNEL_MASK, /*! * The maximum number of supported channels. */ @@ -633,6 +633,13 @@ typedef enum ePhyAttribute * 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. */ @@ -661,517 +668,481 @@ typedef enum ePhyAttribute * The next lower datarate. */ PHY_NEXT_LOWER_TX_DR -}PhyAttribute_t; +} phy_attributes_t; /*! - * Enumeration of initialization types. + * Keeps value in response to a call to + * get_phy_params() API. */ -typedef enum eInitType -{ - /*! - * Performs an initialization and overwrites all existing data. - */ - INIT_TYPE_INIT, - /*! - * Restores default channels only. - */ - INIT_TYPE_RESTORE -}InitType_t; - -/*! - * Selects a given or a default channel mask. - */ -typedef enum eChannelsMask -{ - /*! - * The channels mask. - */ - CHANNELS_MASK, - /*! - * The channels default mask. - */ - CHANNELS_DEFAULT_MASK -}ChannelsMask_t; - -/*! - * The union for the structure uGetPhyParams. - */ -typedef union uPhyParam +typedef union phy_param_u { /*! * A parameter value. */ - uint32_t Value; + uint32_t value; /*! * A floating point value. */ - float fValue; + float f_value; /*! * A pointer to the channels mask. */ - uint16_t* ChannelsMask; + uint16_t* channel_mask; /*! * A pointer to the channels. */ - ChannelParams_t* Channels; -}PhyParam_t; + channel_params_t* channel_params; +} phy_param_t; -/*! +/** * The parameter structure for the function RegionGetPhyParam. */ -typedef struct sGetPhyParams +typedef struct { - /*! + /** * Set up the parameter to get. */ - PhyAttribute_t Attribute; - /*! - * Datarate. + 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; - /*! - * Uplink dwell time. - * The parameter is needed for the following queries: - * PHY_MIN_TX_DR, PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER, PHY_NEXT_LOWER_TX_DR. - */ - uint8_t UplinkDwellTime; - /*! - * Downlink dwell time. - * The parameter is needed for the following queries: - * PHY_MIN_RX_DR, PHY_MAX_PAYLOAD, PHY_MAX_PAYLOAD_REPEATER. - */ - uint8_t DownlinkDwellTime; -}GetPhyParams_t; + int8_t datarate; -/*! +} get_phy_params_t; + +/** * The parameter structure for the function RegionSetBandTxDone. */ -typedef struct sSetBandTxDoneParams +typedef struct { - /*! + /** * The channel to update. */ - uint8_t Channel; - /*! + uint8_t channel; + /** * Joined set to true, if the node has joined the network. */ - bool Joined; - /*! + bool joined; + /** * The last TX done time. */ - TimerTime_t LastTxDoneTime; -}SetBandTxDoneParams_t; + lorawan_time_t last_tx_done_time; +} set_band_txdone_params_t; -/*! - * The parameter structure for the function RegionVerify. +/** + * The parameter verification structure. */ -typedef union uVerifyParams +typedef union { - /*! + /** * The TX power to verify. */ - int8_t TxPower; - /*! + int8_t tx_power; + /** * Set to true, if the duty cycle is enabled, otherwise false. */ - bool DutyCycle; - /*! + bool duty_cycle; + /** * The number of join trials. */ - uint8_t NbJoinTrials; - /*! + uint8_t nb_join_trials; + /** * The datarate to verify. */ - struct sDatarateParams - { - /*! - * The datarate to verify. - */ - int8_t Datarate; - /*! - * The downlink dwell time. - */ - uint8_t DownlinkDwellTime; - /*! - * The uplink dwell time. - */ - uint8_t UplinkDwellTime; - }DatarateParams; -}VerifyParams_t; + int8_t datarate; -/*! +} verification_params_t; + +/** * The parameter structure for the function RegionApplyCFList. */ -typedef struct sApplyCFListParams +typedef struct { - /*! + /** * The payload containing the CF list. */ - uint8_t* Payload; - /*! + uint8_t* payload; + /** * The size of the payload. */ - uint8_t Size; -}ApplyCFListParams_t; + uint8_t size; +} cflist_params_t; -/*! - * The parameter structure for the function RegionChanMaskSet. +/** + * TX configuration parameters. */ -typedef struct sChanMaskSetParams +typedef struct { - /*! - * A pointer to the channels mask which should be set. - */ - uint16_t* ChannelsMaskIn; - /*! - * A pointer to the channels mask which should be set. - */ - ChannelsMask_t ChannelsMaskType; -}ChanMaskSetParams_t; - -/*! - * The parameter structure for the function RegionAdrNext. - */ -typedef struct sAdrNextParams -{ - /*! - * Set to true, if the function should update the channels mask. - */ - bool UpdateChanMask; - /*! - * Set to true, if ADR is enabled. - */ - bool AdrEnabled; - /*! - * The ADR ack counter. - */ - uint32_t AdrAckCounter; - /*! - * The datarate used currently. - */ - int8_t Datarate; - /*! - * The TX power used currently. - */ - int8_t TxPower; - /*! - * UplinkDwellTime - */ - uint8_t UplinkDwellTime; -}AdrNextParams_t; - -/*! - * The parameter structure for the function RegionRxConfig. - */ -typedef struct sRxConfigParams -{ - /*! - * The RX channel. - */ - uint8_t Channel; - /*! - * The RX datarate. - */ - int8_t Datarate; - /*! - * The RX bandwidth. - */ - uint8_t Bandwidth; - /*! - * The RX datarate offset. - */ - int8_t DrOffset; - /*! - * The RX frequency. - */ - uint32_t Frequency; - /*! - * The RX window timeout - */ - uint32_t WindowTimeout; - /*! - * The RX window offset - */ - int32_t WindowOffset; - /*! - * The downlink dwell time. - */ - uint8_t DownlinkDwellTime; - /*! - * Set to true, if a repeater is supported. - */ - bool RepeaterSupport; - /*! - * Set to true, if RX should be continuous. - */ - bool RxContinuous; - /*! - * Sets the RX window. 0: RX window 1, 1: RX window 2. - */ - bool Window; -}RxConfigParams_t; - -/*! - * The parameter structure for the function RegionTxConfig. - */ -typedef struct sTxConfigParams -{ - /*! + /** * The TX channel. */ - uint8_t Channel; - /*! + uint8_t channel; + /** * The TX datarate. */ - int8_t Datarate; - /*! + int8_t datarate; + /** * The TX power. */ - int8_t TxPower; - /*! + int8_t tx_power; + /** * The Max EIRP, if applicable. */ - float MaxEirp; - /*! + float max_eirp; + /** * The antenna gain, if applicable. */ - float AntennaGain; - /*! + float antenna_gain; + /** * The frame length to set up. */ - uint16_t PktLen; -}TxConfigParams_t; + uint16_t pkt_len; +} tx_config_params_t; -/*! - * The parameter structure for the function RegionLinkAdrReq. +/** + * This structure contains parameters for ADR request coming from + * network server. */ -typedef struct sLinkAdrReqParams +typedef struct { /*! * A pointer to the payload containing the MAC commands. */ - uint8_t* Payload; + uint8_t* payload; /*! * The size of the payload. */ - uint8_t PayloadSize; + uint8_t payload_size; /*! * The uplink dwell time. */ - uint8_t UplinkDwellTime; + uint8_t ul_dwell_time; /*! * Set to true, if ADR is enabled. */ - bool AdrEnabled; + bool adr_enabled; /*! * The current datarate. */ - int8_t CurrentDatarate; + int8_t current_datarate; /*! * The current TX power. */ - int8_t CurrentTxPower; + int8_t current_tx_power; /*! * The current number of repetitions. */ - uint8_t CurrentNbRep; -}LinkAdrReqParams_t; + uint8_t current_nb_rep; +} adr_req_params_t; -/*! - * The parameter structure for the function RegionRxParamSetupReq. +/** + * Structure containing data for local ADR settings */ -typedef struct sRxParamSetupReqParams +typedef struct link_adr_params_s +{ + /** + * The number of repetitions. + */ + uint8_t nb_rep; + /** + * Datarate. + */ + int8_t datarate; + /** + * TX power. + */ + int8_t tx_power; + /** + * Channels mask control field. + */ + uint8_t ch_mask_ctrl; + /** + * Channels mask field. + */ + uint16_t channel_mask; +} link_adr_params_t; + +/** + * Structure used to store ADR values received from network + * for verification (legality) purposes. + */ +typedef struct verify_adr_params_s { /*! + * The current status of the AdrLinkRequest. + */ + uint8_t status; + /*! + * Set to true, if ADR is enabled. + */ + bool adr_enabled; + /*! + * The datarate the AdrLinkRequest wants to set. + */ + int8_t datarate; + /*! + * The TX power the AdrLinkRequest wants to set. + */ + int8_t tx_power; + /*! + * The number of repetitions the AdrLinkRequest wants to set. + */ + uint8_t nb_rep; + /*! + * The current datarate the node is using. + */ + int8_t current_datarate; + /*! + * The current TX power the node is using. + */ + int8_t current_tx_power; + /*! + * The current number of repetitions the node is using. + */ + int8_t current_nb_rep; + + /*! + * A pointer to the first element of the channels mask. + */ + uint16_t* channel_mask; +} verify_adr_params_t; + +/** + * Contains rx parameter setup request coming from + * network server. + */ +typedef struct rx_param_setup_req_s +{ + /** * The datarate to set up. */ - int8_t Datarate; - /*! + int8_t datarate; + /** * The datarate offset. */ - int8_t DrOffset; - /*! + int8_t dr_offset; + /** * The frequency to set up. */ - uint32_t Frequency; -}RxParamSetupReqParams_t; + uint32_t frequency; +} rx_param_setup_req_t; -/*! - * The parameter structure for the function RegionNewChannelReq. +/** + * Contains tx parameter setup request coming from + * network server. */ -typedef struct sNewChannelReqParams +typedef struct tx_param_setup_req_s { - /*! - * A pointer to the new channels. - */ - ChannelParams_t* NewChannel; - /*! - * The channel ID. - */ - int8_t ChannelId; -}NewChannelReqParams_t; - -/*! - * The parameter structure for the function RegionTxParamSetupReq. - */ -typedef struct sTxParamSetupReqParams -{ - /*! + /** * The uplink dwell time. */ - uint8_t UplinkDwellTime; - /*! + uint8_t ul_dwell_time; + /** * The downlink dwell time. */ - uint8_t DownlinkDwellTime; - /*! + uint8_t dl_dwell_time; + /** * The max EIRP. */ - uint8_t MaxEirp; -}TxParamSetupReqParams_t; + uint8_t max_eirp; +} tx_param_setup_req_t; -/*! +/** + * A structure that holds new channel parameters coming + * from the network server. + */ +typedef struct new_channel_req_params_s +{ + /** + * A pointer to the new channel's parameters. + */ + channel_params_t* new_channel; + /** + * The channel ID. + */ + int8_t channel_id; + +} new_channel_req_params_t; + +/** * The parameter structure for the function RegionDlChannelReq. */ -typedef struct sDlChannelReqParams +typedef struct dl_channel_req_params_s { - /*! + /** * The channel ID to add the frequency. */ - uint8_t ChannelId; - /*! + uint8_t channel_id; + /** * The alternative frequency for the Rx1 window. */ - uint32_t Rx1Frequency; -}DlChannelReqParams_t; - -/*! - * The parameter structure for the function RegionAlternateDr. - */ -typedef struct sAlternateDrParams -{ - /*! - * The number of trials. - */ - uint16_t NbTrials; -}AlternateDrParams_t; + uint32_t rx1_frequency; +} dl_channel_req_params_t; /*! * The parameter structure for the function RegionCalcBackOff. */ -typedef struct sCalcBackOffParams +typedef struct backoff_params_s { - /*! + /** * Set to true, if the node has already joined a network, otherwise false. */ - bool Joined; - /*! - * Joined set to true, if the last uplink was a join request. + bool joined; + /** + * set to true, if the last uplink was a join request. */ - bool LastTxIsJoinRequest; - /*! + bool last_tx_was_join_req; + /** * Set to true, if the duty cycle is enabled, otherwise false. */ - bool DutyCycleEnabled; - /*! + bool dc_enabled; + /** * The current channel index. */ - uint8_t Channel; - /*! + uint8_t channel; + /** * Elapsed time since the start of the node. */ - TimerTime_t ElapsedTime; - /*! + lorawan_time_t elapsed_time; + /** * Time-on-air of the last transmission. */ - TimerTime_t TxTimeOnAir; -}CalcBackOffParams_t; + lorawan_time_t tx_toa; -/*! +} backoff_params_t; + +/** * The parameter structure for the function RegionNextChannel. */ -typedef struct sNextChanParams +typedef struct channel_selection_params_s { - /*! + /** * The aggregated time-off time. */ - TimerTime_t AggrTimeOff; - /*! + lorawan_time_t aggregate_timeoff; + /** * The time of the last aggregated TX. */ - TimerTime_t LastAggrTx; - /*! + lorawan_time_t last_aggregate_tx_time; + /** * The current datarate. */ - int8_t Datarate; - /*! + int8_t current_datarate; + /** * Set to true, if the node has already joined a network, otherwise false. */ - bool Joined; - /*! + bool joined; + /** * Set to true, if the duty cycle is enabled, otherwise false. */ - bool DutyCycleEnabled; -}NextChanParams_t; - -/*! - * The parameter structure for the function RegionChannelsAdd. - */ -typedef struct sChannelAddParams -{ - /*! - * A pointer to the new channel to add. - */ - ChannelParams_t* NewChannel; - /*! - * The channel ID to add. - */ - uint8_t ChannelId; -}ChannelAddParams_t; - -/*! - * The parameter structure for the function RegionChannelsRemove. - */ -typedef struct sChannelRemoveParams -{ - /*! - * The channel ID to remove. - */ - uint8_t ChannelId; -}ChannelRemoveParams_t; + bool dc_enabled; +} channel_selection_params_t; /*! * The parameter structure for the function RegionContinuousWave. */ -typedef struct sContinuousWaveParams +typedef struct continuous_wave_mode_params_s { /*! * The current channel index. */ - uint8_t Channel; + uint8_t channel; /*! * The datarate. Used to limit the TX power. */ - int8_t Datarate; + int8_t datarate; /*! * The TX power to set up. */ - int8_t TxPower; + int8_t tx_power; /*! * The max EIRP, if applicable. */ - float MaxEirp; + float max_eirp; /*! * The antenna gain, if applicable. */ - float AntennaGain; + float antenna_gain; /*! * Specifies the time the radio will stay in CW mode. */ - uint16_t Timeout; -}ContinuousWaveParams_t; + uint16_t timeout; +} cw_mode_params_t; + +typedef struct { + void *table; + uint8_t size; +} loraphy_table_t; + +typedef struct { + channel_params_t *channel_list; + uint8_t channel_list_size; + + uint16_t *mask; + uint16_t *default_mask; + uint8_t mask_size; + +} loraphy_channels_t; + +typedef struct { + bool duty_cycle_enabled; + bool accept_tx_param_setup_req; + bool fsk_supported; + bool cflist_supported; + bool custom_channelplans_supported; + bool dl_channel_req_supported; + + uint8_t default_channel_cnt; + uint8_t cflist_channel_cnt; + uint8_t max_channel_cnt; + uint8_t min_tx_power; + uint8_t max_tx_power; + uint8_t default_tx_power; + uint8_t adr_ack_limit; + uint8_t adr_ack_delay; + + uint8_t min_tx_datarate; + uint8_t max_tx_datarate; + uint8_t min_rx_datarate; + uint8_t max_rx_datarate; + uint8_t default_datarate; + uint8_t default_max_datarate; + uint8_t min_rx1_dr_offset; + uint8_t max_rx1_dr_offset; + uint8_t default_rx1_dr_offset; + uint8_t dwell_limit_datarate; + + uint16_t max_rx_window; + uint16_t recv_delay1; + uint16_t recv_delay2; + uint16_t join_accept_delay1; + uint16_t join_accept_delay2; + uint16_t join_channel_mask; + uint16_t max_fcnt_gap; + uint16_t ack_timeout; + uint16_t ack_timeout_rnd; + + float default_max_eirp; + float default_antenna_gain; + + uint8_t rx_window2_datarate; + uint32_t rx_window2_frequency; + + loraphy_table_t bands; + loraphy_table_t bandwidths; + loraphy_table_t datarates; + loraphy_table_t payloads; + loraphy_table_t payloads_with_repeater; + + loraphy_channels_t channels; + + + unsigned ul_dwell_time_setting : 1; + unsigned dl_dwell_time_setting : 1; + +} loraphy_params_t; #endif /* MBED_OS_LORA_PHY_DATASTRUCTURES_ */ diff --git a/features/lorawan/mbed_lib.json b/features/lorawan/mbed_lib.json index e4ddf214fa..f88e0a5cc0 100644 --- a/features/lorawan/mbed_lib.json +++ b/features/lorawan/mbed_lib.json @@ -2,16 +2,7 @@ "name": "lora", "config": { "phy": { - "help": ["Select LoRa PHY layer. See README.md for more information. Default: 0 = LORA_PHY_EU868", - " 1 = LORA_PHY_AS923", - " 2 = LORA_PHY_AU915", - " 3 = LORA_PHY_CN470", - " 4 = LORA_PHY_CN779", - " 5 = LORA_PHY_EU433", - " 6 = LORA_PHY_IN865", - " 7 = LORA_PHY_KR920", - " 8 = LORA_PHY_US915", - " 9 = LORA_PHY_US915_HYBRID"], + "help": "LoRa PHY region. 0 = EU868 (default), 1 = AS923, 2 = AU915, 3 = CN470, 4 = CN779, 5 = EU433, 6 = IN865, 7 = KR920, 8 = US915, 9 = US915_HYBRID", "value": "0" }, "over-the-air-activation": { @@ -19,8 +10,8 @@ "value": true }, "nb-trials": { - "help": "Indicates how many times join can be tried, default: 8", - "value": 8 + "help": "Indicates how many times join can be tried, default: 12", + "value": 12 }, "device-eui": { "help": "Mote device IEEE EUI", @@ -34,10 +25,6 @@ "help": "AES encryption/decryption cipher application key", "value": "{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}" }, - "network-id": { - "help": "Current network ID", - "value": 0 - }, "device-address": { "help": "Device address on the network", "value": "0x00000000" @@ -67,12 +54,12 @@ "value": true }, "duty-cycle-on": { - "help": "Enables/disables duty cycling. NOTE: Disable only for testing. Mandatory in many regions.", - "value": true + "help": "Enables/disables duty cycling. NOTE: Disable only for testing. Mandatory in many regions.", + "value": true }, "lbt-on": { - "help": "Enables/disables LBT. NOTE: [This feature is not yet integrated].", - "value": false + "help": "Enables/disables LBT. NOTE: [This feature is not yet integrated].", + "value": false } } } diff --git a/features/lorawan/system/LoRaWANTimer.cpp b/features/lorawan/system/LoRaWANTimer.cpp index f721fa3f30..4497f75db2 100644 --- a/features/lorawan/system/LoRaWANTimer.cpp +++ b/features/lorawan/system/LoRaWANTimer.cpp @@ -20,41 +20,45 @@ SPDX-License-Identifier: BSD-3-Clause #include "lorawan/system/LoRaWANTimer.h" -static events::EventQueue *_queue = NULL; +LoRaWANTimeHandler::LoRaWANTimeHandler() + : _queue(NULL) +{ +} -void TimerTimeCounterInit(events::EventQueue *queue) +LoRaWANTimeHandler::~LoRaWANTimeHandler() +{ +} + +void LoRaWANTimeHandler::activate_timer_subsystem(events::EventQueue *queue) { _queue = queue; } -TimerTime_t TimerGetCurrentTime( void ) +lorawan_time_t LoRaWANTimeHandler::get_current_time( void ) { const uint32_t current_time = _queue->tick(); - return (TimerTime_t)current_time; + return (lorawan_time_t)current_time; } -TimerTime_t TimerGetElapsedTime( TimerTime_t savedTime ) +lorawan_time_t LoRaWANTimeHandler::get_elapsed_time(lorawan_time_t saved_time) { - return TimerGetCurrentTime() - savedTime; + return get_current_time() - saved_time; } -void TimerInit( TimerEvent_t *obj, void ( *callback )( void ) ) +void LoRaWANTimeHandler::init(timer_event_t &obj, mbed::Callback callback) { - obj->value = 0; - obj->Callback = callback; + obj.callback = callback; + obj.timer_id = 0; } -void TimerStart( TimerEvent_t *obj ) +void LoRaWANTimeHandler::start(timer_event_t &obj, const uint32_t timeout) { - obj->Timer.get()->attach_us( mbed::callback( obj->Callback ), obj->value * 1000 ); + obj.timer_id = _queue->call_in(timeout, obj.callback); + MBED_ASSERT(obj.timer_id != 0); } -void TimerStop( TimerEvent_t *obj ) +void LoRaWANTimeHandler::stop(timer_event_t &obj) { - obj->Timer.get()->detach( ); -} - -void TimerSetValue( TimerEvent_t *obj, uint32_t value ) -{ - obj->value = value; + _queue->cancel(obj.timer_id); + obj.timer_id = 0; } diff --git a/features/lorawan/system/LoRaWANTimer.h b/features/lorawan/system/LoRaWANTimer.h index 92d403b2be..2b5118eb67 100644 --- a/features/lorawan/system/LoRaWANTimer.h +++ b/features/lorawan/system/LoRaWANTimer.h @@ -21,92 +21,63 @@ SPDX-License-Identifier: BSD-3-Clause #ifndef MBED_LORAWAN_SYS_TIMER_H__ #define MBED_LORAWAN_SYS_TIMER_H__ -#include "drivers/Timer.h" -#include "drivers/Ticker.h" +#include #include "lorawan/system/lorawan_data_structures.h" #include "events/EventQueue.h" -#include "platform/SingletonPtr.h" -/*! - * \brief Timer object description - */ -typedef struct TimerEvent_s +class LoRaWANTimeHandler { - uint32_t value; - void ( *Callback )( void ); - SingletonPtr Timer; -}TimerEvent_t; +public: + LoRaWANTimeHandler(); + ~LoRaWANTimeHandler(); -/*! - * \brief Initializes the timer object. - * - * \remark The TimerSetValue function must be called before starting the timer. - * This function initializes the timestamp and reloads the value at 0. - * - * \param [in] obj The structure containing the timer object parameters. - * \param [in] callback The function callback called at the end of the timeout. - */ -void TimerInit( TimerEvent_t *obj, void ( *callback )( void ) ); + /** Activates the timer subsystem. + * + * Embeds EventQueue object to timer subsystem which is subsequently + * used to extract timer information. + * + * @param [in] queue Handle to EventQueue object + */ + void activate_timer_subsystem(events::EventQueue *queue); -/*! - * \brief Starts and adds the timer object to the list of timer events. - * - * \param [in] obj The structure containing the timer object parameters. - */ -void TimerStart( TimerEvent_t *obj ); + /** Read the current time. + * + * @return time The current time. + */ + lorawan_time_t get_current_time(void); -/*! - * \brief Stops and removes the timer object from the list of timer events. - * - * \param [in] obj The structure containing the timer object parameters. - */ -void TimerStop( TimerEvent_t *obj ); + /** Return the time elapsed since a fixed moment in time. + * + * @param [in] saved_time The fixed moment in time. + * @return time The elapsed time. + */ + lorawan_time_t get_elapsed_time(lorawan_time_t saved_time); -/*! - * \brief Resets the timer object. - * - * \param [in] obj The structure containing the timer object parameters. - */ -void TimerReset( TimerEvent_t *obj ); + /** Initializes the timer object. + * + * @remark The TimerSetValue function must be called before starting the timer. + * This function initializes the time-stamp and reloads the value at 0. + * + * @param [in] obj The structure containing the timer object parameters. + * @param [in] callback The function callback called at the end of the timeout. + */ + void init(timer_event_t &obj, mbed::Callback callback); -/*! - * \brief Set a new timeout value. - * - * \param [in] obj The structure containing the timer object parameters. - * \param [in] value The new timeout value. - */ -void TimerSetValue( TimerEvent_t *obj, uint32_t value ); + /** Starts and adds the timer object to the list of timer events. + * + * @param [in] obj The structure containing the timer object parameters. + * @param [in] timeout The new timeout value. + */ + void start(timer_event_t &obj, const uint32_t timeout); -/*! - * \brief Initializes the timer used to get the current time. - * - * \remark The current time corresponds to the time since system startup. - * - * \param [in] queue Handle to EventQueue object - */ -void TimerTimeCounterInit(events::EventQueue *queue); + /** Stops and removes the timer object from the list of timer events. + * + * @param [in] obj The structure containing the timer object parameters. + */ + void stop(timer_event_t &obj); -/*! - * \brief Read the current time. - * - * \retval time The current time. - */ -TimerTime_t TimerGetCurrentTime( void ); - -/*! - * \brief Return the time elapsed since a fixed moment in time. - * - * \param [in] savedTime The fixed moment in time. - * \retval time The elapsed time. - */ -TimerTime_t TimerGetElapsedTime( TimerTime_t savedTime ); - -/*! - * \brief Return the time elapsed since a fixed moment in time. - * - * \param [in] eventInFuture The fixed moment in the future. - * \retval time The difference between now and a future event. - */ -TimerTime_t TimerGetFutureTime( TimerTime_t eventInFuture ); +private: + events::EventQueue *_queue; +}; #endif // MBED_LORAWAN_SYS_TIMER_H__ diff --git a/features/lorawan/system/lorawan_data_structures.h b/features/lorawan/system/lorawan_data_structures.h index 0e61263e42..8a45cce370 100644 --- a/features/lorawan/system/lorawan_data_structures.h +++ b/features/lorawan/system/lorawan_data_structures.h @@ -29,8 +29,8 @@ /*! * \brief Timer time variable definition */ -#ifndef TimerTime_t -typedef uint32_t TimerTime_t; +#ifndef lorawan_time_t +typedef uint32_t lorawan_time_t; #endif // Radio wake-up time from sleep - unit ms. @@ -44,6 +44,21 @@ typedef uint32_t TimerTime_t; #define MSG_MULTICAST_FLAG 0x04 #define MSG_PROPRIETARY_FLAG 0x08 +/** + * A macro to test a if a bit is on in a channel mask or not. + */ +//#define MASK_BIT_TEST(mask, bit) (mask & (1U << bit)) +//#define MASK_BIT_TEST(mask, bit) ((mask)[(bit) / 16] & (1U << ((bit) % 16))) +/** + * A macro to clear a bit in a channel mask. + */ +//#define MASK_BIT_CLEAR(mask, bit) (mask &= ~(1 << bit)) + +/** + * A macro to clear a bit in a channel mask. + */ +//#define MASK_BIT_SET(mask, bit) (mask |= (1 << bit)) + /** * Bit mask for message flags */ @@ -88,13 +103,27 @@ typedef uint32_t TimerTime_t; */ #define LORA_MAX_NB_CHANNELS 16 +/** + * Maximum PHY layer payload size for reception. + */ +#define LORAMAC_PHY_MAXPAYLOAD 255 + +/** + * + * Default user application maximum data size for transmission + */ +// reject if user tries to set more than MTU +#if MBED_CONF_LORA_TX_MAX_SIZE > 255 + #warning "Cannot set TX Max size more than MTU=255" + #define MBED_CONF_LORA_TX_MAX_SIZE 255 +#endif + /*! * LoRaWAN device classes definition. * * LoRaWAN Specification V1.0.2, chapter 2.1. */ -typedef enum eDeviceClass -{ +typedef enum { /*! * LoRaWAN device class A. * @@ -113,17 +142,16 @@ typedef enum eDeviceClass * LoRaWAN Specification V1.0.2, chapter 17. */ CLASS_C, -}DeviceClass_t; +} device_class_t; /*! * LoRaMAC channel parameters definition. */ -typedef union uDrRange -{ +typedef union { /*! * Byte-access to the bits. */ - int8_t Value; + int8_t value; /*! * The structure to store the minimum and the maximum datarate. */ @@ -136,7 +164,7 @@ typedef union uDrRange * * The allowed ranges are region-specific. Please refer to \ref DR_0 to \ref DR_15 for details. */ - int8_t Min : 4; + int8_t min : 4; /*! * The maximum data rate. * @@ -144,70 +172,74 @@ typedef union uDrRange * * The allowed ranges are region-specific. Please refer to \ref DR_0 to \ref DR_15 for details. */ - int8_t Max : 4; - }Fields; -}DrRange_t; + int8_t max : 4; + } fields; +} dr_range_t; /*! * LoRaMAC channel definition. */ -typedef struct sChannelParams -{ +typedef struct { /*! * The frequency in Hz. */ - uint32_t Frequency; + uint32_t frequency; /*! * The alternative frequency for RX window 1. */ - uint32_t Rx1Frequency; + uint32_t rx1_frequency; /*! * The data rate definition. */ - DrRange_t DrRange; + dr_range_t dr_range; /*! * The band index. */ - uint8_t Band; -}ChannelParams_t; + uint8_t band; +} channel_params_t; /*! * LoRaMAC band parameters definition. */ -typedef struct sBand -{ +typedef struct { /*! * The duty cycle. */ - uint16_t DCycle; + uint16_t duty_cycle; /*! * The maximum TX power. */ - int8_t TxMaxPower; + int8_t max_tx_pwr; /*! - * The timestamp of the last JoinReq TX frame. + * The timestamp of the last Join Request TX frame. */ - TimerTime_t LastJoinTxDoneTime; + lorawan_time_t last_join_tx_time; /*! * The timestamp of the last TX frame. */ - TimerTime_t LastTxDoneTime; + lorawan_time_t last_tx_time; /*! * The device off time. */ - TimerTime_t TimeOff; -}Band_t; - + lorawan_time_t off_time; + /*! + * Lower band boundry + */ + uint32_t lower_band_freq; + /*! + * Higher band boundry + */ + uint32_t higher_band_freq; +} band_t; /*! * LoRaMAC receive window 2 channel parameters. */ -typedef struct sRx2ChannelParams -{ +typedef struct { /*! * The frequency in Hz. */ - uint32_t Frequency; + uint32_t frequency; /*! * The data rate. * @@ -215,117 +247,151 @@ typedef struct sRx2ChannelParams * * The allowed ranges are region-specific. Please refer to \ref DR_0 to \ref DR_15 for details. */ - uint8_t Datarate; -}Rx2ChannelParams_t; + uint8_t datarate; +} rx2_channel_params; + +/*! + * LoRaMAC receive window enumeration + */ +typedef enum { + /*! + * LoRaMAC receive window 1 + */ + RX_SLOT_WIN_1, + /*! + * LoRaMAC receive window 2 + */ + RX_SLOT_WIN_2, + /*! + * LoRaMAC receive window 2 for class c - continuous listening + */ + RX_SLOT_WIN_CLASS_C, + /*! + * LoRaMAC class b ping slot window + */ + RX_SLOT_WIN_PING_SLOT +} rx_slot_t; /*! * The global MAC layer parameters. */ -typedef struct sLoRaMacParams -{ +typedef struct { /*! * The TX power in channels. */ - int8_t ChannelsTxPower; + int8_t channel_tx_power; /*! * The data rate in channels. */ - int8_t ChannelsDatarate; + int8_t channel_data_rate; /*! * The system overall timing error in milliseconds. * [-SystemMaxRxError : +SystemMaxRxError] * Default: +/-10 ms */ - uint32_t SystemMaxRxError; + uint32_t max_sys_rx_error; /*! * The minimum number of symbols required to detect an RX frame. * Default: 6 symbols */ - uint8_t MinRxSymbols; + uint8_t min_rx_symb; /*! * LoRaMac maximum time a reception window stays open. */ - uint32_t MaxRxWindow; + uint32_t max_rx_win_time; /*! * Receive delay 1. */ - uint32_t ReceiveDelay1; + uint32_t recv_delay1; /*! * Receive delay 2. */ - uint32_t ReceiveDelay2; + uint32_t recv_delay2; /*! * Join accept delay 1. */ - uint32_t JoinAcceptDelay1; + uint32_t join_accept_delay1; /*! * Join accept delay 1. */ - uint32_t JoinAcceptDelay2; + uint32_t join_accept_delay2; /*! - * The number of uplink messages repetitions [1:15] (unconfirmed messages only). + * The number of uplink messages repetitions (confirmed messages only). */ - uint8_t ChannelsNbRep; + uint8_t retry_num; /*! * The datarate offset between uplink and downlink on first window. */ - uint8_t Rx1DrOffset; + uint8_t rx1_dr_offset; /*! * LoRaMAC 2nd reception window settings. */ - Rx2ChannelParams_t Rx2Channel; + rx2_channel_params rx2_channel; /*! * The uplink dwell time configuration. 0: No limit, 1: 400ms */ - uint8_t UplinkDwellTime; + uint8_t uplink_dwell_time; /*! * The downlink dwell time configuration. 0: No limit, 1: 400ms */ - uint8_t DownlinkDwellTime; + uint8_t downlink_dwell_time; /*! * The maximum possible EIRP. */ - float MaxEirp; + float max_eirp; /*! * The antenna gain of the node. */ - float AntennaGain; -}LoRaMacParams_t; + float antenna_gain; + + /*! + * Maximum duty cycle + * \remark Possibility to shutdown the device. + */ + uint8_t max_duty_cycle; + /*! + * Aggregated duty cycle management + */ + uint16_t aggregated_duty_cycle; + + /*! + * LoRaMac ADR control status + */ + bool adr_on; +} lora_mac_system_params_t; /*! * LoRaMAC multicast channel parameter. */ -typedef struct sMulticastParams -{ +typedef struct multicast_params_s { /*! * Address. */ - uint32_t Address; + uint32_t address; /*! * Network session key. */ - uint8_t NwkSKey[16]; + uint8_t nwk_skey[16]; /*! * Application session key. */ - uint8_t AppSKey[16]; + uint8_t app_skey[16]; /*! * Downlink counter. */ - uint32_t DownLinkCounter; + uint32_t dl_frame_counter; /*! * A reference pointer to the next multicast channel parameters in the list. */ - struct sMulticastParams *Next; -}MulticastParams_t; + struct multicast_params_s *next; +} multicast_params_t; /*! * LoRaMAC frame types. * * LoRaWAN Specification V1.0.2, chapter 4.2.1, table 1. */ -typedef enum eLoRaMacFrameType -{ +typedef enum { /*! * LoRaMAC join request frame. */ @@ -358,15 +424,14 @@ typedef enum eLoRaMacFrameType * LoRaMAC proprietary frame. */ FRAME_TYPE_PROPRIETARY = 0x07, -}LoRaMacFrameType_t; +} mac_frame_type_t; /*! * LoRaMAC mote MAC commands. * * LoRaWAN Specification V1.0.2, chapter 5, table 4. */ -typedef enum eLoRaMacMoteCmd -{ +typedef enum { /*! * LinkCheckReq */ @@ -403,15 +468,14 @@ typedef enum eLoRaMacMoteCmd * DlChannelAns */ MOTE_MAC_DL_CHANNEL_ANS = 0x0A -}LoRaMacMoteCmd_t; +} mote_mac_cmds_t; /*! * LoRaMAC server MAC commands. * * LoRaWAN Specification V1.0.2 chapter 5, table 4. */ -typedef enum eLoRaMacSrvCmd -{ +typedef enum { /*! * LinkCheckAns */ @@ -448,13 +512,12 @@ typedef enum eLoRaMacSrvCmd * DlChannelReq */ SRV_MAC_DL_CHANNEL_REQ = 0x0A, -}LoRaMacSrvCmd_t; +} server_mac_cmds_t; /*! * LoRaMAC battery level indicator. */ -typedef enum eLoRaMacBatteryLevel -{ +typedef enum { /*! * An external power source. */ @@ -471,28 +534,27 @@ typedef enum eLoRaMacBatteryLevel * Battery level - no measurement available. */ BAT_LEVEL_NO_MEASURE = 0xFF, -}LoRaMacBatteryLevel_t; +} device_battery_level_t; /*! * LoRaMAC header field definition (MHDR field). * * LoRaWAN Specification V1.0.2, chapter 4.2. */ -typedef union uLoRaMacHeader -{ +typedef union { /*! * Byte-access to the bits. */ - uint8_t Value; + uint8_t value; /*! * The structure containing single access to header bits. */ - struct sHdrBits + struct hdr_bits_s { /*! * Major version. */ - uint8_t Major : 2; + uint8_t major : 2; /*! * RFU */ @@ -500,54 +562,52 @@ typedef union uLoRaMacHeader /*! * Message type */ - uint8_t MType : 3; - }Bits; -}LoRaMacHeader_t; + uint8_t mtype : 3; + } bits; +} loramac_mhdr_t; /*! * LoRaMAC frame control field definition (FCtrl). * * LoRaWAN Specification V1.0.2, chapter 4.3.1. */ -typedef union uLoRaMacFrameCtrl -{ +typedef union { /*! * Byte-access to the bits. */ - uint8_t Value; + uint8_t value; /*! * The structure containing single access to bits. */ - struct sCtrlBits + struct ctrl_bits_s { /*! * Frame options length. */ - uint8_t FOptsLen : 4; + uint8_t fopts_len : 4; /*! * Frame pending bit. */ - uint8_t FPending : 1; + uint8_t fpending : 1; /*! * Message acknowledge bit. */ - uint8_t Ack : 1; + uint8_t ack : 1; /*! * ADR acknowledgment request bit. */ - uint8_t AdrAckReq : 1; + uint8_t adr_ack_req : 1; /*! * ADR control in the frame header. */ - uint8_t Adr : 1; - }Bits; -}LoRaMacFrameCtrl_t; + uint8_t adr : 1; + } bits; +} loramac_frame_ctrl_t; /*! * The enumeration containing the status of the operation of a MAC service. */ -typedef enum eLoRaMacEventInfoStatus -{ +typedef enum { /*! * Service performed successfully. */ @@ -607,44 +667,47 @@ typedef enum eLoRaMacEventInfoStatus * Crypto methods failure */ LORAMAC_EVENT_INFO_STATUS_CRYPTO_FAIL, -}LoRaMacEventInfoStatus_t; +} loramac_event_info_status_t; /*! - * LoRaMac TX/RX operation state. + * LoRaMac service state flags. */ -typedef union eLoRaMacFlags_t -{ +typedef union { /*! * Byte-access to the bits. */ - uint8_t Value; + uint8_t value; /*! * The structure containing single access to bits. */ - struct sMacFlagBits + struct mac_flag_bits_s { /*! * MCPS-Req pending */ - uint8_t McpsReq : 1; + uint8_t mcps_req : 1; /*! * MCPS-Ind pending */ - uint8_t McpsInd : 1; + uint8_t mcps_ind : 1; /*! * MCPS-Ind pending. Skip indication to the application layer. */ - uint8_t McpsIndSkip : 1; + uint8_t mcps_ind_skip : 1; /*! * MLME-Req pending */ - uint8_t MlmeReq : 1; + uint8_t mlme_req : 1; + /*! + * MLME-Ind pending + */ + uint8_t mlme_ind : 1; /*! * MAC cycle done */ - uint8_t MacDone : 1; - }Bits; -}LoRaMacFlags_t; + uint8_t mac_done : 1; + } bits; +} loramac_flags_t; /*! * @@ -665,12 +728,11 @@ typedef union eLoRaMacFlags_t * * Primitive | Function * ---------------- | :---------------------: - * MCPS-Request | \ref LoRaMacMlmeRequest - * MCPS-Confirm | MacMcpsConfirm in \ref LoRaMacPrimitives_t - * MCPS-Indication | MacMcpsIndication in \ref LoRaMacPrimitives_t + * MCPS-Request | LoRaMacMlmeRequest + * MCPS-Confirm | MacMcpsConfirm in \ref loramac_primitives_t + * MCPS-Indication | MacMcpsIndication in \ref loramac_primitives_t */ -typedef enum eMcps -{ +typedef enum { /*! * Unconfirmed LoRaMAC frame. */ @@ -687,58 +749,42 @@ typedef enum eMcps * Proprietary frame. */ MCPS_PROPRIETARY, -}Mcps_t; +} mcps_type_t; /*! * LoRaMAC MCPS-Request for an unconfirmed frame. */ -typedef struct sMcpsReqUnconfirmed -{ +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; - /*! - * A pointer to the buffer of the frame payload. - */ - void *fBuffer; - /*! - * The size of the frame payload. - */ - uint16_t fBufferSize; + uint8_t fport; + /*! * Uplink datarate, if ADR is off. */ - int8_t Datarate; -}McpsReqUnconfirmed_t; + int8_t data_rate; +} mcps_req_unconfirmed_t; /*! * LoRaMAC MCPS-Request for a confirmed frame. */ -typedef struct sMcpsReqConfirmed -{ +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; - /*! - * A pointer to the buffer of the frame payload. - */ - void *fBuffer; - /*! - * The size of the frame payload. - */ - uint16_t fBufferSize; + uint8_t fport; + /*! * Uplink datarate, if ADR is off. */ - int8_t Datarate; + 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 @@ -756,168 +802,169 @@ typedef struct sMcpsReqConfirmed * 7 | max(DR-3,0) * 8 | max(DR-3,0) * - * Note that if NbTrials is set to 1 or 2, the MAC will not decrease + * 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 NbTrials; -}McpsReqConfirmed_t; + uint8_t nb_trials; +} mcps_req_confirmed_t; /*! * LoRaMAC MCPS-Request for a proprietary frame. */ -typedef struct sMcpsReqProprietary -{ - /*! - * A pointer to the buffer of the frame payload. - */ - void *fBuffer; - /*! - * The size of the frame payload. - */ - uint16_t fBufferSize; +typedef struct { /*! * Uplink datarate, if ADR is off. */ - int8_t Datarate; -}McpsReqProprietary_t; + int8_t data_rate; +} mcps_req_proprietary_t; /*! * LoRaMAC MCPS-Request structure. */ -typedef struct sMcpsReq -{ +typedef struct { /*! * MCPS-Request type. */ - Mcps_t Type; + mcps_type_t type; /*! * MCPS-Request parameters. */ - union uMcpsParam + union { /*! * MCPS-Request parameters for an unconfirmed frame. */ - McpsReqUnconfirmed_t Unconfirmed; + mcps_req_unconfirmed_t unconfirmed; /*! * MCPS-Request parameters for a confirmed frame. */ - McpsReqConfirmed_t Confirmed; + mcps_req_confirmed_t confirmed; /*! * MCPS-Request parameters for a proprietary frame. */ - McpsReqProprietary_t Proprietary; - }Req; -}McpsReq_t; + mcps_req_proprietary_t proprietary; + } req; + + /** Payload data + * + * A pointer to the buffer of the frame payload. + */ + void *f_buffer; + /** Payload size + * + * The size of the frame payload. + */ + uint16_t f_buffer_size; + +} loramac_mcps_req_t; /*! * LoRaMAC MCPS-Confirm. */ -typedef struct sMcpsConfirm -{ +typedef struct { /*! - * Holds the previously performed MCPS-Request. + * Holds the previously performed MCPS-Request type. i.e., the type of + * the MCPS request for which this confirmation is being generated */ - Mcps_t McpsRequest; + mcps_type_t req_type; /*! * The status of the operation. */ - LoRaMacEventInfoStatus_t Status; + loramac_event_info_status_t status; /*! * The uplink datarate. */ - uint8_t Datarate; + uint8_t data_rate; /*! * The transmission power. */ - int8_t TxPower; + int8_t tx_power; /*! * Set if an acknowledgement was received. */ - bool AckReceived; + bool ack_received; /*! * Provides the number of retransmissions. */ - uint8_t NbRetries; + uint8_t nb_retries; /*! * The transmission time on air of the frame. */ - TimerTime_t TxTimeOnAir; + lorawan_time_t tx_toa; /*! * The uplink counter value related to the frame. */ - uint32_t UpLinkCounter; + uint32_t ul_frame_counter; /*! * The uplink frequency related to the frame. */ - uint32_t UpLinkFrequency; -}McpsConfirm_t; + uint32_t ul_frequency; +} loramac_mcps_confirm_t; /*! * LoRaMAC MCPS-Indication primitive. */ -typedef struct sMcpsIndication -{ +typedef struct { /*! * MCPS-Indication type. */ - Mcps_t McpsIndication; + mcps_type_t type; /*! * The status of the operation. */ - LoRaMacEventInfoStatus_t Status; + loramac_event_info_status_t status; /*! * Multicast. */ - uint8_t Multicast; + uint8_t multicast; /*! * The application port. */ - uint8_t Port; + uint8_t port; /*! * The downlink datarate. */ - uint8_t RxDatarate; + uint8_t rx_datarate; /*! * Frame pending status. */ - uint8_t FramePending; + uint8_t fpending_status; /*! * A pointer to the received data stream. */ - uint8_t *Buffer; + uint8_t *buffer; /*! * The size of the received data stream. */ - uint8_t BufferSize; + uint16_t buffer_size; /*! * Indicates, if data is available. */ - bool RxData; + bool is_data_recvd; /*! * The RSSI of the received packet. */ - int16_t Rssi; + int16_t rssi; /*! * The SNR of the received packet. */ - uint8_t Snr; + uint8_t snr; /*! * The receive window. * * [0: Rx window 1, 1: Rx window 2] */ - uint8_t RxSlot; + rx_slot_t rx_slot; /*! * Set if an acknowledgement was received. */ - bool AckReceived; + bool is_ack_recvd; /*! * The downlink counter value for the received frame. */ - uint32_t DownLinkCounter; -}McpsIndication_t; + uint32_t dl_frame_counter; +} loramac_mcps_indication_t; /*! * \brief LoRaMAC management services. @@ -925,22 +972,23 @@ typedef struct sMcpsIndication * \details The following table list the primitives supported by a * specific MAC management service: * - * Name | Request | Indication | Response | Confirm - * --------------------- | :-----: | :--------: | :------: | :-----: - * \ref MLME_JOIN | YES | NO | NO | YES - * \ref MLME_LINK_CHECK | YES | NO | NO | YES - * \ref MLME_TXCW | YES | NO | NO | YES + * Name | Request | Indication | Response | Confirm + * ---------------------------- | :-----: | :--------: | :------: | :-----: + * \ref MLME_JOIN | YES | NO | NO | YES + * \ref MLME_LINK_CHECK | YES | NO | NO | YES + * \ref MLME_TXCW | YES | NO | NO | YES + * \ref MLME_SCHEDULE_UPLINK | NO | YES | NO | NO * * The following table provides links to the function implementations of the * related MLME primitives. * * Primitive | Function * ---------------- | :---------------------: - * MLME-Request | \ref LoRaMacMlmeRequest - * MLME-Confirm | MacMlmeConfirm in \ref LoRaMacPrimitives_t + * MLME-Request | LoRaMacMlmeRequest + * MLME-Confirm | MacMlmeConfirm in \ref loramac_primitives_t + * MLME-Indication | MacMlmeIndication in \ref loramac_primitives_t */ -typedef enum eMlme -{ +typedef enum { /*! * Initiates the Over-the-Air activation. * @@ -965,113 +1013,124 @@ typedef enum eMlme * LoRaWAN end-device certification. */ MLME_TXCW_1, -}Mlme_t; + /*! + * Indicates that the application shall perform an uplink as + * soon as possible. + */ + MLME_SCHEDULE_UPLINK +} mlme_type_t; /*! * LoRaMAC MLME-Request for the join service. */ -typedef struct sMlmeReqJoin -{ +typedef struct { /*! * A globally unique end-device identifier. * * LoRaWAN Specification V1.0.2, chapter 6.2.1. */ - uint8_t *DevEui; + uint8_t *dev_eui; /*! * An application identifier. * * LoRaWAN Specification V1.0.2, chapter 6.1.2 */ - uint8_t *AppEui; + uint8_t *app_eui; /*! * AES-128 application key. * * LoRaWAN Specification V1.0.2, chapter 6.2.2. */ - uint8_t *AppKey; + uint8_t *app_key; /*! * The number of trials for the join request. */ - uint8_t NbTrials; -}MlmeReqJoin_t; + uint8_t nb_trials; +} mlme_join_req_t; /*! * LoRaMAC MLME-Request for TX continuous wave mode. */ -typedef struct sMlmeReqTxCw -{ +typedef struct { /*! * The time while the radio is kept in continuous wave mode, in seconds. */ - uint16_t Timeout; + uint16_t timeout; /*! * The RF frequency to set (only used with the new way). */ - uint32_t Frequency; + uint32_t frequency; /*! * The RF output power to set (only used with the new way). */ - uint8_t Power; -}MlmeReqTxCw_t; + uint8_t power; +} mlme_cw_tx_mode_t; /*! * LoRaMAC MLME-Request structure. */ -typedef struct sMlmeReq -{ +typedef struct { /*! * MLME-Request type. */ - Mlme_t Type; + mlme_type_t type; /*! * MLME-Request parameters. */ - union uMlmeParam - { + union { /*! * MLME-Request parameters for a join request. */ - MlmeReqJoin_t Join; + mlme_join_req_t join; /*! * MLME-Request parameters for TX continuous mode request. */ - MlmeReqTxCw_t TxCw; - }Req; -}MlmeReq_t; + mlme_cw_tx_mode_t cw_tx_mode; + } req; +} loramac_mlme_req_t; /*! * LoRaMAC MLME-Confirm primitive. */ -typedef struct sMlmeConfirm -{ +typedef struct { /*! - * The previously performed MLME-Request. + * The previously performed MLME-Request. i.e., the request type + * for which the confirmation is being generated */ - Mlme_t MlmeRequest; + mlme_type_t req_type; /*! * The status of the operation. */ - LoRaMacEventInfoStatus_t Status; + loramac_event_info_status_t status; /*! * The transmission time on air of the frame. */ - TimerTime_t TxTimeOnAir; + lorawan_time_t tx_toa; /*! * The demodulation margin. Contains the link margin [dB] of the last LinkCheckReq * successfully received. */ - uint8_t DemodMargin; + uint8_t demod_margin; /*! * The number of gateways which received the last LinkCheckReq. */ - uint8_t NbGateways; + uint8_t nb_gateways; /*! * The number of retransmissions. */ - uint8_t NbRetries; -}MlmeConfirm_t; + uint8_t nb_retries; +} loramac_mlme_confirm_t; + +/*! + * LoRaMAC MLME-Indication primitive + */ +typedef struct { + /*! + * MLME-Indication type + */ + mlme_type_t indication_type; +} loramac_mlme_indication_t; /*! * LoRa MAC Information Base (MIB). @@ -1115,11 +1174,10 @@ typedef struct sMlmeConfirm * * Primitive | Function * ---------------- | :---------------------: - * MIB-Set | \ref LoRaMacMibSetRequestConfirm - * MIB-Get | \ref LoRaMacMibGetRequestConfirm + * MIB-Set | LoRaMacMibSetRequestConfirm + * MIB-Get | LoRaMacMibGetRequestConfirm */ -typedef enum eMib -{ +typedef enum { /*! * LoRaWAN device class. * @@ -1316,500 +1374,283 @@ typedef enum eMib * radioTxPower = ( int8_t )floor( maxEirp - antennaGain ) */ MIB_ANTENNA_GAIN -}Mib_t; +} mib_type_t; /*! * LoRaMAC MIB parameters. */ -typedef union uMibParam -{ +typedef union { /*! * LoRaWAN device class. * * Related MIB type: \ref MIB_DEVICE_CLASS */ - DeviceClass_t Class; + device_class_t dev_class; /*! * LoRaWAN network joined attribute * * Related MIB type: \ref MIB_NETWORK_JOINED */ - bool IsNetworkJoined; + bool is_nwk_joined; /*! * Activation state of ADR * * Related MIB type: \ref MIB_ADR */ - bool AdrEnable; + bool is_adr_enable; /*! * Network identifier * * Related MIB type: \ref MIB_NET_ID */ - uint32_t NetID; + uint32_t net_id; /*! * End-device address * * Related MIB type: \ref MIB_DEV_ADDR */ - uint32_t DevAddr; + uint32_t dev_addr; /*! * Network session key * * Related MIB type: \ref MIB_NWK_SKEY */ - uint8_t *NwkSKey; + uint8_t *nwk_skey; /*! * Application session key * * Related MIB type: \ref MIB_APP_SKEY */ - uint8_t *AppSKey; + uint8_t *app_skey; /*! * Enable or disable a public network * * Related MIB type: \ref MIB_PUBLIC_NETWORK */ - bool EnablePublicNetwork; + bool enable_public_nwk; /*! * Enable or disable repeater support * * Related MIB type: \ref MIB_REPEATER_SUPPORT */ - bool EnableRepeaterSupport; + bool enable_repeater_support; /*! * LoRaWAN channel * * Related MIB type: \ref MIB_CHANNELS */ - ChannelParams_t* ChannelList; + channel_params_t* channel_list; /*! * Channel for the receive window 2 * * Related MIB type: \ref MIB_RX2_CHANNEL */ - Rx2ChannelParams_t Rx2Channel; + rx2_channel_params rx2_channel; /*! * Channel for the receive window 2 * * Related MIB type: \ref MIB_RX2_DEFAULT_CHANNEL */ - Rx2ChannelParams_t Rx2DefaultChannel; + rx2_channel_params default_rx2_channel; /*! * Channel mask * * Related MIB type: \ref MIB_CHANNELS_MASK */ - uint16_t* ChannelsMask; + uint16_t* channel_mask; /*! * Default channel mask * * Related MIB type: \ref MIB_CHANNELS_DEFAULT_MASK */ - uint16_t* ChannelsDefaultMask; + uint16_t* default_channel_mask; /*! * Number of frame repetitions * * Related MIB type: \ref MIB_CHANNELS_NB_REP */ - uint8_t ChannelNbRep; + uint8_t channel_nb_rep; /*! * Maximum receive window duration * * Related MIB type: \ref MIB_MAX_RX_WINDOW_DURATION */ - uint32_t MaxRxWindow; + uint32_t max_rx_window; /*! * Receive delay 1 * * Related MIB type: \ref MIB_RECEIVE_DELAY_1 */ - uint32_t ReceiveDelay1; + uint32_t recv_delay1; /*! * Receive delay 2 * * Related MIB type: \ref MIB_RECEIVE_DELAY_2 */ - uint32_t ReceiveDelay2; + uint32_t recv_delay2; /*! * Join accept delay 1 * * Related MIB type: \ref MIB_JOIN_ACCEPT_DELAY_1 */ - uint32_t JoinAcceptDelay1; + uint32_t join_accept_delay1; /*! * Join accept delay 2 * * Related MIB type: \ref MIB_JOIN_ACCEPT_DELAY_2 */ - uint32_t JoinAcceptDelay2; + uint32_t join_accept_delay2; /*! * Channels data rate * * Related MIB type: \ref MIB_CHANNELS_DEFAULT_DATARATE */ - int8_t ChannelsDefaultDatarate; + int8_t default_channel_data_rate; /*! * Channels data rate * * Related MIB type: \ref MIB_CHANNELS_DATARATE */ - int8_t ChannelsDatarate; + int8_t channel_data_rate; /*! * Channels TX power * * Related MIB type: \ref MIB_CHANNELS_DEFAULT_TX_POWER */ - int8_t ChannelsDefaultTxPower; + int8_t default_channel_tx_pwr; /*! * Channels TX power * * Related MIB type: \ref MIB_CHANNELS_TX_POWER */ - int8_t ChannelsTxPower; + int8_t channel_tx_pwr; /*! * LoRaWAN uplink counter * * Related MIB type: \ref MIB_UPLINK_COUNTER */ - uint32_t UpLinkCounter; + uint32_t ul_frame_counter; /*! * LoRaWAN downlink counter * * Related MIB type: \ref MIB_DOWNLINK_COUNTER */ - uint32_t DownLinkCounter; + uint32_t dl_frame_counter; /*! * Multicast channel * * Related MIB type: \ref MIB_MULTICAST_CHANNEL */ - MulticastParams_t* MulticastList; + multicast_params_t* multicast_list; /*! * System overall timing error in milliseconds * * Related MIB type: \ref MIB_SYSTEM_MAX_RX_ERROR */ - uint32_t SystemMaxRxError; + uint32_t max_rx_sys_error; /*! * Minimum required number of symbols to detect an RX frame * * Related MIB type: \ref MIB_MIN_RX_SYMBOLS */ - uint8_t MinRxSymbols; + uint8_t min_rx_symb; /*! * Antenna gain * * Related MIB type: \ref MIB_ANTENNA_GAIN */ - float AntennaGain; -}MibParam_t; + float antenna_gain; +} mib_params_t; /*! * LoRaMAC MIB-RequestConfirm structure */ -typedef struct eMibRequestConfirm -{ +typedef struct { /*! * MIB-Request type */ - Mib_t Type; + mib_type_t type; /*! * MLME-RequestConfirm parameters */ - MibParam_t Param; -}MibRequestConfirm_t; + mib_params_t param; +}loramac_mib_req_confirm_t; /*! * LoRaMAC TX information */ -typedef struct sLoRaMacTxInfo -{ +typedef struct { /*! * Defines the size of the applicable payload that can be processed. */ - uint8_t MaxPossiblePayload; + uint8_t max_possible_payload_size; /*! * The current payload size, dependent on the current datarate. */ - uint8_t CurrentPayloadSize; -}LoRaMacTxInfo_t; + uint8_t current_payload_size; +} loramac_tx_info_t; -/*! - * LoRaMAC status. +/** LoRaMAC status. + * */ -typedef enum eLoRaMacStatus -{ - /*! - * Service started successfully. - */ - LORAMAC_STATUS_OK, - /*! - * Service not started - LoRaMAC is busy. - */ - LORAMAC_STATUS_BUSY, - /*! - * Service unknown. - */ - LORAMAC_STATUS_SERVICE_UNKNOWN, - /*! - * Service not started - invalid parameter. - */ - LORAMAC_STATUS_PARAMETER_INVALID, - /*! - * Service not started - invalid frequency. - */ - LORAMAC_STATUS_FREQUENCY_INVALID, - /*! - * Service not started - invalid datarate. - */ - LORAMAC_STATUS_DATARATE_INVALID, - /*! - * Service not started - invalid frequency and datarate. - */ - LORAMAC_STATUS_FREQ_AND_DR_INVALID, - /*! - * Service not started - the device is not in a LoRaWAN. - */ - LORAMAC_STATUS_NO_NETWORK_JOINED, - /*! - * Service not started - payload length error. - */ - LORAMAC_STATUS_LENGTH_ERROR, - /*! - * Service not started - the device is switched off. - */ - LORAMAC_STATUS_DEVICE_OFF, - /*! - * Service not started - the specified region is not supported - * or not activated with preprocessor definitions. - */ - LORAMAC_STATUS_REGION_NOT_SUPPORTED, - /*! - * Crypto methods failure. - */ - LORAMAC_STATUS_CRYPTO_FAIL, -}LoRaMacStatus_t; +typedef enum lorawan_status { + LORAWAN_STATUS_OK = 0, /**< Service started successfully */ + LORAWAN_STATUS_BUSY = -1000, /**< Service not started - LoRaMAC is busy */ + LORAWAN_STATUS_WOULD_BLOCK = -1001, /**< LoRaMAC cannot send at the moment or have nothing to read */ + LORAWAN_STATUS_SERVICE_UNKNOWN = -1002, /**< Service unknown */ + LORAWAN_STATUS_PARAMETER_INVALID = -1003, /**< Service not started - invalid parameter */ + LORAWAN_STATUS_FREQUENCY_INVALID = -1004, /**< Service not started - invalid frequency */ + LORAWAN_STATUS_DATARATE_INVALID = -1005, /**< Service not started - invalid datarate */ + LORAWAN_STATUS_FREQ_AND_DR_INVALID = -1006, /**< Service not started - invalid frequency and datarate */ + LORAWAN_STATUS_NO_NETWORK_JOINED = -1009, /**< Service not started - the device is not in a LoRaWAN */ + LORAWAN_STATUS_LENGTH_ERROR = -1010, /**< Service not started - payload lenght error */ + LORAWAN_STATUS_DEVICE_OFF = -1011, /**< Service not started - the device is switched off */ + LORAWAN_STATUS_NOT_INITIALIZED = -1012, /**< Service not started - stack not initialized */ + LORAWAN_STATUS_UNSUPPORTED = -1013, /**< Service not supported */ + LORAWAN_STATUS_CRYPTO_FAIL = -1014, /**< Service not started - crypto failure */ + LORAWAN_STATUS_PORT_INVALID = -1015, /**< Invalid port */ + LORAWAN_STATUS_CONNECT_IN_PROGRESS = -1016, /**< Services started - Connection in progress */ + LORAWAN_STATUS_NO_ACTIVE_SESSIONS = -1017, /**< Services not started - No active session */ + LORAWAN_STATUS_IDLE = -1018, /**< Services started - Idle at the moment */ +#if defined(LORAWAN_COMPLIANCE_TEST) + LORAWAN_STATUS_COMPLIANCE_TEST_ON = -1019, /**< Compliance test - is on-going */ +#endif +} lorawan_status_t; /*! * LoRaMAC events structure. * Used to notify upper layers of MAC events. */ -typedef struct sLoRaMacPrimitives -{ +typedef struct { /*! * \brief MCPS-Confirm primitive. * * \param [OUT] MCPS-Confirm parameters. */ - mbed::Callback MacMcpsConfirm; + mbed::Callback mcps_confirm; /*! * \brief MCPS-Indication primitive. * * \param [OUT] MCPS-Indication parameters. */ - mbed::Callback MacMcpsIndication; + mbed::Callback mcps_indication; /*! * \brief MLME-Confirm primitive. * * \param [OUT] MLME-Confirm parameters. */ - mbed::Callback MacMlmeConfirm; -}LoRaMacPrimitives_t; + mbed::Callback mlme_confirm; -/*! - * LoRaMAC callback structure. - */ -typedef struct sLoRaMacCallback -{ /*! - * \brief Measures the battery level. + * \brief MLME-Indication primitive * - * \retval The battery level [0: node is connected to an external - * power source, 1..254: battery level, where 1 is the minimum - * and 254 is the maximum value, 255: the node was not able - * to measure the battery level]. + * \param [OUT] MLME-Indication parameters */ - uint8_t ( *GetBatteryLevel )( void ); - -}LoRaMacCallback_t; - -/** - * The AES encryption/decryption cipher application session key. - */ -#ifdef MBED_CONF_LORA_APPSKEY -#define LORAWAN_APPSKEY MBED_CONF_LORA_APPSKEY -#else -#define LORAWAN_APPSKEY {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} -#endif - -/** - * The AES encryption/decryption cipher network session key. - */ -#ifdef MBED_CONF_LORA_NWKSKEY -#define LORAWAN_NWKSKEY MBED_CONF_LORA_NWKSKEY -#else -#define LORAWAN_NWKSKEY {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} -#endif - -/** - * The device address on the network (big endian). - */ -#ifdef MBED_CONF_LORA_DEVICE_ADDRESS -#define LORAWAN_DEVICE_ADDRESS MBED_CONF_LORA_DEVICE_ADDRESS -#else -#define LORAWAN_DEVICE_ADDRESS 0 -#endif - -/** - * The current network ID. - */ -#ifdef MBED_CONF_LORA_NETWORK_ID -#define LORAWAN_NETWORK_ID MBED_CONF_LORA_NETWORK_ID -#else -#define LORAWAN_NETWORK_ID 0 -#endif - -/** - * The AES encryption/decryption cipher application key. - */ -#ifdef MBED_CONF_LORA_APPLICATION_KEY -#define LORAWAN_APPLICATION_KEY MBED_CONF_LORA_APPLICATION_KEY -#else -#define LORAWAN_APPLICATION_KEY {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} -#endif - -/** - * The application IEEE EUI. - */ - -#ifdef MBED_CONF_LORA_APPLICATION_EUI -#define LORAWAN_APPLICATION_EUI MBED_CONF_LORA_APPLICATION_EUI -#else -#define LORAWAN_APPLICATION_EUI {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} -#endif - -/** - * The mote device IEEE EUI. - */ -#ifdef MBED_CONF_LORA_DEVICE_EUI -#define LORAWAN_DEVICE_EUI MBED_CONF_LORA_DEVICE_EUI -#else -#define LORAWAN_DEVICE_EUI {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} -#endif - -/** - * Indicates how many times join can be tried. - */ -#ifdef MBED_CONF_LORA_NB_TRIALS -#define LORAWAN_NB_TRIALS MBED_CONF_LORA_NB_TRIALS -#else -#define LORAWAN_NB_TRIALS 8 -#endif - -/** - * When set to true, the application uses the Over-the-Air activation procedure. - */ -#if defined(MBED_CONF_LORA_OVER_THE_AIR_ACTIVATION) == 1 -#define OVER_THE_AIR_ACTIVATION MBED_CONF_LORA_OVER_THE_AIR_ACTIVATION -#else -#define OVER_THE_AIR_ACTIVATION false -#endif - -/** - * LoRaWAN connects to a public network or private network, true = public network. - */ -#if defined(MBED_CONF_LORA_PUBLIC_NETWORK) == 1 -#define LORAWAN_PUBLIC_NETWORK MBED_CONF_LORA_PUBLIC_NETWORK -#else -#define LORAWAN_PUBLIC_NETWORK false -#endif - -/** - * Maximum PHY layer payload size for reception. - * This is not user configurable. Its hard coded in LoRaMac.cpp - * and we don't want to change that file too much - */ -#define LORAMAC_PHY_MAXPAYLOAD 255 - -/** - * - * Default user application maximum data size for trasnmission - */ -// reject if user tries to set more than MTU -#if defined MBED_CONF_LORA_TX_MAX_SIZE && MBED_CONF_LORA_TX_MAX_SIZE > 255 -#warning "Cannot set TX Max size more than MTU=255" -#define LORAWAN_TX_MAX_SIZE 255 -#elif defined MBED_CONF_LORA_TX_MAX_SIZE && MBED_CONF_LORA_TX_MAX_SIZE < 255 -#define LORAWAN_TX_MAX_SIZE MBED_CONF_LORA_TX_MAX_SIZE -#else -#define LORAWAN_TX_MAX_SIZE 64 -#endif - -/** - * - * Defines the application data transmission timer cycle, value in [ms] - * Used only when automatic duty cycling is off - */ -#ifdef MBED_CONF_APP_TX_TIMER -#define TX_TIMER MBED_CONF_APP_TX_TIMER -#else -#define TX_TIMER 5000 -#endif - -/** - * - * Defines a random delay for application data transmission cycle, value in [ms] - * Used only when automatic duty cycling is off - */ -#ifdef MBED_CONF_APP_TX_TIMER_RND -#define TX_TIMER_RND MBED_CONF_APP_TX_TIMER_RND -#else -#define TX_TIMER_RND 1000 -#endif - -/** - * - * LoRaWAN Adaptive Data Rate - * - * \remark Please note that when ADR is enabled, the end-device should be static. - */ -#if defined(MBED_CONF_LORA_ADR_ON) == 1 -#define LORAWAN_ADR_ON MBED_CONF_LORA_ADR_ON -#else -#define LORAWAN_ADR_ON false -#endif - -/** - * - * The default application port. - */ -#ifdef MBED_CONF_LORA_APP_PORT -#define LORAWAN_APP_PORT MBED_CONF_LORA_APP_PORT -#else -#define LORAWAN_APP_PORT 0x15 -#endif - -/** - * Default duty cycling setting - */ -#if defined(MBED_CONF_LORA_DUTY_CYCLE_ON) == 1 -#define LORAWAN_DUTYCYCLE_ON MBED_CONF_LORA_DUTY_CYCLE_ON -#else -#define LORAWAN_DUTYCYCLE_ON false -#endif - -/** - * Listen-before-talk setting - */ -#if defined(MBED_CONF_LORA_LBT_ON) == 1 -#define LORAWAN_LBT_ON MBED_CONF_LORA_LBT_ON -#else -#define LORAWAN_LBT_ON false -#endif + mbed::Callback mlme_indication; +}loramac_primitives_t; /** End-device states. * @@ -1842,7 +1683,7 @@ typedef enum lorawan_connect_type { * A structure representing the LoRaWAN Over The Air Activation * parameters. */ -typedef struct lorawan_connect_otaa { +typedef struct { /** End-device identifier * * LoRaWAN Specification V1.0.2, chapter 6.2.1 @@ -1870,7 +1711,7 @@ typedef struct lorawan_connect_otaa { * A structure representing the LoRaWAN Activation By Personalization * parameters. */ -typedef struct lorawan_connect_abp { +typedef struct { /** Network identifier * * LoRaWAN Specification V1.0.2, chapter 6.1.1 @@ -1893,104 +1734,10 @@ typedef struct lorawan_connect_abp { uint8_t *app_skey; } lorawan_connect_abp_t; -/** LoRaMAC data services - * +/** + * Stack level TX message structure */ -typedef enum lora_mac_mcps { - LORA_MCPS_UNCONFIRMED = 0, /**< Unconfirmed LoRaMAC frame */ - LORA_MCPS_CONFIRMED, /**< Confirmed LoRaMAC frame */ - LORA_MCPS_MULTICAST, /**< Multicast LoRaMAC frame */ - LORA_MCPS_PROPRIETARY, /**< Proprietary frame */ -} lora_mac_mcps_t; - -/** LoRaMAC management services - * - */ -typedef enum lora_mac_mlme { - LORA_MLME_JOIN, /**< Initiates the Over-the-Air activation */ - LORA_MLME_LINK_CHECK, /**< LinkCheckReq - Connectivity validation */ - LORA_MLME_TXCW, /**< Sets Tx continuous wave mode */ - LORA_MLME_TXCW_1, /**< Sets Tx continuous wave mode (new LoRa-Alliance CC definition) */ -} lora_mac_mlme_t; - -/** Unconfirmed message. - * - * A message for an unconfirmed frame. - */ -typedef struct lora_mac_unconfirmed { - /** 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 f_port; - /** Uplink datarate - * - * Used if ADR is off - */ - int8_t datarate; -} lora_mac_unconfirmed_t; - -/** Confirmed message. - * - * A message for a confirmed frame. - */ -typedef struct lora_mac_confirmed { - /** 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 f_port; - /** Uplink datarate. - * - * Used if ADR is off. - */ - int8_t datarate; - /** Number of trials. - * - * 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 NbTrials is set to 1 or 2, the MAC does not decrease - * the datarate, if the LoRaMAC layer did not receive an acknowledgment. - */ - uint8_t nb_trials; -} lora_mac_confirmed_t; - -/** A proprietary message. - * - * A message for a proprietary frame. - */ -typedef struct lora_mac_proprietary { - /** Uplink datarate. - * - * Used if ADR is off. - */ - int8_t datarate; -} lora_mac_proprietary_t; - -/** LoRaMAC message structure. - * - */ -typedef struct lora_mac_tx_message { +typedef struct { /** * TX Ongoing flag @@ -2005,7 +1752,7 @@ typedef struct lora_mac_tx_message { /** * Message type */ - lora_mac_mcps_t type; + mcps_type_t type; /** Message parameters. * */ @@ -2014,24 +1761,24 @@ typedef struct lora_mac_tx_message { * * The message parameters for an unconfirmed frame. */ - lora_mac_unconfirmed_t unconfirmed; + mcps_req_unconfirmed_t unconfirmed; /** A confirmed frame. * * The message parameters for a confirmed frame. */ - lora_mac_confirmed_t confirmed; + mcps_req_confirmed_t confirmed; /** A proprietary frame. * * The message parameters for a proprietary frame. */ - lora_mac_proprietary_t proprietary; + mcps_req_proprietary_t proprietary; } message_u; /** Payload data * * Base pointer to the buffer */ - uint8_t f_buffer[LORAWAN_TX_MAX_SIZE]; + uint8_t f_buffer[MBED_CONF_LORA_TX_MAX_SIZE]; /** Payload size. * @@ -2044,476 +1791,48 @@ typedef struct lora_mac_tx_message { */ uint16_t pending_size; -} lora_mac_tx_message_t; - -/** LoRaMAC status. - * - */ -typedef enum lora_mac_status { - LORA_MAC_STATUS_OK = 0, /**< Service started successfully */ - LORA_MAC_STATUS_BUSY = -1000, /**< Service not started - LoRaMAC is busy */ - LORA_MAC_STATUS_WOULD_BLOCK = -1001, /**< LoRaMAC cannot send at the moment or have nothing to read */ - LORA_MAC_STATUS_SERVICE_UNKNOWN = -1002, /**< Service unknown */ - LORA_MAC_STATUS_PARAMETER_INVALID = -1003, /**< Service not started - invalid parameter */ - LORA_MAC_STATUS_FREQUENCY_INVALID = -1004, /**< Service not started - invalid frequency */ - LORA_MAC_STATUS_DATARATE_INVALID = -1005, /**< Service not started - invalid datarate */ - LORA_MAC_STATUS_FREQ_AND_DR_INVALID = -1006, /**< Service not started - invalid frequency and datarate */ - LORA_MAC_STATUS_NO_NETWORK_JOINED = -1009, /**< Service not started - the device is not in a LoRaWAN */ - LORA_MAC_STATUS_LENGTH_ERROR = -1010, /**< Service not started - payload lenght error */ - LORA_MAC_STATUS_DEVICE_OFF = -1011, /**< Service not started - the device is switched off */ - LORA_MAC_STATUS_NOT_INITIALIZED = -1012, /**< Service not started - stack not initialized */ - LORA_MAC_STATUS_UNSUPPORTED = -1013, /**< Service not supported */ - LORA_MAC_STATUS_CRYPTO_FAIL = -1014, /**< Service not started - crypto failure */ - LORA_MAC_STATUS_PORT_INVALID = -1015, /**< Invalid port */ - LORA_MAC_STATUS_CONNECT_IN_PROGRESS = -1016, /**< Services started - Connection in progress */ - LORA_MAC_STATUS_NO_ACTIVE_SESSIONS = -1017, /**< Services not started - No active session */ - LORA_MAC_STATUS_IDLE = -1018, /**< Services started - Idle at the moment */ -#if defined(LORAWAN_COMPLIANCE_TEST) - LORA_MAC_STATUS_COMPLIANCE_TEST_ON = -1019, /**< Compliance test - is on-going */ -#endif -} lora_mac_status_t; - -/** - * - * Enumeration containing the status of the operation of a MAC service - */ -typedef enum lora_mac_event_info_status { - LORA_EVENT_INFO_STATUS_OK = 0, /**< Service performed successfully */ - LORA_EVENT_INFO_STATUS_ERROR, /**< An error occurred during the execution of the service */ - LORA_EVENT_INFO_STATUS_TX_TIMEOUT, /**< A TX timeout occurred */ - LORA_EVENT_INFO_STATUS_RX1_TIMEOUT, /**< An RX timeout occurred on receive window 1 */ - LORA_EVENT_INFO_STATUS_RX2_TIMEOUT, /**< An RX timeout occurred on receive window 2 */ - LORA_EVENT_INFO_STATUS_RX1_ERROR, /**< An RX error occurred on receive window 1 */ - LORA_EVENT_INFO_STATUS_RX2_ERROR, /**< An RX error occurred on receive window 2 */ - LORA_EVENT_INFO_STATUS_JOIN_FAIL, /**< An error occurred in the join procedure */ - LORA_EVENT_INFO_STATUS_DOWNLINK_REPEATED, /**< A frame with an invalid downlink counter */ - LORA_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR, /**< Payload size is not applicable for the datarate */ - LORA_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS, /**< The node has lost MAX_FCNT_GAP or more frames. */ - LORA_EVENT_INFO_STATUS_ADDRESS_FAIL, /**< An address error occurred */ - LORA_EVENT_INFO_STATUS_MIC_FAIL, /**< Message integrity check failure */ - LORA_EVENT_INFO_STATUS_CRYPTO_FAIL /**< Crypto error*/ -} lora_mac_event_info_status_t; - -/** - * LoRaMAC MLME-Request for TX continuous wave mode. - */ -typedef struct mlme_req_tx_cw { - /** - * The time while the radio is kept in continuous wave mode, in seconds. - */ - uint16_t timeout; - /** - * RF frequency to set (only used with the new way). - */ - uint32_t frequency; - /** - * RF output power to set (only used with the new way). - */ - uint8_t power; -} mlme_req_tx_cw_t; - -/** - * LoRaMAC MLME-Request structure - */ -typedef struct lora_mac_mlme_req { - /** - * MLME-Request type - */ - lora_mac_mlme_t type; - - /** - * MLME-Request parameters - */ - union mlme_param { - /** - * MLME-Request parameters for a join request - */ - lorawan_connect_otaa_t join; - /** - * MLME-Request parameters for TX continuous mode request - */ - mlme_req_tx_cw_t tx_cw; - } req; -} lora_mac_mlme_req_t; - -/** - * LoRaMAC MCPS-Request structure - */ -typedef struct lora_mac_mcps_req { - /** - * MCPS-Request type - */ - lora_mac_mcps_t type; - - /** - * MCPS-Request parameters - */ - union mcps_param - { - /** - * MCPS-Request parameters for an unconfirmed frame. - */ - lora_mac_unconfirmed_t unconfirmed; - /** - * MCPS-Request parameters for a confirmed frame. - */ - lora_mac_confirmed_t confirmed; - /** - * MCPS-Request parameters for a proprietary frame. - */ - lora_mac_proprietary_t proprietary; - } req; - /** Payload data - * - * A pointer to the buffer of the frame payload. - */ - void *f_buffer; - /** Payload size - * - * The size of the frame payload. - */ - uint16_t f_buffer_size; -} lora_mac_mcps_req_t; - -/** LoRaMAC MLME-Confirm - * - */ -typedef struct lora_mac_mlme_confirm { - /** The previous MLME-Request. - * - * The previously performed MLME-Request. - */ - lora_mac_mlme_t mlme_request; - /** The status of the operation. - * - * The current status of the MAC service operation. - */ - lora_mac_event_info_status_t status; - /** Time on air. - * - * The transmission time on air of the frame. - */ - TimerTime_t tx_time_on_air; - /** Demodulation margin. - * - * The link margin [dB] of the last LinkCheckReq successfully received. - */ - uint8_t demod_margin; - /** The number of gateways. - * - * The number of gateways that received the last LinkCheckReq. - */ - uint8_t nb_gateways; - /** The retransmission counter. - * - * The number of retransmissions. - */ - uint8_t nb_retries; -} lora_mac_mlme_confirm_t; - -/** LoRaMAC MCPS-Confirm - * - */ -typedef struct lora_mac_mcps_confirm { - /** MCPS-request - * - * Holds the previously performed MCPS-Request. - */ - lora_mac_mcps_t mcps_request; - /** The status of the operation. - * - * The current status of MAC service operation. - */ - lora_mac_event_info_status_t status; - /** Uplink datarate - * - */ - uint8_t datarate; - /** Transmission power - * - */ - int8_t tx_power; - /** ACK-received - * - * Set if an acknowledgement was received. - */ - bool ack_received; - /** Retransmission counter - * - * Provides the number of retransmissions. - */ - uint8_t nb_retries; - /** Time on air - * - * The transmission time on air of the frame. - */ - TimerTime_t tx_time_on_air; - /** Uplink counter - * - * The uplink counter value related to the frame. - */ - uint32_t uplink_counter; - /** Uplink frequency - * - * The uplink frequency related to the frame. - */ - uint32_t uplink_frequency; -} lora_mac_mcps_confirm_t; - -/** LoRaMAC MCPS-Indication - * - */ -typedef struct lora_mac_mcps_indication { - /** MCPS-Indication type - * - */ - lora_mac_mcps_t mcps_indication; - /** The status of the operation - * - * The current status of MAC service operation. - */ - lora_mac_event_info_status_t status; - /** Multicast - * - * This is a multicast message. - */ - uint8_t multicast; - /** Application port - * - */ - uint8_t port; - /** Downlink datarate - * - */ - uint8_t rx_datarate; - /** Frame pending - * - * The frame is pending. - */ - uint8_t frame_pending; - /** Payload data - * - * A pointer to the received data stream. - */ - uint8_t buffer[LORAMAC_PHY_MAXPAYLOAD]; - /** Payload size - * - * The size of the received data stream. - */ - uint16_t buffer_size; - /** RX-data indication - * - * Indicates, if data is available. - */ - bool rx_data; - /** Packet RSSI - * - * The RSSI of the received packet. - */ - int16_t rssi; - /** Packet SNR - * - * The SNR of the received packet. - */ - uint8_t snr; - /** Receive window - * - * [0: Rx window 1, 1: Rx window 2] - */ - uint8_t rx_slot; - /** ACK-received - * - * Set if an acknowledgement was received. - */ - bool ack_received; - /** Downlink counter - * - * The downlink counter value for the received frame. - */ - uint32_t downlink_counter; -} lora_mac_mcps_indication_t; +} loramac_tx_message_t; /** lora_mac_rx_message_type_t * * An enum representing a structure for RX messages. */ -typedef enum lora_mac_rx_message_type { +typedef enum { LORAMAC_RX_MLME_CONFIRM = 0, /**< lora_mac_mlme_confirm_t */ LORAMAC_RX_MCPS_CONFIRM, /**< lora_mac_mcps_confirm_t */ LORAMAC_RX_MCPS_INDICATION /**< lora_mac_mcps_indication_t */ -} lora_mac_rx_message_type_t; +} rx_msg_type; /** lora_mac_rx_message_by_type_t union * * A union representing a structure for RX messages. */ -typedef union lora_mac_rx_message_by_type_t { - lora_mac_mlme_confirm_t mlme_confirm; - lora_mac_mcps_confirm_t mcps_confirm; - lora_mac_mcps_indication_t mcps_indication; -} lora_mac_rx_message_by_type_t; +typedef union { + loramac_mlme_confirm_t mlme_confirm; + loramac_mcps_confirm_t mcps_confirm; + loramac_mcps_indication_t mcps_indication; +} rx_message_u; -/** lora_mac_rx_message_t +/** loramac_rx_message_t * * A structure representing a structure for an RX message. */ -typedef struct lora_mac_rx_message { +typedef struct { bool receive_ready; - lora_mac_rx_message_type_t type; - lora_mac_rx_message_by_type_t rx_message; + rx_msg_type type; + rx_message_u msg; uint16_t pending_size; uint16_t prev_read_size; -} lora_mac_rx_message_t; - -/** lora_mac_dr_range_t union - * - * A union representing a structure for the minimum and maximum data rate. - */ -typedef union lora_mac_dr_range { - /** Byte-access - * - * Byte-access to the bits. - */ - int8_t value; - /** lora_mac_fields_s - * - * A structure to store the minimum and maximum data rate. - */ - struct lora_mac_fields { - /** Minimum data rate - * - * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7] - * - * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4] - */ - int8_t min :4; - /** Maximum data rate - * - * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7] - * - * US915 - [DR_0, DR_1, DR_2, DR_3, DR_4] - */ - int8_t max :4; - } lora_mac_fields_s; -} lora_mac_dr_range_t; - -/** LoRaWAN device class definition - * - */ -typedef enum lora_mac_device_class { - LORA_CLASS_A, /**< LoRaWAN device class A */ - LORA_CLASS_B, /**< LoRaWAN device class B */ - LORA_CLASS_C, /**< LoRaWAN device class C */ -} lora_mac_device_class_t; - -/** LoRaMAC channel definition - * - */ -typedef struct lora_mac_channel_params { - /** Frequency in Hz - * - */ - uint32_t frequency; - /** Alternative frequency for RX window 1 - * - */ - uint32_t rx1_frequency; - /** Data rate definition - * - */ - lora_mac_dr_range_t dr_range; - /** Band index - * - */ - uint8_t band; -} lora_mac_channel_params_t; +} loramac_rx_message_t; /** * Structure to hold A list of LoRa Channels */ typedef struct lora_channels_s { uint8_t id; - lora_mac_channel_params_t ch_param; -} lora_channels_t; + channel_params_t ch_param; +} loramac_channel_t; -/** LoRaMAC receive window 2 channel parameters - * - */ -typedef struct lora_mac_rx2_channel_params { - /** Frequency in Hz - * - */ - uint32_t frequency; - /** Data rate - * - * EU868 - [DR_0, DR_1, DR_2, DR_3, DR_4, DR_5, DR_6, DR_7] - * - * US915 - [DR_8, DR_9, DR_10, DR_11, DR_12, DR_13] - */ - uint8_t datarate; -} lora_mac_rx2_channel_params_t; - -/** LoRaMAC multicast channel parameter - * - */ -typedef struct lora_mac_multicast_params { - /** Address - * - */ - uint32_t address; - /** Network session key - * - */ - uint8_t nwk_skey[16]; - /** Application session key - * - */ - uint8_t app_skey[16]; - /** Downlink counter - * - */ - uint32_t downlink_counter; - /** Next multicast - * - * A reference pointer to the next multicast channel parameters in the list - */ - struct lora_mac_multicast_params *next; -} lora_mac_multicast_params_t; - -/** Enum lora_mac_mib_t - * - */ -typedef enum lora_mac_mib { - LORA_MIB_DEVICE_CLASS, /**< LoRaWAN device class */ - LORA_MIB_NETWORK_JOINED, /**< LoRaWAN network joined attribute */ - LORA_MIB_ADR, /**< Adaptive data rate */ - LORA_MIB_NET_ID, /**< Network identifier */ - LORA_MIB_DEV_ADDR, /**< End-device address */ - LORA_MIB_NWK_SKEY, /**< Network session key */ - LORA_MIB_APP_SKEY, /**< Application session key */ - LORA_MIB_PUBLIC_NETWORK, /**< Set the network type to public or private */ - LORA_MIB_REPEATER_SUPPORT, /**< Support the operation with repeaters */ - LORA_MIB_CHANNELS, /**< Communication channels */ - LORA_MIB_RX2_CHANNEL, /**< Set receive window 2 channel */ - LORA_MIB_RX2_DEFAULT_CHANNEL, /**< Set receive window 2 channel */ - LORA_MIB_CHANNELS_MASK, /**< LoRaWAN channels mask */ - LORA_MIB_CHANNELS_DEFAULT_MASK, /**< LoRaWAN default channels mask */ - LORA_MIB_CHANNELS_NB_REP, /**< Set the number of repetitions on a channel */ - LORA_MIB_MAX_RX_WINDOW_DURATION, /**< Maximum receive window duration in [ms] */ - LORA_MIB_RECEIVE_DELAY_1, /**< Receive delay 1 in [ms] */ - LORA_MIB_RECEIVE_DELAY_2, /**< Receive delay 2 in [ms] */ - LORA_MIB_JOIN_ACCEPT_DELAY_1, /**< Join accept delay 1 in [ms] */ - LORA_MIB_JOIN_ACCEPT_DELAY_2, /**< Join accept delay 2 in [ms] */ - LORA_MIB_CHANNELS_DEFAULT_DATARATE, /**< Default data rate of a channel */ - LORA_MIB_CHANNELS_DATARATE, /**< Data rate of a channel */ - LORA_MIB_CHANNELS_TX_POWER, /**< Transmission power of a channel */ - LORA_MIB_CHANNELS_DEFAULT_TX_POWER, /**< Transmission power of a channel */ - LORA_MIB_UPLINK_COUNTER, /**< LoRaWAN uplink counter */ - LORA_MIB_DOWNLINK_COUNTER, /**< LoRaWAN downlink counter */ - LORA_MIB_MULTICAST_CHANNEL, /**< Multicast channels */ - LORA_MIB_SYSTEM_MAX_RX_ERROR, /**< System overall timing error in milliseconds. */ - LORA_MIB_MIN_RX_SYMBOLS, /**< Minimum number of symbols required to detect an RX frame */ -} lora_mac_mib_t; /** lorawan_connect_t structure * @@ -2568,7 +1887,7 @@ typedef struct lorawan_session { * * A structure for data in commission. */ -typedef struct lora_dev_commission { +typedef struct { /** Connection information * * Saves information for etc. keys @@ -2586,179 +1905,7 @@ typedef struct lora_dev_commission { * Related MIB type: LORA_MIB_DOWNLINK_COUNTER */ uint32_t downlink_counter; -} lora_dev_commission_t; - -/** LoRaMAC MIB parameters - * - */ -typedef union lora_mac_mib_param { - /** LoRaWAN device class - * - * Related MIB type: \ref MIB_DEVICE_CLASS - */ - lora_mac_device_class_t lora_class; - /** LoRaWAN network joined attribute - * - * Related MIB type: \ref MIB_NETWORK_JOINED - */ - bool is_network_joined; - /** Activation state of ADR - * - * Related MIB type: \ref MIB_ADR - */ - bool adr_enable; - /** Network identifier - * - * Related MIB type: \ref MIB_NET_ID - */ - uint32_t net_id; - /** End-device address - * - * Related MIB type: \ref MIB_DEV_ADDR - */ - uint32_t dev_addr; - /** Network session key - * - * Related MIB type: \ref MIB_NWK_SKEY - */ - uint8_t *nwk_skey; - /** Application session key - * - * Related MIB type: \ref MIB_APP_SKEY - */ - uint8_t *app_skey; - /** Enable public network - * - * Enable or disable a public network - * Related MIB type: \ref MIB_PUBLIC_NETWORK - */ - bool enable_public_network; - /** Enable repeater support - * - * Enable or disable repeater support - * Related MIB type: \ref MIB_REPEATER_SUPPORT - */ - bool enable_repeater_support; - /** LoRaWAN Channel - * - * Related MIB type: \ref MIB_CHANNELS - */ - lora_mac_channel_params_t *channel_list; - /** Channel rx 2 - * - * Channel for the receive window 2 - * Related MIB type: \ref MIB_RX2_CHANNEL - */ - lora_mac_rx2_channel_params_t rx2_channel; - /** Default channel rx 2 - * - * Channel for the receive window 2 - * Related MIB type: \ref MIB_RX2_DEFAULT_CHANNEL - */ - lora_mac_rx2_channel_params_t rx2_default_channel; - /** Channel mask - * - * Related MIB type: \ref MIB_CHANNELS_MASK - */ - uint16_t *channels_mask; - /** Default channel mask - * - * Related MIB type: \ref MIB_CHANNELS_DEFAULT_MASK - */ - uint16_t *channels_default_mask; - /** Frame repetition number - * - * Number of frame repetitions - * Related MIB type: \ref MIB_CHANNELS_NB_REP - */ - uint8_t channel_nb_rep; - /** Maximum receive window duration - * - * Related MIB type: \ref MIB_MAX_RX_WINDOW_DURATION - */ - uint32_t max_rx_window; - /** Receive delay 1 - * - * Related MIB type: \ref MIB_RECEIVE_DELAY_1 - */ - uint32_t receive_delay1; - /** Receive delay 2 - * - * Related MIB type: \ref MIB_RECEIVE_DELAY_2 - */ - uint32_t receive_delay2; - /** Join accept delay 1 - * - * Related MIB type: \ref MIB_JOIN_ACCEPT_DELAY_1 - */ - uint32_t join_accept_delay1; - /** Join accept delay 2 - * - * Related MIB type: \ref MIB_JOIN_ACCEPT_DELAY_2 - */ - uint32_t join_accept_delay2; - /** Channels default data rate - * - * Related MIB type: \ref MIB_CHANNELS_DEFAULT_DATARATE - */ - int8_t channels_default_datarate; - /** Channels data rate - * - * Related MIB type: \ref MIB_CHANNELS_DATARATE - */ - int8_t channels_datarate; - /** Channels default TX power - * - * Related MIB type: \ref MIB_CHANNELS_DEFAULT_TX_POWER - */ - int8_t channels_default_tx_power; - /** Channels TX power - * - * Related MIB type: \ref MIB_CHANNELS_TX_POWER - */ - int8_t channels_tx_power; - /** LoRaWAN uplink counter - * - * Related MIB type: \ref MIB_UPLINK_COUNTER - */ - uint32_t uplink_counter; - /** LoRaWAN downlink counter - * - * Related MIB type: \ref MIB_DOWNLINK_COUNTER - */ - uint32_t downlink_counter; - /** Multicast channel - * - * Related MIB type: \ref MIB_MULTICAST_CHANNEL - */ - lora_mac_multicast_params_t *multicast_list; - /** Maximum RX error timing - * - * System overall timing error in milliseconds. - * Related MIB type: \ref MIB_SYSTEM_MAX_RX_ERROR - */ - uint32_t system_max_rx_error; - /** Minimum RX symbols - * - * Minimum required number of symbols to detect an RX frame - * Related MIB type: \ref MIB_MIN_RX_SYMBOLS - */ - uint8_t min_rx_symbols; -} lora_mac_mib_param_t; - -/** LoRaMAC MIB-RequestConfirm structure - * - */ -typedef struct lora_mac_mib_request_confirm { - /** MIB-Request type - * - */ - lora_mac_mib_t type; - /** MIB-RequestConfirm parameters - * - */ - lora_mac_mib_param_t param; -} lora_mac_mib_request_confirm_t; +} lorawan_dev_commission_t; #if defined(LORAWAN_COMPLIANCE_TEST) /** LoRaWAN compliance tests support data @@ -2811,7 +1958,7 @@ typedef struct compliance_test { /** Structure containing the uplink status * */ -typedef struct loramac_uplink_status { +typedef struct { /** Is acked * */ @@ -2841,7 +1988,7 @@ typedef struct loramac_uplink_status { /** A structure containing the downlink status * */ -typedef struct loramac_downlink_status { +typedef struct { /** RSSI of downlink * */ @@ -2872,6 +2019,348 @@ typedef struct loramac_downlink_status { uint8_t buffer_size; } loramac_downlink_status_t; +/*! + * The parameter structure for the function for regional rx configuration. + */ +typedef struct { + /*! + * The RX channel. + */ + uint8_t channel; + /*! + * The RX datarate. + */ + uint8_t datarate; + /*! + * The RX bandwidth. + */ + uint8_t bandwidth; + /*! + * The RX datarate offset. + */ + int8_t dr_offset; + /*! + * The RX frequency. + */ + uint32_t frequency; + /*! + * The RX window timeout + */ + uint32_t window_timeout; + /*! + * The RX window offset + */ + int32_t window_offset; + /*! + * The downlink dwell time. + */ + uint8_t dl_dwell_time; + /*! + * Set to true, if a repeater is supported. + */ + bool is_repeater_supported; + /*! + * Set to true, if RX should be continuous. + */ + bool is_rx_continuous; + /*! + * Sets the RX window. + */ + rx_slot_t rx_slot; +} rx_config_params_t; + +/*! + * \brief Timer object description + */ +typedef struct { + mbed::Callback callback; + int timer_id; +} timer_event_t; + +/*! + * LoRaMac internal states + */ +typedef enum { + LORAMAC_IDLE = 0x00000000, + LORAMAC_TX_RUNNING = 0x00000001, + LORAMAC_RX = 0x00000002, + LORAMAC_ACK_REQ = 0x00000004, + LORAMAC_ACK_RETRY = 0x00000008, + LORAMAC_TX_DELAYED = 0x00000010, + LORAMAC_TX_CONFIG = 0x00000020, + LORAMAC_RX_ABORT = 0x00000040, +} loramac_internal_state; + +typedef struct { + /*! + * Device IEEE EUI + */ + uint8_t *dev_eui; + + /*! + * Application IEEE EUI + */ + uint8_t *app_eui; + + /*! + * AES encryption/decryption cipher application key + */ + uint8_t *app_key; + + /*! + * AES encryption/decryption cipher network session key + */ + uint8_t nwk_skey[16]; + + /*! + * AES encryption/decryption cipher application session key + */ + uint8_t app_skey[16]; + +} loramac_keys; + +typedef struct { + /*! + * Aggregated duty cycle management + */ + lorawan_time_t aggregated_last_tx_time; + lorawan_time_t aggregated_timeoff; + + /*! + * Stores the time at LoRaMac initialization. + * + * \remark Used for the BACKOFF_DC computation. + */ + lorawan_time_t mac_init_time; + + + /*! + * Last transmission time on air + */ + lorawan_time_t tx_toa; + + /*! + * LoRaMac timer used to check the LoRaMacState (runs every second) + */ + timer_event_t mac_state_check_timer; + + /*! + * LoRaMac duty cycle delayed Tx timer + */ + timer_event_t tx_delayed_timer; + + /*! + * LoRaMac reception windows timers + */ + timer_event_t rx_window1_timer; + timer_event_t rx_window2_timer; + + /*! + * Acknowledge timeout timer. Used for packet retransmissions. + */ + timer_event_t ack_timeout_timer; + +} lorawan_timers; + +typedef struct { + + /*! + * Actual device class + */ + device_class_t dev_class; + + /*! + * Holds the type of current Receive window slot + */ + rx_slot_t rx_slot; + + /*! + * Indicates if the node is connected to a private or public network + */ + bool is_nwk_public; + + /*! + * Indicates if the node supports repeaters + */ + bool is_repeater_supported; + + /*! + * IsPacketCounterFixed enables the MIC field tests by fixing the + * ul_frame_counter value + */ + bool is_ul_frame_counter_fixed; + + /*! + * Used for test purposes. Disables the opening of the reception windows. + */ + bool is_rx_window_enabled; + + /*! + * Indicates if the MAC layer has already joined a network. + */ + bool is_nwk_joined; + + /*! + * If the node has sent a FRAME_TYPE_DATA_CONFIRMED_UP this variable indicates + * if the nodes needs to manage the server acknowledgement. + */ + bool is_node_ack_requested; + + /*! + * If the server has sent a FRAME_TYPE_DATA_CONFIRMED_DOWN this variable indicates + * if the ACK bit must be set for the next transmission + */ + bool is_srv_ack_requested; + + /*! + * Enables/Disables duty cycle management (Test only) + */ + bool is_dutycycle_on; + + /*! + * Set to true, if the last uplink was a join request + */ + bool is_last_tx_join_request; + + /*! + * Indicates if the AckTimeout timer has expired or not + */ + bool is_ack_retry_timeout_expired; + + /*! + * Current channel index + */ + uint8_t channel; + + /*! + * Current channel index + */ + uint8_t last_channel_idx; + + /*! + * Uplink messages repetitions counter + */ + uint8_t ul_nb_rep_counter; + + /*! + * Buffer containing the data to be sent or received. + */ + uint8_t buffer[LORAMAC_PHY_MAXPAYLOAD]; + + /*! + * Length of packet in LoRaMacBuffer + */ + uint16_t buffer_pkt_len; + + /*! + * Buffer containing the upper layer data. + */ + uint8_t payload[LORAMAC_PHY_MAXPAYLOAD]; + + /*! + * Length of the payload in LoRaMacBuffer + */ + uint8_t payload_length; + + /*! + * Number of trials to get a frame acknowledged + */ + uint8_t max_ack_timeout_retries; + + /*! + * Number of trials to get a frame acknowledged + */ + uint8_t ack_timeout_retry_counter; + + /*! + * Maximum number of trials for the Join Request + */ + uint8_t max_join_request_trials; + + /*! + * Number of trials for the Join Request + */ + uint8_t join_request_trial_counter; + + /*! + * Mac keys + */ + loramac_keys keys; + + /*! + * LoRaMac tx/rx operation state + */ + loramac_flags_t flags; + + /*! + * Device nonce is a random value extracted by issuing a sequence of RSSI + * measurements + */ + uint16_t dev_nonce; + + /*! + * Network ID ( 3 bytes ) + */ + uint32_t net_id; + + /*! + * Mote Address + */ + uint32_t dev_addr; + + /*! + * LoRaMAC frame counter. Each time a packet is sent the counter is incremented. + * Only the 16 LSB bits are sent + */ + uint32_t ul_frame_counter; + + /*! + * LoRaMAC frame counter. Each time a packet is received the counter is incremented. + * Only the 16 LSB bits are received + */ + uint32_t dl_frame_counter; + + /*! + * Counts the number of missed ADR acknowledgements + */ + uint32_t adr_ack_counter; + + /*! + * LoRaMac internal state + */ + uint32_t mac_state; + + /*! + * LoRaMac reception windows delay + * \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME + * join frame : RxWindowXDelay = JoinAcceptDelayX - RADIO_WAKEUP_TIME + */ + uint32_t rx_window1_delay; + uint32_t rx_window2_delay; + + /*! + * Timer objects and stored values + */ + lorawan_timers timers; + + /*! + * LoRaMac parameters + */ + lora_mac_system_params_t sys_params; + + /*! + * Receive Window configurations for PHY layer + */ + rx_config_params_t rx_window1_config; + rx_config_params_t rx_window2_config; + + /*! + * Multicast channels linked list + */ + multicast_params_t *multicast_channels; + +} loramac_protocol_params; + /** LoRaWAN callback functions * */ @@ -2887,11 +2376,11 @@ typedef enum lora_events { RX_TIMEOUT, RX_ERROR, JOIN_FAILURE, -} lora_events_t; +} lorawan_event_t; typedef struct { // Mandatory. Event Callback must be provided - mbed::Callback events; + mbed::Callback events; // Rest are optional // If the user do not assign these callbacks, these callbacks would return @@ -2899,11 +2388,16 @@ typedef struct { // link_check_resp callback and other such callbacks will be maped in // future releases of Mbed-OS mbed::Callback link_check_resp; - }lorawan_app_callbacks_t; + + // Battery level callback goes in the down direction, i.e., it informs + // the stack about the battery level by calling a function provided + // by the upper layers + mbed::Callback battery_level; + } lorawan_app_callbacks_t; typedef struct lora_channelplan { uint8_t nb_channels; // number of channels - lora_channels_t *channels; -} lora_channelplan_t; + loramac_channel_t *channels; +} lorawan_channelplan_t; #endif /* LORAWAN_SYSTEM_LORAWAN_DATA_STRUCTURES_H_ */