diff options
Diffstat (limited to 'host/lib')
22 files changed, 1710 insertions, 682 deletions
diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index 33314e729..ca00c3597 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -22,12 +22,14 @@ SET(libuhd_sources device.cpp device_addr.cpp gain_handler.cpp - uhd.cpp + metadata.cpp + simple_device.cpp + time_spec.cpp wax.cpp - transport/udp.cpp + transport/udp_simple.cpp + transport/vrt.cpp usrp/dboard/basic.cpp usrp/dboard_base.cpp - usrp/dboard_id.cpp usrp/dboard_interface.cpp usrp/dboard_manager.cpp usrp/usrp2/dboard_impl.cpp @@ -39,6 +41,13 @@ SET(libuhd_sources ) ######################################################################## +# Conditionally add the udp sources +######################################################################## +LIST(APPEND libuhd_sources + transport/udp_zero_copy_none.cpp +) + +######################################################################## # Conditionally add the usrp1e sources ######################################################################## INCLUDE(CheckIncludeFiles) diff --git a/host/lib/device.cpp b/host/lib/device.cpp index 82052708a..a87ba83eb 100644 --- a/host/lib/device.cpp +++ b/host/lib/device.cpp @@ -15,40 +15,22 @@ // asize_t with this program. If not, see <http://www.gnu.org/licenses/>. // -#include <uhd/usrp/usrp1e.hpp> -#include <uhd/usrp/usrp2.hpp> +#include <uhd/device.hpp> #include <uhd/dict.hpp> #include <uhd/utils.hpp> #include <boost/foreach.hpp> #include <boost/format.hpp> #include <boost/weak_ptr.hpp> #include <boost/functional/hash.hpp> +#include <boost/tuple/tuple.hpp> #include <stdexcept> #include <algorithm> using namespace uhd; -/*! - * Create a new device from a device address. - * Based on the address, call the appropriate make functions. - * \param dev_addr the device address - * \return a smart pointer to a device - */ -static device::sptr make_device(const device_addr_t &dev_addr){ - - //create a usrp1e - if (dev_addr["type"] == "usrp1e"){ - return usrp::usrp1e::make(dev_addr); - } - - //create a usrp2 - if (dev_addr["type"] == "usrp2"){ - return usrp::usrp2::make(dev_addr); - } - - throw std::runtime_error("cant make a device"); -} - +/*********************************************************************** + * Helper Functions + **********************************************************************/ /*! * Make a device hash that maps 1 to 1 with a device address. * The hash will be used to identify created devices. @@ -72,19 +54,34 @@ static size_t hash_device_addr( } /*********************************************************************** + * Registration + **********************************************************************/ +typedef boost::tuple<device::discover_t, device::make_t> dev_fcn_reg_t; + +// instantiate the device function registry container +STATIC_INSTANCE(std::vector<dev_fcn_reg_t>, get_dev_fcn_regs) + +void device::register_device( + const discover_t &discover, + const make_t &make +){ + //std::cout << "registering device" << std::endl; + get_dev_fcn_regs().push_back(dev_fcn_reg_t(discover, make)); +} + +/*********************************************************************** * Discover **********************************************************************/ device_addrs_t device::discover(const device_addr_t &hint){ device_addrs_t device_addrs; - //discover the usrp1es - std::vector<device_addr_t> usrp2_addrs = usrp::usrp1e::discover(hint); - device_addrs.insert(device_addrs.begin(), usrp2_addrs.begin(), usrp2_addrs.end()); - - //discover the usrp2s - if (hint.has_key("addr")){ - std::vector<device_addr_t> usrp2_addrs = usrp::usrp2::discover(hint); - device_addrs.insert(device_addrs.begin(), usrp2_addrs.begin(), usrp2_addrs.end()); + BOOST_FOREACH(dev_fcn_reg_t fcn, get_dev_fcn_regs()){ + device_addrs_t discovered_addrs = fcn.get<0>()(hint); + device_addrs.insert( + device_addrs.begin(), + discovered_addrs.begin(), + discovered_addrs.end() + ); } return device_addrs; @@ -94,24 +91,38 @@ device_addrs_t device::discover(const device_addr_t &hint){ * Make **********************************************************************/ device::sptr device::make(const device_addr_t &hint, size_t which){ - std::vector<device_addr_t> device_addrs = discover(hint); + typedef boost::tuple<device_addr_t, make_t> dev_addr_make_t; + std::vector<dev_addr_make_t> dev_addr_makers; + + BOOST_FOREACH(dev_fcn_reg_t fcn, get_dev_fcn_regs()){ + BOOST_FOREACH(device_addr_t dev_addr, fcn.get<0>()(hint)){ + //copy keys that were in hint but not in dev_addr + //this way, we can pass additional transport arguments + BOOST_FOREACH(std::string key, hint.get_keys()){ + if (not dev_addr.has_key(key)) dev_addr[key] = hint[key]; + } + //append the discovered address and its factory function + dev_addr_makers.push_back(dev_addr_make_t(dev_addr, fcn.get<1>())); + } + } //check that we found any devices - if (device_addrs.size() == 0){ + if (dev_addr_makers.size() == 0){ throw std::runtime_error(str( boost::format("No devices found for %s") % device_addr::to_string(hint) )); } //check that the which index is valid - if (device_addrs.size() <= which){ + if (dev_addr_makers.size() <= which){ throw std::runtime_error(str( boost::format("No device at index %d for %s") % which % device_addr::to_string(hint) )); } //create a unique hash for the device address - device_addr_t dev_addr = device_addrs.at(which); + device_addr_t dev_addr; make_t maker; + boost::tie(dev_addr, maker) = dev_addr_makers.at(which); size_t dev_hash = hash_device_addr(dev_addr); //std::cout << boost::format("Hash: %u") % dev_hash << std::endl; @@ -126,7 +137,7 @@ device::sptr device::make(const device_addr_t &hint, size_t which){ } //create and register a new device catch(const std::assert_error &e){ - device::sptr dev = make_device(dev_addr); + device::sptr dev = maker(dev_addr); hash_to_device[dev_hash] = dev; return dev; } diff --git a/host/lib/gain_handler.cpp b/host/lib/gain_handler.cpp index b03d5bda2..847bc0528 100644 --- a/host/lib/gain_handler.cpp +++ b/host/lib/gain_handler.cpp @@ -17,6 +17,7 @@ #include <uhd/gain_handler.hpp> #include <uhd/utils.hpp> +#include <uhd/props.hpp> #include <boost/assign/list_of.hpp> #include <boost/foreach.hpp> #include <boost/format.hpp> @@ -25,143 +26,156 @@ using namespace uhd; /*********************************************************************** - * Helper functions and macros + * gain handler implementation interface **********************************************************************/ -#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 *wax_obj_ptr, wax::obj prop, std::string name){ - return wax::cast<gain_t>((*wax_obj_ptr)[named_prop_t(prop, name)]); +class gain_handler_impl : public gain_handler{ +public: + gain_handler_impl( + const wax::obj &link, + const props_t &props, + is_equal_t is_equal + ); + ~gain_handler_impl(void); + bool intercept_get(const wax::obj &key, wax::obj &val); + bool intercept_set(const wax::obj &key, const wax::obj &val); + +private: + wax::obj _link; + props_t _props; + is_equal_t _is_equal; + + prop_names_t get_gain_names(void); + gain_t get_overall_gain_val(void); + gain_range_t get_overall_gain_range(void); + template <class T> T get_named_prop(const wax::obj &prop, const std::string &name){ + return _link[named_prop_t(prop, name)].as<T>(); + } +}; + +/*********************************************************************** + * the make function + **********************************************************************/ +gain_handler::sptr gain_handler::make( + const wax::obj &link, + const props_t &props, + is_equal_t is_equal +){ + return sptr(new gain_handler_impl(link, props, is_equal)); } /*********************************************************************** - * Class methods of gain handler + * gain handler implementation methods **********************************************************************/ -gain_handler::~gain_handler(void){ +gain_handler::props_t::props_t(void){ /* NOP */ } -void gain_handler::_check_key(const wax::obj &key_){ - wax::obj key; std::string name; - boost::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 &){} +gain_handler_impl::gain_handler_impl( + const wax::obj &link, + const props_t &props, + is_equal_t is_equal +){ + _link = link; + _props = props; + _is_equal = is_equal; } -static gain_t gain_max(gain_t a, gain_t b){ - return std::max(a, b); +gain_handler_impl::~gain_handler_impl(void){ + /* NOP */ } -static gain_t gain_sum(gain_t a, gain_t b){ - return std::sum(a, b); + +prop_names_t gain_handler_impl::get_gain_names(void){ + return _link[_props.names].as<prop_names_t>(); } -bool gain_handler::intercept_get(const wax::obj &key, wax::obj &val){ - _check_key(key); //verify the key - - std::vector<wax::obj> gain_props = boost::assign::list_of - (_gain_prop)(_gain_min_prop)(_gain_max_prop)(_gain_step_prop); - - /*! - * Handle getting overall gains when a name is not specified. - * For the gain props below, set the overall value and return true. - */ - BOOST_FOREACH(wax::obj prop_key, gain_props){ - if (_is_equal(key, prop_key)){ - //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, key, _1) - ); - - //reduce across the gain vector - if (_is_equal(key, _gain_step_prop)){ - val = std::reduce<gain_t>(gains.begin(), gains.end(), gain_max); - } - else{ - val = std::reduce<gain_t>(gains.begin(), gains.end(), gain_sum); - } - return true; - } +gain_t gain_handler_impl::get_overall_gain_val(void){ + gain_t gain_val = 0; + BOOST_FOREACH(std::string name, get_gain_names()){ + gain_val += get_named_prop<gain_t>(_props.value, name); } + return gain_val; +} - return false; +gain_range_t gain_handler_impl::get_overall_gain_range(void){ + gain_t gain_min = 0, gain_max = 0, gain_step = 0; + BOOST_FOREACH(std::string name, get_gain_names()){ + gain_t gain_min_tmp, gain_max_tmp, gain_step_tmp; + boost::tie(gain_min_tmp, gain_max_tmp, gain_step_tmp) = \ + get_named_prop<gain_range_t>(_props.range, name); + gain_min += gain_min_tmp; + gain_max += gain_max_tmp; + gain_step = std::max(gain_step, gain_step_tmp); + } + return gain_range_t(gain_min, gain_max, gain_step); } -bool gain_handler::intercept_set(const wax::obj &key_, const wax::obj &val){ - _check_key(key_); //verify the key +/*********************************************************************** + * gain handler implementation get method + **********************************************************************/ +bool gain_handler_impl::intercept_get(const wax::obj &key_, wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //not a wildcard... dont handle (but check name) + if (name != ""){ + assert_has(get_gain_names(), name, "gain name"); + return false; + } + + if (_is_equal(key, _props.value)){ + val = get_overall_gain_val(); + return true; + } + + if (_is_equal(key, _props.range)){ + val = get_overall_gain_range(); + return true; + } + + return false; //not handled +} +/*********************************************************************** + * gain handler implementation set method + **********************************************************************/ +bool gain_handler_impl::intercept_set(const wax::obj &key_, const wax::obj &val){ wax::obj key; std::string name; boost::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 + //not a gain value key... dont handle + if (not _is_equal(key, _props.value)) return false; + + gain_t gain_val = val.as<gain_t>(); + + //not a wildcard... dont handle (but check name and range) + if (name != ""){ + assert_has(get_gain_names(), name, "gain name"); + gain_t gain_min, gain_max, gain_step; + boost::tie(gain_min, gain_max, gain_step) = \ + get_named_prop<gain_range_t>(_props.range, name); + if (gain_val > gain_max or gain_val < gain_min) throw std::range_error(str( + boost::format("A value of %f for gain %s is out of range of (%f, %f)") + % gain_val % name % gain_min % gain_max )); + return false; } - 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; + + //set the overall gain + BOOST_FOREACH(std::string name, get_gain_names()){ + //get the min, max, step for this gain name + gain_t gain_min, gain_max, gain_step; + boost::tie(gain_min, gain_max, gain_step) = \ + get_named_prop<gain_range_t>(_props.range, name); + + //clip g to be within the allowed range + gain_t g = std::min(std::max(gain_val, 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 + _link[named_prop_t(_props.value, name)] = g; + //subtract g out of the total gain left to apply + gain_val -= g; } - return false; + return true; } diff --git a/host/lib/uhd.cpp b/host/lib/metadata.cpp index 5e250c76f..40fdb7c73 100644 --- a/host/lib/uhd.cpp +++ b/host/lib/metadata.cpp @@ -15,6 +15,23 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include <uhd.hpp> +#include <uhd/metadata.hpp> -//nothing here, just includes the header so the compiler can check +using namespace uhd; + +rx_metadata_t::rx_metadata_t(void){ + stream_id = 0; + has_stream_id = false; + time_spec = time_spec_t(); + has_time_spec = false; + is_fragment = false; +} + +tx_metadata_t::tx_metadata_t(void){ + stream_id = 0; + has_stream_id = false; + time_spec = time_spec_t(); + has_time_spec = false; + start_of_burst = false; + end_of_burst = false; +} diff --git a/host/lib/simple_device.cpp b/host/lib/simple_device.cpp new file mode 100644 index 000000000..62a38cb79 --- /dev/null +++ b/host/lib/simple_device.cpp @@ -0,0 +1,301 @@ +// +// 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/simple_device.hpp> +#include <uhd/device.hpp> +#include <uhd/utils.hpp> +#include <uhd/props.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/trim.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <stdexcept> + +using namespace uhd; + +tune_result_t::tune_result_t(void){ + /* NOP */ +} + +/*********************************************************************** + * Tune Helper Function + **********************************************************************/ +static tune_result_t tune( + double target_freq, + double lo_offset, + wax::obj subdev, + wax::obj dxc, + bool is_tx +){ + wax::obj subdev_freq_proxy = subdev[SUBDEV_PROP_FREQ]; + bool subdev_quadrature = subdev[SUBDEV_PROP_QUADRATURE].as<bool>(); + bool subdev_spectrum_inverted = subdev[SUBDEV_PROP_SPECTRUM_INVERTED].as<bool>(); + wax::obj dxc_freq_proxy = dxc[std::string("freq")]; + double dxc_sample_rate = dxc[std::string("rate")].as<double>(); + + // Ask the d'board to tune as closely as it can to target_freq+lo_offset + double target_inter_freq = target_freq + lo_offset; + subdev_freq_proxy = target_inter_freq; + double actual_inter_freq = subdev_freq_proxy.as<double>(); + + // Calculate the DDC setting that will downconvert the baseband from the + // daughterboard to our target frequency. + double delta_freq = target_freq - actual_inter_freq; + double delta_sign = std::signum(delta_freq); + delta_freq *= delta_sign; + delta_freq = fmod(delta_freq, dxc_sample_rate); + bool inverted = delta_freq > dxc_sample_rate/2.0; + double target_dxc_freq = inverted? (delta_freq - dxc_sample_rate) : (-delta_freq); + target_dxc_freq *= delta_sign; + + // If the spectrum is inverted, and the daughterboard doesn't do + // quadrature downconversion, we can fix the inversion by flipping the + // sign of the dxc_freq... (This only happens using the basic_rx board) + if (subdev_spectrum_inverted){ + inverted = not inverted; + } + if (inverted and not subdev_quadrature){ + target_dxc_freq *= -1.0; + inverted = not inverted; + } + // down conversion versus up conversion, fight! + // your mother is ugly and your going down... + target_dxc_freq *= (is_tx)? -1.0 : +1.0; + + dxc_freq_proxy = target_dxc_freq; + double actual_dxc_freq = dxc_freq_proxy.as<double>(); + + //return some kind of tune result tuple/struct + tune_result_t tune_result; + tune_result.target_inter_freq = target_inter_freq; + tune_result.actual_inter_freq = actual_inter_freq; + tune_result.target_dxc_freq = target_dxc_freq; + tune_result.actual_dxc_freq = actual_dxc_freq; + tune_result.spectrum_inverted = inverted; + return tune_result; +} + +/*********************************************************************** + * Helper Functions + **********************************************************************/ +static std::string trim(const std::string &in){ + return boost::algorithm::trim_copy(in); +} + +device_addr_t args_to_device_addr(const std::string &args){ + device_addr_t addr; + + //split the args at the semi-colons + std::vector<std::string> pairs; + boost::split(pairs, args, boost::is_any_of(";")); + BOOST_FOREACH(std::string pair, pairs){ + if (trim(pair) == "") continue; + + //split the key value pairs at the equals + std::vector<std::string> key_val; + boost::split(key_val, pair, boost::is_any_of("=")); + if (key_val.size() != 2) throw std::runtime_error("invalid args string: "+args); + addr[trim(key_val[0])] = trim(key_val[1]); + } + + return addr; +} + +static std::vector<double> get_xx_rates(wax::obj decerps, wax::obj rate){ + std::vector<double> rates; + BOOST_FOREACH(size_t decerp, decerps.as<std::vector<size_t> >()){ + rates.push_back(rate.as<double>()/decerp); + } + return rates; +} + +/*********************************************************************** + * Simple Device Implementation + **********************************************************************/ +class simple_device_impl : public simple_device{ +public: + simple_device_impl(const device_addr_t &addr){ + _dev = device::make(addr); + _mboard = (*_dev)[DEVICE_PROP_MBOARD]; + _rx_ddc = _mboard[named_prop_t(MBOARD_PROP_RX_DSP, "ddc0")]; + _tx_duc = _mboard[named_prop_t(MBOARD_PROP_TX_DSP, "duc0")]; + _rx_subdev = _mboard[MBOARD_PROP_RX_DBOARD][DBOARD_PROP_SUBDEV]; + _tx_subdev = _mboard[MBOARD_PROP_TX_DBOARD][DBOARD_PROP_SUBDEV]; + } + + ~simple_device_impl(void){ + /* NOP */ + } + + device::sptr get_device(void){ + return _dev; + } + + std::string get_name(void){ + return _mboard[MBOARD_PROP_NAME].as<std::string>(); + } + + /******************************************************************* + * Streaming + ******************************************************************/ + void set_streaming(bool enb){ + _rx_ddc[std::string("enabled")] = enb; + } + + bool get_streaming(void){ + return _rx_ddc[std::string("enabled")].as<bool>(); + } + + /******************************************************************* + * RX methods + ******************************************************************/ + void set_rx_rate(double rate){ + double samp_rate = _rx_ddc[std::string("rate")].as<double>(); + assert_has(get_rx_rates(), rate, "simple device rx rate"); + _rx_ddc[std::string("decim")] = size_t(samp_rate/rate); + } + + double get_rx_rate(void){ + double samp_rate = _rx_ddc[std::string("rate")].as<double>(); + size_t decim = _rx_ddc[std::string("decim")].as<size_t>(); + return samp_rate/decim; + } + + std::vector<double> get_rx_rates(void){ + return get_xx_rates(_rx_ddc[std::string("decims")], _rx_ddc[std::string("rate")]); + } + + tune_result_t set_rx_freq(double target_freq){ + double lo_offset = 0.0; + //if the local oscillator will be in the passband, use an offset + if (_rx_subdev[SUBDEV_PROP_LO_INTERFERES].as<bool>()){ + lo_offset = get_rx_rate()*2.0; + } + return tune(target_freq, lo_offset, _rx_subdev, _rx_ddc, false/* not tx */); + } + + std::vector<double> get_rx_freq_range(void){ + std::vector<double> range(2); + boost::tie(range[0], range[1]) = \ + _rx_subdev[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(); + return range; + } + + void set_rx_gain(float gain){ + _rx_subdev[SUBDEV_PROP_GAIN] = gain; + } + + float get_rx_gain(void){ + return _rx_subdev[SUBDEV_PROP_GAIN].as<gain_t>(); + } + + std::vector<float> get_rx_gain_range(void){ + std::vector<float> range(3); + boost::tie(range[0], range[1], range[2]) = \ + _rx_subdev[SUBDEV_PROP_GAIN_RANGE].as<gain_range_t>(); + return range; + } + + void set_rx_antenna(const std::string &ant){ + _rx_subdev[SUBDEV_PROP_ANTENNA] = ant; + } + + std::string get_rx_antenna(void){ + return _rx_subdev[SUBDEV_PROP_ANTENNA].as<std::string>(); + } + + std::vector<std::string> get_rx_antennas(void){ + return _rx_subdev[SUBDEV_PROP_ANTENNA_NAMES].as<std::vector<std::string> >(); + } + + /******************************************************************* + * TX methods + ******************************************************************/ + void set_tx_rate(double rate){ + double samp_rate = _tx_duc[std::string("rate")].as<double>(); + assert_has(get_tx_rates(), rate, "simple device tx rate"); + _tx_duc[std::string("interp")] = size_t(samp_rate/rate); + } + + double get_tx_rate(void){ + double samp_rate = _tx_duc[std::string("rate")].as<double>(); + size_t interp = _tx_duc[std::string("interp")].as<size_t>(); + return samp_rate/interp; + } + + std::vector<double> get_tx_rates(void){ + return get_xx_rates(_tx_duc[std::string("interps")], _tx_duc[std::string("rate")]); + } + + tune_result_t set_tx_freq(double target_freq){ + double lo_offset = 0.0; + //if the local oscillator will be in the passband, use an offset + if (_tx_subdev[SUBDEV_PROP_LO_INTERFERES].as<bool>()){ + lo_offset = get_tx_rate()*2.0; + } + return tune(target_freq, lo_offset, _tx_subdev, _tx_duc, true/* is tx */); + } + + std::vector<double> get_tx_freq_range(void){ + std::vector<double> range(2); + boost::tie(range[0], range[1]) = \ + _tx_subdev[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(); + return range; + } + + void set_tx_gain(float gain){ + _tx_subdev[SUBDEV_PROP_GAIN] = gain; + } + + float get_tx_gain(void){ + return _tx_subdev[SUBDEV_PROP_GAIN].as<gain_t>(); + } + + std::vector<float> get_tx_gain_range(void){ + std::vector<float> range(3); + boost::tie(range[0], range[1], range[2]) = \ + _tx_subdev[SUBDEV_PROP_GAIN_RANGE].as<gain_range_t>(); + return range; + } + + void set_tx_antenna(const std::string &ant){ + _tx_subdev[SUBDEV_PROP_ANTENNA] = ant; + } + + std::string get_tx_antenna(void){ + return _tx_subdev[SUBDEV_PROP_ANTENNA].as<std::string>(); + } + + std::vector<std::string> get_tx_antennas(void){ + return _tx_subdev[SUBDEV_PROP_ANTENNA_NAMES].as<std::vector<std::string> >(); + } + +private: + device::sptr _dev; + wax::obj _mboard; + wax::obj _rx_ddc; + wax::obj _tx_duc; + wax::obj _rx_subdev; + wax::obj _tx_subdev; +}; + +/*********************************************************************** + * The Make Function + **********************************************************************/ +simple_device::sptr simple_device::make(const std::string &args){ + return sptr(new simple_device_impl(args_to_device_addr(args))); +} diff --git a/host/lib/usrp/dboard_id.cpp b/host/lib/time_spec.cpp index d2ef7cd7d..193441342 100644 --- a/host/lib/usrp/dboard_id.cpp +++ b/host/lib/time_spec.cpp @@ -15,20 +15,26 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include <uhd/usrp/dboard_id.hpp> -#include <boost/format.hpp> -#include <uhd/dict.hpp> +#include <uhd/time_spec.hpp> -using namespace uhd::usrp; +using namespace uhd; -std::string dboard_id::to_string(const dboard_id_t &id){ - //map the dboard ids to string representations - uhd::dict<dboard_id_t, std::string> id_to_str; - id_to_str[ID_NONE] = "none"; - id_to_str[ID_BASIC_TX] = "basic tx"; - id_to_str[ID_BASIC_RX] = "basic rx"; +time_spec_t::time_spec_t(void){ + secs = ~0; + ticks = ~0; +} + +time_spec_t::time_spec_t(uint32_t new_secs, uint32_t new_ticks){ + secs = new_secs; + ticks = new_ticks; +} + +static const boost::posix_time::ptime epoch(boost::gregorian::date(1970,1,1)); +static double time_tick_rate(boost::posix_time::time_duration::ticks_per_second()); - //get the string representation - std::string name = (id_to_str.has_key(id))? id_to_str[id] : "unknown"; - return str(boost::format("%s (0x%.4x)") % name % id); +time_spec_t::time_spec_t(boost::posix_time::ptime time, double tick_rate){ + boost::posix_time::time_duration td = time - epoch; + secs = td.total_seconds(); + double time_ticks_per_device_ticks = time_tick_rate/tick_rate; + ticks = td.fractional_seconds()/time_ticks_per_device_ticks; } diff --git a/host/lib/transport/udp.cpp b/host/lib/transport/udp.cpp deleted file mode 100644 index 878f71410..000000000 --- a/host/lib/transport/udp.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// -// 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/transport/udp.hpp> -#include <boost/format.hpp> -#include <iostream> - -/*********************************************************************** - * UDP implementation class - **********************************************************************/ -class udp_impl : public uhd::transport::udp{ -public: - //structors - udp_impl(const std::string &addr, const std::string &port, bool bcast); - ~udp_impl(void); - - //send/recv - size_t send(const std::vector<boost::asio::const_buffer> &buffs); - size_t send(const boost::asio::const_buffer &buff); - size_t recv(const std::vector<boost::asio::mutable_buffer> &buffs); - size_t recv(const boost::asio::mutable_buffer &buff); - -private: - boost::asio::ip::udp::socket *_socket; - boost::asio::ip::udp::endpoint _receiver_endpoint; - boost::asio::ip::udp::endpoint _sender_endpoint; - boost::asio::io_service _io_service; -}; - -/*********************************************************************** - * UDP public make function - **********************************************************************/ -uhd::transport::udp::sptr uhd::transport::udp::make( - const std::string &addr, - const std::string &port, - bool bcast -){ - return uhd::transport::udp::sptr(new udp_impl(addr, port, bcast)); -} - -/*********************************************************************** - * UDP implementation methods - **********************************************************************/ -udp_impl::udp_impl(const std::string &addr, const std::string &port, bool bcast){ - //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; - - // resolve the address - boost::asio::ip::udp::resolver resolver(_io_service); - boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); - _receiver_endpoint = *resolver.resolve(query); - - // Create and open the socket - _socket = new boost::asio::ip::udp::socket(_io_service); - _socket->open(boost::asio::ip::udp::v4()); - - if (bcast){ - // Allow broadcasting - boost::asio::socket_base::broadcast option(true); - _socket->set_option(option); - } - -} - -udp_impl::~udp_impl(void){ - delete _socket; -} - -size_t udp_impl::send(const std::vector<boost::asio::const_buffer> &buffs){ - return _socket->send_to(buffs, _receiver_endpoint); -} - -size_t udp_impl::send(const boost::asio::const_buffer &buff){ - return _socket->send_to(boost::asio::buffer(buff), _receiver_endpoint); -} - -size_t udp_impl::recv(const std::vector<boost::asio::mutable_buffer> &buffs){ - if (_socket->available() == 0) return 0; - return _socket->receive_from(buffs, _sender_endpoint); -} - -size_t udp_impl::recv(const boost::asio::mutable_buffer &buff){ - if (_socket->available() == 0) return 0; - return _socket->receive_from(boost::asio::buffer(buff), _sender_endpoint); -} diff --git a/host/lib/transport/udp_simple.cpp b/host/lib/transport/udp_simple.cpp new file mode 100644 index 000000000..7004bdfdf --- /dev/null +++ b/host/lib/transport/udp_simple.cpp @@ -0,0 +1,158 @@ +// +// 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/transport/udp_simple.hpp> +#include <boost/thread.hpp> +#include <boost/format.hpp> +#include <iostream> + +using namespace uhd::transport; + +/*********************************************************************** + * Helper Functions + **********************************************************************/ +/*! + * A receive timeout for a socket: + * + * It seems that asio cannot have timeouts with synchronous io. + * However, we can implement a polling loop that will timeout. + * This is okay bacause this is the slow-path implementation. + * + * \param socket the asio socket + */ +static void reasonable_recv_timeout( + boost::asio::ip::udp::socket &socket +){ + boost::asio::deadline_timer timer(socket.get_io_service()); + timer.expires_from_now(boost::posix_time::milliseconds(50)); + while (not (socket.available() or timer.expires_from_now().is_negative())){ + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } +} + +/*********************************************************************** + * UDP connected implementation class + **********************************************************************/ +class udp_connected_impl : public udp_simple{ +public: + //structors + udp_connected_impl(const std::string &addr, const std::string &port); + ~udp_connected_impl(void); + + //send/recv + size_t send(const boost::asio::const_buffer &buff); + size_t recv(const boost::asio::mutable_buffer &buff); + +private: + boost::asio::ip::udp::socket *_socket; + boost::asio::io_service _io_service; +}; + +udp_connected_impl::udp_connected_impl(const std::string &addr, const std::string &port){ + //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + + // resolve the address + boost::asio::ip::udp::resolver resolver(_io_service); + boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); + boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); + + // Create, open, and connect the socket + _socket = new boost::asio::ip::udp::socket(_io_service); + _socket->open(boost::asio::ip::udp::v4()); + _socket->connect(receiver_endpoint); +} + +udp_connected_impl::~udp_connected_impl(void){ + delete _socket; +} + +size_t udp_connected_impl::send(const boost::asio::const_buffer &buff){ + return _socket->send(boost::asio::buffer(buff)); +} + +size_t udp_connected_impl::recv(const boost::asio::mutable_buffer &buff){ + reasonable_recv_timeout(*_socket); + if (not _socket->available()) return 0; + return _socket->receive(boost::asio::buffer(buff)); +} + +/*********************************************************************** + * UDP broadcast implementation class + **********************************************************************/ +class udp_broadcast_impl : public udp_simple{ +public: + //structors + udp_broadcast_impl(const std::string &addr, const std::string &port); + ~udp_broadcast_impl(void); + + //send/recv + size_t send(const boost::asio::const_buffer &buff); + size_t recv(const boost::asio::mutable_buffer &buff); + +private: + boost::asio::ip::udp::socket *_socket; + boost::asio::ip::udp::endpoint _receiver_endpoint; + boost::asio::io_service _io_service; +}; + +udp_broadcast_impl::udp_broadcast_impl(const std::string &addr, const std::string &port){ + //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + + // resolve the address + boost::asio::ip::udp::resolver resolver(_io_service); + boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); + _receiver_endpoint = *resolver.resolve(query); + + // Create and open the socket + _socket = new boost::asio::ip::udp::socket(_io_service); + _socket->open(boost::asio::ip::udp::v4()); + + // Allow broadcasting + boost::asio::socket_base::broadcast option(true); + _socket->set_option(option); + +} + +udp_broadcast_impl::~udp_broadcast_impl(void){ + delete _socket; +} + +size_t udp_broadcast_impl::send(const boost::asio::const_buffer &buff){ + return _socket->send_to(boost::asio::buffer(buff), _receiver_endpoint); +} + +size_t udp_broadcast_impl::recv(const boost::asio::mutable_buffer &buff){ + reasonable_recv_timeout(*_socket); + if (not _socket->available()) return 0; + boost::asio::ip::udp::endpoint sender_endpoint; + return _socket->receive_from(boost::asio::buffer(buff), sender_endpoint); +} + +/*********************************************************************** + * UDP public make functions + **********************************************************************/ +udp_simple::sptr udp_simple::make_connected( + const std::string &addr, const std::string &port +){ + return sptr(new udp_connected_impl(addr, port)); +} + +udp_simple::sptr udp_simple::make_broadcast( + const std::string &addr, const std::string &port +){ + return sptr(new udp_broadcast_impl(addr, port)); +} diff --git a/host/lib/transport/udp_zero_copy_none.cpp b/host/lib/transport/udp_zero_copy_none.cpp new file mode 100644 index 000000000..e29530cf1 --- /dev/null +++ b/host/lib/transport/udp_zero_copy_none.cpp @@ -0,0 +1,152 @@ +// +// 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/transport/udp_zero_copy.hpp> +#include <boost/thread.hpp> +#include <boost/format.hpp> + +using namespace uhd::transport; + +/*********************************************************************** + * Smart buffer implementation for udp zerocopy none + * + * This smart buffer implemention houses a const buffer. + * When the smart buffer is deleted, the buffer is freed. + * The memory in the const buffer is allocated with new [], + * and so the destructor frees the buffer with delete []. + **********************************************************************/ +class smart_buffer_impl : public smart_buffer{ +public: + smart_buffer_impl(const boost::asio::const_buffer &buff){ + _buff = buff; + } + + ~smart_buffer_impl(void){ + delete [] boost::asio::buffer_cast<const uint32_t *>(_buff); + } + + const boost::asio::const_buffer &get(void) const{ + return _buff; + } + +private: + boost::asio::const_buffer _buff; +}; + +/*********************************************************************** + * UDP zero copy implementation class + * + * This is the portable zero copy implementation for systems + * where a faster, platform specific solution is not available. + * + * It uses boost asio udp sockets and the standard recv() class, + * and in-fact, is not actually doing a zero-copy implementation. + **********************************************************************/ +class udp_zero_copy_impl : public udp_zero_copy{ +public: + //structors + udp_zero_copy_impl(const std::string &addr, const std::string &port); + ~udp_zero_copy_impl(void); + + //send/recv + size_t send(const boost::asio::const_buffer &buff); + smart_buffer::sptr recv(void); + +private: + boost::asio::ip::udp::socket *_socket; + boost::asio::io_service _io_service; + + size_t get_recv_buff_size(void); + void set_recv_buff_size(size_t); +}; + +udp_zero_copy_impl::udp_zero_copy_impl(const std::string &addr, const std::string &port){ + //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + + // resolve the address + boost::asio::ip::udp::resolver resolver(_io_service); + boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); + boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); + + // Create, open, and connect the socket + _socket = new boost::asio::ip::udp::socket(_io_service); + _socket->open(boost::asio::ip::udp::v4()); + _socket->connect(receiver_endpoint); + + // set the rx socket buffer size: + // pick a huge size, and deal with whatever we get + set_recv_buff_size(54321e3); //some big number! + size_t current_buff_size = get_recv_buff_size(); + std::cout << boost::format( + "Current rx socket buffer size: %d\n" + ) % current_buff_size; + if (current_buff_size < .1e6) std::cout << boost::format( + "Adjust max rx socket buffer size (linux only):\n" + " sysctl -w net.core.rmem_max=VALUE\n" + ); +} + +udp_zero_copy_impl::~udp_zero_copy_impl(void){ + delete _socket; +} + +size_t udp_zero_copy_impl::send(const boost::asio::const_buffer &buff){ + return _socket->send(boost::asio::buffer(buff)); +} + +smart_buffer::sptr udp_zero_copy_impl::recv(void){ + size_t available = 0; + + //implement timeout through polling and sleeping + boost::asio::deadline_timer timer(_socket->get_io_service()); + timer.expires_from_now(boost::posix_time::milliseconds(50)); + while (not ((available = _socket->available()) or timer.expires_from_now().is_negative())){ + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } + + //allocate memory and create buffer + uint32_t *buff_mem = new uint32_t[available/sizeof(uint32_t)]; + boost::asio::mutable_buffer buff(buff_mem, available); + + //receive only if data is available + if (available){ + _socket->receive(boost::asio::buffer(buff)); + } + + //create a new smart buffer to house the data + return smart_buffer::sptr(new smart_buffer_impl(buff)); +} + +size_t udp_zero_copy_impl::get_recv_buff_size(void){ + boost::asio::socket_base::receive_buffer_size option; + _socket->get_option(option); + return option.value(); +} + +void udp_zero_copy_impl::set_recv_buff_size(size_t new_size){ + boost::asio::socket_base::receive_buffer_size option(new_size); + _socket->set_option(option); +} + +/*********************************************************************** + * UDP zero copy make function + **********************************************************************/ +udp_zero_copy::sptr udp_zero_copy::make( + const std::string &addr, const std::string &port +){ + return sptr(new udp_zero_copy_impl(addr, port)); +} diff --git a/host/lib/transport/vrt.cpp b/host/lib/transport/vrt.cpp new file mode 100644 index 000000000..5029df217 --- /dev/null +++ b/host/lib/transport/vrt.cpp @@ -0,0 +1,108 @@ +// +// 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/transport/vrt.hpp> +#include <netinet/in.h> +#include <stdexcept> + +using namespace uhd::transport; + +void vrt::pack( + const tx_metadata_t &metadata, //input + uint32_t *header_buff, //output + size_t &num_header_words32, //output + size_t num_payload_words32, //input + size_t &num_packet_words32, //output + size_t packet_count //input +){ + uint32_t vrt_hdr_flags = 0; + num_header_words32 = 1; + + //load the vrt header and flags + if(metadata.has_stream_id){ + vrt_hdr_flags |= (0x1 << 28); //IF Data packet with Stream Identifier + header_buff[num_header_words32++] = htonl(metadata.stream_id); + } + + if(metadata.has_time_spec){ + vrt_hdr_flags |= (0x3 << 22) | (0x1 << 20); //TSI: Other, TSF: Sample Count Timestamp + header_buff[num_header_words32++] = htonl(metadata.time_spec.secs); + header_buff[num_header_words32++] = htonl(metadata.time_spec.ticks); + header_buff[num_header_words32++] = 0; //unused part of fractional seconds + } + + vrt_hdr_flags |= (metadata.start_of_burst)? (0x1 << 25) : 0; + vrt_hdr_flags |= (metadata.end_of_burst)? (0x1 << 24) : 0; + + num_packet_words32 = num_header_words32 + num_payload_words32; + + //fill in complete header word + header_buff[0] = htonl(vrt_hdr_flags | + ((packet_count & 0xf) << 16) | + (num_packet_words32 & 0xffff) + ); +} + +void vrt::unpack( + rx_metadata_t &metadata, //output + const uint32_t *header_buff, //input + size_t &num_header_words32, //output + size_t &num_payload_words32, //output + size_t num_packet_words32, //input + size_t &packet_count //output +){ + //clear the metadata + metadata = rx_metadata_t(); + + //extract vrt header + uint32_t vrt_hdr_word = ntohl(header_buff[0]); + size_t packet_words32 = vrt_hdr_word & 0xffff; + packet_count = (vrt_hdr_word >> 16) & 0xf; + + //failure cases + if (packet_words32 == 0 or num_packet_words32 < packet_words32) + throw std::runtime_error("bad vrt header or packet fragment"); + if (vrt_hdr_word & (0x7 << 29)) + throw std::runtime_error("unsupported vrt packet type"); + + //parse the header flags + num_header_words32 = 1; + + if (vrt_hdr_word & (0x1 << 28)){ //stream id + metadata.has_stream_id = true; + metadata.stream_id = ntohl(header_buff[num_header_words32++]); + } + + if (vrt_hdr_word & (0x1 << 27)){ //class id (we dont use) + num_header_words32 += 2; + } + + if (vrt_hdr_word & (0x3 << 22)){ //integer time + metadata.has_time_spec = true; + metadata.time_spec.secs = ntohl(header_buff[num_header_words32++]); + } + + if (vrt_hdr_word & (0x3 << 20)){ //fractional time + metadata.has_time_spec = true; + metadata.time_spec.ticks = ntohl(header_buff[num_header_words32++]); + num_header_words32++; //unused part of fractional seconds + } + + size_t num_trailer_words32 = (vrt_hdr_word & (0x1 << 26))? 1 : 0; + + num_payload_words32 = packet_words32 - num_header_words32 - num_trailer_words32; +} diff --git a/host/lib/usrp/dboard/basic.cpp b/host/lib/usrp/dboard/basic.cpp index f39ebff2f..095b77ce1 100644 --- a/host/lib/usrp/dboard/basic.cpp +++ b/host/lib/usrp/dboard/basic.cpp @@ -15,42 +15,282 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include "dboards.hpp" +#include <uhd/utils.hpp> +#include <uhd/props.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; /*********************************************************************** - * Basic RX dboard + * The basic and lf boards: + * They share a common class because only the frequency bounds differ. **********************************************************************/ -basic_rx::basic_rx(ctor_args_t const& args) : rx_dboard_base(args){ - /* NOP */ +class basic_rx : public rx_dboard_base{ +public: + basic_rx(ctor_args_t const& args, freq_t max_freq); + ~basic_rx(void); + + void rx_get(const wax::obj &key, wax::obj &val); + void rx_set(const wax::obj &key, const wax::obj &val); + +private: + freq_t _max_freq; +}; + +class basic_tx : public tx_dboard_base{ +public: + basic_tx(ctor_args_t const& args, freq_t max_freq); + ~basic_tx(void); + + void tx_get(const wax::obj &key, wax::obj &val); + void tx_set(const wax::obj &key, const wax::obj &val); + +private: + freq_t _max_freq; +}; + +/*********************************************************************** + * Register the basic and LF dboards + **********************************************************************/ +static dboard_base::sptr make_basic_rx(dboard_base::ctor_args_t const& args){ + return dboard_base::sptr(new basic_rx(args, 90e9)); +} + +static dboard_base::sptr make_basic_tx(dboard_base::ctor_args_t const& args){ + return dboard_base::sptr(new basic_tx(args, 90e9)); +} + +static dboard_base::sptr make_lf_rx(dboard_base::ctor_args_t const& args){ + return dboard_base::sptr(new basic_rx(args, 32e6)); +} + +static dboard_base::sptr make_lf_tx(dboard_base::ctor_args_t const& args){ + return dboard_base::sptr(new basic_tx(args, 32e6)); +} + +STATIC_BLOCK(reg_dboards){ + dboard_manager::register_dboard(0x0000, &make_basic_tx, "Basic TX", list_of("")); + dboard_manager::register_dboard(0x0001, &make_basic_rx, "Basic RX", list_of("a")("b")("ab")); + dboard_manager::register_dboard(0x000e, &make_lf_tx, "LF TX", list_of("")); + dboard_manager::register_dboard(0x000f, &make_lf_rx, "LF RX", list_of("a")("b")("ab")); +} + +/*********************************************************************** + * Basic and LF RX dboard + **********************************************************************/ +basic_rx::basic_rx(ctor_args_t const& args, freq_t max_freq) : rx_dboard_base(args){ + _max_freq = max_freq; + // set the gpios to safe values (all inputs) + get_interface()->set_gpio_ddr(dboard_interface::GPIO_RX_BANK, 0x0000, 0xffff); } basic_rx::~basic_rx(void){ /* NOP */ } -void basic_rx::rx_get(const wax::obj &, wax::obj &){ - /* TODO */ +void basic_rx::rx_get(const wax::obj &key_, wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(key.as<subdev_prop_t>()){ + case SUBDEV_PROP_NAME: + val = std::string(str(boost::format("%s:%s") + % dboard_id::to_string(get_rx_id()) + % get_subdev_name() + )); + return; + + case SUBDEV_PROP_OTHERS: + val = prop_names_t(); //empty + return; + + case SUBDEV_PROP_GAIN: + val = gain_t(0); + return; + + case SUBDEV_PROP_GAIN_RANGE: + val = gain_range_t(0, 0, 0); + return; + + case SUBDEV_PROP_GAIN_NAMES: + val = prop_names_t(); //empty + return; + + case SUBDEV_PROP_FREQ: + val = freq_t(0); + return; + + case SUBDEV_PROP_FREQ_RANGE: + val = freq_range_t(+_max_freq, -_max_freq); + return; + + case SUBDEV_PROP_ANTENNA: + val = std::string(""); + return; + + case SUBDEV_PROP_ANTENNA_NAMES: + val = prop_names_t(1, ""); //vector of 1 empty string + return; + + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + + case SUBDEV_PROP_QUADRATURE: + val = (get_subdev_name() == "ab"); //only quadrature in ab mode + return; + + case SUBDEV_PROP_IQ_SWAPPED: + case SUBDEV_PROP_SPECTRUM_INVERTED: + case SUBDEV_PROP_LO_INTERFERES: + val = false; + return; + } } -void basic_rx::rx_set(const wax::obj &, const wax::obj &){ - /* TODO */ +void basic_rx::rx_set(const wax::obj &key_, const wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(key.as<subdev_prop_t>()){ + + case SUBDEV_PROP_GAIN: + ASSERT_THROW(val.as<gain_t>() == gain_t(0)); + return; + + case SUBDEV_PROP_ANTENNA: + ASSERT_THROW(val.as<std::string>() == std::string("")); + return; + + case SUBDEV_PROP_ENABLED: + return; // it wont do you much good, but you can set it + + case SUBDEV_PROP_NAME: + case SUBDEV_PROP_OTHERS: + case SUBDEV_PROP_GAIN_RANGE: + case SUBDEV_PROP_GAIN_NAMES: + case SUBDEV_PROP_FREQ: + case SUBDEV_PROP_FREQ_RANGE: + case SUBDEV_PROP_ANTENNA_NAMES: + case SUBDEV_PROP_QUADRATURE: + case SUBDEV_PROP_IQ_SWAPPED: + case SUBDEV_PROP_SPECTRUM_INVERTED: + case SUBDEV_PROP_LO_INTERFERES: + throw std::runtime_error(str(boost::format( + "Error: trying to set read-only property on %s subdev" + ) % dboard_id::to_string(get_rx_id()))); + } } /*********************************************************************** - * Basic TX dboard + * Basic and LF TX dboard **********************************************************************/ -basic_tx::basic_tx(ctor_args_t const& args) : tx_dboard_base(args){ - /* NOP */ +basic_tx::basic_tx(ctor_args_t const& args, freq_t max_freq) : tx_dboard_base(args){ + _max_freq = max_freq; + // set the gpios to safe values (all inputs) + get_interface()->set_gpio_ddr(dboard_interface::GPIO_TX_BANK, 0x0000, 0xffff); } basic_tx::~basic_tx(void){ /* NOP */ } -void basic_tx::tx_get(const wax::obj &, wax::obj &){ - /* TODO */ +void basic_tx::tx_get(const wax::obj &key_, wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(key.as<subdev_prop_t>()){ + case SUBDEV_PROP_NAME: + val = dboard_id::to_string(get_tx_id()); + return; + + case SUBDEV_PROP_OTHERS: + val = prop_names_t(); //empty + return; + + case SUBDEV_PROP_GAIN: + val = gain_t(0); + return; + + case SUBDEV_PROP_GAIN_RANGE: + val = gain_range_t(0, 0, 0); + return; + + case SUBDEV_PROP_GAIN_NAMES: + val = prop_names_t(); //empty + return; + + case SUBDEV_PROP_FREQ: + val = freq_t(0); + return; + + case SUBDEV_PROP_FREQ_RANGE: + val = freq_range_t(+_max_freq, -_max_freq); + return; + + case SUBDEV_PROP_ANTENNA: + val = std::string(""); + return; + + case SUBDEV_PROP_ANTENNA_NAMES: + val = prop_names_t(1, ""); //vector of 1 empty string + return; + + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + + case SUBDEV_PROP_QUADRATURE: + val = true; + return; + + case SUBDEV_PROP_IQ_SWAPPED: + case SUBDEV_PROP_SPECTRUM_INVERTED: + case SUBDEV_PROP_LO_INTERFERES: + val = false; + return; + } } -void basic_tx::tx_set(const wax::obj &, const wax::obj &){ - /* TODO */ +void basic_tx::tx_set(const wax::obj &key_, const wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(key.as<subdev_prop_t>()){ + + case SUBDEV_PROP_GAIN: + ASSERT_THROW(val.as<gain_t>() == gain_t(0)); + return; + + case SUBDEV_PROP_ANTENNA: + ASSERT_THROW(val.as<std::string>() == std::string("")); + return; + + case SUBDEV_PROP_ENABLED: + return; // it wont do you much good, but you can set it + + case SUBDEV_PROP_NAME: + case SUBDEV_PROP_OTHERS: + case SUBDEV_PROP_GAIN_RANGE: + case SUBDEV_PROP_GAIN_NAMES: + case SUBDEV_PROP_FREQ: + case SUBDEV_PROP_FREQ_RANGE: + case SUBDEV_PROP_ANTENNA_NAMES: + case SUBDEV_PROP_QUADRATURE: + case SUBDEV_PROP_IQ_SWAPPED: + case SUBDEV_PROP_SPECTRUM_INVERTED: + case SUBDEV_PROP_LO_INTERFERES: + throw std::runtime_error(str(boost::format( + "Error: trying to set read-only property on %s subdev" + ) % dboard_id::to_string(get_tx_id()))); + } } diff --git a/host/lib/usrp/dboard/dboards.hpp b/host/lib/usrp/dboard/dboards.hpp deleted file mode 100644 index 79b90d593..000000000 --- a/host/lib/usrp/dboard/dboards.hpp +++ /dev/null @@ -1,53 +0,0 @@ -// -// 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_LOCAL_DBOARDS_HPP -#define INCLUDED_LOCAL_DBOARDS_HPP - -#include <uhd/usrp/dboard_base.hpp> - -using namespace uhd::usrp; - -/*********************************************************************** - * The basic boards: - **********************************************************************/ -class basic_rx : public rx_dboard_base{ -public: - static dboard_base::sptr make(ctor_args_t const& args){ - return dboard_base::sptr(new basic_rx(args)); - } - basic_rx(ctor_args_t const& args); - ~basic_rx(void); - - void rx_get(const wax::obj &key, wax::obj &val); - void rx_set(const wax::obj &key, const wax::obj &val); -}; - -class basic_tx : public tx_dboard_base{ -public: - static dboard_base::sptr make(ctor_args_t const& args){ - return dboard_base::sptr(new basic_tx(args)); - } - basic_tx(ctor_args_t const& args); - ~basic_tx(void); - - void tx_get(const wax::obj &key, wax::obj &val); - void tx_set(const wax::obj &key, const wax::obj &val); - -}; - -#endif /* INCLUDED_LOCAL_DBOARDS_HPP */ diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index cce239f3e..eeabbda99 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -16,87 +16,49 @@ // #include <uhd/usrp/dboard_manager.hpp> +#include <uhd/gain_handler.hpp> #include <uhd/utils.hpp> #include <uhd/dict.hpp> -#include <boost/assign/list_of.hpp> #include <boost/tuple/tuple.hpp> #include <boost/format.hpp> +#include <boost/bind.hpp> #include <boost/foreach.hpp> -#include "dboard/dboards.hpp" using namespace uhd; using namespace uhd::usrp; -using namespace boost::assign; - -/*********************************************************************** - * register internal dboards - * - * Register internal/known dboards located in this build tree. - * Each board should have entries below mapping an id to a constructor. - * The xcvr type boards should register both rx and tx sides. - * - * This function will be called before new boards are registered. - * This allows for internal boards to be externally overridden. - * This function will also be called when creating a new dboard_manager - * to ensure that the maps are filled with the entries below. - **********************************************************************/ -static void register_internal_dboards(void){ - //ensure that this function can only be called once per instance - static bool called = false; - if (called) return; called = true; - //register the known dboards (dboard id, constructor, subdev names) - dboard_manager::register_subdevs(ID_BASIC_TX, &basic_tx::make, list_of("")); - dboard_manager::register_subdevs(ID_BASIC_RX, &basic_rx::make, list_of("a")("b")("ab")); -} /*********************************************************************** * storage and registering for dboards **********************************************************************/ -typedef boost::tuple<dboard_manager::dboard_ctor_t, prop_names_t> args_t; +//dboard registry tuple: dboard constructor, canonical name, subdev names +typedef boost::tuple<dboard_manager::dboard_ctor_t, std::string, prop_names_t> args_t; //map a dboard id to a dboard constructor -static uhd::dict<dboard_id_t, args_t> id_to_args_map; +typedef uhd::dict<dboard_id_t, args_t> id_to_args_map_t; +STATIC_INSTANCE(id_to_args_map_t, get_id_to_args_map) -void dboard_manager::register_subdevs( +void dboard_manager::register_dboard( dboard_id_t dboard_id, dboard_ctor_t dboard_ctor, + const std::string &name, const prop_names_t &subdev_names ){ - register_internal_dboards(); //always call first - id_to_args_map[dboard_id] = args_t(dboard_ctor, subdev_names); + //std::cout << "registering: " << name << std::endl; + if (get_id_to_args_map().has_key(dboard_id)){ + throw std::runtime_error(str(boost::format( + "The dboard id 0x%.4x is already registered to %s." + ) % dboard_id % dboard_id::to_string(dboard_id))); + } + get_id_to_args_map()[dboard_id] = args_t(dboard_ctor, name, subdev_names); } -/*********************************************************************** - * dboard manager implementation class - **********************************************************************/ -class dboard_manager_impl : public dboard_manager{ - -public: - dboard_manager_impl( - dboard_id_t rx_dboard_id, - dboard_id_t tx_dboard_id, - dboard_interface::sptr interface - ); - ~dboard_manager_impl(void); - - //dboard_interface - prop_names_t get_rx_subdev_names(void); - prop_names_t get_tx_subdev_names(void); - wax::obj get_rx_subdev(const std::string &subdev_name); - wax::obj get_tx_subdev(const std::string &subdev_name); - -private: - //list of rx and tx dboards in this dboard_manager - //each dboard here is actually a subdevice proxy - //the subdevice proxy is internal to the cpp file - uhd::dict<std::string, wax::obj> _rx_dboards; - uhd::dict<std::string, wax::obj> _tx_dboards; - dboard_interface::sptr _interface; - void set_nice_gpio_pins(void); -}; +std::string dboard_id::to_string(const dboard_id_t &id){ + std::string name = (get_id_to_args_map().has_key(id))? get_id_to_args_map()[id].get<1>() : "unknown"; + return str(boost::format("%s (0x%.4x)") % name % id); +} /*********************************************************************** - * internal helper classes + * internal helper classe **********************************************************************/ /*! * A special wax proxy object that forwards calls to a subdev. @@ -110,7 +72,17 @@ public: //structors subdev_proxy(dboard_base::sptr subdev, type_t type) : _subdev(subdev), _type(type){ - /* NOP */ + //initialize gain props struct + gain_handler::props_t gain_props; + gain_props.value = SUBDEV_PROP_GAIN; + gain_props.range = SUBDEV_PROP_GAIN_RANGE; + gain_props.names = SUBDEV_PROP_GAIN_NAMES; + + //make a new gain handler + _gain_handler = gain_handler::make( + this->get_link(), gain_props, + boost::bind(&gain_handler::is_equal<subdev_prop_t>, _1, _2) + ); } ~subdev_proxy(void){ @@ -118,11 +90,13 @@ public: } private: + gain_handler::sptr _gain_handler; dboard_base::sptr _subdev; type_t _type; //forward the get calls to the rx or tx void get(const wax::obj &key, wax::obj &val){ + if (_gain_handler->intercept_get(key, val)) return; switch(_type){ case RX_TYPE: return _subdev->rx_get(key, val); case TX_TYPE: return _subdev->tx_get(key, val); @@ -131,6 +105,7 @@ private: //forward the set calls to the rx or tx void set(const wax::obj &key, const wax::obj &val){ + if (_gain_handler->intercept_set(key, val)) return; switch(_type){ case RX_TYPE: return _subdev->rx_set(key, val); case TX_TYPE: return _subdev->tx_set(key, val); @@ -139,6 +114,35 @@ private: }; /*********************************************************************** + * dboard manager implementation class + **********************************************************************/ +class dboard_manager_impl : public dboard_manager{ + +public: + dboard_manager_impl( + dboard_id_t rx_dboard_id, + dboard_id_t tx_dboard_id, + dboard_interface::sptr interface + ); + ~dboard_manager_impl(void); + + //dboard_interface + prop_names_t get_rx_subdev_names(void); + prop_names_t get_tx_subdev_names(void); + wax::obj get_rx_subdev(const std::string &subdev_name); + wax::obj get_tx_subdev(const std::string &subdev_name); + +private: + //list of rx and tx dboards in this dboard_manager + //each dboard here is actually a subdevice proxy + //the subdevice proxy is internal to the cpp file + uhd::dict<std::string, subdev_proxy::sptr> _rx_dboards; + uhd::dict<std::string, subdev_proxy::sptr> _tx_dboards; + dboard_interface::sptr _interface; + void set_nice_gpio_pins(void); +}; + +/*********************************************************************** * make routine for dboard manager **********************************************************************/ dboard_manager::sptr dboard_manager::make( @@ -160,16 +164,16 @@ static args_t get_dboard_args( ){ //special case, its rx and the none id (0xffff) if (xx_type == "rx" and dboard_id == ID_NONE){ - return args_t(&basic_rx::make, list_of("ab")); + return get_dboard_args(0x0001, xx_type); } //special case, its tx and the none id (0xffff) if (xx_type == "tx" and dboard_id == ID_NONE){ - return args_t(&basic_tx::make, list_of("")); + return get_dboard_args(0x0000, xx_type); } //verify that there is a registered constructor for this id - if (not id_to_args_map.has_key(dboard_id)){ + if (not get_id_to_args_map().has_key(dboard_id)){ throw std::runtime_error(str( boost::format("Unregistered %s dboard id: %s") % xx_type % dboard_id::to_string(dboard_id) @@ -177,7 +181,7 @@ static args_t get_dboard_args( } //return the dboard args for this id - return id_to_args_map[dboard_id]; + return get_id_to_args_map()[dboard_id]; } dboard_manager_impl::dboard_manager_impl( @@ -185,14 +189,13 @@ dboard_manager_impl::dboard_manager_impl( dboard_id_t tx_dboard_id, dboard_interface::sptr interface ){ - register_internal_dboards(); //always call first _interface = interface; - dboard_ctor_t rx_dboard_ctor; prop_names_t rx_subdevs; - boost::tie(rx_dboard_ctor, rx_subdevs) = get_dboard_args(rx_dboard_id, "rx"); + dboard_ctor_t rx_dboard_ctor; std::string rx_name; prop_names_t rx_subdevs; + boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_dboard_args(rx_dboard_id, "rx"); - dboard_ctor_t tx_dboard_ctor; prop_names_t tx_subdevs; - boost::tie(tx_dboard_ctor, tx_subdevs) = get_dboard_args(tx_dboard_id, "tx"); + dboard_ctor_t tx_dboard_ctor; std::string tx_name; prop_names_t tx_subdevs; + boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_dboard_args(tx_dboard_id, "tx"); //initialize the gpio pins before creating subdevs set_nice_gpio_pins(); @@ -200,16 +203,16 @@ dboard_manager_impl::dboard_manager_impl( //make xcvr subdevs (make one subdev for both rx and tx dboards) if (rx_dboard_ctor == tx_dboard_ctor){ ASSERT_THROW(rx_subdevs == tx_subdevs); - BOOST_FOREACH(std::string name, rx_subdevs){ + BOOST_FOREACH(std::string subdev, rx_subdevs){ dboard_base::sptr xcvr_dboard = rx_dboard_ctor( - dboard_base::ctor_args_t(name, interface, rx_dboard_id, tx_dboard_id) + dboard_base::ctor_args_t(subdev, interface, rx_dboard_id, tx_dboard_id) ); //create a rx proxy for this xcvr board - _rx_dboards[name] = subdev_proxy::sptr( + _rx_dboards[subdev] = subdev_proxy::sptr( new subdev_proxy(xcvr_dboard, subdev_proxy::RX_TYPE) ); //create a tx proxy for this xcvr board - _tx_dboards[name] = subdev_proxy::sptr( + _tx_dboards[subdev] = subdev_proxy::sptr( new subdev_proxy(xcvr_dboard, subdev_proxy::TX_TYPE) ); } @@ -218,22 +221,22 @@ dboard_manager_impl::dboard_manager_impl( //make tx and rx subdevs (separate subdevs for rx and tx dboards) else{ //make the rx subdevs - BOOST_FOREACH(std::string name, rx_subdevs){ + BOOST_FOREACH(std::string subdev, rx_subdevs){ dboard_base::sptr rx_dboard = rx_dboard_ctor( - dboard_base::ctor_args_t(name, interface, rx_dboard_id, ID_NONE) + dboard_base::ctor_args_t(subdev, interface, rx_dboard_id, ID_NONE) ); //create a rx proxy for this rx board - _rx_dboards[name] = subdev_proxy::sptr( + _rx_dboards[subdev] = subdev_proxy::sptr( new subdev_proxy(rx_dboard, subdev_proxy::RX_TYPE) ); } //make the tx subdevs - BOOST_FOREACH(std::string name, tx_subdevs){ + BOOST_FOREACH(std::string subdev, tx_subdevs){ dboard_base::sptr tx_dboard = tx_dboard_ctor( - dboard_base::ctor_args_t(name, interface, ID_NONE, tx_dboard_id) + dboard_base::ctor_args_t(subdev, interface, ID_NONE, tx_dboard_id) ); //create a tx proxy for this tx board - _tx_dboards[name] = subdev_proxy::sptr( + _tx_dboards[subdev] = subdev_proxy::sptr( new subdev_proxy(tx_dboard, subdev_proxy::TX_TYPE) ); } @@ -257,7 +260,7 @@ wax::obj dboard_manager_impl::get_rx_subdev(const std::string &subdev_name){ str(boost::format("Unknown rx subdev name %s") % subdev_name) ); //get a link to the rx subdev proxy - return wax::cast<subdev_proxy::sptr>(_rx_dboards[subdev_name])->get_link(); + return _rx_dboards[subdev_name]->get_link(); } wax::obj dboard_manager_impl::get_tx_subdev(const std::string &subdev_name){ @@ -265,7 +268,7 @@ wax::obj dboard_manager_impl::get_tx_subdev(const std::string &subdev_name){ str(boost::format("Unknown tx subdev name %s") % subdev_name) ); //get a link to the tx subdev proxy - return wax::cast<subdev_proxy::sptr>(_tx_dboards[subdev_name])->get_link(); + return _tx_dboards[subdev_name]->get_link(); } void dboard_manager_impl::set_nice_gpio_pins(void){ diff --git a/host/lib/usrp/usrp1e/usrp1e_impl.cpp b/host/lib/usrp/usrp1e/usrp1e_impl.cpp index 93265ab17..441f6d060 100644 --- a/host/lib/usrp/usrp1e/usrp1e_impl.cpp +++ b/host/lib/usrp/usrp1e/usrp1e_impl.cpp @@ -15,18 +15,23 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include <unistd.h> +#include <uhd/utils.hpp> #include <boost/format.hpp> +#include <boost/filesystem.hpp> #include "usrp1e_impl.hpp" using namespace uhd; using namespace uhd::usrp; +STATIC_BLOCK(register_usrp1e_device){ + device::register_device(&usrp1e::discover, &usrp1e::make); +} + /*********************************************************************** * Helper Functions **********************************************************************/ static bool file_exists(const std::string &file_path){ - return access(file_path.c_str(), F_OK) == 0; + return boost::filesystem::exists(file_path); } /*********************************************************************** @@ -40,7 +45,6 @@ device_addrs_t usrp1e::discover(const device_addr_t &device_addr){ if (not file_exists(device_addr["node"])) return usrp1e_addrs; device_addr_t new_addr; new_addr["name"] = "USRP1E"; - new_addr["type"] = "usrp1e"; new_addr["node"] = device_addr["node"]; usrp1e_addrs.push_back(new_addr); } @@ -52,7 +56,6 @@ device_addrs_t usrp1e::discover(const device_addr_t &device_addr){ if (not file_exists(node)) continue; device_addr_t new_addr; new_addr["name"] = "USRP1E"; - new_addr["type"] = "usrp1e"; new_addr["node"] = node; usrp1e_addrs.push_back(new_addr); } diff --git a/host/lib/usrp/usrp2/dboard_impl.cpp b/host/lib/usrp/usrp2/dboard_impl.cpp index 32c64f541..6d957436e 100644 --- a/host/lib/usrp/usrp2/dboard_impl.cpp +++ b/host/lib/usrp/usrp2/dboard_impl.cpp @@ -16,6 +16,7 @@ // #include <uhd/utils.hpp> +#include <boost/format.hpp> #include "usrp2_impl.hpp" #include "dboard_interface.hpp" @@ -70,7 +71,7 @@ void usrp2_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){ boost::tie(key, name) = extract_named_prop(key_); //handle the get request conditioned on the key - switch(wax::cast<dboard_prop_t>(key)){ + switch(key.as<dboard_prop_t>()){ case DBOARD_PROP_NAME: val = std::string("usrp2 dboard (rx unit)"); return; @@ -83,8 +84,8 @@ void usrp2_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){ val = _dboard_manager->get_rx_subdev_names(); return; - case DBOARD_PROP_CODEC: - throw std::runtime_error("unhandled prop in usrp2 dboard"); + //case DBOARD_PROP_CODEC: + // throw std::runtime_error("unhandled prop in usrp2 dboard"); } } @@ -100,7 +101,7 @@ void usrp2_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){ boost::tie(key, name) = extract_named_prop(key_); //handle the get request conditioned on the key - switch(wax::cast<dboard_prop_t>(key)){ + switch(key.as<dboard_prop_t>()){ case DBOARD_PROP_NAME: val = std::string("usrp2 dboard (tx unit)"); return; @@ -113,8 +114,8 @@ void usrp2_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){ val = _dboard_manager->get_tx_subdev_names(); return; - case DBOARD_PROP_CODEC: - throw std::runtime_error("unhandled prop in usrp2 dboard"); + //case DBOARD_PROP_CODEC: + // throw std::runtime_error("unhandled prop in usrp2 dboard"); } } diff --git a/host/lib/usrp/usrp2/dsp_impl.cpp b/host/lib/usrp/usrp2/dsp_impl.cpp index 22c00d99a..7520c1757 100644 --- a/host/lib/usrp/usrp2/dsp_impl.cpp +++ b/host/lib/usrp/usrp2/dsp_impl.cpp @@ -16,11 +16,15 @@ // #include <uhd/utils.hpp> +#include <boost/format.hpp> #include <boost/assign/list_of.hpp> #include "usrp2_impl.hpp" using namespace uhd; +static const size_t default_decim = 16; +static const size_t default_interp = 16; + /*********************************************************************** * DDC Helper Methods **********************************************************************/ @@ -36,6 +40,10 @@ static uint32_t calculate_freq_word_and_update_actual_freq(freq_t &freq, freq_t return freq_word; } +static uint32_t calculate_iq_scale_word(int16_t i, int16_t q){ + return (uint16_t(i) << 16) | (uint16_t(q) << 0); +} + void usrp2_impl::init_ddc_config(void){ //create the ddc in the rx dsp dict _rx_dsps["ddc0"] = wax_obj_proxy( @@ -44,7 +52,7 @@ void usrp2_impl::init_ddc_config(void){ ); //initial config and update - _ddc_decim = 16; + _ddc_decim = default_decim; _ddc_freq = 0; update_ddc_config(); @@ -61,6 +69,10 @@ void usrp2_impl::update_ddc_config(void){ calculate_freq_word_and_update_actual_freq(_ddc_freq, get_master_clock_freq()) ); out_data.data.ddc_args.decim = htonl(_ddc_decim); + static const uint32_t default_rx_scale_iq = 1024; + out_data.data.ddc_args.scale_iq = htonl( + calculate_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq) + ); //send and recv usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); @@ -74,6 +86,7 @@ void usrp2_impl::update_ddc_enabled(void){ out_data.data.streaming.enabled = (_ddc_enabled)? 1 : 0; out_data.data.streaming.secs = htonl(_ddc_stream_at.secs); out_data.data.streaming.ticks = htonl(_ddc_stream_at.ticks); + out_data.data.streaming.samples = htonl(_max_rx_samples_per_packet); //send and recv usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); @@ -89,7 +102,7 @@ void usrp2_impl::update_ddc_enabled(void){ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){ //handle the case where the key is an expected dsp property if (key.type() == typeid(dsp_prop_t)){ - switch(wax::cast<dsp_prop_t>(key)){ + switch(key.as<dsp_prop_t>()){ case DSP_PROP_NAME: val = std::string("usrp2 ddc0"); return; @@ -98,7 +111,7 @@ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){ prop_names_t others = boost::assign::list_of ("rate") ("decim") - ("decim_rates") + ("decims") ("freq") ("enabled") ("stream_at") @@ -110,7 +123,7 @@ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){ } //handle string-based properties specific to this dsp - std::string key_name = wax::cast<std::string>(key); + std::string key_name = key.as<std::string>(); if (key_name == "rate"){ val = get_master_clock_freq(); return; @@ -119,7 +132,7 @@ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){ val = _ddc_decim; return; } - else if (key_name == "decim_rates"){ + else if (key_name == "decims"){ val = _allowed_decim_and_interp_rates; return; } @@ -139,20 +152,19 @@ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){ void usrp2_impl::ddc_set(const wax::obj &key, const wax::obj &val){ //handle string-based properties specific to this dsp - std::string key_name = wax::cast<std::string>(key); + std::string key_name = key.as<std::string>(); if (key_name == "decim"){ - size_t new_decim = wax::cast<size_t>(val); - ASSERT_THROW(std::has( - _allowed_decim_and_interp_rates.begin(), - _allowed_decim_and_interp_rates.end(), - new_decim - )); + size_t new_decim = val.as<size_t>(); + assert_has( + _allowed_decim_and_interp_rates, + new_decim, "usrp2 decimation" + ); _ddc_decim = new_decim; //shadow update_ddc_config(); return; } else if (key_name == "freq"){ - freq_t new_freq = wax::cast<freq_t>(val); + freq_t new_freq = val.as<freq_t>(); ASSERT_THROW(new_freq <= get_master_clock_freq()/2.0); ASSERT_THROW(new_freq >= -get_master_clock_freq()/2.0); _ddc_freq = new_freq; //shadow @@ -160,13 +172,13 @@ void usrp2_impl::ddc_set(const wax::obj &key, const wax::obj &val){ return; } else if (key_name == "enabled"){ - bool new_enabled = wax::cast<bool>(val); + bool new_enabled = val.as<bool>(); _ddc_enabled = new_enabled; //shadow update_ddc_enabled(); return; } else if (key_name == "stream_at"){ - time_spec_t new_stream_at = wax::cast<time_spec_t>(val); + time_spec_t new_stream_at = val.as<time_spec_t>(); _ddc_stream_at = new_stream_at; //shadow //update_ddc_enabled(); //dont update from here return; @@ -188,7 +200,7 @@ void usrp2_impl::init_duc_config(void){ ); //initial config and update - _duc_interp = 16; + _duc_interp = default_interp; _duc_freq = 0; update_duc_config(); } @@ -209,7 +221,9 @@ void usrp2_impl::update_duc_config(void){ calculate_freq_word_and_update_actual_freq(_duc_freq, get_master_clock_freq()) ); out_data.data.duc_args.interp = htonl(_duc_interp); - out_data.data.duc_args.scale_iq = htonl(scale); + out_data.data.duc_args.scale_iq = htonl( + calculate_iq_scale_word(scale, scale) + ); //send and recv usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); @@ -222,7 +236,7 @@ void usrp2_impl::update_duc_config(void){ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){ //handle the case where the key is an expected dsp property if (key.type() == typeid(dsp_prop_t)){ - switch(wax::cast<dsp_prop_t>(key)){ + switch(key.as<dsp_prop_t>()){ case DSP_PROP_NAME: val = std::string("usrp2 duc0"); return; @@ -231,7 +245,7 @@ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){ prop_names_t others = boost::assign::list_of ("rate") ("interp") - ("interp_rates") + ("interps") ("freq") ; val = others; @@ -241,7 +255,7 @@ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){ } //handle string-based properties specific to this dsp - std::string key_name = wax::cast<std::string>(key); + std::string key_name = key.as<std::string>(); if (key_name == "rate"){ val = get_master_clock_freq(); return; @@ -250,7 +264,7 @@ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){ val = _duc_interp; return; } - else if (key_name == "interp_rates"){ + else if (key_name == "interps"){ val = _allowed_decim_and_interp_rates; return; } @@ -266,20 +280,19 @@ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){ void usrp2_impl::duc_set(const wax::obj &key, const wax::obj &val){ //handle string-based properties specific to this dsp - std::string key_name = wax::cast<std::string>(key); + std::string key_name = key.as<std::string>(); if (key_name == "interp"){ - size_t new_interp = wax::cast<size_t>(val); - ASSERT_THROW(std::has( - _allowed_decim_and_interp_rates.begin(), - _allowed_decim_and_interp_rates.end(), - new_interp - )); + size_t new_interp = val.as<size_t>(); + assert_has( + _allowed_decim_and_interp_rates, + new_interp, "usrp2 interpolation" + ); _duc_interp = new_interp; //shadow update_duc_config(); return; } else if (key_name == "freq"){ - freq_t new_freq = wax::cast<freq_t>(val); + freq_t new_freq = val.as<freq_t>(); ASSERT_THROW(new_freq <= get_master_clock_freq()/2.0); ASSERT_THROW(new_freq >= -get_master_clock_freq()/2.0); _duc_freq = new_freq; //shadow diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 3def8ddaa..10c1ef8cf 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -27,6 +27,10 @@ extern "C" { #endif +// size of the vrt header and trailer to the host +#define USRP2_HOST_RX_VRT_HEADER_WORDS32 5 +#define USRP2_HOST_RX_VRT_TRAILER_WORDS32 1 //FIXME fpga sets wrong header size when no trailer present + // udp ports for the usrp2 communication // Dynamic and/or private ports: 49152-65535 #define USRP2_UDP_CTRL_PORT 49152 @@ -87,6 +91,9 @@ typedef enum{ USRP2_CTRL_ID_SETUP_THIS_DUC_FOR_ME_BRO, USRP2_CTRL_ID_TOTALLY_SETUP_THE_DUC_DUDE, + USRP2_CTRL_ID_GOT_A_NEW_TIME_FOR_YOU_BRO, + USRP2_CTRL_ID_SWEET_I_GOT_THAT_TIME_DUDE, + USRP2_CTRL_ID_PEACE_OUT } usrp2_ctrl_id_t; @@ -168,18 +175,25 @@ typedef struct{ struct { uint32_t freq_word; uint32_t decim; + uint32_t scale_iq; } ddc_args; struct { uint8_t enabled; uint8_t _pad[3]; uint32_t secs; uint32_t ticks; + uint32_t samples; } streaming; struct { uint32_t freq_word; uint32_t interp; uint32_t scale_iq; } duc_args; + struct { + uint32_t secs; + uint32_t ticks; + uint8_t now; + } time_args; } data; } usrp2_ctrl_data_t; diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 43334ddc6..cc7746720 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -16,12 +16,13 @@ // #include <complex> -#include <boost/shared_array.hpp> #include <boost/format.hpp> #include "usrp2_impl.hpp" using namespace uhd; using namespace uhd::usrp; +using namespace uhd::transport; +namespace asio = boost::asio; /*********************************************************************** * Constants @@ -29,214 +30,228 @@ using namespace uhd::usrp; typedef std::complex<float> fc32_t; typedef std::complex<int16_t> sc16_t; -static const float float_scale_factor = pow(2.0, 15); - -//max length with header, stream id, seconds, fractional seconds -static const size_t max_vrt_header_words = 5; +static const float shorts_per_float = pow(2.0, 15); +static const float floats_per_short = 1.0/shorts_per_float; /*********************************************************************** * Helper Functions **********************************************************************/ -static inline void host_floats_to_usrp2_shorts( - int16_t *usrp2_shorts, - const float *host_floats, - size_t num_samps -){ - for(size_t i = 0; i < num_samps; i++){ - usrp2_shorts[i] = htons(int16_t(host_floats[i]*float_scale_factor)); - } +void usrp2_impl::io_init(void){ + //initially empty copy buffer + _rx_copy_buff = asio::buffer("", 0); + + //send a small data packet so the usrp2 knows the udp source port + uint32_t zero_data = 0; + _data_transport->send(asio::buffer(&zero_data, sizeof(zero_data))); +} + +#define unrolled_loop(__i, __len, __inst) {\ + size_t __i = 0; \ + while(__i < (__len & ~0x7)){ \ + __inst; __i++; __inst; __i++; \ + __inst; __i++; __inst; __i++; \ + __inst; __i++; __inst; __i++; \ + __inst; __i++; __inst; __i++; \ + } \ + while(__i < __len){ \ + __inst; __i++;\ + } \ } -static inline void usrp2_shorts_to_host_floats( - float *host_floats, - const int16_t *usrp2_shorts, +// set a boolean flag that indicates the endianess +#ifdef HAVE_BIG_ENDIAN +static const bool is_big_endian = true; +#else +static const bool is_big_endian = false; +#endif + +static inline void host_floats_to_usrp2_items( + uint32_t *usrp2_items, + const fc32_t *host_floats, size_t num_samps ){ - for(size_t i = 0; i < num_samps; i++){ - host_floats[i] = float(ntohs(usrp2_shorts[i])/float_scale_factor); - } + unrolled_loop(i, num_samps,{ + uint16_t real = host_floats[i].real()*shorts_per_float; + uint16_t imag = host_floats[i].imag()*shorts_per_float; + usrp2_items[i] = htonl((real << 16) | (imag << 0)); + }); } -static inline void host_shorts_to_usrp2_shorts( - int16_t *usrp2_shorts, - const int16_t *host_shorts, +static inline void usrp2_items_to_host_floats( + fc32_t *host_floats, + const uint32_t *usrp2_items, size_t num_samps ){ - for(size_t i = 0; i < num_samps; i++){ - usrp2_shorts[i] = htons(host_shorts[i]); - } + unrolled_loop(i, num_samps,{ + uint32_t item = ntohl(usrp2_items[i]); + int16_t real = item >> 16; + int16_t imag = item >> 0; + host_floats[i] = fc32_t(real*floats_per_short, imag*floats_per_short); + }); } -static inline void usrp2_shorts_to_host_shorts( - int16_t *host_shorts, - const int16_t *usrp2_shorts, +static inline void host_items_to_usrp2_items( + uint32_t *usrp2_items, + const uint32_t *host_items, size_t num_samps ){ - for(size_t i = 0; i < num_samps; i++){ - host_shorts[i] = ntohs(usrp2_shorts[i]); + if (is_big_endian){ + std::memcpy(usrp2_items, host_items, num_samps*sizeof(uint32_t)); + } + else{ + unrolled_loop(i, num_samps, usrp2_items[i] = htonl(host_items[i])); } } -/*********************************************************************** - * Send Raw Data - **********************************************************************/ -size_t usrp2_impl::send_raw( - const boost::asio::const_buffer &buff, - const uhd::metadata_t &metadata +static inline void usrp2_items_to_host_items( + uint32_t *host_items, + const uint32_t *usrp2_items, + size_t num_samps ){ - std::vector<boost::asio::const_buffer> buffs(2); - uint32_t vrt_hdr[max_vrt_header_words]; - uint32_t vrt_hdr_flags = 0; - size_t num_vrt_hdr_words = 1; - - //load the vrt header and flags - if(metadata.has_stream_id){ - vrt_hdr_flags |= (0x1 << 28); //IF Data packet with Stream Identifier - vrt_hdr[num_vrt_hdr_words++] = htonl(metadata.stream_id); + if (is_big_endian){ + std::memcpy(host_items, usrp2_items, num_samps*sizeof(uint32_t)); } - if(metadata.has_time_spec){ - vrt_hdr_flags |= (0x3 << 22) | (0x1 << 20); //TSI: Other, TSF: Sample Count Timestamp - vrt_hdr[num_vrt_hdr_words++] = htonl(metadata.time_spec.secs); - vrt_hdr[num_vrt_hdr_words++] = htonl(metadata.time_spec.ticks); - vrt_hdr[num_vrt_hdr_words++] = 0; //unused part of fractional seconds + else{ + unrolled_loop(i, num_samps, host_items[i] = ntohl(usrp2_items[i])); } - vrt_hdr_flags |= (metadata.start_of_burst)? (0x1 << 25) : 0; - vrt_hdr_flags |= (metadata.end_of_burst)? (0x1 << 24) : 0; - - //fill in complete header word - vrt_hdr[0] = htonl(vrt_hdr_flags | - ((_stream_id_to_packet_seq[metadata.stream_id]++ & 0xf) << 16) | - ((boost::asio::buffer_size(buff)/sizeof(uint32_t)) & 0xffff) - ); - - //load the buffer vector - size_t vrt_hdr_size = num_vrt_hdr_words*sizeof(uint32_t); - buffs[0] = boost::asio::buffer(&vrt_hdr, vrt_hdr_size); - buffs[1] = buff; - - //send and return number of samples - return (_data_transport->send(buffs) - vrt_hdr_size)/sizeof(sc16_t); } /*********************************************************************** * Receive Raw Data **********************************************************************/ -size_t usrp2_impl::recv_raw( - const boost::asio::mutable_buffer &buff, - uhd::metadata_t &metadata -){ - //load the buffer vector - std::vector<boost::asio::mutable_buffer> buffs(2); - uint32_t vrt_hdr[max_vrt_header_words]; - buffs[0] = boost::asio::buffer(vrt_hdr, max_vrt_header_words); - buffs[1] = buff; +void usrp2_impl::recv_raw(rx_metadata_t &metadata){ + //do a receive + _rx_smart_buff = _data_transport->recv(); - //receive into the buffers - size_t bytes_recvd = _data_transport->recv(buffs); + //unpack the vrt header + size_t num_packet_words32 = asio::buffer_size(_rx_smart_buff->get())/sizeof(uint32_t); + if (num_packet_words32 == 0){ + _rx_copy_buff = boost::asio::buffer("", 0); + return; //must exit here after setting the buffer + } + const uint32_t *vrt_hdr = asio::buffer_cast<const uint32_t *>(_rx_smart_buff->get()); + size_t num_header_words32_out, num_payload_words32_out, packet_count_out; + try{ + vrt::unpack( + metadata, //output + vrt_hdr, //input + num_header_words32_out, //output + num_payload_words32_out, //output + num_packet_words32, //input + packet_count_out //output + ); + }catch(const std::exception &e){ + std::cerr << "bad vrt header: " << e.what() << std::endl; + _rx_copy_buff = boost::asio::buffer("", 0); + return; //must exit here after setting the buffer + } - //failure case - if (bytes_recvd < max_vrt_header_words*sizeof(uint32_t)) return 0; + //handle the packet count / sequence number + size_t expected_packet_count = _rx_stream_id_to_packet_seq[metadata.stream_id]; + if (packet_count_out != expected_packet_count){ + std::cerr << "bad packet count: " << packet_count_out << std::endl; + } + _rx_stream_id_to_packet_seq[metadata.stream_id] = (packet_count_out+1)%16; - //unpack the vrt header - metadata = uhd::metadata_t(); - uint32_t vrt_header = ntohl(vrt_hdr[0]); - metadata.has_stream_id = true; - metadata.stream_id = ntohl(vrt_hdr[1]); - metadata.has_time_spec = true; - metadata.time_spec.secs = ntohl(vrt_hdr[2]); - metadata.time_spec.ticks = ntohl(vrt_hdr[3]); - - //return the number of samples received - size_t num_words = vrt_header & 0xffff; - return (num_words*sizeof(uint32_t))/sizeof(sc16_t); + //setup the rx buffer to point to the data + _rx_copy_buff = asio::buffer( + vrt_hdr + num_header_words32_out, + num_payload_words32_out*sizeof(uint32_t) + ); } /*********************************************************************** * Send Data **********************************************************************/ size_t usrp2_impl::send( - const boost::asio::const_buffer &buff, - const uhd::metadata_t &metadata, + const asio::const_buffer &buff, + const tx_metadata_t &metadata, const std::string &type ){ - if (type == "fc32"){ - size_t num_samps = boost::asio::buffer_size(buff)/sizeof(fc32_t); - boost::shared_array<sc16_t> raw_mem(new sc16_t[num_samps]); - boost::asio::mutable_buffer raw_buff(raw_mem.get(), num_samps*sizeof(sc16_t)); - - host_floats_to_usrp2_shorts( - boost::asio::buffer_cast<int16_t*>(raw_buff), - boost::asio::buffer_cast<const float*>(buff), - num_samps*2 //double for complex - ); - - return send_raw(raw_buff, metadata); + uint32_t tx_mem[_mtu/sizeof(uint32_t)]; + uint32_t *items = tx_mem + vrt::max_header_words32; //offset for data + size_t num_samps = _max_tx_samples_per_packet; + + //calculate the number of samples to be copied + //and copy the samples into the send buffer + if (type == "32fc"){ + num_samps = std::min(asio::buffer_size(buff)/sizeof(fc32_t), num_samps); + host_floats_to_usrp2_items(items, asio::buffer_cast<const fc32_t*>(buff), num_samps); + } + else if (type == "16sc"){ + num_samps = std::min(asio::buffer_size(buff)/sizeof(sc16_t), num_samps); + host_items_to_usrp2_items(items, asio::buffer_cast<const uint32_t*>(buff), num_samps); + } + else{ + throw std::runtime_error(str(boost::format("usrp2 send: cannot handle type \"%s\"") % type)); } - if (type == "sc16"){ - #ifdef HAVE_BIG_ENDIAN - return send_raw(buff, metadata); - #else - size_t num_samps = boost::asio::buffer_size(buff)/sizeof(sc16_t); - boost::shared_array<sc16_t> raw_mem(new sc16_t[num_samps]); - boost::asio::mutable_buffer raw_buff(raw_mem.get(), num_samps*sizeof(sc16_t)); - - host_shorts_to_usrp2_shorts( - boost::asio::buffer_cast<int16_t*>(raw_buff), - boost::asio::buffer_cast<const int16_t*>(buff), - num_samps*2 //double for complex - ); + uint32_t vrt_hdr[vrt::max_header_words32]; + size_t num_header_words32, num_packet_words32; + size_t packet_count = _tx_stream_id_to_packet_seq[metadata.stream_id]++; + + //pack metadata into a vrt header + vrt::pack( + metadata, //input + vrt_hdr, //output + num_header_words32, //output + num_samps, //input + num_packet_words32, //output + packet_count //input + ); - return send_raw(raw_buff, metadata); - #endif - } + //copy in the vrt header (yes we left space) + items -= num_header_words32; + std::memcpy(items, vrt_hdr, num_header_words32*sizeof(uint32_t)); - throw std::runtime_error(str(boost::format("usrp2 send: cannot handle type \"%s\"") % type)); + //send and return number of samples + _data_transport->send(asio::buffer(items, num_packet_words32*sizeof(uint32_t))); + return num_samps; } /*********************************************************************** * Receive Data **********************************************************************/ size_t usrp2_impl::recv( - const boost::asio::mutable_buffer &buff, - uhd::metadata_t &metadata, + const asio::mutable_buffer &buff, + rx_metadata_t &metadata, const std::string &type ){ - if (type == "fc32"){ - size_t num_samps = boost::asio::buffer_size(buff)/sizeof(fc32_t); - boost::shared_array<sc16_t> raw_mem(new sc16_t[num_samps]); - boost::asio::mutable_buffer raw_buff(raw_mem.get(), num_samps*sizeof(sc16_t)); - - num_samps = recv_raw(raw_buff, metadata); - - usrp2_shorts_to_host_floats( - boost::asio::buffer_cast<float*>(buff), - boost::asio::buffer_cast<const int16_t*>(raw_buff), - num_samps*2 //double for complex - ); - - return num_samps; + //perform a receive if no rx data is waiting to be copied + if (asio::buffer_size(_rx_copy_buff) == 0){ + recv_raw(metadata); + } + //otherwise flag the metadata to show that is is a fragment + else{ + metadata = rx_metadata_t(); + metadata.is_fragment = true; } - if (type == "sc16"){ - #ifdef HAVE_BIG_ENDIAN - return recv_raw(buff, metadata); - #else - size_t num_samps = boost::asio::buffer_size(buff)/sizeof(sc16_t); - boost::shared_array<sc16_t> raw_mem(new sc16_t[num_samps]); - boost::asio::mutable_buffer raw_buff(raw_mem.get(), num_samps*sizeof(sc16_t)); - - num_samps = recv_raw(raw_buff, metadata); - - usrp2_shorts_to_host_shorts( - boost::asio::buffer_cast<int16_t*>(buff), - boost::asio::buffer_cast<const int16_t*>(raw_buff), - num_samps*2 //double for complex - ); - - return num_samps; - #endif + //extract the number of samples available to copy + //and a pointer into the usrp2 received items memory + size_t bytes_to_copy = asio::buffer_size(_rx_copy_buff); + if (bytes_to_copy == 0) return 0; //nothing to receive + size_t num_samps = bytes_to_copy/sizeof(uint32_t); + const uint32_t *items = asio::buffer_cast<const uint32_t*>(_rx_copy_buff); + + //calculate the number of samples to be copied + //and copy the samples from the recv buffer + if (type == "32fc"){ + num_samps = std::min(asio::buffer_size(buff)/sizeof(fc32_t), num_samps); + usrp2_items_to_host_floats(asio::buffer_cast<fc32_t*>(buff), items, num_samps); + } + else if (type == "16sc"){ + num_samps = std::min(asio::buffer_size(buff)/sizeof(sc16_t), num_samps); + usrp2_items_to_host_items(asio::buffer_cast<uint32_t*>(buff), items, num_samps); } + else{ + throw std::runtime_error(str(boost::format("usrp2 recv: cannot handle type \"%s\"") % type)); + } + + //update the rx copy buffer to reflect the bytes copied + _rx_copy_buff = asio::buffer( + items + num_samps, bytes_to_copy - num_samps*sizeof(uint32_t) + ); - throw std::runtime_error(str(boost::format("usrp2 recv: cannot handle type \"%s\"") % type)); + return num_samps; } diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index cc73b229c..4b15c7f3e 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -16,6 +16,7 @@ // #include <uhd/utils.hpp> +#include <boost/assign/list_of.hpp> #include "usrp2_impl.hpp" using namespace uhd; @@ -28,6 +29,10 @@ void usrp2_impl::mboard_init(void){ boost::bind(&usrp2_impl::mboard_get, this, _1, _2), boost::bind(&usrp2_impl::mboard_set, this, _1, _2) ); + + //set the time on the usrp2 as close as possible to the system utc time + boost::posix_time::ptime now(boost::posix_time::microsec_clock::universal_time()); + set_time_spec(time_spec_t(now, get_master_clock_freq()), true); } void usrp2_impl::init_clock_config(void){ @@ -64,6 +69,19 @@ void usrp2_impl::update_clock_config(void){ ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_GOT_THE_NEW_CLOCK_CONFIG_DUDE); } +void usrp2_impl::set_time_spec(const time_spec_t &time_spec, bool now){ + //setup the out data + usrp2_ctrl_data_t out_data; + out_data.id = htonl(USRP2_CTRL_ID_GOT_A_NEW_TIME_FOR_YOU_BRO); + out_data.data.time_args.secs = htonl(time_spec.secs); + out_data.data.time_args.ticks = htonl(time_spec.ticks); + out_data.data.time_args.now = (now)? 1 : 0; + + //send and recv + usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); + ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_SWEET_I_GOT_THAT_TIME_DUDE); +} + /*********************************************************************** * MBoard Get Properties **********************************************************************/ @@ -71,17 +89,54 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){ wax::obj key; std::string name; boost::tie(key, name) = extract_named_prop(key_); + //handle the other props + if (key.type() == typeid(std::string)){ + if (key.as<std::string>() == "mac-addr"){ + //setup the out data + usrp2_ctrl_data_t out_data; + out_data.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_MAC_ADDR_BRO); + + //send and recv + usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); + ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_MAC_ADDR_DUDE); + + //extract the address + val = reinterpret_cast<mac_addr_t*>(in_data.data.mac_addr)->to_string(); + return; + } + + if (key.as<std::string>() == "ip-addr"){ + //setup the out data + usrp2_ctrl_data_t out_data; + out_data.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_IP_ADDR_BRO); + + //send and recv + usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); + ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE); + + //extract the address + val = boost::asio::ip::address_v4(ntohl(in_data.data.ip_addr)).to_string(); + return; + } + } + //handle the get request conditioned on the key - switch(wax::cast<mboard_prop_t>(key)){ + switch(key.as<mboard_prop_t>()){ case MBOARD_PROP_NAME: val = std::string("usrp2 mboard"); return; - case MBOARD_PROP_OTHERS: - val = prop_names_t(); //empty other props + case MBOARD_PROP_OTHERS:{ + prop_names_t others = boost::assign::list_of + ("mac-addr") + ("ip-addr") + ; + val = others; + } return; case MBOARD_PROP_RX_DBOARD: + ASSERT_THROW(_rx_dboards.has_key(name)); val = _rx_dboards[name].get_link(); return; @@ -90,6 +145,7 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){ return; case MBOARD_PROP_TX_DBOARD: + ASSERT_THROW(_tx_dboards.has_key(name)); val = _tx_dboards[name].get_link(); return; @@ -97,17 +153,12 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){ val = prop_names_t(_tx_dboards.get_keys()); return; - case MBOARD_PROP_MTU: - // FIXME we dont know the real MTU... - // give them something to fragment about - val = size_t(1500); - return; - case MBOARD_PROP_CLOCK_RATE: val = freq_t(get_master_clock_freq()); return; case MBOARD_PROP_RX_DSP: + ASSERT_THROW(_rx_dsps.has_key(name)); val = _rx_dsps[name].get_link(); return; @@ -116,6 +167,7 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){ return; case MBOARD_PROP_TX_DSP: + ASSERT_THROW(_tx_dsps.has_key(name)); val = _tx_dsps[name].get_link(); return; @@ -154,36 +206,73 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){ * MBoard Set Properties **********************************************************************/ void usrp2_impl::mboard_set(const wax::obj &key, const wax::obj &val){ + //handle the other props + if (key.type() == typeid(std::string)){ + if (key.as<std::string>() == "mac-addr"){ + //setup the out data + usrp2_ctrl_data_t out_data; + out_data.id = htonl(USRP2_CTRL_ID_HERE_IS_A_NEW_MAC_ADDR_BRO); + mac_addr_t mac_addr(val.as<std::string>()); + std::memcpy(out_data.data.mac_addr, &mac_addr, sizeof(mac_addr_t)); + + //send and recv + usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); + ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_MAC_ADDR_DUDE); + return; + } + + if (key.as<std::string>() == "ip-addr"){ + //setup the out data + usrp2_ctrl_data_t out_data; + out_data.id = htonl(USRP2_CTRL_ID_HERE_IS_A_NEW_IP_ADDR_BRO); + out_data.data.ip_addr = htonl(boost::asio::ip::address_v4::from_string(val.as<std::string>()).to_ulong()); + + //send and recv + usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); + ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE); + return; + } + } + //handle the get request conditioned on the key - switch(wax::cast<mboard_prop_t>(key)){ + switch(key.as<mboard_prop_t>()){ case MBOARD_PROP_PPS_SOURCE:{ - std::string name = wax::cast<std::string>(val); - ASSERT_THROW(_pps_source_dict.has_key(name)); + std::string name = val.as<std::string>(); + assert_has(_pps_source_dict.get_keys(), name, "usrp2 pps source"); _pps_source = name; //shadow update_clock_config(); } return; case MBOARD_PROP_PPS_POLARITY:{ - std::string name = wax::cast<std::string>(val); - ASSERT_THROW(_pps_polarity_dict.has_key(name)); + std::string name = val.as<std::string>(); + assert_has(_pps_polarity_dict.get_keys(), name, "usrp2 pps polarity"); _pps_polarity = name; //shadow update_clock_config(); } return; case MBOARD_PROP_REF_SOURCE:{ - std::string name = wax::cast<std::string>(val); - ASSERT_THROW(_ref_source_dict.has_key(name)); + std::string name = val.as<std::string>(); + assert_has(_ref_source_dict.get_keys(), name, "usrp2 reference source"); _ref_source = name; //shadow update_clock_config(); } return; + case MBOARD_PROP_TIME_NOW:{ + set_time_spec(val.as<time_spec_t>(), true); + return; + } + + case MBOARD_PROP_TIME_NEXT_PPS:{ + set_time_spec(val.as<time_spec_t>(), false); + return; + } + case MBOARD_PROP_NAME: case MBOARD_PROP_OTHERS: - case MBOARD_PROP_MTU: case MBOARD_PROP_CLOCK_RATE: case MBOARD_PROP_RX_DSP: case MBOARD_PROP_RX_DSP_NAMES: @@ -195,8 +284,6 @@ void usrp2_impl::mboard_set(const wax::obj &key, const wax::obj &val){ case MBOARD_PROP_TX_DBOARD_NAMES: case MBOARD_PROP_PPS_SOURCE_NAMES: case MBOARD_PROP_REF_SOURCE_NAMES: - case MBOARD_PROP_TIME_NOW: - case MBOARD_PROP_TIME_NEXT_PPS: throw std::runtime_error("Error: trying to set read-only property on usrp2 mboard"); } diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 5aa5d6e8d..e1371a094 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -23,6 +23,12 @@ using namespace uhd; using namespace uhd::usrp; +using namespace uhd::transport; +namespace asio = boost::asio; + +STATIC_BLOCK(register_usrp2_device){ + device::register_device(&usrp2::discover, &usrp2::make); +} /*********************************************************************** * Discovery over the udp transport @@ -30,30 +36,26 @@ using namespace uhd::usrp; uhd::device_addrs_t usrp2::discover(const device_addr_t &hint){ device_addrs_t usrp2_addrs; + if (not hint.has_key("addr")) return usrp2_addrs; + //create a udp transport to communicate //TODO if an addr is not provided, search all interfaces? std::string ctrl_port = boost::lexical_cast<std::string>(USRP2_UDP_CTRL_PORT); - transport::udp::sptr udp_transport = \ - transport::udp::make(hint["addr"], ctrl_port, true); + udp_simple::sptr udp_transport = udp_simple::make_broadcast( + hint["addr"], ctrl_port + ); //send a hello control packet usrp2_ctrl_data_t ctrl_data_out; ctrl_data_out.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_IP_ADDR_BRO); udp_transport->send(boost::asio::buffer(&ctrl_data_out, sizeof(ctrl_data_out))); - //loop and recieve until the time is up - size_t num_timeouts = 0; + //loop and recieve until the timeout while(true){ usrp2_ctrl_data_t ctrl_data_in; - size_t len = udp_transport->recv( - boost::asio::buffer(&ctrl_data_in, sizeof(ctrl_data_in)) - ); + size_t len = udp_transport->recv(asio::buffer(&ctrl_data_in, sizeof(ctrl_data_in))); //std::cout << len << "\n"; - if (len < sizeof(usrp2_ctrl_data_t)){ - //sleep a little so we dont burn cpu - if (num_timeouts++ > 50) break; - boost::this_thread::sleep(boost::posix_time::milliseconds(1)); - }else{ + if (len >= sizeof(usrp2_ctrl_data_t)){ //handle the received data switch(ntohl(ctrl_data_in.id)){ case USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE: @@ -61,12 +63,14 @@ uhd::device_addrs_t usrp2::discover(const device_addr_t &hint){ boost::asio::ip::address_v4 ip_addr(ntohl(ctrl_data_in.data.ip_addr)); device_addr_t new_addr; new_addr["name"] = "USRP2"; - new_addr["type"] = "usrp2"; + new_addr["transport"] = "udp"; new_addr["addr"] = ip_addr.to_string(); usrp2_addrs.push_back(new_addr); - break; + //dont break here, it will exit the while loop + //just continue on to the next loop iteration } } + if (len == 0) break; //timeout } return usrp2_addrs; @@ -75,16 +79,18 @@ uhd::device_addrs_t usrp2::discover(const device_addr_t &hint){ /*********************************************************************** * Make **********************************************************************/ -#define num2str(num) (boost::lexical_cast<std::string>(num)) +template <class T> std::string num2str(T num){ + return boost::lexical_cast<std::string>(num); +} device::sptr usrp2::make(const device_addr_t &device_addr){ //create a control transport - transport::udp::sptr ctrl_transport = transport::udp::make( + udp_simple::sptr ctrl_transport = udp_simple::make_connected( device_addr["addr"], num2str(USRP2_UDP_CTRL_PORT) ); //create a data transport - transport::udp::sptr data_transport = transport::udp::make( + udp_zero_copy::sptr data_transport = udp_zero_copy::make( device_addr["addr"], num2str(USRP2_UDP_DATA_PORT) ); @@ -98,8 +104,8 @@ device::sptr usrp2::make(const device_addr_t &device_addr){ * Structors **********************************************************************/ usrp2_impl::usrp2_impl( - transport::udp::sptr ctrl_transport, - transport::udp::sptr data_transport + udp_simple::sptr ctrl_transport, + udp_zero_copy::sptr data_transport ){ _ctrl_transport = ctrl_transport; _data_transport = data_transport; @@ -132,9 +138,8 @@ usrp2_impl::usrp2_impl( //init the tx and rx dboards (do last) dboard_init(); - //send a small data packet so the usrp2 knows the udp source port - uint32_t zero_data = 0; - _data_transport->send(boost::asio::buffer(&zero_data, sizeof(zero_data))); + //init the send and recv io + io_init(); } @@ -160,24 +165,15 @@ usrp2_ctrl_data_t usrp2_impl::ctrl_send_and_recv(const usrp2_ctrl_data_t &out_da out_copy.seq = htonl(++_ctrl_seq_num); _ctrl_transport->send(boost::asio::buffer(&out_copy, sizeof(usrp2_ctrl_data_t))); - //loop and recieve until the time is up - size_t num_timeouts = 0; + //loop until we get the packet or timeout while(true){ usrp2_ctrl_data_t in_data; - size_t len = _ctrl_transport->recv( - boost::asio::buffer(&in_data, sizeof(in_data)) - ); - if (len < sizeof(usrp2_ctrl_data_t)){ - //sleep a little so we dont burn cpu - if (num_timeouts++ > 50) break; - boost::this_thread::sleep(boost::posix_time::milliseconds(1)); - }else{ - //handle the received data - if (ntohl(in_data.seq) == _ctrl_seq_num){ - return in_data; - } - //didnt get seq, continue on... + size_t len = _ctrl_transport->recv(asio::buffer(&in_data, sizeof(in_data))); + if (len >= sizeof(usrp2_ctrl_data_t) and ntohl(in_data.seq) == _ctrl_seq_num){ + return in_data; } + if (len == 0) break; //timeout + //didnt get seq or bad packet, continue looking... } throw std::runtime_error("usrp2 no control response"); } @@ -190,18 +186,28 @@ void usrp2_impl::get(const wax::obj &key_, wax::obj &val){ boost::tie(key, name) = extract_named_prop(key_); //handle the get request conditioned on the key - switch(wax::cast<device_prop_t>(key)){ + switch(key.as<device_prop_t>()){ case DEVICE_PROP_NAME: val = std::string("usrp2 device"); return; case DEVICE_PROP_MBOARD: + ASSERT_THROW(_mboards.has_key(name)); val = _mboards[name].get_link(); return; case DEVICE_PROP_MBOARD_NAMES: val = prop_names_t(_mboards.get_keys()); return; + + case DEVICE_PROP_MAX_RX_SAMPLES: + val = size_t(_max_rx_samples_per_packet); + return; + + case DEVICE_PROP_MAX_TX_SAMPLES: + val = size_t(_max_tx_samples_per_packet); + return; + } } diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 9a4c42d42..fc713c2bf 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -22,7 +22,9 @@ #include <boost/thread.hpp> #include <boost/shared_ptr.hpp> #include <boost/function.hpp> -#include <uhd/transport/udp.hpp> +#include <uhd/transport/vrt.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/transport/udp_zero_copy.hpp> #include <uhd/usrp/dboard_manager.hpp> #include "fw_common.h" @@ -81,8 +83,8 @@ public: * \param data_transport the udp transport for data */ usrp2_impl( - uhd::transport::udp::sptr ctrl_transport, - uhd::transport::udp::sptr data_transport + uhd::transport::udp_simple::sptr ctrl_transport, + uhd::transport::udp_zero_copy::sptr data_transport ); ~usrp2_impl(void); @@ -98,18 +100,32 @@ public: double get_master_clock_freq(void); //the io interface - size_t send(const boost::asio::const_buffer &, const uhd::metadata_t &, const std::string &); - size_t recv(const boost::asio::mutable_buffer &, uhd::metadata_t &, const std::string &); + size_t send(const boost::asio::const_buffer &, const uhd::tx_metadata_t &, const std::string &); + size_t recv(const boost::asio::mutable_buffer &, uhd::rx_metadata_t &, const std::string &); private: //the raw io interface (samples are in the usrp2 native format) - size_t send_raw(const boost::asio::const_buffer &, const uhd::metadata_t &); - size_t recv_raw(const boost::asio::mutable_buffer &, uhd::metadata_t &); - uhd::dict<uint32_t, size_t> _stream_id_to_packet_seq; + void recv_raw(uhd::rx_metadata_t &); + uhd::dict<uint32_t, size_t> _tx_stream_id_to_packet_seq; + uhd::dict<uint32_t, size_t> _rx_stream_id_to_packet_seq; + static const size_t _mtu = 1500; //FIXME we have no idea + static const size_t _hdrs = (2 + 14 + 20 + 8); //size of headers (pad, eth, ip, udp) + static const size_t _max_rx_samples_per_packet = + (_mtu - _hdrs)/sizeof(uint32_t) - + USRP2_HOST_RX_VRT_HEADER_WORDS32 - + USRP2_HOST_RX_VRT_TRAILER_WORDS32 + ; + static const size_t _max_tx_samples_per_packet = + (_mtu - _hdrs)/sizeof(uint32_t) - + uhd::transport::vrt::max_header_words32 + ; + uhd::transport::smart_buffer::sptr _rx_smart_buff; + boost::asio::const_buffer _rx_copy_buff; + void io_init(void); //udp transports for control and data - uhd::transport::udp::sptr _ctrl_transport; - uhd::transport::udp::sptr _data_transport; + uhd::transport::udp_simple::sptr _ctrl_transport; + uhd::transport::udp_zero_copy::sptr _data_transport; //private vars for dealing with send/recv control uint32_t _ctrl_seq_num; @@ -119,6 +135,7 @@ private: std::string _pps_source, _pps_polarity, _ref_source; void init_clock_config(void); void update_clock_config(void); + void set_time_spec(const uhd::time_spec_t &time_spec, bool now); //mappings from clock config strings to over the wire enums uhd::dict<std::string, usrp2_pps_source_t> _pps_source_dict; diff --git a/host/lib/wax.cpp b/host/lib/wax.cpp index c08398c50..0e2e82a3a 100644 --- a/host/lib/wax.cpp +++ b/host/lib/wax.cpp @@ -36,7 +36,11 @@ public: link_args_t(const wax::obj *obj_ptr) : _obj_ptr(obj_ptr){ /* NOP */ } - wax::obj & operator()(void){ + wax::obj & operator()(void) const{ + //recursively resolve link args to get at original pointer + if (_obj_ptr->type() == typeid(link_args_t)){ + return _obj_ptr->as<link_args_t>()(); + } return *const_cast<wax::obj *>(_obj_ptr); } private: @@ -56,10 +60,10 @@ public: proxy_args_t(const wax::obj *obj_ptr, const wax::obj &key) : _key(key){ _obj_link = obj_ptr->get_link(); } - wax::obj & operator()(void){ - return wax::cast<link_args_t>(_obj_link)(); + wax::obj & operator()(void) const{ + return _obj_link.as<link_args_t>()(); } - const wax::obj & key(void){ + const wax::obj & key(void) const{ return _key; } private: @@ -90,7 +94,7 @@ wax::obj wax::obj::operator[](const obj &key){ obj val = resolve(); //check if its a special link and call if (val.type() == typeid(link_args_t)){ - return cast<link_args_t>(val)()[key]; + return val.as<link_args_t>()()[key]; } //unknown obj throw std::runtime_error("cannot use [] on non wax::obj link"); |