uhd/host/lib/usrp/x400/x400_radio_control.cpp
Martin Anderseck df31d13cbc uhd: Add support for USRP X420 (20 GHz USRP)
This adds support for the new USRP X420. It is the latest addition to
our X4xx series and comes with two HBX daughterboards. The HBX is
a one-channel direct conversion transceiver that covers 5G NR FR3 and
X-/KU-Band needs for Research and Prototyping in a frequency range of 10
MHz up to 20 GHz. It supports analog bandwidths of up to 1 GHz.

This commit is the UHD/host portion of the X420 support.

Co-authored-by: Martin Anderseck <martin.anderseck@emerson.com>
Co-authored-by: Ahmed Salman <ahmed.salman@emerson.com>
Co-authored-by: Max Köhler <max.koehler@emerson.com>
Co-authored-by: Markus Schill <markus.schill@emerson.com>
2026-03-20 16:14:54 +01:00

965 lines
36 KiB
C++

//
// Copyright 2020 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
#include "x400_radio_control.hpp"
#include "x400_gpio_control.hpp"
#include "x400_internal_sync.hpp"
#include <uhd/rfnoc/registry.hpp>
#include <uhd/types/serial.hpp>
#include <uhd/utils/log.hpp>
#include <uhd/utils/math.hpp>
#include <uhdlib/rfnoc/reg_iface_adapter.hpp>
#include <uhdlib/usrp/common/x400_rfdc_control.hpp>
#include <uhdlib/usrp/common/x4xx_ch_modes.hpp>
#include <uhdlib/usrp/cores/spi_core_4000.hpp>
#include <uhdlib/usrp/dboard/debug_dboard.hpp>
#include <uhdlib/usrp/dboard/fbx/fbx_dboard.hpp>
#include <uhdlib/usrp/dboard/hbx/hbx_dboard.hpp>
#include <uhdlib/usrp/dboard/null_dboard.hpp>
#include <uhdlib/usrp/dboard/zbx/zbx_dboard.hpp>
#include <uhdlib/utils/prefs.hpp>
#include <future>
using uhd::usrp::x400::ch_mode;
namespace uhd { namespace rfnoc {
x400_radio_control_impl::x400_radio_control_impl(make_args_ptr make_args)
: radio_control_impl(std::move(make_args))
{
RFNOC_LOG_TRACE("Initializing x400_radio_control");
UHD_ASSERT_THROW(get_block_id().get_block_count() < 2);
constexpr char radio_slot_name[2] = {'A', 'B'};
_radio_slot = radio_slot_name[get_block_id().get_block_count()];
_rpc_prefix = get_block_id().get_block_count() == 1 ? "db_1_" : "db_0_";
UHD_ASSERT_THROW(get_mb_controller());
_mb_control = std::dynamic_pointer_cast<mpmd_mb_controller>(get_mb_controller());
UHD_ASSERT_THROW(_mb_control)
RFNOC_LOG_TRACE("num timekeepers: " << _mb_control->get_num_timekeepers()
<< "radio slot: " << _radio_slot);
// select time keeper based on block id but restrict to number of time keepers
// available
const size_t tk_idx =
_mb_control->get_num_timekeepers() >= 2 ? get_block_id().get_block_count() : 0;
_x4xx_timekeeper = std::dynamic_pointer_cast<mpmd_mb_controller::mpmd_timekeeper>(
_mb_control->get_timekeeper(tk_idx));
UHD_ASSERT_THROW(_x4xx_timekeeper);
_rpcc = _mb_control->dynamic_cast_rpc_as<uhd::usrp::x400_rpc_iface>();
if (!_rpcc) {
_rpcc = std::make_shared<uhd::usrp::x400_rpc>(_mb_control->get_rpc_client());
}
_db_rpcc = _mb_control->dynamic_cast_rpc_as<uhd::usrp::dboard_base_rpc_iface>();
if (!_db_rpcc) {
_db_rpcc = std::make_shared<uhd::usrp::dboard_base_rpc>(
_mb_control->get_rpc_client(), _rpc_prefix);
}
const auto all_dboard_info = _rpcc->get_dboard_info();
RFNOC_LOG_TRACE("Hardware detected " << all_dboard_info.size() << " daughterboards.");
// If we have two radio blocks, but there is only one dboard plugged in,
// we skip initialization. The board needs to be in slot A
if (all_dboard_info.size() <= get_block_id().get_block_count()) {
RFNOC_LOG_WARNING("The number of discovered daughterboards did not match the "
"number of radio blocks. Skipping front end initialization.");
_daughterboard = std::make_shared<null_dboard_impl>();
return;
}
// Note: mpmd passed the device args to MPM earlier, so if the user provided
// a master_clock_rate key, then MPM will have used that to initialize the
// master clock rate.
const double master_clock_rate = _db_rpcc->get_master_clock_rate();
set_tick_rate(master_clock_rate);
_x4xx_timekeeper->update_tick_rate(master_clock_rate);
radio_control_impl::set_rate(master_clock_rate);
for (auto& samp_rate_prop : _samp_rate_in) {
set_property(samp_rate_prop.get_id(), get_rate(), samp_rate_prop.get_src_info());
}
for (auto& samp_rate_prop : _samp_rate_out) {
set_property(samp_rate_prop.get_id(), get_rate(), samp_rate_prop.get_src_info());
}
_validate_master_clock_rate_args();
_init_mpm();
RFNOC_LOG_TRACE("Initializing RFDC controls...");
_rfdcc = std::make_shared<x400::rfdc_control>(
// clang-format off
uhd::memmap32_iface_timed{
[this](const uint32_t addr, const uint32_t data, const uhd::time_spec_t& time_spec) {
regs().poke32(addr + x400_regs::RFDC_CTRL_BASE, data, time_spec);
},
[this](const uint32_t addr) {
return regs().peek32(addr + x400_regs::RFDC_CTRL_BASE);
}
},
// clang-format on
get_unique_id() + "::RFDC");
const auto& dboard = all_dboard_info[get_block_id().get_block_count()];
const std::string pid(dboard.at("pid").begin(), dboard.at("pid").end());
RFNOC_LOG_TRACE("Initializing daughterboard driver for PID " << pid);
// We may have physical daughterboards in the system, but no GPIO interface to the
// daughterboard in the FPGA. In this case, just instantiate the null daughterboard.
if (!_rpcc->is_db_gpio_ifc_present(get_block_id().get_block_count())) {
RFNOC_LOG_WARNING(
"Skipping daughterboard initialization, no GPIO interface in FPGA");
_daughterboard = std::make_shared<null_dboard_impl>();
return;
}
if (std::stol(pid) == uhd::usrp::zbx::ZBX_PID) {
auto zbx_rpc_sptr = _mb_control->dynamic_cast_rpc_as<uhd::usrp::zbx_rpc_iface>();
if (!zbx_rpc_sptr) {
zbx_rpc_sptr = std::make_shared<uhd::usrp::zbx_rpc>(
_mb_control->get_rpc_client(), _rpc_prefix);
}
_daughterboard = std::make_shared<uhd::usrp::zbx::zbx_dboard_impl>(
regs(),
regmap::PERIPH_BASE,
[this](const size_t instance) { return get_command_time(instance); },
get_block_id().get_block_count(),
_radio_slot,
_rpc_prefix,
get_unique_id(),
_rpcc,
zbx_rpc_sptr,
_rfdcc,
get_tree());
} else if (std::stol(pid) == uhd::usrp::fbx::FBX_PID) {
auto fbx_rpc_sptr = _mb_control->dynamic_cast_rpc_as<uhd::usrp::fbx_rpc_iface>();
if (!fbx_rpc_sptr) {
fbx_rpc_sptr = std::make_shared<uhd::usrp::fbx_rpc>(
_mb_control->get_rpc_client(), _rpc_prefix);
}
_daughterboard = std::make_shared<uhd::usrp::fbx::fbx_dboard_impl>(
regs(),
regmap::PERIPH_BASE,
[this](const size_t instance) { return get_command_time(instance); },
get_block_id().get_block_count(),
_radio_slot,
get_num_input_ports(),
get_num_output_ports(),
_rpc_prefix,
get_unique_id(),
_rpcc,
fbx_rpc_sptr,
_rfdcc,
get_tree());
auto fbx_dboard =
std::dynamic_pointer_cast<uhd::usrp::fbx::fbx_dboard_impl>(_daughterboard);
if (fbx_dboard != NULL) {
RFNOC_LOG_DEBUG("Registering internal sync feature")
auto int_sync = std::make_shared<uhd::features::internal_sync>(
uhd::features::internal_sync(fbx_dboard->get_fbx_ctrl()));
register_feature(int_sync);
}
} else if (std::stol(pid) == uhd::usrp::hbx::HBX_PID) {
const auto args = get_block_args();
bool ignore_cal_file = false;
if (args.has_key("ignore-cal-file")) {
ignore_cal_file = args.get("ignore-cal-file") == "1";
}
if (ignore_cal_file) {
RFNOC_LOG_WARNING("Ignoring IQ correction and Power calibration files.");
}
auto hbx_rpc_sptr = _mb_control->dynamic_cast_rpc_as<uhd::usrp::hbx_rpc_iface>();
if (!hbx_rpc_sptr) {
hbx_rpc_sptr = std::make_shared<uhd::usrp::hbx_rpc>(
_mb_control->get_rpc_client(), _rpc_prefix);
}
_daughterboard = std::make_shared<uhd::usrp::hbx::hbx_dboard_impl>(
regs(),
regmap::PERIPH_BASE,
[this](const size_t instance) { return get_command_time(instance); },
get_block_id().get_block_count(),
_radio_slot,
_rpc_prefix,
get_unique_id(),
_rpcc,
hbx_rpc_sptr,
_rfdcc,
get_tree(),
ignore_cal_file,
master_clock_rate);
auto hbx_dboard =
std::dynamic_pointer_cast<uhd::usrp::hbx::hbx_dboard_impl>(_daughterboard);
if (hbx_dboard != NULL) {
RFNOC_LOG_DEBUG("Registering internal sync feature")
auto int_sync = std::make_shared<uhd::features::internal_sync>(
uhd::features::internal_sync(hbx_dboard->get_hbx_cpld_ctrl()));
register_feature(int_sync);
}
} else if (std::stol(pid) == uhd::rfnoc::DEBUG_DB_PID) {
_daughterboard = std::make_shared<debug_dboard_impl>();
} else if (std::stol(pid) == uhd::rfnoc::IF_TEST_DBOARD_PID) {
_daughterboard =
std::make_shared<if_test_dboard_impl>(get_block_id().get_block_count(),
_rpc_prefix,
get_unique_id(),
_mb_control,
get_tree());
} else if (std::stol(pid) == uhd::rfnoc::EMPTY_DB_PID) {
_daughterboard = std::make_shared<empty_slot_dboard_impl>();
set_num_output_ports(0);
set_num_input_ports(0);
} else {
RFNOC_LOG_WARNING("Skipping Daughterboard initialization for unsupported PID "
<< "0x" << std::hex << std::stol(pid));
_daughterboard = std::make_shared<null_dboard_impl>();
return;
}
_init_prop_tree();
_rx_pwr_mgr = _daughterboard->get_pwr_mgr(uhd::RX_DIRECTION);
_tx_pwr_mgr = _daughterboard->get_pwr_mgr(uhd::TX_DIRECTION);
_tx_gain_profile_api = _daughterboard->get_tx_gain_profile_api();
_rx_gain_profile_api = _daughterboard->get_rx_gain_profile_api();
if (_daughterboard->is_adc_self_cal_supported()) {
_adc_self_calibration =
std::make_shared<uhd::features::adc_self_calibration>(_rpcc,
_rpc_prefix,
get_unique_id(),
get_block_id().get_block_count(),
_daughterboard);
register_feature(_adc_self_calibration);
}
auto mpm_rpc = _mb_control->dynamic_cast_rpc_as<uhd::usrp::mpmd_rpc_iface>();
if (mpm_rpc->get_gpio_banks().size() > 0) {
_gpios = std::make_shared<x400::gpio_control>(
_rpcc, _mb_control, RFNOC_MAKE_WB_IFACE(regmap::PERIPH_BASE + 0xC000, 0));
// Check if SPI is available as GPIO source, otherwise don't register
// SPI_GETTER_IFace
auto gpio_srcs = _mb_control->get_gpio_srcs("GPIO0");
if (std::count(gpio_srcs.begin(), gpio_srcs.end(), "DB0_SPI") > 0) {
auto spicore = uhd::cores::spi_core_4000::make(
[this](const uint32_t addr, const uint32_t data) {
regs().poke32(addr, data, get_command_time(0));
},
[this](const uint32_t addr) {
return regs().peek32(addr, get_command_time(0));
},
x400_regs::SPI_PERIPH_CFG,
x400_regs::SPI_TRANSACTION_CFG_REG,
x400_regs::SPI_TRANSACTION_GO_REG,
x400_regs::SPI_STATUS_REG,
x400_regs::SPI_CONTROLLER_INFO_REG);
_spi_getter_iface = std::make_shared<x400_spi_getter>(spicore);
register_feature(_spi_getter_iface);
} else {
UHD_LOG_INFO("x400_radio_control",
"SPI functionality not available in this FPGA image. Please update to at "
"least version 7.7 to use SPI.");
}
}
uhd::rfnoc::mb_controller::sync_source_updater_t self_cal_runner =
[&, pid](const uhd::rfnoc::mb_controller::sync_source_t&) {
auto mpm_rpc = _mb_control->dynamic_cast_rpc_as<uhd::usrp::mpmd_rpc_iface>();
auto self_cal_req = mpm_rpc->pop_host_tasks(
"db" + std::to_string(get_block_id().get_block_count()) + "_ADCSelfCal");
if (self_cal_req.size() > 0) {
if (has_feature<uhd::features::adc_self_calibration_iface>()) {
RFNOC_LOG_INFO("Clocking reconfigured, running ADC Self Cal on DB"
<< get_block_id().get_block_count() << "...");
const auto args = get_block_args();
const std::string ch_list = args.get("cal_ch_list", "");
size_t num_calibrations = 0;
auto& self_cal =
get_feature<uhd::features::adc_self_calibration_iface>();
const size_t num_channels = get_num_output_ports();
// Get current setting for guided mode and disable it for the duration
// of the calibration
uhd::prefs::suspend_guided_mode();
auto resume_guided_mode = uhd::utils::scope_exit::make(
[&]() { uhd::prefs::resume_guided_mode(); });
std::vector<std::future<void>> cal_futures;
for (size_t i = 0; i < num_channels; i++) {
auto abs_ch = get_block_id().get_block_count() * num_channels + i;
if (ch_list.size() == 0
or ch_list.find(std::to_string(abs_ch))
!= std::string::npos) {
// Run self-cal in parallel on FBX daughterboards
if (std::stol(pid) == uhd::usrp::fbx::FBX_PID) {
cal_futures.push_back(std::async(std::launch::async,
[&self_cal, i, args]() { self_cal.run(i, args); }));
} else {
RFNOC_LOG_INFO("Calibrating channel " << abs_ch << "...");
self_cal.run(i, args);
}
num_calibrations++;
}
}
// Wait for all channels to complete on FBX daughterboards
if (std::stol(pid) == uhd::usrp::fbx::FBX_PID) {
for (auto& f : cal_futures) {
f.get();
}
}
if (num_calibrations > 0) {
RFNOC_LOG_INFO(
"Calibrated "
<< num_calibrations
<< (num_calibrations > 1 ? " channels." : " channel."));
} else {
RFNOC_LOG_WARNING("Did not find any channels to calibrate!");
}
} else {
RFNOC_LOG_WARNING(
"Clocking reconfigured, ADC Self Cal requested for DB"
<< get_block_id().get_block_count()
<< " but ADC Self Cal feature not available.");
}
}
};
self_cal_runner("");
get_mb_controller()->register_sync_source_updater(self_cal_runner);
}
void x400_radio_control_impl::_init_prop_tree()
{
auto subtree = get_tree()->subtree(fs_path("mboard"));
for (size_t chan_idx = 0; chan_idx < get_num_output_ports(); chan_idx++) {
const fs_path rx_codec_path =
fs_path("rx_codec") / get_dboard_fe_from_chan(chan_idx, uhd::RX_DIRECTION);
const fs_path tx_codec_path =
fs_path("tx_codec") / get_dboard_fe_from_chan(chan_idx, uhd::TX_DIRECTION);
RFNOC_LOG_TRACE("Adding non-RFNoC block properties for channel "
<< chan_idx << " to prop tree paths " << rx_codec_path << " and "
<< tx_codec_path);
// ADC calibration state attributes
subtree->create<bool>(rx_codec_path / "calibration_frozen")
.add_coerced_subscriber([this, chan_idx](bool state) {
_rpcc->set_cal_frozen(state,
get_block_id().get_block_count(),
chan_idx,
size_t(ch_mode::ALL));
})
.set_publisher([this, chan_idx]() {
const auto freeze_states = _rpcc->get_cal_frozen(
get_block_id().get_block_count(), chan_idx, size_t(ch_mode::ALL));
return freeze_states.at(0) == 1;
});
// RFDC NCO
// The NCO is typically only used for converters used in Real mode, therefore we
// expose it on the FE path that corresponds to the current channel in Real mode.
// RX
subtree->create<double>(rx_codec_path / "rfdc" / "freq/value")
.add_desired_subscriber([this, chan_idx](double freq) {
_rpcc->rfdc_set_nco_freq(_get_trx_string(RX_DIRECTION),
get_block_id().get_block_count(),
chan_idx,
freq,
static_cast<size_t>(ch_mode::REAL));
})
.set_publisher([this, chan_idx]() {
const auto nco_freq =
_rpcc->rfdc_get_nco_freq(_get_trx_string(RX_DIRECTION),
get_block_id().get_block_count(),
chan_idx,
static_cast<size_t>(ch_mode::REAL));
return nco_freq;
});
// TX
subtree->create<double>(tx_codec_path / "rfdc" / "freq/value")
.add_desired_subscriber([this, chan_idx](double freq) {
_rpcc->rfdc_set_nco_freq(_get_trx_string(TX_DIRECTION),
get_block_id().get_block_count(),
chan_idx,
freq,
static_cast<size_t>(ch_mode::REAL));
})
.set_publisher([this, chan_idx]() {
const auto nco_freq =
_rpcc->rfdc_get_nco_freq(_get_trx_string(TX_DIRECTION),
get_block_id().get_block_count(),
chan_idx,
static_cast<size_t>(ch_mode::REAL));
return nco_freq;
});
}
}
void x400_radio_control_impl::_validate_master_clock_rate_args()
{
// auto block_args = get_block_args(); // FIXME use the block args!
// Comparing master_clock_rate with get_rate() further down is utterly
// pointless, because get_rate() gets initialized from the DB RPC call
// that returns us the MCR. What we need to do is to parse the block args,
// check if there was a master_clock_rate key in there, and then compare
// those two. FIXME resolve this.
// Note: MCR gets set during the init() call (prior to this), which takes
// in arguments from the device args. So if the block args contain a
// master_clock_rate key, then it should better be whatever the device is
// configured to do.
const double master_clock_rate = _db_rpcc->get_master_clock_rate();
if (!uhd::math::frequencies_are_equal(get_rate(), master_clock_rate)) {
throw uhd::runtime_error(
str(boost::format("Master clock rate mismatch. Device returns %f MHz, "
"but should have been %f MHz.")
% (master_clock_rate / 1e6) % (get_rate() / 1e6)));
}
RFNOC_LOG_DEBUG("Master Clock Rate is: " << (master_clock_rate / 1e6) << " MHz.");
}
void x400_radio_control_impl::_init_mpm()
{
// Init sensors
for (const auto& dir : std::vector<direction_t>{RX_DIRECTION, TX_DIRECTION}) {
const size_t num_chans = (dir == RX_DIRECTION) ? get_num_input_ports()
: get_num_output_ports();
for (size_t chan_idx = 0; chan_idx < num_chans; chan_idx++) {
_init_mpm_sensors(dir, chan_idx);
}
}
}
// @TODO: This should be a method on direction_t
// (or otherwise not duplicated from the implementation in zbx)
std::string x400_radio_control_impl::_get_trx_string(const direction_t dir) const
{
if (dir == RX_DIRECTION) {
return "rx";
} else if (dir == TX_DIRECTION) {
return "tx";
} else {
UHD_THROW_INVALID_CODE_PATH();
}
}
void x400_radio_control_impl::_init_mpm_sensors(
const direction_t dir, const size_t chan_idx)
{
UHD_ASSERT_THROW(dir == RX_DIRECTION || dir == TX_DIRECTION);
const size_t num_chans = (dir == RX_DIRECTION) ? get_num_input_ports()
: get_num_output_ports();
UHD_ASSERT_THROW(chan_idx < num_chans);
const std::string trx = _get_trx_string(dir);
const fs_path fe_path = fs_path("dboard")
/ (dir == RX_DIRECTION ? "rx_frontends" : "tx_frontends")
/ chan_idx;
auto sensor_list = _db_rpcc->get_sensors(trx);
RFNOC_LOG_TRACE("Chan " << chan_idx << ": Found " << sensor_list.size() << " " << trx
<< " sensors.");
for (const auto& sensor_name : sensor_list) {
RFNOC_LOG_TRACE("Adding " << trx << " sensor " << sensor_name);
get_tree()
->create<sensor_value_t>(fe_path / "sensors" / sensor_name)
.add_coerced_subscriber([](const sensor_value_t&) {
throw uhd::runtime_error("Attempting to write to sensor!");
})
.set_publisher([this, trx, sensor_name, chan_idx]() {
return sensor_value_t(
this->_db_rpcc->get_sensor(trx, sensor_name, chan_idx));
});
}
}
fs_path x400_radio_control_impl::_get_db_fe_path(
const size_t chan, const direction_t dir) const
{
const std::string trx = _get_trx_string(dir);
return DB_PATH / (trx + "_frontends") / get_dboard_fe_from_chan(chan, dir);
}
double x400_radio_control_impl::set_rate(const double rate)
{
// X400 does not support runtime rate changes
if (!uhd::math::frequencies_are_equal(rate, get_rate())) {
RFNOC_LOG_WARNING("Requesting invalid sampling rate from device: "
<< (rate / 1e6)
<< " MHz. Actual rate is: " << (get_rate() / 1e6) << " MHz.");
}
return get_rate();
}
std::vector<std::string> x400_radio_control_impl::get_gpio_banks() const
{
if (!_gpios) {
return {};
}
return {x400::GPIO_BANK_NAME};
}
uint32_t x400_radio_control_impl::get_gpio_attr(
const std::string& bank, const std::string& attr)
{
if (!_gpios) {
throw uhd::runtime_error("X410 does not have sufficient GPIO support!");
}
std::lock_guard<std::recursive_mutex> l(_lock);
if (bank != x400::GPIO_BANK_NAME) {
throw uhd::key_error("Invalid GPIO bank " + bank);
}
if (usrp::gpio_atr::gpio_attr_rev_map.count(attr) == 0) {
throw uhd::key_error("Invalid GPIO attribute " + attr);
}
return _gpios->get_gpio_attr(usrp::gpio_atr::gpio_attr_rev_map.at(attr));
}
void x400_radio_control_impl::set_gpio_attr(
const std::string& bank, const std::string& attr, const uint32_t value)
{
if (!_gpios) {
throw uhd::runtime_error("X410 does not have sufficient GPIO support!");
}
std::lock_guard<std::recursive_mutex> l(_lock);
if (bank != x400::GPIO_BANK_NAME) {
throw uhd::key_error("Invalid GPIO bank " + bank);
}
if (usrp::gpio_atr::gpio_attr_rev_map.count(attr) == 0) {
throw uhd::key_error("Invalid GPIO attribute " + attr);
}
_gpios->set_gpio_attr(usrp::gpio_atr::gpio_attr_rev_map.at(attr), value);
}
eeprom_map_t x400_radio_control_impl::get_db_eeprom()
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_db_eeprom();
}
std::vector<uhd::usrp::pwr_cal_mgr::sptr> x400_radio_control_impl::get_pwr_mgr(
const uhd::direction_t trx)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_pwr_mgr(trx);
}
std::string x400_radio_control_impl::get_tx_antenna(const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_antenna(chan);
}
std::vector<std::string> x400_radio_control_impl::get_tx_antennas(const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_antennas(chan);
}
void x400_radio_control_impl::set_tx_antenna(const std::string& ant, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
_daughterboard->set_tx_antenna(ant, chan);
}
std::string x400_radio_control_impl::get_rx_antenna(const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_antenna(chan);
}
std::vector<std::string> x400_radio_control_impl::get_rx_antennas(const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_antennas(chan);
}
void x400_radio_control_impl::set_rx_antenna(const std::string& ant, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
_daughterboard->set_rx_antenna(ant, chan);
}
double x400_radio_control_impl::get_tx_frequency(const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_frequency(chan);
}
double x400_radio_control_impl::set_tx_frequency(const double freq, size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->set_tx_frequency(freq, chan);
}
void x400_radio_control_impl::set_tx_tune_args(
const uhd::device_addr_t& args, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
_daughterboard->set_tx_tune_args(args, chan);
}
uhd::freq_range_t x400_radio_control_impl::get_tx_frequency_range(const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_frequency_range(chan);
}
double x400_radio_control_impl::get_rx_frequency(const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_frequency(chan);
}
double x400_radio_control_impl::set_rx_frequency(const double freq, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->set_rx_frequency(freq, chan);
}
void x400_radio_control_impl::set_rx_tune_args(
const uhd::device_addr_t& args, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
_daughterboard->set_rx_tune_args(args, chan);
}
uhd::freq_range_t x400_radio_control_impl::get_rx_frequency_range(const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_frequency_range(chan);
}
std::vector<std::string> x400_radio_control_impl::get_tx_gain_names(
const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_gain_names(chan);
}
uhd::gain_range_t x400_radio_control_impl::get_tx_gain_range(const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_gain_range(chan);
}
uhd::gain_range_t x400_radio_control_impl::get_tx_gain_range(
const std::string& name, const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_gain_range(name, chan);
}
double x400_radio_control_impl::get_tx_gain(const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_gain(chan);
}
double x400_radio_control_impl::get_tx_gain(const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_gain(name, chan);
}
double x400_radio_control_impl::set_tx_gain(const double gain, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->set_tx_gain(gain, chan);
}
double x400_radio_control_impl::set_tx_gain(
const double gain, const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->set_tx_gain(gain, name, chan);
}
std::vector<std::string> x400_radio_control_impl::get_rx_gain_names(
const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_gain_names(chan);
}
uhd::gain_range_t x400_radio_control_impl::get_rx_gain_range(const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_gain_range(chan);
}
uhd::gain_range_t x400_radio_control_impl::get_rx_gain_range(
const std::string& name, const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_gain_range(name, chan);
}
double x400_radio_control_impl::get_rx_gain(const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_gain(chan);
}
double x400_radio_control_impl::get_rx_gain(const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_gain(name, chan);
}
double x400_radio_control_impl::set_rx_gain(const double gain, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->set_rx_gain(gain, chan);
}
double x400_radio_control_impl::set_rx_gain(
const double gain, const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->set_rx_gain(gain, name, chan);
}
void x400_radio_control_impl::set_rx_agc(const bool enable, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
_daughterboard->set_rx_agc(enable, chan);
}
meta_range_t x400_radio_control_impl::get_tx_bandwidth_range(size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_bandwidth_range(chan);
}
double x400_radio_control_impl::get_tx_bandwidth(const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_bandwidth(chan);
}
double x400_radio_control_impl::set_tx_bandwidth(
const double bandwidth, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->set_tx_bandwidth(bandwidth, chan);
}
meta_range_t x400_radio_control_impl::get_rx_bandwidth_range(size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_bandwidth_range(chan);
}
double x400_radio_control_impl::get_rx_bandwidth(const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_bandwidth(chan);
}
double x400_radio_control_impl::set_rx_bandwidth(
const double bandwidth, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->set_rx_bandwidth(bandwidth, chan);
}
std::vector<std::string> x400_radio_control_impl::get_rx_lo_names(const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_lo_names(chan);
}
std::vector<std::string> x400_radio_control_impl::get_rx_lo_sources(
const std::string& name, const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_lo_sources(name, chan);
}
freq_range_t x400_radio_control_impl::get_rx_lo_freq_range(
const std::string& name, const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_lo_freq_range(name, chan);
}
void x400_radio_control_impl::set_rx_lo_source(
const std::string& src, const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
_daughterboard->set_rx_lo_source(src, name, chan);
}
std::string x400_radio_control_impl::get_rx_lo_source(
const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_lo_source(name, chan);
}
void x400_radio_control_impl::set_rx_lo_export_enabled(
bool enabled, const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
_daughterboard->set_rx_lo_export_enabled(enabled, name, chan);
}
bool x400_radio_control_impl::get_rx_lo_export_enabled(
const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_lo_export_enabled(name, chan);
}
double x400_radio_control_impl::set_rx_lo_freq(
double freq, const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->set_rx_lo_freq(freq, name, chan);
}
double x400_radio_control_impl::get_rx_lo_freq(const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_rx_lo_freq(name, chan);
}
std::vector<std::string> x400_radio_control_impl::get_tx_lo_names(const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_lo_names(chan);
}
std::vector<std::string> x400_radio_control_impl::get_tx_lo_sources(
const std::string& name, const size_t chan) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_lo_sources(name, chan);
}
freq_range_t x400_radio_control_impl::get_tx_lo_freq_range(
const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_lo_freq_range(name, chan);
}
void x400_radio_control_impl::set_tx_lo_source(
const std::string& src, const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
_daughterboard->set_tx_lo_source(src, name, chan);
}
std::string x400_radio_control_impl::get_tx_lo_source(
const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_lo_source(name, chan);
}
void x400_radio_control_impl::set_tx_lo_export_enabled(
const bool enabled, const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
_daughterboard->set_tx_lo_export_enabled(enabled, name, chan);
}
bool x400_radio_control_impl::get_tx_lo_export_enabled(
const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_lo_export_enabled(name, chan);
}
double x400_radio_control_impl::set_tx_lo_freq(
const double freq, const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->set_tx_lo_freq(freq, name, chan);
}
double x400_radio_control_impl::get_tx_lo_freq(const std::string& name, const size_t chan)
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_tx_lo_freq(name, chan);
}
void x400_radio_control_impl::set_command_time(uhd::time_spec_t time, const size_t chan)
{
node_t::set_command_time(time, chan);
_daughterboard->set_command_time(time, chan);
}
/**************************************************************************
* Sensor API
*************************************************************************/
std::vector<std::string> x400_radio_control_impl::get_rx_sensor_names(
const size_t chan) const
{
const fs_path sensor_path = _get_db_fe_path(chan, RX_DIRECTION) / "sensors";
if (get_tree()->exists(sensor_path)) {
return get_tree()->list(sensor_path);
}
return {};
}
uhd::sensor_value_t x400_radio_control_impl::get_rx_sensor(
const std::string& name, const size_t chan)
{
return get_tree()
->access<uhd::sensor_value_t>(
_get_db_fe_path(chan, RX_DIRECTION) / "sensors" / name)
.get();
}
std::vector<std::string> x400_radio_control_impl::get_tx_sensor_names(
const size_t chan) const
{
const fs_path sensor_path = _get_db_fe_path(chan, TX_DIRECTION) / "sensors";
if (get_tree()->exists(sensor_path)) {
return get_tree()->list(sensor_path);
}
return {};
}
uhd::sensor_value_t x400_radio_control_impl::get_tx_sensor(
const std::string& name, const size_t chan)
{
return get_tree()
->access<uhd::sensor_value_t>(
_get_db_fe_path(chan, TX_DIRECTION) / "sensors" / name)
.get();
}
/**************************************************************************
* Radio Identification API Calls
*************************************************************************/
size_t x400_radio_control_impl::get_chan_from_dboard_fe(
const std::string& fe, const uhd::direction_t direction) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_chan_from_dboard_fe(fe, direction);
}
std::string x400_radio_control_impl::get_dboard_fe_from_chan(
const size_t chan, const uhd::direction_t direction) const
{
std::lock_guard<std::recursive_mutex> l(_lock);
return _daughterboard->get_dboard_fe_from_chan(chan, direction);
}
UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT(
x400_radio_control, RADIO_BLOCK, X400, "Radio", true, CLOCK_KEY_AUTO, CLOCK_KEY_AUTO)
}} // namespace uhd::rfnoc