diff --git a/lib/include/srslte/phy/common/phy_common.h b/lib/include/srslte/phy/common/phy_common.h index 74b1ae65b..de7a122c7 100644 --- a/lib/include/srslte/phy/common/phy_common.h +++ b/lib/include/srslte/phy/common/phy_common.h @@ -135,7 +135,7 @@ typedef enum { SRSLTE_SF_NORM = 0, SRSLTE_SF_MBSFN } srslte_sf_t; #define SRSLTE_TA_OFFSET (10e-6) -#define SRSLTE_LTE_TS 1.0 / (15000.0 * 2048) +#define SRSLTE_LTE_TS (1.0f / (15000.0f * 2048.0f)) #define SRSLTE_SLOT_IDX_CPNORM(symbol_idx, symbol_sz) \ (symbol_idx == 0 ? 0 \ diff --git a/srsenb/hdr/stack/mac/mac.h b/srsenb/hdr/stack/mac/mac.h index c9ed6cb0e..8d41a8135 100644 --- a/srsenb/hdr/stack/mac/mac.h +++ b/srsenb/hdr/stack/mac/mac.h @@ -31,6 +31,7 @@ #include "srslte/interfaces/enb_interfaces.h" #include "srslte/interfaces/enb_metrics_interface.h" #include "srslte/interfaces/sched_interface.h" +#include "ta.h" #include "ue.h" #include @@ -114,8 +115,8 @@ private: stack_interface_mac_lte* stack = nullptr; srslte::log* log_h = nullptr; - cell_list_t cells = {}; - mac_args_t args = {}; + cell_list_t cells = {}; + mac_args_t args = {}; // derived from args srslte::task_multiqueue::queue_handler stack_task_queue; diff --git a/srsenb/hdr/stack/mac/ta.h b/srsenb/hdr/stack/mac/ta.h new file mode 100644 index 000000000..46fdd4c04 --- /dev/null +++ b/srsenb/hdr/stack/mac/ta.h @@ -0,0 +1,258 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSENB_TA_H +#define SRSENB_TA_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" + +#include +#include + +namespace srsenb { + +/** + * Time Aligment FSM parent/callback interface for pushing n TA values + */ +class mac_ta_ue_interface +{ +public: + virtual uint32_t set_ta(int ta) = 0; +}; + +/** + * UE's FSM for controlling Time Aligment command generation. + * + * Initially the FSM starts at none state which automatically transitions to Measure. Measurements are collected while + * the FSM is in Measure state. Up to MAX_NOF_MEAS are stored. The FSM uses a minimum of MIN_NOF_MEAS measurements to + * compute the TA average. The TA command is triggered as soon as the TA average is higher than TA_N_THRESHOLD. After + * triggering a TA command, holds in prohibit state for PROHIBIT_PERIOD_MS before start collecting measurements. + * + * +------+ First call +---------+ Trigger +----------+ + * | None | ------------->| Measure |------------>| Prohibit | + * +------+ +---------+ +----------+ + * ^ ^ | + * | | Prohibit expires | + * --+ +------------------------+ + * + * + */ +class ta +{ +private: + /// Control constants + static constexpr uint32_t MAX_NOF_MEAS = 16; ///< Maximum number of measurements to store + static constexpr uint32_t MIN_NOF_MEAS = 4; ///< Minimum number of measurements to compute + static constexpr uint32_t MAX_MEAS_TIME_MS = 100; ///< Maximum time of measurement + static constexpr uint32_t PROHIBIT_PERIOD_MS = 20; ///< Time to wait from the n TA value push to measure again + static constexpr int32_t TA_N_THRESHOLD = 1; ///< n TA value threshold + + /// Parent/callback object + mac_ta_ue_interface* parent = nullptr; + + /// TA measure datatype + typedef struct { + uint32_t ts_ms; ///< Time in which the measurement was taken in ms + float ta_us; ///< TA measurement in microseconds + } ta_meas_t; + + uint32_t meas_t_ms = 0; ///< Time counter in milliseconds + uint32_t meas_count = 0; ///< Number of measures in the buffer + uint32_t meas_idx = 0; ///< Next mesurement index in the buffer + std::vector meas_values; + + // FSM states + typedef enum { + state_none = 0, ///< NO state has been set yet + state_measure, ///< Performing measurement + state_prohibit ///< Waiting for HARQ to transmit CE command, NO measurement shall be stored + } state_t; + state_t state = state_none; + + /** + * Reset Measurement and timer counter + */ + void reset_measurements() + { + meas_t_ms = 0; + meas_count = 0; + meas_idx = 0; + } + + /** + * Averages/extrapolates n TA value + * + * @return the required n TA value for the current time (meas_t_ms) + */ + int get_ta_n() + { + float ta_us = 0.0f; + + // Average all measurements + for (uint32_t i = 0; i < meas_count; i++) { + // Write here a much fancier extrapolation algorithm + ta_us += meas_values[i].ta_us; + } + if (meas_count) { + ta_us /= static_cast(meas_count); + } + + // Return the n_ta value + return static_cast(std::roundf(ta_us * 1e-6f / SRSLTE_LTE_TS / 16.0f)); + } + + /** + * Runs initial state none + * @return 0 + */ + uint32_t run_state_none() + { + reset_measurements(); + state = state_measure; + return 0; + } + + /** + * Runs measure state + * @return the number of enqueued MAC CE carrying TA commands + */ + uint32_t run_state_measure() + { + uint32_t ret = 0; + + // Avoid processing if no measurement or no minimum covered or maximum measuring time is reached + if (meas_count == 0 or (meas_count < MIN_NOF_MEAS and meas_t_ms < MAX_MEAS_TIME_MS)) { + return ret; + } + + // Get TA command value + int ta_n = get_ta_n(); + + // Send command + if (abs(ta_n) > TA_N_THRESHOLD) { + // Set UE TA + ret = parent->set_ta(ta_n); + + // Reset measurement counter + reset_measurements(); + + // Transition to prohibit state + state = state_prohibit; + } + + return ret; + } + + /** + * Runs prohibit state + * @return 0 + */ + uint32_t run_state_prohibit() + { + // Prohibit time expired + if (meas_t_ms >= PROHIBIT_PERIOD_MS) { + // Reset counters + reset_measurements(); + + // Go to measure + state = state_measure; + } + + return 0; + } + + /** + * Runs The internal FSM + * @return the number of eneuqued MAC CE carrying TA commands + */ + uint32_t run_fsm() + { + switch (state) { + case state_none: + return run_state_none(); + case state_measure: + return run_state_measure(); + case state_prohibit: + return run_state_prohibit(); + default:; // Do nothing + } + + return 0; + } + +public: + /** + * TA FSM Constructor + * @param parent_ UE MAC object with TA callback setter + */ + explicit ta(mac_ta_ue_interface* parent_) : parent(parent_), meas_values(MAX_NOF_MEAS) + { + /// Initial FSM run + run_fsm(); + } + + /** + * Pushes TA measurement and runs internal FSM + * + * @param ta_us actual TA measurement in microseconds + * @return the number of MAC CE carrying TA + */ + uint32_t push_value(float ta_us) + { + // Put measurement if state is measurement + if (state == state_measure) { + // Set measurement + meas_values[meas_idx].ts_ms = meas_t_ms; + meas_values[meas_idx].ta_us = ta_us; + + // Increase pointer + meas_idx = (meas_idx + 1) % static_cast(meas_values.size()); + + // Increase count + if (meas_count < static_cast(meas_values.size())) { + meas_count++; + } + } + + // Run state machine + return run_fsm(); + } + + /** + * Increments internal timer 1 ms and runs internal FSM + * + * @param ta_us actual TA measurement in microseconds + * @return the number of MAC CE carrying TA + */ + uint32_t tick() + { + // Increase measurement timestamp counter + meas_t_ms++; + + // Run state machine + return run_fsm(); + } +}; + +} // namespace srsenb + +#endif // SRSENB_TA_H diff --git a/srsenb/hdr/stack/mac/ue.h b/srsenb/hdr/stack/mac/ue.h index ebaae2445..c02c03408 100644 --- a/srsenb/hdr/stack/mac/ue.h +++ b/srsenb/hdr/stack/mac/ue.h @@ -30,12 +30,13 @@ #include "srslte/common/pdu_queue.h" #include "srslte/interfaces/enb_interfaces.h" #include "srslte/interfaces/sched_interface.h" +#include "ta.h" #include #include namespace srsenb { -class ue : public srslte::read_pdu_interface, public srslte::pdu_queue::process_callback +class ue : public srslte::read_pdu_interface, public srslte::pdu_queue::process_callback, public mac_ta_ue_interface { public: ue(uint16_t rnti, @@ -55,7 +56,10 @@ public: void set_tti(uint32_t tti); - uint32_t set_ta(int ta); + uint32_t set_ta(int ta) override; + + uint32_t set_ta_us(float ta_us) { return ta_fsm.push_value(ta_us); }; + uint32_t tick_ta_fsm() { return ta_fsm.tick(); }; void config(uint16_t rnti, uint32_t nof_prb, @@ -138,6 +142,7 @@ private: srslte::byte_buffer_t tx_payload_buffer[SRSLTE_MAX_CARRIERS][SRSLTE_FDD_NOF_HARQ][SRSLTE_MAX_TB]; srslte::block_queue pending_ta_commands; + ta ta_fsm; // For UL there are multiple buffers per PID and are managed by pdu_queue srslte::pdu_queue pdus; diff --git a/srsenb/src/stack/mac/mac.cc b/srsenb/src/stack/mac/mac.cc index 552f346f8..8e3fde0c9 100644 --- a/srsenb/src/stack/mac/mac.cc +++ b/srsenb/src/stack/mac/mac.cc @@ -418,7 +418,13 @@ int mac::snr_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, float snr) int mac::ta_info(uint32_t tti, uint16_t rnti, float ta_us) { - log_h->info("TA: tti=%d, rnti=0x%04x, ta_us=%.1f\n", tti, rnti, ta_us); + srslte::rwlock_write_guard lock(rwlock); + if (ue_db.count(rnti)) { + uint32_t nof_ta_count = ue_db[rnti]->set_ta_us(ta_us); + if (nof_ta_count) { + scheduler.ue_needs_ta_cmd(rnti, nof_ta_count); + } + } return SRSLTE_SUCCESS; } @@ -782,6 +788,14 @@ int mac::get_ul_sched(uint32_t tti, ul_sched_list_t& ul_sched_res_list) log_h->step(tti); + // Execute TA FSM + for (auto& ue : ue_db) { + uint32_t nof_ta_count = ue.second->tick_ta_fsm(); + if (nof_ta_count) { + scheduler.ue_needs_ta_cmd(ue.first, nof_ta_count); + } + } + for (uint32_t enb_cc_idx = 0; enb_cc_idx < cell_config.size(); enb_cc_idx++) { ul_sched_t* phy_ul_sched_res = &ul_sched_res_list[enb_cc_idx]; diff --git a/srsenb/src/stack/mac/ue.cc b/srsenb/src/stack/mac/ue.cc index 6954b2dd6..444c79f2a 100644 --- a/srsenb/src/stack/mac/ue.cc +++ b/srsenb/src/stack/mac/ue.cc @@ -55,7 +55,8 @@ ue::ue(uint16_t rnti_, mac_msg_ul(20, log_), pdus(128), nof_rx_harq_proc(nof_rx_harq_proc_), - nof_tx_harq_proc(nof_tx_harq_proc_) + nof_tx_harq_proc(nof_tx_harq_proc_), + ta_fsm(this) { pdus.init(this, log_h);