[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: [](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