[hamradio-commits] [soapyosmo] 01/08: New upstream version 0.2.5

Andreas E. Bombe aeb at moszumanska.debian.org
Sat Aug 12 20:06:19 UTC 2017


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

aeb pushed a commit to branch master
in repository soapyosmo.

commit 9303121533334f9ec1442ce150dee5d0fb541295
Author: Andreas Bombe <aeb at debian.org>
Date:   Sun Aug 6 17:40:08 2017 -0400

    New upstream version 0.2.5
---
 CMakeLists.txt                                     |   9 +
 Changelog.txt                                      |   8 +
 GrOsmoSDRInterface.hpp                             |  38 +-
 debian/changelog                                   |   6 +
 debian/control                                     |  12 +-
 ....install => soapysdr0.6-module-mirisdr.install} |   0
 ....install => soapysdr0.6-module-osmosdr.install} |   0
 ....install => soapysdr0.6-module-rfspace.install} |   0
 gr-osmosdr/AUTHORS                                 |   1 +
 gr-osmosdr/CMakeLists.txt                          |   2 +
 gr-osmosdr/README                                  |   1 +
 gr-osmosdr/cmake/Modules/FindLibFreeSRP.cmake      |  27 +
 gr-osmosdr/grc/gen_osmosdr_blocks.py               |   2 +
 gr-osmosdr/lib/CMakeLists.txt                      |   8 +
 gr-osmosdr/lib/airspy/airspy_fir_kernels.h         | 101 +++
 gr-osmosdr/lib/airspy/airspy_source_c.cc           |  72 +-
 gr-osmosdr/lib/bladerf/CMakeLists.txt              |   3 +-
 gr-osmosdr/lib/bladerf/bladerf_sink_c.cc           |   7 +-
 gr-osmosdr/lib/bladerf/bladerf_source_c.cc         |  19 +-
 gr-osmosdr/lib/config.h.in                         |   1 +
 gr-osmosdr/lib/device.cc                           |  16 +
 gr-osmosdr/lib/{soapy => freesrp}/CMakeLists.txt   |  15 +-
 gr-osmosdr/lib/freesrp/freesrp_common.cc           | 199 ++++++
 gr-osmosdr/lib/freesrp/freesrp_common.h            |  29 +
 gr-osmosdr/lib/freesrp/freesrp_sink_c.cc           | 280 ++++++++
 gr-osmosdr/lib/freesrp/freesrp_sink_c.h            | 130 ++++
 gr-osmosdr/lib/freesrp/freesrp_source_c.cc         | 341 +++++++++
 gr-osmosdr/lib/freesrp/freesrp_source_c.h          | 131 ++++
 .../lib/freesrp/readerwriterqueue/LICENSE.md       |  28 +
 gr-osmosdr/lib/freesrp/readerwriterqueue/README.md | 114 +++
 .../lib/freesrp/readerwriterqueue/atomicops.h      | 577 ++++++++++++++++
 .../freesrp/readerwriterqueue/readerwriterqueue.h  | 764 +++++++++++++++++++++
 gr-osmosdr/lib/hackrf/hackrf_sink_c.cc             |  27 +-
 gr-osmosdr/lib/hackrf/hackrf_source_c.cc           |  27 +-
 gr-osmosdr/lib/rfspace/rfspace_source_c.cc         |  37 +
 gr-osmosdr/lib/rfspace/rfspace_source_c.h          |   3 +
 gr-osmosdr/lib/rtl/rtl_source_c.cc                 |   2 +-
 gr-osmosdr/lib/rtl_tcp/rtl_tcp_source_c.cc         |   2 +-
 gr-osmosdr/lib/sink_impl.cc                        |  16 +
 gr-osmosdr/lib/soapy/CMakeLists.txt                |   1 +
 gr-osmosdr/lib/soapy/soapy_common.cc               |  43 ++
 gr-osmosdr/lib/soapy/soapy_common.h                |  40 ++
 gr-osmosdr/lib/soapy/soapy_sink_c.cc               |  13 +-
 gr-osmosdr/lib/soapy/soapy_source_c.cc             |   9 +-
 gr-osmosdr/lib/source_impl.cc                      |  19 +
 45 files changed, 3103 insertions(+), 77 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5abc04f..0750edb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -237,6 +237,7 @@ find_package(LibMiriSDR)
 find_package(LibHackRF)
 find_package(LibAIRSPY)
 find_package(LibbladeRF)
+find_package(LibFreeSRP)
 
 ########################################################################
 # module builder
@@ -408,6 +409,14 @@ GR_INCLUDE_SUBDIRECTORY(redpitaya)
 endif(ENABLE_REDPITAYA)
 
 ########################################################################
+# Setup FreeSRP component
+########################################################################
+GR_REGISTER_COMPONENT("FreeSRP support" ENABLE_FREESRP LIBFREESRP_FOUND)
+if(ENABLE_FREESRP)
+GR_INCLUDE_SUBDIRECTORY(freesrp)
+endif(ENABLE_FREESRP)
+
+########################################################################
 # Print Summary
 ########################################################################
 GR_PRINT_COMPONENT_SUMMARY()
diff --git a/Changelog.txt b/Changelog.txt
index b555f12..cb76334 100644
--- a/Changelog.txt
+++ b/Changelog.txt
@@ -1,3 +1,11 @@
+Release 0.2.5 (2017-04-29)
+==========================
+
+- Add support for FreeSRP
+- Added setFrequencyCorrection() and getFrequencyCorrection()
+- Added getBandwidthRange() and getSampleRateRange()
+- Support for optional gain range step in type conversions
+
 Release 0.2.4 (2016-09-01)
 ==========================
 
diff --git a/GrOsmoSDRInterface.hpp b/GrOsmoSDRInterface.hpp
index fe0f7ef..f6699dd 100644
--- a/GrOsmoSDRInterface.hpp
+++ b/GrOsmoSDRInterface.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014-2015 Free Software Foundation, Inc.
+ * Copyright 2014-2017 Free Software Foundation, Inc.
  *
  * This file is part of GrOsmoSDR support modules
  *
@@ -21,6 +21,7 @@
 
 #pragma once
 #include <SoapySDR/Device.hpp>
+#include <SoapySDR/Version.hpp>
 #include <boost/shared_ptr.hpp>
 #include "sink_iface.h"
 #include "source_iface.h"
@@ -234,6 +235,19 @@ public:
         if (dir == SOAPY_SDR_RX and _source) _source->set_iq_balance(balance, channel);
     }
 
+    void setFrequencyCorrection(const int dir, const size_t channel, const double value)
+    {
+        if (dir == SOAPY_SDR_TX and _sink) _sink->set_freq_corr(value, channel);
+        if (dir == SOAPY_SDR_RX and _source) _source->set_freq_corr(value, channel);
+    }
+
+    double getFrequencyCorrection(const int dir, const size_t channel) const
+    {
+        if (dir == SOAPY_SDR_TX and _sink) return _sink->get_freq_corr(channel);
+        if (dir == SOAPY_SDR_RX and _source) return _source->get_freq_corr(channel);
+        return SoapySDR::Device::getFrequencyCorrection(dir, channel);
+    }
+
     /*******************************************************************
      * Gain support
      ******************************************************************/
@@ -393,6 +407,13 @@ public:
         return SoapySDR::Device::listSampleRates(dir, channel);
     }
 
+    SoapySDR::RangeList getSampleRateRange(const int dir, const size_t channel) const
+    {
+        if (dir == SOAPY_SDR_TX and _sink) return this->toRangeList(_sink->get_sample_rates());
+        if (dir == SOAPY_SDR_RX and _source) return this->toRangeList(_source->get_sample_rates());
+        return SoapySDR::Device::getSampleRateRange(dir, channel);
+    }
+
     void setBandwidth(const int dir, const size_t channel, const double bw)
     {
         if (dir == SOAPY_SDR_TX and _sink) _sink->set_bandwidth(bw, channel);
@@ -413,6 +434,13 @@ public:
         return SoapySDR::Device::listBandwidths(dir, channel);
     }
 
+    SoapySDR::RangeList getBandwidthRange(const int dir, const size_t channel) const
+    {
+        if (dir == SOAPY_SDR_TX and _sink) return this->toRangeList(_sink->get_bandwidth_range(channel));
+        if (dir == SOAPY_SDR_RX and _source) return this->toRangeList(_source->get_bandwidth_range(channel));
+        return SoapySDR::Device::getBandwidthRange(dir, channel);
+    }
+
     /*******************************************************************
      * Clocking support
      ******************************************************************/
@@ -488,7 +516,11 @@ private:
         SoapySDR::RangeList out;
         for (size_t i = 0; i < ranges.size(); i++)
         {
+            #ifdef SOAPY_SDR_API_HAS_RANGE_TYPE_STEP
+            out.push_back(SoapySDR::Range(ranges[i].start(), ranges[i].stop(), ranges[i].step()));
+            #else
             out.push_back(SoapySDR::Range(ranges[i].start(), ranges[i].stop()));
+            #endif
         }
         return out;
     }
@@ -496,7 +528,11 @@ private:
     template <typename RangeType>
     SoapySDR::Range toRange(const RangeType &ranges) const
     {
+        #ifdef SOAPY_SDR_API_HAS_RANGE_TYPE_STEP
+        return SoapySDR::Range(ranges.start(), ranges.stop(), ranges.step());
+        #else
         return SoapySDR::Range(ranges.start(), ranges.stop());
+        #endif
     }
 
     template <typename RangeType>
diff --git a/debian/changelog b/debian/changelog
index c47e296..d88be53 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+soapyosmo (0.2.5-1) unstable; urgency=low
+
+  * Release 0.2.5 (2017-04-29)
+
+ -- Josh Blum <josh at pothosware.com>  Sat, 29 Apr 2017 15:03:27 -0000
+
 soapyosmo (0.2.4) unstable; urgency=low
 
   * Release 0.2.4 (2016-09-01)
diff --git a/debian/control b/debian/control
index 640f007..7224669 100644
--- a/debian/control
+++ b/debian/control
@@ -22,7 +22,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}
 Description: Soapy Osmo - shared library
  Soapy SDR plugins for OsmoSDR devices
 
-Package: soapysdr0.5-2-module-osmosdr
+Package: soapysdr0.6-module-osmosdr
 Architecture: any
 Multi-Arch: same
 Depends:
@@ -35,14 +35,14 @@ Description: Soapy Osmo - Osmo SDR module
 
 Package: soapysdr-module-osmosdr
 Architecture: all
-Depends: soapysdr0.5-2-module-osmosdr, ${misc:Depends}
+Depends: soapysdr0.6-module-osmosdr, ${misc:Depends}
 Description: Soapy Osmo - Osmo SDR module
  Soapy SDR plugins for OsmoSDR devices
  .
  This is an empty dependency package that pulls in the OsmoSDR module
  for the default version of libsoapysdr.
 
-Package: soapysdr0.5-2-module-rfspace
+Package: soapysdr0.6-module-rfspace
 Architecture: any
 Multi-Arch: same
 Depends:
@@ -55,14 +55,14 @@ Description: Soapy Osmo - RFSPACE module
 
 Package: soapysdr-module-rfspace
 Architecture: all
-Depends: soapysdr0.5-2-module-rfspace, ${misc:Depends}
+Depends: soapysdr0.6-module-rfspace, ${misc:Depends}
 Description: Soapy Osmo - RFSPACE module
  Soapy SDR plugins for OsmoSDR devices
  .
  This is an empty dependency package that pulls in the RFSPACE module
  for the default version of libsoapysdr.
 
-Package: soapysdr0.5-2-module-mirisdr
+Package: soapysdr0.6-module-mirisdr
 Architecture: any
 Multi-Arch: same
 Depends:
@@ -75,7 +75,7 @@ Description: Soapy Osmo - Mirics SDR module
 
 Package: soapysdr-module-mirisdr
 Architecture: all
-Depends: soapysdr0.5-2-module-mirisdr, ${misc:Depends}
+Depends: soapysdr0.6-module-mirisdr, ${misc:Depends}
 Description: Soapy Osmo - Mirics SDR module
  Soapy SDR plugins for OsmoSDR devices
  .
diff --git a/debian/soapysdr0.5-2-module-mirisdr.install b/debian/soapysdr0.6-module-mirisdr.install
similarity index 100%
rename from debian/soapysdr0.5-2-module-mirisdr.install
rename to debian/soapysdr0.6-module-mirisdr.install
diff --git a/debian/soapysdr0.5-2-module-osmosdr.install b/debian/soapysdr0.6-module-osmosdr.install
similarity index 100%
rename from debian/soapysdr0.5-2-module-osmosdr.install
rename to debian/soapysdr0.6-module-osmosdr.install
diff --git a/debian/soapysdr0.5-2-module-rfspace.install b/debian/soapysdr0.6-module-rfspace.install
similarity index 100%
rename from debian/soapysdr0.5-2-module-rfspace.install
rename to debian/soapysdr0.6-module-rfspace.install
diff --git a/gr-osmosdr/AUTHORS b/gr-osmosdr/AUTHORS
index b654b84..2fd882c 100644
--- a/gr-osmosdr/AUTHORS
+++ b/gr-osmosdr/AUTHORS
@@ -6,3 +6,4 @@ Josh Blum
 SDRplay Ltd.
 Pavel Demin
 Marcus Müller
+Lukas Lao Beyer
diff --git a/gr-osmosdr/CMakeLists.txt b/gr-osmosdr/CMakeLists.txt
index 32e7ff5..296456d 100644
--- a/gr-osmosdr/CMakeLists.txt
+++ b/gr-osmosdr/CMakeLists.txt
@@ -169,8 +169,10 @@ find_package(LibSDRplay)
 endif(ENABLE_NONFREE)
 find_package(LibHackRF)
 find_package(LibAIRSPY)
+find_package(Volk)
 find_package(LibbladeRF)
 find_package(SoapySDR NO_MODULE)
+find_package(LibFreeSRP)
 find_package(Doxygen)
 
 if(NOT GNURADIO_RUNTIME_FOUND)
diff --git a/gr-osmosdr/README b/gr-osmosdr/README
index bc995a6..67fa475 100644
--- a/gr-osmosdr/README
+++ b/gr-osmosdr/README
@@ -17,6 +17,7 @@ as well supports:
  * Ettus USRP Devices through Ettus UHD library
  * Fairwaves UmTRX through Fairwaves' fork of UHD
  * Red Pitaya SDR transceiver (http://bazaar.redpitaya.com)
+ * FreeSRP through libfreesrp
 
 By using the OsmoSDR block you can take advantage of a common software api in
 your application(s) independent of the underlying radio hardware.
diff --git a/gr-osmosdr/cmake/Modules/FindLibFreeSRP.cmake b/gr-osmosdr/cmake/Modules/FindLibFreeSRP.cmake
new file mode 100644
index 0000000..f1e4e74
--- /dev/null
+++ b/gr-osmosdr/cmake/Modules/FindLibFreeSRP.cmake
@@ -0,0 +1,27 @@
+if(NOT LIBFREESRP_FOUND)
+  pkg_check_modules (LIBFREESRP_PKG libfreesrp)
+  find_path(LIBFREESRP_INCLUDE_DIRS NAMES freesrp.hpp
+    PATHS
+    ${LIBFREESRP_PKG_INCLUDE_DIRS}
+    /usr/include
+    /usr/local/include
+  )
+
+  find_library(LIBFREESRP_LIBRARIES NAMES freesrp
+    PATHS
+    ${LIBFREESRP_PKG_LIBRARY_DIRS}
+    /usr/lib
+    /usr/local/lib
+  )
+
+if(LIBFREESRP_INCLUDE_DIRS AND LIBFREESRP_LIBRARIES)
+  set(LIBFREESRP_FOUND TRUE CACHE INTERNAL "libfreesrp found")
+  message(STATUS "Found libfreesrp: ${LIBFREESRP_INCLUDE_DIRS}, ${LIBFREESRP_LIBRARIES}")
+else(LIBFREESRP_INCLUDE_DIRS AND LIBFREESRP_LIBRARIES)
+  set(LIBFREESRP_FOUND FALSE CACHE INTERNAL "libfreesrp found")
+  message(STATUS "libfreesrp not found.")
+endif(LIBFREESRP_INCLUDE_DIRS AND LIBFREESRP_LIBRARIES)
+
+mark_as_advanced(LIBFREESRP_LIBRARIES LIBFREESRP_INCLUDE_DIRS)
+
+endif(NOT LIBFREESRP_FOUND)
diff --git a/gr-osmosdr/grc/gen_osmosdr_blocks.py b/gr-osmosdr/grc/gen_osmosdr_blocks.py
index ad36d39..3daa8a0 100644
--- a/gr-osmosdr/grc/gen_osmosdr_blocks.py
+++ b/gr-osmosdr/grc/gen_osmosdr_blocks.py
@@ -229,6 +229,7 @@ While primarily being developed for the OsmoSDR hardware, this block as well sup
  * Ettus USRP Devices through Ettus UHD library
  * Fairwaves UmTRX through Fairwaves' fork of UHD
  * Red Pitaya SDR transceiver (http://bazaar.redpitaya.com)
+ * FreeSRP through libfreesrp library
 
 By using the osmocom $sourk block you can take advantage of a common software api in your application(s) independent of the underlying radio hardware.
 
@@ -264,6 +265,7 @@ Lines ending with ... mean it's possible to bind devices together by specifying
   file='/path/to/your file',rate=1e6[,freq=100e6][,append=true][,throttle=true] ...
 #end if
   redpitaya=192.168.1.100[:1001]
+  freesrp=0[,fx3='path/to/fx3.img',fpga='path/to/fpga.bin',loopback]
   hackrf=0[,buffers=32][,bias=0|1][,bias_tx=0|1]
   bladerf=0[,tamer=internal|external|external_1pps][,smb=25e6]
   uhd[,serial=...][,lo_offset=0][,mcr=52e6][,nchan=2][,subdev='\\\\'B:0 A:0\\\\''] ...
diff --git a/gr-osmosdr/lib/CMakeLists.txt b/gr-osmosdr/lib/CMakeLists.txt
index a99890a..c05b8d9 100644
--- a/gr-osmosdr/lib/CMakeLists.txt
+++ b/gr-osmosdr/lib/CMakeLists.txt
@@ -241,6 +241,14 @@ GR_INCLUDE_SUBDIRECTORY(redpitaya)
 endif(ENABLE_REDPITAYA)
 
 ########################################################################
+# Setup FreeSRP component
+########################################################################
+GR_REGISTER_COMPONENT("FreeSRP support" ENABLE_FREESRP LIBFREESRP_FOUND)
+if(ENABLE_FREESRP)
+GR_INCLUDE_SUBDIRECTORY(freesrp)
+endif(ENABLE_FREESRP)
+
+########################################################################
 # Setup configuration file
 ########################################################################
 ADD_DEFINITIONS(-DHAVE_CONFIG_H=1)
diff --git a/gr-osmosdr/lib/airspy/airspy_fir_kernels.h b/gr-osmosdr/lib/airspy/airspy_fir_kernels.h
new file mode 100644
index 0000000..e15f4e1
--- /dev/null
+++ b/gr-osmosdr/lib/airspy/airspy_fir_kernels.h
@@ -0,0 +1,101 @@
+#pragma once
+
+#define KERNEL_16_110_LEN   7
+const float KERNEL_16_110[] =
+{
+    -0.031835079193115234f,
+     0.000000000000000000f,
+     0.281831502914428710f,
+     0.500007271766662600f,
+     0.281831502914428710f,
+     0.000000000000000000f,
+    -0.031835079193115234f
+};
+
+#define KERNEL_8_100_LEN    11
+const float KERNEL_8_100[] =
+{
+     0.006633400917053223f,
+     0.000000000000000000f,
+    -0.051035523414611816f,
+     0.000000000000000000f,
+     0.294403314590454100f,
+     0.499997496604919430f,
+     0.294403314590454100f,
+     0.000000000000000000f,
+    -0.051035523414611816f,
+     0.000000000000000000f,
+     0.006633400917053223f
+};
+
+#define KERNEL_4_90_LEN     15
+const float KERNEL_4_90[] =
+{
+    -0.002474188804626465f,
+     0.000000000000000000f,
+     0.016965746879577637f,
+     0.000000000000000000f,
+    -0.067680597305297852f,
+     0.000000000000000000f,
+     0.303180575370788570f,
+     0.500017046928405760f,
+     0.303180575370788570f,
+     0.000000000000000000f,
+    -0.067680597305297852f,
+     0.000000000000000000f,
+     0.016965746879577637f,
+     0.000000000000000000f,
+    -0.002474188804626465f
+};
+
+#define KERNEL_2_80_LEN     47
+const float KERNEL_2_80[KERNEL_2_80_LEN] =
+{
+    -0.000198006629943848f,
+     0.000000000000000000f,
+     0.000576853752136230f,
+     0.000000000000000000f,
+    -0.001352190971374512f,
+     0.000000000000000000f,
+     0.002729177474975586f,
+     0.000000000000000000f,
+    -0.004988193511962891f,
+     0.000000000000000000f,
+     0.008499503135681152f,
+     0.000000000000000000f,
+    -0.013788580894470215f,
+     0.000000000000000000f,
+     0.021713137626647949f,
+     0.000000000000000000f,
+    -0.033980011940002441f,
+     0.000000000000000000f,
+     0.054944872856140137f,
+     0.000000000000000000f,
+    -0.100657463073730470f,
+     0.000000000000000000f,
+     0.316457390785217290f,
+     0.500000000000000000f,
+     0.316457390785217290f,
+     0.000000000000000000f,
+    -0.100657463073730470f,
+     0.000000000000000000f,
+     0.054944872856140137f,
+     0.000000000000000000f,
+    -0.033980011940002441f,
+     0.000000000000000000f,
+     0.021713137626647949f,
+     0.000000000000000000f,
+    -0.013788580894470215f,
+     0.000000000000000000f,
+     0.008499503135681152f,
+     0.000000000000000000f,
+    -0.004988193511962891f,
+     0.000000000000000000f,
+     0.002729177474975586f,
+     0.000000000000000000f,
+    -0.001352190971374512f,
+     0.000000000000000000f,
+     0.000576853752136230f,
+     0.000000000000000000f,
+    -0.000198006629943848f
+};
diff --git a/gr-osmosdr/lib/airspy/airspy_source_c.cc b/gr-osmosdr/lib/airspy/airspy_source_c.cc
index 25f73d4..50150e5 100644
--- a/gr-osmosdr/lib/airspy/airspy_source_c.cc
+++ b/gr-osmosdr/lib/airspy/airspy_source_c.cc
@@ -40,18 +40,24 @@
 #include <gnuradio/io_signature.h>
 
 #include "airspy_source_c.h"
+#include "airspy_fir_kernels.h"
 
 #include "arg_helpers.h"
 
 using namespace boost::assign;
 
+#define AIRSPY_FORMAT_ERROR(ret, msg) \
+  boost::str( boost::format(msg " (%1%) %2%") \
+    % ret % airspy_error_name((enum airspy_error)ret) )
+
 #define AIRSPY_THROW_ON_ERROR(ret, msg) \
-  if ( ret != AIRSPY_SUCCESS )  \
-  throw std::runtime_error( boost::str( boost::format(msg " (%d) %s") \
-      % ret % airspy_error_name((enum airspy_error)ret) ) );
+  if ( ret != AIRSPY_SUCCESS ) \
+  { \
+    throw std::runtime_error( AIRSPY_FORMAT_ERROR(ret, msg) ); \
+  }
 
 #define AIRSPY_FUNC_STR(func, arg) \
-  boost::str(boost::format(func "(%d)") % arg) + " has failed"
+  boost::str(boost::format(func "(%1%)") % arg) + " has failed"
 
 airspy_source_c_sptr make_airspy_source_c (const std::string & args)
 {
@@ -181,11 +187,17 @@ airspy_source_c::~airspy_source_c ()
     if ( airspy_is_streaming( _dev ) == AIRSPY_TRUE )
     {
       ret = airspy_stop_rx( _dev );
-      AIRSPY_THROW_ON_ERROR(ret, "Failed to stop RX streaming")
+      if ( ret != AIRSPY_SUCCESS )
+      {
+        std::cerr << AIRSPY_FORMAT_ERROR(ret, "Failed to stop RX streaming") << std::endl;
+      }
     }
 
     ret = airspy_close( _dev );
-    AIRSPY_THROW_ON_ERROR(ret, "Failed to close AirSpy")
+    if ( ret != AIRSPY_SUCCESS )
+    {
+      std::cerr << AIRSPY_FORMAT_ERROR(ret, "Failed to close AirSpy") << std::endl;
+    }
     _dev = NULL;
   }
 
@@ -638,12 +650,58 @@ std::string airspy_source_c::get_antenna( size_t chan )
 
 double airspy_source_c::set_bandwidth( double bandwidth, size_t chan )
 {
+  if (bandwidth == 0.f)
+    return get_bandwidth( chan );
+
+  {
+    int     ret;
+    int     decim;
+    int     size;
+    const float  *kernel;
+
+    decim = (int)(_sample_rate / bandwidth);
+//    if (decim < 2)
+//    {
+//      kernel = 0;
+//      size = 0;
+//    }
+//    else
+    if (decim < 4)
+    {
+      kernel = KERNEL_2_80;
+      size = KERNEL_2_80_LEN;
+    }
+    else if (decim < 8)
+    {
+      kernel = KERNEL_4_90;
+      size = KERNEL_4_90_LEN;
+    }
+    else if (decim < 16)
+    {
+      kernel = KERNEL_8_100;
+      size = KERNEL_8_100_LEN;
+    }
+    else
+    {
+      kernel = KERNEL_16_110;
+      size = KERNEL_16_110_LEN;
+    }
+
+    if (size)
+    {
+      std::cerr << "  Airspy decim:" << decim
+                << "  kernel size:" << size << std::endl;
+      ret = airspy_set_conversion_filter_float32(_dev, kernel, size);
+      AIRSPY_THROW_ON_ERROR(ret, "Failed to set IQ conversion filter")
+    }
+  }
+
   return get_bandwidth( chan );
 }
 
 double airspy_source_c::get_bandwidth( size_t chan )
 {
-  return 10e6;
+  return _sample_rate;
 }
 
 osmosdr::freq_range_t airspy_source_c::get_bandwidth_range( size_t chan )
diff --git a/gr-osmosdr/lib/bladerf/CMakeLists.txt b/gr-osmosdr/lib/bladerf/CMakeLists.txt
index 922dbe1..c253a22 100644
--- a/gr-osmosdr/lib/bladerf/CMakeLists.txt
+++ b/gr-osmosdr/lib/bladerf/CMakeLists.txt
@@ -24,6 +24,7 @@
 include_directories(
     ${CMAKE_CURRENT_SOURCE_DIR}
     ${LIBBLADERF_INCLUDE_DIRS}
+    ${VOLK_INCLUDE_DIRS}
 )
 
 set(bladerf_srcs
@@ -36,4 +37,4 @@ set(bladerf_srcs
 # Append gnuradio-osmosdr library sources
 ########################################################################
 list(APPEND gr_osmosdr_srcs ${bladerf_srcs})
-list(APPEND gr_osmosdr_libs ${LIBBLADERF_LIBRARIES})
+list(APPEND gr_osmosdr_libs ${LIBBLADERF_LIBRARIES} ${VOLK_LIBRARIES})
diff --git a/gr-osmosdr/lib/bladerf/bladerf_sink_c.cc b/gr-osmosdr/lib/bladerf/bladerf_sink_c.cc
index 5c4b45f..7019629 100644
--- a/gr-osmosdr/lib/bladerf/bladerf_sink_c.cc
+++ b/gr-osmosdr/lib/bladerf/bladerf_sink_c.cc
@@ -38,6 +38,8 @@
 #include <gnuradio/tags.h>
 #include <gnuradio/sync_block.h>
 
+#include <volk/volk.h>
+
 #include "arg_helpers.h"
 #include "bladerf_sink_c.h"
 
@@ -259,10 +261,7 @@ int bladerf_sink_c::work( int noutput_items,
   }
 
   /* Convert floating point samples into fixed point */
-  for (int i = 0; i < 2 * noutput_items;) {
-    _conv_buf[i++] = (int16_t)(scaling * real(*in));
-    _conv_buf[i++] = (int16_t)(scaling * imag(*in++));
-  }
+  volk_32f_s32f_convert_16i(_conv_buf, (float*)in, scaling, 2 * noutput_items);
 
   if (_use_metadata) {
     ret = transmit_with_tags(noutput_items);
diff --git a/gr-osmosdr/lib/bladerf/bladerf_source_c.cc b/gr-osmosdr/lib/bladerf/bladerf_source_c.cc
index ac002db..17aeacf 100644
--- a/gr-osmosdr/lib/bladerf/bladerf_source_c.cc
+++ b/gr-osmosdr/lib/bladerf/bladerf_source_c.cc
@@ -36,6 +36,8 @@
 
 #include <gnuradio/io_signature.h>
 
+#include <volk/volk.h>
+
 #include "arg_helpers.h"
 #include "bladerf_source_c.h"
 #include "osmosdr/source.h"
@@ -139,8 +141,7 @@ int bladerf_source_c::work( int noutput_items,
                             gr_vector_void_star &output_items )
 {
   int ret;
-  int16_t *current;
-  const float scaling = 1.0f / 2048.0f;
+  const float scaling = 2048.0f;
   gr_complex *out = static_cast<gr_complex *>(output_items[0]);
   struct bladerf_metadata meta;
   struct bladerf_metadata *meta_ptr = NULL;
@@ -183,20 +184,8 @@ int bladerf_source_c::work( int noutput_items,
       _consecutive_failures = 0;
   }
 
-  current = _conv_buf;
-
   /* Convert them from fixed to floating point */
-  for (int i = 0; i < noutput_items; ++i) {
-    float x, y;
-
-    x = scaling * *current;
-    current++;
-
-    y = scaling * *current;
-    current++;
-
-    out[i] = gr_complex(x, y) ;
-  }
+  volk_16i_s32f_convert_32f((float*)out, _conv_buf, scaling, 2*noutput_items);
 
   return noutput_items;
 }
diff --git a/gr-osmosdr/lib/config.h.in b/gr-osmosdr/lib/config.h.in
index 1e843a8..42e72f1 100644
--- a/gr-osmosdr/lib/config.h.in
+++ b/gr-osmosdr/lib/config.h.in
@@ -18,6 +18,7 @@
 #cmakedefine ENABLE_AIRSPY
 #cmakedefine ENABLE_SOAPY
 #cmakedefine ENABLE_REDPITAYA
+#cmakedefine ENABLE_FREESRP
 
 //provide NAN define for MSVC older than VC12
 #if defined(_MSC_VER) && (_MSC_VER < 1800)
diff --git a/gr-osmosdr/lib/device.cc b/gr-osmosdr/lib/device.cc
index 3ce40fa..025a22b 100644
--- a/gr-osmosdr/lib/device.cc
+++ b/gr-osmosdr/lib/device.cc
@@ -78,10 +78,18 @@
 #include <airspy_source_c.h>
 #endif
 
+#ifdef ENABLE_SOAPY
+#include <soapy_source_c.h>
+#endif
+
 #ifdef ENABLE_REDPITAYA
 #include <redpitaya_source_c.h>
 #endif
 
+#ifdef ENABLE_FREESRP
+#include <freesrp_source_c.h>
+#endif
+
 #include "arg_helpers.h"
 
 using namespace osmosdr;
@@ -178,6 +186,14 @@ devices_t device::find(const device_t &hint)
   BOOST_FOREACH( std::string dev, airspy_source_c::get_devices() )
     devices.push_back( device_t(dev) );
 #endif
+#ifdef ENABLE_FREESRP
+  BOOST_FOREACH( std::string dev, freesrp_source_c::get_devices() )
+    devices.push_back( device_t(dev) );
+#endif
+#ifdef ENABLE_SOAPY
+  BOOST_FOREACH( std::string dev, soapy_source_c::get_devices() )
+    devices.push_back( device_t(dev) );
+#endif
 
   /* software-only sources should be appended at the very end,
    * hopefully resulting in hardware sources to be shown first
diff --git a/gr-osmosdr/lib/soapy/CMakeLists.txt b/gr-osmosdr/lib/freesrp/CMakeLists.txt
similarity index 77%
copy from gr-osmosdr/lib/soapy/CMakeLists.txt
copy to gr-osmosdr/lib/freesrp/CMakeLists.txt
index 6ada023..46df7e4 100644
--- a/gr-osmosdr/lib/soapy/CMakeLists.txt
+++ b/gr-osmosdr/lib/freesrp/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2015 Free Software Foundation, Inc.
+# Copyright 2012 Free Software Foundation, Inc.
 #
 # This file is part of GNU Radio
 #
@@ -23,16 +23,17 @@
 
 include_directories(
     ${CMAKE_CURRENT_SOURCE_DIR}
-    ${SoapySDR_INCLUDE_DIRS}
+    ${LIBFREESRP_INCLUDE_DIRS}
 )
 
-set(soapy_srcs
-    ${CMAKE_CURRENT_SOURCE_DIR}/soapy_source_c.cc
-    ${CMAKE_CURRENT_SOURCE_DIR}/soapy_sink_c.cc
+set(freesrp_srcs
+    ${CMAKE_CURRENT_SOURCE_DIR}/freesrp_common.cc
+    ${CMAKE_CURRENT_SOURCE_DIR}/freesrp_source_c.cc
+    ${CMAKE_CURRENT_SOURCE_DIR}/freesrp_sink_c.cc
 )
 
 ########################################################################
 # Append gnuradio-osmosdr library sources
 ########################################################################
-list(APPEND gr_osmosdr_srcs ${soapy_srcs})
-list(APPEND gr_osmosdr_libs ${SoapySDR_LIBRARIES})
+list(APPEND gr_osmosdr_srcs ${freesrp_srcs})
+list(APPEND gr_osmosdr_libs ${LIBFREESRP_LIBRARIES})
diff --git a/gr-osmosdr/lib/freesrp/freesrp_common.cc b/gr-osmosdr/lib/freesrp/freesrp_common.cc
new file mode 100644
index 0000000..77db220
--- /dev/null
+++ b/gr-osmosdr/lib/freesrp/freesrp_common.cc
@@ -0,0 +1,199 @@
+#include "freesrp_common.h"
+
+#include <cstdlib>
+
+#include <boost/make_shared.hpp>
+#include <boost/assign.hpp>
+
+#include <arg_helpers.h>
+
+using namespace FreeSRP;
+using namespace std;
+using namespace boost::assign;
+
+boost::shared_ptr<::FreeSRP::FreeSRP> freesrp_common::_srp;
+
+freesrp_common::freesrp_common(const string &args)
+{
+    dict_t dict = params_to_dict(args);
+
+    if(!_srp)
+    {
+        try
+        {
+	    string serial = "";
+
+	    if(dict.count("freesrp"))
+	    {
+		serial = dict["freesrp"];
+	    }
+
+	    if(dict.count("fx3"))
+            {
+                if(Util::find_fx3())
+                {
+                    // Upload firmware to FX3
+                    string firmware_path = string(getenv("HOME")) + "/.freesrp/fx3.img";
+                    if(dict["fx3"].length() > 0)
+                    {
+                        firmware_path = dict["fx3"];
+                    }
+                    Util::find_fx3(true, firmware_path);
+                    cout << "FX3 programmed with '" << firmware_path << "'" << endl;
+                    // Give FX3 time to re-enumerate
+                    this_thread::sleep_for(chrono::milliseconds(600));
+                }
+                else
+                {
+                    cout << "No FX3 in bootloader mode found" << endl;
+                }
+            }
+
+            _srp.reset(new ::FreeSRP::FreeSRP(serial));
+
+            if(dict.count("fpga") || !_srp->fpga_loaded())
+            {
+                string bitstream_path = string(getenv("HOME")) + "/.freesrp/fpga.bin";
+                if(dict["fpga"].length() > 0)
+                {
+                    bitstream_path = dict["fpga"];
+                }
+                fpga_status stat = _srp->load_fpga(bitstream_path);
+                switch(stat)
+                {
+                case FPGA_CONFIG_ERROR:
+                    throw runtime_error("Could not load FPGA configuration!");
+                case FPGA_CONFIG_SKIPPED:
+                    cout << "FPGA already configured. Restart the FreeSRP to load a new bitstream." << endl;
+                    break;
+                case FPGA_CONFIG_DONE:
+                    cout << "FPGA configured with '" << bitstream_path << "'" << endl;
+                    break;
+                }
+            }
+
+            cout << "Connected to FreeSRP" << endl;
+
+            if(dict.count("loopback"))
+            {
+                response res = _srp->send_cmd({SET_LOOPBACK_EN, 1});
+                if(res.error == CMD_OK)
+                {
+                    cout << "AD9364 in loopback mode" << endl;
+                }
+                else
+                {
+                    throw runtime_error("Could not put AD9364 into loopback mode!");
+                }
+            }
+            else
+            {
+                response res = _srp->send_cmd({SET_LOOPBACK_EN, 0});
+                if(res.error != CMD_OK)
+                {
+                    throw runtime_error("Error disabling AD9364 loopback mode!");
+                }
+            }
+
+	    if(dict.count("ignore_overflow"))
+	    {
+		_ignore_overflow = true;
+	    }
+	    else
+	    {
+		_ignore_overflow = false;
+	    }
+        }
+        catch(const runtime_error& e)
+        {
+            cerr << "FreeSRP Error: " << e.what() << endl;
+            throw runtime_error(e.what());
+        }
+    }
+}
+
+vector<string> freesrp_common::get_devices()
+{
+    vector<string> devices;
+
+    try
+    {
+        ::FreeSRP::FreeSRP srp;
+
+        string str;
+        str = "freesrp=0,label='FreeSRP'";
+
+        devices.push_back(str);
+    }
+    catch(const ConnectionError &err)
+    {
+        // No FreeSRP found.
+    }
+
+    return devices;
+}
+
+size_t freesrp_common::get_num_channels( void )
+{
+    return 1;
+}
+
+osmosdr::meta_range_t freesrp_common::get_sample_rates( void )
+{
+    osmosdr::meta_range_t range;
+
+    // Any sample rate between 1e6 and 61.44e6 can be requested.
+    // This list of some integer values is used instead of
+    //       range += osmosdr::range_t(1e6, 61.44e6);
+    // because SoapyOsmo seems to handle the range object differently.
+    range += osmosdr::range_t(1e6);
+    range += osmosdr::range_t(8e6);
+    range += osmosdr::range_t(16e6);
+    range += osmosdr::range_t(20e6);
+    range += osmosdr::range_t(40e6);
+    range += osmosdr::range_t(50e6);
+    range += osmosdr::range_t(61.44e6);
+
+    return range;
+}
+
+osmosdr::freq_range_t freesrp_common::get_freq_range(size_t chan)
+{
+    osmosdr::meta_range_t freq_ranges;
+
+    freq_ranges.push_back(osmosdr::range_t(7e7, 6e9, 2.4));
+
+    return freq_ranges;
+}
+
+
+osmosdr::freq_range_t freesrp_common::get_bandwidth_range(size_t chan)
+{
+    osmosdr::meta_range_t range;
+
+    //range += osmosdr::range_t(2e5, 56e6);
+
+    range += osmosdr::range_t(2e5);
+    range += osmosdr::range_t(1e6);
+    range += osmosdr::range_t(8e6);
+    range += osmosdr::range_t(16e6);
+    range += osmosdr::range_t(20e6);
+    range += osmosdr::range_t(40e6);
+    range += osmosdr::range_t(50e6);
+    range += osmosdr::range_t(56e6);
+
+    return range;
+}
+
+
+double freesrp_common::set_freq_corr( double ppm, size_t chan )
+{
+    // TODO: Set DCXO tuning
+    return 0;
+}
+
+double freesrp_common::get_freq_corr( size_t chan )
+{
+    // TODO: Get DCXO tuning
+    return 0;
+}
diff --git a/gr-osmosdr/lib/freesrp/freesrp_common.h b/gr-osmosdr/lib/freesrp/freesrp_common.h
new file mode 100644
index 0000000..9a5687c
--- /dev/null
+++ b/gr-osmosdr/lib/freesrp/freesrp_common.h
@@ -0,0 +1,29 @@
+#ifndef INCLUDED_FREESRP_COMMON_H
+#define INCLUDED_FREESRP_COMMON_H
+
+#include <vector>
+#include <string>
+
+#include "osmosdr/ranges.h"
+
+#include <freesrp.hpp>
+
+class freesrp_common
+{
+protected:
+    freesrp_common(const std::string &args);
+public:
+    static std::vector<std::string> get_devices();
+
+    size_t get_num_channels( void );
+    osmosdr::meta_range_t get_sample_rates( void );
+    osmosdr::freq_range_t get_freq_range( size_t chan = 0 );
+    osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 );
+    double set_freq_corr( double ppm, size_t chan = 0 );
+    double get_freq_corr( size_t chan = 0 );
+protected:
+    static boost::shared_ptr<::FreeSRP::FreeSRP> _srp;
+    bool _ignore_overflow = false;
+};
+
+#endif
diff --git a/gr-osmosdr/lib/freesrp/freesrp_sink_c.cc b/gr-osmosdr/lib/freesrp/freesrp_sink_c.cc
new file mode 100644
index 0000000..fe692f4
--- /dev/null
+++ b/gr-osmosdr/lib/freesrp/freesrp_sink_c.cc
@@ -0,0 +1,280 @@
+#include "freesrp_sink_c.h"
+
+using namespace FreeSRP;
+using namespace std;
+
+freesrp_sink_c_sptr make_freesrp_sink_c (const string &args)
+{
+    return gnuradio::get_initial_sptr(new freesrp_sink_c (args));
+}
+
+/*
+ * Specify constraints on number of input and output streams.
+ * This info is used to construct the input and output signatures
+ * (2nd & 3rd args to gr_block's constructor).  The input and
+ * output signatures are used by the runtime system to
+ * check that a valid number and type of inputs and outputs
+ * are connected to this block.  In this case, we accept
+ * only 1 input and 0 output.
+ */
+static const int MIN_IN = 1;   // mininum number of input streams
+static const int MAX_IN = 1;   // maximum number of input streams
+static const int MIN_OUT = 0;  // minimum number of output streams
+static const int MAX_OUT = 0;  // maximum number of output streams
+
+freesrp_sink_c::freesrp_sink_c (const string & args) : gr::sync_block("freesrp_sink_c",
+                                                       gr::io_signature::make (MIN_IN, MAX_IN, sizeof (gr_complex)),
+                                                       gr::io_signature::make (MIN_OUT, MAX_OUT, sizeof (gr_complex))),
+                                                       freesrp_common(args)
+{
+    if(_srp == nullptr)
+    {
+        throw runtime_error("FreeSRP not initialized!");
+    }
+}
+
+bool freesrp_sink_c::start()
+{
+    response res = _srp->send_cmd({SET_DATAPATH_EN, 1});
+    if(res.error != CMD_OK)
+    {
+        return false;
+    }
+    _srp->start_tx(std::bind(&freesrp_sink_c::freesrp_tx_callback, this, std::placeholders::_1));
+    return true;
+}
+
+bool freesrp_sink_c::stop()
+{
+    _srp->send_cmd({SET_DATAPATH_EN, 0});
+    _srp->stop_tx();
+    return true;
+}
+
+void freesrp_sink_c::freesrp_tx_callback(vector<sample>& samples)
+{
+    unique_lock<std::mutex> lk(_buf_mut);
+
+    for(sample &s : samples)
+    {
+        if(!_buf_queue.try_dequeue(s))
+        {
+            s.i = 0;
+            s.q = 0;
+        }
+        else
+        {
+            _buf_available_space++;
+        }
+    }
+
+    _buf_cond.notify_one();
+}
+
+int freesrp_sink_c::work(int noutput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items)
+{
+    const gr_complex *in = (const gr_complex *) input_items[0];
+
+    unique_lock<std::mutex> lk(_buf_mut);
+
+    // Wait until enough space is available
+    while(_buf_available_space < (unsigned int) noutput_items)
+    {
+        _buf_cond.wait(lk);
+    }
+
+    for(int i = 0; i < noutput_items; ++i)
+    {
+        sample s;
+        s.i = (int16_t) (real(in[i]) * 2047.0f);
+        s.q = (int16_t) (imag(in[i]) * 2047.0f);
+
+        if(!_buf_queue.try_enqueue(s))
+        {
+            throw runtime_error("Failed to add sample to buffer. This should never happen. Available space reported to be " + to_string(_buf_available_space) + " samples, noutput_items=" + to_string(noutput_items) + ", i=" + to_string(i));
+        }
+        else
+        {
+            _buf_available_space--;
+        }
+    }
+
+    return noutput_items;
+}
+
+double freesrp_sink_c::set_sample_rate( double rate )
+{
+    command cmd = _srp->make_command(SET_TX_SAMP_FREQ, rate);
+    response r = _srp->send_cmd(cmd);
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not set TX sample rate, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return static_cast<double>(r.param);
+    }
+}
+
+double freesrp_sink_c::get_sample_rate( void )
+{
+    response r = _srp->send_cmd({GET_TX_SAMP_FREQ, 0});
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not get TX sample rate, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return r.param;
+    }
+}
+
+double freesrp_sink_c::set_center_freq( double freq, size_t chan )
+{
+    command cmd = _srp->make_command(SET_TX_LO_FREQ, freq);
+    response r = _srp->send_cmd(cmd);
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not set TX LO frequency, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return static_cast<double>(r.param);
+    }
+}
+
+double freesrp_sink_c::get_center_freq( size_t chan )
+{
+    response r = _srp->send_cmd({GET_TX_LO_FREQ, 0});
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not get TX LO frequency, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return static_cast<double>(r.param);
+    }
+}
+
+vector<string> freesrp_sink_c::get_gain_names( size_t chan )
+{
+    vector<string> names;
+
+    names.push_back("TX_RF");
+
+    return names;
+}
+
+osmosdr::gain_range_t freesrp_sink_c::get_gain_range(size_t chan)
+{
+    osmosdr::meta_range_t gain_ranges;
+
+    gain_ranges.push_back(osmosdr::range_t(0, 89.75, 0.25));
+
+    return gain_ranges;
+}
+
+osmosdr::gain_range_t freesrp_sink_c::get_gain_range(const string& name, size_t chan)
+{
+    return get_gain_range(chan);
+}
+
+double freesrp_sink_c::set_gain(double gain, size_t chan)
+{
+    gain = get_gain_range().clip(gain);
+
+    double atten = 89.75 - gain;
+
+    command cmd = _srp->make_command(SET_TX_ATTENUATION, atten * 1000);
+    response r = _srp->send_cmd(cmd);
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not set TX attenuation, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return 89.75 - (((double) r.param) / 1000.0);
+    }
+}
+
+double freesrp_sink_c::set_gain(double gain, const string& name, size_t chan)
+{
+    return set_gain(gain, chan);
+}
+
+double freesrp_sink_c::get_gain(size_t chan)
+{
+    response r = _srp->send_cmd({GET_TX_ATTENUATION, 0});
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not get TX RF attenuation, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return 89.75 - (((double) r.param) / 1000.0);
+    }
+}
+
+double freesrp_sink_c::get_gain(const string& name, size_t chan)
+{
+    return get_gain(chan);
+}
+
+double freesrp_sink_c::set_bb_gain(double gain, size_t chan)
+{
+    return set_gain(gain, chan);
+}
+
+vector<string> freesrp_sink_c::get_antennas(size_t chan)
+{
+    vector<string> antennas;
+
+    antennas.push_back(get_antenna(chan));
+
+    return antennas;
+}
+
+string freesrp_sink_c::set_antenna(const string& antenna, size_t chan)
+{
+    return get_antenna(chan);
+}
+
+string freesrp_sink_c::get_antenna(size_t chan)
+{
+    return "TX";
+}
+
+double freesrp_sink_c::set_bandwidth(double bandwidth, size_t chan)
+{
+    command cmd = _srp->make_command(SET_TX_RF_BANDWIDTH, bandwidth);
+    response r = _srp->send_cmd(cmd);
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not set TX RF bandwidth, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return static_cast<double>(r.param);
+    }
+}
+
+double freesrp_sink_c::get_bandwidth(size_t chan)
+{
+    response r = _srp->send_cmd({GET_TX_RF_BANDWIDTH, 0});
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not get TX RF bandwidth, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return r.param;
+    }
+}
diff --git a/gr-osmosdr/lib/freesrp/freesrp_sink_c.h b/gr-osmosdr/lib/freesrp/freesrp_sink_c.h
new file mode 100644
index 0000000..ce75785
--- /dev/null
+++ b/gr-osmosdr/lib/freesrp/freesrp_sink_c.h
@@ -0,0 +1,130 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2015 Lukas Lao Beyer
+ * Copyright 2013 Dimitri Stolnikov <horiz0n at gmx.net>
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef INCLUDED_FREESRP_SINK_C_H
+#define INCLUDED_FREESRP_SINK_C_H
+
+#include <gnuradio/thread/thread.h>
+#include <gnuradio/block.h>
+#include <gnuradio/sync_block.h>
+
+#include "osmosdr/ranges.h"
+#include "sink_iface.h"
+
+#include "freesrp_common.h"
+#include "readerwriterqueue/readerwriterqueue.h"
+
+#include <mutex>
+#include <condition_variable>
+
+#include <freesrp.hpp>
+
+class freesrp_sink_c;
+
+/*
+ * We use boost::shared_ptr's instead of raw pointers for all access
+ * to gr_blocks (and many other data structures).  The shared_ptr gets
+ * us transparent reference counting, which greatly simplifies storage
+ * management issues.  This is especially helpful in our hybrid
+ * C++ / Python system.
+ *
+ * See http://www.boost.org/libs/smart_ptr/smart_ptr.htm
+ *
+ * As a convention, the _sptr suffix indicates a boost::shared_ptr
+ */
+typedef boost::shared_ptr<freesrp_sink_c> freesrp_sink_c_sptr;
+
+/*!
+ * \brief Return a shared_ptr to a new instance of freesrp_sink_c.
+ *
+ * To avoid accidental use of raw pointers, freesrp_sink_c's
+ * constructor is private.  make_freesrp_sink_c is the public
+ * interface for creating new instances.
+ */
+freesrp_sink_c_sptr make_freesrp_sink_c (const std::string & args = "");
+
+class freesrp_sink_c :
+    public gr::sync_block,
+    public sink_iface,
+    public freesrp_common
+{
+private:
+    // The friend declaration allows freesrp_make_sink_c to
+    // access the private constructor.
+    friend freesrp_sink_c_sptr make_freesrp_sink_c (const std::string & args);
+
+    freesrp_sink_c (const std::string & args);  	// private constructor
+
+public:
+
+    // From freesrp_common:
+    static std::vector<std::string> get_devices() { return freesrp_common::get_devices(); };
+    size_t get_num_channels( void ) { return freesrp_common::get_num_channels(); }
+    osmosdr::meta_range_t get_sample_rates( void ) { return freesrp_common::get_sample_rates(); }
+    osmosdr::freq_range_t get_freq_range( size_t chan = 0 ) { return freesrp_common::get_freq_range(chan); }
+    osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 ) { return freesrp_common::get_bandwidth_range(chan); }
+    double set_freq_corr( double ppm, size_t chan = 0 ) { return freesrp_common::set_freq_corr(ppm, chan); }
+    double get_freq_corr( size_t chan = 0 ) { return freesrp_common::get_freq_corr(chan); }
+
+    bool start();
+    bool stop();
+
+    int work( int noutput_items,
+              gr_vector_const_void_star &input_items,
+              gr_vector_void_star &output_items );
+
+    double set_sample_rate( double rate );
+    double get_sample_rate( void );
+
+    double set_center_freq( double freq, size_t chan = 0 );
+    double get_center_freq( size_t chan = 0 );
+
+    std::vector<std::string> get_gain_names( size_t chan = 0 );
+    osmosdr::gain_range_t get_gain_range( size_t chan = 0 );
+    osmosdr::gain_range_t get_gain_range( const std::string & name, size_t chan = 0 );
+    //TODO: implement this: bool set_gain_mode( bool automatic, size_t chan = 0 );
+    //TODO: implement this: bool get_gain_mode( size_t chan = 0 );
+    double set_gain( double gain, size_t chan = 0 );
+    double set_gain( double gain, const std::string & name, size_t chan = 0 );
+    double get_gain( size_t chan = 0 );
+    double get_gain( const std::string & name, size_t chan = 0 );
+
+    double set_bb_gain( double gain, size_t chan = 0 );
+
+    std::vector< std::string > get_antennas( size_t chan = 0 );
+    std::string set_antenna( const std::string & antenna, size_t chan = 0 );
+    std::string get_antenna( size_t chan = 0 );
+
+    double set_bandwidth( double bandwidth, size_t chan = 0 );
+    double get_bandwidth( size_t chan = 0 );
+
+private:
+
+    void freesrp_tx_callback(std::vector<::FreeSRP::sample> &samples);
+
+    bool _running = false;
+
+    std::mutex _buf_mut{};
+    std::condition_variable _buf_cond{};
+    size_t _buf_available_space = FREESRP_RX_TX_QUEUE_SIZE;
+    moodycamel::ReaderWriterQueue<::FreeSRP::sample> _buf_queue{FREESRP_RX_TX_QUEUE_SIZE};
+};
+
+#endif /* INCLUDED_FREESRP_SINK_C_H */
diff --git a/gr-osmosdr/lib/freesrp/freesrp_source_c.cc b/gr-osmosdr/lib/freesrp/freesrp_source_c.cc
new file mode 100644
index 0000000..9c56780
--- /dev/null
+++ b/gr-osmosdr/lib/freesrp/freesrp_source_c.cc
@@ -0,0 +1,341 @@
+#include "freesrp_source_c.h"
+
+using namespace FreeSRP;
+using namespace std;
+
+freesrp_source_c_sptr make_freesrp_source_c (const string &args)
+{
+    return gnuradio::get_initial_sptr(new freesrp_source_c (args));
+}
+
+/*
+ * Specify constraints on number of input and output streams.
+ * This info is used to construct the input and output signatures
+ * (2nd & 3rd args to gr_block's constructor).  The input and
+ * output signatures are used by the runtime system to
+ * check that a valid number and type of inputs and outputs
+ * are connected to this block.  In this case, we accept
+ * only 0 input and 1 output.
+ */
+static const int MIN_IN = 0;	// mininum number of input streams
+static const int MAX_IN = 0;	// maximum number of input streams
+static const int MIN_OUT = 1;	// minimum number of output streams
+static const int MAX_OUT = 1;	// maximum number of output streams
+
+freesrp_source_c::freesrp_source_c (const string & args) : gr::sync_block ("freesrp_source_c",
+                                                                gr::io_signature::make (MIN_IN, MAX_IN, sizeof (gr_complex)),
+                                                                gr::io_signature::make (MIN_OUT, MAX_OUT, sizeof (gr_complex))),
+                                                                freesrp_common(args)
+{
+    if(_srp == nullptr)
+    {
+        throw runtime_error("FreeSRP not initialized!");
+    }
+}
+
+bool freesrp_source_c::start()
+{
+    response res = _srp->send_cmd({SET_DATAPATH_EN, 1});
+    if(res.error != CMD_OK)
+    {
+        return false;
+    }
+    _srp->start_rx(std::bind(&freesrp_source_c::freesrp_rx_callback, this, std::placeholders::_1));
+
+    _running = true;
+
+    return true;
+}
+
+bool freesrp_source_c::stop()
+{
+    _srp->send_cmd({SET_DATAPATH_EN, 0});
+    _srp->stop_rx();
+
+    _running = false;
+
+    return true;
+}
+
+void freesrp_source_c::freesrp_rx_callback(const vector<sample> &samples)
+{
+    unique_lock<std::mutex> lk(_buf_mut);
+
+    for(const sample &s : samples)
+    {
+        if(!_buf_queue.try_enqueue(s))
+        {
+	    if(!_ignore_overflow)
+	    {
+		throw runtime_error("RX buffer overflow");
+	    }
+        }
+        else
+        {
+            _buf_num_samples++;
+        }
+    }
+
+    _buf_cond.notify_one();
+}
+
+int freesrp_source_c::work(int noutput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items)
+{
+    gr_complex *out = static_cast<gr_complex *>(output_items[0]);
+
+    unique_lock<std::mutex> lk(_buf_mut);
+
+    if(!_running)
+    {
+	return WORK_DONE;
+    }
+
+    // Wait until enough samples collected
+    while(_buf_num_samples < (unsigned int) noutput_items)
+    {
+        _buf_cond.wait(lk);
+    }
+
+    for(int i = 0; i < noutput_items; ++i)
+    {
+        sample s;
+        if(!_buf_queue.try_dequeue(s))
+        {
+            // This should not be happening
+            throw runtime_error("Failed to get sample from buffer. This should never happen. Number of available samples reported to be " + to_string(_buf_num_samples) + ", noutput_items=" + to_string(noutput_items) + ", i=" + to_string(i));
+        }
+        else
+        {
+            _buf_num_samples--;
+        }
+
+        out[i] = gr_complex(((float) s.i) / 2048.0f, ((float) s.q) / 2048.0f);
+    }
+
+    return noutput_items;
+}
+
+double freesrp_source_c::set_sample_rate( double rate )
+{
+    command cmd = _srp->make_command(SET_RX_SAMP_FREQ, rate);
+    response r = _srp->send_cmd(cmd);
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not set RX sample rate, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return static_cast<double>(r.param);
+    }
+}
+
+double freesrp_source_c::get_sample_rate( void )
+{
+    response r = _srp->send_cmd({GET_RX_SAMP_FREQ, 0});
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not get RX sample rate, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return static_cast<double>(r.param);
+    }
+}
+
+double freesrp_source_c::set_center_freq( double freq, size_t chan )
+{
+    command cmd = _srp->make_command(SET_RX_LO_FREQ, freq);
+    response r = _srp->send_cmd(cmd);
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not set RX LO frequency, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return static_cast<double>(r.param);
+    }
+}
+
+double freesrp_source_c::get_center_freq( size_t chan )
+{
+    response r = _srp->send_cmd({GET_RX_LO_FREQ, 0});
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not get RX LO frequency, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return static_cast<double>(r.param);
+    }
+}
+
+vector<string> freesrp_source_c::get_gain_names( size_t chan )
+{
+    vector<string> names;
+
+    names.push_back("RF");
+
+    return names;
+}
+
+osmosdr::gain_range_t freesrp_source_c::get_gain_range(size_t chan)
+{
+    osmosdr::meta_range_t gain_ranges;
+
+    gain_ranges.push_back(osmosdr::range_t(0, 74, 1));
+
+    return gain_ranges;
+}
+
+bool freesrp_source_c::set_gain_mode( bool automatic, size_t chan )
+{
+    uint8_t gc_mode = RF_GAIN_SLOWATTACK_AGC;
+
+    if(!automatic)
+    {
+        gc_mode = RF_GAIN_MGC;
+    }
+
+    command cmd = _srp->make_command(SET_RX_GC_MODE, gc_mode);
+    response r = _srp->send_cmd(cmd);
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not set RX RF gain control mode, error: " << r.error << endl;
+        return false;
+    }
+    else
+    {
+        return r.param != RF_GAIN_MGC;
+    }
+}
+
+bool freesrp_source_c::get_gain_mode( size_t chan )
+{
+    response r = _srp->send_cmd({GET_RX_GC_MODE, 0});
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not get RX RF gain control mode, error: " << r.error << endl;
+        return false;
+    }
+    else
+    {
+        return r.param != RF_GAIN_MGC;
+    }
+}
+
+osmosdr::gain_range_t freesrp_source_c::get_gain_range(const string& name, size_t chan)
+{
+    return get_gain_range(chan);
+}
+
+double freesrp_source_c::set_gain(double gain, size_t chan)
+{
+    gain = get_gain_range().clip(gain);
+
+    command cmd = _srp->make_command(SET_RX_RF_GAIN, gain);
+    response r = _srp->send_cmd(cmd);
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not set RX RF gain, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return r.param;
+    }
+}
+
+double freesrp_source_c::set_gain(double gain, const string& name, size_t chan)
+{
+    if(name == "RF")
+    {
+        return set_gain(gain, chan);
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+double freesrp_source_c::get_gain(size_t chan)
+{
+    response r = _srp->send_cmd({GET_RX_RF_GAIN, 0});
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not get RX RF gain, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return (static_cast<double>(r.param));
+    }
+}
+
+double freesrp_source_c::get_gain(const string& name, size_t chan)
+{
+    if(name == "RF")
+    {
+        return get_gain(chan);
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+double freesrp_source_c::set_bb_gain(double gain, size_t chan)
+{
+    return set_gain(gain, chan);
+}
+
+vector<string> freesrp_source_c::get_antennas(size_t chan)
+{
+    vector<string> antennas;
+
+    antennas.push_back(get_antenna(chan));
+
+    return antennas;
+}
+
+string freesrp_source_c::set_antenna(const string& antenna, size_t chan)
+{
+    return get_antenna(chan);
+}
+
+string freesrp_source_c::get_antenna(size_t chan)
+{
+    return "RX";
+}
+
+double freesrp_source_c::set_bandwidth(double bandwidth, size_t chan)
+{
+    command cmd = _srp->make_command(SET_RX_RF_BANDWIDTH, bandwidth);
+    response r = _srp->send_cmd(cmd);
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not set RX RF bandwidth, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return static_cast<double>(r.param);
+    }
+}
+
+double freesrp_source_c::get_bandwidth(size_t chan)
+{
+    response r = _srp->send_cmd({GET_RX_RF_BANDWIDTH, 0});
+    if(r.error != CMD_OK)
+    {
+        cerr << "Could not get RX RF bandwidth, error: " << r.error << endl;
+        return 0;
+    }
+    else
+    {
+        return static_cast<double>(r.param);
+    }
+}
diff --git a/gr-osmosdr/lib/freesrp/freesrp_source_c.h b/gr-osmosdr/lib/freesrp/freesrp_source_c.h
new file mode 100644
index 0000000..08f115c
--- /dev/null
+++ b/gr-osmosdr/lib/freesrp/freesrp_source_c.h
@@ -0,0 +1,131 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2015 Lukas Lao Beyer
+ * Copyright 2013 Dimitri Stolnikov <horiz0n at gmx.net>
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef INCLUDED_FREESRP_SOURCE_C_H
+#define INCLUDED_FREESRP_SOURCE_C_H
+
+#include <gnuradio/thread/thread.h>
+#include <gnuradio/block.h>
+#include <gnuradio/sync_block.h>
+
+#include "osmosdr/ranges.h"
+#include "source_iface.h"
+
+#include "freesrp_common.h"
+
+#include "readerwriterqueue/readerwriterqueue.h"
+
+#include <freesrp.hpp>
+
+#include <mutex>
+#include <condition_variable>
+
+class freesrp_source_c;
+
+/*
+ * We use boost::shared_ptr's instead of raw pointers for all access
+ * to gr_blocks (and many other data structures).  The shared_ptr gets
+ * us transparent reference counting, which greatly simplifies storage
+ * management issues.  This is especially helpful in our hybrid
+ * C++ / Python system.
+ *
+ * See http://www.boost.org/libs/smart_ptr/smart_ptr.htm
+ *
+ * As a convention, the _sptr suffix indicates a boost::shared_ptr
+ */
+typedef boost::shared_ptr<freesrp_source_c> freesrp_source_c_sptr;
+
+/*!
+ * \brief Return a shared_ptr to a new instance of freesrp_source_c.
+ *
+ * To avoid accidental use of raw pointers, freesrp_source_c's
+ * constructor is private.  freesrp_make_source_c is the public
+ * interface for creating new instances.
+ */
+freesrp_source_c_sptr make_freesrp_source_c (const std::string & args = "");
+
+class freesrp_source_c :
+    public gr::sync_block,
+    public source_iface,
+    public freesrp_common
+{
+private:
+    // The friend declaration allows freesrp_make_source_c to
+    // access the private constructor.
+    friend freesrp_source_c_sptr make_freesrp_source_c (const std::string & args);
+
+    freesrp_source_c (const std::string & args);  	// private constructor
+
+public:
+
+    // From freesrp_common:
+    static std::vector<std::string> get_devices() { return freesrp_common::get_devices(); };
+    size_t get_num_channels( void ) { return freesrp_common::get_num_channels(); }
+    osmosdr::meta_range_t get_sample_rates( void ) { return freesrp_common::get_sample_rates(); }
+    osmosdr::freq_range_t get_freq_range( size_t chan = 0 ) { return freesrp_common::get_freq_range(chan); }
+    osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 ) { return freesrp_common::get_bandwidth_range(chan); }
+    double set_freq_corr( double ppm, size_t chan = 0 ) { return freesrp_common::set_freq_corr(ppm, chan); }
+    double get_freq_corr( size_t chan = 0 ) { return freesrp_common::get_freq_corr(chan); }
+
+    bool start();
+    bool stop();
+
+    int work( int noutput_items,
+            gr_vector_const_void_star &input_items,
+            gr_vector_void_star &output_items );
+
+    double set_sample_rate( double rate );
+    double get_sample_rate( void );
+
+    double set_center_freq( double freq, size_t chan = 0 );
+    double get_center_freq( size_t chan = 0 );
+
+    std::vector<std::string> get_gain_names( size_t chan = 0 );
+    osmosdr::gain_range_t get_gain_range( size_t chan = 0 );
+    osmosdr::gain_range_t get_gain_range( const std::string & name, size_t chan = 0 );
+    bool set_gain_mode( bool automatic, size_t chan = 0 );
+    bool get_gain_mode( size_t chan = 0 );
+    double set_gain( double gain, size_t chan = 0 );
+    double set_gain( double gain, const std::string & name, size_t chan = 0 );
+    double get_gain( size_t chan = 0 );
+    double get_gain( const std::string & name, size_t chan = 0 );
+
+    double set_bb_gain( double gain, size_t chan = 0 );
+
+    std::vector< std::string > get_antennas( size_t chan = 0 );
+    std::string set_antenna( const std::string & antenna, size_t chan = 0 );
+    std::string get_antenna( size_t chan = 0 );
+
+    double set_bandwidth( double bandwidth, size_t chan = 0 );
+    double get_bandwidth( size_t chan = 0 );
+
+private:
+
+    void freesrp_rx_callback(const std::vector<FreeSRP::sample> &samples);
+
+    bool _running = false;
+
+    std::mutex _buf_mut{};
+    std::condition_variable _buf_cond{};
+    size_t _buf_num_samples = 0;
+    moodycamel::ReaderWriterQueue<FreeSRP::sample> _buf_queue{FREESRP_RX_TX_QUEUE_SIZE};
+};
+
+#endif /* INCLUDED_FREESRP_SOURCE_C_H */
diff --git a/gr-osmosdr/lib/freesrp/readerwriterqueue/LICENSE.md b/gr-osmosdr/lib/freesrp/readerwriterqueue/LICENSE.md
new file mode 100644
index 0000000..76d802e
--- /dev/null
+++ b/gr-osmosdr/lib/freesrp/readerwriterqueue/LICENSE.md
@@ -0,0 +1,28 @@
+This license applies to all the code in this repository except that written by third
+parties, namely the files in benchmarks/ext, which have their own licenses, and Jeff
+Preshing's semaphore implementation (used in the blocking queue) which has a zlib
+license (embedded in atomicops.h).
+
+Simplified BSD License:
+
+Copyright (c) 2013-2015, Cameron Desrochers  
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+- Redistributions of source code must retain the above copyright notice, this list of
+conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright notice, this list of
+conditions and the following disclaimer in the documentation and/or other materials
+provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/gr-osmosdr/lib/freesrp/readerwriterqueue/README.md b/gr-osmosdr/lib/freesrp/readerwriterqueue/README.md
new file mode 100644
index 0000000..3d94e1a
--- /dev/null
+++ b/gr-osmosdr/lib/freesrp/readerwriterqueue/README.md
@@ -0,0 +1,114 @@
+# A single-producer, single-consumer lock-free queue for C++
+
+This mini-repository has my very own implementation of a lock-free queue (that I designed from scratch) for C++.
+
+It only supports a two-thread use case (one consuming, and one producing). The threads can't switch roles, though
+you could use this queue completely from a single thread if you wish (but that would sort of defeat the purpose!).
+
+Note: If you need a general purpse multi-producer, multi-consumer lock free queue, I have [one of those too][mpmc].
+
+
+## Features
+
+- [Blazing fast][benchmarks]
+- Compatible with C++11 (supports moving objects instead of making copies)
+- Fully generic (templated container of any type) -- just like `std::queue`, you never need to allocate memory for elements yourself
+  (which saves you the hassle of writing a lock-free memory manager to hold the elements you're queueing)
+- Allocates memory up front, in contiguous blocks
+- Provides a `try_enqueue` method which is guaranteed never to allocate memory (the queue starts with an initial capacity)
+- Also provides an `enqueue` method which can dynamically grow the size of the queue as needed
+- Also provides a blocking version with `wait_dequeue`
+- Completely "wait-free" (no compare-and-swap loop). Enqueue and dequeue are always O(1) (not counting memory allocation)
+- On x86, the memory barriers compile down to no-ops, meaning enqueue and dequeue are just a simple series of loads and stores (and branches)
+
+
+## Use
+
+Simply drop the readerwriterqueue.h and atomicops.h files into your source code and include them :-)
+A modern compiler is required (MSVC2010+, GCC 4.7+, ICC 13+, or any C++11 compliant compiler should work).
+
+Note: If you're using GCC, you really do need GCC 4.7 or above -- [4.6 has a bug][gcc46bug] that prevents the atomic fence primitives
+from working correctly.
+
+Example:
+
+```cpp
+using namespace moodycamel;
+
+ReaderWriterQueue<int> q(100);       // Reserve space for at least 100 elements up front
+
+q.enqueue(17);                       // Will allocate memory if the queue is full
+bool succeeded = q.try_enqueue(18);  // Will only succeed if the queue has an empty slot (never allocates)
+assert(succeeded);
+
+int number;
+succeeded = q.try_dequeue(number);  // Returns false if the queue was empty
+
+assert(succeeded && number == 17);
+
+// You can also peek at the front item of the queue (consumer only)
+int* front = q.peek();
+assert(*front == 18);
+succeeded = q.try_dequeue(number);
+assert(succeeded && number == 18);
+front = q.peek(); 
+assert(front == nullptr);           // Returns nullptr if the queue was empty
+```
+
+The blocking version has the exact same API, with the addition of a `wait_dequeue` method:
+
+```cpp
+BlockingReaderWriterQueue<int> q;
+
+std::thread reader([&]() {
+    int item;
+    for (int i = 0; i != 100; ++i) {
+        q.wait_dequeue(item);
+    }
+});
+std::thread writer([&]() {
+    for (int i = 0; i != 100; ++i) {
+        q.enqueue(i);
+    }
+});
+writer.join();
+reader.join();
+
+assert(q.size_approx() == 0);
+```
+    
+Note that `wait_dequeue` will block indefinitely while the queue is empty; this
+means care must be taken to only call `wait_dequeue` if you're sure another element
+will come along eventually, or if the queue has a static lifetime. This is because
+destroying the queue while a thread is waiting on it will invoke undefined behaviour.
+
+    
+## Disclaimers
+
+The queue should only be used on platforms where aligned integer and pointer access is atomic; fortunately, that
+includes all modern processors (e.g. x86/x86-64, ARM, and PowerPC). *Not* for use with a DEC Alpha processor (which has very weak memory ordering) :-)
+
+Note that it's only been tested on x86(-64); if someone has access to other processors I'd love to run some tests on
+anything that's not x86-based.
+
+Finally, I am not an expert. This is my first foray into lock-free programming, and though I'm confident in the code,
+it's possible that there are bugs despite the effort I put into designing and testing this data structure.
+
+Use this code at your own risk; in particular, lock-free programming is a patent minefield, and this code may very
+well violate a pending patent (I haven't looked). It's worth noting that I came up with this algorithm and
+implementation from scratch, independent of any existing lock-free queues.
+
+
+## More info
+
+See the [LICENSE.md][license] file for the license (simplified BSD).
+
+My [blog post][blog] introduces the context that led to this code, and may be of interest if you're curious
+about lock-free programming.
+
+
+[blog]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++
+[license]: LICENSE.md
+[benchmarks]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++#benchmarks
+[gcc46bug]: http://stackoverflow.com/questions/16429669/stdatomic-thread-fence-has-undefined-reference
+[mpmc]: https://github.com/cameron314/concurrentqueue
diff --git a/gr-osmosdr/lib/freesrp/readerwriterqueue/atomicops.h b/gr-osmosdr/lib/freesrp/readerwriterqueue/atomicops.h
new file mode 100644
index 0000000..1bd2455
--- /dev/null
+++ b/gr-osmosdr/lib/freesrp/readerwriterqueue/atomicops.h
@@ -0,0 +1,577 @@
+// ©2013-2015 Cameron Desrochers.
+// Distributed under the simplified BSD license (see the license file that
+// should have come with this header).
+// Uses Jeff Preshing's semaphore implementation (under the terms of its
+// separate zlib license, embedded below).
+
+#pragma once
+
+// Provides portable (VC++2010+, Intel ICC 13, GCC 4.7+, and anything C++11 compliant) implementation
+// of low-level memory barriers, plus a few semi-portable utility macros (for inlining and alignment).
+// Also has a basic atomic type (limited to hardware-supported atomics with no memory ordering guarantees).
+// Uses the AE_* prefix for macros (historical reasons), and the "moodycamel" namespace for symbols.
+
+#include <cassert>
+#include <type_traits>
+
+
+// Platform detection
+#if defined(__INTEL_COMPILER)
+#define AE_ICC
+#elif defined(_MSC_VER)
+#define AE_VCPP
+#elif defined(__GNUC__)
+#define AE_GCC
+#endif
+
+#if defined(_M_IA64) || defined(__ia64__)
+#define AE_ARCH_IA64
+#elif defined(_WIN64) || defined(__amd64__) || defined(_M_X64) || defined(__x86_64__)
+#define AE_ARCH_X64
+#elif defined(_M_IX86) || defined(__i386__)
+#define AE_ARCH_X86
+#elif defined(_M_PPC) || defined(__powerpc__)
+#define AE_ARCH_PPC
+#else
+#define AE_ARCH_UNKNOWN
+#endif
+
+
+// AE_UNUSED
+#define AE_UNUSED(x) ((void)x)
+
+
+// AE_FORCEINLINE
+#if defined(AE_VCPP) || defined(AE_ICC)
+#define AE_FORCEINLINE __forceinline
+#elif defined(AE_GCC)
+//#define AE_FORCEINLINE __attribute__((always_inline))
+#define AE_FORCEINLINE inline
+#else
+#define AE_FORCEINLINE inline
+#endif
+
+
+// AE_ALIGN
+#if defined(AE_VCPP) || defined(AE_ICC)
+#define AE_ALIGN(x) __declspec(align(x))
+#elif defined(AE_GCC)
+#define AE_ALIGN(x) __attribute__((aligned(x)))
+#else
+// Assume GCC compliant syntax...
+#define AE_ALIGN(x) __attribute__((aligned(x)))
+#endif
+
+
+// Portable atomic fences implemented below:
+
+namespace moodycamel {
+
+enum memory_order {
+	memory_order_relaxed,
+	memory_order_acquire,
+	memory_order_release,
+	memory_order_acq_rel,
+	memory_order_seq_cst,
+
+	// memory_order_sync: Forces a full sync:
+	// #LoadLoad, #LoadStore, #StoreStore, and most significantly, #StoreLoad
+	memory_order_sync = memory_order_seq_cst
+};
+
+}    // end namespace moodycamel
+
+#if (defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))) || defined(AE_ICC)
+// VS2010 and ICC13 don't support std::atomic_*_fence, implement our own fences
+
+#include <intrin.h>
+
+#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
+#define AeFullSync _mm_mfence
+#define AeLiteSync _mm_mfence
+#elif defined(AE_ARCH_IA64)
+#define AeFullSync __mf
+#define AeLiteSync __mf
+#elif defined(AE_ARCH_PPC)
+#include <ppcintrinsics.h>
+#define AeFullSync __sync
+#define AeLiteSync __lwsync
+#endif
+
+
+#ifdef AE_VCPP
+#pragma warning(push)
+#pragma warning(disable: 4365)		// Disable erroneous 'conversion from long to unsigned int, signed/unsigned mismatch' error when using `assert`
+#ifdef __cplusplus_cli
+#pragma managed(push, off)
+#endif
+#endif
+
+namespace moodycamel {
+
+AE_FORCEINLINE void compiler_fence(memory_order order)
+{
+	switch (order) {
+		case memory_order_relaxed: break;
+		case memory_order_acquire: _ReadBarrier(); break;
+		case memory_order_release: _WriteBarrier(); break;
+		case memory_order_acq_rel: _ReadWriteBarrier(); break;
+		case memory_order_seq_cst: _ReadWriteBarrier(); break;
+		default: assert(false);
+	}
+}
+
+// x86/x64 have a strong memory model -- all loads and stores have
+// acquire and release semantics automatically (so only need compiler
+// barriers for those).
+#if defined(AE_ARCH_X86) || defined(AE_ARCH_X64)
+AE_FORCEINLINE void fence(memory_order order)
+{
+	switch (order) {
+		case memory_order_relaxed: break;
+		case memory_order_acquire: _ReadBarrier(); break;
+		case memory_order_release: _WriteBarrier(); break;
+		case memory_order_acq_rel: _ReadWriteBarrier(); break;
+		case memory_order_seq_cst:
+			_ReadWriteBarrier();
+			AeFullSync();
+			_ReadWriteBarrier();
+			break;
+		default: assert(false);
+	}
+}
+#else
+AE_FORCEINLINE void fence(memory_order order)
+{
+	// Non-specialized arch, use heavier memory barriers everywhere just in case :-(
+	switch (order) {
+		case memory_order_relaxed:
+			break;
+		case memory_order_acquire:
+			_ReadBarrier();
+			AeLiteSync();
+			_ReadBarrier();
+			break;
+		case memory_order_release:
+			_WriteBarrier();
+			AeLiteSync();
+			_WriteBarrier();
+			break;
+		case memory_order_acq_rel:
+			_ReadWriteBarrier();
+			AeLiteSync();
+			_ReadWriteBarrier();
+			break;
+		case memory_order_seq_cst:
+			_ReadWriteBarrier();
+			AeFullSync();
+			_ReadWriteBarrier();
+			break;
+		default: assert(false);
+	}
+}
+#endif
+}    // end namespace moodycamel
+#else
+// Use standard library of atomics
+#include <atomic>
+
+namespace moodycamel {
+
+AE_FORCEINLINE void compiler_fence(memory_order order)
+{
+	switch (order) {
+		case memory_order_relaxed: break;
+		case memory_order_acquire: std::atomic_signal_fence(std::memory_order_acquire); break;
+		case memory_order_release: std::atomic_signal_fence(std::memory_order_release); break;
+		case memory_order_acq_rel: std::atomic_signal_fence(std::memory_order_acq_rel); break;
+		case memory_order_seq_cst: std::atomic_signal_fence(std::memory_order_seq_cst); break;
+		default: assert(false);
+	}
+}
+
+AE_FORCEINLINE void fence(memory_order order)
+{
+	switch (order) {
+		case memory_order_relaxed: break;
+		case memory_order_acquire: std::atomic_thread_fence(std::memory_order_acquire); break;
+		case memory_order_release: std::atomic_thread_fence(std::memory_order_release); break;
+		case memory_order_acq_rel: std::atomic_thread_fence(std::memory_order_acq_rel); break;
+		case memory_order_seq_cst: std::atomic_thread_fence(std::memory_order_seq_cst); break;
+		default: assert(false);
+	}
+}
+
+}    // end namespace moodycamel
+
+#endif
+
+
+#if !defined(AE_VCPP) || (_MSC_VER >= 1700 && !defined(__cplusplus_cli))
+#define AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
+#endif
+
+#ifdef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
+#include <atomic>
+#endif
+#include <utility>
+
+// WARNING: *NOT* A REPLACEMENT FOR std::atomic. READ CAREFULLY:
+// Provides basic support for atomic variables -- no memory ordering guarantees are provided.
+// The guarantee of atomicity is only made for types that already have atomic load and store guarantees
+// at the hardware level -- on most platforms this generally means aligned pointers and integers (only).
+namespace moodycamel {
+template<typename T>
+class weak_atomic
+{
+public:
+	weak_atomic() { }
+#ifdef AE_VCPP
+#pragma warning(disable: 4100)		// Get rid of (erroneous) 'unreferenced formal parameter' warning
+#endif
+	template<typename U> weak_atomic(U&& x) : value(std::forward<U>(x)) {  }
+#ifdef __cplusplus_cli
+	// Work around bug with universal reference/nullptr combination that only appears when /clr is on
+	weak_atomic(nullptr_t) : value(nullptr) {  }
+#endif
+	weak_atomic(weak_atomic const& other) : value(other.value) {  }
+	weak_atomic(weak_atomic&& other) : value(std::move(other.value)) {  }
+#ifdef AE_VCPP
+#pragma warning(default: 4100)
+#endif
+
+	AE_FORCEINLINE operator T() const { return load(); }
+
+
+#ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
+	template<typename U> AE_FORCEINLINE weak_atomic const& operator=(U&& x) { value = std::forward<U>(x); return *this; }
+	AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) { value = other.value; return *this; }
+
+	AE_FORCEINLINE T load() const { return value; }
+
+	AE_FORCEINLINE T fetch_add_acquire(T increment)
+	{
+#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
+		if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment);
+#if defined(_M_AMD64)
+		else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment);
+#endif
+#else
+#error Unsupported platform
+#endif
+		assert(false && "T must be either a 32 or 64 bit type");
+		return value;
+	}
+
+	AE_FORCEINLINE T fetch_add_release(T increment)
+	{
+#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
+		if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment);
+#if defined(_M_AMD64)
+		else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment);
+#endif
+#else
+#error Unsupported platform
+#endif
+		assert(false && "T must be either a 32 or 64 bit type");
+		return value;
+	}
+#else
+	template<typename U>
+	AE_FORCEINLINE weak_atomic const& operator=(U&& x)
+	{
+		value.store(std::forward<U>(x), std::memory_order_relaxed);
+		return *this;
+	}
+
+	AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other)
+	{
+		value.store(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed);
+		return *this;
+	}
+
+	AE_FORCEINLINE T load() const { return value.load(std::memory_order_relaxed); }
+
+	AE_FORCEINLINE T fetch_add_acquire(T increment)
+	{
+		return value.fetch_add(increment, std::memory_order_acquire);
+	}
+
+	AE_FORCEINLINE T fetch_add_release(T increment)
+	{
+		return value.fetch_add(increment, std::memory_order_release);
+	}
+#endif
+
+
+private:
+#ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
+	// No std::atomic support, but still need to circumvent compiler optimizations.
+	// `volatile` will make memory access slow, but is guaranteed to be reliable.
+	volatile T value;
+#else
+	std::atomic<T> value;
+#endif
+};
+
+}	// end namespace moodycamel
+
+
+
+// Portable single-producer, single-consumer semaphore below:
+
+#if defined(_WIN32)
+// Avoid including windows.h in a header; we only need a handful of
+// items, so we'll redeclare them here (this is relatively safe since
+// the API generally has to remain stable between Windows versions).
+// I know this is an ugly hack but it still beats polluting the global
+// namespace with thousands of generic names or adding a .cpp for nothing.
+extern "C" {
+	struct _SECURITY_ATTRIBUTES;
+	__declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName);
+	__declspec(dllimport) int __stdcall CloseHandle(void* hObject);
+	__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds);
+	__declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount);
+}
+#elif defined(__MACH__)
+#include <mach/mach.h>
+#elif defined(__unix__)
+#include <semaphore.h>
+#endif
+
+namespace moodycamel
+{
+	// Code in the spsc_sema namespace below is an adaptation of Jeff Preshing's
+	// portable + lightweight semaphore implementations, originally from
+	// https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h
+	// LICENSE:
+	// Copyright (c) 2015 Jeff Preshing
+	//
+	// This software is provided 'as-is', without any express or implied
+	// warranty. In no event will the authors be held liable for any damages
+	// arising from the use of this software.
+	//
+	// Permission is granted to anyone to use this software for any purpose,
+	// including commercial applications, and to alter it and redistribute it
+	// freely, subject to the following restrictions:
+	//
+	// 1. The origin of this software must not be misrepresented; you must not
+	//    claim that you wrote the original software. If you use this software
+	//    in a product, an acknowledgement in the product documentation would be
+	//    appreciated but is not required.
+	// 2. Altered source versions must be plainly marked as such, and must not be
+	//    misrepresented as being the original software.
+	// 3. This notice may not be removed or altered from any source distribution.
+	namespace spsc_sema
+	{
+#if defined(_WIN32)
+		class Semaphore
+		{
+		private:
+		    void* m_hSema;
+
+		    Semaphore(const Semaphore& other);
+		    Semaphore& operator=(const Semaphore& other);
+
+		public:
+		    Semaphore(int initialCount = 0)
+		    {
+		        assert(initialCount >= 0);
+		        const long maxLong = 0x7fffffff;
+		        m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr);
+		    }
+
+		    ~Semaphore()
+		    {
+		        CloseHandle(m_hSema);
+		    }
+
+		    void wait()
+		    {
+		    	const unsigned long infinite = 0xffffffff;
+		        WaitForSingleObject(m_hSema, infinite);
+		    }
+
+		    void signal(int count = 1)
+		    {
+		        ReleaseSemaphore(m_hSema, count, nullptr);
+		    }
+		};
+#elif defined(__MACH__)
+		//---------------------------------------------------------
+		// Semaphore (Apple iOS and OSX)
+		// Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html
+		//---------------------------------------------------------
+		class Semaphore
+		{
+		private:
+		    semaphore_t m_sema;
+
+		    Semaphore(const Semaphore& other);
+		    Semaphore& operator=(const Semaphore& other);
+
+		public:
+		    Semaphore(int initialCount = 0)
+		    {
+		        assert(initialCount >= 0);
+		        semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
+		    }
+
+		    ~Semaphore()
+		    {
+		        semaphore_destroy(mach_task_self(), m_sema);
+		    }
+
+		    void wait()
+		    {
+		        semaphore_wait(m_sema);
+		    }
+
+		    void signal()
+		    {
+		        semaphore_signal(m_sema);
+		    }
+
+		    void signal(int count)
+		    {
+		        while (count-- > 0)
+		        {
+		            semaphore_signal(m_sema);
+		        }
+		    }
+		};
+#elif defined(__unix__)
+		//---------------------------------------------------------
+		// Semaphore (POSIX, Linux)
+		//---------------------------------------------------------
+		class Semaphore
+		{
+		private:
+		    sem_t m_sema;
+
+		    Semaphore(const Semaphore& other);
+		    Semaphore& operator=(const Semaphore& other);
+
+		public:
+		    Semaphore(int initialCount = 0)
+		    {
+		        assert(initialCount >= 0);
+		        sem_init(&m_sema, 0, initialCount);
+		    }
+
+		    ~Semaphore()
+		    {
+		        sem_destroy(&m_sema);
+		    }
+
+		    void wait()
+		    {
+		        // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error
+		        int rc;
+		        do
+		        {
+		            rc = sem_wait(&m_sema);
+		        }
+		        while (rc == -1 && errno == EINTR);
+		    }
+
+		    void signal()
+		    {
+		        sem_post(&m_sema);
+		    }
+
+		    void signal(int count)
+		    {
+		        while (count-- > 0)
+		        {
+		            sem_post(&m_sema);
+		        }
+		    }
+		};
+#else
+#error Unsupported platform! (No semaphore wrapper available)
+#endif
+
+		//---------------------------------------------------------
+		// LightweightSemaphore
+		//---------------------------------------------------------
+		class LightweightSemaphore
+		{
+		public:
+			typedef std::make_signed<std::size_t>::type ssize_t;
+
+		private:
+		    weak_atomic<ssize_t> m_count;
+		    Semaphore m_sema;
+
+		    void waitWithPartialSpinning()
+		    {
+		        ssize_t oldCount;
+		        // Is there a better way to set the initial spin count?
+		        // If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC,
+		        // as threads start hitting the kernel semaphore.
+		        int spin = 10000;
+		        while (--spin >= 0)
+		        {
+		            if (m_count.load() > 0)
+		            {
+		                m_count.fetch_add_acquire(-1);
+		                return;
+		            }
+		            compiler_fence(memory_order_acquire);     // Prevent the compiler from collapsing the loop.
+		        }
+		        oldCount = m_count.fetch_add_acquire(-1);
+		        if (oldCount <= 0)
+		        {
+		            m_sema.wait();
+		        }
+		    }
+
+		public:
+		    LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount)
+		    {
+		        assert(initialCount >= 0);
+		    }
+
+		    bool tryWait()
+		    {
+		        if (m_count.load() > 0)
+		        {
+		        	m_count.fetch_add_acquire(-1);
+		        	return true;
+		        }
+		        return false;
+		    }
+
+		    void wait()
+		    {
+		        if (!tryWait())
+		            waitWithPartialSpinning();
+		    }
+
+		    void signal(ssize_t count = 1)
+		    {
+		    	assert(count >= 0);
+		        ssize_t oldCount = m_count.fetch_add_release(count);
+		        assert(oldCount >= -1);
+		        if (oldCount < 0)
+		        {
+		            m_sema.signal(1);
+		        }
+		    }
+
+		    ssize_t availableApprox() const
+		    {
+		    	ssize_t count = m_count.load();
+		    	return count > 0 ? count : 0;
+		    }
+		};
+	}	// end namespace spsc_sema
+}	// end namespace moodycamel
+
+#if defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))
+#pragma warning(pop)
+#ifdef __cplusplus_cli
+#pragma managed(pop)
+#endif
+#endif
diff --git a/gr-osmosdr/lib/freesrp/readerwriterqueue/readerwriterqueue.h b/gr-osmosdr/lib/freesrp/readerwriterqueue/readerwriterqueue.h
new file mode 100644
index 0000000..e0ac56b
--- /dev/null
+++ b/gr-osmosdr/lib/freesrp/readerwriterqueue/readerwriterqueue.h
@@ -0,0 +1,764 @@
+// ©2013-2015 Cameron Desrochers.
+// Distributed under the simplified BSD license (see the license file that
+// should have come with this header).
+
+#pragma once
+
+#include "atomicops.h"
+#include <type_traits>
+#include <utility>
+#include <cassert>
+#include <stdexcept>
+#include <cstdint>
+#include <cstdlib>		// For malloc/free & size_t
+
+
+// A lock-free queue for a single-consumer, single-producer architecture.
+// The queue is also wait-free in the common path (except if more memory
+// needs to be allocated, in which case malloc is called).
+// Allocates memory sparingly (O(lg(n) times, amortized), and only once if
+// the original maximum size estimate is never exceeded.
+// Tested on x86/x64 processors, but semantics should be correct for all
+// architectures (given the right implementations in atomicops.h), provided
+// that aligned integer and pointer accesses are naturally atomic.
+// Note that there should only be one consumer thread and producer thread;
+// Switching roles of the threads, or using multiple consecutive threads for
+// one role, is not safe unless properly synchronized.
+// Using the queue exclusively from one thread is fine, though a bit silly.
+
+#define CACHE_LINE_SIZE 64
+
+#ifdef AE_VCPP
+#pragma warning(push)
+#pragma warning(disable: 4324)	// structure was padded due to __declspec(align())
+#pragma warning(disable: 4820)	// padding was added
+#pragma warning(disable: 4127)	// conditional expression is constant
+#endif
+
+namespace moodycamel {
+
+template<typename T, size_t MAX_BLOCK_SIZE = 512>
+class ReaderWriterQueue
+{
+	// Design: Based on a queue-of-queues. The low-level queues are just
+	// circular buffers with front and tail indices indicating where the
+	// next element to dequeue is and where the next element can be enqueued,
+	// respectively. Each low-level queue is called a "block". Each block
+	// wastes exactly one element's worth of space to keep the design simple
+	// (if front == tail then the queue is empty, and can't be full).
+	// The high-level queue is a circular linked list of blocks; again there
+	// is a front and tail, but this time they are pointers to the blocks.
+	// The front block is where the next element to be dequeued is, provided
+	// the block is not empty. The back block is where elements are to be
+	// enqueued, provided the block is not full.
+	// The producer thread owns all the tail indices/pointers. The consumer
+	// thread owns all the front indices/pointers. Both threads read each
+	// other's variables, but only the owning thread updates them. E.g. After
+	// the consumer reads the producer's tail, the tail may change before the
+	// consumer is done dequeuing an object, but the consumer knows the tail
+	// will never go backwards, only forwards.
+	// If there is no room to enqueue an object, an additional block (of
+	// equal size to the last block) is added. Blocks are never removed.
+
+public:
+	// Constructs a queue that can hold maxSize elements without further
+	// allocations. If more than MAX_BLOCK_SIZE elements are requested,
+	// then several blocks of MAX_BLOCK_SIZE each are reserved (including
+	// at least one extra buffer block).
+	explicit ReaderWriterQueue(size_t maxSize = 15)
+#ifndef NDEBUG
+		: enqueuing(false)
+		,dequeuing(false)
+#endif
+	{
+		assert(maxSize > 0);
+		assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) && "MAX_BLOCK_SIZE must be a power of 2");
+		assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2");
+
+		Block* firstBlock = nullptr;
+
+		largestBlockSize = ceilToPow2(maxSize + 1);		// We need a spare slot to fit maxSize elements in the block
+		if (largestBlockSize > MAX_BLOCK_SIZE * 2) {
+			// We need a spare block in case the producer is writing to a different block the consumer is reading from, and
+			// wants to enqueue the maximum number of elements. We also need a spare element in each block to avoid the ambiguity
+			// between front == tail meaning "empty" and "full".
+			// So the effective number of slots that are guaranteed to be usable at any time is the block size - 1 times the
+			// number of blocks - 1. Solving for maxSize and applying a ceiling to the division gives us (after simplifying):
+			size_t initialBlockCount = (maxSize + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1);
+			largestBlockSize = MAX_BLOCK_SIZE;
+			Block* lastBlock = nullptr;
+			for (size_t i = 0; i != initialBlockCount; ++i) {
+				auto block = make_block(largestBlockSize);
+				if (block == nullptr) {
+					throw std::bad_alloc();
+				}
+				if (firstBlock == nullptr) {
+					firstBlock = block;
+				}
+				else {
+					lastBlock->next = block;
+				}
+				lastBlock = block;
+				block->next = firstBlock;
+			}
+		}
+		else {
+			firstBlock = make_block(largestBlockSize);
+			if (firstBlock == nullptr) {
+				throw std::bad_alloc();
+			}
+			firstBlock->next = firstBlock;
+		}
+		frontBlock = firstBlock;
+		tailBlock = firstBlock;
+
+		// Make sure the reader/writer threads will have the initialized memory setup above:
+		fence(memory_order_sync);
+	}
+
+	// Note: The queue should not be accessed concurrently while it's
+	// being deleted. It's up to the user to synchronize this.
+	~ReaderWriterQueue()
+	{
+		// Make sure we get the latest version of all variables from other CPUs:
+		fence(memory_order_sync);
+
+		// Destroy any remaining objects in queue and free memory
+		Block* frontBlock_ = frontBlock;
+		Block* block = frontBlock_;
+		do {
+			Block* nextBlock = block->next;
+			size_t blockFront = block->front;
+			size_t blockTail = block->tail;
+
+			for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) {
+				auto element = reinterpret_cast<T*>(block->data + i * sizeof(T));
+				element->~T();
+				(void)element;
+			}
+
+			auto rawBlock = block->rawThis;
+			block->~Block();
+			std::free(rawBlock);
+			block = nextBlock;
+		} while (block != frontBlock_);
+	}
+
+
+	// Enqueues a copy of element if there is room in the queue.
+	// Returns true if the element was enqueued, false otherwise.
+	// Does not allocate memory.
+	AE_FORCEINLINE bool try_enqueue(T const& element)
+	{
+		return inner_enqueue<CannotAlloc>(element);
+	}
+
+	// Enqueues a moved copy of element if there is room in the queue.
+	// Returns true if the element was enqueued, false otherwise.
+	// Does not allocate memory.
+	AE_FORCEINLINE bool try_enqueue(T&& element)
+	{
+		return inner_enqueue<CannotAlloc>(std::forward<T>(element));
+	}
+
+
+	// Enqueues a copy of element on the queue.
+	// Allocates an additional block of memory if needed.
+	// Only fails (returns false) if memory allocation fails.
+	AE_FORCEINLINE bool enqueue(T const& element)
+	{
+		return inner_enqueue<CanAlloc>(element);
+	}
+
+	// Enqueues a moved copy of element on the queue.
+	// Allocates an additional block of memory if needed.
+	// Only fails (returns false) if memory allocation fails.
+	AE_FORCEINLINE bool enqueue(T&& element)
+	{
+		return inner_enqueue<CanAlloc>(std::forward<T>(element));
+	}
+
+
+	// Attempts to dequeue an element; if the queue is empty,
+	// returns false instead. If the queue has at least one element,
+	// moves front to result using operator=, then returns true.
+	template<typename U>
+	bool try_dequeue(U& result)
+	{
+#ifndef NDEBUG
+		ReentrantGuard guard(this->dequeuing);
+#endif
+
+		// High-level pseudocode:
+		// Remember where the tail block is
+		// If the front block has an element in it, dequeue it
+		// Else
+		//     If front block was the tail block when we entered the function, return false
+		//     Else advance to next block and dequeue the item there
+
+		// Note that we have to use the value of the tail block from before we check if the front
+		// block is full or not, in case the front block is empty and then, before we check if the
+		// tail block is at the front block or not, the producer fills up the front block *and
+		// moves on*, which would make us skip a filled block. Seems unlikely, but was consistently
+		// reproducible in practice.
+		// In order to avoid overhead in the common case, though, we do a double-checked pattern
+		// where we have the fast path if the front block is not empty, then read the tail block,
+		// then re-read the front block and check if it's not empty again, then check if the tail
+		// block has advanced.
+
+		Block* frontBlock_ = frontBlock.load();
+		size_t blockTail = frontBlock_->localTail;
+		size_t blockFront = frontBlock_->front.load();
+
+		if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
+			fence(memory_order_acquire);
+
+		non_empty_front_block:
+			// Front block not empty, dequeue from here
+			auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
+			result = std::move(*element);
+			element->~T();
+
+			blockFront = (blockFront + 1) & frontBlock_->sizeMask;
+
+			fence(memory_order_release);
+			frontBlock_->front = blockFront;
+		}
+		else if (frontBlock_ != tailBlock.load()) {
+			fence(memory_order_acquire);
+
+			frontBlock_ = frontBlock.load();
+			blockTail = frontBlock_->localTail = frontBlock_->tail.load();
+			blockFront = frontBlock_->front.load();
+			fence(memory_order_acquire);
+
+			if (blockFront != blockTail) {
+				// Oh look, the front block isn't empty after all
+				goto non_empty_front_block;
+			}
+
+			// Front block is empty but there's another block ahead, advance to it
+			Block* nextBlock = frontBlock_->next;
+			// Don't need an acquire fence here since next can only ever be set on the tailBlock,
+			// and we're not the tailBlock, and we did an acquire earlier after reading tailBlock which
+			// ensures next is up-to-date on this CPU in case we recently were at tailBlock.
+
+			size_t nextBlockFront = nextBlock->front.load();
+			size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
+			fence(memory_order_acquire);
+
+			// Since the tailBlock is only ever advanced after being written to,
+			// we know there's for sure an element to dequeue on it
+			assert(nextBlockFront != nextBlockTail);
+			AE_UNUSED(nextBlockTail);
+
+			// We're done with this block, let the producer use it if it needs
+			fence(memory_order_release);		// Expose possibly pending changes to frontBlock->front from last dequeue
+			frontBlock = frontBlock_ = nextBlock;
+
+			compiler_fence(memory_order_release);	// Not strictly needed
+
+			auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
+
+			result = std::move(*element);
+			element->~T();
+
+			nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
+
+			fence(memory_order_release);
+			frontBlock_->front = nextBlockFront;
+		}
+		else {
+			// No elements in current block and no other block to advance to
+			return false;
+		}
+
+		return true;
+	}
+
+
+	// Returns a pointer to the front element in the queue (the one that
+	// would be removed next by a call to `try_dequeue` or `pop`). If the
+	// queue appears empty at the time the method is called, nullptr is
+	// returned instead.
+	// Must be called only from the consumer thread.
+	T* peek()
+	{
+#ifndef NDEBUG
+		ReentrantGuard guard(this->dequeuing);
+#endif
+		// See try_dequeue() for reasoning
+
+		Block* frontBlock_ = frontBlock.load();
+		size_t blockTail = frontBlock_->localTail;
+		size_t blockFront = frontBlock_->front.load();
+
+		if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
+			fence(memory_order_acquire);
+		non_empty_front_block:
+			return reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
+		}
+		else if (frontBlock_ != tailBlock.load()) {
+			fence(memory_order_acquire);
+			frontBlock_ = frontBlock.load();
+			blockTail = frontBlock_->localTail = frontBlock_->tail.load();
+			blockFront = frontBlock_->front.load();
+			fence(memory_order_acquire);
+
+			if (blockFront != blockTail) {
+				goto non_empty_front_block;
+			}
+
+			Block* nextBlock = frontBlock_->next;
+
+			size_t nextBlockFront = nextBlock->front.load();
+			fence(memory_order_acquire);
+
+			assert(nextBlockFront != nextBlock->tail.load());
+			return reinterpret_cast<T*>(nextBlock->data + nextBlockFront * sizeof(T));
+		}
+
+		return nullptr;
+	}
+
+	// Removes the front element from the queue, if any, without returning it.
+	// Returns true on success, or false if the queue appeared empty at the time
+	// `pop` was called.
+	bool pop()
+	{
+#ifndef NDEBUG
+		ReentrantGuard guard(this->dequeuing);
+#endif
+		// See try_dequeue() for reasoning
+
+		Block* frontBlock_ = frontBlock.load();
+		size_t blockTail = frontBlock_->localTail;
+		size_t blockFront = frontBlock_->front.load();
+
+		if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
+			fence(memory_order_acquire);
+
+		non_empty_front_block:
+			auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
+			element->~T();
+
+			blockFront = (blockFront + 1) & frontBlock_->sizeMask;
+
+			fence(memory_order_release);
+			frontBlock_->front = blockFront;
+		}
+		else if (frontBlock_ != tailBlock.load()) {
+			fence(memory_order_acquire);
+			frontBlock_ = frontBlock.load();
+			blockTail = frontBlock_->localTail = frontBlock_->tail.load();
+			blockFront = frontBlock_->front.load();
+			fence(memory_order_acquire);
+
+			if (blockFront != blockTail) {
+				goto non_empty_front_block;
+			}
+
+			// Front block is empty but there's another block ahead, advance to it
+			Block* nextBlock = frontBlock_->next;
+
+			size_t nextBlockFront = nextBlock->front.load();
+			size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
+			fence(memory_order_acquire);
+
+			assert(nextBlockFront != nextBlockTail);
+			AE_UNUSED(nextBlockTail);
+
+			fence(memory_order_release);
+			frontBlock = frontBlock_ = nextBlock;
+
+			compiler_fence(memory_order_release);
+
+			auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
+			element->~T();
+
+			nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
+
+			fence(memory_order_release);
+			frontBlock_->front = nextBlockFront;
+		}
+		else {
+			// No elements in current block and no other block to advance to
+			return false;
+		}
+
+		return true;
+	}
+
+	// Returns the approximate number of items currently in the queue.
+	// Safe to call from both the producer and consumer threads.
+	inline size_t size_approx() const
+	{
+		size_t result = 0;
+		Block* frontBlock_ = frontBlock.load();
+		Block* block = frontBlock_;
+		do {
+			fence(memory_order_acquire);
+			size_t blockFront = block->front.load();
+			size_t blockTail = block->tail.load();
+			result += (blockTail - blockFront) & block->sizeMask;
+			block = block->next.load();
+		} while (block != frontBlock_);
+		return result;
+	}
+
+
+private:
+	enum AllocationMode { CanAlloc, CannotAlloc };
+
+	template<AllocationMode canAlloc, typename U>
+	bool inner_enqueue(U&& element)
+	{
+#ifndef NDEBUG
+		ReentrantGuard guard(this->enqueuing);
+#endif
+
+		// High-level pseudocode (assuming we're allowed to alloc a new block):
+		// If room in tail block, add to tail
+		// Else check next block
+		//     If next block is not the head block, enqueue on next block
+		//     Else create a new block and enqueue there
+		//     Advance tail to the block we just enqueued to
+
+		Block* tailBlock_ = tailBlock.load();
+		size_t blockFront = tailBlock_->localFront;
+		size_t blockTail = tailBlock_->tail.load();
+
+		size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask;
+		if (nextBlockTail != blockFront || nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) {
+			fence(memory_order_acquire);
+			// This block has room for at least one more element
+			char* location = tailBlock_->data + blockTail * sizeof(T);
+			new (location) T(std::forward<U>(element));
+
+			fence(memory_order_release);
+			tailBlock_->tail = nextBlockTail;
+		}
+		else {
+			fence(memory_order_acquire);
+			if (tailBlock_->next.load() != frontBlock) {
+				// Note that the reason we can't advance to the frontBlock and start adding new entries there
+				// is because if we did, then dequeue would stay in that block, eventually reading the new values,
+				// instead of advancing to the next full block (whose values were enqueued first and so should be
+				// consumed first).
+
+				fence(memory_order_acquire);		// Ensure we get latest writes if we got the latest frontBlock
+
+				// tailBlock is full, but there's a free block ahead, use it
+				Block* tailBlockNext = tailBlock_->next.load();
+				size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load();
+				nextBlockTail = tailBlockNext->tail.load();
+				fence(memory_order_acquire);
+
+				// This block must be empty since it's not the head block and we
+				// go through the blocks in a circle
+				assert(nextBlockFront == nextBlockTail);
+				tailBlockNext->localFront = nextBlockFront;
+
+				char* location = tailBlockNext->data + nextBlockTail * sizeof(T);
+				new (location) T(std::forward<U>(element));
+
+				tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask;
+
+				fence(memory_order_release);
+				tailBlock = tailBlockNext;
+			}
+			else if (canAlloc == CanAlloc) {
+				// tailBlock is full and there's no free block ahead; create a new block
+				auto newBlockSize = largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2;
+				auto newBlock = make_block(newBlockSize);
+				if (newBlock == nullptr) {
+					// Could not allocate a block!
+					return false;
+				}
+				largestBlockSize = newBlockSize;
+
+				new (newBlock->data) T(std::forward<U>(element));
+
+				assert(newBlock->front == 0);
+				newBlock->tail = newBlock->localTail = 1;
+
+				newBlock->next = tailBlock_->next.load();
+				tailBlock_->next = newBlock;
+
+				// Might be possible for the dequeue thread to see the new tailBlock->next
+				// *without* seeing the new tailBlock value, but this is OK since it can't
+				// advance to the next block until tailBlock is set anyway (because the only
+				// case where it could try to read the next is if it's already at the tailBlock,
+				// and it won't advance past tailBlock in any circumstance).
+
+				fence(memory_order_release);
+				tailBlock = newBlock;
+			}
+			else if (canAlloc == CannotAlloc) {
+				// Would have had to allocate a new block to enqueue, but not allowed
+				return false;
+			}
+			else {
+				assert(false && "Should be unreachable code");
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+
+	// Disable copying
+	ReaderWriterQueue(ReaderWriterQueue const&) {  }
+
+	// Disable assignment
+	ReaderWriterQueue& operator=(ReaderWriterQueue const&) {  }
+
+
+
+	AE_FORCEINLINE static size_t ceilToPow2(size_t x)
+	{
+		// From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
+		--x;
+		x |= x >> 1;
+		x |= x >> 2;
+		x |= x >> 4;
+		for (size_t i = 1; i < sizeof(size_t); i <<= 1) {
+			x |= x >> (i << 3);
+		}
+		++x;
+		return x;
+	}
+
+	template<typename U>
+	static AE_FORCEINLINE char* align_for(char* ptr)
+	{
+		const std::size_t alignment = std::alignment_of<U>::value;
+		return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
+	}
+private:
+#ifndef NDEBUG
+	struct ReentrantGuard
+	{
+		ReentrantGuard(bool& _inSection)
+			: inSection(_inSection)
+		{
+			assert(!inSection);
+			if (inSection) {
+				throw std::runtime_error("ReaderWriterQueue does not support enqueuing or dequeuing elements from other elements' ctors and dtors");
+			}
+
+			inSection = true;
+		}
+
+		~ReentrantGuard() { inSection = false; }
+
+	private:
+		ReentrantGuard& operator=(ReentrantGuard const&);
+
+	private:
+		bool& inSection;
+	};
+#endif
+
+	struct Block
+	{
+		// Avoid false-sharing by putting highly contended variables on their own cache lines
+		weak_atomic<size_t> front;	// (Atomic) Elements are read from here
+		size_t localTail;			// An uncontended shadow copy of tail, owned by the consumer
+
+		char cachelineFiller0[CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) - sizeof(size_t)];
+		weak_atomic<size_t> tail;	// (Atomic) Elements are enqueued here
+		size_t localFront;
+
+		char cachelineFiller1[CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) - sizeof(size_t)];	// next isn't very contended, but we don't want it on the same cache line as tail (which is)
+		weak_atomic<Block*> next;	// (Atomic)
+
+		char* data;		// Contents (on heap) are aligned to T's alignment
+
+		const size_t sizeMask;
+
+
+		// size must be a power of two (and greater than 0)
+		Block(size_t const& _size, char* _rawThis, char* _data)
+			: front(0), localTail(0), tail(0), localFront(0), next(nullptr), data(_data), sizeMask(_size - 1), rawThis(_rawThis)
+		{
+		}
+
+	private:
+		// C4512 - Assignment operator could not be generated
+		Block& operator=(Block const&);
+
+	public:
+		char* rawThis;
+	};
+
+
+	static Block* make_block(size_t capacity)
+	{
+		// Allocate enough memory for the block itself, as well as all the elements it will contain
+		auto size = sizeof(Block) + std::alignment_of<Block>::value - 1;
+		size += sizeof(T) * capacity + std::alignment_of<T>::value - 1;
+		auto newBlockRaw = static_cast<char*>(std::malloc(size));
+		if (newBlockRaw == nullptr) {
+			return nullptr;
+		}
+
+		auto newBlockAligned = align_for<Block>(newBlockRaw);
+		auto newBlockData = align_for<T>(newBlockAligned + sizeof(Block));
+		return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData);
+	}
+
+private:
+	weak_atomic<Block*> frontBlock;		// (Atomic) Elements are enqueued to this block
+
+	char cachelineFiller[CACHE_LINE_SIZE - sizeof(weak_atomic<Block*>)];
+	weak_atomic<Block*> tailBlock;		// (Atomic) Elements are dequeued from this block
+
+	size_t largestBlockSize;
+
+#ifndef NDEBUG
+	bool enqueuing;
+	bool dequeuing;
+#endif
+};
+
+// Like ReaderWriterQueue, but also providees blocking operations
+template<typename T, size_t MAX_BLOCK_SIZE = 512>
+class BlockingReaderWriterQueue
+{
+private:
+	typedef ::moodycamel::ReaderWriterQueue<T, MAX_BLOCK_SIZE> ReaderWriterQueue;
+
+public:
+	explicit BlockingReaderWriterQueue(size_t maxSize = 15)
+		: inner(maxSize)
+	{ }
+
+
+	// Enqueues a copy of element if there is room in the queue.
+	// Returns true if the element was enqueued, false otherwise.
+	// Does not allocate memory.
+	AE_FORCEINLINE bool try_enqueue(T const& element)
+	{
+		if (inner.try_enqueue(element)) {
+			sema.signal();
+			return true;
+		}
+		return false;
+	}
+
+	// Enqueues a moved copy of element if there is room in the queue.
+	// Returns true if the element was enqueued, false otherwise.
+	// Does not allocate memory.
+	AE_FORCEINLINE bool try_enqueue(T&& element)
+	{
+		if (inner.try_enqueue(std::forward<T>(element))) {
+			sema.signal();
+			return true;
+		}
+		return false;
+	}
+
+
+	// Enqueues a copy of element on the queue.
+	// Allocates an additional block of memory if needed.
+	// Only fails (returns false) if memory allocation fails.
+	AE_FORCEINLINE bool enqueue(T const& element)
+	{
+		if (inner.enqueue(element)) {
+			sema.signal();
+			return true;
+		}
+		return false;
+	}
+
+	// Enqueues a moved copy of element on the queue.
+	// Allocates an additional block of memory if needed.
+	// Only fails (returns false) if memory allocation fails.
+	AE_FORCEINLINE bool enqueue(T&& element)
+	{
+		if (inner.enqueue(std::forward<T>(element))) {
+			sema.signal();
+			return true;
+		}
+		return false;
+	}
+
+
+	// Attempts to dequeue an element; if the queue is empty,
+	// returns false instead. If the queue has at least one element,
+	// moves front to result using operator=, then returns true.
+	template<typename U>
+	bool try_dequeue(U& result)
+	{
+		if (sema.tryWait()) {
+			bool success = inner.try_dequeue(result);
+			assert(success);
+			AE_UNUSED(success);
+			return true;
+		}
+		return false;
+	}
+
+
+	// Attempts to dequeue an element; if the queue is empty,
+	// waits until an element is available, then dequeues it.
+	template<typename U>
+	void wait_dequeue(U& result)
+	{
+		sema.wait();
+		bool success = inner.try_dequeue(result);
+		AE_UNUSED(result);
+		assert(success);
+		AE_UNUSED(success);
+	}
+
+
+	// Returns a pointer to the front element in the queue (the one that
+	// would be removed next by a call to `try_dequeue` or `pop`). If the
+	// queue appears empty at the time the method is called, nullptr is
+	// returned instead.
+	// Must be called only from the consumer thread.
+	AE_FORCEINLINE T* peek()
+	{
+		return inner.peek();
+	}
+
+	// Removes the front element from the queue, if any, without returning it.
+	// Returns true on success, or false if the queue appeared empty at the time
+	// `pop` was called.
+	AE_FORCEINLINE bool pop()
+	{
+		if (sema.tryWait()) {
+			bool result = inner.pop();
+			assert(result);
+			AE_UNUSED(result);
+			return true;
+		}
+		return false;
+	}
+
+	// Returns the approximate number of items currently in the queue.
+	// Safe to call from both the producer and consumer threads.
+	AE_FORCEINLINE size_t size_approx() const
+	{
+		return sema.availableApprox();
+	}
+
+
+private:
+	// Disable copying & assignment
+	BlockingReaderWriterQueue(ReaderWriterQueue const&) {  }
+	BlockingReaderWriterQueue& operator=(ReaderWriterQueue const&) {  }
+
+private:
+	ReaderWriterQueue inner;
+	spsc_sema::LightweightSemaphore sema;
+};
+
+}    // end namespace moodycamel
+
+#ifdef AE_VCPP
+#pragma warning(pop)
+#endif
diff --git a/gr-osmosdr/lib/hackrf/hackrf_sink_c.cc b/gr-osmosdr/lib/hackrf/hackrf_sink_c.cc
index 8ca1b3e..ee089c6 100644
--- a/gr-osmosdr/lib/hackrf/hackrf_sink_c.cc
+++ b/gr-osmosdr/lib/hackrf/hackrf_sink_c.cc
@@ -56,17 +56,18 @@ using namespace boost::assign;
 
 #define BYTES_PER_SAMPLE  2 /* HackRF device consumes 8 bit unsigned IQ data */
 
-#define HACKRF_FORMAT_ERROR(ret) \
-  boost::str( boost::format("(%d) %s") \
-    % ret % hackrf_error_name((enum hackrf_error)ret) ) \
+#define HACKRF_FORMAT_ERROR(ret, msg) \
+  boost::str( boost::format(msg " (%1%) %2%") \
+    % ret % hackrf_error_name((enum hackrf_error)ret) )
 
 #define HACKRF_THROW_ON_ERROR(ret, msg) \
-  if ( ret != HACKRF_SUCCESS )  \
-  throw std::runtime_error( boost::str( boost::format(msg " (%d) %s") \
-      % ret % hackrf_error_name((enum hackrf_error)ret) ) );
+  if ( ret != HACKRF_SUCCESS ) \
+  { \
+    throw std::runtime_error( HACKRF_FORMAT_ERROR(ret, msg) ); \
+  }
 
 #define HACKRF_FUNC_STR(func, arg) \
-  boost::str(boost::format(func "(%d)") % arg) + " has failed"
+  boost::str(boost::format(func "(%1%)") % arg) + " has failed"
 
 static inline bool cb_init(circular_buffer_t *cb, size_t capacity, size_t sz)
 {
@@ -235,7 +236,7 @@ hackrf_sink_c::hackrf_sink_c (const std::string &args)
     ret = hackrf_set_antenna_enable(_dev, static_cast<uint8_t>(bias));
     if ( ret != HACKRF_SUCCESS )
     {
-      std::cerr << "Failed to apply antenna bias voltage state: " << bias << " " << HACKRF_FORMAT_ERROR(ret) << std::endl;
+      std::cerr << "Failed to apply antenna bias voltage state: " << bias << HACKRF_FORMAT_ERROR(ret, "") << std::endl;
     }
     else
     {
@@ -261,9 +262,15 @@ hackrf_sink_c::~hackrf_sink_c ()
   if (_dev) {
 //    _thread.join();
     int ret = hackrf_stop_tx( _dev );
-    HACKRF_THROW_ON_ERROR(ret, "Failed to stop TX streaming")
+    if ( ret != HACKRF_SUCCESS )
+    {
+      std::cerr << HACKRF_FORMAT_ERROR(ret, "Failed to stop TX streaming") << std::endl;
+    }
     ret = hackrf_close( _dev );
-    HACKRF_THROW_ON_ERROR(ret, "Failed to close HackRF")
+    if ( ret != HACKRF_SUCCESS )
+    {
+      std::cerr << HACKRF_FORMAT_ERROR(ret, "Failed to close HackRF") << std::endl;
+    }
     _dev = NULL;
 
     {
diff --git a/gr-osmosdr/lib/hackrf/hackrf_source_c.cc b/gr-osmosdr/lib/hackrf/hackrf_source_c.cc
index 4211603..30b63c7 100644
--- a/gr-osmosdr/lib/hackrf/hackrf_source_c.cc
+++ b/gr-osmosdr/lib/hackrf/hackrf_source_c.cc
@@ -49,17 +49,18 @@ using namespace boost::assign;
 
 #define BYTES_PER_SAMPLE  2 /* HackRF device produces 8 bit unsigned IQ data */
 
-#define HACKRF_FORMAT_ERROR(ret) \
-  boost::str( boost::format("(%d) %s") \
-    % ret % hackrf_error_name((enum hackrf_error)ret) ) \
+#define HACKRF_FORMAT_ERROR(ret, msg) \
+  boost::str( boost::format(msg " (%1%) %2%") \
+    % ret % hackrf_error_name((enum hackrf_error)ret) )
 
 #define HACKRF_THROW_ON_ERROR(ret, msg) \
-  if ( ret != HACKRF_SUCCESS )  \
-  throw std::runtime_error( boost::str( boost::format(msg " (%d) %s") \
-      % ret % hackrf_error_name((enum hackrf_error)ret) ) );
+  if ( ret != HACKRF_SUCCESS ) \
+  { \
+    throw std::runtime_error( HACKRF_FORMAT_ERROR(ret, msg) ); \
+  }
 
 #define HACKRF_FUNC_STR(func, arg) \
-  boost::str(boost::format(func "(%d)") % arg) + " has failed"
+  boost::str(boost::format(func "(%1%)") % arg) + " has failed"
 
 int hackrf_source_c::_usage = 0;
 boost::mutex hackrf_source_c::_usage_mutex;
@@ -214,7 +215,7 @@ hackrf_source_c::hackrf_source_c (const std::string &args)
     ret = hackrf_set_antenna_enable(_dev, static_cast<uint8_t>(bias));
     if ( ret != HACKRF_SUCCESS )
     {
-      std::cerr << "Failed to apply antenna bias voltage state: " << bias << " " << HACKRF_FORMAT_ERROR(ret) << std::endl;
+      std::cerr << "Failed to apply antenna bias voltage state: " << bias << HACKRF_FORMAT_ERROR(ret, "") << std::endl;
     }
     else
     {
@@ -243,9 +244,15 @@ hackrf_source_c::~hackrf_source_c ()
   if (_dev) {
 //    _thread.join();
     int ret = hackrf_stop_rx( _dev );
-    HACKRF_THROW_ON_ERROR(ret, "Failed to stop RX streaming")
+    if ( ret != HACKRF_SUCCESS )
+    {
+      std::cerr << HACKRF_FORMAT_ERROR(ret, "Failed to stop RX streaming") << std::endl;
+    }
     ret = hackrf_close( _dev );
-    HACKRF_THROW_ON_ERROR(ret, "Failed to close HackRF")
+    if ( ret != HACKRF_SUCCESS )
+    {
+      std::cerr << HACKRF_FORMAT_ERROR(ret, "Failed to close HackRF") << std::endl;
+    }
     _dev = NULL;
 
     {
diff --git a/gr-osmosdr/lib/rfspace/rfspace_source_c.cc b/gr-osmosdr/lib/rfspace/rfspace_source_c.cc
index 93d645d..80f34df 100644
--- a/gr-osmosdr/lib/rfspace/rfspace_source_c.cc
+++ b/gr-osmosdr/lib/rfspace/rfspace_source_c.cc
@@ -343,6 +343,9 @@ rfspace_source_c::rfspace_source_c (const std::string &args)
 
   }
 
+  /* Wait 10 ms before sending queries to device (required for networked radios). */
+  boost::this_thread::sleep_for(boost::chrono::milliseconds(10));
+
   /* request & print device information */
 
   std::vector< unsigned char > response;
@@ -474,6 +477,16 @@ rfspace_source_c::rfspace_source_c (const std::string &args)
     set_sample_rate( 240000 );
     set_bandwidth( 0 );
   }
+
+  /* start TCP keepalive thread */
+  if ( RFSPACE_NETSDR == _radio ||
+       RFSPACE_SDR_IP == _radio ||
+       RFSPACE_CLOUDIQ == _radio )
+  {
+    _run_tcp_keepalive_task = true;
+    _thread = gr::thread::thread( boost::bind(&rfspace_source_c::tcp_keepalive_task, this) );
+  }
+
 #if 0
   std::cerr << "sample_rates: " << get_sample_rates().to_pp_string() << std::endl;
   std::cerr << "sample rate: " << (uint32_t)get_sample_rate() << std::endl;
@@ -504,6 +517,12 @@ rfspace_source_c::~rfspace_source_c ()
 
     _thread.join();
   }
+  else
+  {
+    _run_tcp_keepalive_task = false;
+    _thread.interrupt();
+    _thread.join();
+  }
 
   close(_usb);
 
@@ -579,6 +598,8 @@ bool rfspace_source_c::transaction( const unsigned char *cmd, size_t size,
   }
   else
   {
+    boost::mutex::scoped_lock lock(_tcp_lock);
+
 #ifdef USE_ASIO
     _t.write_some( boost::asio::buffer(cmd, size) );
 
@@ -729,6 +750,22 @@ void rfspace_source_c::usb_read_task()
   }
 }
 
+/* send periodic status requests to keep TCP connection alive */
+void rfspace_source_c::tcp_keepalive_task()
+{
+  std::vector< unsigned char > response;
+  unsigned char status_pkt[] = { 0x04, 0x20, 0x05, 0x00 };
+
+  if ( -1 == _tcp )
+    return;
+
+  while ( _run_tcp_keepalive_task )
+  {
+    boost::this_thread::sleep_for(boost::chrono::seconds(60));
+    transaction( status_pkt, sizeof(status_pkt), response );
+  }
+}
+
 bool rfspace_source_c::start()
 {
   _sequence = 0;
diff --git a/gr-osmosdr/lib/rfspace/rfspace_source_c.h b/gr-osmosdr/lib/rfspace/rfspace_source_c.h
index 7d89ddc..c656063 100644
--- a/gr-osmosdr/lib/rfspace/rfspace_source_c.h
+++ b/gr-osmosdr/lib/rfspace/rfspace_source_c.h
@@ -129,6 +129,7 @@ private: /* functions */
                     std::vector< unsigned char > &response );
 
   void usb_read_task();
+  void tcp_keepalive_task();
 
 private: /* members */
   enum radio_type
@@ -162,6 +163,8 @@ private: /* members */
 
   gr::thread::thread _thread;
   bool _run_usb_read_task;
+  bool _run_tcp_keepalive_task;
+  boost::mutex _tcp_lock;
 
   boost::circular_buffer<gr_complex> *_fifo;
   boost::mutex _fifo_lock;
diff --git a/gr-osmosdr/lib/rtl/rtl_source_c.cc b/gr-osmosdr/lib/rtl/rtl_source_c.cc
index 93328b8..97252a7 100644
--- a/gr-osmosdr/lib/rtl/rtl_source_c.cc
+++ b/gr-osmosdr/lib/rtl/rtl_source_c.cc
@@ -661,7 +661,7 @@ double rtl_source_c::set_if_gain(double gain, size_t chan)
           sum += gains[ j + 1 ];
       }
 
-      double err = abs(gain - sum);
+      double err = std::abs(gain - sum);
       if (err < error) {
         error = err;
         gains[ i + 1 ] = g;
diff --git a/gr-osmosdr/lib/rtl_tcp/rtl_tcp_source_c.cc b/gr-osmosdr/lib/rtl_tcp/rtl_tcp_source_c.cc
index a365688..1b2c533 100644
--- a/gr-osmosdr/lib/rtl_tcp/rtl_tcp_source_c.cc
+++ b/gr-osmosdr/lib/rtl_tcp/rtl_tcp_source_c.cc
@@ -432,7 +432,7 @@ double rtl_tcp_source_c::set_if_gain(double gain, size_t chan)
           sum += gains[ j + 1 ];
       }
 
-      double err = abs(gain - sum);
+      double err = std::abs(gain - sum);
       if (err < error) {
         error = err;
         gains[ i + 1 ] = g;
diff --git a/gr-osmosdr/lib/sink_impl.cc b/gr-osmosdr/lib/sink_impl.cc
index f291b95..285638b 100644
--- a/gr-osmosdr/lib/sink_impl.cc
+++ b/gr-osmosdr/lib/sink_impl.cc
@@ -47,6 +47,9 @@
 #ifdef ENABLE_REDPITAYA
 #include "redpitaya_sink_c.h"
 #endif
+#ifdef ENABLE_FREESRP
+#include <freesrp_sink_c.h>
+#endif
 #ifdef ENABLE_FILE
 #include "file_sink_c.h"
 #endif
@@ -99,6 +102,9 @@ sink_impl::sink_impl( const std::string &args )
 #ifdef ENABLE_REDPITAYA
   dev_types.push_back("redpitaya");
 #endif
+#ifdef ENABLE_FREESRP
+  dev_types.push_back("freesrp");
+#endif
 #ifdef ENABLE_FILE
   dev_types.push_back("file");
 #endif
@@ -145,6 +151,10 @@ sink_impl::sink_impl( const std::string &args )
     BOOST_FOREACH( std::string dev, redpitaya_sink_c::get_devices() )
       dev_list.push_back( dev );
 #endif
+#ifdef ENABLE_FREESRP
+    BOOST_FOREACH( std::string dev, freesrp_sink_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
 #ifdef ENABLE_FILE
     BOOST_FOREACH( std::string dev, file_sink_c::get_devices() )
       dev_list.push_back( dev );
@@ -201,6 +211,12 @@ sink_impl::sink_impl( const std::string &args )
       block = sink; iface = sink.get();
     }
 #endif
+#ifdef ENABLE_FREESRP
+    if ( dict.count("freesrp") ) {
+      freesrp_sink_c_sptr sink = make_freesrp_sink_c( arg );
+      block = sink; iface = sink.get();
+    }
+#endif
 #ifdef ENABLE_FILE
     if ( dict.count("file") ) {
       file_sink_c_sptr sink = make_file_sink_c( arg );
diff --git a/gr-osmosdr/lib/soapy/CMakeLists.txt b/gr-osmosdr/lib/soapy/CMakeLists.txt
index 6ada023..338bcaf 100644
--- a/gr-osmosdr/lib/soapy/CMakeLists.txt
+++ b/gr-osmosdr/lib/soapy/CMakeLists.txt
@@ -27,6 +27,7 @@ include_directories(
 )
 
 set(soapy_srcs
+    ${CMAKE_CURRENT_SOURCE_DIR}/soapy_common.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/soapy_source_c.cc
     ${CMAKE_CURRENT_SOURCE_DIR}/soapy_sink_c.cc
 )
diff --git a/gr-osmosdr/lib/soapy/soapy_common.cc b/gr-osmosdr/lib/soapy/soapy_common.cc
new file mode 100644
index 0000000..0e277e4
--- /dev/null
+++ b/gr-osmosdr/lib/soapy/soapy_common.cc
@@ -0,0 +1,43 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2017 Josh Blum <josh at joshknows.com>
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "soapy_common.h"
+#include <SoapySDR/Version.hpp>
+
+osmosdr::gain_range_t soapy_range_to_gain_range(const SoapySDR::Range &r)
+{
+    //default step size when unspecified
+    double step = 1.0;
+
+    //support the step size in 0.6 API and above
+    //but do not allow unspecified steps
+    //to avoid device by zero in some applications
+    #ifdef SOAPY_SDR_API_HAS_RANGE_TYPE_STEP
+    if (r.step() != 0.0) step = r.step();
+    #endif
+
+    return osmosdr::gain_range_t(r.minimum(), r.maximum(), step);
+}
+
+boost::mutex &get_soapy_maker_mutex(void)
+{
+    static boost::mutex m;
+    return m;
+}
diff --git a/gr-osmosdr/lib/soapy/soapy_common.h b/gr-osmosdr/lib/soapy/soapy_common.h
new file mode 100644
index 0000000..87e46a5
--- /dev/null
+++ b/gr-osmosdr/lib/soapy/soapy_common.h
@@ -0,0 +1,40 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2017 Josh Blum <josh at joshknows.com>
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_SOAPY_COMMON_H
+#define INCLUDED_SOAPY_COMMON_H
+
+#include <osmosdr/ranges.h>
+#include <SoapySDR/Types.hpp>
+#include <boost/thread/mutex.hpp>
+
+/*!
+ * Convert a soapy range to a gain range.
+ * Careful to deal with the step size when zero.
+ */
+osmosdr::gain_range_t soapy_range_to_gain_range(const SoapySDR::Range &r);
+
+/*!
+ * Global mutex to protect factory routines.
+ * (optional under 0.5 release above)
+ */
+boost::mutex &get_soapy_maker_mutex(void);
+
+#endif /* INCLUDED_SOAPY_COMMON_H */
diff --git a/gr-osmosdr/lib/soapy/soapy_sink_c.cc b/gr-osmosdr/lib/soapy/soapy_sink_c.cc
index 9e3c364..1af5a65 100644
--- a/gr-osmosdr/lib/soapy/soapy_sink_c.cc
+++ b/gr-osmosdr/lib/soapy/soapy_sink_c.cc
@@ -1,6 +1,6 @@
 /* -*- c++ -*- */
 /*
- * Copyright 2015-2016 Josh Blum <josh at joshknows.com>
+ * Copyright 2015-2017 Josh Blum <josh at joshknows.com>
  * Copyright 2013 Dimitri Stolnikov <horiz0n at gmx.net>
  *
  * GNU Radio is free software; you can redistribute it and/or modify
@@ -38,17 +38,12 @@
 
 #include "arg_helpers.h"
 #include "soapy_sink_c.h"
+#include "soapy_common.h"
 #include <SoapySDR/Device.hpp>
 #include <SoapySDR/Version.hpp>
 
 using namespace boost::assign;
 
-boost::mutex &get_soapy_maker_mutex(void)
-{
-    static boost::mutex m;
-    return m;
-}
-
 /*
  * Create a new instance of soapy_sink_c and return
  * a boost shared_ptr.  This is effectively the public constructor.
@@ -185,14 +180,14 @@ std::vector<std::string> soapy_sink_c::get_gain_names( size_t chan)
 osmosdr::gain_range_t soapy_sink_c::get_gain_range( size_t chan)
 {
     SoapySDR::Range r = _device->getGainRange(SOAPY_SDR_TX, chan);
-    return osmosdr::gain_range_t(r.minimum(), r.maximum(), 1.0);
+    return soapy_range_to_gain_range(r);
 }
 
 osmosdr::gain_range_t soapy_sink_c::get_gain_range( const std::string & name,
                                                 size_t chan)
 {
     SoapySDR::Range r = _device->getGainRange(SOAPY_SDR_TX, chan, name);
-    return osmosdr::gain_range_t(r.minimum(), r.maximum(), 1.0);
+    return soapy_range_to_gain_range(r);
 }
 
 bool soapy_sink_c::set_gain_mode( bool automatic, size_t chan)
diff --git a/gr-osmosdr/lib/soapy/soapy_source_c.cc b/gr-osmosdr/lib/soapy/soapy_source_c.cc
index 608138e..d066ed8 100644
--- a/gr-osmosdr/lib/soapy/soapy_source_c.cc
+++ b/gr-osmosdr/lib/soapy/soapy_source_c.cc
@@ -1,6 +1,6 @@
 /* -*- c++ -*- */
 /*
- * Copyright 2015-2016 Josh Blum <josh at joshknows.com>
+ * Copyright 2015-2017 Josh Blum <josh at joshknows.com>
  * Copyright 2013 Dimitri Stolnikov <horiz0n at gmx.net>
  *
  * GNU Radio is free software; you can redistribute it and/or modify
@@ -38,14 +38,13 @@
 
 #include "arg_helpers.h"
 #include "soapy_source_c.h"
+#include "soapy_common.h"
 #include "osmosdr/source.h"
 #include <SoapySDR/Device.hpp>
 #include <SoapySDR/Version.hpp>
 
 using namespace boost::assign;
 
-boost::mutex &get_soapy_maker_mutex(void);
-
 /*
  * Create a new instance of soapy_source_c and return
  * a boost shared_ptr.  This is effectively the public constructor.
@@ -182,14 +181,14 @@ std::vector<std::string> soapy_source_c::get_gain_names( size_t chan )
 osmosdr::gain_range_t soapy_source_c::get_gain_range( size_t chan )
 {
     SoapySDR::Range r = _device->getGainRange(SOAPY_SDR_RX, chan);
-    return osmosdr::gain_range_t(r.minimum(), r.maximum(), 1.0);
+    return soapy_range_to_gain_range(r);
 }
 
 osmosdr::gain_range_t soapy_source_c::get_gain_range( const std::string & name,
                                                 size_t chan )
 {
     SoapySDR::Range r = _device->getGainRange(SOAPY_SDR_RX, chan, name);
-    return osmosdr::gain_range_t(r.minimum(), r.maximum(), 1.0);
+    return soapy_range_to_gain_range(r);
 }
 
 bool soapy_source_c::set_gain_mode( bool automatic, size_t chan )
diff --git a/gr-osmosdr/lib/source_impl.cc b/gr-osmosdr/lib/source_impl.cc
index 3aa17f9..a28f314 100644
--- a/gr-osmosdr/lib/source_impl.cc
+++ b/gr-osmosdr/lib/source_impl.cc
@@ -88,6 +88,11 @@
 #include <redpitaya_source_c.h>
 #endif
 
+#ifdef ENABLE_FREESRP
+#include <freesrp_source_c.h>
+#endif
+
+
 #include "arg_helpers.h"
 #include "source_impl.h"
 
@@ -163,6 +168,9 @@ source_impl::source_impl( const std::string &args )
 #ifdef ENABLE_REDPITAYA
   dev_types.push_back("redpitaya");
 #endif
+#ifdef ENABLE_FREESRP
+  dev_types.push_back("freesrp");
+#endif
   std::cerr << "gr-osmosdr "
             << GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") "
             << "gnuradio " << gr::version() << std::endl;
@@ -240,6 +248,10 @@ source_impl::source_impl( const std::string &args )
     BOOST_FOREACH( std::string dev, redpitaya_source_c::get_devices() )
       dev_list.push_back( dev );
 #endif
+#ifdef ENABLE_FREESRP
+    BOOST_FOREACH( std::string dev, freesrp_source_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
 
 //    std::cerr << std::endl;
 //    BOOST_FOREACH( std::string dev, dev_list )
@@ -364,6 +376,13 @@ source_impl::source_impl( const std::string &args )
     }
 #endif
 
+#ifdef ENABLE_FREESRP
+    if ( dict.count("freesrp") ) {
+      freesrp_source_c_sptr src = make_freesrp_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
     if ( iface != NULL && long(block.get()) != 0 ) {
       _devs.push_back( iface );
 

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



More information about the pkg-hamradio-commits mailing list