summaryrefslogtreecommitdiffstats
path: root/host
diff options
context:
space:
mode:
authorPhilip Balister <philip@opensdr.com>2010-10-21 14:17:47 -0400
committerPhilip Balister <philip@opensdr.com>2010-10-21 14:17:47 -0400
commitc31c2d7ef8e4e02854fe51a8a3b8856649464918 (patch)
treed338268cbdecf21c78a2c77e6e7f95e6f669c4ee /host
parent3dc06cddaf0a0cf32f76be7077d1427ff4b71a7e (diff)
parent090b0dd0d38f23cd1d7c4c6a7b0317a6fdfe2b9b (diff)
downloaduhd-c31c2d7ef8e4e02854fe51a8a3b8856649464918.tar.gz
uhd-c31c2d7ef8e4e02854fe51a8a3b8856649464918.tar.bz2
uhd-c31c2d7ef8e4e02854fe51a8a3b8856649464918.zip
Merge branch 'usrp_e' of ettus.sourcerepo.com:ettus/uhdpriv into usrp_e
Diffstat (limited to 'host')
-rw-r--r--host/AUTHORS3
-rw-r--r--host/CMakeLists.txt8
-rw-r--r--host/README4
-rw-r--r--host/docs/CMakeLists.txt1
-rw-r--r--host/docs/build.rst62
-rw-r--r--host/docs/coding.rst20
-rw-r--r--host/docs/dboards.rst48
-rw-r--r--host/docs/index.rst9
-rw-r--r--host/docs/transport.rst88
-rw-r--r--host/docs/usrp1.rst12
-rw-r--r--host/docs/usrp2.rst49
-rw-r--r--host/examples/benchmark_rx_rate.cpp8
-rw-r--r--host/examples/rx_timed_samples.cpp6
-rw-r--r--host/examples/test_async_messages.cpp22
-rw-r--r--host/examples/test_pps_input.cpp16
-rw-r--r--host/examples/tx_timed_samples.cpp11
-rw-r--r--host/examples/tx_waveforms.cpp6
-rw-r--r--host/include/linux/usrp_e.h5
-rw-r--r--host/include/uhd/config.hpp5
-rw-r--r--host/include/uhd/device.hpp40
-rw-r--r--host/include/uhd/device.ipp35
-rw-r--r--host/include/uhd/transport/alignment_buffer.hpp9
-rw-r--r--host/include/uhd/transport/alignment_buffer.ipp15
-rw-r--r--host/include/uhd/transport/bounded_buffer.hpp12
-rw-r--r--host/include/uhd/transport/bounded_buffer.ipp50
-rw-r--r--host/include/uhd/transport/udp_zero_copy.hpp7
-rw-r--r--host/include/uhd/transport/usb_control.hpp6
-rw-r--r--host/include/uhd/transport/usb_device_handle.hpp14
-rw-r--r--host/include/uhd/transport/usb_zero_copy.hpp21
-rw-r--r--host/include/uhd/transport/zero_copy.hpp133
-rw-r--r--host/include/uhd/types/device_addr.hpp20
-rw-r--r--host/include/uhd/types/time_spec.hpp4
-rw-r--r--host/include/uhd/usrp/CMakeLists.txt2
-rw-r--r--host/include/uhd/usrp/dboard_iface.hpp9
-rw-r--r--host/include/uhd/usrp/dsp_props.hpp11
-rw-r--r--host/include/uhd/usrp/mimo_usrp.hpp349
-rw-r--r--host/include/uhd/usrp/multi_usrp.hpp478
-rw-r--r--host/include/uhd/usrp/simple_usrp.hpp221
-rw-r--r--host/include/uhd/usrp/single_usrp.hpp411
-rw-r--r--host/include/uhd/usrp/subdev_props.hpp1
-rw-r--r--host/include/uhd/usrp/subdev_spec.hpp6
-rw-r--r--host/include/uhd/usrp/tune_helper.hpp18
-rw-r--r--host/lib/device.cpp18
-rw-r--r--host/lib/ic_reg_maps/CMakeLists.txt5
-rwxr-xr-x[-rw-r--r--]host/lib/ic_reg_maps/gen_max2118_regs.py0
-rw-r--r--host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py75
-rw-r--r--host/lib/transport/CMakeLists.txt9
-rw-r--r--host/lib/transport/convert_types_impl.hpp20
-rwxr-xr-xhost/lib/transport/gen_convert_types.py10
-rw-r--r--host/lib/transport/libusb1_base.cpp299
-rw-r--r--host/lib/transport/libusb1_base.hpp177
-rw-r--r--host/lib/transport/libusb1_control.cpp75
-rw-r--r--host/lib/transport/libusb1_device_handle.cpp117
-rw-r--r--host/lib/transport/libusb1_zero_copy.cpp745
-rw-r--r--host/lib/transport/msvc/stdint.h35
-rw-r--r--host/lib/transport/udp_zero_copy_asio.cpp293
-rw-r--r--host/lib/transport/usb_dummy_impl.cpp39
-rw-r--r--host/lib/transport/vrt_packet_handler.hpp107
-rw-r--r--host/lib/transport/zero_copy.cpp121
-rw-r--r--host/lib/types.cpp21
-rw-r--r--host/lib/usrp/CMakeLists.txt5
-rw-r--r--host/lib/usrp/dboard/CMakeLists.txt1
-rw-r--r--host/lib/usrp/dboard/db_basic_and_lf.cpp15
-rw-r--r--host/lib/usrp/dboard/db_dbsrx.cpp47
-rw-r--r--host/lib/usrp/dboard/db_rfx.cpp14
-rw-r--r--host/lib/usrp/dboard/db_tvrx.cpp485
-rw-r--r--host/lib/usrp/dboard/db_unknown.cpp14
-rw-r--r--host/lib/usrp/dboard/db_wbx.cpp16
-rw-r--r--host/lib/usrp/dboard/db_xcvr2450.cpp161
-rw-r--r--host/lib/usrp/dboard_manager.cpp37
-rw-r--r--host/lib/usrp/mimo_usrp.cpp347
-rw-r--r--host/lib/usrp/misc_utils.cpp17
-rw-r--r--host/lib/usrp/multi_usrp.cpp435
-rw-r--r--host/lib/usrp/simple_usrp.cpp285
-rw-r--r--host/lib/usrp/single_usrp.cpp337
-rw-r--r--host/lib/usrp/subdev_spec.cpp4
-rw-r--r--host/lib/usrp/tune_helper.cpp38
-rw-r--r--host/lib/usrp/usrp1/codec_ctrl.cpp18
-rw-r--r--host/lib/usrp/usrp1/codec_ctrl.hpp3
-rw-r--r--host/lib/usrp/usrp1/dboard_iface.cpp11
-rw-r--r--host/lib/usrp/usrp1/dsp_impl.cpp97
-rw-r--r--host/lib/usrp/usrp1/io_impl.cpp477
-rw-r--r--host/lib/usrp/usrp1/mboard_impl.cpp74
-rw-r--r--host/lib/usrp/usrp1/usrp1_ctrl.cpp35
-rw-r--r--host/lib/usrp/usrp1/usrp1_iface.cpp18
-rw-r--r--host/lib/usrp/usrp1/usrp1_iface.hpp14
-rw-r--r--host/lib/usrp/usrp1/usrp1_impl.cpp74
-rw-r--r--host/lib/usrp/usrp1/usrp1_impl.hpp25
-rw-r--r--host/lib/usrp/usrp2/clock_ctrl.cpp51
-rw-r--r--host/lib/usrp/usrp2/dboard_iface.cpp4
-rw-r--r--host/lib/usrp/usrp2/dsp_impl.cpp24
-rw-r--r--host/lib/usrp/usrp2/io_impl.cpp60
-rw-r--r--host/lib/usrp/usrp2/mboard_impl.cpp8
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.cpp40
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.hpp72
-rw-r--r--host/lib/usrp/usrp_e/CMakeLists.txt1
-rw-r--r--host/lib/usrp/usrp_e/dboard_iface.cpp5
-rw-r--r--host/lib/usrp/usrp_e/dsp_impl.cpp24
-rw-r--r--host/lib/usrp/usrp_e/io_impl.cpp198
-rw-r--r--host/lib/usrp/usrp_e/usrp_e_impl.hpp13
-rw-r--r--host/lib/usrp/usrp_e/usrp_e_mmap_zero_copy.cpp215
-rw-r--r--host/lib/usrp/wrapper_utils.hpp66
-rw-r--r--host/lib/utils/thread_priority.cpp9
-rw-r--r--host/lib/version.cpp14
-rw-r--r--host/test/CMakeLists.txt19
-rw-r--r--host/test/buffer_test.cpp2
-rw-r--r--host/test/main_test.cpp3
-rw-r--r--host/test/time_spec_test.cpp61
-rw-r--r--host/test/tune_helper_test.cpp22
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);
}