From d5d9da3114bf069c05a8dcb7fca32ccd70405512 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Thu, 28 Jan 2010 19:54:55 -0800 Subject: Added gain handler class to manage wildcard gain settings. Gets overall gains and sets overall gains when used. Wild card gain will be a gain with an empty string name. --- include/usrp_uhd/Makefile.am | 1 + include/usrp_uhd/gain_handler.hpp | 88 ++++++++++++++++++++++ include/usrp_uhd/props.hpp | 12 +++ include/usrp_uhd/utils.hpp | 47 ++++++++++-- lib/Makefile.am | 1 + lib/gain_handler.cpp | 149 ++++++++++++++++++++++++++++++++++++++ lib/usrp/mboard/test.cpp | 14 +--- lib/usrp/usrp.cpp | 7 +- test/Makefile.am | 1 + test/gain_handler_test.cpp | 115 +++++++++++++++++++++++++++++ test/wax_test.cpp | 13 ++++ 11 files changed, 427 insertions(+), 21 deletions(-) create mode 100644 include/usrp_uhd/gain_handler.hpp create mode 100644 lib/gain_handler.cpp create mode 100644 test/gain_handler_test.cpp diff --git a/include/usrp_uhd/Makefile.am b/include/usrp_uhd/Makefile.am index fdb881498..0a19bfe56 100644 --- a/include/usrp_uhd/Makefile.am +++ b/include/usrp_uhd/Makefile.am @@ -10,6 +10,7 @@ this_includedir = $(includedir)/usrp_uhd this_include_HEADERS = \ device.hpp \ device_addr.hpp \ + gain_handler.hpp \ props.hpp \ utils.hpp \ wax.hpp diff --git a/include/usrp_uhd/gain_handler.hpp b/include/usrp_uhd/gain_handler.hpp new file mode 100644 index 000000000..c2f3d0884 --- /dev/null +++ b/include/usrp_uhd/gain_handler.hpp @@ -0,0 +1,88 @@ +// +// Copyright 2010 Ettus Research LLC +// + +#include +#include +#include +#include +#include + +#ifndef INCLUDED_USRP_UHD_GAIN_HANDLER_HPP +#define INCLUDED_USRP_UHD_GAIN_HANDLER_HPP + +namespace usrp_uhd{ + +class gain_handler{ +public: + typedef boost::shared_ptr sptr; + + template gain_handler( + wax::obj::ptr wax_obj_ptr, const T &gain_prop, + const T &gain_min_prop, const T &gain_max_prop, + const T &gain_step_prop, const T &gain_names_prop + ){ + _wax_obj_ptr = wax_obj_ptr; + _gain_prop = gain_prop; + _gain_min_prop = gain_min_prop; + _gain_max_prop = gain_max_prop; + _gain_step_prop = gain_step_prop; + _gain_names_prop = gain_names_prop; + _is_equal = boost::bind(&gain_handler::is_equal, _1, _2); + } + + ~gain_handler(void); + + /*! + * Intercept gets for overall gain, min, max, step. + * Ensures that the gain name is valid. + * \return true for handled, false to pass on + */ + bool intercept_get(const wax::type &key, wax::type &val); + + /*! + * Intercept sets for overall gain. + * Ensures that the gain name is valid. + * Ensures that the new gain is within range. + * \return true for handled, false to pass on + */ + bool intercept_set(const wax::type &key, const wax::type &val); + +private: + + wax::obj::ptr _wax_obj_ptr; + wax::type _gain_prop; + wax::type _gain_min_prop; + wax::type _gain_max_prop; + wax::type _gain_step_prop; + wax::type _gain_names_prop; + + /*! + * Verify that the key is valid: + * If its a named prop for gain, ensure that name is valid. + * If the name if not valid, throw a std::invalid_argument. + * The name can only be valid if its in the list of gain names. + */ + void _check_key(const wax::type &key); + + /* + * Private interface to test if two wax types are equal: + * The constructor will bind an instance of this for a specific type. + * This bound equals functions allows the intercept methods to be non-templated. + */ + template static bool is_equal(const wax::type &a, const wax::type &b){ + try{ + return wax::cast(a) == wax::cast(b); + } + catch(const wax::bad_cast &){ + return false; + } + } + boost::function _is_equal; + +}; + +} //namespace usrp_uhd + +#endif /* INCLUDED_USRP_UHD_GAIN_HANDLER_HPP */ + diff --git a/include/usrp_uhd/props.hpp b/include/usrp_uhd/props.hpp index 426554a53..b74493961 100644 --- a/include/usrp_uhd/props.hpp +++ b/include/usrp_uhd/props.hpp @@ -61,6 +61,18 @@ namespace usrp_uhd{ typedef std::vector prop_names_t; typedef boost::tuple named_prop_t; + /*! + * Utility function to separate a named property into its components. + * \param key a reference to the prop object + * \param name a reference to the name object + */ + inline named_prop_t extract_named_prop(const wax::type &key, const std::string &name = ""){ + if (key.type() == typeid(named_prop_t)){ + return wax::cast(key); + } + return named_prop_t(key, name); + } + /*! * Possible device properties: * In general, a device will have a single mboard. diff --git a/include/usrp_uhd/utils.hpp b/include/usrp_uhd/utils.hpp index a45473ab4..c1b2bad5c 100644 --- a/include/usrp_uhd/utils.hpp +++ b/include/usrp_uhd/utils.hpp @@ -3,15 +3,19 @@ // #include -#include +#include +#include +#include +#include #include +#include #ifndef INCLUDED_USRP_UHD_UTILS_HPP #define INCLUDED_USRP_UHD_UTILS_HPP namespace usrp_uhd{ -template +template //TODO template this better std::vector get_map_keys(const std::map &m){ std::vector v; std::pair p; @@ -21,12 +25,43 @@ std::vector get_map_keys(const std::map &m){ return v; } -//TODO implement a set and get gains that takes a wx obj ptr, and gain properties +} //namespace usrp_uhd + +/*! + * Useful templated functions and classes that I like to pretend are part of stl + */ +namespace std{ -//TODO check name in vector of names + class assert_error : public std::logic_error{ + public: + explicit assert_error(const string& what_arg) : logic_error(what_arg){ + /* NOP */ + } + }; -//TODO optionally extract a name from the named_prop_t + #define ASSERT_THROW(_x) if (not (_x)) { \ + throw std::assert_error("Assertion Failed: " + std::string(#_x)); \ + } -} //namespace usrp_uhd + template + T reduce(InputIterator first, InputIterator last, Function fcn, T init = 0){ + T tmp = init; + for ( ; first != last; ++first ){ + tmp = fcn(tmp, *first); + } + return tmp; + } + + template + bool has(InputIterator first, InputIterator last, const T &elem){ + return last != std::find(first, last, elem); + } + + template + T sum(const T &a, const T &b){ + return a + b; + } + +}//namespace std #endif /* INCLUDED_USRP_UHD_UTILS_HPP */ diff --git a/lib/Makefile.am b/lib/Makefile.am index 69c242c6c..dc3ebd7db 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -13,6 +13,7 @@ lib_LTLIBRARIES = libusrp_uhd.la libusrp_uhd_la_SOURCES = \ device.cpp \ device_addr.cpp \ + gain_handler.cpp \ usrp_uhd.cpp \ wax.cpp diff --git a/lib/gain_handler.cpp b/lib/gain_handler.cpp new file mode 100644 index 000000000..72343c1a4 --- /dev/null +++ b/lib/gain_handler.cpp @@ -0,0 +1,149 @@ +// +// Copyright 2010 Ettus Research LLC +// + +#include +#include +#include +#include +#include +#include + +using namespace usrp_uhd; + +/*********************************************************************** + * Helper functions and macros + **********************************************************************/ +#define GET_PROP_NAMES() \ + wax::cast((*_wax_obj_ptr)[_gain_names_prop]) + +/*! + * Helper function to simplify getting a named gain (also min, max, step). + */ +static gain_t get_named_gain(wax::obj::ptr wax_obj_ptr, wax::type prop, std::string name){ + return wax::cast((*wax_obj_ptr)[named_prop_t(prop, name)]); +} + +/*********************************************************************** + * Class methods of gain handler + **********************************************************************/ +gain_handler::~gain_handler(void){ + /* NOP */ +} + +void gain_handler::_check_key(const wax::type &key_){ + wax::type key; std::string name; + tie(key, name) = extract_named_prop(key_); + + try{ + //only handle non wildcard names + ASSERT_THROW(name != ""); + + //only handle these gain props + ASSERT_THROW( + _is_equal(key, _gain_prop) or + _is_equal(key, _gain_min_prop) or + _is_equal(key, _gain_max_prop) or + _is_equal(key, _gain_step_prop) + ); + + //check that the name is allowed + prop_names_t prop_names = GET_PROP_NAMES(); + ASSERT_THROW(not std::has(prop_names.begin(), prop_names.end(), name)); + + //if we get here, throw an exception + throw std::invalid_argument(str( + boost::format("Unknown gain name %s") % name + )); + } + catch(const std::assert_error &){} +} + +bool gain_handler::intercept_get(const wax::type &key, wax::type &val){ + _check_key(key); //verify the key + + // use a vector of tuples to map properties to a reducer function + // we cant use a map because the wax::type cant be sorted + typedef boost::function reducer_t; + typedef boost::tuple tuple_t; + reducer_t reducer_sum = boost::bind(std::sum, _1, _2); + reducer_t reducer_max = boost::bind(std::max, _1, _2); + std::vector prop_to_reducer = boost::assign::tuple_list_of + (_gain_prop, reducer_sum)(_gain_min_prop, reducer_sum) + (_gain_max_prop, reducer_sum)(_gain_step_prop, reducer_max); + + /*! + * Handle getting overall gains when a name is not specified. + * For the gain props below, set the overall value and return true. + */ + BOOST_FOREACH(tuple_t p2r, prop_to_reducer){ + if (_is_equal(key, p2r.get<0>())){ + //form the gains vector from the props vector + prop_names_t prop_names = GET_PROP_NAMES(); + std::vector gains(prop_names.size()); + std::transform( + prop_names.begin(), prop_names.end(), gains.begin(), + boost::bind(get_named_gain, _wax_obj_ptr, p2r.get<0>(), _1) + ); + + //reduce across the gain vector + val = std::reduce(gains.begin(), gains.end(), p2r.get<1>()); + return true; + } + } + + return false; +} + +bool gain_handler::intercept_set(const wax::type &key_, const wax::type &val){ + _check_key(key_); //verify the key + + wax::type key; std::string name; + tie(key, name) = extract_named_prop(key_); + + /*! + * Verify that a named gain component is in range. + */ + try{ + //only handle the gain props + ASSERT_THROW(_is_equal(key, _gain_prop)); + + //check that the gain is in range + gain_t gain = wax::cast(val); + gain_t gain_min = get_named_gain(_wax_obj_ptr, _gain_min_prop, name); + gain_t gain_max = get_named_gain(_wax_obj_ptr, _gain_max_prop, name); + ASSERT_THROW(gain > gain_max or gain < gain_min); + + //if we get here, throw an exception + throw std::range_error(str( + boost::format("gain %s is out of range of (%f, %f)") % name % gain_min % gain_max + )); + } + catch(const std::assert_error &){} + + /*! + * Handle setting the overall gain. + */ + if (_is_equal(key, _gain_prop) and name == ""){ + gain_t gain = wax::cast(val); + prop_names_t prop_names = GET_PROP_NAMES(); + BOOST_FOREACH(std::string name, prop_names){ + //get the min, max, step for this gain name + gain_t gain_min = get_named_gain(_wax_obj_ptr, _gain_min_prop, name); + gain_t gain_max = get_named_gain(_wax_obj_ptr, _gain_max_prop, name); + gain_t gain_step = get_named_gain(_wax_obj_ptr, _gain_step_prop, name); + + //clip g to be within the allowed range + gain_t g = std::min(std::max(gain, gain_min), gain_max); + //set g to be a multiple of the step size + g -= fmod(g, gain_step); + //set g to be the new gain + (*_wax_obj_ptr)[named_prop_t(_gain_prop, name)] = g; + //subtract g out of the total gain left to apply + gain -= g; + } + return true; + } + + return false; +} diff --git a/lib/usrp/mboard/test.cpp b/lib/usrp/mboard/test.cpp index a12560e9a..321ec0855 100644 --- a/lib/usrp/mboard/test.cpp +++ b/lib/usrp/mboard/test.cpp @@ -47,11 +47,8 @@ public: ~shell_dboard(void){} private: void get(const wax::type &key_, wax::type &val){ - //extract the index if key is a named prop - wax::type key = key_; std::string name = ""; - if (key.type() == typeid(named_prop_t)){ - boost::tie(key, name) = wax::cast(key); - } + wax::type key; std::string name; + tie(key, name) = extract_named_prop(key_); //handle the get request conditioned on the key switch(wax::cast(key)){ @@ -113,11 +110,8 @@ test::~test(void){ } void test::get(const wax::type &key_, wax::type &val){ - //extract the index if key is a named prop - wax::type key = key_; std::string name = ""; - if (key.type() == typeid(named_prop_t)){ - boost::tie(key, name) = wax::cast(key); - } + wax::type key; std::string name; + tie(key, name) = extract_named_prop(key_); //handle the get request conditioned on the key switch(wax::cast(key)){ diff --git a/lib/usrp/usrp.cpp b/lib/usrp/usrp.cpp index 02446c8d1..24ac2bccc 100644 --- a/lib/usrp/usrp.cpp +++ b/lib/usrp/usrp.cpp @@ -42,11 +42,8 @@ usrp::~usrp(void){ } void usrp::get(const wax::type &key_, wax::type &val){ - //extract the index if key is a named prop - wax::type key = key_; std::string name = ""; - if (key.type() == typeid(named_prop_t)){ - boost::tie(key, name) = wax::cast(key); - } + wax::type key; std::string name; + tie(key, name) = extract_named_prop(key_); //handle the get request conditioned on the key switch(wax::cast(key)){ diff --git a/test/Makefile.am b/test/Makefile.am index 46b1de0e6..baebd921a 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -23,6 +23,7 @@ main_test_SOURCES = \ main_test.cpp \ addr_test.cpp \ device_test.cpp \ + gain_handler_test.cpp \ usrp_dboard_test.cpp \ wax_test.cpp diff --git a/test/gain_handler_test.cpp b/test/gain_handler_test.cpp new file mode 100644 index 000000000..339974af5 --- /dev/null +++ b/test/gain_handler_test.cpp @@ -0,0 +1,115 @@ +// +// Copyright 2010 Ettus Research LLC +// + +#include +#include +#include +#include + +using namespace usrp_uhd; + +enum prop_t{ + PROP_GAIN, + PROP_GAIN_MIN, + PROP_GAIN_MAX, + PROP_GAIN_STEP, + PROP_GAIN_NAMES +}; + +class gainful_obj : public wax::obj{ +public: + gainful_obj(void){ + _gain_handler = gain_handler::sptr(new gain_handler( + this, PROP_GAIN, PROP_GAIN_MIN, PROP_GAIN_MAX, PROP_GAIN_STEP, PROP_GAIN_NAMES + )); + _gains["g0"] = 0; + _gains["g1"] = 0; + _gains_min["g0"] = -10; + _gains_min["g1"] = 0; + _gains_max["g0"] = 0; + _gains_max["g1"] = 100; + _gains_step["g0"] = .1; + _gains_step["g1"] = 1.5; + } + + ~gainful_obj(void){} + +private: + void get(const wax::type &key_, wax::type &val){ + if (_gain_handler->intercept_get(key_, val)) return; + + wax::type key; std::string name; + tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(wax::cast(key)){ + case PROP_GAIN: + val = _gains[name]; + return; + + case PROP_GAIN_MIN: + val = _gains_min[name]; + return; + + case PROP_GAIN_MAX: + val = _gains_max[name]; + return; + + case PROP_GAIN_STEP: + val = _gains_step[name]; + return; + + case PROP_GAIN_NAMES: + val = prop_names_t(get_map_keys(_gains)); + return; + } + } + + void set(const wax::type &key_, const wax::type &val){ + if (_gain_handler->intercept_set(key_, val)) return; + + wax::type key; std::string name; + tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(wax::cast(key)){ + case PROP_GAIN: + _gains[name] = wax::cast(val); + return; + + case PROP_GAIN_MIN: + case PROP_GAIN_MAX: + case PROP_GAIN_STEP: + case PROP_GAIN_NAMES: + throw std::runtime_error("cannot set this property"); + } + } + + gain_handler::sptr _gain_handler; + std::map _gains; + std::map _gains_min; + std::map _gains_max; + std::map _gains_step; + +}; + +BOOST_AUTO_TEST_CASE(test_gain_handler){ + std::cout << "Testing the gain handler..." << std::endl; + gainful_obj go0; + + BOOST_CHECK_THROW( + wax::cast(go0[named_prop_t(PROP_GAIN, "fail")]), + std::invalid_argument + ); + + std::cout << "verifying the overall min, max, step" << std::endl; + BOOST_CHECK_EQUAL(wax::cast(go0[PROP_GAIN_MIN]), gain_t(-10)); + BOOST_CHECK_EQUAL(wax::cast(go0[PROP_GAIN_MAX]), gain_t(100)); + BOOST_CHECK_EQUAL(wax::cast(go0[PROP_GAIN_STEP]), gain_t(1.5)); + + std::cout << "verifying the overall gain" << std::endl; + go0[named_prop_t(PROP_GAIN, "g0")] = gain_t(-5); + go0[named_prop_t(PROP_GAIN, "g1")] = gain_t(30); + BOOST_CHECK_EQUAL(wax::cast(go0[PROP_GAIN]), gain_t(25)); +} diff --git a/test/wax_test.cpp b/test/wax_test.cpp index 353130410..294fe0be7 100644 --- a/test/wax_test.cpp +++ b/test/wax_test.cpp @@ -5,6 +5,15 @@ #include #include +enum opt_a_t{OPTION_A_0, OPTION_A_1}; +enum opt_b_t{OPTION_B_0, OPTION_B_1}; + +BOOST_AUTO_TEST_CASE(test_enums){ + wax::type opta = OPTION_A_0; + BOOST_CHECK_THROW(wax::cast(opta), wax::bad_cast); + BOOST_CHECK_EQUAL(wax::cast(opta), OPTION_A_0); +} + /*********************************************************************** * demo class for wax framework **********************************************************************/ @@ -69,6 +78,10 @@ BOOST_AUTO_TEST_CASE(test_proxy){ std::cout << "store proxy" << std::endl; wax::proxy p = wd[size_t(0)][size_t(0)]; p[size_t(0)] = float(5); + + std::cout << "assign proxy" << std::endl; + wax::type a = p[size_t(0)]; + BOOST_CHECK_EQUAL(wax::cast(a), float(5)); } BOOST_AUTO_TEST_CASE(test_print){ -- cgit v1.2.3