diff options
| author | Martin Braun <martin.braun@ettus.com> | 2020-04-09 11:20:29 -0700 | 
|---|---|---|
| committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2020-04-17 07:58:19 -0500 | 
| commit | 760abdf70315be16943496a1a1375f78660d96d8 (patch) | |
| tree | 220c0423ee6ccb4b6c4412f0f2538caa7e2d3514 /host/lib/include/uhdlib/utils | |
| parent | a01dd2da5b647acf7524a4d064f5942d823c686c (diff) | |
| download | uhd-760abdf70315be16943496a1a1375f78660d96d8.tar.gz uhd-760abdf70315be16943496a1a1375f78660d96d8.tar.bz2 uhd-760abdf70315be16943496a1a1375f78660d96d8.zip | |
lib: utils: interpolation: Add bilinear interpolation
This allows to treat a std::map<KeyType<std::map<KeyType, ValueType>> as
a set of x-y coordinates, and bilinearly interpolate a z-value given
four x/y pairs.
Diffstat (limited to 'host/lib/include/uhdlib/utils')
| -rw-r--r-- | host/lib/include/uhdlib/utils/interpolation.hpp | 117 | 
1 files changed, 117 insertions, 0 deletions
| diff --git a/host/lib/include/uhdlib/utils/interpolation.hpp b/host/lib/include/uhdlib/utils/interpolation.hpp index 6105dedc9..7d768eb7a 100644 --- a/host/lib/include/uhdlib/utils/interpolation.hpp +++ b/host/lib/include/uhdlib/utils/interpolation.hpp @@ -11,9 +11,43 @@  #include <uhd/utils/math.hpp>  #include <uhd/utils/interpolation.hpp>  #include <map> +#include <utility>  namespace uhd { namespace math { +//! Return a pair of iterators before and after a key within a map. +// +// Complexity: That of std::map::lower_bound (logarithmic). +// +// If \p key is lower or greater than the range of \p data then both the +// returned iterators will point to the first or last item in the map, respectively. +// If the key is found exactly in the map, then the second iterator will point to +// to that key, and the first iterator will point to the previous item (if +// possible). +template <typename map_type> +std::pair<typename map_type::const_iterator, typename map_type::const_iterator> +get_bounding_iterators(const map_type& data, const typename map_type::key_type& key) +{ +    // Get an iterator to the next bigger item +    auto next_it = data.lower_bound(key); +    if (next_it == data.end()) { +        // This means key is larger than our biggest key in data's first +        // dimension, and thus we return the value of the largest key. +        next_it--; +        return {next_it, next_it}; +    } +    auto prev_it = next_it; +    // If the next if clause is true, then x2 is already the smallest x value, +    // and we keep x1 == x2. Otherwise, we make x1 the next smaller available +    // x value. +    if (prev_it != data.begin()) { +        prev_it--; +    } + +    return {prev_it, next_it}; +} + +  //! Linearly interpolate f(x) given f(x0) = y0 and f(x1) = y1  //  // This draws a line through the coordinates x0/y0 and x1/y1, and then returns @@ -31,6 +65,39 @@ inline InterpType linear_interp(      return y0 + (x - x0) * (y1 - y0) / (x1 - x0);  } +//! Bi-Linearly interpolate f(x, y) given f(xi, yk) = zik and for i, k == 0, 0 +// +// This does one linear interpolation in x-direction and one in y-direction to +// return the z-value for the given x-value and y-value. +// +// \throws uhd::runtime_error if x0 == x1, or y0 == y1 since that doesn't allow +//         us to interpolate. +template <typename InterpType> +inline InterpType bilinear_interp(InterpType x, +    InterpType y, +    InterpType x0, +    InterpType y0, +    InterpType x1, +    InterpType y1, +    InterpType z00, +    InterpType z01, +    InterpType z10, +    InterpType z11) +{ +    if (x0 == x1) { +        throw uhd::runtime_error("bilinear_interp(): x0 and x1 must differ!"); +    } +    if (y0 == y1) { +        throw uhd::runtime_error("bilinear_interp(): y0 and y1 must differ!"); +    } + +    return linear_interp(y, +        y0, +        linear_interp(x, x0, z00, x1, z10), +        y1, +        linear_interp(x, x0, z01, x1, z11)); +} +  /*! Like std::at, but will interpolate the data between two points   *   * Most often, this is used with a std::map (keys need to be sorted!). Unlike a @@ -134,4 +201,54 @@ typename map_type::mapped_type at_lin_interp(          data, key, &uhd::math::linear_interp<typename map_type::mapped_type>);  } +//! Like std::map::at, except it will do a bilinear interpolation in two dimensions +// +// Example: +// ~~~{.cpp} +// std::map<double, std::map<double, double>> data; +// data[1.0][1.0] = 0.0; +// data[1.0][2.0] = 1.0; +// data[2.0][1.0] = 1.0; +// data[2.0][2.0] = 2.0; +// std::cout << at_bilin_interp(data, 1.5, 1.5) << std::endl; // prints 1.0 +// ~~~ +// +// This treats the double-map as a set of x/y/z coordinates, and returns the +// value from the map that corresponds to a bilinear interpolation on those +// coordinates. +// +// For x- or y-values greater than the maximum key, or smaller than the minimum +// key of \p data, we return the value for the closest available key. +template <typename doublemap_type> +typename doublemap_type::mapped_type::mapped_type at_bilin_interp( +    const doublemap_type& data, const typename doublemap_type::key_type& key_x, +    const typename doublemap_type::mapped_type::key_type& key_y) +{ +    // Find x1 and x2 coordinates. They are the x-values closest to key_x. +    const auto x_iters = get_bounding_iterators(data, key_x); +    const auto x1 = x_iters.first->first; +    const auto x2 = x_iters.second->first; +    // x-boundary condition +    if (x1 == x2) { +        return at_lin_interp(x_iters.first->second, key_y); +    } +    // Find y1 and y2 coordinates. They are the y-values closest to key_y. +    const auto y_iters = +        get_bounding_iterators(x_iters.first->second, key_y); +    const auto y1 = y_iters.first->first; +    const auto y2 = y_iters.second->first; +    // y-boundary condition +    if (y1 == y2) { +        return linear_interp(key_x, x1, data.at(x1).at(y1), x2, data.at(x2).at(y1)); +    } + +    // Find z values +    const auto z11 = data.at(x1).at(y1); +    const auto z12 = data.at(x1).at(y2); +    const auto z21 = data.at(x2).at(y1); +    const auto z22 = data.at(x2).at(y2); + +    return bilinear_interp(key_x, key_y, x1, y1, x2, y2, z11, z12, z21, z22); +} +  }} // namespace uhd::math | 
