diff options
| author | Josh Blum <josh@joshknows.com> | 2010-03-15 17:00:04 +0000 | 
|---|---|---|
| committer | Josh Blum <josh@joshknows.com> | 2010-03-15 17:00:04 +0000 | 
| commit | 1bfb556262d12740c71e68a2a071e6e67ed2b3e7 (patch) | |
| tree | a081bfd3a61ccb42372a627be7b57ce19a3baa0d /host/lib | |
| parent | 1b965831ae7588c7879d84de4e5fbd78ca614761 (diff) | |
| parent | fc40ff2f1327d01c72c4d7dbc07a14e473251981 (diff) | |
| download | uhd-1bfb556262d12740c71e68a2a071e6e67ed2b3e7.tar.gz uhd-1bfb556262d12740c71e68a2a071e6e67ed2b3e7.tar.bz2 uhd-1bfb556262d12740c71e68a2a071e6e67ed2b3e7.zip | |
Merge branch 'master' of git@ettus.sourcerepo.com:ettus/uhd into u1e_uhd
Conflicts:
	host/apps/CMakeLists.txt
	host/lib/usrp/usrp2/usrp2_impl.cpp
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"); | 
