File 0001-add-xtrx-support.patch of Package gr-osmosdr

diff --git a/CMakeLists.txt b/CMakeLists.txt
index af21291..faf89cc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -173,6 +173,7 @@ find_package(Volk)
 find_package(LibbladeRF)
 find_package(SoapySDR NO_MODULE)
 find_package(LibFreeSRP)
+find_package(LibXTRX)
 find_package(Doxygen)
 
 if(NOT GNURADIO_RUNTIME_FOUND)
diff --git a/cmake/Modules/FindLibXTRX.cmake b/cmake/Modules/FindLibXTRX.cmake
new file mode 100644
index 0000000..e7681d1
--- /dev/null
+++ b/cmake/Modules/FindLibXTRX.cmake
@@ -0,0 +1,27 @@
+if(NOT LIBXTRX_FOUND)
+  pkg_check_modules (LIBXTRX_PKG libxtrx)
+  find_path(LIBXTRX_INCLUDE_DIRS NAMES xtrx_api.h
+    PATHS
+    ${LIBXTRX_PKG_INCLUDE_DIRS}
+    /usr/include
+    /usr/local/include
+  )
+
+  find_library(LIBXTRX_LIBRARIES NAMES xtrx
+    PATHS
+    ${LIBXTRX_PKG_LIBRARY_DIRS}
+    /usr/lib
+    /usr/local/lib
+  )
+
+if(LIBXTRX_INCLUDE_DIRS AND LIBXTRX_LIBRARIES)
+  set(LIBXTRX_FOUND TRUE CACHE INTERNAL "libxtrx found")
+  message(STATUS "Found libxtrx: ${LIBXTRX_INCLUDE_DIRS}, ${LIBXTRX_LIBRARIES}")
+else(LIBXTRX_INCLUDE_DIRS AND LIBXTRX_LIBRARIES)
+  set(LIBXTRX_FOUND FALSE CACHE INTERNAL "libxtrx found")
+  message(STATUS "libxtrx not found.")
+endif(LIBXTRX_INCLUDE_DIRS AND LIBXTRX_LIBRARIES)
+
+mark_as_advanced(LIBXTRX_LIBRARIES LIBXTRX_INCLUDE_DIRS)
+
+endif(NOT LIBXTRX_FOUND)
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index dbb175a..a9fe524 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -254,6 +254,14 @@ if(ENABLE_FREESRP)
 GR_INCLUDE_SUBDIRECTORY(freesrp)
 endif(ENABLE_FREESRP)
 
+########################################################################
+# Setup XTRX component
+########################################################################
+GR_REGISTER_COMPONENT("XTRX SDR" ENABLE_XTRX LIBXTRX_FOUND)
+if(ENABLE_XTRX)
+GR_INCLUDE_SUBDIRECTORY(xtrx)
+endif(ENABLE_XTRX)
+
 ########################################################################
 # Setup configuration file
 ########################################################################
diff --git a/lib/CMakeLists.txt.orig b/lib/CMakeLists.txt.orig
new file mode 100644
index 0000000..dbb175a
--- /dev/null
+++ b/lib/CMakeLists.txt.orig
@@ -0,0 +1,287 @@
+# Copyright 2011 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# 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.
+
+########################################################################
+# Setup library
+########################################################################
+INCLUDE(GrPlatform) #define LIB_SUFFIX
+INCLUDE(GrMiscUtils)
+INCLUDE(GrComponent)
+
+########################################################################
+# Helpful Macros
+########################################################################
+MACRO(GR_OSMOSDR_APPEND_SRCS)
+    LIST(APPEND gr_osmosdr_srcs ${ARGV})
+ENDMACRO(GR_OSMOSDR_APPEND_SRCS)
+
+MACRO(GR_OSMOSDR_APPEND_LIBS)
+    LIST(APPEND gr_osmosdr_libs ${ARGV})
+ENDMACRO(GR_OSMOSDR_APPEND_LIBS)
+
+GR_OSMOSDR_APPEND_SRCS(
+    source_impl.cc
+    sink_impl.cc
+    ranges.cc
+    device.cc
+    time_spec.cc
+)
+
+#-pthread Adds support for multithreading with the pthreads library.
+#This option sets flags for both the preprocessor and linker. (man gcc)
+if(CMAKE_COMPILER_IS_GNUCXX)
+    list(APPEND Boost_LIBRARIES -pthread)
+endif()
+
+GR_OSMOSDR_APPEND_LIBS(
+    ${Boost_LIBRARIES}
+    ${GNURADIO_ALL_LIBRARIES}
+)
+
+########################################################################
+# Setup defines for high resolution timing
+########################################################################
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Configuring high resolution timing...")
+INCLUDE(CheckCXXSourceCompiles)
+
+SET(CMAKE_REQUIRED_LIBRARIES -lrt)
+CHECK_CXX_SOURCE_COMPILES("
+    #include <ctime>
+    int main(){
+        timespec ts;
+        return clock_gettime(CLOCK_MONOTONIC, &ts);
+    }
+    " HAVE_CLOCK_GETTIME
+)
+UNSET(CMAKE_REQUIRED_LIBRARIES)
+
+INCLUDE(CheckCXXSourceCompiles)
+CHECK_CXX_SOURCE_COMPILES("
+    #include <mach/mach_time.h>
+    int main(){
+        mach_timebase_info_data_t info;
+        mach_timebase_info(&info);
+        mach_absolute_time();
+        return 0;
+    }
+    " HAVE_MACH_ABSOLUTE_TIME
+)
+
+CHECK_CXX_SOURCE_COMPILES("
+    #include <Windows.h>
+    int main(){
+        LARGE_INTEGER value;
+        QueryPerformanceCounter(&value);
+        QueryPerformanceFrequency(&value);
+        return 0;
+    }
+    " HAVE_QUERY_PERFORMANCE_COUNTER
+)
+
+IF(HAVE_CLOCK_GETTIME)
+    MESSAGE(STATUS "  High resolution timing supported through clock_gettime.")
+    SET(TIME_SPEC_DEFS HAVE_CLOCK_GETTIME)
+    GR_OSMOSDR_APPEND_LIBS("-lrt")
+ELSEIF(HAVE_MACH_ABSOLUTE_TIME)
+    MESSAGE(STATUS "  High resolution timing supported through mach_absolute_time.")
+    SET(TIME_SPEC_DEFS HAVE_MACH_ABSOLUTE_TIME)
+ELSEIF(HAVE_QUERY_PERFORMANCE_COUNTER)
+    MESSAGE(STATUS "  High resolution timing supported through QueryPerformanceCounter.")
+    SET(TIME_SPEC_DEFS HAVE_QUERY_PERFORMANCE_COUNTER)
+ELSE()
+    MESSAGE(STATUS "  High resolution timing supported through microsec_clock.")
+    SET(TIME_SPEC_DEFS HAVE_MICROSEC_CLOCK)
+ENDIF()
+
+SET_SOURCE_FILES_PROPERTIES(
+    time_spec.cc
+    PROPERTIES COMPILE_DEFINITIONS "${TIME_SPEC_DEFS}"
+)
+
+########################################################################
+# Setup IQBalance component
+########################################################################
+GR_REGISTER_COMPONENT("Osmocom IQ Imbalance Correction" ENABLE_IQBALANCE GNURADIO_IQBALANCE_FOUND)
+if(ENABLE_IQBALANCE)
+add_definitions(-DHAVE_IQBALANCE=1)
+include_directories(${GNURADIO_IQBALANCE_INCLUDE_DIRS})
+GR_OSMOSDR_APPEND_LIBS(${GNURADIO_IQBALANCE_LIBRARIES})
+endif(ENABLE_IQBALANCE)
+
+########################################################################
+# Setup OsmoSDR component
+########################################################################
+GR_REGISTER_COMPONENT("sysmocom OsmoSDR" ENABLE_OSMOSDR LIBOSMOSDR_FOUND)
+if(ENABLE_OSMOSDR)
+GR_INCLUDE_SUBDIRECTORY(osmosdr)
+endif(ENABLE_OSMOSDR)
+
+########################################################################
+# Setup FCD component
+########################################################################
+GR_REGISTER_COMPONENT("FUNcube Dongle" ENABLE_FCD GNURADIO_FCD_FOUND)
+GR_REGISTER_COMPONENT("FUNcube Dongle Pro+" ENABLE_FCDPP GNURADIO_FCDPP_FOUND)
+if(ENABLE_FCD)
+add_definitions(-DHAVE_FCD=1)
+endif(ENABLE_FCD)
+if(ENABLE_FCDPP)
+add_definitions(-DHAVE_FCDPP=1)
+endif(ENABLE_FCDPP)
+if(ENABLE_FCD OR ENABLE_FCDPP)
+GR_INCLUDE_SUBDIRECTORY(fcd)
+endif(ENABLE_FCD OR ENABLE_FCDPP)
+
+########################################################################
+# Setup File component
+########################################################################
+GR_REGISTER_COMPONENT("IQ File Source & Sink" ENABLE_FILE GNURADIO_BLOCKS_FOUND)
+if(ENABLE_FILE)
+GR_INCLUDE_SUBDIRECTORY(file)
+endif(ENABLE_FILE)
+
+########################################################################
+# Setup RTL component
+########################################################################
+GR_REGISTER_COMPONENT("Osmocom RTLSDR" ENABLE_RTL LIBRTLSDR_FOUND)
+if(ENABLE_RTL)
+GR_INCLUDE_SUBDIRECTORY(rtl)
+endif(ENABLE_RTL)
+
+########################################################################
+# Setup RTL_TCP component
+########################################################################
+GR_REGISTER_COMPONENT("RTLSDR TCP Client" ENABLE_RTL_TCP GNURADIO_BLOCKS_FOUND)
+if(ENABLE_RTL_TCP)
+GR_INCLUDE_SUBDIRECTORY(rtl_tcp)
+endif(ENABLE_RTL_TCP)
+
+########################################################################
+# Setup UHD component
+########################################################################
+GR_REGISTER_COMPONENT("Ettus USRP Devices" ENABLE_UHD UHD_FOUND GNURADIO_UHD_FOUND)
+if(ENABLE_UHD)
+GR_INCLUDE_SUBDIRECTORY(uhd)
+endif(ENABLE_UHD)
+
+########################################################################
+# Setup MiriSDR component
+########################################################################
+GR_REGISTER_COMPONENT("Osmocom MiriSDR" ENABLE_MIRI LIBMIRISDR_FOUND)
+if(ENABLE_MIRI)
+GR_INCLUDE_SUBDIRECTORY(miri)
+endif(ENABLE_MIRI)
+
+########################################################################
+# Setup SDRplay component
+########################################################################
+if(ENABLE_NONFREE)
+GR_REGISTER_COMPONENT("SDRplay RSP (NONFREE)" ENABLE_SDRPLAY LIBSDRPLAY_FOUND)
+if(ENABLE_SDRPLAY)
+GR_INCLUDE_SUBDIRECTORY(sdrplay)
+endif(ENABLE_SDRPLAY)
+endif(ENABLE_NONFREE)
+
+########################################################################
+# Setup HackRF component
+########################################################################
+GR_REGISTER_COMPONENT("HackRF & rad1o Badge" ENABLE_HACKRF LIBHACKRF_FOUND)
+if(ENABLE_HACKRF)
+GR_INCLUDE_SUBDIRECTORY(hackrf)
+endif(ENABLE_HACKRF)
+
+########################################################################
+# Setup bladeRF component
+########################################################################
+GR_REGISTER_COMPONENT("nuand bladeRF" ENABLE_BLADERF LIBBLADERF_FOUND)
+if(ENABLE_BLADERF)
+GR_INCLUDE_SUBDIRECTORY(bladerf)
+endif(ENABLE_BLADERF)
+
+########################################################################
+# Setup RFSPACE component
+########################################################################
+GR_REGISTER_COMPONENT("RFSPACE Receivers" ENABLE_RFSPACE)
+if(ENABLE_RFSPACE)
+GR_INCLUDE_SUBDIRECTORY(rfspace)
+endif(ENABLE_RFSPACE)
+
+########################################################################
+# Setup AIRSPY component
+########################################################################
+GR_REGISTER_COMPONENT("AIRSPY Receiver" ENABLE_AIRSPY LIBAIRSPY_FOUND)
+if(ENABLE_AIRSPY)
+GR_INCLUDE_SUBDIRECTORY(airspy)
+endif(ENABLE_AIRSPY)
+
+########################################################################
+# Setup SoapySDR component
+########################################################################
+GR_REGISTER_COMPONENT("SoapySDR support" ENABLE_SOAPY SoapySDR_FOUND)
+if(ENABLE_SOAPY)
+GR_INCLUDE_SUBDIRECTORY(soapy)
+endif(ENABLE_SOAPY)
+
+########################################################################
+# Setup Red Pitaya component
+########################################################################
+GR_REGISTER_COMPONENT("Red Pitaya SDR" ENABLE_REDPITAYA)
+if(ENABLE_REDPITAYA)
+GR_INCLUDE_SUBDIRECTORY(redpitaya)
+endif(ENABLE_REDPITAYA)
+
+########################################################################
+# Setup FreeSRP component
+########################################################################
+GR_REGISTER_COMPONENT("FreeSRP support" ENABLE_FREESRP LIBFREESRP_FOUND)
+if(ENABLE_FREESRP)
+GR_INCLUDE_SUBDIRECTORY(freesrp)
+endif(ENABLE_FREESRP)
+
+########################################################################
+# Setup configuration file
+########################################################################
+ADD_DEFINITIONS(-DHAVE_CONFIG_H=1)
+include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
+CONFIGURE_FILE(
+    ${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
+    ${CMAKE_CURRENT_BINARY_DIR}/config.h
+@ONLY)
+
+########################################################################
+# Set up Windows DLL resource files
+########################################################################
+IF(MSVC)
+    include(${CMAKE_SOURCE_DIR}/cmake/Modules/GrVersion.cmake)
+
+    configure_file(
+        ${CMAKE_CURRENT_SOURCE_DIR}/gnuradio-osmosdr.rc.in
+        ${CMAKE_CURRENT_BINARY_DIR}/gnuradio-osmosdr.rc
+    @ONLY)
+
+    GR_OSMOSDR_APPEND_SRCS(${CMAKE_CURRENT_BINARY_DIR}/gnuradio-osmosdr.rc)
+ENDIF(MSVC)
+
+########################################################################
+# Setup libgnuradio-osmosdr library
+########################################################################
+ADD_LIBRARY(gnuradio-osmosdr SHARED ${gr_osmosdr_srcs})
+TARGET_LINK_LIBRARIES(gnuradio-osmosdr ${gr_osmosdr_libs})
+SET_TARGET_PROPERTIES(gnuradio-osmosdr PROPERTIES DEFINE_SYMBOL "gnuradio_osmosdr_EXPORTS")
+GR_LIBRARY_FOO(gnuradio-osmosdr)
diff --git a/lib/config.h.in b/lib/config.h.in
index 42e72f1..4f5e4c3 100644
--- a/lib/config.h.in
+++ b/lib/config.h.in
@@ -19,6 +19,7 @@
 #cmakedefine ENABLE_SOAPY
 #cmakedefine ENABLE_REDPITAYA
 #cmakedefine ENABLE_FREESRP
+#cmakedefine ENABLE_XTRX
 
 //provide NAN define for MSVC older than VC12
 #if defined(_MSC_VER) && (_MSC_VER < 1800)
diff --git a/lib/sink_impl.cc b/lib/sink_impl.cc
index 877b31f..34f9670 100644
--- a/lib/sink_impl.cc
+++ b/lib/sink_impl.cc
@@ -48,6 +48,9 @@
 #ifdef ENABLE_FREESRP
 #include <freesrp_sink_c.h>
 #endif
+#ifdef ENABLE_XTRX
+#include "xtrx_sink_c.h"
+#endif
 #ifdef ENABLE_FILE
 #include "file_sink_c.h"
 #endif
@@ -99,6 +102,9 @@ sink_impl::sink_impl( const std::string &args )
 #ifdef ENABLE_FREESRP
   dev_types.push_back("freesrp");
 #endif
+#ifdef ENABLE_XTRX
+  dev_types.push_back("xtrx");
+#endif
 #ifdef ENABLE_FILE
   dev_types.push_back("file");
 #endif
@@ -147,6 +153,9 @@ sink_impl::sink_impl( const std::string &args )
     BOOST_FOREACH( std::string dev, freesrp_sink_c::get_devices() )
       dev_list.push_back( dev );
 #endif
+#ifdef ENABLE_XTRX
+	BOOST_FOREACH( std::string dev, xtrx_sink_c::get_devices() )
+#endif
 #ifdef ENABLE_FILE
     BOOST_FOREACH( std::string dev, file_sink_c::get_devices() )
       dev_list.push_back( dev );
@@ -209,6 +218,12 @@ sink_impl::sink_impl( const std::string &args )
       block = sink; iface = sink.get();
     }
 #endif
+#ifdef ENABLE_XTRX
+    if ( dict.count("xtrx") ) {
+      xtrx_sink_c_sptr sink = make_xtrx_sink_c( arg );
+      block = sink; iface = sink.get();
+    }
+#endif
 #ifdef ENABLE_FILE
     if ( dict.count("file") ) {
       file_sink_c_sptr sink = make_file_sink_c( arg );
diff --git a/lib/sink_impl.cc.orig b/lib/sink_impl.cc.orig
new file mode 100644
index 0000000..877b31f
--- /dev/null
+++ b/lib/sink_impl.cc.orig
@@ -0,0 +1,680 @@
+/* -*- c++ -*- */
+/*
+ * 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 <gnuradio/io_signature.h>
+#include <gnuradio/constants.h>
+
+#ifdef ENABLE_UHD
+#include "uhd_sink_c.h"
+#endif
+#ifdef ENABLE_HACKRF
+#include "hackrf_sink_c.h"
+#endif
+#ifdef ENABLE_BLADERF
+#include "bladerf_sink_c.h"
+#endif
+#ifdef ENABLE_SOAPY
+#include "soapy_sink_c.h"
+#endif
+#ifdef ENABLE_REDPITAYA
+#include "redpitaya_sink_c.h"
+#endif
+#ifdef ENABLE_FREESRP
+#include <freesrp_sink_c.h>
+#endif
+#ifdef ENABLE_FILE
+#include "file_sink_c.h"
+#endif
+
+#include "arg_helpers.h"
+#include "sink_impl.h"
+
+/*
+ * Create a new instance of sink_impl and return
+ * a boost shared_ptr.  This is effectively the public constructor.
+ */
+osmosdr::sink::sptr
+osmosdr::sink::make( const std::string &args )
+{
+  return gnuradio::get_initial_sptr( new sink_impl(args) );
+}
+
+/*
+ * The private constructor
+ */
+sink_impl::sink_impl( const std::string &args )
+  : gr::hier_block2 ("sink_impl",
+        args_to_io_signature(args),
+        gr::io_signature::make(0, 0, 0)),
+    _sample_rate(NAN)
+{
+  size_t channel = 0;
+  bool device_specified = false;
+
+  std::vector< std::string > arg_list = args_to_vector(args);
+
+  std::vector< std::string > dev_types;
+
+#ifdef ENABLE_UHD
+  dev_types.push_back("uhd");
+#endif
+#ifdef ENABLE_HACKRF
+  dev_types.push_back("hackrf");
+#endif
+#ifdef ENABLE_BLADERF
+  dev_types.push_back("bladerf");
+#endif
+#ifdef ENABLE_SOAPY
+  dev_types.push_back("soapy");
+#endif
+#ifdef ENABLE_REDPITAYA
+  dev_types.push_back("redpitaya");
+#endif
+#ifdef ENABLE_FREESRP
+  dev_types.push_back("freesrp");
+#endif
+#ifdef ENABLE_FILE
+  dev_types.push_back("file");
+#endif
+
+  std::cerr << "gr-osmosdr "
+            << GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") "
+            << "gnuradio " << gr::version() << std::endl;
+  std::cerr << "built-in sink types: ";
+  BOOST_FOREACH(std::string dev_type, dev_types)
+    std::cerr << dev_type << " ";
+  std::cerr << std::endl;
+
+  BOOST_FOREACH(std::string arg, arg_list) {
+    dict_t dict = params_to_dict(arg);
+    BOOST_FOREACH(std::string dev_type, dev_types) {
+      if ( dict.count( dev_type ) ) {
+        device_specified = true;
+        break;
+      }
+    }
+  }
+
+  if ( ! device_specified ) {
+    std::vector< std::string > dev_list;
+#ifdef ENABLE_UHD
+    BOOST_FOREACH( std::string dev, uhd_sink_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_BLADERF
+    BOOST_FOREACH( std::string dev, bladerf_sink_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_HACKRF
+    BOOST_FOREACH( std::string dev, hackrf_sink_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_SOAPY
+    BOOST_FOREACH( std::string dev, soapy_sink_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_REDPITAYA
+    BOOST_FOREACH( std::string dev, redpitaya_sink_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_FREESRP
+    BOOST_FOREACH( std::string dev, freesrp_sink_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_FILE
+    BOOST_FOREACH( std::string dev, file_sink_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+
+//    std::cerr << std::endl;
+//    BOOST_FOREACH( std::string dev, dev_list )
+//      std::cerr << "'" << dev << "'" << std::endl;
+
+    if ( dev_list.size() )
+      arg_list.push_back( dev_list.front() );
+    else
+      throw std::runtime_error("No supported devices found (check the connection and/or udev rules).");
+  }
+
+  BOOST_FOREACH(std::string arg, arg_list) {
+
+    dict_t dict = params_to_dict(arg);
+
+//    std::cerr << std::endl;
+//    BOOST_FOREACH( dict_t::value_type &entry, dict )
+//      std::cerr << "'" << entry.first << "' = '" << entry.second << "'" << std::endl;
+
+    sink_iface *iface = NULL;
+    gr::basic_block_sptr block;
+
+#ifdef ENABLE_UHD
+    if ( dict.count("uhd") ) {
+      uhd_sink_c_sptr sink = make_uhd_sink_c( arg );
+      block = sink; iface = sink.get();
+    }
+#endif
+#ifdef ENABLE_HACKRF
+    if ( dict.count("hackrf") ) {
+      hackrf_sink_c_sptr sink = make_hackrf_sink_c( arg );
+      block = sink; iface = sink.get();
+    }
+#endif
+#ifdef ENABLE_BLADERF
+    if ( dict.count("bladerf") ) {
+      bladerf_sink_c_sptr sink = make_bladerf_sink_c( arg );
+      block = sink; iface = sink.get();
+    }
+#endif
+#ifdef ENABLE_SOAPY
+    if ( dict.count("soapy") ) {
+      soapy_sink_c_sptr sink = make_soapy_sink_c( arg );
+      block = sink; iface = sink.get();
+    }
+#endif
+#ifdef ENABLE_REDPITAYA
+    if ( dict.count("redpitaya") ) {
+      redpitaya_sink_c_sptr sink = make_redpitaya_sink_c( arg );
+      block = sink; iface = sink.get();
+    }
+#endif
+#ifdef ENABLE_FREESRP
+    if ( dict.count("freesrp") ) {
+      freesrp_sink_c_sptr sink = make_freesrp_sink_c( arg );
+      block = sink; iface = sink.get();
+    }
+#endif
+#ifdef ENABLE_FILE
+    if ( dict.count("file") ) {
+      file_sink_c_sptr sink = make_file_sink_c( arg );
+      block = sink; iface = sink.get();
+    }
+#endif
+
+    if ( iface != NULL && long(block.get()) != 0 ) {
+      _devs.push_back( iface );
+
+      for (size_t i = 0; i < iface->get_num_channels(); i++) {
+        connect(self(), channel++, block, i);
+      }
+    } else if ( (iface != NULL) || (long(block.get()) != 0) )
+      throw std::runtime_error("Either iface or block are NULL.");
+
+  }
+
+  if (!_devs.size())
+    throw std::runtime_error("No devices specified via device arguments.");
+}
+
+size_t sink_impl::get_num_channels()
+{
+  size_t channels = 0;
+
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    channels += dev->get_num_channels();
+
+  return channels;
+}
+
+#define NO_DEVICES_MSG  "FATAL: No device(s) available to work with."
+
+osmosdr::meta_range_t sink_impl::get_sample_rates()
+{
+  if ( ! _devs.empty() )
+    return _devs[0]->get_sample_rates(); // assume same devices used in the group
+#if 0
+  else
+    throw std::runtime_error(NO_DEVICES_MSG);
+#endif
+  return osmosdr::meta_range_t();
+}
+
+double sink_impl::set_sample_rate(double rate)
+{
+  double sample_rate = 0;
+
+  if (_sample_rate != rate) {
+#if 0
+    if (_devs.empty())
+      throw std::runtime_error(NO_DEVICES_MSG);
+#endif
+    BOOST_FOREACH( sink_iface *dev, _devs )
+      sample_rate = dev->set_sample_rate(rate);
+
+    _sample_rate = sample_rate;
+  }
+
+  return sample_rate;
+}
+
+double sink_impl::get_sample_rate()
+{
+  double sample_rate = 0;
+
+  if (!_devs.empty())
+    sample_rate = _devs[0]->get_sample_rate(); // assume same devices used in the group
+#if 0
+  else
+    throw std::runtime_error(NO_DEVICES_MSG);
+#endif
+  return sample_rate;
+}
+
+osmosdr::freq_range_t sink_impl::get_freq_range( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_freq_range( dev_chan );
+
+  return osmosdr::freq_range_t();
+}
+
+double sink_impl::set_center_freq( double freq, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _center_freq[ chan ] != freq ) {
+          _center_freq[ chan ] = freq;
+          return dev->set_center_freq( freq, dev_chan );
+        } else { return _center_freq[ chan ]; }
+      }
+
+  return 0;
+}
+
+double sink_impl::get_center_freq( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_center_freq( dev_chan );
+
+  return 0;
+}
+
+double sink_impl::set_freq_corr( double ppm, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _freq_corr[ chan ] != ppm ) {
+          _freq_corr[ chan ] = ppm;
+          return dev->set_freq_corr( ppm, dev_chan );
+        } else { return _freq_corr[ chan ]; }
+      }
+
+  return 0;
+}
+
+double sink_impl::get_freq_corr( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_freq_corr( dev_chan );
+
+  return 0;
+}
+
+std::vector<std::string> sink_impl::get_gain_names( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_gain_names( dev_chan );
+
+  return std::vector< std::string >();
+}
+
+osmosdr::gain_range_t sink_impl::get_gain_range( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_gain_range( dev_chan );
+
+  return osmosdr::gain_range_t();
+}
+
+osmosdr::gain_range_t sink_impl::get_gain_range( const std::string & name, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_gain_range( name, dev_chan );
+
+  return osmosdr::gain_range_t();
+}
+
+bool sink_impl::set_gain_mode( bool automatic, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _gain_mode[ chan ] != automatic ) {
+          _gain_mode[ chan ] = automatic;
+          bool mode = dev->set_gain_mode( automatic, dev_chan );
+          if (!automatic) // reapply gain value when switched to manual mode
+            dev->set_gain( _gain[ chan ], dev_chan );
+          return mode;
+        } else { return _gain_mode[ chan ]; }
+      }
+
+  return false;
+}
+
+bool sink_impl::get_gain_mode( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_gain_mode( dev_chan );
+
+  return false;
+}
+
+double sink_impl::set_gain( double gain, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _gain[ chan ] != gain ) {
+          _gain[ chan ] = gain;
+          return dev->set_gain( gain, dev_chan );
+        } else { return _gain[ chan ]; }
+      }
+
+  return 0;
+}
+
+double sink_impl::set_gain( double gain, const std::string & name, size_t chan)
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->set_gain( gain, name, dev_chan );
+
+  return 0;
+}
+
+double sink_impl::get_gain( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_gain( dev_chan );
+
+  return 0;
+}
+
+double sink_impl::get_gain( const std::string & name, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_gain( name, dev_chan );
+
+  return 0;
+}
+
+double sink_impl::set_if_gain( double gain, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _if_gain[ chan ] != gain ) {
+          _if_gain[ chan ] = gain;
+          return dev->set_if_gain( gain, dev_chan );
+        } else { return _if_gain[ chan ]; }
+      }
+
+  return 0;
+}
+
+double sink_impl::set_bb_gain( double gain, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _bb_gain[ chan ] != gain ) {
+          _bb_gain[ chan ] = gain;
+          return dev->set_bb_gain( gain, dev_chan );
+        } else { return _bb_gain[ chan ]; }
+      }
+
+  return 0;
+}
+
+std::vector< std::string > sink_impl::get_antennas( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_antennas( dev_chan );
+
+  return std::vector< std::string >();
+}
+
+std::string sink_impl::set_antenna( const std::string & antenna, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _antenna[ chan ] != antenna ) {
+          _antenna[ chan ] = antenna;
+          return dev->set_antenna( antenna, dev_chan );
+        } else { return _antenna[ chan ]; }
+      }
+
+  return "";
+}
+
+std::string sink_impl::get_antenna( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_antenna( dev_chan );
+
+  return "";
+}
+
+void sink_impl::set_dc_offset( const std::complex<double> &offset, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        dev->set_dc_offset( offset, dev_chan );
+}
+
+void sink_impl::set_iq_balance( const std::complex<double> &balance, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        dev->set_iq_balance( balance, dev_chan );
+}
+
+double sink_impl::set_bandwidth( double bandwidth, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _bandwidth[ chan ] != bandwidth || 0.0f == bandwidth ) {
+          _bandwidth[ chan ] = bandwidth;
+          return dev->set_bandwidth( bandwidth, dev_chan );
+        } else { return _bandwidth[ chan ]; }
+      }
+
+  return 0;
+}
+
+double sink_impl::get_bandwidth( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_bandwidth( dev_chan );
+
+  return 0;
+}
+
+osmosdr::freq_range_t sink_impl::get_bandwidth_range( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( sink_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_bandwidth_range( dev_chan );
+
+  return osmosdr::freq_range_t();
+}
+
+void sink_impl::set_time_source(const std::string &source, const size_t mboard)
+{
+  if (mboard != osmosdr::ALL_MBOARDS){
+      _devs.at(mboard)->set_time_source( source );
+      return;
+  }
+
+  for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */
+      _devs.at(m)->set_time_source( source, osmosdr::ALL_MBOARDS );
+  }
+}
+
+std::string sink_impl::get_time_source(const size_t mboard)
+{
+  return _devs.at(mboard)->get_time_source( mboard );
+}
+
+std::vector<std::string> sink_impl::get_time_sources(const size_t mboard)
+{
+  return _devs.at(mboard)->get_time_sources( mboard );
+}
+
+void sink_impl::set_clock_source(const std::string &source, const size_t mboard)
+{
+  if (mboard != osmosdr::ALL_MBOARDS){
+      _devs.at(mboard)->set_clock_source( source );
+      return;
+  }
+
+  for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */
+      _devs.at(m)->set_clock_source( source, osmosdr::ALL_MBOARDS );
+  }
+}
+
+std::string sink_impl::get_clock_source(const size_t mboard)
+{
+  return _devs.at(mboard)->get_clock_source( mboard );
+}
+
+std::vector<std::string> sink_impl::get_clock_sources(const size_t mboard)
+{
+  return _devs.at(mboard)->get_clock_sources( mboard );
+}
+
+double sink_impl::get_clock_rate(size_t mboard)
+{
+  return _devs.at(mboard)->get_clock_rate( mboard );
+}
+
+void sink_impl::set_clock_rate(double rate, size_t mboard)
+{
+  if (mboard != osmosdr::ALL_MBOARDS){
+      _devs.at(mboard)->set_clock_rate( rate );
+      return;
+  }
+
+  for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */
+      _devs.at(m)->set_clock_rate( rate, osmosdr::ALL_MBOARDS );
+  }
+}
+
+osmosdr::time_spec_t sink_impl::get_time_now(size_t mboard)
+{
+  return _devs.at(mboard)->get_time_now( mboard );
+}
+
+osmosdr::time_spec_t sink_impl::get_time_last_pps(size_t mboard)
+{
+  return _devs.at(mboard)->get_time_last_pps( mboard );
+}
+
+void sink_impl::set_time_now(const osmosdr::time_spec_t &time_spec, size_t mboard)
+{
+  if (mboard != osmosdr::ALL_MBOARDS){
+      _devs.at(mboard)->set_time_now( time_spec );
+      return;
+  }
+
+  for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */
+      _devs.at(m)->set_time_now( time_spec, osmosdr::ALL_MBOARDS );
+  }
+}
+
+void sink_impl::set_time_next_pps(const osmosdr::time_spec_t &time_spec)
+{
+  BOOST_FOREACH( sink_iface *dev, _devs )
+  {
+    dev->set_time_next_pps( time_spec );
+  }
+}
+
+void sink_impl::set_time_unknown_pps(const osmosdr::time_spec_t &time_spec)
+{
+  BOOST_FOREACH( sink_iface *dev, _devs )
+  {
+    dev->set_time_unknown_pps( time_spec );
+  }
+}
diff --git a/lib/source_impl.cc b/lib/source_impl.cc
index a8a3cec..04dcf99 100644
--- a/lib/source_impl.cc
+++ b/lib/source_impl.cc
@@ -91,6 +91,9 @@
 #ifdef ENABLE_FREESRP
 #include <freesrp_source_c.h>
 #endif
+#ifdef ENABLE_XTRX
+#include <xtrx_source_c.h>
+#endif
 
 #include "arg_helpers.h"
 #include "source_impl.h"
@@ -165,6 +168,9 @@ source_impl::source_impl( const std::string &args )
 #endif
 #ifdef ENABLE_FREESRP
   dev_types.push_back("freesrp");
+#endif
+#ifdef ENABLE_XTRX
+  dev_types.push_back("xtrx");
 #endif
   std::cerr << "gr-osmosdr "
             << GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") "
@@ -245,6 +251,10 @@ source_impl::source_impl( const std::string &args )
     BOOST_FOREACH( std::string dev, freesrp_source_c::get_devices() )
       dev_list.push_back( dev );
 #endif
+#ifdef ENABLE_XTRX
+    BOOST_FOREACH( std::string dev, xtrx_source_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
 
 //    std::cerr << std::endl;
 //    BOOST_FOREACH( std::string dev, dev_list )
@@ -376,6 +386,13 @@ source_impl::source_impl( const std::string &args )
     }
 #endif
 
+#ifdef ENABLE_XTRX
+    if ( dict.count("xtrx") ) {
+      xtrx_source_c_sptr src = make_xtrx_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
     if ( iface != NULL && long(block.get()) != 0 ) {
       _devs.push_back( iface );
 
diff --git a/lib/source_impl.cc.orig b/lib/source_impl.cc.orig
new file mode 100644
index 0000000..a8a3cec
--- /dev/null
+++ b/lib/source_impl.cc.orig
@@ -0,0 +1,951 @@
+/* -*- 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.
+ */
+
+/*
+ * 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 <gnuradio/io_signature.h>
+#include <gnuradio/blocks/null_source.h>
+#include <gnuradio/blocks/throttle.h>
+#include <gnuradio/constants.h>
+
+#ifdef ENABLE_OSMOSDR
+#include <osmosdr_src_c.h>
+#endif
+
+#ifdef ENABLE_FCD
+#include <fcd_source_c.h>
+#endif
+
+#ifdef ENABLE_FILE
+#include <file_source_c.h>
+#endif
+
+#ifdef ENABLE_RTL
+#include <rtl_source_c.h>
+#endif
+
+#ifdef ENABLE_RTL_TCP
+#include <rtl_tcp_source_c.h>
+#endif
+
+#ifdef ENABLE_UHD
+#include <uhd_source_c.h>
+#endif
+
+#ifdef ENABLE_MIRI
+#include <miri_source_c.h>
+#endif
+
+#ifdef ENABLE_SDRPLAY
+#include <sdrplay_source_c.h>
+#endif
+
+#ifdef ENABLE_HACKRF
+#include <hackrf_source_c.h>
+#endif
+
+#ifdef ENABLE_BLADERF
+#include <bladerf_source_c.h>
+#endif
+
+#ifdef ENABLE_RFSPACE
+#include <rfspace_source_c.h>
+#endif
+
+#ifdef ENABLE_AIRSPY
+#include <airspy_source_c.h>
+#endif
+
+#ifdef ENABLE_SOAPY
+#include <soapy_source_c.h>
+#endif
+
+#ifdef ENABLE_REDPITAYA
+#include <redpitaya_source_c.h>
+#endif
+
+#ifdef ENABLE_FREESRP
+#include <freesrp_source_c.h>
+#endif
+
+#include "arg_helpers.h"
+#include "source_impl.h"
+
+/*
+ * Create a new instance of source_impl and return
+ * a boost shared_ptr.  This is effectively the public constructor.
+ */
+osmosdr::source::sptr
+osmosdr::source::make( const std::string &args )
+{
+  return gnuradio::get_initial_sptr( new source_impl(args) );
+}
+
+/*
+ * The private constructor
+ */
+source_impl::source_impl( const std::string &args )
+  : gr::hier_block2 ("source_impl",
+        gr::io_signature::make(0, 0, 0),
+        args_to_io_signature(args)),
+    _sample_rate(NAN)
+{
+  size_t channel = 0;
+  bool device_specified = false;
+
+  std::vector< std::string > arg_list = args_to_vector(args);
+
+  std::vector< std::string > dev_types;
+
+#ifdef ENABLE_FILE
+  dev_types.push_back("file");
+#endif
+#ifdef ENABLE_OSMOSDR
+  dev_types.push_back("osmosdr");
+#endif
+#ifdef ENABLE_FCD
+  dev_types.push_back("fcd");
+#endif
+#ifdef ENABLE_RTL
+  dev_types.push_back("rtl");
+#endif
+#ifdef ENABLE_RTL_TCP
+  dev_types.push_back("rtl_tcp");
+#endif
+#ifdef ENABLE_UHD
+  dev_types.push_back("uhd");
+#endif
+#ifdef ENABLE_MIRI
+  dev_types.push_back("miri");
+#endif
+#ifdef ENABLE_SDRPLAY
+  dev_types.push_back("sdrplay");
+#endif
+#ifdef ENABLE_HACKRF
+  dev_types.push_back("hackrf");
+#endif
+#ifdef ENABLE_BLADERF
+  dev_types.push_back("bladerf");
+#endif
+#ifdef ENABLE_RFSPACE
+  dev_types.push_back("rfspace");
+#endif
+#ifdef ENABLE_AIRSPY
+  dev_types.push_back("airspy");
+#endif
+#ifdef ENABLE_SOAPY
+  dev_types.push_back("soapy");
+#endif
+#ifdef ENABLE_REDPITAYA
+  dev_types.push_back("redpitaya");
+#endif
+#ifdef ENABLE_FREESRP
+  dev_types.push_back("freesrp");
+#endif
+  std::cerr << "gr-osmosdr "
+            << GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") "
+            << "gnuradio " << gr::version() << std::endl;
+  std::cerr << "built-in source types: ";
+  BOOST_FOREACH(std::string dev_type, dev_types)
+    std::cerr << dev_type << " ";
+  std::cerr << std::endl;
+
+#ifdef ENABLE_RFSPACE
+  dev_types.push_back("sdr-iq"); /* additional aliases for rfspace backend */
+  dev_types.push_back("sdr-ip");
+  dev_types.push_back("netsdr");
+  dev_types.push_back("cloudiq");
+#endif
+
+  BOOST_FOREACH(std::string arg, arg_list) {
+    dict_t dict = params_to_dict(arg);
+    BOOST_FOREACH(std::string dev_type, dev_types) {
+      if ( dict.count( dev_type ) ) {
+        device_specified = true;
+        break;
+      }
+    }
+  }
+
+  if ( ! device_specified ) {
+    std::vector< std::string > dev_list;
+#ifdef ENABLE_OSMOSDR
+    BOOST_FOREACH( std::string dev, osmosdr_src_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_FCD
+    BOOST_FOREACH( std::string dev, fcd_source_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_RTL
+    BOOST_FOREACH( std::string dev, rtl_source_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_UHD
+    BOOST_FOREACH( std::string dev, uhd_source_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_MIRI
+    BOOST_FOREACH( std::string dev, miri_source_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_SDRPLAY
+    BOOST_FOREACH( std::string dev, sdrplay_source_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_BLADERF
+    BOOST_FOREACH( std::string dev, bladerf_source_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_RFSPACE
+    BOOST_FOREACH( std::string dev, rfspace_source_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_HACKRF
+    BOOST_FOREACH( std::string dev, hackrf_source_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_AIRSPY
+    BOOST_FOREACH( std::string dev, airspy_source_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_SOAPY
+    BOOST_FOREACH( std::string dev, soapy_source_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_REDPITAYA
+    BOOST_FOREACH( std::string dev, redpitaya_source_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+#ifdef ENABLE_FREESRP
+    BOOST_FOREACH( std::string dev, freesrp_source_c::get_devices() )
+      dev_list.push_back( dev );
+#endif
+
+//    std::cerr << std::endl;
+//    BOOST_FOREACH( std::string dev, dev_list )
+//      std::cerr << "'" << dev << "'" << std::endl;
+
+    if ( dev_list.size() )
+      arg_list.push_back( dev_list.front() );
+    else
+      throw std::runtime_error("No supported devices found (check the connection and/or udev rules).");
+  }
+
+  BOOST_FOREACH(std::string arg, arg_list) {
+
+    dict_t dict = params_to_dict(arg);
+
+//    std::cerr << std::endl;
+//    BOOST_FOREACH( dict_t::value_type &entry, dict )
+//      std::cerr << "'" << entry.first << "' = '" << entry.second << "'" << std::endl;
+
+    source_iface *iface = NULL;
+    gr::basic_block_sptr block;
+
+#ifdef ENABLE_OSMOSDR
+    if ( dict.count("osmosdr") ) {
+      osmosdr_src_c_sptr src = osmosdr_make_src_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+#ifdef ENABLE_FCD
+    if ( dict.count("fcd") ) {
+      fcd_source_c_sptr src = make_fcd_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+#ifdef ENABLE_FILE
+    if ( dict.count("file") ) {
+      file_source_c_sptr src = make_file_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+#ifdef ENABLE_RTL
+    if ( dict.count("rtl") ) {
+      rtl_source_c_sptr src = make_rtl_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+#ifdef ENABLE_RTL_TCP
+    if ( dict.count("rtl_tcp") ) {
+      rtl_tcp_source_c_sptr src = make_rtl_tcp_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+#ifdef ENABLE_UHD
+    if ( dict.count("uhd") ) {
+      uhd_source_c_sptr src = make_uhd_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+#ifdef ENABLE_MIRI
+    if ( dict.count("miri") ) {
+      miri_source_c_sptr src = make_miri_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+#ifdef ENABLE_SDRPLAY
+    if ( dict.count("sdrplay") ) {
+      sdrplay_source_c_sptr src = make_sdrplay_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+#ifdef ENABLE_HACKRF
+    if ( dict.count("hackrf") ) {
+      hackrf_source_c_sptr src = make_hackrf_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+#ifdef ENABLE_BLADERF
+    if ( dict.count("bladerf") ) {
+      bladerf_source_c_sptr src = make_bladerf_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+#ifdef ENABLE_RFSPACE
+    if ( dict.count("rfspace") ||
+         dict.count("sdr-iq") ||
+         dict.count("sdr-ip") ||
+         dict.count("netsdr") ||
+         dict.count("cloudiq") ) {
+      rfspace_source_c_sptr src = make_rfspace_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+#ifdef ENABLE_AIRSPY
+    if ( dict.count("airspy") ) {
+      airspy_source_c_sptr src = make_airspy_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+#ifdef ENABLE_SOAPY
+    if ( dict.count("soapy") ) {
+      soapy_source_c_sptr src = make_soapy_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+#ifdef ENABLE_REDPITAYA
+    if ( dict.count("redpitaya") ) {
+      redpitaya_source_c_sptr src = make_redpitaya_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+#ifdef ENABLE_FREESRP
+    if ( dict.count("freesrp") ) {
+      freesrp_source_c_sptr src = make_freesrp_source_c( arg );
+      block = src; iface = src.get();
+    }
+#endif
+
+    if ( iface != NULL && long(block.get()) != 0 ) {
+      _devs.push_back( iface );
+
+      for (size_t i = 0; i < iface->get_num_channels(); i++) {
+#ifdef HAVE_IQBALANCE
+        gr::iqbalance::optimize_c::sptr iq_opt = gr::iqbalance::optimize_c::make( 0 );
+        gr::iqbalance::fix_cc::sptr     iq_fix = gr::iqbalance::fix_cc::make();
+
+        connect(block, i, iq_fix, 0);
+        connect(iq_fix, 0, self(), channel++);
+
+        connect(block, i, iq_opt, 0);
+        msg_connect(iq_opt, "iqbal_corr", iq_fix, "iqbal_corr");
+
+        _iq_opt.push_back( iq_opt.get() );
+        _iq_fix.push_back( iq_fix.get() );
+#else
+        connect(block, i, self(), channel++);
+#endif
+      }
+    } else if ( (iface != NULL) || (long(block.get()) != 0) )
+      throw std::runtime_error("Either iface or block are NULL.");
+
+  }
+
+  if (!_devs.size())
+    throw std::runtime_error("No devices specified via device arguments.");
+}
+
+size_t source_impl::get_num_channels()
+{
+  size_t channels = 0;
+
+  BOOST_FOREACH( source_iface *dev, _devs )
+    channels += dev->get_num_channels();
+
+  return channels;
+}
+
+bool source_impl::seek( long seek_point, int whence, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->seek( seek_point, whence, dev_chan );
+
+  return false;
+}
+
+#define NO_DEVICES_MSG  "FATAL: No device(s) available to work with."
+
+osmosdr::meta_range_t source_impl::get_sample_rates()
+{
+  if ( ! _devs.empty() )
+    return _devs[0]->get_sample_rates(); // assume same devices used in the group
+#if 0
+  else
+    throw std::runtime_error(NO_DEVICES_MSG);
+#endif
+  return osmosdr::meta_range_t();;
+}
+
+double source_impl::set_sample_rate(double rate)
+{
+  double sample_rate = 0;
+
+  if (_sample_rate != rate) {
+#if 0
+    if (_devs.empty())
+      throw std::runtime_error(NO_DEVICES_MSG);
+#endif
+    BOOST_FOREACH( source_iface *dev, _devs )
+      sample_rate = dev->set_sample_rate(rate);
+
+#ifdef HAVE_IQBALANCE
+    size_t channel = 0;
+    BOOST_FOREACH( source_iface *dev, _devs ) {
+      for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) {
+        if ( channel < _iq_opt.size() ) {
+          gr::iqbalance::optimize_c *opt = _iq_opt[channel];
+
+          if ( opt->period() > 0 ) { /* optimize is enabled */
+            opt->set_period( dev->get_sample_rate() / 5 );
+            opt->reset();
+          }
+        }
+
+        channel++;
+      }
+    }
+#endif
+
+    _sample_rate = sample_rate;
+  }
+
+  return sample_rate;
+}
+
+double source_impl::get_sample_rate()
+{
+  double sample_rate = 0;
+
+  if (!_devs.empty())
+    sample_rate = _devs[0]->get_sample_rate(); // assume same devices used in the group
+#if 0
+  else
+    throw std::runtime_error(NO_DEVICES_MSG);
+#endif
+  return sample_rate;
+}
+
+osmosdr::freq_range_t source_impl::get_freq_range( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_freq_range( dev_chan );
+
+  return osmosdr::freq_range_t();
+}
+
+double source_impl::set_center_freq( double freq, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _center_freq[ chan ] != freq ) {
+          _center_freq[ chan ] = freq;
+          return dev->set_center_freq( freq, dev_chan );
+        } else { return _center_freq[ chan ]; }
+      }
+
+  return 0;
+}
+
+double source_impl::get_center_freq( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_center_freq( dev_chan );
+
+  return 0;
+}
+
+double source_impl::set_freq_corr( double ppm, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _freq_corr[ chan ] != ppm ) {
+          _freq_corr[ chan ] = ppm;
+          return dev->set_freq_corr( ppm, dev_chan );
+        } else { return _freq_corr[ chan ]; }
+      }
+
+  return 0;
+}
+
+double source_impl::get_freq_corr( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_freq_corr( dev_chan );
+
+  return 0;
+}
+
+std::vector<std::string> source_impl::get_gain_names( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_gain_names( dev_chan );
+
+  return std::vector< std::string >();
+}
+
+osmosdr::gain_range_t source_impl::get_gain_range( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_gain_range( dev_chan );
+
+  return osmosdr::gain_range_t();
+}
+
+osmosdr::gain_range_t source_impl::get_gain_range( const std::string & name, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_gain_range( name, dev_chan );
+
+  return osmosdr::gain_range_t();
+}
+
+bool source_impl::set_gain_mode( bool automatic, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _gain_mode[ chan ] != automatic ) {
+          _gain_mode[ chan ] = automatic;
+          bool mode = dev->set_gain_mode( automatic, dev_chan );
+          if (!automatic) // reapply gain value when switched to manual mode
+            dev->set_gain( _gain[ chan ], dev_chan );
+          return mode;
+        } else { return _gain_mode[ chan ]; }
+      }
+
+  return false;
+}
+
+bool source_impl::get_gain_mode( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_gain_mode( dev_chan );
+
+  return false;
+}
+
+double source_impl::set_gain( double gain, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _gain[ chan ] != gain ) {
+          _gain[ chan ] = gain;
+          return dev->set_gain( gain, dev_chan );
+        } else { return _gain[ chan ]; }
+      }
+
+  return 0;
+}
+
+double source_impl::set_gain( double gain, const std::string & name, size_t chan)
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->set_gain( gain, name, dev_chan );
+
+  return 0;
+}
+
+double source_impl::get_gain( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_gain( dev_chan );
+
+  return 0;
+}
+
+double source_impl::get_gain( const std::string & name, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_gain( name, dev_chan );
+
+  return 0;
+}
+
+double source_impl::set_if_gain( double gain, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _if_gain[ chan ] != gain ) {
+          _if_gain[ chan ] = gain;
+          return dev->set_if_gain( gain, dev_chan );
+        } else { return _if_gain[ chan ]; }
+      }
+
+  return 0;
+}
+
+double source_impl::set_bb_gain( double gain, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _bb_gain[ chan ] != gain ) {
+          _bb_gain[ chan ] = gain;
+          return dev->set_bb_gain( gain, dev_chan );
+        } else { return _bb_gain[ chan ]; }
+      }
+
+  return 0;
+}
+
+std::vector< std::string > source_impl::get_antennas( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_antennas( dev_chan );
+
+  return std::vector< std::string >();
+}
+
+std::string source_impl::set_antenna( const std::string & antenna, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _antenna[ chan ] != antenna ) {
+          _antenna[ chan ] = antenna;
+          return dev->set_antenna( antenna, dev_chan );
+        } else { return _antenna[ chan ]; }
+      }
+
+  return "";
+}
+
+std::string source_impl::get_antenna( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_antenna( dev_chan );
+
+  return "";
+}
+
+void source_impl::set_dc_offset_mode( int mode, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        dev->set_dc_offset_mode( mode, dev_chan );
+}
+
+void source_impl::set_dc_offset( const std::complex<double> &offset, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        dev->set_dc_offset( offset, dev_chan );
+}
+
+void source_impl::set_iq_balance_mode( int mode, size_t chan )
+{
+  size_t channel = 0;
+#ifdef HAVE_IQBALANCE
+  BOOST_FOREACH( source_iface *dev, _devs ) {
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) {
+      if ( chan == channel++ ) {
+        if ( chan < _iq_opt.size() && chan < _iq_fix.size() ) {
+          gr::iqbalance::optimize_c *opt = _iq_opt[chan];
+          gr::iqbalance::fix_cc *fix = _iq_fix[chan];
+
+          if ( IQBalanceOff == mode  ) {
+            opt->set_period( 0 );
+            /* store current values in order to be able to restore them later */
+            _vals[ chan ] = std::pair< float, float >( fix->mag(), fix->phase() );
+            fix->set_mag( 0.0f );
+            fix->set_phase( 0.0f );
+          } else if ( IQBalanceManual == mode ) {
+            if ( opt->period() == 0 ) { /* transition from Off to Manual */
+              /* restore previous values */
+              std::pair< float, float > val = _vals[ chan ];
+              fix->set_mag( val.first );
+              fix->set_phase( val.second );
+            }
+            opt->set_period( 0 );
+          } else if ( IQBalanceAutomatic == mode ) {
+            opt->set_period( dev->get_sample_rate() / 5 );
+            opt->reset();
+          }
+        }
+      }
+    }
+  }
+#else
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->set_iq_balance_mode( mode, dev_chan );
+#endif
+}
+
+void source_impl::set_iq_balance( const std::complex<double> &balance, size_t chan )
+{
+  size_t channel = 0;
+#ifdef HAVE_IQBALANCE
+  BOOST_FOREACH( source_iface *dev, _devs ) {
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) {
+      if ( chan == channel++ ) {
+        if ( chan < _iq_opt.size() && chan < _iq_fix.size() ) {
+          gr::iqbalance::optimize_c *opt = _iq_opt[chan];
+          gr::iqbalance::fix_cc *fix = _iq_fix[chan];
+
+          if ( opt->period() == 0 ) { /* automatic optimization desabled */
+            fix->set_mag( balance.real() );
+            fix->set_phase( balance.imag() );
+          }
+        }
+      }
+    }
+  }
+#else
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->set_iq_balance( balance, dev_chan );
+#endif
+}
+
+double source_impl::set_bandwidth( double bandwidth, size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ ) {
+        if ( _bandwidth[ chan ] != bandwidth || 0.0f == bandwidth ) {
+          _bandwidth[ chan ] = bandwidth;
+          return dev->set_bandwidth( bandwidth, dev_chan );
+        } else { return _bandwidth[ chan ]; }
+      }
+
+  return 0;
+}
+
+double source_impl::get_bandwidth( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_bandwidth( dev_chan );
+
+  return 0;
+}
+
+osmosdr::freq_range_t source_impl::get_bandwidth_range( size_t chan )
+{
+  size_t channel = 0;
+  BOOST_FOREACH( source_iface *dev, _devs )
+    for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
+      if ( chan == channel++ )
+        return dev->get_bandwidth_range( dev_chan );
+
+  return osmosdr::freq_range_t();
+}
+
+void source_impl::set_time_source(const std::string &source, const size_t mboard)
+{
+  if (mboard != osmosdr::ALL_MBOARDS){
+      _devs.at(mboard)->set_time_source( source );
+      return;
+  }
+
+  for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */
+      _devs.at(m)->set_time_source( source, osmosdr::ALL_MBOARDS );
+  }
+}
+
+std::string source_impl::get_time_source(const size_t mboard)
+{
+  return _devs.at(mboard)->get_time_source( mboard );
+}
+
+std::vector<std::string> source_impl::get_time_sources(const size_t mboard)
+{
+  return _devs.at(mboard)->get_time_sources( mboard );
+}
+
+void source_impl::set_clock_source(const std::string &source, const size_t mboard)
+{
+  if (mboard != osmosdr::ALL_MBOARDS){
+      _devs.at(mboard)->set_clock_source( source );
+      return;
+  }
+
+  for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */
+      _devs.at(m)->set_clock_source( source, osmosdr::ALL_MBOARDS );
+  }
+}
+
+std::string source_impl::get_clock_source(const size_t mboard)
+{
+  return _devs.at(mboard)->get_clock_source( mboard );
+}
+
+std::vector<std::string> source_impl::get_clock_sources(const size_t mboard)
+{
+  return _devs.at(mboard)->get_clock_sources( mboard );
+}
+
+double source_impl::get_clock_rate(size_t mboard)
+{
+  return _devs.at(mboard)->get_clock_rate( mboard );
+}
+
+void source_impl::set_clock_rate(double rate, size_t mboard)
+{
+  if (mboard != osmosdr::ALL_MBOARDS){
+      _devs.at(mboard)->set_clock_rate( rate );
+      return;
+  }
+
+  for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */
+      _devs.at(m)->set_clock_rate( rate, osmosdr::ALL_MBOARDS );
+  }
+}
+
+osmosdr::time_spec_t source_impl::get_time_now(size_t mboard)
+{
+  return _devs.at(mboard)->get_time_now( mboard );
+}
+
+osmosdr::time_spec_t source_impl::get_time_last_pps(size_t mboard)
+{
+  return _devs.at(mboard)->get_time_last_pps( mboard );
+}
+
+void source_impl::set_time_now(const osmosdr::time_spec_t &time_spec, size_t mboard)
+{
+  if (mboard != osmosdr::ALL_MBOARDS){
+      _devs.at(mboard)->set_time_now( time_spec );
+      return;
+  }
+
+  for (size_t m = 0; m < _devs.size(); m++){ /* propagate ALL_MBOARDS */
+      _devs.at(m)->set_time_now( time_spec, osmosdr::ALL_MBOARDS );
+  }
+}
+
+void source_impl::set_time_next_pps(const osmosdr::time_spec_t &time_spec)
+{
+  BOOST_FOREACH( source_iface *dev, _devs )
+  {
+    dev->set_time_next_pps( time_spec );
+  }
+}
+
+void source_impl::set_time_unknown_pps(const osmosdr::time_spec_t &time_spec)
+{
+  BOOST_FOREACH( source_iface *dev, _devs )
+  {
+    dev->set_time_unknown_pps( time_spec );
+  }
+}
diff --git a/lib/xtrx/CMakeLists.txt b/lib/xtrx/CMakeLists.txt
new file mode 100644
index 0000000..5eb2947
--- /dev/null
+++ b/lib/xtrx/CMakeLists.txt
@@ -0,0 +1,39 @@
+# Copyright 2012 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# 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.
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+include_directories(
+    ${CMAKE_CURRENT_SOURCE_DIR}
+    ${LIBXTRX_INCLUDE_DIRS}
+)
+
+set(xtrx_srcs
+    ${CMAKE_CURRENT_SOURCE_DIR}/xtrx_obj.cc
+    ${CMAKE_CURRENT_SOURCE_DIR}/xtrx_source_c.cc
+    ${CMAKE_CURRENT_SOURCE_DIR}/xtrx_sink_c.cc
+)
+
+########################################################################
+# Append gnuradio-osmosdr library sources
+########################################################################
+list(APPEND gr_osmosdr_srcs ${xtrx_srcs})
+list(APPEND gr_osmosdr_libs ${LIBXTRX_LIBRARIES})
diff --git a/lib/xtrx/xtrx_obj.cc b/lib/xtrx/xtrx_obj.cc
new file mode 100644
index 0000000..369f9ec
--- /dev/null
+++ b/lib/xtrx/xtrx_obj.cc
@@ -0,0 +1,132 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2017 Sergey Kostanbaev <sergey.kostanbaev@fairwaves.co>
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "xtrx_obj.h"
+#include <iostream>
+#include <sstream>
+#include <boost/thread.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/thread/mutex.hpp>
+
+static std::map<std::string, xtrx_obj_sptr> s_objects;
+
+xtrx_obj_sptr xtrx_obj::get(const char* xtrx_dev,
+                            unsigned loglevel,
+                            bool lmsreset)
+{
+  std::map<std::string, xtrx_obj_sptr>::iterator i;
+  std::string name(xtrx_dev);
+
+  i = s_objects.find(name);
+  if (i == s_objects.end()) {
+    // No such object
+    s_objects[name].reset(new xtrx_obj(name, loglevel, lmsreset));
+  }
+
+  return s_objects[name];
+}
+
+void xtrx_obj::clear_all()
+{
+  s_objects.clear();
+}
+
+std::vector<std::string> xtrx_obj::get_devices()
+{
+  std::vector<std::string> devices;
+  // TODO
+  devices.push_back("/dev/xtrx0");
+  return devices;
+}
+
+
+xtrx_obj::xtrx_obj(const std::string &path, unsigned loglevel, bool lmsreset)
+  : _run(false)
+  , _sink_rate(0)
+  , _sink_master(0)
+  , _source_rate(0)
+  , _source_master(0)
+  , _flags(0)
+{
+  unsigned xtrxflag = (loglevel & XTRX_O_LOGLVL_MASK) | ((lmsreset) ? XTRX_O_RESET : 0);
+  std::cerr << "xtrx_obj::xtrx_obj = " << xtrxflag << std::endl;
+
+  int res = xtrx_open(path.c_str(), xtrxflag, &_obj);
+  if (res) {
+    std::stringstream message;
+    message << "Couldn't open "  ": Error: " << -res;
+
+    throw std::runtime_error( message.str() );
+  }
+}
+
+double xtrx_obj::set_smaplerate(double rate, double master, bool sink, unsigned flags)
+{
+  boost::mutex::scoped_lock lock(mtx);
+
+  if (sink) {
+    _sink_rate = rate;
+    _sink_master = master;
+  } else {
+    _source_rate = rate;
+    _source_master = master;
+  }
+  _flags |= flags;
+
+  if (_sink_master != 0 && _source_master != 0 && _sink_master != _source_master) {
+    std::stringstream message;
+    message << "Can't operate on diferrent master settings for XTRX sink and source"
+               " sink_master " << _sink_master << " source_master" << _source_master;
+
+    throw std::runtime_error( message.str() );
+  }
+
+  double rxrate, txrate;
+  double actmaster = (_source_master > 0) ? _source_master : _sink_master;
+  int res = xtrx_set_samplerate(_obj,
+                                actmaster,
+                                _source_rate,
+                                _sink_rate,
+                                _flags,
+                                NULL,
+                                &rxrate,
+                                &txrate);
+  if (res) {
+    std::cerr << "Unable to set samplerate, error=" << res << std::endl;
+    return 0;
+  }
+
+  if (sink)
+    return txrate;
+  return rxrate;
+}
+
+
+xtrx_obj::~xtrx_obj()
+{
+  if (_obj) {
+    if (_run) {
+      //boost::mutex::scoped_lock lock(mtx);
+      xtrx_stop(_obj, XTRX_TRX);
+    }
+    xtrx_close(_obj);
+  }
+}
+
+
diff --git a/lib/xtrx/xtrx_obj.h b/lib/xtrx/xtrx_obj.h
new file mode 100644
index 0000000..67f08f3
--- /dev/null
+++ b/lib/xtrx/xtrx_obj.h
@@ -0,0 +1,63 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2017 Sergey Kostanbaev <sergey.kostanbaev@fairwaves.co>
+ *
+ * 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 XTRX_OBJ_H
+#define XTRX_OBJ_H
+
+#include <boost/shared_ptr.hpp>
+#include <xtrx_api.h>
+#include <map>
+#include <vector>
+#include <boost/thread/mutex.hpp>
+
+class xtrx_obj;
+
+typedef boost::shared_ptr<xtrx_obj> xtrx_obj_sptr;
+
+class xtrx_obj
+{
+public:
+  xtrx_obj(const std::string& path, unsigned loglevel, bool lmsreset);
+  ~xtrx_obj();
+
+  static std::vector<std::string> get_devices();
+
+  static xtrx_obj_sptr get(const char* xtrx_dev,
+                           unsigned loglevel,
+                           bool lmsreset);
+  static void clear_all();
+
+  xtrx_dev* dev() { return _obj; }
+
+  double set_smaplerate(double rate, double master, bool sink, unsigned flags);
+
+  boost::mutex mtx;
+protected:
+  xtrx_dev* _obj;
+  bool      _run;
+
+  double    _sink_rate;
+  double    _sink_master;
+  double    _source_rate;
+  double    _source_master;
+
+  unsigned  _flags;
+};
+
+#endif // XTRX_OBJ_H
diff --git a/lib/xtrx/xtrx_sink_c.cc b/lib/xtrx/xtrx_sink_c.cc
new file mode 100644
index 0000000..79ecaff
--- /dev/null
+++ b/lib/xtrx/xtrx_sink_c.cc
@@ -0,0 +1,431 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2016,2017 Sergey Kostanbaev <sergey.kostanbaev@fairwaves.co>
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <map>
+
+#include <boost/assign.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/thread.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/thread/mutex.hpp>
+
+#include <gnuradio/io_signature.h>
+#include <gnuradio/blocks/deinterleave.h>
+#include <gnuradio/blocks/float_to_complex.h>
+
+#include "xtrx_sink_c.h"
+
+#include "arg_helpers.h"
+
+static const int max_burstsz = 4096;
+using namespace boost::assign;
+
+xtrx_sink_c_sptr make_xtrx_sink_c(const std::string &args)
+{
+  return gnuradio::get_initial_sptr(new xtrx_sink_c(args));
+}
+
+static size_t parse_nchan(const std::string &args)
+{
+  size_t nchan = 1;
+
+  dict_t dict = params_to_dict(args);
+
+  if (dict.count("nchan"))
+    nchan = boost::lexical_cast< size_t >( dict["nchan"] );
+
+  if (nchan < 1)
+    nchan = 1;
+  else if (nchan > 2)
+    nchan = 2;
+
+  return nchan;
+}
+
+xtrx_sink_c::xtrx_sink_c(const std::string &args) :
+  gr::sync_block("xtrx_sink_c",
+                 gr::io_signature::make(parse_nchan(args),
+                                        parse_nchan(args),
+                                        sizeof(gr_complex)),
+                 gr::io_signature::make(0, 0, 0)),
+  _sample_flags(0),
+  _rate(0),
+  _master(0),
+  _freq(0),
+  _corr(0),
+  _bandwidth(0),
+  _auto_gain(false),
+  _otw(XTRX_WF_16),
+  _mimo_mode(parse_nchan(args) > 1),
+  _ts(8192),
+  _swap_ab(false),
+  _swap_iq(false),
+  _tdd(false)
+{
+
+  dict_t dict = params_to_dict(args);
+
+  if (dict.count("master")) {
+    _master = boost::lexical_cast< double >( dict["master"]);
+  }
+
+  _channels = parse_nchan(args);
+
+  /*
+  if (dict.count("direct_samp"))
+    direct_samp = boost::lexical_cast< unsigned int >( dict["direct_samp"] );
+
+  if (dict.count("offset_tune"))
+    offset_tune = boost::lexical_cast< unsigned int >( dict["offset_tune"] );
+*/
+
+  std::cerr << args.c_str() << std::endl;
+
+  int loglevel = 4;
+  if (dict.count("loglevel")) {
+    loglevel = boost::lexical_cast< int >( dict["loglevel"] );
+  }
+
+  bool lmsreset = 0;
+  if (dict.count("lmsreset")) {
+    lmsreset = boost::lexical_cast< bool >( dict["lmsreset"] );
+  }
+
+  if (dict.count("swap_ab")) {
+    _swap_ab = true;
+    std::cerr << "xtrx_sink_c: swap AB channels";
+  }
+
+  if (dict.count("swap_iq")) {
+    _swap_iq = true;
+    std::cerr << "xtrx_sink_c: swap IQ";
+  }
+
+  if (dict.count("sfl")) {
+    _sample_flags = boost::lexical_cast< unsigned >( dict["sfl"] );
+  }
+
+  if (dict.count("tdd")) {
+    _tdd = true;
+    std::cerr << "xtrx_sink_c: TDD mode";
+  }
+
+  _xtrx = xtrx_obj::get("/dev/xtrx0", loglevel, lmsreset);
+
+  if (dict.count("refclk")) {
+    xtrx_set_ref_clk(_xtrx->dev(), boost::lexical_cast< unsigned >( dict["refclk"] ), XTRX_CLKSRC_INT);
+  }
+
+  std::cerr << "xtrx_sink_c::xtrx_sink_c()" << std::endl;
+  set_alignment(32);
+  set_output_multiple(max_burstsz);
+}
+
+xtrx_sink_c::~xtrx_sink_c()
+{
+  std::cerr << "xtrx_sink_c::~xtrx_sink_c()" << std::endl;
+}
+
+std::string xtrx_sink_c::name()
+{
+  return "GrLibXTRX";
+}
+
+size_t xtrx_sink_c::get_num_channels( void )
+{
+  return input_signature()->max_streams();
+}
+
+osmosdr::meta_range_t xtrx_sink_c::get_sample_rates( void )
+{
+  osmosdr::meta_range_t range;
+  range += osmosdr::range_t( 1000000, 160000000, 1 );
+  return range;
+}
+
+double xtrx_sink_c::set_sample_rate( double rate )
+{
+  std::cerr << "Set sample rate " << rate << std::endl;
+  _rate = _xtrx->set_smaplerate(rate, _master, true, _sample_flags);
+  return get_sample_rate();
+}
+
+double xtrx_sink_c::get_sample_rate( void )
+{
+  return _rate;
+}
+
+osmosdr::freq_range_t xtrx_sink_c::get_freq_range( size_t chan )
+{
+  osmosdr::freq_range_t range;
+  range += osmosdr::range_t( double(0.03e9), double(3.8e9), 1); // as far as we know
+  return range;
+}
+
+double xtrx_sink_c::set_center_freq( double freq, size_t chan )
+{
+  boost::mutex::scoped_lock lock(_xtrx->mtx);
+
+  _freq = freq;
+  double corr_freq = (freq)*(1.0 + (_corr) * 0.000001);
+
+  std::cerr << "Set freq " << freq << std::endl;
+
+  int res = xtrx_tune(_xtrx->dev(), (_tdd) ? XTRX_TUNE_TX_AND_RX_TDD : XTRX_TUNE_TX_FDD, corr_freq, &_freq);
+  if (res) {
+    std::cerr << "Unable to deliver frequency " << corr_freq << std::endl;
+  }
+
+  return get_center_freq(chan);
+}
+
+double xtrx_sink_c::get_center_freq( size_t chan )
+{
+  return _freq;
+}
+
+double xtrx_sink_c::set_freq_corr( double ppm, size_t chan )
+{
+  _corr = ppm;
+
+  set_center_freq(_freq, chan);
+
+  return get_freq_corr( chan );
+}
+
+double xtrx_sink_c::get_freq_corr( size_t chan )
+{
+  return _corr;
+}
+
+
+static const std::vector<std::string> s_lna_list = boost::assign::list_of("TX");
+
+std::vector<std::string> xtrx_sink_c::get_gain_names( size_t chan )
+{
+  return s_lna_list;
+}
+
+osmosdr::gain_range_t xtrx_sink_c::get_gain_range( size_t chan )
+{
+  return get_gain_range("TX", chan);
+}
+
+osmosdr::gain_range_t xtrx_sink_c::get_gain_range( const std::string & name, size_t chan )
+{
+  osmosdr::gain_range_t range;
+  range += osmosdr::range_t( -31, 0, 1 );
+  return range;
+}
+
+bool xtrx_sink_c::set_gain_mode( bool automatic, size_t chan )
+{
+  _auto_gain = automatic;
+  return get_gain_mode(chan);
+}
+
+bool xtrx_sink_c::get_gain_mode( size_t chan )
+{
+  return _auto_gain;
+}
+
+double xtrx_sink_c::set_gain( double gain, size_t chan )
+{
+  return set_gain(gain, "TX", chan);
+}
+
+double xtrx_sink_c::set_gain( double igain, const std::string & name, size_t chan )
+{
+  boost::mutex::scoped_lock lock(_xtrx->mtx);
+
+  osmosdr::gain_range_t gains = xtrx_sink_c::get_gain_range( name, chan );
+  double gain = gains.clip(igain);
+  double actual_gain;
+
+  std::cerr << "Set TX gain: " << igain << std::endl;
+
+  int res = xtrx_set_gain(_xtrx->dev(), /*(chan == 0) ? XTRX_CH_A : XTRX_CH_B*/ XTRX_CH_AB,
+                          XTRX_TX_PAD_GAIN, gain, &actual_gain);
+  if (res) {
+    std::cerr << "Unable to set gain `" << name.c_str() << "`; err=" << res << std::endl;
+  }
+
+  _gain_tx = actual_gain;
+  return actual_gain;
+}
+
+double xtrx_sink_c::get_gain( size_t chan )
+{
+  return get_gain("TX");
+}
+
+double xtrx_sink_c::get_gain( const std::string & name, size_t chan )
+{
+  return _gain_tx;
+}
+
+double xtrx_sink_c::set_bandwidth( double bandwidth, size_t chan )
+{
+  boost::mutex::scoped_lock lock(_xtrx->mtx);
+  std::cerr << "Set bandwidth " << bandwidth << " chan " << chan << std::endl;
+
+  if (bandwidth <= 0.0) {
+    bandwidth = get_sample_rate() * 0.75;
+    if (bandwidth < 0.5e6) {
+      bandwidth = 0.5e6;
+    }
+  }
+
+  int res = xtrx_tune_tx_bandwidth(_xtrx->dev(), (chan == 0) ? XTRX_CH_A : XTRX_CH_B, bandwidth, &_bandwidth);
+  if (res) {
+    std::cerr << "Can't set bandwidth: " << res << std::endl;
+  }
+  return get_bandwidth(chan);
+}
+
+double xtrx_sink_c::get_bandwidth( size_t chan )
+{
+  return _bandwidth;
+}
+
+
+static const std::map<std::string, xtrx_antenna_t> s_ant_map = boost::assign::map_list_of
+    ("B1", XTRX_TX_L)
+    ("B2", XTRX_TX_W)
+    ;
+static const std::map<xtrx_antenna_t, std::string> s_ant_map_r = boost::assign::map_list_of
+    (XTRX_TX_L, "B1")
+    (XTRX_TX_W, "B2")
+    ;
+
+static xtrx_antenna_t get_ant_type(const std::string& name)
+{
+  std::map<std::string, xtrx_antenna_t>::const_iterator it;
+
+  it = s_ant_map.find(name);
+  if (it != s_ant_map.end()) {
+    return it->second;
+  }
+
+  return XTRX_RX_W;
+}
+
+static const std::vector<std::string> s_ant_list = boost::assign::list_of
+    ("B1")("B2")
+    ;
+
+
+std::vector< std::string > xtrx_sink_c::get_antennas( size_t chan )
+{
+  return s_ant_list;
+}
+
+std::string xtrx_sink_c::set_antenna( const std::string & antenna, size_t chan )
+{
+  boost::mutex::scoped_lock lock(_xtrx->mtx);
+  _ant = get_ant_type(antenna);
+
+  std::cerr << "Set antenna " << antenna << std::endl;
+
+  int res = xtrx_set_antenna(_xtrx->dev(), _ant);
+  if (res) {
+    std::cerr << "Can't set antenna: " << antenna << std::endl;
+  }
+  return get_antenna( chan );
+}
+
+std::string xtrx_sink_c::get_antenna( size_t chan )
+{
+  return s_ant_map_r.find(_ant)->second;
+}
+
+int xtrx_sink_c::work (int noutput_items,
+                       gr_vector_const_void_star &input_items,
+                       gr_vector_void_star &output_items)
+{
+  xtrx_send_ex_info_t nfo;
+  nfo.samples = noutput_items;
+  nfo.buffer_count = input_items.size();
+  nfo.buffers = &input_items[0];
+  nfo.flags = XTRX_TX_DONT_BUFFER;
+  nfo.ts = _ts;
+
+  int res = xtrx_send_sync_ex(_xtrx->dev(), &nfo);
+  if (res) {
+    std::cerr << "Err: " << res << std::endl;
+
+    std::stringstream message;
+    message << "xtrx_send_burst_sync error: " << -res;
+    throw std::runtime_error( message.str() );
+  }
+
+  _ts += noutput_items;
+  for (unsigned i = 0; i < input_items.size(); i++) {
+    consume(i, noutput_items);
+  }
+  return 0;
+}
+
+bool xtrx_sink_c::start()
+{
+  boost::mutex::scoped_lock lock(_xtrx->mtx);
+
+  xtrx_run_params_t params;
+  xtrx_run_params_init(&params);
+
+  params.dir = XTRX_TX;
+  if (!_mimo_mode)
+    params.tx.flags |= XTRX_RSP_SISO_MODE;
+
+  if (_swap_ab)
+    params.tx.flags |= XTRX_RSP_SWAP_AB;
+
+  if (_swap_iq)
+    params.tx.flags |= XTRX_RSP_SWAP_IQ;
+
+  params.tx.hfmt = XTRX_IQ_FLOAT32;
+  params.tx.wfmt = _otw;
+  params.tx.chs = XTRX_CH_AB;
+  params.tx.paketsize = 0;
+  params.rx_stream_start = 256*1024;
+
+  int res = xtrx_run_ex(_xtrx->dev(), &params);
+  if (res) {
+    std::cerr << "Got error: " << res << std::endl;
+  }
+
+  return res == 0;
+}
+
+bool xtrx_sink_c::stop()
+{
+  boost::mutex::scoped_lock lock(_xtrx->mtx);
+
+  //TODO:
+  std::cerr << "xtrx_sink_c::stop()" << std::endl;
+  int res = xtrx_stop(_xtrx->dev(), XTRX_TX);
+  if (res) {
+    std::cerr << "Got error: " << res << std::endl;
+  }
+
+  return res == 0;
+}
diff --git a/lib/xtrx/xtrx_sink_c.h b/lib/xtrx/xtrx_sink_c.h
new file mode 100644
index 0000000..a541b73
--- /dev/null
+++ b/lib/xtrx/xtrx_sink_c.h
@@ -0,0 +1,116 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2016 Sergey Kostanabev <sergey.kostanbaev@fairwaves.co>
+ *
+ * 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 XTRX_SINK_C_H
+#define XTRX_SINK_C_H
+
+#include <gnuradio/block.h>
+#include <gnuradio/sync_block.h>
+
+#include "sink_iface.h"
+#include "xtrx_obj.h"
+
+class xtrx_sink_c;
+
+typedef boost::shared_ptr< xtrx_sink_c > xtrx_sink_c_sptr;
+
+xtrx_sink_c_sptr make_xtrx_sink_c( const std::string & args = "" );
+
+class xtrx_sink_c :
+    public gr::sync_block,
+    public sink_iface
+{
+private:
+  friend xtrx_sink_c_sptr make_xtrx_sink_c(const std::string &args);
+
+  xtrx_sink_c(const std::string &args);
+
+public:
+  ~xtrx_sink_c();
+
+  std::string name();
+
+  static std::vector< std::string > get_devices( bool fake = false ) { return xtrx_obj::get_devices(); }
+
+  size_t get_num_channels( void );
+
+  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 );
+
+  double set_bandwidth( double bandwidth, size_t chan = 0 );
+  double get_bandwidth( size_t chan = 0 );
+
+  int work (int noutput_items,
+            gr_vector_const_void_star &input_items,
+            gr_vector_void_star &output_items);
+
+  bool start();
+  bool stop();
+
+private:
+  xtrx_obj_sptr _xtrx;
+
+  unsigned _sample_flags;
+  double _rate;
+  double _master;
+  double _freq;
+  double _corr;
+  double _bandwidth;
+  bool _auto_gain;
+
+  xtrx_wire_format_t _otw;
+  bool _mimo_mode;
+
+  int _gain_tx;
+
+  unsigned _channels;
+  xtrx_antenna_t _ant;
+
+  uint64_t _ts;
+
+  bool     _swap_ab;
+  bool     _swap_iq;
+
+  bool     _tdd;
+};
+
+#endif // xtrx_sink_c_H
+
diff --git a/lib/xtrx/xtrx_source_c.cc b/lib/xtrx/xtrx_source_c.cc
new file mode 100644
index 0000000..47bb39e
--- /dev/null
+++ b/lib/xtrx/xtrx_source_c.cc
@@ -0,0 +1,512 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2016,2017 Sergey Kostanbaev <sergey.kostanbaev@fairwaves.co>
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <map>
+
+#include <boost/assign.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/thread.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/thread/mutex.hpp>
+
+#include <gnuradio/io_signature.h>
+#include <gnuradio/blocks/deinterleave.h>
+#include <gnuradio/blocks/float_to_complex.h>
+
+#include "xtrx_source_c.h"
+
+#include "arg_helpers.h"
+
+using namespace boost::assign;
+
+
+xtrx_source_c_sptr make_xtrx_source_c(const std::string &args)
+{
+  return gnuradio::get_initial_sptr(new xtrx_source_c(args));
+}
+
+static size_t parse_nchan(const std::string &args)
+{
+  size_t nchan = 1;
+
+  dict_t dict = params_to_dict(args);
+
+  if (dict.count("nchan"))
+    nchan = boost::lexical_cast< size_t >( dict["nchan"] );
+
+  if (nchan < 1)
+    nchan = 1;
+  else if (nchan > 2)
+    nchan = 2;
+
+  return nchan;
+}
+
+xtrx_source_c::xtrx_source_c(const std::string &args) :
+  gr::sync_block("xtrx_source_c",
+                 gr::io_signature::make(0, 0, 0),
+                 gr::io_signature::make(parse_nchan(args),
+                                        parse_nchan(args),
+                                        sizeof(gr_complex))),
+  _sample_flags(0),
+  _rate(0),
+  _master(0),
+  _freq(0),
+  _corr(0),
+  _bandwidth(0),
+  _auto_gain(false),
+  _otw(XTRX_WF_16),
+  _mimo_mode(parse_nchan(args) > 1),
+  _swap_ab(false),
+  _swap_iq(false),
+  _loopback(false),
+  _tdd(false)
+{
+
+  dict_t dict = params_to_dict(args);
+
+  if (dict.count("otw_format")) {
+    const std::string& otw = dict["otw_format"];
+    if (otw == "sc16" || otw == "16") {
+      _otw = XTRX_WF_16;
+    } else if (otw == "sc12" || otw == "12") {
+      _otw = XTRX_WF_12;
+    } else if (otw == "sc8" || otw == "8") {
+      _otw = XTRX_WF_8;
+    } else {
+      throw std::runtime_error("Parameter `otw_format` should be {sc16,sc12,sc8}");
+    }
+  }
+
+  if (dict.count("master")) {
+    _master = boost::lexical_cast< double >( dict["master"]);
+  }
+
+  _channels = parse_nchan(args);
+
+  /*
+  if (dict.count("direct_samp"))
+    direct_samp = boost::lexical_cast< unsigned int >( dict["direct_samp"] );
+
+  if (dict.count("offset_tune"))
+    offset_tune = boost::lexical_cast< unsigned int >( dict["offset_tune"] );
+*/
+
+  std::cerr << args.c_str() << std::endl;
+
+  int loglevel = 4;
+  if (dict.count("loglevel")) {
+    loglevel = boost::lexical_cast< int >( dict["loglevel"] );
+  }
+
+  bool lmsreset = 0;
+  if (dict.count("lmsreset")) {
+    lmsreset = boost::lexical_cast< bool >( dict["lmsreset"] );
+  }
+
+  if (dict.count("swap_ab")) {
+    _swap_ab = true;
+    std::cerr << "xtrx_source_c: swap AB channels";
+  }
+
+  if (dict.count("swap_iq")) {
+    _swap_iq = true;
+    std::cerr << "xtrx_source_c: swap IQ";
+  }
+
+  if (dict.count("sfl")) {
+    _sample_flags = boost::lexical_cast< unsigned >( dict["sfl"] );
+  }
+
+  if (dict.count("loopback")) {
+    _loopback = true;
+    std::cerr << "xtrx_source_c: loopback";
+  }
+
+  if (dict.count("tdd")) {
+    _tdd = true;
+    std::cerr << "xtrx_source_c: TDD mode";
+  }
+
+  _xtrx = xtrx_obj::get("/dev/xtrx0", loglevel, lmsreset);
+
+  if (dict.count("refclk")) {
+    xtrx_set_ref_clk(_xtrx->dev(), boost::lexical_cast< unsigned >( dict["refclk"] ), XTRX_CLKSRC_INT);
+  }
+
+  std::cerr << "xtrx_source_c::xtrx_source_c()" << std::endl;
+  set_alignment(32);
+  if (_otw == XTRX_WF_16) {
+    if (_mimo_mode)
+      set_output_multiple(4096);
+    else
+      set_output_multiple(8192);
+  } else if (_otw == XTRX_WF_8) {
+    if (_mimo_mode)
+      set_output_multiple(8192);
+    else
+      set_output_multiple(16384);
+  }
+}
+
+xtrx_source_c::~xtrx_source_c()
+{
+  std::cerr << "xtrx_source_c::~xtrx_source_c()" << std::endl;
+}
+
+std::string xtrx_source_c::name()
+{
+  return "GrLibXTRX";
+}
+
+size_t xtrx_source_c::get_num_channels( void )
+{
+  return output_signature()->max_streams();
+}
+
+osmosdr::meta_range_t xtrx_source_c::get_sample_rates( void )
+{
+  osmosdr::meta_range_t range;
+  range += osmosdr::range_t( 200000, 160000000, 1 );
+  return range;
+}
+
+double xtrx_source_c::set_sample_rate( double rate )
+{
+  std::cerr << "Set sample rate " << rate << std::endl;
+  _rate = _xtrx->set_smaplerate(rate, _master, false, _sample_flags);
+  return get_sample_rate();
+}
+
+double xtrx_source_c::get_sample_rate( void )
+{
+  return _rate;
+}
+
+osmosdr::freq_range_t xtrx_source_c::get_freq_range( size_t chan )
+{
+  osmosdr::freq_range_t range;
+  range += osmosdr::range_t( double(0.03e9), double(3.8e9), 1); // as far as we know
+  return range;
+}
+
+double xtrx_source_c::set_center_freq( double freq, size_t chan )
+{
+  boost::mutex::scoped_lock lock(_xtrx->mtx);
+
+  _freq = freq;
+  double corr_freq = (freq)*(1.0 + (_corr) * 0.000001);
+
+  std::cerr << "Set freq " << freq << std::endl;
+
+  if (_tdd)
+    return get_center_freq(chan);
+
+  int res = xtrx_tune(_xtrx->dev(), XTRX_TUNE_RX_FDD, corr_freq, &_freq);
+  if (res) {
+    std::cerr << "Unable to deliver frequency " << corr_freq << std::endl;
+  }
+
+  return get_center_freq(chan);
+}
+
+double xtrx_source_c::get_center_freq( size_t chan )
+{
+  return _freq;
+}
+
+double xtrx_source_c::set_freq_corr( double ppm, size_t chan )
+{
+  _corr = ppm;
+
+  set_center_freq(_freq, chan);
+
+  return get_freq_corr( chan );
+}
+
+double xtrx_source_c::get_freq_corr( size_t chan )
+{
+  return _corr;
+}
+
+static const std::map<std::string, xtrx_gain_type_t> s_lna_map = boost::assign::map_list_of
+    ("LNA", XTRX_RX_LNA_GAIN)
+    ("TIA", XTRX_RX_TIA_GAIN)
+    ("PGA", XTRX_RX_PGA_GAIN)
+    ("LB", XTRX_RX_LB_GAIN)
+    ;
+
+static xtrx_gain_type_t get_gain_type(const std::string& name)
+{
+  std::map<std::string, xtrx_gain_type_t>::const_iterator it;
+
+  it = s_lna_map.find(name);
+  if (it != s_lna_map.end()) {
+    return it->second;
+  }
+
+  return XTRX_RX_LNA_GAIN;
+}
+
+static const std::vector<std::string> s_lna_list = boost::assign::list_of
+    ("LNA")("TIA")("PGA")("LB")
+    ;
+
+std::vector<std::string> xtrx_source_c::get_gain_names( size_t chan )
+{
+  return s_lna_list;
+}
+
+osmosdr::gain_range_t xtrx_source_c::get_gain_range( size_t chan )
+{
+  return get_gain_range("LNA", chan);
+}
+
+osmosdr::gain_range_t xtrx_source_c::get_gain_range( const std::string & name, size_t chan )
+{
+  osmosdr::gain_range_t range;
+
+  if (name == "LNA") {
+    range += osmosdr::range_t( 0, 24,  3 );
+    range += osmosdr::range_t( 25, 30, 1 );
+  } else if (name == "TIA") {
+    range += osmosdr::range_t( 0 );
+    range += osmosdr::range_t( 9 );
+    range += osmosdr::range_t( 12 );
+  } else if (name == "PGA") {
+    range += osmosdr::range_t( -12.5, 12.5, 1 );
+  } else if (name == "LB") {
+    range += osmosdr::range_t( -40, 0, 1 );
+  }
+
+  return range;
+}
+
+bool xtrx_source_c::set_gain_mode( bool automatic, size_t chan )
+{
+  _auto_gain = automatic;
+  return get_gain_mode(chan);
+}
+
+bool xtrx_source_c::get_gain_mode( size_t chan )
+{
+  return _auto_gain;
+}
+
+double xtrx_source_c::set_gain( double gain, size_t chan )
+{
+  return set_gain(gain, "LNA", chan);
+}
+
+double xtrx_source_c::set_gain( double igain, const std::string & name, size_t chan )
+{
+  boost::mutex::scoped_lock lock(_xtrx->mtx);
+
+  osmosdr::gain_range_t gains = xtrx_source_c::get_gain_range( name, chan );
+  double gain = gains.clip(igain);
+  double actual_gain;
+  xtrx_gain_type_t gt = get_gain_type(name);
+
+  std::cerr << "Set gain " << name << " (" << gt << "): " << igain << std::endl;
+
+  int res = xtrx_set_gain(_xtrx->dev(), /*(chan == 0) ? XTRX_CH_A : XTRX_CH_B*/ XTRX_CH_AB, gt, gain, &actual_gain);
+  if (res) {
+    std::cerr << "Unable to set gain `" << name.c_str() << "`; err=" << res << std::endl;
+  }
+
+  switch (gt) {
+  case XTRX_RX_LNA_GAIN: _gain_lna = actual_gain; break;
+  case XTRX_RX_TIA_GAIN: _gain_tia = actual_gain; break;
+  case XTRX_RX_PGA_GAIN: _gain_pga = actual_gain; break;
+  default: break;
+  }
+
+  return actual_gain;
+}
+
+double xtrx_source_c::get_gain( size_t chan )
+{
+  return get_gain("LNA");
+}
+
+double xtrx_source_c::get_gain( const std::string & name, size_t chan )
+{
+  xtrx_gain_type_t gt = get_gain_type(name);
+  switch (gt) {
+  case XTRX_RX_LNA_GAIN: return _gain_lna;
+  case XTRX_RX_TIA_GAIN: return _gain_tia;
+  case XTRX_RX_PGA_GAIN: return _gain_pga;
+  default: return 0;
+  }
+}
+
+double xtrx_source_c::set_if_gain(double gain, size_t chan)
+{
+  return set_gain(gain, "PGA", chan);
+}
+
+double xtrx_source_c::set_bandwidth( double bandwidth, size_t chan )
+{
+  boost::mutex::scoped_lock lock(_xtrx->mtx);
+  std::cerr << "Set bandwidth " << bandwidth << " chan " << chan << std::endl;
+
+  if (bandwidth <= 0.0) {
+    bandwidth = get_sample_rate() * 0.75;
+    if (bandwidth < 0.5e6) {
+      bandwidth = 0.5e6;
+    }
+  }
+
+  int res = xtrx_tune_rx_bandwidth(_xtrx->dev(), (chan == 0) ? XTRX_CH_A : XTRX_CH_B, bandwidth, &_bandwidth);
+  if (res) {
+    std::cerr << "Can't set bandwidth: " << res << std::endl;
+  }
+  return get_bandwidth(chan);
+}
+
+double xtrx_source_c::get_bandwidth( size_t chan )
+{
+  return _bandwidth;
+}
+
+
+static const std::map<std::string, xtrx_antenna_t> s_ant_map = boost::assign::map_list_of
+    ("RXL", XTRX_RX_L)
+    ("RXH", XTRX_RX_H)
+    ("RXW", XTRX_RX_W)
+    ("RXL_LB", XTRX_RX_L_LB)
+    ("RXW_LB", XTRX_RX_W_LB)
+    ;
+static const std::map<xtrx_antenna_t, std::string> s_ant_map_r = boost::assign::map_list_of
+    (XTRX_RX_L, "RXL")
+    (XTRX_RX_H, "RXH")
+    (XTRX_RX_W, "RXW")
+    (XTRX_RX_L_LB, "RXL_LB")
+    (XTRX_RX_W_LB, "RXW_LB")
+    ;
+
+static xtrx_antenna_t get_ant_type(const std::string& name)
+{
+  std::map<std::string, xtrx_antenna_t>::const_iterator it;
+
+  it = s_ant_map.find(name);
+  if (it != s_ant_map.end()) {
+    return it->second;
+  }
+
+  return XTRX_RX_W;
+}
+
+static const std::vector<std::string> s_ant_list = boost::assign::list_of
+    ("RXL")("RXH")("RXW")
+    ;
+
+
+std::vector< std::string > xtrx_source_c::get_antennas( size_t chan )
+{
+  return s_ant_list;
+}
+
+std::string xtrx_source_c::set_antenna( const std::string & antenna, size_t chan )
+{
+  boost::mutex::scoped_lock lock(_xtrx->mtx);
+  _ant = get_ant_type(antenna);
+
+  std::cerr << "Set antenna " << antenna << std::endl;
+
+  int res = xtrx_set_antenna(_xtrx->dev(), _ant);
+  if (res) {
+    std::cerr << "Can't set antenna: " << antenna << std::endl;
+  }
+  return get_antenna( chan );
+}
+
+std::string xtrx_source_c::get_antenna( size_t chan )
+{
+  return s_ant_map_r.find(_ant)->second;
+}
+
+int xtrx_source_c::work (int noutput_items,
+                         gr_vector_const_void_star &input_items,
+                         gr_vector_void_star &output_items)
+{
+  xtrx_recv_ex_info_t ri;
+  ri.samples = noutput_items;
+  ri.buffer_count = output_items.size();
+  ri.buffers = &output_items[0];
+  ri.flags = RCVEX_DONT_INSER_ZEROS | RCVEX_DROP_OLD_ON_OVERFLOW;
+
+  int res = xtrx_recv_sync_ex(_xtrx->dev(), &ri);
+  if (res) {
+    std::stringstream message;
+    message << "xtrx_recv_sync error: " << -res;
+    throw std::runtime_error( message.str() );
+  }
+
+  return ri.out_samples;
+}
+
+bool xtrx_source_c::start()
+{
+  boost::mutex::scoped_lock lock(_xtrx->mtx);
+
+  xtrx_run_params_t params;
+  xtrx_run_params_init(&params);
+
+  params.dir = XTRX_RX;
+  if (!_mimo_mode)
+    params.rx.flags |= XTRX_RSP_SISO_MODE;
+
+  if (_swap_ab)
+    params.rx.flags |= XTRX_RSP_SWAP_AB;
+
+  if (_swap_iq)
+    params.rx.flags |= XTRX_RSP_SWAP_IQ;
+
+  params.rx.hfmt = XTRX_IQ_FLOAT32;
+  params.rx.wfmt = _otw;
+  params.rx.chs = XTRX_CH_AB;
+  params.rx.paketsize = 0;
+  params.rx_stream_start = 256*1024;
+
+  params.nflags = (_loopback) ? XTRX_RUN_DIGLOOPBACK : 0;
+
+  int res = xtrx_run_ex(_xtrx->dev(), &params);
+  if (res) {
+    std::cerr << "Got error: " << res << std::endl;
+  }
+
+  return res == 0;
+}
+
+bool xtrx_source_c::stop()
+{
+  boost::mutex::scoped_lock lock(_xtrx->mtx);
+  //TODO:
+  std::cerr << "xtrx_source_c::stop()" << std::endl;
+  int res = xtrx_stop(_xtrx->dev(), XTRX_RX);
+  if (res) {
+    std::cerr << "Got error: " << res << std::endl;
+  }
+
+  return res == 0;
+}
diff --git a/lib/xtrx/xtrx_source_c.h b/lib/xtrx/xtrx_source_c.h
new file mode 100644
index 0000000..c05c7fb
--- /dev/null
+++ b/lib/xtrx/xtrx_source_c.h
@@ -0,0 +1,117 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2016,2017 Sergey Kostanbaev <sergey.kostanbaev@fairwaves.co>
+ *
+ * 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 XTRX_SOURCE_C_H
+#define XTRX_SOURCE_C_H
+
+#include <gnuradio/block.h>
+#include <gnuradio/sync_block.h>
+
+#include "source_iface.h"
+#include "xtrx_obj.h"
+
+class xtrx_source_c;
+
+typedef boost::shared_ptr< xtrx_source_c > xtrx_source_c_sptr;
+
+xtrx_source_c_sptr make_xtrx_source_c( const std::string & args = "" );
+
+class xtrx_source_c :
+    public gr::sync_block,
+    public source_iface
+{
+private:
+  friend xtrx_source_c_sptr make_xtrx_source_c(const std::string &args);
+
+  xtrx_source_c(const std::string &args);
+
+public:
+  ~xtrx_source_c();
+
+  std::string name();
+
+  static std::vector< std::string > get_devices( bool fake = false ) { return xtrx_obj::get_devices(); }
+
+  size_t get_num_channels( void );
+
+  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 );
+
+  double set_if_gain( double gain, size_t chan = 0 );
+
+  std::vector< std::string > get_antennas( size_t chan = 0 );
+  std::string set_antenna( const std::string & antenna, size_t chan = 0 );
+  std::string get_antenna( size_t chan = 0 );
+
+  double set_bandwidth( double bandwidth, size_t chan = 0 );
+  double get_bandwidth( size_t chan = 0 );
+
+  int work (int noutput_items,
+            gr_vector_const_void_star &input_items,
+            gr_vector_void_star &output_items);
+
+  bool start();
+  bool stop();
+
+private:
+  xtrx_obj_sptr _xtrx;
+
+  unsigned _sample_flags;
+  double _rate;
+  double _master;
+  double _freq;
+  double _corr;
+  double _bandwidth;
+  bool _auto_gain;
+
+  xtrx_wire_format_t _otw;
+  bool _mimo_mode;
+
+  int _gain_lna;
+  int _gain_tia;
+  int _gain_pga;
+
+  unsigned _channels;
+  xtrx_antenna_t _ant;
+
+  bool     _swap_ab;
+  bool     _swap_iq;
+  bool     _loopback;
+  bool     _tdd;
+};
+
+#endif // XTRX_SOURCE_C_H
+