From ae2253c516bfdc9ae4575ecd61fe0e6cd8608a0d Mon Sep 17 00:00:00 2001 From: Clayton Smith Date: Sun, 26 Jan 2020 18:48:59 -0500 Subject: [PATCH] hackrf: allow transmit/receive switching To allow switching between transmit and receive mode within a single flow graph, I've followed the model used by the BladeRF: I factored common code into hackrf_common.cc and hackrf_common.h, and created a "_devs" map there to keep track of which devices have been previously opened. Shared pointers are used to track how many source & sink blocks are using a particular device. Because some properties (center frequency, sample rate, bandwidth, RF amplifier state, bias tee) are shared between receive and transmit, the blocks defer setting these until receiving or transmitting is started. That way a source and sink can safely use different values for these properties, and the correct values are set during T/R switching. Because the HackRF driver and/or hardware does not seem to fully transmit all samples sent to it, the code adds some silence to the end of transmissions to ensure all samples are transmitted. I've also replaced boost with C++11 code where possible. From: Clayton Smith Signed-off-by: Sylvain Munaut --- lib/hackrf/CMakeLists.txt | 10 +- lib/hackrf/hackrf_common.cc | 432 ++++++++++++++++++++++++++++++++++ lib/hackrf/hackrf_common.h | 111 +++++++++ lib/hackrf/hackrf_sink_c.cc | 414 +++++++------------------------- lib/hackrf/hackrf_sink_c.h | 35 +-- lib/hackrf/hackrf_source_c.cc | 405 ++++--------------------------- lib/hackrf/hackrf_source_c.h | 36 +-- 7 files changed, 692 insertions(+), 751 deletions(-) create mode 100644 lib/hackrf/hackrf_common.cc create mode 100644 lib/hackrf/hackrf_common.h diff --git a/lib/hackrf/CMakeLists.txt b/lib/hackrf/CMakeLists.txt index b817518..d099b41 100644 --- a/lib/hackrf/CMakeLists.txt +++ b/lib/hackrf/CMakeLists.txt @@ -31,16 +31,8 @@ target_link_libraries(gnuradio-osmosdr ) list(APPEND gr_osmosdr_srcs + ${CMAKE_CURRENT_SOURCE_DIR}/hackrf_common.cc ${CMAKE_CURRENT_SOURCE_DIR}/hackrf_source_c.cc ${CMAKE_CURRENT_SOURCE_DIR}/hackrf_sink_c.cc ) set(gr_osmosdr_srcs ${gr_osmosdr_srcs} PARENT_SCOPE) - -include(CheckFunctionExists) -set(CMAKE_REQUIRED_LIBRARIES ${LIBHACKRF_LIBRARIES}) -CHECK_FUNCTION_EXISTS(hackrf_device_list LIBHACKRF_HAVE_DEVICE_LIST) - -if(LIBHACKRF_HAVE_DEVICE_LIST) - message(STATUS "HackRF multiple device support enabled") - target_compile_definitions(gnuradio-osmosdr PRIVATE -DLIBHACKRF_HAVE_DEVICE_LIST) -endif(LIBHACKRF_HAVE_DEVICE_LIST) diff --git a/lib/hackrf/hackrf_common.cc b/lib/hackrf/hackrf_common.cc new file mode 100644 index 0000000..0a20f2e --- /dev/null +++ b/lib/hackrf/hackrf_common.cc @@ -0,0 +1,432 @@ +/* -*- c++ -*- */ +/* + * Copyright 2020 Clayton Smith + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "hackrf_common.h" + +#include "arg_helpers.h" + +using namespace boost::assign; + +int hackrf_common::_usage = 0; +std::mutex hackrf_common::_usage_mutex; + +std::map> hackrf_common::_devs; +std::mutex hackrf_common::_devs_mutex; + +hackrf_common::hackrf_common(const std::string &args) : + _dev(NULL), + _sample_rate(0), + _center_freq(0), + _freq_corr(0), + _auto_gain(false), + _bandwidth(0), + _bias(false), + _started(false) +{ + int ret; + hackrf_device *raw_dev; + hackrf_device_list_t *list; + int dev_index; + std::string target_serial = "0"; + std::string final_serial = ""; + + dict_t dict = params_to_dict(args); + if (dict.count("hackrf") > 0 && dict["hackrf"].length() > 0) { + target_serial = dict["hackrf"]; + } + + { + std::lock_guard guard(_usage_mutex); + + if (_usage == 0) { + hackrf_init(); /* call only once before the first open */ + } + + _usage++; + } + + list = hackrf_device_list(); + + if (target_serial.length() > 1) { + for (dev_index = 0; dev_index < list->devicecount; dev_index++) { + if (list->serial_numbers[dev_index]) { + std::string serial(list->serial_numbers[dev_index]); + if (serial.compare(serial.length() - target_serial.length(), + target_serial.length(), target_serial) == 0) { + break; + } + } + } + + if (dev_index >= list->devicecount) { + hackrf_device_list_free(list); + throw std::runtime_error( + "No device found with serial number '" + target_serial + "'"); + } + } else { + try { + dev_index = std::stoi(target_serial); + } catch (std::exception &ex) { + hackrf_device_list_free(list); + throw std::runtime_error( + "Failed to use '" + target_serial + "' as HackRF device index number"); + } + + if (dev_index >= list->devicecount) { + hackrf_device_list_free(list); + throw std::runtime_error( + "Failed to use '" + target_serial + "' as HackRF device index: not enough devices"); + } + } + + if (list->serial_numbers[dev_index]) { + final_serial = list->serial_numbers[dev_index]; + } + + { + std::lock_guard guard(_devs_mutex); + + if (_devs.count(final_serial) > 0 && !_devs[final_serial].expired()) { + _dev = hackrf_sptr(_devs[final_serial]); + } else { + ret = hackrf_device_list_open(list, dev_index, &raw_dev); + HACKRF_THROW_ON_ERROR(ret, "Failed to open HackRF device") + _dev = hackrf_sptr(raw_dev, hackrf_common::close); + _devs[final_serial] = static_cast>(_dev); + } + } + + hackrf_device_list_free(list); + + uint8_t board_id; + ret = hackrf_board_id_read(_dev.get(), &board_id); + HACKRF_THROW_ON_ERROR(ret, "Failed to get HackRF board id") + + char version[40]; + memset(version, 0, sizeof(version)); + ret = hackrf_version_string_read(_dev.get(), version, sizeof(version)); + HACKRF_THROW_ON_ERROR(ret, "Failed to read version string") + + std::cerr << "Using " << hackrf_board_id_name(hackrf_board_id(board_id)) << " " + << "with firmware " << version + << std::endl; +} + +void hackrf_common::close(void *dev) +{ + int ret = hackrf_close(static_cast(dev)); + if (ret != HACKRF_SUCCESS) + { + std::cerr << HACKRF_FORMAT_ERROR(ret, "Failed to close HackRF") << std::endl; + } + + { + std::lock_guard guard(_usage_mutex); + + _usage--; + + if (_usage == 0) { + hackrf_exit(); /* call only once after last close */ + } + } +} + +std::vector hackrf_common::get_devices() +{ + std::vector devices; + std::string label; + + { + std::lock_guard guard(_usage_mutex); + + if (_usage == 0) { + hackrf_init(); /* call only once before the first open */ + } + + _usage++; + } + + hackrf_device_list_t *list = hackrf_device_list(); + + for (int i = 0; i < list->devicecount; i++) { + label = "HackRF "; + label += hackrf_usb_board_id_name(list->usb_board_ids[i]); + + std::string args; + if (list->serial_numbers[i]) { + std::string serial(list->serial_numbers[i]); + if (serial.length() > 6) + serial = serial.substr(serial.length() - 6, 6); + args = "hackrf=" + serial; + if (serial.length() ) + label += " " + serial; + } else { + args = "hackrf"; /* will pick the first one, serial number is required for choosing a specific one */ + } + + args += ",label='" + label + "'"; + devices.push_back(args); + } + + hackrf_device_list_free(list); + + { + std::lock_guard guard(_usage_mutex); + + _usage--; + + if (_usage == 0) { + hackrf_exit(); /* call only once after last close */ + } + } + + return devices; +} + +osmosdr::meta_range_t hackrf_common::get_sample_rates() +{ + osmosdr::meta_range_t range; + + /* we only add integer rates here because of better phase noise performance. + * the user is allowed to request arbitrary (fractional) rates within these + * boundaries. */ + + range += osmosdr::range_t( 8e6 ); + range += osmosdr::range_t( 10e6 ); + range += osmosdr::range_t( 12.5e6 ); + range += osmosdr::range_t( 16e6 ); + range += osmosdr::range_t( 20e6 ); /* confirmed to work on fast machines */ + + return range; +} + +double hackrf_common::set_sample_rate( double rate ) +{ + int ret; + + if (_dev.get() && _started) { + ret = hackrf_set_sample_rate( _dev.get(), rate ); + if ( HACKRF_SUCCESS != ret ) { + HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_sample_rate", rate ) ) + } + } + + _sample_rate = rate; + return get_sample_rate(); +} + +double hackrf_common::get_sample_rate() +{ + return _sample_rate; +} + +osmosdr::freq_range_t hackrf_common::get_freq_range( size_t chan ) +{ + osmosdr::freq_range_t range; + + range += osmosdr::range_t( _sample_rate / 2, 7250e6 - _sample_rate / 2 ); + + return range; +} + +double hackrf_common::set_center_freq( double freq, size_t chan ) +{ + int ret; + + #define APPLY_PPM_CORR(val, ppm) ((val) * (1.0 + (ppm) * 0.000001)) + + if (_dev.get() && _started) { + double corr_freq = APPLY_PPM_CORR( freq, _freq_corr ); + ret = hackrf_set_freq( _dev.get(), uint64_t(corr_freq) ); + if ( HACKRF_SUCCESS != ret ) { + HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_freq", corr_freq ) ) + } + } + + _center_freq = freq; + return get_center_freq( chan ); +} + +double hackrf_common::get_center_freq( size_t chan ) +{ + return _center_freq; +} + +double hackrf_common::set_freq_corr( double ppm, size_t chan ) +{ + _freq_corr = ppm; + + set_center_freq( _center_freq ); + + return get_freq_corr( chan ); +} + +double hackrf_common::get_freq_corr( size_t chan ) +{ + return _freq_corr; +} + +bool hackrf_common::set_gain_mode( bool automatic, size_t chan ) +{ + _auto_gain = automatic; + + return get_gain_mode(chan); +} + +bool hackrf_common::get_gain_mode( size_t chan ) +{ + return _auto_gain; +} + +double hackrf_common::set_gain( double gain, size_t chan ) +{ + int ret; + double clip_gain = (gain >= 14.0) ? 14.0 : 0.0; + + if (_dev.get() && _started) { + uint8_t value = (clip_gain == 14.0) ? 1 : 0; + + ret = hackrf_set_amp_enable( _dev.get(), value ); + if ( HACKRF_SUCCESS != ret ) { + HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_amp_enable", value ) ) + } + } + + _amp_gain = clip_gain; + return hackrf_common::get_gain(chan); +} + +double hackrf_common::get_gain( size_t chan ) +{ + return _amp_gain; +} + +std::vector< std::string > hackrf_common::get_antennas( size_t chan ) +{ + std::vector< std::string > antennas; + + antennas += get_antenna( chan ); + + return antennas; +} + +std::string hackrf_common::set_antenna( const std::string & antenna, size_t chan ) +{ + return get_antenna( chan ); +} + +std::string hackrf_common::get_antenna( size_t chan ) +{ + return "TX/RX"; +} + +double hackrf_common::set_bandwidth( double bandwidth, size_t chan ) +{ + int ret; +// osmosdr::freq_range_t bandwidths = get_bandwidth_range( chan ); + + if ( bandwidth == 0.0 ) /* bandwidth of 0 means automatic filter selection */ + bandwidth = _sample_rate * 0.75; /* select narrower filters to prevent aliasing */ + + /* compute best default value depending on sample rate (auto filter) */ + uint32_t bw = hackrf_compute_baseband_filter_bw( uint32_t(bandwidth) ); + + if (_dev.get() && _started) { + ret = hackrf_set_baseband_filter_bandwidth( _dev.get(), bw ); + if (HACKRF_SUCCESS != ret) { + HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_baseband_filter_bandwidth", bw ) ) + } + } + + _bandwidth = bw; + return get_bandwidth(chan); +} + +double hackrf_common::get_bandwidth( size_t chan ) +{ + return _bandwidth; +} + +osmosdr::freq_range_t hackrf_common::get_bandwidth_range( size_t chan ) +{ + osmosdr::freq_range_t bandwidths; + + // TODO: read out from libhackrf when an API is available + + bandwidths += osmosdr::range_t( 1750000 ); + bandwidths += osmosdr::range_t( 2500000 ); + bandwidths += osmosdr::range_t( 3500000 ); + bandwidths += osmosdr::range_t( 5000000 ); + bandwidths += osmosdr::range_t( 5500000 ); + bandwidths += osmosdr::range_t( 6000000 ); + bandwidths += osmosdr::range_t( 7000000 ); + bandwidths += osmosdr::range_t( 8000000 ); + bandwidths += osmosdr::range_t( 9000000 ); + bandwidths += osmosdr::range_t( 10000000 ); + bandwidths += osmosdr::range_t( 12000000 ); + bandwidths += osmosdr::range_t( 14000000 ); + bandwidths += osmosdr::range_t( 15000000 ); + bandwidths += osmosdr::range_t( 20000000 ); + bandwidths += osmosdr::range_t( 24000000 ); + bandwidths += osmosdr::range_t( 28000000 ); + + return bandwidths; +} + +bool hackrf_common::set_bias( bool bias ) +{ + int ret; + + if (_dev.get() && _started) { + ret = hackrf_set_antenna_enable(_dev.get(), static_cast(bias)); + if (ret != HACKRF_SUCCESS) + { + std::cerr << "Failed to apply antenna bias voltage state: " << bias << HACKRF_FORMAT_ERROR(ret, "") << std::endl; + } + } + + _bias = bias; + return get_bias(); +} + +bool hackrf_common::get_bias() +{ + return _bias; +} + +void hackrf_common::start() +{ + _started = true; + set_bandwidth(get_bandwidth()); + set_center_freq(get_center_freq()); + set_sample_rate(get_sample_rate()); + set_gain(get_gain()); + set_bias(get_bias()); +} + +void hackrf_common::stop() +{ + _started = false; +} diff --git a/lib/hackrf/hackrf_common.h b/lib/hackrf/hackrf_common.h new file mode 100644 index 0000000..02eabd2 --- /dev/null +++ b/lib/hackrf/hackrf_common.h @@ -0,0 +1,111 @@ +/* -*- c++ -*- */ +/* + * Copyright 2020 Clayton Smith + * + * 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. + */ +#ifndef INCLUDED_HACKRF_COMMON_H +#define INCLUDED_HACKRF_COMMON_H + +#include +#include +#include +#include + +#include + +#include +#include + +#define BUF_LEN (16 * 32 * 512) /* must be multiple of 512 */ +#define BUF_NUM 15 + +#define BYTES_PER_SAMPLE 2 /* HackRF device produces/consumes 8 bit unsigned IQ data */ + +#define HACKRF_FORMAT_ERROR(ret, msg) \ + boost::str( boost::format(msg " (%1%) %2%") \ + % ret % hackrf_error_name((enum hackrf_error)ret) ) + +#define HACKRF_THROW_ON_ERROR(ret, msg) \ + if ( ret != HACKRF_SUCCESS ) \ + { \ + throw std::runtime_error( HACKRF_FORMAT_ERROR(ret, msg) ); \ + } + +#define HACKRF_FUNC_STR(func, arg) \ + boost::str(boost::format(func "(%1%)") % arg) + " has failed" + +typedef std::shared_ptr hackrf_sptr; + +class hackrf_common +{ +public: + hackrf_common(const std::string &args); + +protected: + static std::vector< std::string > get_devices(); + + 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 ); + + 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 get_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 ); + + bool set_bias( bool bias ); + bool get_bias(); + + void start(); + void stop(); + + hackrf_sptr _dev; + +private: + static void close(void *dev); + + static int _usage; + static std::mutex _usage_mutex; + + static std::map> _devs; + static std::mutex _devs_mutex; + + double _sample_rate; + double _center_freq; + double _freq_corr; + bool _auto_gain; + double _amp_gain; + double _bandwidth; + bool _bias; + bool _started; +}; + +#endif /* INCLUDED_HACKRF_COMMON_H */ diff --git a/lib/hackrf/hackrf_sink_c.cc b/lib/hackrf/hackrf_sink_c.cc index ee089c6..f003b89 100644 --- a/lib/hackrf/hackrf_sink_c.cc +++ b/lib/hackrf/hackrf_sink_c.cc @@ -2,19 +2,20 @@ /* * Copyright 2013 Dimitri Stolnikov * Copyright 2014 Hoernchen + * Copyright 2020 Clayton Smith * - * GNU Radio is free software; you can redistribute it and/or modify + * 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. * - * GNU Radio is distributed in the hope that it will be useful, + * 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 GNU Radio; see the file COPYING. If not, write to + * 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. */ @@ -38,10 +39,6 @@ #endif #include -#include -#include -#include -#include #include @@ -51,24 +48,6 @@ using namespace boost::assign; -#define BUF_LEN (16 * 32 * 512) /* must be multiple of 512 */ -#define BUF_NUM 15 - -#define BYTES_PER_SAMPLE 2 /* HackRF device consumes 8 bit unsigned IQ data */ - -#define HACKRF_FORMAT_ERROR(ret, msg) \ - boost::str( boost::format(msg " (%1%) %2%") \ - % ret % hackrf_error_name((enum hackrf_error)ret) ) - -#define HACKRF_THROW_ON_ERROR(ret, msg) \ - if ( ret != HACKRF_SUCCESS ) \ - { \ - throw std::runtime_error( HACKRF_FORMAT_ERROR(ret, msg) ); \ - } - -#define HACKRF_FUNC_STR(func, arg) \ - boost::str(boost::format(func "(%1%)") % arg) + " has failed" - static inline bool cb_init(circular_buffer_t *cb, size_t capacity, size_t sz) { cb->buffer = malloc(capacity * sz); @@ -103,6 +82,11 @@ static inline bool cb_has_room(circular_buffer_t *cb) return true; } +static inline bool cb_is_empty(circular_buffer_t *cb) +{ + return cb->count == 0; +} + static inline bool cb_push_back(circular_buffer_t *cb, const void *item) { if(cb->count == cb->capacity) @@ -127,9 +111,6 @@ static inline bool cb_pop_front(circular_buffer_t *cb, void *item) return true; } -int hackrf_sink_c::_usage = 0; -boost::mutex hackrf_sink_c::_usage_mutex; - hackrf_sink_c_sptr make_hackrf_sink_c (const std::string & args) { return gnuradio::get_initial_sptr(new hackrf_sink_c (args)); @@ -156,66 +137,21 @@ hackrf_sink_c::hackrf_sink_c (const std::string &args) : gr::sync_block ("hackrf_sink_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), + hackrf_common::hackrf_common(args), _buf(NULL), - _sample_rate(0), - _center_freq(0), - _freq_corr(0), - _auto_gain(false), - _amp_gain(0), - _vga_gain(0), - _bandwidth(0) + _vga_gain(0) { - int ret; - std::string *hackrf_serial = NULL; - dict_t dict = params_to_dict(args); - if (dict.count("hackrf") && dict["hackrf"].length() > 0) - hackrf_serial = &dict["hackrf"]; - _buf_num = 0; if (dict.count("buffers")) - _buf_num = boost::lexical_cast< unsigned int >( dict["buffers"] ); + _buf_num = std::stoi(dict["buffers"]); if (0 == _buf_num) _buf_num = BUF_NUM; - { - boost::mutex::scoped_lock lock( _usage_mutex ); - - if ( _usage == 0 ) - hackrf_init(); /* call only once before the first open */ - - _usage++; - } - - _dev = NULL; -#ifdef LIBHACKRF_HAVE_DEVICE_LIST - if ( hackrf_serial ) - ret = hackrf_open_by_serial( hackrf_serial->c_str(), &_dev ); - else -#endif - ret = hackrf_open( &_dev ); - HACKRF_THROW_ON_ERROR(ret, "Failed to open HackRF device") - - uint8_t board_id; - ret = hackrf_board_id_read( _dev, &board_id ); - HACKRF_THROW_ON_ERROR(ret, "Failed to get HackRF board id") - - char version[40]; - memset(version, 0, sizeof(version)); - ret = hackrf_version_string_read( _dev, version, sizeof(version)); - HACKRF_THROW_ON_ERROR(ret, "Failed to read version string") -#if 0 - read_partid_serialno_t serial_number; - ret = hackrf_board_partid_serialno_read( _dev, &serial_number ); - HACKRF_THROW_ON_ERROR(ret, "Failed to read serial number") -#endif - std::cerr << "Using " << hackrf_board_id_name(hackrf_board_id(board_id)) << " " - << "with firmware " << version << " " - << std::endl; + _stopping = false; if ( BUF_NUM != _buf_num ) { std::cerr << "Using " << _buf_num << " buffers of size " << BUF_LEN << "." @@ -232,26 +168,12 @@ hackrf_sink_c::hackrf_sink_c (const std::string &args) // Check device args to find out if bias/phantom power is desired. if ( dict.count("bias_tx") ) { - bool bias = boost::lexical_cast( dict["bias_tx"] ); - ret = hackrf_set_antenna_enable(_dev, static_cast(bias)); - if ( ret != HACKRF_SUCCESS ) - { - std::cerr << "Failed to apply antenna bias voltage state: " << bias << HACKRF_FORMAT_ERROR(ret, "") << std::endl; - } - else - { - std::cerr << (bias ? "Enabled" : "Disabled") << " antenna bias voltage" << std::endl; - } + hackrf_common::set_bias(dict["bias_tx"] == "1"); } _buf = (int8_t *) malloc( BUF_LEN ); cb_init( &_cbuf, _buf_num, BUF_LEN ); - -// _thread = gr::thread::thread(_hackrf_wait, this); - - ret = hackrf_start_tx( _dev, _hackrf_tx_callback, (void *)this ); - HACKRF_THROW_ON_ERROR(ret, "Failed to start TX streaming") } /* @@ -259,30 +181,6 @@ hackrf_sink_c::hackrf_sink_c (const std::string &args) */ hackrf_sink_c::~hackrf_sink_c () { - if (_dev) { -// _thread.join(); - int ret = hackrf_stop_tx( _dev ); - if ( ret != HACKRF_SUCCESS ) - { - std::cerr << HACKRF_FORMAT_ERROR(ret, "Failed to stop TX streaming") << std::endl; - } - ret = hackrf_close( _dev ); - if ( ret != HACKRF_SUCCESS ) - { - std::cerr << HACKRF_FORMAT_ERROR(ret, "Failed to close HackRF") << std::endl; - } - _dev = NULL; - - { - boost::mutex::scoped_lock lock( _usage_mutex ); - - _usage--; - - if ( _usage == 0 ) - hackrf_exit(); /* call only once after last close */ - } - } - free(_buf); _buf = NULL; @@ -302,11 +200,16 @@ int hackrf_sink_c::hackrf_tx_callback(unsigned char *buffer, uint32_t length) *buffer++ = rand() % 255; #else { - boost::mutex::scoped_lock lock( _buf_mutex ); + std::unique_lock lock(_buf_mutex); if ( ! cb_pop_front( &_cbuf, buffer ) ) { memset(buffer, 0, length); - std::cerr << "U" << std::flush; + if (_stopping) { + _buf_cond.notify_one(); + return -1; + } else { + std::cerr << "U" << std::flush; + } } else { // std::cerr << "-" << std::flush; _buf_cond.notify_one(); @@ -316,42 +219,61 @@ int hackrf_sink_c::hackrf_tx_callback(unsigned char *buffer, uint32_t length) return 0; // TODO: return -1 on error/stop } -void hackrf_sink_c::_hackrf_wait(hackrf_sink_c *obj) -{ - obj->hackrf_wait(); -} - -void hackrf_sink_c::hackrf_wait() -{ -} - bool hackrf_sink_c::start() { - if ( ! _dev ) + if ( ! _dev.get() ) return false; + _stopping = false; _buf_used = 0; -#if 0 - int ret = hackrf_start_tx( _dev, _hackrf_tx_callback, (void *)this ); + hackrf_common::start(); + int ret = hackrf_start_tx( _dev.get(), _hackrf_tx_callback, (void *)this ); if ( ret != HACKRF_SUCCESS ) { std::cerr << "Failed to start TX streaming (" << ret << ")" << std::endl; return false; } -#endif return true; } bool hackrf_sink_c::stop() { - if ( ! _dev ) + int i; + + if ( ! _dev.get() ) return false; -#if 0 - int ret = hackrf_stop_tx( _dev ); + + { + std::unique_lock lock(_buf_mutex); + + while ( ! cb_has_room(&_cbuf) ) + _buf_cond.wait( lock ); + + // Fill the rest of the current buffer with silence. + memset(_buf + _buf_used, 0, BUF_LEN - _buf_used); + cb_push_back( &_cbuf, _buf ); + _buf_used = 0; + + // Add some more silence so the end doesn't get cut off. + memset(_buf, 0, BUF_LEN); + for (i = 0; i < 5; i++) { + while ( ! cb_has_room(&_cbuf) ) + _buf_cond.wait( lock ); + + cb_push_back( &_cbuf, _buf ); + } + + _stopping = true; + + while (hackrf_is_streaming(_dev.get()) == HACKRF_TRUE) + _buf_cond.wait( lock ); + } + + hackrf_common::stop(); + int ret = hackrf_stop_tx( _dev.get() ); if ( ret != HACKRF_SUCCESS ) { std::cerr << "Failed to stop TX streaming (" << ret << ")" << std::endl; return false; } -#endif return true; } @@ -424,7 +346,7 @@ int hackrf_sink_c::work( int noutput_items, const gr_complex *in = (const gr_complex *) input_items[0]; { - boost::mutex::scoped_lock lock( _buf_mutex ); + std::unique_lock lock(_buf_mutex); while ( ! cb_has_room(&_cbuf) ) _buf_cond.wait( lock ); @@ -454,7 +376,7 @@ int hackrf_sink_c::work( int noutput_items, if((unsigned int)noutput_items >= remaining) { { - boost::mutex::scoped_lock lock( _buf_mutex ); + std::unique_lock lock(_buf_mutex); if ( ! cb_push_back( &_cbuf, _buf ) ) { _buf_used = prev_buf_used; @@ -477,78 +399,7 @@ int hackrf_sink_c::work( int noutput_items, std::vector hackrf_sink_c::get_devices() { - std::vector devices; - std::string label; - - { - boost::mutex::scoped_lock lock( _usage_mutex ); - - if ( _usage == 0 ) - hackrf_init(); /* call only once before the first open */ - - _usage++; - } - -#ifdef LIBHACKRF_HAVE_DEVICE_LIST - hackrf_device_list_t *list = hackrf_device_list(); - - for (int i = 0; i < list->devicecount; i++) { - label = "HackRF "; - label += hackrf_usb_board_id_name( list->usb_board_ids[i] ); - - std::string args; - if (list->serial_numbers[i]) { - std::string serial = boost::lexical_cast< std::string >( list->serial_numbers[i] ); - if (serial.length() > 6) - serial = serial.substr(serial.length() - 6, 6); - args = "hackrf=" + serial; - label += " " + serial; - } else - args = "hackrf"; /* will pick the first one, serial number is required for choosing a specific one */ - - boost::algorithm::trim(label); - - args += ",label='" + label + "'"; - devices.push_back( args ); - } - - hackrf_device_list_free(list); -#else - - int ret; - hackrf_device *dev = NULL; - ret = hackrf_open(&dev); - if ( HACKRF_SUCCESS == ret ) - { - std::string args = "hackrf=0"; - - label = "HackRF"; - - uint8_t board_id; - ret = hackrf_board_id_read( dev, &board_id ); - if ( HACKRF_SUCCESS == ret ) - { - label += std::string(" ") + hackrf_board_id_name(hackrf_board_id(board_id)); - } - - args += ",label='" + label + "'"; - devices.push_back( args ); - - ret = hackrf_close(dev); - } - -#endif - - { - boost::mutex::scoped_lock lock( _usage_mutex ); - - _usage--; - - if ( _usage == 0 ) - hackrf_exit(); /* call only once after last close */ - } - - return devices; + return hackrf_common::get_devices(); } size_t hackrf_sink_c::get_num_channels() @@ -558,88 +409,42 @@ size_t hackrf_sink_c::get_num_channels() osmosdr::meta_range_t hackrf_sink_c::get_sample_rates() { - osmosdr::meta_range_t range; - - /* we only add integer rates here because of better phase noise performance. - * the user is allowed to request arbitrary (fractional) rates within these - * boundaries. */ - - range += osmosdr::range_t( 8e6 ); - range += osmosdr::range_t( 10e6 ); - range += osmosdr::range_t( 12.5e6 ); - range += osmosdr::range_t( 16e6 ); - range += osmosdr::range_t( 20e6 ); /* confirmed to work on fast machines */ - - return range; + return hackrf_common::get_sample_rates(); } double hackrf_sink_c::set_sample_rate( double rate ) { - int ret; - - if (_dev) { - ret = hackrf_set_sample_rate( _dev, rate ); - if ( HACKRF_SUCCESS == ret ) { - _sample_rate = rate; - //set_bandwidth( 0.0 ); /* bandwidth of 0 means automatic filter selection */ - } else { - HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_sample_rate", rate ) ) - } - } - - return get_sample_rate(); + return hackrf_common::set_sample_rate(rate); } double hackrf_sink_c::get_sample_rate() { - return _sample_rate; + return hackrf_common::get_sample_rate(); } osmosdr::freq_range_t hackrf_sink_c::get_freq_range( size_t chan ) { - osmosdr::freq_range_t range; - - range += osmosdr::range_t( _sample_rate / 2, 7250e6 - _sample_rate / 2 ); - - return range; + return hackrf_common::get_freq_range(chan); } double hackrf_sink_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 = hackrf_set_freq( _dev, uint64_t(corr_freq) ); - if ( HACKRF_SUCCESS == ret ) { - _center_freq = freq; - } else { - HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_freq", corr_freq ) ) - } - } - - return get_center_freq( chan ); + return hackrf_common::set_center_freq(freq, chan); } double hackrf_sink_c::get_center_freq( size_t chan ) { - return _center_freq; + return hackrf_common::get_center_freq(chan); } double hackrf_sink_c::set_freq_corr( double ppm, size_t chan ) { - _freq_corr = ppm; - - set_center_freq( _center_freq ); - - return get_freq_corr( chan ); + return hackrf_common::set_freq_corr(ppm, chan); } double hackrf_sink_c::get_freq_corr( size_t chan ) { - return _freq_corr; + return hackrf_common::get_freq_corr(chan); } std::vector hackrf_sink_c::get_gain_names( size_t chan ) @@ -672,34 +477,17 @@ osmosdr::gain_range_t hackrf_sink_c::get_gain_range( const std::string & name, s bool hackrf_sink_c::set_gain_mode( bool automatic, size_t chan ) { - _auto_gain = automatic; - - return get_gain_mode(chan); + return hackrf_common::set_gain_mode(automatic, chan); } bool hackrf_sink_c::get_gain_mode( size_t chan ) { - return _auto_gain; + return hackrf_common::get_gain_mode(chan); } double hackrf_sink_c::set_gain( double gain, size_t chan ) { - int ret; - osmosdr::gain_range_t rf_gains = get_gain_range( "RF", chan ); - - if (_dev) { - double clip_gain = rf_gains.clip( gain, true ); - uint8_t value = clip_gain == 14.0f ? 1 : 0; - - ret = hackrf_set_amp_enable( _dev, value ); - if ( HACKRF_SUCCESS == ret ) { - _amp_gain = clip_gain; - } else { - HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_amp_enable", value ) ) - } - } - - return _amp_gain; + return hackrf_common::set_gain(gain, chan); } double hackrf_sink_c::set_gain( double gain, const std::string & name, size_t chan) @@ -717,7 +505,7 @@ double hackrf_sink_c::set_gain( double gain, const std::string & name, size_t ch double hackrf_sink_c::get_gain( size_t chan ) { - return _amp_gain; + return hackrf_common::get_gain(chan); } double hackrf_sink_c::get_gain( const std::string & name, size_t chan ) @@ -738,10 +526,10 @@ double hackrf_sink_c::set_if_gain( double gain, size_t chan ) int ret; osmosdr::gain_range_t if_gains = get_gain_range( "IF", chan ); - if (_dev) { + if (_dev.get()) { double clip_gain = if_gains.clip( gain, true ); - ret = hackrf_set_txvga_gain( _dev, uint32_t(clip_gain) ); + ret = hackrf_set_txvga_gain( _dev.get(), uint32_t(clip_gain) ); if ( HACKRF_SUCCESS == ret ) { _vga_gain = clip_gain; } else { @@ -759,72 +547,30 @@ double hackrf_sink_c::set_bb_gain( double gain, size_t chan ) std::vector< std::string > hackrf_sink_c::get_antennas( size_t chan ) { - std::vector< std::string > antennas; - - antennas += get_antenna( chan ); - - return antennas; + return hackrf_common::get_antennas(chan); } std::string hackrf_sink_c::set_antenna( const std::string & antenna, size_t chan ) { - return get_antenna( chan ); + return hackrf_common::set_antenna(antenna, chan); } std::string hackrf_sink_c::get_antenna( size_t chan ) { - return "TX/RX"; + return hackrf_common::get_antenna(chan); } double hackrf_sink_c::set_bandwidth( double bandwidth, size_t chan ) { - int ret; -// osmosdr::freq_range_t bandwidths = get_bandwidth_range( chan ); - - if ( bandwidth == 0.0 ) /* bandwidth of 0 means automatic filter selection */ - bandwidth = _sample_rate * 0.75; /* select narrower filters to prevent aliasing */ - - if ( _dev ) { - /* compute best default value depending on sample rate (auto filter) */ - uint32_t bw = hackrf_compute_baseband_filter_bw( uint32_t(bandwidth) ); - ret = hackrf_set_baseband_filter_bandwidth( _dev, bw ); - if ( HACKRF_SUCCESS == ret ) { - _bandwidth = bw; - } else { - HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_baseband_filter_bandwidth", bw ) ) - } - } - - return _bandwidth; + return hackrf_common::set_bandwidth(bandwidth, chan); } double hackrf_sink_c::get_bandwidth( size_t chan ) { - return _bandwidth; + return hackrf_common::get_bandwidth(chan); } osmosdr::freq_range_t hackrf_sink_c::get_bandwidth_range( size_t chan ) { - osmosdr::freq_range_t bandwidths; - - // TODO: read out from libhackrf when an API is available - - bandwidths += osmosdr::range_t( 1750000 ); - bandwidths += osmosdr::range_t( 2500000 ); - bandwidths += osmosdr::range_t( 3500000 ); - bandwidths += osmosdr::range_t( 5000000 ); - bandwidths += osmosdr::range_t( 5500000 ); - bandwidths += osmosdr::range_t( 6000000 ); - bandwidths += osmosdr::range_t( 7000000 ); - bandwidths += osmosdr::range_t( 8000000 ); - bandwidths += osmosdr::range_t( 9000000 ); - bandwidths += osmosdr::range_t( 10000000 ); - bandwidths += osmosdr::range_t( 12000000 ); - bandwidths += osmosdr::range_t( 14000000 ); - bandwidths += osmosdr::range_t( 15000000 ); - bandwidths += osmosdr::range_t( 20000000 ); - bandwidths += osmosdr::range_t( 24000000 ); - bandwidths += osmosdr::range_t( 28000000 ); - - return bandwidths; + return hackrf_common::get_bandwidth_range(chan); } diff --git a/lib/hackrf/hackrf_sink_c.h b/lib/hackrf/hackrf_sink_c.h index a7e7ab8..08ff2ca 100644 --- a/lib/hackrf/hackrf_sink_c.h +++ b/lib/hackrf/hackrf_sink_c.h @@ -1,34 +1,35 @@ /* -*- c++ -*- */ /* * Copyright 2013 Dimitri Stolnikov + * Copyright 2020 Clayton Smith * - * GNU Radio is free software; you can redistribute it and/or modify + * 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. * - * GNU Radio is distributed in the hope that it will be useful, + * 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 GNU Radio; see the file COPYING. If not, write to + * 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. */ #ifndef INCLUDED_HACKRF_SINK_C_H #define INCLUDED_HACKRF_SINK_C_H -#include #include -#include -#include +#include +#include #include #include "sink_iface.h" +#include "hackrf_common.h" class hackrf_sink_c; @@ -67,7 +68,8 @@ hackrf_sink_c_sptr make_hackrf_sink_c (const std::string & args = ""); class hackrf_sink_c : public gr::sync_block, - public sink_iface + public sink_iface, + protected hackrf_common { private: // The friend declaration allows hackrf_make_sink_c to @@ -124,29 +126,16 @@ public: private: static int _hackrf_tx_callback(hackrf_transfer* transfer); int hackrf_tx_callback(unsigned char *buffer, uint32_t length); - static void _hackrf_wait(hackrf_sink_c *obj); - void hackrf_wait(); - - static int _usage; - static boost::mutex _usage_mutex; - - hackrf_device *_dev; -// gr::thread::thread _thread; circular_buffer_t _cbuf; int8_t *_buf; unsigned int _buf_num; unsigned int _buf_used; - boost::mutex _buf_mutex; - boost::condition_variable _buf_cond; + bool _stopping; + std::mutex _buf_mutex; + std::condition_variable _buf_cond; - double _sample_rate; - double _center_freq; - double _freq_corr; - bool _auto_gain; - double _amp_gain; double _vga_gain; - double _bandwidth; }; #endif /* INCLUDED_HACKRF_SINK_C_H */ diff --git a/lib/hackrf/hackrf_source_c.cc b/lib/hackrf/hackrf_source_c.cc index 30b63c7..51a6580 100644 --- a/lib/hackrf/hackrf_source_c.cc +++ b/lib/hackrf/hackrf_source_c.cc @@ -1,19 +1,20 @@ /* -*- c++ -*- */ /* * Copyright 2013 Dimitri Stolnikov + * Copyright 2020 Clayton Smith * - * GNU Radio is free software; you can redistribute it and/or modify + * 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. * - * GNU Radio is distributed in the hope that it will be useful, + * 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 GNU Radio; see the file COPYING. If not, write to + * 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. */ @@ -31,10 +32,7 @@ #include #include -#include #include -#include -#include #include @@ -44,27 +42,6 @@ using namespace boost::assign; -#define BUF_LEN (16 * 32 * 512) /* must be multiple of 512 */ -#define BUF_NUM 15 - -#define BYTES_PER_SAMPLE 2 /* HackRF device produces 8 bit unsigned IQ data */ - -#define HACKRF_FORMAT_ERROR(ret, msg) \ - boost::str( boost::format(msg " (%1%) %2%") \ - % ret % hackrf_error_name((enum hackrf_error)ret) ) - -#define HACKRF_THROW_ON_ERROR(ret, msg) \ - if ( ret != HACKRF_SUCCESS ) \ - { \ - throw std::runtime_error( HACKRF_FORMAT_ERROR(ret, msg) ); \ - } - -#define HACKRF_FUNC_STR(func, arg) \ - boost::str(boost::format(func "(%1%)") % arg) + " has failed" - -int hackrf_source_c::_usage = 0; -boost::mutex hackrf_source_c::_usage_mutex; - hackrf_source_c_sptr make_hackrf_source_c (const std::string & args) { return gnuradio::get_initial_sptr(new hackrf_source_c (args)); @@ -91,29 +68,20 @@ hackrf_source_c::hackrf_source_c (const std::string &args) : gr::sync_block ("hackrf_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), + hackrf_common::hackrf_common(args), _buf(NULL), - _sample_rate(0), - _center_freq(0), - _freq_corr(0), - _auto_gain(false), - _amp_gain(0), _lna_gain(0), - _vga_gain(0), - _bandwidth(0) + _vga_gain(0) { - int ret; - std::string hackrf_serial; - dict_t dict = params_to_dict(args); _buf_num = _buf_len = _buf_head = _buf_used = _buf_offset = 0; if (dict.count("buffers")) - _buf_num = boost::lexical_cast< unsigned int >( dict["buffers"] ); + _buf_num = std::stoi(dict["buffers"]); // if (dict.count("buflen")) -// _buf_len = boost::lexical_cast< unsigned int >( dict["buflen"] ); +// _buf_len = std::stoi(dict["buflen"]); if (0 == _buf_num) _buf_num = BUF_NUM; @@ -134,66 +102,6 @@ hackrf_source_c::hackrf_source_c (const std::string &args) #endif } - { - boost::mutex::scoped_lock lock( _usage_mutex ); - - if ( _usage == 0 ) - hackrf_init(); /* call only once before the first open */ - - _usage++; - } - - _dev = NULL; - -#ifdef LIBHACKRF_HAVE_DEVICE_LIST - if (dict.count("hackrf") && dict["hackrf"].length() > 0) { - hackrf_serial = dict["hackrf"]; - - if (hackrf_serial.length() > 1) { - ret = hackrf_open_by_serial( hackrf_serial.c_str(), &_dev ); - } else { - int dev_index = 0; - try { - dev_index = boost::lexical_cast< int >( hackrf_serial ); - } catch ( std::exception &ex ) { - throw std::runtime_error( - "Failed to use '" + hackrf_serial + "' as HackRF device index number: " + ex.what()); - } - - hackrf_device_list_t *list = hackrf_device_list(); - if (dev_index < list->devicecount) { - ret = hackrf_device_list_open(list, dev_index, &_dev); - } else { - hackrf_device_list_free(list); - throw std::runtime_error( - "Failed to use '" + hackrf_serial + "' as HackRF device index: not enough devices"); - } - hackrf_device_list_free(list); - } - } else -#endif - ret = hackrf_open( &_dev ); - - HACKRF_THROW_ON_ERROR(ret, "Failed to open HackRF device") - - uint8_t board_id; - ret = hackrf_board_id_read( _dev, &board_id ); - HACKRF_THROW_ON_ERROR(ret, "Failed to get HackRF board id") - - char version[40]; - memset(version, 0, sizeof(version)); - ret = hackrf_version_string_read( _dev, version, sizeof(version)); - HACKRF_THROW_ON_ERROR(ret, "Failed to read version string") - -#if 0 - read_partid_serialno_t serial_number; - ret = hackrf_board_partid_serialno_read( _dev, &serial_number ); - HACKRF_THROW_ON_ERROR(ret, "Failed to read serial number") -#endif - std::cerr << "Using " << hackrf_board_id_name(hackrf_board_id(board_id)) << " " - << "with firmware " << version << " " - << std::endl; - if ( BUF_NUM != _buf_num || BUF_LEN != _buf_len ) { std::cerr << "Using " << _buf_num << " buffers of size " << _buf_len << "." << std::endl; @@ -211,16 +119,7 @@ hackrf_source_c::hackrf_source_c (const std::string &args) // Check device args to find out if bias/phantom power is desired. if ( dict.count("bias") ) { - bool bias = boost::lexical_cast( dict["bias"] ); - ret = hackrf_set_antenna_enable(_dev, static_cast(bias)); - if ( ret != HACKRF_SUCCESS ) - { - std::cerr << "Failed to apply antenna bias voltage state: " << bias << HACKRF_FORMAT_ERROR(ret, "") << std::endl; - } - else - { - std::cerr << (bias ? "Enabled" : "Disabled") << " antenna bias voltage" << std::endl; - } + hackrf_common::set_bias(dict["bias"] == "1"); } _buf = (unsigned short **) malloc(_buf_num * sizeof(unsigned short *)); @@ -229,11 +128,6 @@ hackrf_source_c::hackrf_source_c (const std::string &args) for(unsigned int i = 0; i < _buf_num; ++i) _buf[i] = (unsigned short *) malloc(_buf_len); } - -// _thread = gr::thread::thread(_hackrf_wait, this); - - ret = hackrf_start_rx( _dev, _hackrf_rx_callback, (void *)this ); - HACKRF_THROW_ON_ERROR(ret, "Failed to start RX streaming") } /* @@ -241,30 +135,6 @@ hackrf_source_c::hackrf_source_c (const std::string &args) */ hackrf_source_c::~hackrf_source_c () { - if (_dev) { -// _thread.join(); - int ret = hackrf_stop_rx( _dev ); - if ( ret != HACKRF_SUCCESS ) - { - std::cerr << HACKRF_FORMAT_ERROR(ret, "Failed to stop RX streaming") << std::endl; - } - ret = hackrf_close( _dev ); - if ( ret != HACKRF_SUCCESS ) - { - std::cerr << HACKRF_FORMAT_ERROR(ret, "Failed to close HackRF") << std::endl; - } - _dev = NULL; - - { - boost::mutex::scoped_lock lock( _usage_mutex ); - - _usage--; - - if ( _usage == 0 ) - hackrf_exit(); /* call only once after last close */ - } - } - if (_buf) { for(unsigned int i = 0; i < _buf_num; ++i) { free(_buf[i]); @@ -284,7 +154,7 @@ int hackrf_source_c::_hackrf_rx_callback(hackrf_transfer *transfer) int hackrf_source_c::hackrf_rx_callback(unsigned char *buf, uint32_t len) { { - boost::mutex::scoped_lock lock( _buf_mutex ); + std::unique_lock lock(_buf_mutex); int buf_tail = (_buf_head + _buf_used) % _buf_num; memcpy(_buf[buf_tail], buf, len); @@ -302,40 +172,31 @@ int hackrf_source_c::hackrf_rx_callback(unsigned char *buf, uint32_t len) return 0; // TODO: return -1 on error/stop } -void hackrf_source_c::_hackrf_wait(hackrf_source_c *obj) -{ - obj->hackrf_wait(); -} - -void hackrf_source_c::hackrf_wait() -{ -} - bool hackrf_source_c::start() { - if ( ! _dev ) + if ( ! _dev.get() ) return false; -#if 0 - int ret = hackrf_start_rx( _dev, _hackrf_rx_callback, (void *)this ); + + hackrf_common::start(); + int ret = hackrf_start_rx( _dev.get(), _hackrf_rx_callback, (void *)this ); if ( ret != HACKRF_SUCCESS ) { std::cerr << "Failed to start RX streaming (" << ret << ")" << std::endl; return false; } -#endif return true; } bool hackrf_source_c::stop() { - if ( ! _dev ) + if ( ! _dev.get() ) return false; -#if 0 - int ret = hackrf_stop_rx( _dev ); + + hackrf_common::stop(); + int ret = hackrf_stop_rx( _dev.get() ); if ( ret != HACKRF_SUCCESS ) { std::cerr << "Failed to stop RX streaming (" << ret << ")" << std::endl; return false; } -#endif return true; } @@ -347,11 +208,11 @@ int hackrf_source_c::work( int noutput_items, bool running = false; - if ( _dev ) - running = (hackrf_is_streaming( _dev ) == HACKRF_TRUE); + if ( _dev.get() ) + running = (hackrf_is_streaming( _dev.get() ) == HACKRF_TRUE); { - boost::mutex::scoped_lock lock( _buf_mutex ); + std::unique_lock lock(_buf_mutex); while (_buf_used < 3 && running) // collect at least 3 buffers _buf_cond.wait( lock ); @@ -373,7 +234,7 @@ int hackrf_source_c::work( int noutput_items, *out++ = _lut[ *(buf + i) ]; { - boost::mutex::scoped_lock lock( _buf_mutex ); + std::unique_lock lock(_buf_mutex); _buf_head = (_buf_head + 1) % _buf_num; _buf_used--; @@ -395,78 +256,7 @@ int hackrf_source_c::work( int noutput_items, std::vector hackrf_source_c::get_devices() { - std::vector devices; - std::string label; - - { - boost::mutex::scoped_lock lock( _usage_mutex ); - - if ( _usage == 0 ) - hackrf_init(); /* call only once before the first open */ - - _usage++; - } - -#ifdef LIBHACKRF_HAVE_DEVICE_LIST - hackrf_device_list_t *list = hackrf_device_list(); - - for (int i = 0; i < list->devicecount; i++) { - label = "HackRF "; - label += hackrf_usb_board_id_name( list->usb_board_ids[i] ); - - std::string args; - if (list->serial_numbers[i]) { - std::string serial = boost::lexical_cast< std::string >( list->serial_numbers[i] ); - if (serial.length() > 6) - serial = serial.substr(serial.length() - 6, 6); - args = "hackrf=" + serial; - label += " " + serial; - } else - args = "hackrf"; /* will pick the first one, serial number is required for choosing a specific one */ - - boost::algorithm::trim(label); - - args += ",label='" + label + "'"; - devices.push_back( args ); - } - - hackrf_device_list_free(list); -#else - - int ret; - hackrf_device *dev = NULL; - ret = hackrf_open(&dev); - if ( HACKRF_SUCCESS == ret ) - { - std::string args = "hackrf=0"; - - label = "HackRF"; - - uint8_t board_id; - ret = hackrf_board_id_read( dev, &board_id ); - if ( HACKRF_SUCCESS == ret ) - { - label += std::string(" ") + hackrf_board_id_name(hackrf_board_id(board_id)); - } - - args += ",label='" + label + "'"; - devices.push_back( args ); - - ret = hackrf_close(dev); - } - -#endif - - { - boost::mutex::scoped_lock lock( _usage_mutex ); - - _usage--; - - if ( _usage == 0 ) - hackrf_exit(); /* call only once after last close */ - } - - return devices; + return hackrf_common::get_devices(); } size_t hackrf_source_c::get_num_channels() @@ -476,88 +266,42 @@ size_t hackrf_source_c::get_num_channels() osmosdr::meta_range_t hackrf_source_c::get_sample_rates() { - osmosdr::meta_range_t range; - - /* we only add integer rates here because of better phase noise performance. - * the user is allowed to request arbitrary (fractional) rates within these - * boundaries. */ - - range += osmosdr::range_t( 8e6 ); - range += osmosdr::range_t( 10e6 ); - range += osmosdr::range_t( 12.5e6 ); - range += osmosdr::range_t( 16e6 ); - range += osmosdr::range_t( 20e6 ); /* confirmed to work on fast machines */ - - return range; + return hackrf_common::get_sample_rates(); } double hackrf_source_c::set_sample_rate( double rate ) { - int ret; - - if (_dev) { - ret = hackrf_set_sample_rate( _dev, rate ); - if ( HACKRF_SUCCESS == ret ) { - _sample_rate = rate; - //set_bandwidth( 0.0 ); /* bandwidth of 0 means automatic filter selection */ - } else { - HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_sample_rate", rate ) ) - } - } - - return get_sample_rate(); + return hackrf_common::set_sample_rate(rate); } double hackrf_source_c::get_sample_rate() { - return _sample_rate; + return hackrf_common::get_sample_rate(); } osmosdr::freq_range_t hackrf_source_c::get_freq_range( size_t chan ) { - osmosdr::freq_range_t range; - - range += osmosdr::range_t( _sample_rate / 2, 7250e6 - _sample_rate / 2 ); - - return range; + return hackrf_common::get_freq_range(chan); } double hackrf_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 = hackrf_set_freq( _dev, uint64_t(corr_freq) ); - if ( HACKRF_SUCCESS == ret ) { - _center_freq = freq; - } else { - HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_freq", corr_freq ) ) - } - } - - return get_center_freq( chan ); + return hackrf_common::set_center_freq(freq, chan); } double hackrf_source_c::get_center_freq( size_t chan ) { - return _center_freq; + return hackrf_common::get_center_freq(chan); } double hackrf_source_c::set_freq_corr( double ppm, size_t chan ) { - _freq_corr = ppm; - - set_center_freq( _center_freq ); - - return get_freq_corr( chan ); + return hackrf_common::set_freq_corr(ppm, chan); } double hackrf_source_c::get_freq_corr( size_t chan ) { - return _freq_corr; + return hackrf_common::get_freq_corr(chan); } std::vector hackrf_source_c::get_gain_names( size_t chan ) @@ -595,34 +339,17 @@ osmosdr::gain_range_t hackrf_source_c::get_gain_range( const std::string & name, bool hackrf_source_c::set_gain_mode( bool automatic, size_t chan ) { - _auto_gain = automatic; - - return get_gain_mode(chan); + return hackrf_common::set_gain_mode(automatic, chan); } bool hackrf_source_c::get_gain_mode( size_t chan ) { - return _auto_gain; + return hackrf_common::get_gain_mode(chan); } double hackrf_source_c::set_gain( double gain, size_t chan ) { - int ret; - osmosdr::gain_range_t rf_gains = get_gain_range( "RF", chan ); - - if (_dev) { - double clip_gain = rf_gains.clip( gain, true ); - uint8_t value = clip_gain == 14.0f ? 1 : 0; - - ret = hackrf_set_amp_enable( _dev, value ); - if ( HACKRF_SUCCESS == ret ) { - _amp_gain = clip_gain; - } else { - HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_amp_enable", value ) ) - } - } - - return _amp_gain; + return hackrf_common::set_gain(gain, chan); } double hackrf_source_c::set_gain( double gain, const std::string & name, size_t chan) @@ -644,7 +371,7 @@ double hackrf_source_c::set_gain( double gain, const std::string & name, size_t double hackrf_source_c::get_gain( size_t chan ) { - return _amp_gain; + return hackrf_common::get_gain(chan); } double hackrf_source_c::get_gain( const std::string & name, size_t chan ) @@ -669,10 +396,10 @@ double hackrf_source_c::set_if_gain(double gain, size_t chan) int ret; osmosdr::gain_range_t rf_gains = get_gain_range( "IF", chan ); - if (_dev) { + if (_dev.get()) { double clip_gain = rf_gains.clip( gain, true ); - ret = hackrf_set_lna_gain( _dev, uint32_t(clip_gain) ); + ret = hackrf_set_lna_gain( _dev.get(), uint32_t(clip_gain) ); if ( HACKRF_SUCCESS == ret ) { _lna_gain = clip_gain; } else { @@ -688,10 +415,10 @@ double hackrf_source_c::set_bb_gain( double gain, size_t chan ) int ret; osmosdr::gain_range_t if_gains = get_gain_range( "BB", chan ); - if (_dev) { + if (_dev.get()) { double clip_gain = if_gains.clip( gain, true ); - ret = hackrf_set_vga_gain( _dev, uint32_t(clip_gain) ); + ret = hackrf_set_vga_gain( _dev.get(), uint32_t(clip_gain) ); if ( HACKRF_SUCCESS == ret ) { _vga_gain = clip_gain; } else { @@ -704,72 +431,30 @@ double hackrf_source_c::set_bb_gain( double gain, size_t chan ) std::vector< std::string > hackrf_source_c::get_antennas( size_t chan ) { - std::vector< std::string > antennas; - - antennas += get_antenna( chan ); - - return antennas; + return hackrf_common::get_antennas(chan); } std::string hackrf_source_c::set_antenna( const std::string & antenna, size_t chan ) { - return get_antenna( chan ); + return hackrf_common::set_antenna(antenna, chan); } std::string hackrf_source_c::get_antenna( size_t chan ) { - return "TX/RX"; + return hackrf_common::get_antenna(chan); } double hackrf_source_c::set_bandwidth( double bandwidth, size_t chan ) { - int ret; -// osmosdr::freq_range_t bandwidths = get_bandwidth_range( chan ); - - if ( bandwidth == 0.0 ) /* bandwidth of 0 means automatic filter selection */ - bandwidth = _sample_rate * 0.75; /* select narrower filters to prevent aliasing */ - - if ( _dev ) { - /* compute best default value depending on sample rate (auto filter) */ - uint32_t bw = hackrf_compute_baseband_filter_bw( uint32_t(bandwidth) ); - ret = hackrf_set_baseband_filter_bandwidth( _dev, bw ); - if ( HACKRF_SUCCESS == ret ) { - _bandwidth = bw; - } else { - HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_baseband_filter_bandwidth", bw ) ) - } - } - - return _bandwidth; + return hackrf_common::set_bandwidth(bandwidth, chan); } double hackrf_source_c::get_bandwidth( size_t chan ) { - return _bandwidth; + return hackrf_common::get_bandwidth(chan); } osmosdr::freq_range_t hackrf_source_c::get_bandwidth_range( size_t chan ) { - osmosdr::freq_range_t bandwidths; - - // TODO: read out from libhackrf when an API is available - - bandwidths += osmosdr::range_t( 1750000 ); - bandwidths += osmosdr::range_t( 2500000 ); - bandwidths += osmosdr::range_t( 3500000 ); - bandwidths += osmosdr::range_t( 5000000 ); - bandwidths += osmosdr::range_t( 5500000 ); - bandwidths += osmosdr::range_t( 6000000 ); - bandwidths += osmosdr::range_t( 7000000 ); - bandwidths += osmosdr::range_t( 8000000 ); - bandwidths += osmosdr::range_t( 9000000 ); - bandwidths += osmosdr::range_t( 10000000 ); - bandwidths += osmosdr::range_t( 12000000 ); - bandwidths += osmosdr::range_t( 14000000 ); - bandwidths += osmosdr::range_t( 15000000 ); - bandwidths += osmosdr::range_t( 20000000 ); - bandwidths += osmosdr::range_t( 24000000 ); - bandwidths += osmosdr::range_t( 28000000 ); - - return bandwidths; + return hackrf_common::get_bandwidth_range(chan); } diff --git a/lib/hackrf/hackrf_source_c.h b/lib/hackrf/hackrf_source_c.h index 6ae81d2..e9aacb9 100644 --- a/lib/hackrf/hackrf_source_c.h +++ b/lib/hackrf/hackrf_source_c.h @@ -1,36 +1,35 @@ /* -*- c++ -*- */ /* * Copyright 2013 Dimitri Stolnikov + * Copyright 2020 Clayton Smith * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify + * 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. * - * GNU Radio is distributed in the hope that it will be useful, + * 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 GNU Radio; see the file COPYING. If not, write to + * 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. */ #ifndef INCLUDED_HACKRF_SOURCE_C_H #define INCLUDED_HACKRF_SOURCE_C_H -#include #include -#include -#include +#include +#include #include #include "source_iface.h" +#include "hackrf_common.h" class hackrf_source_c; @@ -62,12 +61,12 @@ hackrf_source_c_sptr make_hackrf_source_c (const std::string & args = ""); */ class hackrf_source_c : public gr::sync_block, - public source_iface + public source_iface, + protected hackrf_common { private: // The friend declaration allows make_hackrf_source_c to // access the private constructor. - friend hackrf_source_c_sptr make_hackrf_source_c (const std::string & args); /*! @@ -123,35 +122,22 @@ public: private: static int _hackrf_rx_callback(hackrf_transfer* transfer); int hackrf_rx_callback(unsigned char *buf, uint32_t len); - static void _hackrf_wait(hackrf_source_c *obj); - void hackrf_wait(); - - static int _usage; - static boost::mutex _usage_mutex; std::vector _lut; - hackrf_device *_dev; - gr::thread::thread _thread; unsigned short **_buf; unsigned int _buf_num; unsigned int _buf_len; unsigned int _buf_head; unsigned int _buf_used; - boost::mutex _buf_mutex; - boost::condition_variable _buf_cond; + std::mutex _buf_mutex; + std::condition_variable _buf_cond; unsigned int _buf_offset; int _samp_avail; - double _sample_rate; - double _center_freq; - double _freq_corr; - bool _auto_gain; - double _amp_gain; double _lna_gain; double _vga_gain; - double _bandwidth; }; #endif /* INCLUDED_HACKRF_SOURCE_C_H */