diff options
| author | Philip Balister <philip@opensdr.com> | 2010-10-21 14:11:29 -0400 | 
|---|---|---|
| committer | Philip Balister <philip@opensdr.com> | 2010-10-21 14:11:29 -0400 | 
| commit | 090b0dd0d38f23cd1d7c4c6a7b0317a6fdfe2b9b (patch) | |
| tree | ec8a6f261a7a722c6842e1d90fa976905b7ead09 | |
| parent | c830a0d2c491fb61c2b401db94ed0a147bca4a24 (diff) | |
| parent | c347ad973f4b51b00f66d7422cd03e8790de9be3 (diff) | |
| download | uhd-090b0dd0d38f23cd1d7c4c6a7b0317a6fdfe2b9b.tar.gz uhd-090b0dd0d38f23cd1d7c4c6a7b0317a6fdfe2b9b.tar.bz2 uhd-090b0dd0d38f23cd1d7c4c6a7b0317a6fdfe2b9b.zip | |
Merge branch 'usrp_e_mmap_b2' of ettus.sourcerepo.com:ettus/uhdpriv into usrp_e
109 files changed, 5700 insertions, 2736 deletions
| diff --git a/host/AUTHORS b/host/AUTHORS index e0775f3a1..512d4752e 100644 --- a/host/AUTHORS +++ b/host/AUTHORS @@ -24,6 +24,7 @@ Tom Tsou - ttsou@vt.edu      USRP1 host code      USRP1 firmware -Nick Foster - nick@nerdnetworks.org +Nick Foster - nick@ettus.com      LIBUSB host code      USRP1 host code +    TVRX host code diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index 78948d9bd..e5ce78782 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -30,7 +30,7 @@ INCLUDE(${CMAKE_SOURCE_DIR}/config/CPack.cmake)  # Install Dirs  ########################################################################  SET(RUNTIME_DIR bin) -SET(LIBRARY_DIR lib) +SET(LIBRARY_DIR lib${LIB_SUFFIX})  SET(INCLUDE_DIR include)  SET(PKG_DATA_DIR share/uhd)  SET(PKG_DOC_DIR share/doc/uhd) @@ -61,6 +61,10 @@ ENDIF(NOT CMAKE_BUILD_TYPE)  IF(CMAKE_COMPILER_IS_GNUCXX)      ADD_DEFINITIONS(-Wall)      ADD_DEFINITIONS(-Wextra) +    #causes trouble when compiling libusb1.0 on macintosh +    #comment out until mac ports libusb gets its act together +    #ADD_DEFINITIONS(-pedantic) +    #ADD_DEFINITIONS(-ansi)      #only export symbols that are declared to be part of the uhd api:      UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN)  ENDIF(CMAKE_COMPILER_IS_GNUCXX) @@ -80,7 +84,7 @@ IF(UNIX AND EXISTS "/usr/lib64")      LIST(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix  ENDIF(UNIX AND EXISTS "/usr/lib64") -SET(Boost_ADDITIONAL_VERSIONS "1.42.0" "1.42" "1.43.0" "1.43") +SET(Boost_ADDITIONAL_VERSIONS "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44")  FIND_PACKAGE(Boost ${BOOST_MIN_VERSION} REQUIRED COMPONENTS      date_time      filesystem diff --git a/host/README b/host/README index 5018ef541..cab1e0b10 100644 --- a/host/README +++ b/host/README @@ -6,7 +6,8 @@ The hardware driver for Ettus Research products.  ########################################################################  # Supported USRP Motherboards  ######################################################################## -USRP2 - udp over gigabit ethernet +USRP1 +USRP2  ########################################################################  # Supported USRP Daughterboards @@ -19,6 +20,7 @@ RFX Series  XCVR 2450  WBX Series  DBSRX +TVRX  ########################################################################  # Documentation diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt index bbb8812b0..65db3befc 100644 --- a/host/docs/CMakeLists.txt +++ b/host/docs/CMakeLists.txt @@ -25,6 +25,7 @@ SET(manual_sources      dboards.rst      general.rst      images.rst +    transport.rst      usrp1.rst      usrp2.rst  ) diff --git a/host/docs/build.rst b/host/docs/build.rst index d7dfd05e5..a41ce8331 100644 --- a/host/docs/build.rst +++ b/host/docs/build.rst @@ -40,32 +40,33 @@ CMake  ^^^^^^^^^^^^^^^^  * **Purpose:** generates project build files  * **Version:** at least 2.8 -* **Required for:** build time +* **Usage:** build time (required)  * **Download URL:** http://www.cmake.org/cmake/resources/software.html  ^^^^^^^^^^^^^^^^  Boost  ^^^^^^^^^^^^^^^^  * **Purpose:** C++ library -* **Version:** at least 3.6 unix, at least 4.0 windows -* **Required for:** build time + run time +* **Version:** at least 1.36 unix, at least 1.40 windows +* **Usage:** build time + run time (required)  * **Download URL:** http://www.boost.org/users/download/  * **Download URL (windows installer):** http://www.boostpro.com/download  ^^^^^^^^^^^^^^^^  LibUSB  ^^^^^^^^^^^^^^^^ -* **Purpose:** USB userspace library +* **Purpose:** USB-based hardware support  * **Version:** at least 1.0 -* **Required for:** build time + run time (optional) -* **Download URL:** http://www.libusb.org/ +* **Usage:** build time + run time (optional) +* **Download URL:** http://sourceforge.net/projects/libusb/files/libusb-1.0/ +* **Download URL (windows binaries):** http://www.libusb.org/wiki/windows_backend#LatestBinarySnapshots  ^^^^^^^^^^^^^^^^  Python  ^^^^^^^^^^^^^^^^  * **Purpose:** used by Cheetah and utility scripts  * **Version:** at least 2.6 -* **Required for:** build time + run time utility scripts +* **Usage:** build time + run time utility scripts (required)  * **Download URL:** http://www.python.org/download/  ^^^^^^^^^^^^^^^^ @@ -73,7 +74,7 @@ Cheetah  ^^^^^^^^^^^^^^^^  * **Purpose:** source code generation  * **Version:** at least 2.0 -* **Required for:** build time +* **Usage:** build time (required)  * **Download URL:** http://www.cheetahtemplate.org/download.html  * **Download URL (windows installer):** http://feisley.com/python/cheetah/ @@ -81,14 +82,14 @@ Cheetah  Doxygen  ^^^^^^^^^^^^^^^^  * **Purpose:** generates html api documentation -* **Required for:** build time (optional) +* **Usage:** build time (optional)  * **Download URL:** http://www.stack.nl/~dimitri/doxygen/download.html#latestsrc  ^^^^^^^^^^^^^^^^  Docutils  ^^^^^^^^^^^^^^^^  * **Purpose:** generates html user manual -* **Required for:** build time (optional) +* **Usage:** build time (optional)  * **Download URL:** http://docutils.sourceforge.net/  ------------------------------------------------------------------------ @@ -105,9 +106,16 @@ Generate Makefiles with cmake      cd build      cmake ../ -**Notes:** +Additionally, configuration variables can be passed into cmake via the command line. +The following common-use configuration variables are listed below: -* For a custom prefix, use: cmake -DCMAKE_INSTALL_PREFIX=<prefix> ../ +* For a custom install prefix: -DCMAKE_INSTALL_PREFIX=<prefix> +* To install libs into lib64: cmake -DLIB_SUFFIX=64 + +Example usage: +:: + +    cmake -DCMAKE_INSTALL_PREFIX=/opt/uhd ../  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  Build and install @@ -145,6 +153,19 @@ Generate the project with cmake  * Click generate and a project file will be created in the build directory.  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LibUSB cmake notes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +On Windows, cmake does not have the advantage of pkg-config, +so we must manually tell cmake how to locate the LibUSB header and lib. + +From the cmake gui, select "Advanded View": + +* Set LIBUSB_INCLUDE_DIR to the directory with "libusb.h". +* Set LIBUSB_LIBRARIES to the full path for "libusb-1.0.lib". + +Then check the boxes to enable USRP1 support, click configure and generate. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  Build the project in MSVC  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  * Open the generated project file in MSVC. @@ -155,14 +176,23 @@ Build the project in MSVC  **Note:** you may not have permission to build the install target.  You need to be an administrator or to run MSVC as administrator. -** alternative command line instructions ** +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Build the project in MSVC (command line) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Open the Visual Studio Command Prompt Shorcut: +:: -* Open the Visual Studio Command Prompt Shorcut -* DevEnv <uhd-repo-path>\host\build\ALL_BUILD.vcproj /Build Release -* DevEnv <uhd-repo-path>\host\build\INSTALL.vcproj /Build Release +    cd <uhd-repo-path>\host\build +    DevEnv ALL_BUILD.vcproj /Build Release +    DevEnv INSTALL.vcproj /Build Release  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  Setup the PATH environment variable  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  * Add the boost library path to %PATH% (usually c:\\program files\\boost\\<version>\\lib)  * Add the uhd library path to %PATH% (usually c:\\program files\\uhd\\lib) +* Add the libusb library to %PATH% (if using usb support) + +**Note:** +The interface for editing environment variable paths in Windows is very poor. +I recommend using "Rapid Environment Editor" (http://www.rapidee.com) over the default editor. diff --git a/host/docs/coding.rst b/host/docs/coding.rst index 23f350b0f..7533445ea 100644 --- a/host/docs/coding.rst +++ b/host/docs/coding.rst @@ -23,11 +23,11 @@ The device API provides ways to:  See the documentation in *device.hpp* for reference.  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -High-Level: The simple usrp +High-Level: The single usrp  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The goal of the simple usrp API is to wrap high level functions around the device properties. -The simple usrp provides a fat interface to access the most common properties. -The simple usrp provides ways to: +The goal of the single usrp API is to wrap high level functions around the device properties. +The single usrp provides a fat interface to access the most common properties. +The single usrp provides ways to:  * Set and get daughterboard gains.  * Set and get daughterboard antennas. @@ -38,16 +38,16 @@ The simple usrp provides ways to:  * Set the usrp time registers.  * Get the underlying device (as discussed above). -See the documentation in *usrp/simple_usrp.hpp* for reference. +See the documentation in *usrp/single_usrp.hpp* for reference.  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -High-Level: The mimo usrp +High-Level: The multi usrp  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The mimo usrp API provides a wrapper around a device that represents several motherboards. -This API provides convenience calls just like the simple usrp, +The multi usrp API provides a wrapper around a device that represents several motherboards. +This API provides convenience calls just like the single usrp,  however the calls either work across all channels in the configuration,  or take a channel argument to specify which channel to configure. -The mimo usrp provides ways to: +The multi usrp provides ways to:  * Set and get the sample rate across all channels.  * Issue a stream command across all channels. @@ -57,7 +57,7 @@ The mimo usrp provides ways to:  * Tune individual DSPs and daughterboards.  * Get the underlying device (as discussed above). -See the documentation in *usrp/mimo_usrp.hpp* for reference. +See the documentation in *usrp/multi_usrp.hpp* for reference.  ------------------------------------------------------------------------  Integrating custom hardware diff --git a/host/docs/dboards.rst b/host/docs/dboards.rst index 985fbc12b..080117651 100644 --- a/host/docs/dboards.rst +++ b/host/docs/dboards.rst @@ -15,11 +15,12 @@ properties of each board as well.  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  Basic RX and and LFRX  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The Basic RX and LFRX boards have 3 subdevices: +The Basic RX and LFRX boards have 4 subdevices:  * **Subdevice A:** real signal on antenna RXA  * **Subdevice B:** real signal on antenna RXB -* **Subdevice AB:** quadrature subdevice using both antennas +* **Subdevice AB:** quadrature subdevice using both antennas (IQ) +* **Subdevice BA:** quadrature subdevice using both antennas (QI)  The boards have no tunable elements or programmable gains.  Though the magic of aliasing, you can down-convert signals @@ -28,11 +29,12 @@ greater than the Nyquist rate of the ADC.  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  Basic TX and and LFTX  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The Basic TX and LFTX boards have 3 subdevices: +The Basic TX and LFTX boards have 4 subdevices:  * **Subdevice A:** real signal on antenna TXA  * **Subdevice B:** real signal on antenna TXB -* **Subdevice AB:** quadrature subdevice using both antennas +* **Subdevice AB:** quadrature subdevice using both antennas (IQ) +* **Subdevice BA:** quadrature subdevice using both antennas (QI)  The boards have no tunable elements or programmable gains.  Though the magic of aliasing, you can up-convert signals @@ -47,10 +49,12 @@ Receive Antennas: **J3**  The board has no user selectable antenna setting -Recieve Gains:  +Receive Gains:       **GC1**, Range: 0-56dB      **GC2**, Range: 0-24dB +Low-Pass Filter Bandwidth (Hz): 4M-33M +  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  RFX Series  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,26 +66,25 @@ The user may set the receive antenna to be TX/RX or RX2.  However, when using an RFX board in full-duplex mode,  the receive antenna will always be set to RX2, regardless of the settings. -Recieve Gains: **PGA0**, Range: 0-70dB (except RFX400 range is 0-45dB) +Receive Gains: **PGA0**, Range: 0-70dB (except RFX400 range is 0-45dB)  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  XCVR 2450  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The XCVR2450 has a non-contiguous tuning range consisting of a high band and a low band. -The high band consists of frequencies between...TODO +The XCVR2450 has a non-contiguous tuning range consisting of a  +high band (4.9-6.0GHz) and a low band (2.4-2.5GHz).  Transmit Antennas: **J1** or **J2**  Receive Antennas: **J1** or **J2** -When using the XCVR2450 in full-duplex mode, -the user must set the receive antenna and the transmit antenna to be different; -not doing so will yeild undefined results. -  The XCVR2450 uses a common LO for both receive and transmit.  Even though the API allows the RX and TX LOs to be individually set,  a change of one LO setting will be reflected in the other LO setting. +The XCVR2450 does not support full-duplex mode, attempting to operate  +in full-duplex will result in transmit-only operation. +  Transmit Gains:   * **VGA**, Range: 0-30dB   * **BB**, Range: 0-5dB @@ -90,6 +93,10 @@ Receive Gains:   * **LNA**, Range: 0-30.5dB   * **VGA**, Range: 0-62dB +Low-Pass Filter Bandwidths (Hz): + * **RX**: 7.5M, 9.5M, 14M, 18M; (each +-0, 5, or 10%) + * **TX**: 12M, 18M, 24M +  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  WBX Series  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -103,7 +110,22 @@ the receive antenna will always be set to RX2, regardless of the settings.  Transmit Gains: **PGA0**, Range: 0-25dB -Recieve Gains: **PGA0**, Range: 0-31.5dB +Receive Gains: **PGA0**, Range: 0-31.5dB + +Low-Pass Filter Bandwidths (Hz): + * **RX**: 20M (Fixed) + * **TX**: 20M (Fixed) + +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +TVRX +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Receive Antennas: RX + +Receive Gains: + * **RF**, Range: -13.3-50.3dB (frequency-dependent) + * **IF**, Range: -1.5-32.5dB + +Bandpass Filter Bandwidth: 6MHz  ------------------------------------------------------------------------  Daughterboard Modifications diff --git a/host/docs/index.rst b/host/docs/index.rst index bd55edc0b..7f8129e2d 100644 --- a/host/docs/index.rst +++ b/host/docs/index.rst @@ -20,11 +20,12 @@ Building the UHD  ^^^^^^^^^^^^^^^^^^^^^  Application Notes  ^^^^^^^^^^^^^^^^^^^^^ -* `General App Notes <./general.html>`_ +* `General Application Notes <./general.html>`_  * `Firmware and FPGA Image Notes <./images.html>`_ -* `USRP1 App Notes <./usrp1.html>`_ -* `USRP2 App Notes <./usrp2.html>`_ -* `Daughterboard App Notes <./dboards.html>`_ +* `USRP1 Application Notes <./usrp1.html>`_ +* `USRP2 Application Notes <./usrp2.html>`_ +* `Daughterboard Application Notes <./dboards.html>`_ +* `Transport Application Notes <./transport.html>`_  ^^^^^^^^^^^^^^^^^^^^^  API Documentation diff --git a/host/docs/transport.rst b/host/docs/transport.rst new file mode 100644 index 000000000..432db4bb5 --- /dev/null +++ b/host/docs/transport.rst @@ -0,0 +1,88 @@ +======================================================================== +UHD - Transport Application Notes +======================================================================== + +.. contents:: Table of Contents + +------------------------------------------------------------------------ +Introduction +------------------------------------------------------------------------ +A transport is the layer between the packet interface and a device IO interface. +The advanced user can pass optional parameters +into the underlying transport layer through the device address. +These optional parameters control how the transport object allocates memory, +resizes kernel buffers, spawns threads, etc. +When not spcified, the transport layer will use values for these parameters +that are known to perform well on a variety of systems. +The transport parameters are defined below for the various transports in the UHD: + +------------------------------------------------------------------------ +UDP transport (ASIO) +------------------------------------------------------------------------ +The UDP transport is implemented with Boost's ASIO library. +ASIO provides an asynchronous API for user-space sockets. +The transport implementation allocates a number of buffers +and submits asynchronous requests for send and receive. +IO service threads run in the background to process these requests. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Transport parameters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The following parameters can be used to alter the transport's default behavior: + +* **recv_frame_size:** The size of a single receive buffer in bytes +* **num_recv_frames:** The number of receive buffers to allocate +* **send_frame_size:** The size of a single send buffer in bytes +* **num_send_frames:** The number of send buffers to allocate +* **concurrency_hint:** The number of threads to run the IO service + +**Note:** num_send_frames and concurrency_hint will not have an effect +as the asynchronous send implementation is currently disabled. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Resize socket buffers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +It may be useful increase the size of the socket buffers to +move the burden of buffering samples into the kernel, or to +buffer incoming samples faster than they can be processed. +However, if your application cannot process samples fast enough, +no amount of buffering can save you. +The following parameters can be used to alter socket's buffer sizes: + +* **recv_buff_size:** The desired size of the receive buffer in bytes +* **send_buff_size:** The desired size of the send buffer in bytes + +**Note:** Large send buffers tend to decrease transmit performance. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Linux specific notes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +On linux, the maximum buffer sizes are capped by the sysctl values +**net.core.rmem_max** and **net.core.wmem_max**. +To change the maximum values, run the following commands: +:: + +    sudo sysctl -w net.core.rmem_max=<new value> +    sudo sysctl -w net.core.wmem_max=<new value> + +Set the values permanently by editing */etc/sysctl.conf* + +------------------------------------------------------------------------ +USB transport (libusb) +------------------------------------------------------------------------ +The USB transport is implemented with libusb. +Libusb provides an asynchronous API for USB bulk transfers. +The transport implementation allocates a number of buffers +and submits asynchronous requests through libusb. +Event handler threads run in the background to process these requests. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Transport parameters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The following parameters can be used to alter the transport's default behavior: + +* **recv_frame_size:** The size of a single receive transfers in bytes +* **num_recv_frames:** The number of simultaneous receive transfers +* **send_frame_size:** The size of a single send transfers in bytes +* **num_send_frames:** The number of simultaneous send transfers +* **concurrency_hint:** The number of threads to run the event handler diff --git a/host/docs/usrp1.rst b/host/docs/usrp1.rst index 3c1431d30..3443fd871 100644 --- a/host/docs/usrp1.rst +++ b/host/docs/usrp1.rst @@ -100,10 +100,10 @@ OS Specific Notes  ------------------------------------------------------------------------  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Setup Udev on Linux +Linux - setup udev  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On Linux, Udev handles USB plug and unplug events. -The following command creates a Udev rule for the USRP1 +On Linux, udev handles USB plug and unplug events. +The following commands create a udev rule for the USRP1  so that non-root users may access the device:  :: @@ -113,3 +113,9 @@ so that non-root users may access the device:      sudo mv tmpfile /etc/udev/rules.d/10-usrp.rules      sudo udevadm control --reload-rules +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Windows - install driver +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +On Windows, a driver must be installed the first time the USRP1 is attached to the host computer. +A download link for this driver can be found on the UHD wiki page. +Download and unpack the driver, and direct the Windows driver install wizard to the .inf file. diff --git a/host/docs/usrp2.rst b/host/docs/usrp2.rst index 745361b77..0ddcaa4e5 100644 --- a/host/docs/usrp2.rst +++ b/host/docs/usrp2.rst @@ -158,7 +158,7 @@ The value for the addr key is a white-space separated list  of IPv4 addresses or resolvable hostnames.  The first address in the list will represent channel 0,  the second channel 1, and so on... -Use this addressing scheme with the *mimo_usrp* interface. +Use this addressing scheme with the *multi_usrp* interface.  The device address string representation for 2 USRP2s with IPv4 addresses 192.168.10.2 and 192.168.20.2  :: @@ -166,49 +166,22 @@ The device address string representation for 2 USRP2s with IPv4 addresses 192.16      addr=192.168.10.2 192.168.20.2  ------------------------------------------------------------------------ -Resize the send and receive buffers +Hardware setup notes  ------------------------------------------------------------------------ -It may be useful increase the size of the socket buffers to -move the burden of buffering samples into the kernel, or to -buffer incoming samples faster than they can be processed. -However, if you application cannot process samples fast enough, -no amount of buffering can save you. - -By default, the UHD will try to request a reasonably large buffer size for both send and receive. -A warning will be printed on instantiation if the actual buffer size is insufficient. -See the OS specific notes below: - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -OS specific notes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On linux, the maximum buffer sizes are capped by the sysctl values -**net.core.rmem_max** and **net.core.wmem_max**. -To change the maximum values, run the following commands: -:: - -    sudo sysctl -w net.core.rmem_max=<new value> -    sudo sysctl -w net.core.wmem_max=<new value> - -Set the values permanently by editing */etc/sysctl.conf*  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Device address params +Front panel LEDs  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To manually set the size of the buffers, -the usrp2 will accept two optional parameters in the device address. -Each parameter will accept a numeric value for the number of bytes. - -* recv_buff_size -* send_buff_size - -Example, set the args string to the following: -:: +The LEDs on the front panel can be useful in debugging hardware and software issues. +The LEDs reveal the following about the state of the device: -    addr=192.168.10.2, recv_buff_size=100e6 +* **LED A:** transmitting +* **LED B:** undocumented +* **LED C:** receiving +* **LED D:** firmware loaded +* **LED E:** undocumented +* **LED F:** FPGA loaded ------------------------------------------------------------------------- -Hardware setup notes -------------------------------------------------------------------------  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  Ref Clock - 10MHz diff --git a/host/examples/benchmark_rx_rate.cpp b/host/examples/benchmark_rx_rate.cpp index 8fae813cf..36611f97f 100644 --- a/host/examples/benchmark_rx_rate.cpp +++ b/host/examples/benchmark_rx_rate.cpp @@ -17,7 +17,7 @@  #include <uhd/utils/thread_priority.hpp>  #include <uhd/utils/safe_main.hpp> -#include <uhd/usrp/simple_usrp.hpp> +#include <uhd/usrp/single_usrp.hpp>  #include <boost/math/special_functions/round.hpp>  #include <boost/program_options.hpp>  #include <boost/format.hpp> @@ -27,7 +27,7 @@  namespace po = boost::program_options;  static inline void test_device( -    uhd::usrp::simple_usrp::sptr sdev, +    uhd::usrp::single_usrp::sptr sdev,      double rx_rate_sps,      double duration_secs  ){ @@ -118,7 +118,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      po::options_description desc("Allowed options");      desc.add_options()          ("help", "help message") -        ("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args") +        ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args")          ("duration", po::value<double>(&duration)->default_value(10.0), "duration for each test in seconds")          ("rate", po::value<double>(&only_rate), "specify to perform a single test as this rate (sps)")      ; @@ -135,7 +135,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      //create a usrp device      std::cout << std::endl;      std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; -    uhd::usrp::simple_usrp::sptr sdev = uhd::usrp::simple_usrp::make(args); +    uhd::usrp::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args);      std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl;      sdev->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); //stop if left running diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index 4856f6779..441665900 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -17,7 +17,7 @@  #include <uhd/utils/thread_priority.hpp>  #include <uhd/utils/safe_main.hpp> -#include <uhd/usrp/simple_usrp.hpp> +#include <uhd/usrp/single_usrp.hpp>  #include <boost/program_options.hpp>  #include <boost/format.hpp>  #include <iostream> @@ -38,7 +38,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      po::options_description desc("Allowed options");      desc.add_options()          ("help", "help message") -        ("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args") +        ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args")          ("secs", po::value<time_t>(&seconds_in_future)->default_value(3), "number of seconds in the future to receive")          ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to receive")          ("rxrate", po::value<double>(&rx_rate)->default_value(100e6/16), "rate of incoming samples") @@ -60,7 +60,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      //create a usrp device      std::cout << std::endl;      std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; -    uhd::usrp::simple_usrp::sptr sdev = uhd::usrp::simple_usrp::make(args); +    uhd::usrp::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args);      uhd::device::sptr dev = sdev->get_device();      std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; diff --git a/host/examples/test_async_messages.cpp b/host/examples/test_async_messages.cpp index e02bc5f40..e4a996ef5 100644 --- a/host/examples/test_async_messages.cpp +++ b/host/examples/test_async_messages.cpp @@ -18,7 +18,7 @@  #include <uhd/utils/thread_priority.hpp>  #include <uhd/utils/safe_main.hpp>  #include <uhd/utils/static.hpp> -#include <uhd/usrp/simple_usrp.hpp> +#include <uhd/usrp/single_usrp.hpp>  #include <boost/program_options.hpp>  #include <boost/format.hpp>  #include <complex> @@ -26,14 +26,12 @@  namespace po = boost::program_options; -static const size_t async_to_ms = 100; -  /*!   * Test that no messages are received:   *    Send a burst of many samples that will fragment internally.   *    We expect to not get any async messages.   */ -void test_no_async_message(uhd::usrp::simple_usrp::sptr sdev){ +void test_no_async_message(uhd::usrp::single_usrp::sptr sdev){      uhd::device::sptr dev = sdev->get_device();      std::cout << "Test no async message... " << std::flush; @@ -52,13 +50,13 @@ void test_no_async_message(uhd::usrp::simple_usrp::sptr sdev){      );      uhd::async_metadata_t async_md; -    if (dev->recv_async_msg(async_md, async_to_ms)){ +    if (dev->recv_async_msg(async_md)){          std::cout << boost::format(              "failed:\n"              "    Got unexpected event code 0x%x.\n"          ) % async_md.event_code << std::endl;          //clear the async messages -        while (dev->recv_async_msg(async_md, 0)); +        while (dev->recv_async_msg(async_md, 0)){};      }      else{          std::cout << boost::format( @@ -73,7 +71,7 @@ void test_no_async_message(uhd::usrp::simple_usrp::sptr sdev){   *    Send a start of burst packet with no following end of burst.   *    We expect to get an underflow(within a burst) async message.   */ -void test_underflow_message(uhd::usrp::simple_usrp::sptr sdev){ +void test_underflow_message(uhd::usrp::single_usrp::sptr sdev){      uhd::device::sptr dev = sdev->get_device();      std::cout << "Test underflow message... " << std::flush; @@ -88,7 +86,7 @@ void test_underflow_message(uhd::usrp::simple_usrp::sptr sdev){      );      uhd::async_metadata_t async_md; -    if (not dev->recv_async_msg(async_md, async_to_ms)){ +    if (not dev->recv_async_msg(async_md)){          std::cout << boost::format(              "failed:\n"              "    Async message recv timed out.\n" @@ -117,7 +115,7 @@ void test_underflow_message(uhd::usrp::simple_usrp::sptr sdev){   *    Send a burst packet that occurs at a time in the past.   *    We expect to get a time error async message.   */ -void test_time_error_message(uhd::usrp::simple_usrp::sptr sdev){ +void test_time_error_message(uhd::usrp::single_usrp::sptr sdev){      uhd::device::sptr dev = sdev->get_device();      std::cout << "Test time error message... " << std::flush; @@ -135,7 +133,7 @@ void test_time_error_message(uhd::usrp::simple_usrp::sptr sdev){      );      uhd::async_metadata_t async_md; -    if (not dev->recv_async_msg(async_md, async_to_ms)){ +    if (not dev->recv_async_msg(async_md)){          std::cout << boost::format(              "failed:\n"              "    Async message recv timed out.\n" @@ -170,7 +168,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      po::options_description desc("Allowed options");      desc.add_options()          ("help", "help message") -        ("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args") +        ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args")          ("rate", po::value<double>(&rate)->default_value(1.5e6), "rate of outgoing samples")      ;      po::variables_map vm; @@ -186,7 +184,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      //create a usrp device      std::cout << std::endl;      std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; -    uhd::usrp::simple_usrp::sptr sdev = uhd::usrp::simple_usrp::make(args); +    uhd::usrp::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args);      std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl;      //set the tx sample rate diff --git a/host/examples/test_pps_input.cpp b/host/examples/test_pps_input.cpp index e01d32910..4b2bb62a3 100644 --- a/host/examples/test_pps_input.cpp +++ b/host/examples/test_pps_input.cpp @@ -17,7 +17,7 @@  #include <uhd/utils/thread_priority.hpp>  #include <uhd/utils/safe_main.hpp> -#include <uhd/usrp/simple_usrp.hpp> +#include <uhd/usrp/single_usrp.hpp>  #include <boost/program_options.hpp>  #include <boost/format.hpp>  #include <boost/thread.hpp> @@ -31,13 +31,13 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      //variables to be set by po      std::string args; -    float seconds; +    double seconds;      //setup the program options      po::options_description desc("Allowed options");      desc.add_options()          ("help", "help message") -        ("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args") +        ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args")      ;      po::variables_map vm;      po::store(po::parse_command_line(argc, argv, desc), vm); @@ -52,7 +52,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      //create a usrp device      std::cout << std::endl;      std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; -    uhd::usrp::simple_usrp::sptr sdev = uhd::usrp::simple_usrp::make(args); +    uhd::usrp::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args);      uhd::device::sptr dev = sdev->get_device();      std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; @@ -60,20 +60,20 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      std::cout << "Set time to known value (100.0) without regard to pps:" << std::endl;      sdev->set_time_now(uhd::time_spec_t(100.0));      boost::this_thread::sleep(boost::posix_time::seconds(1)); -    std::cout << boost::format("Reading time 1 second later: %f\n") % (sdev->get_time_now().get_full_secs()) << std::endl; +    std::cout << boost::format("Reading time 1 second later: %f\n") % (sdev->get_time_now().get_real_secs()) << std::endl;      //store the time to see if PPS resets it -    seconds = sdev->get_time_now().get_full_secs(); +    seconds = sdev->get_time_now().get_real_secs();      //set a known time at next PPS, check that time increments      uhd::time_spec_t time_spec = uhd::time_spec_t(0.0);      std::cout << "Set time to known value (0.0) at next pps:" << std::endl;      sdev->set_time_next_pps(time_spec);      boost::this_thread::sleep(boost::posix_time::seconds(1)); -    std::cout << boost::format("Reading time 1 second later: %f\n") % (sdev->get_time_now().get_full_secs()) << std::endl; +    std::cout << boost::format("Reading time 1 second later: %f\n") % (sdev->get_time_now().get_real_secs()) << std::endl;      //finished -    if (seconds > sdev->get_time_now().get_full_secs()){ +    if (seconds > sdev->get_time_now().get_real_secs()){          std::cout << std::endl << "Success!" << std::endl << std::endl;          return 0;      } else { diff --git a/host/examples/tx_timed_samples.cpp b/host/examples/tx_timed_samples.cpp index 5b72bd72f..863446682 100644 --- a/host/examples/tx_timed_samples.cpp +++ b/host/examples/tx_timed_samples.cpp @@ -17,7 +17,7 @@  #include <uhd/utils/thread_priority.hpp>  #include <uhd/utils/safe_main.hpp> -#include <uhd/usrp/simple_usrp.hpp> +#include <uhd/usrp/single_usrp.hpp>  #include <boost/program_options.hpp>  #include <boost/format.hpp>  #include <iostream> @@ -40,7 +40,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      po::options_description desc("Allowed options");      desc.add_options()          ("help", "help message") -        ("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args") +        ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args")          ("secs", po::value<time_t>(&seconds_in_future)->default_value(3), "number of seconds in the future to transmit")          ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to transmit")          ("spp", po::value<size_t>(&samps_per_packet)->default_value(1000), "number of samples per packet") @@ -64,7 +64,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      //create a usrp device      std::cout << std::endl;      std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; -    uhd::usrp::simple_usrp::sptr sdev = uhd::usrp::simple_usrp::make(args); +    uhd::usrp::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args);      uhd::device::sptr dev = sdev->get_device();      std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; @@ -95,8 +95,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){          size_t num_tx_samps = dev->send(              &buff.front(), samps_to_send, md,              uhd::io_type_t::COMPLEX_FLOAT32, -            uhd::device::SEND_MODE_FULL_BUFF +            uhd::device::SEND_MODE_FULL_BUFF, +            //send will backup into the host this many seconds before sending: +            seconds_in_future + 0.1 //timeout (delay before transmit + padding)          ); +        if (num_tx_samps < samps_to_send) std::cout << "Send timeout..." << std::endl;          if(verbose) std::cout << std::endl << boost::format("Sent %d samples") % num_tx_samps << std::endl;      } diff --git a/host/examples/tx_waveforms.cpp b/host/examples/tx_waveforms.cpp index 3f319cf68..50982cf88 100644 --- a/host/examples/tx_waveforms.cpp +++ b/host/examples/tx_waveforms.cpp @@ -18,7 +18,7 @@  #include <uhd/utils/thread_priority.hpp>  #include <uhd/utils/safe_main.hpp>  #include <uhd/utils/static.hpp> -#include <uhd/usrp/simple_usrp.hpp> +#include <uhd/usrp/single_usrp.hpp>  #include <boost/program_options.hpp>  #include <boost/thread/thread_time.hpp> //system time  #include <boost/math/special_functions/round.hpp> @@ -70,7 +70,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      po::options_description desc("Allowed options");      desc.add_options()          ("help", "help message") -        ("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args") +        ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args")          ("duration", po::value<size_t>(&total_duration)->default_value(3), "number of seconds to transmit")          ("spb", po::value<size_t>(&spb)->default_value(10000), "samples per buffer")          ("rate", po::value<double>(&rate)->default_value(1.5e6), "rate of outgoing samples") @@ -93,7 +93,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      //create a usrp device      std::cout << std::endl;      std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; -    uhd::usrp::simple_usrp::sptr sdev = uhd::usrp::simple_usrp::make(args); +    uhd::usrp::single_usrp::sptr sdev = uhd::usrp::single_usrp::make(args);      uhd::device::sptr dev = sdev->get_device();      std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; diff --git a/host/include/linux/usrp_e.h b/host/include/linux/usrp_e.h index fd38027d4..cb62d940d 100644 --- a/host/include/linux/usrp_e.h +++ b/host/include/linux/usrp_e.h @@ -34,8 +34,8 @@ struct usrp_e_ctl32 {  #define UE_SPI_TXRX	1  /* Defines for spi ctrl register */ -#define UE_SPI_CTRL_TXNEG	(BIT(10)) -#define UE_SPI_CTRL_RXNEG	(BIT(9)) +#define UE_SPI_CTRL_TXNEG	(1<<10) +#define UE_SPI_CTRL_RXNEG	(1<<9)  #define UE_SPI_PUSH_RISE	0  #define UE_SPI_PUSH_FALL	UE_SPI_CTRL_TXNEG @@ -71,6 +71,7 @@ struct usrp_e_i2c {  #define RB_KERNEL (1<<1)  #define RB_OVERRUN (1<<2)  #define RB_DMA_ACTIVE (1<<3) +#define RB_USER_PROCESS (1<<4)  struct ring_buffer_info {  	int flags; diff --git a/host/include/uhd/config.hpp b/host/include/uhd/config.hpp index dacd3a96b..2918c2340 100644 --- a/host/include/uhd/config.hpp +++ b/host/include/uhd/config.hpp @@ -31,11 +31,12 @@  # pragma warning(disable: 4251) // class 'A<T>' needs to have dll-interface to be used by clients of class 'B'  //# pragma warning(disable: 4127) // conditional expression is constant  //# pragma warning(disable: 4290) // C++ exception specification ignored except to ... -# pragma warning(disable: 4180) // qualifier applied to function type has no meaning; ignored +//# pragma warning(disable: 4180) // qualifier applied to function type has no meaning; ignored  # pragma warning(disable: 4275) // non dll-interface class ... used as base for dll-interface class ...  //# pragma warning(disable: 4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data  //# pragma warning(disable: 4511) // 'class' : copy constructor could not be generated -# pragma warning(disable: 4250) // 'class' : inherits 'method' via dominance +//# pragma warning(disable: 4250) // 'class' : inherits 'method' via dominance +# pragma warning(disable: 4200) // nonstandard extension used : zero-sized array in struct/union  #endif  // define logical operators diff --git a/host/include/uhd/device.hpp b/host/include/uhd/device.hpp index c48b3dfff..992276928 100644 --- a/host/include/uhd/device.hpp +++ b/host/include/uhd/device.hpp @@ -26,7 +26,6 @@  #include <boost/utility.hpp>  #include <boost/shared_ptr.hpp>  #include <boost/function.hpp> -#include <boost/asio/buffer.hpp>  #include <vector>  namespace uhd{ @@ -42,9 +41,6 @@ public:      typedef boost::function<device_addrs_t(const device_addr_t &)> find_t;      typedef boost::function<sptr(const device_addr_t &)> make_t; -    //! A reasonable default timeout for receive -    static const size_t default_recv_timeout_ms = 100; -      /*!       * Register a device into the discovery and factory system.       * @@ -113,12 +109,15 @@ public:       *       * This is a blocking call and will not return until the number       * of samples returned have been read out of each buffer. +     * Under a timeout condition, the number of samples returned +     * may be less than the number of samples specified.       *       * \param buffs a vector of read-only memory containing IF data       * \param nsamps_per_buff the number of samples to send, per buffer       * \param metadata data describing the buffer's contents       * \param io_type the type of data loaded in the buffer       * \param send_mode tells send how to unload the buffer +     * \param timeout the timeout in seconds to wait on a packet       * \return the number of samples sent       */      virtual size_t send( @@ -126,7 +125,8 @@ public:          size_t nsamps_per_buff,          const tx_metadata_t &metadata,          const io_type_t &io_type, -        send_mode_t send_mode +        send_mode_t send_mode, +        double timeout = 0.1      ) = 0;      /*! @@ -137,13 +137,8 @@ public:          size_t nsamps_per_buff,          const tx_metadata_t &metadata,          const io_type_t &io_type, -        send_mode_t send_mode -    ); - -    //! Deprecated -    size_t send( -        const boost::asio::const_buffer &, const tx_metadata_t &, -        const io_type_t &, send_mode_t send_mode +        send_mode_t send_mode, +        double timeout = 0.1      );      /*! @@ -161,7 +156,9 @@ public:       * See the rx metadata fragment flags and offset fields for details.       *       * This is a blocking call and will not return until the number -     * of samples returned have been written into each buffer or timeout. +     * of samples returned have been written into each buffer. +     * Under a timeout condition, the number of samples returned +     * may be less than the number of samples specified.       *       * When using the full buffer recv mode, the metadata only applies       * to the first packet received and written into the recv buffers. @@ -172,7 +169,7 @@ public:       * \param metadata data to fill describing the buffer       * \param io_type the type of data to fill into the buffer       * \param recv_mode tells recv how to load the buffer -     * \param timeout_ms the timeout in milliseconds to wait for a packet +     * \param timeout the timeout in seconds to wait for a packet       * \return the number of samples received or 0 on error       */      virtual size_t recv( @@ -181,7 +178,7 @@ public:          rx_metadata_t &metadata,          const io_type_t &io_type,          recv_mode_t recv_mode, -        size_t timeout_ms = default_recv_timeout_ms +        double timeout = 0.1      ) = 0;      /*! @@ -193,13 +190,7 @@ public:          rx_metadata_t &metadata,          const io_type_t &io_type,          recv_mode_t recv_mode, -        size_t timeout_ms = default_recv_timeout_ms -    ); - -    //! Deprecated -    size_t recv( -        const boost::asio::mutable_buffer &, rx_metadata_t &, -        const io_type_t &, recv_mode_t +        double timeout = 0.1      );      /*! @@ -217,12 +208,11 @@ public:      /*!       * Receive and asynchronous message from the device.       * \param async_metadata the metadata to be filled in -     * \param timeout_ms the timeout in milliseconds to wait for a message +     * \param timeout the timeout in seconds to wait for a message       * \return true when the async_metadata is valid, false for timeout       */      virtual bool recv_async_msg( -        async_metadata_t &async_metadata, -        size_t timeout_ms = default_recv_timeout_ms +        async_metadata_t &async_metadata, double timeout = 0.1      ) = 0;  }; diff --git a/host/include/uhd/device.ipp b/host/include/uhd/device.ipp index 603c52859..e2e51ecd0 100644 --- a/host/include/uhd/device.ipp +++ b/host/include/uhd/device.ipp @@ -25,25 +25,13 @@ namespace uhd{          size_t nsamps_per_buff,          const tx_metadata_t &metadata,          const io_type_t &io_type, -        send_mode_t send_mode +        send_mode_t send_mode, +        double timeout      ){          return this->send(              std::vector<const void *>(1, buff),              nsamps_per_buff, metadata, -            io_type, send_mode -        ); -    } - -    UHD_DEPRECATED UHD_INLINE size_t device::send( -        const boost::asio::const_buffer &buff, -        const tx_metadata_t &metadata, -        const io_type_t &io_type, -        send_mode_t send_mode -    ){ -        return this->send( -            boost::asio::buffer_cast<const void *>(buff), -            boost::asio::buffer_size(buff)/io_type.size, -            metadata, io_type, send_mode +            io_type, send_mode, timeout          );      } @@ -53,25 +41,12 @@ namespace uhd{          rx_metadata_t &metadata,          const io_type_t &io_type,          recv_mode_t recv_mode, -        size_t timeout_ms +        double timeout      ){          return this->recv(              std::vector<void *>(1, buff),              nsamps_per_buff, metadata, -            io_type, recv_mode, timeout_ms -        ); -    } - -    UHD_DEPRECATED UHD_INLINE size_t device::recv( -        const boost::asio::mutable_buffer &buff, -        rx_metadata_t &metadata, -        const io_type_t &io_type, -        recv_mode_t recv_mode -    ){ -        return this->recv( -            boost::asio::buffer_cast<void *>(buff), -            boost::asio::buffer_size(buff)/io_type.size, -            metadata, io_type, recv_mode +            io_type, recv_mode, timeout          );      } diff --git a/host/include/uhd/transport/alignment_buffer.hpp b/host/include/uhd/transport/alignment_buffer.hpp index 29ba74efc..f44a037f8 100644 --- a/host/include/uhd/transport/alignment_buffer.hpp +++ b/host/include/uhd/transport/alignment_buffer.hpp @@ -48,20 +48,17 @@ namespace uhd{ namespace transport{           * \return true if the element fit without popping for space           */          virtual bool push_with_pop_on_full( -            const elem_type &elem, -            const seq_type &seq, -            size_t index +            const elem_type &elem, const seq_type &seq, size_t index          ) = 0;          /*!           * Pop an aligned set of elements from this alignment buffer.           * \param elems a collection to store the aligned elements -         * \param time the timeout time +         * \param timeout the timeout in seconds           * \return false when the operation times out           */          virtual bool pop_elems_with_timed_wait( -            std::vector<elem_type> &elems, -            const time_duration_t &time +            std::vector<elem_type> &elems, double timeout          ) = 0;      }; diff --git a/host/include/uhd/transport/alignment_buffer.ipp b/host/include/uhd/transport/alignment_buffer.ipp index 61b3b60f5..833b5d399 100644 --- a/host/include/uhd/transport/alignment_buffer.ipp +++ b/host/include/uhd/transport/alignment_buffer.ipp @@ -41,9 +41,7 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/          }          UHD_INLINE bool push_with_pop_on_full( -            const elem_type &elem, -            const seq_type &seq, -            size_t index +            const elem_type &elem, const seq_type &seq, size_t index          ){              //clear the buffer for this index if the seqs are mis-ordered              if (seq < _last_seqs[index]){ @@ -54,17 +52,16 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/          }          UHD_INLINE bool pop_elems_with_timed_wait( -            std::vector<elem_type> &elems, -            const time_duration_t &time +            std::vector<elem_type> &elems, double timeout          ){ -            boost::system_time exit_time = boost::get_system_time() + time; +            boost::system_time exit_time = boost::get_system_time() + to_time_dur(timeout);              buff_contents_type buff_contents_tmp;              std::list<size_t> indexes_to_do(_all_indexes);              //do an initial pop to load an initial sequence id              size_t index = indexes_to_do.front();              if (not _buffs[index]->pop_with_timed_wait( -                buff_contents_tmp, exit_time - boost::get_system_time() +                buff_contents_tmp, from_time_dur(exit_time - boost::get_system_time())              )) return false;              elems[index] = buff_contents_tmp.first;              seq_type expected_seq_id = buff_contents_tmp.second; @@ -79,7 +76,7 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/                      indexes_to_do = _all_indexes;                      index = indexes_to_do.front();                      if (not _buffs[index]->pop_with_timed_wait( -                        buff_contents_tmp, exit_time - boost::get_system_time() +                        buff_contents_tmp, from_time_dur(exit_time - boost::get_system_time())                      )) return false;                      elems[index] = buff_contents_tmp.first;                      expected_seq_id = buff_contents_tmp.second; @@ -89,7 +86,7 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/                  //pop an element off for this index                  index = indexes_to_do.front();                  if (not _buffs[index]->pop_with_timed_wait( -                    buff_contents_tmp, exit_time - boost::get_system_time() +                    buff_contents_tmp, from_time_dur(exit_time - boost::get_system_time())                  )) return false;                  //if the sequence id matches: diff --git a/host/include/uhd/transport/bounded_buffer.hpp b/host/include/uhd/transport/bounded_buffer.hpp index d1deece96..aca93b071 100644 --- a/host/include/uhd/transport/bounded_buffer.hpp +++ b/host/include/uhd/transport/bounded_buffer.hpp @@ -20,13 +20,9 @@  #include <uhd/config.hpp>  #include <boost/shared_ptr.hpp> -#include <boost/date_time/posix_time/posix_time_types.hpp>  namespace uhd{ namespace transport{ -    //! typedef for the time duration type for wait operations -    typedef boost::posix_time::time_duration time_duration_t; -      /*!       * Implement a templated bounded buffer:       * Used for passing elements between threads in a producer-consumer model. @@ -64,10 +60,10 @@ namespace uhd{ namespace transport{           * Push a new element into the bounded_buffer.           * Wait until the bounded_buffer becomes non-full or timeout.           * \param elem the new element to push -         * \param time the timeout time +         * \param timeout the timeout in seconds           * \return false when the operation times out           */ -        virtual bool push_with_timed_wait(const elem_type &elem, const time_duration_t &time) = 0; +        virtual bool push_with_timed_wait(const elem_type &elem, double timeout) = 0;          /*!           * Pop an element from the bounded_buffer. @@ -80,10 +76,10 @@ namespace uhd{ namespace transport{           * Pop an element from the bounded_buffer.           * Wait until the bounded_buffer becomes non-empty or timeout.           * \param elem the element reference pop to -         * \param time the timeout time +         * \param timeout the timeout in seconds           * \return false when the operation times out           */ -        virtual bool pop_with_timed_wait(elem_type &elem, const time_duration_t &time) = 0; +        virtual bool pop_with_timed_wait(elem_type &elem, double timeout) = 0;          /*!           * Clear all elements from the bounded_buffer. diff --git a/host/include/uhd/transport/bounded_buffer.ipp b/host/include/uhd/transport/bounded_buffer.ipp index e106e229e..edc7faa06 100644 --- a/host/include/uhd/transport/bounded_buffer.ipp +++ b/host/include/uhd/transport/bounded_buffer.ipp @@ -19,17 +19,28 @@  #define INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_IPP  #include <boost/bind.hpp> +#include <boost/function.hpp>  #include <boost/circular_buffer.hpp>  #include <boost/thread/condition.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp>  namespace uhd{ namespace transport{ namespace{ /*anon*/ +    static UHD_INLINE boost::posix_time::time_duration to_time_dur(double timeout){ +        return boost::posix_time::microseconds(long(timeout*1e6)); +    } + +    static UHD_INLINE double from_time_dur(const boost::posix_time::time_duration &time_dur){ +        return 1e-6*time_dur.total_microseconds(); +    } +      template <typename elem_type>      class bounded_buffer_impl : public bounded_buffer<elem_type>{      public:          bounded_buffer_impl(size_t capacity) : _buffer(capacity){ -            /* NOP */ +            _not_full_fcn = boost::bind(&bounded_buffer_impl<elem_type>::not_full, this); +            _not_empty_fcn = boost::bind(&bounded_buffer_impl<elem_type>::not_empty, this);          }          UHD_INLINE bool push_with_pop_on_full(const elem_type &elem){ @@ -51,15 +62,17 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/          UHD_INLINE void push_with_wait(const elem_type &elem){              boost::unique_lock<boost::mutex> lock(_mutex); -            _full_cond.wait(lock, boost::bind(&bounded_buffer_impl<elem_type>::not_full, this)); +            _full_cond.wait(lock, _not_full_fcn);              _buffer.push_front(elem);              lock.unlock();              _empty_cond.notify_one();          } -        bool push_with_timed_wait(const elem_type &elem, const time_duration_t &time){ +        UHD_INLINE bool push_with_timed_wait(const elem_type &elem, double timeout){              boost::unique_lock<boost::mutex> lock(_mutex); -            if (not _full_cond.timed_wait(lock, time, boost::bind(&bounded_buffer_impl<elem_type>::not_full, this))) return false; +            if (not _full_cond.timed_wait( +                lock, to_time_dur(timeout), _not_full_fcn +            )) return false;              _buffer.push_front(elem);              lock.unlock();              _empty_cond.notify_one(); @@ -68,16 +81,18 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/          UHD_INLINE void pop_with_wait(elem_type &elem){              boost::unique_lock<boost::mutex> lock(_mutex); -            _empty_cond.wait(lock, boost::bind(&bounded_buffer_impl<elem_type>::not_empty, this)); -            elem = _buffer.back(); _buffer.pop_back(); +            _empty_cond.wait(lock, _not_empty_fcn); +            elem = this->pop_back();              lock.unlock();              _full_cond.notify_one();          } -        bool pop_with_timed_wait(elem_type &elem, const time_duration_t &time){ +        UHD_INLINE bool pop_with_timed_wait(elem_type &elem, double timeout){              boost::unique_lock<boost::mutex> lock(_mutex); -            if (not _empty_cond.timed_wait(lock, time, boost::bind(&bounded_buffer_impl<elem_type>::not_empty, this))) return false; -            elem = _buffer.back(); _buffer.pop_back(); +            if (not _empty_cond.timed_wait( +                lock, to_time_dur(timeout), _not_empty_fcn +            )) return false; +            elem = this->pop_back();              lock.unlock();              _full_cond.notify_one();              return true; @@ -85,7 +100,7 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/          UHD_INLINE void clear(void){              boost::unique_lock<boost::mutex> lock(_mutex); -            while (not_empty()) _buffer.pop_back(); +            while (not_empty()) this->pop_back();              lock.unlock();              _full_cond.notify_one();          } @@ -97,6 +112,21 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/          bool not_full(void) const{return not _buffer.full();}          bool not_empty(void) const{return not _buffer.empty();} + +        boost::function<bool(void)> _not_full_fcn, _not_empty_fcn; + +        /*! +         * Three part operation to pop an element: +         * 1) assign elem to the back element +         * 2) assign the back element to empty +         * 3) pop the back to move the counter +         */ +        UHD_INLINE elem_type pop_back(void){ +            elem_type elem = _buffer.back(); +            _buffer.back() = elem_type(); +            _buffer.pop_back(); +            return elem; +        }      };  }}} //namespace diff --git a/host/include/uhd/transport/udp_zero_copy.hpp b/host/include/uhd/transport/udp_zero_copy.hpp index 818709973..bbba97b21 100644 --- a/host/include/uhd/transport/udp_zero_copy.hpp +++ b/host/include/uhd/transport/udp_zero_copy.hpp @@ -20,6 +20,7 @@  #include <uhd/config.hpp>  #include <uhd/transport/zero_copy.hpp> +#include <uhd/types/device_addr.hpp>  #include <boost/shared_ptr.hpp>  namespace uhd{ namespace transport{ @@ -50,14 +51,12 @@ public:       *       * \param addr a string representing the destination address       * \param port a string representing the destination port -     * \param recv_buff_size size in bytes for the recv buffer, 0 for automatic -     * \param send_buff_size size in bytes for the send buffer, 0 for automatic +     * \param hints optional parameters to pass to the underlying transport       */      static sptr make(          const std::string &addr,          const std::string &port, -        size_t recv_buff_size = 0, -        size_t send_buff_size = 0 +        const device_addr_t &hints = device_addr_t()      );  }; diff --git a/host/include/uhd/transport/usb_control.hpp b/host/include/uhd/transport/usb_control.hpp index 6137ecf84..e6c32f78e 100644 --- a/host/include/uhd/transport/usb_control.hpp +++ b/host/include/uhd/transport/usb_control.hpp @@ -18,7 +18,7 @@  #ifndef INCLUDED_UHD_TRANSPORT_USB_CONTROL_HPP  #define INCLUDED_UHD_TRANSPORT_USB_CONTROL_HPP -#include "usb_device_handle.hpp" +#include <uhd/transport/usb_device_handle.hpp>  namespace uhd { namespace transport { @@ -50,9 +50,9 @@ public:       * \param index        2-byte (wIndex)       * \param buff         buffer to hold send or receive data       * \param length       2-byte (wLength) -     * \return             number of bytes submitted +     * \return             number of bytes submitted or error code       */ -    virtual size_t submit(boost::uint8_t request_type, +    virtual ssize_t submit(boost::uint8_t request_type,                            boost::uint8_t request,                            boost::uint16_t value,                            boost::uint16_t index,  diff --git a/host/include/uhd/transport/usb_device_handle.hpp b/host/include/uhd/transport/usb_device_handle.hpp index c3eb72b00..6f8d868be 100644 --- a/host/include/uhd/transport/usb_device_handle.hpp +++ b/host/include/uhd/transport/usb_device_handle.hpp @@ -46,31 +46,25 @@ public:       * Return the device's serial number        * \return a string describing the device's serial number       */ -    virtual UHD_API std::string get_serial() const = 0; +    virtual std::string get_serial() const = 0;      /*!       * Return the device's Vendor ID (usually assigned by the USB-IF)       * \return a Vendor ID       */ -    virtual UHD_API boost::uint16_t get_vendor_id() const = 0; +    virtual boost::uint16_t get_vendor_id() const = 0;      /*!       * Return the device's Product ID (usually assigned by manufacturer)       * \return a Product ID       */ -    virtual UHD_API boost::uint16_t get_product_id() const = 0; - -    /*! -     * Return the device's USB address -     * \return a Product ID -     */ -    virtual UHD_API boost::uint16_t get_device_addr() const = 0; +    virtual boost::uint16_t get_product_id() const = 0;      /*!       * Return a vector of USB devices on this host        * \return a vector of USB device handles that match vid and pid       */ -    static UHD_API std::vector<usb_device_handle::sptr> get_device_list(boost::uint16_t vid, boost::uint16_t pid); +    static std::vector<usb_device_handle::sptr> get_device_list(boost::uint16_t vid, boost::uint16_t pid);  }; //namespace usb diff --git a/host/include/uhd/transport/usb_zero_copy.hpp b/host/include/uhd/transport/usb_zero_copy.hpp index 2edd6d91d..b39171fba 100644 --- a/host/include/uhd/transport/usb_zero_copy.hpp +++ b/host/include/uhd/transport/usb_zero_copy.hpp @@ -18,8 +18,9 @@  #ifndef INCLUDED_UHD_TRANSPORT_USB_ZERO_COPY_HPP  #define INCLUDED_UHD_TRANSPORT_USB_ZERO_COPY_HPP -#include "usb_device_handle.hpp" +#include <uhd/transport/usb_device_handle.hpp>  #include <uhd/transport/zero_copy.hpp> +#include <uhd/types/device_addr.hpp>  namespace uhd { namespace transport { @@ -45,16 +46,16 @@ public:       * The underlying implementation may be platform specific.       *       * \param handle a device handle that uniquely identifying the device -     * \param rx_endpoint an integer specifiying an IN endpoint number  -     * \param tx_endpoint an integer specifiying an OUT endpoint number -     * \param buff_size total number of bytes of buffer space to allocate  -     * \param block_size number of bytes allocated for each I/O transaction  +     * \param recv_endpoint an integer specifiying an IN endpoint number +     * \param send_endpoint an integer specifiying an OUT endpoint number +     * \param hints optional parameters to pass to the underlying transport       */ -    static sptr make(usb_device_handle::sptr handle, -                     unsigned int rx_endpoint, -                     unsigned int tx_endpoint, -		     size_t buff_size = 0,  -                     size_t block_size = 0); +    static sptr make( +        usb_device_handle::sptr handle, +        size_t recv_endpoint, +        size_t send_endpoint, +        const device_addr_t &hints = device_addr_t() +    );  };  }} //namespace diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index 513291b63..7d8fb4b83 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -19,10 +19,10 @@  #define INCLUDED_UHD_TRANSPORT_ZERO_COPY_HPP  #include <uhd/config.hpp> -#include <uhd/utils/pimpl.hpp>  #include <boost/asio/buffer.hpp>  #include <boost/utility.hpp>  #include <boost/shared_ptr.hpp> +#include <boost/function.hpp>  namespace uhd{ namespace transport{ @@ -34,14 +34,27 @@ namespace uhd{ namespace transport{      class UHD_API managed_recv_buffer : boost::noncopyable{      public:          typedef boost::shared_ptr<managed_recv_buffer> sptr; +        typedef boost::function<void(void)> release_fcn_t; + +        /*! +         * Make a safe managed receive buffer: +         * A safe managed buffer ensures that release is called once, +         * either by the user or automatically upon deconstruction. +         * \param buff a reference to the constant buffer +         * \param release_fcn callback to release the memory +         * \return a new managed receive buffer +         */ +        static sptr make_safe( +            const boost::asio::const_buffer &buff, +            const release_fcn_t &release_fcn +        );          /*! -         * Managed recv buffer destructor:           * Signal to the transport that we are done with the buffer. -         * This should be called to release the buffer to the transport. +         * This should be called to release the buffer to the transport object.           * After calling, the referenced memory should be considered invalid.           */ -        virtual ~managed_recv_buffer(void) = 0; +        virtual void release(void) = 0;          /*!           * Get the size of the underlying buffer. @@ -71,20 +84,34 @@ namespace uhd{ namespace transport{      /*!       * A managed send buffer:       * Contains a reference to transport-managed memory, -     * and a method to release the memory after writing. +     * and a method to commit the memory after writing.       */      class UHD_API managed_send_buffer : boost::noncopyable{      public:          typedef boost::shared_ptr<managed_send_buffer> sptr; +        typedef boost::function<void(size_t)> commit_fcn_t; + +        /*! +         * Make a safe managed send buffer: +         * A safe managed buffer ensures that commit is called once, +         * either by the user or automatically upon deconstruction. +         * In the later case, the deconstructor will call commit(0). +         * \param buff a reference to the mutable buffer +         * \param commit_fcn callback to commit the memory +         * \return a new managed send buffer +         */ +        static sptr make_safe( +            const boost::asio::mutable_buffer &buff, +            const commit_fcn_t &commit_fcn +        );          /*!           * Signal to the transport that we are done with the buffer.           * This should be called to commit the write to the transport object.           * After calling, the referenced memory should be considered invalid.           * \param num_bytes the number of bytes written into the buffer -         * \return the number of bytes written, 0 for timeout, negative for error           */ -        virtual ssize_t commit(size_t num_bytes) = 0; +        virtual void commit(size_t num_bytes) = 0;          /*!           * Get the size of the underlying buffer. @@ -122,100 +149,46 @@ namespace uhd{ namespace transport{          /*!           * Get a new receive buffer from this transport object. +         * \param timeout the timeout to get the buffer in seconds           * \return a managed buffer, or null sptr on timeout/error           */ -        virtual managed_recv_buffer::sptr get_recv_buff(void) = 0; +        virtual managed_recv_buffer::sptr get_recv_buff(double timeout = 0.1) = 0;          /*! -         * Get the maximum number of receive frames: -         *   The maximum number of valid managed recv buffers, -         *   or the maximum number of frames in the ring buffer, -         *   depending upon the underlying implementation. +         * Get the number of receive frames: +         * The number of simultaneous receive buffers in use.           * \return number of frames           */          virtual size_t get_num_recv_frames(void) const = 0;          /*! +         * Get the size of a receive frame: +         * The maximum capacity of a single receive buffer. +         * \return frame size in bytes +         */ +        virtual size_t get_recv_frame_size(void) const = 0; + +        /*!           * Get a new send buffer from this transport object. +         * \param timeout the timeout to get the buffer in seconds           * \return a managed buffer, or null sptr on timeout/error           */ -        virtual managed_send_buffer::sptr get_send_buff(void) = 0; +        virtual managed_send_buffer::sptr get_send_buff(double timeout = 0.1) = 0;          /*! -         * Get the maximum number of send frames: -         *   The maximum number of valid managed send buffers, -         *   or the maximum number of frames in the ring buffer, -         *   depending upon the underlying implementation. +         * Get the number of send frames: +         * The number of simultaneous send buffers in use.           * \return number of frames           */          virtual size_t get_num_send_frames(void) const = 0; -    }; - -    /*! -     * A phony-zero-copy interface for transport objects that -     * provides a zero-copy interface on top of copying transport. -     * This interface implements the get managed recv buffer, -     * the base class must implement the private recv method. -     */ -    class UHD_API phony_zero_copy_recv_if : public virtual zero_copy_if{ -    public: -        /*! -         * Create a phony zero copy recv interface. -         * \param max_buff_size max buffer size in bytes -         */ -        phony_zero_copy_recv_if(size_t max_buff_size); - -        //! destructor -        virtual ~phony_zero_copy_recv_if(void); - -        /*! -         * Get a new receive buffer from this transport object. -         */ -        managed_recv_buffer::sptr get_recv_buff(void); - -    private: -        /*! -         * Perform a private copying recv. -         * \param buff the buffer to write data into -         * \return the number of bytes written to buff, 0 for timeout, negative for error -         */ -        virtual ssize_t recv(const boost::asio::mutable_buffer &buff) = 0; - -        UHD_PIMPL_DECL(impl) _impl; -    }; - -    /*! -     * A phony-zero-copy interface for transport objects that -     * provides a zero-copy interface on top of copying transport. -     * This interface implements the get managed send buffer, -     * the base class must implement the private send method. -     */ -    class UHD_API phony_zero_copy_send_if : public virtual zero_copy_if{ -    public: -        /*! -         * Create a phony zero copy send interface. -         * \param max_buff_size max buffer size in bytes -         */ -        phony_zero_copy_send_if(size_t max_buff_size); - -        //! destructor -        virtual ~phony_zero_copy_send_if(void); - -        /*! -         * Get a new send buffer from this transport object. -         */ -        managed_send_buffer::sptr get_send_buff(void); - -    private:          /*! -         * Perform a private copying send. -         * \param buff the buffer to read data from -         * \return the number of bytes read from buff, 0 for timeout, negative for error +         * Get the size of a send frame: +         * The maximum capacity of a single send buffer. +         * \return frame size in bytes           */ -        virtual ssize_t send(const boost::asio::const_buffer &buff) = 0; +        virtual size_t get_send_frame_size(void) const = 0; -        UHD_PIMPL_DECL(impl) _impl;      };  }} //namespace diff --git a/host/include/uhd/types/device_addr.hpp b/host/include/uhd/types/device_addr.hpp index e359d9467..eb3394230 100644 --- a/host/include/uhd/types/device_addr.hpp +++ b/host/include/uhd/types/device_addr.hpp @@ -20,6 +20,8 @@  #include <uhd/config.hpp>  #include <uhd/types/dict.hpp> +#include <boost/lexical_cast.hpp> +#include <stdexcept>  #include <vector>  #include <string> @@ -62,6 +64,24 @@ namespace uhd{           * \return a string with delimiter markup           */          std::string to_string(void) const; + +        /*! +         * Lexically cast a parameter to the specified type, +         * or use the default value if the key is not found. +         * \param key the key as one of the address parameters +         * \param def the value to use when key is not present +         * \return the casted value as type T or the default +         * \throw error when the parameter cannot be casted +         */ +        template <typename T> T cast(const std::string &key, const T &def) const{ +            if (not this->has_key(key)) return def; +            try{ +                return boost::lexical_cast<T>((*this)[key]); +            } +            catch(const boost::bad_lexical_cast &){ +                throw std::runtime_error("cannot cast " + key + " = " + (*this)[key]); +            } +        }      };      //handy typedef for a vector of device addresses diff --git a/host/include/uhd/types/time_spec.hpp b/host/include/uhd/types/time_spec.hpp index 59b85f4b7..57d002d48 100644 --- a/host/include/uhd/types/time_spec.hpp +++ b/host/include/uhd/types/time_spec.hpp @@ -59,7 +59,7 @@ namespace uhd{           * \param tick_count the fractional seconds tick count           * \param tick_rate the number of ticks per second           */ -        time_spec_t(time_t full_secs, size_t tick_count, double tick_rate); +        time_spec_t(time_t full_secs, long tick_count, double tick_rate);          /*!           * Convert the fractional seconds to clock ticks. @@ -67,7 +67,7 @@ namespace uhd{           * \param tick_rate the number of ticks per second           * \return the fractional seconds tick count           */ -        size_t get_tick_count(double tick_rate) const; +        long get_tick_count(double tick_rate) const;          /*!           * Get the time as a real-valued seconds count. diff --git a/host/include/uhd/usrp/CMakeLists.txt b/host/include/uhd/usrp/CMakeLists.txt index 130956f8a..abddf3951 100644 --- a/host/include/uhd/usrp/CMakeLists.txt +++ b/host/include/uhd/usrp/CMakeLists.txt @@ -40,7 +40,9 @@ INSTALL(FILES      ### interfaces ###      simple_usrp.hpp +    single_usrp.hpp      mimo_usrp.hpp +    multi_usrp.hpp      DESTINATION ${INCLUDE_DIR}/uhd/usrp  ) diff --git a/host/include/uhd/usrp/dboard_iface.hpp b/host/include/uhd/usrp/dboard_iface.hpp index c7db244f2..c430ecd3f 100644 --- a/host/include/uhd/usrp/dboard_iface.hpp +++ b/host/include/uhd/usrp/dboard_iface.hpp @@ -242,6 +242,15 @@ public:       * \param enb true for enabled       */      virtual void set_clock_enabled(unit_t unit, bool enb) = 0; + +    /*! +     * Get the rate of the codec. +     * For rx, this is the rate the ADC feeds the DSP. +     * For tx, this is the rate the DSP feeds the DAC. +     * \param unit which unit rx or tx +     * \return the codec rate in Hz +     */ +    virtual double get_codec_rate(unit_t unit) = 0;  };  }} //namespace diff --git a/host/include/uhd/usrp/dsp_props.hpp b/host/include/uhd/usrp/dsp_props.hpp index 75d8c0a60..54ea5666b 100644 --- a/host/include/uhd/usrp/dsp_props.hpp +++ b/host/include/uhd/usrp/dsp_props.hpp @@ -37,11 +37,12 @@ namespace uhd{ namespace usrp{       *   Set the shift property and read it back to get actual shift.       */      enum dsp_prop_t{ -        DSP_PROP_NAME         = 'n', //ro, std::string -        DSP_PROP_OTHERS       = 'o', //ro, prop_names_t -        DSP_PROP_FREQ_SHIFT   = 'f', //rw, double Hz -        DSP_PROP_CODEC_RATE   = 'c', //ro, double Sps -        DSP_PROP_HOST_RATE    = 'h'  //rw, double Sps +        DSP_PROP_NAME              = 'n', //ro, std::string +        DSP_PROP_OTHERS            = 'o', //ro, prop_names_t +        DSP_PROP_FREQ_SHIFT        = 'f', //rw, double Hz +        DSP_PROP_FREQ_SHIFT_NAMES  = 'F', //ro, prop_names_t +        DSP_PROP_CODEC_RATE        = 'c', //ro, double Sps +        DSP_PROP_HOST_RATE         = 'h'  //rw, double Sps      };  }} //namespace diff --git a/host/include/uhd/usrp/mimo_usrp.hpp b/host/include/uhd/usrp/mimo_usrp.hpp index 10a404059..78833e24e 100644 --- a/host/include/uhd/usrp/mimo_usrp.hpp +++ b/host/include/uhd/usrp/mimo_usrp.hpp @@ -32,12 +32,12 @@  namespace uhd{ namespace usrp{  /*! - * The MIMO USRP device class: + * The MIMO USRP device class (DEPRECATED):   * A mimo usrp facilitates ease-of-use for multi-usrp scenarios.   * The wrapper provides convenience functions to control the group   * of underlying devices as if they consisted of a single device.   */ -class UHD_API mimo_usrp : boost::noncopyable{ +class UHD_API UHD_DEPRECATED mimo_usrp : boost::noncopyable{  public:      typedef boost::shared_ptr<mimo_usrp> sptr; @@ -148,6 +148,8 @@ public:       * \return the rssi in dB       */      virtual float read_rssi(size_t chan) = 0; +     +    virtual void set_rx_bandwidth(size_t chan, float bandwidth) = 0;      /*******************************************************************       * TX methods @@ -177,4 +179,347 @@ public:  }} +#include <uhd/utils/warning.hpp> +#include <uhd/usrp/tune_helper.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/gain_group.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/warning.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <uhd/usrp/mboard_props.hpp> +#include <uhd/usrp/device_props.hpp> +#include <uhd/usrp/dboard_props.hpp> +#include <uhd/usrp/dsp_props.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <stdexcept> +#include <iostream> + +namespace uhd{ namespace usrp{ namespace /*anon*/{ + +static inline freq_range_t add_dsp_shift(const freq_range_t &range, wax::obj dsp){ +    double codec_rate = dsp[DSP_PROP_CODEC_RATE].as<double>(); +    return freq_range_t(range.min - codec_rate/2.0, range.max + codec_rate/2.0); +} + +/*********************************************************************** + * MIMO USRP Implementation + **********************************************************************/ +class mimo_usrp_impl : public mimo_usrp{ +public: +    mimo_usrp_impl(const device_addr_t &addr){ +        _dev = device::make(addr); + +        //set the clock config across all mboards (TODO set through api) +        clock_config_t clock_config; +        clock_config.ref_source = clock_config_t::REF_SMA; +        clock_config.pps_source = clock_config_t::PPS_SMA; +        for (size_t chan = 0; chan < get_num_channels(); chan++){ +            _mboard(chan)[MBOARD_PROP_CLOCK_CONFIG] = clock_config; +        } +    } + +    ~mimo_usrp_impl(void){ +        /* NOP */ +    } + +    device::sptr get_device(void){ +        return _dev; +    } + +    std::string get_pp_string(void){ +        std::string buff = str(boost::format( +            "MIMO USRP:\n" +            "  Device: %s\n" +        ) +            % (*_dev)[DEVICE_PROP_NAME].as<std::string>() +        ); +        for (size_t chan = 0; chan < get_num_channels(); chan++){ +            buff += str(boost::format( +                "  Channel: %u\n" +                "    Mboard: %s\n" +                "    RX DSP: %s\n" +                "    RX Dboard: %s\n" +                "    RX Subdev: %s\n" +                "    TX DSP: %s\n" +                "    TX Dboard: %s\n" +                "    TX Subdev: %s\n" +            ) % chan +                % _mboard(chan)[MBOARD_PROP_NAME].as<std::string>() +                % _rx_dsp(chan)[DSP_PROP_NAME].as<std::string>() +                % _rx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() +                % _rx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() +                % _tx_dsp(chan)[DSP_PROP_NAME].as<std::string>() +                % _tx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() +                % _tx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() +            ); +        } +        return buff; +    } + +    size_t get_num_channels(void){ +        return (*_dev)[DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>().size(); +    } + +    /******************************************************************* +     * Misc +     ******************************************************************/ +    time_spec_t get_time_now(void){ +        //the time on the first mboard better be the same on all +        return _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +    } + +    void set_time_next_pps(const time_spec_t &time_spec){ +        for (size_t chan = 0; chan < get_num_channels(); chan++){ +            _mboard(chan)[MBOARD_PROP_TIME_NEXT_PPS] = time_spec; +        } +    } + +    void set_time_unknown_pps(const time_spec_t &time_spec){ +        std::cout << "Set time with unknown pps edge:" << std::endl; +        std::cout << "    1) set times next pps (race condition)" << std::endl; +        set_time_next_pps(time_spec); +        boost::this_thread::sleep(boost::posix_time::seconds(1)); + +        std::cout << "    2) catch seconds rollover at pps edge" << std::endl; +        time_t last_secs = 0, curr_secs = 0; +        while(curr_secs == last_secs){ +            last_secs = curr_secs; +            curr_secs = get_time_now().get_full_secs(); +        } + +        std::cout << "    3) set times next pps (synchronously)" << std::endl; +        set_time_next_pps(time_spec); +        boost::this_thread::sleep(boost::posix_time::seconds(1)); + +        //verify that the time registers are read to be within a few RTT +        for (size_t chan = 1; chan < get_num_channels(); chan++){ +            time_spec_t time_0 = _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +            time_spec_t time_i = _mboard(chan)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +            if (time_i < time_0 or (time_i - time_0) > time_spec_t(0.01)){ //10 ms: greater than RTT but not too big +                uhd::print_warning(str(boost::format( +                    "Detected time deviation between board %d and board 0.\n" +                    "Board 0 time is %f seconds.\n" +                    "Board %d time is %f seconds.\n" +                ) % chan % time_0.get_real_secs() % chan % time_i.get_real_secs())); +            } +        } +    } + +    void issue_stream_cmd(const stream_cmd_t &stream_cmd){ +        for (size_t chan = 0; chan < get_num_channels(); chan++){ +            _mboard(chan)[MBOARD_PROP_STREAM_CMD] = stream_cmd; +        } +    } + +    /******************************************************************* +     * RX methods +     ******************************************************************/ +    void set_rx_subdev_spec(size_t chan, const subdev_spec_t &spec){ +        UHD_ASSERT_THROW(spec.size() <= 1); +        _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC] = spec; +    } + +    subdev_spec_t get_rx_subdev_spec(size_t chan){ +        return _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>(); +    } + +    void set_rx_rate_all(double rate){ +        std::vector<double> _actual_rates; +        for (size_t chan = 0; chan < get_num_channels(); chan++){ +            _rx_dsp(chan)[DSP_PROP_HOST_RATE] = rate; +            _actual_rates.push_back(_rx_dsp(chan)[DSP_PROP_HOST_RATE].as<double>()); +        } +        _rx_rate = _actual_rates.front(); +        if (std::count(_actual_rates, _rx_rate) != _actual_rates.size()) throw std::runtime_error( +            "MIMO configuratio error: rx rate inconsistent across mboards" +        ); +    } + +    double get_rx_rate_all(void){ +        return _rx_rate; +    } + +    tune_result_t set_rx_freq(size_t chan, double target_freq){ +        return tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan), 0, target_freq); +    } + +    tune_result_t set_rx_freq(size_t chan, double target_freq, double lo_off){ +        return tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan), 0, target_freq, lo_off); +    } + +    double get_rx_freq(size_t chan){ +        return derive_freq_from_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan), 0); +    } + +    freq_range_t get_rx_freq_range(size_t chan){ +        return add_dsp_shift(_rx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _rx_dsp(chan)); +    } + +    void set_rx_gain(size_t chan, float gain){ +        return _rx_gain_group(chan)->set_value(gain); +    } + +    float get_rx_gain(size_t chan){ +        return _rx_gain_group(chan)->get_value(); +    } + +    gain_range_t get_rx_gain_range(size_t chan){ +        return _rx_gain_group(chan)->get_range(); +    } + +    void set_rx_antenna(size_t chan, const std::string &ant){ +        _rx_subdev(chan)[SUBDEV_PROP_ANTENNA] = ant; +    } + +    std::string get_rx_antenna(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); +    } + +    std::vector<std::string> get_rx_antennas(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); +    } + +    bool get_rx_lo_locked(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); +    } + +    float read_rssi(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_RSSI].as<float>(); +    } +     +    void set_rx_bandwidth(size_t chan, float bandwidth){ +        _rx_subdev(chan)[SUBDEV_PROP_BANDWIDTH] = bandwidth; +    } + +    /******************************************************************* +     * TX methods +     ******************************************************************/ +    void set_tx_subdev_spec(size_t chan, const subdev_spec_t &spec){ +        UHD_ASSERT_THROW(spec.size() <= 1); +        _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC] = spec; +    } + +    subdev_spec_t get_tx_subdev_spec(size_t chan){ +        return _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>(); +    } + +    void set_tx_rate_all(double rate){ +        std::vector<double> _actual_rates; +        for (size_t chan = 0; chan < get_num_channels(); chan++){ +            _tx_dsp(chan)[DSP_PROP_HOST_RATE] = rate; +            _actual_rates.push_back(_tx_dsp(chan)[DSP_PROP_HOST_RATE].as<double>()); +        } +        _tx_rate = _actual_rates.front(); +        if (std::count(_actual_rates, _tx_rate) != _actual_rates.size()) throw std::runtime_error( +            "MIMO configuratio error: tx rate inconsistent across mboards" +        ); +    } + +    double get_tx_rate_all(void){ +        return _tx_rate; +    } + +    tune_result_t set_tx_freq(size_t chan, double target_freq){ +        return tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan), 0, target_freq); +    } + +    tune_result_t set_tx_freq(size_t chan, double target_freq, double lo_off){ +        return tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan), 0, target_freq, lo_off); +    } + +    double get_tx_freq(size_t chan){ +        return derive_freq_from_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan), 0); +    } + +    freq_range_t get_tx_freq_range(size_t chan){ +        return add_dsp_shift(_tx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _tx_dsp(chan)); +    } + +    void set_tx_gain(size_t chan, float gain){ +        return _tx_gain_group(chan)->set_value(gain); +    } + +    float get_tx_gain(size_t chan){ +        return _tx_gain_group(chan)->get_value(); +    } + +    gain_range_t get_tx_gain_range(size_t chan){ +        return _tx_gain_group(chan)->get_range(); +    } + +    void set_tx_antenna(size_t chan, const std::string &ant){ +        _tx_subdev(chan)[SUBDEV_PROP_ANTENNA] = ant; +    } + +    std::string get_tx_antenna(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); +    } + +    std::vector<std::string> get_tx_antennas(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); +    } + +    bool get_tx_lo_locked(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); +    } + +private: +    device::sptr _dev; +    wax::obj _mboard(size_t chan){ +        prop_names_t names = (*_dev)[DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>(); +        return (*_dev)[named_prop_t(DEVICE_PROP_MBOARD, names.at(chan))]; +    } +    wax::obj _rx_dsp(size_t chan){ +        return _mboard(chan)[MBOARD_PROP_RX_DSP]; +    } +    wax::obj _tx_dsp(size_t chan){ +        return _mboard(chan)[MBOARD_PROP_TX_DSP]; +    } +    wax::obj _rx_dboard(size_t chan){ +        std::string db_name = _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().db_name; +        return _mboard(chan)[named_prop_t(MBOARD_PROP_RX_DBOARD, db_name)]; +    } +    wax::obj _tx_dboard(size_t chan){ +        std::string db_name = _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().db_name; +        return _mboard(chan)[named_prop_t(MBOARD_PROP_TX_DBOARD, db_name)]; +    } +    wax::obj _rx_subdev(size_t chan){ +        std::string sd_name = _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; +        return _rx_dboard(chan)[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; +    } +    wax::obj _tx_subdev(size_t chan){ +        std::string sd_name = _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; +        return _tx_dboard(chan)[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; +    } +    gain_group::sptr _rx_gain_group(size_t chan){ +        std::string sd_name = _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; +        return _rx_dboard(chan)[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); +    } +    gain_group::sptr _tx_gain_group(size_t chan){ +        std::string sd_name = _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; +        return _tx_dboard(chan)[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); +    } + +    //shadows +    double _rx_rate, _tx_rate; +}; +}}} + +namespace uhd{ namespace usrp{ +/*********************************************************************** + * The Make Function + **********************************************************************/ +inline mimo_usrp::sptr mimo_usrp::make(const device_addr_t &dev_addr){ +    uhd::print_warning( +        "The mimo USRP interface has been deprecated.\n" +        "Please switch to the multi USRP interface.\n" +        "#include <uhd/usrp/multi_usrp.hpp>\n" +        "multi_usrp::sptr sdev = multi_usrp::make(args);\n" +    ); +    return sptr(new mimo_usrp_impl(dev_addr)); +} +}} +  #endif /* INCLUDED_UHD_USRP_MIMO_USRP_HPP */ diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp new file mode 100644 index 000000000..6adba85bd --- /dev/null +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -0,0 +1,478 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_USRP_MULTI_USRP_HPP +#define INCLUDED_UHD_USRP_MULTI_USRP_HPP + +#include <uhd/config.hpp> +#include <uhd/device.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/stream_cmd.hpp> +#include <uhd/types/clock_config.hpp> +#include <uhd/types/tune_result.hpp> +#include <uhd/usrp/subdev_spec.hpp> +#include <uhd/usrp/dboard_iface.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <vector> + +namespace uhd{ namespace usrp{ + +/*! + * The multi-USRP device class: + * A multi-USRP facilitates ease-of-use for multiple USRP scenarios. + * The wrapper provides convenience functions to control the group + * of underlying devices as if they consisted of a single device. + * + * A few notes about a multi-USRP configuration: + *  - All boards share a common RX sample rate + *  - All boards share a common TX sample rate + *  - All boards share a common RX subdevice specification size + *  - All boards share a common TX subdevice specification size + *  - All boards must have synchronized times (see the set_time_*() calls) + * + * Example to setup channel mapping: + * <pre> + * + * //create a multi_usrp with two boards in the configuration + * device_addr_t dev_addr; + * dev_addr["addr"] = "192.168.10.2 192.168.10.3"; + * multi_usrp::sptr dev = multi_usrp::make(dev_addr); + * + * //set the board on 10.2 to use the A RX subdevice (RX channel 0) + * dev->set_rx_subdev_spec(":A", 0); + * + * //set the board on 10.3 to use the B RX subdevice (RX channel 1) + * dev->set_rx_subdev_spec(":B", 1); + * + * //set both boards to use the AB TX subdevice (TX channels 0 and 1) + * dev->set_tx_subdev_spec(":AB", multi_usrp::ALL_MBOARDS); + * + * //now that all the channels are mapped, continue with configuration... + * + * </pre> + */ +class UHD_API multi_usrp : boost::noncopyable{ +public: +    typedef boost::shared_ptr<multi_usrp> sptr; + +    //! A wildcard motherboard index +    static const size_t ALL_MBOARDS = size_t(~0); + +    /*! +     * Make a new multi usrp from the device address. +     * \param dev_addr the device address +     * \return a new single usrp object +     */ +    static sptr make(const device_addr_t &dev_addr); + +    /*! +     * Get the underlying device object. +     * This is needed to get access to the streaming API and properties. +     * \return the device object within this single usrp +     */ +    virtual device::sptr get_device(void) = 0; + +    /******************************************************************* +     * Mboard methods +     ******************************************************************/ +    /*! +     * Get a printable summary for this USRP configuration. +     * \return a printable string +     */ +    virtual std::string get_pp_string(void) = 0; + +    /*! +     * Get canonical name for this USRP motherboard. +     * \param mboard which motherboard to query +     * \return a string representing the name +     */ +    virtual std::string get_mboard_name(size_t mboard) = 0; + +    /*! +     * Gets the current time in the usrp time registers. +     * \return a timespec representing current usrp time +     */ +    virtual time_spec_t get_time_now(void) = 0; + +    /*! +     * Set the time registers on the usrp at the next pps tick. +     * The values will not be latched in until the pulse occurs. +     * It is recommended that the user sleep(1) after calling to ensure +     * that the time registers will be in a known state prior to use. +     * +     * Note: Because this call sets the time on the "next" pps, +     * the seconds in the time spec should be current seconds + 1. +     * +     * \param time_spec the time to latch into the usrp device +     */ +    virtual void set_time_next_pps(const time_spec_t &time_spec) = 0; + +    /*! +     * Synchronize the times across all motherboards in this configuration. +     * Use this method to sync the times when the edge of the PPS is unknown. +     * +     * Ex: Host machine is not attached to serial port of GPSDO +     * and can therefore not query the GPSDO for the PPS edge. +     * +     * This is a 3-step process, and will take at most 3 seconds to complete. +     * Upon completion, the times will be synchronized to the time provided. +     * +     * - Step1: set the time at the next pps (potential race condition) +     * - Step2: wait for the seconds to rollover to catch the pps edge +     * - Step3: set the time at the next pps (synchronous for all boards) +     * +     * \param time_spec the time to latch into the usrp device +     */ +    virtual void set_time_unknown_pps(const time_spec_t &time_spec) = 0; + +    /*! +     * Issue a stream command to the usrp device. +     * This tells the usrp to send samples into the host. +     * See the documentation for stream_cmd_t for more info. +     * \param stream_cmd the stream command to issue +     */ +    virtual void issue_stream_cmd(const stream_cmd_t &stream_cmd) = 0; + +    /*! +     * Set the clock configuration for the usrp device. +     * This tells the usrp how to get a 10Mhz reference and PPS clock. +     * See the documentation for clock_config_t for more info. +     * \param clock_config the clock configuration to set +     * \param mboard which motherboard to set the config +     */ +    virtual void set_clock_config(const clock_config_t &clock_config, size_t mboard) = 0; + +    /*! +     * Get the number of USRP motherboards in this configuration. +     */ +    virtual size_t get_num_mboards(void) = 0; + +    /******************************************************************* +     * RX methods +     ******************************************************************/ +    /*! +     * Set the RX subdevice specification: +     * The subdev spec maps a physical part of a daughter-board to a channel number. +     * Set the subdev spec before calling into any methods with a channel number. +     * The subdev spec must be the same size across all motherboards. +     * \param spec the new subdevice specification +     * \param mboard the motherboard index 0 to M-1 +     */ +    virtual void set_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec, size_t mboard) = 0; + +    /*! +     * Get the RX subdevice specification. +     * \param mboard the motherboard index 0 to M-1 +     * \return the subdevice specification in use +     */ +    virtual uhd::usrp::subdev_spec_t get_rx_subdev_spec(size_t mboard) = 0; + +    /*! +     * Get the number of RX channels in this configuration. +     * This is the number of USRPs times the number of RX channels per board, +     * where the number of RX channels per board is homogeneous among all USRPs. +     */ +    virtual size_t get_rx_num_channels(void) = 0; + +    /*! +     * Get the name of the RX subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the subdevice name +     */ +    virtual std::string get_rx_subdev_name(size_t chan) = 0; + +    /*! +     * Set the RX sample rate across all channels. +     * \param rate the rate in Sps +     */ +    virtual void set_rx_rate(double rate) = 0; + +    /*! +     * Gets the RX sample rate for all channels. +     * \return the rate in Sps +     */ +    virtual double get_rx_rate(void) = 0; + +    /*! +     * Set the RX center frequency. +     * \param freq the frequency in Hz +     * \param chan the channel index 0 to N-1 +     * \return a tune result object +     */ +    virtual tune_result_t set_rx_freq(double freq, size_t chan) = 0; + +    /*! +     * Set the RX center frequency. +     * \param freq the frequency in Hz +     * \param lo_off an LO offset in Hz +     * \param chan the channel index 0 to N-1 +     * \return a tune result object +     */ +    virtual tune_result_t set_rx_freq(double freq, double lo_off, size_t chan) = 0; + +    /*! +     * Get the RX center frequency. +     * \param chan the channel index 0 to N-1 +     * \return the frequency in Hz +     */ +    virtual double get_rx_freq(size_t chan) = 0; + +    /*! +     * Get the RX center frequency range. +     * \param chan the channel index 0 to N-1 +     * \return a frequency range object +     */ +    virtual freq_range_t get_rx_freq_range(size_t chan) = 0; + +    /*! +     * Set the RX gain: +     * Distribute among gain elements in the RX path. +     * \param gain the gain in dB +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_rx_gain(float gain, size_t chan) = 0; + +    /*! +     * Get the RX gain: +     * Summation of gain elements in the RX path. +     * \param chan the channel index 0 to N-1 +     * \return the gain in dB +     */ +    virtual float get_rx_gain(size_t chan) = 0; + +    /*! +     * Get the RX gain range. +     * \param chan the channel index 0 to N-1 +     * \return a gain range object +     */ +    virtual gain_range_t get_rx_gain_range(size_t chan) = 0; + +    /*! +     * Select the RX antenna on the subdevice. +     * \param ant the antenna name +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_rx_antenna(const std::string &ant, size_t chan) = 0; + +    /*! +     * Get the selected RX antenna on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the antenna name +     */ +    virtual std::string get_rx_antenna(size_t chan) = 0; + +    /*! +     * Get a list of possible RX antennas on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return a vector of antenna names +     */ +    virtual std::vector<std::string> get_rx_antennas(size_t chan) = 0; + +    /*! +     * Get the locked status of the LO on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return true for locked +     */ +    virtual bool get_rx_lo_locked(size_t chan) = 0; + +    /*! +     * Set the RX bandwidth on the subdevice. +     * \param bandwidth the bandwidth in Hz +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_rx_bandwidth(double bandwidth, size_t chan) = 0; + +    /*! +     * Get the RX bandwidth on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the bandwidth in Hz +     */ +    virtual double get_rx_bandwidth(size_t chan) = 0; + +    /*! +     * Read the RSSI value on the RX subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the rssi in dB +     * \throw exception if RSSI readback not supported +     */ +    virtual float read_rssi(size_t chan) = 0; + +    /*! +     * Get the dboard interface object for the RX subdevice. +     * The dboard interface gives access to GPIOs, SPI, I2C, low-speed ADC and DAC. +     * Use at your own risk! +     * \param chan the channel index 0 to N-1 +     * \return the dboard interface sptr +     */ +    virtual dboard_iface::sptr get_rx_dboard_iface(size_t chan) = 0; + +    /******************************************************************* +     * TX methods +     ******************************************************************/ +    /*! +     * Set the TX subdevice specification: +     * The subdev spec maps a physical part of a daughter-board to a channel number. +     * Set the subdev spec before calling into any methods with a channel number. +     * The subdev spec must be the same size across all motherboards. +     * \param spec the new subdevice specification +     * \param mboard the motherboard index 0 to M-1 +     */ +    virtual void set_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec, size_t mboard) = 0; + +    /*! +     * Get the TX subdevice specification. +     * \param mboard the motherboard index 0 to M-1 +     * \return the subdevice specification in use +     */ +    virtual uhd::usrp::subdev_spec_t get_tx_subdev_spec(size_t mboard) = 0; + +    /*! +     * Get the number of TX channels in this configuration. +     * This is the number of USRPs times the number of TX channels per board, +     * where the number of TX channels per board is homogeneous among all USRPs. +     */ +    virtual size_t get_tx_num_channels(void) = 0; + +    /*! +     * Get the name of the TX subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the subdevice name +     */ +    virtual std::string get_tx_subdev_name(size_t chan) = 0; + +    /*! +     * Set the TX sample rate across all channels. +     * \param rate the rate in Sps +     */ +    virtual void set_tx_rate(double rate) = 0; + +    /*! +     * Gets the TX sample rate for all channels. +     * \return the rate in Sps +     */ +    virtual double get_tx_rate(void) = 0; + +    /*! +     * Set the TX center frequency. +     * \param freq the frequency in Hz +     * \param chan the channel index 0 to N-1 +     * \return a tune result object +     */ +    virtual tune_result_t set_tx_freq(double freq, size_t chan) = 0; + +    /*! +     * Set the TX center frequency. +     * \param freq the frequency in Hz +     * \param lo_off an LO offset in Hz +     * \param chan the channel index 0 to N-1 +     * \return a tune result object +     */ +    virtual tune_result_t set_tx_freq(double freq, double lo_off, size_t chan) = 0; + +    /*! +     * Get the TX center frequency. +     * \param chan the channel index 0 to N-1 +     * \return the frequency in Hz +     */ +    virtual double get_tx_freq(size_t chan) = 0; + +    /*! +     * Get the TX center frequency range. +     * \param chan the channel index 0 to N-1 +     * \return a frequency range object +     */ +    virtual freq_range_t get_tx_freq_range(size_t chan) = 0; + +    /*! +     * Set the TX gain: +     * Distribute among gain elements in the TX path. +     * \param gain the gain in dB +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_tx_gain(float gain, size_t chan) = 0; + +    /*! +     * Get the TX gain: +     * Summation of gain elements in the TX path. +     * \param chan the channel index 0 to N-1 +     * \return the gain in dB +     */ +    virtual float get_tx_gain(size_t chan) = 0; + +    /*! +     * Get the TX gain range. +     * \param chan the channel index 0 to N-1 +     * \return a gain range object +     */ +    virtual gain_range_t get_tx_gain_range(size_t chan) = 0; + +    /*! +     * Select the TX antenna on the subdevice. +     * \param ant the antenna name +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_tx_antenna(const std::string &ant, size_t chan) = 0; + +    /*! +     * Get the selected TX antenna on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the antenna name +     */ +    virtual std::string get_tx_antenna(size_t chan) = 0; + +    /*! +     * Get a list of possible TX antennas on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return a vector of antenna names +     */ +    virtual std::vector<std::string> get_tx_antennas(size_t chan) = 0; + +    /*! +     * Get the locked status of the LO on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return true for locked +     */ +    virtual bool get_tx_lo_locked(size_t chan) = 0; + +    /*! +     * Set the TX bandwidth on the subdevice. +     * \param bandwidth the bandwidth in Hz +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_tx_bandwidth(double bandwidth, size_t chan) = 0; + +    /*! +     * Get the TX bandwidth on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the bandwidth in Hz +     */ +    virtual double get_tx_bandwidth(size_t chan) = 0; + +    /*! +     * Get the dboard interface object for the TX subdevice. +     * The dboard interface gives access to GPIOs, SPI, I2C, low-speed ADC and DAC. +     * Use at your own risk! +     * \param chan the channel index 0 to N-1 +     * \return the dboard interface sptr +     */ +    virtual dboard_iface::sptr get_tx_dboard_iface(size_t chan) = 0; +}; + +}} + +#endif /* INCLUDED_UHD_USRP_MULTI_USRP_HPP */ diff --git a/host/include/uhd/usrp/simple_usrp.hpp b/host/include/uhd/usrp/simple_usrp.hpp index 08b9c01ea..22f4d64ba 100644 --- a/host/include/uhd/usrp/simple_usrp.hpp +++ b/host/include/uhd/usrp/simple_usrp.hpp @@ -33,12 +33,13 @@  namespace uhd{ namespace usrp{  /*! - * The simple USRP device class: + * The simple USRP device class (DEPRECATED): + * This interface has been deprecated in favor of the single USRP interface.   * A simple usrp facilitates ease-of-use for most use-case scenarios.   * The wrapper provides convenience functions to tune the devices   * as well as to set the dboard gains, antennas, and other properties.   */ -class UHD_API simple_usrp : boost::noncopyable{ +class UHD_API UHD_DEPRECATED simple_usrp : boost::noncopyable{  public:      typedef boost::shared_ptr<simple_usrp> sptr; @@ -138,6 +139,8 @@ public:      virtual float read_rssi(void) = 0;      virtual dboard_iface::sptr get_rx_dboard_iface(void) = 0; +     +    virtual void set_rx_bandwidth(float) = 0;      /*******************************************************************       * TX methods @@ -168,4 +171,218 @@ public:  }} +#include <uhd/usrp/single_usrp.hpp> +#include <uhd/utils/warning.hpp> + +namespace uhd{ namespace usrp{ namespace /*anon*/{ + +/*********************************************************************** + * Simple USRP Implementation + **********************************************************************/ +class simple_usrp_impl : public simple_usrp{ +public: +    simple_usrp_impl(const device_addr_t &addr){ +        _sdev = single_usrp::make(addr); +    } + +    ~simple_usrp_impl(void){ +        /* NOP */ +    } + +    device::sptr get_device(void){ +        return _sdev->get_device(); +    } + +    std::string get_pp_string(void){ +        return _sdev->get_pp_string(); +    } + +    /******************************************************************* +     * Misc +     ******************************************************************/ +    time_spec_t get_time_now(void){ +        return _sdev->get_time_now(); +    } + +    void set_time_now(const time_spec_t &time_spec){ +        return _sdev->set_time_now(time_spec); +    } + +    void set_time_next_pps(const time_spec_t &time_spec){ +        return _sdev->set_time_next_pps(time_spec); +    } + +    void issue_stream_cmd(const stream_cmd_t &stream_cmd){ +        return _sdev->issue_stream_cmd(stream_cmd); +    } + +    void set_clock_config(const clock_config_t &clock_config){ +        return _sdev->set_clock_config(clock_config); +    } + +    /******************************************************************* +     * RX methods +     ******************************************************************/ +    void set_rx_subdev_spec(const subdev_spec_t &spec){ +        return _sdev->set_rx_subdev_spec(spec); +    } + +    subdev_spec_t get_rx_subdev_spec(void){ +        return _sdev->get_rx_subdev_spec(); +    } + +    void set_rx_rate(double rate){ +        return _sdev->set_rx_rate(rate); +    } + +    double get_rx_rate(void){ +        return _sdev->get_rx_rate(); +    } + +    tune_result_t set_rx_freq(double target_freq){ +        return _sdev->set_rx_freq(target_freq); +    } + +    tune_result_t set_rx_freq(double target_freq, double lo_off){ +        return _sdev->set_rx_freq(target_freq, lo_off); +    } + +    double get_rx_freq(void){ +        return _sdev->get_rx_freq(); +    } + +    freq_range_t get_rx_freq_range(void){ +        return _sdev->get_rx_freq_range(); +    } + +    void set_rx_gain(float gain){ +        return _sdev->set_rx_gain(gain); +    } + +    float get_rx_gain(void){ +        return _sdev->get_rx_gain(); +    } + +    gain_range_t get_rx_gain_range(void){ +        return _sdev->get_rx_gain_range(); +    } + +    void set_rx_antenna(const std::string &ant){ +        return _sdev->set_rx_antenna(ant); +    } + +    std::string get_rx_antenna(void){ +        return _sdev->get_rx_antenna(); +    } + +    std::vector<std::string> get_rx_antennas(void){ +        return _sdev->get_rx_antennas(); +    } + +    bool get_rx_lo_locked(void){ +        return _sdev->get_rx_lo_locked(); +    } + +    float read_rssi(void){ +        return _sdev->read_rssi(); +    } + +    dboard_iface::sptr get_rx_dboard_iface(void){ +        return _sdev->get_rx_dboard_iface(); +    } +     +    void set_rx_bandwidth(float bandwidth) { +        return _sdev->set_rx_bandwidth(bandwidth); +    } + +    /******************************************************************* +     * TX methods +     ******************************************************************/ +    void set_tx_subdev_spec(const subdev_spec_t &spec){ +        return _sdev->set_tx_subdev_spec(spec); +    } + +    subdev_spec_t get_tx_subdev_spec(void){ +        return _sdev->get_tx_subdev_spec(); +    } + +    void set_tx_rate(double rate){ +        return _sdev->set_tx_rate(rate); +    } + +    double get_tx_rate(void){ +        return _sdev->get_tx_rate(); +    } + +    tune_result_t set_tx_freq(double target_freq){ +        return _sdev->set_tx_freq(target_freq); +    } + +    tune_result_t set_tx_freq(double target_freq, double lo_off){ +        return _sdev->set_tx_freq(target_freq, lo_off); +    } + +    double get_tx_freq(void){ +        return _sdev->get_tx_freq(); +    } + +    freq_range_t get_tx_freq_range(void){ +        return _sdev->get_tx_freq_range(); +    } + +    void set_tx_gain(float gain){ +        return _sdev->set_tx_gain(gain); +    } + +    float get_tx_gain(void){ +        return _sdev->get_tx_gain(); +    } + +    gain_range_t get_tx_gain_range(void){ +        return _sdev->get_tx_gain_range(); +    } + +    void set_tx_antenna(const std::string &ant){ +        return _sdev->set_tx_antenna(ant); +    } + +    std::string get_tx_antenna(void){ +        return _sdev->get_tx_antenna(); +    } + +    std::vector<std::string> get_tx_antennas(void){ +        return _sdev->get_tx_antennas(); +    } + +    bool get_tx_lo_locked(void){ +        return _sdev->get_tx_lo_locked(); +    } + +    dboard_iface::sptr get_tx_dboard_iface(void){ +        return _sdev->get_tx_dboard_iface(); +    } + +private: +    single_usrp::sptr _sdev; +}; + +}}} + +namespace uhd{ namespace usrp{ + +/*********************************************************************** + * The Make Function + **********************************************************************/ +inline simple_usrp::sptr simple_usrp::make(const device_addr_t &dev_addr){ +    uhd::print_warning( +        "The simple USRP interface has been deprecated.\n" +        "Please switch to the single USRP interface.\n" +        "#include <uhd/usrp/single_usrp.hpp>\n" +        "single_usrp::sptr sdev = single_usrp::make(args);\n" +    ); +    return sptr(new simple_usrp_impl(dev_addr)); +} + +}} +  #endif /* INCLUDED_UHD_USRP_SIMPLE_USRP_HPP */ diff --git a/host/include/uhd/usrp/single_usrp.hpp b/host/include/uhd/usrp/single_usrp.hpp new file mode 100644 index 000000000..74a978f05 --- /dev/null +++ b/host/include/uhd/usrp/single_usrp.hpp @@ -0,0 +1,411 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_USRP_SINGLE_USRP_HPP +#define INCLUDED_UHD_USRP_SINGLE_USRP_HPP + +#include <uhd/config.hpp> +#include <uhd/device.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/stream_cmd.hpp> +#include <uhd/types/clock_config.hpp> +#include <uhd/types/tune_result.hpp> +#include <uhd/usrp/subdev_spec.hpp> +#include <uhd/usrp/dboard_iface.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <vector> + +namespace uhd{ namespace usrp{ + +/*! + * The single-USRP device class: + * A single-USRP facilitates ease-of-use for most use-case scenarios. + * The wrapper provides convenience functions to tune the devices + * as well as to set the dboard gains, antennas, and other properties. + * This wrapper supports multi-channel configurations per motherboard. + */ +class UHD_API single_usrp : boost::noncopyable{ +public: +    typedef boost::shared_ptr<single_usrp> sptr; + +    /*! +     * Make a new single usrp from the device address. +     * \param dev_addr the device address +     * \return a new single usrp object +     */ +    static sptr make(const device_addr_t &dev_addr); + +    /*! +     * Get the underlying device object. +     * This is needed to get access to the streaming API and properties. +     * \return the device object within this single usrp +     */ +    virtual device::sptr get_device(void) = 0; + +    /******************************************************************* +     * Mboard methods +     ******************************************************************/ +    /*! +     * Get a printable summary for this USRP configuration. +     * \return a printable string +     */ +    virtual std::string get_pp_string(void) = 0; + +    /*! +     * Get canonical name for this USRP motherboard. +     * \return a string representing the name +     */ +    virtual std::string get_mboard_name(void) = 0; + +    /*! +     * Gets the current time in the usrp time registers. +     * \return a timespec representing current usrp time +     */ +    virtual time_spec_t get_time_now(void) = 0; + +    /*! +     * Sets the time registers on the usrp immediately. +     * \param time_spec the time to latch into the usrp device +     */ +    virtual void set_time_now(const time_spec_t &time_spec) = 0; + +    /*! +     * Set the time registers on the usrp at the next pps tick. +     * The values will not be latched in until the pulse occurs. +     * It is recommended that the user sleep(1) after calling to ensure +     * that the time registers will be in a known state prior to use. +     * +     * Note: Because this call sets the time on the "next" pps, +     * the seconds in the time spec should be current seconds + 1. +     * +     * \param time_spec the time to latch into the usrp device +     */ +    virtual void set_time_next_pps(const time_spec_t &time_spec) = 0; + +    /*! +     * Issue a stream command to the usrp device. +     * This tells the usrp to send samples into the host. +     * See the documentation for stream_cmd_t for more info. +     * \param stream_cmd the stream command to issue +     */ +    virtual void issue_stream_cmd(const stream_cmd_t &stream_cmd) = 0; + +    /*! +     * Set the clock configuration for the usrp device. +     * This tells the usrp how to get a 10Mhz reference and PPS clock. +     * See the documentation for clock_config_t for more info. +     * \param clock_config the clock configuration to set +     */ +    virtual void set_clock_config(const clock_config_t &clock_config) = 0; + +    /******************************************************************* +     * RX methods +     ******************************************************************/ +    /*! +     * Set the RX subdevice specification: +     * The subdev spec maps a physical part of a daughter-board to a channel number. +     * Set the subdev spec before calling into any methods with a channel number. +     * The subdev spec must be the same size across all motherboards. +     * \param spec the new subdevice specification +     */ +    virtual void set_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec) = 0; + +    /*! +     * Get the RX subdevice specification. +     * \return the subdevice specification in use +     */ +    virtual uhd::usrp::subdev_spec_t get_rx_subdev_spec(void) = 0; + +    /*! +     * Get the name of the RX subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the subdevice name +     */ +    virtual std::string get_rx_subdev_name(size_t chan = 0) = 0; + +    /*! +     * Set the RX sample rate across all channels. +     * \param rate the rate in Sps +     */ +    virtual void set_rx_rate(double rate) = 0; + +    /*! +     * Gets the RX sample rate for all channels. +     * \return the rate in Sps +     */ +    virtual double get_rx_rate(void) = 0; + +    /*! +     * Set the RX center frequency. +     * \param freq the frequency in Hz +     * \param chan the channel index 0 to N-1 +     * \return a tune result object +     */ +    virtual tune_result_t set_rx_freq(double freq, size_t chan = 0) = 0; + +    /*! +     * Set the RX center frequency. +     * \param freq the frequency in Hz +     * \param lo_off an LO offset in Hz +     * \param chan the channel index 0 to N-1 +     * \return a tune result object +     */ +    virtual tune_result_t set_rx_freq(double freq, double lo_off, size_t chan = 0) = 0; + +    /*! +     * Get the RX center frequency. +     * \param chan the channel index 0 to N-1 +     * \return the frequency in Hz +     */ +    virtual double get_rx_freq(size_t chan = 0) = 0; + +    /*! +     * Get the RX center frequency range. +     * \param chan the channel index 0 to N-1 +     * \return a frequency range object +     */ +    virtual freq_range_t get_rx_freq_range(size_t chan = 0) = 0; + +    /*! +     * Set the RX gain: +     * Distribute among gain elements in the RX path. +     * \param gain the gain in dB +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_rx_gain(float gain, size_t chan = 0) = 0; + +    /*! +     * Get the RX gain: +     * Summation of gain elements in the RX path. +     * \param chan the channel index 0 to N-1 +     * \return the gain in dB +     */ +    virtual float get_rx_gain(size_t chan = 0) = 0; + +    /*! +     * Get the RX gain range. +     * \param chan the channel index 0 to N-1 +     * \return a gain range object +     */ +    virtual gain_range_t get_rx_gain_range(size_t chan = 0) = 0; + +    /*! +     * Select the RX antenna on the subdevice. +     * \param ant the antenna name +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_rx_antenna(const std::string &ant, size_t chan = 0) = 0; + +    /*! +     * Get the selected RX antenna on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the antenna name +     */ +    virtual std::string get_rx_antenna(size_t chan = 0) = 0; + +    /*! +     * Get a list of possible RX antennas on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return a vector of antenna names +     */ +    virtual std::vector<std::string> get_rx_antennas(size_t chan = 0) = 0; + +    /*! +     * Get the locked status of the LO on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return true for locked +     */ +    virtual bool get_rx_lo_locked(size_t chan = 0) = 0; + +    /*! +     * Set the RX bandwidth on the subdevice. +     * \param bandwidth the bandwidth in Hz +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_rx_bandwidth(double bandwidth, size_t chan = 0) = 0; + +    /*! +     * Get the RX bandwidth on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the bandwidth in Hz +     */ +    virtual double get_rx_bandwidth(size_t chan = 0) = 0; + +    /*! +     * Read the RSSI value on the RX subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the rssi in dB +     * \throw exception if RSSI readback not supported +     */ +    virtual float read_rssi(size_t chan = 0) = 0; + +    /*! +     * Get the dboard interface object for the RX subdevice. +     * The dboard interface gives access to GPIOs, SPI, I2C, low-speed ADC and DAC. +     * Use at your own risk! +     * \param chan the channel index 0 to N-1 +     * \return the dboard interface sptr +     */ +    virtual dboard_iface::sptr get_rx_dboard_iface(size_t chan = 0) = 0; + +    /******************************************************************* +     * TX methods +     ******************************************************************/ +    /*! +     * Set the TX subdevice specification: +     * The subdev spec maps a physical part of a daughter-board to a channel number. +     * Set the subdev spec before calling into any methods with a channel number. +     * The subdev spec must be the same size across all motherboards. +     * \param spec the new subdevice specification +     */ +    virtual void set_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec) = 0; + +    /*! +     * Get the TX subdevice specification. +     * \return the subdevice specification in use +     */ +    virtual uhd::usrp::subdev_spec_t get_tx_subdev_spec(void) = 0; + +    /*! +     * Get the name of the TX subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the subdevice name +     */ +    virtual std::string get_tx_subdev_name(size_t chan = 0) = 0; + +    /*! +     * Set the TX sample rate across all channels. +     * \param rate the rate in Sps +     */ +    virtual void set_tx_rate(double rate) = 0; + +    /*! +     * Gets the TX sample rate for all channels. +     * \return the rate in Sps +     */ +    virtual double get_tx_rate(void) = 0; + +    /*! +     * Set the TX center frequency. +     * \param freq the frequency in Hz +     * \param chan the channel index 0 to N-1 +     * \return a tune result object +     */ +    virtual tune_result_t set_tx_freq(double freq, size_t chan = 0) = 0; + +    /*! +     * Set the TX center frequency. +     * \param freq the frequency in Hz +     * \param lo_off an LO offset in Hz +     * \param chan the channel index 0 to N-1 +     * \return a tune result object +     */ +    virtual tune_result_t set_tx_freq(double freq, double lo_off, size_t chan = 0) = 0; + +    /*! +     * Get the TX center frequency. +     * \param chan the channel index 0 to N-1 +     * \return the frequency in Hz +     */ +    virtual double get_tx_freq(size_t chan = 0) = 0; + +    /*! +     * Get the TX center frequency range. +     * \param chan the channel index 0 to N-1 +     * \return a frequency range object +     */ +    virtual freq_range_t get_tx_freq_range(size_t chan = 0) = 0; + +    /*! +     * Set the TX gain: +     * Distribute among gain elements in the TX path. +     * \param gain the gain in dB +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_tx_gain(float gain, size_t chan = 0) = 0; + +    /*! +     * Get the TX gain: +     * Summation of gain elements in the TX path. +     * \param chan the channel index 0 to N-1 +     * \return the gain in dB +     */ +    virtual float get_tx_gain(size_t chan = 0) = 0; + +    /*! +     * Get the TX gain range. +     * \param chan the channel index 0 to N-1 +     * \return a gain range object +     */ +    virtual gain_range_t get_tx_gain_range(size_t chan = 0) = 0; + +    /*! +     * Select the TX antenna on the subdevice. +     * \param ant the antenna name +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_tx_antenna(const std::string &ant, size_t chan = 0) = 0; + +    /*! +     * Get the selected TX antenna on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the antenna name +     */ +    virtual std::string get_tx_antenna(size_t chan = 0) = 0; + +    /*! +     * Get a list of possible TX antennas on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return a vector of antenna names +     */ +    virtual std::vector<std::string> get_tx_antennas(size_t chan = 0) = 0; + +    /*! +     * Get the locked status of the LO on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return true for locked +     */ +    virtual bool get_tx_lo_locked(size_t chan = 0) = 0; + +    /*! +     * Set the TX bandwidth on the subdevice. +     * \param bandwidth the bandwidth in Hz +     * \param chan the channel index 0 to N-1 +     */ +    virtual void set_tx_bandwidth(double bandwidth, size_t chan = 0) = 0; + +    /*! +     * Get the TX bandwidth on the subdevice. +     * \param chan the channel index 0 to N-1 +     * \return the bandwidth in Hz +     */ +    virtual double get_tx_bandwidth(size_t chan = 0) = 0; + +    /*! +     * Get the dboard interface object for the TX subdevice. +     * The dboard interface gives access to GPIOs, SPI, I2C, low-speed ADC and DAC. +     * Use at your own risk! +     * \param chan the channel index 0 to N-1 +     * \return the dboard interface sptr +     */ +    virtual dboard_iface::sptr get_tx_dboard_iface(size_t chan = 0) = 0; +}; + +}} + +#endif /* INCLUDED_UHD_USRP_SINGLE_USRP_HPP */ diff --git a/host/include/uhd/usrp/subdev_props.hpp b/host/include/uhd/usrp/subdev_props.hpp index cd07cb7a8..8f096ffe4 100644 --- a/host/include/uhd/usrp/subdev_props.hpp +++ b/host/include/uhd/usrp/subdev_props.hpp @@ -53,6 +53,7 @@ namespace uhd{ namespace usrp{          SUBDEV_PROP_ANTENNA_NAMES     = 'A', //ro, prop_names_t          SUBDEV_PROP_LO_LOCKED         = 'L', //ro, bool          SUBDEV_PROP_CONNECTION        = 'c', //ro, subdev_conn_t +        SUBDEV_PROP_ENABLED           = 'e', //rw, bool          SUBDEV_PROP_USE_LO_OFFSET     = 'l', //ro, bool          SUBDEV_PROP_RSSI              = 'R', //ro, float          SUBDEV_PROP_BANDWIDTH         = 'B'  //rw, double diff --git a/host/include/uhd/usrp/subdev_spec.hpp b/host/include/uhd/usrp/subdev_spec.hpp index 2f32509b9..5de3bb3b8 100644 --- a/host/include/uhd/usrp/subdev_spec.hpp +++ b/host/include/uhd/usrp/subdev_spec.hpp @@ -19,6 +19,7 @@  #define INCLUDED_UHD_USRP_SUBDEV_SPEC_HPP  #include <uhd/config.hpp> +#include <boost/operators.hpp>  #include <vector>  #include <string> @@ -27,7 +28,7 @@ namespace uhd{ namespace usrp{      /*!       * A subdevice specification (daughterboard, subdevice) name pairing.       */ -    struct UHD_API subdev_spec_pair_t{ +    struct UHD_API subdev_spec_pair_t : boost::equality_comparable<subdev_spec_pair_t>{          //! The daughterboard name          std::string db_name; @@ -45,6 +46,9 @@ namespace uhd{ namespace usrp{          );      }; +    //! overloaded comparison operator for subdev_spec_pair_t +    UHD_API bool operator==(const subdev_spec_pair_t &, const subdev_spec_pair_t &); +      /*!       * A list of (daughterboard name, subdevice name) pairs:       * diff --git a/host/include/uhd/usrp/tune_helper.hpp b/host/include/uhd/usrp/tune_helper.hpp index df3907b3e..ec133fa08 100644 --- a/host/include/uhd/usrp/tune_helper.hpp +++ b/host/include/uhd/usrp/tune_helper.hpp @@ -31,12 +31,13 @@ namespace uhd{ namespace usrp{       * The ddc cordic is setup to bring the IF down to baseband.       * \param subdev the dboard subdevice object with properties       * \param ddc the mboard dsp object with properties +     * \param chan the channel of the dsp to tune       * \param target_freq the desired center frequency       * \param lo_offset an offset for the subdevice IF from center       * \return a tune result struct       */      UHD_API tune_result_t tune_rx_subdev_and_dsp( -        wax::obj subdev, wax::obj ddc, +        wax::obj subdev, wax::obj ddc, size_t chan,          double target_freq, double lo_offset      ); @@ -46,17 +47,19 @@ namespace uhd{ namespace usrp{       * is calculated based on the subdevice and BW.       */      UHD_API tune_result_t tune_rx_subdev_and_dsp( -        wax::obj subdev, wax::obj ddc, double target_freq +        wax::obj subdev, wax::obj ddc, +        size_t chan, double target_freq      );      /*!       * Calculate the overall frequency from the combination of dboard IF and DDC shift.       * \param subdev the dboard subdevice object with properties       * \param ddc the mboard dsp object with properties +     * \param chan the channel of the dsp to tune       * \return the overall tune frequency of the system in Hz       */      UHD_API double derive_freq_from_rx_subdev_and_dsp( -        wax::obj subdev, wax::obj ddc +        wax::obj subdev, wax::obj ddc, size_t chan      );      /*! @@ -66,12 +69,13 @@ namespace uhd{ namespace usrp{       * The duc cordic is setup to bring the baseband up to IF.       * \param subdev the dboard subdevice object with properties       * \param duc the mboard dsp object with properties +     * \param chan the channel of the dsp to tune       * \param target_freq the desired center frequency       * \param lo_offset an offset for the subdevice IF from center       * \return a tune result struct       */      UHD_API tune_result_t tune_tx_subdev_and_dsp( -        wax::obj subdev, wax::obj duc, +        wax::obj subdev, wax::obj duc, size_t chan,          double target_freq, double lo_offset      ); @@ -81,17 +85,19 @@ namespace uhd{ namespace usrp{       * is calculated based on the subdevice and BW.       */      UHD_API tune_result_t tune_tx_subdev_and_dsp( -        wax::obj subdev, wax::obj duc, double target_freq +        wax::obj subdev, wax::obj duc, +        size_t chan, double target_freq      );      /*!       * Calculate the overall frequency from the combination of dboard IF and DUC shift.       * \param subdev the dboard subdevice object with properties       * \param duc the mboard dsp object with properties +     * \param chan the channel of the dsp to tune       * \return the overall tune frequency of the system in Hz       */      UHD_API double derive_freq_from_tx_subdev_and_dsp( -        wax::obj subdev, wax::obj duc +        wax::obj subdev, wax::obj duc, size_t chan      );  }} diff --git a/host/lib/device.cpp b/host/lib/device.cpp index d575ebaab..386588a08 100644 --- a/host/lib/device.cpp +++ b/host/lib/device.cpp @@ -26,6 +26,7 @@  #include <boost/functional/hash.hpp>  #include <boost/tuple/tuple.hpp>  #include <stdexcept> +#include <iostream>  using namespace uhd; @@ -73,12 +74,17 @@ device_addrs_t device::find(const device_addr_t &hint){      device_addrs_t device_addrs;      BOOST_FOREACH(const 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() -        ); +        try{ +            device_addrs_t discovered_addrs = fcn.get<0>()(hint); +            device_addrs.insert( +                device_addrs.begin(), +                discovered_addrs.begin(), +                discovered_addrs.end() +            ); +        } +        catch(const std::exception &e){ +            std::cerr << "Device discovery error: " << e.what() << std::endl; +        }      }      return device_addrs; diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt index f8e15c13d..25f34a280 100644 --- a/host/lib/ic_reg_maps/CMakeLists.txt +++ b/host/lib/ic_reg_maps/CMakeLists.txt @@ -68,3 +68,8 @@ LIBUHD_PYTHON_GEN_SOURCE(      ${CMAKE_SOURCE_DIR}/lib/ic_reg_maps/gen_ad9522_regs.py      ${CMAKE_BINARY_DIR}/lib/ic_reg_maps/ad9522_regs.hpp  ) + +LIBUHD_PYTHON_GEN_SOURCE( +    ${CMAKE_SOURCE_DIR}/lib/ic_reg_maps/gen_tuner_4937di5_regs.py +    ${CMAKE_BINARY_DIR}/lib/ic_reg_maps/tuner_4937di5_regs.hpp +) diff --git a/host/lib/ic_reg_maps/gen_max2118_regs.py b/host/lib/ic_reg_maps/gen_max2118_regs.py index 506fbaec8..506fbaec8 100644..100755 --- a/host/lib/ic_reg_maps/gen_max2118_regs.py +++ b/host/lib/ic_reg_maps/gen_max2118_regs.py diff --git a/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py b/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py new file mode 100644 index 000000000..73f7aa3db --- /dev/null +++ b/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# +# 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/>. +# + +######################################################################## +# Template for raw text data describing registers +# name addr[bit range inclusive] default optional enums +######################################################################## +REGS_TMPL="""\ +######################################################################## +## Note: offsets given from perspective of data bits (excludes address) +######################################################################## +## Divider byte 1 +######################################################################## +db1                   0[0:6]        0x00 +######################################################################## +## Divider byte 2 +######################################################################## +db2                   1[0:7]        0x00 +######################################################################## +## Control byte 1 +######################################################################## +cb7                   2[7]          0x01 +cp                    2[6]          0x00     low,high +os                    2[0]          0x00     on,off +rs                    2[1:2]        0x00     d512=3,d640=0,d1024=1 +test                  2[3:5]        0x01     normal=0x01,cpoff=0x02,cpsink=0x06,cpsrc=0x07,cptest1=0x04,cptest2=0x05 +######################################################################## +## Control byte 2 +######################################################################## +bandsel               3[4:7]        0x03     uhf=0x03,vhfhi=0x09,vhflo=0x0a +power                 3[3]          0x00     on,off +""" + +######################################################################## +# Template for methods in the body of the struct +######################################################################## +BODY_TMPL="""\ +boost::uint8_t get_reg(boost::uint8_t addr){ +    boost::uint8_t reg = 0; +    switch(addr){ +    #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) +    case $addr: +        #for $reg in filter(lambda r: r.get_addr() == addr, $regs) +        reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); +        #end for +        break; +    #end for +    } +    return boost::uint8_t(reg); +} + +""" + +if __name__ == '__main__': +    import common; common.generate( +        name='tuner_4937di5_regs', +        regs_tmpl=REGS_TMPL, +        body_tmpl=BODY_TMPL, +        file=__file__, +    ) diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 96ef8e505..b95d46381 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -30,9 +30,16 @@ IF(LIBUSB_FOUND)          ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_control.cpp          ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_zero_copy.cpp          ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_base.cpp -        ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_device_handle.cpp +        ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_base.hpp      ) +    IF(MSVC) #include our custom stdint for libusb +        INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/transport/msvc) +    ENDIF(MSVC)      SET(HAVE_USB_SUPPORT TRUE) +ELSE(LIBUSB_FOUND) +    LIBUHD_APPEND_SOURCES( +        ${CMAKE_SOURCE_DIR}/lib/transport/usb_dummy_impl.cpp +    )  ENDIF(LIBUSB_FOUND)  IF(HAVE_USB_SUPPORT) diff --git a/host/lib/transport/convert_types_impl.hpp b/host/lib/transport/convert_types_impl.hpp index b9a47eb0e..48ff99725 100644 --- a/host/lib/transport/convert_types_impl.hpp +++ b/host/lib/transport/convert_types_impl.hpp @@ -266,6 +266,26 @@ static UHD_INLINE void item32_to_fc32_nswap(      }  } +#elif defined(USE_ARM_NEON_H) +static UHD_INLINE void item32_to_fc32_nswap( +    const item32_t *input, fc32_t *output, size_t nsamps) +{ +    size_t i; + +    float32x4_t Q1 = vdupq_n_f32(floats_per_short); +    for (i=0; i < (nsamps & ~0x03); i+=2) { +        int16x4_t D0 = vld1_s16(reinterpret_cast<const int16_t *>(&input[i])); +        int16x4_t D1 = vrev32_s16(D0); +        int32x4_t Q2 = vmovl_s16(D1); +        float32x4_t Q3 = vcvtq_f32_s32(Q2); +        float32x4_t Q4 = vmulq_f32(Q3, Q1); +        vst1q_f32((reinterpret_cast<float *>(&output[i])), Q4); +    } + +    for (; i < nsamps; i++) +        output[i] = item32_to_fc32(input[i]); +} +  #else  static UHD_INLINE void item32_to_fc32_nswap(      const item32_t *input, fc32_t *output, size_t nsamps diff --git a/host/lib/transport/gen_convert_types.py b/host/lib/transport/gen_convert_types.py index adbd22868..f9509c81d 100755 --- a/host/lib/transport/gen_convert_types.py +++ b/host/lib/transport/gen_convert_types.py @@ -99,9 +99,9 @@ void transport::convert_io_type_to_otw_type(              nsamps_per_io_buff          );          #else -        for (size_t i = 0; i < nsamps_per_io_buff; i++){ +        for (size_t i = 0, j = 0; i < nsamps_per_io_buff; i++){              #for $j in range($num_chans) -            reinterpret_cast<$(out_type)_t *>(otw_buff)[i*$num_chans + $j] = +            reinterpret_cast<$(out_type)_t *>(otw_buff)[j++] =                  #if $ph.get_swap_type($pred) == 'bswap'                  uhd::byteswap($(converter)(reinterpret_cast<const $(in_type)_t *>(io_buffs[$j])[i]));                  #else @@ -139,13 +139,13 @@ void transport::convert_otw_type_to_io_type(              nsamps_per_io_buff          );          #else -        for (size_t i = 0; i < nsamps_per_io_buff; i++){ +        for (size_t i = 0, j = 0; i < nsamps_per_io_buff; i++){              #for $j in range($num_chans)              reinterpret_cast<$(out_type)_t *>(io_buffs[$j])[i] =                  #if $ph.get_swap_type($pred) == 'bswap' -                $(converter)(uhd::byteswap(reinterpret_cast<const $(in_type)_t *>(otw_buff)[i*$num_chans + $j])); +                $(converter)(uhd::byteswap(reinterpret_cast<const $(in_type)_t *>(otw_buff)[j++]));                  #else -                $(converter)(reinterpret_cast<const $(in_type)_t *>(otw_buff)[i*$num_chans + $j]); +                $(converter)(reinterpret_cast<const $(in_type)_t *>(otw_buff)[j++]);                  #end if              #end for          } diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index e21c39aa3..cfa77d9ca 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -17,110 +17,249 @@  #include "libusb1_base.hpp"  #include <uhd/utils/assert.hpp> +#include <uhd/types/dict.hpp> +#include <boost/weak_ptr.hpp> +#include <boost/foreach.hpp>  #include <iostream> +using namespace uhd;  using namespace uhd::transport; -/********************************************************** - * Helper Methods - **********************************************************/ +/*********************************************************************** + * libusb session + **********************************************************************/ +class libusb_session_impl : public libusb::session{ +public: +    libusb_session_impl(void){ +        UHD_ASSERT_THROW(libusb_init(&_context) == 0); +        libusb_set_debug(_context, debug_level); +    } + +    ~libusb_session_impl(void){ +        libusb_exit(_context); +    } + +    libusb_context *get_context(void) const{ +        return _context; +    } + +private: +    libusb_context *_context; +}; + +libusb::session::sptr libusb::session::get_global_session(void){ +    static boost::weak_ptr<session> global_session; -/********************************************************** - * libusb namespace  - **********************************************************/ -void libusb::init(libusb_context **ctx, int debug_level) -{ -    if (libusb_init(ctx) < 0) -        std::cerr << "error: libusb_init" << std::endl; +    //not expired -> get existing session +    if (not global_session.expired()) return global_session.lock(); -    libusb_set_debug(*ctx, debug_level); +    //create a new global session +    sptr new_global_session(new libusb_session_impl()); +    global_session = new_global_session; +    return new_global_session;  } -libusb_device_handle *libusb::open_device(libusb_context *ctx, -                                          usb_device_handle::sptr handle) -{ -    libusb_device_handle *dev_handle = NULL; -    libusb_device **libusb_dev_list; -    size_t dev_cnt = libusb_get_device_list(ctx, &libusb_dev_list); - -    //find and open the USB device  -    for (size_t i = 0; i < dev_cnt; i++) { -        libusb_device *dev = libusb_dev_list[i]; - -        if (compare_device(dev, handle)) { -            libusb_open(dev, &dev_handle); -            libusb_unref_device(dev); -            break; -        } -             -        libusb_unref_device(dev); +/*********************************************************************** + * libusb device + **********************************************************************/ +class libusb_device_impl : public libusb::device{ +public: +    libusb_device_impl(libusb_device *dev){ +        _session = libusb::session::get_global_session(); +        _dev = dev;      } -    return dev_handle; -} +    ~libusb_device_impl(void){ +        libusb_unref_device(this->get()); +    } + +    libusb_device *get(void) const{ +        return _dev; +    } + +private: +    libusb::session::sptr _session; //always keep a reference to session +    libusb_device *_dev; +}; + +/*********************************************************************** + * libusb device list + **********************************************************************/ +class libusb_device_list_impl : public libusb::device_list{ +public: +    libusb_device_list_impl(void){ +        libusb::session::sptr sess = libusb::session::get_global_session(); -//note: changed order of checks so it only tries to get_serial and get_device_address if vid and pid match -//doing this so it doesn't try to open the device if it's not ours -bool libusb::compare_device(libusb_device *dev, -                            usb_device_handle::sptr handle) -{ -    std::string serial         = handle->get_serial(); -    boost::uint16_t vendor_id  = handle->get_vendor_id(); -    boost::uint16_t product_id = handle->get_product_id(); -    boost::uint8_t device_addr = handle->get_device_addr(); - -    libusb_device_descriptor libusb_desc; -    if (libusb_get_device_descriptor(dev, &libusb_desc) < 0) -        return false; -    if (vendor_id != libusb_desc.idVendor) -        return false; -    if (product_id != libusb_desc.idProduct) -        return false;  -    if (serial != get_serial(dev)) -        return false; -    if (device_addr != libusb_get_device_address(dev)) -        return false; - -    return true; +        //allocate a new list of devices +        libusb_device** dev_list; +        ssize_t ret = libusb_get_device_list(sess->get_context(), &dev_list); +        if (ret < 0) throw std::runtime_error("cannot enumerate usb devices"); + +        //fill the vector of device references +        for (size_t i = 0; i < size_t(ret); i++) _devs.push_back( +            libusb::device::sptr(new libusb_device_impl(dev_list[i])) +        ); + +        //free the device list but dont unref (done in ~device) +        libusb_free_device_list(dev_list, false/*dont unref*/); +    } + +    size_t size(void) const{ +        return _devs.size(); +    } + +    libusb::device::sptr at(size_t i) const{ +        return _devs.at(i); +    } + +private: +    std::vector<libusb::device::sptr> _devs; +}; + +libusb::device_list::sptr libusb::device_list::make(void){ +    return sptr(new libusb_device_list_impl());  } +/*********************************************************************** + * libusb device descriptor + **********************************************************************/ +class libusb_device_descriptor_impl : public libusb::device_descriptor{ +public: +    libusb_device_descriptor_impl(libusb::device::sptr dev){ +        _dev = dev; +        UHD_ASSERT_THROW(libusb_get_device_descriptor(_dev->get(), &_desc) == 0); +    } -bool libusb::open_interface(libusb_device_handle *dev_handle, -                            int interface) -{ -    int ret = libusb_claim_interface(dev_handle, interface); -    if (ret < 0) { -        std::cerr << "error: libusb_claim_interface() " << ret << std::endl; -        return false; +    const libusb_device_descriptor &get(void) const{ +        return _desc;      } -    else { -        return true; + +    std::string get_ascii_serial(void) const{ +        if (this->get().iSerialNumber == 0) return ""; + +        libusb::device_handle::sptr handle( +            libusb::device_handle::get_cached_handle(_dev) +        ); + +        unsigned char buff[512]; +        ssize_t ret = libusb_get_string_descriptor_ascii( +            handle->get(), this->get().iSerialNumber, buff, sizeof(buff) +        ); +        if (ret < 0) return ""; //on error, just return empty string + +        return std::string((char *)buff, ret);      } + +private: +    libusb::device::sptr _dev; //always keep a reference to device +    libusb_device_descriptor _desc; +}; + +libusb::device_descriptor::sptr libusb::device_descriptor::make(device::sptr dev){ +    return sptr(new libusb_device_descriptor_impl(dev));  } +/*********************************************************************** + * libusb device handle + **********************************************************************/ +class libusb_device_handle_impl : public libusb::device_handle{ +public: +    libusb_device_handle_impl(libusb::device::sptr dev){ +        _dev = dev; +        UHD_ASSERT_THROW(libusb_open(_dev->get(), &_handle) == 0); +    } + +    ~libusb_device_handle_impl(void){ +        //release all claimed interfaces +        for (size_t i = 0; i < _claimed.size(); i++){ +            libusb_release_interface(this->get(), _claimed[i]); +        } +        libusb_close(_handle); +    } + +    libusb_device_handle *get(void) const{ +        return _handle; +    } + +    void claim_interface(int interface){ +        UHD_ASSERT_THROW(libusb_claim_interface(this->get(), interface) == 0); +        _claimed.push_back(interface); +    } + +private: +    libusb::device::sptr _dev; //always keep a reference to device +    libusb_device_handle *_handle; +    std::vector<int> _claimed; +}; + +libusb::device_handle::sptr libusb::device_handle::get_cached_handle(device::sptr dev){ +    static uhd::dict<libusb_device *, boost::weak_ptr<device_handle> > handles; + +    //not expired -> get existing handle +    if (handles.has_key(dev->get()) and not handles[dev->get()].expired()){ +        return handles[dev->get()].lock(); +    } + +    //create a new cached handle +    try{ +        sptr new_handle(new libusb_device_handle_impl(dev)); +        handles[dev->get()] = new_handle; +        return new_handle; +    } +    catch(const std::exception &e){ +        std::cerr << "USB open failed: see the application notes for your device." << std::endl; +        throw std::runtime_error(e.what()); +    } +} -std::string libusb::get_serial(libusb_device *dev) -{ -    unsigned char buff[32]; +/*********************************************************************** + * libusb special handle + **********************************************************************/ +class libusb_special_handle_impl : public libusb::special_handle{ +public: +    libusb_special_handle_impl(libusb::device::sptr dev){ +        _dev = dev; +    } -    libusb_device_descriptor desc; -    if (libusb_get_device_descriptor(dev, &desc) < 0) -        return ""; +    libusb::device::sptr get_device(void) const{ +        return _dev; +    } -    if (desc.iSerialNumber == 0) -        return ""; +    std::string get_serial(void) const{ +        return libusb::device_descriptor::make(this->get_device())->get_ascii_serial(); +    } -    //open the device because we have to -    libusb_device_handle *dev_handle; -    if (libusb_open(dev, &dev_handle) < 0) -        return ""; +    boost::uint16_t get_vendor_id(void) const{ +        return libusb::device_descriptor::make(this->get_device())->get().idVendor; +    } -    if (libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber, -                                           buff, sizeof(buff)) < 0) { -        return ""; +    boost::uint16_t get_product_id(void) const{ +        return libusb::device_descriptor::make(this->get_device())->get().idProduct;      } -    libusb_close(dev_handle); +private: +    libusb::device::sptr _dev; //always keep a reference to device +}; + +libusb::special_handle::sptr libusb::special_handle::make(device::sptr dev){ +    return sptr(new libusb_special_handle_impl(dev)); +} + +/*********************************************************************** + * list device handles implementations + **********************************************************************/ +std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list( +    boost::uint16_t vid, boost::uint16_t pid +){ +    std::vector<usb_device_handle::sptr> handles; + +    libusb::device_list::sptr dev_list = libusb::device_list::make(); +    for (size_t i = 0; i < dev_list->size(); i++){ +        usb_device_handle::sptr handle = libusb::special_handle::make(dev_list->at(i)); +        if (handle->get_vendor_id() == vid and handle->get_product_id() == pid){ +            handles.push_back(handle); +        } +    } -    return (char*) buff; +    return handles;  } diff --git a/host/lib/transport/libusb1_base.hpp b/host/lib/transport/libusb1_base.hpp index abe5e22a2..04c1d6574 100644 --- a/host/lib/transport/libusb1_base.hpp +++ b/host/lib/transport/libusb1_base.hpp @@ -15,80 +15,135 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#ifndef INCLUDED_TRANSPORT_LIBUSB_HPP -#define INCLUDED_TRANSPORT_LIBUSB_HPP +#ifndef INCLUDED_LIBUHD_TRANSPORT_LIBUSB_HPP +#define INCLUDED_LIBUHD_TRANSPORT_LIBUSB_HPP  #include <uhd/config.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp>  #include <uhd/transport/usb_device_handle.hpp> -#include <libusb-1.0/libusb.h> +#include <libusb.h> +/*********************************************************************** + * Libusb object oriented smart pointer wrappers: + * The following wrappers provide allocation and automatic deallocation + * for various libusb data types and handles. The construction routines + * also store tables of already allocated structures to avoid multiple + * occurrences of opened handles (for example). + **********************************************************************/  namespace uhd { namespace transport {  namespace libusb { -    /* -     * Initialize libusb and set debug level -     * Takes a pointer to context pointer because that's -     * how libusb rolls. Debug levels. -     *      -     *   Level 0: no messages ever printed by the library (default) -     *   Level 1: error messages are printed to stderr -     *   Level 2: warning and error messages are printed to stderr -     *   Level 3: informational messages are printed to stdout, warning -     *            and error messages are printed to stderr -     * -     * \param ctx pointer to context pointer -     * \param debug_level + +    /*! +     * This session class holds a global libusb context for this process. +     * The get global session call will create a new context if none exists. +     * When all references to session are destroyed, the context will be freed.       */ -    void init(libusb_context **ctx, int debug_level); - -    /* -     * Open the device specified by a generic handle -     * Find the libusb_device cooresponding to the generic handle -     * and open it for I/O, which returns a libusb_device_handle -     * ready for an interface -     * \param ctx the libusb context used for init -     * \return a libusb_device_handle ready for action  +    class session : boost::noncopyable { +    public: +        typedef boost::shared_ptr<session> sptr; + +        /*! +         *   Level 0: no messages ever printed by the library (default) +         *   Level 1: error messages are printed to stderr +         *   Level 2: warning and error messages are printed to stderr +         *   Level 3: informational messages are printed to stdout, warning +         *            and error messages are printed to stderr +         */ +        static const int debug_level = 0; + +        //! get a shared pointer to the global session +        static sptr get_global_session(void); + +        //! get the underlying libusb context pointer +        virtual libusb_context *get_context(void) const = 0; +    }; + +    /*! +     * Holds a device pointer with a reference to the session.       */ -    libusb_device_handle *open_device(libusb_context *ctx, -                                      usb_device_handle::sptr handle); - -    /* -     * Compare a libusb device with a generic handle  -     * Check the descriptors and open the device to check the -     * serial number string. Compare values against the given -     * handle. The libusb context is already implied in the -     * libusb_device. -     * \param dev a libusb_device pointer -     * \param handle a generic handle specifier -     * \return true if handle and device match, false otherwise +    class device : boost::noncopyable { +    public: +        typedef boost::shared_ptr<device> sptr; + +        //! get the underlying device pointer +        virtual libusb_device *get(void) const = 0; +    }; + +    /*! +     * This device list class holds a device list that will be +     * automatically freed when the last reference is destroyed.       */ -    bool compare_device(libusb_device *dev, usb_device_handle::sptr handle); - -    /* -     * Open an interface to the device -     * This is a logical operation for operating system housekeeping as -     * nothing is sent over the bus. The interface much correspond -     * to the USB device descriptors. -     * \param dev_handle libusb handle to an opened device -     * \param interface integer of the interface to use -     * \return true on success, false on error +    class device_list : boost::noncopyable { +    public: +        typedef boost::shared_ptr<device_list> sptr; + +        //! make a new device list +        static sptr make(void); + +        //! the number of devices in this list +        virtual size_t size() const = 0; + +        //! get the device pointer at a particular index +        virtual device::sptr at(size_t index) const = 0; +    }; + +    /*! +     * Holds a device descriptor and a reference to the device.       */ -    bool open_interface(libusb_device_handle *dev_handle, int interface); - -    /* -     * Get serial number  -     * The standard USB device descriptor contains an index to an -     * actual serial number string descriptor. The index is readily -     * readble, but the string descriptor requires probing the device. -     * Because this call attempts to open the device, it may not -     * succeed because not all USB devices are readily opened. -     * The default language is used for the request (English). -     * \param dev a libusb_device pointer -     * \return string serial number or 0 on error or unavailablity +    class device_descriptor : boost::noncopyable { +    public: +        typedef boost::shared_ptr<device_descriptor> sptr; + +        //! make a new descriptor from a device reference +        static sptr make(device::sptr); + +        //! get the underlying device descriptor +        virtual const libusb_device_descriptor &get(void) const = 0; + +        virtual std::string get_ascii_serial(void) const = 0; +    }; + +    /*! +     * Holds a device handle and a reference to the device.       */ -    std::string get_serial(libusb_device *dev); +    class device_handle : boost::noncopyable { +    public: +        typedef boost::shared_ptr<device_handle> sptr; + +        //! get a cached handle or make a new one given the device +        static sptr get_cached_handle(device::sptr); + +        //! get the underlying device handle +        virtual libusb_device_handle *get(void) const = 0; + +        /*! +         * Open USB interfaces for control using magic value +         * IN interface:      2 +         * OUT interface:     1 +         * Control interface: 0 +         */ +        virtual void claim_interface(int) = 0; +    }; + +    /*! +     * The special handle is our internal implementation of the +     * usb device handle which is used publicly to identify a device. +     */ +    class special_handle : public usb_device_handle { +    public: +        typedef boost::shared_ptr<special_handle> sptr; + +        //! make a new special handle from device +        static sptr make(device::sptr); + +        //! get the underlying device reference +        virtual device::sptr get_device(void) const = 0; +    }; +  }  }} //namespace -#endif /* INCLUDED_TRANSPORT_LIBUSB_HPP */ +#endif /* INCLUDED_LIBUHD_TRANSPORT_LIBUSB_HPP */ diff --git a/host/lib/transport/libusb1_control.cpp b/host/lib/transport/libusb1_control.cpp index 3531128b2..f903907d0 100644 --- a/host/lib/transport/libusb1_control.cpp +++ b/host/lib/transport/libusb1_control.cpp @@ -20,7 +20,6 @@  using namespace uhd::transport; -const int libusb_debug_level = 0;  const int libusb_timeout = 0;  /*********************************************************************** @@ -28,68 +27,38 @@ const int libusb_timeout = 0;   **********************************************************************/  class libusb_control_impl : public usb_control {  public: -    libusb_control_impl(usb_device_handle::sptr handle); -    ~libusb_control_impl(); +    libusb_control_impl(libusb::device_handle::sptr handle): +        _handle(handle) +    { +        _handle->claim_interface(0 /* control interface */); +    } -    size_t submit(boost::uint8_t request_type, +    ssize_t submit(boost::uint8_t request_type,                    boost::uint8_t request,                    boost::uint16_t value,                    boost::uint16_t index,                    unsigned char *buff, -                  boost::uint16_t length);  +                  boost::uint16_t length +    ){ +        return libusb_control_transfer(_handle->get(), +                                       request_type, +                                       request, +                                       value, +                                       index, +                                       buff, +                                       length, +                                       libusb_timeout); +    }  private: -    libusb_context       *_ctx; -    libusb_device_handle *_dev_handle; +    libusb::device_handle::sptr _handle;  }; - -libusb_control_impl::libusb_control_impl(usb_device_handle::sptr handle) -{ -    libusb::init(&_ctx, libusb_debug_level); - -    // Find and open the libusb_device corresponding to the -    // given handle and return the libusb_device_handle -    // that can be used for I/O purposes. -    _dev_handle = libusb::open_device(_ctx, handle); - -    // Open USB interfaces for control using magic value -    // IN interface:      2 -    // OUT interface:     1 -    // Control interface: 0 -    libusb::open_interface(_dev_handle, 0); -} - - -libusb_control_impl::~libusb_control_impl() -{ -    libusb_close(_dev_handle); -    libusb_exit(_ctx); -} - - -size_t libusb_control_impl::submit(boost::uint8_t request_type, -                                   boost::uint8_t request, -                                   boost::uint16_t value, -                                   boost::uint16_t index,  -                                   unsigned char *buff, -                                   boost::uint16_t length)  -{ -    return libusb_control_transfer(_dev_handle, -                                   request_type, -                                   request, -                                   value, -                                   index, -                                   buff,  -                                   length,  -                                   libusb_timeout); -} - -  /***********************************************************************   * USB control public make functions   **********************************************************************/ -usb_control::sptr usb_control::make(usb_device_handle::sptr handle) -{ -    return sptr(new libusb_control_impl(handle)); +usb_control::sptr usb_control::make(usb_device_handle::sptr handle){ +    return sptr(new libusb_control_impl(libusb::device_handle::get_cached_handle( +        boost::static_pointer_cast<libusb::special_handle>(handle)->get_device() +    )));  } diff --git a/host/lib/transport/libusb1_device_handle.cpp b/host/lib/transport/libusb1_device_handle.cpp deleted file mode 100644 index 43d0f0e26..000000000 --- a/host/lib/transport/libusb1_device_handle.cpp +++ /dev/null @@ -1,117 +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 "libusb1_base.hpp" -#include <uhd/utils/assert.hpp> -#include <iostream> - -using namespace uhd::transport; - -const int libusb_debug_level = 0; - -/**************************************************************** - * libusb USB device handle implementation class - ***************************************************************/ -class libusb1_device_handle_impl : public usb_device_handle { -public: -    libusb1_device_handle_impl(std::string serial, -                               boost::uint32_t product_id, -                               boost::uint32_t vendor_id, -                               boost::uint32_t device_addr) -      : _serial(serial), _product_id(product_id),  -        _vendor_id(vendor_id), _device_addr(device_addr) -    { -        /* NOP */ -    } - -    ~libusb1_device_handle_impl() -    { -        /* NOP */ -    } - -    std::string get_serial() const -    { -        return _serial; -    } - -    boost::uint16_t get_vendor_id() const -    { -        return _vendor_id; -    } - - -    boost::uint16_t get_product_id() const -    { -        return _product_id; -    } - -    boost::uint16_t get_device_addr() const -    { -        return _device_addr; -    } - -private: -    std::string     _serial; -    boost::uint32_t _product_id; -    boost::uint32_t _vendor_id; -    boost::uint32_t _device_addr; -}; - - -usb_device_handle::sptr make_usb_device_handle(libusb_device *dev) -{ -    libusb_device_descriptor desc; - -    if (libusb_get_device_descriptor(dev, &desc) < 0) { -        UHD_ASSERT_THROW("USB: failed to get device descriptor"); -    } - -    std::string     serial      = libusb::get_serial(dev); -    boost::uint32_t product_id  = desc.idProduct; -    boost::uint32_t vendor_id   = desc.idVendor; -    boost::uint32_t device_addr = libusb_get_device_address(dev); - -    return usb_device_handle::sptr(new libusb1_device_handle_impl( -        serial, -        product_id, -        vendor_id, -        device_addr)); -} - -std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list(boost::uint16_t vid, boost::uint16_t pid) -{ -    libusb_context *ctx = NULL; -    libusb_device** libusb_device_list; -    std::vector<usb_device_handle::sptr> device_handle_list; -    libusb_device_descriptor desc; - -    libusb::init(&ctx, libusb_debug_level); - -    size_t dev_size = libusb_get_device_list(ctx, &libusb_device_list); -    for (size_t i = 0; i < dev_size; i++) { -        libusb_device *dev = libusb_device_list[i]; -        if(libusb_get_device_descriptor(dev, &desc) < 0) { -          UHD_ASSERT_THROW("USB: failed to get device descriptor"); -        } -        if(desc.idVendor == vid && desc.idProduct == pid) { -          device_handle_list.push_back(make_usb_device_handle(dev)); -        } -    } - -    libusb_exit(ctx); -    return device_handle_list;  -} diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index b890a87f9..f589d7c77 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -17,16 +17,22 @@  #include "libusb1_base.hpp"  #include <uhd/transport/usb_zero_copy.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/utils/thread_priority.hpp>  #include <uhd/utils/assert.hpp> -#include <boost/asio.hpp> -#include <boost/format.hpp> +#include <boost/shared_array.hpp> +#include <boost/foreach.hpp> +#include <boost/thread.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <vector>  #include <iostream> -#include <iomanip> +using namespace uhd;  using namespace uhd::transport; -const int libusb_debug_level = 0; -const int libusb_timeout = 0; +static const double CLEANUP_TIMEOUT   = 0.2;    //seconds +static const size_t DEFAULT_NUM_XFERS = 16;     //num xfers +static const size_t DEFAULT_XFER_SIZE = 32*512; //bytes  /***********************************************************************   * Helper functions @@ -54,56 +60,57 @@ void pp_transfer(libusb_transfer *lut)   *   create a bidirectional interface. It is a zero copy implementation   *   with respect to libusb, however, each send and recv requires a copy   *   operation from kernel to userspace; this is due to the usbfs - *   interface provided by the kernel.  + *   interface provided by the kernel.   **********************************************************************/  class usb_endpoint { -private: -    libusb_device_handle *_dev_handle; -    libusb_context *_ctx; -    int  _endpoint; -    bool _input; - -    size_t _transfer_size; -    size_t _num_transfers; - -    // Transfer state lists (transfers are free, pending, or completed) -    std::list<libusb_transfer *>  _free_list; -    std::list<libusb_transfer *>  _pending_list; -    std::list<libusb_transfer *>  _completed_list; - -    // Calls for processing asynchronous I/O  -    libusb_transfer *allocate_transfer(int buff_len); -    bool cancel(libusb_transfer *lut); -    bool cancel_all(); -    bool reap_pending_list(); -    bool reap_pending_list_timeout(); -    bool reap_completed_list(); - -    // Transfer state manipulators  -    void free_list_add(libusb_transfer *lut); -    void pending_list_add(libusb_transfer *lut); -    void completed_list_add(libusb_transfer *lut); -    libusb_transfer *free_list_get(); -    libusb_transfer *completed_list_get(); -    bool pending_list_remove(libusb_transfer *lut); - -    // Debug use -    void print_transfer_status(libusb_transfer *lut); -  public: -    usb_endpoint(libusb_device_handle *dev_handle, -                 libusb_context *ctx, int endpoint, bool input, -                 size_t transfer_size, size_t num_transfers); +    typedef boost::shared_ptr<usb_endpoint> sptr; -    ~usb_endpoint(); +    usb_endpoint( +        libusb::device_handle::sptr handle, +        int endpoint, +        bool input, +        size_t transfer_size, +        size_t num_transfers +    ); + +    ~usb_endpoint(void);      // Exposed interface for submitting / retrieving transfer buffers -    bool submit(libusb_transfer *lut); -    libusb_transfer *get_completed_transfer(); -    libusb_transfer *get_free_transfer(); + +    //! Submit a new transfer that was presumably just filled or emptied. +    void submit(libusb_transfer *lut); + +    /*! +     * Get an available transfer: +     * For inputs, this is a just filled transfer. +     * For outputs, this is a just emptied transfer. +     * \param timeout the timeout to wait for a lut +     * \return the transfer pointer or NULL if timeout +     */ +    libusb_transfer *get_lut_with_wait(double timeout);      //Callback use only      void callback_handle_transfer(libusb_transfer *lut); + +private: +    libusb::device_handle::sptr _handle; +    int  _endpoint; +    bool _input; + +    //! hold a bounded buffer of completed transfers +    typedef bounded_buffer<libusb_transfer *> lut_buff_type; +    lut_buff_type::sptr _completed_list; + +    //! a list of all transfer structs we allocated +    std::vector<libusb_transfer *> _all_luts; + +    //! a block of memory for the transfer buffers +    boost::shared_array<char> _buffer; + +    // Calls for processing asynchronous I/O +    libusb_transfer *allocate_transfer(void *mem, size_t len); +    void print_transfer_status(libusb_transfer *lut);  }; @@ -116,9 +123,8 @@ public:   * it from the pending to completed status list.   * \param lut pointer to libusb_transfer   */ -static void callback(libusb_transfer *lut) -{ -    usb_endpoint *endpoint = (usb_endpoint *) lut->user_data;  +static void callback(libusb_transfer *lut){ +    usb_endpoint *endpoint = (usb_endpoint *) lut->user_data;      endpoint->callback_handle_transfer(lut);  } @@ -127,14 +133,9 @@ static void callback(libusb_transfer *lut)   * Accessor call to allow list access from callback space   * \param pointer to libusb_transfer   */ -void usb_endpoint::callback_handle_transfer(libusb_transfer *lut) -{ -    if (!pending_list_remove(lut)) { -        std::cerr << "USB: pending remove failed" << std::endl; -        return; -    } - -    completed_list_add(lut);     +void usb_endpoint::callback_handle_transfer(libusb_transfer *lut){ +    boost::this_thread::disable_interruption di; //disable because the wait can throw +    _completed_list->push_with_wait(lut);  } @@ -142,21 +143,28 @@ void usb_endpoint::callback_handle_transfer(libusb_transfer *lut)   * Constructor   * Allocate libusb transfers and mark as free.  For IN endpoints,   * submit the transfers so that they're ready to return when - * data is available.  + * data is available.   */ -usb_endpoint::usb_endpoint(libusb_device_handle *dev_handle, -                          libusb_context *ctx, int endpoint, bool input, -                          size_t transfer_size, size_t num_transfers) -    : _dev_handle(dev_handle), -      _ctx(ctx), _endpoint(endpoint), _input(input), -      _transfer_size(transfer_size), _num_transfers(num_transfers) +usb_endpoint::usb_endpoint( +    libusb::device_handle::sptr handle, +    int endpoint, +    bool input, +    size_t transfer_size, +    size_t num_transfers +): +    _handle(handle), +    _endpoint(endpoint), +    _input(input)  { -    unsigned int i; -    for (i = 0; i < _num_transfers; i++) { -        free_list_add(allocate_transfer(_transfer_size)); +    _completed_list = lut_buff_type::make(num_transfers); +    _buffer = boost::shared_array<char>(new char[num_transfers*transfer_size]); +    for (size_t i = 0; i < num_transfers; i++){ +        _all_luts.push_back(allocate_transfer(_buffer.get() + i*transfer_size, transfer_size)); -        if (_input) -            submit(free_list_get()); +        //input luts are immediately submitted to be filled +        //output luts go into the completed list as free buffers +        if (_input) this->submit(_all_luts.back()); +        else _completed_list->push_with_wait(_all_luts.back());      }  } @@ -168,47 +176,44 @@ usb_endpoint::usb_endpoint(libusb_device_handle *dev_handle,   * the transfers. Libusb will deallocate the data buffer held by   * each transfer.   */ -usb_endpoint::~usb_endpoint() -{ -    cancel_all(); - -    while (!_pending_list.empty()) { -        if (!reap_pending_list()) -            std::cerr << "error: destructor failed to reap" << std::endl; +usb_endpoint::~usb_endpoint(void){ +    //cancel all transfers +    BOOST_FOREACH(libusb_transfer *lut, _all_luts){ +        libusb_cancel_transfer(lut);      } -    while (!_completed_list.empty()) { -        if (!reap_completed_list()) -            std::cerr << "error: destructor failed to reap" << std::endl; -    } +    //collect canceled transfers (drain the queue) +    while (this->get_lut_with_wait(CLEANUP_TIMEOUT) != NULL){}; -    while (!_free_list.empty()) { -        libusb_free_transfer(free_list_get()); +    //free all transfers +    BOOST_FOREACH(libusb_transfer *lut, _all_luts){ +        libusb_free_transfer(lut);      }  }  /* - * Allocate a libusb transfer  + * Allocate a libusb transfer   * The allocated transfer - and buffer it contains - is repeatedly   * submitted, reaped, and reused and should not be freed until shutdown. - * \param buff_len size of the individual buffer held by each transfer + * \param mem a pointer to the buffer memory + * \param len size of the individual buffer   * \return pointer to an allocated libusb_transfer   */ -libusb_transfer *usb_endpoint::allocate_transfer(int buff_len) -{ +libusb_transfer *usb_endpoint::allocate_transfer(void *mem, size_t len){      libusb_transfer *lut = libusb_alloc_transfer(0); - -    unsigned char *buff = new unsigned char[buff_len]; +    UHD_ASSERT_THROW(lut != NULL);      unsigned int endpoint = ((_endpoint & 0x7f) | (_input ? 0x80 : 0)); +    unsigned char *buff = reinterpret_cast<unsigned char *>(mem); +    libusb_transfer_cb_fn lut_callback = libusb_transfer_cb_fn(&callback);      libusb_fill_bulk_transfer(lut,                // transfer -                              _dev_handle,        // dev_handle +                              _handle->get(),     // dev_handle                                endpoint,           // endpoint                                buff,               // buffer -                              buff_len,           // length -                              callback,           // callback +                              len,                // length +                              lut_callback,       // callback                                this,               // user_data                                0);                 // timeout      return lut; @@ -219,97 +224,18 @@ libusb_transfer *usb_endpoint::allocate_transfer(int buff_len)   * Asynchonous transfer submission   * Submit a libusb transfer to libusb add pending status   * \param lut pointer to libusb_transfer - * \return true on success or false on error  - */ -bool usb_endpoint::submit(libusb_transfer *lut) -{ -    int retval; -    if ((retval = libusb_submit_transfer(lut)) < 0) { -        std::cerr << "error: libusb_submit_transfer: " << retval << std::endl; -        return false; -    } - -    pending_list_add(lut); -    return true; -} - - -/* - * Cancel a pending transfer  - * Search the pending list for the transfer and cancel if found. - * \param lut pointer to libusb_transfer to cancel - * \return true on success or false if transfer is not found - * - * Note: success only indicates submission of cancelation request. - * Sucessful cancelation is not known until the callback occurs. + * \return true on success or false on error   */ -bool usb_endpoint::cancel(libusb_transfer *lut) -{ -    std::list<libusb_transfer*>::iterator iter; -    for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) { -        if (*iter == lut) {  -            libusb_cancel_transfer(lut);  -            return true; -        } -    } -    return false; +void usb_endpoint::submit(libusb_transfer *lut){ +    UHD_ASSERT_THROW(libusb_submit_transfer(lut) == 0);  } - -/* - * Cancel all pending transfers  - * \return bool true if cancelation request is submitted - * - * Note: success only indicates submission of cancelation request. - * Sucessful cancelation is not known until the callback occurs. - */ -bool usb_endpoint::cancel_all() -{ -    std::list<libusb_transfer*>::iterator iter; - -    for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) { -        if (libusb_cancel_transfer(*iter) < 0) { -            std::cerr << "error: libusb_cancal_transfer() failed" << std::endl; -            return false; -        } -    } - -    return true; -} - - -/* - * Reap completed transfers - * return true if at least one transfer was reaped, false otherwise.  - * Check completed transfers for errors and mark as free. This is a - * blocking call.  - * \return bool true if a libusb transfer is reaped, false otherwise - */ -bool usb_endpoint::reap_completed_list() -{ -    libusb_transfer *lut; - -    if (_completed_list.empty()) { -        if (!reap_pending_list_timeout()) -            return false; -    } - -    while (!_completed_list.empty()) { -        lut = completed_list_get(); -        print_transfer_status(lut); -        free_list_add(lut); -    } - -    return true; -} - -  /*   * Print status errors of a completed transfer   * \param lut pointer to an libusb_transfer   */ -void usb_endpoint::print_transfer_status(libusb_transfer *lut) -{ +void usb_endpoint::print_transfer_status(libusb_transfer *lut){ +    std::cout << "here " << lut->status << std::endl;      switch (lut->status) {      case LIBUSB_TRANSFER_COMPLETED:          if (lut->actual_length < lut->length) { @@ -345,382 +271,136 @@ void usb_endpoint::print_transfer_status(libusb_transfer *lut)      }  } - -/* - * Reap pending transfers without timeout  - * This is a blocking call. Reaping submitted transfers is - * handled by libusb and the assigned callback function. - * Block until at least one transfer is reaped. - * \return true true if a transfer was reaped or false otherwise - */ -bool usb_endpoint::reap_pending_list() -{ -    int retval; - -    if ((retval = libusb_handle_events(_ctx)) < 0) { -        std::cerr << "error: libusb_handle_events: " << retval << std::endl; -        return false; -    } - -    return true; -} - - -/* - * Reap pending transfers with timeout  - * This call blocks until a transfer is reaped or timeout. - * Reaping submitted transfers is handled by libusb and the - * assigned callback function. Block until at least one - * transfer is reaped or timeout occurs. - * \return true if a transfer was reaped or false otherwise - */ -bool usb_endpoint::reap_pending_list_timeout() -{ -    int retval; -    timeval tv; - -    tv.tv_sec = 0; -    tv.tv_usec = 100000; //100ms - -    size_t pending_list_size = _pending_list.size(); - -    if ((retval = libusb_handle_events_timeout(_ctx, &tv)) < 0) { -        std::cerr << "error: libusb_handle_events: " << retval << std::endl; -        return false; -    } - -    if (_pending_list.size() < pending_list_size) { -        return true; -    } -    else { -        return false; -    } -} - - -/* - * Get a free transfer - * The transfer has an empty data bufer for OUT requests  - * \return pointer to a libusb_transfer - */ -libusb_transfer *usb_endpoint::get_free_transfer() -{ -    if (_free_list.empty()) { -        if (!reap_completed_list()) -            return NULL;  -    } - -    return free_list_get(); -} - - -/* - * Get a completed transfer  - * The transfer has a full data buffer for IN requests - * \return pointer to libusb_transfer - */ -libusb_transfer *usb_endpoint::get_completed_transfer() -{ -    if (_completed_list.empty()) { -        if (!reap_pending_list_timeout()) -            return NULL;  -    } - -    return completed_list_get(); -} - -/* - * List operations  - */ -void usb_endpoint::free_list_add(libusb_transfer *lut) -{ -    _free_list.push_back(lut); -} - -void usb_endpoint::pending_list_add(libusb_transfer *lut) -{ -    _pending_list.push_back(lut); -} - -void usb_endpoint::completed_list_add(libusb_transfer *lut) -{ -    _completed_list.push_back(lut); -} - - -/* - * Free and completed lists don't have ordered content  - * Pop transfers from the front as needed - */ -libusb_transfer *usb_endpoint::free_list_get() -{ +libusb_transfer *usb_endpoint::get_lut_with_wait(double timeout){ +    boost::this_thread::disable_interruption di; //disable because the wait can throw      libusb_transfer *lut; - -    if (_free_list.size() == 0) { -        return NULL;  -    } -    else {  -        lut = _free_list.front(); -        _free_list.pop_front(); -        return lut; -    } +    if (_completed_list->pop_with_timed_wait(lut, timeout)) return lut; +    return NULL;  } - -/* - * Free and completed lists don't have ordered content  - * Pop transfers from the front as needed - */ -libusb_transfer *usb_endpoint::completed_list_get() -{ -    libusb_transfer *lut; - -    if (_completed_list.empty()) { -        return NULL; -    } -    else {  -        lut = _completed_list.front(); -        _completed_list.pop_front(); -        return lut; -    } -} - - -/* - * Search and remove transfer from pending list - * Assuming that the callbacks occur in order, the front element - * should yield the correct transfer. If not, then something else - * is going on. If no transfers match, then something went wrong. - */ -bool usb_endpoint::pending_list_remove(libusb_transfer *lut) -{ -    std::list<libusb_transfer*>::iterator iter; -    for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) { -        if (*iter == lut) {  -            _pending_list.erase(iter); -            return true; -        } -    } -    return false; -} - -  /*********************************************************************** - * Managed buffers  + * USB zero_copy device class   **********************************************************************/ -/* - * Libusb managed receive buffer - * Construct a recv buffer from a libusb transfer. The memory held by - * the libusb transfer is exposed through the managed buffer interface. - * Upon destruction, the transfer and buffer are resubmitted to the - * endpoint for further use.  - */ -class libusb_managed_recv_buffer_impl : public managed_recv_buffer { +class libusb_zero_copy_impl : public usb_zero_copy, public boost::enable_shared_from_this<libusb_zero_copy_impl> {  public: -    libusb_managed_recv_buffer_impl(libusb_transfer *lut, -                                    usb_endpoint *endpoint) -        : _buff(lut->buffer, lut->length) -    { -        _lut = lut; -        _endpoint = endpoint; -    } -    ~libusb_managed_recv_buffer_impl() -    { -       if (!_endpoint->submit(_lut)) -           std::cerr << "USB: failed to submit IN transfer" << std::endl; -    } +    libusb_zero_copy_impl( +        libusb::device_handle::sptr handle, +        size_t recv_endpoint, +        size_t send_endpoint, +        const device_addr_t &hints +    ); -private: -    const boost::asio::const_buffer &get() const -    { -        return _buff;  +    ~libusb_zero_copy_impl(void){ +        _threads_running = false; +        _thread_group.join_all();      } -    libusb_transfer *_lut; -    usb_endpoint *_endpoint; -    const boost::asio::const_buffer _buff; -}; - -/* - * Libusb managed send buffer - * Construct a send buffer from a libusb transfer. The memory held by - * the libusb transfer is exposed through the managed buffer interface. - * Committing the buffer will set the data length and submit the buffer - * to the endpoint. Submitting a buffer multiple times or destroying - * the buffer before committing is an error. For the latter, the transfer - * is returned to the endpoint with no data for reuse. - */ -class libusb_managed_send_buffer_impl : public managed_send_buffer { -public: -    libusb_managed_send_buffer_impl(libusb_transfer *lut, -                                    usb_endpoint *endpoint, -                                    size_t buff_size) -        : _buff(lut->buffer, buff_size), _committed(false) -    { -        _lut = lut; -        _endpoint = endpoint; -    } +    managed_recv_buffer::sptr get_recv_buff(double); +    managed_send_buffer::sptr get_send_buff(double); -    ~libusb_managed_send_buffer_impl() -    { -        if (!_committed) { -            _lut->length = 0; -            _lut->actual_length = 0; -            _endpoint->submit(_lut); -        } -    } +    size_t get_num_recv_frames(void) const { return _num_recv_frames; } +    size_t get_num_send_frames(void) const { return _num_send_frames; } -    ssize_t commit(size_t num_bytes) -    { -        if (_committed) { -            std::cerr << "UHD: send buffer already committed" << std::endl; -            return 0; -        } -         -        UHD_ASSERT_THROW(num_bytes <= boost::asio::buffer_size(_buff)); +    size_t get_recv_frame_size(void) const { return _recv_frame_size; } +    size_t get_send_frame_size(void) const { return _send_frame_size; } -        _lut->length = num_bytes; -        _lut->actual_length = 0; +private: +    void release(libusb_transfer *lut){ +        _recv_ep->submit(lut); +    } -        if (_endpoint->submit(_lut)) { -            _committed = true; -            return num_bytes; +    void commit(libusb_transfer *lut, size_t num_bytes){ +        lut->length = num_bytes; +        try{ +            _send_ep->submit(lut);          } -        else { -            return 0; +        catch(const std::exception &e){ +            std::cerr << "Error in commit: " << e.what() << std::endl;          }      } -private: -    const boost::asio::mutable_buffer &get() const -    { -        return _buff;  +    libusb::device_handle::sptr _handle; +    const size_t _recv_frame_size, _num_recv_frames; +    const size_t _send_frame_size, _num_send_frames; +    usb_endpoint::sptr _recv_ep, _send_ep; + +    //event handler threads +    boost::thread_group _thread_group; +    bool _threads_running; + +    void run_event_loop(void){ +        set_thread_priority_safe(); +        libusb::session::sptr session = libusb::session::get_global_session(); +        _threads_running = true; +        while(_threads_running){ +            timeval tv; +            tv.tv_sec = 0; +            tv.tv_usec = 100000; //100ms +            libusb_handle_events_timeout(session->get_context(), &tv); +        }      } - -    libusb_transfer *_lut; -    usb_endpoint *_endpoint; -    const boost::asio::mutable_buffer _buff; -    bool _committed; -}; - - -/*********************************************************************** - * USB zero_copy device class - **********************************************************************/ -class libusb_zero_copy_impl : public usb_zero_copy -{ -private: -    usb_endpoint          *_rx_ep; -    usb_endpoint          *_tx_ep; - -    // Maintain libusb values -    libusb_context       *_rx_ctx; -    libusb_context       *_tx_ctx; -    libusb_device_handle *_rx_dev_handle; -    libusb_device_handle *_tx_dev_handle; - -    size_t _recv_buff_size; -    size_t _send_buff_size; -    size_t _num_frames; - -public: -    typedef boost::shared_ptr<libusb_zero_copy_impl> sptr; - -    libusb_zero_copy_impl(usb_device_handle::sptr handle, -                          unsigned int rx_endpoint, -                          unsigned int tx_endpoint, -                          size_t recv_buff_size, -                          size_t send_buff_size); -     -    ~libusb_zero_copy_impl(); - -    managed_recv_buffer::sptr get_recv_buff(void); -    managed_send_buffer::sptr get_send_buff(void); - -    size_t get_num_recv_frames(void) const { return _num_frames; } -    size_t get_num_send_frames(void) const { return _num_frames; }  };  /*   * Constructor   * Initializes libusb, opens devices, and sets up interfaces for I/O. - * Finally, creates endpoints for asynchronous I/O.  + * Finally, creates endpoints for asynchronous I/O.   */ -libusb_zero_copy_impl::libusb_zero_copy_impl(usb_device_handle::sptr handle, -                                             unsigned int rx_endpoint, -                                             unsigned int tx_endpoint, -                                             size_t buff_size, -                                             size_t block_size) - : _rx_ctx(NULL), _tx_ctx(NULL), _rx_dev_handle(NULL), _tx_dev_handle(NULL), -   _recv_buff_size(block_size), _send_buff_size(block_size), -   _num_frames(buff_size / block_size) +libusb_zero_copy_impl::libusb_zero_copy_impl( +    libusb::device_handle::sptr handle, +    size_t recv_endpoint, +    size_t send_endpoint, +    const device_addr_t &hints +): +    _handle(handle), +    _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", DEFAULT_XFER_SIZE))), +    _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_XFERS))), +    _send_frame_size(size_t(hints.cast<double>("send_frame_size", DEFAULT_XFER_SIZE))), +    _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_XFERS)))  { -    // Initialize libusb with separate contexts to allow -    // thread safe operation of transmit and receive  -    libusb::init(&_rx_ctx, libusb_debug_level); -    libusb::init(&_tx_ctx, libusb_debug_level); - -    UHD_ASSERT_THROW((_rx_ctx != NULL) && (_tx_ctx != NULL)); - -    // Find and open the libusb_device corresponding to the -    // given handle and return the libusb_device_handle -    // that can be used for I/O purposes. -    _rx_dev_handle = libusb::open_device(_rx_ctx, handle); -    _tx_dev_handle = libusb::open_device(_tx_ctx, handle); - -    // Open USB interfaces for tx/rx using magic values. -    // IN interface:      2 -    // OUT interface:     1 -    // Control interface: 0 -    libusb::open_interface(_rx_dev_handle, 2); -    libusb::open_interface(_tx_dev_handle, 1); - -    _rx_ep = new usb_endpoint(_rx_dev_handle,  // libusb device_handle -                              _rx_ctx,         // libusb context -                              rx_endpoint,     // USB endpoint number +    _handle->claim_interface(2 /*in interface*/); +    _handle->claim_interface(1 /*out interface*/); + +    _recv_ep = usb_endpoint::sptr(new usb_endpoint( +                              _handle,         // libusb device_handle +                              recv_endpoint,   // USB endpoint number                                true,            // IN endpoint -                              _recv_buff_size, // buffer size per transfer  -                              _num_frames);    // number of libusb transfers +                              this->get_recv_frame_size(),  // buffer size per transfer +                              this->get_num_recv_frames()   // number of libusb transfers +    )); -    _tx_ep = new usb_endpoint(_tx_dev_handle,  // libusb device_handle -                              _tx_ctx,         // libusb context -                              tx_endpoint,     // USB endpoint number +    _send_ep = usb_endpoint::sptr(new usb_endpoint( +                              _handle,         // libusb device_handle +                              send_endpoint,   // USB endpoint number                                false,           // OUT endpoint -                              _send_buff_size, // buffer size per transfer -                              _num_frames);    // number of libusb transfers +                              this->get_send_frame_size(),  // buffer size per transfer +                              this->get_num_send_frames()   // number of libusb transfers +    )); + +    //spawn the event handler threads +    size_t concurrency = hints.cast<size_t>("concurrency_hint", 1); +    for (size_t i = 0; i < concurrency; i++) _thread_group.create_thread( +        boost::bind(&libusb_zero_copy_impl::run_event_loop, this) +    );  } - -libusb_zero_copy_impl::~libusb_zero_copy_impl() -{ -    delete _rx_ep; -    delete _tx_ep;  - -    libusb_close(_rx_dev_handle); -    libusb_close(_tx_dev_handle); - -    libusb_exit(_rx_ctx); -    libusb_exit(_tx_ctx); -} - -  /*   * Construct a managed receive buffer from a completed libusb transfer   * (happy with buffer full of data) obtained from the receive endpoint.   * Return empty pointer if no transfer is available (timeout or error). - * \return pointer to a managed receive buffer  + * \return pointer to a managed receive buffer   */ -managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff() -{ -    libusb_transfer *lut = _rx_ep->get_completed_transfer(); +managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff(double timeout){ +    libusb_transfer *lut = _recv_ep->get_lut_with_wait(timeout);      if (lut == NULL) {          return managed_recv_buffer::sptr();      }      else { -        return managed_recv_buffer::sptr( -            new libusb_managed_recv_buffer_impl(lut, -                                                _rx_ep)); +        return managed_recv_buffer::make_safe( +            boost::asio::const_buffer(lut->buffer, lut->actual_length), +            boost::bind(&libusb_zero_copy_impl::release, shared_from_this(), lut) +        );      }  } @@ -729,39 +409,34 @@ managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff()   * Construct a managed send buffer from a free libusb transfer (with   * empty buffer). Return empty pointer of no transfer is available   * (timeout or error). - * \return pointer to a managed send buffer  + * \return pointer to a managed send buffer   */ -managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff() -{ -    libusb_transfer *lut = _tx_ep->get_free_transfer(); +managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff(double timeout){ +    libusb_transfer *lut = _send_ep->get_lut_with_wait(timeout);      if (lut == NULL) {          return managed_send_buffer::sptr();      }      else { -        return managed_send_buffer::sptr( -            new libusb_managed_send_buffer_impl(lut, -                                                _tx_ep, -                                                _send_buff_size)); +        return managed_send_buffer::make_safe( +            boost::asio::mutable_buffer(lut->buffer, this->get_send_frame_size()), +            boost::bind(&libusb_zero_copy_impl::commit, shared_from_this(), lut, _1) +        );      }  } -  /***********************************************************************   * USB zero_copy make functions   **********************************************************************/ -usb_zero_copy::sptr usb_zero_copy::make(usb_device_handle::sptr handle, -                                        unsigned int rx_endpoint, -                                        unsigned int tx_endpoint, -                                        size_t buff_size, -                                        size_t block_size) - -{ -    return sptr(new libusb_zero_copy_impl(handle, -                                          rx_endpoint, -                                          tx_endpoint, -                                          buff_size,  -                                          block_size)); +usb_zero_copy::sptr usb_zero_copy::make( +    usb_device_handle::sptr handle, +    size_t recv_endpoint, +    size_t send_endpoint, +    const device_addr_t &hints +){ +    libusb::device_handle::sptr dev_handle(libusb::device_handle::get_cached_handle( +        boost::static_pointer_cast<libusb::special_handle>(handle)->get_device() +    )); +    return sptr(new libusb_zero_copy_impl( +        dev_handle, recv_endpoint, send_endpoint, hints +    ));  } - - - diff --git a/host/lib/transport/msvc/stdint.h b/host/lib/transport/msvc/stdint.h new file mode 100644 index 000000000..b3eb61aae --- /dev/null +++ b/host/lib/transport/msvc/stdint.h @@ -0,0 +1,35 @@ +//
 +// 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_LIBUHD_TRANSPORT_STDINT_H
 +#define INCLUDED_LIBUHD_TRANSPORT_STDINT_H
 +
 +#include <boost/cstdint.hpp>
 +
 +//provide a stdint implementation for libusb
 +
 +typedef boost::uint64_t uint64_t;
 +typedef boost::uint32_t uint32_t;
 +typedef boost::uint16_t uint16_t;
 +typedef boost::uint8_t uint8_t;
 +
 +typedef boost::int64_t int64_t;
 +typedef boost::int32_t int32_t;
 +typedef boost::int16_t int16_t;
 +typedef boost::int8_t int8_t;
 +
 +#endif /* INCLUDED_LIBUHD_TRANSPORT_STDINT_H */
 diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp index ee989ee2b..d84aeefdd 100644 --- a/host/lib/transport/udp_zero_copy_asio.cpp +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -17,25 +17,58 @@  #include <uhd/transport/udp_zero_copy.hpp>  #include <uhd/transport/udp_simple.hpp> //mtu +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/utils/thread_priority.hpp>  #include <uhd/utils/assert.hpp>  #include <uhd/utils/warning.hpp> -#include <boost/cstdint.hpp> +#include <boost/shared_array.hpp>  #include <boost/asio.hpp>  #include <boost/format.hpp> +#include <boost/thread.hpp> +#include <boost/enable_shared_from_this.hpp>  #include <iostream> +using namespace uhd;  using namespace uhd::transport; +namespace asio = boost::asio;  /***********************************************************************   * Constants   **********************************************************************/ +//Define this to the the boost async io calls to perform receive. +//Otherwise, get_recv_buff uses a blocking receive with timeout. +//#define USE_ASIO_ASYNC_RECV + +//Define this to the the boost async io calls to perform send. +//Otherwise, the commit callback uses a blocking send. +//#define USE_ASIO_ASYNC_SEND +  //enough buffering for half a second of samples at full rate on usrp2 -static const size_t MIN_RECV_SOCK_BUFF_SIZE = size_t(sizeof(boost::uint32_t) * 25e6 * 0.5); +static const size_t MIN_RECV_SOCK_BUFF_SIZE = size_t(4 * 25e6 * 0.5); +  //Large buffers cause more underflow at high rates.  //Perhaps this is due to the kernel scheduling,  //but may change with host-based flow control.  static const size_t MIN_SEND_SOCK_BUFF_SIZE = size_t(10e3); -static const double RECV_TIMEOUT = 0.1; //100 ms + +//The number of async frames to allocate for each send and recv: +//The non-async recv can have a very large number of recv frames +//because the CPU overhead is independent of the number of frames. +#ifdef USE_ASIO_ASYNC_RECV +static const size_t DEFAULT_NUM_RECV_FRAMES = 32; +#else +static const size_t DEFAULT_NUM_RECV_FRAMES = MIN_RECV_SOCK_BUFF_SIZE/udp_simple::mtu; +#endif +//The non-async send only ever requires a single frame +//because the buffer will be committed before a new get. +#ifdef USE_ASIO_ASYNC_SEND +static const size_t DEFAULT_NUM_SEND_FRAMES = 32; +#else +static const size_t DEFAULT_NUM_SEND_FRAMES = MIN_SEND_SOCK_BUFF_SIZE/udp_simple::mtu;; +#endif + +//a single concurrent thread for io_service seems to be the fastest +static const size_t CONCURRENCY_HINT = 1;  /***********************************************************************   * Zero Copy UDP implementation with ASIO: @@ -44,39 +77,68 @@ static const double RECV_TIMEOUT = 0.1; //100 ms   *   However, it is not a true zero copy implementation as each   *   send and recv requires a copy operation to/from userspace.   **********************************************************************/ -class udp_zero_copy_impl: -    public phony_zero_copy_recv_if, -    public phony_zero_copy_send_if, -    public udp_zero_copy -{ +class udp_zero_copy_asio_impl : public udp_zero_copy, public boost::enable_shared_from_this<udp_zero_copy_asio_impl> {  public: -    typedef boost::shared_ptr<udp_zero_copy_impl> sptr; +    typedef boost::shared_ptr<udp_zero_copy_asio_impl> sptr; -    udp_zero_copy_impl( +    udp_zero_copy_asio_impl(          const std::string &addr, -        const std::string &port +        const std::string &port, +        const device_addr_t &hints      ): -        phony_zero_copy_recv_if(udp_simple::mtu), -        phony_zero_copy_send_if(udp_simple::mtu) +        _io_service(hints.cast<size_t>("concurrency_hint", CONCURRENCY_HINT)), +        _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", udp_simple::mtu))), +        _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_RECV_FRAMES))), +        _send_frame_size(size_t(hints.cast<double>("send_frame_size", udp_simple::mtu))), +        _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_SEND_FRAMES)))      {          //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); +        //resolve the address +        asio::ip::udp::resolver resolver(_io_service); +        asio::ip::udp::resolver::query query(asio::ip::udp::v4(), addr, port); +        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()); +        //create, open, and connect the socket +        _socket = new asio::ip::udp::socket(_io_service); +        _socket->open(asio::ip::udp::v4());          _socket->connect(receiver_endpoint);          _sock_fd = _socket->native();      } -    ~udp_zero_copy_impl(void){ +    ~udp_zero_copy_asio_impl(void){ +        delete _work; //allow io_service run to complete +        _thread_group.join_all(); //wait for service threads to exit          delete _socket;      } +    void init(void){ +        //allocate all recv frames and release them to begin xfers +        _pending_recv_buffs = pending_buffs_type::make(_num_recv_frames); +        _recv_buffer = boost::shared_array<char>(new char[_num_recv_frames*_recv_frame_size]); +        for (size_t i = 0; i < _num_recv_frames; i++){ +            release(_recv_buffer.get() + i*_recv_frame_size); +        } + +        //allocate all send frames and push them into the fifo +        _pending_send_buffs = pending_buffs_type::make(_num_send_frames); +        _send_buffer = boost::shared_array<char>(new char[_num_send_frames*_send_frame_size]); +        for (size_t i = 0; i < _num_send_frames; i++){ +            handle_send(_send_buffer.get() + i*_send_frame_size); +        } + +        //spawn the service threads that will run the io service +        _work = new asio::io_service::work(_io_service); //new work to delete later +        for (size_t i = 0; i < CONCURRENCY_HINT; i++) _thread_group.create_thread( +            boost::bind(&udp_zero_copy_asio_impl::service, this) +        ); +    } + +    void service(void){ +        set_thread_priority_safe(); +        _io_service.run(); +    } +      //get size for internal socket buffer      template <typename Opt> size_t get_buff_size(void) const{          Opt option; @@ -91,60 +153,165 @@ public:          return get_buff_size<Opt>();      } +    //! handle a recv callback -> push the filled memory into the fifo +    UHD_INLINE void handle_recv(void *mem, size_t len){ +        boost::this_thread::disable_interruption di; //disable because the wait can throw +        _pending_recv_buffs->push_with_wait(boost::asio::buffer(mem, len)); +    } -    //The number of frames is approximately the buffer size divided by the max datagram size. -    //In reality, this is a phony zero-copy interface and the number of frames is infinite. -    //However, its sensible to advertise a frame count that is approximate to buffer size. -    //This way, the transport caller will have an idea about how much buffering to create. - -    size_t get_num_recv_frames(void) const{ -        return this->get_buff_size<boost::asio::socket_base::receive_buffer_size>()/udp_simple::mtu; +    //////////////////////////////////////////////////////////////////// +    #ifdef USE_ASIO_ASYNC_RECV +    //////////////////////////////////////////////////////////////////// +    //! pop a filled recv buffer off of the fifo and bind with the release callback +    managed_recv_buffer::sptr get_recv_buff(double timeout){ +        boost::this_thread::disable_interruption di; //disable because the wait can throw +        asio::mutable_buffer buff; +        if (_pending_recv_buffs->pop_with_timed_wait(buff, timeout)){ +            return managed_recv_buffer::make_safe( +                buff, boost::bind( +                    &udp_zero_copy_asio_impl::release, +                    shared_from_this(), +                    asio::buffer_cast<void*>(buff) +                ) +            ); +        } +        return managed_recv_buffer::sptr();      } -    size_t get_num_send_frames(void) const{ -        return this->get_buff_size<boost::asio::socket_base::send_buffer_size>()/udp_simple::mtu; +    //! release a recv buffer -> start an async recv on the buffer +    void release(void *mem){ +        _socket->async_receive( +            boost::asio::buffer(mem, this->get_recv_frame_size()), +            boost::bind( +                &udp_zero_copy_asio_impl::handle_recv, +                shared_from_this(), mem, +                asio::placeholders::bytes_transferred +            ) +        );      } -private: -    boost::asio::ip::udp::socket   *_socket; -    boost::asio::io_service        _io_service; -    int                            _sock_fd; +    //////////////////////////////////////////////////////////////////// +    #else /*USE_ASIO_ASYNC_RECV*/ +    //////////////////////////////////////////////////////////////////// +    managed_recv_buffer::sptr get_recv_buff(double timeout){ +        boost::this_thread::disable_interruption di; //disable because the wait can throw +        asio::mutable_buffer buff; -    ssize_t recv(const boost::asio::mutable_buffer &buff){          //setup timeval for timeout          timeval tv;          tv.tv_sec = 0; -        tv.tv_usec = int(RECV_TIMEOUT*1e6); +        tv.tv_usec = long(timeout*1e6);          //setup rset for timeout          fd_set rset;          FD_ZERO(&rset);          FD_SET(_sock_fd, &rset); -        //call select to perform timed wait -        if (::select(_sock_fd+1, &rset, NULL, NULL, &tv) <= 0) return 0; +        //call select to perform timed wait and grab an available buffer with wait +        //if the condition is true, call receive and return the managed buffer +        if ( +            ::select(_sock_fd+1, &rset, NULL, NULL, &tv) > 0 and +            _pending_recv_buffs->pop_with_timed_wait(buff, timeout) +        ){ +            return managed_recv_buffer::make_safe( +                asio::buffer( +                    boost::asio::buffer_cast<void *>(buff), +                    _socket->receive(asio::buffer(buff)) +                ), +                boost::bind( +                    &udp_zero_copy_asio_impl::release, +                    shared_from_this(), +                    asio::buffer_cast<void*>(buff) +                ) +            ); +        } +        return managed_recv_buffer::sptr(); +    } + +    void release(void *mem){ +        boost::this_thread::disable_interruption di; //disable because the wait can throw +        handle_recv(mem, this->get_recv_frame_size()); +    } -        return ::recv( -            _sock_fd, -            boost::asio::buffer_cast<char *>(buff), -            boost::asio::buffer_size(buff), 0 -        ); +    //////////////////////////////////////////////////////////////////// +    #endif /*USE_ASIO_ASYNC_RECV*/ +    //////////////////////////////////////////////////////////////////// + +    size_t get_num_recv_frames(void) const {return _num_recv_frames;} +    size_t get_recv_frame_size(void) const {return _recv_frame_size;} + +    //! handle a send callback -> push the emptied memory into the fifo +    UHD_INLINE void handle_send(void *mem){ +        boost::this_thread::disable_interruption di; //disable because the wait can throw +        _pending_send_buffs->push_with_wait(boost::asio::buffer(mem, this->get_send_frame_size()));      } -    ssize_t send(const boost::asio::const_buffer &buff){ -        return ::send( -            _sock_fd, -            boost::asio::buffer_cast<const char *>(buff), -            boost::asio::buffer_size(buff), 0 +    //! pop an empty send buffer off of the fifo and bind with the commit callback +    managed_send_buffer::sptr get_send_buff(double timeout){ +        boost::this_thread::disable_interruption di; //disable because the wait can throw +        asio::mutable_buffer buff; +        if (_pending_send_buffs->pop_with_timed_wait(buff, timeout)){ +            return managed_send_buffer::make_safe( +                buff, boost::bind( +                    &udp_zero_copy_asio_impl::commit, +                    shared_from_this(), +                    asio::buffer_cast<void*>(buff), _1 +                ) +            ); +        } +        return managed_send_buffer::sptr(); +    } + +    //////////////////////////////////////////////////////////////////// +    #ifdef USE_ASIO_ASYNC_SEND +    //////////////////////////////////////////////////////////////////// +    //! commit a send buffer -> start an async send on the buffer +    void commit(void *mem, size_t len){ +        _socket->async_send( +            boost::asio::buffer(mem, len), +            boost::bind( +                &udp_zero_copy_asio_impl::handle_send, +                shared_from_this(), mem +            )          );      } + +    //////////////////////////////////////////////////////////////////// +    #else /*USE_ASIO_ASYNC_SEND*/ +    //////////////////////////////////////////////////////////////////// +    void commit(void *mem, size_t len){ +        _socket->send(asio::buffer(mem, len)); +        handle_send(mem); +    } + +    //////////////////////////////////////////////////////////////////// +    #endif /*USE_ASIO_ASYNC_SEND*/ +    //////////////////////////////////////////////////////////////////// + +    size_t get_num_send_frames(void) const {return _num_send_frames;} +    size_t get_send_frame_size(void) const {return _send_frame_size;} + +private: +    //asio guts -> socket and service +    asio::ip::udp::socket   *_socket; +    asio::io_service        _io_service; +    asio::io_service::work  *_work; +    int                     _sock_fd; + +    //memory management -> buffers and fifos +    boost::thread_group _thread_group; +    boost::shared_array<char> _send_buffer, _recv_buffer; +    typedef bounded_buffer<asio::mutable_buffer> pending_buffs_type; +    pending_buffs_type::sptr _pending_recv_buffs, _pending_send_buffs; +    const size_t _recv_frame_size, _num_recv_frames; +    const size_t _send_frame_size, _num_send_frames;  };  /***********************************************************************   * UDP zero copy make function   **********************************************************************/  template<typename Opt> static void resize_buff_helper( -    udp_zero_copy_impl::sptr udp_trans, +    udp_zero_copy_asio_impl::sptr udp_trans,      size_t target_size,      const std::string &name  ){ @@ -152,6 +319,13 @@ template<typename Opt> static void resize_buff_helper(      if (name == "recv") min_sock_buff_size = MIN_RECV_SOCK_BUFF_SIZE;      if (name == "send") min_sock_buff_size = MIN_SEND_SOCK_BUFF_SIZE; +    std::string help_message; +    #if defined(UHD_PLATFORM_LINUX) +        help_message = str(boost::format( +            "Please run: sudo sysctl -w net.core.%smem_max=%d\n" +        ) % ((name == "recv")?"r":"w") % min_sock_buff_size); +    #endif /*defined(UHD_PLATFORM_LINUX)*/ +      //resize the buffer if size was provided      if (target_size > 0){          size_t actual_size = udp_trans->resize_buff<Opt>(target_size); @@ -165,8 +339,8 @@ template<typename Opt> static void resize_buff_helper(          if (actual_size < target_size) uhd::print_warning(str(boost::format(              "The %s buffer is smaller than the requested size.\n"              "The minimum recommended buffer size is %d bytes.\n" -            "See the USRP2 application notes on buffer resizing.\n" -        ) % name % min_sock_buff_size)); +            "See the transport application notes on buffer resizing.\n%s" +        ) % name % min_sock_buff_size % help_message));      }      //only enable on platforms that are happy with the large buffer resize @@ -181,14 +355,21 @@ template<typename Opt> static void resize_buff_helper(  udp_zero_copy::sptr udp_zero_copy::make(      const std::string &addr,      const std::string &port, -    size_t recv_buff_size, -    size_t send_buff_size +    const device_addr_t &hints  ){ -    udp_zero_copy_impl::sptr udp_trans(new udp_zero_copy_impl(addr, port)); +    udp_zero_copy_asio_impl::sptr udp_trans( +        new udp_zero_copy_asio_impl(addr, port, hints) +    ); + +    //extract buffer size hints from the device addr +    size_t recv_buff_size = size_t(hints.cast<double>("recv_buff_size", 0.0)); +    size_t send_buff_size = size_t(hints.cast<double>("send_buff_size", 0.0));      //call the helper to resize send and recv buffers -    resize_buff_helper<boost::asio::socket_base::receive_buffer_size>(udp_trans, recv_buff_size, "recv"); -    resize_buff_helper<boost::asio::socket_base::send_buffer_size>   (udp_trans, send_buff_size, "send"); +    resize_buff_helper<asio::socket_base::receive_buffer_size>(udp_trans, recv_buff_size, "recv"); +    resize_buff_helper<asio::socket_base::send_buffer_size>   (udp_trans, send_buff_size, "send"); + +    udp_trans->init(); //buffers resized -> call init() to use      return udp_trans;  } diff --git a/host/lib/transport/usb_dummy_impl.cpp b/host/lib/transport/usb_dummy_impl.cpp new file mode 100644 index 000000000..8a9772e7f --- /dev/null +++ b/host/lib/transport/usb_dummy_impl.cpp @@ -0,0 +1,39 @@ +// +// 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/usb_device_handle.hpp> +#include <uhd/transport/usb_control.hpp> +#include <uhd/transport/usb_zero_copy.hpp> +#include <uhd/utils/exception.hpp> + +using namespace uhd; +using namespace uhd::transport; + +std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list(boost::uint16_t, boost::uint16_t){ +    return std::vector<usb_device_handle::sptr>(); //empty list +} + +usb_control::sptr usb_control::make(usb_device_handle::sptr){ +    throw std::runtime_error("no usb support -> usb_control::make not implemented"); +} + +usb_zero_copy::sptr usb_zero_copy::make( +    usb_device_handle::sptr, +    size_t, size_t, const device_addr_t & +){ +    throw std::runtime_error("no usb support -> usb_zero_copy::make not implemented"); +} diff --git a/host/lib/transport/vrt_packet_handler.hpp b/host/lib/transport/vrt_packet_handler.hpp index 7e0588f03..939517411 100644 --- a/host/lib/transport/vrt_packet_handler.hpp +++ b/host/lib/transport/vrt_packet_handler.hpp @@ -150,7 +150,8 @@ template <typename T> UHD_INLINE T get_context_code(          const vrt_unpacker_t &vrt_unpacker,          const get_recv_buffs_t &get_recv_buffs,          const handle_overflow_t &handle_overflow, -        size_t vrt_header_offset_words32 +        size_t vrt_header_offset_words32, +        size_t chans_per_otw_buff      ){          metadata.error_code = uhd::rx_metadata_t::ERROR_CODE_NONE; @@ -184,15 +185,21 @@ template <typename T> UHD_INLINE T get_context_code(          //extract the number of samples available to copy          size_t bytes_per_item = otw_type.get_sample_size();          size_t nsamps_available = state.size_of_copy_buffs/bytes_per_item; -        size_t nsamps_to_copy = std::min(total_samps, nsamps_available); +        size_t nsamps_to_copy = std::min(total_samps*chans_per_otw_buff, nsamps_available);          size_t bytes_to_copy = nsamps_to_copy*bytes_per_item; +        size_t nsamps_to_copy_per_io_buff = nsamps_to_copy/chans_per_otw_buff; + +        std::vector<void *> io_buffs(chans_per_otw_buff); +        for (size_t i = 0; i < state.width; i+=chans_per_otw_buff){ + +            //fill a vector with pointers to the io buffers +            for (size_t j = 0; j < chans_per_otw_buff; j++){ +                io_buffs[j] = reinterpret_cast<boost::uint8_t *>(buffs[i+j]) + offset_bytes; +            } -        for (size_t i = 0; i < state.width; i++){              //copy-convert the samples from the recv buffer              uhd::transport::convert_otw_type_to_io_type( -                state.copy_buffs[i], otw_type, -                reinterpret_cast<boost::uint8_t *>(buffs[i]) + offset_bytes, -                io_type, nsamps_to_copy +                state.copy_buffs[i], otw_type, io_buffs, io_type, nsamps_to_copy_per_io_buff              );              //update the rx copy buffer to reflect the bytes copied @@ -206,7 +213,7 @@ template <typename T> UHD_INLINE T get_context_code(          metadata.fragment_offset = state.fragment_offset_in_samps;          state.fragment_offset_in_samps += nsamps_to_copy; //set for next call -        return nsamps_to_copy; +        return nsamps_to_copy_per_io_buff;      }      /******************************************************************* @@ -224,7 +231,8 @@ template <typename T> UHD_INLINE T get_context_code(          const vrt_unpacker_t &vrt_unpacker,          const get_recv_buffs_t &get_recv_buffs,          const handle_overflow_t &handle_overflow = &handle_overflow_nop, -        size_t vrt_header_offset_words32 = 0 +        size_t vrt_header_offset_words32 = 0, +        size_t chans_per_otw_buff = 1      ){          switch(recv_mode){ @@ -241,7 +249,8 @@ template <typename T> UHD_INLINE T get_context_code(                  vrt_unpacker,                  get_recv_buffs,                  handle_overflow, -                vrt_header_offset_words32 +                vrt_header_offset_words32, +                chans_per_otw_buff              );          } @@ -261,7 +270,8 @@ template <typename T> UHD_INLINE T get_context_code(                      vrt_unpacker,                      get_recv_buffs,                      handle_overflow, -                    vrt_header_offset_words32 +                    vrt_header_offset_words32, +                    chans_per_otw_buff                  );                  if (num_samps == 0) break; //had a recv timeout or error, break loop                  accum_num_samps += num_samps; @@ -293,47 +303,49 @@ template <typename T> UHD_INLINE T get_context_code(       * Pack a vrt header, copy-convert the data, and send it.       *  - helper function for vrt_packet_handler::send       ******************************************************************/ -    static UHD_INLINE void _send1( +    static UHD_INLINE size_t _send1(          send_state &state,          const std::vector<const void *> &buffs, -        size_t offset_bytes, -        size_t num_samps, +        const size_t offset_bytes, +        const size_t num_samps,          uhd::transport::vrt::if_packet_info_t &if_packet_info,          const uhd::io_type_t &io_type,          const uhd::otw_type_t &otw_type,          const vrt_packer_t &vrt_packer,          const get_send_buffs_t &get_send_buffs, -        size_t vrt_header_offset_words32 +        const size_t vrt_header_offset_words32, +        const size_t chans_per_otw_buff      ){          //load the rest of the if_packet_info in here -        if_packet_info.num_payload_words32 = (num_samps*otw_type.get_sample_size())/sizeof(boost::uint32_t); +        if_packet_info.num_payload_words32 = (num_samps*chans_per_otw_buff*otw_type.get_sample_size())/sizeof(boost::uint32_t);          if_packet_info.packet_count = state.next_packet_seq++;          //get send buffers for each channel -        managed_send_buffs_t send_buffs(buffs.size()); -        UHD_ASSERT_THROW(get_send_buffs(send_buffs)); +        managed_send_buffs_t send_buffs(buffs.size()/chans_per_otw_buff); +        if (not get_send_buffs(send_buffs)) return 0; -        for (size_t i = 0; i < buffs.size(); i++){ +        std::vector<const void *> io_buffs(chans_per_otw_buff); +        for (size_t i = 0; i < buffs.size(); i+=chans_per_otw_buff){              //calculate pointers with offsets to io and otw memory -            const boost::uint8_t *io_mem = reinterpret_cast<const boost::uint8_t *>(buffs[i]) + offset_bytes; +            for (size_t j = 0; j < chans_per_otw_buff; j++){ +                io_buffs[j] = reinterpret_cast<const boost::uint8_t *>(buffs[i+j]) + offset_bytes; +            }              boost::uint32_t *otw_mem = send_buffs[i]->cast<boost::uint32_t *>() + vrt_header_offset_words32;              //pack metadata into a vrt header              vrt_packer(otw_mem, if_packet_info); +            otw_mem += if_packet_info.num_header_words32;              //copy-convert the samples into the send buffer              uhd::transport::convert_io_type_to_otw_type( -                io_mem, io_type, -                otw_mem + if_packet_info.num_header_words32, otw_type, -                num_samps +                io_buffs, io_type, otw_mem, otw_type, num_samps              );              //commit the samples to the zero-copy interface              size_t num_bytes_total = (vrt_header_offset_words32+if_packet_info.num_packet_words32)*sizeof(boost::uint32_t); -            if (send_buffs[i]->commit(num_bytes_total) < ssize_t(num_bytes_total)){ -                std::cerr << "commit to send buffer returned less than commit size" << std::endl; -            } +            send_buffs[i]->commit(num_bytes_total);          } +        return num_samps;      }      /******************************************************************* @@ -351,7 +363,8 @@ template <typename T> UHD_INLINE T get_context_code(          const vrt_packer_t &vrt_packer,          const get_send_buffs_t &get_send_buffs,          size_t max_samples_per_packet, -        size_t vrt_header_offset_words32 = 0 +        size_t vrt_header_offset_words32 = 0, +        size_t chans_per_otw_buff = 1      ){          //translate the metadata to vrt if packet info          uhd::transport::vrt::if_packet_info_t if_packet_info; @@ -367,7 +380,6 @@ template <typename T> UHD_INLINE T get_context_code(          ////////////////////////////////////////////////////////////////          case uhd::device::SEND_MODE_ONE_PACKET:{          //////////////////////////////////////////////////////////////// -            size_t num_samps = std::min(total_num_samps, max_samples_per_packet);              //fill in parts of the packet info overwrote in full buff mode              if_packet_info.has_tsi = metadata.has_time_spec; @@ -375,49 +387,54 @@ template <typename T> UHD_INLINE T get_context_code(              if_packet_info.sob = metadata.start_of_burst;              if_packet_info.eob = metadata.end_of_burst; -            _send1( +            return _send1(                  state,                  buffs, 0, -                num_samps, +                std::min(total_num_samps, max_samples_per_packet),                  if_packet_info,                  io_type, otw_type,                  vrt_packer,                  get_send_buffs, -                vrt_header_offset_words32 +                vrt_header_offset_words32, +                chans_per_otw_buff              ); -            return num_samps;          }          ////////////////////////////////////////////////////////////////          case uhd::device::SEND_MODE_FULL_BUFF:{          //////////////////////////////////////////////////////////////// -            //calculate constants for fragmentation -            const size_t num_fragments = (total_num_samps+max_samples_per_packet-1)/max_samples_per_packet; -            static const size_t first_fragment_index = 0; -            const size_t final_fragment_index = num_fragments-1; +            size_t total_num_samps_sent = 0;              //loop through the following fragment indexes -            for (size_t n = first_fragment_index; n <= final_fragment_index; n++){ +            while(total_num_samps_sent < total_num_samps){ + +                //calculate per-loop-iteration variables +                const size_t total_num_samps_unsent = total_num_samps - total_num_samps_sent; +                const bool first_fragment = (total_num_samps_sent == 0); +                const bool final_fragment = (total_num_samps_unsent <= max_samples_per_packet);                  //calculate new flags for the fragments -                if_packet_info.has_tsi = metadata.has_time_spec  and (n == first_fragment_index); -                if_packet_info.has_tsf = metadata.has_time_spec  and (n == first_fragment_index); -                if_packet_info.sob     = metadata.start_of_burst and (n == first_fragment_index); -                if_packet_info.eob     = metadata.end_of_burst   and (n == final_fragment_index); +                if_packet_info.has_tsi = metadata.has_time_spec  and first_fragment; +                if_packet_info.has_tsf = if_packet_info.has_tsi; +                if_packet_info.sob     = metadata.start_of_burst and first_fragment; +                if_packet_info.eob     = metadata.end_of_burst   and final_fragment;                  //send the fragment with the helper function -                _send1( +                const size_t num_samps_sent = _send1(                      state, -                    buffs, n*max_samples_per_packet*io_type.size, -                    (n == final_fragment_index)?(total_num_samps%max_samples_per_packet):max_samples_per_packet, +                    buffs, total_num_samps_sent*io_type.size, +                    std::min(total_num_samps_unsent, max_samples_per_packet),                      if_packet_info,                      io_type, otw_type,                      vrt_packer,                      get_send_buffs, -                    vrt_header_offset_words32 +                    vrt_header_offset_words32, +                    chans_per_otw_buff                  ); +                total_num_samps_sent += num_samps_sent; +                if (num_samps_sent == 0) return total_num_samps_sent;              } -            return total_num_samps; +            return total_num_samps_sent;          }          default: throw std::runtime_error("unknown send mode"); diff --git a/host/lib/transport/zero_copy.cpp b/host/lib/transport/zero_copy.cpp index 8a1cde694..a5a864a04 100644 --- a/host/lib/transport/zero_copy.cpp +++ b/host/lib/transport/zero_copy.cpp @@ -16,32 +16,35 @@  //  #include <uhd/transport/zero_copy.hpp> -#include <boost/cstdint.hpp> -#include <boost/function.hpp> -#include <boost/bind.hpp>  using namespace uhd::transport;  /*********************************************************************** - * The pure-virtual deconstructor needs an implementation to be happy + * Safe managed receive buffer   **********************************************************************/ -managed_recv_buffer::~managed_recv_buffer(void){ +static void release_nop(void){      /* NOP */  } -/*********************************************************************** - * Phony zero-copy recv interface implementation - **********************************************************************/ - -//! phony zero-copy recv buffer implementation -class managed_recv_buffer_impl : public managed_recv_buffer{ +class safe_managed_receive_buffer : public managed_recv_buffer{  public: -    managed_recv_buffer_impl(const boost::asio::const_buffer &buff) : _buff(buff){ +    safe_managed_receive_buffer( +        const boost::asio::const_buffer &buff, +        const release_fcn_t &release_fcn +    ): +        _buff(buff), _release_fcn(release_fcn) +    {          /* NOP */      } -    ~managed_recv_buffer_impl(void){ -        delete [] this->cast<const boost::uint8_t *>(); +    ~safe_managed_receive_buffer(void){ +        _release_fcn(); +    } + +    void release(void){ +        release_fcn_t release_fcn = _release_fcn; +        _release_fcn = &release_nop; +        return release_fcn();      }  private: @@ -50,64 +53,42 @@ private:      }      const boost::asio::const_buffer _buff; +    release_fcn_t _release_fcn;  }; -//! phony zero-copy recv interface implementation -struct phony_zero_copy_recv_if::impl{ -    impl(size_t max_buff_size) : max_buff_size(max_buff_size){ -        /* NOP */ -    } -    size_t max_buff_size; -}; - -phony_zero_copy_recv_if::phony_zero_copy_recv_if(size_t max_buff_size){ -    _impl = UHD_PIMPL_MAKE(impl, (max_buff_size)); -} - -phony_zero_copy_recv_if::~phony_zero_copy_recv_if(void){ -    /* NOP */ -} - -managed_recv_buffer::sptr phony_zero_copy_recv_if::get_recv_buff(void){ -    //allocate memory -    boost::uint8_t *recv_mem = new boost::uint8_t[_impl->max_buff_size]; - -    //call recv() with timeout option -    ssize_t num_bytes = this->recv(boost::asio::buffer(recv_mem, _impl->max_buff_size)); - -    if (num_bytes <= 0) return managed_recv_buffer::sptr(); //NULL sptr - -    //create a new managed buffer to house the data -    return managed_recv_buffer::sptr( -        new managed_recv_buffer_impl(boost::asio::buffer(recv_mem, num_bytes)) -    ); +managed_recv_buffer::sptr managed_recv_buffer::make_safe( +    const boost::asio::const_buffer &buff, +    const release_fcn_t &release_fcn +){ +    return sptr(new safe_managed_receive_buffer(buff, release_fcn));  }  /*********************************************************************** - * Phony zero-copy send interface implementation + * Safe managed send buffer   **********************************************************************/ +static void commit_nop(size_t){ +    /* NOP */ +} -//! phony zero-copy send buffer implementation -class managed_send_buffer_impl : public managed_send_buffer{ +class safe_managed_send_buffer : public managed_send_buffer{  public: -    typedef boost::function<ssize_t(const boost::asio::const_buffer &)> send_fcn_t; - -    managed_send_buffer_impl( +    safe_managed_send_buffer(          const boost::asio::mutable_buffer &buff, -        const send_fcn_t &send_fcn +        const commit_fcn_t &commit_fcn      ): -        _buff(buff), -        _send_fcn(send_fcn) +        _buff(buff), _commit_fcn(commit_fcn)      {          /* NOP */      } -    ~managed_send_buffer_impl(void){ -        /* NOP */ +    ~safe_managed_send_buffer(void){ +        _commit_fcn(0);      } -    ssize_t commit(size_t num_bytes){ -        return _send_fcn(boost::asio::buffer(_buff, num_bytes)); +    void commit(size_t num_bytes){ +        commit_fcn_t commit_fcn = _commit_fcn; +        _commit_fcn = &commit_nop; +        return commit_fcn(num_bytes);      }  private: @@ -116,28 +97,12 @@ private:      }      const boost::asio::mutable_buffer _buff; -    const send_fcn_t                  _send_fcn; -}; - -//! phony zero-copy send interface implementation -struct phony_zero_copy_send_if::impl{ -    boost::uint8_t *send_mem; -    managed_send_buffer::sptr send_buff; +    commit_fcn_t _commit_fcn;  }; -phony_zero_copy_send_if::phony_zero_copy_send_if(size_t max_buff_size){ -    _impl = UHD_PIMPL_MAKE(impl, ()); -    _impl->send_mem = new boost::uint8_t[max_buff_size]; -    _impl->send_buff = managed_send_buffer::sptr(new managed_send_buffer_impl( -        boost::asio::buffer(_impl->send_mem, max_buff_size), -        boost::bind(&phony_zero_copy_send_if::send, this, _1) -    )); -} - -phony_zero_copy_send_if::~phony_zero_copy_send_if(void){ -    delete [] _impl->send_mem; -} - -managed_send_buffer::sptr phony_zero_copy_send_if::get_send_buff(void){ -    return _impl->send_buff; //FIXME there is only ever one send buff, we assume that the caller doesnt hang onto these +safe_managed_send_buffer::sptr managed_send_buffer::make_safe( +    const boost::asio::mutable_buffer &buff, +    const commit_fcn_t &commit_fcn +){ +    return sptr(new safe_managed_send_buffer(buff, commit_fcn));  } diff --git a/host/lib/types.cpp b/host/lib/types.cpp index f957cd83f..6aa82b012 100644 --- a/host/lib/types.cpp +++ b/host/lib/types.cpp @@ -124,14 +124,14 @@ time_spec_t::time_spec_t(time_t full_secs, double frac_secs):      /* NOP */  } -time_spec_t::time_spec_t(time_t full_secs, size_t tick_count, double tick_rate): +time_spec_t::time_spec_t(time_t full_secs, long tick_count, double tick_rate):      _full_secs(full_secs),      _frac_secs(double(tick_count)/tick_rate)  {      /* NOP */  } -size_t time_spec_t::get_tick_count(double tick_rate) const{ +long time_spec_t::get_tick_count(double tick_rate) const{      return boost::math::iround(this->get_frac_secs()*tick_rate);  } @@ -140,7 +140,9 @@ double time_spec_t::get_real_secs(void) const{  }  time_t time_spec_t::get_full_secs(void) const{ -    return this->_full_secs + time_t(std::floor(this->_frac_secs)); +    double intpart; +    std::modf(this->_frac_secs, &intpart); +    return this->_full_secs + time_t(intpart);  }  double time_spec_t::get_frac_secs(void) const{ @@ -160,13 +162,18 @@ time_spec_t &time_spec_t::operator-=(const time_spec_t &rhs){  }  bool uhd::operator==(const time_spec_t &lhs, const time_spec_t &rhs){ -    return lhs.get_full_secs() == rhs.get_full_secs() and lhs.get_frac_secs() == rhs.get_frac_secs(); +    return +        lhs.get_full_secs() == rhs.get_full_secs() and +        lhs.get_frac_secs() == rhs.get_frac_secs() +    ;  }  bool uhd::operator<(const time_spec_t &lhs, const time_spec_t &rhs){ -    if (lhs.get_full_secs() < rhs.get_full_secs()) return true; -    if (lhs.get_full_secs() > rhs.get_full_secs()) return false; -    return lhs.get_frac_secs() < rhs.get_frac_secs(); +    return ( +        (lhs.get_full_secs() < rhs.get_full_secs()) or ( +        (lhs.get_full_secs() == rhs.get_full_secs()) and +        (lhs.get_frac_secs() < rhs.get_frac_secs()) +    ));  }  /*********************************************************************** diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index 8b8c18a8b..c264252e1 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -23,11 +23,12 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_SOURCE_DIR}/lib/usrp/dboard_id.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/dboard_manager.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/dsp_utils.cpp -    ${CMAKE_SOURCE_DIR}/lib/usrp/mimo_usrp.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/misc_utils.cpp -    ${CMAKE_SOURCE_DIR}/lib/usrp/simple_usrp.cpp +    ${CMAKE_SOURCE_DIR}/lib/usrp/multi_usrp.cpp +    ${CMAKE_SOURCE_DIR}/lib/usrp/single_usrp.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/subdev_spec.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/tune_helper.cpp +    ${CMAKE_SOURCE_DIR}/lib/usrp/wrapper_utils.hpp  )  INCLUDE(${CMAKE_SOURCE_DIR}/lib/usrp/dboard/CMakeLists.txt) diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt index 3e995009e..8d3d11530 100644 --- a/host/lib/usrp/dboard/CMakeLists.txt +++ b/host/lib/usrp/dboard/CMakeLists.txt @@ -24,5 +24,6 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_wbx.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_dbsrx.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_unknown.cpp +    ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_tvrx.cpp  ) diff --git a/host/lib/usrp/dboard/db_basic_and_lf.cpp b/host/lib/usrp/dboard/db_basic_and_lf.cpp index 2a9bf2ca5..41f6f8002 100644 --- a/host/lib/usrp/dboard/db_basic_and_lf.cpp +++ b/host/lib/usrp/dboard/db_basic_and_lf.cpp @@ -59,6 +59,7 @@ private:  static const uhd::dict<std::string, subdev_conn_t> sd_name_to_conn = map_list_of      ("AB", SUBDEV_CONN_COMPLEX_IQ) +    ("BA", SUBDEV_CONN_COMPLEX_QI)      ("A",  SUBDEV_CONN_REAL_I)      ("B",  SUBDEV_CONN_REAL_Q)  ; @@ -148,6 +149,10 @@ void basic_rx::rx_get(const wax::obj &key_, wax::obj &val){          val = sd_name_to_conn[get_subdev_name()];          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -177,6 +182,9 @@ void basic_rx::rx_set(const wax::obj &key_, const wax::obj &val){      case SUBDEV_PROP_FREQ:          return; // it wont do you much good, but you can set it +    case SUBDEV_PROP_ENABLED: +        return; //always enabled +      default: UHD_THROW_PROP_SET_ERROR();      }  } @@ -240,6 +248,10 @@ void basic_tx::tx_get(const wax::obj &key_, wax::obj &val){          val = sd_name_to_conn[get_subdev_name()];          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -269,6 +281,9 @@ void basic_tx::tx_set(const wax::obj &key_, const wax::obj &val){      case SUBDEV_PROP_FREQ:          return; // it wont do you much good, but you can set it +    case SUBDEV_PROP_ENABLED: +        return; //always enabled +      default: UHD_THROW_PROP_SET_ERROR();      }  } diff --git a/host/lib/usrp/dboard/db_dbsrx.cpp b/host/lib/usrp/dboard/db_dbsrx.cpp index 81434f054..939a79e58 100644 --- a/host/lib/usrp/dboard/db_dbsrx.cpp +++ b/host/lib/usrp/dboard/db_dbsrx.cpp @@ -69,7 +69,7 @@ public:  private:      double _lo_freq; -    float _bandwidth; +    double _bandwidth;      uhd::dict<std::string, float> _gains;      max2118_write_regs_t _max2118_write_regs;      max2118_read_regs_t _max2118_read_regs; @@ -79,7 +79,7 @@ private:      void set_lo_freq(double target_freq);      void set_gain(float gain, const std::string &name); -    void set_bandwidth(float bandwidth); +    void set_bandwidth(double bandwidth);      void send_reg(boost::uint8_t start_reg, boost::uint8_t stop_reg){          start_reg = boost::uint8_t(std::clip(int(start_reg), 0x0, 0x5)); @@ -239,7 +239,6 @@ void dbsrx::set_lo_freq(double target_freq){      double actual_freq=0.0, pfd_freq=0.0, ref_clock=0.0;      int R=0, N=0, r=0, m=0;      bool update_filter_settings = false; -      //choose refclock      std::vector<double> clock_rates = this->get_iface()->get_clock_rates(dboard_iface::UNIT_RX);      BOOST_FOREACH(ref_clock, std::reversed(std::sorted(clock_rates))){ @@ -251,7 +250,7 @@ void dbsrx::set_lo_freq(double target_freq){          if(dbsrx_debug) std::cerr << boost::format(              "DBSRX: trying ref_clock %f and m_divider %d" -        ) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % m << std::endl; +        ) % (ref_clock) % m << std::endl;          if (m >= 32) continue; @@ -277,15 +276,17 @@ void dbsrx::set_lo_freq(double target_freq){          }      }  -    //Assert because we failed to find a suitable combination of ref_clock, R and N  -    UHD_ASSERT_THROW(ref_clock/(1 << m) < 1e6 or ref_clock/(1 << m) > 2.5e6); -    UHD_ASSERT_THROW((pfd_freq < dbsrx_pfd_freq_range.min) or (pfd_freq > dbsrx_pfd_freq_range.max)); -    UHD_ASSERT_THROW((N < 256) or (N > 32768));      done_loop: +    //Assert because we failed to find a suitable combination of ref_clock, R and N  +    UHD_ASSERT_THROW(ref_clock <= 27.0e6 and ref_clock >= 0.0); +    UHD_ASSERT_THROW(ref_clock/m >= 1e6 and ref_clock/m <= 2.5e6); +    UHD_ASSERT_THROW((pfd_freq >= dbsrx_pfd_freq_range.min) and (pfd_freq <= dbsrx_pfd_freq_range.max)); +    UHD_ASSERT_THROW((N >= 256) and (N <= 32768)); +      if(dbsrx_debug) std::cerr << boost::format( -        "DBSRX: choose ref_clock %f and m_divider %d" -    ) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % m << std::endl; +        "DBSRX: choose ref_clock (current: %f, new: %f) and m_divider %d" +    ) % (this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)) % ref_clock % m << std::endl;      //if ref_clock or m divider changed, we need to update the filter settings      if (ref_clock != this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX) or m != _max2118_write_regs.m_divider) update_filter_settings = true; @@ -349,7 +350,7 @@ void dbsrx::set_lo_freq(double target_freq){                          "DBSRX: Tuning exceeded vco range, _max2118_write_regs.osc_band == %d\n"                           ) % int(_max2118_write_regs.osc_band))                  ); -                UHD_ASSERT_THROW(_max2118_read_regs.adc == 0); +                UHD_ASSERT_THROW(_max2118_read_regs.adc != 0); //just to cause a throw              }              if (_max2118_write_regs.osc_band <= 0) break;              _max2118_write_regs.osc_band -= 1; @@ -363,7 +364,7 @@ void dbsrx::set_lo_freq(double target_freq){                          "DBSRX: Tuning exceeded vco range, _max2118_write_regs.osc_band == %d\n"                           ) % int(_max2118_write_regs.osc_band))                  ); -                UHD_ASSERT_THROW(_max2118_read_regs.adc == 0); +                UHD_ASSERT_THROW(_max2118_read_regs.adc != 7); //just to cause a throw              }              if (_max2118_write_regs.osc_band >= 7) break;              _max2118_write_regs.osc_band += 1; @@ -401,6 +402,7 @@ void dbsrx::set_lo_freq(double target_freq){          << boost::format("    Ref    Freq=%fMHz\n") % (ref_clock/1e6)          << boost::format("    Target Freq=%fMHz\n") % (target_freq/1e6)          << boost::format("    Actual Freq=%fMHz\n") % (_lo_freq/1e6) +        << boost::format("    VCO    Freq=%fMHz\n") % (vco_freq/1e6)          << std::endl;      if (update_filter_settings) set_bandwidth(_bandwidth); @@ -480,9 +482,9 @@ void dbsrx::set_gain(float gain, const std::string &name){  /***********************************************************************   * Bandwidth Handling   **********************************************************************/ -void dbsrx::set_bandwidth(float bandwidth){ +void dbsrx::set_bandwidth(double bandwidth){      //clip the input -    bandwidth = std::clip<float>(bandwidth, 4e6, 33e6); +    bandwidth = std::clip<double>(bandwidth, 4e6, 33e6);      double ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX); @@ -492,7 +494,7 @@ void dbsrx::set_bandwidth(float bandwidth){      _max2118_write_regs.f_dac = std::clip<int>(int((((bandwidth*_max2118_write_regs.m_divider)/ref_clock) - 4)/0.145),0,127);      //determine actual bandwidth -    _bandwidth = float((ref_clock/(_max2118_write_regs.m_divider))*(4+0.145*_max2118_write_regs.f_dac)); +    _bandwidth = double((ref_clock/(_max2118_write_regs.m_divider))*(4+0.145*_max2118_write_regs.f_dac));      if (dbsrx_debug) std::cerr << boost::format(          "DBSRX Filter Bandwidth: %f MHz, m: %d, f_dac: %d\n" @@ -551,6 +553,10 @@ void dbsrx::rx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_IQ;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -559,12 +565,6 @@ void dbsrx::rx_get(const wax::obj &key_, wax::obj &val){          val = this->get_locked();          return; -/* -    case SUBDEV_PROP_RSSI: -        val = this->get_rssi(); -        return; -*/ -      case SUBDEV_PROP_BANDWIDTH:          val = _bandwidth;          return; @@ -587,8 +587,11 @@ void dbsrx::rx_set(const wax::obj &key_, const wax::obj &val){          this->set_gain(val.as<float>(), key.name);          return; +    case SUBDEV_PROP_ENABLED: +        return; //always enabled +      case SUBDEV_PROP_BANDWIDTH: -        this->set_bandwidth(val.as<float>()); +        this->set_bandwidth(val.as<double>());          return;      default: UHD_THROW_PROP_SET_ERROR(); diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index c3ab96e59..cfc34381e 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -444,6 +444,10 @@ void rfx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_QI;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -474,6 +478,9 @@ void rfx_xcvr::rx_set(const wax::obj &key_, const wax::obj &val){          this->set_rx_ant(val.as<std::string>());          return; +    case SUBDEV_PROP_ENABLED: +        return; //always enabled +      default: UHD_THROW_PROP_SET_ERROR();      }  } @@ -524,6 +531,10 @@ void rfx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_IQ;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = true;          return; @@ -554,6 +565,9 @@ void rfx_xcvr::tx_set(const wax::obj &key_, const wax::obj &val){          this->set_tx_ant(val.as<std::string>());          return; +    case SUBDEV_PROP_ENABLED: +        return; //always enabled +      default: UHD_THROW_PROP_SET_ERROR();      }  } diff --git a/host/lib/usrp/dboard/db_tvrx.cpp b/host/lib/usrp/dboard/db_tvrx.cpp new file mode 100644 index 000000000..10be8d1c3 --- /dev/null +++ b/host/lib/usrp/dboard/db_tvrx.cpp @@ -0,0 +1,485 @@ +// +// 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/>. +// + +// No RX IO Pins Used + +// RX IO Functions + +//ADC/DAC functions: +//DAC 1: RF AGC +//DAC 2: IF AGC + +//min freq: 50e6 +//max freq: 860e6 +//gain range: [0:1dB:115dB] + +#include <uhd/utils/static.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/warning.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <boost/array.hpp> +#include <boost/math/special_functions/round.hpp> +#include <utility> +#include <cmath> +#include <cfloat> +#include <limits> +#include <tuner_4937di5_regs.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +/*********************************************************************** + * The tvrx constants + **********************************************************************/ +static const bool tvrx_debug = false; + +static const freq_range_t tvrx_freq_range(50e6, 860e6); + +static const prop_names_t tvrx_antennas = list_of("RX"); + +static const uhd::dict<std::string, freq_range_t> tvrx_freq_ranges = map_list_of +    ("VHFLO", freq_range_t(50e6, 158e6)) +    ("VHFHI", freq_range_t(158e6, 454e6)) +    ("UHF"  , freq_range_t(454e6, 860e6)) +; + +static const boost::array<double, 17> vhflo_gains_db = +    {{-6.00000, -6.00000, -6.00000, -4.00000, 0.00000, +     5.00000, 10.00000, 17.40000, 26.30000, 36.00000, +     43.00000, 48.00000, 49.50000, 50.10000, 50.30000, +     50.30000, 50.30000}}; + +static const boost::array<double, 17> vhfhi_gains_db = +    {{-13.3000,  -13.3000,  -13.3000,   -1.0000,    7.7000, +    11.0000,   14.7000,   19.3000,   26.1000,   36.0000, +    42.7000,   46.0000,   47.0000,   47.8000,   48.2000, +    48.2000,   48.2000}}; + +static const boost::array<double, 17> uhf_gains_db = +    {{-8.0000,   -8.0000,   -7.0000,    4.0000,   10.2000, +     14.5000,   17.5000,   20.0000,   24.5000,   30.8000, +     37.0000,   39.8000,   40.7000,   41.6000,   42.6000, +     43.2000,   43.8000}}; + +static const boost::array<double, 17> tvrx_if_gains_db = +    {{-1.50000,   -1.50000,   -1.50000,   -1.00000,    0.20000, +     2.10000,    4.30000,    6.40000,    9.00000,   12.00000, +     14.80000,   18.20000,   26.10000,   32.50000,  32.50000, +     32.50000,   32.50000}}; + +//gain linearization data +//this is from the datasheet and is dB vs. volts (below) +//i tried to curve fit this, but it's really just so nonlinear that you'd +//need dang near as many coefficients as to just map it like this and interp. +//these numbers are culled from the 4937DI5 datasheet and are probably totally inaccurate +//but if it's better than the old linear fit i'm happy +static const uhd::dict<std::string, boost::array<double, 17> > tvrx_rf_gains_db = map_list_of +    ("VHFLO", vhflo_gains_db) +    ("VHFHI", vhfhi_gains_db) +    ("UHF"  , uhf_gains_db) +; + +//sample voltages for the above points +static const boost::array<double, 17> tvrx_gains_volts = +    {{0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0}}; + +static uhd::dict<std::string, gain_range_t> get_tvrx_gain_ranges(void) { +    double rfmax = 0.0, rfmin = FLT_MAX; +    BOOST_FOREACH(const std::string range, tvrx_rf_gains_db.keys()) { +        double my_max = tvrx_rf_gains_db[range].back(); //we're assuming it's monotonic +        double my_min = tvrx_rf_gains_db[range].front(); //if it's not this is wrong wrong wrong +        if(my_max > rfmax) rfmax = my_max; +        if(my_min < rfmin) rfmin = my_min; +    } + +    double ifmin = tvrx_if_gains_db.front(); +    double ifmax = tvrx_if_gains_db.back(); + +    return map_list_of +        ("RF", gain_range_t(float(rfmin), float(rfmax), float((rfmax-rfmin)/4096.0))) +        ("IF", gain_range_t(float(ifmin), float(ifmax), float((ifmax-ifmin)/4096.0))) +    ; +} + +static const double opamp_gain = 1.22; //onboard DAC opamp gain +static const double tvrx_if_freq = 43.75e6; //IF freq of TVRX module +static const boost::uint16_t reference_divider = 640; //clock reference divider to use +static const double reference_freq = 4.0e6; + +/*********************************************************************** + * The tvrx dboard class + **********************************************************************/ +class tvrx : public rx_dboard_base{ +public: +    tvrx(ctor_args_t args); +    ~tvrx(void); + +    void rx_get(const wax::obj &key, wax::obj &val); +    void rx_set(const wax::obj &key, const wax::obj &val); + +private: +    uhd::dict<std::string, float> _gains; +    double _lo_freq; +    tuner_4937di5_regs_t _tuner_4937di5_regs; +    boost::uint8_t _tuner_4937di5_addr(void){ +        return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x61 : 0x60; //ok really? we could rename that call +    }; + +    void set_gain(float gain, const std::string &name); +    void set_freq(double freq); + +    void update_regs(void){ +        byte_vector_t regs_vector(4); + +        //get the register data +        for(int i=0; i<4; i++){ +            regs_vector[i] = _tuner_4937di5_regs.get_reg(i); +            if(tvrx_debug) std::cerr << boost::format( +                "tvrx: send reg 0x%02x, value 0x%04x" +            ) % int(i) % int(regs_vector[i]) << std::endl; +        } + +        //send the data +        this->get_iface()->write_i2c( +            _tuner_4937di5_addr(), regs_vector +        ); +    } + +}; + +/*********************************************************************** + * Register the tvrx dboard + **********************************************************************/ +static dboard_base::sptr make_tvrx(dboard_base::ctor_args_t args){ +    return dboard_base::sptr(new tvrx(args)); +} + +UHD_STATIC_BLOCK(reg_tvrx_dboard){ +    //register the factory function for the rx dbid +    dboard_manager::register_dboard(0x0040, &make_tvrx, "tvrx"); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){ +    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); + +    //set the gpio directions and atr controls (identically) +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0); // All unused in atr +    if (this->get_iface()->get_special_props().soft_clock_divider){ +        this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x1); // GPIO0 is clock +    } +    else{ +        this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs +    } + +    //send initial register settings if necessary + +    //set default freq +    _lo_freq = tvrx_freq_range.min + tvrx_if_freq; //init _lo_freq to a sane default +    set_freq(tvrx_freq_range.min); + +    //set default gains +    BOOST_FOREACH(const std::string &name, get_tvrx_gain_ranges().keys()){ +        set_gain(get_tvrx_gain_ranges()[name].min, name); +    } +} + +tvrx::~tvrx(void){ +} + +/*! Return a string corresponding to the relevant band + * \param freq the frequency of interest + * \return a string corresponding to the band + */ + +static std::string get_band(double freq) { +    BOOST_FOREACH(const std::string &band, tvrx_freq_ranges.keys()) { +        if(freq >= tvrx_freq_ranges[band].min && freq <= tvrx_freq_ranges[band].max){ +            if(tvrx_debug) std::cout << "Band: " << band << std::endl; +            return band; +        } +    } +    UHD_THROW_INVALID_CODE_PATH(); +} + +/*********************************************************************** + * Gain Handling + **********************************************************************/ +/*! + * Execute a linear interpolation to find the voltage corresponding to a desired gain + * \param gain the desired gain in dB + * \param db_vector the vector of dB readings + * \param volts_vector the corresponding vector of voltages db_vector was sampled at + * \return a voltage to feed the TVRX analog gain + */ + +static double gain_interp(double gain, boost::array<double, 17> db_vector, boost::array<double, 17> volts_vector) { +    double volts; +    gain = std::clip<double>(gain, db_vector.front(), db_vector.back()); //let's not get carried away here + +    boost::uint8_t gain_step = 0; +    //find which bin we're in +    for(size_t i = 0; i < db_vector.size()-1; i++) { +        if(gain >= db_vector[i] && gain <= db_vector[i+1]) gain_step = i; +    } + +    //find the current slope for linear interpolation +    double slope = (volts_vector[gain_step + 1] - volts_vector[gain_step]) +                / (db_vector[gain_step + 1] - db_vector[gain_step]); + +    //the problem here is that for gains approaching the maximum, the voltage slope becomes infinite +    //i.e., a small change in gain requires an infinite change in voltage +    //to cope, we limit the slope + +    if(slope == std::numeric_limits<double>::infinity()) +        return volts_vector[gain_step]; + +    //use the volts per dB slope to find the final interpolated voltage +    volts = volts_vector[gain_step] + (slope * (gain - db_vector[gain_step])); + +    if(tvrx_debug) +        std::cout << "Gain interp: gain: " << gain << ", gain_step: " << int(gain_step) << ", slope: " << slope << ", volts: " << volts << std::endl; + +    return volts; +} + +/*! + * Convert a requested gain for the RF gain into a DAC voltage. + * The gain passed into the function will be set to the actual value. + * \param gain the requested gain in dB + * \return dac voltage value + */ + +static float rf_gain_to_voltage(float gain, double lo_freq){ +    //clip the input +    gain = std::clip<float>(gain, get_tvrx_gain_ranges()["RF"].min, get_tvrx_gain_ranges()["RF"].max); + +    //first we need to find out what band we're in, because gains are different across different bands +    std::string band = get_band(lo_freq + tvrx_if_freq); + +    //this is the voltage at the TVRX gain input +    double gain_volts = gain_interp(gain, tvrx_rf_gains_db[band], tvrx_gains_volts); +    //this is the voltage at the USRP DAC output +    double dac_volts = gain_volts / opamp_gain; + +    dac_volts = std::clip<double>(dac_volts, 0.0, 3.3); + +    if (tvrx_debug) std::cerr << boost::format( +        "tvrx RF AGC gain: %f dB, dac_volts: %f V" +    ) % gain % dac_volts << std::endl; + +    return float(dac_volts); +} + +/*! + * Convert a requested gain for the IF gain into a DAC voltage. + * The gain passed into the function will be set to the actual value. + * \param gain the requested gain in dB + * \return dac voltage value + */ + +static float if_gain_to_voltage(float gain){ +    //clip the input +    gain = std::clip<float>(gain, get_tvrx_gain_ranges()["IF"].min, get_tvrx_gain_ranges()["IF"].max); + +    double gain_volts = gain_interp(gain, tvrx_if_gains_db, tvrx_gains_volts); +    double dac_volts = gain_volts / opamp_gain; + +    dac_volts = std::clip<double>(dac_volts, 0.0, 3.3); + +    if (tvrx_debug) std::cerr << boost::format( +        "tvrx IF AGC gain: %f dB, dac_volts: %f V" +    ) % gain % dac_volts << std::endl; + +    return float(dac_volts); +} + +void tvrx::set_gain(float gain, const std::string &name){ +    assert_has(get_tvrx_gain_ranges().keys(), name, "tvrx gain name"); +    if (name == "RF"){ +        this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_B, rf_gain_to_voltage(gain, _lo_freq)); +    } +    else if(name == "IF"){ +        this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, if_gain_to_voltage(gain)); +    } +    else UHD_THROW_INVALID_CODE_PATH(); +    _gains[name] = gain; +} + +/*! + * Set the tuner to center the desired frequency at 43.75MHz + * \param freq the requested frequency + */ + +void tvrx::set_freq(double freq) { +    freq = std::clip<double>(freq, tvrx_freq_range.min, tvrx_freq_range.max); +    std::string prev_band = get_band(_lo_freq - tvrx_if_freq); +    std::string new_band = get_band(freq); + +    double target_lo_freq = freq + tvrx_if_freq; //the desired LO freq for high-side mixing +    double f_ref = reference_freq / double(reference_divider); //your tuning step size + +    int divisor = int((target_lo_freq + (f_ref * 4.0)) / (f_ref * 8)); //the divisor we'll use +    double actual_lo_freq = (f_ref * 8 * divisor); //the LO freq we'll actually get + +    if((divisor & ~0x7fff)) UHD_THROW_INVALID_CODE_PATH(); + +    //now we update the registers +    _tuner_4937di5_regs.db1 = (divisor >> 8) & 0xff; +    _tuner_4937di5_regs.db2 = divisor & 0xff; + +    if(new_band == "VHFLO") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_VHFLO; +    else if(new_band == "VHFHI") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_VHFHI; +    else if(new_band == "UHF") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_UHF; +    else UHD_THROW_INVALID_CODE_PATH(); + +    _tuner_4937di5_regs.power = tuner_4937di5_regs_t::POWER_OFF; +    update_regs(); + +    //ok don't forget to reset RF gain here if the new band != the old band +    //we do this because the gains are different for different band settings +    //not FAR off, but we do this to be consistent +    if(prev_band != new_band) set_gain(_gains["RF"], "RF"); + +    if(tvrx_debug) +        std::cout << boost::format("set_freq: target LO: %f f_ref: %f divisor: %i actual LO: %f") % target_lo_freq % f_ref % divisor % actual_lo_freq << std::endl; + +    _lo_freq = actual_lo_freq; //for rx props +} + +/*********************************************************************** + * Get the alias frequency of frequency freq when sampled at fs. + * \param freq the frequency of interest + * \param fs the sample rate + * \return the alias frequency + **********************************************************************/ + +static double get_alias(double freq, double fs) { +    double alias; +    freq = fmod(freq, fs); +    if(freq >= (fs/2)) { +        alias = fs - freq; +    } else { +        alias = freq; +    } +    return alias; +} + +/*********************************************************************** + * RX Get and Set + **********************************************************************/ +void tvrx::rx_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +    double codec_rate; + +    //handle the get request conditioned on the key +    switch(key.as<subdev_prop_t>()){ +    case SUBDEV_PROP_NAME: +        val = get_rx_id().to_pp_string(); +        return; + +    case SUBDEV_PROP_OTHERS: +        val = prop_names_t(); //empty +        return; + +    case SUBDEV_PROP_GAIN: +        assert_has(_gains.keys(), key.name, "tvrx gain name"); +        val = _gains[key.name]; +        return; + +    case SUBDEV_PROP_GAIN_RANGE: +        assert_has(get_tvrx_gain_ranges().keys(), key.name, "tvrx gain name"); +        val = get_tvrx_gain_ranges()[key.name]; +        return; + +    case SUBDEV_PROP_GAIN_NAMES: +        val = prop_names_t(get_tvrx_gain_ranges().keys()); +        return; + +    case SUBDEV_PROP_FREQ: +    /* +     * so here we have to do some magic. because the TVRX uses a relatively high IF, +     * we have to watch the sample rate to see if the IF will be aliased +     * or if it will fall within Nyquist. +     */ +        codec_rate = this->get_iface()->get_codec_rate(dboard_iface::UNIT_RX); +        val = (_lo_freq - tvrx_if_freq) + get_alias(tvrx_if_freq, codec_rate); +        return; + +    case SUBDEV_PROP_FREQ_RANGE: +        val = tvrx_freq_range; +        return; + +    case SUBDEV_PROP_ANTENNA: +        val = tvrx_antennas.front(); //there's only one +        return; + +    case SUBDEV_PROP_ANTENNA_NAMES: +        val = tvrx_antennas; +        return; + +    case SUBDEV_PROP_CONNECTION: +        val = SUBDEV_CONN_COMPLEX_IQ; +        return; + +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; + +    case SUBDEV_PROP_USE_LO_OFFSET: +        val = false; +        return; + +    case SUBDEV_PROP_LO_LOCKED: +        val = true; +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +void tvrx::rx_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the get request conditioned on the key +    switch(key.as<subdev_prop_t>()){ +    case SUBDEV_PROP_GAIN: +        this->set_gain(val.as<float>(), key.name); +        return; + +    case SUBDEV_PROP_FREQ: +        this->set_freq(val.as<double>()); +        return; + +    case SUBDEV_PROP_ENABLED: +        return; //always enabled + +    default: UHD_THROW_PROP_SET_ERROR(); +    } +} + diff --git a/host/lib/usrp/dboard/db_unknown.cpp b/host/lib/usrp/dboard/db_unknown.cpp index f6f4f4a61..ec7ab440b 100644 --- a/host/lib/usrp/dboard/db_unknown.cpp +++ b/host/lib/usrp/dboard/db_unknown.cpp @@ -122,6 +122,10 @@ void unknown_rx::rx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_IQ;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -151,6 +155,9 @@ void unknown_rx::rx_set(const wax::obj &key_, const wax::obj &val){      case SUBDEV_PROP_FREQ:          return; // it wont do you much good, but you can set it +    case SUBDEV_PROP_ENABLED: +        return; //always enabled +      default: UHD_THROW_PROP_SET_ERROR();      }  } @@ -211,6 +218,10 @@ void unknown_tx::tx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_IQ;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -240,6 +251,9 @@ void unknown_tx::tx_set(const wax::obj &key_, const wax::obj &val){      case SUBDEV_PROP_FREQ:          return; // it wont do you much good, but you can set it +    case SUBDEV_PROP_ENABLED: +        return; //always enabled +      default: UHD_THROW_PROP_SET_ERROR();      }  } diff --git a/host/lib/usrp/dboard/db_wbx.cpp b/host/lib/usrp/dboard/db_wbx.cpp index ccd897674..907268aac 100644 --- a/host/lib/usrp/dboard/db_wbx.cpp +++ b/host/lib/usrp/dboard/db_wbx.cpp @@ -154,7 +154,7 @@ static dboard_base::sptr make_wbx(dboard_base::ctor_args_t args){  }  UHD_STATIC_BLOCK(reg_wbx_dboards){ -    dboard_manager::register_dboard(0x0052, 0x0053, &make_wbx, "WBX"); +    dboard_manager::register_dboard(0x0053, 0x0052, &make_wbx, "WBX");  }  /*********************************************************************** @@ -513,6 +513,10 @@ void wbx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_IQ;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -543,6 +547,9 @@ void wbx_xcvr::rx_set(const wax::obj &key_, const wax::obj &val){          this->set_rx_ant(val.as<std::string>());          return; +    case SUBDEV_PROP_ENABLED: +        return; //always enabled +      default: UHD_THROW_PROP_SET_ERROR();      }  } @@ -597,6 +604,10 @@ void wbx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_IQ;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -627,6 +638,9 @@ void wbx_xcvr::tx_set(const wax::obj &key_, const wax::obj &val){          this->set_tx_ant(val.as<std::string>());          return; +    case SUBDEV_PROP_ENABLED: +        return; //always enabled +      default: UHD_THROW_PROP_SET_ERROR();      }  } diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index 798ff74a3..fb1367113 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -51,6 +51,7 @@  #include <uhd/utils/static.hpp>  #include <uhd/utils/assert.hpp>  #include <uhd/utils/algorithm.hpp> +#include <uhd/utils/warning.hpp>  #include <uhd/types/ranges.hpp>  #include <uhd/types/dict.hpp>  #include <uhd/usrp/subdev_props.hpp> @@ -72,6 +73,7 @@ using namespace boost::assign;  static const bool xcvr2450_debug = false;  static const freq_range_t xcvr_freq_range(2.4e9, 6.0e9); +static const freq_range_t xcvr_freq_band_seperation(2.5e9, 4.9e9);  static const prop_names_t xcvr_antennas = list_of("J1")("J2"); @@ -100,6 +102,7 @@ public:  private:      double _lo_freq; +    double _rx_bandwidth, _tx_bandwidth;      uhd::dict<std::string, float> _tx_gains, _rx_gains;      std::string _tx_ant, _rx_ant;      int _ad9515div; @@ -110,6 +113,8 @@ private:      void set_rx_ant(const std::string &ant);      void set_tx_gain(float gain, const std::string &name);      void set_rx_gain(float gain, const std::string &name); +    void set_rx_bandwidth(double bandwidth); +    void set_tx_bandwidth(double bandwidth);      void update_atr(void);      void spi_reset(void); @@ -176,6 +181,9 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){      spi_reset(); //prepare the spi +    _rx_bandwidth = 9.5e6; +    _tx_bandwidth = 12.0e6; +      //setup the misc max2829 registers      _max2829_regs.mimo_select         = max2829_regs_t::MIMO_SELECT_MIMO;      _max2829_regs.band_sel_mimo       = max2829_regs_t::BAND_SEL_MIMO_MIMO; @@ -183,7 +191,7 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){      _max2829_regs.rssi_high_bw        = max2829_regs_t::RSSI_HIGH_BW_6MHZ;      _max2829_regs.tx_lpf_coarse_adj   = max2829_regs_t::TX_LPF_COARSE_ADJ_12MHZ;      _max2829_regs.rx_lpf_coarse_adj   = max2829_regs_t::RX_LPF_COARSE_ADJ_9_5MHZ; -    _max2829_regs.rx_lpf_fine_adj     = max2829_regs_t::RX_LPF_FINE_ADJ_95; +    _max2829_regs.rx_lpf_fine_adj     = max2829_regs_t::RX_LPF_FINE_ADJ_100;      _max2829_regs.rx_vga_gain_spi     = max2829_regs_t::RX_VGA_GAIN_SPI_SPI;      _max2829_regs.rssi_output_range   = max2829_regs_t::RSSI_OUTPUT_RANGE_HIGH;      _max2829_regs.rssi_op_mode        = max2829_regs_t::RSSI_OP_MODE_ENABLED; @@ -244,15 +252,24 @@ void xcvr2450::update_atr(void){      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        POWER_UP_RXIO | RX_DIS_RXIO);      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     POWER_UP_RXIO | RX_ENB_RXIO);      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     POWER_UP_RXIO | RX_DIS_RXIO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, POWER_UP_RXIO | RX_ENB_RXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, POWER_UP_RXIO | RX_DIS_RXIO);  }  /***********************************************************************   * Tuning   **********************************************************************/  void xcvr2450::set_lo_freq(double target_freq){ +    //clip for highband and lowband +    if((target_freq > xcvr_freq_band_seperation.min) and (target_freq < xcvr_freq_band_seperation.max)){ +        if(target_freq - xcvr_freq_band_seperation.min < xcvr_freq_band_seperation.max - target_freq){ +            target_freq = xcvr_freq_band_seperation.min; +        }else{ +            target_freq = xcvr_freq_band_seperation.max; +        } +    } + +    //clip for max and min      target_freq = std::clip(target_freq, xcvr_freq_range.min, xcvr_freq_range.max); -    //TODO: clip for highband and lowband      //variables used in the calculation below      double scaler = xcvr2450::is_highband(target_freq)? (4.0/5.0) : (4.0/3.0); @@ -434,6 +451,114 @@ void xcvr2450::set_rx_gain(float gain, const std::string &name){      _rx_gains[name] = gain;  } + +/*********************************************************************** + * Bandwidth Handling + **********************************************************************/ +static max2829_regs_t::tx_lpf_coarse_adj_t bandwidth_to_tx_lpf_coarse_reg(double &bandwidth){ +    int reg = std::clip(boost::math::iround((bandwidth-6.0e6)/6.0e6), 1, 3); + +    switch(reg){ +    case 1: // bandwidth < 15MHz +        bandwidth = 12e6; +        return max2829_regs_t::TX_LPF_COARSE_ADJ_12MHZ; +    case 2: // 15MHz < bandwidth < 21MHz +        bandwidth = 18e6; +        return max2829_regs_t::TX_LPF_COARSE_ADJ_18MHZ; +    case 3: // bandwidth > 21MHz +        bandwidth = 24e6; +        return max2829_regs_t::TX_LPF_COARSE_ADJ_24MHZ; +    } +    UHD_THROW_INVALID_CODE_PATH(); +} + +static max2829_regs_t::rx_lpf_fine_adj_t bandwidth_to_rx_lpf_fine_reg(double &bandwidth, double requested_bandwidth){ +    int reg = std::clip(boost::math::iround((requested_bandwidth/bandwidth)/0.05), 18, 22); + +    switch(reg){ +    case 18: // requested_bandwidth < 92.5% +        bandwidth = 0.9 * bandwidth; +        return max2829_regs_t::RX_LPF_FINE_ADJ_90; +    case 19: // 92.5% < requested_bandwidth < 97.5% +        bandwidth = 0.95 * bandwidth; +        return max2829_regs_t::RX_LPF_FINE_ADJ_95; +    case 20: // 97.5% < requested_bandwidth < 102.5% +        bandwidth = 1.0 * bandwidth; +        return max2829_regs_t::RX_LPF_FINE_ADJ_100; +    case 21: // 102.5% < requested_bandwidth < 107.5% +        bandwidth = 1.05 * bandwidth; +        return max2829_regs_t::RX_LPF_FINE_ADJ_105; +    case 22: // 107.5% < requested_bandwidth +        bandwidth = 1.1 * bandwidth; +        return max2829_regs_t::RX_LPF_FINE_ADJ_110; +    } +    UHD_THROW_INVALID_CODE_PATH(); +} + +static max2829_regs_t::rx_lpf_coarse_adj_t bandwidth_to_rx_lpf_coarse_reg(double &bandwidth){ +    int reg = std::clip(boost::math::iround((bandwidth-7.0e6)/1.0e6), 0, 11); + +    switch(reg){ +    case 0: // bandwidth < 7.5MHz +    case 1: // 7.5MHz < bandwidth < 8.5MHz +        bandwidth = 7.5e6; +        return max2829_regs_t::RX_LPF_COARSE_ADJ_7_5MHZ; +    case 2: // 8.5MHz < bandwidth < 9.5MHz +    case 3: // 9.5MHz < bandwidth < 10.5MHz +    case 4: // 10.5MHz < bandwidth < 11.5MHz +        bandwidth = 9.5e6; +        return max2829_regs_t::RX_LPF_COARSE_ADJ_9_5MHZ; +    case 5: // 11.5MHz < bandwidth < 12.5MHz +    case 6: // 12.5MHz < bandwidth < 13.5MHz +    case 7: // 13.5MHz < bandwidth < 14.5MHz +    case 8: // 14.5MHz < bandwidth < 15.5MHz +        bandwidth = 14e6; +        return max2829_regs_t::RX_LPF_COARSE_ADJ_14MHZ; +    case 9: // 15.5MHz < bandwidth < 16.5MHz +    case 10: // 16.5MHz < bandwidth < 17.5MHz +    case 11: // 17.5MHz < bandwidth +        bandwidth = 18e6; +        return max2829_regs_t::RX_LPF_COARSE_ADJ_18MHZ; +    } +    UHD_THROW_INVALID_CODE_PATH(); +} + +void xcvr2450::set_rx_bandwidth(double bandwidth){ +    double requested_bandwidth = bandwidth; + +    //compute coarse low pass cutoff frequency setting +    _max2829_regs.rx_lpf_coarse_adj = bandwidth_to_rx_lpf_coarse_reg(bandwidth); + +    //compute fine low pass cutoff frequency setting +    _max2829_regs.rx_lpf_fine_adj = bandwidth_to_rx_lpf_fine_reg(bandwidth, requested_bandwidth); + +    //shadow bandwidth setting +    _rx_bandwidth = bandwidth; + +    //update register +    send_reg(0x7); + +    if (xcvr2450_debug) std::cerr << boost::format( +        "XCVR2450 RX Bandwidth (lp_fc): %f Hz, coarse reg: %d, fine reg: %d" +    ) % _rx_bandwidth % (int(_max2829_regs.rx_lpf_coarse_adj)) % (int(_max2829_regs.rx_lpf_fine_adj)) << std::endl; +} + +void xcvr2450::set_tx_bandwidth(double bandwidth){ +    //compute coarse low pass cutoff frequency setting +    _max2829_regs.tx_lpf_coarse_adj = bandwidth_to_tx_lpf_coarse_reg(bandwidth); + +    //shadow bandwidth setting +    _tx_bandwidth = bandwidth; + +    //update register +    send_reg(0x7); + +    if (xcvr2450_debug) std::cerr << boost::format( +        "XCVR2450 TX Bandwidth (lp_fc): %f Hz, coarse reg: %d" +    ) % _tx_bandwidth % (int(_max2829_regs.tx_lpf_coarse_adj)) << std::endl; +} + +  /***********************************************************************   * RX Get and Set   **********************************************************************/ @@ -484,6 +609,10 @@ void xcvr2450::rx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_IQ;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -496,6 +625,10 @@ void xcvr2450::rx_get(const wax::obj &key_, wax::obj &val){          val = this->get_rssi();          return; +    case SUBDEV_PROP_BANDWIDTH: +        val = _rx_bandwidth; +        return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } @@ -518,6 +651,13 @@ void xcvr2450::rx_set(const wax::obj &key_, const wax::obj &val){          this->set_rx_ant(val.as<std::string>());          return; +    case SUBDEV_PROP_BANDWIDTH: +        this->set_rx_bandwidth(val.as<double>()); +        return; + +    case SUBDEV_PROP_ENABLED: +        return; //always enabled +      default: UHD_THROW_PROP_SET_ERROR();      }  } @@ -572,6 +712,10 @@ void xcvr2450::tx_get(const wax::obj &key_, wax::obj &val){          val = SUBDEV_CONN_COMPLEX_QI;          return; +    case SUBDEV_PROP_ENABLED: +        val = true; //always enabled +        return; +      case SUBDEV_PROP_USE_LO_OFFSET:          val = false;          return; @@ -580,6 +724,10 @@ void xcvr2450::tx_get(const wax::obj &key_, wax::obj &val){          val = this->get_locked();          return; +    case SUBDEV_PROP_BANDWIDTH: +        val = _tx_bandwidth; +        return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } @@ -598,10 +746,17 @@ void xcvr2450::tx_set(const wax::obj &key_, const wax::obj &val){          this->set_tx_gain(val.as<float>(), key.name);          return; +    case SUBDEV_PROP_BANDWIDTH: +        this->set_tx_bandwidth(val.as<double>()); +        return; +      case SUBDEV_PROP_ANTENNA:          this->set_tx_ant(val.as<std::string>());          return; +    case SUBDEV_PROP_ENABLED: +        return; //always enabled +      default: UHD_THROW_PROP_SET_ERROR();      }  } diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index ab80875f5..78daa1b4d 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -18,6 +18,7 @@  #include "dboard_ctor_args.hpp"  #include <uhd/usrp/dboard_manager.hpp>  #include <uhd/usrp/subdev_props.hpp> +#include <uhd/utils/warning.hpp>  #include <uhd/utils/static.hpp>  #include <uhd/utils/assert.hpp>  #include <uhd/types/dict.hpp> @@ -85,7 +86,7 @@ std::string dboard_id_t::to_pp_string(void) const{  }  /*********************************************************************** - * internal helper classe + * internal helper classes   **********************************************************************/  /*!   * A special wax proxy object that forwards calls to a subdev. @@ -176,10 +177,6 @@ static args_t get_dboard_args(  ){      //special case, the none id was provided, use the following ids      if (dboard_id == dboard_id_t::none() or force_to_unknown){ -        std::cerr << boost::format( -            "Warning: unknown dboard-id or dboard-id combination: %s\n" -            "    -> defaulting to the unknown board type" -        ) % dboard_id.to_pp_string() << std::endl;          UHD_ASSERT_THROW(get_id_to_args_map().has_key(0xfff1));          UHD_ASSERT_THROW(get_id_to_args_map().has_key(0xfff0));          switch(unit){ @@ -191,6 +188,9 @@ static args_t get_dboard_args(      //verify that there is a registered constructor for this id      if (not get_id_to_args_map().has_key(dboard_id)){ +        uhd::print_warning(str(boost::format( +            "Unknown dboard ID: %s.\n" +        ) % dboard_id.to_pp_string()));          return get_dboard_args(unit, dboard_id, true);      } @@ -214,12 +214,25 @@ dboard_manager_impl::dboard_manager_impl(          (get_xcvr_id_to_id_map()[tx_dboard_id] == rx_dboard_id)      ); +    //warn for invalid dboard id xcvr combinations +    if (rx_dboard_is_xcvr != this_dboard_is_xcvr or tx_dboard_is_xcvr != this_dboard_is_xcvr){ +        uhd::print_warning(str(boost::format( +            "Unknown transceiver board ID combination...\n" +            "RX dboard ID: %s\n" +            "TX dboard ID: %s\n" +        ) % rx_dboard_id.to_pp_string() % tx_dboard_id.to_pp_string())); +    } +      //extract dboard constructor and settings (force to unknown for messed up xcvr status)      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(dboard_iface::UNIT_RX, rx_dboard_id, rx_dboard_is_xcvr != this_dboard_is_xcvr); +    boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_dboard_args( +        dboard_iface::UNIT_RX, rx_dboard_id, rx_dboard_is_xcvr != this_dboard_is_xcvr +    );      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(dboard_iface::UNIT_TX, tx_dboard_id, tx_dboard_is_xcvr != this_dboard_is_xcvr); +    boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_dboard_args( +        dboard_iface::UNIT_TX, tx_dboard_id, tx_dboard_is_xcvr != this_dboard_is_xcvr +    );      //initialize the gpio pins before creating subdevs      set_nice_dboard_if(); @@ -317,4 +330,14 @@ void dboard_manager_impl::set_nice_dboard_if(void){          _iface->set_pin_ctrl(unit, 0x0000); //all gpio          _iface->set_clock_enabled(unit, false); //clock off      } + +    //disable all rx subdevices +    BOOST_FOREACH(const std::string &sd_name, this->get_rx_subdev_names()){ +        this->get_rx_subdev(sd_name)[SUBDEV_PROP_ENABLED] = false; +    } + +    //disable all tx subdevices +    BOOST_FOREACH(const std::string &sd_name, this->get_tx_subdev_names()){ +        this->get_tx_subdev(sd_name)[SUBDEV_PROP_ENABLED] = false; +    }  } diff --git a/host/lib/usrp/mimo_usrp.cpp b/host/lib/usrp/mimo_usrp.cpp deleted file mode 100644 index e78d38fc0..000000000 --- a/host/lib/usrp/mimo_usrp.cpp +++ /dev/null @@ -1,347 +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/usrp/mimo_usrp.hpp> -#include <uhd/usrp/tune_helper.hpp> -#include <uhd/utils/assert.hpp> -#include <uhd/utils/gain_group.hpp> -#include <uhd/utils/algorithm.hpp> -#include <uhd/utils/warning.hpp> -#include <uhd/usrp/subdev_props.hpp> -#include <uhd/usrp/mboard_props.hpp> -#include <uhd/usrp/device_props.hpp> -#include <uhd/usrp/dboard_props.hpp> -#include <uhd/usrp/dsp_props.hpp> -#include <boost/foreach.hpp> -#include <boost/format.hpp> -#include <boost/thread.hpp> -#include <stdexcept> -#include <iostream> - -using namespace uhd; -using namespace uhd::usrp; - -static inline freq_range_t add_dsp_shift(const freq_range_t &range, wax::obj dsp){ -    double codec_rate = dsp[DSP_PROP_CODEC_RATE].as<double>(); -    return freq_range_t(range.min - codec_rate/2.0, range.max + codec_rate/2.0); -} - -/*********************************************************************** - * MIMO USRP Implementation - **********************************************************************/ -class mimo_usrp_impl : public mimo_usrp{ -public: -    mimo_usrp_impl(const device_addr_t &addr){ -        _dev = device::make(addr); - -        //set the clock config across all mboards (TODO set through api) -        clock_config_t clock_config; -        clock_config.ref_source = clock_config_t::REF_SMA; -        clock_config.pps_source = clock_config_t::PPS_SMA; -        for (size_t chan = 0; chan < get_num_channels(); chan++){ -            _mboard(chan)[MBOARD_PROP_CLOCK_CONFIG] = clock_config; -        } -    } - -    ~mimo_usrp_impl(void){ -        /* NOP */ -    } - -    device::sptr get_device(void){ -        return _dev; -    } - -    std::string get_pp_string(void){ -        std::string buff = str(boost::format( -            "MIMO USRP:\n" -            "  Device: %s\n" -        ) -            % (*_dev)[DEVICE_PROP_NAME].as<std::string>() -        ); -        for (size_t chan = 0; chan < get_num_channels(); chan++){ -            buff += str(boost::format( -                "  Channel: %u\n" -                "    Mboard: %s\n" -                "    RX DSP: %s\n" -                "    RX Dboard: %s\n" -                "    RX Subdev: %s\n" -                "    TX DSP: %s\n" -                "    TX Dboard: %s\n" -                "    TX Subdev: %s\n" -            ) % chan -                % _mboard(chan)[MBOARD_PROP_NAME].as<std::string>() -                % _rx_dsp(chan)[DSP_PROP_NAME].as<std::string>() -                % _rx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() -                % _rx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() -                % _tx_dsp(chan)[DSP_PROP_NAME].as<std::string>() -                % _tx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() -                % _tx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() -            ); -        } -        return buff; -    } - -    size_t get_num_channels(void){ -        return (*_dev)[DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>().size(); -    } - -    /******************************************************************* -     * Misc -     ******************************************************************/ -    time_spec_t get_time_now(void){ -        //the time on the first mboard better be the same on all -        return _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); -    } - -    void set_time_next_pps(const time_spec_t &time_spec){ -        for (size_t chan = 0; chan < get_num_channels(); chan++){ -            _mboard(chan)[MBOARD_PROP_TIME_NEXT_PPS] = time_spec; -        } -    } - -    void set_time_unknown_pps(const time_spec_t &time_spec){ -        std::cout << "Set time with unknown pps edge:" << std::endl; -        std::cout << "    1) set times next pps (race condition)" << std::endl; -        set_time_next_pps(time_spec); -        boost::this_thread::sleep(boost::posix_time::seconds(1)); - -        std::cout << "    2) catch seconds rollover at pps edge" << std::endl; -        time_t last_secs = 0, curr_secs = 0; -        while(curr_secs == last_secs){ -            last_secs = curr_secs; -            curr_secs = get_time_now().get_full_secs(); -        } - -        std::cout << "    3) set times next pps (synchronously)" << std::endl; -        set_time_next_pps(time_spec); -        boost::this_thread::sleep(boost::posix_time::seconds(1)); - -        //verify that the time registers are read to be within a few RTT -        for (size_t chan = 1; chan < get_num_channels(); chan++){ -            time_spec_t time_0 = _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); -            time_spec_t time_i = _mboard(chan)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); -            if (time_i < time_0 or (time_i - time_0) > time_spec_t(0.01)){ //10 ms: greater than RTT but not too big -                uhd::print_warning(str(boost::format( -                    "Detected time deviation between board %d and board 0.\n" -                    "Board 0 time is %f seconds.\n" -                    "Board %d time is %f seconds.\n" -                ) % chan % time_0.get_real_secs() % chan % time_i.get_real_secs())); -            } -        } -    } - -    void issue_stream_cmd(const stream_cmd_t &stream_cmd){ -        for (size_t chan = 0; chan < get_num_channels(); chan++){ -            _mboard(chan)[MBOARD_PROP_STREAM_CMD] = stream_cmd; -        } -    } - -    /******************************************************************* -     * RX methods -     ******************************************************************/ -    void set_rx_subdev_spec(size_t chan, const subdev_spec_t &spec){ -        UHD_ASSERT_THROW(spec.size() <= 1); -        _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC] = spec; -    } - -    subdev_spec_t get_rx_subdev_spec(size_t chan){ -        return _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>(); -    } - -    void set_rx_rate_all(double rate){ -        std::vector<double> _actual_rates; -        for (size_t chan = 0; chan < get_num_channels(); chan++){ -            _rx_dsp(chan)[DSP_PROP_HOST_RATE] = rate; -            _actual_rates.push_back(_rx_dsp(chan)[DSP_PROP_HOST_RATE].as<double>()); -        } -        _rx_rate = _actual_rates.front(); -        if (std::count(_actual_rates, _rx_rate) != _actual_rates.size()) throw std::runtime_error( -            "MIMO configuratio error: rx rate inconsistent across mboards" -        ); -    } - -    double get_rx_rate_all(void){ -        return _rx_rate; -    } - -    tune_result_t set_rx_freq(size_t chan, double target_freq){ -        return tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan), target_freq); -    } - -    tune_result_t set_rx_freq(size_t chan, double target_freq, double lo_off){ -        return tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan), target_freq, lo_off); -    } - -    double get_rx_freq(size_t chan){ -        return derive_freq_from_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan)); -    } - -    freq_range_t get_rx_freq_range(size_t chan){ -        return add_dsp_shift(_rx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _rx_dsp(chan)); -    } - -    void set_rx_gain(size_t chan, float gain){ -        return _rx_gain_group(chan)->set_value(gain); -    } - -    float get_rx_gain(size_t chan){ -        return _rx_gain_group(chan)->get_value(); -    } - -    gain_range_t get_rx_gain_range(size_t chan){ -        return _rx_gain_group(chan)->get_range(); -    } - -    void set_rx_antenna(size_t chan, const std::string &ant){ -        _rx_subdev(chan)[SUBDEV_PROP_ANTENNA] = ant; -    } - -    std::string get_rx_antenna(size_t chan){ -        return _rx_subdev(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); -    } - -    std::vector<std::string> get_rx_antennas(size_t chan){ -        return _rx_subdev(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); -    } - -    bool get_rx_lo_locked(size_t chan){ -        return _rx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); -    } - -    float read_rssi(size_t chan){ -        return _rx_subdev(chan)[SUBDEV_PROP_RSSI].as<float>(); -    } - -    /******************************************************************* -     * TX methods -     ******************************************************************/ -    void set_tx_subdev_spec(size_t chan, const subdev_spec_t &spec){ -        UHD_ASSERT_THROW(spec.size() <= 1); -        _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC] = spec; -    } - -    subdev_spec_t get_tx_subdev_spec(size_t chan){ -        return _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>(); -    } - -    void set_tx_rate_all(double rate){ -        std::vector<double> _actual_rates; -        for (size_t chan = 0; chan < get_num_channels(); chan++){ -            _tx_dsp(chan)[DSP_PROP_HOST_RATE] = rate; -            _actual_rates.push_back(_tx_dsp(chan)[DSP_PROP_HOST_RATE].as<double>()); -        } -        _tx_rate = _actual_rates.front(); -        if (std::count(_actual_rates, _tx_rate) != _actual_rates.size()) throw std::runtime_error( -            "MIMO configuratio error: tx rate inconsistent across mboards" -        ); -    } - -    double get_tx_rate_all(void){ -        return _tx_rate; -    } - -    tune_result_t set_tx_freq(size_t chan, double target_freq){ -        return tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan), target_freq); -    } - -    tune_result_t set_tx_freq(size_t chan, double target_freq, double lo_off){ -        return tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan), target_freq, lo_off); -    } - -    double get_tx_freq(size_t chan){ -        return derive_freq_from_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan)); -    } - -    freq_range_t get_tx_freq_range(size_t chan){ -        return add_dsp_shift(_tx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _tx_dsp(chan)); -    } - -    void set_tx_gain(size_t chan, float gain){ -        return _tx_gain_group(chan)->set_value(gain); -    } - -    float get_tx_gain(size_t chan){ -        return _tx_gain_group(chan)->get_value(); -    } - -    gain_range_t get_tx_gain_range(size_t chan){ -        return _tx_gain_group(chan)->get_range(); -    } - -    void set_tx_antenna(size_t chan, const std::string &ant){ -        _tx_subdev(chan)[SUBDEV_PROP_ANTENNA] = ant; -    } - -    std::string get_tx_antenna(size_t chan){ -        return _tx_subdev(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); -    } - -    std::vector<std::string> get_tx_antennas(size_t chan){ -        return _tx_subdev(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); -    } - -    bool get_tx_lo_locked(size_t chan){ -        return _tx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); -    } - -private: -    device::sptr _dev; -    wax::obj _mboard(size_t chan){ -        prop_names_t names = (*_dev)[DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>(); -        return (*_dev)[named_prop_t(DEVICE_PROP_MBOARD, names.at(chan))]; -    } -    wax::obj _rx_dsp(size_t chan){ -        return _mboard(chan)[MBOARD_PROP_RX_DSP]; -    } -    wax::obj _tx_dsp(size_t chan){ -        return _mboard(chan)[MBOARD_PROP_TX_DSP]; -    } -    wax::obj _rx_dboard(size_t chan){ -        std::string db_name = _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().db_name; -        return _mboard(chan)[named_prop_t(MBOARD_PROP_RX_DBOARD, db_name)]; -    } -    wax::obj _tx_dboard(size_t chan){ -        std::string db_name = _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().db_name; -        return _mboard(chan)[named_prop_t(MBOARD_PROP_TX_DBOARD, db_name)]; -    } -    wax::obj _rx_subdev(size_t chan){ -        std::string sd_name = _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; -        return _rx_dboard(chan)[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; -    } -    wax::obj _tx_subdev(size_t chan){ -        std::string sd_name = _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; -        return _tx_dboard(chan)[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; -    } -    gain_group::sptr _rx_gain_group(size_t chan){ -        std::string sd_name = _mboard(chan)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; -        return _rx_dboard(chan)[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); -    } -    gain_group::sptr _tx_gain_group(size_t chan){ -        std::string sd_name = _mboard(chan)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; -        return _tx_dboard(chan)[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); -    } - -    //shadows -    double _rx_rate, _tx_rate; -}; - -/*********************************************************************** - * The Make Function - **********************************************************************/ -mimo_usrp::sptr mimo_usrp::make(const device_addr_t &dev_addr){ -    return sptr(new mimo_usrp_impl(dev_addr)); -} diff --git a/host/lib/usrp/misc_utils.cpp b/host/lib/usrp/misc_utils.cpp index 5cfcdc8d3..05308baba 100644 --- a/host/lib/usrp/misc_utils.cpp +++ b/host/lib/usrp/misc_utils.cpp @@ -17,6 +17,7 @@  #include <uhd/usrp/misc_utils.hpp>  #include <uhd/utils/assert.hpp> +#include <uhd/utils/algorithm.hpp>  #include <uhd/utils/gain_group.hpp>  #include <uhd/usrp/dboard_id.hpp>  #include <uhd/usrp/subdev_props.hpp> @@ -186,6 +187,22 @@ static void verify_xx_subdev_spec(              "Validate %s subdev spec failed: %s\n    %s"          ) % xx_type % subdev_spec.to_string() % e.what()));      } + +    //now use the subdev spec to enable the subdevices in use and vice-versa +    BOOST_FOREACH(const std::string &db_name, mboard[dboard_names_prop].as<prop_names_t>()){ +        wax::obj dboard = mboard[named_prop_t(dboard_prop, db_name)]; +        BOOST_FOREACH(const std::string &sd_name, dboard[DBOARD_PROP_SUBDEV_NAMES].as<prop_names_t>()){ +            try{ +                bool enable = std::has(subdev_spec, subdev_spec_pair_t(db_name, sd_name)); +                dboard[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)][SUBDEV_PROP_ENABLED] = enable; +            } +            catch(const std::exception &e){ +                throw std::runtime_error(str(boost::format( +                    "Cannot set enabled property on subdevice %s:%s\n    %s" +                ) % db_name % sd_name % e.what())); +            } +        } +    }  }  void usrp::verify_rx_subdev_spec(subdev_spec_t &subdev_spec, wax::obj mboard){ diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp new file mode 100644 index 000000000..027530f31 --- /dev/null +++ b/host/lib/usrp/multi_usrp.cpp @@ -0,0 +1,435 @@ +// +// 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 "wrapper_utils.hpp" +#include <uhd/usrp/multi_usrp.hpp> +#include <uhd/usrp/tune_helper.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/warning.hpp> +#include <uhd/utils/gain_group.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <uhd/usrp/mboard_props.hpp> +#include <uhd/usrp/device_props.hpp> +#include <uhd/usrp/dboard_props.hpp> +#include <uhd/usrp/dsp_props.hpp> +#include <boost/thread.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <stdexcept> +#include <iostream> + +using namespace uhd; +using namespace uhd::usrp; + +/*********************************************************************** + * Simple USRP Implementation + **********************************************************************/ +class multi_usrp_impl : public multi_usrp{ +public: +    multi_usrp_impl(const device_addr_t &addr){ +        _dev = device::make(addr); +    } + +    device::sptr get_device(void){ +        return _dev; +    } + +    /******************************************************************* +     * Mboard methods +     ******************************************************************/ +    std::string get_pp_string(void){ +        std::string buff = str(boost::format( +            "Multi USRP:\n" +            "  Device: %s\n" +        ) +            % (*_dev)[DEVICE_PROP_NAME].as<std::string>() +        ); +        for (size_t m = 0; m < get_num_mboards(); m++){ +            buff += str(boost::format( +                "  Mboard %d: %s\n" +            ) % m +                % _mboard(m)[MBOARD_PROP_NAME].as<std::string>() +            ); +        } + +        //----------- rx side of life ---------------------------------- +        for (size_t m = 0, chan = 0; m < get_num_mboards(); m++){ +            buff += str(boost::format( +                "  RX DSP %d: %s\n" +            ) % m +                % _rx_dsp(m)[DSP_PROP_NAME].as<std::string>() +            ); +            for (; chan < (m + 1)*get_rx_subdev_spec(m).size(); chan++){ +                buff += str(boost::format( +                    "  RX Channel: %u\n" +                    "    RX Dboard: %s\n" +                    "    RX Subdev: %s\n" +                ) % chan +                    % _rx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() +                    % _rx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() +                ); +            } +        } + +        //----------- tx side of life ---------------------------------- +        for (size_t m = 0, chan = 0; m < get_num_mboards(); m++){ +            buff += str(boost::format( +                "  TX DSP %d: %s\n" +            ) % m +                % _tx_dsp(m)[DSP_PROP_NAME].as<std::string>() +            ); +            for (; chan < (m + 1)*get_tx_subdev_spec(m).size(); chan++){ +                buff += str(boost::format( +                    "  TX Channel: %u\n" +                    "    TX Dboard: %s\n" +                    "    TX Subdev: %s\n" +                ) % chan +                    % _tx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() +                    % _tx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() +                ); +            } +        } + +        return buff; +    } + +    std::string get_mboard_name(size_t mboard){ +        return _mboard(mboard)[MBOARD_PROP_NAME].as<std::string>(); +    } + +    time_spec_t get_time_now(void){ +        return _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +    } + +    void set_time_next_pps(const time_spec_t &time_spec){ +        for (size_t m = 0; m < get_num_mboards(); m++){ +            _mboard(m)[MBOARD_PROP_TIME_NEXT_PPS] = time_spec; +        } +    } + +    void set_time_unknown_pps(const time_spec_t &time_spec){ +        std::cout << "Set time with unknown pps edge:" << std::endl; +        std::cout << "    1) set times next pps (race condition)" << std::endl; +        set_time_next_pps(time_spec); +        boost::this_thread::sleep(boost::posix_time::seconds(1)); + +        std::cout << "    2) catch seconds rollover at pps edge" << std::endl; +        time_t last_secs = 0, curr_secs = 0; +        while(curr_secs == last_secs){ +            last_secs = curr_secs; +            curr_secs = get_time_now().get_full_secs(); +        } + +        std::cout << "    3) set times next pps (synchronously)" << std::endl; +        set_time_next_pps(time_spec); +        boost::this_thread::sleep(boost::posix_time::seconds(1)); + +        //verify that the time registers are read to be within a few RTT +        for (size_t m = 1; m < get_num_mboards(); m++){ +            time_spec_t time_0 = _mboard(0)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +            time_spec_t time_i = _mboard(m)[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +            if (time_i < time_0 or (time_i - time_0) > time_spec_t(0.01)){ //10 ms: greater than RTT but not too big +                uhd::print_warning(str(boost::format( +                    "Detected time deviation between board %d and board 0.\n" +                    "Board 0 time is %f seconds.\n" +                    "Board %d time is %f seconds.\n" +                ) % m % time_0.get_real_secs() % m % time_i.get_real_secs())); +            } +        } +    } + +    void issue_stream_cmd(const stream_cmd_t &stream_cmd){ +        for (size_t m = 0; m < get_num_mboards(); m++){ +            _mboard(m)[MBOARD_PROP_STREAM_CMD] = stream_cmd; +        } +    } + +    void set_clock_config(const clock_config_t &clock_config, size_t mboard){ +        if (mboard != ALL_MBOARDS){ +            _mboard(mboard)[MBOARD_PROP_CLOCK_CONFIG] = clock_config; +            return; +        } +        for (size_t m = 0; m < get_num_mboards(); m++){ +            set_clock_config(clock_config, m); +        } +    } + +    size_t get_num_mboards(void){ +        return (*_dev)[DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>().size(); +    } + +    /******************************************************************* +     * RX methods +     ******************************************************************/ +    void set_rx_subdev_spec(const subdev_spec_t &spec, size_t mboard){ +        if (mboard != ALL_MBOARDS){ +            _mboard(mboard)[MBOARD_PROP_RX_SUBDEV_SPEC] = spec; +            return; +        } +        for (size_t m = 0; m < get_num_mboards(); m++){ +            set_rx_subdev_spec(spec, m); +        } +    } + +    subdev_spec_t get_rx_subdev_spec(size_t mboard){ +        return _mboard(mboard)[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>(); +    } + +    size_t get_rx_num_channels(void){ +        return rx_cpm()*get_num_mboards(); //total num channels +    } + +    std::string get_rx_subdev_name(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>(); +    } + +    void set_rx_rate(double rate){ +        for (size_t m = 0; m < get_num_mboards(); m++){ +            _rx_dsp(m)[DSP_PROP_HOST_RATE] = rate; +        } +        do_samp_rate_warning_message(rate, get_rx_rate(), "RX"); +    } + +    double get_rx_rate(void){ +        return _rx_dsp(0)[DSP_PROP_HOST_RATE].as<double>(); +    } + +    tune_result_t set_rx_freq(double target_freq, size_t chan){ +        tune_result_t r = tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan/rx_cpm()), chan%rx_cpm(), target_freq); +        do_tune_freq_warning_message(target_freq, get_rx_freq(chan), "RX"); +        return r; +    } + +    tune_result_t set_rx_freq(double target_freq, double lo_off, size_t chan){ +        tune_result_t r = tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan/rx_cpm()), chan%rx_cpm(), target_freq, lo_off); +        do_tune_freq_warning_message(target_freq, get_rx_freq(chan), "RX"); +        return r; +    } + +    double get_rx_freq(size_t chan){ +        return derive_freq_from_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan/rx_cpm()), chan%rx_cpm()); +    } + +    freq_range_t get_rx_freq_range(size_t chan){ +        return add_dsp_shift(_rx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _rx_dsp(chan/rx_cpm())); +    } + +    void set_rx_gain(float gain, size_t chan){ +        return _rx_gain_group(chan)->set_value(gain); +    } + +    float get_rx_gain(size_t chan){ +        return _rx_gain_group(chan)->get_value(); +    } + +    gain_range_t get_rx_gain_range(size_t chan){ +        return _rx_gain_group(chan)->get_range(); +    } + +    void set_rx_antenna(const std::string &ant, size_t chan){ +        _rx_subdev(chan)[SUBDEV_PROP_ANTENNA] = ant; +    } + +    std::string get_rx_antenna(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); +    } + +    std::vector<std::string> get_rx_antennas(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); +    } + +    bool get_rx_lo_locked(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); +    } + +    void set_rx_bandwidth(double bandwidth, size_t chan){ +        _rx_subdev(chan)[SUBDEV_PROP_BANDWIDTH] = bandwidth; +    } + +    double get_rx_bandwidth(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_BANDWIDTH].as<double>(); +    } + +    float read_rssi(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_RSSI].as<float>(); +    } + +    dboard_iface::sptr get_rx_dboard_iface(size_t chan){ +        return _rx_dboard(chan)[DBOARD_PROP_DBOARD_IFACE].as<dboard_iface::sptr>(); +    } + +    /******************************************************************* +     * TX methods +     ******************************************************************/ +    void set_tx_subdev_spec(const subdev_spec_t &spec, size_t mboard){ +        if (mboard != ALL_MBOARDS){ +            _mboard(mboard)[MBOARD_PROP_TX_SUBDEV_SPEC] = spec; +            return; +        } +        for (size_t m = 0; m < get_num_mboards(); m++){ +            set_tx_subdev_spec(spec, m); +        } +    } + +    subdev_spec_t get_tx_subdev_spec(size_t mboard){ +        return _mboard(mboard)[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>(); +    } + +    std::string get_tx_subdev_name(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>(); +    } + +    size_t get_tx_num_channels(void){ +        return tx_cpm()*get_num_mboards(); //total num channels +    } + +    void set_tx_rate(double rate){ +        for (size_t m = 0; m < get_num_mboards(); m++){ +            _tx_dsp(m)[DSP_PROP_HOST_RATE] = rate; +        } +        do_samp_rate_warning_message(rate, get_tx_rate(), "TX"); +    } + +    double get_tx_rate(void){ +        return _tx_dsp(0)[DSP_PROP_HOST_RATE].as<double>(); +    } + +    tune_result_t set_tx_freq(double target_freq, size_t chan){ +        tune_result_t r = tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan/tx_cpm()), chan%tx_cpm(), target_freq); +        do_tune_freq_warning_message(target_freq, get_tx_freq(chan), "TX"); +        return r; +    } + +    tune_result_t set_tx_freq(double target_freq, double lo_off, size_t chan){ +        tune_result_t r = tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan/tx_cpm()), chan%tx_cpm(), target_freq, lo_off); +        do_tune_freq_warning_message(target_freq, get_tx_freq(chan), "TX"); +        return r; +    } + +    double get_tx_freq(size_t chan){ +        return derive_freq_from_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan/tx_cpm()), chan%tx_cpm()); +    } + +    freq_range_t get_tx_freq_range(size_t chan){ +        return add_dsp_shift(_tx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _tx_dsp(chan/tx_cpm())); +    } + +    void set_tx_gain(float gain, size_t chan){ +        return _tx_gain_group(chan)->set_value(gain); +    } + +    float get_tx_gain(size_t chan){ +        return _tx_gain_group(chan)->get_value(); +    } + +    gain_range_t get_tx_gain_range(size_t chan){ +        return _tx_gain_group(chan)->get_range(); +    } + +    void set_tx_antenna(const std::string &ant, size_t chan){ +        _tx_subdev(chan)[SUBDEV_PROP_ANTENNA] = ant; +    } + +    std::string get_tx_antenna(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); +    } + +    std::vector<std::string> get_tx_antennas(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); +    } + +    bool get_tx_lo_locked(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); +    } + +    void set_tx_bandwidth(double bandwidth, size_t chan){ +        _tx_subdev(chan)[SUBDEV_PROP_BANDWIDTH] = bandwidth; +    } + +    double get_tx_bandwidth(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_BANDWIDTH].as<double>(); +    } + +    dboard_iface::sptr get_tx_dboard_iface(size_t chan){ +        return _tx_dboard(chan)[DBOARD_PROP_DBOARD_IFACE].as<dboard_iface::sptr>(); +    } + +private: +    device::sptr _dev; + +    size_t rx_cpm(void){ //channels per mboard +        size_t nchan = get_rx_subdev_spec(0).size(); +        for (size_t m = 1; m < get_num_mboards(); m++){ +            if (nchan != get_rx_subdev_spec(m).size()){ +                throw std::runtime_error("rx subdev spec size inconsistent across all mboards"); +            } +        } +        return nchan; +    } + +    size_t tx_cpm(void){ //channels per mboard +        size_t nchan = get_tx_subdev_spec(0).size(); +        for (size_t m = 1; m < get_num_mboards(); m++){ +            if (nchan != get_tx_subdev_spec(m).size()){ +                throw std::runtime_error("tx subdev spec size inconsistent across all mboards"); +            } +        } +        return nchan; +    } + +    wax::obj _mboard(size_t mboard){ +        std::string mb_name = (*_dev)[DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>().at(mboard); +        return (*_dev)[named_prop_t(DEVICE_PROP_MBOARD, mb_name)]; +    } +    wax::obj _rx_dsp(size_t mboard){ +        return _mboard(mboard)[MBOARD_PROP_RX_DSP]; +    } +    wax::obj _tx_dsp(size_t mboard){ +        return _mboard(mboard)[MBOARD_PROP_TX_DSP]; +    } +    wax::obj _rx_dboard(size_t chan){ +        std::string db_name = get_rx_subdev_spec(chan/rx_cpm()).at(chan%rx_cpm()).db_name; +        return _mboard(chan/rx_cpm())[named_prop_t(MBOARD_PROP_RX_DBOARD, db_name)]; +    } +    wax::obj _tx_dboard(size_t chan){ +        std::string db_name = get_tx_subdev_spec(chan/tx_cpm()).at(chan%tx_cpm()).db_name; +        return _mboard(chan/tx_cpm())[named_prop_t(MBOARD_PROP_TX_DBOARD, db_name)]; +    } +    wax::obj _rx_subdev(size_t chan){ +        std::string sd_name = get_rx_subdev_spec(chan/rx_cpm()).at(chan%rx_cpm()).sd_name; +        return _rx_dboard(chan)[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; +    } +    wax::obj _tx_subdev(size_t chan){ +        std::string sd_name = get_tx_subdev_spec(chan/tx_cpm()).at(chan%tx_cpm()).sd_name; +        return _tx_dboard(chan)[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; +    } +    gain_group::sptr _rx_gain_group(size_t chan){ +        std::string sd_name = get_rx_subdev_spec(chan/rx_cpm()).at(chan%rx_cpm()).sd_name; +        return _rx_dboard(chan)[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); +    } +    gain_group::sptr _tx_gain_group(size_t chan){ +        std::string sd_name = get_tx_subdev_spec(chan/tx_cpm()).at(chan%tx_cpm()).sd_name; +        return _tx_dboard(chan)[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); +    } +}; + +/*********************************************************************** + * The Make Function + **********************************************************************/ +multi_usrp::sptr multi_usrp::make(const device_addr_t &dev_addr){ +    return sptr(new multi_usrp_impl(dev_addr)); +} diff --git a/host/lib/usrp/simple_usrp.cpp b/host/lib/usrp/simple_usrp.cpp deleted file mode 100644 index e573d0fc0..000000000 --- a/host/lib/usrp/simple_usrp.cpp +++ /dev/null @@ -1,285 +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/usrp/simple_usrp.hpp> -#include <uhd/usrp/tune_helper.hpp> -#include <uhd/utils/assert.hpp> -#include <uhd/utils/gain_group.hpp> -#include <uhd/usrp/subdev_props.hpp> -#include <uhd/usrp/mboard_props.hpp> -#include <uhd/usrp/device_props.hpp> -#include <uhd/usrp/dboard_props.hpp> -#include <uhd/usrp/dsp_props.hpp> -#include <boost/foreach.hpp> -#include <boost/format.hpp> -#include <stdexcept> -#include <iostream> - -using namespace uhd; -using namespace uhd::usrp; - -static inline freq_range_t add_dsp_shift(const freq_range_t &range, wax::obj dsp){ -    double codec_rate = dsp[DSP_PROP_CODEC_RATE].as<double>(); -    return freq_range_t(range.min - codec_rate/2.0, range.max + codec_rate/2.0); -} - -/*********************************************************************** - * Simple USRP Implementation - **********************************************************************/ -class simple_usrp_impl : public simple_usrp{ -public: -    simple_usrp_impl(const device_addr_t &addr){ -        _dev = device::make(addr); -    } - -    ~simple_usrp_impl(void){ -        /* NOP */ -    } - -    device::sptr get_device(void){ -        return _dev; -    } - -    std::string get_pp_string(void){ -        return str(boost::format( -            "Simple USRP:\n" -            "  Device: %s\n" -            "  Mboard: %s\n" -            "  RX DSP: %s\n" -            "  RX Dboard: %s\n" -            "  RX Subdev: %s\n" -            "  TX DSP: %s\n" -            "  TX Dboard: %s\n" -            "  TX Subdev: %s\n" -        ) -            % (*_dev)[DEVICE_PROP_NAME].as<std::string>() -            % _mboard()[MBOARD_PROP_NAME].as<std::string>() -            % _rx_dsp()[DSP_PROP_NAME].as<std::string>() -            % _rx_dboard()[DBOARD_PROP_NAME].as<std::string>() -            % _rx_subdev()[SUBDEV_PROP_NAME].as<std::string>() -            % _tx_dsp()[DSP_PROP_NAME].as<std::string>() -            % _tx_dboard()[DBOARD_PROP_NAME].as<std::string>() -            % _tx_subdev()[SUBDEV_PROP_NAME].as<std::string>() -        ); -    } - -    /******************************************************************* -     * Misc -     ******************************************************************/ -    time_spec_t get_time_now(void){ -        return _mboard()[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); -    } - -    void set_time_now(const time_spec_t &time_spec){ -        _mboard()[MBOARD_PROP_TIME_NOW] = time_spec; -    } - -    void set_time_next_pps(const time_spec_t &time_spec){ -        _mboard()[MBOARD_PROP_TIME_NEXT_PPS] = time_spec; -    } - -    void issue_stream_cmd(const stream_cmd_t &stream_cmd){ -        _mboard()[MBOARD_PROP_STREAM_CMD] = stream_cmd; -    } - -    void set_clock_config(const clock_config_t &clock_config){ -        _mboard()[MBOARD_PROP_CLOCK_CONFIG] = clock_config; -    } - -    /******************************************************************* -     * RX methods -     ******************************************************************/ -    void set_rx_subdev_spec(const subdev_spec_t &spec){ -        _mboard()[MBOARD_PROP_RX_SUBDEV_SPEC] = spec; -        std::cout << "RX " << _mboard()[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().to_pp_string() << std::endl; -    } - -    subdev_spec_t get_rx_subdev_spec(void){ -        return _mboard()[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>(); -    } - -    void set_rx_rate(double rate){ -        _rx_dsp()[DSP_PROP_HOST_RATE] = rate; -    } - -    double get_rx_rate(void){ -        return _rx_dsp()[DSP_PROP_HOST_RATE].as<double>(); -    } - -    tune_result_t set_rx_freq(double target_freq){ -        return tune_rx_subdev_and_dsp(_rx_subdev(), _rx_dsp(), target_freq); -    } - -    tune_result_t set_rx_freq(double target_freq, double lo_off){ -        return tune_rx_subdev_and_dsp(_rx_subdev(), _rx_dsp(), target_freq, lo_off); -    } - -    double get_rx_freq(void){ -        return derive_freq_from_rx_subdev_and_dsp(_rx_subdev(), _rx_dsp()); -    } - -    freq_range_t get_rx_freq_range(void){ -        return add_dsp_shift(_rx_subdev()[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _rx_dsp()); -    } - -    void set_rx_gain(float gain){ -        return _rx_gain_group()->set_value(gain); -    } - -    float get_rx_gain(void){ -        return _rx_gain_group()->get_value(); -    } - -    gain_range_t get_rx_gain_range(void){ -        return _rx_gain_group()->get_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<prop_names_t>(); -    } - -    bool get_rx_lo_locked(void){ -        return _rx_subdev()[SUBDEV_PROP_LO_LOCKED].as<bool>(); -    } - -    float read_rssi(void){ -        return _rx_subdev()[SUBDEV_PROP_RSSI].as<float>(); -    } - -    dboard_iface::sptr get_rx_dboard_iface(void){ -        return _rx_dboard()[DBOARD_PROP_DBOARD_IFACE].as<dboard_iface::sptr>(); -    } - -    /******************************************************************* -     * TX methods -     ******************************************************************/ -    void set_tx_subdev_spec(const subdev_spec_t &spec){ -        _mboard()[MBOARD_PROP_TX_SUBDEV_SPEC] = spec; -        std::cout << "TX " << _mboard()[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().to_pp_string() << std::endl; -    } - -    subdev_spec_t get_tx_subdev_spec(void){ -        return _mboard()[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>(); -    } - -    void set_tx_rate(double rate){ -        _tx_dsp()[DSP_PROP_HOST_RATE] = rate; -    } - -    double get_tx_rate(void){ -        return _tx_dsp()[DSP_PROP_HOST_RATE].as<double>(); -    } - -    tune_result_t set_tx_freq(double target_freq){ -        return tune_tx_subdev_and_dsp(_tx_subdev(), _tx_dsp(), target_freq); -    } - -    tune_result_t set_tx_freq(double target_freq, double lo_off){ -        return tune_tx_subdev_and_dsp(_tx_subdev(), _tx_dsp(), target_freq, lo_off); -    } - -    double get_tx_freq(void){ -        return derive_freq_from_tx_subdev_and_dsp(_tx_subdev(), _tx_dsp()); -    } - -    freq_range_t get_tx_freq_range(void){ -        return add_dsp_shift(_tx_subdev()[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _tx_dsp()); -    } - -    void set_tx_gain(float gain){ -        return _tx_gain_group()->set_value(gain); -    } - -    float get_tx_gain(void){ -        return _tx_gain_group()->get_value(); -    } - -    gain_range_t get_tx_gain_range(void){ -        return _tx_gain_group()->get_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<prop_names_t>(); -    } - -    bool get_tx_lo_locked(void){ -        return _tx_subdev()[SUBDEV_PROP_LO_LOCKED].as<bool>(); -    } - -    dboard_iface::sptr get_tx_dboard_iface(void){ -        return _tx_dboard()[DBOARD_PROP_DBOARD_IFACE].as<dboard_iface::sptr>(); -    } - -private: -    device::sptr _dev; -    wax::obj _mboard(void){ -        return (*_dev)[DEVICE_PROP_MBOARD]; -    } -    wax::obj _rx_dsp(void){ -        return _mboard()[MBOARD_PROP_RX_DSP]; -    } -    wax::obj _tx_dsp(void){ -        return _mboard()[MBOARD_PROP_TX_DSP]; -    } -    wax::obj _rx_dboard(void){ -        std::string db_name = _mboard()[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().db_name; -        return _mboard()[named_prop_t(MBOARD_PROP_RX_DBOARD, db_name)]; -    } -    wax::obj _tx_dboard(void){ -        std::string db_name = _mboard()[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().db_name; -        return _mboard()[named_prop_t(MBOARD_PROP_TX_DBOARD, db_name)]; -    } -    wax::obj _rx_subdev(void){ -        std::string sd_name = _mboard()[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; -        return _rx_dboard()[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; -    } -    wax::obj _tx_subdev(void){ -        std::string sd_name = _mboard()[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; -        return _tx_dboard()[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; -    } -    gain_group::sptr _rx_gain_group(void){ -        std::string sd_name = _mboard()[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; -        return _rx_dboard()[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); -    } -    gain_group::sptr _tx_gain_group(void){ -        std::string sd_name = _mboard()[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>().front().sd_name; -        return _tx_dboard()[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); -    } -}; - -/*********************************************************************** - * The Make Function - **********************************************************************/ -simple_usrp::sptr simple_usrp::make(const device_addr_t &dev_addr){ -    return sptr(new simple_usrp_impl(dev_addr)); -} diff --git a/host/lib/usrp/single_usrp.cpp b/host/lib/usrp/single_usrp.cpp new file mode 100644 index 000000000..2faa1280c --- /dev/null +++ b/host/lib/usrp/single_usrp.cpp @@ -0,0 +1,337 @@ +// +// 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 "wrapper_utils.hpp" +#include <uhd/usrp/single_usrp.hpp> +#include <uhd/usrp/tune_helper.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/gain_group.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <uhd/usrp/mboard_props.hpp> +#include <uhd/usrp/device_props.hpp> +#include <uhd/usrp/dboard_props.hpp> +#include <uhd/usrp/dsp_props.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <stdexcept> +#include <iostream> + +using namespace uhd; +using namespace uhd::usrp; + +/*********************************************************************** + * Simple USRP Implementation + **********************************************************************/ +class single_usrp_impl : public single_usrp{ +public: +    single_usrp_impl(const device_addr_t &addr){ +        _dev = device::make(addr); +    } + +    device::sptr get_device(void){ +        return _dev; +    } + +    /******************************************************************* +     * Mboard methods +     ******************************************************************/ +    std::string get_pp_string(void){ +        std::string buff = str(boost::format( +            "Single USRP:\n" +            "  Device: %s\n" +            "  Mboard: %s\n" +        ) +            % (*_dev)[DEVICE_PROP_NAME].as<std::string>() +            % _mboard()[MBOARD_PROP_NAME].as<std::string>() +        ); + +        //----------- rx side of life ---------------------------------- +        buff += str(boost::format( +            "  RX DSP: %s\n" +        ) +            % _rx_dsp()[DSP_PROP_NAME].as<std::string>() +        ); +        for (size_t chan = 0; chan < this->get_rx_subdev_spec().size(); chan++){ +            buff += str(boost::format( +                "  RX Channel: %u\n" +                "    RX Dboard: %s\n" +                "    RX Subdev: %s\n" +            ) % chan +                % _rx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() +                % _rx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() +            ); +        } + +        //----------- tx side of life ---------------------------------- +        buff += str(boost::format( +            "  TX DSP: %s\n" +        ) +            % _tx_dsp()[DSP_PROP_NAME].as<std::string>() +        ); +        for (size_t chan = 0; chan < this->get_tx_subdev_spec().size(); chan++){ +            buff += str(boost::format( +                "  TX Channel: %u\n" +                "    TX Dboard: %s\n" +                "    TX Subdev: %s\n" +            ) % chan +                % _tx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() +                % _tx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() +            ); +        } + +        return buff; +    } + +    std::string get_mboard_name(void){ +        return _mboard()[MBOARD_PROP_NAME].as<std::string>(); +    } + +    time_spec_t get_time_now(void){ +        return _mboard()[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +    } + +    void set_time_now(const time_spec_t &time_spec){ +        _mboard()[MBOARD_PROP_TIME_NOW] = time_spec; +    } + +    void set_time_next_pps(const time_spec_t &time_spec){ +        _mboard()[MBOARD_PROP_TIME_NEXT_PPS] = time_spec; +    } + +    void issue_stream_cmd(const stream_cmd_t &stream_cmd){ +        _mboard()[MBOARD_PROP_STREAM_CMD] = stream_cmd; +    } + +    void set_clock_config(const clock_config_t &clock_config){ +        _mboard()[MBOARD_PROP_CLOCK_CONFIG] = clock_config; +    } + +    /******************************************************************* +     * RX methods +     ******************************************************************/ +    void set_rx_subdev_spec(const subdev_spec_t &spec){ +        _mboard()[MBOARD_PROP_RX_SUBDEV_SPEC] = spec; +    } + +    subdev_spec_t get_rx_subdev_spec(void){ +        return _mboard()[MBOARD_PROP_RX_SUBDEV_SPEC].as<subdev_spec_t>(); +    } + +    std::string get_rx_subdev_name(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>(); +    } + +    void set_rx_rate(double rate){ +        _rx_dsp()[DSP_PROP_HOST_RATE] = rate; +        do_samp_rate_warning_message(rate, get_rx_rate(), "RX"); +    } + +    double get_rx_rate(void){ +        return _rx_dsp()[DSP_PROP_HOST_RATE].as<double>(); +    } + +    tune_result_t set_rx_freq(double target_freq, size_t chan){ +        tune_result_t r = tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(), chan, target_freq); +        do_tune_freq_warning_message(target_freq, get_rx_freq(chan), "RX"); +        return r; +    } + +    tune_result_t set_rx_freq(double target_freq, double lo_off, size_t chan){ +        tune_result_t r = tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(), chan, target_freq, lo_off); +        do_tune_freq_warning_message(target_freq, get_rx_freq(chan), "RX"); +        return r; +    } + +    double get_rx_freq(size_t chan){ +        return derive_freq_from_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(), chan); +    } + +    freq_range_t get_rx_freq_range(size_t chan){ +        return add_dsp_shift(_rx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _rx_dsp()); +    } + +    void set_rx_gain(float gain, size_t chan){ +        return _rx_gain_group(chan)->set_value(gain); +    } + +    float get_rx_gain(size_t chan){ +        return _rx_gain_group(chan)->get_value(); +    } + +    gain_range_t get_rx_gain_range(size_t chan){ +        return _rx_gain_group(chan)->get_range(); +    } + +    void set_rx_antenna(const std::string &ant, size_t chan){ +        _rx_subdev(chan)[SUBDEV_PROP_ANTENNA] = ant; +    } + +    std::string get_rx_antenna(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); +    } + +    std::vector<std::string> get_rx_antennas(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); +    } + +    bool get_rx_lo_locked(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); +    } + +    void set_rx_bandwidth(double bandwidth, size_t chan){ +        _rx_subdev(chan)[SUBDEV_PROP_BANDWIDTH] = bandwidth; +    } + +    double get_rx_bandwidth(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_BANDWIDTH].as<double>(); +    } + +    float read_rssi(size_t chan){ +        return _rx_subdev(chan)[SUBDEV_PROP_RSSI].as<float>(); +    } + +    dboard_iface::sptr get_rx_dboard_iface(size_t chan){ +        return _rx_dboard(chan)[DBOARD_PROP_DBOARD_IFACE].as<dboard_iface::sptr>(); +    } + +    /******************************************************************* +     * TX methods +     ******************************************************************/ +    void set_tx_subdev_spec(const subdev_spec_t &spec){ +        _mboard()[MBOARD_PROP_TX_SUBDEV_SPEC] = spec; +    } + +    subdev_spec_t get_tx_subdev_spec(void){ +        return _mboard()[MBOARD_PROP_TX_SUBDEV_SPEC].as<subdev_spec_t>(); +    } + +    std::string get_tx_subdev_name(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>(); +    } + +    void set_tx_rate(double rate){ +        _tx_dsp()[DSP_PROP_HOST_RATE] = rate; +        do_samp_rate_warning_message(rate, get_tx_rate(), "TX"); +    } + +    double get_tx_rate(void){ +        return _tx_dsp()[DSP_PROP_HOST_RATE].as<double>(); +    } + +    tune_result_t set_tx_freq(double target_freq, size_t chan){ +        tune_result_t r = tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(), chan, target_freq); +        do_tune_freq_warning_message(target_freq, get_tx_freq(chan), "TX"); +        return r; +    } + +    tune_result_t set_tx_freq(double target_freq, double lo_off, size_t chan){ +        tune_result_t r = tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(), chan, target_freq, lo_off); +        do_tune_freq_warning_message(target_freq, get_tx_freq(chan), "TX"); +        return r; +    } + +    double get_tx_freq(size_t chan){ +        return derive_freq_from_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(), chan); +    } + +    freq_range_t get_tx_freq_range(size_t chan){ +        return add_dsp_shift(_tx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _tx_dsp()); +    } + +    void set_tx_gain(float gain, size_t chan){ +        return _tx_gain_group(chan)->set_value(gain); +    } + +    float get_tx_gain(size_t chan){ +        return _tx_gain_group(chan)->get_value(); +    } + +    gain_range_t get_tx_gain_range(size_t chan){ +        return _tx_gain_group(chan)->get_range(); +    } + +    void set_tx_antenna(const std::string &ant, size_t chan){ +        _tx_subdev(chan)[SUBDEV_PROP_ANTENNA] = ant; +    } + +    std::string get_tx_antenna(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); +    } + +    std::vector<std::string> get_tx_antennas(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); +    } + +    bool get_tx_lo_locked(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); +    } + +    void set_tx_bandwidth(double bandwidth, size_t chan){ +        _tx_subdev(chan)[SUBDEV_PROP_BANDWIDTH] = bandwidth; +    } + +    double get_tx_bandwidth(size_t chan){ +        return _tx_subdev(chan)[SUBDEV_PROP_BANDWIDTH].as<double>(); +    } + +    dboard_iface::sptr get_tx_dboard_iface(size_t chan){ +        return _tx_dboard(chan)[DBOARD_PROP_DBOARD_IFACE].as<dboard_iface::sptr>(); +    } + +private: +    device::sptr _dev; +    wax::obj _mboard(void){ +        return (*_dev)[DEVICE_PROP_MBOARD]; +    } +    wax::obj _rx_dsp(void){ +        return _mboard()[MBOARD_PROP_RX_DSP]; +    } +    wax::obj _tx_dsp(void){ +        return _mboard()[MBOARD_PROP_TX_DSP]; +    } +    wax::obj _rx_dboard(size_t chan){ +        std::string db_name = this->get_rx_subdev_spec().at(chan).db_name; +        return _mboard()[named_prop_t(MBOARD_PROP_RX_DBOARD, db_name)]; +    } +    wax::obj _tx_dboard(size_t chan){ +        std::string db_name = this->get_tx_subdev_spec().at(chan).db_name; +        return _mboard()[named_prop_t(MBOARD_PROP_TX_DBOARD, db_name)]; +    } +    wax::obj _rx_subdev(size_t chan){ +        std::string sd_name = this->get_rx_subdev_spec().at(chan).sd_name; +        return _rx_dboard(chan)[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; +    } +    wax::obj _tx_subdev(size_t chan){ +        std::string sd_name = this->get_tx_subdev_spec().at(chan).sd_name; +        return _tx_dboard(chan)[named_prop_t(DBOARD_PROP_SUBDEV, sd_name)]; +    } +    gain_group::sptr _rx_gain_group(size_t chan){ +        std::string sd_name = this->get_rx_subdev_spec().at(chan).sd_name; +        return _rx_dboard(chan)[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); +    } +    gain_group::sptr _tx_gain_group(size_t chan){ +        std::string sd_name = this->get_tx_subdev_spec().at(chan).sd_name; +        return _tx_dboard(chan)[named_prop_t(DBOARD_PROP_GAIN_GROUP, sd_name)].as<gain_group::sptr>(); +    } +}; + +/*********************************************************************** + * The Make Function + **********************************************************************/ +single_usrp::sptr single_usrp::make(const device_addr_t &dev_addr){ +    return sptr(new single_usrp_impl(dev_addr)); +} diff --git a/host/lib/usrp/subdev_spec.cpp b/host/lib/usrp/subdev_spec.cpp index 7a3e72867..95d2cbb12 100644 --- a/host/lib/usrp/subdev_spec.cpp +++ b/host/lib/usrp/subdev_spec.cpp @@ -34,6 +34,10 @@ subdev_spec_pair_t::subdev_spec_pair_t(      /* NOP */  } +bool usrp::operator==(const subdev_spec_pair_t &lhs, const subdev_spec_pair_t &rhs){ +    return (lhs.db_name == rhs.db_name) and (lhs.sd_name == rhs.sd_name); +} +  subdev_spec_t::subdev_spec_t(const std::string &markup){      BOOST_FOREACH(const std::string &pair, std::split_string(markup)){          if (pair == "") continue; diff --git a/host/lib/usrp/tune_helper.cpp b/host/lib/usrp/tune_helper.cpp index e516477d3..7633c67f2 100644 --- a/host/lib/usrp/tune_helper.cpp +++ b/host/lib/usrp/tune_helper.cpp @@ -30,11 +30,12 @@ using namespace uhd::usrp;   **********************************************************************/  static tune_result_t tune_xx_subdev_and_dxc(      dboard_iface::unit_t unit, -    wax::obj subdev, wax::obj dxc, +    wax::obj subdev, wax::obj dxc, size_t chan,      double target_freq, double lo_offset  ){      wax::obj subdev_freq_proxy = subdev[SUBDEV_PROP_FREQ]; -    wax::obj dxc_freq_proxy = dxc[DSP_PROP_FREQ_SHIFT]; +    std::string freq_name = dxc[DSP_PROP_FREQ_SHIFT_NAMES].as<prop_names_t>().at(chan); +    wax::obj dxc_freq_proxy = dxc[named_prop_t(DSP_PROP_FREQ_SHIFT, freq_name)];      double dxc_sample_rate = dxc[DSP_PROP_CODEC_RATE].as<double>();      // Ask the d'board to tune as closely as it can to target_freq+lo_offset @@ -65,11 +66,12 @@ static tune_result_t tune_xx_subdev_and_dxc(  static double derive_freq_from_xx_subdev_and_dxc(      dboard_iface::unit_t unit, -    wax::obj subdev, wax::obj dxc +    wax::obj subdev, wax::obj dxc, size_t chan  ){      //extract actual dsp and IF frequencies      double actual_inter_freq = subdev[SUBDEV_PROP_FREQ].as<double>(); -    double actual_dxc_freq = dxc[DSP_PROP_FREQ_SHIFT].as<double>(); +    std::string freq_name = dxc[DSP_PROP_FREQ_SHIFT_NAMES].as<prop_names_t>().at(chan); +    double actual_dxc_freq = dxc[named_prop_t(DSP_PROP_FREQ_SHIFT, freq_name)].as<double>();      //invert the sign on the dxc freq given the following conditions      if (unit == dboard_iface::UNIT_TX) actual_dxc_freq *= -1.0; @@ -81,50 +83,54 @@ static double derive_freq_from_xx_subdev_and_dxc(   * RX Tune   **********************************************************************/  tune_result_t usrp::tune_rx_subdev_and_dsp( -    wax::obj subdev, wax::obj ddc, +    wax::obj subdev, wax::obj ddc, size_t chan,      double target_freq, double lo_offset  ){ -    return tune_xx_subdev_and_dxc(dboard_iface::UNIT_RX, subdev, ddc, target_freq, lo_offset); +    return tune_xx_subdev_and_dxc(dboard_iface::UNIT_RX, subdev, ddc, chan, target_freq, lo_offset);  }  tune_result_t usrp::tune_rx_subdev_and_dsp(      wax::obj subdev, wax::obj ddc, -    double target_freq +    size_t chan, double target_freq  ){      double lo_offset = 0.0;      //if the local oscillator will be in the passband, use an offset      if (subdev[SUBDEV_PROP_USE_LO_OFFSET].as<bool>()){          lo_offset = 2.0*ddc[DSP_PROP_HOST_RATE].as<double>();      } -    return tune_rx_subdev_and_dsp(subdev, ddc, target_freq, lo_offset); +    return tune_rx_subdev_and_dsp(subdev, ddc, chan, target_freq, lo_offset);  } -double usrp::derive_freq_from_rx_subdev_and_dsp(wax::obj subdev, wax::obj ddc){ -    return derive_freq_from_xx_subdev_and_dxc(dboard_iface::UNIT_RX, subdev, ddc); +double usrp::derive_freq_from_rx_subdev_and_dsp( +    wax::obj subdev, wax::obj ddc, size_t chan +){ +    return derive_freq_from_xx_subdev_and_dxc(dboard_iface::UNIT_RX, subdev, ddc, chan);  }  /***********************************************************************   * TX Tune   **********************************************************************/  tune_result_t usrp::tune_tx_subdev_and_dsp( -    wax::obj subdev, wax::obj duc, +    wax::obj subdev, wax::obj duc, size_t chan,      double target_freq, double lo_offset  ){ -    return tune_xx_subdev_and_dxc(dboard_iface::UNIT_TX, subdev, duc, target_freq, lo_offset); +    return tune_xx_subdev_and_dxc(dboard_iface::UNIT_TX, subdev, duc, chan, target_freq, lo_offset);  }  tune_result_t usrp::tune_tx_subdev_and_dsp(      wax::obj subdev, wax::obj duc, -    double target_freq +    size_t chan, double target_freq  ){      double lo_offset = 0.0;      //if the local oscillator will be in the passband, use an offset      if (subdev[SUBDEV_PROP_USE_LO_OFFSET].as<bool>()){          lo_offset = 2.0*duc[DSP_PROP_HOST_RATE].as<double>();      } -    return tune_tx_subdev_and_dsp(subdev, duc, target_freq, lo_offset); +    return tune_tx_subdev_and_dsp(subdev, duc, chan, target_freq, lo_offset);  } -double usrp::derive_freq_from_tx_subdev_and_dsp(wax::obj subdev, wax::obj duc){ -    return derive_freq_from_xx_subdev_and_dxc(dboard_iface::UNIT_TX, subdev, duc); +double usrp::derive_freq_from_tx_subdev_and_dsp( +    wax::obj subdev, wax::obj duc, size_t chan +){ +    return derive_freq_from_xx_subdev_and_dxc(dboard_iface::UNIT_TX, subdev, duc, chan);  } diff --git a/host/lib/usrp/usrp1/codec_ctrl.cpp b/host/lib/usrp/usrp1/codec_ctrl.cpp index 08f2d2a8e..4aa730573 100644 --- a/host/lib/usrp/usrp1/codec_ctrl.cpp +++ b/host/lib/usrp/usrp1/codec_ctrl.cpp @@ -61,6 +61,9 @@ public:      float get_tx_pga_gain(void);      void set_rx_pga_gain(float, char);      float get_rx_pga_gain(char); +     +    //rx adc buffer control +    void bypass_adc_buffers(bool bypass);  private:      usrp1_iface::sptr _iface; @@ -126,7 +129,7 @@ usrp1_codec_ctrl_impl::usrp1_codec_ctrl_impl(usrp1_iface::sptr iface,      _ad9862_regs.clkout2_div_factor = ad9862_regs_t::CLKOUT2_DIV_FACTOR_2;      //write the register settings to the codec -    for (uint8_t addr = 0; addr <= 25; addr++) { +    for (boost::uint8_t addr = 0; addr <= 25; addr++) {          this->send_reg(addr);      } @@ -199,7 +202,7 @@ float usrp1_codec_ctrl_impl::get_rx_pga_gain(char which){   **********************************************************************/  static float aux_adc_to_volts(boost::uint8_t high, boost::uint8_t low)  { -    return float((boost::uint16_t(high) << 2) | low)*3.3/0x3ff; +    return float(((boost::uint16_t(high) << 2) | low)*3.3)/0x3ff;  }  float usrp1_codec_ctrl_impl::read_aux_adc(aux_adc_t which) @@ -419,6 +422,17 @@ void usrp1_codec_ctrl_impl::set_duc_freq(double freq)  }  /*********************************************************************** + * Codec Control ADC buffer bypass + * Disable this for AC-coupled daughterboards (TVRX) + * By default it is initialized TRUE. + **********************************************************************/ +void usrp1_codec_ctrl_impl::bypass_adc_buffers(bool bypass) { +    _ad9862_regs.byp_buffer_a = bypass; +    _ad9862_regs.byp_buffer_b = bypass; +    this->send_reg(2); +} + +/***********************************************************************   * Codec Control Make   **********************************************************************/  usrp1_codec_ctrl::sptr usrp1_codec_ctrl::make(usrp1_iface::sptr iface, diff --git a/host/lib/usrp/usrp1/codec_ctrl.hpp b/host/lib/usrp/usrp1/codec_ctrl.hpp index 259d10ef4..e2e8a010d 100644 --- a/host/lib/usrp/usrp1/codec_ctrl.hpp +++ b/host/lib/usrp/usrp1/codec_ctrl.hpp @@ -92,6 +92,9 @@ public:      //! Set the TX modulator frequency      virtual void set_duc_freq(double freq) = 0; +     +    //! Enable or disable ADC buffer bypass +    virtual void bypass_adc_buffers(bool bypass) = 0;  };  #endif /* INCLUDED_USRP1_CODEC_CTRL_HPP */ diff --git a/host/lib/usrp/usrp1/dboard_iface.cpp b/host/lib/usrp/usrp1/dboard_iface.cpp index 4791b55ce..1ac15a46a 100644 --- a/host/lib/usrp/usrp1/dboard_iface.cpp +++ b/host/lib/usrp/usrp1/dboard_iface.cpp @@ -32,6 +32,8 @@ using namespace uhd;  using namespace uhd::usrp;  using namespace boost::assign; +static const dboard_id_t tvrx_id(0x0040); +  class usrp1_dboard_iface : public dboard_iface {  public: @@ -51,6 +53,10 @@ public:          //init the clock rate shadows          this->set_clock_rate(UNIT_RX, this->get_clock_rates(UNIT_RX).front());          this->set_clock_rate(UNIT_TX, this->get_clock_rates(UNIT_TX).front()); +         +        //yes this is evil but it's necessary for TVRX to work on USRP1 +        if(_rx_dboard_id == tvrx_id) _codec->bypass_adc_buffers(false); +        //else _codec->bypass_adc_buffers(false); //don't think this is necessary      }      ~usrp1_dboard_iface() @@ -93,6 +99,7 @@ public:      std::vector<double> get_clock_rates(unit_t);      double get_clock_rate(unit_t);      void set_clock_enabled(unit_t, bool); +    double get_codec_rate(unit_t);  private:      usrp1_iface::sptr _iface; @@ -170,6 +177,10 @@ void usrp1_dboard_iface::set_clock_enabled(unit_t, bool)      //TODO we can only enable for special case anyway...  } +double usrp1_dboard_iface::get_codec_rate(unit_t){ +    return _clock->get_master_clock_freq(); +} +  /***********************************************************************   * GPIO   **********************************************************************/ diff --git a/host/lib/usrp/usrp1/dsp_impl.cpp b/host/lib/usrp/usrp1/dsp_impl.cpp index d5a88fa2d..e9a5e60a6 100644 --- a/host/lib/usrp/usrp1/dsp_impl.cpp +++ b/host/lib/usrp/usrp1/dsp_impl.cpp @@ -21,6 +21,8 @@  #include <uhd/usrp/dsp_props.hpp>  #include <boost/bind.hpp>  #include <boost/format.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/assign/list_of.hpp>  #include <iostream>  #include <cmath> @@ -42,11 +44,15 @@ void usrp1_impl::rx_dsp_init(void)  /***********************************************************************   * RX DDC Get   **********************************************************************/ -void usrp1_impl::rx_dsp_get(const wax::obj &key, wax::obj &val) -{ +void usrp1_impl::rx_dsp_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()){      case DSP_PROP_NAME: -        val = std::string("usrp1 ddc0"); +        val = str(boost::format("usrp1 ddc %uX %s") +            % this->get_num_ddcs() +            % (this->has_rx_halfband()? "+ hb" : "") +        );          return;      case DSP_PROP_OTHERS: @@ -54,7 +60,16 @@ void usrp1_impl::rx_dsp_get(const wax::obj &key, wax::obj &val)          return;      case DSP_PROP_FREQ_SHIFT: -        val = _rx_dsp_freq; +        val = _rx_dsp_freqs[key.name]; +        return; + +    case DSP_PROP_FREQ_SHIFT_NAMES:{ +            prop_names_t names; +            for(size_t i = 0; i < this->get_num_ddcs(); i++){ +                names.push_back(boost::lexical_cast<std::string>(i)); +            } +            val = names; +        }          return;      case DSP_PROP_CODEC_RATE: @@ -73,30 +88,26 @@ void usrp1_impl::rx_dsp_get(const wax::obj &key, wax::obj &val)  /***********************************************************************   * RX DDC Set   **********************************************************************/ -void usrp1_impl::rx_dsp_set(const wax::obj &key, const wax::obj &val) -{ +void usrp1_impl::rx_dsp_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()) {      case DSP_PROP_FREQ_SHIFT: {              double new_freq = val.as<double>();              boost::uint32_t reg_word = dsp_type1::calc_cordic_word_and_update(                  new_freq, _clock_ctrl->get_master_clock_freq()); -            //TODO TODO TODO TODO TODO TODO TODO TODO TODO  -            // -            // Handle multiple receive channels / DDC's -            // -            //TODO TODO TODO TODO TODO TODO TODO TODO TODO -            _iface->poke32(FR_RX_FREQ_0, reg_word); -            _iface->poke32(FR_RX_FREQ_1, reg_word); -            _iface->poke32(FR_RX_FREQ_2, reg_word); -            _iface->poke32(FR_RX_FREQ_3, reg_word); - -            _rx_dsp_freq = new_freq; +            static const uhd::dict<std::string, boost::uint32_t> +            freq_name_to_reg_val = boost::assign::map_list_of +                ("0", FR_RX_FREQ_0) ("1", FR_RX_FREQ_1) +                ("2", FR_RX_FREQ_2) ("3", FR_RX_FREQ_3) +            ; +            _iface->poke32(freq_name_to_reg_val[key.name], reg_word); +            _rx_dsp_freqs[key.name] = new_freq;              return;          }      case DSP_PROP_HOST_RATE: { -            unsigned int rate = -                    _clock_ctrl->get_master_clock_freq() / val.as<double>(); +            size_t rate = size_t(_clock_ctrl->get_master_clock_freq() / val.as<double>());              if ((rate & 0x01) || (rate < 4) || (rate > 256)) {                  std::cerr << "Decimation must be even and between 4 and 256" @@ -106,7 +117,7 @@ void usrp1_impl::rx_dsp_set(const wax::obj &key, const wax::obj &val)              _rx_dsp_decim = rate;              //TODO Poll every 100ms. Make it selectable? -            _rx_samps_per_poll_interval = 0.1 * _clock_ctrl->get_master_clock_freq() / rate; +            _rx_samps_per_poll_interval = size_t(0.1 * _clock_ctrl->get_master_clock_freq() / rate);              _iface->poke32(FR_DECIM_RATE, _rx_dsp_decim/2 - 1);          } @@ -133,11 +144,15 @@ void usrp1_impl::tx_dsp_init(void)  /***********************************************************************   * TX DUC Get   **********************************************************************/ -void usrp1_impl::tx_dsp_get(const wax::obj &key, wax::obj &val) -{ +void usrp1_impl::tx_dsp_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()) {      case DSP_PROP_NAME: -        val = std::string("usrp1 duc0"); +        val = str(boost::format("usrp1 duc %uX %s") +            % this->get_num_ducs() +            % (this->has_tx_halfband()? "+ hb" : "") +        );          return;      case DSP_PROP_OTHERS: @@ -145,7 +160,16 @@ void usrp1_impl::tx_dsp_get(const wax::obj &key, wax::obj &val)          return;      case DSP_PROP_FREQ_SHIFT: -        val = _tx_dsp_freq; +        val = _tx_dsp_freqs[key.name]; +        return; + +    case DSP_PROP_FREQ_SHIFT_NAMES:{ +            prop_names_t names; +            for(size_t i = 0; i < this->get_num_ducs(); i++){ +                names.push_back(boost::lexical_cast<std::string>(i)); +            } +            val = names; +        }          return;      case DSP_PROP_CODEC_RATE: @@ -164,26 +188,25 @@ void usrp1_impl::tx_dsp_get(const wax::obj &key, wax::obj &val)  /***********************************************************************   * TX DUC Set   **********************************************************************/ -void usrp1_impl::tx_dsp_set(const wax::obj &key, const wax::obj &val) -{ +void usrp1_impl::tx_dsp_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()) { -    //TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO -    // -    // Set both codec frequencies until we have duality properties  -    // -    //TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO      case DSP_PROP_FREQ_SHIFT: {              double new_freq = val.as<double>(); -            _codec_ctrls[DBOARD_SLOT_A]->set_duc_freq(new_freq); -            _codec_ctrls[DBOARD_SLOT_B]->set_duc_freq(new_freq); -            _tx_dsp_freq = new_freq; + +            //map the freq shift key to a subdev spec to a particular codec chip +            std::string db_name = _tx_subdev_spec.at(boost::lexical_cast<size_t>(key.name)).db_name; +            if (db_name == "A") _codec_ctrls[DBOARD_SLOT_A]->set_duc_freq(new_freq); +            if (db_name == "B") _codec_ctrls[DBOARD_SLOT_B]->set_duc_freq(new_freq); + +            _tx_dsp_freqs[key.name] = new_freq;              return;          }      case DSP_PROP_HOST_RATE: { -            unsigned int rate = -                    _clock_ctrl->get_master_clock_freq() * 2 / val.as<double>(); +            size_t rate = size_t(_clock_ctrl->get_master_clock_freq() * 2 / val.as<double>());              if ((rate & 0x01) || (rate < 8) || (rate > 512)) {                  std::cerr << "Interpolation rate must be even and between 8 and 512" @@ -194,7 +217,7 @@ void usrp1_impl::tx_dsp_set(const wax::obj &key, const wax::obj &val)              _tx_dsp_interp = rate;              //TODO Poll every 100ms. Make it selectable?  -            _tx_samps_per_poll_interval = 0.1 * _clock_ctrl->get_master_clock_freq() * 2 / rate; +            _tx_samps_per_poll_interval = size_t(0.1 * _clock_ctrl->get_master_clock_freq() * 2 / rate);              _iface->poke32(FR_INTERP_RATE, _tx_dsp_interp / 4 - 1);              return; diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index 92e8bc20a..6728d9b15 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -33,76 +33,148 @@ using namespace uhd::usrp;  using namespace uhd::transport;  namespace asio = boost::asio; -struct usrp1_send_state { -    uhd::transport::managed_send_buffer::sptr send_buff; -    size_t bytes_written; -    size_t underrun_poll_samp_count; +static const size_t alignment_padding = 512; -    size_t bytes_free() -    { -        if (send_buff != NULL) -            return send_buff->size() - bytes_written; -        else -            return 0; +/*********************************************************************** + * Helper struct to associate an offset with a buffer + **********************************************************************/ +class offset_send_buffer{ +public: +    typedef boost::shared_ptr<offset_send_buffer> sptr; + +    static sptr make(managed_send_buffer::sptr buff, size_t offset = 0){ +        return sptr(new offset_send_buffer(buff, offset));      } -}; -struct usrp1_recv_state { -    uhd::transport::managed_recv_buffer::sptr recv_buff; -    size_t bytes_read; -    size_t overrun_poll_samp_count; +    //member variables +    managed_send_buffer::sptr buff; +    size_t offset; /* in bytes */ -    size_t bytes_avail() -    { -        if (recv_buff != NULL) -            return recv_buff->size() - bytes_read; -        else -            return 0; -    } +private: +    offset_send_buffer(managed_send_buffer::sptr buff, size_t offset): +        buff(buff), offset(offset){/* NOP */}  };  /***********************************************************************   * IO Implementation Details   **********************************************************************/ -struct usrp1_impl::io_impl { -    io_impl(); -    ~io_impl(void); - -    //state handling for buffer management  -    usrp1_recv_state recv_state; -    usrp1_send_state send_state; - -    //send transport management  -    bool get_send_buffer(zero_copy_if::sptr zc_if); -    size_t copy_convert_send_samps(const void *buff, size_t num_samps, -                              size_t sample_offset, const io_type_t io_type, -                              otw_type_t otw_type); -    bool conditional_buff_commit(bool force); -    bool check_underrun(usrp_ctrl::sptr ctrl_if, -                        size_t poll_interval, bool force); - -    //recv transport management  -    bool get_recv_buffer(zero_copy_if::sptr zc_if); -    size_t copy_convert_recv_samps(void *buff, size_t num_samps, -                              size_t sample_offset, const io_type_t io_type, -                              otw_type_t otw_type); -    bool check_overrun(usrp_ctrl::sptr ctrl_if, -                        size_t poll_interval, bool force); +struct usrp1_impl::io_impl{ +    io_impl(zero_copy_if::sptr data_transport): +        data_transport(data_transport), +        underflow_poll_samp_count(0), +        overflow_poll_samp_count(0), +        curr_buff_committed(true), +        curr_buff(offset_send_buffer::make(data_transport->get_send_buff())) +    { +        /* NOP */ +    } + +    ~io_impl(void){ +        flush_send_buff(); +    } + +    zero_copy_if::sptr data_transport; + +    //state management for the vrt packet handler code +    vrt_packet_handler::recv_state packet_handler_recv_state; +    vrt_packet_handler::send_state packet_handler_send_state; + +    //state management for overflow and underflow +    size_t underflow_poll_samp_count; +    size_t overflow_poll_samp_count; + +    //wrapper around the actual send buffer interface +    //all of this to ensure only aligned lengths are committed +    //NOTE: you must commit before getting a new buffer +    //since the vrt packet handler obeys this, we are ok +    bool curr_buff_committed; +    offset_send_buffer::sptr curr_buff; +    void commit_send_buff(offset_send_buffer::sptr, offset_send_buffer::sptr, size_t); +    void flush_send_buff(void); +    bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &, double);  }; -usrp1_impl::io_impl::io_impl() -{ -    send_state.send_buff = uhd::transport::managed_send_buffer::sptr(); -    recv_state.recv_buff = uhd::transport::managed_recv_buffer::sptr(); +/*! + * Perform an actual commit on the send buffer: + * Copy the remainder of alignment to the next buffer. + * Commit the current buffer at multiples of alignment. + */ +void usrp1_impl::io_impl::commit_send_buff( +    offset_send_buffer::sptr curr, +    offset_send_buffer::sptr next, +    size_t num_bytes +){ +    //total number of bytes now in the current buffer +    size_t bytes_in_curr_buffer = curr->offset + num_bytes; + +    //calculate how many to commit and remainder +    size_t num_bytes_remaining = bytes_in_curr_buffer % alignment_padding; +    size_t num_bytes_to_commit = bytes_in_curr_buffer - num_bytes_remaining; + +    //copy the remainder into the next buffer +    std::memcpy( +        next->buff->cast<char *>() + next->offset, +        curr->buff->cast<char *>() + num_bytes_to_commit, +        num_bytes_remaining +    ); + +    //update the offset into the next buffer +    next->offset += num_bytes_remaining; + +    //commit the current buffer +    curr->buff->commit(num_bytes_to_commit); +    curr_buff_committed = true;  } -usrp1_impl::io_impl::~io_impl(void) -{ -   /* NOP */ +/*! + * Flush the current buffer by padding out to alignment and committing. + */ +void usrp1_impl::io_impl::flush_send_buff(void){ +    //calculate the number of bytes to alignment +    size_t bytes_to_pad = (-1*curr_buff->offset)%alignment_padding; + +    //get the buffer, clear, and commit (really current buffer) +    vrt_packet_handler::managed_send_buffs_t buffs(1); +    if (this->get_send_buffs(buffs, 0.1)){ +        std::memset(buffs[0]->cast<void *>(), 0, bytes_to_pad); +        buffs[0]->commit(bytes_to_pad); +    }  } -void usrp1_impl::io_init(void) -{ +/*! + * Get a managed send buffer with the alignment padding: + * Always grab the next send buffer so we can timeout here. + */ +bool usrp1_impl::io_impl::get_send_buffs( +    vrt_packet_handler::managed_send_buffs_t &buffs, double timeout +){ +    UHD_ASSERT_THROW(curr_buff_committed and buffs.size() == 1); + +    //try to get a new managed buffer with timeout +    offset_send_buffer::sptr next_buff(offset_send_buffer::make(data_transport->get_send_buff(timeout))); +    if (not next_buff->buff.get()) return false; /* propagate timeout here */ + +    //calculate the buffer pointer and size given the offset +    //references to the buffers are held in the bound function +    buffs[0] = managed_send_buffer::make_safe( +        boost::asio::buffer( +            curr_buff->buff->cast<char *>() + curr_buff->offset, +            curr_buff->buff->size()         - curr_buff->offset +        ), +        boost::bind(&usrp1_impl::io_impl::commit_send_buff, this, curr_buff, next_buff, _1) +    ); + +    //store the next buffer for the next call +    curr_buff = next_buff; +    curr_buff_committed = false; + +    return true; +} + +/*********************************************************************** + * Initialize internals within this file + **********************************************************************/ +void usrp1_impl::io_init(void){      _rx_otw_type.width = 16;      _rx_otw_type.shift = 0;      _rx_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; @@ -111,216 +183,131 @@ void usrp1_impl::io_init(void)      _tx_otw_type.shift = 0;      _tx_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; -    _io_impl = UHD_PIMPL_MAKE(io_impl, ()); +    _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport));  }  /*********************************************************************** - * Data Send + * Data send + helper functions   **********************************************************************/ -bool usrp1_impl::io_impl::get_send_buffer(zero_copy_if::sptr zc_if) -{ -    if (send_state.send_buff == NULL) { - -        send_state.send_buff = zc_if->get_send_buff(); -        if (send_state.send_buff == NULL) -            return false; - -        send_state.bytes_written = 0; -    } - -    return true; -} - -size_t usrp1_impl::io_impl::copy_convert_send_samps(const void *buff, -                                                    size_t num_samps, -                                                    size_t sample_offset, -                                                    const io_type_t io_type, -                                                    otw_type_t otw_type) -{ -    UHD_ASSERT_THROW(send_state.bytes_free() % otw_type.get_sample_size() == 0); - -    size_t samps_free = send_state.bytes_free() / otw_type.get_sample_size(); -    size_t copy_samps = std::min(num_samps - sample_offset, samps_free);  - -    const boost::uint8_t *io_mem = -        reinterpret_cast<const boost::uint8_t *>(buff); - -    boost::uint8_t *otw_mem = send_state.send_buff->cast<boost::uint8_t *>(); - -    convert_io_type_to_otw_type(io_mem + sample_offset * io_type.size, -                                io_type, -                                otw_mem + send_state.bytes_written, -                                otw_type, -                                copy_samps); - -    send_state.bytes_written += copy_samps * otw_type.get_sample_size(); -    send_state.underrun_poll_samp_count += copy_samps; - -    return copy_samps; -} - -bool usrp1_impl::io_impl::conditional_buff_commit(bool force) -{ -    if (send_state.bytes_written % 512) -        return false; - -    if (force || send_state.bytes_free() == 0) { -        send_state.send_buff->commit(send_state.bytes_written); -        send_state.send_buff = uhd::transport::managed_send_buffer::sptr(); -        return true; -    } -     -    return false; +static void usrp1_bs_vrt_packer( +    boost::uint32_t *, +    vrt::if_packet_info_t &if_packet_info +){ +    if_packet_info.num_header_words32 = 0; +    if_packet_info.num_packet_words32 = if_packet_info.num_payload_words32;  } -bool usrp1_impl::io_impl::check_underrun(usrp_ctrl::sptr ctrl_if, -                                         size_t poll_interval, -                                         bool force) -{ -    unsigned char underrun = 0; - -    bool ready_to_poll = send_state.underrun_poll_samp_count > poll_interval; - -    if (force || ready_to_poll) { -        int ret = ctrl_if->usrp_control_read(VRQ_GET_STATUS, -                                             0, -                                             GS_TX_UNDERRUN, -                                             &underrun, sizeof(char)); -        if (ret < 0) -            std::cerr << "USRP: underrun check failed" << std::endl; -        if (underrun) -            std::cerr << "U" << std::flush; - -        send_state.underrun_poll_samp_count = 0; -    } - -    return (bool) underrun; +size_t usrp1_impl::get_max_send_samps_per_packet(void) const { +    return (_data_transport->get_send_frame_size() - alignment_padding) +        / _tx_otw_type.get_sample_size() +        / _tx_subdev_spec.size() +    ;  } -size_t usrp1_impl::send(const std::vector<const void *> &buffs, -                        size_t num_samps, -                        const tx_metadata_t &, -                        const io_type_t &io_type, -                        send_mode_t) -{ -    UHD_ASSERT_THROW(buffs.size() == 1); - -    size_t total_samps_sent = 0; - -    while (total_samps_sent < num_samps) { -        if (!_io_impl->get_send_buffer(_data_transport)) -            return 0; - -        total_samps_sent += _io_impl->copy_convert_send_samps(buffs[0], -                                                              num_samps, -                                                              total_samps_sent, -                                                              io_type, -                                                              _tx_otw_type); -        if (total_samps_sent == num_samps) -            _io_impl->conditional_buff_commit(true); -        else -            _io_impl->conditional_buff_commit(false); - -        _io_impl->check_underrun(_ctrl_transport, -                                 _tx_samps_per_poll_interval, false); +size_t usrp1_impl::send( +    const std::vector<const void *> &buffs, size_t num_samps, +    const tx_metadata_t &metadata, const io_type_t &io_type, +    send_mode_t send_mode, double timeout +){ +    size_t num_samps_sent = vrt_packet_handler::send( +        _io_impl->packet_handler_send_state,       //last state of the send handler +        buffs, num_samps,                          //buffer to fill +        metadata, send_mode,                       //samples metadata +        io_type, _tx_otw_type,                     //input and output types to convert +        _clock_ctrl->get_master_clock_freq(),      //master clock tick rate +        &usrp1_bs_vrt_packer, +        boost::bind(&usrp1_impl::io_impl::get_send_buffs, _io_impl.get(), _1, timeout), +        get_max_send_samps_per_packet(), +        0,                                         //vrt header offset +        _tx_subdev_spec.size()                     //num channels +    ); + +    //Don't honor sob because it is normal to be always bursting... +    //handle eob flag (commit the buffer) +    if (metadata.end_of_burst) _io_impl->flush_send_buff(); + +    //handle the polling for underflow conditions +    _io_impl->underflow_poll_samp_count += num_samps_sent; +    if (_io_impl->underflow_poll_samp_count >= _tx_samps_per_poll_interval){ +        _io_impl->underflow_poll_samp_count = 0; //reset count +        boost::uint8_t underflow = 0; +        ssize_t ret = _ctrl_transport->usrp_control_read( +            VRQ_GET_STATUS, 0, GS_TX_UNDERRUN, +            &underflow, sizeof(underflow) +        ); +        if (ret < 0)        std::cerr << "USRP: underflow check failed" << std::endl; +        else if (underflow) std::cerr << "U" << std::flush;      } -    return total_samps_sent; +    return num_samps_sent;  }  /*********************************************************************** - * Data Recv + * Data recv + helper functions   **********************************************************************/ -bool usrp1_impl::io_impl::get_recv_buffer(zero_copy_if::sptr zc_if) -{ -    if ((recv_state.recv_buff == NULL) || (recv_state.bytes_avail() == 0)) { - -        recv_state.recv_buff = zc_if->get_recv_buff(); -        if (recv_state.recv_buff == NULL) -            return false; - -        recv_state.bytes_read = 0; -    } - -    return true; +static void usrp1_bs_vrt_unpacker( +    const boost::uint32_t *, +    vrt::if_packet_info_t &if_packet_info +){ +    if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; +    if_packet_info.num_payload_words32 = if_packet_info.num_packet_words32; +    if_packet_info.num_header_words32 = 0; +    if_packet_info.packet_count = 0; +    if_packet_info.sob = false; +    if_packet_info.eob = false; +    if_packet_info.has_sid = false; +    if_packet_info.has_cid = false; +    if_packet_info.has_tsi = false; +    if_packet_info.has_tsf = false; +    if_packet_info.has_tlr = false;  } -size_t usrp1_impl::io_impl::copy_convert_recv_samps(void *buff, -                                                    size_t num_samps, -                                                    size_t sample_offset, -                                                    const io_type_t io_type, -                                                    otw_type_t otw_type) -{ -    UHD_ASSERT_THROW(recv_state.bytes_avail() % otw_type.get_sample_size() == 0); - -    size_t samps_avail = recv_state.bytes_avail() / otw_type.get_sample_size(); -    size_t copy_samps = std::min(num_samps - sample_offset, samps_avail);  - -    const boost::uint8_t *otw_mem = -        recv_state.recv_buff->cast<const boost::uint8_t *>(); - -    boost::uint8_t *io_mem = reinterpret_cast<boost::uint8_t *>(buff); - -    convert_otw_type_to_io_type(otw_mem + recv_state.bytes_read, -                                otw_type, -                                io_mem + sample_offset * io_type.size, -                                io_type, -                                copy_samps); - -    recv_state.bytes_read += copy_samps * otw_type.get_sample_size(); -    recv_state.overrun_poll_samp_count += copy_samps; - -    return copy_samps; +static bool get_recv_buffs( +    zero_copy_if::sptr zc_if, double timeout, +    vrt_packet_handler::managed_recv_buffs_t &buffs +){ +    UHD_ASSERT_THROW(buffs.size() == 1); +    buffs[0] = zc_if->get_recv_buff(timeout); +    return buffs[0].get() != NULL;  } -bool usrp1_impl::io_impl::check_overrun(usrp_ctrl::sptr ctrl_if, -                                        size_t poll_interval, -                                        bool force) -{ -    unsigned char overrun = 0; - -    bool ready_to_poll = recv_state.overrun_poll_samp_count > poll_interval; - -    if (force || ready_to_poll) { -        int ret = ctrl_if->usrp_control_read(VRQ_GET_STATUS, -                                             0, -                                             GS_RX_OVERRUN, -                                             &overrun, sizeof(char)); -        if (ret < 0) -            std::cerr << "USRP: overrrun check failed" << std::endl; -        if (overrun) -            std::cerr << "O" << std::flush; - -        recv_state.overrun_poll_samp_count = 0; -    } - -    return (bool) overrun; +size_t usrp1_impl::get_max_recv_samps_per_packet(void) const { +    return _data_transport->get_recv_frame_size() +        / _rx_otw_type.get_sample_size() +        / _rx_subdev_spec.size() +    ;  } -size_t usrp1_impl::recv(const std::vector<void *> &buffs, -                        size_t num_samps, -                        rx_metadata_t &, -                        const io_type_t &io_type, -                        recv_mode_t, -                        size_t) -{ -    UHD_ASSERT_THROW(buffs.size() == 1); - -    size_t total_samps_recv = 0; - -    while (total_samps_recv < num_samps) { - -        if (!_io_impl->get_recv_buffer(_data_transport)) -            return 0; - -        total_samps_recv += _io_impl->copy_convert_recv_samps(buffs[0], -                                                              num_samps, -                                                              total_samps_recv, -                                                              io_type, -                                                              _rx_otw_type); -        _io_impl->check_overrun(_ctrl_transport, -                                _rx_samps_per_poll_interval, false); +size_t usrp1_impl::recv( +    const std::vector<void *> &buffs, size_t num_samps, +    rx_metadata_t &metadata, const io_type_t &io_type, +    recv_mode_t recv_mode, double timeout +){ +    size_t num_samps_recvd = vrt_packet_handler::recv( +        _io_impl->packet_handler_recv_state,       //last state of the recv handler +        buffs, num_samps,                          //buffer to fill +        metadata, recv_mode,                       //samples metadata +        io_type, _rx_otw_type,                     //input and output types to convert +        _clock_ctrl->get_master_clock_freq(),      //master clock tick rate +        &usrp1_bs_vrt_unpacker, +        boost::bind(&get_recv_buffs, _data_transport, timeout, _1), +        &vrt_packet_handler::handle_overflow_nop, +        0,                                         //vrt header offset +        _rx_subdev_spec.size()                     //num channels +    ); + +    //handle the polling for overflow conditions +    _io_impl->overflow_poll_samp_count += num_samps_recvd; +    if (_io_impl->overflow_poll_samp_count >= _rx_samps_per_poll_interval){ +        _io_impl->overflow_poll_samp_count = 0; //reset count +        boost::uint8_t overflow = 0; +        ssize_t ret = _ctrl_transport->usrp_control_read( +            VRQ_GET_STATUS, 0, GS_RX_OVERRUN, +            &overflow, sizeof(overflow) +        ); +        if (ret < 0)       std::cerr << "USRP: overflow check failed" << std::endl; +        else if (overflow) std::cerr << "O" << std::flush;      } -    return total_samps_recv;  +    return num_samps_recvd;  } diff --git a/host/lib/usrp/usrp1/mboard_impl.cpp b/host/lib/usrp/usrp1/mboard_impl.cpp index a90532cb8..fe3774eb4 100644 --- a/host/lib/usrp/usrp1/mboard_impl.cpp +++ b/host/lib/usrp/usrp1/mboard_impl.cpp @@ -36,6 +36,8 @@  using namespace uhd;  using namespace uhd::usrp; +static const bool usrp1_mboard_verbose = false; +  /***********************************************************************   * Calculate the RX mux value:   *    The I and Q mux values are intentionally reversed to flip I and Q @@ -146,6 +148,7 @@ static boost::uint32_t calc_tx_mux(      //calculate the channel flags      int channel_flags = 0, chan = 0; +    uhd::dict<std::string, int> slot_to_chan_count = boost::assign::map_list_of("A", 0)("B", 0);      BOOST_FOREACH(const subdev_spec_pair_t &pair, subdev_spec){          wax::obj dboard = mboard[named_prop_t(MBOARD_PROP_TX_DBOARD, pair.db_name)];          wax::obj subdev = dboard[named_prop_t(DBOARD_PROP_SUBDEV, pair.sd_name)]; @@ -155,6 +158,14 @@ static boost::uint32_t calc_tx_mux(          if (pair.db_name == "A") channel_flags |= chan_to_conn_to_flag[chan][conn] << 0;          if (pair.db_name == "B") channel_flags |= chan_to_conn_to_flag[chan][conn] << 8; +        //sanity check, only 1 channel per slot +        slot_to_chan_count[pair.db_name]++; +        if (slot_to_chan_count[pair.db_name] > 1){ +            throw std::runtime_error(str(boost::format( +                "dboard slot %s assigned to multiple channels in subdev spec %s" +            ) % pair.db_name % subdev_spec.to_string())); +        } +          //increment for the next channel          chan++;      } @@ -172,23 +183,23 @@ static boost::uint32_t calc_tx_mux(   * |               Reserved                        |T|DUCs |R|DDCs |   * +-----------------------------------------------+-+-----+-+-----+   */ -static int num_ddcs(boost::uint32_t regval) -{ +size_t usrp1_impl::get_num_ddcs(void){ +    boost::uint32_t regval = _iface->peek32(FR_RB_CAPS);      return (regval >> 0) & 0x0007;  } -static int num_ducs(boost::uint32_t regval) -{ +size_t usrp1_impl::get_num_ducs(void){ +    boost::uint32_t regval = _iface->peek32(FR_RB_CAPS);      return (regval >> 4) & 0x0007;  } -static bool has_rx_halfband(boost::uint32_t regval) -{ +bool usrp1_impl::has_rx_halfband(void){ +    boost::uint32_t regval = _iface->peek32(FR_RB_CAPS);      return (regval >> 3) & 0x0001;  } -static bool has_tx_halfband(boost::uint32_t regval) -{ +bool usrp1_impl::has_tx_halfband(void){ +    boost::uint32_t regval = _iface->peek32(FR_RB_CAPS);      return (regval >> 7) & 0x0001;  } @@ -220,26 +231,25 @@ void usrp1_impl::mboard_init(void)      // Set default for TX format to 16-bit I&Q      _iface->poke32(FR_TX_FORMAT, 0x00000000); -    // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO  -    //  -    // Do something useful with the capabilities register -    // -    boost::uint32_t regval = _iface->peek32(FR_RB_CAPS); -    std::cout << "USRP1 Capabilities" << std::endl; -    std::cout << "    number of duc's: " << num_ddcs(regval) << std::endl; -    std::cout << "    number of ddc's: " << num_ducs(regval) << std::endl; -    std::cout << "    rx halfband:     " << has_rx_halfband(regval) << std::endl; -    std::cout << "    tx halfband:     " << has_tx_halfband(regval) << std::endl; +    if (usrp1_mboard_verbose){ +        std::cout << "USRP1 Capabilities" << std::endl; +        std::cout << "    number of duc's: " << get_num_ddcs() << std::endl; +        std::cout << "    number of ddc's: " << get_num_ducs() << std::endl; +        std::cout << "    rx halfband:     " << has_rx_halfband() << std::endl; +        std::cout << "    tx halfband:     " << has_tx_halfband() << std::endl; +    }  }  void usrp1_impl::issue_stream_cmd(const stream_cmd_t &stream_cmd)  { -    if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS) { -        _iface->write_firmware_cmd(VRQ_FPGA_SET_RX_ENABLE, true, 0, 0, 0); -    } +    switch(stream_cmd.stream_mode){ +    case stream_cmd_t::STREAM_MODE_START_CONTINUOUS: +        return _iface->write_firmware_cmd(VRQ_FPGA_SET_RX_ENABLE, true, 0, 0, 0); + +    case stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS: +        return _iface->write_firmware_cmd(VRQ_FPGA_SET_RX_ENABLE, false, 0, 0, 0); -    if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS) { -        _iface->write_firmware_cmd(VRQ_FPGA_SET_RX_ENABLE, false, 0, 0, 0); +    default: throw std::runtime_error("unsupported stream command type for USRP1");      }  } @@ -269,7 +279,7 @@ void usrp1_impl::mboard_get(const wax::obj &key_, wax::obj &val)      //handle the get request conditioned on the key      switch(key.as<mboard_prop_t>()){      case MBOARD_PROP_NAME: -        val = std::string("usrp1 mboard"); +        val = std::string("usrp1 mboard - " + (*_mboard_proxy)[std::string("serial")].as<std::string>());          return;      case MBOARD_PROP_OTHERS: @@ -361,18 +371,26 @@ void usrp1_impl::mboard_set(const wax::obj &key, const wax::obj &val)      case MBOARD_PROP_RX_SUBDEV_SPEC:          _rx_subdev_spec = val.as<subdev_spec_t>(); +        if (_rx_subdev_spec.size() > this->get_num_ddcs()){ +            throw std::runtime_error(str(boost::format( +                "USRP1 suports up to %u RX channels.\n" +                "However, this RX subdev spec requires %u channels\n" +            ) % this->get_num_ddcs() % _rx_subdev_spec.size())); +        }          verify_rx_subdev_spec(_rx_subdev_spec, _mboard_proxy->get_link()); -        //sanity check -        UHD_ASSERT_THROW(_rx_subdev_spec.size() <= 2);          //set the mux and set the number of rx channels          _iface->poke32(FR_RX_MUX, calc_rx_mux(_rx_subdev_spec, _mboard_proxy->get_link()));          return;      case MBOARD_PROP_TX_SUBDEV_SPEC:          _tx_subdev_spec = val.as<subdev_spec_t>(); +        if (_tx_subdev_spec.size() > this->get_num_ducs()){ +            throw std::runtime_error(str(boost::format( +                "USRP1 suports up to %u TX channels.\n" +                "However, this TX subdev spec requires %u channels\n" +            ) % this->get_num_ducs() % _tx_subdev_spec.size())); +        }          verify_tx_subdev_spec(_tx_subdev_spec, _mboard_proxy->get_link()); -        //sanity check -        UHD_ASSERT_THROW(_tx_subdev_spec.size() <= 2);          //set the mux and set the number of tx channels          _iface->poke32(FR_TX_MUX, calc_tx_mux(_tx_subdev_spec, _mboard_proxy->get_link()));          return; diff --git a/host/lib/usrp/usrp1/usrp1_ctrl.cpp b/host/lib/usrp/usrp1/usrp1_ctrl.cpp index 451129ef5..5043aed7d 100644 --- a/host/lib/usrp/usrp1/usrp1_ctrl.cpp +++ b/host/lib/usrp/usrp1/usrp1_ctrl.cpp @@ -38,6 +38,8 @@ enum firmware_code {  #define FX2_FIRMWARE_LOAD 0xa0 +static const bool load_img_msg = true; +  /***********************************************************************   * Helper Functions   **********************************************************************/ @@ -178,6 +180,7 @@ public:          unsigned char reset_n = 0;          //hit the reset line +        if (load_img_msg) std::cout << "Loading firmware image: " << filestring << "..." << std::flush;          usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0,                             &reset_y, 1); @@ -206,14 +209,14 @@ public:              }                //type 0x01 is end               else if (type == 0x01) { +                usrp_set_firmware_hash(hash); //set hash before reset                  usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0,                                     &reset_n, 1); -                usrp_set_firmware_hash(hash);                  file.close();                  //wait for things to settle                  boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); - +                if (load_img_msg) std::cout << " done" << std::endl;                  return USRP_FIRMWARE_LOAD_SUCCESS;               }              //type anything else is unhandled @@ -249,45 +252,49 @@ public:          unsigned char buf[ep0_size];          int ret; -        FILE *fp; -        if ((fp = fopen(filename, "rb")) == NULL) { +        if (load_img_msg) std::cout << "Loading FPGA image: " << filestring << "..." << std::flush; +        std::ifstream file; +        file.open(filename, std::ios::in | std::ios::binary); +        if (not file.good()) {              std::cerr << "cannot open fpga input file" << std::endl; -            fclose(fp); +            file.close();              return -1;          }          if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_BEGIN) < 0) {              std::cerr << "fpga load error" << std::endl; -            fclose(fp); +            file.close();              return -1;          } -        ssize_t n; -        while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) { +        while (not file.eof()) { +            file.read((char *)buf, sizeof(buf)); +            size_t n = file.gcount();              ret = usrp_control_write(VRQ_FPGA_LOAD, 0, FL_XFER,                                       buf, n); -            if (ret != n) { +            if (ret < 0 or size_t(ret) != n) {                  std::cerr << "fpga load error " << ret << std::endl; -                fclose(fp); +                file.close();                  return -1;              }          }          if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_END) < 0) {              std::cerr << "fpga load error" << std::endl; -            fclose(fp); +            file.close();              return -1;          }          usrp_set_fpga_hash(hash); -        fclose(fp); +        file.close(); +        if (load_img_msg) std::cout << " done" << std::endl;          return 0;       }      int usrp_load_eeprom(std::string filestring)      {          const char *filename = filestring.c_str(); -        const uint16_t i2c_addr = 0x50; +        const boost::uint16_t i2c_addr = 0x50;          //FIXME: verify types          int len; @@ -416,7 +423,7 @@ public:      } -    int usrp_control_write_cmd(uint8_t request, uint16_t value, uint16_t index) +    int usrp_control_write_cmd(boost::uint8_t request, boost::uint16_t value, boost::uint16_t index)      {          return usrp_control_write(request, value, index, 0, 0);      } diff --git a/host/lib/usrp/usrp1/usrp1_iface.cpp b/host/lib/usrp/usrp1/usrp1_iface.cpp index 4bc18dd16..64ced2905 100644 --- a/host/lib/usrp/usrp1/usrp1_iface.cpp +++ b/host/lib/usrp/usrp1/usrp1_iface.cpp @@ -49,7 +49,7 @@ public:       ******************************************************************/      void poke32(boost::uint32_t addr, boost::uint32_t value)      { -        boost::uint32_t swapped = byteswap(value); +        boost::uint32_t swapped = uhd::htonx(value);          if (iface_debug) {              std::cout.fill('0'); @@ -72,15 +72,9 @@ public:              std::cerr << "USRP: failed memory write: " << ret << std::endl;      } -    void poke16(boost::uint32_t, boost::uint16_t) -    { -        //fpga only handles 32 bit writes -        std::cerr << "USRP: unsupported operation: poke16()" << std::endl; -    } -      boost::uint32_t peek32(boost::uint32_t addr)      { -        uint32_t value_out; +        boost::uint32_t value_out;          boost::uint8_t w_index_h = SPI_ENABLE_FPGA & 0xff;          boost::uint8_t w_index_l = (SPI_FMT_MSB | SPI_FMT_HDR_1) & 0xff; @@ -95,13 +89,7 @@ public:          if (ret < 0)              std::cerr << "USRP: failed memory read: " << ret << std::endl; -        return byteswap(value_out); -    } - -    boost::uint16_t peek16(boost::uint32_t addr) -    { -        uint32_t val = peek32(addr); -        return boost::uint16_t(val & 0xff); +        return uhd::ntohx(value_out);      }      /******************************************************************* diff --git a/host/lib/usrp/usrp1/usrp1_iface.hpp b/host/lib/usrp/usrp1/usrp1_iface.hpp index 9a3fdd6bc..3f608584a 100644 --- a/host/lib/usrp/usrp1/usrp1_iface.hpp +++ b/host/lib/usrp/usrp1/usrp1_iface.hpp @@ -54,20 +54,6 @@ public:      virtual boost::uint32_t peek32(boost::uint32_t addr) = 0;      /*! -     * Write a register (16 bits) -     * \param addr the address -     * \param data the 16bit data -     */ -    virtual void poke16(boost::uint32_t addr, boost::uint16_t data) = 0; - -    /*! -     * read a register (16 bits) -     * \param addr the address -     * \return the 16bit data -     */ -    virtual boost::uint16_t peek16(boost::uint32_t addr) = 0; - -    /*!       * Perform an spi transaction.       * \param which_slave the slave device number       * \param config spi config args diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index a6806dbc3..276ca86f6 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -30,6 +30,7 @@  #include <boost/assign/list_of.hpp>  #include <boost/filesystem.hpp>  #include <boost/thread/thread.hpp> +#include <boost/lexical_cast.hpp>  #include <iostream>  using namespace uhd; @@ -62,7 +63,7 @@ static device_addrs_t usrp1_find(const device_addr_t &hint)              hint.has_key("fw")? hint["fw"] : "usrp1_fw.ihx"          );      } -    catch(const std::exception &e){ +    catch(...){          uhd::print_warning(              "Could not locate USRP1 firmware.\n"              "Please install the images package.\n" @@ -74,29 +75,29 @@ static device_addrs_t usrp1_find(const device_addr_t &hint)      boost::uint16_t vid = hint.has_key("uninit") ? FX2_VENDOR_ID : USRP1_VENDOR_ID;      boost::uint16_t pid = hint.has_key("uninit") ? FX2_PRODUCT_ID : USRP1_PRODUCT_ID; -    //see what we got on the USB bus -    std::vector<usb_device_handle::sptr> device_list = -        usb_device_handle::get_device_list(vid, pid); - -    if(device_list.size() == 0) return usrp1_addrs; //return nothing if no USRPs found +    // Important note: +    // The get device list calls are nested inside the for loop. +    // This allows the usb guts to decontruct when not in use, +    // so that re-enumeration after fw load can occur successfully. +    // This requirement is a courtesy of libusb1.0 on windows.      //find the usrps and load firmware -    BOOST_FOREACH(usb_device_handle::sptr handle, device_list) { -            usb_control::sptr ctrl_transport = usb_control::make(handle); -            usrp_ctrl::sptr usrp_ctrl = usrp_ctrl::make(ctrl_transport); -            usrp_ctrl->usrp_load_firmware(usrp1_fw_image); +    BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { +        usrp_ctrl::make(usb_control::make(handle))->usrp_load_firmware(usrp1_fw_image);      }      //get descriptors again with serial number, but using the initialized VID/PID now since we have firmware      vid = USRP1_VENDOR_ID;      pid = USRP1_PRODUCT_ID; -    device_list = usb_device_handle::get_device_list(vid, pid); -    BOOST_FOREACH(usb_device_handle::sptr handle, device_list) { -            device_addr_t new_addr; -            new_addr["type"] = "usrp1"; -            new_addr["serial"] = handle->get_serial(); +    BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { +        device_addr_t new_addr; +        new_addr["type"] = "usrp1"; +        new_addr["serial"] = handle->get_serial(); +        //this is a found usrp1 when a hint serial is not specified or it matches +        if (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]){              usrp1_addrs.push_back(new_addr); +        }      }      return usrp1_addrs; @@ -105,8 +106,8 @@ static device_addrs_t usrp1_find(const device_addr_t &hint)  /***********************************************************************   * Make   **********************************************************************/ -static device::sptr usrp1_make(const device_addr_t &device_addr) -{ +static device::sptr usrp1_make(const device_addr_t &device_addr){ +      //extract the FPGA path for the USRP1      std::string usrp1_fpga_image = find_image_path(          device_addr.has_key("fpga")? device_addr["fpga"] : "usrp1_fpga.rbf" @@ -117,25 +118,26 @@ static device::sptr usrp1_make(const device_addr_t &device_addr)      std::vector<usb_device_handle::sptr> device_list =          usb_device_handle::get_device_list(USRP1_VENDOR_ID, USRP1_PRODUCT_ID); -    //create data and control transports -    usb_zero_copy::sptr data_transport; -    usrp_ctrl::sptr usrp_ctrl; - - -    BOOST_FOREACH(usb_device_handle::sptr handle, device_list) { -        if (handle->get_serial() == device_addr["serial"]) { -            usb_control::sptr ctrl_transport = usb_control::make(handle); -            usrp_ctrl = usrp_ctrl::make(ctrl_transport); -            usrp_ctrl->usrp_load_fpga(usrp1_fpga_image); - -            data_transport = usb_zero_copy::make(handle,        // identifier -                                                 6,             // IN endpoint -                                                 2,             // OUT endpoint -                                                 2 * (1 << 20), // buffer size -                                                 16384);        // transfer size +    //locate the matching handle in the device list +    usb_device_handle::sptr handle; +    BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) { +        if (dev_handle->get_serial() == device_addr["serial"]){ +            handle = dev_handle;              break;          }      } +    UHD_ASSERT_THROW(handle.get() != NULL); //better be found + +    //create control objects and a data transport +    usb_control::sptr ctrl_transport = usb_control::make(handle); +    usrp_ctrl::sptr usrp_ctrl = usrp_ctrl::make(ctrl_transport); +    usrp_ctrl->usrp_load_fpga(usrp1_fpga_image); +    usb_zero_copy::sptr data_transport = usb_zero_copy::make( +        handle,        // identifier +        6,             // IN endpoint +        2,             // OUT endpoint +        device_addr    // param hints +    );      //create the usrp1 implementation guts      return device::sptr(new usrp1_impl(data_transport, usrp_ctrl)); @@ -171,7 +173,7 @@ usrp1_impl::usrp1_impl(uhd::transport::usb_zero_copy::sptr data_transport,      //initialize the mboard      mboard_init(); -    //initialize the dboards  +    //initialize the dboards      dboard_init();      //initialize the dsps @@ -195,9 +197,9 @@ usrp1_impl::~usrp1_impl(void){      /* NOP */  } -bool usrp1_impl::recv_async_msg(uhd::async_metadata_t &, size_t timeout_ms){ +bool usrp1_impl::recv_async_msg(uhd::async_metadata_t &, double timeout){      //dummy fill-in for the recv_async_msg -    boost::this_thread::sleep(boost::posix_time::milliseconds(timeout_ms)); +    boost::this_thread::sleep(boost::posix_time::microseconds(long(timeout*1e6)));      return false;  } diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index c2f693eeb..ff4d40762 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -83,17 +83,18 @@ public:                  size_t,                  const uhd::tx_metadata_t &,                  const uhd::io_type_t &, -                send_mode_t); +                send_mode_t, double);      size_t recv(const std::vector<void *> &,                  size_t, uhd::rx_metadata_t &,                  const uhd::io_type_t &, -                recv_mode_t, -                size_t timeout); +                recv_mode_t, double); -    size_t get_max_send_samps_per_packet(void) const { return 0; } -    size_t get_max_recv_samps_per_packet(void) const { return 0; } -    bool recv_async_msg(uhd::async_metadata_t &, size_t); +    size_t get_max_send_samps_per_packet(void) const; + +    size_t get_max_recv_samps_per_packet(void) const; + +    bool recv_async_msg(uhd::async_metadata_t &, double);  private:      /*! @@ -179,19 +180,27 @@ private:      void rx_dsp_init(void);      void rx_dsp_get(const wax::obj &, wax::obj &);      void rx_dsp_set(const wax::obj &, const wax::obj &); -    double _rx_dsp_freq; size_t _rx_dsp_decim; +    uhd::dict<std::string, double> _rx_dsp_freqs; +    size_t _rx_dsp_decim;      wax_obj_proxy::sptr _rx_dsp_proxy;      //tx dsp functions and settings      void tx_dsp_init(void);      void tx_dsp_get(const wax::obj &, wax::obj &);      void tx_dsp_set(const wax::obj &, const wax::obj &); -    double _tx_dsp_freq; size_t _tx_dsp_interp; +    uhd::dict<std::string, double> _tx_dsp_freqs; +    size_t _tx_dsp_interp;      wax_obj_proxy::sptr _tx_dsp_proxy;      //transports      uhd::transport::usb_zero_copy::sptr _data_transport;      usrp_ctrl::sptr _ctrl_transport; + +    //capabilities +    size_t get_num_ducs(void); +    size_t get_num_ddcs(void); +    bool has_rx_halfband(void); +    bool has_tx_halfband(void);  };  #endif /* INCLUDED_USRP1_IMPL_HPP */ diff --git a/host/lib/usrp/usrp2/clock_ctrl.cpp b/host/lib/usrp/usrp2/clock_ctrl.cpp index 02227afad..1e1c9b7b8 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.cpp +++ b/host/lib/usrp/usrp2/clock_ctrl.cpp @@ -20,6 +20,7 @@  #include "usrp2_regs.hpp" //spi slave constants  #include <uhd/utils/assert.hpp>  #include <boost/cstdint.hpp> +#include <iostream>  using namespace uhd; @@ -67,12 +68,60 @@ public:          this->enable_dac_clock(true);          this->enable_adc_clock(true); +        /* always driving the mimo reference */ +        this->enable_mimo_clock_out(true);      }      ~usrp2_clock_ctrl_impl(void){ -        /* private clock enables, must be set here */ +        //power down clock outputs +        this->enable_external_ref(false); +        this->enable_rx_dboard_clock(false); +        this->enable_tx_dboard_clock(false);          this->enable_dac_clock(false);          this->enable_adc_clock(false); +        this->enable_mimo_clock_out(false); +    } + +    void enable_mimo_clock_out(bool enb){ +        //FIXME TODO put this revision read in a common place +        boost::uint8_t rev_hi = _iface->read_eeprom(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_REV_MSB, 1).at(0); + +        //calculate the low and high dividers +        size_t divider = size_t(this->get_master_clock_rate()/10e6); +        size_t high = divider/2; +        size_t low = divider - high; + +        switch(rev_hi){ +        case 3: //clock 2 +            _ad9510_regs.power_down_lvpecl_out2 = enb? +                ad9510_regs_t::POWER_DOWN_LVPECL_OUT2_NORMAL : +                ad9510_regs_t::POWER_DOWN_LVPECL_OUT2_SAFE_PD; +            _ad9510_regs.output_level_lvpecl_out2 = ad9510_regs_t::OUTPUT_LEVEL_LVPECL_OUT2_810MV; +            //set the registers (divider - 1) +            _ad9510_regs.divider_low_cycles_out2 = low - 1; +            _ad9510_regs.divider_high_cycles_out2 = high - 1; +            _ad9510_regs.bypass_divider_out2 = 0; +            this->write_reg(0x3e); +            this->write_reg(0x4c); +            break; + +        case 4: //clock 5 +            _ad9510_regs.power_down_lvds_cmos_out5 = enb? 0 : 1; +            _ad9510_regs.lvds_cmos_select_out5 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT5_LVDS; +            _ad9510_regs.output_level_lvds_out5 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT5_1_75MA; +            //set the registers (divider - 1) +            _ad9510_regs.divider_low_cycles_out5 = low - 1; +            _ad9510_regs.divider_high_cycles_out5 = high - 1; +            _ad9510_regs.bypass_divider_out5 = 0; +            this->write_reg(0x41); +            this->write_reg(0x52); +            break; + +        //TODO FIXME do i want to throw, what about uninitialized boards? +        //default: throw std::runtime_error("unknown rev hi in mboard eeprom"); +        default: std::cerr << "unknown rev hi: " << rev_hi << std::endl; +        } +        this->update_regs();      }      //uses output clock 7 (cmos) diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index f6d2b718a..fdfbf0d17 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -61,6 +61,7 @@ public:      double get_clock_rate(unit_t);      std::vector<double> get_clock_rates(unit_t);      void set_clock_enabled(unit_t, bool); +    double get_codec_rate(unit_t);      void write_spi(          unit_t unit, @@ -158,6 +159,9 @@ void usrp2_dboard_iface::set_clock_enabled(unit_t unit, bool enb){      }  } +double usrp2_dboard_iface::get_codec_rate(unit_t){ +    return _clock_ctrl->get_master_clock_rate(); +}  /***********************************************************************   * GPIO   **********************************************************************/ diff --git a/host/lib/usrp/usrp2/dsp_impl.cpp b/host/lib/usrp/usrp2/dsp_impl.cpp index 6422142ce..0c85e643f 100644 --- a/host/lib/usrp/usrp2/dsp_impl.cpp +++ b/host/lib/usrp/usrp2/dsp_impl.cpp @@ -56,7 +56,9 @@ void usrp2_mboard_impl::init_ddc_config(void){  /***********************************************************************   * DDC Properties   **********************************************************************/ -void usrp2_mboard_impl::ddc_get(const wax::obj &key, wax::obj &val){ +void usrp2_mboard_impl::ddc_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()){      case DSP_PROP_NAME:          val = std::string("usrp2 ddc0"); @@ -70,6 +72,10 @@ void usrp2_mboard_impl::ddc_get(const wax::obj &key, wax::obj &val){          val = _ddc_freq;          return; +    case DSP_PROP_FREQ_SHIFT_NAMES: +        val = prop_names_t(1, ""); +        return; +      case DSP_PROP_CODEC_RATE:          val = get_master_clock_freq();          return; @@ -82,7 +88,9 @@ void usrp2_mboard_impl::ddc_get(const wax::obj &key, wax::obj &val){      }  } -void usrp2_mboard_impl::ddc_set(const wax::obj &key, const wax::obj &val){ +void usrp2_mboard_impl::ddc_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()){      case DSP_PROP_FREQ_SHIFT:{ @@ -131,7 +139,9 @@ void usrp2_mboard_impl::init_duc_config(void){  /***********************************************************************   * DUC Properties   **********************************************************************/ -void usrp2_mboard_impl::duc_get(const wax::obj &key, wax::obj &val){ +void usrp2_mboard_impl::duc_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()){      case DSP_PROP_NAME:          val = std::string("usrp2 duc0"); @@ -145,6 +155,10 @@ void usrp2_mboard_impl::duc_get(const wax::obj &key, wax::obj &val){          val = _duc_freq;          return; +    case DSP_PROP_FREQ_SHIFT_NAMES: +        val = prop_names_t(1, ""); +        return; +      case DSP_PROP_CODEC_RATE:          val = get_master_clock_freq();          return; @@ -157,7 +171,9 @@ void usrp2_mboard_impl::duc_get(const wax::obj &key, wax::obj &val){      }  } -void usrp2_mboard_impl::duc_set(const wax::obj &key, const wax::obj &val){ +void usrp2_mboard_impl::duc_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()){      case DSP_PROP_FREQ_SHIFT:{ diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 4e883cf81..bbe9c273f 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -46,7 +46,7 @@ struct usrp2_impl::io_impl{      io_impl(size_t num_frames, size_t width):          packet_handler_recv_state(width), -        recv_pirate_booty(alignment_buffer_type::make(num_frames, width)), +        recv_pirate_booty(alignment_buffer_type::make(num_frames-3, width)),          async_msg_fifo(bounded_buffer<async_metadata_t>::make(100/*messages deep*/))      {          /* NOP */ @@ -58,9 +58,9 @@ struct usrp2_impl::io_impl{          recv_pirate_crew.join_all();      } -    bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs, size_t timeout_ms){ +    bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs, double timeout){          boost::this_thread::disable_interruption di; //disable because the wait can throw -        return recv_pirate_booty->pop_elems_with_timed_wait(buffs, boost::posix_time::milliseconds(timeout_ms)); +        return recv_pirate_booty->pop_elems_with_timed_wait(buffs, timeout);      }      //state management for the vrt packet handler code @@ -150,7 +150,7 @@ void usrp2_impl::io_init(void){          std::memcpy(send_buff->cast<void*>(), &data, sizeof(data));          send_buff->commit(sizeof(data));          //drain the recv buffers (may have junk) -        while (data_transport->get_recv_buff().get()); +        while (data_transport->get_recv_buff().get()){};      }      //the number of recv frames is the number for the first transport @@ -168,51 +168,57 @@ void usrp2_impl::io_init(void){              _mboards.at(i), i          ));      } - -    std::cout << "RX samples per packet: " << get_max_recv_samps_per_packet() << std::endl; -    std::cout << "TX samples per packet: " << get_max_send_samps_per_packet() << std::endl; -    std::cout << "Recv pirate num frames: " << num_frames << std::endl;  }  /***********************************************************************   * Async Data   **********************************************************************/  bool usrp2_impl::recv_async_msg( -    async_metadata_t &async_metadata, size_t timeout_ms +    async_metadata_t &async_metadata, double timeout  ){      boost::this_thread::disable_interruption di; //disable because the wait can throw -    return _io_impl->async_msg_fifo->pop_with_timed_wait( -        async_metadata, boost::posix_time::milliseconds(timeout_ms) -    ); +    return _io_impl->async_msg_fifo->pop_with_timed_wait(async_metadata, timeout);  }  /***********************************************************************   * Send Data   **********************************************************************/ -bool get_send_buffs( +static bool get_send_buffs(      const std::vector<udp_zero_copy::sptr> &trans, -    vrt_packet_handler::managed_send_buffs_t &buffs +    vrt_packet_handler::managed_send_buffs_t &buffs, +    double timeout  ){      UHD_ASSERT_THROW(trans.size() == buffs.size()); +    bool good = true;      for (size_t i = 0; i < buffs.size(); i++){ -        buffs[i] = trans[i]->get_send_buff(); +        buffs[i] = trans[i]->get_send_buff(timeout); +        good = good and (buffs[i].get() != NULL);      } -    return true; +    return good; +} + +size_t usrp2_impl::get_max_send_samps_per_packet(void) const{ +    static const size_t hdr_size = 0 +        + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) +        - sizeof(vrt::if_packet_info_t().cid) //no class id ever used +    ; +    const size_t bpp = _data_transports.front()->get_send_frame_size() - hdr_size; +    return bpp/_tx_otw_type.get_sample_size();  }  size_t usrp2_impl::send(      const std::vector<const void *> &buffs, size_t num_samps,      const tx_metadata_t &metadata, const io_type_t &io_type, -    send_mode_t send_mode +    send_mode_t send_mode, double timeout  ){      return vrt_packet_handler::send(          _io_impl->packet_handler_send_state,       //last state of the send handler          buffs, num_samps,                          //buffer to fill          metadata, send_mode,                       //samples metadata -        io_type, _io_helper.get_tx_otw_type(),     //input and output types to convert +        io_type, _tx_otw_type,                     //input and output types to convert          _mboards.front()->get_master_clock_freq(), //master clock tick rate          uhd::transport::vrt::if_hdr_pack_be, -        boost::bind(&get_send_buffs, _data_transports, _1), +        boost::bind(&get_send_buffs, _data_transports, _1, timeout),          get_max_send_samps_per_packet()      );  } @@ -220,18 +226,28 @@ size_t usrp2_impl::send(  /***********************************************************************   * Receive Data   **********************************************************************/ +size_t usrp2_impl::get_max_recv_samps_per_packet(void) const{ +    static const size_t hdr_size = 0 +        + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) +        + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer +        - sizeof(vrt::if_packet_info_t().cid) //no class id ever used +    ; +    const size_t bpp = _data_transports.front()->get_recv_frame_size() - hdr_size; +    return bpp/_rx_otw_type.get_sample_size(); +} +  size_t usrp2_impl::recv(      const std::vector<void *> &buffs, size_t num_samps,      rx_metadata_t &metadata, const io_type_t &io_type, -    recv_mode_t recv_mode, size_t timeout_ms +    recv_mode_t recv_mode, double timeout  ){      return vrt_packet_handler::recv(          _io_impl->packet_handler_recv_state,       //last state of the recv handler          buffs, num_samps,                          //buffer to fill          metadata, recv_mode,                       //samples metadata -        io_type, _io_helper.get_rx_otw_type(),     //input and output types to convert +        io_type, _rx_otw_type,                     //input and output types to convert          _mboards.front()->get_master_clock_freq(), //master clock tick rate          uhd::transport::vrt::if_hdr_unpack_be, -        boost::bind(&usrp2_impl::io_impl::get_recv_buffs, _io_impl.get(), _1, timeout_ms) +        boost::bind(&usrp2_impl::io_impl::get_recv_buffs, _io_impl.get(), _1, timeout)      );  } diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index 0b9f8ee83..a0e6adfad 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -38,10 +38,10 @@ using namespace uhd::usrp;  usrp2_mboard_impl::usrp2_mboard_impl(      size_t index,      transport::udp_simple::sptr ctrl_transport, -    const usrp2_io_helper &io_helper +    size_t recv_frame_size  ):      _index(index), -    _io_helper(io_helper) +    _recv_frame_size(recv_frame_size)  {      //make a new interface for usrp2 stuff      _iface = usrp2_iface::make(ctrl_transport); @@ -75,7 +75,7 @@ usrp2_mboard_impl::usrp2_mboard_impl(      this->issue_ddc_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);      //init the rx control registers -    _iface->poke32(U2_REG_RX_CTRL_NSAMPS_PER_PKT, _io_helper.get_max_recv_samps_per_packet()); +    _iface->poke32(U2_REG_RX_CTRL_NSAMPS_PER_PKT, _recv_frame_size);      _iface->poke32(U2_REG_RX_CTRL_NCHANNELS, 1);      _iface->poke32(U2_REG_RX_CTRL_CLEAR_OVERRUN, 1); //reset      _iface->poke32(U2_REG_RX_CTRL_VRT_HEADER, 0 @@ -178,7 +178,7 @@ void usrp2_mboard_impl::set_time_spec(const time_spec_t &time_spec, bool now){  void usrp2_mboard_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){      _iface->poke32(U2_REG_RX_CTRL_STREAM_CMD, dsp_type1::calc_stream_cmd_word( -        stream_cmd, _io_helper.get_max_recv_samps_per_packet() +        stream_cmd, _recv_frame_size      ));      _iface->poke32(U2_REG_RX_CTRL_TIME_SECS,  boost::uint32_t(stream_cmd.time_spec.get_full_secs()));      _iface->poke32(U2_REG_RX_CTRL_TIME_TICKS, stream_cmd.time_spec.get_tick_count(get_master_clock_freq())); diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 568c87a22..a680708ad 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -124,26 +124,7 @@ static uhd::device_addrs_t usrp2_find(const device_addr_t &hint){  /***********************************************************************   * Make   **********************************************************************/ -template <typename out_type, typename in_type> -out_type lexical_cast(const in_type &in){ -    try{ -        return boost::lexical_cast<out_type>(in); -    }catch(...){ -        throw std::runtime_error(str(boost::format( -            "failed to cast \"%s\" to type \"%s\"" -        ) % boost::lexical_cast<std::string>(in) % typeid(out_type).name())); -    } -} -  static device::sptr usrp2_make(const device_addr_t &device_addr){ -    //extract the receive and send buffer sizes -    size_t recv_buff_size = 0, send_buff_size= 0 ; -    if (device_addr.has_key("recv_buff_size")){ -        recv_buff_size = size_t(lexical_cast<double>(device_addr["recv_buff_size"])); -    } -    if (device_addr.has_key("send_buff_size")){ -        send_buff_size = size_t(lexical_cast<double>(device_addr["send_buff_size"])); -    }      //create a ctrl and data transport for each address      std::vector<udp_simple::sptr> ctrl_transports; @@ -154,8 +135,7 @@ static device::sptr usrp2_make(const device_addr_t &device_addr){              addr, num2str(USRP2_UDP_CTRL_PORT)          ));          data_transports.push_back(udp_zero_copy::make( -            addr, num2str(USRP2_UDP_DATA_PORT), -            recv_buff_size, send_buff_size +            addr, num2str(USRP2_UDP_DATA_PORT), device_addr          ));      } @@ -178,11 +158,23 @@ usrp2_impl::usrp2_impl(  ):      _data_transports(data_transports)  { +    //setup rx otw type +    _rx_otw_type.width = 16; +    _rx_otw_type.shift = 0; +    _rx_otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; + +    //setup tx otw type +    _tx_otw_type.width = 16; +    _tx_otw_type.shift = 0; +    _tx_otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; + +    //!!!!! set the otw type here before continuing, its used below +      //create a new mboard handler for each control transport      for(size_t i = 0; i < ctrl_transports.size(); i++){ -        _mboards.push_back(usrp2_mboard_impl::sptr( -            new usrp2_mboard_impl(i, ctrl_transports[i], _io_helper) -        )); +        _mboards.push_back(usrp2_mboard_impl::sptr(new usrp2_mboard_impl( +            i, ctrl_transports[i], this->get_max_recv_samps_per_packet() +        )));          //use an empty name when there is only one mboard          std::string name = (ctrl_transports.size() > 1)? boost::lexical_cast<std::string>(i) : "";          _mboard_dict[name] = _mboards.back(); diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 157d17057..558726a2b 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -72,54 +72,6 @@ private:  };  /*! - * The io helper class encapculates the max packet sizes and otw types. - * The otw types are read-only for now, this will be reimplemented - * when it becomes possible to change the otw type in the usrp2. - */ -class usrp2_io_helper{ -public: -    usrp2_io_helper(void){ -        //setup rx otw type -        _rx_otw_type.width = 16; -        _rx_otw_type.shift = 0; -        _rx_otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; - -        //setup tx otw type -        _tx_otw_type.width = 16; -        _tx_otw_type.shift = 0; -        _tx_otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; -    } - -    inline size_t get_max_send_samps_per_packet(void) const{ -        return _max_tx_bytes_per_packet/_tx_otw_type.get_sample_size(); -    } - -    inline size_t get_max_recv_samps_per_packet(void) const{ -        return _max_rx_bytes_per_packet/_rx_otw_type.get_sample_size(); -    } - -    inline const uhd::otw_type_t &get_rx_otw_type(void) const{ -        return _rx_otw_type; -    } - -    inline const uhd::otw_type_t &get_tx_otw_type(void) const{ -        return _tx_otw_type; -    } - -private: -    uhd::otw_type_t _rx_otw_type, _tx_otw_type; -    static const size_t _max_rx_bytes_per_packet = uhd::transport::udp_simple::mtu -        - uhd::transport::vrt::max_if_hdr_words32*sizeof(boost::uint32_t) -        - sizeof(uhd::transport::vrt::if_packet_info_t().tlr) //forced to have trailer -        + sizeof(uhd::transport::vrt::if_packet_info_t().cid) //no class id ever used -    ; -    static const size_t _max_tx_bytes_per_packet = uhd::transport::udp_simple::mtu -        - uhd::transport::vrt::max_if_hdr_words32*sizeof(boost::uint32_t) -        + sizeof(uhd::transport::vrt::if_packet_info_t().cid) //no class id ever used -    ; -}; - -/*!   * USRP2 mboard implementation guts:   * The implementation details are encapsulated here.   * Handles properties on the mboard, dboard, dsps... @@ -129,7 +81,11 @@ public:      typedef boost::shared_ptr<usrp2_mboard_impl> sptr;      //structors -    usrp2_mboard_impl(size_t index, uhd::transport::udp_simple::sptr, const usrp2_io_helper &); +    usrp2_mboard_impl( +        size_t index, +        uhd::transport::udp_simple::sptr, +        size_t recv_frame_size +    );      ~usrp2_mboard_impl(void);      inline double get_master_clock_freq(void){ @@ -139,7 +95,7 @@ public:  private:      size_t _index;      int _rev_hi, _rev_lo; -    const usrp2_io_helper &_io_helper; +    const size_t _recv_frame_size;      //properties for this mboard      void get(const wax::obj &, wax::obj &); @@ -228,23 +184,19 @@ public:      ~usrp2_impl(void);      //the io interface -    size_t get_max_send_samps_per_packet(void) const{ -        return _io_helper.get_max_send_samps_per_packet(); -    }      size_t send(          const std::vector<const void *> &, size_t,          const uhd::tx_metadata_t &, const uhd::io_type_t &, -        uhd::device::send_mode_t +        uhd::device::send_mode_t, double      ); -    size_t get_max_recv_samps_per_packet(void) const{ -        return _io_helper.get_max_recv_samps_per_packet(); -    }      size_t recv(          const std::vector<void *> &, size_t,          uhd::rx_metadata_t &, const uhd::io_type_t &, -        uhd::device::recv_mode_t, size_t +        uhd::device::recv_mode_t, double      ); -    bool recv_async_msg(uhd::async_metadata_t &, size_t); +    size_t get_max_send_samps_per_packet(void) const; +    size_t get_max_recv_samps_per_packet(void) const; +    bool recv_async_msg(uhd::async_metadata_t &, double);  private:      //device properties interface @@ -257,7 +209,7 @@ private:      //io impl methods and members      std::vector<uhd::transport::udp_zero_copy::sptr> _data_transports; -    const usrp2_io_helper _io_helper; +    uhd::otw_type_t _rx_otw_type, _tx_otw_type;      UHD_PIMPL_DECL(io_impl) _io_impl;      void io_init(void);  }; diff --git a/host/lib/usrp/usrp_e/CMakeLists.txt b/host/lib/usrp/usrp_e/CMakeLists.txt index da759d931..bab868f90 100644 --- a/host/lib/usrp/usrp_e/CMakeLists.txt +++ b/host/lib/usrp/usrp_e/CMakeLists.txt @@ -54,6 +54,7 @@ IF(ENABLE_USRP_E)          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_impl.hpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_iface.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_iface.hpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_mmap_zero_copy.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_regs.hpp      )  ELSE(ENABLE_USRP_E) diff --git a/host/lib/usrp/usrp_e/dboard_iface.cpp b/host/lib/usrp/usrp_e/dboard_iface.cpp index 1bd177f60..6898df8df 100644 --- a/host/lib/usrp/usrp_e/dboard_iface.cpp +++ b/host/lib/usrp/usrp_e/dboard_iface.cpp @@ -91,6 +91,7 @@ public:      std::vector<double> get_clock_rates(unit_t);      double get_clock_rate(unit_t);      void set_clock_enabled(unit_t, bool); +    double get_codec_rate(unit_t);  private:      usrp_e_iface::sptr _iface; @@ -140,6 +141,10 @@ void usrp_e_dboard_iface::set_clock_enabled(unit_t unit, bool enb){      }  } +double usrp_e_dboard_iface::get_codec_rate(unit_t){ +    return _clock->get_fpga_clock_rate(); +} +  /***********************************************************************   * GPIO   **********************************************************************/ diff --git a/host/lib/usrp/usrp_e/dsp_impl.cpp b/host/lib/usrp/usrp_e/dsp_impl.cpp index c133eafae..9312bb603 100644 --- a/host/lib/usrp/usrp_e/dsp_impl.cpp +++ b/host/lib/usrp/usrp_e/dsp_impl.cpp @@ -44,7 +44,9 @@ void usrp_e_impl::rx_ddc_init(void){  /***********************************************************************   * RX DDC Get   **********************************************************************/ -void usrp_e_impl::rx_ddc_get(const wax::obj &key, wax::obj &val){ +void usrp_e_impl::rx_ddc_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()){      case DSP_PROP_NAME:          val = std::string("usrp-e ddc0"); @@ -58,6 +60,10 @@ void usrp_e_impl::rx_ddc_get(const wax::obj &key, wax::obj &val){          val = _ddc_freq;          return; +    case DSP_PROP_FREQ_SHIFT_NAMES: +        val = prop_names_t(1, ""); +        return; +      case DSP_PROP_CODEC_RATE:          val = MASTER_CLOCK_RATE;          return; @@ -73,7 +79,9 @@ void usrp_e_impl::rx_ddc_get(const wax::obj &key, wax::obj &val){  /***********************************************************************   * RX DDC Set   **********************************************************************/ -void usrp_e_impl::rx_ddc_set(const wax::obj &key, const wax::obj &val){ +void usrp_e_impl::rx_ddc_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()){      case DSP_PROP_FREQ_SHIFT:{ @@ -119,7 +127,9 @@ void usrp_e_impl::tx_duc_init(void){  /***********************************************************************   * TX DUC Get   **********************************************************************/ -void usrp_e_impl::tx_duc_get(const wax::obj &key, wax::obj &val){ +void usrp_e_impl::tx_duc_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()){      case DSP_PROP_NAME:          val = std::string("usrp-e duc0"); @@ -133,6 +143,10 @@ void usrp_e_impl::tx_duc_get(const wax::obj &key, wax::obj &val){          val = _duc_freq;          return; +    case DSP_PROP_FREQ_SHIFT_NAMES: +        val = prop_names_t(1, ""); +        return; +      case DSP_PROP_CODEC_RATE:          val = MASTER_CLOCK_RATE;          return; @@ -148,7 +162,9 @@ void usrp_e_impl::tx_duc_get(const wax::obj &key, wax::obj &val){  /***********************************************************************   * TX DUC Set   **********************************************************************/ -void usrp_e_impl::tx_duc_set(const wax::obj &key, const wax::obj &val){ +void usrp_e_impl::tx_duc_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()){      case DSP_PROP_FREQ_SHIFT:{ diff --git a/host/lib/usrp/usrp_e/io_impl.cpp b/host/lib/usrp/usrp_e/io_impl.cpp index 31ea4c6c0..d89a7db07 100644 --- a/host/lib/usrp/usrp_e/io_impl.cpp +++ b/host/lib/usrp/usrp_e/io_impl.cpp @@ -22,8 +22,6 @@  #include <uhd/transport/bounded_buffer.hpp>  #include "../../transport/vrt_packet_handler.hpp"  #include <boost/bind.hpp> -#include <fcntl.h> //read, write -#include <poll.h>  #include <boost/format.hpp>  #include <boost/thread.hpp>  #include <iostream> @@ -32,88 +30,14 @@ using namespace uhd;  using namespace uhd::usrp;  using namespace uhd::transport; +zero_copy_if::sptr usrp_e_make_mmap_zero_copy(usrp_e_iface::sptr iface); +  /***********************************************************************   * Constants   **********************************************************************/ -static const size_t MAX_BUFF_SIZE = 2048; -static const bool usrp_e_io_impl_verbose = false;  static const size_t tx_async_report_sid = 1;  static const int underflow_flags = async_metadata_t::EVENT_CODE_UNDERFLOW | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET; - -/*********************************************************************** - * Data Transport (phony zero-copy with read/write) - **********************************************************************/ -class data_transport: -    public transport::phony_zero_copy_recv_if, -    public transport::phony_zero_copy_send_if -{ -public: -    data_transport(int fd): -        transport::phony_zero_copy_recv_if(MAX_BUFF_SIZE), -        transport::phony_zero_copy_send_if(MAX_BUFF_SIZE), -        _fd(fd) -    { -        /* NOP */ -    } - -    size_t get_num_recv_frames(void) const{ -        return 100; //FIXME no idea! -        //this will be an important number when packet ring gets implemented -    } - -    size_t get_num_send_frames(void) const{ -        return 100; //FIXME no idea! -        //this will be an important number when packet ring gets implemented -    } - -private: -    int _fd; -    ssize_t send(const boost::asio::const_buffer &buff){ -        return write(_fd, -            boost::asio::buffer_cast<const void *>(buff), -            boost::asio::buffer_size(buff) -        ); -    } -    ssize_t recv(const boost::asio::mutable_buffer &buff){ -        //std::cout << boost::format( -        //    "calling read on fd %d, buff size is %d" -        //) % _fd % boost::asio::buffer_size(buff) << std::endl; - -        //setup and call poll on the file descriptor -        //return 0 and do not read when poll times out -        pollfd pfd; -        pfd.fd = _fd; -        pfd.events = POLLIN; -        ssize_t poll_ret = poll(&pfd, 1, 100/*ms*/); -        if (poll_ret <= 0){ -            if (usrp_e_io_impl_verbose) std::cerr << boost::format( -                "usrp-e io impl recv(): poll() returned non-positive value: %d\n" -                "    -> return 0 for timeout" -            ) % poll_ret << std::endl; -            return 0; //timeout -        } - -        //perform the blocking read(...) -        ssize_t read_ret = read(_fd, -            boost::asio::buffer_cast<void *>(buff), -            boost::asio::buffer_size(buff) -        ); -        if (read_ret < 0){ -            if (usrp_e_io_impl_verbose) std::cerr << boost::format( -                "usrp-e io impl recv(): read() returned small value: %d\n" -                "    -> return -1 for error" -            ) % read_ret << std::endl; -            return -1; -        } - -        //std::cout << "len " << int(read_ret) << std::endl; -        //for (size_t i = 0; i < 9; i++){ -        //    std::cout << boost::format("    0x%08x") % boost::asio::buffer_cast<boost::uint32_t *>(buff)[i] << std::endl; -        //} - -        return read_ret; -    } -}; +static const bool recv_debug = false;  /***********************************************************************   * io impl details (internal to this file) @@ -126,11 +50,11 @@ struct usrp_e_impl::io_impl{      //state management for the vrt packet handler code      vrt_packet_handler::recv_state packet_handler_recv_state;      vrt_packet_handler::send_state packet_handler_send_state; -    data_transport transport; +    zero_copy_if::sptr data_xport;      bool continuous_streaming; -    io_impl(int fd): -        transport(fd), -        recv_pirate_booty(recv_booty_type::make(transport.get_num_recv_frames())), +    io_impl(usrp_e_iface::sptr iface): +        data_xport(usrp_e_make_mmap_zero_copy(iface)), +        recv_pirate_booty(recv_booty_type::make(data_xport->get_num_recv_frames())),          async_msg_fifo(bounded_buffer<async_metadata_t>::make(100/*messages deep*/))      {          /* NOP */ @@ -142,10 +66,10 @@ struct usrp_e_impl::io_impl{          recv_pirate_crew.join_all();      } -    bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs, size_t timeout_ms){ +    bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs, double timeout){          UHD_ASSERT_THROW(buffs.size() == 1);          boost::this_thread::disable_interruption di; //disable because the wait can throw -        return recv_pirate_booty->pop_with_timed_wait(buffs.front(), boost::posix_time::milliseconds(timeout_ms)); +        return recv_pirate_booty->pop_with_timed_wait(buffs.front(), timeout);      }      //a pirate's life is the life for me! @@ -167,12 +91,19 @@ void usrp_e_impl::io_impl::recv_pirate_loop(  ){      set_thread_priority_safe();      recv_pirate_crew_raiding = true; -    //size_t next_packet_seq = 0;      while(recv_pirate_crew_raiding){ -        managed_recv_buffer::sptr buff = this->transport.get_recv_buff(); +        managed_recv_buffer::sptr buff = this->data_xport->get_recv_buff();          if (not buff.get()) continue; //ignore timeout/error buffers +        if (recv_debug){ +            std::cout << "len " << buff->size() << std::endl; +            for (size_t i = 0; i < 9; i++){ +                std::cout << boost::format("    0x%08x") % buff->cast<const boost::uint32_t *>()[i] << std::endl; +            } +            std::cout << std::endl << std::endl; +        } +          try{              //extract the vrt header packet info              vrt::if_packet_info_t if_packet_info; @@ -198,12 +129,12 @@ void usrp_e_impl::io_impl::recv_pirate_loop(                  continue;              } +            //same number of frames as the data transport -> always immediate +            recv_pirate_booty->push_with_wait(buff); +          }catch(const std::exception &e){              std::cerr << "Error (usrp-e recv pirate loop): " << e.what() << std::endl;          } - -        //usrp-e back-pressures on receive: push with wait -        recv_pirate_booty->push_with_wait(buff);      }  } @@ -211,6 +142,15 @@ void usrp_e_impl::io_impl::recv_pirate_loop(   * Helper Functions   **********************************************************************/  void usrp_e_impl::io_init(void){ +    //setup otw types +    _send_otw_type.width = 16; +    _send_otw_type.shift = 0; +    _send_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; + +    _recv_otw_type.width = 16; +    _recv_otw_type.shift = 0; +    _recv_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; +      //setup rx data path      _iface->poke32(UE_REG_CTRL_RX_NSAMPS_PER_PKT, get_max_recv_samps_per_packet());      _iface->poke32(UE_REG_CTRL_RX_NCHANNELS, 1); @@ -228,7 +168,7 @@ void usrp_e_impl::io_init(void){      _iface->poke32(UE_REG_CTRL_TX_REPORT_SID, tx_async_report_sid);      _iface->poke32(UE_REG_CTRL_TX_POLICY, UE_FLAG_CTRL_TX_POLICY_NEXT_PACKET); -    _io_impl = UHD_PIMPL_MAKE(io_impl, (_iface->get_file_descriptor())); +    _io_impl = UHD_PIMPL_MAKE(io_impl, (_iface));      //spawn a pirate, yarrr!      _io_impl->recv_pirate_crew.create_thread(boost::bind( @@ -257,34 +197,38 @@ void usrp_e_impl::handle_overrun(size_t){   * Data Send   **********************************************************************/  bool get_send_buffs( -    data_transport *trans, +    zero_copy_if::sptr trans, double timeout,      vrt_packet_handler::managed_send_buffs_t &buffs  ){      UHD_ASSERT_THROW(buffs.size() == 1); -    buffs[0] = trans->get_send_buff(); -    return buffs[0].get(); +    buffs[0] = trans->get_send_buff(timeout); +    return buffs[0].get() != NULL; +} + +#if 0 +size_t usrp_e_impl::get_max_send_samps_per_packet(void) const{ +    static const size_t hdr_size = 0 +        + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) +        - sizeof(vrt::if_packet_info_t().cid) //no class id ever used +    ; +    size_t bpp = _io_impl->data_xport->get_send_frame_size() - hdr_size; +    return bpp/_send_otw_type.get_sample_size();  } +#endif  size_t usrp_e_impl::send( -    const std::vector<const void *> &buffs, -    size_t num_samps, -    const tx_metadata_t &metadata, -    const io_type_t &io_type, -    send_mode_t send_mode +    const std::vector<const void *> &buffs, size_t num_samps, +    const tx_metadata_t &metadata, const io_type_t &io_type, +    send_mode_t send_mode, double timeout  ){ -    otw_type_t send_otw_type; -    send_otw_type.width = 16; -    send_otw_type.shift = 0; -    send_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; -      return vrt_packet_handler::send(          _io_impl->packet_handler_send_state,       //last state of the send handler          buffs, num_samps,                          //buffer to fill          metadata, send_mode,                       //samples metadata -        io_type, send_otw_type,                    //input and output types to convert +        io_type, _send_otw_type,                   //input and output types to convert          MASTER_CLOCK_RATE,                         //master clock tick rate          uhd::transport::vrt::if_hdr_pack_le, -        boost::bind(&get_send_buffs, &_io_impl->transport, _1), +        boost::bind(&get_send_buffs, _io_impl->data_xport, timeout, _1),          get_max_send_samps_per_packet()      );  } @@ -292,36 +236,31 @@ size_t usrp_e_impl::send(  /***********************************************************************   * Data Recv   **********************************************************************/ -bool get_recv_buffs( -    data_transport *trans, -    vrt_packet_handler::managed_recv_buffs_t &buffs -){ -    UHD_ASSERT_THROW(buffs.size() == 1); -    buffs[0] = trans->get_recv_buff(); -    return buffs[0].get(); +#if 0 +size_t usrp_e_impl::get_max_recv_samps_per_packet(void) const{ +    static const size_t hdr_size = 0 +        + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) +        + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer +        - sizeof(vrt::if_packet_info_t().cid) //no class id ever used +    ; +    size_t bpp = _io_impl->data_xport->get_recv_frame_size() - hdr_size; +    return bpp/_recv_otw_type.get_sample_size();  } +#endif  size_t usrp_e_impl::recv( -    const std::vector<void *> &buffs, -    size_t num_samps, -    rx_metadata_t &metadata, -    const io_type_t &io_type, -    recv_mode_t recv_mode, -    size_t timeout_ms +    const std::vector<void *> &buffs, size_t num_samps, +    rx_metadata_t &metadata, const io_type_t &io_type, +    recv_mode_t recv_mode, double timeout  ){ -    otw_type_t recv_otw_type; -    recv_otw_type.width = 16; -    recv_otw_type.shift = 0; -    recv_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; -      return vrt_packet_handler::recv(          _io_impl->packet_handler_recv_state,       //last state of the recv handler          buffs, num_samps,                          //buffer to fill          metadata, recv_mode,                       //samples metadata -        io_type, recv_otw_type,                    //input and output types to convert +        io_type, _recv_otw_type,                   //input and output types to convert          MASTER_CLOCK_RATE,                         //master clock tick rate          uhd::transport::vrt::if_hdr_unpack_le, -        boost::bind(&usrp_e_impl::io_impl::get_recv_buffs, _io_impl.get(), _1, timeout_ms), +        boost::bind(&usrp_e_impl::io_impl::get_recv_buffs, _io_impl.get(), _1, timeout),          boost::bind(&usrp_e_impl::handle_overrun, this, _1)      );  } @@ -330,11 +269,8 @@ size_t usrp_e_impl::recv(   * Async Recv   **********************************************************************/  bool usrp_e_impl::recv_async_msg( -    async_metadata_t &async_metadata, -    size_t timeout_ms +    async_metadata_t &async_metadata, double timeout  ){      boost::this_thread::disable_interruption di; //disable because the wait can throw -    return _io_impl->async_msg_fifo->pop_with_timed_wait( -        async_metadata, boost::posix_time::milliseconds(timeout_ms) -    ); +    return _io_impl->async_msg_fifo->pop_with_timed_wait(async_metadata, timeout);  } diff --git a/host/lib/usrp/usrp_e/usrp_e_impl.hpp b/host/lib/usrp/usrp_e/usrp_e_impl.hpp index 2457e27cc..9799cd645 100644 --- a/host/lib/usrp/usrp_e/usrp_e_impl.hpp +++ b/host/lib/usrp/usrp_e/usrp_e_impl.hpp @@ -22,6 +22,7 @@  #include <uhd/utils/pimpl.hpp>  #include <uhd/usrp/subdev_spec.hpp>  #include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/types/otw_type.hpp>  #include <uhd/types/clock_config.hpp>  #include <uhd/types/stream_cmd.hpp>  #include <uhd/usrp/dboard_manager.hpp> @@ -82,11 +83,16 @@ public:      ~usrp_e_impl(void);      //the io interface -    size_t send(const std::vector<const void *> &, size_t, const uhd::tx_metadata_t &, const uhd::io_type_t &, send_mode_t); -    size_t recv(const std::vector<void *> &, size_t, uhd::rx_metadata_t &, const uhd::io_type_t &, recv_mode_t, size_t); -    bool recv_async_msg(uhd::async_metadata_t &, size_t); +    size_t send(const std::vector<const void *> &, size_t, const uhd::tx_metadata_t &, const uhd::io_type_t &, send_mode_t, double); +    size_t recv(const std::vector<void *> &, size_t, uhd::rx_metadata_t &, const uhd::io_type_t &, recv_mode_t, double); +    bool recv_async_msg(uhd::async_metadata_t &, double); +#if 0 +    size_t get_max_send_samps_per_packet(void) const; +    size_t get_max_recv_samps_per_packet(void) const; +#else      size_t get_max_send_samps_per_packet(void) const{return 503;}      size_t get_max_recv_samps_per_packet(void) const{return 503;} +#endif  private:      //interface to ioctls and file descriptor @@ -97,6 +103,7 @@ private:      //handle io stuff      UHD_PIMPL_DECL(io_impl) _io_impl; +    uhd::otw_type_t _send_otw_type, _recv_otw_type;      void io_init(void);      void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd);      void handle_overrun(size_t); diff --git a/host/lib/usrp/usrp_e/usrp_e_mmap_zero_copy.cpp b/host/lib/usrp/usrp_e/usrp_e_mmap_zero_copy.cpp new file mode 100644 index 000000000..274bb043e --- /dev/null +++ b/host/lib/usrp/usrp_e/usrp_e_mmap_zero_copy.cpp @@ -0,0 +1,215 @@ +// +// 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 "usrp_e_iface.hpp" +#include <uhd/transport/zero_copy.hpp> +#include <uhd/utils/assert.hpp> +#include <linux/usrp_e.h> +#include <sys/mman.h> //mmap +#include <unistd.h> //getpagesize +#include <poll.h> //poll +#include <boost/bind.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <iostream> + +using namespace uhd; +using namespace uhd::transport; + +static const bool fp_verbose = false; //fast-path verbose +static const bool sp_verbose = false; //slow-path verbose +static const size_t poll_breakout = 10; //how many poll timeouts constitute a full timeout + +/*********************************************************************** + * The zero copy interface implementation + **********************************************************************/ +class usrp_e_mmap_zero_copy_impl : public zero_copy_if, public boost::enable_shared_from_this<usrp_e_mmap_zero_copy_impl> { +public: +    usrp_e_mmap_zero_copy_impl(usrp_e_iface::sptr iface): +        _fd(iface->get_file_descriptor()), _recv_index(0), _send_index(0) +    { +        //get system sizes +        iface->ioctl(USRP_E_GET_RB_INFO, &_rb_size); +        size_t page_size = getpagesize(); +        _frame_size = page_size/2; + +        //calculate the memory size +        _map_size = +            (_rb_size.num_pages_rx_flags + _rb_size.num_pages_tx_flags) * page_size + +            (_rb_size.num_rx_frames + _rb_size.num_tx_frames) * _frame_size; + +        //print sizes summary +        if (sp_verbose){ +            std::cout << "page_size:          " << page_size                   << std::endl; +            std::cout << "frame_size:         " << _frame_size                 << std::endl; +            std::cout << "num_pages_rx_flags: " << _rb_size.num_pages_rx_flags << std::endl; +            std::cout << "num_rx_frames:      " << _rb_size.num_rx_frames      << std::endl; +            std::cout << "num_pages_tx_flags: " << _rb_size.num_pages_tx_flags << std::endl; +            std::cout << "num_tx_frames:      " << _rb_size.num_tx_frames      << std::endl; +            std::cout << "map_size:           " << _map_size                   << std::endl; +        } + +        //call mmap to get the memory +        _mapped_mem = ::mmap( +            NULL, _map_size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0 +        ); +        UHD_ASSERT_THROW(_mapped_mem != MAP_FAILED); + +        //calculate the memory offsets for info and buffers +        size_t recv_info_off = 0; +        size_t recv_buff_off = recv_info_off + (_rb_size.num_pages_rx_flags * page_size); +        size_t send_info_off = recv_buff_off + (_rb_size.num_rx_frames * _frame_size); +        size_t send_buff_off = send_info_off + (_rb_size.num_pages_tx_flags * page_size); + +        //print offset summary +        if (sp_verbose){ +            std::cout << "recv_info_off: " << recv_info_off << std::endl; +            std::cout << "recv_buff_off: " << recv_buff_off << std::endl; +            std::cout << "send_info_off: " << send_info_off << std::endl; +            std::cout << "send_buff_off: " << send_buff_off << std::endl; +        } + +        //set the internal pointers for info and buffers +        typedef ring_buffer_info (*rbi_pta)[]; +        char *rb_ptr = reinterpret_cast<char *>(_mapped_mem); +        _recv_info = reinterpret_cast<rbi_pta>(rb_ptr + recv_info_off); +        _recv_buff = rb_ptr + recv_buff_off; +        _send_info = reinterpret_cast<rbi_pta>(rb_ptr + send_info_off); +        _send_buff = rb_ptr + send_buff_off; +    } + +    ~usrp_e_mmap_zero_copy_impl(void){ +        if (sp_verbose) std::cout << "cleanup: munmap" << std::endl; +        ::munmap(_mapped_mem, _map_size); +    } + +    managed_recv_buffer::sptr get_recv_buff(double timeout){ +        if (fp_verbose) std::cout << "get_recv_buff: " << _recv_index << std::endl; + +        //grab pointers to the info and buffer +        ring_buffer_info *info = (*_recv_info) + _recv_index; +        void *mem = _recv_buff + _frame_size*_recv_index; + +        //poll/wait for a ready frame +        if (not (info->flags & RB_USER)){ +            for (size_t i = 0; i < poll_breakout; i++){ +                pollfd pfd; +                pfd.fd = _fd; +                pfd.events = POLLIN; +                ssize_t poll_ret = ::poll(&pfd, 1, size_t(timeout*1e3/poll_breakout)); +                if (fp_verbose) std::cout << "  POLLIN: " << poll_ret << std::endl; +                if (poll_ret > 0) goto found_user_frame; //good poll, continue on +            } +            return managed_recv_buffer::sptr(); //timed-out for real +        } found_user_frame: + +        //the process has claimed the frame +        info->flags = RB_USER_PROCESS; + +        //increment the index for the next call +        if (++_recv_index == size_t(_rb_size.num_rx_frames)) _recv_index = 0; + +        //return the managed buffer for this frame +        if (fp_verbose) std::cout << "  make_recv_buff: " << info->len << std::endl; +        return managed_recv_buffer::make_safe( +            boost::asio::const_buffer(mem, info->len), +            boost::bind(&usrp_e_mmap_zero_copy_impl::release, shared_from_this(), info) +        ); +    } + +    size_t get_num_recv_frames(void) const{ +        return _rb_size.num_rx_frames; +    } + +    size_t get_recv_frame_size(void) const{ +        return _frame_size; +    } + +    managed_send_buffer::sptr get_send_buff(double timeout){ +        if (fp_verbose) std::cout << "get_send_buff: " << _send_index << std::endl; + +        //grab pointers to the info and buffer +        ring_buffer_info *info = (*_send_info) + _send_index; +        void *mem = _send_buff + _frame_size*_send_index; + +        //poll/wait for a ready frame +        if (not (info->flags & RB_KERNEL)){ +            pollfd pfd; +            pfd.fd = _fd; +            pfd.events = POLLOUT; +            ssize_t poll_ret = ::poll(&pfd, 1, size_t(timeout*1e3)); +            if (fp_verbose) std::cout << "  POLLOUT: " << poll_ret << std::endl; +            if (poll_ret <= 0) return managed_send_buffer::sptr(); +        } + +        //increment the index for the next call +        if (++_send_index == size_t(_rb_size.num_tx_frames)) _send_index = 0; + +        //return the managed buffer for this frame +        if (fp_verbose) std::cout << "  make_send_buff: " << _frame_size << std::endl; +        return managed_send_buffer::make_safe( +            boost::asio::mutable_buffer(mem, _frame_size), +            boost::bind(&usrp_e_mmap_zero_copy_impl::commit, shared_from_this(), info, _1) +        ); +    } + +    size_t get_num_send_frames(void) const{ +        return _rb_size.num_tx_frames; +    } + +    size_t get_send_frame_size(void) const{ +        return _frame_size; +    } + +private: + +    void release(ring_buffer_info *info){ +        if (fp_verbose) std::cout << "recv buff: release" << std::endl; +        info->flags = RB_KERNEL; +    } + +    void commit(ring_buffer_info *info, size_t len){ +        if (fp_verbose) std::cout << "send buff: commit " << len << std::endl; +        info->len = len; +        info->flags = RB_USER; +        if (::write(_fd, NULL, 0) < 0){ +            std::cerr << UHD_THROW_SITE_INFO("write error") << std::endl; +        } +    } + +    int _fd; + +    //the mapped memory itself +    void *_mapped_mem; + +    //mapped memory sizes +    usrp_e_ring_buffer_size_t _rb_size; +    size_t _frame_size, _map_size; + +    //pointers to sections in the mapped memory +    ring_buffer_info (*_recv_info)[], (*_send_info)[]; +    char *_recv_buff, *_send_buff; + +    //indexes into sub-sections of mapped memory +    size_t _recv_index, _send_index; +}; + +/*********************************************************************** + * The zero copy interface make function + **********************************************************************/ +zero_copy_if::sptr usrp_e_make_mmap_zero_copy(usrp_e_iface::sptr iface){ +    return zero_copy_if::sptr(new usrp_e_mmap_zero_copy_impl(iface)); +} diff --git a/host/lib/usrp/wrapper_utils.hpp b/host/lib/usrp/wrapper_utils.hpp new file mode 100644 index 000000000..aee230fc0 --- /dev/null +++ b/host/lib/usrp/wrapper_utils.hpp @@ -0,0 +1,66 @@ +// +// 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_LIBUHD_USRP_WRAPPER_UTILS_HPP +#define INCLUDED_LIBUHD_USRP_WRAPPER_UTILS_HPP + +#include <uhd/wax.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/usrp/dsp_props.hpp> +#include <uhd/utils/warning.hpp> +#include <boost/format.hpp> +#include <cmath> + +static inline uhd::freq_range_t add_dsp_shift( +    const uhd::freq_range_t &range, +    wax::obj dsp +){ +    double codec_rate = dsp[uhd::usrp::DSP_PROP_CODEC_RATE].as<double>(); +    return uhd::freq_range_t(range.min - codec_rate/2.0, range.max + codec_rate/2.0); +} + +static inline void do_samp_rate_warning_message( +    double target_rate, +    double actual_rate, +    const std::string &xx +){ +    static const double max_allowed_error = 1.0; //Sps +    if (std::abs(target_rate - actual_rate) > max_allowed_error){ +        uhd::print_warning(str(boost::format( +            "The hardware does not support the requested %s sample rate:\n" +            "Target sample rate: %f MSps\n" +            "Actual sample rate: %f MSps\n" +        ) % xx % (target_rate/1e6) % (actual_rate/1e6))); +    } +} + +static inline void do_tune_freq_warning_message( +    double target_freq, +    double actual_freq, +    const std::string &xx +){ +    static const double max_allowed_error = 1.0; //Hz +    if (std::abs(target_freq - actual_freq) > max_allowed_error){ +        uhd::print_warning(str(boost::format( +            "The hardware does not support the requested %s frequency:\n" +            "Target frequency: %f MHz\n" +            "Actual frequency: %f MHz\n" +        ) % xx % (target_freq/1e6) % (actual_freq/1e6))); +    } +} + +#endif /* INCLUDED_LIBUHD_USRP_WRAPPER_UTILS_HPP */ diff --git a/host/lib/utils/thread_priority.cpp b/host/lib/utils/thread_priority.cpp index c35e5fcb1..f09d1b1d6 100644 --- a/host/lib/utils/thread_priority.cpp +++ b/host/lib/utils/thread_priority.cpp @@ -16,6 +16,8 @@  //  #include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/warning.hpp> +#include <boost/format.hpp>  #include <stdexcept>  #include <iostream> @@ -24,7 +26,12 @@ bool uhd::set_thread_priority_safe(float priority, bool realtime){          set_thread_priority(priority, realtime);          return true;      }catch(const std::exception &e){ -        std::cerr << "set_thread_priority: " << e.what() << std::endl; +        uhd::print_warning(str(boost::format( +            "%s\n" +            "Failed to set thread priority %d (%s):\n" +            "Performance may be negatively affected.\n" +            "See the general application notes.\n" +        ) % e.what() % priority % (realtime?"realtime":"")));          return false;      }  } diff --git a/host/lib/version.cpp b/host/lib/version.cpp index 5edbca09b..93fdecb1a 100644 --- a/host/lib/version.cpp +++ b/host/lib/version.cpp @@ -21,3 +21,17 @@  std::string uhd::get_version_string(void){      return UHD_VERSION_STRING;  } + +#include <uhd/utils/static.hpp> +#include <boost/version.hpp> +#include <iostream> + +UHD_STATIC_BLOCK(print_system_info){ +    std::cout +        << BOOST_PLATFORM << "; " +        << BOOST_COMPILER << "; " +        << "Boost_" << BOOST_VERSION << "; " +        << "UHD_" << uhd::get_version_string() +        << std::endl << std::endl +    ; +} diff --git a/host/test/CMakeLists.txt b/host/test/CMakeLists.txt index 0d4607f68..2cc987f0c 100644 --- a/host/test/CMakeLists.txt +++ b/host/test/CMakeLists.txt @@ -18,8 +18,7 @@  ########################################################################  # unit test suite  ######################################################################## -ADD_EXECUTABLE(main_test -    main_test.cpp +SET(test_sources      addr_test.cpp      buffer_test.cpp      byteswap_test.cpp @@ -28,13 +27,24 @@ ADD_EXECUTABLE(main_test      error_test.cpp      gain_group_test.cpp      subdev_spec_test.cpp +    time_spec_test.cpp      tune_helper_test.cpp      vrt_test.cpp      warning_test.cpp      wax_test.cpp  ) -TARGET_LINK_LIBRARIES(main_test uhd) -ADD_TEST(test main_test) + +#turn each test cpp file into an executable with an int main() function +ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) + +#for each source: build an executable, register it as a test, and install +FOREACH(test_source ${test_sources}) +    GET_FILENAME_COMPONENT(test_name ${test_source} NAME_WE) +    ADD_EXECUTABLE(${test_name} ${test_source}) +    TARGET_LINK_LIBRARIES(${test_name} uhd) +    ADD_TEST(${test_name} ${test_name}) +    INSTALL(TARGETS ${test_name} RUNTIME DESTINATION ${PKG_DATA_DIR}/tests) +ENDFOREACH(test_source)  ########################################################################  # demo of a loadable module @@ -42,6 +52,5 @@ ADD_TEST(test main_test)  ADD_LIBRARY(module_test MODULE module_test.cpp)  INSTALL(TARGETS -   main_test     RUNTIME DESTINATION ${PKG_DATA_DIR}/tests  ) diff --git a/host/test/buffer_test.cpp b/host/test/buffer_test.cpp index aadb3f951..8445412e7 100644 --- a/host/test/buffer_test.cpp +++ b/host/test/buffer_test.cpp @@ -23,7 +23,7 @@  using namespace boost::assign;  using namespace uhd::transport; -static const boost::posix_time::milliseconds timeout(10); +static const double timeout = 0.01/*secs*/;  BOOST_AUTO_TEST_CASE(test_bounded_buffer_with_timed_wait){      bounded_buffer<int>::sptr bb(bounded_buffer<int>::make(3)); diff --git a/host/test/main_test.cpp b/host/test/main_test.cpp deleted file mode 100644 index 0b47303b7..000000000 --- a/host/test/main_test.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MAIN -#include <boost/test/unit_test.hpp> diff --git a/host/test/time_spec_test.cpp b/host/test/time_spec_test.cpp new file mode 100644 index 000000000..5ad782160 --- /dev/null +++ b/host/test/time_spec_test.cpp @@ -0,0 +1,61 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/test/unit_test.hpp> +#include <uhd/types/time_spec.hpp> +#include <boost/foreach.hpp> +#include <iostream> + +BOOST_AUTO_TEST_CASE(test_time_spec_compare){ +    std::cout << "Testing time specification compare..." << std::endl; + +    BOOST_CHECK(uhd::time_spec_t(2.0) == uhd::time_spec_t(2.0)); +    BOOST_CHECK(uhd::time_spec_t(2.0) > uhd::time_spec_t(1.0)); +    BOOST_CHECK(uhd::time_spec_t(1.0) < uhd::time_spec_t(2.0)); + +    BOOST_CHECK(uhd::time_spec_t(1.1) == uhd::time_spec_t(1.1)); +    BOOST_CHECK(uhd::time_spec_t(1.1) > uhd::time_spec_t(1.0)); +    BOOST_CHECK(uhd::time_spec_t(1.0) < uhd::time_spec_t(1.1)); + +    BOOST_CHECK(uhd::time_spec_t(0.1) == uhd::time_spec_t(0.1)); +    BOOST_CHECK(uhd::time_spec_t(0.2) > uhd::time_spec_t(0.1)); +    BOOST_CHECK(uhd::time_spec_t(0.1) < uhd::time_spec_t(0.2)); +} + +#define CHECK_TS_EQUAL(lhs, rhs) \ +    BOOST_CHECK_CLOSE((lhs).get_real_secs(), (rhs).get_real_secs(), 0.001) + +BOOST_AUTO_TEST_CASE(test_time_spec_arithmetic){ +    std::cout << "Testing time specification arithmetic..." << std::endl; + +    CHECK_TS_EQUAL(uhd::time_spec_t(2.3) + uhd::time_spec_t(1.0), uhd::time_spec_t(3.3)); +    CHECK_TS_EQUAL(uhd::time_spec_t(2.3) - uhd::time_spec_t(1.0), uhd::time_spec_t(1.3)); +    CHECK_TS_EQUAL(uhd::time_spec_t(1.0) + uhd::time_spec_t(2.3), uhd::time_spec_t(3.3)); +    CHECK_TS_EQUAL(uhd::time_spec_t(1.0) - uhd::time_spec_t(2.3), uhd::time_spec_t(-1.3)); +} + +BOOST_AUTO_TEST_CASE(test_time_spec_parts){ +    std::cout << "Testing time specification parts..." << std::endl; + +    BOOST_CHECK_EQUAL(uhd::time_spec_t(1.1).get_full_secs(), 1); +    BOOST_CHECK_CLOSE(uhd::time_spec_t(1.1).get_frac_secs(), 0.1, 0.001); +    BOOST_CHECK_EQUAL(uhd::time_spec_t(1.1).get_tick_count(100), 10); + +    BOOST_CHECK_EQUAL(uhd::time_spec_t(-1.1).get_full_secs(), -1); +    BOOST_CHECK_CLOSE(uhd::time_spec_t(-1.1).get_frac_secs(), -0.1, 0.001); +    BOOST_CHECK_EQUAL(uhd::time_spec_t(-1.1).get_tick_count(100), -10); +} diff --git a/host/test/tune_helper_test.cpp b/host/test/tune_helper_test.cpp index 570f47293..1ef4af330 100644 --- a/host/test/tune_helper_test.cpp +++ b/host/test/tune_helper_test.cpp @@ -99,7 +99,8 @@ public:          /* NOP */      }  private: -    void get(const wax::obj &key, wax::obj &val){ +    void get(const wax::obj &key_, wax::obj &val){ +        named_prop_t key = named_prop_t::extract(key_);          switch(key.as<dsp_prop_t>()){          case DSP_PROP_CODEC_RATE:              val = _codec_rate; @@ -109,11 +110,16 @@ private:              val = _freq_shift;              return; +        case DSP_PROP_FREQ_SHIFT_NAMES: +            val = prop_names_t(1, ""); +            return; +          default: UHD_THROW_PROP_GET_ERROR();          }      } -    void set(const wax::obj &key, const wax::obj &val){ +    void set(const wax::obj &key_, const wax::obj &val){ +        named_prop_t key = named_prop_t::extract(key_);          switch(key.as<dsp_prop_t>()){          case DSP_PROP_FREQ_SHIFT:              _freq_shift = val.as<double>(); @@ -136,12 +142,12 @@ BOOST_AUTO_TEST_CASE(test_tune_helper_rx){      dummy_dsp dsp(100e6);      std::cout << "Testing tune helper RX automatic LO offset" << std::endl; -    tune_result_t tr = tune_rx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 2.3451e9); +    tune_result_t tr = tune_rx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 0, 2.3451e9);      std::cout << tr.to_pp_string() << std::endl;      BOOST_CHECK_CLOSE(tr.actual_inter_freq, 2.345e9, tolerance);      BOOST_CHECK_CLOSE(tr.actual_dsp_freq, -100e3, tolerance); -    double freq_derived = derive_freq_from_rx_subdev_and_dsp(subdev.get_link(), dsp.get_link()); +    double freq_derived = derive_freq_from_rx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 0);      BOOST_CHECK_CLOSE(freq_derived, 2.3451e9, tolerance);  } @@ -150,12 +156,12 @@ BOOST_AUTO_TEST_CASE(test_tune_helper_tx){      dummy_dsp dsp(100e6);      std::cout << "Testing tune helper TX automatic LO offset" << std::endl; -    tune_result_t tr = tune_tx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 2.3451e9); +    tune_result_t tr = tune_tx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 0, 2.3451e9);      std::cout << tr.to_pp_string() << std::endl;      BOOST_CHECK_CLOSE(tr.actual_inter_freq, 2.345e9, tolerance);      BOOST_CHECK_CLOSE(tr.actual_dsp_freq, 100e3, tolerance); -    double freq_derived = derive_freq_from_tx_subdev_and_dsp(subdev.get_link(), dsp.get_link()); +    double freq_derived = derive_freq_from_tx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 0);      BOOST_CHECK_CLOSE(freq_derived, 2.3451e9, tolerance);  } @@ -164,11 +170,11 @@ BOOST_AUTO_TEST_CASE(test_tune_helper_rx_nyquist){      dummy_dsp dsp(100e6);      std::cout << "Testing tune helper RX dummy basic board" << std::endl; -    tune_result_t tr = tune_rx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 55e6); +    tune_result_t tr = tune_rx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 0, 55e6);      std::cout << tr.to_pp_string() << std::endl;      BOOST_CHECK_CLOSE(tr.actual_inter_freq, 0.0, tolerance);      BOOST_CHECK_CLOSE(tr.actual_dsp_freq, 45e6, tolerance); -    double freq_derived = derive_freq_from_rx_subdev_and_dsp(subdev.get_link(), dsp.get_link()); +    double freq_derived = derive_freq_from_rx_subdev_and_dsp(subdev.get_link(), dsp.get_link(), 0);      BOOST_CHECK_CLOSE(freq_derived, -45e6, tolerance);  } | 
