diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index b720413cd6436082aead6bdfbb9e66f5b6132f6b..0743aa7ee712b487a7d2374b74b798c620bea42a 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -12,7 +12,6 @@
 include(GrPlatform) #define LIB_SUFFIX
 list(APPEND bladeRF_sources
     source_impl.cc
-    source_impl.cc
 )
 
 set(bladeRF_sources "${bladeRF_sources}" PARENT_SCOPE)
@@ -21,8 +20,17 @@ if(NOT bladeRF_sources)
     return()
 endif(NOT bladeRF_sources)
 
+MACRO (APPEND_LIB_LIST)
+    SET (gr_bladerf_libs "${gr_bladerf_libs};${ARGN}" CACHE INTERNAL "lib list")
+ENDMACRO(APPEND_LIB_LIST)
+
+set(gr_bladerf_libs "" CACHE INTERNAL "lib that accumulates link targets")
+
+APPEND_LIB_LIST(gnuradio::gnuradio-runtime)
+
 add_library(gnuradio-bladeRF SHARED ${bladeRF_sources})
-target_link_libraries(gnuradio-bladeRF gnuradio::gnuradio-runtime)
+target_link_libraries(gnuradio-bladeRF ${gr_bladerf_libs})
+
 target_include_directories(gnuradio-bladeRF
     PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
     PUBLIC $<INSTALL_INTERFACE:include>
@@ -35,6 +43,9 @@ if(APPLE)
     )
 endif(APPLE)
 
+add_subdirectory(bladerf)
+
+
 ########################################################################
 # Install built library files
 ########################################################################
diff --git a/lib/arg_helpers.h b/lib/arg_helpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..3b02b8ffb250ba640a542ebd66466a775b3c96c6
--- /dev/null
+++ b/lib/arg_helpers.h
@@ -0,0 +1,164 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2012 Dimitri Stolnikov <horiz0n@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 OSMOSDR_ARG_HELPERS_H
+#define OSMOSDR_ARG_HELPERS_H
+
+#include <iostream>
+#include <vector>
+#include <map>
+
+#include <gnuradio/io_signature.h>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/tokenizer.hpp>
+#include <ciso646>
+
+typedef std::map< std::string, std::string > dict_t;
+typedef std::pair< std::string, std::string > pair_t;
+
+inline std::string dict_to_args_string( const dict_t &d )
+{
+    std::string out;
+    for (const pair_t pair : d)
+    {
+        if (not out.empty()) out += ",";
+        out += pair.first + "='" + pair.second + "'";
+    }
+    return out;
+}
+
+inline std::vector< std::string > args_to_vector( const std::string &args )
+{
+  std::vector< std::string > result;
+
+  boost::escaped_list_separator<char> separator("\\", " ", "'");
+  typedef boost::tokenizer< boost::escaped_list_separator<char> > tokenizer_t;
+  tokenizer_t tokens(args, separator);
+
+  for (std::string token : tokens)
+    result.push_back(token);
+
+  return result;
+}
+
+inline std::vector< std::string > params_to_vector( const std::string &params )
+{
+  std::vector< std::string > result;
+
+  boost::escaped_list_separator<char> separator("\\", ",", "'");
+  typedef boost::tokenizer< boost::escaped_list_separator<char> > tokenizer_t;
+  tokenizer_t tokens(params, separator);
+
+  for (std::string token : tokens)
+    result.push_back(token);
+
+  return result;
+}
+
+inline pair_t param_to_pair( const std::string &param )
+{
+  pair_t result;
+
+  std::size_t pos = param.find('=');
+  if(pos != std::string::npos)
+  {
+    result.first = param.substr(0, pos);
+    result.second = param.substr(pos + 1);
+  }
+  else
+  {
+    result.first = param;
+    result.second = "";
+  }
+
+  return result;
+}
+
+inline dict_t params_to_dict( const std::string &params )
+{
+  dict_t result;
+
+  std::vector< std::string > param_list = params_to_vector( params );
+  for (std::string param : param_list)
+  {
+    pair_t pair = param_to_pair( param );
+    std::string value = pair.second;
+    if (value.length() && value[0] == '\'' && value[ value.length() - 1 ] == '\'')
+      value = value.substr(1, value.length() - 1);
+    result[ pair.first ] = value;
+  }
+
+  return result;
+}
+
+struct is_nchan_argument
+{
+  bool operator ()(const std::string &str)
+  {
+    return str.find("numchan=") == 0;
+  }
+};
+
+inline gr::io_signature::sptr args_to_io_signature( const std::string &args )
+{
+  size_t max_nchan = 0;
+  size_t dev_nchan = 0;
+  std::vector< std::string > arg_list = args_to_vector( args );
+
+  for (std::string arg : arg_list)
+  {
+    if ( arg.find( "numchan=" ) == 0 ) // try to parse global nchan value
+    {
+      pair_t pair = param_to_pair( arg );
+      max_nchan = boost::lexical_cast<size_t>( pair.second );
+    }
+  }
+
+  arg_list.erase( std::remove_if( // remove any global nchan tokens
+                    arg_list.begin(),
+                    arg_list.end(),
+                    is_nchan_argument() ),
+                  arg_list.end() );
+
+  // try to parse device specific nchan values, assume 1 channel if none given
+
+  for (std::string arg : arg_list)
+  {
+    dict_t dict = params_to_dict(arg);
+    if (dict.count("nchan"))
+    {
+      dev_nchan += boost::lexical_cast<size_t>( dict["nchan"] );
+    }
+    else // no channels given via args
+    {
+      dev_nchan++; // assume one channel
+    }
+  }
+
+  // if at least one nchan was given, perform a sanity check
+  if ( max_nchan && dev_nchan && max_nchan != dev_nchan )
+    throw std::runtime_error("Wrong device arguments specified. Missing nchan?");
+
+  const size_t nchan = std::max<size_t>(dev_nchan, 1); // assume at least one
+  return gr::io_signature::make(nchan, nchan, sizeof(gr_complex));
+}
+
+#endif // OSMOSDR_ARG_HELPERS_H
diff --git a/lib/bladerf/CMakeLists.txt b/lib/bladerf/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..decf2688786faa0a95f8fca54aaed98528851c68
--- /dev/null
+++ b/lib/bladerf/CMakeLists.txt
@@ -0,0 +1,39 @@
+# Copyright 2013 Free Software Foundation, Inc.
+#
+# This file is part of gr-bladeRF
+#
+# gr-bladeRF is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, or
+# (at your option) any later version.
+#
+# gr-bladeRF 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 gr-bladeRF; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+target_include_directories(gnuradio-bladeRF PRIVATE
+    ${CMAKE_CURRENT_SOURCE_DIR}
+    ${LIBBLADERF_INCLUDE_DIRS}
+    ${Volk_INCLUDE_DIRS}
+)
+
+APPEND_LIB_LIST(
+    ${LIBBLADERF_LIBRARIES}
+    ${Volk_LIBRARIES}
+)
+
+list(APPEND bladeRF_sources
+    ${CMAKE_CURRENT_SOURCE_DIR}/bladerf_source_c.cc
+    ${CMAKE_CURRENT_SOURCE_DIR}/bladerf_sink_c.cc
+    ${CMAKE_CURRENT_SOURCE_DIR}/bladerf_common.cc
+)
+set(bladeRF_sources ${bladeRF_sources} PARENT_SCOPE)
diff --git a/lib/bladerf/bladerf_common.cc b/lib/bladerf/bladerf_common.cc
new file mode 100644
index 0000000000000000000000000000000000000000..67bf7367447423fde5bd4cbeb5ad075de35924b7
--- /dev/null
+++ b/lib/bladerf/bladerf_common.cc
@@ -0,0 +1,1208 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2013-2017 Nuand LLC
+ * Copyright 2013 Dimitri Stolnikov <horiz0n@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.
+ */
+
+/*
+ * config.h is generated by configure.  It contains the results
+ * of probing for features, options etc.  It should be the first
+ * file included in your .cc file.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <boost/assign.hpp>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include "bladerf_common.h"
+
+/* Defaults for these values. */
+static size_t const NUM_BUFFERS = 512;
+static size_t const NUM_SAMPLES_PER_BUFFER = (4 * 1024);
+static size_t const NUM_TRANSFERS = 32;
+static size_t const STREAM_TIMEOUT_MS = 3000;
+
+using namespace boost::assign;
+
+std::mutex bladerf_common::_devs_mutex;
+std::list<std::weak_ptr<struct bladerf>> bladerf_common::_devs;
+
+/* name for system-wide gain (which is not its own libbladeRF gain stage) */
+static const char *SYSTEM_GAIN_NAME = "system";
+
+/* Determines if bladerf_version is greater or equal to major.minor.patch */
+static bool _version_greater_or_equal(const struct bladerf_version *version,
+                                      unsigned int major,
+                                      unsigned int minor, unsigned int patch)
+{
+  if (version->major > major) {
+    // 2.0.0 > 1.9.9
+    return true;
+  } else if ((version->major == major) && (version->minor > minor)) {
+    // 1.9.9 > 1.8.9
+    return true;
+  } else if ((version->major == major) &&
+             (version->minor == minor) &&
+             (version->patch >= patch)) {
+    // 1.8.9 > 1.8.8
+    return true;
+  } else {
+    return false;
+  }
+}
+
+/* Returns TRUE if an expansion board is attached, FALSE otherwise */
+static bool _is_xb_attached(bladerf_sptr _dev)
+{
+  int status;
+  bladerf_xb xb = BLADERF_XB_NONE;
+
+  status = bladerf_expansion_get_attached(_dev.get(), &xb);
+  if (status != 0) {
+    return false;
+  }
+
+  return (xb != BLADERF_XB_NONE);
+}
+
+/* Gets a value from a const dict */
+static std::string const _get(dict_t const &dict, std::string key)
+{
+  std::string rv("");
+
+  dict_t::const_iterator it = dict.find(key);
+
+  if (it != dict.end()) {
+    rv = it->second;
+  }
+
+  return rv;
+}
+
+static bool _is_tx(bladerf_channel ch)
+{
+  return (1 == (ch & BLADERF_DIRECTION_MASK));
+}
+
+size_t num_streams(bladerf_channel_layout layout)
+{
+#ifdef BLADERF_COMPATIBILITY
+  return 1;
+#else
+
+  switch (layout) {
+    case BLADERF_RX_X1:
+    case BLADERF_TX_X1:
+      return 1;
+    case BLADERF_RX_X2:
+    case BLADERF_TX_X2:
+      return 2;
+  }
+
+  assert(false);
+
+  return 0;
+#endif
+}
+
+/******************************************************************************
+ * Public methods
+ ******************************************************************************/
+bladerf_common::bladerf_common() :
+  _dev(NULL),
+  _pfx("[bladeRF common] "),
+  _failures(0),
+  _num_buffers(NUM_BUFFERS),
+  _samples_per_buffer(NUM_SAMPLES_PER_BUFFER),
+  _num_transfers(NUM_TRANSFERS),
+  _stream_timeout(STREAM_TIMEOUT_MS),
+  _format(BLADERF_FORMAT_SC16_Q11)
+{
+}
+
+/******************************************************************************
+ * Protected methods
+ ******************************************************************************/
+void bladerf_common::init(dict_t const &dict, bladerf_direction direction)
+{
+  int status;
+  std::string device_name("");
+  struct bladerf_version ver;
+
+  BLADERF_DEBUG("entering initialization");
+
+  _pfx = boost::str(boost::format("[bladeRF %s] ")
+          % (direction == BLADERF_TX ? "sink" : "source"));
+
+  /* libbladeRF verbosity */
+  if (dict.count("verbosity")) {
+    set_verbosity(_get(dict, "verbosity"));
+  }
+
+  /* Board identifier */
+  if (dict.count("bladerf")) {
+    std::string const value = _get(dict, "bladerf");
+    if (value.length() > 0) {
+      if (value.length() <= 2) {
+        /* If the value is two digits or less, we'll assume the user is
+         * providing an instance number */
+        unsigned int device_number = 0;
+
+        try {
+          device_number = boost::lexical_cast<unsigned int>(value);
+          device_name = boost::str(boost::format("*:instance=%d")
+                                   % device_number);
+        } catch (std::exception &ex) {
+          BLADERF_THROW(boost::str(boost::format("Failed to use '%s' as "
+                        "device number: %s") % value % ex.what()));
+        }
+
+      } else {
+        /* Otherwise, we'll assume it's a serial number. libbladeRF v1.4.1
+         * supports matching a subset of a serial number. For earlier versions,
+         * we require the entire serial number.
+         *
+         * libbladeRF is responsible for rejecting bad serial numbers, so we
+         * may just pass whatever the user has provided.
+         */
+        bladerf_version(&ver);
+        if (_version_greater_or_equal(&ver, 1, 4, 1) ||
+            value.length() == (BLADERF_SERIAL_LENGTH - 1)) {
+          device_name = std::string("*:serial=") + value;
+        } else {
+          BLADERF_THROW(boost::str(boost::format("A full serial number must "
+                        "be supplied with libbladeRF %s. libbladeRF >= v1.4.1 "
+                        "supports opening a device via a subset of its serial "
+                        "#.") % ver.describe));
+        }
+      }
+    }
+  }
+
+  /* Open the board! */
+  try {
+    BLADERF_INFO(boost::str(boost::format("Opening Nuand bladeRF with "
+                  "device identifier string '%s'") % device_name));
+
+    _dev = open(device_name);
+  } catch (std::exception &ex) {
+    BLADERF_THROW(boost::str(boost::format("Failed to open bladeRF device "
+                  "'%s': %s") % device_name % ex.what()));
+  }
+
+  if (NULL == _dev) {
+    BLADERF_THROW(boost::str(boost::format("Failed to get device handle for "
+                  "'%s': _dev is NULL") % device_name));
+  }
+
+  /* Load a FPGA */
+  if (dict.count("fpga")) {
+    if (dict.count("fpga-reload") == 0 &&
+        bladerf_is_fpga_configured(_dev.get()) == 1) {
+
+      BLADERF_WARNING("FPGA is already loaded. Set fpga-reload=1 to force a "
+                      "reload.");
+    } else {
+      std::string fpga = _get(dict, "fpga");
+
+      BLADERF_INFO("Loading FPGA bitstream from " << fpga);
+
+      status = bladerf_load_fpga(_dev.get(), fpga.c_str());
+      if (status != 0) {
+        BLADERF_WARNING("Could not load FPGA bitstream: "
+                        << bladerf_strerror(status));
+      } else {
+        BLADERF_INFO("The FPGA bitstream was loaded successfully");
+      }
+    }
+  }
+
+  if (bladerf_is_fpga_configured(_dev.get()) != 1) {
+    BLADERF_THROW("The FPGA is not configured! Provide device argument "
+                  "fpga=/path/to/the/bitstream.rbf to load it.");
+  }
+
+  /* XB-200 Transverter Board */
+  if (dict.count("xb200")) {
+    status = bladerf_expansion_attach(_dev.get(), BLADERF_XB_200);
+    if (status != 0) {
+      BLADERF_WARNING("Could not attach XB-200: " << bladerf_strerror(status));
+    } else {
+      bladerf_xb200_filter filter = BLADERF_XB200_AUTO_1DB;
+
+      if (_get(dict, "xb200") == "custom") {
+        filter = BLADERF_XB200_CUSTOM;
+      } else if (_get(dict, "xb200") == "50M") {
+        filter = BLADERF_XB200_50M;
+      } else if (_get(dict, "xb200") == "144M") {
+        filter = BLADERF_XB200_144M;
+      } else if (_get(dict, "xb200") == "222M") {
+        filter = BLADERF_XB200_222M;
+      } else if (_get(dict, "xb200") == "auto3db") {
+        filter = BLADERF_XB200_AUTO_3DB;
+      } else if (_get(dict, "xb200") == "auto") {
+        filter = BLADERF_XB200_AUTO_1DB;
+      } else {
+        filter = BLADERF_XB200_AUTO_1DB;
+      }
+
+      status = bladerf_xb200_set_filterbank(_dev.get(), direction, filter);
+      if (status != 0) {
+        BLADERF_WARNING("Could not set XB-200 filter: "
+                        << bladerf_strerror(status));
+      }
+    }
+  }
+
+  /* Show some info about the device we've opened */
+  print_device_info();
+
+  if (dict.count("tamer")) {
+    set_clock_source(_get(dict, "tamer"));
+    BLADERF_INFO(boost::str(boost::format("Tamer mode set to '%s'")
+                  % get_clock_source()));
+  }
+
+  if (dict.count("smb")) {
+    set_smb_frequency(boost::lexical_cast<double>(_get(dict, "smb")));
+    BLADERF_INFO(boost::str(boost::format("SMB frequency set to %f Hz")
+                  % get_smb_frequency()));
+  }
+
+  /* Initialize buffer and sample configuration */
+  if (dict.count("buffers")) {
+    _num_buffers = boost::lexical_cast<size_t>(_get(dict, "buffers"));
+  }
+
+  if (dict.count("buflen")) {
+    _samples_per_buffer = boost::lexical_cast<size_t>(_get(dict, "buflen"));
+  }
+
+  if (dict.count("transfers")) {
+    _num_transfers = boost::lexical_cast<size_t>(_get(dict, "transfers"));
+  }
+
+  if (dict.count("stream_timeout")) {
+    _stream_timeout = boost::lexical_cast<unsigned int>(_get(dict, "stream_timeout"));
+  } else if (dict.count("stream_timeout_ms")) {
+    // reverse compatibility
+    _stream_timeout = boost::lexical_cast<unsigned int>(_get(dict, "stream_timeout_ms"));
+  }
+
+  if (dict.count("enable_metadata") > 0) {
+    _format = BLADERF_FORMAT_SC16_Q11_META;
+  }
+
+  /* Require value to be >= 2 so we can ensure we have twice as many
+   * buffers as transfers */
+  if (_num_buffers <= 1) {
+    _num_buffers = NUM_BUFFERS;
+  }
+
+  if (0 == _samples_per_buffer) {
+    _samples_per_buffer = NUM_SAMPLES_PER_BUFFER;
+  } else {
+    if ((_samples_per_buffer < 1024) || (_samples_per_buffer % 1024 != 0)) {
+      BLADERF_WARNING(boost::str(boost::format("Invalid \"buflen\" value "
+                      "(%d). A multiple of 1024 is required. Defaulting "
+                      "to %d")
+                      % _samples_per_buffer % NUM_SAMPLES_PER_BUFFER));
+      _samples_per_buffer = NUM_SAMPLES_PER_BUFFER;
+    }
+  }
+
+  /* If the user hasn't specified the desired number of transfers, set it to
+   * at least num_buffers/2 */
+  if (0 == _num_transfers) {
+    _num_transfers = std::min(NUM_TRANSFERS, _num_buffers / 2);
+  } else if (_num_transfers >= _num_buffers) {
+    _num_transfers = std::min(NUM_TRANSFERS, _num_buffers / 2);
+    BLADERF_WARNING(boost::str(boost::format("Clamping \"transfers\" to %d. "
+                    "Try using a smaller \"transfers\" value if timeouts "
+                    "occur.") % _num_transfers));
+  }
+
+  BLADERF_INFO(boost::str(boost::format("Buffers: %d, samples per buffer: "
+                "%d, active transfers: %d")
+                % _num_buffers
+                % _samples_per_buffer
+                % _num_transfers));
+}
+
+std::vector<std::string> bladerf_common::devices()
+{
+  struct bladerf_devinfo *devices;
+  ssize_t n_devices;
+  std::vector<std::string> ret;
+
+  n_devices = bladerf_get_device_list(&devices);
+
+  if (n_devices > 0) {
+    for (ssize_t i = 0; i < n_devices; i++) {
+      std::string serial(devices[i].serial);
+      std::string devstr;
+
+      if (serial.length() == 32) {
+        serial.replace(4, 24, "...");
+      }
+
+      devstr = boost::str(boost::format("bladerf=%s,label='Nuand bladeRF%s%s'")
+                % devices[i].instance
+                % (serial.length() > 0 ? " SN " : "")
+                % serial);
+
+      ret.push_back(devstr);
+    }
+
+    bladerf_free_device_list(devices);
+  }
+
+  return ret;
+}
+
+bladerf_board_type bladerf_common::get_board_type()
+{
+  if (NULL == _dev || NULL == _dev.get()) {
+    BLADERF_WARNING("no bladeRF device is open");
+    return BOARD_TYPE_NONE;
+  }
+
+  std::string boardname = std::string(bladerf_get_board_name(_dev.get()));
+
+  if (boardname == "bladerf1") {
+    return BOARD_TYPE_BLADERF_1;
+  }
+
+  if (boardname == "bladerf2") {
+    return BOARD_TYPE_BLADERF_2;
+  }
+
+  BLADERF_WARNING(boost::str(boost::format("model '%s' is not recognized")
+                  % boardname));
+
+  return BOARD_TYPE_UNKNOWN;
+}
+
+size_t bladerf_common::get_max_channels(bladerf_direction direction)
+{
+#ifdef BLADERF_COMPATIBILITY
+  return 1;
+#else
+  return bladerf_get_channel_count(_dev.get(), direction);
+#endif
+}
+
+void bladerf_common::set_channel_enable(bladerf_channel ch, bool enable)
+{
+  _enables[ch] = enable;
+}
+
+bool bladerf_common::get_channel_enable(bladerf_channel ch)
+{
+  return _enables[ch];
+}
+
+void bladerf_common::set_verbosity(std::string const &verbosity)
+{
+  bladerf_log_level l;
+
+  if (verbosity == "verbose") {
+    l = BLADERF_LOG_LEVEL_VERBOSE;
+  } else if (verbosity == "debug") {
+    l = BLADERF_LOG_LEVEL_DEBUG;
+  } else if (verbosity == "info") {
+    l = BLADERF_LOG_LEVEL_INFO;
+  } else if (verbosity == "warning") {
+    l = BLADERF_LOG_LEVEL_WARNING;
+  } else if (verbosity == "error") {
+    l = BLADERF_LOG_LEVEL_ERROR;
+  } else if (verbosity == "critical") {
+    l = BLADERF_LOG_LEVEL_CRITICAL;
+  } else if (verbosity == "silent") {
+    l = BLADERF_LOG_LEVEL_SILENT;
+  } else {
+    BLADERF_THROW(boost::str(boost::format("Invalid log level: %s")
+                  % verbosity));
+  }
+
+  bladerf_log_set_verbosity(l);
+}
+
+bladerf_channel bladerf_common::str2channel(std::string const &ch)
+{
+  std::string prefix, numstr;
+  unsigned int numint;
+
+  /* We expect strings like "RX1" or "TX2" */
+  if (ch.length() < 3) {
+    /* It's too short */
+    return BLADERF_CHANNEL_INVALID;
+  }
+
+  prefix = ch.substr(0,2);
+  numstr = ch.substr(2,std::string::npos);
+  numint = boost::lexical_cast<unsigned int>(numstr) - 1;
+
+  if (prefix == "RX") {
+    return BLADERF_CHANNEL_RX(numint);
+  }
+
+  if (prefix == "TX") {
+    return BLADERF_CHANNEL_TX(numint);
+  }
+
+  return BLADERF_CHANNEL_INVALID;
+}
+
+std::string bladerf_common::channel2str(bladerf_channel ch)
+{
+  if (ch == BLADERF_CHANNEL_INVALID) {
+    return "OFF";
+  }
+
+  return boost::str(boost::format("%s%d")
+          % (_is_tx(ch) ? "TX" : "RX")
+          % (channel2rfport(ch) + 1));
+}
+
+int bladerf_common::channel2rfport(bladerf_channel ch)
+{
+  return (ch >> 1);
+}
+
+bladerf_channel bladerf_common::chan2channel(bladerf_direction direction,
+                                             size_t chan)
+{
+  for (bladerf_channel_map::value_type &i : _chanmap) {
+    bladerf_channel ch = i.first;
+    if (
+        (i.second == (int)chan) && (
+         (direction == BLADERF_TX && _is_tx(ch)) ||
+         (direction == BLADERF_RX && !_is_tx(ch))
+        )
+       ) {
+      return i.first;
+    }
+  }
+
+  return BLADERF_CHANNEL_INVALID;
+}
+
+osmosdr::meta_range_t bladerf_common::sample_rates(bladerf_channel ch)
+{
+  osmosdr::meta_range_t sample_rates;
+
+#ifdef BLADERF_COMPATIBILITY
+  /* assuming the same for RX & TX */
+  sample_rates += osmosdr::range_t( 160e3, 200e3, 40e3 );
+  sample_rates += osmosdr::range_t( 300e3, 900e3, 100e3 );
+  sample_rates += osmosdr::range_t( 1e6, 40e6, 1e6 );
+#else
+
+  int status;
+  const bladerf_range *brf_sample_rates;
+
+  status = bladerf_get_sample_rate_range(_dev.get(), ch, &brf_sample_rates);
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "bladerf_get_sample_rate_range failed");
+  }
+
+  /* Suggest a variety of sample rates */
+  sample_rates += osmosdr::range_t(brf_sample_rates->min,
+                                   brf_sample_rates->max / 4.0,
+                                   brf_sample_rates->max / 16.0);
+  sample_rates += osmosdr::range_t(brf_sample_rates->max / 4.0,
+                                   brf_sample_rates->max / 2.0,
+                                   brf_sample_rates->max / 8.0);
+  sample_rates += osmosdr::range_t(brf_sample_rates->max / 2.0,
+                                   brf_sample_rates->max,
+                                   brf_sample_rates->max / 4.0);
+#endif
+
+  return sample_rates;
+}
+
+double bladerf_common::set_sample_rate(double rate, bladerf_channel ch)
+{
+  int status;
+  struct bladerf_rational_rate rational_rate, actual;
+
+  rational_rate.integer = static_cast<uint32_t>(rate);
+  rational_rate.den = 10000;
+  rational_rate.num = (rate - rational_rate.integer) * rational_rate.den;
+
+  status = bladerf_set_rational_sample_rate(_dev.get(), ch,
+                                            &rational_rate, &actual);
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "Failed to set sample rate");
+  }
+
+  return actual.integer + (actual.num / static_cast<double>(actual.den));
+}
+
+double bladerf_common::get_sample_rate(bladerf_channel ch)
+{
+  int status;
+  struct bladerf_rational_rate rate;
+
+  status = bladerf_get_rational_sample_rate(_dev.get(), ch, &rate);
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "Failed to get sample rate");
+  }
+
+  return rate.integer + rate.num / static_cast<double>(rate.den);
+}
+
+osmosdr::freq_range_t bladerf_common::freq_range(bladerf_channel ch)
+{
+#ifdef BLADERF_COMPATIBILITY
+  return osmosdr::freq_range_t( _is_xb_attached(_dev) ? 0 : 280e6,
+                                BLADERF_FREQUENCY_MAX );
+#else
+
+  int status;
+  const struct bladerf_range *range;
+
+  status = bladerf_get_frequency_range(_dev.get(), ch, &range);
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "bladerf_get_frequency_range failed");
+  };
+
+  return osmosdr::freq_range_t(static_cast<double>(range->min),
+                               static_cast<double>(range->max),
+                               static_cast<double>(range->step));
+#endif
+}
+
+double bladerf_common::set_center_freq(double freq, bladerf_channel ch)
+{
+  int status;
+  uint64_t freqint = static_cast<uint64_t>(freq + 0.5);
+
+  /* Check frequency range */
+  if (freqint < freq_range(ch).start() || freqint > freq_range(ch).stop()) {
+    BLADERF_WARNING(boost::str(boost::format("Frequency %d Hz is outside "
+                    "range, ignoring") % freqint));
+  } else {
+    status = bladerf_set_frequency(_dev.get(), ch, freqint);
+    if (status != 0) {
+      BLADERF_THROW_STATUS(status, boost::str(boost::format("Failed to set center "
+                    "frequency to %d Hz") % freqint));
+    }
+  }
+
+  return get_center_freq(ch);
+}
+
+double bladerf_common::get_center_freq(bladerf_channel ch)
+{
+  int status;
+  uint64_t freq;
+
+  status = bladerf_get_frequency(_dev.get(), ch, &freq);
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "Failed to get center frequency");
+  }
+
+  return static_cast<double>(freq);
+}
+
+osmosdr::freq_range_t bladerf_common::filter_bandwidths(bladerf_channel ch)
+{
+  osmosdr::freq_range_t bandwidths;
+
+#ifdef BLADERF_COMPATIBILITY
+  std::vector<double> half_bandwidths; /* in MHz */
+  half_bandwidths += \
+      0.75, 0.875, 1.25, 1.375, 1.5, 1.92, 2.5,
+      2.75, 3, 3.5, 4.375, 5, 6, 7, 10, 14;
+
+  for (double half_bw : half_bandwidths)
+    bandwidths += osmosdr::range_t( half_bw * 2e6 );
+#else
+
+  int status;
+  const bladerf_range *range;
+
+  status = bladerf_get_bandwidth_range(_dev.get(), ch, &range);
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "bladerf_get_bandwidth_range failed");
+  }
+
+  bandwidths += osmosdr::range_t(range->min, range->max, range->step);
+#endif
+
+  return bandwidths;
+}
+
+double bladerf_common::set_bandwidth(double bandwidth, bladerf_channel ch)
+{
+  int status;
+  uint32_t bwint;
+
+  if (bandwidth == 0.0) {
+    /* bandwidth of 0 means automatic filter selection */
+    /* select narrower filters to prevent aliasing */
+    bandwidth = get_sample_rate(ch) * 0.75;
+  }
+
+  bwint = static_cast<uint32_t>(bandwidth + 0.5);
+
+  status = bladerf_set_bandwidth(_dev.get(), ch, bwint, NULL);
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "could not set bandwidth");
+  }
+
+  return get_bandwidth(ch);
+}
+
+double bladerf_common::get_bandwidth(bladerf_channel ch)
+{
+  int status;
+  uint32_t bandwidth;
+
+  status = bladerf_get_bandwidth(_dev.get(), ch, &bandwidth);
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "could not get bandwidth");
+  }
+
+  return static_cast<double>(bandwidth);
+}
+
+std::vector<std::string> bladerf_common::get_gain_names(bladerf_channel ch)
+{
+  std::vector<std::string> names;
+
+#ifdef BLADERF_COMPATIBILITY
+  names += "LNA", "VGA1", "VGA2";
+#else
+
+  const size_t max_count = 16;
+  char *gain_names[max_count];
+  int count;
+  names += SYSTEM_GAIN_NAME;
+
+  count = bladerf_get_gain_stages(_dev.get(), ch,
+                                  reinterpret_cast<const char **>(&gain_names),
+                                  max_count);
+  if (count < 0) {
+    BLADERF_THROW_STATUS(count, "Failed to enumerate gain stages");
+  }
+
+  for (int i = 0; i < count; ++i) {
+    char *tmp = gain_names[i];
+    printf("FOUND %s\n", tmp);
+    names += std::string(tmp);
+  };
+#endif
+
+  return names;
+}
+
+osmosdr::gain_range_t bladerf_common::get_gain_range(bladerf_channel ch)
+{
+  /* This is an overall system gain range. */
+  return get_gain_range(SYSTEM_GAIN_NAME, ch);
+}
+
+osmosdr::gain_range_t bladerf_common::get_gain_range(std::string const &name,
+                                                     bladerf_channel ch)
+{
+#ifdef BLADERF_COMPATIBILITY
+  if( name == "LNA" ) {
+    return osmosdr::gain_range_t( 0, 6, 3 );
+  } else if( name == "VGA1" ) {
+    return osmosdr::gain_range_t( 5, 30, 1 );
+  } else if( name == "VGA2" ) {
+    return osmosdr::gain_range_t( 0, 30, 3 );
+  } else {
+    BLADERF_THROW_STATUS(BLADERF_ERR_UNSUPPORTED, boost::str(boost::format(
+                         "Failed to get gain range for stage '%s'") % name));
+  }
+#else
+
+  int status;
+  const bladerf_range *range;
+
+  if (name == SYSTEM_GAIN_NAME) {
+    status = bladerf_get_gain_range(_dev.get(), ch, &range);
+  } else {
+    status = bladerf_get_gain_stage_range(_dev.get(), ch, name.c_str(), &range);
+  }
+
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, boost::str(boost::format("Failed to get gain "
+                         "range for stage '%s'") % name));
+  }
+
+  return osmosdr::gain_range_t(range->min, range->max, range->step);
+#endif
+}
+
+bool bladerf_common::set_gain_mode(bool automatic, bladerf_channel ch,
+                                   bladerf_gain_mode agc_mode)
+{
+  int status;
+  bladerf_gain_mode mode = automatic ? agc_mode : BLADERF_GAIN_MGC;
+
+  status = bladerf_set_gain_mode(_dev.get(), ch, mode);
+
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, boost::str(boost::format("Setting gain mode "
+                         "to '%s' failed")
+                         % (automatic ? "automatic" : "manual")));
+  }
+
+  return get_gain_mode(ch);
+}
+
+bool bladerf_common::get_gain_mode(bladerf_channel ch)
+{
+  int status;
+  bladerf_gain_mode gainmode = BLADERF_GAIN_DEFAULT;
+
+  status = bladerf_get_gain_mode(_dev.get(), ch, &gainmode);
+
+  if (status != 0) {
+    BLADERF_WARN_STATUS(status, "Failed to get gain mode");
+  }
+
+  return (gainmode != BLADERF_GAIN_MGC);
+}
+
+double bladerf_common::set_gain(double gain, bladerf_channel ch)
+{
+  return set_gain(gain, SYSTEM_GAIN_NAME, ch);
+}
+
+double bladerf_common::set_gain(double gain,
+                                std::string const &name,
+                                bladerf_channel ch)
+{
+  int status;
+
+#ifdef BLADERF_COMPATIBILITY
+  if( name == "LNA" ) {
+    bladerf_lna_gain g;
+
+    if ( gain >= 6.0f )
+      g = BLADERF_LNA_GAIN_MAX;
+    else if ( gain >= 3.0f )
+      g = BLADERF_LNA_GAIN_MID;
+    else /* gain < 3.0f */
+      g = BLADERF_LNA_GAIN_BYPASS;
+
+    status = bladerf_set_lna_gain( _dev.get(), g );
+  } else if( name == "VGA1" ) {
+    status = bladerf_set_rxvga1( _dev.get(), (int)gain );
+  } else if( name == "VGA2" ) {
+    status = bladerf_set_rxvga2( _dev.get(), (int)gain );
+  } else {
+    status = BLADERF_ERR_UNSUPPORTED;
+  }
+#else
+
+  if (name == SYSTEM_GAIN_NAME) {
+    status = bladerf_set_gain(_dev.get(), ch, static_cast<int>(gain));
+  } else {
+    status = bladerf_set_gain_stage(_dev.get(), ch, name.c_str(),
+                                    static_cast<int>(gain));
+  }
+#endif
+
+  /* Check for errors */
+  if (BLADERF_ERR_UNSUPPORTED == status) {
+    // unsupported, but not worth crashing out
+    BLADERF_WARNING(boost::str(boost::format("Gain stage '%s' not supported "
+                    "by device") % name));
+  } else if (status != 0) {
+    BLADERF_THROW_STATUS(status, boost::str(boost::format("Failed to set "
+                         "gain for stage '%s'") % name));
+  }
+
+  return get_gain(name, ch);
+}
+
+double bladerf_common::get_gain(bladerf_channel ch)
+{
+  return get_gain(SYSTEM_GAIN_NAME, ch);
+}
+
+double bladerf_common::get_gain(std::string const &name, bladerf_channel ch)
+{
+  int status;
+  int g = 0;
+
+#ifdef BLADERF_COMPATIBILITY
+  if( name == "LNA" ) {
+    bladerf_lna_gain lna_g;
+    status = bladerf_get_lna_gain( _dev.get(), &lna_g );
+    g = lna_g == BLADERF_LNA_GAIN_BYPASS ? 0 : lna_g == BLADERF_LNA_GAIN_MID ? 3 : 6;
+  } else if( name == "VGA1" ) {
+    status = bladerf_get_rxvga1( _dev.get(), &g );
+  } else if( name == "VGA2" ) {
+    status = bladerf_get_rxvga2( _dev.get(), &g );
+  } else {
+    status = BLADERF_ERR_UNSUPPORTED;
+  }
+#else
+
+  if (name == SYSTEM_GAIN_NAME) {
+    status = bladerf_get_gain(_dev.get(), ch, &g);
+  } else {
+    status = bladerf_get_gain_stage(_dev.get(), ch, name.c_str(), &g);
+  }
+#endif
+
+  /* Check for errors */
+  if (status != 0) {
+    BLADERF_WARN_STATUS(status, boost::str(boost::format("Could not get gain "
+                         "for stage '%s'") % name));
+  }
+
+  return static_cast<double>(g);
+}
+
+std::vector<std::string> bladerf_common::get_antennas(bladerf_direction dir)
+{
+  std::vector<std::string> antennas;
+
+  for (size_t i = 0; i < get_max_channels(dir); ++i) {
+    switch (dir) {
+      case BLADERF_RX:
+        antennas += channel2str(BLADERF_CHANNEL_RX(i));
+        break;
+      case BLADERF_TX:
+        antennas += channel2str(BLADERF_CHANNEL_TX(i));
+        break;
+      default:
+        break;
+    }
+  }
+
+  return antennas;
+}
+
+bool bladerf_common::set_antenna(bladerf_direction dir,
+                                 size_t chan,
+                                 const std::string &antenna)
+{
+  if (!is_antenna_valid(dir, antenna)) {
+    BLADERF_THROW("Invalid antenna: " + antenna);
+  }
+
+  // This port's old antenna
+  bladerf_channel old_channel = chan2channel(dir, chan);
+  // This port's new antenna
+  bladerf_channel new_channel = str2channel(antenna);
+  // The new antenna's old port
+  int old_chan = _chanmap[new_channel];
+
+  if (old_channel != new_channel || old_chan != (int)chan) {
+    // Disable the old antenna, if it's not going to be used
+    if (old_chan == -1) {
+      set_channel_enable(old_channel, false);
+    }
+
+    // Swap antennas
+    _chanmap[old_channel] = old_chan;
+    _chanmap[new_channel] = chan;
+
+    // Enable the new antenna
+    set_channel_enable(new_channel, true);
+  }
+
+  return true;
+}
+
+int bladerf_common::set_dc_offset(std::complex<double> const &offset,
+                                  bladerf_channel ch)
+{
+  int ret = 0;
+  int16_t val_i, val_q;
+
+  val_i = static_cast<int16_t>(offset.real() * DCOFF_SCALE);
+  val_q = static_cast<int16_t>(offset.imag() * DCOFF_SCALE);
+
+  ret = bladerf_set_correction(_dev.get(), ch,
+                               BLADERF_CORR_LMS_DCOFF_I, val_i);
+  ret |= bladerf_set_correction(_dev.get(), ch,
+                                BLADERF_CORR_LMS_DCOFF_Q, val_q);
+
+  return ret;
+}
+
+int bladerf_common::set_iq_balance(std::complex<double> const &balance,
+                                   bladerf_channel ch)
+{
+  int ret = 0;
+  int16_t val_gain, val_phase;
+
+  val_gain = static_cast<int16_t>(balance.real() * GAIN_SCALE);
+  val_phase = static_cast<int16_t>(balance.imag() * PHASE_SCALE);
+
+  ret = bladerf_set_correction(_dev.get(), ch,
+                               BLADERF_CORR_FPGA_GAIN, val_gain);
+  ret |= bladerf_set_correction(_dev.get(), ch,
+                                BLADERF_CORR_FPGA_PHASE, val_phase);
+
+  return ret;
+}
+
+std::vector<std::string> bladerf_common::get_clock_sources(size_t mboard)
+{
+  std::vector<std::string> sources;
+
+  // assumes zero-based 1:1 mapping
+  sources.push_back("internal");        // BLADERF_VCTCXO_TAMER_DISABLED
+  sources.push_back("external_1pps");   // BLADERF_VCTCXO_TAMER_1_PPS
+  sources.push_back("external");        // BLADERF_VCTCXO_TAMER_10_MHZ
+
+  return sources;
+}
+
+void bladerf_common::set_clock_source(std::string const &source,
+                                      size_t mboard)
+{
+  int status;
+  bladerf_vctcxo_tamer_mode tamer_mode;
+  std::vector<std::string> clock_sources;
+  int index;
+
+  tamer_mode = BLADERF_VCTCXO_TAMER_DISABLED;
+  clock_sources = get_clock_sources(mboard);
+  index = std::find(clock_sources.begin(), clock_sources.end(), source) - clock_sources.begin();
+
+  if (index < static_cast<int>(clock_sources.size())) {
+    tamer_mode = static_cast<bladerf_vctcxo_tamer_mode>(index);
+  }
+
+  status = bladerf_set_vctcxo_tamer_mode(_dev.get(), tamer_mode);
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "Failed to set VCTCXO tamer mode");
+  }
+}
+
+std::string bladerf_common::get_clock_source(size_t mboard)
+{
+  int status;
+  bladerf_vctcxo_tamer_mode tamer_mode;
+  std::vector<std::string> clock_sources;
+
+  tamer_mode = BLADERF_VCTCXO_TAMER_INVALID;
+
+  status = bladerf_get_vctcxo_tamer_mode(_dev.get(), &tamer_mode);
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "Failed to get VCTCXO tamer mode");
+  }
+
+  clock_sources = get_clock_sources(mboard);
+
+  return clock_sources.at(tamer_mode);
+}
+
+void bladerf_common::set_smb_frequency(double frequency)
+{
+  int status;
+  uint32_t freqint = static_cast<uint32_t>(frequency + 0.5);
+  uint32_t actual_frequency = freqint;
+
+  if (_is_xb_attached(_dev)) {
+    BLADERF_WARNING("Cannot use SMB port when expansion board is attached");
+    return;
+  }
+
+  status = bladerf_set_smb_frequency(_dev.get(),
+                                     freqint,
+                                     &actual_frequency);
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "Failed to set SMB frequency");
+  }
+
+  if (freqint != actual_frequency) {
+    BLADERF_WARNING(boost::str(boost::format("Wanted SMB frequency %f (%d) "
+                    "Hz, actual frequency is %d Hz")
+                    % frequency % freqint % actual_frequency));
+  }
+}
+
+double bladerf_common::get_smb_frequency()
+{
+  int status;
+  unsigned int actual_frequency;
+
+  if (_is_xb_attached(_dev)) {
+    BLADERF_WARNING("Cannot use SMB port when expansion board is attached");
+    return 0.0;
+  }
+
+  status = bladerf_get_smb_frequency(_dev.get(), &actual_frequency);
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "Failed to get SMB frequency");
+  }
+
+  return static_cast<double>(actual_frequency);
+}
+
+/******************************************************************************
+ * Private methods
+ ******************************************************************************/
+bladerf_sptr bladerf_common::open(std::string const &device_name)
+{
+  int status;
+  struct bladerf *raw_dev = NULL;
+  struct bladerf_devinfo devinfo;
+
+  std::lock_guard<std::mutex> lock(_devs_mutex);
+
+  /* Initialize the information used to identify the desired device
+   * to all wildcard (i.e., "any device") values */
+  bladerf_init_devinfo(&devinfo);
+
+  /* Populate the devinfo structure from device_name */
+  status = bladerf_get_devinfo_from_str(device_name.c_str(), &devinfo);
+  if (status < 0) {
+    BLADERF_THROW_STATUS(status, boost::str(boost::format("Failed to get "
+                         "devinfo for '%s'") % device_name));
+  }
+
+  /* Do we already have this device open? */
+  bladerf_sptr cached_dev = get_cached_device(devinfo);
+
+  if (cached_dev) {
+    return cached_dev;
+  }
+
+  /* Open the device. */
+  status = bladerf_open_with_devinfo(&raw_dev, &devinfo);
+  if (status < 0) {
+    BLADERF_THROW_STATUS(status, boost::str(boost::format("Failed to open "
+                         "device for '%s'") % device_name));
+  }
+
+  /* Add the device handle to our cache */
+  bladerf_sptr dev = bladerf_sptr(raw_dev, bladerf_common::close);
+
+  _devs.push_back(static_cast<std::weak_ptr<struct bladerf>>(dev));
+
+  return dev;
+}
+
+void bladerf_common::close(void *dev)
+{
+  std::lock_guard<std::mutex> lock(_devs_mutex);
+  std::list<std::weak_ptr<struct bladerf>>::iterator it(_devs.begin());
+
+  /* Prune expired entries from device cache */
+  while (it != _devs.end()) {
+    if ((*it).expired()) {
+      it = _devs.erase(it);
+    } else {
+      ++it;
+    }
+  }
+
+  bladerf_close(static_cast<struct bladerf *>(dev));
+}
+
+bladerf_sptr bladerf_common::get_cached_device(struct bladerf_devinfo devinfo)
+{
+  /* Lock to _devs must be aquired by caller */
+  int status;
+  struct bladerf_devinfo other_devinfo;
+
+  for (std::weak_ptr<struct bladerf> dev : _devs) {
+    status = bladerf_get_devinfo(bladerf_sptr(dev).get(), &other_devinfo);
+    if (status < 0) {
+      BLADERF_THROW_STATUS(status, "Failed to get devinfo for cached device");
+    }
+
+    if (bladerf_devinfo_matches(&devinfo, &other_devinfo)) {
+      return bladerf_sptr(dev);
+    }
+  }
+
+  return bladerf_sptr();
+}
+
+void bladerf_common::print_device_info()
+{
+  char serial[BLADERF_SERIAL_LENGTH];
+  struct bladerf_version ver;
+
+  std::cout << _pfx << "Device: ";
+
+  switch (get_board_type()) {
+    case BOARD_TYPE_BLADERF_1:
+      std::cout << "Nuand bladeRF";
+      break;
+    case BOARD_TYPE_BLADERF_2:
+      std::cout << "Nuand bladeRF 2.0";
+      break;
+    default:
+      std::cout << "Unknown Device";
+      break;
+  }
+
+  if (bladerf_get_serial(_dev.get(), serial) == 0) {
+    std::string strser(serial);
+
+    if (strser.length() == 32) {
+      strser.replace(4, 24, "...");
+    }
+
+    std::cout << " Serial # " << strser;
+  } else {
+    std::cout << " Serial # UNKNOWN";
+  }
+
+  if (bladerf_fw_version(_dev.get(), &ver) == 0) {
+    std::cout << " FW v" << ver.major << "." << ver.minor << "." << ver.patch;
+  } else {
+    std::cout << " FW version UNKNOWN";
+  }
+
+  if (bladerf_fpga_version(_dev.get(), &ver) == 0) {
+    std::cout << " FPGA v" << ver.major << "." << ver.minor << "." << ver.patch;
+  } else {
+    std::cout << " FPGA version UNKNOWN";
+  }
+
+  std::cout << std::endl;
+}
+
+bool bladerf_common::is_antenna_valid(bladerf_direction dir,
+                                      const std::string &antenna)
+{
+  for (std::string ant : get_antennas(dir)) {
+    if (antenna == ant) {
+      return true;
+    }
+  }
+
+  return false;
+}
diff --git a/lib/bladerf/bladerf_common.h b/lib/bladerf/bladerf_common.h
new file mode 100644
index 0000000000000000000000000000000000000000..741b1e75b4e90daf4ddddc0cbb5862eccbb7646f
--- /dev/null
+++ b/lib/bladerf/bladerf_common.h
@@ -0,0 +1,293 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2013-2017 Nuand LLC
+ * Copyright 2013 Dimitri Stolnikov <horiz0n@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_BLADERF_COMMON_H
+#define INCLUDED_BLADERF_COMMON_H
+
+#include <list>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include <libbladeRF.h>
+
+#include "osmosdr/ranges.h"
+#include "arg_helpers.h"
+
+#include "bladerf_compat.h"
+
+#ifdef _MSC_VER
+#include <cstddef>
+typedef ptrdiff_t ssize_t;
+#endif //_MSC_VER
+
+#define BLADERF_DEBUG_ENABLE
+
+typedef std::shared_ptr<struct bladerf> bladerf_sptr;
+
+/* Identification of the bladeRF hardware in use */
+typedef enum {
+  BOARD_TYPE_UNKNOWN,   /**< Board type is unknown */
+  BOARD_TYPE_NONE,      /**< Uninitialized or no board present */
+  BOARD_TYPE_BLADERF_1, /**< bladeRF 1 (LMS6002D-based, 1RX/1TX) */
+  BOARD_TYPE_BLADERF_2, /**< bladeRF 2 (AD9361-based, 2RX/2TX) */
+} bladerf_board_type;
+
+/* Mapping of bladerf_channel to bool */
+typedef std::map<bladerf_channel, bool> bladerf_channel_enable_map;
+
+/* Mapping of bladerf_channel to gnuradio port/chan */
+typedef std::map<bladerf_channel, int> bladerf_channel_map;
+
+/* Convenience macros for throwing a runtime error */
+#define BLADERF_THROW(message)                                              \
+  {                                                                         \
+    throw std::runtime_error(std::string(__FUNCTION__) + ": " + message);   \
+  }
+
+#define BLADERF_THROW_STATUS(status, message)                               \
+  {                                                                         \
+    BLADERF_THROW(boost::str(boost::format("%s: %s (%d)") % message         \
+      % bladerf_strerror(status) % status));                                \
+  }
+
+/* Convenience macros for printing a warning message to stderr */
+#define BLADERF_WARNING(message)                                            \
+  {                                                                         \
+    std::cerr << _pfx << __FUNCTION__ << ": " << message << std::endl;      \
+  }
+
+#define BLADERF_WARN_STATUS(status, message)                                \
+  {                                                                         \
+    BLADERF_WARNING(message << ": " << bladerf_strerror(status));           \
+  }                                                                         \
+
+/* Convenience macro for printing an informational message to stdout */
+#define BLADERF_INFO(message)                                               \
+  {                                                                         \
+    std::cout << _pfx << __FUNCTION__ << ": " << message << std::endl;      \
+  }
+
+/* Convenience macro for printing a debug message to stdout */
+#ifdef BLADERF_DEBUG_ENABLE
+#define BLADERF_DEBUG(message) BLADERF_INFO("DEBUG: " << message)
+#else
+#define BLADERF_DEBUG(message)
+#endif // BLADERF_DEBUG_ENABLE
+
+/* Given a bladerf_channel_layout, calculate the number of streams */
+size_t num_streams(bladerf_channel_layout layout);
+
+/**
+ * Common class for bladeRF interaction
+ */
+class bladerf_common
+{
+public:
+  /*****************************************************************************
+   * Public methods
+   ****************************************************************************/
+  bladerf_common();
+
+protected:
+  /*****************************************************************************
+   * Protected methods
+   ****************************************************************************/
+
+  /**
+   * Handle initialization and parameters common to both source & sink
+   *
+   * Specify arguments in key=value,key=value format, e.g.
+   *    bladerf=0,buffers=512
+   *
+   * Recognized arguments:
+   *  Key             Allowed values
+   * ---------------------------------------------------------------------------
+   * REQUIRED:
+   *  bladerf         a valid instance or serial number
+   * USB INTERFACE CONTROL:
+   *  buffers         (default: NUM_BUFFERS)
+   *  buflen          (default: NUM_SAMPLES_PER_BUFFER)
+   *  stream_timeout  valid time in milliseconds (default: 3000)
+   *  transfers       (default: NUM_TRANSFERS)
+   * FPGA CONTROL:
+   *  enable_metadata 1 to enable metadata
+   *  fpga            a path to a valid .rbf file
+   *  fpga-reload     1 to force reloading the FPGA unconditionally
+   * RF CONTROL:
+   *  agc             1 to enable, 0 to disable (default: hardware-dependent)
+   *  agc_mode        default, manual, fast, slow, hybrid (default: default)
+   *  loopback        bb_txlpf_rxvga2, bb_txlpf_rxlpf, bb_txvga1_rxvga2,
+   *                  bb_txvga1_rxlpf, rf_lna1, rf_lna2, rf_lna3, firmware,
+   *                  ad9361_bist, none (default: none)
+   *                    ** Note: valid on receive channels only
+   *  rxmux           baseband, 12bit, 32bit, digital (default: baseband)
+   *                    ** Note: valid on receive channels only
+   *  smb             a valid frequency
+   *  tamer           internal, external_1pps, external (default: internal)
+   *  xb200           auto, auto3db, 50M, 144M, 222M, custom (default: auto)
+   * MISC:
+   *  verbosity       verbose, debug, info, warning, error, critical, silent
+   *                    (default: info)
+   *                    ** Note: applies only to libbladeRF logging
+   */
+  void init(dict_t const &dict, bladerf_direction direction);
+
+  /* Get a vector of available devices */
+  static std::vector<std::string> devices();
+  /* Get the type of the open bladeRF board */
+  bladerf_board_type get_board_type();
+  /* Get the maximum number of channels supported in a given direction */
+  size_t get_max_channels(bladerf_direction direction);
+
+  void set_channel_enable(bladerf_channel ch, bool enable);
+  bool get_channel_enable(bladerf_channel ch);
+
+  /* Set libbladeRF verbosity */
+  void set_verbosity(std::string const &verbosity);
+
+  /* Convert an antenna/channel name (e.g. "RX2") to a bladerf_channel */
+  bladerf_channel str2channel(std::string const &ch);
+  /* Convert a bladerf_channel to an antenna/channel name (e.g. "RX2") */
+  std::string channel2str(bladerf_channel ch);
+  /* Convert a bladerf_channel to a hardware port identifier */
+  int channel2rfport(bladerf_channel ch);
+
+  /* Using the channel map, get the bladerf_channel for a gnuradio chan */
+  bladerf_channel chan2channel(bladerf_direction direction, size_t chan = 0);
+
+  /* Get range of supported sampling rates for channel ch */
+  osmosdr::meta_range_t sample_rates(bladerf_channel ch);
+  /* Set sampling rate on channel ch to rate */
+  double set_sample_rate(double rate, bladerf_channel ch);
+  /* Get the current sampling rate on channel ch */
+  double get_sample_rate(bladerf_channel ch);
+
+  /* Get range of supported RF frequencies for channel ch */
+  osmosdr::freq_range_t freq_range(bladerf_channel ch);
+  /* Set center RF frequency of channel ch to freq */
+  double set_center_freq(double freq, bladerf_channel ch);
+  /* Get the center RF frequency of channel ch */
+  double get_center_freq(bladerf_channel ch);
+
+  /* Get range of supported bandwidths for channel ch */
+  osmosdr::freq_range_t filter_bandwidths(bladerf_channel ch);
+  /* Set the bandwidth on channel ch to bandwidth */
+  double set_bandwidth(double bandwidth, bladerf_channel ch);
+  /* Get the current bandwidth of channel ch */
+  double get_bandwidth(bladerf_channel ch);
+
+  /* Get the names of gain stages on channel ch */
+  std::vector<std::string> get_gain_names(bladerf_channel ch);
+  /* Get range of supported overall gain values on channel ch */
+  osmosdr::gain_range_t get_gain_range(bladerf_channel ch);
+  /* Get range of supported gain values for gain stage 'name' on channel ch */
+  osmosdr::gain_range_t get_gain_range(std::string const &name,
+                                       bladerf_channel ch);
+
+  /* Enable or disable the automatic gain control on channel ch */
+  bool set_gain_mode(bool automatic, bladerf_channel ch,
+                     bladerf_gain_mode agc_mode = BLADERF_GAIN_DEFAULT);
+  /* Get the current automatic gain control status on channel ch */
+  bool get_gain_mode(bladerf_channel ch);
+
+  /* Set the overall gain value on channel ch */
+  double set_gain(double gain, bladerf_channel ch);
+  /* Set the gain of stage 'name' on channel ch */
+  double set_gain(double gain, std::string const &name, bladerf_channel ch);
+  /* Get the overall gain value on channel ch */
+  double get_gain(bladerf_channel ch);
+  /* Get the gain of stage 'name' on channel ch */
+  double get_gain(std::string const &name, bladerf_channel ch);
+
+  /* Get the list of antennas supported by a channel */
+  std::vector<std::string> get_antennas(bladerf_direction dir);
+  bool set_antenna(bladerf_direction dir, size_t chan, const std::string &antenna);
+
+  /* Set the DC offset on channel ch */
+  int set_dc_offset(std::complex<double> const &offset, bladerf_channel ch);
+  /* Set the IQ balance on channel ch */
+  int set_iq_balance(std::complex<double> const &balance, bladerf_channel ch);
+
+  /* Get the list of supported clock sources */
+  std::vector<std::string> get_clock_sources(size_t mboard = 0);
+  /* Set the clock source to */
+  void set_clock_source(std::string const &source, size_t mboard = 0);
+  /* Get the name of the current clock source */
+  std::string get_clock_source(size_t mboard = 0);
+
+  /* Set the SMB frequency */
+  void set_smb_frequency(double frequency);
+  /* Get the current SMB frequency */
+  double get_smb_frequency();
+
+  /*****************************************************************************
+   * Protected members
+   ****************************************************************************/
+  bladerf_sptr _dev;            /**< shared pointer for the active device */
+  std::string _pfx;             /**< prefix for console messages */
+  unsigned int _failures;       /**< counter for consecutive rx/tx failures */
+
+  size_t _num_buffers;          /**< number of buffers to allocate */
+  size_t _samples_per_buffer;   /**< how many samples per buffer */
+  size_t _num_transfers;        /**< number of active backend transfers */
+  unsigned int _stream_timeout; /**< timeout for backend transfers */
+
+  bladerf_format _format;       /**< sample format to use */
+
+  bladerf_channel_map _chanmap; /**< map of antennas to channels */
+  bladerf_channel_enable_map _enables;  /**< enabled channels */
+
+  /*****************************************************************************
+   * Protected constants
+   ****************************************************************************/
+  /* Maximum bladerf_sync_{rx,tx} failures to allow before giving up */
+  static const unsigned int MAX_CONSECUTIVE_FAILURES = 3;
+
+  /* BladeRF IQ correction parameters */
+  static const int16_t DCOFF_SCALE = 2048;
+  static const int16_t GAIN_SCALE = 4096;
+  static const int16_t PHASE_SCALE = 4096;
+
+private:
+  /*****************************************************************************
+   * Private methods
+   ****************************************************************************/
+  /* Open the bladeRF described by device_name. Returns a sptr if successful */
+  bladerf_sptr open(const std::string &device_name);
+  /* Called by shared_ptr when a bladerf_sptr hits a refcount of 0 */
+  static void close(void *dev);
+  /* If a device described by devinfo is open, this returns a sptr to it */
+  static bladerf_sptr get_cached_device(struct bladerf_devinfo devinfo);
+  /* Prints a summary of device information */
+  void print_device_info();
+
+  bool is_antenna_valid(bladerf_direction dir, const std::string &antenna);
+
+  /*****************************************************************************
+   * Private members
+   ****************************************************************************/
+  static std::mutex _devs_mutex;  /**< mutex for access to _devs */
+  static std::list<std::weak_ptr<struct bladerf>> _devs;  /**< dev cache */
+};
+
+#endif
diff --git a/lib/bladerf/bladerf_compat.h b/lib/bladerf/bladerf_compat.h
new file mode 100644
index 0000000000000000000000000000000000000000..2ad24be3d4c9d7335c5dbf5b54a00848eeefc64b
--- /dev/null
+++ b/lib/bladerf/bladerf_compat.h
@@ -0,0 +1,78 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2017 Nuand LLC
+ *
+ * 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_BLADERF_COMPAT_H
+#define INCLUDED_BLADERF_COMPAT_H
+
+#if defined(LIBBLADERF_API_VERSION) && (LIBBLADERF_API_VERSION < 0x01080100)
+    #warning Old libbladeRF detected: using compatibility workarounds.
+
+    #define BLADERF_COMPATIBILITY
+
+    /* New libbladeRF supports multiple channels, via various enums. */
+    typedef bladerf_module bladerf_channel;
+    #define BLADERF_CHANNEL_RX(ch) BLADERF_MODULE_RX
+    #define BLADERF_CHANNEL_TX(ch) BLADERF_MODULE_TX
+    #define BLADERF_CHANNEL_INVALID BLADERF_MODULE_INVALID
+
+    typedef bladerf_module bladerf_channel_layout;
+    #define BLADERF_RX_X1 BLADERF_MODULE_RX
+    #define BLADERF_TX_X1 BLADERF_MODULE_TX
+    #define BLADERF_RX_X2 BLADERF_MODULE_INVALID
+    #define BLADERF_TX_X2 BLADERF_MODULE_INVALID
+
+    typedef bladerf_module bladerf_direction;
+    #define BLADERF_RX BLADERF_MODULE_RX
+    #define BLADERF_TX BLADERF_MODULE_TX
+    #define BLADERF_DIRECTION_MASK (0x1)
+
+    /* Changed API calls */
+    static
+    int bladerf_get_frequency(struct bladerf *dev,
+                              bladerf_channel ch,
+                              uint64_t *freq) // was unsigned int *frequency
+    {
+        unsigned int f32 = 0;
+        int status = bladerf_get_frequency(dev, ch, &f32);
+        *freq = static_cast<uint64_t>(f32);
+        return status;
+    }
+
+    static
+    int bladerf_sync_tx(struct bladerf *dev,
+                        void const *samples,  // was void *samples
+                        unsigned int num_samples,
+                        struct bladerf_metadata *metadata,
+                        unsigned int timeout_ms)
+    {
+        void *s = const_cast<void *>(samples);
+        return bladerf_sync_tx(dev, s, num_samples, metadata, timeout_ms);
+    }
+
+    /* Changed enums/defines */
+    #define BLADERF_GAIN_DEFAULT BLADERF_GAIN_AUTOMATIC
+    #define BLADERF_GAIN_MGC BLADERF_GAIN_MANUAL
+    #define BLADERF_RX_MUX_BASEBAND BLADERF_RX_MUX_BASEBAND_LMS
+
+    /* New functionality with no equivalent */
+    #define BLADERF_LB_AD9361_BIST BLADERF_LB_NONE
+    #define bladerf_get_board_name(name) "bladerf1"
+
+#endif // libbladeRF < 1.8.1
+#endif // INCLUDED_BLADERF_COMPAT_H
diff --git a/lib/bladerf/bladerf_sink_c.cc b/lib/bladerf/bladerf_sink_c.cc
new file mode 100644
index 0000000000000000000000000000000000000000..6ee3acd927fdd290e33caf4d5e8b0026073329b5
--- /dev/null
+++ b/lib/bladerf/bladerf_sink_c.cc
@@ -0,0 +1,609 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2013-2017 Nuand LLC
+ * Copyright 2013 Dimitri Stolnikov <horiz0n@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.
+ */
+
+/*
+ * config.h is generated by configure.  It contains the results
+ * of probing for features, options etc.  It should be the first
+ * file included in your .cc file.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <iostream>
+
+#include <boost/assign.hpp>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <gnuradio/io_signature.h>
+
+#include <volk/volk.h>
+
+#include "arg_helpers.h"
+#include "bladerf_sink_c.h"
+#include "osmosdr/sink.h"
+
+using namespace boost::assign;
+
+/******************************************************************************
+ * Functions
+ ******************************************************************************/
+
+/*
+ * Create a new instance of bladerf_sink_c and return
+ * a boost shared_ptr.  This is effectively the public constructor.
+ */
+bladerf_sink_c_sptr make_bladerf_sink_c(const std::string &args)
+{
+  return gnuradio::get_initial_sptr(new bladerf_sink_c(args));
+}
+
+/******************************************************************************
+ * Private methods
+ ******************************************************************************/
+
+/*
+ * The private constructor
+ */
+bladerf_sink_c::bladerf_sink_c(const std::string &args) :
+  gr::sync_block( "bladerf_sink_c",
+                  args_to_io_signature(args),
+                  gr::io_signature::make(0, 0, 0)),
+  _16icbuf(NULL),
+  _32fcbuf(NULL),
+  _in_burst(false),
+  _running(false)
+{
+  dict_t dict = params_to_dict(args);
+
+  /* Perform src/sink agnostic initializations */
+  init(dict, BLADERF_TX);
+
+  /* Check for RX-only params */
+  if (dict.count("loopback")) {
+    BLADERF_WARNING("Warning: 'loopback' has been specified on a bladeRF "
+                    "sink, and will have no effect. This parameter should be "
+                    "specified on the associated bladeRF source.");
+  }
+
+  if (dict.count("rxmux")) {
+    BLADERF_WARNING("Warning: 'rxmux' has been specified on a bladeRF sink, "
+                    "and will have no effect.");
+  }
+
+  /* Bias tee */
+  if (dict.count("biastee")) {
+    set_biastee_mode(dict["biastee"]);
+  }
+
+  /* Initialize channel <-> antenna map */
+  for (std::string ant : get_antennas()) {
+    _chanmap[str2channel(ant)] = -1;
+  }
+
+  /* Bounds-checking output signature depending on our underlying hardware */
+  if (get_num_channels() > get_max_channels()) {
+    BLADERF_WARNING("Warning: number of channels specified on command line ("
+                    << get_num_channels() << ") is greater than the maximum "
+                    "number supported by this device (" << get_max_channels()
+                    << "). Resetting to " << get_max_channels() << ".");
+
+    set_input_signature(gr::io_signature::make(get_max_channels(),
+                                               get_max_channels(),
+                                               sizeof(gr_complex)));
+  }
+
+  /* Set up constraints */
+  int const alignment_multiple = volk_get_alignment() / sizeof(gr_complex);
+  set_alignment(std::max(1,alignment_multiple));
+  set_max_noutput_items(_samples_per_buffer);
+  set_output_multiple(get_num_channels());
+
+  /* Set channel layout */
+  _layout = (get_num_channels() > 1) ? BLADERF_TX_X2 : BLADERF_TX_X1;
+
+  /* Initial wiring of antennas to channels */
+  for (size_t ch = 0; ch < get_num_channels(); ++ch) {
+    set_channel_enable(BLADERF_CHANNEL_TX(ch), true);
+    _chanmap[BLADERF_CHANNEL_TX(ch)] = ch;
+  }
+
+  BLADERF_DEBUG("initialization complete");
+}
+
+
+/******************************************************************************
+ * Public methods
+ ******************************************************************************/
+
+std::string bladerf_sink_c::name()
+{
+  return "bladeRF transmitter";
+}
+
+std::vector<std::string> bladerf_sink_c::get_devices()
+{
+  return bladerf_common::devices();
+}
+
+size_t bladerf_sink_c::get_max_channels()
+{
+  return bladerf_common::get_max_channels(BLADERF_TX);
+}
+
+size_t bladerf_sink_c::get_num_channels()
+{
+  return input_signature()->max_streams();
+}
+
+bool bladerf_sink_c::start()
+{
+  int status;
+
+  BLADERF_DEBUG("starting sink");
+
+  gr::thread::scoped_lock guard(d_mutex);
+
+  _in_burst = false;
+
+  status = bladerf_sync_config(_dev.get(), _layout, _format, _num_buffers,
+                               _samples_per_buffer, _num_transfers,
+                               _stream_timeout);
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "bladerf_sync_config failed");
+  }
+
+  for (size_t ch = 0; ch < get_max_channels(); ++ch) {
+    bladerf_channel brfch = BLADERF_CHANNEL_TX(ch);
+    if (get_channel_enable(brfch)) {
+      status = bladerf_enable_module(_dev.get(), brfch, true);
+      if (status != 0) {
+        BLADERF_THROW_STATUS(status, "bladerf_enable_module failed");
+      }
+    }
+  }
+
+  /* Allocate memory for conversions in work() */
+  size_t alignment = volk_get_alignment();
+
+  _16icbuf = reinterpret_cast<int16_t *>(volk_malloc(2*_samples_per_buffer*sizeof(int16_t), alignment));
+  _32fcbuf = reinterpret_cast<gr_complex *>(volk_malloc(_samples_per_buffer*sizeof(gr_complex), alignment));
+
+  _running = true;
+
+  return true;
+}
+
+bool bladerf_sink_c::stop()
+{
+  int status;
+
+  BLADERF_DEBUG("stopping sink");
+
+  gr::thread::scoped_lock guard(d_mutex);
+
+  if (!_running) {
+    BLADERF_WARNING("sink already stopped, nothing to do here");
+    return true;
+  }
+
+  _running = false;
+
+  for (size_t ch = 0; ch < get_max_channels(); ++ch) {
+    bladerf_channel brfch = BLADERF_CHANNEL_TX(ch);
+    if (get_channel_enable(brfch)) {
+      status = bladerf_enable_module(_dev.get(), brfch, false);
+      if (status != 0) {
+        BLADERF_THROW_STATUS(status, "bladerf_enable_module failed");
+      }
+    }
+  }
+
+  /* Deallocate conversion memory */
+  volk_free(_16icbuf);
+  volk_free(_32fcbuf);
+  _16icbuf = NULL;
+  _32fcbuf = NULL;
+
+  return true;
+}
+
+int bladerf_sink_c::work(int noutput_items,
+                          gr_vector_const_void_star &input_items,
+                          gr_vector_void_star &output_items)
+{
+  int status;
+  size_t nstreams = num_streams(_layout);
+
+  gr::thread::scoped_lock guard(d_mutex);
+
+  // if we aren't running, nothing to do here
+  if (!_running) {
+    return 0;
+  }
+
+  // copy the samples from input_items
+  gr_complex const **in = reinterpret_cast<gr_complex const **>(&input_items[0]);
+
+  if (nstreams > 1) {
+    // we need to interleave the streams as we copy
+    gr_complex *intl_out = _32fcbuf;
+
+    for (size_t i = 0; i < (noutput_items/nstreams); ++i) {
+      for (size_t n = 0; n < nstreams; ++n) {
+        memcpy(intl_out++, in[n]++, sizeof(gr_complex));
+      }
+    }
+  } else {
+    // no interleaving to do: simply copy everything
+    memcpy(_32fcbuf, in[0], noutput_items * sizeof(gr_complex));
+  }
+
+  // convert floating point to fixed point and scale
+  // input_items is gr_complex (2x float), so num_points is 2*noutput_items
+  volk_32f_s32f_convert_16i(_16icbuf, reinterpret_cast<float const *>(_32fcbuf),
+                            SCALING_FACTOR, 2*noutput_items);
+
+  // transmit the samples from the temp buffer
+  if (BLADERF_FORMAT_SC16_Q11_META == _format) {
+    status = transmit_with_tags(_16icbuf, noutput_items);
+  } else {
+    status = bladerf_sync_tx(_dev.get(), static_cast<void const *>(_16icbuf),
+                             noutput_items, NULL, _stream_timeout);
+  }
+
+  // handle failure
+  if (status != 0) {
+    BLADERF_WARNING("bladerf_sync_tx error: " << bladerf_strerror(status));
+    ++_failures;
+
+    if (_failures >= MAX_CONSECUTIVE_FAILURES) {
+      BLADERF_WARNING("Consecutive error limit hit. Shutting down.");
+      return WORK_DONE;
+    }
+  } else {
+    _failures = 0;
+  }
+
+  return noutput_items;
+}
+
+int bladerf_sink_c::transmit_with_tags(int16_t const *samples,
+                                        int noutput_items)
+{
+  int status;
+  int count = 0;
+
+  // For a long burst, we may be transmitting the burst contents over
+  // multiple work calls, so we'll just be sending the entire buffer
+  // Therefore, we initialize our indicies for this case.
+  int start_idx = 0;
+  int end_idx = (noutput_items - 1);
+
+  struct bladerf_metadata meta;
+  std::vector<gr::tag_t> tags;
+
+  int const INVALID_IDX = -1;
+  int16_t const zeros[8] = { 0 };
+
+  memset(&meta, 0, sizeof(meta));
+
+  BLADERF_DEBUG("transmit_with_tags(" << noutput_items << ")");
+
+  // Important Note: We assume that these tags are ordered by their offsets.
+  // This is true for GNU Radio 3.7.7.x, since the GR runtime libs store
+  // these in a multimap.
+  //
+  // If you're using an earlier GNU Radio version, you may have to sort
+  // the tags vector.
+  get_tags_in_window(tags, 0, 0, noutput_items);
+
+  if (tags.size() == 0) {
+    if (_in_burst) {
+      BLADERF_DEBUG("TX'ing " << noutput_items << " samples within a burst...");
+
+      return bladerf_sync_tx(_dev.get(), samples, noutput_items,
+                             &meta, _stream_timeout);
+    } else {
+      BLADERF_WARNING("Dropping " << noutput_items
+                      << " samples not in a burst.");
+    }
+  }
+
+  for (gr::tag_t tag : tags) {
+    // Upon seeing an SOB tag, update our offset. We'll TX the start of the
+    // burst when we see an EOB or at the end of this function - whichever
+    // occurs first.
+    if (pmt::symbol_to_string(tag.key) == "tx_sob") {
+      if (_in_burst) {
+        BLADERF_WARNING("Got SOB while already within a burst");
+
+        return BLADERF_ERR_INVAL;
+      } else {
+        start_idx = static_cast<int>(tag.offset - nitems_read(0));
+
+        BLADERF_DEBUG("Got SOB " << start_idx << " samples into work payload");
+
+        meta.flags |= (BLADERF_META_FLAG_TX_NOW | BLADERF_META_FLAG_TX_BURST_START);
+        _in_burst = true;
+      }
+
+    } else if (pmt::symbol_to_string(tag.key) == "tx_eob") {
+      if (!_in_burst) {
+        BLADERF_WARNING("Got EOB while not in burst");
+        return BLADERF_ERR_INVAL;
+      }
+
+      // Upon seeing an EOB, transmit what we have and reset our state
+      end_idx = static_cast<int>(tag.offset - nitems_read(0));
+      BLADERF_DEBUG("Got EOB " << end_idx << " samples into work payload");
+
+      if ((start_idx == INVALID_IDX) || (start_idx > end_idx)) {
+        BLADERF_DEBUG("Buffer indicies are in an invalid state!");
+        return BLADERF_ERR_INVAL;
+      }
+
+      count = end_idx - start_idx + 1;
+
+      BLADERF_DEBUG("TXing @ EOB [" << start_idx << ":" << end_idx << "]");
+
+      status = bladerf_sync_tx(_dev.get(),
+                               static_cast<void const *>(&samples[2 * start_idx]),
+                               count, &meta, _stream_timeout);
+      if (status != 0) {
+        return status;
+      }
+
+      /* TODO: libbladeRF should now take care of this for us,
+       *       as of the libbladeRF version that includes the
+       *       TX_UPDATE_TIMESTAMP flag.  Verify this potentially remove this.
+       *       (The meta.flags changes would then be applied to the previous
+       *       bladerf_sync_tx() call.)
+       */
+      BLADERF_DEBUG("TXing Zeros with burst end flag");
+
+      meta.flags &= ~(BLADERF_META_FLAG_TX_NOW | BLADERF_META_FLAG_TX_BURST_START);
+      meta.flags |= BLADERF_META_FLAG_TX_BURST_END;
+
+      status = bladerf_sync_tx(_dev.get(), static_cast<void const *>(zeros),
+                               4, &meta, _stream_timeout);
+
+      /* Reset our state */
+      start_idx = INVALID_IDX;
+      end_idx = (noutput_items - 1);
+      meta.flags = 0;
+      _in_burst = false;
+
+      if (status != 0) {
+        BLADERF_DEBUG("Failed to send zero samples to flush EOB");
+        return status;
+      }
+    }
+  }
+
+  // We had a start of burst with no end yet - transmit those samples
+  if (_in_burst) {
+    count = end_idx - start_idx + 1;
+
+    BLADERF_DEBUG("TXing SOB [" << start_idx << ":" << end_idx << "]");
+
+    status = bladerf_sync_tx(_dev.get(),
+                             static_cast<void const *>(&samples[2 * start_idx]),
+                             count, &meta, _stream_timeout);
+  }
+
+  return status;
+}
+
+osmosdr::meta_range_t bladerf_sink_c::get_sample_rates()
+{
+  return sample_rates(chan2channel(BLADERF_TX, 0));
+}
+
+double bladerf_sink_c::set_sample_rate(double rate)
+{
+  return bladerf_common::set_sample_rate(rate, chan2channel(BLADERF_TX, 0));
+}
+
+double bladerf_sink_c::get_sample_rate()
+{
+  return bladerf_common::get_sample_rate(chan2channel(BLADERF_TX, 0));
+}
+
+osmosdr::freq_range_t bladerf_sink_c::get_freq_range(size_t chan)
+{
+  return bladerf_common::freq_range(chan2channel(BLADERF_TX, chan));
+}
+
+double bladerf_sink_c::set_center_freq(double freq, size_t chan)
+{
+  return bladerf_common::set_center_freq(freq, chan2channel(BLADERF_TX, chan));
+}
+
+double bladerf_sink_c::get_center_freq(size_t chan)
+{
+  return bladerf_common::get_center_freq(chan2channel(BLADERF_TX, chan));
+}
+
+double bladerf_sink_c::set_freq_corr(double ppm, size_t chan)
+{
+  /* TODO: Write the VCTCXO with a correction value (also changes RX ppm value!) */
+  BLADERF_WARNING("Frequency correction is not implemented.");
+  return get_freq_corr(chan2channel(BLADERF_TX, chan));
+}
+
+double bladerf_sink_c::get_freq_corr(size_t chan)
+{
+  /* TODO: Return back the frequency correction in ppm */
+  return 0;
+}
+
+std::vector<std::string> bladerf_sink_c::get_gain_names(size_t chan)
+{
+  return bladerf_common::get_gain_names(chan2channel(BLADERF_TX, chan));
+}
+
+osmosdr::gain_range_t bladerf_sink_c::get_gain_range(size_t chan)
+{
+  return bladerf_common::get_gain_range(chan2channel(BLADERF_TX, chan));
+}
+
+osmosdr::gain_range_t bladerf_sink_c::get_gain_range(const std::string &name,
+                                                     size_t chan)
+{
+  return bladerf_common::get_gain_range(name, chan2channel(BLADERF_TX, chan));
+}
+
+bool bladerf_sink_c::set_gain_mode(bool automatic, size_t chan)
+{
+  return bladerf_common::set_gain_mode(automatic,
+                                       chan2channel(BLADERF_TX, chan));
+}
+
+bool bladerf_sink_c::get_gain_mode(size_t chan)
+{
+  return bladerf_common::get_gain_mode(chan2channel(BLADERF_TX, chan));
+}
+
+double bladerf_sink_c::set_gain(double gain, size_t chan)
+{
+  return bladerf_common::set_gain(gain, chan2channel(BLADERF_TX, chan));
+}
+
+double bladerf_sink_c::set_gain(double gain, const std::string &name,
+                                size_t chan)
+{
+  return bladerf_common::set_gain(gain, name, chan2channel(BLADERF_TX, chan));
+}
+
+double bladerf_sink_c::get_gain(size_t chan)
+{
+  return bladerf_common::get_gain(chan2channel(BLADERF_TX, chan));
+}
+
+double bladerf_sink_c::get_gain(const std::string &name, size_t chan)
+{
+  return bladerf_common::get_gain(name, chan2channel(BLADERF_TX, chan));
+}
+
+std::vector<std::string> bladerf_sink_c::get_antennas(size_t chan)
+{
+  return bladerf_common::get_antennas(BLADERF_TX);
+}
+
+std::string bladerf_sink_c::set_antenna(const std::string &antenna,
+                                        size_t chan)
+{
+  bool _was_running = _running;
+
+  if (_was_running) {
+    stop();
+  }
+
+  bladerf_common::set_antenna(BLADERF_TX, chan, antenna);
+
+  if (_was_running) {
+    start();
+  }
+
+  return get_antenna(chan);
+}
+
+std::string bladerf_sink_c::get_antenna(size_t chan)
+{
+  return channel2str(chan2channel(BLADERF_TX, chan));
+}
+
+void bladerf_sink_c::set_dc_offset(const std::complex < double > &offset,
+                                   size_t chan)
+{
+  int status;
+
+  status = bladerf_common::set_dc_offset(offset, chan2channel(BLADERF_TX, chan));
+
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "could not set dc offset");
+  }
+}
+
+void bladerf_sink_c::set_iq_balance(const std::complex < double > &balance,
+                                    size_t chan)
+{
+  int status;
+
+  status = bladerf_common::set_iq_balance(balance, chan2channel(BLADERF_TX, chan));
+
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "could not set iq balance");
+  }
+}
+
+osmosdr::freq_range_t bladerf_sink_c::get_bandwidth_range(size_t chan)
+{
+  return filter_bandwidths(chan2channel(BLADERF_TX, chan));
+}
+
+double bladerf_sink_c::set_bandwidth(double bandwidth, size_t chan)
+{
+  return bladerf_common::set_bandwidth(bandwidth, chan2channel(BLADERF_TX, chan));
+}
+
+double bladerf_sink_c::get_bandwidth(size_t chan)
+{
+  return bladerf_common::get_bandwidth(chan2channel(BLADERF_TX, chan));
+}
+
+std::vector < std::string > bladerf_sink_c::get_clock_sources(size_t mboard)
+{
+  return bladerf_common::get_clock_sources(mboard);
+}
+
+void bladerf_sink_c::set_clock_source(const std::string &source,
+                                      size_t mboard)
+{
+  bladerf_common::set_clock_source(source, mboard);
+}
+
+std::string bladerf_sink_c::get_clock_source(size_t mboard)
+{
+  return bladerf_common::get_clock_source(mboard);
+}
+
+void bladerf_sink_c::set_biastee_mode(const std::string &mode)
+{
+  int status;
+  bool enable;
+
+  if (mode == "on" || mode == "1" || mode == "rx") {
+    enable = true;
+  } else {
+    enable = false;
+  }
+
+  status = bladerf_set_bias_tee(_dev.get(), BLADERF_CHANNEL_TX(0), enable);
+  if (BLADERF_ERR_UNSUPPORTED == status) {
+    // unsupported, but not worth crashing out
+    BLADERF_WARNING("Bias-tee not supported by device");
+  } else if (status != 0) {
+    BLADERF_THROW_STATUS(status, "Failed to set bias-tee");
+  }
+}
diff --git a/lib/bladerf/bladerf_sink_c.h b/lib/bladerf/bladerf_sink_c.h
new file mode 100644
index 0000000000000000000000000000000000000000..268f8df9dc5546a8c31cf0c1fd2e921a986a69e0
--- /dev/null
+++ b/lib/bladerf/bladerf_sink_c.h
@@ -0,0 +1,139 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2013-2017 Nuand LLC
+ * Copyright 2013 Dimitri Stolnikov <horiz0n@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_BLADERF_SINK_C_H
+#define INCLUDED_BLADERF_SINK_C_H
+
+#include <gnuradio/sync_block.h>
+#include "sink_iface.h"
+#include "bladerf_common.h"
+
+#include "osmosdr/ranges.h"
+
+class bladerf_sink_c;
+
+/*
+ * We use std::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 std::shared_ptr
+ */
+typedef std::shared_ptr<bladerf_sink_c> bladerf_sink_c_sptr;
+
+/*!
+ * \brief Return a shared_ptr to a new instance of bladerf_sink_c.
+ *
+ * To avoid accidental use of raw pointers, bladerf_sink_c's
+ * constructor is private.  make_bladerf_sink_c is the public
+ * interface for creating new instances.
+ */
+bladerf_sink_c_sptr make_bladerf_sink_c(const std::string &args = "");
+
+class bladerf_sink_c :
+  public gr::sync_block,
+  public sink_iface,
+  protected bladerf_common
+{
+private:
+  // The friend declaration allows bladerf_make_sink_c to
+  // access the private constructor.
+  friend bladerf_sink_c_sptr make_bladerf_sink_c(const std::string &args);
+
+  bladerf_sink_c(const std::string &args);
+
+  bool is_antenna_valid(const std::string &antenna);
+
+public:
+  std::string name();
+
+  static std::vector<std::string> get_devices();
+
+  size_t get_max_channels(void);
+  size_t get_num_channels(void);
+
+  bool start();
+  bool stop();
+
+  int work(int noutput_items,
+           gr_vector_const_void_star &input_items,
+           gr_vector_void_star &output_items);
+
+  osmosdr::meta_range_t get_sample_rates(void);
+  double set_sample_rate(double rate);
+  double get_sample_rate(void);
+
+  osmosdr::freq_range_t get_freq_range(size_t chan = 0);
+  double set_center_freq(double freq, size_t chan = 0);
+  double get_center_freq(size_t chan = 0);
+
+  double set_freq_corr(double ppm, size_t chan = 0);
+  double get_freq_corr(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);
+
+  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);
+
+  void set_dc_offset(const std::complex<double> &offset, size_t chan);
+  void set_iq_balance(const std::complex<double> &balance, size_t chan);
+
+  osmosdr::freq_range_t get_bandwidth_range(size_t chan = 0);
+  double set_bandwidth(double bandwidth, size_t chan = 0);
+  double get_bandwidth(size_t chan = 0);
+
+  std::vector<std::string> get_clock_sources(size_t mboard);
+  void set_clock_source(const std::string &source, size_t mboard = 0);
+  std::string get_clock_source(size_t mboard);
+
+  void set_biastee_mode(const std::string &mode);
+
+private:
+  int transmit_with_tags(int16_t const *samples, int noutput_items);
+
+  // Sample-handling buffers
+  int16_t *_16icbuf;              /**< raw samples to bladeRF */
+  gr_complex *_32fcbuf;           /**< intermediate buffer for conversions */
+
+  bool _in_burst;                 /**< are we currently in a burst? */
+  bool _running;                  /**< is the sink running? */
+  bladerf_channel_layout _layout; /**< channel layout */
+
+  gr::thread::mutex d_mutex;      /**< mutex to protect set/work access */
+
+  /* Scaling factor used when converting from float to int16_t */
+  const float SCALING_FACTOR = 2048.0f;
+};
+
+#endif // INCLUDED_BLADERF_SINK_C_H
diff --git a/lib/bladerf/bladerf_source_c.cc b/lib/bladerf/bladerf_source_c.cc
new file mode 100644
index 0000000000000000000000000000000000000000..83db677a1015986908fcf509048b0ce81b3867fa
--- /dev/null
+++ b/lib/bladerf/bladerf_source_c.cc
@@ -0,0 +1,680 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2013-2017 Nuand LLC
+ * Copyright 2013 Dimitri Stolnikov <horiz0n@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.
+ */
+
+/*
+ * config.h is generated by configure.  It contains the results
+ * of probing for features, options etc.  It should be the first
+ * file included in your .cc file.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <iostream>
+
+#include <boost/assign.hpp>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <gnuradio/io_signature.h>
+
+#include <volk/volk.h>
+
+#include "arg_helpers.h"
+#include "bladerf_source_c.h"
+#include "osmosdr/source.h"
+
+using namespace boost::assign;
+
+/******************************************************************************
+ * Functions
+ ******************************************************************************/
+
+/*
+ * Create a new instance of bladerf_source_c and return
+ * a boost shared_ptr.  This is effectively the public constructor.
+ */
+bladerf_source_c_sptr make_bladerf_source_c(const std::string &args)
+{
+  return gnuradio::get_initial_sptr(new bladerf_source_c(args));
+}
+
+/******************************************************************************
+ * Private methods
+ ******************************************************************************/
+
+/*
+ * The private constructor
+ */
+bladerf_source_c::bladerf_source_c(const std::string &args) :
+  gr::sync_block( "bladerf_source_c",
+                  gr::io_signature::make(0, 0, 0),
+                  args_to_io_signature(args)),
+  _16icbuf(NULL),
+  _32fcbuf(NULL),
+  _running(false),
+  _agcmode(BLADERF_GAIN_DEFAULT)
+{
+  int status;
+
+  dict_t dict = params_to_dict(args);
+
+  /* Perform src/sink agnostic initializations */
+  init(dict, BLADERF_RX);
+
+  /* Handle setting of sampling mode */
+  if (dict.count("sampling")) {
+    bladerf_sampling sampling = BLADERF_SAMPLING_UNKNOWN;
+
+    if (dict["sampling"] == "internal") {
+      sampling = BLADERF_SAMPLING_INTERNAL;
+    } else if (dict["sampling"] == "external") {
+      sampling = BLADERF_SAMPLING_EXTERNAL;
+    } else {
+      BLADERF_WARNING("Invalid sampling mode: " + dict["sampling"]);
+    }
+
+    if (sampling != BLADERF_SAMPLING_UNKNOWN) {
+      status = bladerf_set_sampling(_dev.get(), sampling);
+      if (status != 0) {
+        BLADERF_WARNING("Problem while setting sampling mode: " <<
+                        bladerf_strerror(status));
+      }
+    }
+  }
+
+  /* Bias tee */
+  if (dict.count("biastee")) {
+    set_biastee_mode(dict["biastee"]);
+  }
+
+  /* Loopback */
+  set_loopback_mode(dict.count("loopback") ? dict["loopback"] : "none");
+
+  /* RX Mux */
+  set_rx_mux_mode(dict.count("rxmux") ? dict["rxmux"] : "baseband");
+
+  /* AGC mode */
+  if (dict.count("agc_mode")) {
+    set_agc_mode(dict["agc_mode"]);
+  }
+
+  /* Specify initial gain mode */
+  if (dict.count("agc")) {
+    for (size_t i = 0; i < get_max_channels(); ++i) {
+      set_gain_mode(boost::lexical_cast<bool>(dict["agc"]), BLADERF_CHANNEL_RX(i));
+      BLADERF_INFO(boost::str(boost::format("%s gain mode set to '%s'")
+                    % channel2str(BLADERF_CHANNEL_RX(i))
+                    % get_gain_mode(BLADERF_CHANNEL_RX(i))));
+    }
+  }
+
+  /* Warn user about using an old FPGA version, as we no longer strip off the
+   * markers that were pressent in the pre-v0.0.1 FPGA */
+  {
+    struct bladerf_version fpga_version;
+
+    if (bladerf_fpga_version(_dev.get(), &fpga_version) != 0) {
+      BLADERF_WARNING("Failed to get FPGA version");
+    } else if (fpga_version.major <= 0 &&
+               fpga_version.minor <= 0 &&
+               fpga_version.patch < 1) {
+      BLADERF_WARNING("Warning: FPGA version v0.0.1 or later is required. "
+                      "Using an earlier FPGA version will result in "
+                      "misinterpeted samples.");
+    }
+  }
+
+  /* Initialize channel <-> antenna map */
+  for (std::string ant : get_antennas()) {
+    _chanmap[str2channel(ant)] = -1;
+  }
+
+  /* Bounds-checking output signature depending on our underlying hardware */
+  if (get_num_channels() > get_max_channels()) {
+    BLADERF_WARNING("Warning: number of channels specified on command line ("
+                    << get_num_channels() << ") is greater than the maximum "
+                    "number supported by this device (" << get_max_channels()
+                    << "). Resetting to " << get_max_channels() << ".");
+
+    set_output_signature(gr::io_signature::make(get_max_channels(),
+                                                get_max_channels(),
+                                                sizeof(gr_complex)));
+  }
+
+  /* Set up constraints */
+  int const alignment_multiple = volk_get_alignment() / sizeof(gr_complex);
+  set_alignment(std::max(1,alignment_multiple));
+  set_max_noutput_items(_samples_per_buffer);
+  set_output_multiple(get_num_channels());
+
+  /* Set channel layout */
+  _layout = (get_num_channels() > 1) ? BLADERF_RX_X2 : BLADERF_RX_X1;
+
+  /* Initial wiring of antennas to channels */
+  for (size_t ch = 0; ch < get_num_channels(); ++ch) {
+    set_channel_enable(BLADERF_CHANNEL_RX(ch), true);
+    _chanmap[BLADERF_CHANNEL_RX(ch)] = ch;
+  }
+
+  BLADERF_DEBUG("initialization complete");
+}
+
+bool bladerf_source_c::is_antenna_valid(const std::string &antenna)
+{
+  for (std::string ant : get_antennas()) {
+    if (antenna == ant) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+/******************************************************************************
+ * Public methods
+ ******************************************************************************/
+
+std::string bladerf_source_c::name()
+{
+  return "bladeRF receiver";
+}
+
+std::vector<std::string> bladerf_source_c::get_devices()
+{
+  return bladerf_common::devices();
+}
+
+size_t bladerf_source_c::get_max_channels()
+{
+  return bladerf_common::get_max_channels(BLADERF_RX);
+}
+
+size_t bladerf_source_c::get_num_channels()
+{
+  return output_signature()->max_streams();
+}
+
+bool bladerf_source_c::start()
+{
+  int status;
+
+  BLADERF_DEBUG("starting source");
+
+  gr::thread::scoped_lock guard(d_mutex);
+
+  status = bladerf_sync_config(_dev.get(), _layout, _format, _num_buffers,
+                               _samples_per_buffer, _num_transfers,
+                               _stream_timeout);
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "bladerf_sync_config failed");
+  }
+
+  for (size_t ch = 0; ch < get_max_channels(); ++ch) {
+    bladerf_channel brfch = BLADERF_CHANNEL_RX(ch);
+    if (get_channel_enable(brfch)) {
+      status = bladerf_enable_module(_dev.get(), brfch, true);
+      if (status != 0) {
+        BLADERF_THROW_STATUS(status, "bladerf_enable_module failed");
+      }
+    }
+  }
+
+  /* Allocate memory for conversions in work() */
+  size_t alignment = volk_get_alignment();
+
+  _16icbuf = reinterpret_cast<int16_t *>(volk_malloc(2*_samples_per_buffer*sizeof(int16_t), alignment));
+  _32fcbuf = reinterpret_cast<gr_complex *>(volk_malloc(_samples_per_buffer*sizeof(gr_complex), alignment));
+
+  _running = true;
+
+  return true;
+}
+
+bool bladerf_source_c::stop()
+{
+  int status;
+
+  BLADERF_DEBUG("stopping source");
+
+  gr::thread::scoped_lock guard(d_mutex);
+
+  if (!_running) {
+    BLADERF_WARNING("source already stopped, nothing to do here");
+    return true;
+  }
+
+  _running = false;
+
+  for (size_t ch = 0; ch < get_max_channels(); ++ch) {
+    bladerf_channel brfch = BLADERF_CHANNEL_RX(ch);
+    if (get_channel_enable(brfch)) {
+      status = bladerf_enable_module(_dev.get(), brfch, false);
+      if (status != 0) {
+        BLADERF_THROW_STATUS(status, "bladerf_enable_module failed");
+      }
+    }
+  }
+
+  /* Deallocate conversion memory */
+  volk_free(_16icbuf);
+  volk_free(_32fcbuf);
+  _16icbuf = NULL;
+  _32fcbuf = NULL;
+
+  return true;
+}
+
+int bladerf_source_c::work(int noutput_items,
+                          gr_vector_const_void_star &input_items,
+                          gr_vector_void_star &output_items)
+{
+  int status;
+  struct bladerf_metadata meta;
+  struct bladerf_metadata *meta_ptr = NULL;
+  size_t nstreams = num_streams(_layout);
+
+  gr::thread::scoped_lock guard(d_mutex);
+
+  // if we aren't running, nothing to do here
+  if (!_running) {
+    return 0;
+  }
+
+  // set up metadata
+  if (BLADERF_FORMAT_SC16_Q11_META == _format) {
+    memset(&meta, 0, sizeof(meta));
+    meta.flags = BLADERF_META_FLAG_RX_NOW;
+    meta_ptr = &meta;
+  }
+
+  // grab samples into temp buffer
+  status = bladerf_sync_rx(_dev.get(), static_cast<void *>(_16icbuf),
+                           noutput_items, meta_ptr, _stream_timeout);
+  if (status != 0) {
+    BLADERF_WARNING(boost::str(boost::format("bladerf_sync_rx error: %s")
+                    % bladerf_strerror(status)));
+    ++_failures;
+
+    if (_failures >= MAX_CONSECUTIVE_FAILURES) {
+      BLADERF_WARNING("Consecutive error limit hit. Shutting down.");
+      return WORK_DONE;
+    }
+  } else {
+    _failures = 0;
+  }
+
+  // convert from int16_t to float
+  // output_items is gr_complex (2x float), so num_points is 2*noutput_items
+  volk_16i_s32f_convert_32f(reinterpret_cast<float *>(_32fcbuf), _16icbuf,
+                            SCALING_FACTOR, 2*noutput_items);
+
+  // copy the samples into output_items
+  gr_complex **out = reinterpret_cast<gr_complex **>(&output_items[0]);
+
+  if (nstreams > 1) {
+    // we need to deinterleave the multiplex as we copy
+    gr_complex const *deint_in = _32fcbuf;
+
+    for (size_t i = 0; i < (noutput_items/nstreams); ++i) {
+      for (size_t n = 0; n < nstreams; ++n) {
+        memcpy(out[n]++, deint_in++, sizeof(gr_complex));
+      }
+    }
+  } else {
+    // no deinterleaving to do: simply copy everything
+    memcpy(out[0], _32fcbuf, sizeof(gr_complex) * noutput_items);
+  }
+
+  return noutput_items;
+}
+
+osmosdr::meta_range_t bladerf_source_c::get_sample_rates()
+{
+  return sample_rates(chan2channel(BLADERF_RX, 0));
+}
+
+double bladerf_source_c::set_sample_rate(double rate)
+{
+  return bladerf_common::set_sample_rate(rate, chan2channel(BLADERF_RX, 0));
+}
+
+double bladerf_source_c::get_sample_rate()
+{
+  return bladerf_common::get_sample_rate(chan2channel(BLADERF_RX, 0));
+}
+
+osmosdr::freq_range_t bladerf_source_c::get_freq_range(size_t chan)
+{
+  return bladerf_common::freq_range(chan2channel(BLADERF_RX, chan));
+}
+
+double bladerf_source_c::set_center_freq(double freq, size_t chan)
+{
+  return bladerf_common::set_center_freq(freq, chan2channel(BLADERF_RX, chan));
+}
+
+double bladerf_source_c::get_center_freq(size_t chan)
+{
+  return bladerf_common::get_center_freq(chan2channel(BLADERF_RX, chan));
+}
+
+double bladerf_source_c::set_freq_corr(double ppm, size_t chan)
+{
+  /* TODO: Write the VCTCXO with a correction value (also changes TX ppm value!) */
+  BLADERF_WARNING("Frequency correction is not implemented.");
+  return get_freq_corr(chan2channel(BLADERF_RX, chan));
+}
+
+double bladerf_source_c::get_freq_corr(size_t chan)
+{
+  /* TODO: Return back the frequency correction in ppm */
+  return 0;
+}
+
+std::vector<std::string> bladerf_source_c::get_gain_names(size_t chan)
+{
+  return bladerf_common::get_gain_names(chan2channel(BLADERF_RX, chan));
+}
+
+osmosdr::gain_range_t bladerf_source_c::get_gain_range(size_t chan)
+{
+  return bladerf_common::get_gain_range(chan2channel(BLADERF_RX, chan));
+}
+
+osmosdr::gain_range_t bladerf_source_c::get_gain_range(const std::string &name,
+                                                       size_t chan)
+{
+  return bladerf_common::get_gain_range(name, chan2channel(BLADERF_RX, chan));
+}
+
+bool bladerf_source_c::set_gain_mode(bool automatic, size_t chan)
+{
+  return bladerf_common::set_gain_mode(automatic,
+                                       chan2channel(BLADERF_RX, chan),
+                                       _agcmode);
+}
+
+bool bladerf_source_c::get_gain_mode(size_t chan)
+{
+  return bladerf_common::get_gain_mode(chan2channel(BLADERF_RX, chan));
+}
+
+double bladerf_source_c::set_gain(double gain, size_t chan)
+{
+  return bladerf_common::set_gain(gain, chan2channel(BLADERF_RX, chan));
+}
+
+double bladerf_source_c::set_gain(double gain, const std::string &name,
+                                  size_t chan)
+{
+  return bladerf_common::set_gain(gain, name, chan2channel(BLADERF_RX, chan));
+}
+
+double bladerf_source_c::get_gain(size_t chan)
+{
+  return bladerf_common::get_gain(chan2channel(BLADERF_RX, chan));
+}
+
+double bladerf_source_c::get_gain(const std::string &name, size_t chan)
+{
+  return bladerf_common::get_gain(name, chan2channel(BLADERF_RX, chan));
+}
+
+std::vector<std::string> bladerf_source_c::get_antennas(size_t chan)
+{
+  return bladerf_common::get_antennas(BLADERF_RX);
+}
+
+std::string bladerf_source_c::set_antenna(const std::string &antenna,
+                                          size_t chan)
+{
+  bool _was_running = _running;
+
+  if (_was_running) {
+    stop();
+  }
+
+  bladerf_common::set_antenna(BLADERF_RX, chan, antenna);
+
+  if (_was_running) {
+    start();
+  }
+
+  return get_antenna(chan);
+}
+
+std::string bladerf_source_c::get_antenna(size_t chan)
+{
+  return channel2str(chan2channel(BLADERF_RX, chan));
+}
+
+void bladerf_source_c::set_dc_offset_mode(int mode, size_t chan)
+{
+  if (osmosdr::source::DCOffsetOff == mode) {
+    //_src->set_auto_dc_offset( false, chan );
+    /* reset to default for off-state */
+    set_dc_offset(std::complex<double>(0.0, 0.0), chan);
+  } else if (osmosdr::source::DCOffsetManual == mode) {
+    /* disable auto mode, but keep correcting with last known values */
+    //_src->set_auto_dc_offset( false, chan );
+  } else if (osmosdr::source::DCOffsetAutomatic == mode) {
+    //_src->set_auto_dc_offset( true, chan );
+    BLADERF_WARNING("Automatic DC correction mode is not implemented.");
+  }
+}
+
+void bladerf_source_c::set_dc_offset(const std::complex<double> &offset,
+                                     size_t chan)
+{
+  int status;
+
+  status = bladerf_common::set_dc_offset(offset, chan2channel(BLADERF_RX, chan));
+
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "could not set dc offset");
+  }
+}
+
+void bladerf_source_c::set_iq_balance_mode(int mode, size_t chan)
+{
+  if (osmosdr::source::IQBalanceOff == mode) {
+    //_src->set_auto_iq_balance( false, chan );
+    /* reset to default for off-state */
+    set_iq_balance(std::complex<double>(0.0, 0.0), chan);
+  } else if (osmosdr::source::IQBalanceManual == mode) {
+    /* disable auto mode, but keep correcting with last known values */
+    //_src->set_auto_iq_balance( false, chan );
+  } else if (osmosdr::source::IQBalanceAutomatic == mode) {
+    //_src->set_auto_iq_balance( true, chan );
+    BLADERF_WARNING("Automatic IQ correction mode is not implemented.");
+  }
+}
+
+void bladerf_source_c::set_iq_balance(const std::complex<double> &balance,
+                                      size_t chan)
+{
+  int status;
+
+  status = bladerf_common::set_iq_balance(balance, chan2channel(BLADERF_RX, chan));
+
+  if (status != 0) {
+    BLADERF_THROW_STATUS(status, "could not set iq balance");
+  }
+}
+
+osmosdr::freq_range_t bladerf_source_c::get_bandwidth_range(size_t chan)
+{
+  return filter_bandwidths(chan2channel(BLADERF_RX, chan));
+}
+
+double bladerf_source_c::set_bandwidth(double bandwidth, size_t chan)
+{
+  return bladerf_common::set_bandwidth(bandwidth,
+                                       chan2channel(BLADERF_RX, chan));
+}
+
+double bladerf_source_c::get_bandwidth(size_t chan)
+{
+  return bladerf_common::get_bandwidth(chan2channel(BLADERF_RX, chan));
+}
+
+std::vector<std::string> bladerf_source_c::get_clock_sources(size_t mboard)
+{
+  return bladerf_common::get_clock_sources(mboard);
+}
+
+void bladerf_source_c::set_clock_source(const std::string &source,
+                                        size_t mboard)
+{
+  bladerf_common::set_clock_source(source, mboard);
+}
+
+std::string bladerf_source_c::get_clock_source(size_t mboard)
+{
+  return bladerf_common::get_clock_source(mboard);
+}
+
+void bladerf_source_c::set_biastee_mode(const std::string &mode)
+{
+  int status;
+  bool enable;
+
+  if (mode == "on" || mode == "1" || mode == "rx") {
+    enable = true;
+  } else {
+    enable = false;
+  }
+
+  status = bladerf_set_bias_tee(_dev.get(), BLADERF_CHANNEL_RX(0), enable);
+  if (BLADERF_ERR_UNSUPPORTED == status) {
+    // unsupported, but not worth crashing out
+    BLADERF_WARNING("Bias-tee not supported by device");
+  } else if (status != 0) {
+    BLADERF_THROW_STATUS(status, "Failed to set bias-tee");
+  }
+}
+
+void bladerf_source_c::set_loopback_mode(const std::string &loopback)
+{
+  int status;
+  bladerf_loopback mode;
+
+  if (loopback == "bb_txlpf_rxvga2") {
+    mode = BLADERF_LB_BB_TXLPF_RXVGA2;
+  } else if (loopback == "bb_txlpf_rxlpf") {
+    mode = BLADERF_LB_BB_TXLPF_RXLPF;
+  } else if (loopback == "bb_txvga1_rxvga2") {
+    mode = BLADERF_LB_BB_TXVGA1_RXVGA2;
+  } else if (loopback == "bb_txvga1_rxlpf") {
+    mode = BLADERF_LB_BB_TXVGA1_RXLPF;
+  } else if (loopback == "rf_lna1") {
+    mode = BLADERF_LB_RF_LNA1;
+  } else if (loopback == "rf_lna2") {
+    mode = BLADERF_LB_RF_LNA2;
+  } else if (loopback == "rf_lna3") {
+    mode = BLADERF_LB_RF_LNA3;
+  } else if (loopback == "firmware") {
+    mode = BLADERF_LB_FIRMWARE;
+  } else if (loopback == "rfic_bist") {
+    mode = BLADERF_LB_RFIC_BIST;
+  } else if (loopback == "none") {
+    mode = BLADERF_LB_NONE;
+  } else {
+    BLADERF_THROW("Unknown loopback mode: " + loopback);
+  }
+
+  status = bladerf_set_loopback(_dev.get(), mode);
+  if (BLADERF_ERR_UNSUPPORTED == status) {
+    // unsupported, but not worth crashing out
+    BLADERF_WARNING("Loopback mode not supported by device: " + loopback);
+  } else if (status != 0) {
+    BLADERF_THROW_STATUS(status, "Failed to set loopback mode");
+  }
+}
+
+void bladerf_source_c::set_rx_mux_mode(const std::string &rxmux)
+{
+  int status;
+  bladerf_rx_mux mode;
+
+  if (rxmux == "baseband") {
+    mode = BLADERF_RX_MUX_BASEBAND;
+  } else if (rxmux == "12bit") {
+    mode = BLADERF_RX_MUX_12BIT_COUNTER;
+  } else if (rxmux == "32bit") {
+    mode = BLADERF_RX_MUX_32BIT_COUNTER;
+  } else if (rxmux == "digital") {
+    mode = BLADERF_RX_MUX_DIGITAL_LOOPBACK;
+  } else {
+    BLADERF_THROW("Unknown RX mux mode: " + rxmux);
+  }
+
+  status = bladerf_set_rx_mux(_dev.get(), mode);
+  if (BLADERF_ERR_UNSUPPORTED == status) {
+    // unsupported, but not worth crashing out
+    BLADERF_WARNING("RX mux mode not supported by device: " + rxmux);
+  } else if (status != 0) {
+    BLADERF_THROW_STATUS(status, "Failed to set RX mux mode");
+  }
+}
+
+void bladerf_source_c::set_agc_mode(const std::string &agcmode)
+{
+#ifndef BLADERF_COMPATIBILITY
+  int status;
+  bladerf_gain_mode mode;
+  bool ok = false;
+  struct bladerf_gain_modes const *modes = NULL;
+
+  /* Get the list of AGC modes */
+  status = bladerf_get_gain_modes(_dev.get(), BLADERF_CHANNEL_RX(0), &modes);
+  if (status < 0) {
+    BLADERF_THROW_STATUS(status, "failed to get gain modes");
+  }
+
+  size_t count = status;
+
+  /* Compare... */
+  for (size_t i = 0; i < count; ++i) {
+    if (agcmode == std::string(modes[i].name)) {
+      mode = modes[i].mode;
+      ok = true;
+      BLADERF_DEBUG("Setting gain mode to " << mode << " (" << agcmode << ")");
+      break;
+    }
+  }
+
+  if (!ok) {
+    BLADERF_WARNING("Unknown gain mode \"" << agcmode << "\"");
+    return;
+  }
+
+  _agcmode = mode;
+
+  for (size_t i = 0; i < get_num_channels(); ++i) {
+    if (bladerf_common::get_gain_mode(BLADERF_CHANNEL_RX(i))) {
+      /* Refresh this */
+      bladerf_common::set_gain_mode(true, BLADERF_CHANNEL_RX(i), _agcmode);
+    }
+  }
+#endif
+}
diff --git a/lib/bladerf/bladerf_source_c.h b/lib/bladerf/bladerf_source_c.h
new file mode 100644
index 0000000000000000000000000000000000000000..0cc26f43e28098214c632f327772a51f818d5c51
--- /dev/null
+++ b/lib/bladerf/bladerf_source_c.h
@@ -0,0 +1,144 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2013-2017 Nuand LLC
+ * Copyright 2013 Dimitri Stolnikov <horiz0n@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_BLADERF_SOURCE_C_H
+#define INCLUDED_BLADERF_SOURCE_C_H
+
+#include <gnuradio/sync_block.h>
+#include "source_iface.h"
+#include "bladerf_common.h"
+
+#include "osmosdr/ranges.h"
+
+class bladerf_source_c;
+
+/*
+ * We use std::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 std::shared_ptr
+ */
+typedef std::shared_ptr<bladerf_source_c> bladerf_source_c_sptr;
+
+/*!
+ * \brief Return a shared_ptr to a new instance of bladerf_source_c.
+ *
+ * To avoid accidental use of raw pointers, bladerf_source_c's
+ * constructor is private.  bladerf_make_source_c is the public
+ * interface for creating new instances.
+ */
+bladerf_source_c_sptr make_bladerf_source_c(const std::string &args = "");
+
+class bladerf_source_c :
+  public gr::sync_block,
+  public source_iface,
+  protected bladerf_common
+{
+private:
+  // The friend declaration allows bladerf_make_source_c to
+  // access the private constructor.
+  friend bladerf_source_c_sptr make_bladerf_source_c(const std::string &args);
+
+  bladerf_source_c(const std::string &args);
+
+  bool is_antenna_valid(const std::string &antenna);
+
+public:
+  std::string name();
+
+  static std::vector<std::string> get_devices();
+
+  size_t get_max_channels(void);
+  size_t get_num_channels(void);
+
+  bool start();
+  bool stop();
+
+  int work(int noutput_items,
+           gr_vector_const_void_star &input_items,
+           gr_vector_void_star &output_items);
+
+  osmosdr::meta_range_t get_sample_rates(void);
+  double set_sample_rate(double rate);
+  double get_sample_rate(void);
+
+  osmosdr::freq_range_t get_freq_range(size_t chan = 0);
+  double set_center_freq(double freq, size_t chan = 0);
+  double get_center_freq(size_t chan = 0);
+
+  double set_freq_corr(double ppm, size_t chan = 0);
+  double get_freq_corr(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);
+
+  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);
+
+  void set_dc_offset_mode(int mode, size_t chan = 0);
+  void set_dc_offset(const std::complex<double> &offset, size_t chan = 0);
+
+  void set_iq_balance_mode(int mode, size_t chan = 0);
+  void set_iq_balance(const std::complex<double> &balance, size_t chan = 0);
+
+  osmosdr::freq_range_t get_bandwidth_range(size_t chan = 0);
+  double set_bandwidth(double bandwidth, size_t chan = 0);
+  double get_bandwidth(size_t chan = 0);
+
+  std::vector<std::string> get_clock_sources(size_t mboard);
+  void set_clock_source(const std::string &source, size_t mboard = 0);
+  std::string get_clock_source(size_t mboard);
+
+  void set_biastee_mode(const std::string &mode);
+
+  void set_loopback_mode(const std::string &loopback);
+  void set_rx_mux_mode(const std::string &rxmux);
+  void set_agc_mode(const std::string &agcmode);
+
+private:
+  // Sample-handling buffers
+  int16_t *_16icbuf;              /**< raw samples from bladeRF */
+  gr_complex *_32fcbuf;           /**< intermediate buffer to gnuradio */
+
+  bool _running;                  /**< is the source running? */
+  bladerf_channel_layout _layout; /**< channel layout */
+  bladerf_gain_mode _agcmode;     /**< gain mode when AGC is enabled */
+
+  gr::thread::mutex d_mutex;      /**< mutex to protect set/work access */
+
+  /* Scaling factor used when converting from int16_t to float */
+  const float SCALING_FACTOR = 2048.0f;
+};
+
+#endif // INCLUDED_BLADERF_SOURCE_C_H
diff --git a/lib/source_impl.cc b/lib/source_impl.cc
index 10f3ae2ac29497d96d0a927ac2083927714102b1..c099f920f2676a9435921f07fcd9af6173129f10 100644
--- a/lib/source_impl.cc
+++ b/lib/source_impl.cc
@@ -7,6 +7,7 @@
 
 #include <gnuradio/io_signature.h>
 #include "source_impl.h"
+#include "arg_helpers.h"
 
 namespace gr {
   namespace bladeRF {
@@ -16,22 +17,15 @@ namespace gr {
       return gnuradio::make_block_sptr<source_impl>(args);
     }
 
-    inline gr::io_signature::sptr make_io_signature( const std::string &args )
-    {
-        size_t max_nchan = 0;
-        size_t dev_nchan = 0;
-        const size_t nchan = std::max<size_t>(dev_nchan, 1); // assume at least one
-        return gr::io_signature::make(nchan, nchan, sizeof(gr_complex));
-    }
-
-    /*
+     /*
      * The private constructor
      */
     source_impl::source_impl(const std::string & args)
       : gr::hier_block2("source",
               gr::io_signature::make(0,0,0),
-              make_io_signature(args))
+              args_to_io_signature(args))
     {
+
       //connect(self(), 0, d_firstblock, 0);
       // connect other blocks
       //connect(d_lastblock, 0, self(), 0);