diff --git a/lib/include/srslte/interfaces/radio_interfaces.h b/lib/include/srslte/interfaces/radio_interfaces.h index 447a0fee0..71b375b34 100644 --- a/lib/include/srslte/interfaces/radio_interfaces.h +++ b/lib/include/srslte/interfaces/radio_interfaces.h @@ -58,6 +58,35 @@ public: virtual uint32_t size() = 0; }; +/** + * Class used to pass RF devices timestamps to the radio instance + * + * The class provides an abstraction to store a number of timestamps underlying RF devices. + */ +class rf_timestamp_interface +{ +public: + virtual const srslte_timestamp_t& get(uint32_t idx) const = 0; + virtual srslte_timestamp_t* get_ptr(uint32_t idx) = 0; + virtual void add(double secs) = 0; + virtual void sub(double secs) = 0; + + void copy(const rf_timestamp_interface& other) + { + // Nothing to copy + if (this == &other) { + return; + } + + // Copy timestamps + for (uint32_t i = 0; i < SRSLTE_MAX_CHANNELS; i++) { + *this->get_ptr(i) = other.get(i); + } + } + + srslte_timestamp_t& operator[](uint32_t idx) { return *this->get_ptr(idx); } +}; + /** * Radio interface for the PHY. * @@ -94,7 +123,7 @@ public: * @param tx_time Time to transmit all signals * @return it returns true if the transmission was successful, otherwise it returns false */ - virtual bool tx(rf_buffer_interface& buffer, const uint32_t& nof_samples, const srslte_timestamp_t& tx_time) = 0; + virtual bool tx(rf_buffer_interface& buffer, const uint32_t& nof_samples, const rf_timestamp_interface& tx_time) = 0; /** * Indicates the radio to receive from all antennas and carriers synchronously and store the samples @@ -105,7 +134,7 @@ public: * @param tx_time Time at which the samples were received. Note the time is the same for all carriers * @return */ - virtual bool rx_now(rf_buffer_interface& buffer, const uint32_t& nof_samples, srslte_timestamp_t* rxd_time) = 0; + virtual bool rx_now(rf_buffer_interface& buffer, const uint32_t& nof_samples, rf_timestamp_interface& rxd_time) = 0; /** * Sets the TX frequency for all antennas in the provided carrier index diff --git a/lib/include/srslte/phy/rf/rf.h b/lib/include/srslte/phy/rf/rf.h index 4ef9c76dd..9aa7acaa3 100644 --- a/lib/include/srslte/phy/rf/rf.h +++ b/lib/include/srslte/phy/rf/rf.h @@ -29,6 +29,10 @@ #include "srslte/config.h" +#ifdef __cplusplus +extern "C" { +#endif + #define RF_PARAM_LEN (256) typedef struct { @@ -164,4 +168,8 @@ SRSLTE_API int srslte_rf_send_multi(srslte_rf_t* rf, bool is_start_of_burst, bool is_end_of_burst); +#ifdef __cplusplus +} +#endif + #endif // SRSLTE_RF_H diff --git a/lib/include/srslte/radio/channel_mapping.h b/lib/include/srslte/radio/channel_mapping.h new file mode 100644 index 000000000..a59d727a6 --- /dev/null +++ b/lib/include/srslte/radio/channel_mapping.h @@ -0,0 +1,133 @@ +/* + * 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_CHANNEL_MAPPING_H +#define SRSLTE_CHANNEL_MAPPING_H + +#include +#include +#include +#include + +namespace srslte { + +/** + * This class manages the mapping between logical and physical channels. + * A physical channel in this class is a carrier index in the radio class, which + * has multiple antenna ports all tuned to the same frequency. + * + * Every group of channels tuned associated with a carrier go through the same band-pass filter. This + * class then manages the allocation of frequencies to these group of channels. + * + * The same object is reused for the reception and transmission. + * + * When the UE wants to tune a logical channel to a new frequency it requests this class + * to provide an available channel that supports this frequency. At that point, + * that channel can not be used anymore until a call to release(). + * + */ +class channel_mapping +{ +public: + /** Configures a band. A band is defined by an upper and lower frequency boundaries. + * If the upper and lower frequencies are not configured (default is zero), it means + * that they support any frequency + */ + class band_cfg + { + public: + void set(float low_freq_, float high_freq_) + { + low_freq = low_freq_; + high_freq = high_freq_; + } + bool contains(float freq) + { + if (low_freq == 0 && high_freq == 0) { + return true; + } else { + return freq >= low_freq && freq <= high_freq; + } + } + float get_low() { return low_freq; } + float get_high() { return high_freq; } + + private: + float low_freq = 0; + float high_freq = 0; + }; + + /** Each channel is defined by the band it supports and the physical carrier index in the radio + */ + typedef struct { + band_cfg band; + uint32_t carrier_idx; + } channel_cfg_t; + + /** + * Sets the channel configuration. If no channels are configured no physical channels can be allocated + * @param channels_ + */ + void set_channels(const std::list& channels_) { available_channels = channels_; } + + /** + * Finds an unused physical channel that supports the provided frequency and assigns it to logical channel + * logical_ch + * @param logical_ch logical channel index + * @param freq Frequency (in Hz) that we want to receive/transmitt + * @return true if a physical channel supporting this frequency was found or false otherwise + */ + bool allocate_freq(const uint32_t& logical_ch, const float& freq); + + /** + * Releases the allocation of a logical channel allowing to be used in the next call to allocate_freq + * @param logical_ch logical channel index + * @return false if logical_ch is not allocated, true otherwise + */ + bool release_freq(const uint32_t& logical_ch); + + /** + * Obtains the carrier index configured in set_channels() in the radio to which the logical channel logical_ch has + * been mapped to + * @param logical_ch logical channel index + * @return <0 if logical_ch is not allocated, true otherwise + * + * @see channel_cfg_t + */ + int get_carrier_idx(const uint32_t& logical_ch); + + /** + * Checks if the channel has been allocated using allocate_freq() + * + * @param logical_ch logical channel index + * @return true if the channel is allocated, false otherwise + */ + bool is_allocated(const uint32_t& logical_ch); + +private: + std::list available_channels = {}; + std::map allocated_channels = {}; + std::mutex mutex = {}; +}; + +} // namespace srslte + +#endif // SRSLTE_CHANNEL_MAPPING_H diff --git a/lib/include/srslte/radio/radio.h b/lib/include/srslte/radio/radio.h index 56663582b..cfeedcede 100644 --- a/lib/include/srslte/radio/radio.h +++ b/lib/include/srslte/radio/radio.h @@ -19,130 +19,22 @@ * */ -#include - +#include "channel_mapping.h" #include "radio_metrics.h" +#include "rf_buffer.h" +#include "rf_timestamp.h" #include "srslte/common/interfaces_common.h" #include "srslte/common/log_filter.h" -#include "srslte/common/trace.h" #include "srslte/interfaces/radio_interfaces.h" #include "srslte/phy/rf/rf.h" -#include "srslte/srslte.h" #include +#include #ifndef SRSLTE_RADIO_H #define SRSLTE_RADIO_H namespace srslte { -/** - * Implemenation of the rf_buffer_interface for the current radio implementation which uses flat arrays. - * @see rf_buffer_interface - * @see radio - * - */ -class rf_buffer_t : public rf_buffer_interface -{ -public: - /** - * Creates an object and allocates memory for nof_subframes_ assuming the - * largest system bandwidth - * @param nof_subframes_ Number of subframes to allocate - */ - explicit rf_buffer_t(uint32_t nof_subframes_) - { - if (nof_subframes_ > 0) { - // Allocate buffers for an integer number of subframes - for (uint32_t i = 0; i < SRSLTE_MAX_CHANNELS; i++) { - sample_buffer[i] = srslte_vec_cf_malloc(nof_subframes_ * SRSLTE_SF_LEN_MAX); - srslte_vec_cf_zero(sample_buffer[i], SRSLTE_SF_LEN_MAX); - } - allocated = true; - nof_subframes = nof_subframes_; - } - } - /** - * Creates an object and sets the buffers to the flat array pointed by data. Note that data must - * contain up to SRSLTE_MAX_CHANNELS pointers - * @param data Flat array to use as initializer for the internal buffer pointers - */ - explicit rf_buffer_t(cf_t* data[SRSLTE_MAX_CHANNELS]) - { - for (uint32_t i = 0; i < SRSLTE_MAX_CHANNELS; i++) { - sample_buffer[i] = data[i]; - } - } - /** - * Creates an object from a single array pointer. The rest of the channel pointers will be left to NULL - * @param data Flat array to use as initializer for the internal buffer pointers - */ - explicit rf_buffer_t(cf_t* data) { sample_buffer[0] = data; } - /** - * Default constructor leaves the internal pointers to NULL - */ - rf_buffer_t() = default; - - /** - * The destructor will deallocate memory only if it was allocated passing nof_subframes > 0 - */ - ~rf_buffer_t() - { - if (allocated) { - free_all(); - } - } - /** - * Overrides the = operator such that the lvalue internal buffers point to the pointers inside rvalue. - * If memory has already been allocated in the lvalue object, it will free it before pointing the - * buffers to the lvalue. - * After this operator, when the lvalue is destroyed no memory will be freed. - * @param other rvalue - * @return lvalue - */ - rf_buffer_t& operator=(const rf_buffer_t& other) - { - if (this == &other) { - return *this; - } - if (this->allocated) { - free_all(); - this->allocated = false; - } - for (int i = 0; i < SRSLTE_MAX_CHANNELS; i++) { - this->sample_buffer[i] = other.sample_buffer[i]; - } - return *this; - } - - rf_buffer_t(const rf_buffer_t& other) = delete; - cf_t* get(const uint32_t& channel_idx) const override { return sample_buffer.at(channel_idx); } - void set(const uint32_t& channel_idx, cf_t* ptr) override { sample_buffer.at(channel_idx) = ptr; } - cf_t* get(const uint32_t& logical_ch, const uint32_t& port_idx, const uint32_t& nof_antennas) const override - { - return sample_buffer.at(logical_ch * nof_antennas + port_idx); - } - void set(const uint32_t& logical_ch, const uint32_t& port_idx, const uint32_t& nof_antennas, cf_t* ptr) override - { - sample_buffer.at(logical_ch * nof_antennas + port_idx) = ptr; - } - void** to_void() override { return (void**)sample_buffer.data(); } - cf_t** to_cf_t() override { return sample_buffer.data(); } - uint32_t size() override { return nof_subframes * SRSLTE_SF_LEN_MAX; } - -private: - std::array sample_buffer = {}; - bool allocated = false; - uint32_t nof_subframes = 0; - void free_all() - { - for (uint32_t i = 0; i < SRSLTE_MAX_CHANNELS; i++) { - if (sample_buffer[i]) { - free(sample_buffer[i]); - } - } - } -}; - /** * Implementation of the radio interface for the PHY * @@ -168,8 +60,8 @@ public: // trx functions void tx_end() override; - bool tx(rf_buffer_interface& buffer, const uint32_t& nof_samples, const srslte_timestamp_t& tx_time) override; - bool rx_now(rf_buffer_interface& buffer, const uint32_t& nof_samples, srslte_timestamp_t* rxd_time) override; + bool tx(rf_buffer_interface& buffer, const uint32_t& nof_samples, const rf_timestamp_interface& tx_time) override; + bool rx_now(rf_buffer_interface& buffer, const uint32_t& nof_samples, rf_timestamp_interface& rxd_time) override; // setter void set_tx_freq(const uint32_t& carrier_idx, const double& freq) override; @@ -192,38 +84,36 @@ public: srslte_rf_info_t* get_info() override; // Other functions - bool get_metrics(rf_metrics_t* metrics); - float get_rssi(); - bool has_rssi(); - void get_time(srslte_timestamp_t* now); + bool get_metrics(rf_metrics_t* metrics); void handle_rf_msg(srslte_rf_error_t error); static void rf_msg_callback(void* arg, srslte_rf_error_t error); private: - srslte_rf_t rf_device = {}; - srslte_rf_info_t 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; - - srslte_timestamp_t end_of_burst_time = {}; - bool is_start_of_burst = 0; - uint32_t tx_adv_nsamples = 0; - double tx_adv_sec = 0.0f; // Transmission time advance to compensate for antenna->timestamp delay - bool tx_adv_auto = false; - bool tx_adv_negative = false; - bool is_initialized = false; - bool radio_is_streaming = false; - bool continuous_tx = false; - double freq_offset = 0.0f; - double cur_tx_srate = 0.0f; - uint32_t nof_antennas = 0; - uint32_t nof_channels = 0; - uint32_t nof_carriers = 0; + 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; + + rf_timestamp_t end_of_burst_time = {}; + bool is_start_of_burst = false; + uint32_t tx_adv_nsamples = 0; + double tx_adv_sec = 0.0; // Transmission time advance to compensate for antenna->timestamp delay + bool tx_adv_auto = false; + bool tx_adv_negative = false; + bool is_initialized = false; + bool radio_is_streaming = false; + bool continuous_tx = false; + double freq_offset = 0.0; + double cur_tx_srate = 0.0; + uint32_t nof_antennas = 0; + uint32_t nof_channels = 0; + uint32_t nof_channels_x_dev = 0; + uint32_t nof_carriers = 0; std::vector cur_tx_freqs = {}; std::vector cur_rx_freqs = {}; @@ -232,114 +122,79 @@ private: ///< shall be stopped // Define default values for known radios - constexpr static double uhd_default_tx_adv_samples = 98; + constexpr static int uhd_default_tx_adv_samples = 98; constexpr static double uhd_default_tx_adv_offset_sec = 4 * 1e-6; + constexpr static int lime_default_tx_adv_samples = 98; + constexpr static double lime_default_tx_adv_offset_sec = 4 * 1e-6; + constexpr static int blade_default_tx_adv_samples = 27; constexpr static double blade_default_tx_adv_offset_sec = 1e-6; /** - * This class manages the mapping between logical and physical channels. - * A physical channel in this class is a carrier index in the radio class, which - * has multiple antenna ports all tuned to the same frequency. - * - * Every group of channels tuned associated with a carrier go through the same band-pass filter. This - * class then manages the allocation of frequencies to these group of channels. - * - * The same object is reused for the reception and transmission. - * - * When the UE wants to tune a logical channel to a new frequency it requests this class - * to provide an available channel that supports this frequency. At that point, - * that channel can not be used anymore until a call to release(). - * + * Get device calibrated transmit time in advanced seconds + * @param device_name actual device name + * @return transmit time in advanced in seconds */ - class channel_mapping - { - public: - /** Configures a band. A band is defined by an upper and lower frequency boundaries. - * If the upper and lower frequencies are not configured (default is zero), it means - * that they support any frequency - */ - class band_cfg - { - public: - void set(float low_freq_, float high_freq_) - { - low_freq = low_freq_; - high_freq = high_freq_; - } - bool contains(float freq) - { - if (low_freq == 0 && high_freq == 0) { - return true; - } else { - return freq >= low_freq && freq <= high_freq; - } - } - float get_low() { return low_freq; } - float get_high() { return high_freq; } - - private: - float low_freq = 0; - float high_freq = 0; - }; - - /** Each channel is defined by the band it supports and the physical carrier index in the radio - */ - typedef struct { - band_cfg band; - uint32_t carrier_idx; - } channel_cfg_t; + double get_dev_cal_tx_adv_sec(const std::string& device_name); - /** - * Sets the channel configuration. If no channels are configured no physical channels can be allocated - * @param channels_ - */ - void set_channels(const std::list& channels_) { available_channels = channels_; } - - /** - * Finds an unused physical channel that supports the provided frequency and assigns it to logical channel - * logical_ch - * @param logical_ch logical channel index - * @param freq Frequency (in Hz) that we want to receive/transmitt - * @return true if a physical channel supporting this frequency was found or false otherwise - */ - bool allocate_freq(const uint32_t& logical_ch, const float& freq); - - /** - * Releases the allocation of a logical channel allowing to be used in the next call to allocate_freq - * @param logical_ch logical channel index - * @return false if logical_ch is not allocated, true otherwise - */ - bool release_freq(const uint32_t& logical_ch); - - /** - * Obtains the carrier index configured in set_channels() in the radio to which the logical channel logical_ch has - * been mapped to - * @param logical_ch logical channel index - * @return <0 if logical_ch is not allocated, true otherwise - * - * @see channel_cfg_t - */ - int get_carrier_idx(const uint32_t& logical_ch); + channel_mapping rx_channel_mapping = {}, tx_channel_mapping = {}; - /** - * Checks if the channel has been allocated using allocate_freq() - * - * @param logical_ch logical channel index - * @return true if the channel is allocated, false otherwise - */ - bool is_allocated(const uint32_t& logical_ch); + /** + * Helper method for opening a RF device + * + * @param device_idx Device index + * @param device_name Device name + * @param devive_args Device arguments + * @return it returns true if the device was opened successful, otherwise it returns false + */ + bool open_dev(const uint32_t& device_idx, const std::string& device_name, const std::string& devive_args); - private: - std::list available_channels = {}; - std::map allocated_channels = {}; - std::mutex mutex = {}; - }; + /** + * Helper method for transmitting over a single RF device. This function maps automatically the logical transmit + * buffers to the physical RF buffers for the given device. + * + * Also, it takes care internally of transmission gaps and overlaps. So, it applies time compensation per channel + * basis. + * + * @param device_idx Device index + * @param buffer Common transmit buffer + * @param nof_samples_ number of samples to transmit + * @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 uint32_t& nof_samples_, + const srslte_timestamp_t& tx_time_); - channel_mapping rx_channel_mapping = {}, tx_channel_mapping = {}; + /** + * Helper method for receiving over a single RF device. This function maps automatically the logical receive buffers + * to the physical RF buffers for the given device. + * + * @param device_idx Device index + * @param buffer Common receive buffers + * @param nof_samples Number of samples to receive + * @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, + const uint32_t& nof_samples, + srslte_timestamp_t* rxd_time); + /** + * Helper method for mapping logical channels into physical radio buffers. + * + * @param map Channel mapping, it can be either Tx or Rx mapping + * @param device_idx RF Device index for the buffer mapping + * @param sample_offset The physical radio buffer pointer offset + * @param buffer Logical channels buffer + * @param radio_buffers Actual physical radio buffer + * @return It returns true if the mapping was successful, otherwise it returns false. + */ bool map_channels(channel_mapping& map, + uint32_t device_idx, uint32_t sample_offset, const rf_buffer_interface& buffer, void* radio_buffers[SRSLTE_MAX_CHANNELS]); diff --git a/lib/include/srslte/radio/rf_buffer.h b/lib/include/srslte/radio/rf_buffer.h new file mode 100644 index 000000000..bb0a3b7f3 --- /dev/null +++ b/lib/include/srslte/radio/rf_buffer.h @@ -0,0 +1,139 @@ +/* + * 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_RF_BUFFER_H +#define SRSLTE_RF_BUFFER_H + +#include "srslte/interfaces/radio_interfaces.h" + +namespace srslte { + +/** + * Implemenation of the rf_buffer_interface for the current radio implementation which uses flat arrays. + * @see rf_buffer_interface + * @see radio + * + */ +class rf_buffer_t : public rf_buffer_interface +{ +public: + /** + * Creates an object and allocates memory for nof_subframes_ assuming the + * largest system bandwidth + * @param nof_subframes_ Number of subframes to allocate + */ + explicit rf_buffer_t(uint32_t nof_subframes_) + { + if (nof_subframes_ > 0) { + // Allocate buffers for an integer number of subframes + for (uint32_t i = 0; i < SRSLTE_MAX_CHANNELS; i++) { + sample_buffer[i] = srslte_vec_cf_malloc(nof_subframes_ * SRSLTE_SF_LEN_MAX); + srslte_vec_cf_zero(sample_buffer[i], SRSLTE_SF_LEN_MAX); + } + allocated = true; + nof_subframes = nof_subframes_; + } + } + /** + * Creates an object and sets the buffers to the flat array pointed by data. Note that data must + * contain up to SRSLTE_MAX_CHANNELS pointers + * @param data Flat array to use as initializer for the internal buffer pointers + */ + explicit rf_buffer_t(cf_t* data[SRSLTE_MAX_CHANNELS]) + { + for (uint32_t i = 0; i < SRSLTE_MAX_CHANNELS; i++) { + sample_buffer[i] = data[i]; + } + } + /** + * Creates an object from a single array pointer. The rest of the channel pointers will be left to NULL + * @param data Flat array to use as initializer for the internal buffer pointers + */ + explicit rf_buffer_t(cf_t* data) { sample_buffer[0] = data; } + /** + * Default constructor leaves the internal pointers to NULL + */ + rf_buffer_t() = default; + + /** + * The destructor will deallocate memory only if it was allocated passing nof_subframes > 0 + */ + ~rf_buffer_t() + { + if (allocated) { + free_all(); + } + } + /** + * Overrides the = operator such that the lvalue internal buffers point to the pointers inside rvalue. + * If memory has already been allocated in the lvalue object, it will free it before pointing the + * buffers to the lvalue. + * After this operator, when the lvalue is destroyed no memory will be freed. + * @param other rvalue + * @return lvalue + */ + rf_buffer_t& operator=(const rf_buffer_t& other) + { + if (this == &other) { + return *this; + } + if (this->allocated) { + free_all(); + this->allocated = false; + } + for (int i = 0; i < SRSLTE_MAX_CHANNELS; i++) { + this->sample_buffer[i] = other.sample_buffer[i]; + } + return *this; + } + + rf_buffer_t(const rf_buffer_t& other) = delete; + cf_t* get(const uint32_t& channel_idx) const override { return sample_buffer.at(channel_idx); } + void set(const uint32_t& channel_idx, cf_t* ptr) override { sample_buffer.at(channel_idx) = ptr; } + cf_t* get(const uint32_t& logical_ch, const uint32_t& port_idx, const uint32_t& nof_antennas) const override + { + return sample_buffer.at(logical_ch * nof_antennas + port_idx); + } + void set(const uint32_t& logical_ch, const uint32_t& port_idx, const uint32_t& nof_antennas, cf_t* ptr) override + { + sample_buffer.at(logical_ch * nof_antennas + port_idx) = ptr; + } + void** to_void() override { return (void**)sample_buffer.data(); } + cf_t** to_cf_t() override { return sample_buffer.data(); } + uint32_t size() override { return nof_subframes * SRSLTE_SF_LEN_MAX; } + +private: + std::array sample_buffer = {}; + bool allocated = false; + uint32_t nof_subframes = 0; + void free_all() + { + for (uint32_t i = 0; i < SRSLTE_MAX_CHANNELS; i++) { + if (sample_buffer[i]) { + free(sample_buffer[i]); + } + } + } +}; + +} // namespace srslte + +#endif // SRSLTE_RF_BUFFER_H diff --git a/lib/include/srslte/radio/rf_timestamp.h b/lib/include/srslte/radio/rf_timestamp.h new file mode 100644 index 000000000..f47cc9a33 --- /dev/null +++ b/lib/include/srslte/radio/rf_timestamp.h @@ -0,0 +1,107 @@ +/* + * 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_RF_TIMESTAMP_H +#define SRSLTE_RF_TIMESTAMP_H + +#include "srslte/interfaces/radio_interfaces.h" + +namespace srslte { + +/** + * Implemenation of the rf_buffer_interface for the current radio implementation which uses flat arrays. + * @see rf_buffer_interface + * @see radio + * + */ +class rf_timestamp_t : public rf_timestamp_interface +{ +public: + /** + * Default constructor, all timestamps are zero by default + */ + rf_timestamp_t() = default; + + /** + * Copy constructor + * @param other the other object to copy + */ + rf_timestamp_t(const rf_timestamp_t& other) { copy(other); } + + /** + * Default destructor + */ + ~rf_timestamp_t() = default; + + /** + * Gets a timestamp by reference + * @return Given timestamp of the indicated device if the index is bounded, otherwise it returns the default timestamp + */ + const srslte_timestamp_t& get(uint32_t idx) const override + { + if (idx >= SRSLTE_MAX_CHANNELS) { + return default_ts; + } + + return timestamps[idx]; + } + + /** + * Get the timestamp array pointer + * @return timestamp pointer with the value if the channel index is bounded. Otherwise, returns nullptr + */ + srslte_timestamp_t* get_ptr(uint32_t idx) override + { + if (idx >= SRSLTE_MAX_CHANNELS) { + return nullptr; + } + return ×tamps[idx]; + } + + /** + * Add a given amount of seconds to all the timestamps + * @param secs number of seconds + */ + void add(double secs) override + { + for (srslte_timestamp_t& ts : timestamps) { + srslte_timestamp_add(&ts, 0, secs); + } + } + + /** + * Subtract a given amount of seconds to all the timestamps + * @param secs number of seconds + */ + void sub(double secs) override + { + for (srslte_timestamp_t& ts : timestamps) { + srslte_timestamp_sub(&ts, 0, secs); + } + } + +private: + const srslte_timestamp_t default_ts = {}; + std::array timestamps = {}; +}; +} // namespace srslte + +#endif // SRSLTE_RF_TIMESTAMP_H diff --git a/lib/src/radio/CMakeLists.txt b/lib/src/radio/CMakeLists.txt index 9537a10c8..3d4a0fa3a 100644 --- a/lib/src/radio/CMakeLists.txt +++ b/lib/src/radio/CMakeLists.txt @@ -19,7 +19,7 @@ # if(RF_FOUND) - add_library(srslte_radio STATIC radio.cc) + add_library(srslte_radio STATIC radio.cc channel_mapping.cc) target_link_libraries(srslte_radio srslte_rf) endif(RF_FOUND) diff --git a/lib/src/radio/channel_mapping.cc b/lib/src/radio/channel_mapping.cc new file mode 100644 index 000000000..3a85be064 --- /dev/null +++ b/lib/src/radio/channel_mapping.cc @@ -0,0 +1,76 @@ +/* + * 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/. + * + */ + +#include "srslte/radio/channel_mapping.h" +#include "srslte/phy/utils/debug.h" + +namespace srslte { + +bool channel_mapping::allocate_freq(const uint32_t& logical_ch, const float& freq) +{ + std::lock_guard lock(mutex); + + if (allocated_channels.count(logical_ch)) { + ERROR("allocate_freq: Carrier logical_ch=%d already allocated to channel=%d\n", + logical_ch, + allocated_channels[logical_ch].carrier_idx); + return false; + } + + // Find first available channel that supports this frequency and allocated it + for (auto c = available_channels.begin(); c != available_channels.end(); ++c) { + if (c->band.contains(freq)) { + allocated_channels[logical_ch] = *c; + available_channels.erase(c); + return true; + } + } + ERROR("allocate_freq: No channels available for frequency=%.1f\n", freq); + return false; +} + +bool channel_mapping::release_freq(const uint32_t& logical_ch) +{ + std::lock_guard lock(mutex); + if (allocated_channels.count(logical_ch)) { + available_channels.push_back(allocated_channels[logical_ch]); + allocated_channels.erase(logical_ch); + return true; + } + return false; +} + +int channel_mapping::get_carrier_idx(const uint32_t& logical_ch) +{ + std::lock_guard lock(mutex); + if (allocated_channels.count(logical_ch)) { + return allocated_channels[logical_ch].carrier_idx; + } + return -1; +} + +bool channel_mapping::is_allocated(const uint32_t& logical_ch) +{ + std::lock_guard lock(mutex); + return allocated_channels.count(logical_ch) > 0; +} + +} // namespace srslte \ No newline at end of file diff --git a/lib/src/radio/radio.cc b/lib/src/radio/radio.cc index 8356da955..4a3e8cdc8 100644 --- a/lib/src/radio/radio.cc +++ b/lib/src/radio/radio.cc @@ -19,25 +19,21 @@ * */ -#include "srslte/srslte.h" -extern "C" { -#include "srslte/phy/rf/rf.h" -} -#include "srslte/common/log_filter.h" #include "srslte/radio/radio.h" +#include "srslte/config.h" #include -#include +#include #include namespace srslte { -radio::radio(srslte::log_filter* log_h_) : logger(nullptr), log_h(log_h_), zeros(NULL) +radio::radio(srslte::log_filter* log_h_) : logger(nullptr), log_h(log_h_), zeros(nullptr) { zeros = srslte_vec_cf_malloc(SRSLTE_SF_LEN_MAX); srslte_vec_cf_zero(zeros, SRSLTE_SF_LEN_MAX); } -radio::radio(srslte::logger* logger_) : logger(logger_), log_h(nullptr), zeros(NULL) +radio::radio(srslte::logger* logger_) : logger(logger_), log_h(nullptr), zeros(nullptr) { zeros = srslte_vec_cf_malloc(SRSLTE_SF_LEN_MAX); srslte_vec_cf_zero(zeros, SRSLTE_SF_LEN_MAX); @@ -51,6 +47,18 @@ radio::~radio() } } +static inline void split_string(const std::string& input, char delimiter, std::vector& list) +{ + std::stringstream ss(input); + while (ss.good()) { + std::string substr; + getline(ss, substr, delimiter); + if (not substr.empty()) { + list.push_back(substr); + } + } +} + int radio::init(const rf_args_t& args, phy_interface_radio* phy_) { phy = phy_; @@ -89,23 +97,35 @@ int radio::init(const rf_args_t& args, phy_interface_radio* phy_) cur_tx_freqs.resize(nof_carriers); cur_rx_freqs.resize(nof_carriers); - // Init and start Radio - char* device_args = nullptr; - if (args.device_args != "auto") { - device_args = (char*)args.device_args.c_str(); - } - char* dev_name = nullptr; - if (args.device_name != "auto") { - dev_name = (char*)args.device_name.c_str(); + // Split multiple RF channels using `;` delimiter + std::vector device_args_list; + split_string(args.device_args, ';', device_args_list); + + // Add auto if list is empty + if (device_args_list.empty()) { + device_args_list.emplace_back("auto"); } - log_h->console("Opening %d channels in RF device=%s with args=%s\n", - nof_channels, - dev_name ? dev_name : "default", - device_args ? device_args : "default"); - if (srslte_rf_open_devname(&rf_device, dev_name, device_args, nof_channels)) { - log_h->error("Error opening RF device\n"); + + // Makes sure it is possible to have the same number of RF channels in each RF device + if (nof_channels % device_args_list.size() != 0) { + log_h->console( + "Error: The number of required RF channels (%d) is not divisible between the number of RF devices (%ld).\n", + nof_channels, + device_args_list.size()); return SRSLTE_ERROR; } + nof_channels_x_dev = nof_channels / device_args_list.size(); + + // Allocate RF devices + rf_devices.resize(device_args_list.size()); + rf_info.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++) { + if (not open_dev(device_idx, args.device_name, device_args_list[device_idx])) { + log_h->error("Error opening RF device %d\n", device_idx); + } + } is_start_of_burst = true; is_initialized = true; @@ -139,15 +159,6 @@ int radio::init(const rf_args_t& args, phy_interface_radio* phy_) // Frequency offset freq_offset = args.freq_offset; - // Get device info - rf_info = *get_info(); - - // Suppress radio stdout - srslte_rf_suppress_stdout(&rf_device); - - // Register handler for processing O/U/L - srslte_rf_register_error_handler(&rf_device, rf_msg_callback, this); - return SRSLTE_SUCCESS; } @@ -163,13 +174,17 @@ void radio::stop() zeros = NULL; } if (is_initialized) { - srslte_rf_close(&rf_device); + for (srslte_rf_t& rf_device : rf_devices) { + srslte_rf_close(&rf_device); + } } } void radio::reset() { - srslte_rf_stop_rx_stream(&rf_device); + for (srslte_rf_t& rf_device : rf_devices) { + srslte_rf_stop_rx_stream(&rf_device); + } radio_is_streaming = false; usleep(100000); } @@ -198,72 +213,113 @@ bool radio::start_agc(bool tx_gain_same_rx) if (!is_initialized) { return false; } - if (srslte_rf_start_gain_thread(&rf_device, tx_gain_same_rx)) { - ERROR("Error starting AGC Thread RF device\n"); - return false; + for (srslte_rf_t& rf_device : rf_devices) { + if (srslte_rf_start_gain_thread(&rf_device, tx_gain_same_rx)) { + ERROR("Error starting AGC Thread RF device\n"); + return false; + } } - return true; } -bool radio::rx_now(rf_buffer_interface& buffer, const uint32_t& nof_samples, srslte_timestamp_t* rxd_time) +bool radio::rx_now(rf_buffer_interface& buffer, const uint32_t& nof_samples, rf_timestamp_interface& rxd_time) { - if (!is_initialized) { - return false; - } bool ret = true; - if (!radio_is_streaming) { - srslte_rf_start_rx_stream(&rf_device, false); + if (not radio_is_streaming) { + for (srslte_rf_t& rf_device : rf_devices) { + srslte_rf_start_rx_stream(&rf_device, false); + } radio_is_streaming = true; } - time_t* full_secs = rxd_time ? &rxd_time->full_secs : NULL; - double* frac_secs = rxd_time ? &rxd_time->frac_secs : NULL; + for (uint32_t device_idx = 0; device_idx < (uint32_t)rf_devices.size(); device_idx++) { + ret &= rx_dev(device_idx, buffer, nof_samples, rxd_time.get_ptr(device_idx)); + } + + return ret; +} + +bool radio::rx_dev(const uint32_t& device_idx, + const rf_buffer_interface& buffer, + const uint32_t& nof_samples, + srslte_timestamp_t* rxd_time) +{ + if (!is_initialized) { + return false; + } + + time_t* full_secs = rxd_time ? &rxd_time->full_secs : nullptr; + double* frac_secs = rxd_time ? &rxd_time->frac_secs : nullptr; void* radio_buffers[SRSLTE_MAX_CHANNELS] = {}; - if (!map_channels(rx_channel_mapping, 0, buffer, radio_buffers)) { + if (not map_channels(rx_channel_mapping, device_idx, 0, buffer, radio_buffers)) { log_h->error("Mapping logical channels to physical channels for transmission\n"); return false; } - if (srslte_rf_recv_with_time_multi(&rf_device, radio_buffers, nof_samples, true, full_secs, frac_secs) > 0) { - ret = true; - } else { - ret = false; + int ret = + srslte_rf_recv_with_time_multi(&rf_devices[device_idx], radio_buffers, nof_samples, true, full_secs, frac_secs); + return ret > 0; +} + +bool radio::tx(rf_buffer_interface& buffer, const uint32_t& nof_samples, const rf_timestamp_interface& tx_time) +{ + bool ret = true; + + for (uint32_t device_idx = 0; device_idx < (uint32_t)rf_devices.size(); device_idx++) { + ret &= tx_dev(device_idx, buffer, nof_samples, tx_time.get(device_idx)); } + is_start_of_burst = false; + return ret; } -void radio::get_time(srslte_timestamp_t* now) +bool radio::open_dev(const uint32_t& device_idx, const std::string& device_name, const std::string& devive_args) { - if (!is_initialized) { - return; + srslte_rf_t* rf_device = &rf_devices[device_idx]; + + char* dev_args = nullptr; + if (devive_args != "auto") { + dev_args = (char*)devive_args.c_str(); } - srslte_rf_get_time(&rf_device, &now->full_secs, &now->frac_secs); -} -float radio::get_rssi() -{ - if (!is_initialized) { - return 0.0f; + char* dev_name = nullptr; + if (device_name != "auto") { + dev_name = (char*)device_name.c_str(); } - return srslte_rf_get_rssi(&rf_device); -} -bool radio::has_rssi() -{ - if (!is_initialized) { + log_h->console("Opening %d channels in RF device=%s with args=%s\n", + nof_channels_x_dev, + dev_name ? dev_name : "default", + dev_args ? dev_args : "default"); + + if (srslte_rf_open_devname(rf_device, dev_name, dev_args, nof_channels_x_dev)) { + log_h->error("Error opening RF device\n"); return false; } - return srslte_rf_has_rssi(&rf_device); + + // Suppress radio stdout + srslte_rf_suppress_stdout(rf_device); + + // Register handler for processing O/U/L + srslte_rf_register_error_handler(rf_device, rf_msg_callback, this); + + // Get device info + rf_info[device_idx] = *srslte_rf_get_info(rf_device); + + return true; } -bool radio::tx(rf_buffer_interface& buffer, const uint32_t& nof_samples_, const srslte_timestamp_t& tx_time_) +bool radio::tx_dev(const uint32_t& device_idx, + rf_buffer_interface& buffer, + const uint32_t& nof_samples_, + const srslte_timestamp_t& tx_time_) { - uint32_t nof_samples = nof_samples_; - uint32_t sample_offset = 0; + uint32_t nof_samples = nof_samples_; + uint32_t sample_offset = 0; + srslte_rf_t* rf_device = &rf_devices[device_idx]; // Return instantly if the radio module is not initialised if (!is_initialized) { @@ -281,7 +337,7 @@ bool radio::tx(rf_buffer_interface& buffer, const uint32_t& nof_samples_, const // Calculate transmission overlap/gap if it is not start of the burst if (not is_start_of_burst) { // Calculates transmission time overlap with previous transmission - srslte_timestamp_t ts_overlap = end_of_burst_time; + srslte_timestamp_t ts_overlap = end_of_burst_time[device_idx]; srslte_timestamp_sub(&ts_overlap, tx_time.full_secs, tx_time.frac_secs); // Calculates number of overlap samples with previous transmission @@ -297,9 +353,9 @@ bool radio::tx(rf_buffer_interface& buffer, const uint32_t& nof_samples_, const } // Trim the first past_nsamples - sample_offset = (uint32_t)past_nsamples; // Sets an offset for moving first samples offset - tx_time = end_of_burst_time; // Keeps same transmission time - nof_samples = nof_samples - past_nsamples; // Subtracts the number of trimmed samples + sample_offset = (uint32_t)past_nsamples; // Sets an offset for moving first samples offset + tx_time = end_of_burst_time[device_idx]; // Keeps same transmission time + nof_samples = nof_samples - past_nsamples; // Subtracts the number of trimmed samples } else if (past_nsamples < 0) { // if the gap is bigger than TX_MAX_GAP_ZEROS, stop burst @@ -313,8 +369,13 @@ bool radio::tx(rf_buffer_interface& buffer, const uint32_t& nof_samples_, const uint32_t nzeros = SRSLTE_MIN(gap_nsamples, SRSLTE_SF_LEN_MAX); // Zeros transmission - int ret = srslte_rf_send_timed2( - &rf_device, zeros, nzeros, end_of_burst_time.full_secs, end_of_burst_time.frac_secs, false, false); + int ret = srslte_rf_send_timed2(rf_device, + zeros, + nzeros, + end_of_burst_time[device_idx].full_secs, + end_of_burst_time[device_idx].frac_secs, + false, + false); if (ret < SRSLTE_SUCCESS) { return false; } @@ -323,25 +384,25 @@ bool radio::tx(rf_buffer_interface& buffer, const uint32_t& nof_samples_, const gap_nsamples -= nzeros; // Increase timestamp - srslte_timestamp_add(&end_of_burst_time, 0, (double)nzeros / cur_tx_srate); + srslte_timestamp_add(&end_of_burst_time[device_idx], 0, (double)nzeros / cur_tx_srate); } } } } // Save possible end of burst time - srslte_timestamp_copy(&end_of_burst_time, &tx_time); - srslte_timestamp_add(&end_of_burst_time, 0, (double)nof_samples / cur_tx_srate); + srslte_timestamp_copy(&end_of_burst_time[device_idx], &tx_time); + srslte_timestamp_add(&end_of_burst_time[device_idx], 0, (double)nof_samples / cur_tx_srate); void* radio_buffers[SRSLTE_MAX_CHANNELS] = {}; - if (!map_channels(rx_channel_mapping, sample_offset, buffer, radio_buffers)) { + if (not map_channels(rx_channel_mapping, device_idx, sample_offset, buffer, radio_buffers)) { log_h->error("Mapping logical channels to physical channels for transmission\n"); return false; } int ret = srslte_rf_send_timed_multi( - &rf_device, radio_buffers, nof_samples, tx_time.full_secs, tx_time.frac_secs, true, is_start_of_burst, false); - is_start_of_burst = false; + rf_device, radio_buffers, nof_samples, tx_time.full_secs, tx_time.frac_secs, true, is_start_of_burst, false); + return ret > SRSLTE_SUCCESS; } @@ -351,7 +412,10 @@ void radio::tx_end() return; } if (!is_start_of_burst) { - srslte_rf_send_timed2(&rf_device, zeros, 0, end_of_burst_time.full_secs, end_of_burst_time.frac_secs, false, true); + for (uint32_t i = 0; i < (uint32_t)rf_devices.size(); i++) { + srslte_rf_send_timed2( + &rf_devices[i], zeros, 0, end_of_burst_time[i].full_secs, end_of_burst_time[i].frac_secs, false, true); + } is_start_of_burst = true; } } @@ -387,7 +451,13 @@ void radio::set_rx_freq(const uint32_t& carrier_idx, const double& freq) if ((physical_channel_idx + 1) * nof_antennas <= nof_channels) { cur_rx_freqs[physical_channel_idx] = freq; for (uint32_t i = 0; i < nof_antennas; i++) { - srslte_rf_set_rx_freq(&rf_device, physical_channel_idx * nof_antennas + i, freq + freq_offset); + uint32_t phys_antenna_idx = physical_channel_idx * nof_antennas + i; + + // From channel number deduce RF device index and channel + uint32_t rf_device_idx = phys_antenna_idx / nof_channels_x_dev; + uint32_t rf_channel_idx = phys_antenna_idx % nof_channels_x_dev; + + srslte_rf_set_rx_freq(&rf_devices[rf_device_idx], rf_channel_idx, freq + freq_offset); } } else { log_h->error("set_rx_freq: physical_channel_idx=%d for %d antennas exceeds maximum channels (%d)\n", @@ -408,7 +478,9 @@ void radio::set_rx_gain(const float& gain) if (!is_initialized) { return; } - srslte_rf_set_rx_gain(&rf_device, gain); + for (srslte_rf_t& rf_device : rf_devices) { + srslte_rf_set_rx_gain(&rf_device, gain); + } } void radio::set_rx_gain_th(const float& gain) @@ -416,7 +488,9 @@ void radio::set_rx_gain_th(const float& gain) if (!is_initialized) { return; } - srslte_rf_set_rx_gain_th(&rf_device, gain); + for (srslte_rf_t& rf_device : rf_devices) { + srslte_rf_set_rx_gain_th(&rf_device, gain); + } } void radio::set_rx_srate(const double& srate) @@ -424,7 +498,9 @@ void radio::set_rx_srate(const double& srate) if (!is_initialized) { return; } - srslte_rf_set_rx_srate(&rf_device, srate); + for (srslte_rf_t& rf_device : rf_devices) { + srslte_rf_set_rx_srate(&rf_device, srate); + } } void radio::set_tx_freq(const uint32_t& carrier_idx, const double& freq) @@ -447,7 +523,13 @@ void radio::set_tx_freq(const uint32_t& carrier_idx, const double& freq) if ((physical_channel_idx + 1) * nof_antennas <= nof_channels) { cur_tx_freqs[physical_channel_idx] = freq; for (uint32_t i = 0; i < nof_antennas; i++) { - srslte_rf_set_tx_freq(&rf_device, physical_channel_idx * nof_antennas + i, freq + freq_offset); + uint32_t phys_antenna_idx = physical_channel_idx * nof_antennas + i; + + // From channel number deduce RF device index and channel + uint32_t rf_device_idx = phys_antenna_idx / nof_channels_x_dev; + uint32_t rf_channel_idx = phys_antenna_idx % nof_channels_x_dev; + + srslte_rf_set_tx_freq(&rf_devices[rf_device_idx], rf_channel_idx, freq + freq_offset); } } else { log_h->error("set_tx_freq: physical_channel_idx=%d for %d antennas exceeds maximum channels (%d)\n", @@ -468,7 +550,9 @@ void radio::set_tx_gain(const float& gain) if (!is_initialized) { return; } - srslte_rf_set_tx_gain(&rf_device, gain); + for (srslte_rf_t& rf_device : rf_devices) { + srslte_rf_set_tx_gain(&rf_device, gain); + } } double radio::get_freq_offset() @@ -481,23 +565,18 @@ float radio::get_rx_gain() if (!is_initialized) { return 0.0f; } - return srslte_rf_get_rx_gain(&rf_device); + return (float)srslte_rf_get_rx_gain(&rf_devices[0]); } -void radio::set_tx_srate(const double& srate) +double radio::get_dev_cal_tx_adv_sec(const std::string& device_name) { - if (!is_initialized) { - return; - } - cur_tx_srate = srslte_rf_set_tx_srate(&rf_device, srate); - int nsamples = 0; /* Set time advance for each known device if in auto mode */ if (tx_adv_auto) { /* This values have been calibrated using the prach_test_usrp tool in srsLTE */ - if (!strcmp(srslte_rf_name(&rf_device), "uhd_b200")) { + if (device_name == "uhd_b200") { double srate_khz = round(cur_tx_srate / 1e3); if (srate_khz == 1.92e3) { @@ -523,10 +602,10 @@ void radio::set_tx_srate(const double& srate) log_h->console( "\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", cur_tx_srate); - nsamples = cur_tx_srate * (uhd_default_tx_adv_samples * (1 / cur_tx_srate) + uhd_default_tx_adv_offset_sec); + nsamples = uhd_default_tx_adv_samples + (int)(cur_tx_srate * uhd_default_tx_adv_offset_sec); } - } else if (!strcmp(srslte_rf_name(&rf_device), "uhd_usrp2")) { + } else if (device_name == "uhd_usrp2") { double srate_khz = round(cur_tx_srate / 1e3); if (srate_khz == 1.92e3) { nsamples = 14; // estimated @@ -545,10 +624,10 @@ void radio::set_tx_srate(const double& srate) log_h->console( "\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", cur_tx_srate); - nsamples = cur_tx_srate * (uhd_default_tx_adv_samples * (1 / cur_tx_srate) + uhd_default_tx_adv_offset_sec); + nsamples = uhd_default_tx_adv_samples + (int)(cur_tx_srate * uhd_default_tx_adv_offset_sec); } - } else if (!strcmp(srslte_rf_name(&rf_device), "lime")) { + } else if (device_name == "lime") { double srate_khz = round(cur_tx_srate / 1e3); if (srate_khz == 1.92e3) { nsamples = 28; @@ -567,14 +646,14 @@ void radio::set_tx_srate(const double& srate) log_h->console( "\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", cur_tx_srate); - nsamples = cur_tx_srate * (uhd_default_tx_adv_samples * (1 / cur_tx_srate) + uhd_default_tx_adv_offset_sec); + nsamples = lime_default_tx_adv_samples + (int)(cur_tx_srate * lime_default_tx_adv_offset_sec); } - } else if (!strcmp(srslte_rf_name(&rf_device), "uhd_x300")) { + } else if (device_name == "uhd_x300") { // In X300 TX/RX offset is independent of sampling rate nsamples = 45; - } else if (!strcmp(srslte_rf_name(&rf_device), "bladerf")) { + } else if (device_name == "bladerf") { double srate_khz = round(cur_tx_srate / 1e3); if (srate_khz == 1.92e3) { @@ -594,9 +673,9 @@ void radio::set_tx_srate(const double& srate) log_h->console( "\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", cur_tx_srate); - nsamples = blade_default_tx_adv_samples + blade_default_tx_adv_offset_sec * cur_tx_srate; + nsamples = blade_default_tx_adv_samples + (int)(blade_default_tx_adv_offset_sec * cur_tx_srate); } - } else if (!strcmp(srslte_rf_name(&rf_device), "zmq")) { + } else if (device_name == "zmq") { nsamples = 0; } } else { @@ -605,7 +684,22 @@ void radio::set_tx_srate(const double& srate) } // Calculate TX advance in seconds from samples and sampling rate - tx_adv_sec = nsamples / cur_tx_srate; + return (double)nsamples / cur_tx_srate; +} + +void radio::set_tx_srate(const double& srate) +{ + if (!is_initialized) { + return; + } + + for (srslte_rf_t& rf_device : rf_devices) { + cur_tx_srate = srslte_rf_set_tx_srate(&rf_device, srate); + } + + // Get calibrated advanced + tx_adv_sec = get_dev_cal_tx_adv_sec(std::string(srslte_rf_name(&rf_devices[0]))); + if (tx_adv_sec < 0) { tx_adv_sec *= -1; tx_adv_negative = true; @@ -615,9 +709,9 @@ void radio::set_tx_srate(const double& srate) srslte_rf_info_t* radio::get_info() { if (!is_initialized) { - return NULL; + return nullptr; } - return srslte_rf_get_info(&rf_device); + return srslte_rf_get_info(&rf_devices[0]); } bool radio::get_metrics(rf_metrics_t* metrics) @@ -668,6 +762,7 @@ void radio::rf_msg_callback(void* arg, srslte_rf_error_t error) } bool radio::map_channels(channel_mapping& map, + uint32_t device_idx, uint32_t sample_offset, const rf_buffer_interface& buffer, void* radio_buffers[SRSLTE_MAX_CHANNELS]) @@ -679,21 +774,38 @@ bool radio::map_channels(channel_mapping& map, // Conversion from safe C++ std::array to the unsafe C interface. We must ensure that the RF driver implementation // accepts up to SRSLTE_MAX_CHANNELS buffers for (uint32_t i = 0; i < nof_carriers; i++) { - if (map.is_allocated(i)) { - uint32_t physical_idx = map.get_carrier_idx(i); - for (uint32_t j = 0; j < nof_antennas; j++) { - if (physical_idx * nof_antennas + j < SRSLTE_MAX_CHANNELS) { - cf_t* ptr = buffer.get(i, j, nof_antennas); - - // Add sample offset only if it is a valid pointer - if (ptr != nullptr) { - ptr += sample_offset; - } + // Skip if not allocated + if (not map.is_allocated(i)) { + continue; + } + + // Get physical channel + uint32_t physical_idx = map.get_carrier_idx(i); + + // Map each antenna + for (uint32_t j = 0; j < nof_antennas; j++) { + // Detect mapping out-of-bounds + if (physical_idx * nof_antennas + j >= SRSLTE_MAX_CHANNELS) { + return false; + } + + // Calculate actual physical port index + uint32_t phys_chan_idx = physical_idx * nof_antennas + j; - radio_buffers[physical_idx * nof_antennas + j] = ptr; - } else { - return false; + // Deduce physical device and port + uint32_t rf_device_idx = phys_chan_idx / nof_channels_x_dev; + uint32_t rf_channel_idx = phys_chan_idx % nof_channels_x_dev; + + // Set pointer if device index matches + if (rf_device_idx == device_idx) { + cf_t* ptr = buffer.get(i, j, nof_antennas); + + // Add sample offset only if it is a valid pointer + if (ptr != nullptr) { + ptr += sample_offset; } + + radio_buffers[rf_channel_idx] = ptr; } } } @@ -733,56 +845,4 @@ bool radio::config_rf_channels(const rf_args_t& args) return true; } -/*** - * Carrier mapping class - */ -bool radio::channel_mapping::allocate_freq(const uint32_t& logical_ch, const float& freq) -{ - std::lock_guard lock(mutex); - - if (allocated_channels.count(logical_ch)) { - ERROR("allocate_freq: Carrier logical_ch=%d already allocated to channel=%d\n", - logical_ch, - allocated_channels[logical_ch].carrier_idx); - return false; - } - - // Find first available channel that supports this frequency and allocated it - for (auto c = available_channels.begin(); c != available_channels.end(); ++c) { - if (c->band.contains(freq)) { - allocated_channels[logical_ch] = *c; - available_channels.erase(c); - return true; - } - } - ERROR("allocate_freq: No channels available for frequency=%.1f\n", freq); - return false; -} - -bool radio::channel_mapping::release_freq(const uint32_t& logical_ch) -{ - std::lock_guard lock(mutex); - if (allocated_channels.count(logical_ch)) { - available_channels.push_back(allocated_channels[logical_ch]); - allocated_channels.erase(logical_ch); - return true; - } - return false; -} - -int radio::channel_mapping::get_carrier_idx(const uint32_t& logical_ch) -{ - std::lock_guard lock(mutex); - if (allocated_channels.count(logical_ch)) { - return allocated_channels[logical_ch].carrier_idx; - } - return -1; -} - -bool radio::channel_mapping::is_allocated(const uint32_t& logical_ch) -{ - std::lock_guard lock(mutex); - return allocated_channels.count(logical_ch) > 0; -} - } // namespace srslte diff --git a/lib/src/radio/test/benchmark_radio.cc b/lib/src/radio/test/benchmark_radio.cc index de2c8a3fd..304e2f02a 100644 --- a/lib/src/radio/test/benchmark_radio.cc +++ b/lib/src/radio/test/benchmark_radio.cc @@ -265,19 +265,15 @@ private: int main(int argc, char** argv) { - int ret = SRSLTE_ERROR; - radio* radio_h[SRSLTE_MAX_RADIOS] = {NULL}; - srslte_timestamp_t ts_prev[SRSLTE_MAX_RADIOS], ts_rx[SRSLTE_MAX_RADIOS], ts_tx; - uint32_t nof_gaps = 0; - char filename[256] = {}; - srslte_filesink_t filesink[SRSLTE_MAX_RADIOS] = {}; - srslte_dft_plan_t dft_plan = {}, idft_plan = {}; - srslte_agc_t agc[SRSLTE_MAX_RADIOS] = {}; - phy_dummy phy; - - bzero(&ts_prev, sizeof(ts_prev)); - bzero(&ts_rx, sizeof(ts_rx)); - bzero(&ts_tx, sizeof(ts_tx)); + int ret = SRSLTE_ERROR; + radio* radio_h[SRSLTE_MAX_RADIOS] = {nullptr}; + srslte::rf_timestamp_t ts_prev[SRSLTE_MAX_RADIOS], ts_rx[SRSLTE_MAX_RADIOS], ts_tx; + uint32_t nof_gaps = 0; + char filename[256] = {}; + srslte_filesink_t filesink[SRSLTE_MAX_RADIOS] = {}; + srslte_dft_plan_t dft_plan = {}, idft_plan = {}; + srslte_agc_t agc[SRSLTE_MAX_RADIOS] = {}; + phy_dummy phy; rf_buffer_t rf_buffers[SRSLTE_MAX_RADIOS] = {}; @@ -413,7 +409,7 @@ int main(int argc, char** argv) // receive each radio for (uint32_t r = 0; r < nof_radios; r++) { - radio_h[r]->rx_now(rf_buffers[r], frame_size, &ts_rx[r]); + radio_h[r]->rx_now(rf_buffers[r], frame_size, ts_rx[r]); } // run agc @@ -426,8 +422,8 @@ int main(int argc, char** argv) // Transmit if (tx_enable) { for (uint32_t r = 0; r < nof_radios; r++) { - srslte_timestamp_copy(&ts_tx, &ts_rx[r]); - srslte_timestamp_add(&ts_tx, 0, 0.004); + ts_tx.copy(ts_rx[r]); + ts_tx.add(0.004); radio_h[r]->tx(rf_buffers[r], frame_size, ts_tx); } } @@ -485,20 +481,19 @@ int main(int argc, char** argv) if (i != 0) { for (uint32_t r = 0; r < nof_radios; r++) { srslte_timestamp_t ts_diff; - bzero(&ts_diff, sizeof(ts_diff)); - srslte_timestamp_copy(&ts_diff, &ts_rx[r]); - srslte_timestamp_sub(&ts_diff, ts_prev[r].full_secs, ts_prev[r].frac_secs); - gap = (int)round(srslte_timestamp_real(&ts_diff) * srate) - frame_size; + srslte_timestamp_copy(&ts_diff, ts_rx[r].get_ptr(0)); + srslte_timestamp_sub(&ts_diff, ts_prev[r].get_ptr(0)->full_secs, ts_prev[r].get_ptr(0)->frac_secs); + gap = (int32_t)round(srslte_timestamp_real(&ts_diff) * srate) - (int32_t)frame_size; - if (gap) { + if (gap != 0) { INFO("Timestamp gap (%d samples) detected! Frame %d/%d. ts=%.9f+%.9f=%.9f\n", gap, i + 1, nof_frames, - srslte_timestamp_real(&ts_prev[r]), + srslte_timestamp_real(ts_prev[r].get_ptr(0)), srslte_timestamp_real(&ts_diff), - srslte_timestamp_real(&ts_rx[r])); + srslte_timestamp_real(ts_rx[r].get_ptr(0))); nof_gaps++; } } @@ -506,7 +501,7 @@ int main(int argc, char** argv) /* Save timestamp */ for (uint32_t r = 0; r < nof_radios; r++) { - srslte_timestamp_copy(&ts_prev[r], &ts_rx[r]); + ts_prev[r].copy(ts_rx[r]); } nof_samples -= frame_size; diff --git a/srsenb/hdr/phy/phy_common.h b/srsenb/hdr/phy/phy_common.h index c9aeeba81..52ab62089 100644 --- a/srsenb/hdr/phy/phy_common.h +++ b/srsenb/hdr/phy/phy_common.h @@ -63,7 +63,7 @@ public: * @param nof_samples number of samples to transmit * @param tx_time timestamp to transmit samples */ - void worker_end(void* tx_sem_id, srslte::rf_buffer_t& buffer, uint32_t nof_samples, srslte_timestamp_t tx_time); + void worker_end(void* tx_sem_id, srslte::rf_buffer_t& buffer, uint32_t nof_samples, srslte::rf_timestamp_t& tx_time); // Common objects phy_args_t params = {}; diff --git a/srsenb/hdr/phy/sf_worker.h b/srsenb/hdr/phy/sf_worker.h index c41de4e8e..0fdd22a48 100644 --- a/srsenb/hdr/phy/sf_worker.h +++ b/srsenb/hdr/phy/sf_worker.h @@ -39,7 +39,7 @@ public: void init(phy_common* phy, srslte::log* log_h); cf_t* get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx); - void set_time(uint32_t tti, uint32_t tx_worker_cnt, srslte_timestamp_t tx_time); + void set_time(uint32_t tti_, uint32_t tx_worker_cnt_, const srslte::rf_timestamp_t& tx_time_); int add_rnti(uint16_t rnti, uint32_t cc_idx); void rem_rnti(uint16_t rnti); @@ -65,10 +65,10 @@ private: bool running = false; std::mutex work_mutex; - uint32_t tti_rx = 0, tti_tx_dl = 0, tti_tx_ul = 0; - uint32_t t_rx = 0, t_tx_dl = 0, t_tx_ul = 0; - uint32_t tx_worker_cnt = 0; - srslte_timestamp_t tx_time = {}; + uint32_t tti_rx = 0, tti_tx_dl = 0, tti_tx_ul = 0; + uint32_t t_rx = 0, t_tx_dl = 0, t_tx_ul = 0; + uint32_t tx_worker_cnt = 0; + srslte::rf_timestamp_t tx_time = {}; std::vector > cc_workers; diff --git a/srsenb/src/phy/phy_common.cc b/srsenb/src/phy/phy_common.cc index 091d41646..51d3dbbe0 100644 --- a/srsenb/src/phy/phy_common.cc +++ b/srsenb/src/phy/phy_common.cc @@ -124,17 +124,17 @@ void phy_common::set_ul_grants(uint32_t tti, const stack_interface_phy_lte::ul_s * Each worker uses this function to indicate that all processing is done and data is ready for transmission or * there is no transmission at all (tx_enable). In that case, the end of burst message will be sent to the radio */ -void phy_common::worker_end(void* tx_sem_id, - srslte::rf_buffer_t& buffer, - uint32_t nof_samples, - srslte_timestamp_t tx_time) +void phy_common::worker_end(void* tx_sem_id, + srslte::rf_buffer_t& buffer, + uint32_t nof_samples, + srslte::rf_timestamp_t& tx_time) { // Wait for the green light to transmit in the current TTI semaphore.wait(tx_sem_id); // Run DL channel emulator if created if (dl_channel) { - dl_channel->run(buffer.to_cf_t(), buffer.to_cf_t(), nof_samples, tx_time); + dl_channel->run(buffer.to_cf_t(), buffer.to_cf_t(), nof_samples, tx_time.get(0)); } // Always transmit on single radio diff --git a/srsenb/src/phy/sf_worker.cc b/srsenb/src/phy/sf_worker.cc index b1be21ef0..9f6abf5d8 100644 --- a/srsenb/src/phy/sf_worker.cc +++ b/srsenb/src/phy/sf_worker.cc @@ -111,7 +111,7 @@ cf_t* sf_worker::get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx) return cc_workers[cc_idx]->get_buffer_rx(antenna_idx); } -void sf_worker::set_time(uint32_t tti_, uint32_t tx_worker_cnt_, srslte_timestamp_t tx_time_) +void sf_worker::set_time(uint32_t tti_, uint32_t tx_worker_cnt_, const srslte::rf_timestamp_t& tx_time_) { tti_rx = tti_; tti_tx_dl = TTI_ADD(tti_rx, FDD_HARQ_DELAY_UL_MS); @@ -122,7 +122,7 @@ void sf_worker::set_time(uint32_t tti_, uint32_t tx_worker_cnt_, srslte_timestam t_tx_ul = TTIMOD(tti_tx_ul); tx_worker_cnt = tx_worker_cnt_; - srslte_timestamp_copy(&tx_time, &tx_time_); + tx_time.copy(tx_time_); for (auto& w : cc_workers) { w->set_tti(tti_); diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc index 174296af8..e710c949e 100644 --- a/srsenb/src/phy/txrx.cc +++ b/srsenb/src/phy/txrx.cc @@ -87,11 +87,10 @@ void txrx::stop() void txrx::run_thread() { - sf_worker* worker = nullptr; - srslte::rf_buffer_t buffer = {}; - srslte_timestamp_t rx_time = {}; - srslte_timestamp_t tx_time = {}; - uint32_t sf_len = SRSLTE_SF_LEN_PRB(worker_com->get_nof_prb(0)); + sf_worker* worker = nullptr; + srslte::rf_buffer_t buffer = {}; + srslte::rf_timestamp_t timestamp = {}; + uint32_t sf_len = SRSLTE_SF_LEN_PRB(worker_com->get_nof_prb(0)); float samp_rate = srslte_sampling_freq_hz(worker_com->get_nof_prb(0)); @@ -134,24 +133,23 @@ void txrx::run_thread() } } - radio_h->rx_now(buffer, sf_len, &rx_time); + radio_h->rx_now(buffer, sf_len, timestamp); if (ul_channel) { - ul_channel->run(buffer.to_cf_t(), buffer.to_cf_t(), sf_len, rx_time); + ul_channel->run(buffer.to_cf_t(), buffer.to_cf_t(), sf_len, timestamp.get(0)); } - /* Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time */ - srslte_timestamp_copy(&tx_time, &rx_time); - srslte_timestamp_add(&tx_time, 0, FDD_HARQ_DELAY_UL_MS * 1e-3); + // Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time + timestamp.add(FDD_HARQ_DELAY_UL_MS * 1e-3); Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", tti, tx_worker_cnt, - tx_time.full_secs, - tx_time.frac_secs, + timestamp.get(0).full_secs, + timestamp.get(0).frac_secs, worker->get_id()); - worker->set_time(tti, tx_worker_cnt, tx_time); + worker->set_time(tti, tx_worker_cnt, timestamp); tx_worker_cnt = (tx_worker_cnt + 1) % nof_workers; // Trigger phy worker execution diff --git a/srsenb/test/phy/enb_phy_test.cc b/srsenb/test/phy/enb_phy_test.cc index d696ee344..7c51585b2 100644 --- a/srsenb/test/phy/enb_phy_test.cc +++ b/srsenb/test/phy/enb_phy_test.cc @@ -80,7 +80,7 @@ private: srslte::log_filter log_h; std::vector ringbuffers_tx; std::vector ringbuffers_rx; - srslte_timestamp_t ts_rx = {}; + srslte::rf_timestamp_t ts_rx = {}; double rx_srate = 0.0; bool running = true; @@ -186,7 +186,9 @@ public: } } - bool tx(srslte::rf_buffer_interface& buffer, const uint32_t& nof_samples, const srslte_timestamp_t& tx_time) override + bool tx(srslte::rf_buffer_interface& buffer, + const uint32_t& nof_samples, + const srslte::rf_timestamp_interface& tx_time) override { int err = SRSLTE_SUCCESS; @@ -207,7 +209,9 @@ public: return err >= SRSLTE_SUCCESS; } void tx_end() override {} - bool rx_now(srslte::rf_buffer_interface& buffer, const uint32_t& nof_samples, srslte_timestamp_t* rxd_time) override + bool rx_now(srslte::rf_buffer_interface& buffer, + const uint32_t& nof_samples, + srslte::rf_timestamp_interface& rxd_time) override { int err = SRSLTE_SUCCESS; @@ -224,13 +228,11 @@ public: } // Copy new timestamp - if (rxd_time) { - *rxd_time = ts_rx; - } + rxd_time = ts_rx; // Copy new timestamp if (std::isnormal(rx_srate)) { - srslte_timestamp_add(&ts_rx, 0, static_cast(nof_samples) / rx_srate); + ts_rx.add(static_cast(nof_samples) / rx_srate); } // Notify Rx diff --git a/srsue/hdr/phy/phy_common.h b/srsue/hdr/phy/phy_common.h index 05a50c1cf..981791c48 100644 --- a/srsue/hdr/phy/phy_common.h +++ b/srsue/hdr/phy/phy_common.h @@ -138,8 +138,11 @@ public: srslte_pdsch_ack_resource_t resource); bool get_dl_pending_ack(srslte_ul_sf_cfg_t* sf, uint32_t cc_idx, srslte_pdsch_ack_cc_t* ack); - void - worker_end(void* h, bool tx_enable, srslte::rf_buffer_t& buffer, uint32_t nof_samples, srslte_timestamp_t tx_time); + void worker_end(void* h, + bool tx_enable, + srslte::rf_buffer_t& buffer, + uint32_t nof_samples, + srslte::rf_timestamp_t& tx_time); void set_cell(const srslte_cell_t& c); void set_nof_workers(uint32_t nof_workers); diff --git a/srsue/hdr/phy/sf_worker.h b/srsue/hdr/phy/sf_worker.h index 360f90b59..26b72e21c 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); + void set_tx_time(const srslte::rf_timestamp_t& tx_time); void set_prach(cf_t* prach_ptr, float prach_power); void set_cfo(const uint32_t& cc_idx, float cfo); @@ -104,13 +104,13 @@ private: srslte_tdd_config_t tdd_config = {}; std::condition_variable cell_init_cond; - bool cell_initiated = false; + bool cell_initiated = false; cf_t* prach_ptr = nullptr; float prach_power = 0; - uint32_t tti = 0; - srslte_timestamp_t tx_time = {}; + uint32_t tti = 0; + srslte::rf_timestamp_t tx_time = {}; uint32_t rssi_read_cnt = 0; }; diff --git a/srsue/hdr/phy/sync.h b/srsue/hdr/phy/sync.h index 2f878a55a..44021410b 100644 --- a/srsue/hdr/phy/sync.h +++ b/srsue/hdr/phy/sync.h @@ -153,6 +153,7 @@ private: bool running = false; 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 // Objects for internal use diff --git a/srsue/src/phy/phy_common.cc b/srsue/src/phy/phy_common.cc index b344ed2d0..c02312426 100644 --- a/srsue/src/phy/phy_common.cc +++ b/srsue/src/phy/phy_common.cc @@ -47,10 +47,7 @@ phy_common::phy_common() reset(); } -phy_common::~phy_common() -{ - -} +phy_common::~phy_common() = default; void phy_common::set_nof_workers(uint32_t nof_workers_) { @@ -101,8 +98,8 @@ void phy_common::set_ue_dl_cfg(srslte_ue_dl_cfg_t* ue_dl_cfg) chest_cfg->noise_alg = SRSLTE_NOISE_ALG_PSS; } - chest_cfg->rsrp_neighbour = false; - chest_cfg->sync_error_enable = args->correct_sync_error; + chest_cfg->rsrp_neighbour = false; + chest_cfg->sync_error_enable = args->correct_sync_error; chest_cfg->estimator_alg = args->interpolate_subframe_enabled ? SRSLTE_ESTIMATOR_ALG_INTERPOLATE : SRSLTE_ESTIMATOR_ALG_AVERAGE; chest_cfg->cfo_estimate_enable = args->cfo_ref_mask != 0; @@ -524,23 +521,23 @@ bool phy_common::get_dl_pending_ack(srslte_ul_sf_cfg_t* sf, uint32_t cc_idx, srs * Each worker uses this function to indicate that all processing is done and data is ready for transmission or * there is no transmission at all (tx_enable). In that case, the end of burst message will be sent to the radio */ -void phy_common::worker_end(void* tx_sem_id, - bool tx_enable, - srslte::rf_buffer_t& buffer, - uint32_t nof_samples, - srslte_timestamp_t tx_time) +void phy_common::worker_end(void* tx_sem_id, + bool tx_enable, + srslte::rf_buffer_t& buffer, + uint32_t nof_samples, + srslte::rf_timestamp_t& tx_time) { // Wait for the green light to transmit in the current TTI semaphore.wait(tx_sem_id); // Add Time Alignment - srslte_timestamp_sub(&tx_time, 0, ta.get_sec()); + tx_time.sub((double)ta.get_sec()); // For each radio, transmit - if (tx_enable && !srslte_timestamp_iszero(&tx_time)) { + if (tx_enable) { if (ul_channel) { - ul_channel->run(buffer.to_cf_t(), buffer.to_cf_t(), nof_samples, tx_time); + ul_channel->run(buffer.to_cf_t(), buffer.to_cf_t(), nof_samples, tx_time.get(0)); } radio_h->tx(buffer, nof_samples, tx_time); @@ -552,9 +549,9 @@ void phy_common::worker_end(void* tx_sem_id, } else { if (!radio_h->get_is_start_of_burst()) { - if (ul_channel && !srslte_timestamp_iszero(&tx_time)) { + if (ul_channel) { srslte_vec_cf_zero(zeros_multi.get(0), nof_samples); - ul_channel->run(zeros_multi.to_cf_t(), zeros_multi.to_cf_t(), nof_samples, tx_time); + ul_channel->run(zeros_multi.to_cf_t(), zeros_multi.to_cf_t(), nof_samples, tx_time.get(0)); } radio_h->tx(zeros_multi, nof_samples, tx_time); @@ -662,11 +659,11 @@ void phy_common::reset() { reset_radio(); - sr_enabled = false; - cur_pathloss = 0; - cur_pusch_power = 0; - sr_last_tx_tti = -1; - cur_pusch_power = 0; + sr_enabled = false; + cur_pathloss = 0; + cur_pusch_power = 0; + sr_last_tx_tti = -1; + cur_pusch_power = 0; pcell_report_period = 20; ZERO_OBJECT(pathloss); diff --git a/srsue/src/phy/sf_worker.cc b/srsue/src/phy/sf_worker.cc index fb4a29f2d..df896a783 100644 --- a/srsue/src/phy/sf_worker.cc +++ b/srsue/src/phy/sf_worker.cc @@ -138,9 +138,9 @@ void sf_worker::set_tti(uint32_t tti_) } } -void sf_worker::set_tx_time(srslte_timestamp_t tx_time_) +void sf_worker::set_tx_time(const srslte::rf_timestamp_t& tx_time_) { - tx_time = tx_time_; + tx_time.copy(tx_time_); } void sf_worker::set_prach(cf_t* prach_ptr_, float prach_power_) diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index 4dc1f8195..0b05a3a53 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -429,11 +429,6 @@ void sync::run_camping_in_sync_state(sf_worker* worker, srslte::rf_buffer_t& syn } } - // Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time - srslte_timestamp_t tx_time; - srslte_ue_sync_get_last_timestamp(&ue_sync, &tx_time); // Get Rx Timestamp - srslte_timestamp_add(&tx_time, 0, FDD_HARQ_DELAY_DL_MS * 1e-3); // Add Tx delay - 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 @@ -443,7 +438,10 @@ void sync::run_camping_in_sync_state(sf_worker* worker, srslte::rf_buffer_t& syn } worker->set_tti(tti); - worker->set_tx_time(tx_time); + + // Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time + last_rx_time.add(FDD_HARQ_DELAY_DL_MS * 1e-3); + worker->set_tx_time(last_rx_time); // Advance/reset prach subframe pointer if (prach_ptr) { @@ -807,15 +805,21 @@ void sync::get_current_cell(srslte_cell_t* cell_, uint32_t* earfcn_) int sync::radio_recv_fnc(srslte::rf_buffer_t& data, uint32_t nsamples, srslte_timestamp_t* rx_time) { - srslte_timestamp_t ts = {}; - - // Use local timestamp if timestamp is not provided - if (!rx_time) { - rx_time = &ts; - } + // This function is designed for being called from the UE sync object which will pass a null rx_time in case + // receive dummy samples. So, rf_timestamp points at dummy timestamp in case rx_time is not provided + srslte::rf_timestamp_t dummy_ts = {}; + srslte::rf_timestamp_t& rf_timestamp = (rx_time == nullptr) ? dummy_ts : last_rx_time; // Receive - if (radio_h->rx_now(data, nsamples, rx_time)) { + if (radio_h->rx_now(data, nsamples, rf_timestamp)) { + srslte_timestamp_t dummy_flat_ts = {}; + + // Load flat timestamp + if (rx_time == nullptr) { + rx_time = &dummy_flat_ts; + } + *rx_time = rf_timestamp.get(0); + // check timestamp reset if (forced_rx_time_init || srslte_timestamp_iszero(&tti_ts) || srslte_timestamp_compare(rx_time, &tti_ts) < 0) { if (srslte_timestamp_compare(rx_time, &tti_ts) < 0) { diff --git a/srsue/test/phy/scell_search_test.cc b/srsue/test/phy/scell_search_test.cc index 86db578d3..fcd1a2401 100644 --- a/srsue/test/phy/scell_search_test.cc +++ b/srsue/test/phy/scell_search_test.cc @@ -136,13 +136,13 @@ public: } } - int work(srslte_dl_sf_cfg_t* dl_sf, - srslte_dci_cfg_t* dci_cfg, - srslte_dci_dl_t* dci, - srslte_softbuffer_tx_t** softbuffer_tx, - uint8_t** data_tx, - cf_t* baseband_buffer, - const srslte_timestamp_t& ts) + int work(srslte_dl_sf_cfg_t* dl_sf, + srslte_dci_cfg_t* dci_cfg, + srslte_dci_dl_t* dci, + srslte_softbuffer_tx_t** softbuffer_tx, + uint8_t** data_tx, + cf_t* baseband_buffer, + const srslte::rf_timestamp_t& ts) { int ret = SRSLTE_SUCCESS; @@ -189,7 +189,7 @@ public: srslte_enb_dl_gen_signal(&enb_dl); // Apply channel - channel->run(signal_buffer, signal_buffer, sf_len, ts); + channel->run(signal_buffer, signal_buffer, sf_len, ts.get(0)); // Combine Tx ports for (uint32_t i = 1; i < enb_dl.cell.nof_ports; i++) { @@ -417,7 +417,7 @@ int main(int argc, char** argv) // Common for simulation and over-the-air cf_t* baseband_buffer = srslte_vec_cf_malloc(SRSLTE_SF_LEN_MAX); - srslte_timestamp_t ts = {}; + srslte::rf_timestamp_t ts = {}; srsue::scell::intra_measure intra_measure; srslte::log_filter logger("intra_measure"); dummy_rrc rrc; @@ -567,7 +567,7 @@ int main(int argc, char** argv) if (radio) { // Receive radio srslte::rf_buffer_t radio_buffer(baseband_buffer); - radio->rx_now(radio_buffer, SRSLTE_SF_LEN_PRB(cell_base.nof_prb), &ts); + radio->rx_now(radio_buffer, SRSLTE_SF_LEN_PRB(cell_base.nof_prb), ts); } else { // Run eNb simulator bool put_pdsch = serving_cell_pdsch_enable; @@ -594,9 +594,9 @@ int main(int argc, char** argv) dci.tb_cw_swap = false; dci.pconf = false; dci.power_offset = false; - dci.tpc_pucch = false; - dci.ra_preamble = false; - dci.ra_mask_idx = false; + dci.tpc_pucch = 0; + dci.ra_preamble = 0; + dci.ra_mask_idx = 0; dci.srs_request = false; dci.srs_request_present = false; dci.cif_present = false; @@ -653,7 +653,7 @@ int main(int argc, char** argv) } // Increase Time counter - srslte_timestamp_add(&ts, 0, 0.001f); + ts.add(0.001); // Give data to intra measure component intra_measure.write(sf_idx % 10240, baseband_buffer, SRSLTE_SF_LEN_PRB(cell_base.nof_prb)); diff --git a/srsue/test/phy/ue_phy_test.cc b/srsue/test/phy/ue_phy_test.cc index 25f517337..de401b89a 100644 --- a/srsue/test/phy/ue_phy_test.cc +++ b/srsue/test/phy/ue_phy_test.cc @@ -131,7 +131,7 @@ private: std::mutex mutex; std::condition_variable cvar; srslte_rf_info_t rf_info = {}; - srslte_timestamp_t tx_last_tx = {}; + srslte::rf_timestamp_t tx_last_tx = {}; uint32_t count_late = 0; CALLBACK(rx_now) @@ -191,8 +191,9 @@ private: uint32_t get_count_late() { return count_late; } - bool - tx(srslte::rf_buffer_interface& buffer, const uint32_t& nof_samples, const srslte_timestamp_t& tx_time) override + bool tx(srslte::rf_buffer_interface& buffer, + const uint32_t& nof_samples, + const srslte::rf_timestamp_interface& tx_time) override { bool ret = true; notify_tx(); @@ -201,18 +202,20 @@ private: if (!std::isnormal(tx_srate)) { count_late++; } - if (srslte_timestamp_compare(&tx_time, &tx_last_tx) < 0) { + if (srslte_timestamp_compare(&tx_time.get(0), tx_last_tx.get_ptr(0)) < 0) { ret = false; } - tx_last_tx = tx_time; - srslte_timestamp_add(&tx_last_tx, 0, (double)nof_samples / (double)tx_srate); + tx_last_tx.copy(tx_time); + tx_last_tx.add((double)nof_samples / (double)tx_srate); return ret; } void release_freq(const uint32_t& carrier_idx) override{}; void tx_end() override {} - bool rx_now(srslte::rf_buffer_interface& buffer, const uint32_t& nof_samples, srslte_timestamp_t* rxd_time) override + bool rx_now(srslte::rf_buffer_interface& buffer, + const uint32_t& nof_samples, + srslte::rf_timestamp_interface& rxd_time) override { notify_rx_now(); @@ -255,9 +258,7 @@ private: } // Set Rx timestamp - if (rxd_time) { - srslte_timestamp_init_uint64(rxd_time, rx_timestamp, (double)base_srate); - } + srslte_timestamp_init_uint64(rxd_time.get_ptr(0), rx_timestamp, (double)base_srate); // Update timestamp rx_timestamp += base_nsamples;