From 828815c7e333514e289e19d50f0069496da2f03a Mon Sep 17 00:00:00 2001 From: Hasnain Virk Date: Mon, 11 Jun 2018 15:29:49 +0300 Subject: [PATCH] Adding precise timing for receive delays RX1 and 2 delays needed to be more precise and aggregate tx time was drifiting because of timing difference between actual tx interrupt and our processing of that interrupt ever so slightly. We now take a timestamp of the tx interrupt and take a time diff while instantiating delay timers. The timestamp is then used to update the aggregate tx time. Two new methods are introduced in the LoRaMac class which provide current timing and current receive slot. These functions are used by LoRaWANStack for its processing. --- features/lorawan/LoRaWANStack.cpp | 7 +- features/lorawan/LoRaWANStack.h | 1 + features/lorawan/lorastack/mac/LoRaMac.cpp | 107 +++++++++++---------- features/lorawan/lorastack/mac/LoRaMac.h | 14 ++- 4 files changed, 74 insertions(+), 55 deletions(-) diff --git a/features/lorawan/LoRaWANStack.cpp b/features/lorawan/LoRaWANStack.cpp index d91de2e7f6..15c04fc209 100644 --- a/features/lorawan/LoRaWANStack.cpp +++ b/features/lorawan/LoRaWANStack.cpp @@ -503,6 +503,7 @@ lorawan_status_t LoRaWANStack::acquire_backoff_metadata(int& backoff) ****************************************************************************/ void LoRaWANStack::tx_interrupt_handler(void) { + _tx_timestamp = _loramac.get_current_time(); const int ret = _queue->call(this, &LoRaWANStack::process_transmission); MBED_ASSERT(ret != 0); (void)ret; @@ -565,7 +566,7 @@ void LoRaWANStack::process_transmission_timeout() void LoRaWANStack::process_transmission(void) { tr_debug("Transmission completed"); - _loramac.on_radio_tx_done(); + _loramac.on_radio_tx_done(_tx_timestamp); make_tx_metadata_available(); @@ -680,10 +681,12 @@ void LoRaWANStack::process_reception(const uint8_t *const payload, uint16_t size void LoRaWANStack::process_reception_timeout(bool is_timeout) { + rx_slot_t slot = _loramac.get_current_slot(); + // when is_timeout == false, a CRC error took place in the received frame // we treat that erroneous frame as no frame received at all, hence handle // it exactly as we would handle timeout - rx_slot_t slot = _loramac.on_radio_rx_timeout(is_timeout); + _loramac.on_radio_rx_timeout(is_timeout); if (slot == RX_SLOT_WIN_2 && !_loramac.nwk_joined()) { state_controller(DEVICE_STATE_JOINING); diff --git a/features/lorawan/LoRaWANStack.h b/features/lorawan/LoRaWANStack.h index b373a5d3d0..21e85d752b 100644 --- a/features/lorawan/LoRaWANStack.h +++ b/features/lorawan/LoRaWANStack.h @@ -548,6 +548,7 @@ private: volatile bool _ready_for_rx; uint8_t _rx_payload[LORAMAC_PHY_MAXPAYLOAD]; events::EventQueue *_queue; + lorawan_time_t _tx_timestamp; #if defined(LORAWAN_COMPLIANCE_TEST) diff --git a/features/lorawan/lorastack/mac/LoRaMac.cpp b/features/lorawan/lorastack/mac/LoRaMac.cpp index d8be5bde1f..bcd8226e90 100644 --- a/features/lorawan/lorastack/mac/LoRaMac.cpp +++ b/features/lorawan/lorastack/mac/LoRaMac.cpp @@ -189,41 +189,14 @@ void LoRaMac::post_process_mlme_ind() _mlme_indication.pending = false; } -void LoRaMac::on_radio_tx_done(void) +lorawan_time_t LoRaMac::get_current_time(void) { - lorawan_time_t cur_time = _lora_time.get_current_time(); + return _lora_time.get_current_time(); +} - if (_device_class != CLASS_C) { - _lora_phy.put_radio_to_sleep(); - } else { - // this will open a continuous RX2 window until time==RECV_DELAY1 - if (!_continuous_rx2_window_open) { - open_rx2_window(); - } - } - - if (_params.is_rx_window_enabled == true) { - // start timer after which rx1_window will get opened - _lora_time.start(_params.timers.rx_window1_timer, _params.rx_window1_delay); - - if (_device_class != CLASS_C) { - _lora_time.start(_params.timers.rx_window2_timer, _params.rx_window2_delay); - } - - if (_params.is_node_ack_requested) { - _lora_time.start(_params.timers.ack_timeout_timer, - _params.rx_window2_delay + _lora_phy.get_ack_timeout()); - } - } else { - _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_OK; - _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; - } - - _params.last_channel_idx = _params.channel; - - _lora_phy.set_last_tx_done(_params.channel, _is_nwk_joined, cur_time); - - _params.timers.aggregated_last_tx_time = cur_time; +rx_slot_t LoRaMac::get_current_slot(void) +{ + return _params.rx_slot; } /** @@ -656,7 +629,44 @@ void LoRaMac::set_batterylevel_callback(mbed::Callback battery_le _mac_commands.set_batterylevel_callback(battery_level); } -void LoRaMac::on_radio_rx_done(const uint8_t *const payload, uint16_t size, +void LoRaMac::on_radio_tx_done(lorawan_time_t timestamp) +{ + if (_device_class == CLASS_C) { + // this will open a continuous RX2 window until time==RECV_DELAY1 + open_rx2_window(); + } else { + _lora_phy.put_radio_to_sleep(); + } + + 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); + } + + if (_params.is_node_ack_requested) { + _lora_time.start(_params.timers.ack_timeout_timer, + (_params.rx_window2_delay - time_diff) + + _lora_phy.get_ack_timeout()); + } + } else { + _mcps_confirmation.status = LORAMAC_EVENT_INFO_STATUS_OK; + _mlme_confirmation.status = LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT; + } + + _params.last_channel_idx = _params.channel; + + _lora_phy.set_last_tx_done(_params.channel, _is_nwk_joined, timestamp); + + _params.timers.aggregated_last_tx_time = timestamp; +} + +void LoRaMac::on_radio_rx_done(const uint8_t* const payload, uint16_t size, int16_t rssi, int8_t snr) { // stop the RX1 timer here if its the first RX slot. @@ -731,9 +741,11 @@ void LoRaMac::on_radio_tx_timeout(void) post_process_mcps_req(); } -rx_slot_t LoRaMac::on_radio_rx_timeout(bool is_timeout) +void LoRaMac::on_radio_rx_timeout(bool is_timeout) { - if (_device_class != CLASS_C) { + if (_device_class == CLASS_C && !_continuous_rx2_window_open) { + open_rx2_window(); + } else { _lora_phy.put_radio_to_sleep(); } @@ -763,12 +775,6 @@ rx_slot_t LoRaMac::on_radio_rx_timeout(bool is_timeout) LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT : LORAMAC_EVENT_INFO_STATUS_RX2_ERROR; } - - if (_device_class == CLASS_C) { - open_rx2_window(); - } - - return _params.rx_slot; } bool LoRaMac::continue_joining_process() @@ -857,7 +863,6 @@ void LoRaMac::on_backoff_timer_expiry(void) void LoRaMac::open_rx1_window(void) { Lock lock(*this); - tr_debug("Opening RX1 Window"); _continuous_rx2_window_open = false; _lora_time.stop(_params.timers.rx_window1_timer); _params.rx_slot = RX_SLOT_WIN_1; @@ -878,25 +883,26 @@ void LoRaMac::open_rx1_window(void) _lora_phy.setup_rx_window(_params.rx_window1_config.is_rx_continuous, _params.sys_params.max_rx_win_time); + + tr_debug("Opening RX1 Window"); } void LoRaMac::open_rx2_window() { Lock lock(*this); - tr_debug("Opening RX2 Window"); + _continuous_rx2_window_open = true; _lora_time.stop(_params.timers.rx_window2_timer); _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; + _params.rx_window2_config.rx_slot = _params.rx_window2_config.is_rx_continuous ? + RX_SLOT_WIN_CLASS_C : RX_SLOT_WIN_2; if (get_device_class() == CLASS_C) { - _continuous_rx2_window_open = true; _params.rx_window2_config.is_rx_continuous = true; } else { - _continuous_rx2_window_open = false; _params.rx_window2_config.is_rx_continuous = false; } @@ -907,9 +913,10 @@ void LoRaMac::open_rx2_window() _lora_phy.setup_rx_window(_params.rx_window2_config.is_rx_continuous, _params.sys_params.max_rx_win_time); - _params.rx_slot = _params.rx_window2_config.is_rx_continuous ? - RX_SLOT_WIN_CLASS_C : RX_SLOT_WIN_2; + _params.rx_slot = _params.rx_window2_config.rx_slot; } + + tr_debug("Opening RX2 Window, Frequency = %u", _params.rx_window2_config.frequency); } void LoRaMac::on_ack_timeout_timer_event(void) @@ -1346,8 +1353,6 @@ void LoRaMac::set_device_class(const device_class_t &device_class, tr_debug("Changing device class to -> CLASS_C"); open_rx2_window(); } - - } void LoRaMac::setup_link_check_request() diff --git a/features/lorawan/lorastack/mac/LoRaMac.h b/features/lorawan/lorastack/mac/LoRaMac.h index 479869aa97..3b6dbccd73 100644 --- a/features/lorawan/lorastack/mac/LoRaMac.h +++ b/features/lorawan/lorastack/mac/LoRaMac.h @@ -386,7 +386,7 @@ public: /** * MAC operations upon successful transmission */ - void on_radio_tx_done(void); + void on_radio_tx_done(lorawan_time_t timestamp); /** * MAC operations upon reception @@ -407,7 +407,7 @@ public: * * @return current RX slot */ - rx_slot_t on_radio_rx_timeout(bool is_timeout); + void on_radio_rx_timeout(bool is_timeout); /** * Handles retransmissions of Join requests if an Accept @@ -455,6 +455,16 @@ public: */ lorawan_status_t clear_tx_pipe(void); + /** + * Gets the current time + */ + lorawan_time_t get_current_time(void); + + /** + * Gets the current receive slot + */ + rx_slot_t get_current_slot(void); + /** * These locks trample through to the upper layers and make * the stack thread safe.