diff options
author | Paul David <paul.david@ettus.com> | 2016-09-23 10:48:32 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2016-11-29 15:02:16 -0800 |
commit | a9dfc903fa3c141d58a2575898b47c9592d51e98 (patch) | |
tree | e663a1918a3a66dccd5d40749eac8abf26ed69d9 | |
parent | fc54c27b48028c9c8139175d40b4b061800f32e3 (diff) | |
download | uhd-a9dfc903fa3c141d58a2575898b47c9592d51e98.tar.gz uhd-a9dfc903fa3c141d58a2575898b47c9592d51e98.tar.bz2 uhd-a9dfc903fa3c141d58a2575898b47c9592d51e98.zip |
calibration: generic containers for datasets
- Includes a container for power calibration data
- Unit tests to check underlying container functionality
- Nearest neighbor and bilinear interpolation
-rw-r--r-- | host/include/uhd/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/include/uhd/cal/CMakeLists.txt | 23 | ||||
-rw-r--r-- | host/include/uhd/cal/container.hpp | 104 | ||||
-rw-r--r-- | host/include/uhd/cal/power_container.hpp | 79 | ||||
-rw-r--r-- | host/lib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/lib/cal/CMakeLists.txt | 32 | ||||
-rw-r--r-- | host/lib/cal/interpolation.hpp | 71 | ||||
-rw-r--r-- | host/lib/cal/interpolation.ipp | 199 | ||||
-rw-r--r-- | host/lib/cal/power_container_impl.cpp | 80 | ||||
-rw-r--r-- | host/lib/cal/power_container_impl.hpp | 77 | ||||
-rw-r--r-- | host/tests/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/tests/cal_container_test.cpp | 200 |
12 files changed, 868 insertions, 0 deletions
diff --git a/host/include/uhd/CMakeLists.txt b/host/include/uhd/CMakeLists.txt index e31ff80a0..42c89a9f3 100644 --- a/host/include/uhd/CMakeLists.txt +++ b/host/include/uhd/CMakeLists.txt @@ -18,6 +18,7 @@ ADD_SUBDIRECTORY(rfnoc) ADD_SUBDIRECTORY(transport) ADD_SUBDIRECTORY(types) +ADD_SUBDIRECTORY(cal) ADD_SUBDIRECTORY(usrp) ADD_SUBDIRECTORY(usrp_clock) ADD_SUBDIRECTORY(utils) diff --git a/host/include/uhd/cal/CMakeLists.txt b/host/include/uhd/cal/CMakeLists.txt new file mode 100644 index 000000000..14107ee53 --- /dev/null +++ b/host/include/uhd/cal/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2016 Ettus Research +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +UHD_INSTALL(FILES + container.hpp + power_container.hpp + DESTINATION ${INCLUDE_DIR}/uhd/cal + COMPONENT headers +) diff --git a/host/include/uhd/cal/container.hpp b/host/include/uhd/cal/container.hpp new file mode 100644 index 000000000..e4f418311 --- /dev/null +++ b/host/include/uhd/cal/container.hpp @@ -0,0 +1,104 @@ +// +// Copyright 2016 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_CAL_CONTAINER_HPP +#define INCLUDED_UHD_CAL_CONTAINER_HPP + +#include <uhd/config.hpp> +#include <boost/serialization/serialization.hpp> +#include <boost/serialization/vector.hpp> +#include <boost/serialization/string.hpp> +#include <boost/serialization/map.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/shared_ptr.hpp> + +namespace uhd { +namespace cal { + +class base_container { +public: + typedef std::map<std::string, std::string> metadata_t; + typedef boost::shared_ptr<base_container> sptr; +}; + +/*! + * An interface for creating and managing a generic calibration + * data container. + * + * These containers are used to represent N dimensional data structures + * in order to accommodate a mapping from multi-variable input to a scalar + * value (e.g. gain, frequency, temperature -> power level [dBm]). + * + * The container only supports inputs of the same type to be mapped. + * + */ +template<typename in_type, typename out_type> +class UHD_API cal_container : public base_container { +public: + typedef std::map<in_type, out_type> container_t; + + /*! + * Get the mapping from an input to an output + * from the calibration container. + * + * \param args input values + * \returns the output of the mapping (a scalar value) + * \throws uhd::assertion_error if the dimensions of the input args + * are incorrect for this container + */ + virtual out_type get(const in_type &args) = 0; + + /*! + * Add a data point to the container. + * This function records a mapping R^n -> R between an input vector + * and output scalar. + * + * \param output the output of the data point mapping + * \param args input values + */ + virtual void add(const out_type output, const in_type &args) = 0; + + /*! + * Associate some metadata with the container. + * + * \param data a map of metadata (string -> string). + */ + virtual void add_metadata(const metadata_t &data) = 0; + + /*! + * Retrieve metadata from the container. + * + * \returns map of metadata. + */ + virtual const metadata_t &get_metadata() = 0; + +public: + typedef boost::archive::text_iarchive iarchive_type; + typedef boost::archive::text_oarchive oarchive_type; + +protected: + friend class boost::serialization::access; + + virtual void serialize(iarchive_type & ar, const unsigned int) = 0; + virtual void serialize(oarchive_type & ar, const unsigned int) = 0; +}; + +} // namespace cal +} // namespace uhd + +#endif /* INCLUDED_UHD_CAL_CONTAINER_HPP */ diff --git a/host/include/uhd/cal/power_container.hpp b/host/include/uhd/cal/power_container.hpp new file mode 100644 index 000000000..37f7bd8df --- /dev/null +++ b/host/include/uhd/cal/power_container.hpp @@ -0,0 +1,79 @@ +// +// Copyright 2016 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_CAL_POWER_CONTAINER_HPP +#define INCLUDED_UHD_CAL_POWER_CONTAINER_HPP + +#include <uhd/config.hpp> +#include <uhd/cal/container.hpp> +#include <boost/shared_ptr.hpp> + +namespace uhd { +namespace cal { + +class UHD_API power_container : public cal_container<std::vector<double>, double> { +public: + typedef boost::shared_ptr<power_container> sptr; + + /*! + * Create a container for data related to power calibration. + * + * \returns shared pointer to the container + */ + static sptr make(); + + /*! + * Get the mapping from an input to an output + * from the calibration container. + * + * \param args input values + * \returns the output of the mapping (a scalar value) + * \throws uhd::assertion_error if the number of input values are incorrect + * for the container type + */ + virtual double get(const std::vector<double> &args) = 0; + + /*! + * Add a data point to the container. + * This function records a mapping R^n -> R between an input vector + * and output scalar. For example, a mapping might be + * (power level, frequency, temperature) -> gain. + * + * \param output the output of the data point mapping + * \param args input values + */ + virtual void add(const double output, const std::vector<double> &args) = 0; + + /*! + * Associate some metadata with the container. + * + * \param data a map of metadata (string -> string). + */ + virtual void add_metadata(const metadata_t &data) = 0; + + /*! + * Retrieve metadata from the container. + * + * \returns map of metadata. + */ + virtual const metadata_t &get_metadata() = 0; +}; + +} // namespace cal +} // namespace uhd + +#endif /* INCLUDED_UHD_CAL_POWER_CONTAINER_HPP */ diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index a4be9fd81..fce1021c1 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -91,6 +91,7 @@ INCLUDE_SUBDIRECTORY(ic_reg_maps) INCLUDE_SUBDIRECTORY(types) INCLUDE_SUBDIRECTORY(convert) INCLUDE_SUBDIRECTORY(rfnoc) +INCLUDE_SUBDIRECTORY(cal) INCLUDE_SUBDIRECTORY(usrp) INCLUDE_SUBDIRECTORY(usrp_clock) INCLUDE_SUBDIRECTORY(utils) diff --git a/host/lib/cal/CMakeLists.txt b/host/lib/cal/CMakeLists.txt new file mode 100644 index 000000000..5c616a9da --- /dev/null +++ b/host/lib/cal/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# Copyright 2016 Ettus Research +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## +INCLUDE(CheckIncludeFileCXX) +MESSAGE(STATUS "") + +######################################################################## +# Convert types generation +######################################################################## +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + +LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/power_container_impl.cpp +) diff --git a/host/lib/cal/interpolation.hpp b/host/lib/cal/interpolation.hpp new file mode 100644 index 000000000..34f084fbd --- /dev/null +++ b/host/lib/cal/interpolation.hpp @@ -0,0 +1,71 @@ +// +// Copyright 2016 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_INTERPOLATION_HPP +#define INCLUDED_UHD_INTERPOLATION_HPP + +#include <uhd/exception.hpp> +#include <boost/format.hpp> +#include <map> +#include <cmath> + +namespace uhd { +namespace cal { + +template<typename in_type, typename out_type> +struct interp +{ +public: + typedef std::vector<in_type> args_t; + typedef std::map<args_t, out_type> container_t; + + /*! + * Nearest neighbor interpolation given a mapping: R^n -> R + * + * 1) search for the nearest point in R^n + * 2) find the nearest output scalars in R + * + * \param data input data container + * \param args input data point + * \returns interpolated output value + */ + const out_type nn_interp(container_t &data, const args_t &args); + + /*! + * Bilinear interpolation given a mapping: R^2 -> R + * + * 1) search for 4 surrounding points in R^2 + * 2) find the output scalars in R + * 3) solve the system of equations given our input mappings + * + * \param data input data container + * \param args input data point + * \returns interpolated output value + */ + const out_type bl_interp(container_t &data, const args_t &args); + +private: + /*! + * Calculate the distance between two points + */ + static in_type calc_dist(const args_t &a, const args_t &b); +}; + +} // namespace cal +} // namespace uhd + +#endif /* INCLUDED_UHD_INTERPOLATION_HPP */ diff --git a/host/lib/cal/interpolation.ipp b/host/lib/cal/interpolation.ipp new file mode 100644 index 000000000..f58b70b68 --- /dev/null +++ b/host/lib/cal/interpolation.ipp @@ -0,0 +1,199 @@ +// +// Copyright 2016 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_INTERPOLATION_IPP +#define INCLUDED_UHD_INTERPOLATION_IPP + +#include "interpolation.hpp" +#include <uhd/utils/msg.hpp> +#include <boost/numeric/ublas/io.hpp> +#include <boost/numeric/ublas/matrix.hpp> +#include <boost/numeric/ublas/lu.hpp> + +using namespace boost::numeric; + +namespace uhd { +namespace cal { + +#define CAL_INTERP_METHOD(return_type, method, args, ...) \ + template<typename in_type, typename out_type> \ + return_type interp<in_type, out_type>::\ + method(args, __VA_ARGS__) + +#define ARGS_T typename interp<in_type, out_type>::args_t +#define CONTAINER_T typename interp<in_type, out_type>::container_t + +CAL_INTERP_METHOD(in_type, calc_dist, const ARGS_T &a, const ARGS_T &b) +{ + in_type dist = 0; + for (size_t i = 0; i < std::min(a.size(), b.size()); i++) + { + dist += std::abs(a[i] - b[i]); + } + return dist; +} + +CAL_INTERP_METHOD(const out_type, nn_interp, CONTAINER_T &data, const ARGS_T &args) +{ + // Check the cache for the output + if (data.find(args) != data.end()) { + return data[args]; + } + + out_type output = 0; + in_type min_dist = 0; + typename container_t::const_iterator citer; + for (citer = data.begin(); citer != data.end(); citer++) + { + in_type dist = calc_dist(citer->first, args); + if (citer == data.begin() || dist < min_dist) { + min_dist = dist; + output = data[citer->first]; + } + } + + return output; +} + +CAL_INTERP_METHOD(const out_type, bl_interp, CONTAINER_T &data, const ARGS_T &args) +{ + if (args.size() != 2) { + throw uhd::assertion_error(str(boost::format( + "Bilinear interpolation expects 2D values. Received %d.") + % args.size() + )); + } + + if (data.size() < 4) { + throw uhd::assertion_error(str(boost::format( + "Bilinear interpolation requires at least 4 input points. Found %d.") + % data.size() + )); + } + + // Locate the nearest 4 points + typedef std::pair<interp<in_type, out_type>::args_t, out_type> cal_pair_t; + typename std::vector<cal_pair_t> nearest; + + // Initialize the resulting pair to something + cal_pair_t pair = *data.begin(); + + for (size_t i = 0; i < 4; i++) { + bool init = true; + in_type min_dist = 0; + typename container_t::const_iterator citer; + for (citer = data.begin(); citer != data.end(); citer++) + { + cal_pair_t temp = *citer; + if (std::find(nearest.begin(), nearest.end(), temp) == nearest.end()) + { + in_type dist = calc_dist(citer->first, args); + if (dist < min_dist || init) + { + min_dist = dist; + pair = temp; + init = false; + } + } + } + // Push back the nearest pair + nearest.push_back(pair); + } + + // + // Since these points are not grid aligned, + // we perform irregular bilinear interpolation. + // This math involves finding our interpolation + // function using lagrange multipliers: + // + // f(x, y) = ax^2 + bxy + cy^2 + dx + ey + f + // + // The solution is to solve the following system: + // + // A x b + // | E X' | | s | - | 0 | + // | X 0 | | l | - | z | + // + // where s is a vector of the above coefficients. + // + typename ublas::matrix<in_type> A(10, 10, 0.0); + + // E + A(0, 0) = 1.0; A(1, 1) = 1.0; A(2, 2) = 1.0; + + in_type x1, x2, x3, x4; + in_type y1, y2, y3, y4; + + x1 = nearest[0].first[0]; y1 = nearest[0].first[1]; + x2 = nearest[1].first[0]; y2 = nearest[1].first[1]; + x3 = nearest[2].first[0]; y3 = nearest[2].first[1]; + x4 = nearest[3].first[0]; y4 = nearest[3].first[1]; + + // X + A(0, 6) = x1*x1; A(1, 6) = x1*y1; A(2, 6) = y1*y1; A(3, 6) = x1; A(4, 6) = y1; A(5, 6) = 1.0; + A(0, 7) = x2*x2; A(1, 7) = x2*y2; A(2, 7) = y2*y2; A(3, 7) = x2; A(4, 7) = y2; A(5, 7) = 1.0; + A(0, 8) = x3*x3; A(1, 8) = x3*y3; A(2, 8) = y3*y3; A(3, 8) = x3; A(4, 8) = y3; A(5, 8) = 1.0; + A(0, 9) = x4*x4; A(1, 9) = x4*y4; A(2, 9) = y4*y4; A(3, 9) = x4; A(4, 9) = y4; A(5, 9) = 1.0; + + // X' + A(6, 0) = x1*x1; A(6, 1) = x1*y1; A(6, 2) = y1*y1; A(6, 3) = x1; A(6, 4) = y1; A(6, 5) = 1.0; + A(7, 0) = x2*x2; A(7, 1) = x2*y2; A(7, 2) = y2*y2; A(7, 3) = x2; A(7, 4) = y2; A(7, 5) = 1.0; + A(8, 0) = x3*x3; A(8, 1) = x3*y3; A(8, 2) = y3*y3; A(8, 3) = x3; A(8, 4) = y3; A(8, 5) = 1.0; + A(9, 0) = x4*x4; A(9, 1) = x4*y4; A(9, 2) = y4*y4; A(9, 3) = x4; A(9, 4) = y4; A(9, 5) = 1.0; + + // z + typename ublas::vector<in_type> b(10, 0.0); + b(6) = nearest[0].second; + b(7) = nearest[1].second; + b(8) = nearest[2].second; + b(9) = nearest[3].second; + + typename ublas::matrix<in_type> A_t = A; + typename ublas::vector<in_type> s = b; + typename ublas::permutation_matrix<in_type> P(A_t.size1()); + + // Use LUP factorization to solve for the coefficients + // We're solving the problem in the form of Ax = b + bool is_singular = ublas::lu_factorize(A_t, P); + + out_type output = 0; + + // Fall back to 1D interpolation if the matrix is singular + if (is_singular) { + // Warn the user that the A matrix is singular + UHD_MSG(warning) << "Bilinear interpolation: singular matrix detected." << std::endl + << "Performing 1D linear interpolation against the nearest measurements." << std::endl + << "Provide calibration data with more measurements" << std::endl; + + output = (b[7] - b[6]) / 2.0; + output += b[6]; + return output; + } + ublas::lu_substitute(A_t, P, s); + + in_type x = args[0]; + in_type y = args[1]; + + // Utilize the solution to calculate the interpolation function + output = s[0]*x*x + s[1]*x*y + s[2]*y*y + s[3]*x + s[4]*y + s[5]; + return output; +} + +} // namespace cal +} // namespace uhd + +#endif /* INCLUDED_UHD_INTERPOLATION_IPP */ diff --git a/host/lib/cal/power_container_impl.cpp b/host/lib/cal/power_container_impl.cpp new file mode 100644 index 000000000..5d07c3b7e --- /dev/null +++ b/host/lib/cal/power_container_impl.cpp @@ -0,0 +1,80 @@ +// +// Copyright 2016 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "power_container_impl.hpp" +#include <uhd/exception.hpp> +#include <boost/format.hpp> + +using namespace uhd; +using namespace uhd::cal; + +power_container::sptr power_container::make() +{ + return power_container::sptr(new power_container_impl()); +} + +power_container_impl::power_container_impl() : + _nargs(0), _mode(interp_mode_t::NEAREST) +{ + /* NOP */ +} + +double power_container_impl::get(const std::vector<double> &args) +{ + this->verify_nargs(args); + switch (_mode) + { + case interp_mode_t::BILINEAR : + return _interpolator.bl_interp(_data, args); + case interp_mode_t::NEAREST : + return _interpolator.nn_interp(_data, args); + default: + return _interpolator.nn_interp(_data, args); + } +} + +void power_container_impl::add(const double output, const std::vector<double> &args) +{ + if (_nargs == 0) + { + _nargs = args.size(); + _mode = _nargs == 2 ? interp_mode_t::BILINEAR : interp_mode_t::NEAREST; + } + this->verify_nargs(args); + _data[args] = output; +} + +void power_container_impl::add_metadata(const power_container::metadata_t &data) +{ + _metadata = data; +} + +const power_container_impl::metadata_t &power_container_impl::get_metadata() +{ + return _metadata; +} + +void power_container_impl::verify_nargs(const std::vector<double> &args) +{ + // Check that the number of arguments expected are correct + if (args.size() != _nargs) { + throw uhd::assertion_error(str(boost::format( + "power_container_impl: Expected %d number of arguments/values instead of %d") + % _nargs % args.size() + )); + } +} diff --git a/host/lib/cal/power_container_impl.hpp b/host/lib/cal/power_container_impl.hpp new file mode 100644 index 000000000..4c2bcab79 --- /dev/null +++ b/host/lib/cal/power_container_impl.hpp @@ -0,0 +1,77 @@ +// +// Copyright 2016 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_CAL_POWER_CONTAINER_IMPL_HPP +#define INCLUDED_UHD_CAL_POWER_CONTAINER_IMPL_HPP + +#include "interpolation.ipp" +#include <uhd/cal/power_container.hpp> + +namespace uhd { +namespace cal { + +enum interp_mode_t +{ + BILINEAR, //! linear interpolation + NEAREST //! nearest neighbor interpolation +}; + +class power_container_impl : public power_container { +public: + typedef std::map<std::vector<double>, double> container_t; + + power_container_impl(); + + double get(const std::vector<double> &args); + const metadata_t &get_metadata(); + + void add(const double output, const std::vector<double> &args); + void add_metadata(const metadata_t &data); + +private: + // Container data to be serialized + size_t _nargs; + metadata_t _metadata; + interp_mode_t _mode; + container_t _data; + + interp<double, double> _interpolator; + + void verify_nargs(const std::vector<double> &args); + +protected: + friend class boost::serialization::access; + + void serialize(iarchive_type & ar, const unsigned int) { + ar & _nargs; + ar & _metadata; + ar & _mode; + ar & _data; + } + + void serialize(oarchive_type & ar, const unsigned int) { + ar & _nargs; + ar & _metadata; + ar & _mode; + ar & _data; + } +}; + +} // namespace cal +} // namespace uhd + +#endif /* INCLUDED_UHD_CAL_POWER_CONTAINER_IMPL_HPP */ diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 8f7fdcd7c..c691ce1fd 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -28,6 +28,7 @@ SET(test_sources buffer_test.cpp byteswap_test.cpp cast_test.cpp + cal_container_test.cpp chdr_test.cpp convert_test.cpp dict_test.cpp diff --git a/host/tests/cal_container_test.cpp b/host/tests/cal_container_test.cpp new file mode 100644 index 000000000..f45ca429d --- /dev/null +++ b/host/tests/cal_container_test.cpp @@ -0,0 +1,200 @@ +// +// Copyright 2016 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/cal/power_container.hpp> +#include <uhd/exception.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/shared_ptr.hpp> +#include <vector> +#include <fstream> + +using namespace uhd; +using namespace uhd::cal; + +static const double eps = 1e-8; + +//////////////////////////////////////////////////////////////////////// +BOOST_AUTO_TEST_CASE(test_power_container_bilinear){ +//////////////////////////////////////////////////////////////////////// + + // Create the data container + power_container::sptr container = power_container::make(); + + // Create some data points to add + std::vector<double> pt0(2, 0.0); + std::vector<double> pt1(2, 0.0); + std::vector<double> pt2(2, 0.0); + std::vector<double> pt3(2, 2.0); + + pt1[0] = 2.0; + pt2[1] = 2.0; + + container->add(1.0, pt0); + container->add(1.0, pt1); + container->add(0.0, pt2); + container->add(0.0, pt3); + + // Add points to interpolate against + std::vector<double> test0(2, 1.0); + std::vector<double> test1(2, 1.5); + std::vector<double> test2(2, 0.0); + test2[1] = 1.0; + + BOOST_CHECK_CLOSE(container->get(test0), 0.50, eps); + BOOST_CHECK_CLOSE(container->get(test1), 0.25, eps); + BOOST_CHECK_CLOSE(container->get(test2), 0.50, eps); +} + + +//////////////////////////////////////////////////////////////////////// +BOOST_AUTO_TEST_CASE(test_power_temp_container){ +//////////////////////////////////////////////////////////////////////// + + // Create the data container + power_container::sptr container = power_container::make(); + + // Create some data points to add + std::vector<double> pt0(3, 1.0); + std::vector<double> pt1(3, 2.0); + std::vector<double> pt2(3, 3.0); + + container->add(1.0, pt0); + container->add(2.0, pt1); + container->add(5.0, pt2); + + // Add points to interpolate against + std::vector<double> test0(3, 1.99); + std::vector<double> test1(3, 1.29); + std::vector<double> test2; + test2.push_back(2.59); + test2.push_back(1.29); + test2.push_back(2.99); + + BOOST_CHECK_CLOSE(container->get(test0), 2.0, eps); + BOOST_CHECK_CLOSE(container->get(test1), 1.0, eps); + BOOST_CHECK_CLOSE(container->get(test2), 5.0, eps); +} + +//////////////////////////////////////////////////////////////////////// +BOOST_AUTO_TEST_CASE(test_power_container_metadata){ +//////////////////////////////////////////////////////////////////////// + + // Create the data container + power_container::sptr container = power_container::make(); + + // Create some metadata to add + base_container::metadata_t data; + + std::string fake_serial = "F2A432"; + data["x300"] = fake_serial; + + // Add some metadata + container->add_metadata(data); + + // Check to see if the metadata matches + power_container::metadata_t recovered_data = container->get_metadata(); + + BOOST_CHECK_EQUAL(recovered_data["x300"], fake_serial); +} + +//////////////////////////////////////////////////////////////////////// +BOOST_AUTO_TEST_CASE(test_power_serialization){ +//////////////////////////////////////////////////////////////////////// + + // Create the data container + power_container::sptr container = power_container::make(); + + // Create some metadata to add + base_container::metadata_t data; + + std::string fake_serial = "F2A432"; + data["x300"] = fake_serial; + + // Add some metadata + container->add_metadata(data); + + // Create some data points to add + std::vector<double> pt0(3, 1.0); + std::vector<double> pt1(3, 2.0); + std::vector<double> pt2(3, 3.0); + + container->add(1.0, pt0); + container->add(2.0, pt1); + container->add(5.0, pt2); + + std::string filename("test_power_serialization"); + + // Create/open a file to store the container + { + std::ofstream ofile(filename.c_str()); + + boost::archive::text_oarchive oarchive(ofile); + oarchive << *container; + } + + // Restore to another data container + power_container::sptr new_container = power_container::make(); + + { + std::ifstream ifile(filename.c_str()); + boost::archive::text_iarchive iarchive(ifile); + + iarchive >> *new_container; + } + + // Add points to interpolate against + std::vector<double> test0(3, 1.99); + std::vector<double> test1(3, 1.29); + std::vector<double> test2; + test2.push_back(2.59); + test2.push_back(1.29); + test2.push_back(2.99); + + power_container::metadata_t recovered_data = new_container->get_metadata(); + + BOOST_CHECK_CLOSE(new_container->get(test0), 2.0, eps); + BOOST_CHECK_CLOSE(new_container->get(test1), 1.0, eps); + BOOST_CHECK_CLOSE(new_container->get(test2), 5.0, eps); + + // Check to see if the metadata matches + BOOST_CHECK_EQUAL(recovered_data["x300"], fake_serial); + + std::remove(filename.c_str()); +} + +//////////////////////////////////////////////////////////////////////// +BOOST_AUTO_TEST_CASE(test_interp_singular){ +//////////////////////////////////////////////////////////////////////// + + // Create the data container + power_container::sptr container = power_container::make(); + + // Create some data points to add + // that result in a singular matrix + std::vector<double> pt0(2, 1.0); + std::vector<double> pt1(2, 2.0); + std::vector<double> pt2(2, 3.0); + std::vector<double> pt3(2, 4.0); + + container->add(1.0, pt0); + container->add(2.0, pt1); + container->add(3.0, pt2); + container->add(4.0, pt3); + + std::vector<double> test(2, 2.5); + BOOST_CHECK_CLOSE(container->get(test), 2.5, eps); +} |