aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib')
-rw-r--r--host/lib/include/uhdlib/usrp/common/apply_corrections.hpp47
-rw-r--r--host/lib/include/uhdlib/utils/paths.hpp2
-rw-r--r--host/lib/usrp/common/apply_corrections.cpp295
-rw-r--r--host/lib/utils/paths.cpp26
4 files changed, 200 insertions, 170 deletions
diff --git a/host/lib/include/uhdlib/usrp/common/apply_corrections.hpp b/host/lib/include/uhdlib/usrp/common/apply_corrections.hpp
index dffefffea..fb9aaa8f1 100644
--- a/host/lib/include/uhdlib/usrp/common/apply_corrections.hpp
+++ b/host/lib/include/uhdlib/usrp/common/apply_corrections.hpp
@@ -14,26 +14,67 @@
namespace uhd { namespace usrp {
+/*! Apply TX DC offset or IQ imbalance corrections (RFNoC version)
+ *
+ * \param sub_tree Property tree object
+ * \param db_serial Daughterboard serial
+ * \param tx_fe_corr_path This is the path relative to \p sub_tree where the
+ * coefficients are stored. The path should end in
+ * `iq_balance/value` or `dc_offset/value` and be a
+ * complex number.
+ * \param tx_lo_freq The current LO frequency. Used to look up coefficients in the cal
+ * data set.
+ */
void apply_tx_fe_corrections(property_tree::sptr sub_tree, // starts at mboards/x
const std::string& db_serial,
const fs_path tx_fe_corr_path,
const double tx_lo_freq // actual lo freq
);
+/*! Apply TX DC offset or IQ imbalance corrections (Gen-2 USRP version)
+ *
+ * \param sub_tree Property tree object. It's the motherboard subtree, i.e.,
+ * what comes after /mboards/X.
+ * \param slot Daughterboard slot ("A" or "B"), used to auto-detect the paths.
+ * \param tx_lo_freq The current LO frequency. Used to look up coefficients in the cal
+ * data set.
+ */
void apply_tx_fe_corrections(property_tree::sptr sub_tree, // starts at mboards/x
const std::string& slot, // name of dboard slot
const double tx_lo_freq // actual lo freq
);
+
+/*! Apply RX DC offset or IQ imbalance corrections (RFNoC version)
+ *
+ * \param sub_tree Property tree object
+ * \param db_serial Daughterboard serial
+ * \param tx_fe_corr_path This is the path relative to \p sub_tree where the
+ * coefficients are stored. The path should end in
+ * `iq_balance/value` or `dc_offset/value` and be a
+ * complex number.
+ * \param tx_lo_freq The current LO frequency. Used to look up coefficients in the cal
+ * data set.
+ */
void apply_rx_fe_corrections(property_tree::sptr sub_tree, // starts at mboards/x
- const std::string& slot, // name of dboard slot
+ const std::string& db_serial,
+ const fs_path rx_fe_corr_path,
const double rx_lo_freq // actual lo freq
);
+/*! Apply RX DC offset or IQ imbalance corrections (Gen-2 USRP version)
+ *
+ * \param sub_tree Property tree object. It's the motherboard subtree, i.e.,
+ * what comes after /mboards/X.
+ * \param slot Daughterboard slot ("A" or "B"), used to auto-detect the paths.
+ * \param rx_lo_freq The current LO frequency. Used to look up coefficients in the cal
+ * data set.
+ */
void apply_rx_fe_corrections(property_tree::sptr sub_tree, // starts at mboards/x
- const std::string& db_serial,
- const fs_path rx_fe_corr_path,
+ const std::string& slot, // name of dboard slot
const double rx_lo_freq // actual lo freq
);
+
+
}} // namespace uhd::usrp
#endif /* INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP */
diff --git a/host/lib/include/uhdlib/utils/paths.hpp b/host/lib/include/uhdlib/utils/paths.hpp
index 7f0dc4046..7caac8a7d 100644
--- a/host/lib/include/uhdlib/utils/paths.hpp
+++ b/host/lib/include/uhdlib/utils/paths.hpp
@@ -18,6 +18,8 @@ namespace uhd {
*/
std::string path_expandvars(const std::string& path);
+std::string get_appdata_path(void);
+
} /* namespace uhd */
#endif /* INCLUDED_UHDLIB_UTILS_PATHS_HPP */
diff --git a/host/lib/usrp/common/apply_corrections.cpp b/host/lib/usrp/common/apply_corrections.cpp
index b75ab7d95..3cdf3d29f 100644
--- a/host/lib/usrp/common/apply_corrections.cpp
+++ b/host/lib/usrp/common/apply_corrections.cpp
@@ -5,237 +5,198 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
-#include <uhd/types/dict.hpp>
+#include <uhd/cal/container.hpp>
+#include <uhd/cal/database.hpp>
+#include <uhd/cal/iq_cal.hpp>
#include <uhd/usrp/dboard_eeprom.hpp>
#include <uhd/utils/csv.hpp>
#include <uhd/utils/log.hpp>
-#include <uhd/utils/paths.hpp>
#include <uhdlib/usrp/common/apply_corrections.hpp>
-#include <boost/filesystem.hpp>
-#include <boost/thread/mutex.hpp>
+#include <uhdlib/utils/paths.hpp>
+#include <unordered_map>
+#include <boost/filesystem.hpp> // For deprecated CSV reader only
#include <complex>
#include <cstdio>
#include <fstream>
+#include <mutex>
-namespace fs = boost::filesystem;
+using namespace uhd::usrp::cal;
-boost::mutex corrections_mutex;
-
-/***********************************************************************
- * Helper routines
- **********************************************************************/
-static double linear_interp(double x, double x0, double y0, double x1, double y1)
-{
- return y0 + (x - x0) * (y1 - y0) / (x1 - x0);
-}
+std::mutex corrections_mutex;
/***********************************************************************
* FE apply corrections implementation
**********************************************************************/
-struct fe_cal_t
-{
- double lo_freq;
- double iq_corr_real;
- double iq_corr_imag;
-};
-
-static bool fe_cal_comp(fe_cal_t a, fe_cal_t b)
-{
- return (a.lo_freq < b.lo_freq);
-}
+namespace {
-static uhd::dict<std::string, std::vector<fe_cal_t>> fe_cal_cache;
+// Cache the loaded data so we don't have to serialize on every tune
+std::unordered_map<std::string, iq_cal::sptr> fe_cal_cache;
-static bool is_same_freq(const double f1, const double f2)
+// Deprecated CSV file loader. Delete this function once we remove CSV support.
+// Then, also delete the uhd::csv module.
+bool load_legacy_fe_corrections(const std::string& cal_key,
+ const std::string& db_serial, const std::string& file_prefix)
{
- const double epsilon = 0.1;
- return ((f1 - epsilon) < f2 and (f1 + epsilon) > f2);
-}
+ namespace fs = boost::filesystem;
+ const std::string file_prefix_deprecated = file_prefix + "_cal_v0.2_";
+ // make the calibration file path
+ const fs::path cal_data_path = fs::path(uhd::get_appdata_path()) / ".uhd" / "cal"
+ / (file_prefix_deprecated + db_serial + ".csv");
+ UHD_LOG_TRACE(
+ "CAL", "Checking for deprecated CSV-based cal data at " << cal_data_path);
+ if (not fs::exists(cal_data_path)) {
+ return false;
+ }
-static std::complex<double> get_fe_correction(
- const std::string& key, const double lo_freq)
-{
- const std::vector<fe_cal_t>& datas = fe_cal_cache[key];
- if (datas.empty())
- throw uhd::runtime_error("empty calibration table " + key);
-
- // search for lo freq
- size_t lo_index = 0;
- size_t hi_index = datas.size() - 1;
- for (size_t i = 0; i < datas.size(); i++) {
- if (is_same_freq(datas[i].lo_freq, lo_freq)) {
- hi_index = i;
- lo_index = i;
- break;
+ // The serial/timestamp don't really matter, we never look them up once we
+ // generate the container here.
+ auto iq_cal_container = iq_cal::make(file_prefix, db_serial, 0);
+
+ // parse csv file
+ std::ifstream cal_data(cal_data_path.string().c_str());
+ const uhd::csv::rows_type rows = uhd::csv::to_rows(cal_data);
+ bool read_data = false, skip_next = false;
+ for (const uhd::csv::row_type& row : rows) {
+ if (not read_data and not row.empty() and row[0] == "DATA STARTS HERE") {
+ read_data = true;
+ skip_next = true;
+ continue;
}
- if (datas[i].lo_freq > lo_freq) {
- hi_index = i;
- break;
+ if (not read_data)
+ continue;
+ if (skip_next) {
+ skip_next = false;
+ continue;
}
- lo_index = i;
- }
- if (lo_index == 0)
- return std::complex<double>(
- datas[lo_index].iq_corr_real, datas[lo_index].iq_corr_imag);
- if (hi_index == lo_index)
- return std::complex<double>(
- datas[hi_index].iq_corr_real, datas[hi_index].iq_corr_imag);
-
- // interpolation time
- return std::complex<double>(linear_interp(lo_freq,
- datas[lo_index].lo_freq,
- datas[lo_index].iq_corr_real,
- datas[hi_index].lo_freq,
- datas[hi_index].iq_corr_real),
- linear_interp(lo_freq,
- datas[lo_index].lo_freq,
- datas[lo_index].iq_corr_imag,
- datas[hi_index].lo_freq,
- datas[hi_index].iq_corr_imag));
+ iq_cal_container->set_cal_coeff(
+ std::stod(row[0]), {std::stod(row[1]), std::stod(row[2])});
+ }
+ fe_cal_cache.insert({cal_key, iq_cal_container});
+ UHD_LOGGER_INFO("CAL") << "Calibration data loaded: " << cal_data_path.string();
+ return true;
}
-static void apply_fe_corrections(uhd::property_tree::sptr sub_tree,
+void apply_fe_corrections(uhd::property_tree::sptr sub_tree,
const std::string& db_serial,
const uhd::fs_path& fe_path,
const std::string& file_prefix,
const double lo_freq)
{
- // make the calibration file path
- const fs::path cal_data_path = fs::path(uhd::get_app_path()) / ".uhd" / "cal"
- / (file_prefix + db_serial + ".csv");
- if (not fs::exists(cal_data_path))
- return;
-
- // parse csv file or get from cache
- if (not fe_cal_cache.has_key(cal_data_path.string())) {
- std::ifstream cal_data(cal_data_path.string().c_str());
- const uhd::csv::rows_type rows = uhd::csv::to_rows(cal_data);
-
- bool read_data = false, skip_next = false;
- ;
- std::vector<fe_cal_t> datas;
- for (const uhd::csv::row_type& row : rows) {
- if (not read_data and not row.empty() and row[0] == "DATA STARTS HERE") {
- read_data = true;
- skip_next = true;
- continue;
+ const auto cal_key = file_prefix + ":" + db_serial;
+ // Check if we need to load cal data
+ if (!fe_cal_cache.count(cal_key)) {
+ if (database::has_cal_data(file_prefix, db_serial)) {
+ try {
+ const auto cal_data = database::read_cal_data(file_prefix, db_serial);
+ fe_cal_cache.insert({cal_key, container::make<iq_cal>(cal_data)});
+ UHD_LOG_DEBUG("CAL",
+ "Loaded calibration data for " << file_prefix
+ << " serial=" << db_serial);
+ } catch (const uhd::exception& ex) {
+ UHD_LOG_WARNING("CAL",
+ "Error occurred reading cal data: `" << ex.what()
+ << "'. Skipping future loads.");
+ fe_cal_cache.insert({cal_key, nullptr});
}
- if (not read_data)
- continue;
- if (skip_next) {
- skip_next = false;
- continue;
- }
- fe_cal_t data;
- std::sscanf(row[0].c_str(), "%lf", &data.lo_freq);
- std::sscanf(row[1].c_str(), "%lf", &data.iq_corr_real);
- std::sscanf(row[2].c_str(), "%lf", &data.iq_corr_imag);
- datas.push_back(data);
+ // Delete the following else clause once we remove CSV support
+ } else if (load_legacy_fe_corrections(cal_key, db_serial, file_prefix)) {
+ UHD_LOG_WARNING("CAL",
+ "Found deprecated (CSV-based) cal data format. This feature will go away "
+ "in the future, please convert your calibration data to the new binary "
+ "format, or re-run your self-cal routines. For more information, see "
+ "https://files.ettus.com/manual/page_calibration.html");
+ } else {
+ // If there is no cal data, store a nullptr so we can skip the check
+ // next time.
+ fe_cal_cache.insert({cal_key, nullptr});
+ UHD_LOG_TRACE("CAL",
+ "No calibration data found for " << file_prefix
+ << " serial=" << db_serial);
}
- std::sort(datas.begin(), datas.end(), fe_cal_comp);
- fe_cal_cache[cal_data_path.string()] = datas;
- UHD_LOGGER_INFO("CAL") << "Calibration data loaded: " << cal_data_path.string();
}
+ // Check if valid data even exists
+ if (fe_cal_cache.at(cal_key) == nullptr) {
+ return;
+ }
+
+ // OK we have cal data: Now apply it
sub_tree->access<std::complex<double>>(fe_path).set(
- get_fe_correction(cal_data_path.string(), lo_freq));
+ fe_cal_cache.at(cal_key)->get_cal_coeff(lo_freq));
}
-/***********************************************************************
- * Wrapper routines with nice try/catch + print
- **********************************************************************/
-void uhd::usrp::apply_tx_fe_corrections( // overloading to work according to rfnoc tree
- // struct
- property_tree::sptr sub_tree, // starts at mboards/x
+} // namespace
+
+/******************************************************************************
+ * Wrapper routines with nice try/catch + print, RFNoC device version
+ *****************************************************************************/
+void uhd::usrp::apply_tx_fe_corrections(property_tree::sptr sub_tree,
const std::string& db_serial,
const uhd::fs_path tx_fe_corr_path,
- const double lo_freq // actual lo freq
-)
+ const double lo_freq)
{
- boost::mutex::scoped_lock l(corrections_mutex);
+ std::lock_guard<std::mutex> l(corrections_mutex);
try {
- apply_fe_corrections(sub_tree,
- db_serial,
- tx_fe_corr_path + "/iq_balance/value",
- "tx_iq_cal_v0.2_",
- lo_freq);
- apply_fe_corrections(sub_tree,
- db_serial,
- tx_fe_corr_path + "/dc_offset/value",
- "tx_dc_cal_v0.2_",
- lo_freq);
+ apply_fe_corrections(
+ sub_tree, db_serial, tx_fe_corr_path + "/iq_balance/value", "tx_iq", lo_freq);
} catch (const std::exception& e) {
UHD_LOGGER_ERROR("CAL") << "Failure in apply_tx_fe_corrections: " << e.what();
}
-}
-
-void uhd::usrp::apply_tx_fe_corrections(
- property_tree::sptr sub_tree, // starts at mboards/x
- const std::string& slot, // name of dboard slot
- const double lo_freq // actual lo freq
-)
-{
- boost::mutex::scoped_lock l(corrections_mutex);
-
- // extract eeprom serial
- const uhd::fs_path db_path = "dboards/" + slot + "/tx_eeprom";
- const std::string db_serial =
- sub_tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get().serial;
try {
- apply_fe_corrections(sub_tree,
- db_serial,
- "tx_frontends/" + slot + "/iq_balance/value",
- "tx_iq_cal_v0.2_",
- lo_freq);
- apply_fe_corrections(sub_tree,
- db_serial,
- "tx_frontends/" + slot + "/dc_offset/value",
- "tx_dc_cal_v0.2_",
- lo_freq);
+ apply_fe_corrections(
+ sub_tree, db_serial, tx_fe_corr_path + "/dc_offset/value", "tx_dc", lo_freq);
} catch (const std::exception& e) {
UHD_LOGGER_ERROR("CAL") << "Failure in apply_tx_fe_corrections: " << e.what();
}
}
-void uhd::usrp::apply_rx_fe_corrections( // overloading to work according to rfnoc tree
- // struct
- property_tree::sptr sub_tree, // starts at mboards/x
+void uhd::usrp::apply_rx_fe_corrections(property_tree::sptr sub_tree,
const std::string& db_serial,
const uhd::fs_path rx_fe_corr_path,
- const double lo_freq // actual lo freq
-)
+ const double lo_freq)
{
- boost::mutex::scoped_lock l(corrections_mutex);
+ std::lock_guard<std::mutex> l(corrections_mutex);
try {
- apply_fe_corrections(sub_tree,
- db_serial,
- rx_fe_corr_path + "/iq_balance/value",
- "rx_iq_cal_v0.2_",
- lo_freq);
+ apply_fe_corrections(
+ sub_tree, db_serial, rx_fe_corr_path + "/iq_balance/value", "rx_iq", lo_freq);
} catch (const std::exception& e) {
UHD_LOGGER_ERROR("CAL") << "Failure in apply_tx_fe_corrections: " << e.what();
}
}
+/******************************************************************************
+ * Gen-2 versions
+ *****************************************************************************/
+void uhd::usrp::apply_tx_fe_corrections(
+ property_tree::sptr sub_tree, // starts at mboards/x
+ const std::string& slot, // name of dboard slot
+ const double lo_freq // actual lo freq
+)
+{
+ std::lock_guard<std::mutex> l(corrections_mutex);
+
+ // extract eeprom serial
+ const uhd::fs_path db_path = "dboards/" + slot + "/tx_eeprom";
+ const std::string db_serial =
+ sub_tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get().serial;
+ const uhd::fs_path corr_path("tx_frontends/" + slot);
+
+ apply_tx_fe_corrections(sub_tree, db_serial, corr_path, lo_freq);
+}
+
void uhd::usrp::apply_rx_fe_corrections(
property_tree::sptr sub_tree, // starts at mboards/x
const std::string& slot, // name of dboard slot
const double lo_freq // actual lo freq
)
{
- boost::mutex::scoped_lock l(corrections_mutex);
+ std::lock_guard<std::mutex> l(corrections_mutex);
const uhd::fs_path db_path = "dboards/" + slot + "/rx_eeprom";
const std::string db_serial =
sub_tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get().serial;
- try {
- apply_fe_corrections(sub_tree,
- db_serial,
- "rx_frontends/" + slot + "/iq_balance/value",
- "rx_iq_cal_v0.2_",
- lo_freq);
- } catch (const std::exception& e) {
- UHD_LOGGER_ERROR("CAL") << "Failure in apply_rx_fe_corrections: " << e.what();
- }
+ const uhd::fs_path corr_path("rx_frontends/" + slot);
+
+ apply_rx_fe_corrections(sub_tree, db_serial, corr_path, lo_freq);
}
diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp
index ccf52f257..3ecd5fe38 100644
--- a/host/lib/utils/paths.cpp
+++ b/host/lib/utils/paths.cpp
@@ -8,6 +8,7 @@
#include <uhd/config.hpp>
#include <uhd/exception.hpp>
#include <uhd/utils/paths.hpp>
+#include <uhd/utils/log.hpp>
#include <uhdlib/utils/paths.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/version.hpp>
@@ -189,6 +190,31 @@ std::string uhd::get_app_path(void)
return uhd::get_tmp_path();
}
+// Only used for deprecated CSV file loader. Delete this once CSV support is
+// removed.
+std::string uhd::get_appdata_path(void)
+{
+ const std::string uhdcalib_path = get_env_var("UHD_CONFIG_DIR");
+ if (not uhdcalib_path.empty()) {
+ UHD_LOG_WARNING("UHD",
+ "The environment variable UHD_CONFIG_DIR is deprecated. Refer to "
+ "https://files.ettus.com/manual/page_calibration.html for how to store "
+ "calibration data.");
+ return uhdcalib_path;
+ }
+
+ const std::string appdata_path = get_env_var("APPDATA");
+ if (not appdata_path.empty())
+ return appdata_path;
+
+ const std::string home_path = get_env_var("HOME");
+ if (not home_path.empty())
+ return home_path;
+
+ return uhd::get_tmp_path();
+}
+
+
#if BOOST_VERSION >= 106100
std::string uhd::get_pkg_path(void)
{