mirror of https://github.com/pvnis/srsRAN_4G.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
9.6 KiB
C++
298 lines
9.6 KiB
C++
/**
|
|
*
|
|
* \section COPYRIGHT
|
|
*
|
|
* Copyright 2013-2021 Software Radio Systems Limited
|
|
*
|
|
* By using this file, you agree to the terms and conditions set
|
|
* forth in the LICENSE file which can be found at the top level of
|
|
* the distribution.
|
|
*
|
|
*/
|
|
|
|
#include "channel_mapping.h"
|
|
#include "radio_metrics.h"
|
|
#include "rf_buffer.h"
|
|
#include "rf_timestamp.h"
|
|
#include "srsran/common/interfaces_common.h"
|
|
#include "srsran/interfaces/radio_interfaces.h"
|
|
#include "srsran/phy/resampling/resampler.h"
|
|
#include "srsran/phy/rf/rf.h"
|
|
#include "srsran/radio/radio_base.h"
|
|
#include "srsran/srslog/srslog.h"
|
|
#include "srsran/srsran.h"
|
|
|
|
#include <condition_variable>
|
|
#include <list>
|
|
#include <string>
|
|
|
|
#ifndef SRSRAN_RADIO_DUMMY_H
|
|
#define SRSRAN_RADIO_DUMMY_H
|
|
|
|
namespace srsran {
|
|
|
|
/**
|
|
* Implementation of radio dummy for the PHY testing
|
|
*
|
|
* It uses ringbuffers from srsRAN library to emulate baseband transmission and reception. The current implementation
|
|
* does not support dynamic sampling rates, gains and frequencies.
|
|
*/
|
|
class radio_dummy : public srsran::radio_base, public srsran::radio_interface_phy
|
|
{
|
|
private:
|
|
static const uint32_t TEMP_BUFFER_SZ = SRSRAN_SF_LEN_MAX * SRSRAN_NOF_SF_X_FRAME;
|
|
srslog::basic_logger& logger;
|
|
std::vector<srsran_ringbuffer_t> rx_ring_buffers;
|
|
std::vector<srsran_ringbuffer_t> tx_ring_buffers;
|
|
std::mutex tx_mutex;
|
|
std::atomic<double> srate_hz = {0.0f};
|
|
std::atomic<float> rx_gain = {1.0f};
|
|
std::atomic<float> tx_gain = {1.0f};
|
|
cf_t* temp_buffer = nullptr;
|
|
uint64_t rx_timestamp = 0;
|
|
uint64_t tx_timestamp = 0;
|
|
srsran_rf_info_t rf_info = {};
|
|
std::atomic<bool> is_initialised = {false};
|
|
std::atomic<bool> quit = {false};
|
|
|
|
void write_ring_buffers(std::vector<srsran_ringbuffer_t>& buffers, cf_t** buffer, uint32_t nsamples)
|
|
{
|
|
for (uint32_t i = 0; i < buffers.size(); i++) {
|
|
int ret = SRSRAN_SUCCESS;
|
|
do {
|
|
if (ret != SRSRAN_SUCCESS) {
|
|
logger.error("Ring buffer write failed (full). Trying again.");
|
|
}
|
|
ret = srsran_ringbuffer_write_timed(&buffers[i], buffer[i], (int)(sizeof(cf_t) * nsamples), 1000);
|
|
} while (ret == SRSRAN_ERROR_TIMEOUT and not quit);
|
|
}
|
|
}
|
|
|
|
void read_ring_buffers(std::vector<srsran_ringbuffer_t>& buffers, cf_t** buffer, uint32_t nsamples)
|
|
{
|
|
for (uint32_t i = 0; i < buffers.size(); i++) {
|
|
int ret = SRSRAN_SUCCESS;
|
|
do {
|
|
if (ret != SRSRAN_SUCCESS) {
|
|
logger.error("Ring buffer read failed. Trying again.");
|
|
}
|
|
ret = srsran_ringbuffer_read_timed(&buffers[i], buffer[i], (int)(sizeof(cf_t) * nsamples), 1000);
|
|
} while (ret == SRSRAN_ERROR_TIMEOUT and not quit);
|
|
}
|
|
}
|
|
|
|
void write_zeros_ring_buffers(std::vector<srsran_ringbuffer_t>& buffers, uint32_t nsamples)
|
|
{
|
|
uint32_t n = SRSRAN_MIN(nsamples, TEMP_BUFFER_SZ);
|
|
srsran_vec_cf_zero(temp_buffer, n);
|
|
|
|
std::array<cf_t*, SRSRAN_MAX_CHANNELS> zero_buffer_pointers = {};
|
|
for (cf_t*& ptr : zero_buffer_pointers) {
|
|
ptr = temp_buffer;
|
|
}
|
|
|
|
while (nsamples > 0) {
|
|
// Get new number of samples
|
|
n = SRSRAN_MIN(nsamples, TEMP_BUFFER_SZ);
|
|
|
|
// Write zeros in the buffers
|
|
write_ring_buffers(buffers, zero_buffer_pointers.data(), n);
|
|
|
|
nsamples -= n;
|
|
}
|
|
}
|
|
|
|
void advance_tx_timestamp(uint64_t ts, bool round_sf = false)
|
|
{
|
|
std::lock_guard<std::mutex> lock(tx_mutex);
|
|
|
|
// Make sure new timestamp has not passed
|
|
if (ts < tx_timestamp) {
|
|
return;
|
|
}
|
|
|
|
// Calculate transmission gap
|
|
uint32_t tx_gap = (uint32_t)(ts - tx_timestamp);
|
|
|
|
// Round gap to subframe size
|
|
if (round_sf) {
|
|
uint64_t sf_sz = (uint64_t)(srate_hz / 1e3);
|
|
tx_gap = sf_sz * SRSRAN_CEIL(tx_gap, sf_sz);
|
|
}
|
|
|
|
// Skip zeros if there is no gap
|
|
if (tx_gap == 0) {
|
|
return;
|
|
}
|
|
|
|
// Write zeros in tx ring buffer
|
|
write_zeros_ring_buffers(tx_ring_buffers, tx_gap);
|
|
|
|
// Update new transmit timestamp
|
|
tx_timestamp += tx_gap;
|
|
}
|
|
|
|
public:
|
|
radio_dummy() : logger(srslog::fetch_basic_logger("RF", false)) {}
|
|
|
|
~radio_dummy()
|
|
{
|
|
for (auto& rb : rx_ring_buffers) {
|
|
srsran_ringbuffer_free(&rb);
|
|
}
|
|
for (auto& rb : tx_ring_buffers) {
|
|
srsran_ringbuffer_free(&rb);
|
|
}
|
|
if (temp_buffer) {
|
|
free(temp_buffer);
|
|
}
|
|
}
|
|
|
|
std::string get_type() override { return "dummy"; }
|
|
int init(const rf_args_t& args_, phy_interface_radio* phy_) override
|
|
{
|
|
// Set logger level
|
|
logger.set_level(srslog::str_to_basic_level(args_.log_level));
|
|
|
|
// Get base sampling rate and assert the value is valid
|
|
srate_hz = args_.srate_hz;
|
|
if (not std::isnormal(srate_hz)) {
|
|
logger.error("A valid sampling rate is missing");
|
|
return SRSRAN_ERROR;
|
|
}
|
|
|
|
// Create receiver ring buffers
|
|
rx_ring_buffers.resize(args_.nof_carriers * (size_t)args_.nof_antennas);
|
|
for (auto& rb : rx_ring_buffers) {
|
|
if (srsran_ringbuffer_init(&rb, (int)sizeof(cf_t) * TEMP_BUFFER_SZ) != SRSRAN_SUCCESS) {
|
|
perror("init softbuffer");
|
|
}
|
|
}
|
|
|
|
// Create transmitter ring buffers
|
|
tx_ring_buffers.resize(args_.nof_carriers * (size_t)args_.nof_antennas);
|
|
for (auto& rb : tx_ring_buffers) {
|
|
if (srsran_ringbuffer_init(&rb, (int)sizeof(cf_t) * TEMP_BUFFER_SZ) != SRSRAN_SUCCESS) {
|
|
perror("init softbuffer");
|
|
}
|
|
}
|
|
|
|
// Create temporal buffer
|
|
temp_buffer = srsran_vec_cf_malloc(TEMP_BUFFER_SZ);
|
|
if (!temp_buffer) {
|
|
perror("malloc");
|
|
}
|
|
|
|
// Set RF Info (in dB)
|
|
rf_info.min_rx_gain = 0.0f;
|
|
rf_info.max_rx_gain = 90.0f;
|
|
rf_info.min_tx_gain = 0.0f;
|
|
rf_info.max_tx_gain = 90.0f;
|
|
|
|
// Finally, the radio is initialised
|
|
is_initialised = true;
|
|
|
|
return SRSRAN_SUCCESS;
|
|
}
|
|
void stop() override { quit = true; }
|
|
bool get_metrics(rf_metrics_t* metrics) override { return false; }
|
|
|
|
void set_loglevel(std::string& str) { logger.set_level(srslog::str_to_basic_level(str)); }
|
|
|
|
void write_rx(cf_t** buffer, uint32_t nsamples) { write_ring_buffers(rx_ring_buffers, buffer, nsamples); }
|
|
|
|
void read_tx(cf_t** buffer, uint32_t nsamples) { read_ring_buffers(tx_ring_buffers, buffer, nsamples); }
|
|
|
|
bool tx(srsran::rf_buffer_interface& buffer, const srsran::rf_timestamp_interface& tx_time) override
|
|
{
|
|
bool ret = true;
|
|
|
|
// Convert timestamp to samples
|
|
uint64_t tx_time_n = srsran_timestamp_uint64(&tx_time.get(0), srate_hz);
|
|
|
|
// Check if the transmission is in the past
|
|
{
|
|
std::lock_guard<std::mutex> lock(tx_mutex);
|
|
if (tx_time_n < tx_timestamp) {
|
|
logger.error("Error transmission in the past for %d samples", (int)(tx_timestamp - tx_time_n));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Advance TX to timestamp
|
|
advance_tx_timestamp(tx_time_n);
|
|
|
|
// From now on, protect buffers
|
|
std::lock_guard<std::mutex> lock(tx_mutex);
|
|
|
|
// Write transmission buffers into the ring buffer
|
|
write_ring_buffers(tx_ring_buffers, buffer.to_cf_t(), buffer.get_nof_samples());
|
|
|
|
// Increment transmit timestamp
|
|
tx_timestamp += buffer.get_nof_samples();
|
|
|
|
return ret;
|
|
}
|
|
void release_freq(const uint32_t& carrier_idx) override{};
|
|
void tx_end() override {}
|
|
bool rx_now(srsran::rf_buffer_interface& buffer, srsran::rf_timestamp_interface& rxd_time) override
|
|
{
|
|
// Advance Tx buffer
|
|
advance_tx_timestamp(rx_timestamp + buffer.get_nof_samples(), true);
|
|
|
|
// Read samples
|
|
read_ring_buffers(rx_ring_buffers, buffer.to_cf_t(), buffer.get_nof_samples());
|
|
|
|
// Apply Rx gain
|
|
for (uint32_t i = 0; i < rx_ring_buffers.size(); i++) {
|
|
cf_t* ptr = buffer.get(i);
|
|
srsran_vec_sc_prod_cfc(ptr, rx_gain, ptr, buffer.get_nof_samples());
|
|
}
|
|
|
|
// Set Rx timestamp
|
|
srsran_timestamp_init_uint64(rxd_time.get_ptr(0), rx_timestamp, (double)srate_hz);
|
|
|
|
// Advance timestamp
|
|
rx_timestamp += buffer.get_nof_samples();
|
|
|
|
return true;
|
|
}
|
|
void set_tx_freq(const uint32_t& channel_idx, const double& freq) override
|
|
{
|
|
logger.info("Set Tx freq to %+.0f MHz.", freq * 1.0e-6);
|
|
}
|
|
void set_rx_freq(const uint32_t& channel_idx, const double& freq) override
|
|
{
|
|
logger.info("Set Rx freq to %+.0f MHz.", freq * 1.0e-6);
|
|
}
|
|
void set_rx_gain_th(const float& gain) override
|
|
{
|
|
rx_gain = srsran_convert_dB_to_amplitude(gain);
|
|
logger.info("Set Rx gain-th to %+.1f dB (%.6f).", gain, rx_gain.load());
|
|
}
|
|
void set_tx_gain(const float& gain) override
|
|
{
|
|
tx_gain = srsran_convert_dB_to_amplitude(gain);
|
|
logger.info("Set Tx gain to %+.1f dB (%.6f).", gain, tx_gain.load());
|
|
}
|
|
void set_rx_gain(const float& gain) override
|
|
{
|
|
rx_gain = srsran_convert_dB_to_amplitude(gain);
|
|
logger.info("Set Rx gain to %+.1f dB (%.6f).", gain, rx_gain.load());
|
|
}
|
|
void set_tx_srate(const double& srate) override { logger.info("Set Tx sampling rate to %+.3f MHz.", srate * 1.0e-6); }
|
|
void set_rx_srate(const double& srate) override { logger.info("Set Rx sampling rate to %+.3f MHz.", srate * 1.0e-6); }
|
|
void set_channel_rx_offset(uint32_t ch, int32_t offset_samples) override{};
|
|
float get_rx_gain() override { return srsran_convert_amplitude_to_dB(rx_gain); }
|
|
double get_freq_offset() override { return 0; }
|
|
bool is_continuous_tx() override { return false; }
|
|
bool get_is_start_of_burst() override { return false; }
|
|
bool is_init() override { return is_initialised; }
|
|
void reset() override {}
|
|
srsran_rf_info_t* get_info() override { return &rf_info; }
|
|
};
|
|
|
|
} // namespace srsran
|
|
|
|
#endif // SRSRAN_RADIO_DUMMY_H
|