aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/cal
diff options
context:
space:
mode:
authorLars Amsel <lars.amsel@ni.com>2021-06-04 08:27:50 +0200
committerAaron Rossetto <aaron.rossetto@ni.com>2021-06-10 12:01:53 -0500
commit2a575bf9b5a4942f60e979161764b9e942699e1e (patch)
tree2f0535625c30025559ebd7494a4b9e7122550a73 /host/lib/cal
parente17916220cc955fa219ae37f607626ba88c4afe3 (diff)
downloaduhd-2a575bf9b5a4942f60e979161764b9e942699e1e.tar.gz
uhd-2a575bf9b5a4942f60e979161764b9e942699e1e.tar.bz2
uhd-2a575bf9b5a4942f60e979161764b9e942699e1e.zip
uhd: Add support for the USRP X410
Co-authored-by: Lars Amsel <lars.amsel@ni.com> Co-authored-by: Michael Auchter <michael.auchter@ni.com> Co-authored-by: Martin Braun <martin.braun@ettus.com> Co-authored-by: Paul Butler <paul.butler@ni.com> Co-authored-by: Cristina Fuentes <cristina.fuentes-curiel@ni.com> Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com> Co-authored-by: Virendra Kakade <virendra.kakade@ni.com> Co-authored-by: Lane Kolbly <lane.kolbly@ni.com> Co-authored-by: Max Köhler <max.koehler@ni.com> Co-authored-by: Andrew Lynch <andrew.lynch@ni.com> Co-authored-by: Grant Meyerhoff <grant.meyerhoff@ni.com> Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com> Co-authored-by: Thomas Vogel <thomas.vogel@ni.com>
Diffstat (limited to 'host/lib/cal')
-rw-r--r--host/lib/cal/CMakeLists.txt1
-rw-r--r--host/lib/cal/cal_python.hpp44
-rw-r--r--host/lib/cal/dsa_cal.cpp253
3 files changed, 298 insertions, 0 deletions
diff --git a/host/lib/cal/CMakeLists.txt b/host/lib/cal/CMakeLists.txt
index a50d654ff..da6bdf1a2 100644
--- a/host/lib/cal/CMakeLists.txt
+++ b/host/lib/cal/CMakeLists.txt
@@ -12,5 +12,6 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/database.cpp
${CMAKE_CURRENT_SOURCE_DIR}/iq_cal.cpp
${CMAKE_CURRENT_SOURCE_DIR}/pwr_cal.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/dsa_cal.cpp
)
diff --git a/host/lib/cal/cal_python.hpp b/host/lib/cal/cal_python.hpp
index fa38c568b..919f86e30 100644
--- a/host/lib/cal/cal_python.hpp
+++ b/host/lib/cal/cal_python.hpp
@@ -10,6 +10,7 @@
#include <uhd/cal/database.hpp>
#include <uhd/cal/iq_cal.hpp>
#include <uhd/cal/pwr_cal.hpp>
+#include <uhd/cal/dsa_cal.hpp>
#include <uhd/utils/interpolation.hpp>
#include <uhd/utils/pybind_adaptors.hpp>
#include <pybind11/stl.h>
@@ -124,6 +125,49 @@ void export_cal(py::module& m)
py::arg("power_dbm"),
py::arg("freq"),
py::arg("temperature") = boost::optional<int>());
+
+ py::class_<zbx_tx_dsa_cal, container, zbx_tx_dsa_cal::sptr>(m, "zbx_tx_dsa_cal")
+ .def(py::init([](const std::string& name,
+ const std::string& serial,
+ const uint64_t timestamp) {
+ return zbx_tx_dsa_cal::make(name, serial, timestamp);
+ }))
+ .def(py::init([]() { return zbx_tx_dsa_cal::make(); }))
+ .def(py::init([](const py::bytes data) {
+ return container::make<zbx_tx_dsa_cal>(pybytes_to_vector(data));
+ }))
+ .def("add_frequency_band",
+ &zbx_tx_dsa_cal::add_frequency_band,
+ py::arg("max_freq"),
+ py::arg("name"),
+ py::arg("steps"))
+ .def("clear", &zbx_tx_dsa_cal::clear)
+ .def("get_dsa_setting",
+ &zbx_tx_dsa_cal::get_dsa_setting,
+ py::arg("freq"),
+ py::arg("gain_index"));
+
+ py::class_<zbx_rx_dsa_cal, container, zbx_rx_dsa_cal::sptr>(m, "zbx_rx_dsa_cal")
+ .def(py::init([](const std::string& name,
+ const std::string& serial,
+ const uint64_t timestamp) {
+ return zbx_rx_dsa_cal::make(name, serial, timestamp);
+ }))
+ .def(py::init([]() { return zbx_rx_dsa_cal::make(); }))
+ .def(py::init([](const py::bytes data) {
+ return container::make<zbx_rx_dsa_cal>(pybytes_to_vector(data));
+ }))
+ .def("add_frequency_band",
+ &zbx_rx_dsa_cal::add_frequency_band,
+ py::arg("max_freq"),
+ py::arg("name"),
+ py::arg("steps"))
+ .def("clear", &zbx_rx_dsa_cal::clear)
+ .def("get_dsa_setting",
+ &zbx_rx_dsa_cal::get_dsa_setting,
+ py::arg("freq"),
+ py::arg("gain_index"));
+
}
#endif /* INCLUDED_UHD_CAL_PYTHON_HPP */
diff --git a/host/lib/cal/dsa_cal.cpp b/host/lib/cal/dsa_cal.cpp
new file mode 100644
index 000000000..c0890005c
--- /dev/null
+++ b/host/lib/cal/dsa_cal.cpp
@@ -0,0 +1,253 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+#include <uhd/cal/dsa_cal.hpp>
+#include <uhd/cal/dsa_cal_generated.h>
+#include <uhd/utils/log.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/math.hpp>
+#include <map>
+#include <string>
+#include <array>
+#include <boost/range/adaptor/indexed.hpp>
+
+using namespace uhd::usrp::cal;
+
+namespace {
+constexpr size_t VERSION_MAJOR = 2;
+constexpr size_t VERSION_MINOR = 0;
+
+/***********************************************************************
+ * Helper routines
+ **********************************************************************/
+template<typename base, size_t num_gain_stages, int num_dsa>
+class dsa_cal_impl : public base
+{
+public:
+ dsa_cal_impl(const std::string& name = "",
+ const std::string& serial = "",
+ const uint64_t timestamp = 0)
+ : _name(name)
+ , _serial(serial)
+ , _timestamp(timestamp)
+ {
+ }
+
+ /**************************************************************************
+ * Container API (Basics)
+ *************************************************************************/
+ std::string get_name() const
+ {
+ return _name;
+ }
+
+ std::string get_serial() const
+ {
+ return _serial;
+ }
+
+ uint64_t get_timestamp() const
+ {
+ return _timestamp;
+ }
+
+ /**************************************************************************
+ * Specific APIs
+ *************************************************************************/
+ void add_frequency_band(
+ const double max_freq,
+ const std::string& name,
+ const std::array<std::array<uint32_t, num_dsa>, num_gain_stages> dsa_values)
+ {
+ _data[max_freq] = name_dsas_pair(name, dsa_values);
+ }
+
+ const std::array<uint32_t, num_dsa> get_dsa_setting(double freq, size_t gain_index) const
+ {
+ if (_data.empty()) {
+ throw uhd::runtime_error("Cannot get DSA settings from an empty container.");
+ }
+ // find the lowest band with band_freq_max <= freq
+ const uint64_t freqi = static_cast<uint64_t>(freq);
+ const auto freq_it = _data.lower_bound(freqi);
+ if (freq_it == _data.end()) {
+ throw uhd::value_error("No DSA band found for freq " + std::to_string(freq));
+ }
+ // select DSA setting using gain_index
+ if (gain_index >= num_gain_stages) {
+ throw uhd::value_error(
+ "gain index " + std::to_string(gain_index) + " out of bounds.");
+ }
+ return freq_it->second.second[gain_index];
+ }
+
+ bool is_same_band(double freq1, double freq2) const
+ {
+ const uint64_t freqi1 = static_cast<uint64_t>(freq1);
+ const auto freq_it1 = _data.lower_bound(freqi1);
+ const uint64_t freqi2 = static_cast<uint64_t>(freq2);
+ const auto freq_it2 = _data.lower_bound(freqi2);
+ return freq_it1 == freq_it2;
+ }
+
+ std::vector<uint32_t> get_band_settings(double freq, uint8_t dsa) const
+ {
+ std::vector<uint32_t> result;
+ // find the lowest band with band_freq_max <= freq
+ const uint64_t freqi = static_cast<uint64_t>(freq);
+ const auto freq_it = _data.lower_bound(freqi);
+ if (freq_it == _data.end()) {
+ throw uhd::value_error("No DSA band found for freq " + std::to_string(freq));
+ }
+ // select DSA setting using gain_index
+ for (auto item : freq_it->second.second) {
+ result.push_back(item[dsa]);
+ }
+ return result;
+ }
+
+ void clear()
+ {
+ _data.clear();
+ }
+
+
+ /**************************************************************************
+ * Container API (Serialization/Deserialization)
+ *************************************************************************/
+ std::vector<uint8_t> serialize()
+ {
+ const size_t initial_size_bytes = 2 * num_gain_stages * num_dsa * sizeof(uint32_t); // 2 band of DSA values
+ flatbuffers::FlatBufferBuilder builder(initial_size_bytes);
+
+ std::vector<flatbuffers::Offset<BandDsaMap>> band_dsas;
+ band_dsas.reserve(_data.size());
+ for (auto& band_dsas_pair : _data) {
+ const uint64_t freq = band_dsas_pair.first;
+ std::vector<flatbuffers::Offset<DsaStep>> dsa_steps;
+ auto name_dsas = band_dsas_pair.second;
+ for (auto const& values: name_dsas.second) // iterate over gain indizes
+ {
+ std::vector<uint32_t> steps(values.begin(), values.end());
+ dsa_steps.push_back(CreateDsaStepDirect(builder, &steps));
+ }
+
+ band_dsas.push_back(
+ CreateBandDsaMapDirect(builder, freq, &dsa_steps, name_dsas.first.c_str()));
+
+ }
+
+ // Now load it all into the FlatBuffer
+ auto metadata = CreateMetadataDirect(builder,
+ _name.c_str(),
+ _serial.c_str(),
+ _timestamp,
+ VERSION_MAJOR,
+ VERSION_MINOR);
+ auto gain_dsa_cal =
+ CreateDsaCalDirect(builder, metadata, &band_dsas);
+ FinishDsaCalBuffer(builder, gain_dsa_cal);
+ const size_t table_size = builder.GetSize();
+ const uint8_t* table = builder.GetBufferPointer();
+ return std::vector<uint8_t>(table, table + table_size);
+ }
+
+ // This will amend the existing table. If that's not desired, then it is
+ // necessary to call clear() ahead of time.
+ void deserialize(const std::vector<uint8_t>& data)
+ {
+ clear();
+ auto verifier = flatbuffers::Verifier(data.data(), data.size());
+ if (!VerifyDsaCalBuffer(verifier)) {
+ throw uhd::runtime_error("dsa_cal: Invalid data provided! ");
+ }
+ auto cal_table = GetDsaCal(static_cast<const void*>(data.data()));
+ if (cal_table->metadata()->version_major() != VERSION_MAJOR) {
+ throw uhd::runtime_error("dsa_cal: Compat number mismatch!");
+ }
+ if (cal_table->metadata()->version_minor() != VERSION_MINOR) {
+ UHD_LOG_WARNING("CAL",
+ "gain_cal: Expected compat number "
+ << VERSION_MAJOR << "." << VERSION_MINOR << ", got "
+ << cal_table->metadata()->version_major() << "."
+ << cal_table->metadata()->version_minor());
+ }
+ _name = std::string(cal_table->metadata()->name()->c_str());
+ _serial = std::string(cal_table->metadata()->serial()->c_str());
+ _timestamp = cal_table->metadata()->timestamp();
+
+ auto band_dsa_map = cal_table->band_dsa_map();
+ for (auto it = band_dsa_map->begin(); it != band_dsa_map->end(); ++it) {
+ const uint64_t max_freq = it->max_freq();
+ const std::string name(it->name()->c_str());
+ auto gains = it->gains();
+ if (gains->size() != num_gain_stages) {
+ UHD_LOG_ERROR("CAL", "Invalid number of gain indizes. Got: "
+ << gains->size() << " expected: " << num_gain_stages);
+ throw uhd::runtime_error("Invalid number of gain indizes");
+ }
+ std::array<std::array<uint32_t, num_dsa>, num_gain_stages> indizes;
+ int i = 0;
+ for (auto gain_it = gains->begin(); gain_it != gains->end(); ++gain_it) {
+ if (gain_it->steps()->size() != num_dsa) {
+ UHD_LOG_ERROR("CAL", "Invalid number of attenuator indizes. Got: "
+ << gain_it->steps()->size() << " expected: " << num_dsa);
+ throw uhd::runtime_error("Invalid number of attenuator indizes");
+ }
+ std::copy(gain_it->steps()->begin(),
+ gain_it->steps()->end(),
+ indizes[i++].begin());
+ }
+ add_frequency_band(max_freq, name, indizes);
+ }
+ }
+
+
+private:
+ std::string _name;
+ std::string _serial;
+ uint64_t _timestamp;
+
+ using dsa_steps = std::array<std::array<uint32_t, num_dsa>, num_gain_stages>;
+ using name_dsas_pair = std::pair<std::string, dsa_steps>;
+
+ std::map<uint64_t /* freq */, name_dsas_pair> _data;
+
+};
+
+} //namespace
+std::shared_ptr<zbx_tx_dsa_cal> zbx_tx_dsa_cal::make()
+{
+ return std::make_shared<dsa_cal_impl<
+ zbx_tx_dsa_cal,
+ zbx_tx_dsa_cal::NUM_GAIN_STAGES,
+ zbx_tx_dsa_cal::NUM_DSA>>();
+}
+
+std::shared_ptr<zbx_tx_dsa_cal> zbx_tx_dsa_cal::make(
+ const std::string& name, const std::string& serial, const uint64_t timestamp)
+{
+ return std::make_shared<dsa_cal_impl<
+ zbx_tx_dsa_cal,
+ zbx_tx_dsa_cal::NUM_GAIN_STAGES,
+ zbx_tx_dsa_cal::NUM_DSA>>(name, serial, timestamp);
+}
+
+std::shared_ptr<zbx_rx_dsa_cal> zbx_rx_dsa_cal::make()
+{
+ return std::make_shared<dsa_cal_impl<
+ zbx_rx_dsa_cal,
+ zbx_rx_dsa_cal::NUM_GAIN_STAGES,
+ zbx_rx_dsa_cal::NUM_DSA>>();
+}
+
+std::shared_ptr<zbx_rx_dsa_cal> zbx_rx_dsa_cal::make(
+ const std::string& name, const std::string& serial, const uint64_t timestamp)
+{
+ return std::make_shared<dsa_cal_impl<
+ zbx_rx_dsa_cal,
+ zbx_rx_dsa_cal::NUM_GAIN_STAGES,
+ zbx_rx_dsa_cal::NUM_DSA>>(name, serial, timestamp);
+}