// // Copyright 2011-2016 Ettus Research LLC // Copyright 2018 Ettus Research, a National Instruments Company // // SPDX-License-Identifier: GPL-3.0-or-later // #include #include #include #include #include #include #include #include #include #include // For deprecated CSV reader only #include #include #include #include using namespace uhd::usrp::cal; std::mutex corrections_mutex; /*********************************************************************** * FE apply corrections implementation **********************************************************************/ namespace { // Cache the loaded data so we don't have to serialize on every tune std::unordered_map fe_cal_cache; // 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) { 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; } // 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 (not read_data) continue; if (skip_next) { skip_next = false; continue; } 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; } 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) { 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(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}); } // 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); } } // 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>(fe_path).set( fe_cal_cache.at(cal_key)->get_cal_coeff(lo_freq)); } } // 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) { std::lock_guard l(corrections_mutex); try { 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(); } try { 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(property_tree::sptr sub_tree, const std::string& db_serial, const uhd::fs_path rx_fe_corr_path, const double lo_freq) { std::lock_guard l(corrections_mutex); try { 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 ) { // extract eeprom serial const uhd::fs_path db_path = "dboards/" + slot + "/tx_eeprom"; const std::string db_serial = sub_tree->access(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 ) { const uhd::fs_path db_path = "dboards/" + slot + "/rx_eeprom"; const std::string db_serial = sub_tree->access(db_path).get().serial; const uhd::fs_path corr_path("rx_frontends/" + slot); apply_rx_fe_corrections(sub_tree, db_serial, corr_path, lo_freq); }