aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosh Blum <josh@joshknows.com>2010-01-28 19:54:55 -0800
committerJosh Blum <josh@joshknows.com>2010-01-28 19:54:55 -0800
commitd5d9da3114bf069c05a8dcb7fca32ccd70405512 (patch)
tree5c2c63efe9175ebd2b22c6b4899b997e9fed5b11
parentfc1bffcfd9761c1f60cf322bb58e7f9c8096a5c0 (diff)
downloaduhd-d5d9da3114bf069c05a8dcb7fca32ccd70405512.tar.gz
uhd-d5d9da3114bf069c05a8dcb7fca32ccd70405512.tar.bz2
uhd-d5d9da3114bf069c05a8dcb7fca32ccd70405512.zip
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.
-rw-r--r--include/usrp_uhd/Makefile.am1
-rw-r--r--include/usrp_uhd/gain_handler.hpp88
-rw-r--r--include/usrp_uhd/props.hpp12
-rw-r--r--include/usrp_uhd/utils.hpp47
-rw-r--r--lib/Makefile.am1
-rw-r--r--lib/gain_handler.cpp149
-rw-r--r--lib/usrp/mboard/test.cpp14
-rw-r--r--lib/usrp/usrp.cpp7
-rw-r--r--test/Makefile.am1
-rw-r--r--test/gain_handler_test.cpp115
-rw-r--r--test/wax_test.cpp13
11 files changed, 427 insertions, 21 deletions
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 <boost/shared_ptr.hpp>
+#include <usrp_uhd/wax.hpp>
+#include <usrp_uhd/props.hpp>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+
+#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<gain_handler> sptr;
+
+ template <class T> 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<T>, _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 <class T> static bool is_equal(const wax::type &a, const wax::type &b){
+ try{
+ return wax::cast<T>(a) == wax::cast<T>(b);
+ }
+ catch(const wax::bad_cast &){
+ return false;
+ }
+ }
+ boost::function<bool(const wax::type &, const wax::type &)> _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
@@ -62,6 +62,18 @@ namespace usrp_uhd{
typedef boost::tuple<wax::type, std::string> 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<named_prop_t>(key);
+ }
+ return named_prop_t(key, name);
+ }
+
+ /*!
* Possible device properties:
* In general, a device will have a single mboard.
* In certain mimo applications, multiple boards
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 <boost/foreach.hpp>
-#include <map>
+#include <boost/format.hpp>
+#include <boost/function.hpp>
+#include <stdexcept>
+#include <algorithm>
#include <vector>
+#include <map>
#ifndef INCLUDED_USRP_UHD_UTILS_HPP
#define INCLUDED_USRP_UHD_UTILS_HPP
namespace usrp_uhd{
-template <class Key, class T>
+template <class Key, class T> //TODO template this better
std::vector<Key> get_map_keys(const std::map<Key, T> &m){
std::vector<Key> v;
std::pair<Key, T> p;
@@ -21,12 +25,43 @@ std::vector<Key> get_map_keys(const std::map<Key, T> &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<class T, class InputIterator, class Function>
+ 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<class T, class InputIterator>
+ bool has(InputIterator first, InputIterator last, const T &elem){
+ return last != std::find(first, last, elem);
+ }
+
+ template <class T>
+ 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 <usrp_uhd/gain_handler.hpp>
+#include <usrp_uhd/utils.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <vector>
+
+using namespace usrp_uhd;
+
+/***********************************************************************
+ * Helper functions and macros
+ **********************************************************************/
+#define GET_PROP_NAMES() \
+ wax::cast<prop_names_t>((*_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<gain_t>((*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<gain_t(gain_t, gain_t)> reducer_t;
+ typedef boost::tuple<wax::type, reducer_t> tuple_t;
+ reducer_t reducer_sum = boost::bind(std::sum<gain_t>, _1, _2);
+ reducer_t reducer_max = boost::bind(std::max<gain_t>, _1, _2);
+ std::vector<tuple_t> 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<gain_t> 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<gain_t>(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<gain_t>(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<gain_t>(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<named_prop_t>(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<dboard_prop_t>(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<named_prop_t>(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<mboard_prop_t>(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<named_prop_t>(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<device_prop_t>(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 <boost/test/unit_test.hpp>
+#include <usrp_uhd/gain_handler.hpp>
+#include <usrp_uhd/utils.hpp>
+#include <iostream>
+
+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<prop_t>(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<prop_t>(key)){
+ case PROP_GAIN:
+ _gains[name] = wax::cast<gain_t>(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<std::string, gain_t> _gains;
+ std::map<std::string, gain_t> _gains_min;
+ std::map<std::string, gain_t> _gains_max;
+ std::map<std::string, gain_t> _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<gain_t>(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<gain_t>(go0[PROP_GAIN_MIN]), gain_t(-10));
+ BOOST_CHECK_EQUAL(wax::cast<gain_t>(go0[PROP_GAIN_MAX]), gain_t(100));
+ BOOST_CHECK_EQUAL(wax::cast<gain_t>(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<gain_t>(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 <boost/test/unit_test.hpp>
#include <usrp_uhd/wax.hpp>
+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<opt_b_t>(opta), wax::bad_cast);
+ BOOST_CHECK_EQUAL(wax::cast<opt_a_t>(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<float>(a), float(5));
}
BOOST_AUTO_TEST_CASE(test_print){