diff options
Diffstat (limited to 'host/lib')
-rw-r--r-- | host/lib/include/uhdlib/usrp/common/apply_corrections.hpp | 47 | ||||
-rw-r--r-- | host/lib/include/uhdlib/utils/paths.hpp | 2 | ||||
-rw-r--r-- | host/lib/usrp/common/apply_corrections.cpp | 295 | ||||
-rw-r--r-- | host/lib/utils/paths.cpp | 26 |
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) { |