diff --git a/lib/include/srslte/interfaces/radio_interfaces.h b/lib/include/srslte/interfaces/radio_interfaces.h index 83660ed49..aabdc5509 100644 --- a/lib/include/srslte/interfaces/radio_interfaces.h +++ b/lib/include/srslte/interfaces/radio_interfaces.h @@ -186,6 +186,14 @@ public: */ virtual void set_rx_srate(const double& srate) = 0; + /** + * Sets relative offset between receiver channels. It does not guarantee that the offset is corrected by the radio + * implementation. + * @param ch logical channel index + * @param offset_samples Offset in samples, it can be negative + */ + virtual void set_channel_rx_offset(uint32_t ch, int32_t offset_samples) = 0; + // getter virtual double get_freq_offset() = 0; virtual float get_rx_gain() = 0; diff --git a/lib/include/srslte/radio/radio.h b/lib/include/srslte/radio/radio.h index 3bd149d15..424995727 100644 --- a/lib/include/srslte/radio/radio.h +++ b/lib/include/srslte/radio/radio.h @@ -73,6 +73,7 @@ public: void set_rx_gain(const float& gain) override; void set_tx_srate(const double& srate) override; void set_rx_srate(const double& srate) override; + void set_channel_rx_offset(uint32_t ch, int32_t offset_samples) override; // getter double get_freq_offset() override; @@ -90,14 +91,15 @@ public: static void rf_msg_callback(void* arg, srslte_rf_error_t error); private: - std::vector rf_devices = {}; - std::vector rf_info = {}; - rf_metrics_t rf_metrics = {}; - log_filter log_local = {}; - log_filter* log_h = nullptr; - srslte::logger* logger = nullptr; - phy_interface_radio* phy = nullptr; - cf_t* zeros = nullptr; + std::vector rf_devices = {}; + std::vector rf_info = {}; + std::vector rx_offset_n = {}; + rf_metrics_t rf_metrics = {}; + log_filter log_local = {}; + log_filter* log_h = nullptr; + srslte::logger* logger = nullptr; + phy_interface_radio* phy = nullptr; + cf_t* zeros = nullptr; rf_timestamp_t end_of_burst_time = {}; bool is_start_of_burst = false; @@ -163,9 +165,7 @@ private: * @param tx_time_ Timestamp to transmit (read only) * @return it returns true if the transmission was successful, otherwise it returns false */ - bool tx_dev(const uint32_t& device_idx, - rf_buffer_interface& buffer, - const srslte_timestamp_t& tx_time_); + bool tx_dev(const uint32_t& device_idx, rf_buffer_interface& buffer, const srslte_timestamp_t& tx_time_); /** * Helper method for receiving over a single RF device. This function maps automatically the logical receive buffers @@ -176,9 +176,7 @@ private: * @param rxd_time Points at the receive time (write only) * @return it returns true if the reception was successful, otherwise it returns false */ - bool rx_dev(const uint32_t& device_idx, - const rf_buffer_interface& buffer, - srslte_timestamp_t* rxd_time); + bool rx_dev(const uint32_t& device_idx, const rf_buffer_interface& buffer, srslte_timestamp_t* rxd_time); /** * Helper method for mapping logical channels into physical radio buffers. diff --git a/lib/src/radio/radio.cc b/lib/src/radio/radio.cc index da6ed0d84..acbdc32ac 100644 --- a/lib/src/radio/radio.cc +++ b/lib/src/radio/radio.cc @@ -108,6 +108,7 @@ int radio::init(const rf_args_t& args, phy_interface_radio* phy_) // Allocate RF devices rf_devices.resize(device_args_list.size()); rf_info.resize(device_args_list.size()); + rx_offset_n.resize(device_args_list.size()); // Init and start Radios for (uint32_t device_idx = 0; device_idx < (uint32_t)device_args_list.size(); device_idx++) { @@ -247,8 +248,39 @@ bool radio::rx_dev(const uint32_t& device_idx, return false; } - int ret = srslte_rf_recv_with_time_multi( - &rf_devices[device_idx], radio_buffers, buffer.get_nof_samples(), true, full_secs, frac_secs); + // Apply Rx offset into the number of samples and reset value + int nof_samples_offset = rx_offset_n.at(device_idx); + uint32_t nof_samples = buffer.get_nof_samples(); + + // Number of samples adjust from device time offset + if (nof_samples_offset < 0 and (uint32_t)(-nof_samples_offset) > nof_samples) { + // Avoid overflow subtraction + nof_samples = 0; + } else { + // Limit the number of samples to a maximum of 2 times the requested number of samples + nof_samples = SRSLTE_MIN(nof_samples + nof_samples_offset, 2 * nof_samples); + } + + // Subtract number of offset samples + rx_offset_n.at(device_idx) = nof_samples_offset - ((int)nof_samples - (int)buffer.get_nof_samples()); + + int ret = + srslte_rf_recv_with_time_multi(&rf_devices[device_idx], radio_buffers, nof_samples, true, full_secs, frac_secs); + + // If the number of received samples filled the buffer, there is nothing else to do + if (buffer.get_nof_samples() <= nof_samples) { + return ret > 0; + } + + // Otherwise, set rest of buffer to zero + uint32_t nof_zeros = buffer.get_nof_samples() - nof_samples; + for (auto& b : radio_buffers) { + if (b != nullptr) { + cf_t* ptr = (cf_t*)b; + srslte_vec_cf_zero(&ptr[nof_samples], nof_zeros); + } + } + return ret > 0; } @@ -491,6 +523,26 @@ void radio::set_rx_srate(const double& srate) } } +void radio::set_channel_rx_offset(uint32_t ch, int32_t offset_samples) +{ + int physical_channel_idx = rx_channel_mapping.get_carrier_idx(ch); + + // Return if invalid index + if (physical_channel_idx < SRSLTE_SUCCESS) { + return; + } + + // Calculate device index + uint32_t device_idx = (nof_antennas * (uint32_t)physical_channel_idx) / nof_channels_x_dev; + + // Bound device index + if (device_idx >= rx_offset_n.size()) { + return; + } + + rx_offset_n[ch] = offset_samples; +} + void radio::set_tx_freq(const uint32_t& carrier_idx, const double& freq) { if (!is_initialized) { diff --git a/srsenb/test/phy/enb_phy_test.cc b/srsenb/test/phy/enb_phy_test.cc index 91591f766..e5b8d22ba 100644 --- a/srsenb/test/phy/enb_phy_test.cc +++ b/srsenb/test/phy/enb_phy_test.cc @@ -247,6 +247,7 @@ public: void set_rx_gain(const float& gain) override {} void set_tx_srate(const double& srate) override {} void set_rx_srate(const double& srate) override { rx_srate = srate; } + void set_channel_rx_offset(uint32_t ch, int32_t offset_samples) override{}; void set_tx_gain(const float& gain) override {} float get_rx_gain() override { return 0; } double get_freq_offset() override { return 0; } @@ -266,16 +267,16 @@ private: static constexpr float prob_ul_grant = 0.10f; static constexpr uint32_t cfi = 2; - srsenb::phy_cell_cfg_list_t phy_cell_cfg; - srsenb::phy_interface_rrc_lte::phy_rrc_cfg_list_t phy_rrc; - std::mutex mutex; - std::condition_variable cvar; - srslte::log_filter log_h; - srslte_softbuffer_tx_t softbuffer_tx = {}; - srslte_softbuffer_rx_t softbuffer_rx[SRSLTE_MAX_CARRIERS][SRSLTE_FDD_NOF_HARQ] = {}; - uint8_t* data = nullptr; - uint16_t ue_rnti = 0; - srslte_random_t random_gen = nullptr; + srsenb::phy_cell_cfg_list_t phy_cell_cfg; + srsenb::phy_interface_rrc_lte::phy_rrc_cfg_list_t phy_rrc; + std::mutex mutex; + std::condition_variable cvar; + srslte::log_filter log_h; + srslte_softbuffer_tx_t softbuffer_tx = {}; + srslte_softbuffer_rx_t softbuffer_rx[SRSLTE_MAX_CARRIERS][SRSLTE_FDD_NOF_HARQ] = {}; + uint8_t* data = nullptr; + uint16_t ue_rnti = 0; + srslte_random_t random_gen = nullptr; CALLBACK(sr_detected); CALLBACK(rach_detected); @@ -754,20 +755,20 @@ typedef std::unique_ptr unique_dummy_stack_t; class dummy_ue { private: - std::vector ue_dl_v = {}; - std::vector ue_ul_v = {}; - std::vector buffers = {}; - dummy_radio* radio = nullptr; - uint32_t sf_len = 0; - uint32_t nof_ports = 0; - uint16_t rnti = 0; - srslte_dl_sf_cfg_t sf_dl_cfg = {}; - srslte_ul_sf_cfg_t sf_ul_cfg = {}; - srslte_softbuffer_tx_t softbuffer_tx = {}; - uint8_t* tx_data = nullptr; - srsenb::phy_interface_rrc_lte::phy_rrc_cfg_list_t phy_rrc_cfg = {}; - srslte::log_filter log_h; - std::map last_ri = {}; + std::vector ue_dl_v = {}; + std::vector ue_ul_v = {}; + std::vector buffers = {}; + dummy_radio* radio = nullptr; + uint32_t sf_len = 0; + uint32_t nof_ports = 0; + uint16_t rnti = 0; + srslte_dl_sf_cfg_t sf_dl_cfg = {}; + srslte_ul_sf_cfg_t sf_ul_cfg = {}; + srslte_softbuffer_tx_t softbuffer_tx = {}; + uint8_t* tx_data = nullptr; + srsenb::phy_interface_rrc_lte::phy_rrc_cfg_list_t phy_rrc_cfg = {}; + srslte::log_filter log_h; + std::map last_ri = {}; public: dummy_ue(dummy_radio* _radio, const srsenb::phy_cell_cfg_list_t& cell_list, std::string log_level, uint16_t rnti_) : @@ -1147,10 +1148,10 @@ private: srslte::log_filter log_h; srslte::logger_stdout logger_stdout; - args_t args = {}; ///< Test arguments - srsenb::phy_args_t phy_args; ///< PHY arguments - srsenb::phy_cfg_t phy_cfg; ///< eNb Cell/Carrier configuration - srsenb::phy_interface_rrc_lte::phy_rrc_cfg_list_t phy_rrc_cfg; ///< UE PHY configuration + args_t args = {}; ///< Test arguments + srsenb::phy_args_t phy_args; ///< PHY arguments + srsenb::phy_cfg_t phy_cfg; ///< eNb Cell/Carrier configuration + srsenb::phy_interface_rrc_lte::phy_rrc_cfg_list_t phy_rrc_cfg; ///< UE PHY configuration uint64_t tti_counter = 0; typedef enum { diff --git a/srsue/hdr/phy/scell/scell_sync.h b/srsue/hdr/phy/scell/scell_sync.h new file mode 100644 index 000000000..cb1473f70 --- /dev/null +++ b/srsue/hdr/phy/scell/scell_sync.h @@ -0,0 +1,192 @@ +/* + * 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 SRSLTE_SCELL_SYNC_H +#define SRSLTE_SCELL_SYNC_H + +namespace srsue { +namespace scell { + +/** + * Radio feedback interface + */ +class sync_callback +{ +public: + /** + * Provides secondary serving cell synchronization feedback + * @param ch channel index + * @param offset Number of samples to offset + */ + virtual void set_rx_channel_offset(uint32_t ch, int32_t offset) = 0; +}; + +class sync +{ +private: + /** + * FSM + * + * Init +------+ Set cell +------------+ PSS found +----------+ + * or -->| IDLE |---------->| Search PSS |---------->| In-Synch | + * Stop +------+ +------------+ +----------+ + * ^ Set Cell | + * | | + * +----------------------+ + */ + typedef enum { STATE_IDLE = 0, STATE_SEARCH_PSS, STATE_IN_SYNCH } state_t; + + state_t state = STATE_IDLE; + sync_callback* callback = nullptr; + uint32_t channel = 0; + srslte_sync_t find_pss = {}; + int32_t sf_len = 0; + std::array temp; + + /** + * Executes the PSS search state + * @param tti + * @param buffer + */ + void run_state_search_pss(uint32_t tti, cf_t* buffer) + { + uint32_t peak_pos = 0; + + // Append new base-band + srslte_vec_cf_copy(&temp[sf_len], buffer, sf_len); + + // Run PSS search + switch (srslte_sync_find(&find_pss, temp.data(), 0, &peak_pos)) { + + case SRSLTE_SYNC_FOUND: + if (callback != nullptr) { + // Calculate Sample Offset from TTI difference + int tti_mod = (int)tti % (SRSLTE_NOF_SF_X_FRAME / 2); + int tti_offset = (tti_mod < 3) ? tti_mod : (tti_mod - SRSLTE_NOF_SF_X_FRAME / 2); + + // Calculate sample offset from PSS correlation peak + int offset = (int)(peak_pos - (3 * sf_len) / 2); + + // Provide offset through feedback interface + callback->set_rx_channel_offset(channel, offset + tti_offset * sf_len); + } + state = STATE_IN_SYNCH; + break; + case SRSLTE_SYNC_FOUND_NOSPACE: + ERROR("No space error\n"); + break; + case SRSLTE_SYNC_NOFOUND: + // Ignore + break; + case SRSLTE_SYNC_ERROR: + ERROR("Error finding PSS\n"); + break; + } + + // If the state has not changed, copy new data into the temp buffer + if (state == STATE_SEARCH_PSS) { + srslte_vec_cf_copy(&temp[0], buffer, sf_len); + } + } + +public: + /** + * Constructor + * @param _callback provides the class for giving feedback + * @param _channel provides the channel index where the feedback needs to be applied + */ + sync(sync_callback* _callback, uint32_t _channel) : callback(_callback), channel(_channel) + { + // Initialise Find PSS object + if (srslte_sync_init(&find_pss, SRSLTE_SF_LEN_MAX, SRSLTE_SF_LEN_MAX, SRSLTE_SYMBOL_SZ_MAX) != SRSLTE_SUCCESS) { + ERROR("Initiating Synchroniser\n"); + } + } + + /** + * Sets the cell for the synchronizer + */ + void set_cell(const srslte_cell_t& cell) + { + uint32_t symbol_sz = srslte_symbol_sz(cell.nof_prb); + sf_len = SRSLTE_SF_LEN_PRB(cell.nof_prb); + + // Resize Sync object + if (srslte_sync_resize(&find_pss, 2 * sf_len, 2 * sf_len, symbol_sz) != SRSLTE_SUCCESS) { + ERROR("Error setting cell sync find\n"); + } + + // Configure + srslte_sync_set_frame_type(&find_pss, cell.frame_type); + srslte_sync_set_N_id_2(&find_pss, cell.id % SRSLTE_NOF_NID_2); + srslte_sync_set_N_id_1(&find_pss, cell.id / SRSLTE_NOF_NID_2); + srslte_sync_set_cfo_ema_alpha(&find_pss, 0.1); + srslte_sync_set_em_alpha(&find_pss, 1); + srslte_sync_set_threshold(&find_pss, 3.0); + + // Reset Temporal buffer + srslte_vec_cf_zero(temp.data(), 2 * sf_len); + + // Go to search PSS + state = STATE_SEARCH_PSS; + } + + /** + * Resets the class, goes back into IDLE mode + */ + void stop() { state = STATE_IDLE; } + + /** + * Runs internal FSM, performing Synchronization operations on the provided buffer. It expects data per sub-frame + * basis (1 ms). + * @param tti Current primary serving cell time + * @param buffer Base-band buffer of the given secondary serving cell + */ + void run(uint32_t tti, cf_t* buffer) + { + switch (state) { + case STATE_IDLE: + // Do nothing + break; + case STATE_SEARCH_PSS: + run_state_search_pss(tti, buffer); + break; + case STATE_IN_SYNCH: + // Do nothing + break; + } + } + + /** + * Get channel index + * @return The channel number it has been configured + */ + uint32_t get_channel() const { return channel; } + + /** + * Indicates whether the secondary serving cell assigned to the instance is in-sync + * @return true if it is in sync state + */ + bool is_in_sync() const { return state == STATE_IN_SYNCH; } +}; +} // namespace scell +} // namespace srsue +#endif // SRSLTE_SCELL_SYNC_H diff --git a/srsue/hdr/phy/sync.h b/srsue/hdr/phy/sync.h index d7bf8d6d9..6ed0e2ffa 100644 --- a/srsue/hdr/phy/sync.h +++ b/srsue/hdr/phy/sync.h @@ -30,6 +30,7 @@ #include "phy_common.h" #include "prach.h" #include "scell/intra_measure.h" +#include "scell/scell_sync.h" #include "search.h" #include "sf_worker.h" #include "sfn_sync.h" @@ -47,7 +48,7 @@ namespace srsue { typedef _Complex float cf_t; -class sync : public srslte::thread, public chest_feedback_itf, public search_callback +class sync : public srslte::thread, public chest_feedback_itf, public search_callback, public scell::sync_callback { public: sync() : thread("SYNC"), sf_buffer(sync_nof_rx_subframes), dummy_buffer(sync_nof_rx_subframes){}; @@ -93,6 +94,25 @@ public: srslte::radio_interface_phy* get_radio() override { return radio_h; } + /** + * Sets secondary serving cell for synchronization purposes + * @param cc_idx component carrier index + * @param _cell Cell information + */ + void scell_sync_set(uint32_t cc_idx, const srslte_cell_t& _cell); + + /** + * Stops all secondary serving cell synchronization + */ + void scell_sync_stop(); + + /** + * Implements Secondary Serving cell feedback + * @param ch Feedback channel + * @param offset Number of samples to offset + */ + void set_rx_channel_offset(uint32_t ch, int32_t offset) override { radio_h->set_channel_rx_offset(ch, offset); } + private: void reset(); void radio_error(); @@ -136,7 +156,7 @@ private: * - IDLE: Receives and discards received samples. Does not maintain synchronization. * */ - void run_thread() final; + void run_thread() final; /** * Helper method, executed when the UE is camping and in-sync @@ -154,7 +174,7 @@ private: bool is_overflow = false; srslte::rf_timestamp_t last_rx_time; - bool forced_rx_time_init = true; // Rx time sync after first receive from radio + bool forced_rx_time_init = true; // Rx time sync after first receive from radio // Objects for internal use search search_p; @@ -180,6 +200,9 @@ private: // Object for synchronization of the primary cell srslte_ue_sync_t ue_sync = {}; + // Object for synchronization secondary serving cells + std::vector > scell_sync; + // Buffer for primary and secondary cell samples const static uint32_t sync_nof_rx_subframes = 5; srslte::rf_buffer_t sf_buffer = {}; diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index 7206af63e..f5e26b85e 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -303,11 +303,13 @@ void phy::meas_stop() bool phy::cell_select(const phy_cell_t* cell) { + sfsync.scell_sync_stop(); return sfsync.cell_select(cell); } phy_interface_rrc_lte::cell_search_ret_t phy::cell_search(phy_cell_t* cell) { + sfsync.scell_sync_stop(); return sfsync.cell_search(cell); } @@ -440,9 +442,12 @@ void phy::set_config(srslte::phy_cfg_t& config_, uint32_t cc_idx, uint32_t earfc workers[i]->set_config(cc_idx, config_); } - // Set inter-frequency measurement primary cell if (cell_info) { + // Set inter-frequency measurement sfsync.set_inter_frequency_measurement(cc_idx, earfcn, *cell_info); + + // Set secondary serving cell synchronization + sfsync.scell_sync_set(cc_idx, *cell_info); } if (cc_idx == 0) { diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index dff6e699c..66adf1484 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -104,6 +104,12 @@ void sync::init(srslte::radio_interface_phy* _radio, intra_freq_meas.push_back(std::unique_ptr(q)); } + // Allocate Secondary serving cell synchronization + for (uint32_t i = 1; i < worker_com->args->nof_carriers; i++) { + // Give the logical channel + scell_sync.push_back(std::unique_ptr(new scell::sync(this, i * worker_com->args->nof_rx_ant))); + } + reset(); running = true; @@ -380,6 +386,11 @@ void sync::run_camping_in_sync_state(sf_worker* worker, srslte::rf_buffer_t& syn force_camping_sfn_sync = true; } + // Run secondary serving cell synchronization + for (auto& e : scell_sync) { + e->run(tti, sync_buffer.get(e->get_channel(), 0, worker_com->args->nof_rx_ant)); + } + if (is_overflow) { force_camping_sfn_sync = true; is_overflow = false; @@ -927,4 +938,30 @@ void sync::meas_stop() } } +void sync::scell_sync_set(uint32_t cc_idx, const srslte_cell_t& _cell) +{ + // Ignore if PCell + if (cc_idx == 0) { + return; + } + + // Decrement to match SCell index + cc_idx--; + + // Ignore if out of range + if (cc_idx >= scell_sync.size()) { + return; + } + + // Set secondary serving cell + scell_sync[cc_idx]->set_cell(_cell); +} + +void sync::scell_sync_stop() +{ + for (auto& e : scell_sync) { + e->stop(); + } +} + } // namespace srsue diff --git a/srsue/test/phy/ue_phy_test.cc b/srsue/test/phy/ue_phy_test.cc index 021580009..5e1d33fd2 100644 --- a/srsue/test/phy/ue_phy_test.cc +++ b/srsue/test/phy/ue_phy_test.cc @@ -305,6 +305,7 @@ private: rx_srate = (float)srate; log_h.info("Set Rx sampling rate to %+.3f MHz.\n", srate * 1.0e-6); } + void set_channel_rx_offset(uint32_t ch, int32_t offset_samples) override{}; float get_rx_gain() override { std::unique_lock lock(mutex);