mirror of https://github.com/PentHertz/srsLTE.git
Refactored radio class for acommodating multiple RF devices
This commit is contained in:
parent
e2146e90ad
commit
89b24b54e5
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 "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 <list>
|
||||
#include <string>
|
||||
|
||||
#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<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
|
||||
*
|
||||
|
@ -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;
|
||||
std::vector<srslte_rf_t> rf_devices = {};
|
||||
std::vector<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;
|
||||
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<double> cur_tx_freqs = {};
|
||||
std::vector<double> 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;
|
||||
|
||||
/**
|
||||
* 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 = {};
|
||||
};
|
||||
double get_dev_cal_tx_adv_sec(const std::string& device_name);
|
||||
|
||||
channel_mapping rx_channel_mapping = {}, tx_channel_mapping = {};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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_);
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
|
|
|
@ -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 ×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<srslte_timestamp_t, SRSLTE_MAX_CHANNELS> timestamps = {};
|
||||
};
|
||||
} // namespace srslte
|
||||
|
||||
#endif // SRSLTE_RF_TIMESTAMP_H
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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/config.h"
|
||||
#include <list>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
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<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_)
|
||||
{
|
||||
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();
|
||||
// Split multiple RF channels using `;` delimiter
|
||||
std::vector<std::string> 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");
|
||||
}
|
||||
char* dev_name = nullptr;
|
||||
if (args.device_name != "auto") {
|
||||
dev_name = (char*)args.device_name.c_str();
|
||||
}
|
||||
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;
|
||||
|
||||
void* radio_buffers[SRSLTE_MAX_CHANNELS] = {};
|
||||
if (!map_channels(rx_channel_mapping, 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;
|
||||
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;
|
||||
}
|
||||
|
||||
void radio::get_time(srslte_timestamp_t* now)
|
||||
{
|
||||
if (!is_initialized) {
|
||||
return;
|
||||
}
|
||||
srslte_rf_get_time(&rf_device, &now->full_secs, &now->frac_secs);
|
||||
}
|
||||
|
||||
float radio::get_rssi()
|
||||
{
|
||||
if (!is_initialized) {
|
||||
return 0.0f;
|
||||
}
|
||||
return srslte_rf_get_rssi(&rf_device);
|
||||
}
|
||||
|
||||
bool radio::has_rssi()
|
||||
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;
|
||||
}
|
||||
return srslte_rf_has_rssi(&rf_device);
|
||||
|
||||
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 (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;
|
||||
}
|
||||
|
||||
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 srslte_timestamp_t& tx_time_)
|
||||
bool radio::tx(rf_buffer_interface& buffer, const uint32_t& nof_samples, const rf_timestamp_interface& tx_time)
|
||||
{
|
||||
uint32_t nof_samples = nof_samples_;
|
||||
uint32_t sample_offset = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
bool radio::open_dev(const uint32_t& device_idx, const std::string& device_name, const std::string& devive_args)
|
||||
{
|
||||
srslte_rf_t* rf_device = &rf_devices[device_idx];
|
||||
|
||||
char* dev_args = nullptr;
|
||||
if (devive_args != "auto") {
|
||||
dev_args = (char*)devive_args.c_str();
|
||||
}
|
||||
|
||||
char* dev_name = nullptr;
|
||||
if (device_name != "auto") {
|
||||
dev_name = (char*)device_name.c_str();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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_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;
|
||||
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);
|
||||
// Skip if not allocated
|
||||
if (not map.is_allocated(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add sample offset only if it is a valid pointer
|
||||
if (ptr != nullptr) {
|
||||
ptr += sample_offset;
|
||||
}
|
||||
// Get physical channel
|
||||
uint32_t physical_idx = map.get_carrier_idx(i);
|
||||
|
||||
radio_buffers[physical_idx * nof_antennas + j] = ptr;
|
||||
} else {
|
||||
return false;
|
||||
// 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;
|
||||
|
||||
// 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<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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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<std::unique_ptr<cc_worker> > cc_workers;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -80,7 +80,7 @@ private:
|
|||
srslte::log_filter log_h;
|
||||
std::vector<srslte_ringbuffer_t*> ringbuffers_tx;
|
||||
std::vector<srslte_ringbuffer_t*> 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<double>(nof_samples) / rx_srate);
|
||||
ts_rx.add(static_cast<double>(nof_samples) / rx_srate);
|
||||
}
|
||||
|
||||
// Notify Rx
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue