mirror of https://github.com/PentHertz/srsLTE.git
RFNOC: added DUC/DDC internal loopback option and test
This commit is contained in:
parent
dcf05f7a53
commit
f80c779d88
|
@ -43,6 +43,12 @@ if(RF_FOUND)
|
|||
endif(UHD_ENABLE_CUSTOM_RFNOC)
|
||||
endif (UHD_FOUND)
|
||||
|
||||
if (UHD_FOUND AND UHD_ENABLE_CUSTOM_RFNOC)
|
||||
add_executable(rfnoc_test rfnoc_test.cc)
|
||||
target_link_libraries(rfnoc_test srslte_rf ${UHD_LIBRARIES} ${Boost_LIBRARIES} /usr/lib/x86_64-linux-gnu/libboost_system.so)
|
||||
message(info ${Boost_LIBRARIES})
|
||||
endif (UHD_FOUND AND UHD_ENABLE_CUSTOM_RFNOC)
|
||||
|
||||
if (BLADERF_FOUND)
|
||||
add_definitions(-DENABLE_BLADERF)
|
||||
list(APPEND SOURCES_RF rf_blade_imp.c)
|
||||
|
|
|
@ -72,12 +72,15 @@ private:
|
|||
const size_t spp = 1920 / 8;
|
||||
|
||||
// Primary parameters
|
||||
bool loopback = false;
|
||||
double master_clock_rate = 184.32e6;
|
||||
double bw_hz = 100.0;
|
||||
size_t nof_radios = 1; ///< Number of RF devices in the device
|
||||
size_t nof_channels = 1; ///< Number of Channels per Radio
|
||||
std::vector<double> rx_freq_hz;
|
||||
std::vector<double> rx_center_freq_hz;
|
||||
std::vector<double> tx_freq_hz;
|
||||
std::vector<double> tx_center_freq_hz;
|
||||
size_t dma_fifo_depth = 8192UL * 4096UL;
|
||||
|
||||
// Radio control
|
||||
|
@ -126,6 +129,13 @@ private:
|
|||
// Parse master clock rate
|
||||
parse_param(args, "master_clock_rate", master_clock_rate, false);
|
||||
|
||||
// Parser loopback (optional)
|
||||
loopback = args.has_key("loopback");
|
||||
if (loopback) {
|
||||
args.pop("loopback");
|
||||
Warning("RFNOC Loopback activated!")
|
||||
}
|
||||
|
||||
// Parse number of radios
|
||||
parse_param(args, "rfnoc_nof_radios", nof_radios);
|
||||
if (nof_radios == 0) {
|
||||
|
@ -143,7 +153,9 @@ private:
|
|||
// Set secondary parameters
|
||||
bw_hz = master_clock_rate / 2.0;
|
||||
tx_freq_hz.resize(nof_radios * nof_channels);
|
||||
tx_center_freq_hz.resize(nof_radios);
|
||||
rx_freq_hz.resize(nof_radios * nof_channels);
|
||||
rx_center_freq_hz.resize(nof_radios);
|
||||
|
||||
return UHD_ERROR_NONE;
|
||||
}
|
||||
|
@ -153,9 +165,12 @@ private:
|
|||
UHD_SAFE_C_SAVE_ERROR(
|
||||
this,
|
||||
// Create Radio control
|
||||
radio_ctrl.resize(nof_radios);
|
||||
radio_id.resize(nof_radios);
|
||||
for (size_t i = 0; i < nof_radios; i++) {
|
||||
if (not loopback) {
|
||||
radio_ctrl.resize(nof_radios);
|
||||
radio_id.resize(nof_radios);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < radio_ctrl.size(); i++) {
|
||||
// Create handle for radio object
|
||||
radio_id[i] = uhd::rfnoc::block_id_t(0, RADIO_BLOCK_NAME, i);
|
||||
// This next line will fail if the radio is not actually available
|
||||
|
@ -210,6 +225,7 @@ private:
|
|||
if (ddc_ctrl[i] == nullptr) {
|
||||
ddc_id[i] = uhd::rfnoc::block_id_t(0, DDC_BLOCK_NAME, i);
|
||||
ddc_ctrl[i] = device3->get_block_ctrl<uhd::rfnoc::ddc_block_ctrl>(ddc_id[i]);
|
||||
ddc_ctrl[i]->clear();
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < nof_channels; j++) {
|
||||
|
@ -244,6 +260,7 @@ private:
|
|||
if (duc_ctrl[i] == nullptr) {
|
||||
duc_id[i] = uhd::rfnoc::block_id_t(0, DUC_BLOCK_NAME, i);
|
||||
duc_ctrl[i] = device3->get_block_ctrl<uhd::rfnoc::duc_block_ctrl>(duc_id[i]);
|
||||
duc_ctrl[i]->clear();
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < nof_channels; j++) {
|
||||
|
@ -307,6 +324,35 @@ private:
|
|||
})
|
||||
}
|
||||
|
||||
uhd_error connect_loopback()
|
||||
{
|
||||
size_t nof_samples_per_packet = 0;
|
||||
|
||||
if (spp != 0) {
|
||||
nof_samples_per_packet = spp * 4 + 2 * sizeof(uint64_t);
|
||||
}
|
||||
|
||||
UHD_SAFE_C_SAVE_ERROR(this,
|
||||
|
||||
// Get Tx and Rx Graph
|
||||
graph = device3->create_graph("graph");
|
||||
|
||||
for (size_t i = 0; i < nof_radios; i++) {
|
||||
// DUC -> DDC
|
||||
UHD_LOG_DEBUG(graph->get_name(), "Connecting " << duc_id[i] << " -> " << ddc_id[i]);
|
||||
graph->connect(duc_id[i], 0, ddc_id[i], 0, nof_samples_per_packet);
|
||||
|
||||
for (size_t j = 0; j < nof_channels; j++) {
|
||||
// DMA FIFO -> DUC
|
||||
size_t dma_port = i * nof_channels + j;
|
||||
UHD_LOG_DEBUG(graph->get_name(),
|
||||
"Connecting " << dma_id << ":" << dma_port << " -> " << duc_id[i] << ":"
|
||||
<< j);
|
||||
graph->connect(dma_id, dma_port, duc_id[i], j, nof_samples_per_packet);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public:
|
||||
static bool is_required(const uhd::device_addr_t& device_addr)
|
||||
{
|
||||
|
@ -360,7 +406,11 @@ public:
|
|||
}
|
||||
|
||||
// Connect components
|
||||
err = connect();
|
||||
if (loopback) {
|
||||
err = connect_loopback();
|
||||
} else {
|
||||
err = connect();
|
||||
}
|
||||
if (err != UHD_ERROR_NONE) {
|
||||
return err;
|
||||
}
|
||||
|
@ -402,10 +452,19 @@ public:
|
|||
}
|
||||
uhd_error get_time_now(uhd::time_spec_t& timespec) override
|
||||
{
|
||||
if (loopback) {
|
||||
timespec = 0.0;
|
||||
return UHD_ERROR_NONE;
|
||||
}
|
||||
|
||||
UHD_SAFE_C_SAVE_ERROR(this, timespec = radio_ctrl[0]->get_time_now(););
|
||||
}
|
||||
uhd_error set_sync_source(const std::string& source) override
|
||||
{
|
||||
if (loopback) {
|
||||
return UHD_ERROR_NONE;
|
||||
}
|
||||
|
||||
UHD_SAFE_C_SAVE_ERROR(this, for (size_t radio_idx = 0; radio_idx < nof_radios; radio_idx++) {
|
||||
UHD_LOG_DEBUG(radio_id[radio_idx], "Setting sync source to " << source);
|
||||
radio_ctrl[radio_idx]->set_clock_source(source);
|
||||
|
@ -495,6 +554,10 @@ public:
|
|||
return UHD_ERROR_INDEX;
|
||||
}
|
||||
|
||||
if (radio_ctrl.size() == 0) {
|
||||
return UHD_ERROR_NONE;
|
||||
}
|
||||
|
||||
size_t radio_idx = ch / nof_channels;
|
||||
size_t channel_idx = ch % nof_channels;
|
||||
|
||||
|
@ -517,6 +580,10 @@ public:
|
|||
return UHD_ERROR_INDEX;
|
||||
}
|
||||
|
||||
if (radio_ctrl.size() == 0) {
|
||||
return UHD_ERROR_NONE;
|
||||
}
|
||||
|
||||
size_t radio_idx = ch / nof_channels;
|
||||
size_t channel_idx = ch % nof_channels;
|
||||
|
||||
|
@ -531,8 +598,23 @@ public:
|
|||
radio_ctrl[radio_idx]->set_rx_gain(gain, 0);
|
||||
UHD_LOG_DEBUG(radio_id[radio_idx], "Actual RX Gain: " << radio_ctrl[radio_idx]->get_rx_gain(0) << " dB...");)
|
||||
}
|
||||
uhd_error get_tx_gain(double& gain) override { UHD_SAFE_C_SAVE_ERROR(this, gain = radio_ctrl[0]->get_tx_gain(0);) }
|
||||
uhd_error get_rx_gain(double& gain) override { UHD_SAFE_C_SAVE_ERROR(this, gain = radio_ctrl[0]->get_rx_gain(0);) }
|
||||
uhd_error get_tx_gain(double& gain) override
|
||||
{
|
||||
if (radio_ctrl.size() == 0) {
|
||||
return UHD_ERROR_NONE;
|
||||
}
|
||||
|
||||
UHD_SAFE_C_SAVE_ERROR(this, gain = radio_ctrl[0]->get_tx_gain(0);)
|
||||
}
|
||||
uhd_error get_rx_gain(double& gain) override
|
||||
{
|
||||
|
||||
if (radio_ctrl.size() == 0) {
|
||||
return UHD_ERROR_NONE;
|
||||
}
|
||||
|
||||
UHD_SAFE_C_SAVE_ERROR(this, gain = radio_ctrl[0]->get_rx_gain(0);)
|
||||
}
|
||||
uhd_error set_tx_freq(uint32_t ch, double target_freq, double& actual_freq) override
|
||||
{
|
||||
if (ch >= tx_freq_hz.size()) {
|
||||
|
@ -557,31 +639,30 @@ public:
|
|||
|
||||
Debug("Tuning Tx " << ch << " to " << actual_freq / 1e6 << " MHz...");
|
||||
size_t i = ch / nof_channels;
|
||||
|
||||
// Calculate center frequency from average
|
||||
double center_freq_hz = 0.0;
|
||||
for (size_t j = 0; j < nof_channels; j++) {
|
||||
if (not std::isnormal(tx_freq_hz[i * nof_channels + j])) {
|
||||
tx_freq_hz[i * nof_channels + j] = target_freq;
|
||||
}
|
||||
center_freq_hz += tx_freq_hz[i * nof_channels + j];
|
||||
}
|
||||
center_freq_hz /= nof_channels;
|
||||
size_t j = ch % nof_channels;
|
||||
|
||||
UHD_SAFE_C_SAVE_ERROR(
|
||||
this,
|
||||
|
||||
// Set Radio Tx freq
|
||||
UHD_LOG_DEBUG(radio_id[i], "Setting TX Freq: " << center_freq_hz / 1e6 << " MHz...");
|
||||
radio_ctrl[i]->set_tx_frequency(center_freq_hz, 0);
|
||||
center_freq_hz = radio_ctrl[i]->get_tx_frequency(0);
|
||||
UHD_LOG_DEBUG(radio_id[i], "Actual TX Freq: " << center_freq_hz / 1e6 << " MHz...");
|
||||
if (not std::isnormal(tx_center_freq_hz[i]) and not loopback) {
|
||||
UHD_LOG_DEBUG(radio_id[i],
|
||||
"Setting TX Freq: " << target_freq / 1e6 << " (" << uint64_t(target_freq) << ") MHz...");
|
||||
radio_ctrl[i]->set_tx_frequency(target_freq, 0);
|
||||
tx_center_freq_hz[i] = radio_ctrl[i]->get_tx_frequency(0);
|
||||
UHD_LOG_DEBUG(radio_id[i],
|
||||
"Actual TX Freq: " << tx_center_freq_hz[i] / 1e6 << "(" << uint64_t(tx_center_freq_hz[i])
|
||||
<< ") MHz...");
|
||||
}
|
||||
|
||||
// Setup DUC
|
||||
for (size_t j = 0; j < nof_channels; j++) {
|
||||
double freq_hz = tx_freq_hz[nof_channels * i + j] - center_freq_hz;
|
||||
UHD_LOG_DEBUG(duc_id[i], "Setting " << j << " freq: " << freq_hz / 1e6 << " MHz ...");
|
||||
double freq_hz = tx_freq_hz[nof_channels * i + j] - tx_center_freq_hz[i];
|
||||
if (std::round(duc_ctrl[i]->get_arg<double>("freq") != freq_hz)) {
|
||||
UHD_LOG_DEBUG(duc_id[i],
|
||||
"Setting " << j << " freq: " << freq_hz / 1e6 << "(" << uint64_t(freq_hz) << ") MHz ...");
|
||||
duc_ctrl[i]->set_arg("freq", std::to_string(freq_hz), j);
|
||||
freq_hz = duc_ctrl[i]->get_arg<double>("freq");
|
||||
Debug("Actual Tx " << ch << " freq error: " << tx_center_freq_hz[i] + freq_hz - target_freq << " Hz...");
|
||||
})
|
||||
}
|
||||
uhd_error set_rx_freq(uint32_t ch, double target_freq, double& actual_freq) override
|
||||
|
@ -608,31 +689,26 @@ public:
|
|||
|
||||
Debug("Tuning Rx " << ch << " to " << actual_freq / 1e6 << " MHz...");
|
||||
size_t i = ch / nof_channels;
|
||||
|
||||
// Calculate center frequency from average
|
||||
double center_freq_hz = 0.0;
|
||||
for (size_t j = 0; j < nof_channels; j++) {
|
||||
if (not std::isnormal(rx_freq_hz[i * nof_channels + j])) {
|
||||
rx_freq_hz[i * nof_channels + j] = target_freq;
|
||||
}
|
||||
center_freq_hz += rx_freq_hz[i * nof_channels + j];
|
||||
}
|
||||
center_freq_hz /= nof_channels;
|
||||
size_t j = ch % nof_channels;
|
||||
|
||||
UHD_SAFE_C_SAVE_ERROR(
|
||||
this,
|
||||
|
||||
// Set Radio Tx freq
|
||||
UHD_LOG_DEBUG(radio_id[i], "Setting RX Freq: " << center_freq_hz / 1e6 << " MHz...");
|
||||
radio_ctrl[i]->set_rx_frequency(center_freq_hz, 0);
|
||||
center_freq_hz = radio_ctrl[i]->get_rx_frequency(0);
|
||||
UHD_LOG_DEBUG(radio_id[i], "Actual RX Freq: " << center_freq_hz / 1e6 << " MHz...");
|
||||
if (not std::isnormal(rx_center_freq_hz[i]) and not loopback) {
|
||||
UHD_LOG_DEBUG(radio_id[i], "Setting RX Freq: " << target_freq / 1e6 << " MHz...");
|
||||
radio_ctrl[i]->set_rx_frequency(target_freq, 0);
|
||||
rx_center_freq_hz[i] = radio_ctrl[i]->get_rx_frequency(0);
|
||||
UHD_LOG_DEBUG(radio_id[i], "Actual RX Freq: " << rx_center_freq_hz[i] / 1e6 << " MHz...");
|
||||
}
|
||||
|
||||
// Setup DDC
|
||||
for (size_t j = 0; j < nof_channels; j++) {
|
||||
double freq_hz = center_freq_hz - rx_freq_hz[nof_channels * i + j];
|
||||
double freq_hz = rx_center_freq_hz[i] - rx_freq_hz[nof_channels * i + j];
|
||||
if (std::round(ddc_ctrl[i]->get_arg<double>("freq") != freq_hz)) {
|
||||
UHD_LOG_DEBUG(ddc_id[i], "Setting " << j << " freq: " << freq_hz / 1e6 << " MHz ...");
|
||||
ddc_ctrl[i]->set_arg("freq", freq_hz, j);
|
||||
freq_hz = ddc_ctrl[i]->get_arg<double>("freq");
|
||||
Debug("Actual Rx " << ch << " freq error: " << rx_center_freq_hz[i] - freq_hz - target_freq << " Hz...");
|
||||
})
|
||||
}
|
||||
};
|
||||
|
|
|
@ -129,8 +129,8 @@ public:
|
|||
|
||||
UHD_SAFE_C_SAVE_ERROR(this, uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
|
||||
stream_cmd.time_spec = time_spec;
|
||||
stream_cmd.time_spec += 0.1;
|
||||
stream_cmd.stream_now = false;
|
||||
stream_cmd.time_spec += delay;
|
||||
stream_cmd.stream_now = not std::isnormal(delay);
|
||||
|
||||
rx_stream->issue_stream_cmd(stream_cmd);)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,357 @@
|
|||
//
|
||||
// Copyright 2016 Ettus Research LLC
|
||||
// Copyright 2018 Ettus Research, a National Instruments Company
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
|
||||
// Example UHD/RFNoC application: Connect an rx radio to a tx radio and
|
||||
// run a loopback.
|
||||
#include "rf_uhd_rfnoc.h"
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/program_options/cmdline.hpp>
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <srslte/config.h>
|
||||
#include <srslte/phy/utils/vector.h>
|
||||
#include <uhd/device3.hpp>
|
||||
#include <uhd/rfnoc/dma_fifo_block_ctrl.hpp>
|
||||
#include <uhd/rfnoc/duc_ch2_block_ctrl.hpp>
|
||||
|
||||
#define TESTASSERT(cond) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
printf("[%s][Line %d] Fail at \"%s\"\n", __FUNCTION__, __LINE__, (#cond)); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/****************************************************************************
|
||||
* main
|
||||
***************************************************************************/
|
||||
cf_t cexp_(float arg)
|
||||
{
|
||||
std::complex<float> v = std::polar(0.5f, arg);
|
||||
return *((cf_t*)&v);
|
||||
}
|
||||
|
||||
struct rfnoc_custom_testbench_s {
|
||||
public:
|
||||
typedef struct {
|
||||
// Main parameters
|
||||
std::string ip_address = "192.168.40.2";
|
||||
double duration_s = 1.0;
|
||||
double master_clock_rate_hz = 184.32e6;
|
||||
double srate_hz = 1.92e6;
|
||||
double init_tx_time = 0.1;
|
||||
size_t period_nsamples = 19;
|
||||
uint32_t nof_channels = 2;
|
||||
uint32_t nof_radios = 1;
|
||||
bool asynchronous_msg = false;
|
||||
bool blocking = false;
|
||||
size_t blocking_min_tx = 1;
|
||||
|
||||
// Getters
|
||||
float get_freq_norm() { return 1.0f / float(period_nsamples); }
|
||||
} config_s;
|
||||
|
||||
private:
|
||||
config_s config;
|
||||
|
||||
std::mutex mutex;
|
||||
std::condition_variable tx_cvar;
|
||||
std::condition_variable rx_cvar;
|
||||
|
||||
cf_t* tx_buffer[2] = {};
|
||||
cf_t* rx_buffer[2] = {};
|
||||
|
||||
std::shared_ptr<rf_uhd_safe_interface> rfnoc;
|
||||
size_t packet_size_nsamples = 0;
|
||||
|
||||
// Stats
|
||||
size_t txd_nsamples_total = 0;
|
||||
size_t txd_count = 0;
|
||||
size_t rxd_nsamples_total = 0;
|
||||
size_t rxd_count = 0;
|
||||
bool running = true;
|
||||
|
||||
void check_async_msg()
|
||||
{
|
||||
bool valid = false;
|
||||
uhd::async_metadata_t async_metadata;
|
||||
|
||||
rfnoc->recv_async_msg(async_metadata, 0.0, valid);
|
||||
|
||||
if (valid) {
|
||||
switch (async_metadata.event_code) {
|
||||
|
||||
case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
|
||||
Warning("BURST ACK") break;
|
||||
case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
|
||||
Warning("UNDERFLOW") break;
|
||||
case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
|
||||
Warning("SEQUENCE ERROR") break;
|
||||
case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
|
||||
Warning("TIME ERROR") break;
|
||||
case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
|
||||
Warning("UNDERFLOW IN PACKET") break;
|
||||
case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
|
||||
Warning("ERROR IN BURST") break;
|
||||
case uhd::async_metadata_t::EVENT_CODE_USER_PAYLOAD:
|
||||
Warning("ERROR USER PAYLOAD") break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* tx_routine()
|
||||
{
|
||||
size_t txd_nsamples = 0;
|
||||
void* tx_buffs_ptr[] = {tx_buffer[0], tx_buffer[1]};
|
||||
|
||||
uhd::tx_metadata_t tx_metadata;
|
||||
tx_metadata.has_time_spec = true;
|
||||
tx_metadata.time_spec = config.init_tx_time;
|
||||
tx_metadata.start_of_burst = true;
|
||||
|
||||
while (tx_metadata.time_spec < config.duration_s + config.init_tx_time and running) {
|
||||
cf_t v = cexp_(2.0f * M_PI * float(txd_nsamples_total % config.period_nsamples) * config.get_freq_norm());
|
||||
srslte_vec_gen_sine(v, config.get_freq_norm(), tx_buffer[0], packet_size_nsamples);
|
||||
srslte_vec_sc_prod_cfc(tx_buffer[0], -1, tx_buffer[1], packet_size_nsamples);
|
||||
|
||||
if (config.blocking) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
while (rxd_count + config.blocking_min_tx <= txd_count and running) {
|
||||
std::cv_status status = rx_cvar.wait_for(lock, std::chrono::milliseconds(100));
|
||||
if (status == std::cv_status::timeout) {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actual transmission
|
||||
rfnoc->send(tx_buffs_ptr, packet_size_nsamples, tx_metadata, 0.1, txd_nsamples);
|
||||
tx_metadata.start_of_burst = false;
|
||||
|
||||
// Update Tx state
|
||||
mutex.lock();
|
||||
txd_nsamples_total += txd_nsamples;
|
||||
txd_count++;
|
||||
tx_cvar.notify_all();
|
||||
mutex.unlock();
|
||||
tx_metadata.time_spec += packet_size_nsamples / config.srate_hz;
|
||||
tx_metadata.start_of_burst = false;
|
||||
|
||||
if (not config.asynchronous_msg) {
|
||||
check_async_msg();
|
||||
}
|
||||
}
|
||||
|
||||
tx_metadata.end_of_burst = true;
|
||||
rfnoc->send(tx_buffs_ptr, 0, tx_metadata, 0.1, txd_nsamples);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* rx_routine()
|
||||
{
|
||||
void* rx_buffs_ptr[] = {rx_buffer[0], rx_buffer[1]};
|
||||
|
||||
do {
|
||||
uhd::rx_metadata_t rx_metadata;
|
||||
size_t rxd_nsamples = 0;
|
||||
|
||||
if (config.blocking) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
while (rxd_count >= txd_count and running) {
|
||||
std::cv_status status = tx_cvar.wait_for(lock, std::chrono::milliseconds(100));
|
||||
if (status == std::cv_status::timeout) {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rfnoc->receive(rx_buffs_ptr, packet_size_nsamples, rx_metadata, 0.1, false, rxd_nsamples);
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (rx_metadata.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) {
|
||||
running = false;
|
||||
} else {
|
||||
// Update stats
|
||||
rxd_nsamples_total += rxd_nsamples;
|
||||
rxd_count++;
|
||||
float avg_power = srslte_vec_avg_power_cf(rx_buffer[0], rxd_nsamples);
|
||||
if (std::isnan(avg_power) or avg_power > 1e-6) {
|
||||
Warning("-- RX " << rxd_count << " Received " << avg_power);
|
||||
}
|
||||
}
|
||||
rx_cvar.notify_all();
|
||||
} while (running);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* async_msg_routine()
|
||||
{
|
||||
while (running) {
|
||||
check_async_msg();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
rfnoc_custom_testbench_s(const config_s& config_)
|
||||
{
|
||||
rfnoc = std::make_shared<rf_uhd_rfnoc>();
|
||||
|
||||
config = config_;
|
||||
}
|
||||
int init()
|
||||
{
|
||||
uhd::device_addr_t hint;
|
||||
hint["ip_addr"] = config.ip_address;
|
||||
hint["master_clock_rate"] = std::to_string(config.master_clock_rate_hz);
|
||||
hint["rfnoc_nof_channels"] = std::to_string(config.nof_channels);
|
||||
hint["rfnoc_nof_radios"] = std::to_string(config.nof_radios);
|
||||
hint["loopback"] = "true";
|
||||
uhd::log::set_console_level(uhd::log::severity_level::trace);
|
||||
|
||||
if (rfnoc->usrp_make(hint, config.nof_channels * config.nof_radios) != UHD_ERROR_NONE) {
|
||||
Warning(rfnoc->last_error);
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
if (rfnoc->set_tx_rate(config.srate_hz) != UHD_ERROR_NONE) {
|
||||
Warning(rfnoc->last_error);
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
if (rfnoc->set_rx_rate(config.srate_hz) != UHD_ERROR_NONE) {
|
||||
Warning(rfnoc->last_error);
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
size_t nof_tx_samples = 0;
|
||||
size_t nof_rx_samples = 0;
|
||||
rfnoc->get_tx_stream(nof_tx_samples);
|
||||
rfnoc->get_rx_stream(nof_rx_samples);
|
||||
packet_size_nsamples = SRSLTE_MIN(nof_tx_samples, nof_rx_samples);
|
||||
|
||||
for (size_t j = 0; j < config.nof_channels; j++) {
|
||||
tx_buffer[j] = srslte_vec_cf_malloc(packet_size_nsamples);
|
||||
rx_buffer[j] = srslte_vec_cf_malloc(packet_size_nsamples);
|
||||
}
|
||||
|
||||
if (rfnoc->start_rx_stream(config.init_tx_time) != UHD_ERROR_NONE) {
|
||||
Warning(rfnoc->last_error);
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
int run()
|
||||
{
|
||||
running = true;
|
||||
|
||||
std::thread tx_thread(&rfnoc_custom_testbench_s::tx_routine, this);
|
||||
std::thread rx_thread(&rfnoc_custom_testbench_s::rx_routine, this);
|
||||
|
||||
if (config.asynchronous_msg) {
|
||||
std::thread async_msg_thread(&rfnoc_custom_testbench_s::async_msg_routine, this);
|
||||
async_msg_thread.join();
|
||||
}
|
||||
|
||||
tx_thread.join();
|
||||
rx_thread.join();
|
||||
|
||||
Info("Tx packets: " << txd_count);
|
||||
Info("Tx samples: " << txd_nsamples_total);
|
||||
|
||||
Info("Rx packets: " << rxd_count);
|
||||
Info("Rx samples: " << rxd_nsamples_total);
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
~rfnoc_custom_testbench_s()
|
||||
{
|
||||
for (size_t j = 0; j < config.nof_channels; j++) {
|
||||
if (tx_buffer[j] != nullptr) {
|
||||
free(tx_buffer[j]);
|
||||
}
|
||||
if (rx_buffer[j] != nullptr) {
|
||||
free(rx_buffer[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static int test(const rfnoc_custom_testbench_s::config_s& config)
|
||||
{
|
||||
rfnoc_custom_testbench_s context(config);
|
||||
|
||||
TESTASSERT(context.init() == SRSLTE_SUCCESS);
|
||||
TESTASSERT(context.run() == SRSLTE_SUCCESS);
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
static int parse_args(int argc, char** argv, rfnoc_custom_testbench_s::config_s& args)
|
||||
{
|
||||
int ret = SRSLTE_SUCCESS;
|
||||
|
||||
bpo::options_description options;
|
||||
bpo::options_description common("Common execution options");
|
||||
|
||||
// clang-format off
|
||||
common.add_options()
|
||||
("ip_addr", bpo::value<std::string>(&args.ip_address), "Device 3 IP address")
|
||||
("duration", bpo::value<double>(&args.duration_s), "Duration of the execution in seconds")
|
||||
("master_clock_rate", bpo::value<double>(&args.master_clock_rate_hz), "Device master clock rate in Hz")
|
||||
("srate", bpo::value<double>(&args.srate_hz), "Sampling rate in Hz")
|
||||
("init_time", bpo::value<double>(&args.init_tx_time), "Initial time in seconds")
|
||||
("period_nsamples", bpo::value<size_t>(&args.period_nsamples), "Sine wave period in number of samples")
|
||||
("asynch_msg", bpo::value<bool>(&args.asynchronous_msg), "Enable/disable asynchronous message")
|
||||
("blocking", bpo::value<bool>(&args.blocking), "Block transmitter/receiver for every transaction")
|
||||
("blocking_min_tx", bpo::value<size_t>(&args.blocking_min_tx),"Ensure a minimum of enqueued Tx")
|
||||
;
|
||||
options.add(common).add_options()("help", "Show this message");
|
||||
// clang-format on
|
||||
|
||||
bpo::variables_map vm;
|
||||
try {
|
||||
bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm);
|
||||
bpo::notify(vm);
|
||||
} catch (bpo::error& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
ret = SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
// help option was given or error - print usage and exit
|
||||
if (vm.count("help") || ret) {
|
||||
std::cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << std::endl << std::endl;
|
||||
std::cout << options << std::endl << std::endl;
|
||||
ret = SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
rfnoc_custom_testbench_s::config_s config;
|
||||
|
||||
TESTASSERT(parse_args(argc, argv, config) == SRSLTE_SUCCESS);
|
||||
|
||||
TESTASSERT(test(config) == SRSLTE_SUCCESS);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue