diff options
Diffstat (limited to 'host')
| -rw-r--r-- | host/include/uhd/utils/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/include/uhd/utils/gain_group.hpp | 85 | ||||
| -rw-r--r-- | host/lib/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/gain_group.cpp | 151 | ||||
| -rw-r--r-- | host/test/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/test/gain_group_test.cpp | 122 | 
6 files changed, 361 insertions, 0 deletions
| diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index d484788b2..4a5f20e3c 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -22,6 +22,7 @@ INSTALL(FILES      byteswap.hpp      byteswap.ipp      exception.hpp +    gain_group.hpp      gain_handler.hpp      pimpl.hpp      props.hpp diff --git a/host/include/uhd/utils/gain_group.hpp b/host/include/uhd/utils/gain_group.hpp new file mode 100644 index 000000000..3955dfa9a --- /dev/null +++ b/host/include/uhd/utils/gain_group.hpp @@ -0,0 +1,85 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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_UTILS_GAIN_GROUP_HPP +#define INCLUDED_UHD_UTILS_GAIN_GROUP_HPP + +#include <uhd/config.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/function.hpp> +#include <boost/utility.hpp> + +namespace uhd{ + +/*! + * A set of function to control a gain element. + */ +struct UHD_API gain_fcns_t{ +    boost::function<gain_range_t(void)> get_range; +    boost::function<float(void)>        get_value; +    boost::function<void(float)>        set_value; +}; + +class UHD_API gain_group : boost::noncopyable{ +public: +    typedef boost::shared_ptr<gain_group> sptr; + +    /*! +     * Get the overall gain range for this group. +     * Overall step is defined as the minimum step size. +     * \return a gain range with overall min, max, step +     */ +    virtual gain_range_t get_range(void) = 0; + +    /*! +     * Get the overall gain value for this group. +     * \return a summation of all the gain values +     */ +    virtual float get_value(void) = 0; + +    /*! +     * Set the overall gain value for this group. +     * The power will be distributed across individual gain elements. +     * The semantics of how to do this are determined by the priority. +     * \param gain the gain to set across the group +     */ +    virtual void set_value(float gain) = 0; + +    /*! +     * Register a set of gain functions into this group. +     * Priority determines how power will be distributed +     * with higher priorities getting the power first, +     * and lower priorities getting the remainder power. +     * \param gain_fcns the set of gain functions +     * \param priority the priority of the gain element +     */ +    virtual void register_fcns( +        const gain_fcns_t &gain_fcns, size_t priority = 0 +    ) = 0; + +    /*! +     * Make a new empty gain group. +     * \return a gain group object. +     */ +    static sptr make(void); +}; + +} //namespace uhd + +#endif /* INCLUDED_UHD_UTILS_GAIN_GROUP_HPP */ + diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index 767029dc4..010478821 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -112,6 +112,7 @@ CONFIGURE_FILE(  LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/device.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/gain_group.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/gain_handler.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/load_modules.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/thread_priority.cpp diff --git a/host/lib/gain_group.cpp b/host/lib/gain_group.cpp new file mode 100644 index 000000000..3ae9a285e --- /dev/null +++ b/host/lib/gain_group.cpp @@ -0,0 +1,151 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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/utils/gain_group.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/assert.hpp> +#include <boost/foreach.hpp> +#include <boost/bind.hpp> +#include <boost/math/special_functions/round.hpp> +#include <algorithm> +#include <vector> +#include <iostream> + +#define rint(x) boost::math::iround(x) + +using namespace uhd; + +static const bool verbose = false; + +static bool compare_by_step_size( +    const size_t &rhs, const size_t &lhs, std::vector<gain_fcns_t> &fcns +){ +    return fcns.at(rhs).get_range().step > fcns.at(lhs).get_range().step; +} + +/*********************************************************************** + * gain group implementation + **********************************************************************/ +class gain_group_impl : public gain_group{ +public: +    gain_group_impl(void){ +        /*NOP*/ +    } + +    gain_range_t get_range(void){ +        float overall_min = 0, overall_max = 0, overall_step = 0; +        BOOST_FOREACH(const gain_fcns_t &fcns, get_all_fcns()){ +            const gain_range_t range = fcns.get_range(); +            overall_min += range.min; +            overall_max += range.max; +            //the overall step is the min (zero is invalid, first run) +            if (overall_step == 0) overall_step = range.step; +            overall_step = std::min(overall_step, range.step); +        } +        return gain_range_t(overall_min, overall_max, overall_step); +    } + +    float get_value(void){ +        float overall_gain = 0; +        BOOST_FOREACH(const gain_fcns_t &fcns, get_all_fcns()){ +            overall_gain += fcns.get_value(); +        } +        return overall_gain; +    } + +    void set_value(float gain){ +        std::vector<gain_fcns_t> all_fcns = get_all_fcns(); + +        //get the max step size among the gains +        float max_step = 0; +        BOOST_FOREACH(const gain_fcns_t &fcns, all_fcns){ +            max_step = std::max(max_step, fcns.get_range().step); +        } + +        //create gain bucket to distribute power +        std::vector<float> gain_bucket; + +        //distribute power according to priority (round to max step) +        float gain_left_to_distribute = gain; +        BOOST_FOREACH(const gain_fcns_t &fcns, all_fcns){ +            const gain_range_t range = fcns.get_range(); +            gain_bucket.push_back( +                max_step*rint(std::clip(gain_left_to_distribute, range.min, range.max)/max_step) +            ); +            gain_left_to_distribute -= gain_bucket.back(); +        } + +        //get a list of indexes sorted by step size large to small +        std::vector<size_t> indexes_step_size_dec; +        for (size_t i = 0; i < all_fcns.size(); i++){ +            indexes_step_size_dec.push_back(i); +        } +        std::sort( +            indexes_step_size_dec.begin(), indexes_step_size_dec.end(), +            boost::bind(&compare_by_step_size, _1, _2, all_fcns) +        ); +        UHD_ASSERT_THROW( +            all_fcns.at(indexes_step_size_dec.front()).get_range().step >= +            all_fcns.at(indexes_step_size_dec.back()).get_range().step +        ); + +        //distribute the remainder (less than max step) +        //fill in the largest step sizes first that are less than the remainder +        BOOST_FOREACH(size_t i, indexes_step_size_dec){ +            const gain_range_t range = all_fcns.at(i).get_range(); +            float additional_gain = range.step*rint( +                std::clip(gain_bucket.at(i) + gain_left_to_distribute, range.min, range.max +            )/range.step) - gain_bucket.at(i); +            gain_bucket.at(i) += additional_gain; +            gain_left_to_distribute -= additional_gain; +        } +        if (verbose) std::cout << "gain_left_to_distribute " << gain_left_to_distribute << std::endl; + +        //now write the bucket out to the individual gain values +        for (size_t i = 0; i < gain_bucket.size(); i++){ +            if (verbose) std::cout << gain_bucket.at(i) << std::endl; +            all_fcns.at(i).set_value(gain_bucket.at(i)); +        } +    } + +    void register_fcns( +        const gain_fcns_t &gain_fcns, size_t priority +    ){ +        _registry[priority].push_back(gain_fcns); +    } + +private: +    //! get the gain function sets in order (highest priority first) +    std::vector<gain_fcns_t> get_all_fcns(void){ +        std::vector<gain_fcns_t> all_fcns; +        BOOST_FOREACH(ssize_t key, std::sorted(_registry.keys())){ +            const std::vector<gain_fcns_t> &fcns = _registry[key]; +            all_fcns.insert(all_fcns.begin(), fcns.begin(), fcns.end()); +        } +        return all_fcns; +    } + +    uhd::dict<size_t, std::vector<gain_fcns_t> > _registry; +}; + +/*********************************************************************** + * gain group factory function + **********************************************************************/ +gain_group::sptr gain_group::make(void){ +    return sptr(new gain_group_impl()); +} diff --git a/host/test/CMakeLists.txt b/host/test/CMakeLists.txt index b7dcb741a..25cae6e7f 100644 --- a/host/test/CMakeLists.txt +++ b/host/test/CMakeLists.txt @@ -26,6 +26,7 @@ ADD_EXECUTABLE(main_test      convert_types_test.cpp      dict_test.cpp      error_test.cpp +    gain_group_test.cpp      gain_handler_test.cpp      tune_helper_test.cpp      vrt_test.cpp diff --git a/host/test/gain_group_test.cpp b/host/test/gain_group_test.cpp new file mode 100644 index 000000000..4d337afb9 --- /dev/null +++ b/host/test/gain_group_test.cpp @@ -0,0 +1,122 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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 <boost/test/unit_test.hpp> +#include <uhd/utils/gain_group.hpp> +#include <boost/bind.hpp> +#include <boost/math/special_functions/round.hpp> +#include <iostream> + +#define rint(x) boost::math::iround(x) + +using namespace uhd; + +/*********************************************************************** + * Define gain element classes with needed functions + **********************************************************************/ +class gain_element1{ +public: + +    gain_range_t get_range(void){ +        return gain_range_t(0, 90, 1); +    } + +    float get_value(void){ +        return _gain; +    } + +    void set_value(float gain){ +        float step = get_range().step; +        _gain = step*rint(gain/step); +    } + +private: +    float _gain; +}; + +class gain_element2{ +public: + +    gain_range_t get_range(void){ +        return gain_range_t(-20, 10, 0.1); +    } + +    float get_value(void){ +        return _gain; +    } + +    void set_value(float gain){ +        float step = get_range().step; +        _gain = step*rint(gain/step); +    } + +private: +    float _gain; +}; + +//create static instances of gain elements to be shared by the tests +static gain_element1 g1; +static gain_element2 g2; + +static gain_group::sptr get_gain_group(size_t pri1 = 0, size_t pri2 = 0){ +    //create instance of gain group +    gain_fcns_t gain_fcns; +    gain_group::sptr gg(gain_group::make()); + +    //load gain group with function sets +    gain_fcns.get_range = boost::bind(&gain_element1::get_range, &g1); +    gain_fcns.get_value = boost::bind(&gain_element1::get_value, &g1); +    gain_fcns.set_value = boost::bind(&gain_element1::set_value, &g1, _1); +    gg->register_fcns(gain_fcns, pri1); + +    gain_fcns.get_range = boost::bind(&gain_element2::get_range, &g2); +    gain_fcns.get_value = boost::bind(&gain_element2::get_value, &g2); +    gain_fcns.set_value = boost::bind(&gain_element2::set_value, &g2, _1); +    gg->register_fcns(gain_fcns, pri2); + +    return gg; +} + +/*********************************************************************** + * Test cases + **********************************************************************/ +static const double tolerance = 0.001; + +BOOST_AUTO_TEST_CASE(test_gain_group_overall){ +    gain_group::sptr gg = get_gain_group(); + +    //test the overall stuff +    gg->set_value(80); +    BOOST_CHECK_CLOSE(gg->get_value(), 80, tolerance); +    BOOST_CHECK_CLOSE(gg->get_range().min, -20, tolerance); +    BOOST_CHECK_CLOSE(gg->get_range().max, 100, tolerance); +    BOOST_CHECK_CLOSE(gg->get_range().step, 0.1, tolerance); +} + +BOOST_AUTO_TEST_CASE(test_gain_group_priority){ +    gain_group::sptr gg = get_gain_group(0, 1); + +    //test the overall stuff +    gg->set_value(80); +    BOOST_CHECK_CLOSE(gg->get_value(), 80, tolerance); +    BOOST_CHECK_CLOSE(gg->get_range().min, -20, tolerance); +    BOOST_CHECK_CLOSE(gg->get_range().max, 100, tolerance); +    BOOST_CHECK_CLOSE(gg->get_range().step, 0.1, tolerance); + +    //test the the higher priority gain got filled first (gain 2) +    BOOST_CHECK_CLOSE(g2.get_value(), g2.get_range().max, tolerance); +} | 
