Refactored radio class for acommodating multiple RF devices

master
Xavier Arteaga 5 years ago committed by Xavier Arteaga
parent e2146e90ad
commit 89b24b54e5

@ -58,6 +58,35 @@ public:
virtual uint32_t size() = 0; 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. * Radio interface for the PHY.
* *
@ -94,7 +123,7 @@ public:
* @param tx_time Time to transmit all signals * @param tx_time Time to transmit all signals
* @return it returns true if the transmission was successful, otherwise it returns false * @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 * 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 * @param tx_time Time at which the samples were received. Note the time is the same for all carriers
* @return * @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 * Sets the TX frequency for all antennas in the provided carrier index

@ -29,6 +29,10 @@
#include "srslte/config.h" #include "srslte/config.h"
#ifdef __cplusplus
extern "C" {
#endif
#define RF_PARAM_LEN (256) #define RF_PARAM_LEN (256)
typedef struct { typedef struct {
@ -164,4 +168,8 @@ SRSLTE_API int srslte_rf_send_multi(srslte_rf_t* rf,
bool is_start_of_burst, bool is_start_of_burst,
bool is_end_of_burst); bool is_end_of_burst);
#ifdef __cplusplus
}
#endif
#endif // SRSLTE_RF_H #endif // SRSLTE_RF_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 <cinttypes>
#include <list>
#include <map>
#include <mutex>
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<channel_cfg_t>& 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<channel_cfg_t> available_channels = {};
std::map<uint32_t, channel_cfg_t> allocated_channels = {};
std::mutex mutex = {};
};
} // namespace srslte
#endif // SRSLTE_CHANNEL_MAPPING_H

@ -19,130 +19,22 @@
* *
*/ */
#include <string.h> #include "channel_mapping.h"
#include "radio_metrics.h" #include "radio_metrics.h"
#include "rf_buffer.h"
#include "rf_timestamp.h"
#include "srslte/common/interfaces_common.h" #include "srslte/common/interfaces_common.h"
#include "srslte/common/log_filter.h" #include "srslte/common/log_filter.h"
#include "srslte/common/trace.h"
#include "srslte/interfaces/radio_interfaces.h" #include "srslte/interfaces/radio_interfaces.h"
#include "srslte/phy/rf/rf.h" #include "srslte/phy/rf/rf.h"
#include "srslte/srslte.h"
#include <list> #include <list>
#include <string>
#ifndef SRSLTE_RADIO_H #ifndef SRSLTE_RADIO_H
#define SRSLTE_RADIO_H #define SRSLTE_RADIO_H
namespace srslte { 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<cf_t*, SRSLTE_MAX_CHANNELS> 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 * Implementation of the radio interface for the PHY
* *
@ -168,8 +60,8 @@ public:
// trx functions // trx functions
void tx_end() override; void tx_end() override;
bool tx(rf_buffer_interface& buffer, const uint32_t& nof_samples, const srslte_timestamp_t& tx_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, srslte_timestamp_t* rxd_time) override; bool rx_now(rf_buffer_interface& buffer, const uint32_t& nof_samples, rf_timestamp_interface& rxd_time) override;
// setter // setter
void set_tx_freq(const uint32_t& carrier_idx, const double& freq) override; void set_tx_freq(const uint32_t& carrier_idx, const double& freq) override;
@ -193,16 +85,13 @@ public:
// Other functions // Other functions
bool get_metrics(rf_metrics_t* metrics); bool get_metrics(rf_metrics_t* metrics);
float get_rssi();
bool has_rssi();
void get_time(srslte_timestamp_t* now);
void handle_rf_msg(srslte_rf_error_t error); void handle_rf_msg(srslte_rf_error_t error);
static void rf_msg_callback(void* arg, srslte_rf_error_t error); static void rf_msg_callback(void* arg, srslte_rf_error_t error);
private: private:
srslte_rf_t rf_device = {}; std::vector<srslte_rf_t> rf_devices = {};
srslte_rf_info_t rf_info = {}; std::vector<srslte_rf_info_t> rf_info = {};
rf_metrics_t rf_metrics = {}; rf_metrics_t rf_metrics = {};
log_filter log_local = {}; log_filter log_local = {};
log_filter* log_h = nullptr; log_filter* log_h = nullptr;
@ -210,19 +99,20 @@ private:
phy_interface_radio* phy = nullptr; phy_interface_radio* phy = nullptr;
cf_t* zeros = nullptr; cf_t* zeros = nullptr;
srslte_timestamp_t end_of_burst_time = {}; rf_timestamp_t end_of_burst_time = {};
bool is_start_of_burst = 0; bool is_start_of_burst = false;
uint32_t tx_adv_nsamples = 0; uint32_t tx_adv_nsamples = 0;
double tx_adv_sec = 0.0f; // Transmission time advance to compensate for antenna->timestamp delay double tx_adv_sec = 0.0; // Transmission time advance to compensate for antenna->timestamp delay
bool tx_adv_auto = false; bool tx_adv_auto = false;
bool tx_adv_negative = false; bool tx_adv_negative = false;
bool is_initialized = false; bool is_initialized = false;
bool radio_is_streaming = false; bool radio_is_streaming = false;
bool continuous_tx = false; bool continuous_tx = false;
double freq_offset = 0.0f; double freq_offset = 0.0;
double cur_tx_srate = 0.0f; double cur_tx_srate = 0.0;
uint32_t nof_antennas = 0; uint32_t nof_antennas = 0;
uint32_t nof_channels = 0; uint32_t nof_channels = 0;
uint32_t nof_channels_x_dev = 0;
uint32_t nof_carriers = 0; uint32_t nof_carriers = 0;
std::vector<double> cur_tx_freqs = {}; std::vector<double> cur_tx_freqs = {};
@ -232,114 +122,79 @@ private:
///< shall be stopped ///< shall be stopped
// Define default values for known radios // 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 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 int blade_default_tx_adv_samples = 27;
constexpr static double blade_default_tx_adv_offset_sec = 1e-6; constexpr static double blade_default_tx_adv_offset_sec = 1e-6;
/** /**
* This class manages the mapping between logical and physical channels. * Get device calibrated transmit time in advanced seconds
* A physical channel in this class is a carrier index in the radio class, which * @param device_name actual device name
* has multiple antenna ports all tuned to the same frequency. * @return transmit time in advanced in seconds
*
* 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 { double get_dev_cal_tx_adv_sec(const std::string& device_name);
band_cfg band;
uint32_t carrier_idx;
} channel_cfg_t;
/** channel_mapping rx_channel_mapping = {}, tx_channel_mapping = {};
* Sets the channel configuration. If no channels are configured no physical channels can be allocated
* @param channels_
*/
void set_channels(const std::list<channel_cfg_t>& channels_) { available_channels = channels_; }
/** /**
* Finds an unused physical channel that supports the provided frequency and assigns it to logical channel * Helper method for opening a RF device
* logical_ch *
* @param logical_ch logical channel index * @param device_idx Device index
* @param freq Frequency (in Hz) that we want to receive/transmitt * @param device_name Device name
* @return true if a physical channel supporting this frequency was found or false otherwise * @param devive_args Device arguments
* @return it returns true if the device was opened successful, otherwise it returns false
*/ */
bool allocate_freq(const uint32_t& logical_ch, const float& freq); bool open_dev(const uint32_t& device_idx, const std::string& device_name, const std::string& devive_args);
/** /**
* Releases the allocation of a logical channel allowing to be used in the next call to allocate_freq * Helper method for transmitting over a single RF device. This function maps automatically the logical transmit
* @param logical_ch logical channel index * buffers to the physical RF buffers for the given device.
* @return false if logical_ch is not allocated, true otherwise *
* 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 release_freq(const uint32_t& logical_ch); bool tx_dev(const uint32_t& device_idx,
rf_buffer_interface& buffer,
const uint32_t& nof_samples_,
const srslte_timestamp_t& tx_time_);
/** /**
* Obtains the carrier index configured in set_channels() in the radio to which the logical channel logical_ch has * Helper method for receiving over a single RF device. This function maps automatically the logical receive buffers
* been mapped to * to the physical RF buffers for the given device.
* @param logical_ch logical channel index
* @return <0 if logical_ch is not allocated, true otherwise
* *
* @see channel_cfg_t * @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
*/ */
int get_carrier_idx(const uint32_t& logical_ch); bool rx_dev(const uint32_t& device_idx,
const rf_buffer_interface& buffer,
const uint32_t& nof_samples,
srslte_timestamp_t* rxd_time);
/** /**
* Checks if the channel has been allocated using allocate_freq() * Helper method for mapping logical channels into physical radio buffers.
* *
* @param logical_ch logical channel index * @param map Channel mapping, it can be either Tx or Rx mapping
* @return true if the channel is allocated, false otherwise * @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 is_allocated(const uint32_t& logical_ch);
private:
std::list<channel_cfg_t> available_channels = {};
std::map<uint32_t, channel_cfg_t> allocated_channels = {};
std::mutex mutex = {};
};
channel_mapping rx_channel_mapping = {}, tx_channel_mapping = {};
bool map_channels(channel_mapping& map, bool map_channels(channel_mapping& map,
uint32_t device_idx,
uint32_t sample_offset, uint32_t sample_offset,
const rf_buffer_interface& buffer, const rf_buffer_interface& buffer,
void* radio_buffers[SRSLTE_MAX_CHANNELS]); void* radio_buffers[SRSLTE_MAX_CHANNELS]);

@ -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<cf_t*, SRSLTE_MAX_CHANNELS> 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

@ -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 &timestamps[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<srslte_timestamp_t, SRSLTE_MAX_CHANNELS> timestamps = {};
};
} // namespace srslte
#endif // SRSLTE_RF_TIMESTAMP_H

@ -19,7 +19,7 @@
# #
if(RF_FOUND) 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) target_link_libraries(srslte_radio srslte_rf)
endif(RF_FOUND) endif(RF_FOUND)

@ -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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock(mutex);
return allocated_channels.count(logical_ch) > 0;
}
} // namespace srslte

@ -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/radio/radio.h"
#include "srslte/config.h"
#include <list> #include <list>
#include <string.h> #include <string>
#include <unistd.h> #include <unistd.h>
namespace srslte { 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); zeros = srslte_vec_cf_malloc(SRSLTE_SF_LEN_MAX);
srslte_vec_cf_zero(zeros, 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); zeros = srslte_vec_cf_malloc(SRSLTE_SF_LEN_MAX);
srslte_vec_cf_zero(zeros, 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<std::string>& 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_) int radio::init(const rf_args_t& args, phy_interface_radio* phy_)
{ {
phy = 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_tx_freqs.resize(nof_carriers);
cur_rx_freqs.resize(nof_carriers); cur_rx_freqs.resize(nof_carriers);
// Init and start Radio // Split multiple RF channels using `;` delimiter
char* device_args = nullptr; std::vector<std::string> device_args_list;
if (args.device_args != "auto") { split_string(args.device_args, ';', device_args_list);
device_args = (char*)args.device_args.c_str();
} // Add auto if list is empty
char* dev_name = nullptr; if (device_args_list.empty()) {
if (args.device_name != "auto") { device_args_list.emplace_back("auto");
dev_name = (char*)args.device_name.c_str();
} }
log_h->console("Opening %d channels in RF device=%s with args=%s\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, nof_channels,
dev_name ? dev_name : "default", device_args_list.size());
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");
return SRSLTE_ERROR; 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_start_of_burst = true;
is_initialized = true; is_initialized = true;
@ -139,15 +159,6 @@ int radio::init(const rf_args_t& args, phy_interface_radio* phy_)
// Frequency offset // Frequency offset
freq_offset = args.freq_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; return SRSLTE_SUCCESS;
} }
@ -163,13 +174,17 @@ void radio::stop()
zeros = NULL; zeros = NULL;
} }
if (is_initialized) { if (is_initialized) {
for (srslte_rf_t& rf_device : rf_devices) {
srslte_rf_close(&rf_device); srslte_rf_close(&rf_device);
} }
}
} }
void radio::reset() void radio::reset()
{ {
for (srslte_rf_t& rf_device : rf_devices) {
srslte_rf_stop_rx_stream(&rf_device); srslte_rf_stop_rx_stream(&rf_device);
}
radio_is_streaming = false; radio_is_streaming = false;
usleep(100000); usleep(100000);
} }
@ -198,72 +213,113 @@ bool radio::start_agc(bool tx_gain_same_rx)
if (!is_initialized) { if (!is_initialized) {
return false; return false;
} }
for (srslte_rf_t& rf_device : rf_devices) {
if (srslte_rf_start_gain_thread(&rf_device, tx_gain_same_rx)) { if (srslte_rf_start_gain_thread(&rf_device, tx_gain_same_rx)) {
ERROR("Error starting AGC Thread RF device\n"); ERROR("Error starting AGC Thread RF device\n");
return false; return false;
} }
}
return true; 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; bool ret = true;
if (!radio_is_streaming) { if (not radio_is_streaming) {
for (srslte_rf_t& rf_device : rf_devices) {
srslte_rf_start_rx_stream(&rf_device, false); srslte_rf_start_rx_stream(&rf_device, false);
}
radio_is_streaming = true; radio_is_streaming = true;
} }
time_t* full_secs = rxd_time ? &rxd_time->full_secs : NULL; for (uint32_t device_idx = 0; device_idx < (uint32_t)rf_devices.size(); device_idx++) {
double* frac_secs = rxd_time ? &rxd_time->frac_secs : NULL; 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] = {}; 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"); log_h->error("Mapping logical channels to physical channels for transmission\n");
return false; return false;
} }
if (srslte_rf_recv_with_time_multi(&rf_device, radio_buffers, nof_samples, true, full_secs, frac_secs) > 0) { int ret =
ret = true; srslte_rf_recv_with_time_multi(&rf_devices[device_idx], radio_buffers, nof_samples, true, full_secs, frac_secs);
} else { return ret > 0;
ret = false; }
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; 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) { srslte_rf_t* rf_device = &rf_devices[device_idx];
return;
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() char* dev_name = nullptr;
{ if (device_name != "auto") {
if (!is_initialized) { dev_name = (char*)device_name.c_str();
return 0.0f;
} }
return srslte_rf_get_rssi(&rf_device);
}
bool radio::has_rssi() log_h->console("Opening %d channels in RF device=%s with args=%s\n",
{ nof_channels_x_dev,
if (!is_initialized) { 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 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 nof_samples = nof_samples_;
uint32_t sample_offset = 0; uint32_t sample_offset = 0;
srslte_rf_t* rf_device = &rf_devices[device_idx];
// Return instantly if the radio module is not initialised // Return instantly if the radio module is not initialised
if (!is_initialized) { 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 // Calculate transmission overlap/gap if it is not start of the burst
if (not is_start_of_burst) { if (not is_start_of_burst) {
// Calculates transmission time overlap with previous transmission // 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); srslte_timestamp_sub(&ts_overlap, tx_time.full_secs, tx_time.frac_secs);
// Calculates number of overlap samples with previous transmission // Calculates number of overlap samples with previous transmission
@ -298,7 +354,7 @@ bool radio::tx(rf_buffer_interface& buffer, const uint32_t& nof_samples_, const
// Trim the first past_nsamples // Trim the first past_nsamples
sample_offset = (uint32_t)past_nsamples; // Sets an offset for moving first samples offset sample_offset = (uint32_t)past_nsamples; // Sets an offset for moving first samples offset
tx_time = end_of_burst_time; // Keeps same transmission time tx_time = end_of_burst_time[device_idx]; // Keeps same transmission time
nof_samples = nof_samples - past_nsamples; // Subtracts the number of trimmed samples nof_samples = nof_samples - past_nsamples; // Subtracts the number of trimmed samples
} else if (past_nsamples < 0) { } else if (past_nsamples < 0) {
@ -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); uint32_t nzeros = SRSLTE_MIN(gap_nsamples, SRSLTE_SF_LEN_MAX);
// Zeros transmission // Zeros transmission
int ret = srslte_rf_send_timed2( int ret = srslte_rf_send_timed2(rf_device,
&rf_device, zeros, nzeros, end_of_burst_time.full_secs, end_of_burst_time.frac_secs, false, false); zeros,
nzeros,
end_of_burst_time[device_idx].full_secs,
end_of_burst_time[device_idx].frac_secs,
false,
false);
if (ret < SRSLTE_SUCCESS) { if (ret < SRSLTE_SUCCESS) {
return false; return false;
} }
@ -323,25 +384,25 @@ bool radio::tx(rf_buffer_interface& buffer, const uint32_t& nof_samples_, const
gap_nsamples -= nzeros; gap_nsamples -= nzeros;
// Increase timestamp // 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 // Save possible end of burst time
srslte_timestamp_copy(&end_of_burst_time, &tx_time); srslte_timestamp_copy(&end_of_burst_time[device_idx], &tx_time);
srslte_timestamp_add(&end_of_burst_time, 0, (double)nof_samples / cur_tx_srate); srslte_timestamp_add(&end_of_burst_time[device_idx], 0, (double)nof_samples / cur_tx_srate);
void* radio_buffers[SRSLTE_MAX_CHANNELS] = {}; 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"); log_h->error("Mapping logical channels to physical channels for transmission\n");
return false; return false;
} }
int ret = srslte_rf_send_timed_multi( 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); 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;
return ret > SRSLTE_SUCCESS; return ret > SRSLTE_SUCCESS;
} }
@ -351,7 +412,10 @@ void radio::tx_end()
return; return;
} }
if (!is_start_of_burst) { 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; 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) { if ((physical_channel_idx + 1) * nof_antennas <= nof_channels) {
cur_rx_freqs[physical_channel_idx] = freq; cur_rx_freqs[physical_channel_idx] = freq;
for (uint32_t i = 0; i < nof_antennas; i++) { 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 { } else {
log_h->error("set_rx_freq: physical_channel_idx=%d for %d antennas exceeds maximum channels (%d)\n", 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) { if (!is_initialized) {
return; return;
} }
for (srslte_rf_t& rf_device : rf_devices) {
srslte_rf_set_rx_gain(&rf_device, gain); srslte_rf_set_rx_gain(&rf_device, gain);
}
} }
void radio::set_rx_gain_th(const float& 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) { if (!is_initialized) {
return; return;
} }
for (srslte_rf_t& rf_device : rf_devices) {
srslte_rf_set_rx_gain_th(&rf_device, gain); srslte_rf_set_rx_gain_th(&rf_device, gain);
}
} }
void radio::set_rx_srate(const double& srate) void radio::set_rx_srate(const double& srate)
@ -424,7 +498,9 @@ void radio::set_rx_srate(const double& srate)
if (!is_initialized) { if (!is_initialized) {
return; return;
} }
for (srslte_rf_t& rf_device : rf_devices) {
srslte_rf_set_rx_srate(&rf_device, srate); srslte_rf_set_rx_srate(&rf_device, srate);
}
} }
void radio::set_tx_freq(const uint32_t& carrier_idx, const double& freq) 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) { if ((physical_channel_idx + 1) * nof_antennas <= nof_channels) {
cur_tx_freqs[physical_channel_idx] = freq; cur_tx_freqs[physical_channel_idx] = freq;
for (uint32_t i = 0; i < nof_antennas; i++) { 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 { } else {
log_h->error("set_tx_freq: physical_channel_idx=%d for %d antennas exceeds maximum channels (%d)\n", 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) { if (!is_initialized) {
return; return;
} }
for (srslte_rf_t& rf_device : rf_devices) {
srslte_rf_set_tx_gain(&rf_device, gain); srslte_rf_set_tx_gain(&rf_device, gain);
}
} }
double radio::get_freq_offset() double radio::get_freq_offset()
@ -481,23 +565,18 @@ float radio::get_rx_gain()
if (!is_initialized) { if (!is_initialized) {
return 0.0f; 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; int nsamples = 0;
/* Set time advance for each known device if in auto mode */ /* Set time advance for each known device if in auto mode */
if (tx_adv_auto) { if (tx_adv_auto) {
/* This values have been calibrated using the prach_test_usrp tool in srsLTE */ /* 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); double srate_khz = round(cur_tx_srate / 1e3);
if (srate_khz == 1.92e3) { if (srate_khz == 1.92e3) {
@ -523,10 +602,10 @@ void radio::set_tx_srate(const double& srate)
log_h->console( log_h->console(
"\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", "\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n",
cur_tx_srate); 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); double srate_khz = round(cur_tx_srate / 1e3);
if (srate_khz == 1.92e3) { if (srate_khz == 1.92e3) {
nsamples = 14; // estimated nsamples = 14; // estimated
@ -545,10 +624,10 @@ void radio::set_tx_srate(const double& srate)
log_h->console( log_h->console(
"\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", "\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n",
cur_tx_srate); 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); double srate_khz = round(cur_tx_srate / 1e3);
if (srate_khz == 1.92e3) { if (srate_khz == 1.92e3) {
nsamples = 28; nsamples = 28;
@ -567,14 +646,14 @@ void radio::set_tx_srate(const double& srate)
log_h->console( log_h->console(
"\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", "\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n",
cur_tx_srate); 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 // In X300 TX/RX offset is independent of sampling rate
nsamples = 45; nsamples = 45;
} else if (!strcmp(srslte_rf_name(&rf_device), "bladerf")) { } else if (device_name == "bladerf") {
double srate_khz = round(cur_tx_srate / 1e3); double srate_khz = round(cur_tx_srate / 1e3);
if (srate_khz == 1.92e3) { if (srate_khz == 1.92e3) {
@ -594,9 +673,9 @@ void radio::set_tx_srate(const double& srate)
log_h->console( log_h->console(
"\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", "\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n",
cur_tx_srate); 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; nsamples = 0;
} }
} else { } else {
@ -605,7 +684,22 @@ void radio::set_tx_srate(const double& srate)
} }
// Calculate TX advance in seconds from samples and sampling rate // 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) { if (tx_adv_sec < 0) {
tx_adv_sec *= -1; tx_adv_sec *= -1;
tx_adv_negative = true; tx_adv_negative = true;
@ -615,9 +709,9 @@ void radio::set_tx_srate(const double& srate)
srslte_rf_info_t* radio::get_info() srslte_rf_info_t* radio::get_info()
{ {
if (!is_initialized) { 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) 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, bool radio::map_channels(channel_mapping& map,
uint32_t device_idx,
uint32_t sample_offset, uint32_t sample_offset,
const rf_buffer_interface& buffer, const rf_buffer_interface& buffer,
void* radio_buffers[SRSLTE_MAX_CHANNELS]) void* radio_buffers[SRSLTE_MAX_CHANNELS])
@ -679,10 +774,30 @@ 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 // 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 // accepts up to SRSLTE_MAX_CHANNELS buffers
for (uint32_t i = 0; i < nof_carriers; i++) { for (uint32_t i = 0; i < nof_carriers; i++) {
if (map.is_allocated(i)) { // Skip if not allocated
if (not map.is_allocated(i)) {
continue;
}
// Get physical channel
uint32_t physical_idx = map.get_carrier_idx(i); uint32_t physical_idx = map.get_carrier_idx(i);
// Map each antenna
for (uint32_t j = 0; j < nof_antennas; j++) { for (uint32_t j = 0; j < nof_antennas; j++) {
if (physical_idx * nof_antennas + j < SRSLTE_MAX_CHANNELS) { // 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;
// 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); cf_t* ptr = buffer.get(i, j, nof_antennas);
// Add sample offset only if it is a valid pointer // Add sample offset only if it is a valid pointer
@ -690,10 +805,7 @@ bool radio::map_channels(channel_mapping& map,
ptr += sample_offset; ptr += sample_offset;
} }
radio_buffers[physical_idx * nof_antennas + j] = ptr; radio_buffers[rf_channel_idx] = ptr;
} else {
return false;
}
} }
} }
} }
@ -733,56 +845,4 @@ bool radio::config_rf_channels(const rf_args_t& args)
return true; return true;
} }
/***
* Carrier mapping class
*/
bool radio::channel_mapping::allocate_freq(const uint32_t& logical_ch, const float& freq)
{
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock(mutex);
return allocated_channels.count(logical_ch) > 0;
}
} // namespace srslte } // namespace srslte

@ -266,8 +266,8 @@ private:
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
int ret = SRSLTE_ERROR; int ret = SRSLTE_ERROR;
radio* radio_h[SRSLTE_MAX_RADIOS] = {NULL}; radio* radio_h[SRSLTE_MAX_RADIOS] = {nullptr};
srslte_timestamp_t ts_prev[SRSLTE_MAX_RADIOS], ts_rx[SRSLTE_MAX_RADIOS], ts_tx; srslte::rf_timestamp_t ts_prev[SRSLTE_MAX_RADIOS], ts_rx[SRSLTE_MAX_RADIOS], ts_tx;
uint32_t nof_gaps = 0; uint32_t nof_gaps = 0;
char filename[256] = {}; char filename[256] = {};
srslte_filesink_t filesink[SRSLTE_MAX_RADIOS] = {}; srslte_filesink_t filesink[SRSLTE_MAX_RADIOS] = {};
@ -275,10 +275,6 @@ int main(int argc, char** argv)
srslte_agc_t agc[SRSLTE_MAX_RADIOS] = {}; srslte_agc_t agc[SRSLTE_MAX_RADIOS] = {};
phy_dummy phy; phy_dummy phy;
bzero(&ts_prev, sizeof(ts_prev));
bzero(&ts_rx, sizeof(ts_rx));
bzero(&ts_tx, sizeof(ts_tx));
rf_buffer_t rf_buffers[SRSLTE_MAX_RADIOS] = {}; rf_buffer_t rf_buffers[SRSLTE_MAX_RADIOS] = {};
float delay_idx[SRSLTE_MAX_RADIOS] = {0}; float delay_idx[SRSLTE_MAX_RADIOS] = {0};
@ -413,7 +409,7 @@ int main(int argc, char** argv)
// receive each radio // receive each radio
for (uint32_t r = 0; r < nof_radios; r++) { 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 // run agc
@ -426,8 +422,8 @@ int main(int argc, char** argv)
// Transmit // Transmit
if (tx_enable) { if (tx_enable) {
for (uint32_t r = 0; r < nof_radios; r++) { for (uint32_t r = 0; r < nof_radios; r++) {
srslte_timestamp_copy(&ts_tx, &ts_rx[r]); ts_tx.copy(ts_rx[r]);
srslte_timestamp_add(&ts_tx, 0, 0.004); ts_tx.add(0.004);
radio_h[r]->tx(rf_buffers[r], frame_size, ts_tx); radio_h[r]->tx(rf_buffers[r], frame_size, ts_tx);
} }
} }
@ -485,20 +481,19 @@ int main(int argc, char** argv)
if (i != 0) { if (i != 0) {
for (uint32_t r = 0; r < nof_radios; r++) { for (uint32_t r = 0; r < nof_radios; r++) {
srslte_timestamp_t ts_diff; srslte_timestamp_t ts_diff;
bzero(&ts_diff, sizeof(ts_diff));
srslte_timestamp_copy(&ts_diff, &ts_rx[r]); srslte_timestamp_copy(&ts_diff, ts_rx[r].get_ptr(0));
srslte_timestamp_sub(&ts_diff, ts_prev[r].full_secs, ts_prev[r].frac_secs); srslte_timestamp_sub(&ts_diff, ts_prev[r].get_ptr(0)->full_secs, ts_prev[r].get_ptr(0)->frac_secs);
gap = (int)round(srslte_timestamp_real(&ts_diff) * srate) - frame_size; 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", INFO("Timestamp gap (%d samples) detected! Frame %d/%d. ts=%.9f+%.9f=%.9f\n",
gap, gap,
i + 1, i + 1,
nof_frames, 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_diff),
srslte_timestamp_real(&ts_rx[r])); srslte_timestamp_real(ts_rx[r].get_ptr(0)));
nof_gaps++; nof_gaps++;
} }
} }
@ -506,7 +501,7 @@ int main(int argc, char** argv)
/* Save timestamp */ /* Save timestamp */
for (uint32_t r = 0; r < nof_radios; r++) { 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; nof_samples -= frame_size;

@ -63,7 +63,7 @@ public:
* @param nof_samples number of samples to transmit * @param nof_samples number of samples to transmit
* @param tx_time timestamp to transmit samples * @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 // Common objects
phy_args_t params = {}; phy_args_t params = {};

@ -39,7 +39,7 @@ public:
void init(phy_common* phy, srslte::log* log_h); void init(phy_common* phy, srslte::log* log_h);
cf_t* get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx); 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); int add_rnti(uint16_t rnti, uint32_t cc_idx);
void rem_rnti(uint16_t rnti); void rem_rnti(uint16_t rnti);
@ -68,7 +68,7 @@ private:
uint32_t tti_rx = 0, tti_tx_dl = 0, tti_tx_ul = 0; 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 t_rx = 0, t_tx_dl = 0, t_tx_ul = 0;
uint32_t tx_worker_cnt = 0; uint32_t tx_worker_cnt = 0;
srslte_timestamp_t tx_time = {}; srslte::rf_timestamp_t tx_time = {};
std::vector<std::unique_ptr<cc_worker> > cc_workers; std::vector<std::unique_ptr<cc_worker> > cc_workers;

@ -127,14 +127,14 @@ void phy_common::set_ul_grants(uint32_t tti, const stack_interface_phy_lte::ul_s
void phy_common::worker_end(void* tx_sem_id, void phy_common::worker_end(void* tx_sem_id,
srslte::rf_buffer_t& buffer, srslte::rf_buffer_t& buffer,
uint32_t nof_samples, uint32_t nof_samples,
srslte_timestamp_t tx_time) srslte::rf_timestamp_t& tx_time)
{ {
// Wait for the green light to transmit in the current TTI // Wait for the green light to transmit in the current TTI
semaphore.wait(tx_sem_id); semaphore.wait(tx_sem_id);
// Run DL channel emulator if created // Run DL channel emulator if created
if (dl_channel) { 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 // Always transmit on single radio

@ -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); 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_rx = tti_;
tti_tx_dl = TTI_ADD(tti_rx, FDD_HARQ_DELAY_UL_MS); 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); t_tx_ul = TTIMOD(tti_tx_ul);
tx_worker_cnt = tx_worker_cnt_; tx_worker_cnt = tx_worker_cnt_;
srslte_timestamp_copy(&tx_time, &tx_time_); tx_time.copy(tx_time_);
for (auto& w : cc_workers) { for (auto& w : cc_workers) {
w->set_tti(tti_); w->set_tti(tti_);

@ -89,8 +89,7 @@ void txrx::run_thread()
{ {
sf_worker* worker = nullptr; sf_worker* worker = nullptr;
srslte::rf_buffer_t buffer = {}; srslte::rf_buffer_t buffer = {};
srslte_timestamp_t rx_time = {}; srslte::rf_timestamp_t timestamp = {};
srslte_timestamp_t tx_time = {};
uint32_t sf_len = SRSLTE_SF_LEN_PRB(worker_com->get_nof_prb(0)); 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)); 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) { 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 */ // Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time
srslte_timestamp_copy(&tx_time, &rx_time); timestamp.add(FDD_HARQ_DELAY_UL_MS * 1e-3);
srslte_timestamp_add(&tx_time, 0, FDD_HARQ_DELAY_UL_MS * 1e-3);
Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n",
tti, tti,
tx_worker_cnt, tx_worker_cnt,
tx_time.full_secs, timestamp.get(0).full_secs,
tx_time.frac_secs, timestamp.get(0).frac_secs,
worker->get_id()); 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; tx_worker_cnt = (tx_worker_cnt + 1) % nof_workers;
// Trigger phy worker execution // Trigger phy worker execution

@ -80,7 +80,7 @@ private:
srslte::log_filter log_h; srslte::log_filter log_h;
std::vector<srslte_ringbuffer_t*> ringbuffers_tx; std::vector<srslte_ringbuffer_t*> ringbuffers_tx;
std::vector<srslte_ringbuffer_t*> ringbuffers_rx; std::vector<srslte_ringbuffer_t*> ringbuffers_rx;
srslte_timestamp_t ts_rx = {}; srslte::rf_timestamp_t ts_rx = {};
double rx_srate = 0.0; double rx_srate = 0.0;
bool running = true; 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; int err = SRSLTE_SUCCESS;
@ -207,7 +209,9 @@ public:
return err >= SRSLTE_SUCCESS; return err >= SRSLTE_SUCCESS;
} }
void tx_end() 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
{ {
int err = SRSLTE_SUCCESS; int err = SRSLTE_SUCCESS;
@ -224,13 +228,11 @@ public:
} }
// Copy new timestamp // Copy new timestamp
if (rxd_time) { rxd_time = ts_rx;
*rxd_time = ts_rx;
}
// Copy new timestamp // Copy new timestamp
if (std::isnormal(rx_srate)) { if (std::isnormal(rx_srate)) {
srslte_timestamp_add(&ts_rx, 0, static_cast<double>(nof_samples) / rx_srate); ts_rx.add(static_cast<double>(nof_samples) / rx_srate);
} }
// Notify Rx // Notify Rx

@ -138,8 +138,11 @@ public:
srslte_pdsch_ack_resource_t resource); 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); bool get_dl_pending_ack(srslte_ul_sf_cfg_t* sf, uint32_t cc_idx, srslte_pdsch_ack_cc_t* ack);
void void worker_end(void* h,
worker_end(void* h, bool tx_enable, srslte::rf_buffer_t& buffer, uint32_t nof_samples, srslte_timestamp_t tx_time); 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_cell(const srslte_cell_t& c);
void set_nof_workers(uint32_t nof_workers); void set_nof_workers(uint32_t nof_workers);

@ -55,7 +55,7 @@ public:
cf_t* get_buffer(uint32_t cc_idx, uint32_t antenna_idx); cf_t* get_buffer(uint32_t cc_idx, uint32_t antenna_idx);
uint32_t get_buffer_len(); uint32_t get_buffer_len();
void set_tti(uint32_t tti); 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_prach(cf_t* prach_ptr, float prach_power);
void set_cfo(const uint32_t& cc_idx, float cfo); void set_cfo(const uint32_t& cc_idx, float cfo);
@ -110,7 +110,7 @@ private:
float prach_power = 0; float prach_power = 0;
uint32_t tti = 0; uint32_t tti = 0;
srslte_timestamp_t tx_time = {}; srslte::rf_timestamp_t tx_time = {};
uint32_t rssi_read_cnt = 0; uint32_t rssi_read_cnt = 0;
}; };

@ -153,6 +153,7 @@ private:
bool running = false; bool running = false;
bool is_overflow = 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 bool forced_rx_time_init = true; // Rx time sync after first receive from radio
// Objects for internal use // Objects for internal use

@ -47,10 +47,7 @@ phy_common::phy_common()
reset(); reset();
} }
phy_common::~phy_common() phy_common::~phy_common() = default;
{
}
void phy_common::set_nof_workers(uint32_t nof_workers_) void phy_common::set_nof_workers(uint32_t nof_workers_)
{ {
@ -528,19 +525,19 @@ void phy_common::worker_end(void* tx_sem_id,
bool tx_enable, bool tx_enable,
srslte::rf_buffer_t& buffer, srslte::rf_buffer_t& buffer,
uint32_t nof_samples, uint32_t nof_samples,
srslte_timestamp_t tx_time) srslte::rf_timestamp_t& tx_time)
{ {
// Wait for the green light to transmit in the current TTI // Wait for the green light to transmit in the current TTI
semaphore.wait(tx_sem_id); semaphore.wait(tx_sem_id);
// Add Time Alignment // Add Time Alignment
srslte_timestamp_sub(&tx_time, 0, ta.get_sec()); tx_time.sub((double)ta.get_sec());
// For each radio, transmit // For each radio, transmit
if (tx_enable && !srslte_timestamp_iszero(&tx_time)) { if (tx_enable) {
if (ul_channel) { 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); radio_h->tx(buffer, nof_samples, tx_time);
@ -552,9 +549,9 @@ void phy_common::worker_end(void* tx_sem_id,
} else { } else {
if (!radio_h->get_is_start_of_burst()) { 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); 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); radio_h->tx(zeros_multi, nof_samples, tx_time);

@ -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_) void sf_worker::set_prach(cf_t* prach_ptr_, float prach_power_)

@ -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); 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 // 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_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 // Advance/reset prach subframe pointer
if (prach_ptr) { 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) int sync::radio_recv_fnc(srslte::rf_buffer_t& data, uint32_t nsamples, srslte_timestamp_t* rx_time)
{ {
srslte_timestamp_t 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;
// Use local timestamp if timestamp is not provided // Receive
if (!rx_time) { if (radio_h->rx_now(data, nsamples, rf_timestamp)) {
rx_time = &ts; srslte_timestamp_t dummy_flat_ts = {};
// Load flat timestamp
if (rx_time == nullptr) {
rx_time = &dummy_flat_ts;
} }
*rx_time = rf_timestamp.get(0);
// Receive
if (radio_h->rx_now(data, nsamples, rx_time)) {
// check timestamp reset // check timestamp reset
if (forced_rx_time_init || srslte_timestamp_iszero(&tti_ts) || srslte_timestamp_compare(rx_time, &tti_ts) < 0) { 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) { if (srslte_timestamp_compare(rx_time, &tti_ts) < 0) {

@ -142,7 +142,7 @@ public:
srslte_softbuffer_tx_t** softbuffer_tx, srslte_softbuffer_tx_t** softbuffer_tx,
uint8_t** data_tx, uint8_t** data_tx,
cf_t* baseband_buffer, cf_t* baseband_buffer,
const srslte_timestamp_t& ts) const srslte::rf_timestamp_t& ts)
{ {
int ret = SRSLTE_SUCCESS; int ret = SRSLTE_SUCCESS;
@ -189,7 +189,7 @@ public:
srslte_enb_dl_gen_signal(&enb_dl); srslte_enb_dl_gen_signal(&enb_dl);
// Apply channel // 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 // Combine Tx ports
for (uint32_t i = 1; i < enb_dl.cell.nof_ports; i++) { 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 // Common for simulation and over-the-air
cf_t* baseband_buffer = srslte_vec_cf_malloc(SRSLTE_SF_LEN_MAX); 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; srsue::scell::intra_measure intra_measure;
srslte::log_filter logger("intra_measure"); srslte::log_filter logger("intra_measure");
dummy_rrc rrc; dummy_rrc rrc;
@ -567,7 +567,7 @@ int main(int argc, char** argv)
if (radio) { if (radio) {
// Receive radio // Receive radio
srslte::rf_buffer_t radio_buffer(baseband_buffer); 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 { } else {
// Run eNb simulator // Run eNb simulator
bool put_pdsch = serving_cell_pdsch_enable; bool put_pdsch = serving_cell_pdsch_enable;
@ -594,9 +594,9 @@ int main(int argc, char** argv)
dci.tb_cw_swap = false; dci.tb_cw_swap = false;
dci.pconf = false; dci.pconf = false;
dci.power_offset = false; dci.power_offset = false;
dci.tpc_pucch = false; dci.tpc_pucch = 0;
dci.ra_preamble = false; dci.ra_preamble = 0;
dci.ra_mask_idx = false; dci.ra_mask_idx = 0;
dci.srs_request = false; dci.srs_request = false;
dci.srs_request_present = false; dci.srs_request_present = false;
dci.cif_present = false; dci.cif_present = false;
@ -653,7 +653,7 @@ int main(int argc, char** argv)
} }
// Increase Time counter // Increase Time counter
srslte_timestamp_add(&ts, 0, 0.001f); ts.add(0.001);
// Give data to intra measure component // Give data to intra measure component
intra_measure.write(sf_idx % 10240, baseband_buffer, SRSLTE_SF_LEN_PRB(cell_base.nof_prb)); intra_measure.write(sf_idx % 10240, baseband_buffer, SRSLTE_SF_LEN_PRB(cell_base.nof_prb));

@ -131,7 +131,7 @@ private:
std::mutex mutex; std::mutex mutex;
std::condition_variable cvar; std::condition_variable cvar;
srslte_rf_info_t rf_info = {}; srslte_rf_info_t rf_info = {};
srslte_timestamp_t tx_last_tx = {}; srslte::rf_timestamp_t tx_last_tx = {};
uint32_t count_late = 0; uint32_t count_late = 0;
CALLBACK(rx_now) CALLBACK(rx_now)
@ -191,8 +191,9 @@ private:
uint32_t get_count_late() { return count_late; } uint32_t get_count_late() { return count_late; }
bool bool tx(srslte::rf_buffer_interface& buffer,
tx(srslte::rf_buffer_interface& buffer, const uint32_t& nof_samples, const srslte_timestamp_t& tx_time) override const uint32_t& nof_samples,
const srslte::rf_timestamp_interface& tx_time) override
{ {
bool ret = true; bool ret = true;
notify_tx(); notify_tx();
@ -201,18 +202,20 @@ private:
if (!std::isnormal(tx_srate)) { if (!std::isnormal(tx_srate)) {
count_late++; 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; ret = false;
} }
tx_last_tx = tx_time; tx_last_tx.copy(tx_time);
srslte_timestamp_add(&tx_last_tx, 0, (double)nof_samples / (double)tx_srate); tx_last_tx.add((double)nof_samples / (double)tx_srate);
return ret; return ret;
} }
void release_freq(const uint32_t& carrier_idx) override{}; void release_freq(const uint32_t& carrier_idx) override{};
void tx_end() 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(); notify_rx_now();
@ -255,9 +258,7 @@ private:
} }
// Set Rx timestamp // Set Rx timestamp
if (rxd_time) { srslte_timestamp_init_uint64(rxd_time.get_ptr(0), rx_timestamp, (double)base_srate);
srslte_timestamp_init_uint64(rxd_time, rx_timestamp, (double)base_srate);
}
// Update timestamp // Update timestamp
rx_timestamp += base_nsamples; rx_timestamp += base_nsamples;

Loading…
Cancel
Save