Hello,
I hope it will go in the right place, here you can find a patch adding HydraSDR device
support that upgrades the AirSpy R2.
I had to create wrappers around GPIOs definition as putting the headers in namespace was a
failure at run time. Indeed, the GPIOs have same references for now and this is the only
clean way I've found to support both AirSpy and HydraSDR without disabling one of
each.
Please find the patch here that is also directly available and working on rfswift.io
through this repo
https://github.com/PentHertz/gr-osmosdr:
############################################################################
diff --git a/AUTHORS b/AUTHORS
index 2fd882c..590b6db 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -7,3 +7,4 @@ SDRplay Ltd.
Pavel Demin
Marcus Müller
Lukas Lao Beyer
+Penthertz folks
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3a6b0da..6d86c8e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -171,6 +171,7 @@ endif(ENABLE_NONFREE)
find_package(LibHackRF)
find_package(LibAIRSPY)
find_package(LibAIRSPYHF)
+find_package(LibHYDRASDR)
find_package(LibbladeRF)
find_package(GnuradioFuncube)
find_package(SoapySDR NO_MODULE)
diff --git a/cmake/Modules/FindLibHYDRASDR.cmake b/cmake/Modules/FindLibHYDRASDR.cmake
new file mode 100644
index 0000000..1b11d72
--- /dev/null
+++ b/cmake/Modules/FindLibHYDRASDR.cmake
@@ -0,0 +1,42 @@
+if(NOT PKG_CONFIG_FOUND)
+ INCLUDE(FindPkgConfig)
+endif()
+PKG_CHECK_MODULES(PC_LIBHYDRASDR libhydrasdr)
+
+message("Debug: Running PKG_CHECK_MODULES for libhydrasdr")
+PKG_CHECK_MODULES(PC_LIBHYDRASDR libhydrasdr)
+message("Debug: PC_LIBHYDRASDR_FOUND=${PC_LIBHYDRASDR_FOUND}")
+message("Debug: PC_LIBHYDRASDR_INCLUDEDIR=${PC_LIBHYDRASDR_INCLUDEDIR}")
+message("Debug: PC_LIBHYDRASDR_LIBDIR=${PC_LIBHYDRASDR_LIBDIR}")
+
+message("Debug: Looking for libhydrasdr/hydrasdr.h")
+
+FIND_PATH(
+LIBHYDRASDR_INCLUDE_DIRS
+ NAMES libhydrasdr/hydrasdr.h
+ HINTS $ENV{LIBHYDRASDR_DIR}/include
+ ${PC_LIBHYDRASDR_INCLUDEDIR}
+ PATHS /usr/local/include
+ /usr/include
+)
+
+message("Debug: LIBHYDRASDR_INCLUDE_DIRS=${LIBHYDRASDR_INCLUDE_DIRS}")
+message("Debug: Looking for hydrasdr library")
+
+FIND_LIBRARY(
+ LIBHYDRASDR_LIBRARIES
+ NAMES hydrasdr
+ HINTS $ENV{LIBHYDRASDR_DIR}/lib
+ ${PC_LIBHYDRASDR_LIBDIR}
+ PATHS /usr/local/lib
+ /usr/lib
+)
+
+message("Debug: LIBHYDRASDR_LIBRARIES=${LIBHYDRASDR_LIBRARIES}")
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibHYDRASDR DEFAULT_MSG LIBHYDRASDR_LIBRARIES
LIBHYDRASDR_INCLUDE_DIRS)
+MARK_AS_ADVANCED(LIBHYDRASDR_LIBRARIES LIBHYDRASDR_INCLUDE_DIRS)
+
+message("Debug: LibHYDRASDR_FOUND=${LibHYDRASDR_FOUND}")
+message("Debug: LIBHYDRASDR_FOUND=${LIBHYDRASDR_FOUND}")
diff --git a/grc/gen_osmosdr_blocks.py b/grc/gen_osmosdr_blocks.py
index 3bba1b2..5300189 100644
--- a/grc/gen_osmosdr_blocks.py
+++ b/grc/gen_osmosdr_blocks.py
@@ -164,6 +164,7 @@ documentation: |-
* gnuradio .cfile input through libgnuradio-blocks
* RFSPACE SDR-IQ, SDR-IP, NetSDR (incl. X2 option)
* AirSpy Wideband Receiver through libairspy
+ * HydraSDR Wideband Receiver through libhydrasdr
% endif
% if sourk == 'sink':
* gnuradio .cfile output through libgnuradio-blocks
@@ -204,6 +205,7 @@ documentation: |-
cloudiq=127.0.0.1[:50000]
sdr-iq=/dev/ttyUSB0
airspy=0[,bias=0|1][,linearity][,sensitivity]
+ hydrasdr=0[,bias=0|1][,linearity][,sensitivity]
% endif
% if sourk == 'sink':
file='/path/to/your file',rate=1e6[,freq=100e6][,append=true][,throttle=true]
...
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 8673038..e28aef5 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -239,6 +239,14 @@ if(ENABLE_AIRSPYHF)
add_subdirectory(airspyhf)
endif(ENABLE_AIRSPYHF)
+########################################################################
+# Setup HYDRASDR component
+########################################################################
+GR_REGISTER_COMPONENT("HYDRASDR Receiver" ENABLE_HYDRASDR LIBHYDRASDR_FOUND)
+if(ENABLE_HYDRASDR)
+ add_subdirectory(hydrasdr)
+endif(ENABLE_HYDRASDR)
+
########################################################################
# Setup SoapySDR component
########################################################################
diff --git a/lib/airspy/airspy_source_c.h b/lib/airspy/airspy_source_c.h
index 81add15..75b2b95 100644
--- a/lib/airspy/airspy_source_c.h
+++ b/lib/airspy/airspy_source_c.h
@@ -29,7 +29,7 @@
#include <gnuradio/sync_block.h>
-#include <libairspy/airspy.h>
+#include "airspy_wrapper.h"
#include "source_iface.h"
diff --git a/lib/airspy/airspy_wrapper.h b/lib/airspy/airspy_wrapper.h
new file mode 100644
index 0000000..7c26b5f
--- /dev/null
+++ b/lib/airspy/airspy_wrapper.h
@@ -0,0 +1,102 @@
+#ifndef AIRSPY_WRAPPER_H
+#define AIRSPY_WRAPPER_H
+
+// Remap ALL conflicting symbols for AIRSPY
+#define RECEIVER_MODE_OFF AIRSPY_RECEIVER_MODE_OFF
+#define RECEIVER_MODE_RX AIRSPY_RECEIVER_MODE_RX
+#define receiver_mode_t airspy_receiver_mode_t
+
+#define GPIO_PORT0 AIRSPY_GPIO_PORT0
+#define GPIO_PORT1 AIRSPY_GPIO_PORT1
+#define GPIO_PORT2 AIRSPY_GPIO_PORT2
+#define GPIO_PORT3 AIRSPY_GPIO_PORT3
+#define GPIO_PORT4 AIRSPY_GPIO_PORT4
+#define GPIO_PORT5 AIRSPY_GPIO_PORT5
+#define GPIO_PORT6 AIRSPY_GPIO_PORT6
+#define GPIO_PORT7 AIRSPY_GPIO_PORT7
+
+#define GPIO_PIN0 AIRSPY_GPIO_PIN0
+#define GPIO_PIN1 AIRSPY_GPIO_PIN1
+#define GPIO_PIN2 AIRSPY_GPIO_PIN2
+#define GPIO_PIN3 AIRSPY_GPIO_PIN3
+#define GPIO_PIN4 AIRSPY_GPIO_PIN4
+#define GPIO_PIN5 AIRSPY_GPIO_PIN5
+#define GPIO_PIN6 AIRSPY_GPIO_PIN6
+#define GPIO_PIN7 AIRSPY_GPIO_PIN7
+#define GPIO_PIN8 AIRSPY_GPIO_PIN8
+#define GPIO_PIN9 AIRSPY_GPIO_PIN9
+#define GPIO_PIN10 AIRSPY_GPIO_PIN10
+#define GPIO_PIN11 AIRSPY_GPIO_PIN11
+#define GPIO_PIN12 AIRSPY_GPIO_PIN12
+#define GPIO_PIN13 AIRSPY_GPIO_PIN13
+#define GPIO_PIN14 AIRSPY_GPIO_PIN14
+#define GPIO_PIN15 AIRSPY_GPIO_PIN15
+#define GPIO_PIN16 AIRSPY_GPIO_PIN16
+#define GPIO_PIN17 AIRSPY_GPIO_PIN17
+#define GPIO_PIN18 AIRSPY_GPIO_PIN18
+#define GPIO_PIN19 AIRSPY_GPIO_PIN19
+#define GPIO_PIN20 AIRSPY_GPIO_PIN20
+#define GPIO_PIN21 AIRSPY_GPIO_PIN21
+#define GPIO_PIN22 AIRSPY_GPIO_PIN22
+#define GPIO_PIN23 AIRSPY_GPIO_PIN23
+#define GPIO_PIN24 AIRSPY_GPIO_PIN24
+#define GPIO_PIN25 AIRSPY_GPIO_PIN25
+#define GPIO_PIN26 AIRSPY_GPIO_PIN26
+#define GPIO_PIN27 AIRSPY_GPIO_PIN27
+#define GPIO_PIN28 AIRSPY_GPIO_PIN28
+#define GPIO_PIN29 AIRSPY_GPIO_PIN29
+#define GPIO_PIN30 AIRSPY_GPIO_PIN30
+#define GPIO_PIN31 AIRSPY_GPIO_PIN31
+
+extern "C" {
+#include <libairspy/airspy.h>
+}
+
+// Undefine ALL remapped symbols
+#undef RECEIVER_MODE_OFF
+#undef RECEIVER_MODE_RX
+#undef receiver_mode_t
+
+#undef GPIO_PORT0
+#undef GPIO_PORT1
+#undef GPIO_PORT2
+#undef GPIO_PORT3
+#undef GPIO_PORT4
+#undef GPIO_PORT5
+#undef GPIO_PORT6
+#undef GPIO_PORT7
+
+#undef GPIO_PIN0
+#undef GPIO_PIN1
+#undef GPIO_PIN2
+#undef GPIO_PIN3
+#undef GPIO_PIN4
+#undef GPIO_PIN5
+#undef GPIO_PIN6
+#undef GPIO_PIN7
+#undef GPIO_PIN8
+#undef GPIO_PIN9
+#undef GPIO_PIN10
+#undef GPIO_PIN11
+#undef GPIO_PIN12
+#undef GPIO_PIN13
+#undef GPIO_PIN14
+#undef GPIO_PIN15
+#undef GPIO_PIN16
+#undef GPIO_PIN17
+#undef GPIO_PIN18
+#undef GPIO_PIN19
+#undef GPIO_PIN20
+#undef GPIO_PIN21
+#undef GPIO_PIN22
+#undef GPIO_PIN23
+#undef GPIO_PIN24
+#undef GPIO_PIN25
+#undef GPIO_PIN26
+#undef GPIO_PIN27
+#undef GPIO_PIN28
+#undef GPIO_PIN29
+#undef GPIO_PIN30
+#undef GPIO_PIN31
+
+#endif // AIRSPY_WRAPPER_H
diff --git a/lib/config.h.in b/lib/config.h.in
index 4e63444..6562506 100644
--- a/lib/config.h.in
+++ b/lib/config.h.in
@@ -20,6 +20,7 @@
#cmakedefine ENABLE_REDPITAYA
#cmakedefine ENABLE_FREESRP
#cmakedefine ENABLE_XTRX
+#cmakedefine ENABLE_HYDRASDR
//provide NAN define for MSVC older than VC12
#if defined(_MSC_VER) && (_MSC_VER < 1800)
diff --git a/lib/device.cc b/lib/device.cc
index 1dee01f..6bc577d 100644
--- a/lib/device.cc
+++ b/lib/device.cc
@@ -77,6 +77,10 @@
#include <airspyhf_source_c.h>
#endif
+#ifdef ENABLE_HYDRASDR
+#include <hydrasdr_source_c.h>
+#endif
+
#ifdef ENABLE_SOAPY
#include <soapy_source_c.h>
#endif
diff --git a/lib/hydrasdr/CMakeLists.txt b/lib/hydrasdr/CMakeLists.txt
new file mode 100644
index 0000000..42b6b75
--- /dev/null
+++ b/lib/hydrasdr/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Copyright 2012 Free Software Foundation, Inc.
+#
+# This file is part of gr-osmosdr
+#
+# gr-osmosdr 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.
+#
+# gr-osmosdr is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with gr-osmosdr; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+target_include_directories(gnuradio-osmosdr PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${LIBHYDRASDR_INCLUDE_DIRS}
+)
+
+APPEND_LIB_LIST(
+ gnuradio::gnuradio-filter
+ ${Gnuradio-blocks_LIBRARIES}
+ ${LIBHYDRASDR_LIBRARIES}
+)
+
+list(APPEND gr_osmosdr_srcs
+ ${CMAKE_CURRENT_SOURCE_DIR}/hydrasdr_source_c.cc
+)
+set(gr_osmosdr_srcs ${gr_osmosdr_srcs} PARENT_SCOPE)
diff --git a/lib/hydrasdr/hydrasdr_fir_kernels.h b/lib/hydrasdr/hydrasdr_fir_kernels.h
new file mode 100644
index 0000000..e15f4e1
--- /dev/null
+++ b/lib/hydrasdr/hydrasdr_fir_kernels.h
@@ -0,0 +1,101 @@
+#pragma once
+
+#define KERNEL_16_110_LEN 7
+const float KERNEL_16_110[] =
+{
+ -0.031835079193115234f,
+ 0.000000000000000000f,
+ 0.281831502914428710f,
+ 0.500007271766662600f,
+ 0.281831502914428710f,
+ 0.000000000000000000f,
+ -0.031835079193115234f
+};
+
+#define KERNEL_8_100_LEN 11
+const float KERNEL_8_100[] =
+{
+ 0.006633400917053223f,
+ 0.000000000000000000f,
+ -0.051035523414611816f,
+ 0.000000000000000000f,
+ 0.294403314590454100f,
+ 0.499997496604919430f,
+ 0.294403314590454100f,
+ 0.000000000000000000f,
+ -0.051035523414611816f,
+ 0.000000000000000000f,
+ 0.006633400917053223f
+};
+
+#define KERNEL_4_90_LEN 15
+const float KERNEL_4_90[] =
+{
+ -0.002474188804626465f,
+ 0.000000000000000000f,
+ 0.016965746879577637f,
+ 0.000000000000000000f,
+ -0.067680597305297852f,
+ 0.000000000000000000f,
+ 0.303180575370788570f,
+ 0.500017046928405760f,
+ 0.303180575370788570f,
+ 0.000000000000000000f,
+ -0.067680597305297852f,
+ 0.000000000000000000f,
+ 0.016965746879577637f,
+ 0.000000000000000000f,
+ -0.002474188804626465f
+};
+
+#define KERNEL_2_80_LEN 47
+const float KERNEL_2_80[KERNEL_2_80_LEN] =
+{
+ -0.000198006629943848f,
+ 0.000000000000000000f,
+ 0.000576853752136230f,
+ 0.000000000000000000f,
+ -0.001352190971374512f,
+ 0.000000000000000000f,
+ 0.002729177474975586f,
+ 0.000000000000000000f,
+ -0.004988193511962891f,
+ 0.000000000000000000f,
+ 0.008499503135681152f,
+ 0.000000000000000000f,
+ -0.013788580894470215f,
+ 0.000000000000000000f,
+ 0.021713137626647949f,
+ 0.000000000000000000f,
+ -0.033980011940002441f,
+ 0.000000000000000000f,
+ 0.054944872856140137f,
+ 0.000000000000000000f,
+ -0.100657463073730470f,
+ 0.000000000000000000f,
+ 0.316457390785217290f,
+ 0.500000000000000000f,
+ 0.316457390785217290f,
+ 0.000000000000000000f,
+ -0.100657463073730470f,
+ 0.000000000000000000f,
+ 0.054944872856140137f,
+ 0.000000000000000000f,
+ -0.033980011940002441f,
+ 0.000000000000000000f,
+ 0.021713137626647949f,
+ 0.000000000000000000f,
+ -0.013788580894470215f,
+ 0.000000000000000000f,
+ 0.008499503135681152f,
+ 0.000000000000000000f,
+ -0.004988193511962891f,
+ 0.000000000000000000f,
+ 0.002729177474975586f,
+ 0.000000000000000000f,
+ -0.001352190971374512f,
+ 0.000000000000000000f,
+ 0.000576853752136230f,
+ 0.000000000000000000f,
+ -0.000198006629943848f
+};
diff --git a/lib/hydrasdr/hydrasdr_source_c.cc b/lib/hydrasdr/hydrasdr_source_c.cc
new file mode 100644
index 0000000..79f2c35
--- /dev/null
+++ b/lib/hydrasdr/hydrasdr_source_c.cc
@@ -0,0 +1,714 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2013 Dimitri Stolnikov <horiz0n(a)gmx.net>
+ * Copyright 2025 Sébastien Dudek @
Penthertz.com
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * 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 <stdexcept>
+#include <iostream>
+#include <algorithm>
+
+#include <boost/assign.hpp>
+#include <boost/format.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/thread/thread.hpp>
+
+#include <gnuradio/io_signature.h>
+
+#include "hydrasdr_source_c.h"
+#include "hydrasdr_fir_kernels.h"
+
+#include "arg_helpers.h"
+
+using namespace boost::assign;
+
+#define HYDRASDR_FORMAT_ERROR(ret, msg) \
+ boost::str( boost::format(msg " (%1%) %2%") \
+ % ret % hydrasdr_error_name((enum hydrasdr_error)ret) )
+
+#define HYDRASDR_THROW_ON_ERROR(ret, msg) \
+ if ( ret != HYDRASDR_SUCCESS ) \
+ { \
+ throw std::runtime_error( HYDRASDR_FORMAT_ERROR(ret, msg) ); \
+ }
+
+#define HYDRASDR_FUNC_STR(func, arg) \
+ boost::str(boost::format(func "(%1%)") % arg) + " has failed"
+
+hydrasdr_source_c_sptr make_hydrasdr_source_c (const std::string & args)
+{
+ return gnuradio::get_initial_sptr(new hydrasdr_source_c (args));
+}
+
+/*
+ * Specify constraints on number of input and output streams.
+ * This info is used to construct the input and output signatures
+ * (2nd & 3rd args to gr::block's constructor). The input and
+ * output signatures are used by the runtime system to
+ * check that a valid number and type of inputs and outputs
+ * are connected to this block. In this case, we accept
+ * only 0 input and 1 output.
+ */
+static const int MIN_IN = 0; // mininum number of input streams
+static const int MAX_IN = 0; // maximum number of input streams
+static const int MIN_OUT = 1; // minimum number of output streams
+static const int MAX_OUT = 1; // maximum number of output streams
+
+/*
+ * The private constructor
+ */
+hydrasdr_source_c::hydrasdr_source_c (const std::string &args)
+ : gr::sync_block ("hydrasdr_source_c",
+ gr::io_signature::make(MIN_IN, MAX_IN, sizeof (gr_complex)),
+ gr::io_signature::make(MIN_OUT, MAX_OUT, sizeof (gr_complex))),
+ _dev(NULL),
+ _sample_rate(0),
+ _center_freq(0),
+ _freq_corr(0),
+ _auto_gain(false),
+ _gain_policy(linearity),
+ _lna_gain(0),
+ _mix_gain(0),
+ _vga_gain(0),
+ _bandwidth(0)
+{
+ int ret;
+
+ dict_t dict = params_to_dict(args);
+
+ _dev = NULL;
+ ret = hydrasdr_open( &_dev );
+ HYDRASDR_THROW_ON_ERROR(ret, "Failed to open HydraSDR device")
+
+ uint8_t board_id;
+ ret = hydrasdr_board_id_read( _dev, &board_id );
+ HYDRASDR_THROW_ON_ERROR(ret, "Failed to get HydraSDR board id")
+
+ char version[128];
+ memset(version, 0, sizeof(version));
+ ret = hydrasdr_version_string_read( _dev, version, sizeof(version));
+ HYDRASDR_THROW_ON_ERROR(ret, "Failed to read version string")
+#if 0
+ hydrasdr_read_partid_serialno_t part_serial;
+ ret = hydrasdr_board_partid_serialno_read( _dev, &part_serial );
+ HYDRASDR_THROW_ON_ERROR(ret, "Failed to read serial number")
+#endif
+ uint32_t num_rates;
+ hydrasdr_get_samplerates(_dev, &num_rates, 0);
+ uint32_t *samplerates = (uint32_t *) malloc(num_rates * sizeof(uint32_t));
+ hydrasdr_get_samplerates(_dev, samplerates, num_rates);
+ for (size_t i = 0; i < num_rates; i++)
+ _sample_rates.push_back( std::pair<double, uint32_t>( samplerates[i], i ) );
+ free(samplerates);
+
+ /* since they may (and will) give us an unsorted array we have to sort it here
+ * to play nice with the monotonic requirement of meta-range later on */
+ std::sort(_sample_rates.begin(), _sample_rates.end());
+
+ std::cerr << "Using " << version << ", samplerates:
";
+
+ for (size_t i = 0; i < _sample_rates.size(); i++)
+ std::cerr << boost::format("%gM ") % (_sample_rates[i].first / 1e6);
+
+ std::cerr << std::endl;
+
+ set_center_freq( (get_freq_range().start() + get_freq_range().stop()) / 2.0 );
+ set_sample_rate( get_sample_rates().start() );
+ set_bandwidth( 0 );
+
+ if ( dict.count( "linearity" ) )
+ _gain_policy = linearity;
+
+ if ( dict.count( "sensitivity" ) )
+ _gain_policy = sensitivity;
+
+ set_lna_gain( 8 ); /* preset to a reasonable default (non-GRC use case) */
+
+ set_mix_gain( 5 ); /* preset to a reasonable default (non-GRC use case) */
+
+ set_if_gain( 5 ); /* preset to a reasonable default (non-GRC use case) */
+
+ if ( dict.count( "bias" ) )
+ {
+ bool bias = boost::lexical_cast<bool>( dict["bias"] );
+ int ret = hydrasdr_set_rf_bias(_dev, (uint8_t)bias);
+ HYDRASDR_THROW_ON_ERROR(ret, "Failed to enable DC bias")
+ }
+
+/* pack 4 sets of 12 bits into 3 sets 16 bits for the data transfer across the
+ * USB bus. The default is is unpacked, to transfer 12 bits across the USB bus
+ * in 16 bit words. libhydrasdr transparently unpacks if packing is enabled */
+ if ( dict.count( "pack" ) )
+ {
+ bool pack = boost::lexical_cast<bool>( dict["pack"] );
+ int ret = hydrasdr_set_packing(_dev, (uint8_t)pack);
+ HYDRASDR_THROW_ON_ERROR(ret, "Failed to set USB bit packing")
+ }
+
+ _fifo = new boost::circular_buffer<gr_complex>(5000000);
+ if (!_fifo) {
+ throw std::runtime_error( std::string(__FUNCTION__) + " " +
+ "Failed to allocate a sample FIFO!" );
+ }
+}
+
+/*
+ * Our virtual destructor.
+ */
+hydrasdr_source_c::~hydrasdr_source_c ()
+{
+ int ret;
+
+ if (_dev) {
+ if ( hydrasdr_is_streaming( _dev ) == HYDRASDR_TRUE )
+ {
+ ret = hydrasdr_stop_rx( _dev );
+ if ( ret != HYDRASDR_SUCCESS )
+ {
+ std::cerr << HYDRASDR_FORMAT_ERROR(ret, "Failed to stop RX
streaming") << std::endl;
+ }
+ }
+
+ ret = hydrasdr_close( _dev );
+ if ( ret != HYDRASDR_SUCCESS )
+ {
+ std::cerr << HYDRASDR_FORMAT_ERROR(ret, "Failed to close HydraSDR")
<< std::endl;
+ }
+ _dev = NULL;
+ }
+
+ if (_fifo)
+ {
+ delete _fifo;
+ _fifo = NULL;
+ }
+}
+
+int hydrasdr_source_c::_hydrasdr_rx_callback(hydrasdr_transfer *transfer)
+{
+ hydrasdr_source_c *obj = (hydrasdr_source_c *)transfer->ctx;
+
+ return obj->hydrasdr_rx_callback((float *)transfer->samples,
transfer->sample_count);
+}
+
+int hydrasdr_source_c::hydrasdr_rx_callback(void *samples, int sample_count)
+{
+ size_t i, n_avail, to_copy, num_samples = sample_count;
+ float *sample = (float *)samples;
+
+ _fifo_lock.lock();
+
+ n_avail = _fifo->capacity() - _fifo->size();
+ to_copy = (n_avail < num_samples ? n_avail : num_samples);
+
+ for (i = 0; i < to_copy; i++ )
+ {
+ /* Push sample to the fifo */
+ _fifo->push_back( gr_complex( *sample, *(sample+1) ) );
+
+ /* offset to the next I+Q sample */
+ sample += 2;
+ }
+
+ _fifo_lock.unlock();
+
+ /* We have made some new samples available to the consumer in work() */
+ if (to_copy) {
+ //std::cerr << "+" << std::flush;
+ _samp_avail.notify_one();
+ }
+
+ /* Indicate overrun, if neccesary */
+ if (to_copy < num_samples)
+ std::cerr << "O" << std::flush;
+
+ return 0; // TODO: return -1 on error/stop
+}
+
+bool hydrasdr_source_c::start()
+{
+ if ( ! _dev )
+ return false;
+
+ int ret = hydrasdr_start_rx( _dev, _hydrasdr_rx_callback, (void *)this );
+ if ( ret != HYDRASDR_SUCCESS ) {
+ std::cerr << "Failed to start RX streaming (" << ret <<
")" << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+bool hydrasdr_source_c::stop()
+{
+ if ( ! _dev )
+ return false;
+
+ int ret = hydrasdr_stop_rx( _dev );
+ if ( ret != HYDRASDR_SUCCESS ) {
+ std::cerr << "Failed to stop RX streaming (" << ret <<
")" << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+int hydrasdr_source_c::work( int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items )
+{
+ gr_complex *out = (gr_complex *)output_items[0];
+
+ bool running = false;
+
+ if ( _dev )
+ running = (hydrasdr_is_streaming( _dev ) == HYDRASDR_TRUE);
+
+ if ( ! running )
+ return WORK_DONE;
+
+ std::unique_lock<std::mutex> lock(_fifo_lock);
+
+ /* Wait until we have the requested number of samples */
+ int n_samples_avail = _fifo->size();
+
+ while (n_samples_avail < noutput_items) {
+ _samp_avail.wait(lock);
+ n_samples_avail = _fifo->size();
+ }
+
+ for(int i = 0; i < noutput_items; ++i) {
+ out[i] = _fifo->at(0);
+ _fifo->pop_front();
+ }
+
+ //std::cerr << "-" << std::flush;
+
+ return noutput_items;
+}
+
+std::vector<std::string> hydrasdr_source_c::get_devices()
+{
+ std::vector<std::string> devices;
+ std::string label;
+
+ int ret;
+ hydrasdr_device *dev = NULL;
+ ret = hydrasdr_open(&dev);
+ if ( HYDRASDR_SUCCESS == ret )
+ {
+ std::string args = "hydrasdr=0";
+
+ label = "HydraSDR RFOne";
+
+ uint8_t board_id;
+ ret = hydrasdr_board_id_read( dev, &board_id );
+ if ( HYDRASDR_SUCCESS == ret )
+ {
+ label += std::string(" ") +
hydrasdr_board_id_name(hydrasdr_board_id(board_id));
+ }
+
+ args += ",label='" + label + "'";
+ devices.push_back( args );
+
+ ret = hydrasdr_close(dev);
+ }
+
+ return devices;
+}
+
+size_t hydrasdr_source_c::get_num_channels()
+{
+ return 1;
+}
+
+osmosdr::meta_range_t hydrasdr_source_c::get_sample_rates()
+{
+ osmosdr::meta_range_t range;
+
+ for (size_t i = 0; i < _sample_rates.size(); i++)
+ range += osmosdr::range_t( _sample_rates[i].first );
+
+ return range;
+}
+
+double hydrasdr_source_c::set_sample_rate( double rate )
+{
+ int ret = HYDRASDR_SUCCESS;
+
+ if (_dev) {
+ bool found_supported_rate = false;
+ uint32_t samp_rate_index = 0;
+
+ for( unsigned int i = 0; i < _sample_rates.size(); i++ )
+ {
+ if( _sample_rates[i].first == rate )
+ {
+ samp_rate_index = _sample_rates[i].second;
+
+ found_supported_rate = true;
+ }
+ }
+
+ if ( ! found_supported_rate )
+ {
+ throw std::runtime_error(
+ boost::str( boost::format("Unsupported samplerate: %gM") % (rate/1e6) )
);
+ }
+
+ ret = hydrasdr_set_samplerate( _dev, samp_rate_index );
+ if ( HYDRASDR_SUCCESS == ret ) {
+ _sample_rate = rate;
+ } else {
+ HYDRASDR_THROW_ON_ERROR( ret, HYDRASDR_FUNC_STR(
"hydrasdr_set_samplerate", rate ) )
+ }
+ }
+
+ return get_sample_rate();
+}
+
+double hydrasdr_source_c::get_sample_rate()
+{
+ return _sample_rate;
+}
+
+osmosdr::freq_range_t hydrasdr_source_c::get_freq_range( size_t chan )
+{
+ osmosdr::freq_range_t range;
+
+ range += osmosdr::range_t( 24e6, 1766e6 );
+
+ return range;
+}
+
+double hydrasdr_source_c::set_center_freq( double freq, size_t chan )
+{
+ int ret;
+
+ #define APPLY_PPM_CORR(val, ppm) ((val) * (1.0 + (ppm) * 0.000001))
+
+ if (_dev) {
+ double corr_freq = APPLY_PPM_CORR( freq, _freq_corr );
+ ret = hydrasdr_set_freq( _dev, uint64_t(corr_freq) );
+ if ( HYDRASDR_SUCCESS == ret ) {
+ _center_freq = freq;
+ } else {
+ HYDRASDR_THROW_ON_ERROR( ret, HYDRASDR_FUNC_STR( "hydrasdr_set_freq",
corr_freq ) )
+ }
+ }
+
+ return get_center_freq( chan );
+}
+
+double hydrasdr_source_c::get_center_freq( size_t chan )
+{
+ return _center_freq;
+}
+
+double hydrasdr_source_c::set_freq_corr( double ppm, size_t chan )
+{
+ _freq_corr = ppm;
+
+ set_center_freq( _center_freq );
+
+ return get_freq_corr( chan );
+}
+
+double hydrasdr_source_c::get_freq_corr( size_t chan )
+{
+ return _freq_corr;
+}
+
+std::vector<std::string> hydrasdr_source_c::get_gain_names( size_t chan )
+{
+ std::vector< std::string > names;
+
+ names += "LNA";
+ names += "MIX";
+ names += "IF";
+
+ return names;
+}
+
+osmosdr::gain_range_t hydrasdr_source_c::get_gain_range( size_t chan )
+{
+ return osmosdr::gain_range_t( 0, 21, 1 );
+}
+
+osmosdr::gain_range_t hydrasdr_source_c::get_gain_range( const std::string & name,
size_t chan )
+{
+ /* They don't spec any gain values in dB so we simply use gain stage indices for
now. */
+
+ if ( "LNA" == name ) {
+ return osmosdr::gain_range_t( 0, 15, 1 );
+ }
+
+ if ( "MIX" == name ) {
+ return osmosdr::gain_range_t( 0, 15, 1 );
+ }
+
+ if ( "IF" == name ) {
+ return osmosdr::gain_range_t( 0, 15, 1 );
+ }
+
+ return osmosdr::gain_range_t();
+}
+
+bool hydrasdr_source_c::set_gain_mode( bool automatic, size_t chan )
+{
+ if ( automatic ) {
+ hydrasdr_set_lna_agc( _dev, 1 );
+ hydrasdr_set_mixer_agc( _dev, 1 );
+ } else {
+ hydrasdr_set_lna_agc( _dev, 0 );
+ hydrasdr_set_mixer_agc( _dev, 0 );
+
+ set_lna_gain( _lna_gain );
+ set_mix_gain( _mix_gain );
+ }
+
+ _auto_gain = automatic;
+
+ return get_gain_mode(chan);
+}
+
+bool hydrasdr_source_c::get_gain_mode( size_t chan )
+{
+ return _auto_gain;
+}
+
+double hydrasdr_source_c::set_gain( double gain, size_t chan )
+{
+ int ret = HYDRASDR_SUCCESS;
+ osmosdr::gain_range_t gains = get_gain_range( chan );
+
+ if (_dev) {
+ double clip_gain = gains.clip( gain, true );
+ uint8_t value = clip_gain;
+
+ if ( _gain_policy == linearity ) {
+ ret = hydrasdr_set_linearity_gain( _dev, value );
+ if ( HYDRASDR_SUCCESS == ret ) {
+ _gain = clip_gain;
+ } else {
+ HYDRASDR_THROW_ON_ERROR( ret, HYDRASDR_FUNC_STR(
"hydrasdr_set_linearity_gain", value ) )
+ }
+ } else if ( _gain_policy == sensitivity ) {
+ ret = hydrasdr_set_sensitivity_gain( _dev, value );
+ if ( HYDRASDR_SUCCESS == ret ) {
+ _gain = clip_gain;
+ } else {
+ HYDRASDR_THROW_ON_ERROR( ret, HYDRASDR_FUNC_STR(
"hydrasdr_set_sensitivity_gain", value ) )
+ }
+ }
+ }
+
+ return _gain;
+}
+
+double hydrasdr_source_c::set_gain( double gain, const std::string & name, size_t
chan)
+{
+ if ( "LNA" == name ) {
+ return set_lna_gain( gain, chan );
+ }
+
+ if ( "MIX" == name ) {
+ return set_mix_gain( gain, chan );
+ }
+
+ if ( "IF" == name ) {
+ return set_if_gain( gain, chan );
+ }
+
+ return set_gain( gain, chan );
+}
+
+double hydrasdr_source_c::get_gain( size_t chan )
+{
+ return _gain;
+}
+
+double hydrasdr_source_c::get_gain( const std::string & name, size_t chan )
+{
+ if ( "LNA" == name ) {
+ return _lna_gain;
+ }
+
+ if ( "MIX" == name ) {
+ return _mix_gain;
+ }
+
+ if ( "IF" == name ) {
+ return _vga_gain;
+ }
+
+ return get_gain( chan );
+}
+
+double hydrasdr_source_c::set_lna_gain( double gain, size_t chan )
+{
+ int ret = HYDRASDR_SUCCESS;
+ osmosdr::gain_range_t gains = get_gain_range( "LNA", chan );
+
+ if (_dev) {
+ double clip_gain = gains.clip( gain, true );
+ uint8_t value = clip_gain;
+
+ ret = hydrasdr_set_lna_gain( _dev, value );
+ if ( HYDRASDR_SUCCESS == ret ) {
+ _lna_gain = clip_gain;
+ } else {
+ HYDRASDR_THROW_ON_ERROR( ret, HYDRASDR_FUNC_STR( "hydrasdr_set_lna_gain",
value ) )
+ }
+ }
+
+ return _lna_gain;
+}
+
+double hydrasdr_source_c::set_mix_gain(double gain, size_t chan)
+{
+ int ret;
+ osmosdr::gain_range_t gains = get_gain_range( "MIX", chan );
+
+ if (_dev) {
+ double clip_gain = gains.clip( gain, true );
+ uint8_t value = clip_gain;
+
+ ret = hydrasdr_set_mixer_gain( _dev, value );
+ if ( HYDRASDR_SUCCESS == ret ) {
+ _mix_gain = clip_gain;
+ } else {
+ HYDRASDR_THROW_ON_ERROR( ret, HYDRASDR_FUNC_STR(
"hydrasdr_set_mixer_gain", value ) )
+ }
+ }
+
+ return _mix_gain;
+}
+
+double hydrasdr_source_c::set_if_gain(double gain, size_t chan)
+{
+ int ret;
+ osmosdr::gain_range_t gains = get_gain_range( "MIX", chan );
+
+ if (_dev) {
+ double clip_gain = gains.clip( gain, true );
+ uint8_t value = clip_gain;
+
+ ret = hydrasdr_set_vga_gain( _dev, value );
+ if ( HYDRASDR_SUCCESS == ret ) {
+ _vga_gain = clip_gain;
+ } else {
+ HYDRASDR_THROW_ON_ERROR( ret, HYDRASDR_FUNC_STR( "hydrasdr_set_vga_gain",
value ) )
+ }
+ }
+
+ return _vga_gain;
+}
+
+std::vector< std::string > hydrasdr_source_c::get_antennas( size_t chan )
+{
+ std::vector< std::string > antennas;
+
+ antennas += get_antenna( chan );
+
+ return antennas;
+}
+
+std::string hydrasdr_source_c::set_antenna( const std::string & antenna, size_t chan
)
+{
+ return get_antenna( chan );
+}
+
+std::string hydrasdr_source_c::get_antenna( size_t chan )
+{
+ return "RX";
+}
+
+double hydrasdr_source_c::set_bandwidth( double bandwidth, size_t chan )
+{
+ if (bandwidth == 0.f)
+ return get_bandwidth( chan );
+
+ {
+ int ret;
+ int decim;
+ int size;
+ const float *kernel;
+
+ decim = (int)(_sample_rate / bandwidth);
+// if (decim < 2)
+// {
+// kernel = 0;
+// size = 0;
+// }
+// else
+ if (decim < 4)
+ {
+ kernel = KERNEL_2_80;
+ size = KERNEL_2_80_LEN;
+ }
+ else if (decim < 8)
+ {
+ kernel = KERNEL_4_90;
+ size = KERNEL_4_90_LEN;
+ }
+ else if (decim < 16)
+ {
+ kernel = KERNEL_8_100;
+ size = KERNEL_8_100_LEN;
+ }
+ else
+ {
+ kernel = KERNEL_16_110;
+ size = KERNEL_16_110_LEN;
+ }
+
+ if (size)
+ {
+ std::cerr << " HydraSDR decim:" << decim
+ << " kernel size:" << size << std::endl;
+ ret = hydrasdr_set_conversion_filter_float32(_dev, kernel, size);
+ HYDRASDR_THROW_ON_ERROR(ret, "Failed to set IQ conversion filter")
+ }
+ }
+
+ return get_bandwidth( chan );
+}
+
+double hydrasdr_source_c::get_bandwidth( size_t chan )
+{
+ return _sample_rate;
+}
+
+osmosdr::freq_range_t hydrasdr_source_c::get_bandwidth_range( size_t chan )
+{
+ osmosdr::freq_range_t bandwidths;
+
+ bandwidths += osmosdr::range_t( get_bandwidth( chan ) );
+
+ return bandwidths;
+}
diff --git a/lib/hydrasdr/hydrasdr_source_c.h b/lib/hydrasdr/hydrasdr_source_c.h
new file mode 100644
index 0000000..7541dfb
--- /dev/null
+++ b/lib/hydrasdr/hydrasdr_source_c.h
@@ -0,0 +1,154 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2013 Dimitri Stolnikov <horiz0n(a)gmx.net>
+ * Copyright 2025 Adapted to HydraSDR by Sébastien Dudek @
Penthertz.com
+ *
+ * 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.
+ */
+#ifndef INCLUDED_HYDRASDR_SOURCE_C_H
+#define INCLUDED_HYDRASDR_SOURCE_C_H
+
+#include <boost/circular_buffer.hpp>
+
+#include <mutex>
+#include <condition_variable>
+
+#include <gnuradio/sync_block.h>
+
+#include "hydrasdr_wrapper.h"
+
+#include "source_iface.h"
+
+class hydrasdr_source_c;
+
+/*
+ * We use std::shared_ptr's instead of raw pointers for all access
+ * to gr::blocks (and many other data structures). The shared_ptr gets
+ * us transparent reference counting, which greatly simplifies storage
+ * management issues. This is especially helpful in our hybrid
+ * C++ / Python system.
+ *
+ * See
http://www.boost.org/libs/smart_ptr/smart_ptr.htm
+ *
+ * As a convention, the _sptr suffix indicates a std::shared_ptr
+ */
+typedef std::shared_ptr<hydrasdr_source_c> hydrasdr_source_c_sptr;
+
+/*!
+ * \brief Return a shared_ptr to a new instance of hydrasdr_source_c.
+ *
+ * To avoid accidental use of raw pointers, hydrasdr_source_c's
+ * constructor is private. make_hydrasdr_source_c is the public
+ * interface for creating new instances.
+ */
+hydrasdr_source_c_sptr make_hydrasdr_source_c (const std::string & args =
"");
+
+/*!
+ * \brief Provides a stream of complex samples.
+ * \ingroup block
+ */
+class hydrasdr_source_c :
+ public gr::sync_block,
+ public source_iface
+{
+private:
+ // The friend declaration allows make_hydrasdr_source_c to
+ // access the private constructor.
+
+ friend hydrasdr_source_c_sptr make_hydrasdr_source_c (const std::string & args);
+
+ /*!
+ * \brief Provides a stream of complex samples.
+ */
+ hydrasdr_source_c (const std::string & args); // private constructor
+
+public:
+ ~hydrasdr_source_c (); // public destructor
+
+ bool start();
+ bool stop();
+
+ int work( int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items );
+
+ static std::vector< std::string > 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_lna_gain( double gain, size_t chan = 0 );
+ double set_mix_gain(double gain, 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 );
+ osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 );
+
+private:
+ static int _hydrasdr_rx_callback(hydrasdr_transfer* transfer);
+ int hydrasdr_rx_callback(void *samples, int sample_count);
+
+ hydrasdr_device *_dev;
+
+ boost::circular_buffer<gr_complex> *_fifo;
+ std::mutex _fifo_lock;
+ std::condition_variable _samp_avail;
+
+ std::vector< std::pair<double, uint32_t> > _sample_rates;
+ double _sample_rate;
+ double _center_freq;
+ double _freq_corr;
+ bool _auto_gain;
+ double _gain;
+ enum gain_policy
+ {
+ linearity,
+ sensitivity
+ };
+ enum gain_policy _gain_policy;
+ double _lna_gain;
+ double _mix_gain;
+ double _vga_gain;
+ double _bandwidth;
+};
+
+#endif /* INCLUDED_HYDRASDR_SOURCE_C_H */
diff --git a/lib/hydrasdr/hydrasdr_wrapper.h b/lib/hydrasdr/hydrasdr_wrapper.h
new file mode 100644
index 0000000..bc44d48
--- /dev/null
+++ b/lib/hydrasdr/hydrasdr_wrapper.h
@@ -0,0 +1,102 @@
+#ifndef HYDRASDR_WRAPPER_H
+#define HYDRASDR_WRAPPER_H
+
+// Remap ALL conflicting symbols for HYDRASDR
+#define RECEIVER_MODE_OFF HYDRA_RECEIVER_MODE_OFF
+#define RECEIVER_MODE_RX HYDRA_RECEIVER_MODE_RX
+#define receiver_mode_t hydra_receiver_mode_t
+
+#define GPIO_PORT0 HYDRA_GPIO_PORT0
+#define GPIO_PORT1 HYDRA_GPIO_PORT1
+#define GPIO_PORT2 HYDRA_GPIO_PORT2
+#define GPIO_PORT3 HYDRA_GPIO_PORT3
+#define GPIO_PORT4 HYDRA_GPIO_PORT4
+#define GPIO_PORT5 HYDRA_GPIO_PORT5
+#define GPIO_PORT6 HYDRA_GPIO_PORT6
+#define GPIO_PORT7 HYDRA_GPIO_PORT7
+
+#define GPIO_PIN0 HYDRA_GPIO_PIN0
+#define GPIO_PIN1 HYDRA_GPIO_PIN1
+#define GPIO_PIN2 HYDRA_GPIO_PIN2
+#define GPIO_PIN3 HYDRA_GPIO_PIN3
+#define GPIO_PIN4 HYDRA_GPIO_PIN4
+#define GPIO_PIN5 HYDRA_GPIO_PIN5
+#define GPIO_PIN6 HYDRA_GPIO_PIN6
+#define GPIO_PIN7 HYDRA_GPIO_PIN7
+#define GPIO_PIN8 HYDRA_GPIO_PIN8
+#define GPIO_PIN9 HYDRA_GPIO_PIN9
+#define GPIO_PIN10 HYDRA_GPIO_PIN10
+#define GPIO_PIN11 HYDRA_GPIO_PIN11
+#define GPIO_PIN12 HYDRA_GPIO_PIN12
+#define GPIO_PIN13 HYDRA_GPIO_PIN13
+#define GPIO_PIN14 HYDRA_GPIO_PIN14
+#define GPIO_PIN15 HYDRA_GPIO_PIN15
+#define GPIO_PIN16 HYDRA_GPIO_PIN16
+#define GPIO_PIN17 HYDRA_GPIO_PIN17
+#define GPIO_PIN18 HYDRA_GPIO_PIN18
+#define GPIO_PIN19 HYDRA_GPIO_PIN19
+#define GPIO_PIN20 HYDRA_GPIO_PIN20
+#define GPIO_PIN21 HYDRA_GPIO_PIN21
+#define GPIO_PIN22 HYDRA_GPIO_PIN22
+#define GPIO_PIN23 HYDRA_GPIO_PIN23
+#define GPIO_PIN24 HYDRA_GPIO_PIN24
+#define GPIO_PIN25 HYDRA_GPIO_PIN25
+#define GPIO_PIN26 HYDRA_GPIO_PIN26
+#define GPIO_PIN27 HYDRA_GPIO_PIN27
+#define GPIO_PIN28 HYDRA_GPIO_PIN28
+#define GPIO_PIN29 HYDRA_GPIO_PIN29
+#define GPIO_PIN30 HYDRA_GPIO_PIN30
+#define GPIO_PIN31 HYDRA_GPIO_PIN31
+
+extern "C" {
+#include <libhydrasdr/hydrasdr.h>
+}
+
+// Undefine ALL remapped symbols
+#undef RECEIVER_MODE_OFF
+#undef RECEIVER_MODE_RX
+#undef receiver_mode_t
+
+#undef GPIO_PORT0
+#undef GPIO_PORT1
+#undef GPIO_PORT2
+#undef GPIO_PORT3
+#undef GPIO_PORT4
+#undef GPIO_PORT5
+#undef GPIO_PORT6
+#undef GPIO_PORT7
+
+#undef GPIO_PIN0
+#undef GPIO_PIN1
+#undef GPIO_PIN2
+#undef GPIO_PIN3
+#undef GPIO_PIN4
+#undef GPIO_PIN5
+#undef GPIO_PIN6
+#undef GPIO_PIN7
+#undef GPIO_PIN8
+#undef GPIO_PIN9
+#undef GPIO_PIN10
+#undef GPIO_PIN11
+#undef GPIO_PIN12
+#undef GPIO_PIN13
+#undef GPIO_PIN14
+#undef GPIO_PIN15
+#undef GPIO_PIN16
+#undef GPIO_PIN17
+#undef GPIO_PIN18
+#undef GPIO_PIN19
+#undef GPIO_PIN20
+#undef GPIO_PIN21
+#undef GPIO_PIN22
+#undef GPIO_PIN23
+#undef GPIO_PIN24
+#undef GPIO_PIN25
+#undef GPIO_PIN26
+#undef GPIO_PIN27
+#undef GPIO_PIN28
+#undef GPIO_PIN29
+#undef GPIO_PIN30
+#undef GPIO_PIN31
+
+#endif // HYDRASDR_WRAPPER_H
diff --git a/test_find.cmake b/test_find.cmake
new file mode 100644
index 0000000..74a7a40
--- /dev/null
+++ b/test_find.cmake
@@ -0,0 +1,15 @@
+FIND_PATH(
+ LIBHYDRASDR_INCLUDE_DIRS
+ NAMES libhydrasdr/hydrasdr.h
+ PATHS /usr/include
+)
+
+FIND_LIBRARY(
+ LIBHYDRASDR_LIBRARIES
+ NAMES hydrasdr
+ PATHS /usr/lib
+ /lib
+)
+
+message("LIBHYDRASDR_INCLUDE_DIRS: ${LIBHYDRASDR_INCLUDE_DIRS}")
+message("LIBHYDRASDR_LIBRARIES: ${LIBHYDRASDR_LIBRARIES}")
diff --git a/thirdparty/libhydrasdr/hydrasdr.h b/thirdparty/libhydrasdr/hydrasdr.h
new file mode 100644
index 0000000..78d7268
--- /dev/null
+++ b/thirdparty/libhydrasdr/hydrasdr.h
@@ -0,0 +1,224 @@
+/*
+Copyright (c) 2012, Jared Boone <jared(a)sharebrained.com>
+Copyright (c) 2013, Michael Ossmann <mike(a)ossmann.com>
+Copyright (C) 2013-2016, Youssef Touil <youssef(a)airspy.com>
+Copyright (c) 2013-2025, Benjamin Vernoux <bvernoux(a)hydrasdr.com>
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+Neither the name of HydraSDR nor the names of its contributors may be used to endorse or
promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
+*/
+
+#ifndef __HYDRASDR_H__
+#define __HYDRASDR_H__
+
+#include <stdint.h>
+#include "hydrasdr_commands.h"
+
+#define HYDRASDR_VERSION "1.0.0"
+#define HYDRASDR_VER_MAJOR 1
+#define HYDRASDR_VER_MINOR 0
+#define HYDRASDR_VER_REVISION 0
+
+#ifdef _WIN32
+ #define ADD_EXPORTS
+
+ /* You should define ADD_EXPORTS *only* when building the DLL. */
+ #ifdef ADD_EXPORTS
+ #define ADDAPI __declspec(dllexport)
+ #else
+ #define ADDAPI __declspec(dllimport)
+ #endif
+
+ /* Define calling convention in one place, for convenience. */
+ #define ADDCALL __cdecl
+
+#else /* _WIN32 not defined. */
+
+ /* Define with no value on non-Windows OSes. */
+ #define ADDAPI
+ #define ADDCALL
+
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum hydrasdr_error
+{
+ HYDRASDR_SUCCESS = 0,
+ HYDRASDR_TRUE = 1,
+ HYDRASDR_ERROR_INVALID_PARAM = -2,
+ HYDRASDR_ERROR_NOT_FOUND = -5,
+ HYDRASDR_ERROR_BUSY = -6,
+ HYDRASDR_ERROR_NO_MEM = -11,
+ HYDRASDR_ERROR_UNSUPPORTED = -12,
+ HYDRASDR_ERROR_LIBUSB = -1000,
+ HYDRASDR_ERROR_THREAD = -1001,
+ HYDRASDR_ERROR_STREAMING_THREAD_ERR = -1002,
+ HYDRASDR_ERROR_STREAMING_STOPPED = -1003,
+ HYDRASDR_ERROR_OTHER = -9999,
+};
+
+enum hydrasdr_board_id
+{
+ HYDRASDR_BOARD_ID_PROTO_HYDRASDR = 0,
+ HYDRASDR_BOARD_ID_INVALID = 0xFF,
+};
+
+enum hydrasdr_sample_type
+{
+ HYDRASDR_SAMPLE_FLOAT32_IQ = 0, /* 2 * 32bit float per sample */
+ HYDRASDR_SAMPLE_FLOAT32_REAL = 1, /* 1 * 32bit float per sample */
+ HYDRASDR_SAMPLE_INT16_IQ = 2, /* 2 * 16bit int per sample */
+ HYDRASDR_SAMPLE_INT16_REAL = 3, /* 1 * 16bit int per sample */
+ HYDRASDR_SAMPLE_UINT16_REAL = 4, /* 1 * 16bit unsigned int per sample */
+ HYDRASDR_SAMPLE_RAW = 5, /* Raw packed samples from the device */
+ HYDRASDR_SAMPLE_END = 6 /* Number of supported sample types */
+};
+
+#define MAX_CONFIG_PAGE_SIZE (0x10000)
+
+struct hydrasdr_device;
+
+typedef struct {
+ struct hydrasdr_device* device;
+ void* ctx;
+ void* samples;
+ int sample_count;
+ uint64_t dropped_samples;
+ enum hydrasdr_sample_type sample_type;
+} hydrasdr_transfer_t, hydrasdr_transfer;
+
+typedef struct {
+ uint32_t part_id[2];
+ uint32_t serial_no[4];
+} hydrasdr_read_partid_serialno_t;
+
+typedef struct {
+ uint32_t major_version;
+ uint32_t minor_version;
+ uint32_t revision;
+} hydrasdr_lib_version_t;
+
+typedef int (*hydrasdr_sample_block_cb_fn)(hydrasdr_transfer* transfer);
+
+extern ADDAPI void ADDCALL hydrasdr_lib_version(hydrasdr_lib_version_t* lib_version);
+
+extern ADDAPI int ADDCALL hydrasdr_list_devices(uint64_t *serials, int count);
+
+extern ADDAPI int ADDCALL hydrasdr_open_sn(struct hydrasdr_device** device, uint64_t
serial_number);
+extern ADDAPI int ADDCALL hydrasdr_open_fd(struct hydrasdr_device** device, int fd);
+extern ADDAPI int ADDCALL hydrasdr_open(struct hydrasdr_device** device);
+extern ADDAPI int ADDCALL hydrasdr_close(struct hydrasdr_device* device);
+
+/* Use hydrasdr_get_samplerates(device, buffer, 0) to get the number of available sample
rates. It will be returned in the first element of buffer */
+extern ADDAPI int ADDCALL hydrasdr_get_samplerates(struct hydrasdr_device* device,
uint32_t* buffer, const uint32_t len);
+
+/* Parameter samplerate can be either the index of a samplerate or directly its value in
Hz within the list returned by hydrasdr_get_samplerates() */
+extern ADDAPI int ADDCALL hydrasdr_set_samplerate(struct hydrasdr_device* device,
uint32_t samplerate);
+
+extern ADDAPI int ADDCALL hydrasdr_set_conversion_filter_float32(struct hydrasdr_device*
device, const float *kernel, const uint32_t len);
+extern ADDAPI int ADDCALL hydrasdr_set_conversion_filter_int16(struct hydrasdr_device*
device, const int16_t *kernel, const uint32_t len);
+
+extern ADDAPI int ADDCALL hydrasdr_start_rx(struct hydrasdr_device* device,
hydrasdr_sample_block_cb_fn callback, void* rx_ctx);
+extern ADDAPI int ADDCALL hydrasdr_stop_rx(struct hydrasdr_device* device);
+
+/* return HYDRASDR_TRUE if success */
+extern ADDAPI int ADDCALL hydrasdr_is_streaming(struct hydrasdr_device* device);
+
+extern ADDAPI int ADDCALL hydrasdr_si5351c_write(struct hydrasdr_device* device, uint8_t
register_number, uint8_t value);
+extern ADDAPI int ADDCALL hydrasdr_si5351c_read(struct hydrasdr_device* device, uint8_t
register_number, uint8_t* value);
+
+extern ADDAPI int ADDCALL hydrasdr_config_write(struct hydrasdr_device* device, const
uint8_t page_index, const uint16_t length, unsigned char *data);
+extern ADDAPI int ADDCALL hydrasdr_config_read(struct hydrasdr_device* device, const
uint8_t page_index, const uint16_t length, unsigned char *data);
+
+extern ADDAPI int ADDCALL hydrasdr_r82x_write(struct hydrasdr_device* device, uint8_t
register_number, uint8_t value);
+extern ADDAPI int ADDCALL hydrasdr_r82x_read(struct hydrasdr_device* device, uint8_t
register_number, uint8_t* value);
+
+/* Parameter value shall be 0=clear GPIO or 1=set GPIO */
+extern ADDAPI int ADDCALL hydrasdr_gpio_write(struct hydrasdr_device* device,
hydrasdr_gpio_port_t port, hydrasdr_gpio_pin_t pin, uint8_t value);
+/* Parameter value corresponds to GPIO state 0 or 1 */
+extern ADDAPI int ADDCALL hydrasdr_gpio_read(struct hydrasdr_device* device,
hydrasdr_gpio_port_t port, hydrasdr_gpio_pin_t pin, uint8_t* value);
+
+/* Parameter value shall be 0=GPIO Input direction or 1=GPIO Output direction */
+extern ADDAPI int ADDCALL hydrasdr_gpiodir_write(struct hydrasdr_device* device,
hydrasdr_gpio_port_t port, hydrasdr_gpio_pin_t pin, uint8_t value);
+extern ADDAPI int ADDCALL hydrasdr_gpiodir_read(struct hydrasdr_device* device,
hydrasdr_gpio_port_t port, hydrasdr_gpio_pin_t pin, uint8_t* value);
+
+extern ADDAPI int ADDCALL hydrasdr_spiflash_erase(struct hydrasdr_device* device);
+extern ADDAPI int ADDCALL hydrasdr_spiflash_write(struct hydrasdr_device* device, const
uint32_t address, const uint16_t length, unsigned char* const data);
+extern ADDAPI int ADDCALL hydrasdr_spiflash_read(struct hydrasdr_device* device, const
uint32_t address, const uint16_t length, unsigned char* data);
+
+extern ADDAPI int ADDCALL hydrasdr_board_id_read(struct hydrasdr_device* device, uint8_t*
value);
+/* Parameter length shall be at least 128bytes to avoid possible string clipping */
+extern ADDAPI int ADDCALL hydrasdr_version_string_read(struct hydrasdr_device* device,
char* version, uint8_t length);
+
+extern ADDAPI int ADDCALL hydrasdr_board_partid_serialno_read(struct hydrasdr_device*
device, hydrasdr_read_partid_serialno_t* read_partid_serialno);
+
+extern ADDAPI int ADDCALL hydrasdr_set_sample_type(struct hydrasdr_device* device, enum
hydrasdr_sample_type sample_type);
+
+/* Parameter freq_hz shall be between 24000000(24MHz) and 1800000000(1.8GHz) and more
with extensions */
+extern ADDAPI int ADDCALL hydrasdr_set_freq(struct hydrasdr_device* device, const
uint64_t freq_hz);
+
+/* Parameter value shall be between 0 and 15 */
+extern ADDAPI int ADDCALL hydrasdr_set_lna_gain(struct hydrasdr_device* device, uint8_t
value);
+
+/* Parameter value shall be between 0 and 15 */
+extern ADDAPI int ADDCALL hydrasdr_set_mixer_gain(struct hydrasdr_device* device, uint8_t
value);
+
+/* Parameter value shall be between 0 and 15 */
+extern ADDAPI int ADDCALL hydrasdr_set_vga_gain(struct hydrasdr_device* device, uint8_t
value);
+
+/* Parameter value:
+ 0=Disable LNA Automatic Gain Control
+ 1=Enable LNA Automatic Gain Control
+*/
+extern ADDAPI int ADDCALL hydrasdr_set_lna_agc(struct hydrasdr_device* device, uint8_t
value);
+/* Parameter value:
+ 0=Disable MIXER Automatic Gain Control
+ 1=Enable MIXER Automatic Gain Control
+*/
+extern ADDAPI int ADDCALL hydrasdr_set_mixer_agc(struct hydrasdr_device* device, uint8_t
value);
+
+/* Parameter value: 0..21 */
+extern ADDAPI int ADDCALL hydrasdr_set_linearity_gain(struct hydrasdr_device* device,
uint8_t value);
+
+/* Parameter value: 0..21 */
+extern ADDAPI int ADDCALL hydrasdr_set_sensitivity_gain(struct hydrasdr_device* device,
uint8_t value);
+
+/* Parameter value shall be 0=Disable BiasT or 1=Enable BiasT */
+extern ADDAPI int ADDCALL hydrasdr_set_rf_bias(struct hydrasdr_device* dev, uint8_t
value);
+
+/* Parameter value shall be 0=Disable Packing or 1=Enable Packing */
+extern ADDAPI int ADDCALL hydrasdr_set_packing(struct hydrasdr_device* device, uint8_t
value);
+
+extern ADDAPI const char* ADDCALL hydrasdr_error_name(enum hydrasdr_error errcode);
+extern ADDAPI const char* ADDCALL hydrasdr_board_id_name(enum hydrasdr_board_id
board_id);
+
+/* Parameter sector_num shall be between 2 & 13 (sector 0 & 1 are reserved for
the firmware) */
+extern ADDAPI int ADDCALL hydrasdr_spiflash_erase_sector(struct hydrasdr_device* device,
const uint16_t sector_num);
+
+extern ADDAPI int ADDCALL hydrasdr_reset(struct hydrasdr_device* device);
+
+extern ADDAPI int ADDCALL hydrasdr_set_rf_port(struct hydrasdr_device* device,
hydrasdr_rf_port_t rf_port);
+
+#ifdef __cplusplus
+} // __cplusplus defined.
+#endif
+
+#endif//__HYDRASDR_H__
diff --git a/thirdparty/libhydrasdr/hydrasdr_commands.h
b/thirdparty/libhydrasdr/hydrasdr_commands.h
new file mode 100644
index 0000000..8ccf6cb
--- /dev/null
+++ b/thirdparty/libhydrasdr/hydrasdr_commands.h
@@ -0,0 +1,132 @@
+/*
+Copyright (c) 2013-2025 Benjamin Vernoux <bvernoux(a)hydrasdr.com>
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+Neither the name of HydraSDR nor the names of its contributors may be used to endorse or
promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
+*/
+
+#ifndef __HYDRASDR_COMMANDS_H__
+#define __HYDRASDR_COMMANDS_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef enum
+{
+ RECEIVER_MODE_OFF = 0,
+ RECEIVER_MODE_RX = 1
+} receiver_mode_t;
+
+// Commands (usb vendor request) shared between Firmware and Host.
+#define HYDRASDR_CMD_MAX (28)
+typedef enum
+{
+ HYDRASDR_RESET = 0,
+ HYDRASDR_RECEIVER_MODE = 1,
+ HYDRASDR_SI5351C_WRITE = 2,
+ HYDRASDR_SI5351C_READ = 3,
+ HYDRASDR_R82X_WRITE = 4,
+ HYDRASDR_R82X_READ = 5,
+ HYDRASDR_SPIFLASH_ERASE = 6,
+ HYDRASDR_SPIFLASH_WRITE = 7,
+ HYDRASDR_SPIFLASH_READ = 8,
+ HYDRASDR_BOARD_ID_READ = 9,
+ HYDRASDR_VERSION_STRING_READ = 10,
+ HYDRASDR_BOARD_PARTID_SERIALNO_READ = 11,
+ HYDRASDR_SET_SAMPLERATE = 12,
+ HYDRASDR_SET_FREQ = 13,
+ HYDRASDR_SET_LNA_GAIN = 14,
+ HYDRASDR_SET_MIXER_GAIN = 15,
+ HYDRASDR_SET_VGA_GAIN = 16,
+ HYDRASDR_SET_LNA_AGC = 17,
+ HYDRASDR_SET_MIXER_AGC = 18,
+ HYDRASDR_MS_VENDOR_CMD = 19,
+ HYDRASDR_SET_RF_BIAS_CMD = 20,
+ HYDRASDR_GPIO_WRITE = 21,
+ HYDRASDR_GPIO_READ = 22,
+ HYDRASDR_GPIODIR_WRITE = 23,
+ HYDRASDR_GPIODIR_READ = 24,
+ HYDRASDR_GET_SAMPLERATES = 25,
+ HYDRASDR_SET_PACKING = 26,
+ HYDRASDR_SPIFLASH_ERASE_SECTOR = 27,
+ HYDRASDR_SET_RF_PORT = HYDRASDR_CMD_MAX
+} hydrasdr_vendor_request;
+
+typedef enum
+{
+ GPIO_PORT0 = 0,
+ GPIO_PORT1 = 1,
+ GPIO_PORT2 = 2,
+ GPIO_PORT3 = 3,
+ GPIO_PORT4 = 4,
+ GPIO_PORT5 = 5,
+ GPIO_PORT6 = 6,
+ GPIO_PORT7 = 7
+} hydrasdr_gpio_port_t;
+
+typedef enum
+{
+ GPIO_PIN0 = 0,
+ GPIO_PIN1 = 1,
+ GPIO_PIN2 = 2,
+ GPIO_PIN3 = 3,
+ GPIO_PIN4 = 4,
+ GPIO_PIN5 = 5,
+ GPIO_PIN6 = 6,
+ GPIO_PIN7 = 7,
+ GPIO_PIN8 = 8,
+ GPIO_PIN9 = 9,
+ GPIO_PIN10 = 10,
+ GPIO_PIN11 = 11,
+ GPIO_PIN12 = 12,
+ GPIO_PIN13 = 13,
+ GPIO_PIN14 = 14,
+ GPIO_PIN15 = 15,
+ GPIO_PIN16 = 16,
+ GPIO_PIN17 = 17,
+ GPIO_PIN18 = 18,
+ GPIO_PIN19 = 19,
+ GPIO_PIN20 = 20,
+ GPIO_PIN21 = 21,
+ GPIO_PIN22 = 22,
+ GPIO_PIN23 = 23,
+ GPIO_PIN24 = 24,
+ GPIO_PIN25 = 25,
+ GPIO_PIN26 = 26,
+ GPIO_PIN27 = 27,
+ GPIO_PIN28 = 28,
+ GPIO_PIN29 = 29,
+ GPIO_PIN30 = 30,
+ GPIO_PIN31 = 31
+} hydrasdr_gpio_pin_t;
+
+typedef enum
+{
+ RF_PORT_RX0 = 0, /* RX Channel 0 (default/standard AIR_IN called ANT) */
+ RF_PORT_RX1 = 1, /* RX Channel 1 (called also CABLE1) */
+ RF_PORT_RX2 = 2 /* RX Channel 2 (called also CABLE2) */
+} hydrasdr_rf_port_t;
+
+#ifdef __cplusplus
+} // __cplusplus defined.
+#endif
+
+#endif//__HYDRASDR_COMMANDS_H__
############################################
Thank you in advance and feel free if you have a cleaner way that work than the wrappers I
made.
Best regards,
Seb