diff options
Diffstat (limited to 'host/lib/cal')
| -rw-r--r-- | host/lib/cal/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/cal/cal_python.hpp | 44 | ||||
| -rw-r--r-- | host/lib/cal/dsa_cal.cpp | 253 | 
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); +}  | 
