mirror of
https://github.com/EttusResearch/uhd.git
synced 2026-10-25 17:36:10 +00:00
SelfCal: Expose config parameters
Exposes the following ADC self-cal parameters on X4xx devices to be used in the argument string: -cal_freq -cal_dac_mux_i -cal_dac_mux_q -cal_tone_duration -cal_ch_list -cal_delay
This commit is contained in:
parent
51c1a74eee
commit
b4c24091ed
@ -8,7 +8,10 @@
|
||||
|
||||
#include <uhd/config.hpp>
|
||||
#include <uhd/features/discoverable_feature.hpp>
|
||||
#include <uhd/types/device_addr.hpp>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace uhd { namespace features {
|
||||
|
||||
@ -41,6 +44,10 @@ public:
|
||||
// signal chain, use the rx_codec/<n>/calibration_frozen property on the
|
||||
// motherboard's property tree.
|
||||
virtual void run(const size_t chan) = 0;
|
||||
|
||||
//! Runs calibration on the specified channel but takes custom calibration
|
||||
// parameters.
|
||||
virtual void run(const size_t chan, const uhd::device_addr_t params) = 0;
|
||||
};
|
||||
|
||||
}} // namespace uhd::features
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
|
||||
#include <uhd/config.hpp>
|
||||
#include <uhd/exception.hpp>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
@ -29,6 +30,22 @@ inline T hexstr_cast(const std::string& in)
|
||||
return x;
|
||||
}
|
||||
|
||||
//! Convert hexadecimal, decimal, octal or other strings that support the >>
|
||||
//! operator into a value depending on the prefix.
|
||||
//
|
||||
// Example:
|
||||
// 10, 0x10, 010 get parsed to decimal 10, 16, 8.
|
||||
// uint32_t x = fromstr_cast<uint32_t>("0xaffe");
|
||||
// Uses istringstream.
|
||||
template <typename T>
|
||||
inline T fromstr_cast(const std::string& in)
|
||||
{
|
||||
T x;
|
||||
std::istringstream is(in);
|
||||
is >> std::setbase(0) >> x;
|
||||
return x;
|
||||
}
|
||||
|
||||
//! Generic cast-from-string function
|
||||
template <typename data_t>
|
||||
data_t from_str(const std::string&)
|
||||
|
||||
@ -57,6 +57,7 @@ public:
|
||||
0,
|
||||
0,
|
||||
"calib_mode1",
|
||||
2000,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -79,13 +79,16 @@ public:
|
||||
uhd::usrp::x400::adc_self_cal_params_t get_adc_self_cal_params(const double) override
|
||||
{
|
||||
// FBX uses a fixed low freq for both TX and RX
|
||||
return {100e6, // rx_freq
|
||||
return {
|
||||
100e6, // rx_freq
|
||||
100e6, // tx_freq
|
||||
{0x7FFF, 0}, // output full scale dac mux
|
||||
100, // delay
|
||||
4000, // under
|
||||
4192, // over
|
||||
"calib_mode2"};
|
||||
"calib_mode2",
|
||||
2000, // 2 seconds were found to be sufficient
|
||||
};
|
||||
}
|
||||
|
||||
bool select_adc_self_cal_gain(size_t) override;
|
||||
|
||||
@ -45,6 +45,7 @@ public:
|
||||
0,
|
||||
0,
|
||||
"calib_mode1",
|
||||
2000,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -27,6 +27,7 @@ struct adc_self_cal_params_t
|
||||
uint32_t threshold_under;
|
||||
uint32_t threshold_over;
|
||||
std::string calibration_mode;
|
||||
uint32_t calibration_time;
|
||||
};
|
||||
|
||||
/*! Interface for daughterboards which support being plugged into a X400 motherboard.
|
||||
|
||||
@ -107,6 +107,7 @@ public:
|
||||
8192, // over, Set the threshold to detect half-scale
|
||||
// setup_threshold call uses 14-bit ADC values
|
||||
"calib_mode2", // calibration_mode
|
||||
2000, // 2 seconds were found to be sufficent
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
//
|
||||
|
||||
#include "adc_self_calibration.hpp"
|
||||
#include <uhd/utils/cast.hpp>
|
||||
#include <uhd/utils/log.hpp>
|
||||
#include <uhd/utils/scope_exit.hpp>
|
||||
#include <chrono>
|
||||
@ -27,7 +28,7 @@ adc_self_calibration::adc_self_calibration(uhd::usrp::x400_rpc_iface::sptr rpcc,
|
||||
{
|
||||
}
|
||||
|
||||
void adc_self_calibration::run(size_t chan)
|
||||
void adc_self_calibration::run(size_t chan, uhd::device_addr_t params)
|
||||
{
|
||||
const auto tx_gain_profile =
|
||||
_daughterboard->get_tx_gain_profile_api()->get_gain_profile(chan);
|
||||
@ -38,13 +39,81 @@ void adc_self_calibration::run(size_t chan)
|
||||
"profile for RX or TX is not 'default'.");
|
||||
}
|
||||
|
||||
// The frequency that we need to feed into the ADC is, by decree,
|
||||
// 13109 / 32768 times the ADC sample rate. (approx. 1.2GHz for a 3Gsps rate)
|
||||
// This is at 0.4 * Fs, right on the boundary between modes 1 and 2 for the
|
||||
// ADC self-cal.
|
||||
// The frequency that we need to feed into the ADC is, by decree and if not specified
|
||||
// differently by the user, 13109 / 32768 times the ADC sample rate for X410.
|
||||
// (approx. 1.2GHz for a 3Gsps rate) This is at 0.4 * Fs, right on the boundary
|
||||
// between modes 1 and 2 for the ADC self-cal. But devices may also specify different
|
||||
// values in their ADC self cal params.
|
||||
const double cal_tone_freq = _daughterboard->get_converter_rate() * 13109.0 / 32768.0;
|
||||
|
||||
const auto cal_params = _daughterboard->get_adc_self_cal_params(cal_tone_freq);
|
||||
auto cal_params = _daughterboard->get_adc_self_cal_params(cal_tone_freq);
|
||||
|
||||
if (params.has_key("cal_freq")) {
|
||||
const double cal_freq = params.cast<double>("cal_freq", cal_params.rx_freq);
|
||||
if (cal_freq < 0 or cal_freq > 0.4 * _daughterboard->get_converter_rate()) {
|
||||
RFNOC_LOG_WARNING("Invalid value for `cal_freq`: "
|
||||
<< cal_freq
|
||||
<< ". Valid values are "
|
||||
"between 0 Hz and 0.4 x converter_rate. Using default "
|
||||
<< cal_params.rx_freq / 1e6 << " MHz.");
|
||||
} else {
|
||||
cal_params.rx_freq = cal_freq;
|
||||
cal_params.tx_freq = cal_freq;
|
||||
// 0.4 * Fs/2 as limit between cal mode and 2 according to PG269
|
||||
if (cal_freq <= (0.4 * _daughterboard->get_converter_rate())) {
|
||||
cal_params.calibration_mode = "calib_mode2";
|
||||
} else {
|
||||
// This will never be reached as long as we want to limit ourselves to
|
||||
// calib_mode2
|
||||
cal_params.calibration_mode = "calib_mode1";
|
||||
}
|
||||
RFNOC_LOG_DEBUG("Custom cal_freq: " << cal_freq << " => Calibration Mode: "
|
||||
<< cal_params.calibration_mode);
|
||||
}
|
||||
}
|
||||
if (params.has_key("cal_dac_mux_i")) {
|
||||
const int32_t dac_mux_i =
|
||||
uhd::cast::fromstr_cast<int32_t>(params.get("cal_dac_mux_i"));
|
||||
if (dac_mux_i < 0 or dac_mux_i > 0xFFFF) {
|
||||
RFNOC_LOG_WARNING("Invalid value for `cal_dac_mux_i`: "
|
||||
<< dac_mux_i << ". Using default value "
|
||||
<< cal_params.dac_iq_values.real());
|
||||
} else {
|
||||
cal_params.dac_iq_values.real(dac_mux_i);
|
||||
RFNOC_LOG_DEBUG("Custom cal_dac_mux_i: (" << dac_mux_i);
|
||||
}
|
||||
}
|
||||
if (params.has_key("cal_dac_mux_q")) {
|
||||
const int32_t dac_mux_q =
|
||||
uhd::cast::fromstr_cast<int32_t>(params.get("cal_dac_mux_q"));
|
||||
if (dac_mux_q < 0 or dac_mux_q > 0xFFFF) {
|
||||
RFNOC_LOG_WARNING("Invalid value for `cal_dac_mux_q`: "
|
||||
<< dac_mux_q << ". Using default value "
|
||||
<< cal_params.dac_iq_values.imag());
|
||||
} else {
|
||||
cal_params.dac_iq_values.imag(dac_mux_q);
|
||||
RFNOC_LOG_DEBUG("Custom cal_dac_mux_q: (" << dac_mux_q);
|
||||
}
|
||||
}
|
||||
if (params.has_key("cal_tone_duration")) {
|
||||
const uint32_t cal_time =
|
||||
params.cast<uint32_t>("cal_tone_duration", cal_params.calibration_time);
|
||||
if (cal_time > MAX_CAL_DURATION) {
|
||||
RFNOC_LOG_WARNING("Invalid value for `cal_tone_duration`: "
|
||||
<< cal_time << ". Maximum value: " << MAX_CAL_DURATION
|
||||
<< ". Using default " << cal_params.calibration_time
|
||||
<< " ms.");
|
||||
} else {
|
||||
cal_params.calibration_time = cal_time;
|
||||
RFNOC_LOG_DEBUG("Custom cal_tone_duration: " << cal_time << " ms");
|
||||
}
|
||||
}
|
||||
if (params.has_key("cal_delay")) {
|
||||
const uint32_t threshold_delay =
|
||||
params.cast<uint32_t>("cal_delay", cal_params.threshold_delay);
|
||||
cal_params.threshold_delay = threshold_delay;
|
||||
RFNOC_LOG_DEBUG("Custom cal_delay: " << cal_params.threshold_delay << " ms");
|
||||
}
|
||||
|
||||
// Switch to CAL_LOOPBACK and save the current antenna
|
||||
const auto rx_antenna = _daughterboard->get_rx_antenna(chan);
|
||||
@ -153,8 +222,14 @@ void adc_self_calibration::run(size_t chan)
|
||||
|
||||
// Let the ADC calibrate
|
||||
// 2000ms was found experimentally to be sufficient
|
||||
constexpr auto calibration_time = 2000ms;
|
||||
auto calibration_time = std::chrono::milliseconds(cal_params.calibration_time);
|
||||
std::this_thread::sleep_for(calibration_time);
|
||||
}
|
||||
|
||||
void adc_self_calibration::run(size_t chan)
|
||||
{
|
||||
const uhd::device_addr_t params;
|
||||
adc_self_calibration::run(chan, params);
|
||||
}
|
||||
|
||||
}} // namespace uhd::features
|
||||
|
||||
@ -26,6 +26,13 @@ public:
|
||||
|
||||
void run(const size_t channel) override;
|
||||
|
||||
void run(const size_t chan, const uhd::device_addr_t params) override;
|
||||
|
||||
// Maximum duration for self-cal in ms. From tests we found that 2 seconds are good,
|
||||
// so we limit the maximum value to twice 2 seconds to keep the overall calibration
|
||||
// duration at a convenient level.
|
||||
const uint32_t MAX_CAL_DURATION = 4000;
|
||||
|
||||
private:
|
||||
//! Reference to the RPC client
|
||||
uhd::usrp::x400_rpc_iface::sptr _rpcc;
|
||||
|
||||
@ -266,14 +266,21 @@ x400_radio_control_impl::x400_radio_control_impl(make_args_ptr make_args)
|
||||
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() << "...");
|
||||
auto args = get_block_args();
|
||||
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();
|
||||
for (size_t i = 0; i < num_channels; i++) {
|
||||
RFNOC_LOG_INFO("Calibrating channel " << i << "...");
|
||||
self_cal.run(i);
|
||||
num_calibrations++;
|
||||
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) {
|
||||
RFNOC_LOG_INFO("Calibrating channel " << abs_ch << "...");
|
||||
self_cal.run(i, args);
|
||||
num_calibrations++;
|
||||
}
|
||||
}
|
||||
if (num_calibrations > 0) {
|
||||
RFNOC_LOG_DEBUG(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user