diff options
author | Martin Braun <martin.braun@ettus.com> | 2020-04-01 16:44:08 -0700 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2020-04-17 07:58:19 -0500 |
commit | 1a19bc653ee499545addb2a8718d12069bfd6fcd (patch) | |
tree | fa9c0b16432d559163e1e93dde836efd2fe3a1de /host/include | |
parent | 760abdf70315be16943496a1a1375f78660d96d8 (diff) | |
download | uhd-1a19bc653ee499545addb2a8718d12069bfd6fcd.tar.gz uhd-1a19bc653ee499545addb2a8718d12069bfd6fcd.tar.bz2 uhd-1a19bc653ee499545addb2a8718d12069bfd6fcd.zip |
cal: Add pwr_cal container
This is a cal container for all types of power cal (RX or TX) that rely
on a single, overall gain value.
Includes Python API.
Diffstat (limited to 'host/include')
-rw-r--r-- | host/include/uhd/cal/CMakeLists.txt | 5 | ||||
-rw-r--r-- | host/include/uhd/cal/pwr_cal.fbs | 46 | ||||
-rw-r--r-- | host/include/uhd/cal/pwr_cal.hpp | 151 | ||||
-rw-r--r-- | host/include/uhd/cal/pwr_cal_generated.h | 340 |
4 files changed, 541 insertions, 1 deletions
diff --git a/host/include/uhd/cal/CMakeLists.txt b/host/include/uhd/cal/CMakeLists.txt index 8d8dfa11e..38b4885ef 100644 --- a/host/include/uhd/cal/CMakeLists.txt +++ b/host/include/uhd/cal/CMakeLists.txt @@ -5,16 +5,19 @@ # UHD_INSTALL(FILES - database.hpp container.hpp + database.hpp iq_cal.hpp + pwr_cal.hpp iq_cal_generated.h + pwr_cal_generated.h DESTINATION ${INCLUDE_DIR}/uhd/cal COMPONENT headers ) UHD_INSTALL(FILES iq_cal.fbs + pwr_cal.fbs DESTINATION ${PKG_DATA_DIR}/cal COMPONENT headers ) diff --git a/host/include/uhd/cal/pwr_cal.fbs b/host/include/uhd/cal/pwr_cal.fbs new file mode 100644 index 000000000..7cf66feee --- /dev/null +++ b/host/include/uhd/cal/pwr_cal.fbs @@ -0,0 +1,46 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +// Calibration Table for power calibration coefficents. +// See also pwr_cal.hpp + +include "cal_metadata.fbs"; + +namespace uhd.usrp.cal; + +// Maps a gain value (dB) to a power value (dBm) +struct PowerMap +{ + gain: double (key); + power_dbm: double; +} + +// For a given frequency, stores all gain -> power measurements, as well as a +// max and min power level the device can reasonably produce. +table FreqPowerMap +{ + freq: ulong (key); + powers: [PowerMap]; + min_power: double; // Minimum power achievable at this frequency + max_power: double; // Maximum power achievable at this frequency +} + +// Given a temperature, store a gain table +table TempFreqMap +{ + temperature: int (key); // Temperature in C + freqs: [FreqPowerMap]; +} + +table PowerCal +{ + metadata: Metadata; + temp_freq_map: [TempFreqMap]; + ref_gain: int = -1; +} + +root_type PowerCal; +file_identifier "dB/m"; // dB per dBm diff --git a/host/include/uhd/cal/pwr_cal.hpp b/host/include/uhd/cal/pwr_cal.hpp new file mode 100644 index 000000000..b30c6dfc2 --- /dev/null +++ b/host/include/uhd/cal/pwr_cal.hpp @@ -0,0 +1,151 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include <uhd/cal/container.hpp> +#include <uhd/config.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/optional.hpp> +#include <map> + +namespace uhd { namespace usrp { namespace cal { + +/*! Class that stores power levels (in dBm) at various gains, frequencies, and + * temperatures. + * + * This container class is suitable for all cases where devices want to store a + * gain-to-power mapping from a single, overall gain value. + * + * The underlying data structure stores the power level for every gain/frequency + * point that is part of this data set. It can also do a reverse look-up of gain + * values for a given power value. + * + * The interpolation algorithms assume a monotonic gain/power profile, i.e., + * f(gain) = power is monotonically increasing. If the power is not monotonically + * increasing, the interpolation algorithms still work, but get_gain() might + * produce a greater than necessary interpolation error. For that case, this + * class provides get_gain_coerced(), which helps with both coercion of the + * interpolated gain into a gain value that can be used at the call site, as + * well as minimizing the effect on the application using this container. + * + * All interpolation algorithms first interpolate temperature by finding the + * nearest available temperature data. For example, if the data set includes + * calibration data for 40C and 50C, and the actual temperature is measured at + * 48C, then the data set for 50C is used, and the data set for 40C is not + * considered at all. + * Within a data set, frequency and gain are interpolated in two dimensions (the + * same is true for frequency and power for get_gain() and get_gain_coerced()) + * using a bilinear interpolation. + */ +class UHD_API pwr_cal : public container +{ +public: + using sptr = std::shared_ptr<pwr_cal>; + + /*! Add or update a power level data point + * + * Note: Power measurements can only be written atomically. It is not + * possible to add individual gain/power points using this method. + * + * \param gain_power_map A mapping gain -> power (dB -> dBm) for all + * measured gain points for this frequency. + * \param max_power The maximum available power for this frequency. + * \param min_power The minimum available power for this frequency. + * \param freq The frequency at which this power level was measured + * \param temperature The temperature at which this power level was measured, + * in Celsius. This parameter is optional, the return + * value for get_temperature() is used if no temperature + * is provided here. + */ + virtual void add_power_table(const std::map<double, double>& gain_power_map, + const double max_power, + const double min_power, + const double freq, + const boost::optional<int> temperature = boost::none) = 0; + + /*! Clear all stored values + * + * Note: All of the getters will throw a uhd::assertion_error if called after + * clearing the data. + */ + virtual void clear() = 0; + + /*! Set the current default temperature + * + * Some of the API calls have a optional temperature argument. However, in + * practice, it is often useful to set an ambient temperature once, or only + * when it has changed, and then use a default temperature instead of + * providing the temperature on every call. This sets the default + * temperature. + */ + virtual void set_temperature(const int temperature) = 0; + + //! Return the current default temperature + virtual int get_temperature() const = 0; + + /*! Set the reference gain point, i.e., the gain value where by definition + * the difference between linearized power and measured power is zero. + * + * Currently unused. + */ + virtual void set_ref_gain(const double gain) = 0; + + /*! Return the current reference gain point. + * + * Currently unused. + */ + virtual double get_ref_gain() const = 0; + + /*! Return the min and max power available for this frequency + */ + virtual uhd::meta_range_t get_power_limits(const double freq, + const boost::optional<int> temperature = boost::none) const = 0; + + /*! Returns the power at a gain value. + * + * This will interpolate from the given data set to obtain a power value if + * gain and frequency are not exactly within the given data set. + * + * \param gain The gain at which we are checking the power + * \param freq The frequency at which we are checking the power + * \param temperature The temperature at which we are checking the power. If + * none is given, uses the current default temperature + * (see set_temperature()). + */ + virtual double get_power(const double gain, + const double freq, + const boost::optional<int> temperature = boost::none) const = 0; + + /*! Look up a gain value from a power value. + * + * This is the reverse function to get_power(). Like get_power(), it will + * interpolate in two dimensions (linearly) if the requested power value is + * not part of the measurement data. + * + * Note: \p power_dbm is coerced into the available power range using + * get_power_limits(). + * + * \param power_dbm The power (in dBm) at which we are getting the gain value for + * \param freq The frequency at which we are finding the gain + * \param temperature The temperature at which we are finding the gain. If + * none is given, uses the current default temperature + * (see set_temperature()). + */ + virtual double get_gain(const double power_dbm, + const double freq, + const boost::optional<int> temperature = boost::none) const = 0; + + //! Factory for new cal data sets + static sptr make( + const std::string& name, const std::string& serial, const uint64_t timestamp); + + //! Default factory + static sptr make(); +}; + +}}} // namespace uhd::usrp::cal + diff --git a/host/include/uhd/cal/pwr_cal_generated.h b/host/include/uhd/cal/pwr_cal_generated.h new file mode 100644 index 000000000..f7ea8d8a7 --- /dev/null +++ b/host/include/uhd/cal/pwr_cal_generated.h @@ -0,0 +1,340 @@ +// automatically generated by the FlatBuffers compiler, do not modify + + +#ifndef FLATBUFFERS_GENERATED_PWRCAL_UHD_USRP_CAL_H_ +#define FLATBUFFERS_GENERATED_PWRCAL_UHD_USRP_CAL_H_ + +#include "flatbuffers/flatbuffers.h" + +#include "cal_metadata_generated.h" + +namespace uhd { +namespace usrp { +namespace cal { + +struct PowerMap; + +struct FreqPowerMap; +struct FreqPowerMapBuilder; + +struct TempFreqMap; +struct TempFreqMapBuilder; + +struct PowerCal; +struct PowerCalBuilder; + +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) PowerMap FLATBUFFERS_FINAL_CLASS { + private: + double gain_; + double power_dbm_; + + public: + PowerMap() { + memset(static_cast<void *>(this), 0, sizeof(PowerMap)); + } + PowerMap(double _gain, double _power_dbm) + : gain_(flatbuffers::EndianScalar(_gain)), + power_dbm_(flatbuffers::EndianScalar(_power_dbm)) { + } + double gain() const { + return flatbuffers::EndianScalar(gain_); + } + bool KeyCompareLessThan(const PowerMap *o) const { + return gain() < o->gain(); + } + int KeyCompareWithValue(double val) const { + return static_cast<int>(gain() > val) - static_cast<int>(gain() < val); + } + double power_dbm() const { + return flatbuffers::EndianScalar(power_dbm_); + } +}; +FLATBUFFERS_STRUCT_END(PowerMap, 16); + +struct FreqPowerMap FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef FreqPowerMapBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_FREQ = 4, + VT_POWERS = 6, + VT_MIN_POWER = 8, + VT_MAX_POWER = 10 + }; + uint64_t freq() const { + return GetField<uint64_t>(VT_FREQ, 0); + } + bool KeyCompareLessThan(const FreqPowerMap *o) const { + return freq() < o->freq(); + } + int KeyCompareWithValue(uint64_t val) const { + return static_cast<int>(freq() > val) - static_cast<int>(freq() < val); + } + const flatbuffers::Vector<const uhd::usrp::cal::PowerMap *> *powers() const { + return GetPointer<const flatbuffers::Vector<const uhd::usrp::cal::PowerMap *> *>(VT_POWERS); + } + double min_power() const { + return GetField<double>(VT_MIN_POWER, 0.0); + } + double max_power() const { + return GetField<double>(VT_MAX_POWER, 0.0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField<uint64_t>(verifier, VT_FREQ) && + VerifyOffset(verifier, VT_POWERS) && + verifier.VerifyVector(powers()) && + VerifyField<double>(verifier, VT_MIN_POWER) && + VerifyField<double>(verifier, VT_MAX_POWER) && + verifier.EndTable(); + } +}; + +struct FreqPowerMapBuilder { + typedef FreqPowerMap Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_freq(uint64_t freq) { + fbb_.AddElement<uint64_t>(FreqPowerMap::VT_FREQ, freq, 0); + } + void add_powers(flatbuffers::Offset<flatbuffers::Vector<const uhd::usrp::cal::PowerMap *>> powers) { + fbb_.AddOffset(FreqPowerMap::VT_POWERS, powers); + } + void add_min_power(double min_power) { + fbb_.AddElement<double>(FreqPowerMap::VT_MIN_POWER, min_power, 0.0); + } + void add_max_power(double max_power) { + fbb_.AddElement<double>(FreqPowerMap::VT_MAX_POWER, max_power, 0.0); + } + explicit FreqPowerMapBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + FreqPowerMapBuilder &operator=(const FreqPowerMapBuilder &); + flatbuffers::Offset<FreqPowerMap> Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset<FreqPowerMap>(end); + return o; + } +}; + +inline flatbuffers::Offset<FreqPowerMap> CreateFreqPowerMap( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t freq = 0, + flatbuffers::Offset<flatbuffers::Vector<const uhd::usrp::cal::PowerMap *>> powers = 0, + double min_power = 0.0, + double max_power = 0.0) { + FreqPowerMapBuilder builder_(_fbb); + builder_.add_max_power(max_power); + builder_.add_min_power(min_power); + builder_.add_freq(freq); + builder_.add_powers(powers); + return builder_.Finish(); +} + +inline flatbuffers::Offset<FreqPowerMap> CreateFreqPowerMapDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t freq = 0, + std::vector<uhd::usrp::cal::PowerMap> *powers = nullptr, + double min_power = 0.0, + double max_power = 0.0) { + auto powers__ = powers ? _fbb.CreateVectorOfSortedStructs<uhd::usrp::cal::PowerMap>(powers) : 0; + return uhd::usrp::cal::CreateFreqPowerMap( + _fbb, + freq, + powers__, + min_power, + max_power); +} + +struct TempFreqMap FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef TempFreqMapBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_TEMPERATURE = 4, + VT_FREQS = 6 + }; + int32_t temperature() const { + return GetField<int32_t>(VT_TEMPERATURE, 0); + } + bool KeyCompareLessThan(const TempFreqMap *o) const { + return temperature() < o->temperature(); + } + int KeyCompareWithValue(int32_t val) const { + return static_cast<int>(temperature() > val) - static_cast<int>(temperature() < val); + } + const flatbuffers::Vector<flatbuffers::Offset<uhd::usrp::cal::FreqPowerMap>> *freqs() const { + return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<uhd::usrp::cal::FreqPowerMap>> *>(VT_FREQS); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField<int32_t>(verifier, VT_TEMPERATURE) && + VerifyOffset(verifier, VT_FREQS) && + verifier.VerifyVector(freqs()) && + verifier.VerifyVectorOfTables(freqs()) && + verifier.EndTable(); + } +}; + +struct TempFreqMapBuilder { + typedef TempFreqMap Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_temperature(int32_t temperature) { + fbb_.AddElement<int32_t>(TempFreqMap::VT_TEMPERATURE, temperature, 0); + } + void add_freqs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<uhd::usrp::cal::FreqPowerMap>>> freqs) { + fbb_.AddOffset(TempFreqMap::VT_FREQS, freqs); + } + explicit TempFreqMapBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + TempFreqMapBuilder &operator=(const TempFreqMapBuilder &); + flatbuffers::Offset<TempFreqMap> Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset<TempFreqMap>(end); + return o; + } +}; + +inline flatbuffers::Offset<TempFreqMap> CreateTempFreqMap( + flatbuffers::FlatBufferBuilder &_fbb, + int32_t temperature = 0, + flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<uhd::usrp::cal::FreqPowerMap>>> freqs = 0) { + TempFreqMapBuilder builder_(_fbb); + builder_.add_freqs(freqs); + builder_.add_temperature(temperature); + return builder_.Finish(); +} + +inline flatbuffers::Offset<TempFreqMap> CreateTempFreqMapDirect( + flatbuffers::FlatBufferBuilder &_fbb, + int32_t temperature = 0, + std::vector<flatbuffers::Offset<uhd::usrp::cal::FreqPowerMap>> *freqs = nullptr) { + auto freqs__ = freqs ? _fbb.CreateVectorOfSortedTables<uhd::usrp::cal::FreqPowerMap>(freqs) : 0; + return uhd::usrp::cal::CreateTempFreqMap( + _fbb, + temperature, + freqs__); +} + +struct PowerCal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef PowerCalBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_METADATA = 4, + VT_TEMP_FREQ_MAP = 6, + VT_REF_GAIN = 8 + }; + const Metadata *metadata() const { + return GetPointer<const Metadata *>(VT_METADATA); + } + const flatbuffers::Vector<flatbuffers::Offset<uhd::usrp::cal::TempFreqMap>> *temp_freq_map() const { + return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<uhd::usrp::cal::TempFreqMap>> *>(VT_TEMP_FREQ_MAP); + } + int32_t ref_gain() const { + return GetField<int32_t>(VT_REF_GAIN, -1); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_METADATA) && + verifier.VerifyTable(metadata()) && + VerifyOffset(verifier, VT_TEMP_FREQ_MAP) && + verifier.VerifyVector(temp_freq_map()) && + verifier.VerifyVectorOfTables(temp_freq_map()) && + VerifyField<int32_t>(verifier, VT_REF_GAIN) && + verifier.EndTable(); + } +}; + +struct PowerCalBuilder { + typedef PowerCal Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_metadata(flatbuffers::Offset<Metadata> metadata) { + fbb_.AddOffset(PowerCal::VT_METADATA, metadata); + } + void add_temp_freq_map(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<uhd::usrp::cal::TempFreqMap>>> temp_freq_map) { + fbb_.AddOffset(PowerCal::VT_TEMP_FREQ_MAP, temp_freq_map); + } + void add_ref_gain(int32_t ref_gain) { + fbb_.AddElement<int32_t>(PowerCal::VT_REF_GAIN, ref_gain, -1); + } + explicit PowerCalBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + PowerCalBuilder &operator=(const PowerCalBuilder &); + flatbuffers::Offset<PowerCal> Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset<PowerCal>(end); + return o; + } +}; + +inline flatbuffers::Offset<PowerCal> CreatePowerCal( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset<Metadata> metadata = 0, + flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<uhd::usrp::cal::TempFreqMap>>> temp_freq_map = 0, + int32_t ref_gain = -1) { + PowerCalBuilder builder_(_fbb); + builder_.add_ref_gain(ref_gain); + builder_.add_temp_freq_map(temp_freq_map); + builder_.add_metadata(metadata); + return builder_.Finish(); +} + +inline flatbuffers::Offset<PowerCal> CreatePowerCalDirect( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset<Metadata> metadata = 0, + std::vector<flatbuffers::Offset<uhd::usrp::cal::TempFreqMap>> *temp_freq_map = nullptr, + int32_t ref_gain = -1) { + auto temp_freq_map__ = temp_freq_map ? _fbb.CreateVectorOfSortedTables<uhd::usrp::cal::TempFreqMap>(temp_freq_map) : 0; + return uhd::usrp::cal::CreatePowerCal( + _fbb, + metadata, + temp_freq_map__, + ref_gain); +} + +inline const uhd::usrp::cal::PowerCal *GetPowerCal(const void *buf) { + return flatbuffers::GetRoot<uhd::usrp::cal::PowerCal>(buf); +} + +inline const uhd::usrp::cal::PowerCal *GetSizePrefixedPowerCal(const void *buf) { + return flatbuffers::GetSizePrefixedRoot<uhd::usrp::cal::PowerCal>(buf); +} + +inline const char *PowerCalIdentifier() { + return "dB/m"; +} + +inline bool PowerCalBufferHasIdentifier(const void *buf) { + return flatbuffers::BufferHasIdentifier( + buf, PowerCalIdentifier()); +} + +inline bool VerifyPowerCalBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer<uhd::usrp::cal::PowerCal>(PowerCalIdentifier()); +} + +inline bool VerifySizePrefixedPowerCalBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer<uhd::usrp::cal::PowerCal>(PowerCalIdentifier()); +} + +inline void FinishPowerCalBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset<uhd::usrp::cal::PowerCal> root) { + fbb.Finish(root, PowerCalIdentifier()); +} + +inline void FinishSizePrefixedPowerCalBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset<uhd::usrp::cal::PowerCal> root) { + fbb.FinishSizePrefixed(root, PowerCalIdentifier()); +} + +} // namespace cal +} // namespace usrp +} // namespace uhd + +#endif // FLATBUFFERS_GENERATED_PWRCAL_UHD_USRP_CAL_H_ |