From 198684ce32d63dc076965559f9fb800682ec21d8 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Thu, 9 Apr 2020 12:42:55 +0200 Subject: [PATCH] SRSUE: all TA control logic into a single class and faster TA response --- srsue/hdr/phy/phy.h | 4 - srsue/hdr/phy/phy_common.h | 4 + srsue/hdr/phy/sf_worker.h | 3 +- srsue/hdr/phy/sync.h | 5 - srsue/hdr/phy/ta_control.h | 238 ++++++++++++++++++++++++++++++++++++ srsue/src/phy/phy.cc | 22 +--- srsue/src/phy/phy_common.cc | 2 + srsue/src/phy/sf_worker.cc | 12 +- srsue/src/phy/sync.cc | 32 ++--- 9 files changed, 265 insertions(+), 57 deletions(-) create mode 100644 srsue/hdr/phy/ta_control.h diff --git a/srsue/hdr/phy/phy.h b/srsue/hdr/phy/phy.h index 8d3595cc0..f33c25640 100644 --- a/srsue/hdr/phy/phy.h +++ b/srsue/hdr/phy/phy.h @@ -156,10 +156,6 @@ private: srslte::phy_cfg_t config = {}; phy_args_t args = {}; - /* Current time advance */ - uint32_t n_ta = 0; - uint32_t n_ta_base = 0; - static void set_default_args(phy_args_t& args); bool check_args(const phy_args_t& args); }; diff --git a/srsue/hdr/phy/phy_common.h b/srsue/hdr/phy/phy_common.h index a3d511402..8d64b6e72 100644 --- a/srsue/hdr/phy/phy_common.h +++ b/srsue/hdr/phy/phy_common.h @@ -30,6 +30,7 @@ #include "srslte/interfaces/ue_interfaces.h" #include "srslte/radio/radio.h" #include "srslte/srslte.h" +#include "ta_control.h" #include #include #include @@ -88,6 +89,9 @@ public: srslte::tti_semaphore semaphore; + // Time Aligment Controller, internal thread safe + ta_control ta; + phy_common(); ~phy_common(); diff --git a/srsue/hdr/phy/sf_worker.h b/srsue/hdr/phy/sf_worker.h index 247504d60..360f90b59 100644 --- a/srsue/hdr/phy/sf_worker.h +++ b/srsue/hdr/phy/sf_worker.h @@ -55,7 +55,7 @@ public: cf_t* get_buffer(uint32_t cc_idx, uint32_t antenna_idx); uint32_t get_buffer_len(); void set_tti(uint32_t tti); - void set_tx_time(srslte_timestamp_t tx_time, int next_offset); + void set_tx_time(srslte_timestamp_t tx_time); void set_prach(cf_t* prach_ptr, float prach_power); void set_cfo(const uint32_t& cc_idx, float cfo); @@ -111,7 +111,6 @@ private: uint32_t tti = 0; srslte_timestamp_t tx_time = {}; - int next_offset = {}; uint32_t rssi_read_cnt = 0; }; diff --git a/srsue/hdr/phy/sync.h b/srsue/hdr/phy/sync.h index 6f06578a1..da5c8928a 100644 --- a/srsue/hdr/phy/sync.h +++ b/srsue/hdr/phy/sync.h @@ -78,7 +78,6 @@ public: void out_of_sync() final; void set_cfo(float cfo) final; - void set_time_adv_sec(float time_adv_sec); void get_current_cell(srslte_cell_t* cell, uint32_t* earfcn = nullptr); uint32_t get_current_tti(); @@ -177,8 +176,6 @@ private: std::vector > intra_freq_meas; uint32_t current_sflen = 0; - int next_offset = 0; // Sample offset triggered by Time aligment commands - int next_radio_offset = 0; // Sample offset triggered by SFO compensation // Pointers to other classes stack_interface_phy_lte* stack = nullptr; @@ -344,8 +341,6 @@ private: // This is the primary cell srslte_cell_t cell = {}; bool started = false; - float time_adv_sec = 0; - float next_time_adv_sec = 0; uint32_t tti = 0; srslte_timestamp_t tti_ts = {}; srslte_timestamp_t radio_ts = {}; diff --git a/srsue/hdr/phy/ta_control.h b/srsue/hdr/phy/ta_control.h new file mode 100644 index 000000000..892d703d4 --- /dev/null +++ b/srsue/hdr/phy/ta_control.h @@ -0,0 +1,238 @@ +/* + * Copyright 2013-2020 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 SRSUE_TA_CONTROL_H +#define SRSUE_TA_CONTROL_H + +#include +#include +#include + +namespace srsue { + +class ta_control +{ +private: + srslte::log* log_h = nullptr; + mutable std::mutex mutex; + uint32_t next_base_nta = 0; + float next_base_sec = 0.0f; + float current_base_sec = 0.0f; + float prev_base_sec = 0.0f; + int32_t pending_nsamples = 0; + +public: + /** + * Sets the logging instance + * + * @param loh_h_ logging instance pointer + */ + void set_logger(srslte::log* log_h_) { log_h = log_h_; } + + /** + * Sets the next base time in seconds, discarding previous changes. + * + * @param ta_base_sec Time Alignment value in seconds + */ + void set_base_sec(float ta_base_sec) + { + std::lock_guard lock(mutex); + + // Forces next base + next_base_sec = ta_base_sec; + + // Update base in nta + next_base_nta = static_cast(roundf(next_base_sec / SRSLTE_LTE_TS)); + + // Resets pending samples + pending_nsamples = 0; + + // Log information information if available + if (log_h) { + log_h->info("PHY: Set TA base: n_ta: %d, ta_usec: %.1f\n", next_base_nta, next_base_sec * 1e6f); + } + } + + /** + * Increments (delta) the next base time. The value in seconds will be added to the next base. + * + * @param ta_delta_sec Time Alignment increment value in seconds + */ + void add_delta_sec(float ta_delta_sec) + { + std::lock_guard lock(mutex); + + // Increments the next base + next_base_sec += ta_delta_sec; + + // Update base in nta + next_base_nta = static_cast(roundf(next_base_sec / SRSLTE_LTE_TS)); + + // Log information information if available + if (log_h) { + log_h->info("PHY: Set TA: ta_delta_usec: %.1f, n_ta: %d, ta_usec: %.1f\n", + ta_delta_sec * 1e6f, + next_base_nta, + next_base_sec * 1e6f); + } + } + + /** + * Increments (delta) the next base time according to time alignment command from a Random Access Response (RAR). + * + * @param ta_cmd Time Alignment command + */ + void add_ta_cmd_rar(uint32_t ta_cmd) + { + std::lock_guard lock(mutex); + + // Update base nta + next_base_nta += srslte_N_ta_new_rar(ta_cmd); + + // Update base in seconds + next_base_sec = static_cast(next_base_nta) * SRSLTE_LTE_TS; + + // Log information information if available + if (log_h) { + log_h->info( + "PHY: Set TA RAR: ta_cmd: %d, n_ta: %d, ta_usec: %.1f\n", ta_cmd, next_base_nta, next_base_sec * 1e6f); + } + } + + /** + * Increments (delta) the next base time according to time alignment command from a MAC Control Element. + * + * @param ta_cmd Time Alignment command + */ + void add_ta_cmd_new(uint32_t ta_cmd) + { + std::lock_guard lock(mutex); + + // Update base nta + next_base_nta = srslte_N_ta_new(next_base_nta, ta_cmd); + + // Update base in seconds + next_base_sec = static_cast(next_base_nta) * SRSLTE_LTE_TS; + + // Log information information if available + if (log_h) { + log_h->info("PHY: Set TA: ta_cmd: %d, n_ta: %d, ta_usec: %.1f\n", ta_cmd, next_base_nta, next_base_sec * 1e6f); + } + } + + /** + * Increments/Decrements the number pending samples for next transmission + * + * @param nsamples is the number of samples + */ + void set_pending_nsamples(int32_t nsamples) + { + std::lock_guard lock(mutex); + + // Increment number of pending samples + pending_nsamples += nsamples; + } + + /** + * Get the previous time alignment in seconds + * + * @return Time alignment in seconds + */ + float get_previous_sec() const + { + std::lock_guard lock(mutex); + + // Returns the previous base + return prev_base_sec; + } + + /** + * Get the current time alignment in seconds + * + * @return Time alignment in seconds + */ + float get_current_sec() const + { + std::lock_guard lock(mutex); + + // Returns the current base + return current_base_sec; + } + + /** + * Get the current time alignment in microseconds + * + * @return Time alignment in microseconds + */ + float get_current_usec() const + { + std::lock_guard lock(mutex); + + // Returns the current base + return current_base_sec * 1e6f; + } + + /** + * Get the current time alignment in kilometers between the eNb and the UE + * + * @return Distance based on the current time base + */ + float get_current_km() const + { + std::lock_guard lock(mutex); + + // Returns the current base, one direction distance + return current_base_sec * (3.6f * 3e8f / 2.0f); + } + + /** + * Get the total number of pending samples to transmit, considering the time difference between the next base and the + * current base. + * + * Attention: this method changes the internal state of the class. + * + * @return the total number of pending samples considering the sampling rate + */ + int32_t get_pending_nsamples(uint32_t sampling_rate_hz) + { + std::lock_guard lock(mutex); + + // Convert sampling rate to float + float srate = static_cast(sampling_rate_hz); + + // Calculate number of samples + int32_t nsamples = static_cast(roundf(srate * (current_base_sec - next_base_sec))); + + // Update current base + prev_base_sec = current_base_sec; + current_base_sec = next_base_sec; + + // Reset pending samples + pending_nsamples = 0; + + // Return number of samples + return nsamples; + } +}; + +} // namespace srsue + +#endif // SRSUE_TA_CONTROL_H diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index b92705385..02d61c1d4 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -257,22 +257,12 @@ void phy::get_metrics(phy_metrics_t* m) void phy::set_timeadv_rar(uint32_t ta_cmd) { - n_ta = srslte_N_ta_new_rar(ta_cmd); - n_ta += n_ta_base; - sfsync.set_time_adv_sec(((float)n_ta) * SRSLTE_LTE_TS); - Info("PHY: Set TA RAR: ta_cmd: %d, n_ta: %d, ta_usec: %.1f\n", ta_cmd, n_ta, ((float)n_ta) * SRSLTE_LTE_TS * 1e6); + common.ta.add_ta_cmd_rar(ta_cmd); } void phy::set_timeadv(uint32_t ta_cmd) { - uint32_t new_nta = srslte_N_ta_new(n_ta, ta_cmd); - sfsync.set_time_adv_sec(((float)new_nta) * SRSLTE_LTE_TS); - Info("PHY: Set TA: ta_cmd: %d, n_ta: %d, old_n_ta: %d, ta_usec: %.1f\n", - ta_cmd, - new_nta, - n_ta, - ((float)new_nta) * SRSLTE_LTE_TS * 1e6); - n_ta = new_nta; + common.ta.add_ta_cmd_new(ta_cmd); } void phy::set_activation_deactivation_scell(uint32_t cmd) @@ -341,9 +331,7 @@ float phy::get_pathloss_db() void phy::prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm, float ta_base_sec) { - n_ta = 0; /* Reset Time aligment */ - n_ta_base = (uint32_t)roundf(ta_base_sec / SRSLTE_LTE_TS); - sfsync.set_time_adv_sec(ta_base_sec); + common.ta.set_base_sec(ta_base_sec); common.reset_radio(); if (!prach_buffer.prepare_to_send(preamble_idx, allowed_subframe, target_power_dbm)) { Error("Preparing PRACH to send\n"); @@ -370,9 +358,7 @@ void phy::radio_failure() void phy::reset() { Info("Resetting PHY\n"); - n_ta = 0; - n_ta_base = 0; - sfsync.set_time_adv_sec(0); + common.ta.set_base_sec(0); for (uint32_t i = 0; i < nof_workers; i++) { workers[i]->reset(); } diff --git a/srsue/src/phy/phy_common.cc b/srsue/src/phy/phy_common.cc index 3add90cf3..cdf116cfd 100644 --- a/srsue/src/phy/phy_common.cc +++ b/srsue/src/phy/phy_common.cc @@ -68,6 +68,8 @@ void phy_common::init(phy_args_t* _args, args = _args; sr_last_tx_tti = -1; + ta.set_logger(_log); + // Instantiate UL channel emulator if (args->ul_channel_args.enable) { ul_channel = srslte::channel_ptr(new srslte::channel(args->ul_channel_args, args->nof_carriers * args->nof_rx_ant)); diff --git a/srsue/src/phy/sf_worker.cc b/srsue/src/phy/sf_worker.cc index d50479464..1d17dfafd 100644 --- a/srsue/src/phy/sf_worker.cc +++ b/srsue/src/phy/sf_worker.cc @@ -138,9 +138,8 @@ void sf_worker::set_tti(uint32_t tti_) } } -void sf_worker::set_tx_time(srslte_timestamp_t tx_time_, int next_offset_) +void sf_worker::set_tx_time(srslte_timestamp_t tx_time_) { - next_offset = next_offset_; tx_time = tx_time_; } @@ -207,11 +206,13 @@ void sf_worker::work_imp() bool rx_signal_ok = false; bool tx_signal_ready = false; uint32_t nof_samples = SRSLTE_SF_LEN_PRB(cell.nof_prb); + int32_t next_offset = 0; - /***** Downlink Processing *******/ { std::lock_guard lock(mutex); + /***** Downlink Processing *******/ + // Loop through all carriers. carrier_idx=0 is PCell for (uint32_t carrier_idx = 0; carrier_idx < cc_workers.size(); carrier_idx++) { @@ -230,6 +231,9 @@ void sf_worker::work_imp() } } + // Load TA + next_offset = phy->ta.get_pending_nsamples(nof_samples * 1000U); + /***** Uplink Generation + Transmission *******/ /* If TTI+4 is an uplink subframe (TODO: Support short PRACH and SRS in UpPts special subframes) */ @@ -259,10 +263,12 @@ void sf_worker::work_imp() // Set PRACH buffer signal pointer if (prach_ptr) { + srslte_timestamp_sub(&tx_time, 0, phy->ta.get_current_sec()); tx_signal_ready = true; tx_signal_ptr.set(0, prach_ptr); prach_ptr = nullptr; } else { + srslte_timestamp_sub(&tx_time, 0, phy->ta.get_previous_sec()); nof_samples += next_offset; } diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index 85f01ee91..91bdc6ec5 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -136,9 +136,6 @@ void sync::reset() { in_sync_cnt = 0; out_of_sync_cnt = 0; - time_adv_sec = 0; - next_offset = 0; - next_radio_offset = 0; current_earfcn = -1; srate_mode = SRATE_NONE; sfn_p.reset(); @@ -476,7 +473,7 @@ void sync::run_thread() metrics.sfo = srslte_ue_sync_get_sfo(&ue_sync); metrics.cfo = srslte_ue_sync_get_cfo(&ue_sync); - metrics.ta_us = time_adv_sec * 1e6f; + metrics.ta_us = worker_com->ta.get_current_usec(); for (uint32_t i = 0; i < worker_com->args->nof_carriers; i++) { worker_com->set_sync_metrics(i, metrics); } @@ -493,9 +490,9 @@ void sync::run_thread() srslte_timestamp_t rx_time, tx_time; srslte_ue_sync_get_last_timestamp(&ue_sync, &rx_time); srslte_timestamp_copy(&tx_time, &rx_time); - srslte_timestamp_add(&tx_time, 0, FDD_HARQ_DELAY_DL_MS * 1e-3 - time_adv_sec); + srslte_timestamp_add(&tx_time, 0, FDD_HARQ_DELAY_DL_MS * 1e-3); - worker->set_prach(prach_ptr ? &prach_ptr[prach_sf_cnt * SRSLTE_SF_LEN_PRB(cell.nof_prb)] : NULL, + worker->set_prach(prach_ptr ? &prach_ptr[prach_sf_cnt * SRSLTE_SF_LEN_PRB(cell.nof_prb)] : nullptr, prach_power); // Set CFO for all Carriers @@ -505,21 +502,14 @@ void sync::run_thread() } worker->set_tti(tti); - worker->set_tx_time(tx_time, next_radio_offset + next_offset); - next_offset = 0; - ZERO_OBJECT(next_radio_offset); - - // Process time aligment command - if (next_time_adv_sec != time_adv_sec) { - time_adv_sec = next_time_adv_sec; - } + worker->set_tx_time(tx_time); // Advance/reset prach subframe pointer if (prach_ptr) { prach_sf_cnt++; if (prach_sf_cnt == prach_nof_sf) { prach_sf_cnt = 0; - prach_ptr = NULL; + prach_ptr = nullptr; } } @@ -643,14 +633,6 @@ void sync::set_agc_enable(bool enable) } } -void sync::set_time_adv_sec(float time_adv_sec_) -{ - // If transmitting earlier, transmit less samples to align time advance. If transmit later just delay next TX - next_offset = (int)round((time_adv_sec - time_adv_sec_) * srslte_sampling_freq_hz(cell.nof_prb)); - next_time_adv_sec = time_adv_sec_; - Info("Applying time_adv_sec=%.1f us, next_offset=%d\n", time_adv_sec_ * 1e6, next_offset); -} - float sync::get_tx_cfo() { float cfo = srslte_ue_sync_get_cfo(&ue_sync); @@ -882,9 +864,9 @@ int sync::radio_recv_fnc(srslte::rf_buffer_t& data, uint32_t nsamples, srslte_ti int offset = nsamples - current_sflen; if (abs(offset) < 10 && offset != 0) { - next_radio_offset = offset; + worker_com->ta.set_pending_nsamples(offset); } else if (nsamples < 10) { - next_radio_offset = nsamples; + worker_com->ta.set_pending_nsamples(nsamples); } log_h->debug("SYNC: received %d samples from radio\n", nsamples);