[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