mirror of https://github.com/pvnis/srsRAN_4G.git
Initial real-time Tx gain setting (#2976)
* Make filename const in filesink * Sine generation returns the next phase * Avoid malloc/free in radio class * Implement Tx gain in ZMQ * Initial ratio RT gain test * UHD: use timed Tx gain commands to align changes to subframes * Minor improvement in test_radio_rt_gain * Fix compilation * Check RF gain thread id before joining * Remove redundant zero initialization. Co-authored-by: Fabian Eckermann <fabian@srs.io>master
parent
ebab12403f
commit
322f57a952
@ -0,0 +1,300 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* \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 "srsran/common/test_common.h"
|
||||||
|
#include "srsran/radio/radio.h"
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// shorten boost program options namespace
|
||||||
|
namespace bpo = boost::program_options;
|
||||||
|
|
||||||
|
// Test arguments
|
||||||
|
struct test_args_s {
|
||||||
|
bool valid = false;
|
||||||
|
double srate_hz = 3.84e6;
|
||||||
|
double freq_hz = 3.5e9;
|
||||||
|
float rx_gain_db = 20.0f;
|
||||||
|
float tx_gain_db_begin = 0.0f;
|
||||||
|
float tx_gain_db_end = 30.0f;
|
||||||
|
float tx_gain_db_step = 1.0f;
|
||||||
|
uint32_t tx_delay_ms = 4;
|
||||||
|
uint32_t step_period_ms = 1;
|
||||||
|
uint32_t nof_repetitions = 3;
|
||||||
|
uint32_t power_ramping_ms = 50;
|
||||||
|
uint32_t pre_tx_ms = 50; // After main loop acquire a few more ms
|
||||||
|
uint32_t post_tx_ms = 50; // After main loop acquire a few more ms
|
||||||
|
std::string filename = "/tmp/baseband.iq.dat";
|
||||||
|
std::string device_name = "zmq";
|
||||||
|
std::string device_args = "tx_port=tcp://*:5555,rx_port=tcp://localhost:5555,base_srate=3.84e6";
|
||||||
|
|
||||||
|
test_args_s(int argc, char** argv)
|
||||||
|
{
|
||||||
|
bpo::options_description options;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
options.add_options()
|
||||||
|
("srate", bpo::value<double>(&srate_hz)->default_value(srate_hz), "Sampling rate in Hz")
|
||||||
|
("freq", bpo::value<double>(&freq_hz)->default_value(freq_hz), "Center frequency in Hz")
|
||||||
|
("rx_gain", bpo::value<float>(&rx_gain_db)->default_value(rx_gain_db), "Receiver gain in dB")
|
||||||
|
("tx_gain_begin", bpo::value<float>(&tx_gain_db_begin)->default_value(tx_gain_db_begin), "Initial transmitter gain in dB")
|
||||||
|
("tx_gain_end", bpo::value<float>(&tx_gain_db_end)->default_value(tx_gain_db_end), "Final transmitter gain in dB")
|
||||||
|
("tx_gain_step", bpo::value<float>(&tx_gain_db_step)->default_value(tx_gain_db_step), "Step transmitter gain in dB")
|
||||||
|
("tx_delay", bpo::value<uint32_t>(&tx_delay_ms)->default_value(tx_delay_ms), "Delay between Rx and Tx in milliseconds")
|
||||||
|
("step_period", bpo::value<uint32_t>(&step_period_ms)->default_value(step_period_ms), "Transmitter gain step period in milliseconds")
|
||||||
|
("repetitions", bpo::value<uint32_t>(&nof_repetitions)->default_value(nof_repetitions), "Number of transmit gain steering repetitions")
|
||||||
|
("power_ramping", bpo::value<uint32_t>(&power_ramping_ms)->default_value(power_ramping_ms), "Transmitter initial power ramping in milliseconds")
|
||||||
|
("pre_tx", bpo::value<uint32_t>(&pre_tx_ms)->default_value(pre_tx_ms), "Initial acquisition time before start transmission in milliseconds")
|
||||||
|
("post_tx", bpo::value<uint32_t>(&pre_tx_ms)->default_value(pre_tx_ms), "Initial acquisition time after ending transmission in milliseconds")
|
||||||
|
("filename", bpo::value<std::string>(&filename)->default_value(filename), "File sink filename")
|
||||||
|
("dev_name", bpo::value<std::string>(&device_name)->default_value(device_name), "RF Device name")
|
||||||
|
("dev_args", bpo::value<std::string>(&device_args)->default_value(device_args), "RF Device arguments")
|
||||||
|
("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);
|
||||||
|
valid = true;
|
||||||
|
} catch (bpo::error& e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// help option was given or error - print usage and exit
|
||||||
|
if (vm.count("help") > 0 or not valid) {
|
||||||
|
std::cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << std::endl << std::endl;
|
||||||
|
std::cout << options << std::endl << std::endl;
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class phy_radio_listener : public srsran::phy_interface_radio
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST", false);
|
||||||
|
uint32_t overflow_count = 0;
|
||||||
|
uint32_t failure_count = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
phy_radio_listener() {}
|
||||||
|
void radio_overflow() override
|
||||||
|
{
|
||||||
|
overflow_count++;
|
||||||
|
logger.error("Overflow");
|
||||||
|
}
|
||||||
|
void radio_failure() override
|
||||||
|
{
|
||||||
|
failure_count++;
|
||||||
|
logger.error("Failure");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class test_sink
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
srslog::basic_logger& logger = srslog::fetch_basic_logger("SINK", false);
|
||||||
|
srsran_filesink_t filesink = {};
|
||||||
|
std::vector<float> meas_power_dB;
|
||||||
|
|
||||||
|
public:
|
||||||
|
test_sink(const std::string& filename)
|
||||||
|
{
|
||||||
|
if (srsran_filesink_init(&filesink, filename.c_str(), SRSRAN_COMPLEX_FLOAT_BIN) < SRSRAN_SUCCESS) {
|
||||||
|
ERROR("Error initiating filesink");
|
||||||
|
filesink = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~test_sink()
|
||||||
|
{
|
||||||
|
// Free filesink
|
||||||
|
srsran_filesink_free(&filesink);
|
||||||
|
|
||||||
|
// Print measure power
|
||||||
|
printf("Measured power: ");
|
||||||
|
srsran_vec_fprint_f(stdout, meas_power_dB.data(), (int)meas_power_dB.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(std::vector<cf_t>& buffer, uint32_t nsamp)
|
||||||
|
{
|
||||||
|
// Measure average power
|
||||||
|
float avg_pwr = srsran_vec_avg_power_cf(buffer.data(), nsamp);
|
||||||
|
|
||||||
|
// Push measurement in dB
|
||||||
|
meas_power_dB.push_back(srsran_convert_power_to_dB(avg_pwr));
|
||||||
|
|
||||||
|
// Write signal in file
|
||||||
|
srsran_filesink_write(&filesink, (void*)buffer.data(), (int)nsamp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class test_source
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// cf_t phase = 1.0f;
|
||||||
|
cf_t phase = 0.01;
|
||||||
|
float freq_norm = 0.125;
|
||||||
|
|
||||||
|
public:
|
||||||
|
test_source() {}
|
||||||
|
|
||||||
|
~test_source() {}
|
||||||
|
|
||||||
|
void generate(std::vector<cf_t>& buffer, uint32_t nsamp)
|
||||||
|
{
|
||||||
|
phase *= srsran_vec_gen_sine(phase, freq_norm, buffer.data(), (int)nsamp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST", false);
|
||||||
|
srslog::init();
|
||||||
|
srsran::radio radio;
|
||||||
|
phy_radio_listener radio_listener;
|
||||||
|
|
||||||
|
enum { BEFORE_RAMPING, RAMPING, GAIN_STEERING, POST_GAIN_STEERING, END } state = BEFORE_RAMPING;
|
||||||
|
|
||||||
|
test_args_s args(argc, argv);
|
||||||
|
TESTASSERT(args.valid);
|
||||||
|
|
||||||
|
// Calculate number of steps
|
||||||
|
TESTASSERT(std::isnormal(args.tx_gain_db_step));
|
||||||
|
uint32_t nof_steps = (args.tx_gain_db_end - args.tx_gain_db_begin) / args.tx_gain_db_step;
|
||||||
|
|
||||||
|
// Calculate subframe size in 1ms
|
||||||
|
TESTASSERT(std::isnormal(args.srate_hz));
|
||||||
|
uint32_t sf_sz = (uint32_t)std::round(1e-3 * args.srate_hz);
|
||||||
|
|
||||||
|
// Allocate baseband buffer
|
||||||
|
std::vector<cf_t> buffer(sf_sz);
|
||||||
|
|
||||||
|
// Prepare radio arguments
|
||||||
|
srsran::rf_args_t rf_args = {};
|
||||||
|
rf_args.log_level = "info";
|
||||||
|
rf_args.srate_hz = args.srate_hz;
|
||||||
|
rf_args.dl_freq = args.freq_hz;
|
||||||
|
rf_args.ul_freq = args.freq_hz;
|
||||||
|
rf_args.rx_gain = args.rx_gain_db;
|
||||||
|
rf_args.tx_gain = args.tx_gain_db_begin;
|
||||||
|
rf_args.nof_carriers = 1;
|
||||||
|
rf_args.nof_antennas = 1;
|
||||||
|
rf_args.device_name = args.device_name;
|
||||||
|
rf_args.device_args = args.device_args;
|
||||||
|
|
||||||
|
// Initialise radio
|
||||||
|
TESTASSERT(radio.init(rf_args, &radio_listener) == SRSRAN_SUCCESS);
|
||||||
|
|
||||||
|
// Setup LO frequencies
|
||||||
|
radio.set_tx_freq(0, args.freq_hz);
|
||||||
|
radio.set_rx_freq(0, args.freq_hz);
|
||||||
|
|
||||||
|
// Setup sampling rate
|
||||||
|
radio.set_tx_srate(args.srate_hz);
|
||||||
|
radio.set_rx_srate(args.srate_hz);
|
||||||
|
|
||||||
|
// Setup initial gains
|
||||||
|
radio.set_tx_gain(args.tx_gain_db_begin);
|
||||||
|
radio.set_rx_gain(args.rx_gain_db);
|
||||||
|
|
||||||
|
// Create signal sink
|
||||||
|
test_sink sink(args.filename);
|
||||||
|
|
||||||
|
// Create signal source
|
||||||
|
test_source source;
|
||||||
|
|
||||||
|
// Perform Tx/Rx
|
||||||
|
uint32_t sf_count = 0;
|
||||||
|
uint32_t repetition = 0;
|
||||||
|
while (state != END) {
|
||||||
|
switch (state) {
|
||||||
|
case BEFORE_RAMPING:
|
||||||
|
if (sf_count >= args.pre_tx_ms) {
|
||||||
|
logger.info("-- Starting power ramping stage");
|
||||||
|
state = RAMPING;
|
||||||
|
sf_count = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RAMPING:
|
||||||
|
if (sf_count >= args.power_ramping_ms) {
|
||||||
|
logger.info("-- Starting gain steering stage");
|
||||||
|
state = GAIN_STEERING;
|
||||||
|
sf_count = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GAIN_STEERING:
|
||||||
|
if (sf_count >= nof_steps * args.step_period_ms) {
|
||||||
|
repetition++;
|
||||||
|
sf_count = 0;
|
||||||
|
logger.info("-- Finished repetition %d of %d", repetition, args.nof_repetitions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repetition >= args.nof_repetitions) {
|
||||||
|
logger.info("-- Starting post gain steering stage");
|
||||||
|
state = POST_GAIN_STEERING;
|
||||||
|
radio.tx_end();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (sf_count % args.step_period_ms == 0 and sf_count != 0) {
|
||||||
|
float tx_gain_db = args.tx_gain_db_begin + args.tx_gain_db_step * (sf_count / args.step_period_ms);
|
||||||
|
logger.info("-- New Tx gain %+.2f dB", tx_gain_db);
|
||||||
|
radio.set_tx_gain(tx_gain_db);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case POST_GAIN_STEERING:
|
||||||
|
if (sf_count >= args.post_tx_ms) {
|
||||||
|
logger.info("-- Ending...");
|
||||||
|
state = END;
|
||||||
|
sf_count = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case END:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare reception buffers
|
||||||
|
srsran::rf_buffer_t rf_buffer = {};
|
||||||
|
rf_buffer.set(0, buffer.data());
|
||||||
|
rf_buffer.set_nof_samples(sf_sz);
|
||||||
|
|
||||||
|
// Receive
|
||||||
|
srsran::rf_timestamp_t ts = {};
|
||||||
|
TESTASSERT(radio.rx_now(rf_buffer, ts));
|
||||||
|
|
||||||
|
// Save signal, including the time before transmission
|
||||||
|
sink.write(buffer, sf_sz);
|
||||||
|
|
||||||
|
if (state == RAMPING or state == GAIN_STEERING) {
|
||||||
|
// Generate transmit signal
|
||||||
|
source.generate(buffer, sf_sz);
|
||||||
|
|
||||||
|
// Update timestamp for Tx
|
||||||
|
ts.add(1e-3 * (double)args.tx_delay_ms);
|
||||||
|
|
||||||
|
// Transmit
|
||||||
|
radio.tx(rf_buffer, ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increase SF counting
|
||||||
|
sf_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tear down radio
|
||||||
|
radio.stop();
|
||||||
|
|
||||||
|
return SRSRAN_SUCCESS;
|
||||||
|
}
|
Loading…
Reference in New Issue