[hamradio-commits] [soapybladerf] 01/02: Imported Upstream version 0.3.2

Andreas E. Bombe aeb at moszumanska.debian.org
Wed Aug 24 19:23:18 UTC 2016


This is an automated email from the git hooks/post-receive script.

aeb pushed a commit to branch master
in repository soapybladerf.

commit af3c103f96396a91d4a6c92f7bc7603a095c4449
Author: Andreas Bombe <aeb at debian.org>
Date:   Wed Aug 17 02:03:27 2016 +0200

    Imported Upstream version 0.3.2
---
 .travis.yml             |  56 ++++
 CMakeLists.txt          |  53 +++
 Changelog.txt           |  46 +++
 FindLibbladeRF.cmake    |  28 ++
 LICENSE.LGPLv2.1        | 502 +++++++++++++++++++++++++++
 README.md               |  18 +
 bladeRF_Registation.cpp | 113 +++++++
 bladeRF_Settings.cpp    | 877 ++++++++++++++++++++++++++++++++++++++++++++++++
 bladeRF_SoapySDR.hpp    | 312 +++++++++++++++++
 bladeRF_Streaming.cpp   | 494 +++++++++++++++++++++++++++
 debian/changelog        |  41 +++
 debian/compat           |   1 +
 debian/control          |  23 ++
 debian/copyright        |  10 +
 debian/docs             |   1 +
 debian/rules            |  21 ++
 debian/source/format    |   1 +
 self_test.py            |  46 +++
 18 files changed, 2643 insertions(+)

diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..4123f6b
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,56 @@
+########################################################################
+## Travis CI config for SoapyBladeRF
+##
+## * installs bladerf from PPA
+## * installs SoapySDR from source
+## * confirms build and install
+## * checks that drivers load
+########################################################################
+
+sudo: required
+dist: trusty
+
+language: cpp
+compiler: gcc
+
+env:
+  global:
+    - INSTALL_PREFIX=/usr/local
+    - SOAPY_SDR_BRANCH=master
+  matrix:
+    - BUILD_TYPE=Debug
+    - BUILD_TYPE=Release
+
+before_install:
+  # regular ubuntu packages
+  - sudo add-apt-repository main
+  - sudo add-apt-repository universe
+
+  # driver development files from ppa
+  - sudo add-apt-repository -y ppa:bladerf/bladerf
+
+  # update after package changes
+  - sudo apt-get update
+
+install:
+  #sdr development files
+  - sudo apt-get install --no-install-recommends -q -y libbladerf-dev
+
+  # install SoapySDR from source
+  - git clone https://github.com/pothosware/SoapySDR.git
+  - pushd SoapySDR
+  - git checkout ${SOAPY_SDR_BRANCH}
+  - mkdir build && cd build
+  - cmake ../ -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DENABLE_PYTHON=OFF -DENABLE_PYTHON3=OFF
+  - make && sudo make install
+  - popd
+
+script:
+  - mkdir build && cd build
+  - cmake ../ -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=${BUILD_TYPE}
+  - make && sudo make install
+  # print info about the install
+  - export LD_LIBRARY_PATH=${INSTALL_PREFIX}/lib:${LD_LIBRARY_PATH}
+  - export PATH=${INSTALL_PREFIX}/bin:${PATH}
+  - SoapySDRUtil --info
+  - SoapySDRUtil --check=bladerf
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..9465efc
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,53 @@
+########################################################################
+# Build Soapy SDR support module for blade RF
+########################################################################
+cmake_minimum_required(VERSION 2.8.7)
+project(SoapyBladeRF CXX)
+
+find_package(SoapySDR "0.4" NO_MODULE)
+if (NOT SoapySDR_FOUND)
+    message(FATAL_ERROR "Soapy SDR development files not found...")
+endif ()
+
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
+find_package(LibbladeRF)
+
+if (NOT LIBBLADERF_FOUND)
+    message(FATAL_ERROR "Blade RF development files not found...")
+endif ()
+message(STATUS "LIBBLADERF_INCLUDE_DIRS - ${LIBBLADERF_INCLUDE_DIRS}")
+message(STATUS "LIBBLADERF_LIBRARIES - ${LIBBLADERF_LIBRARIES}")
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${LIBBLADERF_INCLUDE_DIRS})
+
+#enable c++11 features
+if(CMAKE_COMPILER_IS_GNUCXX)
+
+    #C++11 is a required language feature for this project
+    include(CheckCXXCompilerFlag)
+    CHECK_CXX_COMPILER_FLAG("-std=c++11" HAS_STD_CXX11)
+    if(HAS_STD_CXX11)
+        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+    else(HAS_STD_CXX11)
+        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+    endif()
+
+    #disable warnings for unused parameters
+    add_definitions(-Wno-unused-parameter)
+
+endif(CMAKE_COMPILER_IS_GNUCXX)
+
+if (APPLE)
+   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wc++11-extensions")
+endif(APPLE)
+
+SOAPY_SDR_MODULE_UTIL(
+    TARGET bladeRFSupport
+    SOURCES
+        bladeRF_Registation.cpp
+        bladeRF_Settings.cpp
+        bladeRF_Streaming.cpp
+    LIBRARIES
+        ${LIBBLADERF_LIBRARIES}
+)
diff --git a/Changelog.txt b/Changelog.txt
new file mode 100644
index 0000000..b63384c
--- /dev/null
+++ b/Changelog.txt
@@ -0,0 +1,46 @@
+Release 0.3.2 (2016-05-20)
+==========================
+
+- Added settings hooks for xb200 support
+- Added settings hooks for sampling mode
+- Added settings hooks for loopback modes
+
+Release 0.3.1 (2016-03-01)
+==========================
+
+- Fix tx end of burst implementation in deactivateStream()
+- Clear EOB when the last sample will not be transmitted
+- Implemented masked GPIO write based on v1.5.0 API
+
+Release 0.3.0 (2015-11-20)
+==========================
+
+- Implemented getStreamFormats() for SoapySDR v0.4
+- Implemented getNativeStreamFormat() for SoapySDR v0.4
+- Implemented getStreamArgsInfo() for SoapySDR v0.4
+
+Release 0.2.0 (2015-10-10)
+==========================
+
+- Added GPIO access hooks for CONFIG and EXPANSION bank
+
+Release 0.1.2 (2015-09-16)
+==========================
+
+- Return SOAPY_SDR_NOT_SUPPORTED for RX readStreamStatus()
+- Fix readStreamStatus() timeout infinite loop condition
+
+Release 0.1.1 (2015-08-15)
+==========================
+
+- Fix undefined behavior with bladerf_sync_rx() minimum timeout
+- Clip read/write number of samples to conversion buffer size
+- Arbitrary sized conversion buffers based on buffer size
+- Fix find function serial string to use null terminator
+- Support use of BLADERF_META_FLAG_TX_UPDATE_TIMESTAMP flag
+- Remove BLADERF_META_FLAG_TX_BURST_END padding (2015.07)
+
+Release 0.1.0 (2015-07-14)
+==========================
+
+- Initial release of Soapy BladeRF support module
diff --git a/FindLibbladeRF.cmake b/FindLibbladeRF.cmake
new file mode 100644
index 0000000..1a1961b
--- /dev/null
+++ b/FindLibbladeRF.cmake
@@ -0,0 +1,28 @@
+if(NOT LIBBLADERF_FOUND)
+  INCLUDE(FindPkgConfig)
+  pkg_check_modules (LIBBLADERF_PKG libbladeRF)
+  find_path(LIBBLADERF_INCLUDE_DIRS NAMES libbladeRF.h
+    PATHS
+    ${LIBBLADERF_PKG_INCLUDE_DIRS}
+    /usr/include
+    /usr/local/include
+  )
+
+  find_library(LIBBLADERF_LIBRARIES NAMES bladeRF
+    PATHS
+    ${LIBBLADERF_PKG_LIBRARY_DIRS}
+    /usr/lib
+    /usr/local/lib
+  )
+
+if(LIBBLADERF_INCLUDE_DIRS AND LIBBLADERF_LIBRARIES)
+  set(LIBBLADERF_FOUND TRUE CACHE INTERNAL "libbladeRF found")
+  message(STATUS "Found libbladeRF: ${LIBBLADERF_INCLUDE_DIRS}, ${LIBBLADERF_LIBRARIES}")
+else(LIBBLADERF_INCLUDE_DIRS AND LIBBLADERF_LIBRARIES)
+  set(LIBBLADERF_FOUND FALSE CACHE INTERNAL "libbladeRF found")
+  message(STATUS "libbladeRF not found.")
+endif(LIBBLADERF_INCLUDE_DIRS AND LIBBLADERF_LIBRARIES)
+
+mark_as_advanced(LIBBLADERF_LIBRARIES LIBBLADERF_INCLUDE_DIRS)
+
+endif(NOT LIBBLADERF_FOUND)
diff --git a/LICENSE.LGPLv2.1 b/LICENSE.LGPLv2.1
new file mode 100644
index 0000000..e5ab03e
--- /dev/null
+++ b/LICENSE.LGPLv2.1
@@ -0,0 +1,502 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8bbdd34
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+# Soapy SDR plugin for Blade RF
+
+##Build Status
+
+- Travis: [![Travis Build Status](https://travis-ci.org/pothosware/SoapyBladeRF.svg?branch=master)](https://travis-ci.org/pothosware/SoapyBladeRF)
+
+##Dependencies
+
+* SoapySDR - https://github.com/pothosware/SoapySDR/wiki
+* LibBladeRF - http://www.github.com/nuand/bladeRF
+
+##Documentation
+
+* https://github.com/pothosware/SoapyBladeRF/wiki
+
+## Licensing information
+
+* LGPLv2.1: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
diff --git a/bladeRF_Registation.cpp b/bladeRF_Registation.cpp
new file mode 100644
index 0000000..598d230
--- /dev/null
+++ b/bladeRF_Registation.cpp
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the bladeRF project:
+ *   http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015-2016 Josh Blum
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <SoapySDR/Registry.hpp>
+#include "bladeRF_SoapySDR.hpp"
+#include <cstdio>
+#include <sstream>
+#include <cstdlib>
+#include <cstring>
+
+static SoapySDR::Kwargs devinfo_to_kwargs(const bladerf_devinfo &info)
+{
+    SoapySDR::Kwargs args;
+
+    args["backend"] = bladerf_backend_str(info.backend);
+
+    char deviceStr[100];
+    sprintf(deviceStr, "0x%02X:0x%02X", int(info.usb_bus), int(info.usb_addr));
+    args["device"] = deviceStr;
+
+    char instanceStr[100];
+    sprintf(instanceStr, "%u", info.instance);
+    args["instance"] = instanceStr;
+
+    args["serial"] = std::string(info.serial);
+    return args;
+}
+
+static bladerf_devinfo kwargs_to_devinfo(const SoapySDR::Kwargs &args)
+{
+    std::stringstream ss;
+
+    if (args.count("backend") != 0)
+    {
+        ss << args.at("backend") << ":";
+    }
+    else ss << "*:";
+
+    if (args.count("device") != 0)
+    {
+        ss << "device=" << args.at("device") << " ";
+    }
+
+    if (args.count("instance") != 0)
+    {
+        ss << "instance=" << args.at("instance") << " ";
+    }
+
+    if (args.count("serial") != 0)
+    {
+        ss << "serial=" << args.at("serial") << " ";
+    }
+
+    bladerf_devinfo info;
+    bladerf_init_devinfo(&info);
+    bladerf_get_devinfo_from_str(ss.str().c_str(), &info);
+    return info;
+}
+
+static std::vector<SoapySDR::Kwargs> find_bladeRF(const SoapySDR::Kwargs &matchArgs)
+{
+    const bladerf_devinfo matchinfo = kwargs_to_devinfo(matchArgs);
+
+    std::vector<SoapySDR::Kwargs> results;
+    bladerf_devinfo *infos = NULL;
+    int ret = 0;
+    ret = bladerf_get_device_list(&infos);
+
+    for (int i = 0; i < ret; i++)
+    {
+        if (bladerf_devinfo_matches(infos+i, &matchinfo))
+        {
+            results.push_back(devinfo_to_kwargs(infos[i]));
+        }
+    }
+
+    bladerf_free_device_list(infos);
+    return results;
+}
+
+static SoapySDR::Device *make_bladeRF(const SoapySDR::Kwargs &args)
+{
+    SoapySDR::Device *bladerf = new bladeRF_SoapySDR(kwargs_to_devinfo(args));
+
+    //apply applicable settings found in args
+    for (const auto &info : bladerf->getSettingInfo())
+    {
+        if (args.count(info.key) == 0) continue;
+        bladerf->writeSetting(info.key, args.at(info.key));
+    }
+
+    return bladerf;
+}
+
+static SoapySDR::Registry register__bladeRF("bladerf", &find_bladeRF, &make_bladeRF, SOAPY_SDR_ABI_VERSION);
diff --git a/bladeRF_Settings.cpp b/bladeRF_Settings.cpp
new file mode 100644
index 0000000..d940c8f
--- /dev/null
+++ b/bladeRF_Settings.cpp
@@ -0,0 +1,877 @@
+/*
+ * This file is part of the bladeRF project:
+ *   http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015-2016 Josh Blum
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "bladeRF_SoapySDR.hpp"
+#include <SoapySDR/Logger.hpp>
+#include <algorithm> //find
+#include <stdexcept>
+#include <cstdio>
+
+/*******************************************************************
+ * Device init/shutdown
+ ******************************************************************/
+
+bladeRF_SoapySDR::bladeRF_SoapySDR(const bladerf_devinfo &devinfo):
+    _rxSampRate(1.0),
+    _txSampRate(1.0),
+    _inTxBurst(false),
+    _rxFloats(false),
+    _txFloats(false),
+    _rxOverflow(false),
+    _rxNextTicks(0),
+    _txNextTicks(0),
+    _timeNsOffset(0),
+    _rxBuffSize(0),
+    _txBuffSize(0),
+    _rxMinTimeoutMs(0),
+    _dev(NULL)
+{
+    bladerf_devinfo info = devinfo;
+    SoapySDR::logf(SOAPY_SDR_INFO, "bladerf_open_with_devinfo()");
+    int ret = bladerf_open_with_devinfo(&_dev, &info);
+
+    if (ret < 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_open_with_devinfo() returned %s", _err2str(ret).c_str());
+        throw std::runtime_error("bladerf_open_with_devinfo() failed " + _err2str(ret));
+    }
+
+    char serialStr[BLADERF_SERIAL_LENGTH];
+    ret = bladerf_get_serial(_dev, serialStr);
+    if (ret == 0) SoapySDR::logf(SOAPY_SDR_INFO, "bladerf_get_serial() = %s", serialStr);
+
+    //initialize the sample rates to something
+    this->setSampleRate(SOAPY_SDR_RX, 0, 1e6);
+    this->setSampleRate(SOAPY_SDR_TX, 0, 1e6);
+}
+
+bladeRF_SoapySDR::~bladeRF_SoapySDR(void)
+{
+    SoapySDR::logf(SOAPY_SDR_INFO, "bladerf_close()");
+    if (_dev != NULL) bladerf_close(_dev);
+}
+
+/*******************************************************************
+ * Identification API
+ ******************************************************************/
+
+SoapySDR::Kwargs bladeRF_SoapySDR::getHardwareInfo(void) const
+{
+    SoapySDR::Kwargs info;
+
+    {
+        char serialStr[BLADERF_SERIAL_LENGTH];
+        int ret = bladerf_get_serial(_dev, serialStr);
+        if (ret == 0) info["serial"] = serialStr;
+    }
+
+    {
+        bladerf_fpga_size fpgaSize = BLADERF_FPGA_UNKNOWN;
+        int ret = bladerf_get_fpga_size(_dev, &fpgaSize);
+        char fpgaStr[100];
+        sprintf(fpgaStr, "%u", int(fpgaSize));
+        if (ret == 0) info["fpga_size"] = fpgaStr;
+    }
+
+    {
+        struct bladerf_version verInfo;
+        int ret = bladerf_fw_version(_dev, &verInfo);
+        if (ret == 0) info["fw_version"] = verInfo.describe;
+    }
+
+    {
+        struct bladerf_version verInfo;
+        int ret = bladerf_fpga_version(_dev, &verInfo);
+        if (ret == 0) info["fpga_version"] = verInfo.describe;
+    }
+
+    return info;
+}
+
+/*******************************************************************
+ * Antenna API
+ ******************************************************************/
+
+std::vector<std::string> bladeRF_SoapySDR::listAntennas(const int direction, const size_t) const
+{
+    std::vector<std::string> options;
+    if (direction == SOAPY_SDR_TX) options.push_back("TX");
+    if (direction == SOAPY_SDR_RX) options.push_back("RX");
+    return options;
+}
+
+void bladeRF_SoapySDR::setAntenna(const int, const size_t, const std::string &)
+{
+    return; //nothing to set, ignore it
+}
+
+std::string bladeRF_SoapySDR::getAntenna(const int direction, const size_t channel) const
+{
+    if (direction == SOAPY_SDR_TX) return "TX";
+    if (direction == SOAPY_SDR_RX) return "RX";
+    return SoapySDR::Device::getAntenna(direction, channel);
+}
+
+/*******************************************************************
+ * Gain API
+ ******************************************************************/
+
+std::vector<std::string> bladeRF_SoapySDR::listGains(const int direction, const size_t) const
+{
+    std::vector<std::string> options;
+    if (direction == SOAPY_SDR_RX) options.push_back("LNA");
+    options.push_back("VGA1");
+    options.push_back("VGA2");
+    return options;
+}
+
+void bladeRF_SoapySDR::setGain(const int direction, const size_t, const double value)
+{
+    const int ret = bladerf_set_gain(_dev, _dir2mod(direction), int(value));
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_set_gain(%f) returned %s", value, _err2str(ret).c_str());
+        throw std::runtime_error("setGain() " + _err2str(ret));
+    }
+}
+
+void bladeRF_SoapySDR::setGain(const int direction, const size_t, const std::string &name, const double value)
+{
+    int ret = 0;
+    if (direction == SOAPY_SDR_RX and name == "LNA")
+    {
+        if      (value < 1.5) ret = bladerf_set_lna_gain(_dev, BLADERF_LNA_GAIN_BYPASS);
+        else if (value < 4.5) ret = bladerf_set_lna_gain(_dev, BLADERF_LNA_GAIN_MID);
+        else                  ret = bladerf_set_lna_gain(_dev, BLADERF_LNA_GAIN_MAX);
+    }
+    else if (direction == SOAPY_SDR_RX and name == "VGA1") ret = bladerf_set_rxvga1(_dev, int(value));
+    else if (direction == SOAPY_SDR_RX and name == "VGA2") ret = bladerf_set_rxvga2(_dev, int(value));
+    else if (direction == SOAPY_SDR_TX and name == "VGA1") ret = bladerf_set_txvga1(_dev, int(value));
+    else if (direction == SOAPY_SDR_TX and name == "VGA2") ret = bladerf_set_txvga2(_dev, int(value));
+    else throw std::runtime_error("setGain("+name+") -- unknown name");
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_set_vga(%f) returned %s", value, _err2str(ret).c_str());
+        throw std::runtime_error("setGain("+name+") " + _err2str(ret));
+    }
+}
+
+double bladeRF_SoapySDR::getGain(const int direction, const size_t, const std::string &name) const
+{
+    int ret = 0;
+    int gain = 0;
+    if (direction == SOAPY_SDR_RX and name == "LNA")
+    {
+        bladerf_lna_gain lnaGain;
+        ret = bladerf_get_lna_gain(_dev, &lnaGain);
+        switch (lnaGain)
+        {
+        case BLADERF_LNA_GAIN_UNKNOWN: gain = 0; break;
+        case BLADERF_LNA_GAIN_BYPASS: gain = 0; break;
+        case BLADERF_LNA_GAIN_MID: gain = BLADERF_LNA_GAIN_MID_DB; break;
+        case BLADERF_LNA_GAIN_MAX: gain = BLADERF_LNA_GAIN_MAX_DB; break;
+        }
+    }
+    else if (direction == SOAPY_SDR_RX and name == "VGA1") ret = bladerf_get_rxvga1(_dev, &gain);
+    else if (direction == SOAPY_SDR_RX and name == "VGA2") ret = bladerf_get_rxvga2(_dev, &gain);
+    else if (direction == SOAPY_SDR_TX and name == "VGA1") ret = bladerf_get_txvga1(_dev, &gain);
+    else if (direction == SOAPY_SDR_TX and name == "VGA2") ret = bladerf_get_txvga2(_dev, &gain);
+    else throw std::runtime_error("getGain("+name+") -- unknown name");
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_get_vga() returned %s", _err2str(ret).c_str());
+        throw std::runtime_error("getGain("+name+") " + _err2str(ret));
+    }
+    return gain;
+}
+
+SoapySDR::Range bladeRF_SoapySDR::getGainRange(const int direction, const size_t, const std::string &name) const
+{
+    if (direction == SOAPY_SDR_RX and name == "LNA")  return SoapySDR::Range(0, BLADERF_LNA_GAIN_MAX_DB);
+    if (direction == SOAPY_SDR_RX and name == "VGA1") return SoapySDR::Range(BLADERF_RXVGA1_GAIN_MIN, BLADERF_RXVGA1_GAIN_MAX);
+    if (direction == SOAPY_SDR_RX and name == "VGA2") return SoapySDR::Range(BLADERF_RXVGA2_GAIN_MIN, BLADERF_RXVGA2_GAIN_MAX);
+    if (direction == SOAPY_SDR_TX and name == "VGA1") return SoapySDR::Range(BLADERF_TXVGA1_GAIN_MIN, BLADERF_TXVGA1_GAIN_MAX);
+    if (direction == SOAPY_SDR_TX and name == "VGA2") return SoapySDR::Range(BLADERF_TXVGA2_GAIN_MIN, BLADERF_TXVGA2_GAIN_MAX);
+    else throw std::runtime_error("getGainRange("+name+") -- unknown name");
+}
+
+/*******************************************************************
+ * Frequency API
+ ******************************************************************/
+
+void bladeRF_SoapySDR::setFrequency(const int direction, const size_t, const std::string &name, const double frequency, const SoapySDR::Kwargs &)
+{
+    if (name == "BB") return; //for compatibility
+    if (name != "RF") throw std::runtime_error("setFrequency("+name+") unknown name");
+
+    int ret = bladerf_set_frequency(_dev, _dir2mod(direction), (unsigned int)(frequency));
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_set_frequency(%f) returned %s", frequency, _err2str(ret).c_str());
+        throw std::runtime_error("setFrequency("+name+") " + _err2str(ret));
+    }
+}
+
+double bladeRF_SoapySDR::getFrequency(const int direction, const size_t, const std::string &name) const
+{
+    if (name == "BB") return 0.0; //for compatibility
+    if (name != "RF") throw std::runtime_error("getFrequency("+name+") unknown name");
+
+    unsigned int freq = 0;
+    int ret = bladerf_get_frequency(_dev, _dir2mod(direction), &freq);
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_get_frequency() returned %s", _err2str(ret).c_str());
+        throw std::runtime_error("getFrequency("+name+") " + _err2str(ret));
+    }
+    return freq;
+}
+
+std::vector<std::string> bladeRF_SoapySDR::listFrequencies(const int, const size_t) const
+{
+    std::vector<std::string> components;
+    components.push_back("RF");
+    return components;
+}
+
+SoapySDR::RangeList bladeRF_SoapySDR::getFrequencyRange(const int, const size_t, const std::string &name) const
+{
+    if (name == "BB") return SoapySDR::RangeList(1, SoapySDR::Range(0.0, 0.0)); //for compatibility
+    if (name != "RF") throw std::runtime_error("getFrequencyRange("+name+") unknown name");
+
+    const bool has_xb200 = bladerf_expansion_attach(_dev, BLADERF_XB_200) != 0;
+    const double minFreq = has_xb200?BLADERF_FREQUENCY_MIN_XB200:BLADERF_FREQUENCY_MIN;
+    return SoapySDR::RangeList(1, SoapySDR::Range(minFreq, BLADERF_FREQUENCY_MAX));
+}
+
+/*******************************************************************
+ * Sample Rate API
+ ******************************************************************/
+
+void bladeRF_SoapySDR::setSampleRate(const int direction, const size_t channel, const double rate)
+{
+    bladerf_rational_rate ratRate;
+    ratRate.integer = uint64_t(rate);
+    ratRate.den = uint64_t(1 << 14); //arbitrary denominator -- should be big enough
+    ratRate.num = uint64_t(rate - ratRate.integer) * ratRate.den;
+
+    //stash the approximate hardware time so it can be restored
+    const long long timeNow = this->getHardwareTime();
+
+    int ret = bladerf_set_rational_sample_rate(_dev, _dir2mod(direction), &ratRate, NULL);
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_set_rational_sample_rate(%f) returned %s", rate, _err2str(ret).c_str());
+        throw std::runtime_error("setSampleRate() " + _err2str(ret));
+    }
+
+    //stash the actual rate
+    const double actual = this->getSampleRate(direction, channel);
+    if (direction == SOAPY_SDR_RX)
+    {
+        _rxSampRate = actual;
+        this->updateRxMinTimeoutMs();
+    }
+    if (direction == SOAPY_SDR_TX)
+    {
+        _txSampRate = actual;
+    }
+
+    //restore the previous hardware time setting (after rate stash)
+    this->setHardwareTime(timeNow);
+
+    SoapySDR::logf(SOAPY_SDR_INFO, "setSampleRate(%d, %f MHz), actual = %f MHz", direction, rate/1e6, actual/1e6);
+}
+
+double bladeRF_SoapySDR::getSampleRate(const int direction, const size_t) const
+{
+    bladerf_rational_rate ratRate;
+    int ret = bladerf_get_rational_sample_rate(_dev, _dir2mod(direction), &ratRate);
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_get_rational_sample_rate() returned %s", _err2str(ret).c_str());
+        throw std::runtime_error("getSampleRate() " + _err2str(ret));
+    }
+
+    return double(ratRate.integer) + (double(ratRate.num)/double(ratRate.den));
+}
+
+std::vector<double> bladeRF_SoapySDR::listSampleRates(const int, const size_t) const
+{
+    std::vector<double> options;
+    for (double r = 160e3; r <= 200e3; r += 40e3) options.push_back(r);
+    for (double r = 300e3; r <= 900e3; r += 100e3) options.push_back(r);
+    for (double r = 1e6; r <= 40e6; r += 1e6) options.push_back(r);
+    //options.push_back(BLADERF_SAMPLERATE_MIN);
+    //options.push_back(BLADERF_SAMPLERATE_REC_MAX);
+    return options;
+}
+
+void bladeRF_SoapySDR::setBandwidth(const int direction, const size_t, const double bw)
+{
+    //bypass the filter when sufficiently large BW is selected
+    if (bw > BLADERF_BANDWIDTH_MAX)
+    {
+        bladerf_set_lpf_mode(_dev, _dir2mod(direction), BLADERF_LPF_BYPASSED);
+        return;
+    }
+
+    //otherwise set to normal and configure the filter bandwidth
+    bladerf_set_lpf_mode(_dev, _dir2mod(direction), BLADERF_LPF_NORMAL);
+    int ret = bladerf_set_bandwidth(_dev, _dir2mod(direction), (unsigned int)(bw), NULL);
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_set_bandwidth(%f) returned %s", bw, _err2str(ret).c_str());
+        throw std::runtime_error("setBandwidth() " + _err2str(ret));
+    }
+}
+
+double bladeRF_SoapySDR::getBandwidth(const int direction, const size_t) const
+{
+    unsigned int bw = 0;
+    int ret = bladerf_get_bandwidth(_dev, _dir2mod(direction), &bw);
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_get_bandwidth() returned %s", _err2str(ret).c_str());
+        throw std::runtime_error("getBandwidth() " + _err2str(ret));
+    }
+    return bw;
+}
+
+std::vector<double> bladeRF_SoapySDR::listBandwidths(const int, const size_t) const
+{
+    std::vector<double> options;
+    options.push_back(0.75);
+    options.push_back(0.875);
+    options.push_back(1.25);
+    options.push_back(1.375);
+    options.push_back(1.5);
+    options.push_back(1.92);
+    options.push_back(2.5);
+    options.push_back(2.75);
+    options.push_back(3);
+    options.push_back(3.5);
+    options.push_back(4.375);
+    options.push_back(5);
+    options.push_back(6);
+    options.push_back(7);
+    options.push_back(10);
+    options.push_back(14);
+    for (size_t i = 0; i < options.size(); i++) options[i] *= 2e6;
+    //options.push_back(BLADERF_BANDWIDTH_MIN);
+    //options.push_back(BLADERF_BANDWIDTH_MAX);
+    return options;
+}
+
+/*******************************************************************
+ * Time API
+ ******************************************************************/
+
+bool bladeRF_SoapySDR::hasHardwareTime(const std::string &what) const
+{
+    if (not what.empty()) return SoapySDR::Device::hasHardwareTime(what);
+    return true;
+}
+
+long long bladeRF_SoapySDR::getHardwareTime(const std::string &what) const
+{
+    if (not what.empty()) return SoapySDR::Device::getHardwareTime(what);
+    uint64_t ticksNow = 0;
+    const int ret = bladerf_get_timestamp(_dev, BLADERF_MODULE_RX, &ticksNow);
+
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_get_timestamp() returned %s", _err2str(ret).c_str());
+        throw std::runtime_error("getHardwareTime() " + _err2str(ret));
+    }
+
+    return _rxTicksToTimeNs(ticksNow);
+}
+
+void bladeRF_SoapySDR::setHardwareTime(const long long timeNs, const std::string &what)
+{
+    if (not what.empty()) return SoapySDR::Device::setHardwareTime(timeNs, what);
+
+    //reset the counters with GPIO and stash the offset
+    //this is the same as setting the time because
+    //we maintain the offset math within the driver
+
+    int ret = 0;
+    uint32_t original = 0;
+    ret |= bladerf_config_gpio_read(_dev, &original);
+    ret |= bladerf_config_gpio_write(_dev, original & ~(BLADERF_GPIO_TIMESTAMP));
+    ret |= bladerf_config_gpio_write(_dev, original | BLADERF_GPIO_TIMESTAMP);
+
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_config_gpio_read/write() returned %s", _err2str(ret).c_str());
+        throw std::runtime_error("setHardwareTime() " + _err2str(ret));
+    }
+
+    _timeNsOffset = timeNs;
+}
+
+/*******************************************************************
+ * Register API
+ ******************************************************************/
+
+void bladeRF_SoapySDR::writeRegister(const unsigned addr, const unsigned value)
+{
+    const int ret = bladerf_lms_write(_dev, uint8_t(addr), uint8_t(value));
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_lms_write(0x%x) returned %s", addr, _err2str(ret).c_str());
+        throw std::runtime_error("writeRegister() " + _err2str(ret));
+    }
+}
+
+unsigned bladeRF_SoapySDR::readRegister(const unsigned addr) const
+{
+    uint8_t value = 0;
+    const int ret = bladerf_lms_read(_dev, uint8_t(addr), &value);
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_lms_read(0x%x) returned %s", addr, _err2str(ret).c_str());
+        throw std::runtime_error("readRegister() " + _err2str(ret));
+    }
+    return value;
+}
+
+/*******************************************************************
+* Settings API
+******************************************************************/
+
+SoapySDR::ArgInfoList bladeRF_SoapySDR::getSettingInfo(void) const
+{
+    SoapySDR::ArgInfoList setArgs;
+
+    // XB200 setting
+    SoapySDR::ArgInfo xb200SettingArg;
+    xb200SettingArg.key = "xb200";
+    xb200SettingArg.value = "disabled";
+    xb200SettingArg.name = "XB200 Transverter";
+    xb200SettingArg.description = "bladeRF XB200 Transverter Board";
+    xb200SettingArg.type = SoapySDR::ArgInfo::STRING;
+    xb200SettingArg.options.push_back("disabled");
+    xb200SettingArg.optionNames.push_back("Disabled");
+    xb200SettingArg.options.push_back("50M");
+    xb200SettingArg.optionNames.push_back("Filterbank: 50M");
+    xb200SettingArg.options.push_back("144M");
+    xb200SettingArg.optionNames.push_back("Filterbank: 144M");
+    xb200SettingArg.options.push_back("222M");
+    xb200SettingArg.optionNames.push_back("Filterbank: 222M");
+    xb200SettingArg.options.push_back("auto1db");
+    xb200SettingArg.optionNames.push_back("Filterbank: Auto (1dB)");
+    xb200SettingArg.options.push_back("auto3db");
+    xb200SettingArg.optionNames.push_back("Filterbank: Auto (3dB)");
+    xb200SettingArg.options.push_back("auto");
+    xb200SettingArg.optionNames.push_back("Filterbank: Auto");
+    xb200SettingArg.options.push_back("custom");
+    xb200SettingArg.optionNames.push_back("Filterbank: Custom");
+
+    setArgs.push_back(xb200SettingArg);
+
+    // Sampling mode
+    SoapySDR::ArgInfo samplingModeArg;
+    samplingModeArg.key = "sampling_mode";
+    samplingModeArg.value = "internal";
+    samplingModeArg.name = "Sampling Mode";
+    samplingModeArg.description = "Internal = Via RX/TX connectors, External = Direct sampling from J60/J61 connectors";
+    samplingModeArg.type = SoapySDR::ArgInfo::STRING;
+    samplingModeArg.options.push_back("internal");
+    samplingModeArg.optionNames.push_back("Internal (Default)");
+    samplingModeArg.options.push_back("external");
+    samplingModeArg.optionNames.push_back("Direct Sampling");
+
+    setArgs.push_back(samplingModeArg);
+
+    // Loopback
+    SoapySDR::ArgInfo lookbackArg;
+    lookbackArg.key = "loopback";
+    lookbackArg.value = "disabled";
+    lookbackArg.name = "Loopback Mode";
+    lookbackArg.description = "Enable/disable internal loopback";
+    lookbackArg.type = SoapySDR::ArgInfo::STRING;
+    lookbackArg.options.push_back("disabled");
+    lookbackArg.optionNames.push_back("Disabled");
+    lookbackArg.options.push_back("firmware");
+    lookbackArg.optionNames.push_back("FX3 Firmware");
+    lookbackArg.options.push_back("bb_txlpf_rxvga2");
+    lookbackArg.optionNames.push_back("Baseband: TXLPF to RXVGA2");
+    lookbackArg.options.push_back("bb_txvga1_rxvga2");
+    lookbackArg.optionNames.push_back("Baseband: TXVGA1 to RXVGA2");
+    lookbackArg.options.push_back("bb_txlpf_rxlpf");
+    lookbackArg.optionNames.push_back("Baseband: TXLPF to RXLPF");
+    lookbackArg.options.push_back("bb_txvga1_rxlpf");
+    lookbackArg.optionNames.push_back("Baseband: TXVGA1 to RXLPF");
+    lookbackArg.options.push_back("rf_lna1");
+    lookbackArg.optionNames.push_back("RF: TXMIX to LNA1");
+    lookbackArg.options.push_back("rf_lna2");
+    lookbackArg.optionNames.push_back("RF: TXMIX to LNA2");
+    lookbackArg.options.push_back("rf_lna3");
+    lookbackArg.optionNames.push_back("RF: TXMIX to LNA3");
+
+    setArgs.push_back(lookbackArg);
+
+    return setArgs;
+}
+
+void bladeRF_SoapySDR::writeSetting(const std::string &key, const std::string &value)
+{
+    if (key == "xb200")
+    {
+        // Verify that a valid setting has arrived
+        std::vector<std::string> xb200_validSettings{ "disabled", "50M", "144M", "222M", "auto1db", "auto3db", "auto", "custom" };
+        if (std::find(std::begin(xb200_validSettings), std::end(xb200_validSettings), value) != std::end(xb200_validSettings))
+        {
+            // --> Valid setting has arrived
+
+            // Get attached expansion device
+            bladerf_xb _bladerf_xb_attached = bladerf_xb::BLADERF_XB_NONE;
+            bladerf_expansion_get_attached(_dev, &_bladerf_xb_attached);
+
+            // If "disabled," ensure board is bypassed, if present, and return
+            if (value == "disabled")
+            {
+                if (_bladerf_xb_attached == bladerf_xb::BLADERF_XB_200)
+                {
+                    // Apply bypass around connected XB200
+                    SoapySDR::logf(SOAPY_SDR_INFO, "bladeRF: Disabling connected XB200 by bypassing signal path");
+                    bladerf_xb200_set_path(_dev, bladerf_module::BLADERF_MODULE_RX, bladerf_xb200_path::BLADERF_XB200_BYPASS);
+                }
+
+                return;
+            }
+
+            // Attach the XB200, if it isn't already attached
+            if (_bladerf_xb_attached == bladerf_xb::BLADERF_XB_NONE)
+            {
+                if (bladerf_expansion_attach(_dev, bladerf_xb::BLADERF_XB_200))
+                {
+                    SoapySDR::logf(SOAPY_SDR_ERROR, "bladeRF: Could not attach to XB200");
+                    return;
+                }
+            }
+            SoapySDR::logf(SOAPY_SDR_INFO, "bladeRF: XB200 is attached");
+
+            // Which filterbank was selected?
+            bladerf_xb200_filter filter = bladerf_xb200_filter::BLADERF_XB200_AUTO_1DB;
+
+            if (value == "50M")
+            {
+                // 50-54 MHz (6 meter band) filterbank
+                filter = bladerf_xb200_filter::BLADERF_XB200_50M;
+            }
+            else if (value == "144M")
+            {
+                // 144-148 MHz (2 meter band) filterbank
+                filter = bladerf_xb200_filter::BLADERF_XB200_144M;
+            }
+            else if (value == "222M")
+            {
+                // 222-225 MHz (1.25 meter band) filterbank
+                // Note that this filter option is technically wider, covering 206-235 MHz
+                filter = bladerf_xb200_filter::BLADERF_XB200_222M;
+            }
+            else if (value == "auto1db")
+            {
+                // The other filter options are automatically selected depending on the RX or TX
+                // module's current frequency, based upon the 1dB points of the on-board filters
+                // For frequencies outside the range of the on-board filters, the custom path is used
+                filter = bladerf_xb200_filter::BLADERF_XB200_AUTO_1DB;
+            }
+            else if (value == "auto3db")
+            {
+                // The other filter options are automatically selected depending on the RX or TX
+                // module's current frequency, based upon the 3dB points of the on-board filters
+                // For frequencies outside the range of the on-board filters, the custom path is used
+                filter = bladerf_xb200_filter::BLADERF_XB200_AUTO_3DB;
+            }
+            else if (value == "custom")
+            {
+                // The custom filter bank path across the FILT and FILT-ANT SMA connectors
+                filter = bladerf_xb200_filter::BLADERF_XB200_CUSTOM;
+            }
+            else
+            {
+                // Default: Auto, 1dB points
+                // The other filter options are automatically selected depending on the RX or TX
+                // module's current frequency, based upon the 1dB points of the on-board filters
+                // For frequencies outside the range of the on-board filters, the custom path is used
+                filter = bladerf_xb200_filter::BLADERF_XB200_AUTO_1DB;
+            }
+
+            // Set the filterbank
+            SoapySDR::logf(SOAPY_SDR_INFO, "bladeRF: Set XB200 filterbank '%s'", value.c_str());
+            int ret = bladerf_xb200_set_filterbank(_dev, bladerf_module::BLADERF_MODULE_RX, filter);
+            if (ret != 0)
+            {
+                SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_xb200_set_filterbank(%s) returned %s", value.c_str(), _err2str(ret).c_str());
+                throw std::runtime_error("writeSetting() " + _err2str(ret));
+            }
+
+            // Check signal path
+            bladerf_xb200_path _bladerf_xb200_path = bladerf_xb200_path::BLADERF_XB200_MIX;
+            bladerf_xb200_get_path(_dev, bladerf_module::BLADERF_MODULE_RX, &_bladerf_xb200_path);
+            if (_bladerf_xb200_path != bladerf_xb200_path::BLADERF_XB200_MIX)
+            {
+                // Apply mix path through XB200
+                SoapySDR::logf(SOAPY_SDR_INFO, "bladeRF: Adjusting mix path through XB200");
+                bladerf_xb200_set_path(_dev, bladerf_module::BLADERF_MODULE_RX, bladerf_xb200_path::BLADERF_XB200_MIX);
+            }
+        }
+        else
+        {
+            // --> Invalid setting has arrived
+            SoapySDR::logf(SOAPY_SDR_ERROR, "bladeRF: Invalid XB200 setting '%s'", value.c_str());
+            //throw std::runtime_error("writeSetting(" + key + "," + value + ") unknown value");
+        }
+    }
+    else if (key == "sampling_mode")
+    {
+        /* Configure the sampling of the LMS6002D to be either internal or external.
+        ** Internal sampling will read from the RXVGA2 driver internal to the chip.
+        ** External sampling will connect the ADC inputs to the external inputs for direct sampling.
+        */
+
+        // Verify that a valid setting has arrived
+        std::vector<std::string> sampling_mode_validSettings{ "internal", "external" };
+        if (std::find(std::begin(sampling_mode_validSettings), std::end(sampling_mode_validSettings), value) != std::end(sampling_mode_validSettings))
+        {
+            // --> Valid setting has arrived
+
+            // Set the sampling mode
+            int ret = 0;
+            if (value == "external")
+            {
+                // External/direct sampling
+                SoapySDR::logf(SOAPY_SDR_INFO, "bladeRF: Set sampling mode to direct/external sampling", value.c_str());
+                ret = bladerf_set_sampling(_dev, bladerf_sampling::BLADERF_SAMPLING_EXTERNAL);
+            }
+            else
+            {
+                // Default: Internal
+                SoapySDR::logf(SOAPY_SDR_INFO, "bladeRF: Set sampling mode to internal sampling", value.c_str());
+                ret = bladerf_set_sampling(_dev, bladerf_sampling::BLADERF_SAMPLING_INTERNAL);
+            }
+            if (ret != 0)
+            {
+                SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_set_sampling(%s) returned %s", value.c_str(), _err2str(ret).c_str());
+                throw std::runtime_error("writeSetting() " + _err2str(ret));
+            }
+        }
+        else
+        {
+            // --> Invalid setting has arrived
+            SoapySDR::logf(SOAPY_SDR_ERROR, "bladeRF: Invalid sampling mode '%s'", value.c_str());
+            //throw std::runtime_error("writeSetting(" + key + "," + value + ") unknown value");
+        }
+    }
+    else if (key == "loopback")
+    {
+        // Verify that a valid setting has arrived
+        std::vector<std::string> loopback_validSettings{ "disabled", "firmware", "bb_txlpf_rxvga2", "bb_txvga1_rxvga2", "bb_txlpf_rxlpf", "bb_txvga1_rxlpf", "rf_lna1", "rf_lna2", "rf_lna3" };
+        if (std::find(std::begin(loopback_validSettings), std::end(loopback_validSettings), value) != std::end(loopback_validSettings))
+        {
+            // --> Valid setting has arrived
+
+            // Which loopback mode was selected?
+            bladerf_loopback loopback = bladerf_loopback::BLADERF_LB_NONE;
+
+            if (value == "firmware")
+            {
+                // Firmware loopback inside of the FX3
+                loopback = bladerf_loopback::BLADERF_LB_FIRMWARE;
+            }
+            else if (value == "bb_txlpf_rxvga2")
+            {
+                // Baseband loopback. TXLPF output is connected to the RXVGA2 input.
+                loopback = bladerf_loopback::BLADERF_LB_BB_TXLPF_RXVGA2;
+            }
+            else if (value == "bb_txvga1_rxvga2")
+            {
+                // Baseband loopback. TXVGA1 output is connected to the RXVGA2 input.
+                loopback = bladerf_loopback::BLADERF_LB_BB_TXVGA1_RXVGA2;
+            }
+            else if (value == "bb_txlpf_rxlpf")
+            {
+                // Baseband loopback. TXLPF output is connected to the RXLPF input.
+                loopback = bladerf_loopback::BLADERF_LB_BB_TXLPF_RXLPF;
+            }
+            else if (value == "bb_txvga1_rxlpf")
+            {
+                // Baseband loopback. TXVGA1 output is connected to RXLPF input.
+                loopback = bladerf_loopback::BLADERF_LB_BB_TXVGA1_RXLPF;
+            }
+            else if (value == "rf_lna1")
+            {
+                // RF loopback. The TXMIX output, through the AUX PA, is connected to the output of LNA1.
+                loopback = bladerf_loopback::BLADERF_LB_RF_LNA1;
+            }
+            else if (value == "rf_lna2")
+            {
+                // RF loopback. The TXMIX output, through the AUX PA, is connected to the output of LNA2.
+                loopback = bladerf_loopback::BLADERF_LB_RF_LNA2;
+            }
+            else if (value == "rf_lna3")
+            {
+                // RF loopback. The TXMIX output, through the AUX PA, is connected to the output of LNA3.
+                loopback = bladerf_loopback::BLADERF_LB_RF_LNA3;
+            }
+            else
+            {
+                // Default: Disabled
+                // Disables loopback and returns to normal operation
+                loopback = bladerf_loopback::BLADERF_LB_NONE;
+            }
+
+            // If the loopback isn't already set, set the loopback
+            bladerf_loopback _bladerf_loopback = bladerf_loopback::BLADERF_LB_NONE;
+            bladerf_get_loopback(_dev, &_bladerf_loopback);
+            if (_bladerf_loopback != loopback)
+            {
+                SoapySDR::logf(SOAPY_SDR_INFO, "bladeRF: Loopback set '%s'", value.c_str());
+                int ret = bladerf_set_loopback(_dev, loopback);
+                if (ret != 0)
+                {
+                    SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_set_loopback(%s) returned %s", value.c_str(), _err2str(ret).c_str());
+                    throw std::runtime_error("writeSetting() " + _err2str(ret));
+                }
+            }
+        }
+        else
+        {
+            // --> Invalid setting has arrived
+            SoapySDR::logf(SOAPY_SDR_ERROR, "bladeRF: Invalid loopback setting '%s'", value.c_str());
+            //throw std::runtime_error("writeSetting(" + key + "," + value + ") unknown value");
+        }
+    }
+    else
+    {
+        throw std::runtime_error("writeSetting(" + key + ") unknown setting");
+    }
+}
+
+/*******************************************************************
+ * GPIO API
+ ******************************************************************/
+
+std::vector<std::string> bladeRF_SoapySDR::listGPIOBanks(void) const
+{
+    std::vector<std::string> banks;
+    banks.push_back("CONFIG");
+    banks.push_back("EXPANSION");
+    return banks;
+}
+
+void bladeRF_SoapySDR::writeGPIO(const std::string &bank, const unsigned value)
+{
+    int ret = 0;
+    if (bank == "CONFIG")
+    {
+        ret = bladerf_config_gpio_write(_dev, value);
+    }
+    else if (bank == "EXPANSION")
+    {
+        ret = bladerf_expansion_gpio_write(_dev, value);
+    }
+    else throw std::runtime_error("writeGPIO("+bank+") unknown bank name");
+
+    if (ret != 0) throw std::runtime_error("writeGPIO("+bank+") " + _err2str(ret));
+}
+
+void bladeRF_SoapySDR::writeGPIO(const std::string &bank, const unsigned value, const unsigned mask)
+{
+    #if defined(LIBBLADERF_API_VERSION) && (LIBBLADERF_API_VERSION >= 0x01050000)
+    if (bank == "EXPANSION")
+    {
+        int ret = bladerf_expansion_gpio_masked_write(_dev, mask, value);
+        if (ret != 0) throw std::runtime_error("writeGPIODir("+bank+") " + _err2str(ret));
+        return;
+    }
+    #endif
+    return SoapySDR::Device::writeGPIO(bank, value, mask);
+}
+
+unsigned bladeRF_SoapySDR::readGPIO(const std::string &bank) const
+{
+    uint32_t value = 0;
+    int ret = 0;
+    if (bank == "CONFIG")
+    {
+        ret = bladerf_config_gpio_read(_dev, &value);
+    }
+    else if (bank == "EXPANSION")
+    {
+        ret = bladerf_expansion_gpio_read(_dev, &value);
+    }
+    else throw std::runtime_error("readGPIO("+bank+") unknown bank name");
+
+    if (ret != 0) throw std::runtime_error("readGPIO("+bank+") " + _err2str(ret));
+    return value;
+}
+
+void bladeRF_SoapySDR::writeGPIODir(const std::string &bank, const unsigned dir)
+{
+    int ret = 0;
+    if (bank == "CONFIG")
+    {
+        throw std::runtime_error("data direction not configurable for CONFIG bank");
+    }
+    else if (bank == "EXPANSION")
+    {
+        ret = bladerf_expansion_gpio_dir_write(_dev, dir);
+    }
+    else throw std::runtime_error("writeGPIODir("+bank+") unknown bank name");
+
+    if (ret != 0) throw std::runtime_error("writeGPIODir("+bank+") " + _err2str(ret));
+}
+
+void bladeRF_SoapySDR::writeGPIODir(const std::string &bank, const unsigned dir, const unsigned mask)
+{
+    #if defined(LIBBLADERF_API_VERSION) && (LIBBLADERF_API_VERSION >= 0x01050000)
+    if (bank == "EXPANSION")
+    {
+        int ret = bladerf_expansion_gpio_dir_masked_write(_dev, mask, dir);
+        if (ret != 0) throw std::runtime_error("writeGPIODir("+bank+") " + _err2str(ret));
+        return;
+    }
+    #endif
+    return SoapySDR::Device::writeGPIODir(bank, dir, mask);
+}
+
+unsigned bladeRF_SoapySDR::readGPIODir(const std::string &bank) const
+{
+    uint32_t value = 0;
+    int ret = 0;
+    if (bank == "CONFIG")
+    {
+        throw std::runtime_error("data direction not configurable for CONFIG bank");
+    }
+    else if (bank == "EXPANSION")
+    {
+        ret = bladerf_expansion_gpio_dir_read(_dev, &value);
+    }
+    else throw std::runtime_error("readGPIODir("+bank+") unknown bank name");
+
+    if (ret != 0) throw std::runtime_error("readGPIODir("+bank+") " + _err2str(ret));
+    return value;
+}
diff --git a/bladeRF_SoapySDR.hpp b/bladeRF_SoapySDR.hpp
new file mode 100644
index 0000000..f696080
--- /dev/null
+++ b/bladeRF_SoapySDR.hpp
@@ -0,0 +1,312 @@
+/*
+ * This file is part of the bladeRF project:
+ *   http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015-2016 Josh Blum
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include <SoapySDR/Device.hpp>
+#include <SoapySDR/Time.hpp>
+#include <libbladeRF.h>
+#include <cstdio>
+#include <queue>
+
+/*!
+ * Storage for rx commands and tx responses
+ */
+struct StreamMetadata
+{
+    int flags;
+    long long timeNs;
+    size_t numElems;
+    int code;
+};
+
+/*!
+ * The SoapySDR device interface for a blade RF.
+ * The overloaded virtual methods calls into the blade RF C API.
+ */
+class bladeRF_SoapySDR : public SoapySDR::Device
+{
+public:
+
+    //! initialize blade RF from device info
+    bladeRF_SoapySDR(const bladerf_devinfo &devinfo);
+
+    //! destructor shuts down and cleans up
+    ~bladeRF_SoapySDR(void);
+
+    /*******************************************************************
+     * Identification API
+     ******************************************************************/
+
+    std::string getDriverKey(void) const
+    {
+        return "bladeRF";
+    }
+
+    std::string getHardwareKey(void) const
+    {
+        return "bladeRF";
+    }
+
+    SoapySDR::Kwargs getHardwareInfo(void) const;
+
+    /*******************************************************************
+     * Channels API
+     ******************************************************************/
+
+    size_t getNumChannels(const int) const
+    {
+        return 1;
+    }
+
+    bool getFullDuplex(const int, const size_t) const
+    {
+        return true;
+    }
+
+    /*******************************************************************
+     * Stream API
+     ******************************************************************/
+    std::vector<std::string> getStreamFormats(const int direction, const size_t channel) const;
+
+    std::string getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const;
+
+    SoapySDR::ArgInfoList getStreamArgsInfo(const int direction, const size_t channel) const;
+
+    SoapySDR::Stream *setupStream(
+        const int direction,
+        const std::string &format,
+        const std::vector<size_t> &channels = std::vector<size_t>(),
+        const SoapySDR::Kwargs &args = SoapySDR::Kwargs());
+
+    void closeStream(SoapySDR::Stream *stream);
+
+    size_t getStreamMTU(SoapySDR::Stream *stream) const;
+
+    int activateStream(
+        SoapySDR::Stream *stream,
+        const int flags = 0,
+        const long long timeNs = 0,
+        const size_t numElems = 0);
+
+    int deactivateStream(
+        SoapySDR::Stream *stream,
+        const int flags = 0,
+        const long long timeNs = 0);
+
+    int readStream(
+        SoapySDR::Stream *stream,
+        void * const *buffs,
+        const size_t numElems,
+        int &flags,
+        long long &timeNs,
+        const long timeoutUs = 100000);
+
+    int writeStream(
+        SoapySDR::Stream *stream,
+        const void * const *buffs,
+        const size_t numElems,
+        int &flags,
+        const long long timeNs = 0,
+        const long timeoutUs = 100000);
+
+    int readStreamStatus(
+        SoapySDR::Stream *stream,
+        size_t &chanMask,
+        int &flags,
+        long long &timeNs,
+        const long timeoutUs
+    );
+
+    /*******************************************************************
+     * Antenna API
+     ******************************************************************/
+
+    std::vector<std::string> listAntennas(const int direction, const size_t channel) const;
+
+    void setAntenna(const int direction, const size_t channel, const std::string &name);
+
+    std::string getAntenna(const int direction, const size_t channel) const;
+
+    /*******************************************************************
+     * Gain API
+     ******************************************************************/
+
+    std::vector<std::string> listGains(const int direction, const size_t channel) const;
+
+    void setGain(const int direction, const size_t channel, const double value);
+
+    void setGain(const int direction, const size_t channel, const std::string &name, const double value);
+
+    double getGain(const int direction, const size_t channel, const std::string &name) const;
+
+    SoapySDR::Range getGainRange(const int direction, const size_t channel, const std::string &name) const;
+
+    /*******************************************************************
+     * Frequency API
+     ******************************************************************/
+
+    void setFrequency(const int direction, const size_t channel, const std::string &name, const double frequency, const SoapySDR::Kwargs &args = SoapySDR::Kwargs());
+
+    double getFrequency(const int direction, const size_t channel, const std::string &name) const;
+
+    std::vector<std::string> listFrequencies(const int direction, const size_t channel) const;
+
+    SoapySDR::RangeList getFrequencyRange(const int direction, const size_t channel, const std::string &name) const;
+
+    /*******************************************************************
+     * Sample Rate API
+     ******************************************************************/
+
+    void setSampleRate(const int direction, const size_t channel, const double rate);
+
+    double getSampleRate(const int direction, const size_t channel) const;
+
+    std::vector<double> listSampleRates(const int direction, const size_t channel) const;
+
+    void setBandwidth(const int direction, const size_t channel, const double bw);
+
+    double getBandwidth(const int direction, const size_t channel) const;
+
+    std::vector<double> listBandwidths(const int direction, const size_t channel) const;
+
+    /*******************************************************************
+     * Time API
+     ******************************************************************/
+
+    bool hasHardwareTime(const std::string &what = "") const;
+
+    long long getHardwareTime(const std::string &what = "") const;
+
+    void setHardwareTime(const long long timeNs, const std::string &what = "");
+
+    /*******************************************************************
+     * Register API
+     ******************************************************************/
+
+    void writeRegister(const unsigned addr, const unsigned value);
+
+    unsigned readRegister(const unsigned addr) const;
+
+    /*******************************************************************
+     * Settings API
+     ******************************************************************/
+
+    SoapySDR::ArgInfoList getSettingInfo(void) const;
+
+    void writeSetting(const std::string &key, const std::string &value);
+
+    /*******************************************************************
+     * GPIO API
+     ******************************************************************/
+
+    std::vector<std::string> listGPIOBanks(void) const;
+
+    void writeGPIO(const std::string &bank, const unsigned value);
+
+    void writeGPIO(const std::string &bank, const unsigned value, const unsigned mask);
+
+    unsigned readGPIO(const std::string &bank) const;
+
+    void writeGPIODir(const std::string &bank, const unsigned dir);
+
+    void writeGPIODir(const std::string &bank, const unsigned dir, const unsigned mask);
+
+    unsigned readGPIODir(const std::string &bank) const;
+
+private:
+
+    static bladerf_module _dir2mod(const int direction)
+    {
+        return (direction == SOAPY_SDR_RX)?BLADERF_MODULE_RX:BLADERF_MODULE_TX;
+    }
+
+    static std::string _err2str(const int err)
+    {
+        const char *msg = NULL;
+        switch (err)
+        {
+        case BLADERF_ERR_UNEXPECTED: msg = "An unexpected failure occurred"; break;
+        case BLADERF_ERR_RANGE: msg = "Provided parameter is out of range"; break;
+        case BLADERF_ERR_INVAL: msg = "Invalid operation/parameter"; break;
+        case BLADERF_ERR_MEM: msg = "Memory allocation error"; break;
+        case BLADERF_ERR_IO: msg = "File/Device I/O error"; break;
+        case BLADERF_ERR_TIMEOUT: msg = "Operation timed out"; break;
+        case BLADERF_ERR_NODEV: msg = "No device(s) available"; break;
+        case BLADERF_ERR_UNSUPPORTED: msg = "Operation not supported"; break;
+        case BLADERF_ERR_MISALIGNED: msg = "Misaligned flash access"; break;
+        case BLADERF_ERR_CHECKSUM: msg = "Invalid checksum"; break;
+        case BLADERF_ERR_NO_FILE: msg = "File not found"; break;
+        case BLADERF_ERR_UPDATE_FPGA: msg = "An FPGA update is required"; break;
+        case BLADERF_ERR_UPDATE_FW: msg = "A firmware update is requied"; break;
+        case BLADERF_ERR_TIME_PAST: msg = "Requested timestamp is in the past"; break;
+        default: msg = "Unknown error code"; break;
+        }
+        char buff[256];
+        sprintf(buff, "%d - %s", err, msg);
+        return buff;
+    }
+
+    long long _rxTicksToTimeNs(const long long ticks) const
+    {
+        return SoapySDR::ticksToTimeNs(ticks, _rxSampRate) + _timeNsOffset;
+    }
+
+    long long _timeNsToRxTicks(const long long timeNs) const
+    {
+        return SoapySDR::timeNsToTicks(timeNs-_timeNsOffset, _rxSampRate);
+    }
+
+    long long _txTicksToTimeNs(const long long ticks) const
+    {
+        return SoapySDR::ticksToTimeNs(ticks, _txSampRate) + _timeNsOffset;
+    }
+
+    long long _timeNsToTxTicks(const long long timeNs) const
+    {
+        return SoapySDR::timeNsToTicks(timeNs-_timeNsOffset, _txSampRate);
+    }
+
+    void updateRxMinTimeoutMs(void)
+    {
+        //the 2x factor allows padding so we aren't on the fence
+        _rxMinTimeoutMs = long((2*1000*_rxBuffSize)/_rxSampRate);
+    }
+
+    double _rxSampRate;
+    double _txSampRate;
+    bool _inTxBurst;
+    bool _rxFloats;
+    bool _txFloats;
+    bool _rxOverflow;
+    long long _rxNextTicks;
+    long long _txNextTicks;
+    long long _timeNsOffset;
+    int16_t *_rxConvBuff;
+    int16_t *_txConvBuff;
+    size_t _rxBuffSize;
+    size_t _txBuffSize;
+    long _rxMinTimeoutMs;
+    std::queue<StreamMetadata> _rxCmds;
+    std::queue<StreamMetadata> _txResps;
+
+    bladerf *_dev;
+};
diff --git a/bladeRF_Streaming.cpp b/bladeRF_Streaming.cpp
new file mode 100644
index 0000000..60aca1b
--- /dev/null
+++ b/bladeRF_Streaming.cpp
@@ -0,0 +1,494 @@
+/*
+ * This file is part of the bladeRF project:
+ *   http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015-2016 Josh Blum
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "bladeRF_SoapySDR.hpp"
+#include <SoapySDR/Logger.hpp>
+#include <stdexcept>
+
+//cross platform usleep()
+#ifdef _MSC_VER
+#include <windows.h>
+#define usleep(t) Sleep((t)/1000)
+#else
+#include <unistd.h>
+#endif
+
+#define DEF_NUM_BUFFS 32
+#define DEF_BUFF_LEN 4096
+
+#define STRINGIFY_(x) #x
+#define STRINGIFY(x) STRINGIFY_(x)
+
+std::vector<std::string> bladeRF_SoapySDR::getStreamFormats(const int, const size_t) const
+{
+    std::vector<std::string> formats;
+    formats.push_back("CS16");
+    formats.push_back("CF32");
+    return formats;
+}
+
+std::string bladeRF_SoapySDR::getNativeStreamFormat(const int, const size_t, double &fullScale) const
+{
+    fullScale = 2048;
+    return "CS16";
+}
+
+SoapySDR::ArgInfoList bladeRF_SoapySDR::getStreamArgsInfo(const int, const size_t) const
+{
+    SoapySDR::ArgInfoList streamArgs;
+
+    SoapySDR::ArgInfo buffersArg;
+    buffersArg.key = "buffers";
+    buffersArg.value = STRINGIFY(DEF_NUM_BUFFS);
+    buffersArg.name = "Buffer Count";
+    buffersArg.description = "Number of async USB buffers.";
+    buffersArg.units = "buffers";
+    buffersArg.type = SoapySDR::ArgInfo::INT;
+    streamArgs.push_back(buffersArg);
+
+    SoapySDR::ArgInfo lengthArg;
+    lengthArg.key = "buflen";
+    lengthArg.value = STRINGIFY(DEF_BUFF_LEN);
+    lengthArg.name = "Buffer Length";
+    lengthArg.description = "Number of bytes per USB buffer, the number must be a multiple of 1024.";
+    lengthArg.units = "bytes";
+    lengthArg.type = SoapySDR::ArgInfo::INT;
+    streamArgs.push_back(lengthArg);
+
+    SoapySDR::ArgInfo xfersArg;
+    xfersArg.key = "transfers";
+    xfersArg.value = "0";
+    xfersArg.name = "Num Transfers";
+    xfersArg.description = "Number of async USB transfers. Use 0 for automatic";
+    xfersArg.units = "bytes";
+    xfersArg.type = SoapySDR::ArgInfo::INT;
+    xfersArg.range = SoapySDR::Range(0, 32);
+    streamArgs.push_back(xfersArg);
+
+    return streamArgs;
+}
+
+SoapySDR::Stream *bladeRF_SoapySDR::setupStream(
+    const int direction,
+    const std::string &format,
+    const std::vector<size_t> &channels,
+    const SoapySDR::Kwargs &args)
+{
+    //check the channel configuration
+    if (channels.size() > 1 or (channels.size() > 0 and channels.at(0) != 0))
+    {
+        throw std::runtime_error("setupStream invalid channel selection");
+    }
+
+    //check the format
+    if (format == "CF32") {}
+    else if (format == "CS16") {}
+    else throw std::runtime_error("setupStream invalid format " + format);
+
+    //determine the number of buffers to allocate
+    int numBuffs = (args.count("buffers") == 0)? 0 : atoi(args.at("buffers").c_str());
+    if (numBuffs == 0) numBuffs = DEF_NUM_BUFFS;
+    if (numBuffs == 1) numBuffs++;
+
+    //determine the size of each buffer in samples
+    int bufSize = (args.count("buflen") == 0)? 0 : atoi(args.at("buflen").c_str());
+    if (bufSize == 0) bufSize = DEF_BUFF_LEN;
+    if ((bufSize % 1024) != 0) bufSize = ((bufSize/1024) + 1) * 1024;
+
+    //determine the number of active transfers
+    int numXfers = (args.count("transfers") == 0)? 0 : atoi(args.at("transfers").c_str());
+    if (numXfers == 0) numXfers = numBuffs/2;
+    if (numXfers > numBuffs) numXfers = numBuffs; //cant have more than available buffers
+    if (numXfers > 32) numXfers = 32; //libusb limit
+
+    //setup the stream for sync tx/rx calls
+    int ret = bladerf_sync_config(
+        _dev,
+        _dir2mod(direction),
+        BLADERF_FORMAT_SC16_Q11_META,
+        numBuffs,
+        bufSize,
+        numXfers,
+        1000); //1 second timeout
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_sync_config() returned %d", ret);
+        throw std::runtime_error("setupStream() " + _err2str(ret));
+    }
+
+    //activate the stream here -- only call once
+    ret = bladerf_enable_module(_dev, _dir2mod(direction), true);
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_enable_module(true) returned %d", ret);
+        throw std::runtime_error("setupStream() " + _err2str(ret));
+    }
+
+    if (direction == SOAPY_SDR_RX)
+    {
+        _rxOverflow = false;
+        _rxFloats = (format == "CF32");
+        _rxConvBuff = new int16_t[bufSize*2];
+        _rxBuffSize = bufSize;
+        this->updateRxMinTimeoutMs();
+    }
+
+    if (direction == SOAPY_SDR_TX)
+    {
+        _txFloats = (format == "CF32");
+        _txConvBuff = new int16_t[bufSize*2];
+        _txBuffSize = bufSize;
+    }
+
+    return (SoapySDR::Stream *)(new int(direction));
+}
+
+void bladeRF_SoapySDR::closeStream(SoapySDR::Stream *stream)
+{
+    const int direction = *reinterpret_cast<int *>(stream);
+
+    //deactivate the stream here -- only call once
+    const int ret = bladerf_enable_module(_dev, _dir2mod(direction), false);
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_enable_module(false) returned %s", _err2str(ret).c_str());
+        throw std::runtime_error("closeStream() " + _err2str(ret));
+    }
+
+    //cleanup stream convert buffers
+    if (direction == SOAPY_SDR_RX)
+    {
+        delete [] _rxConvBuff;
+    }
+
+    if (direction == SOAPY_SDR_TX)
+    {
+        delete [] _txConvBuff;
+    }
+
+    delete reinterpret_cast<int *>(stream);
+}
+
+size_t bladeRF_SoapySDR::getStreamMTU(SoapySDR::Stream *stream) const
+{
+    const int direction = *reinterpret_cast<int *>(stream);
+    return (direction == SOAPY_SDR_RX)?_rxBuffSize:_txBuffSize;
+}
+
+int bladeRF_SoapySDR::activateStream(
+    SoapySDR::Stream *stream,
+    const int flags,
+    const long long timeNs,
+    const size_t numElems)
+{
+    const int direction = *reinterpret_cast<int *>(stream);
+
+    if (direction == SOAPY_SDR_RX)
+    {
+        StreamMetadata cmd;
+        cmd.flags = flags;
+        cmd.timeNs = timeNs;
+        cmd.numElems = numElems;
+        _rxCmds.push(cmd);
+    }
+
+    if (direction == SOAPY_SDR_TX)
+    {
+        if (flags != 0) return SOAPY_SDR_NOT_SUPPORTED;
+    }
+
+    return 0;
+}
+
+int bladeRF_SoapySDR::deactivateStream(
+    SoapySDR::Stream *stream,
+    const int flags,
+    const long long)
+{
+    const int direction = *reinterpret_cast<int *>(stream);
+    if (flags != 0) return SOAPY_SDR_NOT_SUPPORTED;
+
+    if (direction == SOAPY_SDR_RX)
+    {
+        //clear all commands when deactivating
+        while (not _rxCmds.empty()) _rxCmds.pop();
+    }
+
+    if (direction == SOAPY_SDR_TX)
+    {
+        //in a burst -> end it
+        if (_inTxBurst)
+        {
+            //initialize metadata
+            bladerf_metadata md;
+            md.timestamp = 0;
+            md.flags = BLADERF_META_FLAG_TX_BURST_END;
+            md.status = 0;
+
+            //send the tx samples
+            _txConvBuff[0] = 0;
+            _txConvBuff[1] = 0;
+            bladerf_sync_tx(_dev, _txConvBuff, 1, &md, 100/*ms*/);
+        }
+        _inTxBurst = false;
+    }
+
+    return 0;
+}
+
+int bladeRF_SoapySDR::readStream(
+    SoapySDR::Stream *,
+    void * const *buffs,
+    size_t numElems,
+    int &flags,
+    long long &timeNs,
+    const long timeoutUs)
+{
+    //clip to the available conversion buffer size
+    numElems = std::min(numElems, _rxBuffSize);
+
+    //extract the front-most command
+    //no command, this is a timeout...
+    if (_rxCmds.empty()) return SOAPY_SDR_TIMEOUT;
+    StreamMetadata &cmd = _rxCmds.front();
+
+    //clear output metadata
+    flags = 0;
+    timeNs = 0;
+
+    //return overflow status indicator
+    if (_rxOverflow)
+    {
+        _rxOverflow = false;
+        flags |= SOAPY_SDR_HAS_TIME;
+        timeNs = _rxTicksToTimeNs(_rxNextTicks);
+        return SOAPY_SDR_OVERFLOW;
+    }
+
+    //initialize metadata
+    bladerf_metadata md;
+    md.timestamp = 0;
+    md.flags = 0;
+    md.status = 0;
+
+    //without a soapy sdr time flag, set the blade rf now flag
+    if ((cmd.flags & SOAPY_SDR_HAS_TIME) == 0) md.flags |= BLADERF_META_FLAG_RX_NOW;
+    md.timestamp = _timeNsToRxTicks(cmd.timeNs);
+    if (cmd.numElems > 0) numElems = std::min(cmd.numElems, numElems);
+    cmd.flags = 0; //clear flags for subsequent calls
+
+    //prepare buffers
+    void *samples = (void *)buffs[0];
+    if (_rxFloats) samples = _rxConvBuff;
+
+    //recv the rx samples
+    const long timeoutMs = std::max(_rxMinTimeoutMs, timeoutUs/1000);
+    int ret = bladerf_sync_rx(_dev, samples, numElems, &md, timeoutMs);
+    if (ret == BLADERF_ERR_TIMEOUT) return SOAPY_SDR_TIMEOUT;
+    if (ret == BLADERF_ERR_TIME_PAST) return SOAPY_SDR_TIME_ERROR;
+    if (ret != 0)
+    {
+        //any error when this is a finite burst causes the command to be removed
+        if (cmd.numElems > 0) _rxCmds.pop();
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_sync_rx() returned %s", _err2str(ret).c_str());
+        return SOAPY_SDR_STREAM_ERROR;
+    }
+
+    //perform the int16 to float conversion
+    if (_rxFloats)
+    {
+        float *output = (float *)buffs[0];
+        for (size_t i = 0; i < 2 * md.actual_count; i++)
+        {
+            output[i] = float(_rxConvBuff[i])/2048;
+        }
+    }
+
+    //unpack the metadata
+    flags |= SOAPY_SDR_HAS_TIME;
+    timeNs = _rxTicksToTimeNs(md.timestamp);
+
+    //parse the status
+    if ((md.status & BLADERF_META_STATUS_OVERRUN) != 0)
+    {
+        SoapySDR::log(SOAPY_SDR_SSI, "0");
+        _rxOverflow = true;
+    }
+
+    //consume from the command if this is a finite burst
+    if (cmd.numElems > 0)
+    {
+        cmd.numElems -= md.actual_count;
+        if (cmd.numElems == 0) _rxCmds.pop();
+    }
+
+    _rxNextTicks = md.timestamp + md.actual_count;
+    return md.actual_count;
+}
+
+int bladeRF_SoapySDR::writeStream(
+    SoapySDR::Stream *,
+    const void * const *buffs,
+    size_t numElems,
+    int &flags,
+    const long long timeNs,
+    const long timeoutUs)
+{
+    //clear EOB when the last sample will not be transmitted
+    if (numElems > _txBuffSize) flags &= ~(SOAPY_SDR_END_BURST);
+
+    //clip to the available conversion buffer size
+    numElems = std::min(numElems, _txBuffSize);
+
+    //initialize metadata
+    bladerf_metadata md;
+    md.timestamp = 0;
+    md.flags = 0;
+    md.status = 0;
+
+    //time and burst start
+    if (_inTxBurst)
+    {
+        if ((flags & SOAPY_SDR_HAS_TIME) != 0)
+        {
+            md.timestamp = _timeNsToTxTicks(timeNs);
+            md.flags |= BLADERF_META_FLAG_TX_UPDATE_TIMESTAMP;
+            _txNextTicks = md.timestamp;
+        }
+    }
+    else
+    {
+        md.flags |= BLADERF_META_FLAG_TX_BURST_START;
+        if ((flags & SOAPY_SDR_HAS_TIME) != 0)
+        {
+            md.timestamp = _timeNsToTxTicks(timeNs);
+        }
+        else
+        {
+            md.flags |= BLADERF_META_FLAG_TX_NOW;
+            bladerf_get_timestamp(_dev, BLADERF_MODULE_TX, &md.timestamp);
+        }
+        _txNextTicks = md.timestamp;
+    }
+
+    //end of burst
+    if ((flags & SOAPY_SDR_END_BURST) != 0)
+    {
+        md.flags |= BLADERF_META_FLAG_TX_BURST_END;
+    }
+
+    //prepare buffers
+    void *samples = (void *)buffs[0];
+    if (_txFloats) samples = _txConvBuff;
+
+    //perform the float to int16 conversion
+    if (_txFloats)
+    {
+        float *input = (float *)buffs[0];
+        for (size_t i = 0; i < 2 * numElems; i++)
+        {
+            _txConvBuff[i] = int16_t(input[i]*2048);
+        }
+    }
+
+    //send the tx samples
+    int ret = bladerf_sync_tx(_dev, samples, numElems, &md, timeoutUs/1000);
+    if (ret == BLADERF_ERR_TIMEOUT) return SOAPY_SDR_TIMEOUT;
+    if (ret == BLADERF_ERR_TIME_PAST) return SOAPY_SDR_TIME_ERROR;
+    if (ret != 0)
+    {
+        SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_sync_tx() returned %s", _err2str(ret).c_str());
+        return SOAPY_SDR_STREAM_ERROR;
+    }
+    _txNextTicks += numElems;
+
+    //always in a burst after successful tx
+    _inTxBurst = true;
+
+    //parse the status
+    if ((md.status & BLADERF_META_STATUS_UNDERRUN) != 0)
+    {
+        SoapySDR::log(SOAPY_SDR_SSI, "U");
+        StreamMetadata resp;
+        resp.flags = 0;
+        resp.code = SOAPY_SDR_UNDERFLOW;
+        _txResps.push(resp);
+    }
+
+    //end burst status message
+    if ((flags & SOAPY_SDR_END_BURST) != 0)
+    {
+        StreamMetadata resp;
+        resp.flags = SOAPY_SDR_END_BURST | SOAPY_SDR_HAS_TIME;
+        resp.timeNs = this->_txTicksToTimeNs(_txNextTicks);
+        resp.code = 0;
+        _txResps.push(resp);
+        _inTxBurst = false;
+    }
+
+    return numElems;
+}
+
+int bladeRF_SoapySDR::readStreamStatus(
+    SoapySDR::Stream *stream,
+    size_t &,
+    int &flags,
+    long long &timeNs,
+    const long timeoutUs
+)
+{
+    const int direction = *reinterpret_cast<int *>(stream);
+    if (direction == SOAPY_SDR_RX) return SOAPY_SDR_NOT_SUPPORTED;
+
+    //wait for an event to be ready considering the timeout and time
+    //this is an emulation by polling and waiting on the hardware time
+    long long timeNowNs = this->getHardwareTime();
+    const long long exitTimeNs = timeNowNs + (timeoutUs*1000);
+    while (true)
+    {
+        //no status to report, sleep for a bit
+        if (_txResps.empty()) goto pollSleep;
+
+        //no time on the current status, done waiting...
+        if ((_txResps.front().flags & SOAPY_SDR_HAS_TIME) == 0) break;
+
+        //current status time expired, done waiting...
+        if (_txResps.front().timeNs < timeNowNs) break;
+
+        //sleep a bit, never more than time remaining
+        pollSleep:
+        usleep(std::min<long>(1000, (exitTimeNs-timeNowNs)/1000));
+
+        //check for timeout expired
+        timeNowNs = this->getHardwareTime();
+        if (exitTimeNs < timeNowNs) return SOAPY_SDR_TIMEOUT;
+    }
+
+    //extract the most recent status event
+    if (_txResps.empty()) return SOAPY_SDR_TIMEOUT;
+    StreamMetadata resp = _txResps.front();
+    _txResps.pop();
+
+    //load the output from the response
+    flags = resp.flags;
+    timeNs = resp.timeNs;
+    return resp.code;
+}
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..17b4c6c
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,41 @@
+soapybladerf (0.3.2) unstable; urgency=low
+
+  * Release 0.3.2 (2016-05-20)
+
+ -- Josh Blum <josh at pothosware.com>  Fri, 20 May 2016 09:41:10 -0700
+
+soapybladerf (0.3.1) unstable; urgency=low
+
+  * Release 0.3.1 (2016-03-01)
+
+ -- Josh Blum <josh at pothosware.com>  Tue, 01 Mar 2016 13:16:19 -0800
+
+soapybladerf (0.3.0) unstable; urgency=low
+
+  * Release 0.3.0 (2015-11-20)
+
+ -- Josh Blum <josh at pothosware.com>  Fri, 23 Oct 2015 18:39:30 -0700
+
+soapybladerf (0.2.0) unstable; urgency=low
+
+  * Release 0.2.0 (2015-10-10)
+
+ -- Josh Blum <josh at pothosware.com>  Sat, 10 Oct 2015 11:26:14 -0700
+
+soapybladerf (0.1.2) unstable; urgency=low
+
+  * Release 0.1.2 (2015-09-16)
+
+ -- Josh Blum <josh at pothosware.com>  Wed, 16 Sep 2015 01:34:58 -0700
+
+soapybladerf (0.1.1) unstable; urgency=low
+
+  * Release 0.1.1 (2015-08-15)
+
+ -- Josh Blum <josh at pothosware.com>  Sat, 15 Aug 2015 11:18:36 -0700
+
+soapybladerf (0.1.0) unstable; urgency=low
+
+  * Release 0.1.0 (2015-07-14)
+
+ -- Josh Blum <josh at pothosware.com>  Mon, 13 Jul 2015 22:02:57 -0700
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..2eaf775
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,23 @@
+Source: soapybladerf
+Section: libs
+Priority: optional
+Maintainer: Josh Blum <josh at pothosware.com>
+Build-Depends:
+    debhelper (>= 9.0.0),
+    cmake,
+    libbladerf-dev,
+    libsoapysdr-dev
+Standards-Version: 3.9.5
+Homepage: https://github.com/pothosware/SoapyBladeRF/wiki
+Vcs-Git: https://github.com/pothosware/SoapyBladeRF.git
+Vcs-Browser: https://github.com/pothosware/SoapyBladeRF
+
+Package: soapysdr-bladerf
+Section: libs
+Architecture: any
+Conflicts: soapyosmo-bladerf
+Replaces: soapyosmo-bladerf
+Pre-Depends: ${misc:Pre-Depends}
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: Soapy BladeRF - BladeRF device support for Soapy SDR.
+ A Soapy module that supports BladeRF devices within the Soapy API.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..51157fc
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,10 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: soapybladerf
+Source: https://github.com/pothosware/SoapyBladeRF/wiki
+
+Files: *
+Copyright: 2014-2016 Josh Blum <josh at pothosware.com>
+License: LGPL-2.1
+ On Debian systems, the full text of the GNU Lesser General Public
+ License version 2.1 can be found in the file
+ `/usr/share/common-licenses/LGPL-2.1'.
diff --git a/debian/docs b/debian/docs
new file mode 100644
index 0000000..b43bf86
--- /dev/null
+++ b/debian/docs
@@ -0,0 +1 @@
+README.md
diff --git a/debian/rules b/debian/rules
new file mode 100644
index 0000000..f7d5e49
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,21 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+
+DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
+export DEB_HOST_MULTIARCH
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# This has to be exported to make some magic below work.
+export DH_OPTIONS
+
+
+%:
+	dh $@ --buildsystem=cmake --parallel
+
+override_dh_auto_configure:
+	dh_auto_configure -- -DLIB_SUFFIX="/$(DEB_HOST_MULTIARCH)"
+
+override_dh_installchangelogs:
+	dh_installchangelogs Changelog.txt
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/self_test.py b/self_test.py
new file mode 100644
index 0000000..b9ba047
--- /dev/null
+++ b/self_test.py
@@ -0,0 +1,46 @@
+import SoapySDR
+from SoapySDR import * #SOAPY_SDR_* constants
+import numpy as np
+
+if __name__ == "__main__":
+    bladerf = SoapySDR.Device(dict(driver="bladerf"))
+    print bladerf
+
+    for i in range(5):
+        print("  Make rx stream #%d"%i)
+        rxStream = bladerf.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [0])
+        for j in range(5):
+            lastTimeNs = 0
+            numSampsTotal = 10000
+            print("    Activate, get %d samples, Deactivate #%d"%(numSampsTotal, j))
+            bladerf.activateStream(rxStream, SOAPY_SDR_END_BURST, 0, numSampsTotal)
+            buff = np.array([0]*1024, np.complex64)
+            while numSampsTotal != 0:
+                sr = bladerf.readStream(rxStream, [buff], buff.size)
+                assert(sr.ret > 0)
+                numSampsTotal -= sr.ret
+                if not (sr.timeNs > lastTimeNs):
+                    print("Fail %s, %d"%(sr, numSampsTotal))
+                assert(sr.timeNs > lastTimeNs)
+                lastTimeNs = sr.timeNs
+            bladerf.deactivateStream(rxStream)
+        bladerf.closeStream(rxStream)
+
+    for i in range(5):
+        print("  Make tx stream #%d"%i)
+        txStream = bladerf.setupStream(SOAPY_SDR_TX, SOAPY_SDR_CF32, [0])
+        for j in range(5):
+            numSampsTotal = 10000
+            print("    Activate, send %d samples, Deactivate #%d"%(numSampsTotal, j))
+            bladerf.activateStream(txStream)
+            buff = np.array([0]*1024, np.complex64)
+            while numSampsTotal != 0:
+                size = min(buff.size, numSampsTotal)
+                flags = 0
+                #if size == numSampsTotal: flags |= SOAPY_SDR_END_BURST
+                sr = bladerf.writeStream(txStream, [buff], size, flags)
+                if not (sr.ret > 0): print("Fail %s, %d"%(sr, numSampsTotal))
+                assert(sr.ret > 0)
+                numSampsTotal -= sr.ret
+            bladerf.deactivateStream(txStream)
+        bladerf.closeStream(txStream)

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-hamradio/soapybladerf.git



More information about the pkg-hamradio-commits mailing list