mirror of https://github.com/ARMmbed/mbed-os.git
Adding QOS handling and fixing bugs for Class C
LinkADRReq mac command can be used by the network server to set a certain level of QOS using NbTrans field which is applicable to Unconfirmed traffic only for 1.0.2 spec. This commit introduces mechanisms to facilitate this QOS. It means to repeat an outgoing unconfirmed message NbTrans times without changing its frame counter. For class C, we have retired the ack_expiry_timer_for_class_c and have replaced it with another timer which mimics the RX2 closure as in Class A but doesn't actually close RX2 window. It's just a mechanism by which the state machine is informed that the you can proceed forward, we have not received anything in RX2 window either. This is needed as RX2 doesn't timeout in class C (i.e., the radio remains in continuous mode). In addition to that we need to close any pending timers for Receive windows after the MIC has passed and the Duplicate counter check has also been passed.pull/8183/head
parent
5bfa7c03ba
commit
94eb4c0bc0
|
@ -40,7 +40,7 @@ SPDX-License-Identifier: BSD-3-Clause
|
|||
* Control flags for transient states
|
||||
*/
|
||||
#define IDLE_FLAG 0x00000000
|
||||
#define TX_ONGOING_FLAG 0x00000001
|
||||
#define RETRY_EXHAUSTED_FLAG 0x00000001
|
||||
#define MSG_RECVD_FLAG 0x00000002
|
||||
#define CONNECTED_FLAG 0x00000004
|
||||
#define USING_OTAA_FLAG 0x00000008
|
||||
|
@ -68,6 +68,7 @@ LoRaWANStack::LoRaWANStack()
|
|||
_tx_metadata(),
|
||||
_rx_metadata(),
|
||||
_num_retry(1),
|
||||
_qos_cnt(1),
|
||||
_ctrl_flags(IDLE_FLAG),
|
||||
_app_port(INVALID_PORT),
|
||||
_link_check_requested(false),
|
||||
|
@ -274,7 +275,6 @@ lorawan_status_t LoRaWANStack::stop_sending(void)
|
|||
|
||||
if (_loramac.clear_tx_pipe() == LORAWAN_STATUS_OK) {
|
||||
_ctrl_flags &= ~TX_DONE_FLAG;
|
||||
_ctrl_flags &= ~TX_ONGOING_FLAG;
|
||||
_loramac.set_tx_ongoing(false);
|
||||
_device_current_state = DEVICE_STATE_IDLE;
|
||||
return LORAWAN_STATUS_OK;
|
||||
|
@ -453,7 +453,8 @@ lorawan_status_t LoRaWANStack::set_device_class(const device_class_t &device_cla
|
|||
if (device_class == CLASS_B) {
|
||||
return LORAWAN_STATUS_UNSUPPORTED;
|
||||
}
|
||||
_loramac.set_device_class(device_class, mbed::callback(this, &LoRaWANStack::handle_ack_expiry_for_class_c));
|
||||
_loramac.set_device_class(device_class,
|
||||
mbed::callback(this, &LoRaWANStack::post_process_tx_no_reception));
|
||||
return LORAWAN_STATUS_OK;
|
||||
}
|
||||
|
||||
|
@ -563,7 +564,6 @@ void LoRaWANStack::process_transmission_timeout()
|
|||
// this is a fatal error and should not happen
|
||||
tr_debug("TX Timeout");
|
||||
_loramac.on_radio_tx_timeout();
|
||||
_ctrl_flags &= ~TX_ONGOING_FLAG;
|
||||
_ctrl_flags &= ~TX_DONE_FLAG;
|
||||
if (_device_current_state == DEVICE_STATE_JOINING) {
|
||||
mlme_confirm_handler();
|
||||
|
@ -579,39 +579,108 @@ void LoRaWANStack::process_transmission(void)
|
|||
tr_debug("Transmission completed");
|
||||
_loramac.on_radio_tx_done(_tx_timestamp);
|
||||
|
||||
make_tx_metadata_available();
|
||||
|
||||
if (_device_current_state == DEVICE_STATE_JOINING) {
|
||||
_device_current_state = DEVICE_STATE_AWAITING_JOIN_ACCEPT;
|
||||
}
|
||||
|
||||
if (_device_current_state == DEVICE_STATE_SENDING) {
|
||||
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
|
||||
_ctrl_flags |= TX_ONGOING_FLAG;
|
||||
_ctrl_flags &= ~TX_DONE_FLAG;
|
||||
tr_debug("Awaiting ACK");
|
||||
_device_current_state = DEVICE_STATE_AWAITING_ACK;
|
||||
} else if (_loramac.get_device_class() == CLASS_A) {
|
||||
// Class A unconfirmed message sent, TX_DONE event will be sent to
|
||||
// application when RX2 windows is elapsed, i.e., in process_reception_timeout()
|
||||
_ctrl_flags &= ~TX_ONGOING_FLAG;
|
||||
_ctrl_flags |= TX_DONE_FLAG;
|
||||
} else if (_loramac.get_device_class() == CLASS_C) {
|
||||
// In Class C, reception timeout never happens, so we handle the state
|
||||
// progression for TX_DONE in UNCONFIRMED case here
|
||||
_loramac.post_process_mcps_req();
|
||||
state_controller(DEVICE_STATE_STATUS_CHECK);
|
||||
state_machine_run_to_completion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LoRaWANStack::handle_ack_expiry_for_class_c(void)
|
||||
void LoRaWANStack::post_process_tx_with_reception()
|
||||
{
|
||||
_ctrl_flags &= ~TX_DONE_FLAG;
|
||||
_ctrl_flags |= TX_ONGOING_FLAG;
|
||||
tr_error("Retries exhausted for Class C device");
|
||||
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
|
||||
// if ack was not received, we will try retransmission after
|
||||
// ACK_TIMEOUT. handle_data_frame() already disables ACK_TIMEOUT timer
|
||||
// if ack was received. Otherwise, following method will be called in
|
||||
// LoRaMac.cpp, on_ack_timeout_timer_event().
|
||||
if (_loramac.get_mcps_indication()->is_ack_recvd) {
|
||||
_ctrl_flags |= TX_DONE_FLAG;
|
||||
_ctrl_flags &= ~RETRY_EXHAUSTED_FLAG;
|
||||
tr_debug("Ack=OK, NbTrials=%d",
|
||||
_loramac.get_mcps_confirmation()->nb_retries);
|
||||
_loramac.post_process_mcps_req();
|
||||
make_tx_metadata_available();
|
||||
state_controller(DEVICE_STATE_STATUS_CHECK);
|
||||
} else {
|
||||
if (!_loramac.continue_sending_process()
|
||||
&& _loramac.get_current_slot() != RX_SLOT_WIN_1) {
|
||||
tr_error("Retries exhausted for Class %s device",
|
||||
_loramac.get_device_class() == CLASS_A ? "A" : "C");
|
||||
_ctrl_flags &= ~TX_DONE_FLAG;
|
||||
_ctrl_flags |= RETRY_EXHAUSTED_FLAG;
|
||||
state_controller(DEVICE_STATE_STATUS_CHECK);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// handle UNCONFIRMED case here, RX slots were turned off due to
|
||||
// valid packet reception.
|
||||
uint8_t prev_QOS_level = _loramac.get_prev_QOS_level();
|
||||
uint8_t QOS_level = _loramac.get_QOS_level();
|
||||
|
||||
// We will not apply QOS on the post-processing of the previous
|
||||
// outgoing message as we would have received QOS instruction in response
|
||||
// to that particular message
|
||||
if (QOS_level > LORAWAN_DEFAULT_QOS && _qos_cnt < QOS_level
|
||||
&& (prev_QOS_level == QOS_level)) {
|
||||
_ctrl_flags &= ~TX_DONE_FLAG;
|
||||
const int ret = _queue->call(this, &LoRaWANStack::state_controller,
|
||||
DEVICE_STATE_SCHEDULING);
|
||||
MBED_ASSERT(ret != 0);
|
||||
(void) ret;
|
||||
_qos_cnt++;
|
||||
tr_info("QOS: repeated transmission #%d queued", _qos_cnt);
|
||||
} else {
|
||||
_loramac.post_process_mcps_req();
|
||||
_ctrl_flags |= TX_DONE_FLAG;
|
||||
make_tx_metadata_available();
|
||||
state_controller(DEVICE_STATE_STATUS_CHECK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LoRaWANStack::post_process_tx_no_reception()
|
||||
{
|
||||
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
|
||||
_loramac.post_process_mcps_req();
|
||||
if (_loramac.continue_sending_process()) {
|
||||
_ctrl_flags &= ~TX_DONE_FLAG;
|
||||
_ctrl_flags &= ~RETRY_EXHAUSTED_FLAG;
|
||||
return;
|
||||
}
|
||||
|
||||
tr_error("Retries exhausted for Class %s device",
|
||||
_loramac.get_device_class() == CLASS_A ? "A" : "C");
|
||||
_ctrl_flags &= ~TX_DONE_FLAG;
|
||||
_ctrl_flags |= RETRY_EXHAUSTED_FLAG;
|
||||
} else {
|
||||
_ctrl_flags |= TX_DONE_FLAG;
|
||||
|
||||
uint8_t prev_QOS_level = _loramac.get_prev_QOS_level();
|
||||
uint8_t QOS_level = _loramac.get_QOS_level();
|
||||
|
||||
if (QOS_level > LORAWAN_DEFAULT_QOS && (prev_QOS_level == QOS_level)) {
|
||||
if (_qos_cnt < QOS_level) {
|
||||
const int ret = _queue->call(this, &LoRaWANStack::state_controller,
|
||||
DEVICE_STATE_SCHEDULING);
|
||||
MBED_ASSERT(ret != 0);
|
||||
(void)ret;
|
||||
_qos_cnt++;
|
||||
tr_info("QOS: repeated transmission #%d queued", _qos_cnt);
|
||||
state_machine_run_to_completion();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_loramac.post_process_mcps_req();
|
||||
make_tx_metadata_available();
|
||||
state_controller(DEVICE_STATE_STATUS_CHECK);
|
||||
state_machine_run_to_completion();
|
||||
}
|
||||
|
||||
void LoRaWANStack::handle_scheduling_failure(void)
|
||||
|
@ -621,16 +690,18 @@ void LoRaWANStack::handle_scheduling_failure(void)
|
|||
state_machine_run_to_completion();
|
||||
}
|
||||
|
||||
|
||||
void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size,
|
||||
int16_t rssi, int8_t snr)
|
||||
{
|
||||
_device_current_state = DEVICE_STATE_RECEIVING;
|
||||
|
||||
_ctrl_flags &= ~MSG_RECVD_FLAG;
|
||||
_ctrl_flags &= ~TX_DONE_FLAG;
|
||||
_ctrl_flags &= ~RETRY_EXHAUSTED_FLAG;
|
||||
|
||||
_loramac.on_radio_rx_done(payload, size, rssi, snr);
|
||||
|
||||
make_rx_metadata_available();
|
||||
|
||||
if (_loramac.get_mlme_confirmation()->pending) {
|
||||
_loramac.post_process_mlme_request();
|
||||
mlme_confirm_handler();
|
||||
|
@ -646,36 +717,10 @@ void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size
|
|||
return;
|
||||
}
|
||||
|
||||
// if the outgoing message was of CONFIRMED type
|
||||
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
|
||||
// if ack was not received, we will try retransmission after
|
||||
// ACK_TIMEOUT. handle_data_frame() already disables ACK_TIMEOUT timer
|
||||
// if ack was received. Otherwise, following method will be called in
|
||||
// LoRaMac.cpp, on_ack_timeout_timer_event().
|
||||
if (_loramac.get_mcps_indication()->is_ack_recvd) {
|
||||
tr_debug("Ack=OK, NbTrials=%d",
|
||||
_loramac.get_mcps_confirmation()->nb_retries);
|
||||
_loramac.post_process_mcps_req();
|
||||
_ctrl_flags |= TX_DONE_FLAG;
|
||||
_ctrl_flags &= ~TX_ONGOING_FLAG;
|
||||
state_controller(DEVICE_STATE_STATUS_CHECK);
|
||||
} else {
|
||||
if (!_loramac.continue_sending_process() &&
|
||||
_loramac.get_current_slot() != RX_SLOT_WIN_1) {
|
||||
tr_error("Retries exhausted for Class A device");
|
||||
_ctrl_flags &= ~TX_DONE_FLAG;
|
||||
_ctrl_flags |= TX_ONGOING_FLAG;
|
||||
state_controller(DEVICE_STATE_STATUS_CHECK);
|
||||
}
|
||||
}
|
||||
} else if (_loramac.get_device_class() == CLASS_A) {
|
||||
// handle UNCONFIRMED case here, RX slots were turned off due to
|
||||
// valid packet reception. For Class C, an outgoing UNCONFIRMED message
|
||||
// gets its handling in process_transmission.
|
||||
_loramac.post_process_mcps_req();
|
||||
_ctrl_flags |= TX_DONE_FLAG;
|
||||
state_controller(DEVICE_STATE_STATUS_CHECK);
|
||||
}
|
||||
make_rx_metadata_available();
|
||||
|
||||
// Post process transmission in response to the reception
|
||||
post_process_tx_with_reception();
|
||||
|
||||
// handle any pending MCPS indication
|
||||
if (_loramac.get_mcps_indication()->pending) {
|
||||
|
@ -684,11 +729,8 @@ void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size
|
|||
state_controller(DEVICE_STATE_STATUS_CHECK);
|
||||
}
|
||||
|
||||
// change the state only if a TX cycle completes for Class A
|
||||
// For class C it's not needed as it will already be in receiving
|
||||
// state, no matter if the TX cycle completed or not.
|
||||
if (!(_ctrl_flags & TX_ONGOING_FLAG)) {
|
||||
// we are done here, update the state
|
||||
// complete the cycle only if TX_DONE_FLAG is set
|
||||
if (_ctrl_flags & TX_DONE_FLAG) {
|
||||
state_machine_run_to_completion();
|
||||
}
|
||||
|
||||
|
@ -729,18 +771,7 @@ void LoRaWANStack::process_reception_timeout(bool is_timeout)
|
|||
* never occurs.
|
||||
*/
|
||||
if (slot == RX_SLOT_WIN_2) {
|
||||
_loramac.post_process_mcps_req();
|
||||
|
||||
if (_loramac.get_mcps_confirmation()->req_type == MCPS_CONFIRMED) {
|
||||
if (_loramac.continue_sending_process()) {
|
||||
return;
|
||||
} else {
|
||||
tr_error("Retries exhausted for Class A device");
|
||||
}
|
||||
}
|
||||
|
||||
state_controller(DEVICE_STATE_STATUS_CHECK);
|
||||
state_machine_run_to_completion();
|
||||
post_process_tx_no_reception();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1091,8 +1122,7 @@ void LoRaWANStack::process_shutdown_state(lorawan_status_t &op_status)
|
|||
_lw_session.active = false;
|
||||
_device_current_state = DEVICE_STATE_SHUTDOWN;
|
||||
op_status = LORAWAN_STATUS_DEVICE_OFF;
|
||||
_ctrl_flags &= ~CONNECTED_FLAG;
|
||||
_ctrl_flags &= ~CONN_IN_PROGRESS_FLAG;
|
||||
_ctrl_flags = 0;
|
||||
send_event_to_application(DISCONNECTED);
|
||||
}
|
||||
|
||||
|
@ -1108,20 +1138,15 @@ void LoRaWANStack::process_status_check_state()
|
|||
// Another possibility is the case when the stack fails to schedule a
|
||||
// deferred transmission and a scheduling failure handler is invoked.
|
||||
_ctrl_flags &= ~TX_DONE_FLAG;
|
||||
_ctrl_flags &= ~TX_ONGOING_FLAG;
|
||||
_loramac.set_tx_ongoing(false);
|
||||
_loramac.reset_ongoing_tx();
|
||||
mcps_confirm_handler();
|
||||
|
||||
} else if (_device_current_state == DEVICE_STATE_RECEIVING) {
|
||||
|
||||
if ((_ctrl_flags & TX_DONE_FLAG) || (_ctrl_flags & TX_ONGOING_FLAG)) {
|
||||
// for CONFIRMED case, ack validity is already checked
|
||||
// If it was a successful transmission, TX_ONGOING_FLAG will not be set.
|
||||
// If it was indeed set, that means the device was in Class C mode and
|
||||
// CONFIRMED transmission was in place and the ack retries maxed out.
|
||||
if ((_ctrl_flags & TX_DONE_FLAG) || (_ctrl_flags & RETRY_EXHAUSTED_FLAG)) {
|
||||
_ctrl_flags &= ~TX_DONE_FLAG;
|
||||
_ctrl_flags &= ~TX_ONGOING_FLAG;
|
||||
_ctrl_flags &= ~RETRY_EXHAUSTED_FLAG;
|
||||
_loramac.set_tx_ongoing(false);
|
||||
_loramac.reset_ongoing_tx();
|
||||
// if an automatic uplink is ongoing, we should not send a TX_DONE
|
||||
|
@ -1153,7 +1178,6 @@ void LoRaWANStack::process_scheduling_state(lorawan_status_t &op_status)
|
|||
|
||||
op_status = _loramac.send_ongoing_tx();
|
||||
if (op_status == LORAWAN_STATUS_OK) {
|
||||
_ctrl_flags |= TX_ONGOING_FLAG;
|
||||
_ctrl_flags &= ~TX_DONE_FLAG;
|
||||
_loramac.set_tx_ongoing(true);
|
||||
_device_current_state = DEVICE_STATE_SENDING;
|
||||
|
|
|
@ -483,9 +483,11 @@ private:
|
|||
void make_tx_metadata_available(void);
|
||||
void make_rx_metadata_available(void);
|
||||
|
||||
void handle_ack_expiry_for_class_c(void);
|
||||
void handle_scheduling_failure(void);
|
||||
|
||||
void post_process_tx_with_reception(void);
|
||||
void post_process_tx_no_reception(void);
|
||||
|
||||
private:
|
||||
LoRaMac _loramac;
|
||||
radio_events_t radio_events;
|
||||
|
@ -497,6 +499,7 @@ private:
|
|||
lorawan_tx_metadata _tx_metadata;
|
||||
lorawan_rx_metadata _rx_metadata;
|
||||
uint8_t _num_retry;
|
||||
uint8_t _qos_cnt;
|
||||
uint32_t _ctrl_flags;
|
||||
uint8_t _app_port;
|
||||
bool _link_check_requested;
|
||||
|
|
|
@ -78,7 +78,8 @@ LoRaMac::LoRaMac()
|
|||
_mlme_confirmation(),
|
||||
_is_nwk_joined(false),
|
||||
_continuous_rx2_window_open(false),
|
||||
_device_class(CLASS_A)
|
||||
_device_class(CLASS_A),
|
||||
_prev_qos_level(LORAWAN_DEFAULT_QOS)
|
||||
{
|
||||
_params.keys.dev_eui = NULL;
|
||||
_params.keys.app_eui = NULL;
|
||||
|
@ -173,6 +174,9 @@ void LoRaMac::post_process_mcps_req()
|
|||
if (_params.is_ul_frame_counter_fixed == false) {
|
||||
_params.ul_frame_counter++;
|
||||
_params.adr_ack_counter++;
|
||||
if (_params.sys_params.nb_trans > 1) {
|
||||
_mcps_confirmation.nb_retries = _params.ul_nb_rep_counter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -514,10 +518,6 @@ void LoRaMac::handle_data_frame(const uint8_t *const payload,
|
|||
return;
|
||||
}
|
||||
|
||||
// message is intended for us and MIC have passed, stop RX2 Window
|
||||
// Spec: 3.3.4 Receiver Activity during the receive windows
|
||||
_lora_time.stop(_params.timers.rx_window2_timer);
|
||||
|
||||
_mcps_confirmation.ack_received = false;
|
||||
_mcps_indication.is_ack_recvd = false;
|
||||
_mcps_indication.pending = true;
|
||||
|
@ -567,6 +567,8 @@ void LoRaMac::handle_data_frame(const uint8_t *const payload,
|
|||
tr_debug("Discarding duplicate frame");
|
||||
_mcps_indication.pending = false;
|
||||
_mcps_indication.status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED;
|
||||
|
||||
return;
|
||||
}
|
||||
} else if (msg_type == FRAME_TYPE_DATA_UNCONFIRMED_DOWN) {
|
||||
_params.is_srv_ack_requested = false;
|
||||
|
@ -584,6 +586,19 @@ void LoRaMac::handle_data_frame(const uint8_t *const payload,
|
|||
_params.dl_frame_counter = downlink_counter;
|
||||
}
|
||||
|
||||
// message is intended for us and MIC have passed, stop RX2 Window
|
||||
// Spec: 3.3.4 Receiver Activity during the receive windows
|
||||
if (get_current_slot() == RX_SLOT_WIN_1) {
|
||||
_lora_time.stop(_params.timers.rx_window2_timer);
|
||||
} else {
|
||||
_lora_time.stop(_params.timers.rx_window1_timer);
|
||||
_lora_time.stop(_params.timers.rx_window2_timer);
|
||||
}
|
||||
|
||||
if (_device_class == CLASS_C) {
|
||||
_lora_time.stop(_rx2_closure_timer_for_class_c);
|
||||
}
|
||||
|
||||
if (_params.is_node_ack_requested && fctrl.bits.ack) {
|
||||
_mcps_confirmation.ack_received = fctrl.bits.ack;
|
||||
_mcps_indication.is_ack_recvd = fctrl.bits.ack;
|
||||
|
@ -629,17 +644,32 @@ void LoRaMac::on_radio_tx_done(lorawan_time_t timestamp)
|
|||
_lora_phy->put_radio_to_sleep();
|
||||
}
|
||||
|
||||
if ((_mcps_confirmation.req_type == MCPS_UNCONFIRMED)
|
||||
&& (_params.sys_params.nb_trans > 1)) {
|
||||
_params.ul_nb_rep_counter++;
|
||||
MBED_ASSERT(_params.ul_nb_rep_counter <= _params.sys_params.nb_trans);
|
||||
}
|
||||
|
||||
if (_params.is_rx_window_enabled == true) {
|
||||
lorawan_time_t time_diff = _lora_time.get_current_time() - timestamp;
|
||||
// start timer after which rx1_window will get opened
|
||||
_lora_time.start(_params.timers.rx_window1_timer,
|
||||
_params.rx_window1_delay - time_diff);
|
||||
|
||||
if (_device_class != CLASS_C) {
|
||||
_lora_time.start(_params.timers.rx_window2_timer,
|
||||
_params.rx_window2_delay - time_diff);
|
||||
// start timer after which rx2_window will get opened
|
||||
_lora_time.start(_params.timers.rx_window2_timer,
|
||||
_params.rx_window2_delay - time_diff);
|
||||
|
||||
// If class C and an Unconfirmed messgae is outgoing,
|
||||
// this will start a timer which will invoke rx2 would be
|
||||
// closure handler
|
||||
if (get_device_class() == CLASS_C) {
|
||||
_lora_time.start(_rx2_closure_timer_for_class_c,
|
||||
(_params.rx_window2_delay - time_diff) +
|
||||
_params.rx_window2_config.window_timeout);
|
||||
}
|
||||
|
||||
// start timer after which ack wait will timeout (for Confirmed messages)
|
||||
if (_params.is_node_ack_requested) {
|
||||
_lora_time.start(_params.timers.ack_timeout_timer,
|
||||
(_params.rx_window2_delay - time_diff) +
|
||||
|
@ -663,6 +693,7 @@ void LoRaMac::on_radio_rx_done(const uint8_t *const payload, uint16_t size,
|
|||
int16_t rssi, int8_t snr)
|
||||
{
|
||||
if (_device_class == CLASS_C && !_continuous_rx2_window_open) {
|
||||
_lora_time.stop(_rx2_closure_timer_for_class_c);
|
||||
open_rx2_window();
|
||||
} else if (_device_class != CLASS_C){
|
||||
_lora_time.stop(_params.timers.rx_window1_timer);
|
||||
|
@ -704,6 +735,7 @@ void LoRaMac::on_radio_tx_timeout(void)
|
|||
{
|
||||
_lora_time.stop(_params.timers.rx_window1_timer);
|
||||
_lora_time.stop(_params.timers.rx_window2_timer);
|
||||
_lora_time.stop(_rx2_closure_timer_for_class_c);
|
||||
_lora_time.stop(_params.timers.ack_timeout_timer);
|
||||
|
||||
if (_device_class == CLASS_C) {
|
||||
|
@ -717,16 +749,19 @@ void LoRaMac::on_radio_tx_timeout(void)
|
|||
|
||||
_mac_commands.clear_command_buffer();
|
||||
|
||||
_mcps_confirmation.nb_retries = _params.ack_timeout_retry_counter;
|
||||
if (_mcps_confirmation.req_type == MCPS_CONFIRMED) {
|
||||
_mcps_confirmation.nb_retries = _params.ack_timeout_retry_counter;
|
||||
} else {
|
||||
_mcps_confirmation.nb_retries = _params.ul_nb_rep_counter;
|
||||
}
|
||||
|
||||
_mcps_confirmation.ack_received = false;
|
||||
_mcps_confirmation.tx_toa = 0;
|
||||
}
|
||||
|
||||
void LoRaMac::on_radio_rx_timeout(bool is_timeout)
|
||||
{
|
||||
if (_device_class == CLASS_C && !_continuous_rx2_window_open) {
|
||||
open_rx2_window();
|
||||
} else {
|
||||
if (_device_class == CLASS_A) {
|
||||
_lora_phy->put_radio_to_sleep();
|
||||
}
|
||||
|
||||
|
@ -905,12 +940,6 @@ void LoRaMac::on_ack_timeout_timer_event(void)
|
|||
Lock lock(*this);
|
||||
|
||||
if (_params.ack_timeout_retry_counter > _params.max_ack_timeout_retries) {
|
||||
if (get_device_class() == CLASS_C) {
|
||||
// no need to use EventQueue as LoRaWANStack and LoRaMac are always
|
||||
// in same context
|
||||
_mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_ERROR;
|
||||
_ack_expiry_handler_for_class_c.call();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1280,7 +1309,7 @@ int16_t LoRaMac::prepare_ongoing_tx(const uint8_t port,
|
|||
if (flags & MSG_PROPRIETARY_FLAG) {
|
||||
_ongoing_tx_msg.type = MCPS_PROPRIETARY;
|
||||
_ongoing_tx_msg.fport = port;
|
||||
_ongoing_tx_msg.nb_trials = 1;
|
||||
_ongoing_tx_msg.nb_trials = _params.sys_params.nb_trans;
|
||||
// a proprietary frame only includes an MHDR field which contains MTYPE field.
|
||||
// Everything else is at the discretion of the implementer
|
||||
fopts_len = 0;
|
||||
|
@ -1365,10 +1394,12 @@ device_class_t LoRaMac::get_device_class() const
|
|||
}
|
||||
|
||||
void LoRaMac::set_device_class(const device_class_t &device_class,
|
||||
mbed::Callback<void(void)>ack_expiry_handler)
|
||||
mbed::Callback<void(void)>rx2_would_be_closure_handler)
|
||||
{
|
||||
_device_class = device_class;
|
||||
_ack_expiry_handler_for_class_c = ack_expiry_handler;
|
||||
_rx2_would_be_closure_for_class_c = rx2_would_be_closure_handler;
|
||||
|
||||
_lora_time.init(_rx2_closure_timer_for_class_c, _rx2_would_be_closure_for_class_c);
|
||||
|
||||
if (CLASS_A == _device_class) {
|
||||
tr_debug("Changing device class to -> CLASS_A");
|
||||
|
@ -1744,6 +1775,8 @@ lorawan_status_t LoRaMac::initialize(EventQueue *queue,
|
|||
|
||||
_ev_queue = queue;
|
||||
_scheduling_failure_handler = scheduling_failure_handler;
|
||||
_rx2_closure_timer_for_class_c.callback = NULL;
|
||||
_rx2_closure_timer_for_class_c.timer_id = -1;
|
||||
|
||||
_channel_plan.activate_channelplan_subsystem(_lora_phy);
|
||||
|
||||
|
@ -1757,7 +1790,7 @@ lorawan_status_t LoRaMac::initialize(EventQueue *queue,
|
|||
_params.timers.aggregated_timeoff = 0;
|
||||
|
||||
_lora_phy->reset_to_default_values(&_params, true);
|
||||
_params.sys_params.retry_num = 1;
|
||||
_params.sys_params.nb_trans = 1;
|
||||
|
||||
reset_mac_parameters();
|
||||
|
||||
|
@ -1937,3 +1970,18 @@ void LoRaMac::bind_phy(LoRaPHY &phy)
|
|||
{
|
||||
_lora_phy = &phy;
|
||||
}
|
||||
|
||||
uint8_t LoRaMac::get_QOS_level()
|
||||
{
|
||||
if (_prev_qos_level != _params.sys_params.nb_trans) {
|
||||
_prev_qos_level = _params.sys_params.nb_trans;
|
||||
}
|
||||
|
||||
return _params.sys_params.nb_trans;
|
||||
}
|
||||
|
||||
uint8_t LoRaMac::get_prev_QOS_level()
|
||||
{
|
||||
return _prev_qos_level;
|
||||
}
|
||||
|
||||
|
|
|
@ -295,10 +295,11 @@ public:
|
|||
/**
|
||||
* @brief set_device_class Sets active device class.
|
||||
* @param device_class Device class to use.
|
||||
* @param ack_expiry_handler callback function to inform about ack expiry
|
||||
* @param rx2_would_be_closure_handler callback function to inform about
|
||||
* would be closure of RX2 window
|
||||
*/
|
||||
void set_device_class(const device_class_t &device_class,
|
||||
mbed::Callback<void(void)>ack_expiry_handler);
|
||||
mbed::Callback<void(void)>rx2_would_be_closure_handler);
|
||||
|
||||
/**
|
||||
* @brief setup_link_check_request Adds link check request command
|
||||
|
@ -404,6 +405,17 @@ public:
|
|||
*/
|
||||
rx_slot_t get_current_slot(void);
|
||||
|
||||
/**
|
||||
* Indicates what level of QOS is set by network server. QOS level is set
|
||||
* in response to a LinkADRReq for UNCONFIRMED messages
|
||||
*/
|
||||
uint8_t get_QOS_level(void);
|
||||
|
||||
/**
|
||||
*Indicates level of QOS used for the previous outgoing message
|
||||
*/
|
||||
uint8_t get_prev_QOS_level(void);
|
||||
|
||||
/**
|
||||
* These locks trample through to the upper layers and make
|
||||
* the stack thread safe.
|
||||
|
@ -632,10 +644,16 @@ private:
|
|||
|
||||
/**
|
||||
* Class C doesn't timeout in RX2 window as it is a continuous window.
|
||||
* We use this callback to inform the LoRaWANStack controller that the
|
||||
* system cannot do more retries.
|
||||
* We use this callback to inform the LoRaWANStack controller that we did
|
||||
* not receive a downlink in a time equal to normal Class A type RX2
|
||||
* window timeout. This marks a 'would-be' closure for RX2, actual RX2 is
|
||||
* not closed. Mostly network servers will send right at the beginning of
|
||||
* RX2 window if they have something to send. So if we didn't receive anything
|
||||
* in the time period equal to would be RX2 delay (which is a function of
|
||||
* uplink message length and data rate), we will invoke this callback to let
|
||||
* the upper layer know.
|
||||
*/
|
||||
mbed::Callback<void(void)> _ack_expiry_handler_for_class_c;
|
||||
mbed::Callback<void(void)> _rx2_would_be_closure_for_class_c;
|
||||
|
||||
/**
|
||||
* Transmission is async, i.e., a call to schedule_tx() may be deferred to
|
||||
|
@ -645,6 +663,8 @@ private:
|
|||
*/
|
||||
mbed::Callback<void(void)> _scheduling_failure_handler;
|
||||
|
||||
timer_event_t _rx2_closure_timer_for_class_c;
|
||||
|
||||
/**
|
||||
* Structure to hold MCPS indication data.
|
||||
*/
|
||||
|
@ -672,6 +692,8 @@ private:
|
|||
bool _continuous_rx2_window_open;
|
||||
|
||||
device_class_t _device_class;
|
||||
|
||||
uint8_t _prev_qos_level;
|
||||
};
|
||||
|
||||
#endif // MBED_LORAWAN_MAC_H__
|
||||
|
|
|
@ -1031,7 +1031,7 @@ uint8_t LoRaPHY::link_ADR_request(adr_req_params_t *link_adr_req,
|
|||
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.current_nb_rep = link_adr_req->current_nb_trans;
|
||||
|
||||
verify_params.datarate = adr_settings.datarate;
|
||||
verify_params.tx_power = adr_settings.tx_power;
|
||||
|
|
|
@ -479,7 +479,7 @@ uint8_t LoRaPHYAU915::link_ADR_request(adr_req_params_t* params,
|
|||
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.current_nb_rep = params->current_nb_trans;
|
||||
verify_params.channel_mask = temp_channel_masks;
|
||||
|
||||
|
||||
|
|
|
@ -510,7 +510,7 @@ uint8_t LoRaPHYCN470::link_ADR_request(adr_req_params_t* params,
|
|||
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.current_nb_rep = params->current_nb_trans;
|
||||
verify_params.channel_mask = temp_channel_masks;
|
||||
|
||||
|
||||
|
|
|
@ -517,7 +517,7 @@ uint8_t LoRaPHYUS915::link_ADR_request(adr_req_params_t* params,
|
|||
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.current_nb_rep = params->current_nb_trans;
|
||||
verify_params.channel_mask = temp_channel_masks;
|
||||
|
||||
// Verify the parameters and update, if necessary
|
||||
|
|
|
@ -329,9 +329,10 @@ typedef struct {
|
|||
*/
|
||||
int8_t current_tx_power;
|
||||
/*!
|
||||
* The current number of repetitions.
|
||||
* The current number of repetitions for obtaining a QOS level set by
|
||||
* NS (applicable only to unconfirmed messages).
|
||||
*/
|
||||
uint8_t current_nb_rep;
|
||||
uint8_t current_nb_trans;
|
||||
} adr_req_params_t;
|
||||
|
||||
/**
|
||||
|
|
|
@ -70,6 +70,8 @@ typedef uint32_t lorawan_time_t;
|
|||
*/
|
||||
#define LORAMAC_PHY_MAXPAYLOAD 255
|
||||
|
||||
#define LORAWAN_DEFAULT_QOS 1
|
||||
|
||||
/**
|
||||
*
|
||||
* Default user application maximum data size for transmission
|
||||
|
@ -187,9 +189,10 @@ typedef struct {
|
|||
*/
|
||||
uint32_t join_accept_delay2;
|
||||
/*!
|
||||
* The number of uplink messages repetitions (confirmed messages only).
|
||||
* The number of uplink messages repetitions for QOS set by network server
|
||||
* in LinkADRReq mac command (unconfirmed messages only).
|
||||
*/
|
||||
uint8_t retry_num;
|
||||
uint8_t nb_trans;
|
||||
/*!
|
||||
* The datarate offset between uplink and downlink on first window.
|
||||
*/
|
||||
|
@ -874,6 +877,9 @@ typedef struct {
|
|||
*/
|
||||
int8_t data_rate;
|
||||
/*!
|
||||
*
|
||||
* For CONFIRMED Messages:
|
||||
*
|
||||
* 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
|
||||
|
@ -892,6 +898,13 @@ typedef struct {
|
|||
*
|
||||
* 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.
|
||||
*
|
||||
* For UNCONFIRMED Messages:
|
||||
*
|
||||
* Provides a certain QOS level set by network server in LinkADRReq MAC
|
||||
* command. The device will transmit the given UNCONFIRMED message nb_trials
|
||||
* time with same frame counter until a downlink is received. Standard defined
|
||||
* range is 1:15. Data rates will NOT be adapted according to chapter 18.4.
|
||||
*/
|
||||
uint8_t nb_trials;
|
||||
|
||||
|
|
Loading…
Reference in New Issue