fbx: Add guided mode for X440

Since X440 is special in terms of operation in different Nyquist zones
users may stumble upon unexpected RF performance compared to other
devices if frequencies are chosen close to a Nyquist boundary. The
guided mode provides more information about the Nyquist zone in which
the chosen frequency is located in the info log. For disabling the
guided mode, `guided_mode=false` can be added to the UHD config file
in the "Global" section.
This commit is contained in:
Martin Anderseck 2024-05-24 15:00:27 +02:00 committed by Joerg Hofrichter
parent 479f65d9d6
commit 7f6e8e3276
12 changed files with 137 additions and 112 deletions

View File

@ -235,122 +235,15 @@ public:
* \tparam argN_t Data type of the Nth argument to the constructor
*
* \param container A shared pointer to the container to add the node to
* \param arg1 First arg to ctor
* \param args arguments to the ctor
* \param ...
*
*/
template <typename worker_t, typename arg1_t>
template <typename worker_t, typename... Args>
inline static void add_worker_node(
expert_container::sptr container, arg1_t const& arg1)
expert_container::sptr container, Args const&... args)
{
container->add_worker(new worker_t(arg1));
}
template <typename worker_t, typename arg1_t, typename arg2_t>
inline static void add_worker_node(
expert_container::sptr container, arg1_t const& arg1, arg2_t const& arg2)
{
container->add_worker(new worker_t(arg1, arg2));
}
template <typename worker_t, typename arg1_t, typename arg2_t, typename arg3_t>
inline static void add_worker_node(expert_container::sptr container,
arg1_t const& arg1,
arg2_t const& arg2,
arg3_t const& arg3)
{
container->add_worker(new worker_t(arg1, arg2, arg3));
}
template <typename worker_t,
typename arg1_t,
typename arg2_t,
typename arg3_t,
typename arg4_t>
inline static void add_worker_node(expert_container::sptr container,
arg1_t const& arg1,
arg2_t const& arg2,
arg3_t const& arg3,
arg4_t const& arg4)
{
container->add_worker(new worker_t(arg1, arg2, arg3, arg4));
}
template <typename worker_t,
typename arg1_t,
typename arg2_t,
typename arg3_t,
typename arg4_t,
typename arg5_t>
inline static void add_worker_node(expert_container::sptr container,
arg1_t const& arg1,
arg2_t const& arg2,
arg3_t const& arg3,
arg4_t const& arg4,
arg5_t const& arg5)
{
container->add_worker(new worker_t(arg1, arg2, arg3, arg4, arg5));
}
template <typename worker_t,
typename arg1_t,
typename arg2_t,
typename arg3_t,
typename arg4_t,
typename arg5_t,
typename arg6_t>
inline static void add_worker_node(expert_container::sptr container,
arg1_t const& arg1,
arg2_t const& arg2,
arg3_t const& arg3,
arg4_t const& arg4,
arg5_t const& arg5,
arg6_t const& arg6)
{
container->add_worker(new worker_t(arg1, arg2, arg3, arg4, arg5, arg6));
}
template <typename worker_t,
typename arg1_t,
typename arg2_t,
typename arg3_t,
typename arg4_t,
typename arg5_t,
typename arg6_t,
typename arg7_t>
inline static void add_worker_node(expert_container::sptr container,
arg1_t const& arg1,
arg2_t const& arg2,
arg3_t const& arg3,
arg4_t const& arg4,
arg5_t const& arg5,
arg6_t const& arg6,
arg7_t const& arg7)
{
container->add_worker(new worker_t(arg1, arg2, arg3, arg4, arg5, arg6, arg7));
}
template <typename worker_t,
typename arg1_t,
typename arg2_t,
typename arg3_t,
typename arg4_t,
typename arg5_t,
typename arg6_t,
typename arg7_t,
typename arg8_t>
inline static void add_worker_node(expert_container::sptr container,
arg1_t const& arg1,
arg2_t const& arg2,
arg3_t const& arg3,
arg4_t const& arg4,
arg5_t const& arg5,
arg6_t const& arg6,
arg7_t const& arg7,
arg7_t const& arg8)
{
container->add_worker(
new worker_t(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8));
container->add_worker(new worker_t(args...));
}
};
}} // namespace uhd::experts

View File

@ -77,4 +77,7 @@ UHD_API int from_str(const std::string& val);
template <>
UHD_API std::string from_str(const std::string& val);
//! Create an ordinal string from a number.
UHD_API std::string to_ordinal_string(int val);
}} // namespace uhd::cast

View File

@ -164,7 +164,7 @@ struct UHD_API logging_info
/*! Set the global log level
*
* The global log level gets applied before the specific log level.
* So, if the global log level is 'info', no logger can can print
* So, if the global log level is 'info', no logger can print
* messages at level 'debug' or below.
*/
UHD_API void set_log_level(uhd::log::severity_level level);

View File

@ -209,6 +209,7 @@ public:
const uhd::fs_path fe_path,
const uhd::direction_t trx,
const size_t chan,
const double rfdc_rate,
const std::string rpc_prefix,
int db_idx,
uhd::usrp::x400_rpc_iface::sptr rpcc)
@ -224,6 +225,7 @@ public:
, _db_idx(db_idx)
, _rpcc(rpcc)
, _trx(trx)
, _rfdc_rate(rfdc_rate)
, _chan(chan)
{
bind_accessor(_rfdc_freq_desired);
@ -255,6 +257,7 @@ private:
const size_t _db_idx;
uhd::usrp::x400_rpc_iface::sptr _rpcc;
const uhd::direction_t _trx;
const double _rfdc_rate;
const size_t _chan;
};

View File

@ -0,0 +1,20 @@
//
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
#pragma once
#include <uhd/utils/log.hpp>
#include <uhdlib/utils/prefs.hpp>
#define UHD_LOG_GUIDE(message) \
if (uhd::prefs::is_guided_mode()) { \
UHD_LOG_INFO("Guide", message); \
}
#define UHD_LOG_IF_GUIDED(statement) \
if (uhd::prefs::is_guided_mode()) { \
statement; \
}

View File

@ -12,6 +12,11 @@
namespace uhd { namespace prefs {
/*! For general configuration items not targeted to a specific device we have the
* "General" section.
*/
constexpr char GLOBAL_SECTION[] = "Global";
/*! Return a reference to an object representing the UHD config file
* state.
*
@ -19,6 +24,18 @@ namespace uhd { namespace prefs {
*/
config_parser& get_uhd_config();
/*! Guided mode helps the user by providing additional information about current settings
* via info level log messages. In case this is too much it can be turned off in the
* config file. This is the check if guided mode is enabled.
*/
bool is_guided_mode();
/*! Sets the `guided_mode_suspended` flag in the config to true. */
void suspend_guided_mode();
/*! Sets the `guided_mode_suspended` flag in the config to false. */
void resume_guided_mode();
/*! Convenience function to update device args with settings from
* config files.
*

View File

@ -154,6 +154,7 @@ void fbx_dboard_impl::_init_experts(expert_container::sptr expert,
fe_path,
trx,
chan_idx,
_rfdc_rate,
_rpc_prefix,
_db_idx,
_mb_rpcc);

View File

@ -6,10 +6,12 @@
#include <uhd/types/direction.hpp>
#include <uhd/utils/assert_has.hpp>
#include <uhd/utils/cast.hpp>
#include <uhd/utils/log.hpp>
#include <uhd/utils/math.hpp>
#include <uhdlib/usrp/dboard/fbx/fbx_expert.hpp>
#include <uhdlib/utils/interpolation.hpp>
#include <uhdlib/utils/log.hpp>
#include <algorithm>
#include <array>
@ -127,6 +129,44 @@ void fbx_rfdc_freq_expert::resolve()
_get_trx_string(_trx), _db_idx, _chan, desired_rfdc_freq);
_if_frequency_coerced = _rfdc_freq_coerced;
_coerced_frequency = _if_frequency_coerced;
// Both are dirty during the session start and we don't want to spam the user with
// logs there.
if (!(_rfdc_freq_desired.is_dirty() && _frequency_desired.is_dirty())) {
UHD_LOG_IF_GUIDED(
auto nyquist_boundary = _rfdc_rate / 2;
uint32_t nyquist_zone =
std::floor(_if_frequency_coerced / nyquist_boundary) + 1;
// Provide more information about implications of chosen frequency.
UHD_LOG_GUIDE(_get_trx_string(_trx)
<< " frequency " << (_if_frequency_coerced / 1e6)
<< " MHz on DB " << _db_idx << ", channel " << _chan
<< " is in the " << uhd::cast::to_ordinal_string(nyquist_zone)
<< " Nyquist zone.");
// warnings according to
// https://kb.ettus.com/About_Sampling_Rates_and_Master_Clock_Rates_for_the_USRP_X440#Aliases_and_Nyquist_Zones
if ((nyquist_zone > 2 && _trx == TX_DIRECTION)
|| (nyquist_zone > 3 && _trx == RX_DIRECTION)) {
UHD_LOG_GUIDE(
_get_trx_string(_trx)
<< " operation in this Nyquist zone will result in decreased RF "
"performance.");
} auto passband_freq = static_cast<int>(std::round(_if_frequency_coerced))
% static_cast<int>(nyquist_boundary);
if (passband_freq < 0.1 * nyquist_boundary
|| passband_freq > 0.9 * nyquist_boundary) {
auto pass_low_mhz =
(nyquist_zone * nyquist_boundary - 0.9 * nyquist_boundary) / 1e6;
auto pass_high_mhz =
(nyquist_zone * nyquist_boundary - 0.1 * nyquist_boundary) / 1e6;
UHD_LOG_GUIDE(_get_trx_string(_trx)
<< " frequency " << (_if_frequency_coerced / 1e6)
<< " MHz is outside the 80% Nyquist passband ("
<< pass_low_mhz << " MHz to " << pass_high_mhz
<< " MHz for this Nyquist zone). This "
<< "may result in aliasing and undesired effects.");
});
}
}
void fbx_sync_expert::resolve()

View File

@ -18,6 +18,7 @@
#include <uhdlib/usrp/dboard/fbx/fbx_dboard.hpp>
#include <uhdlib/usrp/dboard/null_dboard.hpp>
#include <uhdlib/usrp/dboard/zbx/zbx_dboard.hpp>
#include <uhdlib/utils/prefs.hpp>
namespace uhd { namespace rfnoc {
@ -247,6 +248,13 @@ x400_radio_control_impl::x400_radio_control_impl(make_args_ptr make_args)
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(); });
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

View File

@ -60,3 +60,12 @@ std::string cast::from_str(const std::string& val)
{
return val;
}
std::string cast::to_ordinal_string(int val)
{
const std::string ordinals = "thstndrd";
std::string result = std::to_string(val);
int index = (val % 10) * (val % 10 < 4 && !(10 < val % 100 && val % 100 < 14));
result += ordinals.substr(2 * index, 2);
return result;
}

View File

@ -4,6 +4,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
#include <uhd/utils/cast.hpp>
#include <uhd/utils/log.hpp>
#include <uhd/utils/paths.hpp>
#include <uhdlib/utils/paths.hpp>
@ -116,6 +117,28 @@ config_parser& uhd::prefs::get_uhd_config()
return _conf_files;
}
bool uhd::prefs::is_guided_mode()
{
const std::string guided_mode = uhd::prefs::get_uhd_config().get<std::string>(
uhd::prefs::GLOBAL_SECTION, "guided_mode", "true");
const std::string guided_mode_suspended =
uhd::prefs::get_uhd_config().get<std::string>(
uhd::prefs::GLOBAL_SECTION, "guided_mode_suspended", "false");
return cast::from_str<bool>(guided_mode)
&& !cast::from_str<bool>(guided_mode_suspended);
}
void uhd::prefs::suspend_guided_mode()
{
uhd::prefs::get_uhd_config().set<std::string>(
uhd::prefs::GLOBAL_SECTION, "guided_mode_suspended", "true");
}
void uhd::prefs::resume_guided_mode()
{
uhd::prefs::get_uhd_config().set<std::string>(
uhd::prefs::GLOBAL_SECTION, "guided_mode_suspended", "false");
}
device_addr_t uhd::prefs::get_usrp_args(const uhd::device_addr_t& user_args)
{

View File

@ -417,6 +417,10 @@ IF(ENABLE_X400)
${UHD_SOURCE_DIR}/lib/usrp/cores/gpio_atr_3000.cpp
${UHD_SOURCE_DIR}/lib/usrp/cores/spi_core_4000.cpp
${UHD_SOURCE_DIR}/lib/usrp/x400/x400_internal_sync.cpp
${UHD_SOURCE_DIR}/lib/utils/prefs.cpp
${UHD_SOURCE_DIR}/lib/utils/pathslib.cpp
${UHD_SOURCE_DIR}/lib/utils/paths.cpp
${UHD_SOURCE_DIR}/lib/utils/config_parser.cpp
$<TARGET_OBJECTS:uhd_rpclib>
INCLUDE_DIRS ${UHD_SOURCE_DIR}/lib/deps/rpclib/include
INCLUDE_DIRS ${UHD_SOURCE_DIR}/lib/deps/flatbuffers/include
@ -448,6 +452,10 @@ IF(ENABLE_X400)
${UHD_SOURCE_DIR}/lib/usrp/cores/gpio_atr_3000.cpp
${UHD_SOURCE_DIR}/lib/usrp/cores/spi_core_4000.cpp
${UHD_SOURCE_DIR}/lib/usrp/x400/x400_internal_sync.cpp
${UHD_SOURCE_DIR}/lib/utils/prefs.cpp
${UHD_SOURCE_DIR}/lib/utils/pathslib.cpp
${UHD_SOURCE_DIR}/lib/utils/paths.cpp
${UHD_SOURCE_DIR}/lib/utils/config_parser.cpp
$<TARGET_OBJECTS:uhd_rpclib>
INCLUDE_DIRS ${UHD_SOURCE_DIR}/lib/deps/rpclib/include
INCLUDE_DIRS ${UHD_SOURCE_DIR}/lib/deps/flatbuffers/include