aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host/include/uhd/CMakeLists.txt1
-rw-r--r--host/include/uhd/cal/CMakeLists.txt12
-rw-r--r--host/include/uhd/cal/database.hpp139
-rw-r--r--host/lib/CMakeLists.txt1
-rw-r--r--host/lib/cal/CMakeLists.txt13
-rw-r--r--host/lib/cal/cal_python.hpp61
-rw-r--r--host/lib/cal/database.cpp205
-rw-r--r--host/lib/rc/CMakeLists.txt1
-rw-r--r--host/lib/rc/cal/test.cal1
-rw-r--r--host/python/uhd/usrp/cal/libtypes.py19
-rw-r--r--host/tests/CMakeLists.txt1
-rw-r--r--host/tests/cal_database_test.cpp88
12 files changed, 542 insertions, 0 deletions
diff --git a/host/include/uhd/CMakeLists.txt b/host/include/uhd/CMakeLists.txt
index 429d4fe63..f6bdd2a8f 100644
--- a/host/include/uhd/CMakeLists.txt
+++ b/host/include/uhd/CMakeLists.txt
@@ -6,6 +6,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
#
+add_subdirectory(cal)
add_subdirectory(rfnoc)
add_subdirectory(transport)
add_subdirectory(types)
diff --git a/host/include/uhd/cal/CMakeLists.txt b/host/include/uhd/cal/CMakeLists.txt
new file mode 100644
index 000000000..6c8355fec
--- /dev/null
+++ b/host/include/uhd/cal/CMakeLists.txt
@@ -0,0 +1,12 @@
+#
+# Copyright 2020 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+UHD_INSTALL(FILES
+ database.hpp
+ DESTINATION ${INCLUDE_DIR}/uhd/cal
+ COMPONENT headers
+)
+
diff --git a/host/include/uhd/cal/database.hpp b/host/include/uhd/cal/database.hpp
new file mode 100644
index 000000000..ca607de06
--- /dev/null
+++ b/host/include/uhd/cal/database.hpp
@@ -0,0 +1,139 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_CAL_DATABASE_HPP
+#define INCLUDED_LIBUHD_CAL_DATABASE_HPP
+
+#include <uhd/config.hpp>
+#include <stddef.h>
+#include <string>
+#include <vector>
+
+namespace uhd { namespace usrp { namespace cal {
+
+//! Identify the source of calibration data, i.e., where was it stored
+//
+// This enum lists the sources in reverse order of priority, i.e., user-provided
+// data has the highest priority, and hard-coded data from the resource compiler
+// has the lowest priority.
+enum class source {
+ NONE, //!< No calibration data available
+ ANY, //!< Undefined source
+ RC, //!< Internal Resource Compiler (i.e., hard-coded within UHD)
+ FLASH, //!< Stored on device flash memory, e.g. EEPROM
+ FILESYSTEM, //!< Stored on the local filesystem
+ USER //!< Provided by the user
+};
+
+/*! Calibration Data Storage/Retrieval Class
+ *
+ * UHD can store calibration data on disk or compiled within UHD. This class
+ * provides access to both locations.
+ *
+ * \section cal_db_blob Format of binary data
+ *
+ * This class can read and write binary data, but it does not verify the data
+ * or expect any kind of format. It simply manages BLOBs (binary large objects).
+ * It is up to the consumers and producers of this data to agree on a format.
+ * Typically, since this class stores calibration data, it will be consuming
+ * data that was produced by uhd::usrp::cal::container::serialize().
+ *
+ * \section cal_db_serial Serial number and key
+ *
+ * Calibration data is indexed by two keys: An arbitrary key that describes the
+ * type of calibration data (e.g., "rx_iq") and a serial number. The serial
+ * number has to uniquely identify the device for which the calibration data was
+ * obtained. This can either be the serial number of the daughterboard (if the
+ * calibration data only relates to the daughterboard), the motherboard (for
+ * example, if there is no such thing as a daughterboard, or the data only
+ * relates to the motherboard), it can be combination of both daughterboard and
+ * motherboard serial (if the calibration data is only valid for a combination),
+ * or it can be a combination of a device serial number and a channel index
+ * (if a device with single serial has different channels that have separate
+ * characteristics).
+ *
+ * It is up to the individual device drivers which value they use for the serial
+ * numbers and keys.
+ *
+ * Note that the serial number is irrelevant when the data is pulled out of the
+ * resource compiler. By definition, it is not permitted to store data in the
+ * resource compiler that is specific to a certain serial number, only data that
+ * applies to an entire family of devices is permitted.
+ */
+class UHD_API database
+{
+public:
+
+ //! Return a calibration data set as a serialized string
+ //
+ // Note: the \p source_type parameter can be used to specify where to read
+ // cal data from. However, this class only has
+ // access to RC and FILESYSTEM type cal data. ANY
+ // will pick FILESYSTEM data if both are available,
+ // and RC data if only RC data is available.
+ // \param key The calibration type key (e.g., "rx_iq")
+ // \param serial The serial number of the device this data is for. See also
+ // \ref cal_db_serial
+ // \param source_type Where to read the calibration data from. See comments
+ // above. For anything other than RC, FILESYSTEM, or ANY,
+ // this will always throw a uhd::key_error because this
+ // class does not have access to user data or EEPROM data.
+ //
+ // \throws uhd::key_error if no calibration data is found matching the source
+ // type.
+ static std::vector<uint8_t> read_cal_data(const std::string& key,
+ const std::string& serial,
+ const source source_type = source::ANY);
+
+ //! Check if calibration data exists for a given source type
+ //
+ // This can be called before calling read_cal_data() to avoid having to
+ // catch an exception. If \p source_type is FILESYSTEM, then it will only
+ // return true if a file is found with the appropriate cal data. The same
+ // is true for RC. If \p is ANY, then having either RC or FILESYSTEM data
+ // will yield true.
+ //
+ // \param key The calibration type key (e.g., "rx_iq")
+ // \param serial The serial number of the device this data is for. See also
+ // \ref cal_db_serial
+ // \param source_type Where to read the calibration data from. For anything
+ // other than RC, FILESYSTEM, or ANY, this will always
+ // return false because this class does not have access
+ // to user data or EEPROM data.
+ // \return true if calibration data is available that matches this key/serial
+ // pair.
+ static bool has_cal_data(const std::string& key,
+ const std::string& serial,
+ const source source_type = source::ANY);
+
+ //! Store calibration data to the local filesystem database
+ //
+ // This implies a source type of FILESYSTEM. Note that writing the data does
+ // not apply it to a currently running UHD session. Devices will typically
+ // load calibration data at initialization time, and thus this call will
+ // take effect only for future UHD sessions.
+ //
+ // If calibration data for this key/serial pair already exists in the
+ // database, the original data will be backed up by renaming the original
+ // file from `filename.cal` to `filename.cal.$TIMESTAMP`. Alternatively, a
+ // custom extension can be chosen instead of `$TIMESTAMP`.
+ //
+ // \param key The calibration type key (e.g., "rx_iq")
+ // \param serial The serial number of the device this data is for. See also
+ // \ref cal_db_serial
+ // \param cal_data The calibration data to be written
+ // \param backup_ext A custom extension for backing up calibration data. If
+ // left empty, a POSIX timestamp is used.
+ static void write_cal_data(const std::string& key,
+ const std::string& serial,
+ const std::vector<uint8_t>& cal_data,
+ const std::string& backup_ext = "");
+};
+
+
+}}} // namespace uhd::usrp::cal
+
+#endif /* INCLUDED_LIBUHD_CAL_DATABASE_HPP */
diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt
index 4d747c9c7..5a3377a55 100644
--- a/host/lib/CMakeLists.txt
+++ b/host/lib/CMakeLists.txt
@@ -82,6 +82,7 @@ LIBUHD_REGISTER_COMPONENT("DPDK" ENABLE_DPDK ON "ENABLE_MPMD;DPDK_FOUND" OFF OFF
# Include subdirectories (different than add)
########################################################################
INCLUDE_SUBDIRECTORY(include)
+INCLUDE_SUBDIRECTORY(cal)
INCLUDE_SUBDIRECTORY(ic_reg_maps)
INCLUDE_SUBDIRECTORY(types)
INCLUDE_SUBDIRECTORY(convert)
diff --git a/host/lib/cal/CMakeLists.txt b/host/lib/cal/CMakeLists.txt
new file mode 100644
index 000000000..5c2c8a617
--- /dev/null
+++ b/host/lib/cal/CMakeLists.txt
@@ -0,0 +1,13 @@
+#
+# Copyright 2020 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/database.cpp
+)
+
diff --git a/host/lib/cal/cal_python.hpp b/host/lib/cal/cal_python.hpp
new file mode 100644
index 000000000..0fe87046f
--- /dev/null
+++ b/host/lib/cal/cal_python.hpp
@@ -0,0 +1,61 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_UHD_CAL_PYTHON_HPP
+#define INCLUDED_UHD_CAL_PYTHON_HPP
+
+#include <uhd/cal/database.hpp>
+
+std::vector<uint8_t> pybytes_to_vector(const py::bytes& data)
+{
+ const std::string data_str = std::string(data);
+ return std::vector<uint8_t>(data_str.cbegin(), data_str.cend());
+}
+
+py::bytes vector_to_pybytes(const std::vector<uint8_t>& data)
+{
+ return py::bytes(std::string(data.cbegin(), data.cend()));
+}
+
+void export_cal(py::module& m)
+{
+ using namespace uhd::usrp::cal;
+
+ // Cal Database
+ using database = uhd::usrp::cal::database;
+ using source = uhd::usrp::cal::source;
+
+ py::enum_<source>(m, "source")
+ .value("ANY", source::ANY)
+ .value("RC", source::RC)
+ .value("FILESYSTEM", source::FILESYSTEM)
+ .value("FLASH", source::FLASH)
+ .value("USER", source::USER)
+ .value("NONE", source::NONE);
+
+ py::class_<database>(m, "database")
+ .def_static("read_cal_data",
+ [](const std::string& key,
+ const std::string& serial,
+ const source source_type) {
+ return vector_to_pybytes(
+ database::read_cal_data(key, serial, source_type));
+ },
+ py::arg("key"),
+ py::arg("serial"),
+ py::arg("source_type") = source::ANY)
+ .def_static("has_cal_data",
+ &database::has_cal_data,
+ py::arg("key"),
+ py::arg("serial"),
+ py::arg("source_type") = source::ANY)
+ .def_static("write_cal_data",
+ [](const std::string& key, const std::string& serial, const py::bytes data) {
+ database::write_cal_data(key, serial, pybytes_to_vector(data));
+ });
+}
+
+#endif /* INCLUDED_UHD_CAL_PYTHON_HPP */
diff --git a/host/lib/cal/database.cpp b/host/lib/cal/database.cpp
new file mode 100644
index 000000000..16fcd4b71
--- /dev/null
+++ b/host/lib/cal/database.cpp
@@ -0,0 +1,205 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/cal/database.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/paths.hpp>
+#include <cmrc/cmrc.hpp>
+#include <boost/filesystem.hpp>
+#include <ctime>
+#include <fstream>
+
+CMRC_DECLARE(rc);
+
+using namespace uhd::usrp::cal;
+namespace rc = cmrc::rc;
+namespace fs = boost::filesystem;
+
+namespace {
+constexpr char LOG_ID[] = "CAL::DATABASE";
+constexpr char CAL_EXT[] = ".cal";
+//! This value is just for sanity checking. We pick a value (in bytes) that we
+// are guaranteed to never exceed. Its only purpose is to avoid loading files
+// that can't possibly be valid cal data based on the filesize. This can avoid
+// someone bringing down a UHD session by trying to import a huge file, because
+// we first load it entirely into heap space, and then deserialize it from there.
+constexpr size_t CALDATA_MAX_SIZE = 10 * 1024 * 1024; // 10 MiB
+
+
+//! Map a cal resource key into a source::RC path name
+std::string get_cal_path_rc(const std::string& key)
+{
+ return std::string("cal/") + key + CAL_EXT;
+}
+
+//! Return true if a cal data resource with given key exists
+bool has_cal_data_rc(const std::string& key)
+{
+ auto fs = rc::get_filesystem();
+ return fs.is_file(get_cal_path_rc(key));
+}
+
+//! Return a byte array for a given cal resource
+std::vector<uint8_t> get_cal_data_rc(const std::string& key)
+{
+ try {
+ auto fs = rc::get_filesystem();
+ auto file = fs.open(get_cal_path_rc(key));
+ return std::vector<uint8_t>(file.cbegin(), file.cend());
+ } catch (const std::system_error&) {
+ throw uhd::key_error(std::string("Unable to open resource with key: ") + key);
+ }
+}
+
+void check_or_create_dir(fs::path dir)
+{
+ if (fs::exists(dir)) {
+ if (fs::is_directory(dir)) {
+ return;
+ }
+ UHD_LOG_ERROR(LOG_ID, "Path exists, but is not a directory: " << dir);
+ throw uhd::runtime_error("Path exists, but is not a directory!");
+ }
+
+ if (!fs::create_directory(dir)) {
+ UHD_LOG_ERROR(LOG_ID, "Cannot create cal data directory: " << dir);
+ throw uhd::runtime_error("Cannot create cal data directory!");
+ }
+ UHD_LOG_DEBUG(LOG_ID, "Created directory: " << dir);
+}
+
+//! Make sure the calibration storage directory exists.
+//
+// The path returned by uhd::get_cal_data_path() might not exist (e.g., when run
+// for the first time). This directory must be created before we try writing to
+// it, or we won't be able to open the file.
+//
+// C++ doesn't have a mkdir -p equivalent, so we check the parent directory and
+// the directory itself, in that order. Most of the time, the cal data path is
+// in $XDG_DATA_HOME/uhd/cal_data. We assume that $XDG_DATA_HOME exists, and
+// then first check $XDG_DATA_HOME/uhd, then $XDG_DATA_HOME/uhd/cal_data.
+//
+// This will not work if the user sets $UHD_CAL_DATA_PATH to an arbitrary path
+// that requires multiple levels of directories to be created, but they will get
+// a clear error message in that case.
+void assert_cal_dir_exists()
+{
+ const auto cal_path = fs::path(uhd::get_cal_data_path());
+ if (!cal_path.parent_path().empty()) {
+ check_or_create_dir(cal_path.parent_path());
+ }
+ check_or_create_dir(cal_path);
+}
+
+
+//! Map a cal resource key into a filesystem path name (relative to get_cal_data_path())
+std::string get_cal_path_fs(const std::string& key, const std::string& serial)
+{
+ return key + "_" + serial + CAL_EXT;
+}
+
+//! Return true if a cal data resource with given key exists
+bool has_cal_data_fs(const std::string& key, const std::string& serial)
+{
+ auto const cal_file_path =
+ fs::path(uhd::get_cal_data_path()) / get_cal_path_fs(key, serial);
+ UHD_LOG_TRACE(LOG_ID, "Checking for file at " << cal_file_path.string());
+ // We might want to check readability also
+ return fs::exists(cal_file_path) && fs::is_regular_file(cal_file_path);
+}
+
+//! Return a byte array for a given filesystem resource
+std::vector<uint8_t> get_cal_data_fs(const std::string& key, const std::string& serial)
+{
+ if (!has_cal_data_fs(key, serial)) {
+ throw uhd::key_error(
+ std::string("Cannot find cal file for key=") + key + ", serial=" + serial);
+ }
+ const auto cal_file_path =
+ fs::path(uhd::get_cal_data_path()) / get_cal_path_fs(key, serial);
+ // We read the filesize first to do a sanity check (is this file small
+ // enough to reasonably be cal data?) and also to pre-allocate heap space in
+ // which we'll load the full data for future deserialization.
+ const size_t filesize = fs::file_size(cal_file_path);
+ if (filesize > CALDATA_MAX_SIZE) {
+ throw uhd::key_error(
+ std::string("The following cal data file exceeds maximum size limitations: ")
+ + cal_file_path.string());
+ }
+ std::vector<uint8_t> result(filesize, 0);
+ std::ifstream file(cal_file_path.string(), std::ios::binary);
+ UHD_LOG_TRACE(LOG_ID, "Reading " << filesize << " bytes from " << cal_file_path);
+ file.read(reinterpret_cast<char*>(&result[0]), filesize);
+ return result;
+}
+
+} // namespace
+
+std::vector<uint8_t> database::read_cal_data(
+ const std::string& key, const std::string& serial, const source source_type)
+{
+ if (source_type == source::FILESYSTEM || source_type == source::ANY) {
+ if (has_cal_data_fs(key, serial)) {
+ return get_cal_data_fs(key, serial);
+ }
+ }
+
+ if (source_type == source::RC || source_type == source::ANY) {
+ if (has_cal_data_rc(key)) {
+ return get_cal_data_rc(key);
+ }
+ }
+
+ const std::string err_msg =
+ std::string("Calibration Data not found for: key=") + key + ", serial=" + serial;
+ UHD_LOG_ERROR(LOG_ID, err_msg);
+ throw uhd::key_error(err_msg);
+}
+
+bool database::has_cal_data(
+ const std::string& key, const std::string& serial, const source source_type)
+{
+ if (source_type == source::FILESYSTEM || source_type == source::ANY) {
+ if (has_cal_data_fs(key, serial)) {
+ return true;
+ }
+ }
+
+ if (source_type == source::RC || source_type == source::ANY) {
+ if (has_cal_data_rc(key)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void database::write_cal_data(const std::string& key,
+ const std::string& serial,
+ const std::vector<uint8_t>& cal_data,
+ const std::string& backup_ext)
+{
+ assert_cal_dir_exists();
+
+ const auto cal_file_path =
+ (fs::path(uhd::get_cal_data_path()) / get_cal_path_fs(key, serial)).string();
+
+ if (fs::exists(cal_file_path)) {
+ const auto ext = backup_ext.empty() ? std::to_string(time(NULL)) : backup_ext;
+ const auto cal_file_path_backup = fs::path(uhd::get_cal_data_path())
+ / (get_cal_path_fs(key, serial) + "." + ext);
+ UHD_LOG_WARNING(LOG_ID,
+ "Calibration data already exists for key: `"
+ << key << "' serial: `" << serial
+ << "'. Backing up to: " << cal_file_path_backup);
+ fs::rename(fs::path(cal_file_path), cal_file_path_backup);
+ }
+
+ std::ofstream file(cal_file_path, std::ios::binary);
+ UHD_LOG_DEBUG(LOG_ID, "Writing to " << cal_file_path);
+ file.write(reinterpret_cast<const char*>(cal_data.data()), cal_data.size());
+}
diff --git a/host/lib/rc/CMakeLists.txt b/host/lib/rc/CMakeLists.txt
index 1595e7e85..d2a01bffb 100644
--- a/host/lib/rc/CMakeLists.txt
+++ b/host/lib/rc/CMakeLists.txt
@@ -8,4 +8,5 @@ include(CMakeRC)
cmrc_add_resource_library(uhd-resources
ALIAS uhd_rc
NAMESPACE rc
+ cal/test.cal
)
diff --git a/host/lib/rc/cal/test.cal b/host/lib/rc/cal/test.cal
new file mode 100644
index 000000000..861c2bb2c
--- /dev/null
+++ b/host/lib/rc/cal/test.cal
@@ -0,0 +1 @@
+rc::cal::test_data \ No newline at end of file
diff --git a/host/python/uhd/usrp/cal/libtypes.py b/host/python/uhd/usrp/cal/libtypes.py
new file mode 100644
index 000000000..754028c24
--- /dev/null
+++ b/host/python/uhd/usrp/cal/libtypes.py
@@ -0,0 +1,19 @@
+#
+# Copyright 2020 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+"""
+Import cal types into Python
+"""
+
+from ... import libpyuhd as lib
+
+# Disable PyLint because we want to make it look like the following classes are
+# defined in Python, but they're just renames of lib types. They therefore
+# follow name conventions for Python classes, not for global constants.
+# pylint: disable=invalid-name
+# database is a class, but we treat it like a namespace, i.e., a submodule
+database = lib.cal.database
+Source = lib.cal.source
+# pylint: enable=invalid-name
diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt
index bf47f6638..f987aa194 100644
--- a/host/tests/CMakeLists.txt
+++ b/host/tests/CMakeLists.txt
@@ -25,6 +25,7 @@ set(test_sources
buffer_test.cpp
byteswap_test.cpp
cast_test.cpp
+ cal_database_test.cpp
chdr_test.cpp
constrained_device_args_test.cpp
convert_test.cpp
diff --git a/host/tests/cal_database_test.cpp b/host/tests/cal_database_test.cpp
new file mode 100644
index 000000000..cd189138b
--- /dev/null
+++ b/host/tests/cal_database_test.cpp
@@ -0,0 +1,88 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/cal/database.hpp>
+#include <uhd/utils/paths.hpp>
+#include <stdlib.h> // putenv or _putenv
+#include <boost/filesystem.hpp>
+#include <boost/test/unit_test.hpp>
+#include <iostream>
+
+using namespace uhd::usrp::cal;
+namespace fs = boost::filesystem;
+
+BOOST_AUTO_TEST_CASE(test_rc)
+{
+ BOOST_CHECK(!database::has_cal_data("does_not_exist", "1234", source::RC));
+ BOOST_CHECK(database::has_cal_data("test", "1234", source::RC));
+ BOOST_CHECK(database::has_cal_data("test", "1234"));
+ BOOST_CHECK(!database::has_cal_data("test", "1234", source::FILESYSTEM));
+
+ const auto test_data = database::read_cal_data("test", "", source::RC);
+ const std::string test_str(test_data.cbegin(), test_data.cend());
+ // The expected string is also in the test.cal file. We could, in this test,
+ // open that file and dynamically generate the expected data, but let's not.
+ // First, that adds complexity here, and second, both test.cal and this test
+ // are hashed with the same git commit, and thus we also test the integrity
+ // of test.cal.
+ BOOST_CHECK_EQUAL(test_str, "rc::cal::test_data");
+}
+
+BOOST_AUTO_TEST_CASE(test_fs)
+{
+ BOOST_CHECK(!database::has_cal_data("does_not_exist", "1234", source::FILESYSTEM));
+
+ const auto tmp_dir = uhd::get_tmp_path();
+ const auto tmp_cal_path = fs::path(tmp_dir) / "CAL_TEST";
+ boost::system::error_code ec;
+ fs::create_directory(tmp_cal_path, ec);
+ if (ec) {
+ std::cout << "WARNING: Could not create temp cal path. Skipping test."
+ << std::endl;
+ return;
+ }
+ std::cout << "Using temporary cal path: " << tmp_cal_path << std::endl;
+
+ // Now we do a non-portable hack to override the cal path during runtime:
+#ifdef UHD_PLATFORM_WIN32
+ const std::string putenv_str =
+ std::string("UHD_CAL_DATA_PATH=") + tmp_cal_path.string();
+ _putenv(putenv_str.c_str());
+#else
+ setenv("UHD_CAL_DATA_PATH", tmp_cal_path.string().c_str(), /* overwrite */ 1);
+#endif
+
+ // Because of the hack, we won't fail if it didn't work, but instead, print
+ // a warning and exit this test. Running the following lines requires the
+ // hack to succeed.
+ if (uhd::get_cal_data_path() != tmp_cal_path) {
+ std::cout << "WARNING: Unable to update UHD_CAL_DATA_PATH! get_cal_data_path(): "
+ << uhd::get_cal_data_path() << std::endl;
+ return;
+ }
+
+ std::vector<uint8_t> mock_data{1, 2, 3, 4, 5};
+ database::write_cal_data("mock_data", "1234", mock_data);
+ auto mock_data_rb = database::read_cal_data("mock_data", "1234");
+ BOOST_CHECK_EQUAL_COLLECTIONS(
+ mock_data.begin(), mock_data.end(), mock_data_rb.begin(), mock_data_rb.end());
+
+ BOOST_CHECK(!database::has_cal_data("mock_data", "abcd"));
+ std::vector<uint8_t> mock_data2{2, 3, 4, 5, 6};
+ database::write_cal_data("mock_data", "abcd", mock_data);
+ // Write it twice to force a backup
+ database::write_cal_data("mock_data", "abcd", mock_data2, "BACKUP");
+ mock_data_rb = database::read_cal_data("mock_data", "abcd");
+ BOOST_CHECK_EQUAL_COLLECTIONS(
+ mock_data2.begin(), mock_data2.end(), mock_data_rb.begin(), mock_data_rb.end());
+ BOOST_CHECK(database::has_cal_data("mock_data", "abcd"));
+ BOOST_CHECK(fs::exists(tmp_cal_path / "mock_data_abcd.cal.BACKUP"));
+
+ fs::remove_all(tmp_cal_path, ec);
+ if (ec) {
+ std::cout << "WARNING: Could not remove temp cal path." << std::endl;
+ }
+}