diff options
Diffstat (limited to 'lib/hackrf')
-rw-r--r-- | lib/hackrf/CMakeLists.txt | 33 | ||||
-rw-r--r-- | lib/hackrf/hackrf_common.cc | 427 | ||||
-rw-r--r-- | lib/hackrf/hackrf_common.h | 113 | ||||
-rw-r--r-- | lib/hackrf/hackrf_sink_c.cc | 425 | ||||
-rw-r--r-- | lib/hackrf/hackrf_sink_c.h | 37 | ||||
-rw-r--r-- | lib/hackrf/hackrf_source_c.cc | 453 | ||||
-rw-r--r-- | lib/hackrf/hackrf_source_c.h | 40 |
7 files changed, 804 insertions, 724 deletions
diff --git a/lib/hackrf/CMakeLists.txt b/lib/hackrf/CMakeLists.txt index a0ec70a..c7af0c9 100644 --- a/lib/hackrf/CMakeLists.txt +++ b/lib/hackrf/CMakeLists.txt @@ -1,19 +1,19 @@ # Copyright 2012 Free Software Foundation, Inc. # -# This file is part of gr-osmosdr +# This file is part of GNU Radio # -# gr-osmosdr is free software; you can redistribute it and/or modify +# 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. # -# gr-osmosdr is distributed in the hope that it will be useful, +# 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 gr-osmosdr; see the file COPYING. If not, write to +# 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. @@ -21,18 +21,27 @@ # This file included, use CMake directory variables ######################################################################## -target_include_directories(gnuradio-osmosdr PRIVATE +include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${LIBHACKRF_INCLUDE_DIRS} ) -APPEND_LIB_LIST( - ${LIBHACKRF_LIBRARIES} -) - -list(APPEND gr_osmosdr_srcs - ${CMAKE_CURRENT_SOURCE_DIR}/hackrf_common.cc +set(hackrf_srcs ${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") + add_definitions(-DLIBHACKRF_HAVE_DEVICE_LIST) +endif(LIBHACKRF_HAVE_DEVICE_LIST) + +######################################################################## +# Append gnuradio-osmosdr library sources +######################################################################## +list(APPEND gr_osmosdr_srcs ${hackrf_srcs}) +list(APPEND gr_osmosdr_libs ${LIBHACKRF_LIBRARIES}) diff --git a/lib/hackrf/hackrf_common.cc b/lib/hackrf/hackrf_common.cc deleted file mode 100644 index 666dc60..0000000 --- a/lib/hackrf/hackrf_common.cc +++ /dev/null @@ -1,427 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2020 Clayton Smith <argilo@gmail.com> - * - * 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 "hackrf_common.h" - -#include "arg_helpers.h" - -int hackrf_common::_usage = 0; -std::mutex hackrf_common::_usage_mutex; - -std::map<std::string, std::weak_ptr<hackrf_device>> 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), - _requested_bandwidth(0), - _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<std::mutex> 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<std::mutex> 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<std::weak_ptr<struct hackrf_device>>(_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<hackrf_device *>(dev)); - if (ret != HACKRF_SUCCESS) - { - std::cerr << HACKRF_FORMAT_ERROR(ret, "Failed to close HackRF") << std::endl; - } - - { - std::lock_guard<std::mutex> guard(_usage_mutex); - - _usage--; - - if (_usage == 0) { - hackrf_exit(); /* call only once after last close */ - } - } -} - -std::vector<std::string> hackrf_common::get_devices() -{ - std::vector<std::string> devices; - std::string label; - - { - std::lock_guard<std::mutex> 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<std::mutex> 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.push_back(osmosdr::range_t( 8e6 )); - range.push_back(osmosdr::range_t( 10e6 )); - range.push_back(osmosdr::range_t( 12.5e6 )); - range.push_back(osmosdr::range_t( 16e6 )); - range.push_back(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.push_back(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 ) -{ - return { get_antenna( chan ) }; -} - -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 ); - - _requested_bandwidth = bandwidth; - 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.push_back(osmosdr::range_t( 1750000 )); - bandwidths.push_back(osmosdr::range_t( 2500000 )); - bandwidths.push_back(osmosdr::range_t( 3500000 )); - bandwidths.push_back(osmosdr::range_t( 5000000 )); - bandwidths.push_back(osmosdr::range_t( 5500000 )); - bandwidths.push_back(osmosdr::range_t( 6000000 )); - bandwidths.push_back(osmosdr::range_t( 7000000 )); - bandwidths.push_back(osmosdr::range_t( 8000000 )); - bandwidths.push_back(osmosdr::range_t( 9000000 )); - bandwidths.push_back(osmosdr::range_t( 10000000 )); - bandwidths.push_back(osmosdr::range_t( 12000000 )); - bandwidths.push_back(osmosdr::range_t( 14000000 )); - bandwidths.push_back(osmosdr::range_t( 15000000 )); - bandwidths.push_back(osmosdr::range_t( 20000000 )); - bandwidths.push_back(osmosdr::range_t( 24000000 )); - bandwidths.push_back(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<uint8_t>(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_center_freq(get_center_freq()); - set_sample_rate(get_sample_rate()); - if (_requested_bandwidth != 0) - set_bandwidth(get_bandwidth()); - 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 deleted file mode 100644 index d1ab47b..0000000 --- a/lib/hackrf/hackrf_common.h +++ /dev/null @@ -1,113 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2020 Clayton Smith <argilo@gmail.com> - * - * 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 <map> -#include <memory> -#include <mutex> -#include <string> -#include <vector> - -#include <boost/format.hpp> - -#include <osmosdr/ranges.h> -#include <libhackrf/hackrf.h> - -#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 signed 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_device> 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<std::string, std::weak_ptr<hackrf_device>> _devs; - static std::mutex _devs_mutex; - - double _sample_rate; - double _center_freq; - double _freq_corr; - bool _auto_gain; - double _amp_gain; - double _requested_bandwidth; - 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 1762934..ee089c6 100644 --- a/lib/hackrf/hackrf_sink_c.cc +++ b/lib/hackrf/hackrf_sink_c.cc @@ -2,20 +2,19 @@ /* * Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net> * Copyright 2014 Hoernchen <la@tfc-server.de> - * Copyright 2020 Clayton Smith <argilo@gmail.com> * - * gr-osmosdr is free software; you can redistribute it and/or modify + * 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. * - * gr-osmosdr is distributed in the hope that it will be useful, + * 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 gr-osmosdr; see the file COPYING. If not, write to + * 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. */ @@ -38,12 +37,38 @@ #include <emmintrin.h> #endif +#include <boost/assign.hpp> +#include <boost/format.hpp> +#include <boost/detail/endian.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/thread/thread.hpp> + #include <gnuradio/io_signature.h> #include "hackrf_sink_c.h" #include "arg_helpers.h" +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); @@ -78,11 +103,6 @@ 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) @@ -107,6 +127,9 @@ 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)); @@ -133,21 +156,66 @@ 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))), - hackrf_common::hackrf_common(args), + _dev(NULL), _buf(NULL), - _vga_gain(0) + _sample_rate(0), + _center_freq(0), + _freq_corr(0), + _auto_gain(false), + _amp_gain(0), + _vga_gain(0), + _bandwidth(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 = std::stoi(dict["buffers"]); + _buf_num = boost::lexical_cast< unsigned int >( dict["buffers"] ); if (0 == _buf_num) _buf_num = BUF_NUM; - _stopping = false; + { + 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; if ( BUF_NUM != _buf_num ) { std::cerr << "Using " << _buf_num << " buffers of size " << BUF_LEN << "." @@ -164,12 +232,26 @@ 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") ) { - hackrf_common::set_bias(dict["bias_tx"] == "1"); + bool bias = boost::lexical_cast<bool>( dict["bias_tx"] ); + ret = hackrf_set_antenna_enable(_dev, static_cast<uint8_t>(bias)); + if ( ret != HACKRF_SUCCESS ) + { + std::cerr << "Failed to apply antenna bias voltage state: " << bias << HACKRF_FORMAT_ERROR(ret, "") << std::endl; + } + else + { + std::cerr << (bias ? "Enabled" : "Disabled") << " antenna bias voltage" << std::endl; + } } _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") } /* @@ -177,6 +259,30 @@ 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; @@ -196,16 +302,11 @@ int hackrf_sink_c::hackrf_tx_callback(unsigned char *buffer, uint32_t length) *buffer++ = rand() % 255; #else { - std::lock_guard<std::mutex> lock(_buf_mutex); + boost::mutex::scoped_lock lock( _buf_mutex ); if ( ! cb_pop_front( &_cbuf, buffer ) ) { memset(buffer, 0, length); - if (_stopping) { - _buf_cond.notify_one(); - return -1; - } else { - std::cerr << "U" << std::flush; - } + std::cerr << "U" << std::flush; } else { // std::cerr << "-" << std::flush; _buf_cond.notify_one(); @@ -215,61 +316,42 @@ 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.get() ) + if ( ! _dev ) return false; - _stopping = false; _buf_used = 0; - hackrf_common::start(); - int ret = hackrf_start_tx( _dev.get(), _hackrf_tx_callback, (void *)this ); +#if 0 + int ret = hackrf_start_tx( _dev, _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() { - int i; - - if ( ! _dev.get() ) + if ( ! _dev ) return false; - - { - std::unique_lock<std::mutex> 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 0 + int ret = hackrf_stop_tx( _dev ); if ( ret != HACKRF_SUCCESS ) { std::cerr << "Failed to stop TX streaming (" << ret << ")" << std::endl; return false; } +#endif return true; } @@ -342,7 +424,7 @@ int hackrf_sink_c::work( int noutput_items, const gr_complex *in = (const gr_complex *) input_items[0]; { - std::unique_lock<std::mutex> lock(_buf_mutex); + boost::mutex::scoped_lock lock( _buf_mutex ); while ( ! cb_has_room(&_cbuf) ) _buf_cond.wait( lock ); @@ -372,7 +454,7 @@ int hackrf_sink_c::work( int noutput_items, if((unsigned int)noutput_items >= remaining) { { - std::lock_guard<std::mutex> lock(_buf_mutex); + boost::mutex::scoped_lock lock( _buf_mutex ); if ( ! cb_push_back( &_cbuf, _buf ) ) { _buf_used = prev_buf_used; @@ -395,7 +477,78 @@ int hackrf_sink_c::work( int noutput_items, std::vector<std::string> hackrf_sink_c::get_devices() { - return hackrf_common::get_devices(); + std::vector<std::string> 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; } size_t hackrf_sink_c::get_num_channels() @@ -405,47 +558,98 @@ size_t hackrf_sink_c::get_num_channels() osmosdr::meta_range_t hackrf_sink_c::get_sample_rates() { - return 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_sink_c::set_sample_rate( double rate ) { - return hackrf_common::set_sample_rate(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(); } double hackrf_sink_c::get_sample_rate() { - return hackrf_common::get_sample_rate(); + return _sample_rate; } osmosdr::freq_range_t hackrf_sink_c::get_freq_range( size_t chan ) { - return hackrf_common::get_freq_range(chan); + osmosdr::freq_range_t range; + + range += osmosdr::range_t( _sample_rate / 2, 7250e6 - _sample_rate / 2 ); + + return range; } double hackrf_sink_c::set_center_freq( double freq, size_t chan ) { - return hackrf_common::set_center_freq(freq, 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 ); } double hackrf_sink_c::get_center_freq( size_t chan ) { - return hackrf_common::get_center_freq(chan); + return _center_freq; } double hackrf_sink_c::set_freq_corr( double ppm, size_t chan ) { - return hackrf_common::set_freq_corr(ppm, chan); + _freq_corr = ppm; + + set_center_freq( _center_freq ); + + return get_freq_corr( chan ); } double hackrf_sink_c::get_freq_corr( size_t chan ) { - return hackrf_common::get_freq_corr(chan); + return _freq_corr; } std::vector<std::string> hackrf_sink_c::get_gain_names( size_t chan ) { - return { "RF", "IF" }; + std::vector< std::string > names; + + names += "RF"; + names += "IF"; + + return names; } osmosdr::gain_range_t hackrf_sink_c::get_gain_range( size_t chan ) @@ -468,17 +672,34 @@ 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 ) { - return hackrf_common::set_gain_mode(automatic, chan); + _auto_gain = automatic; + + return get_gain_mode(chan); } bool hackrf_sink_c::get_gain_mode( size_t chan ) { - return hackrf_common::get_gain_mode(chan); + return _auto_gain; } double hackrf_sink_c::set_gain( double gain, size_t chan ) { - return hackrf_common::set_gain(gain, 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; } double hackrf_sink_c::set_gain( double gain, const std::string & name, size_t chan) @@ -496,7 +717,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 hackrf_common::get_gain(chan); + return _amp_gain; } double hackrf_sink_c::get_gain( const std::string & name, size_t chan ) @@ -517,10 +738,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.get()) { + if (_dev) { double clip_gain = if_gains.clip( gain, true ); - ret = hackrf_set_txvga_gain( _dev.get(), uint32_t(clip_gain) ); + ret = hackrf_set_txvga_gain( _dev, uint32_t(clip_gain) ); if ( HACKRF_SUCCESS == ret ) { _vga_gain = clip_gain; } else { @@ -538,30 +759,72 @@ double hackrf_sink_c::set_bb_gain( double gain, size_t chan ) std::vector< std::string > hackrf_sink_c::get_antennas( size_t chan ) { - return hackrf_common::get_antennas(chan); + std::vector< std::string > antennas; + + antennas += get_antenna( chan ); + + return antennas; } std::string hackrf_sink_c::set_antenna( const std::string & antenna, size_t chan ) { - return hackrf_common::set_antenna(antenna, chan); + return get_antenna( chan ); } std::string hackrf_sink_c::get_antenna( size_t chan ) { - return hackrf_common::get_antenna(chan); + return "TX/RX"; } double hackrf_sink_c::set_bandwidth( double bandwidth, size_t chan ) { - return hackrf_common::set_bandwidth(bandwidth, 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; } double hackrf_sink_c::get_bandwidth( size_t chan ) { - return hackrf_common::get_bandwidth(chan); + return _bandwidth; } osmosdr::freq_range_t hackrf_sink_c::get_bandwidth_range( size_t chan ) { - return hackrf_common::get_bandwidth_range(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; } diff --git a/lib/hackrf/hackrf_sink_c.h b/lib/hackrf/hackrf_sink_c.h index 08ff2ca..a7e7ab8 100644 --- a/lib/hackrf/hackrf_sink_c.h +++ b/lib/hackrf/hackrf_sink_c.h @@ -1,35 +1,34 @@ /* -*- c++ -*- */ /* * Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net> - * Copyright 2020 Clayton Smith <argilo@gmail.com> * - * gr-osmosdr is free software; you can redistribute it and/or modify + * 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. * - * gr-osmosdr is distributed in the hope that it will be useful, + * 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 gr-osmosdr; see the file COPYING. If not, write to + * 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_HACKRF_SINK_C_H #define INCLUDED_HACKRF_SINK_C_H +#include <gnuradio/thread/thread.h> #include <gnuradio/sync_block.h> -#include <condition_variable> -#include <mutex> +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition_variable.hpp> #include <libhackrf/hackrf.h> #include "sink_iface.h" -#include "hackrf_common.h" class hackrf_sink_c; @@ -68,8 +67,7 @@ hackrf_sink_c_sptr make_hackrf_sink_c (const std::string & args = ""); class hackrf_sink_c : public gr::sync_block, - public sink_iface, - protected hackrf_common + public sink_iface { private: // The friend declaration allows hackrf_make_sink_c to @@ -126,16 +124,29 @@ 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; - bool _stopping; - std::mutex _buf_mutex; - std::condition_variable _buf_cond; - + boost::mutex _buf_mutex; + boost::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 03ea3bd..30b63c7 100644 --- a/lib/hackrf/hackrf_source_c.cc +++ b/lib/hackrf/hackrf_source_c.cc @@ -1,20 +1,19 @@ /* -*- c++ -*- */ /* * Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net> - * Copyright 2020 Clayton Smith <argilo@gmail.com> * - * gr-osmosdr is free software; you can redistribute it and/or modify + * 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. * - * gr-osmosdr is distributed in the hope that it will be useful, + * 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 gr-osmosdr; see the file COPYING. If not, write to + * 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. */ @@ -30,7 +29,12 @@ #include <stdexcept> #include <iostream> -#include <chrono> + +#include <boost/assign.hpp> +#include <boost/format.hpp> +#include <boost/detail/endian.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/thread/thread.hpp> #include <gnuradio/io_signature.h> @@ -38,6 +42,29 @@ #include "arg_helpers.h" +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)); @@ -64,20 +91,29 @@ 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))), - hackrf_common::hackrf_common(args), + _dev(NULL), _buf(NULL), + _sample_rate(0), + _center_freq(0), + _freq_corr(0), + _auto_gain(false), + _amp_gain(0), _lna_gain(0), - _vga_gain(0) + _vga_gain(0), + _bandwidth(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 = std::stoi(dict["buffers"]); + _buf_num = boost::lexical_cast< unsigned int >( dict["buffers"] ); // if (dict.count("buflen")) -// _buf_len = std::stoi(dict["buflen"]); +// _buf_len = boost::lexical_cast< unsigned int >( dict["buflen"] ); if (0 == _buf_num) _buf_num = BUF_NUM; @@ -88,10 +124,76 @@ hackrf_source_c::hackrf_source_c (const std::string &args) _samp_avail = _buf_len / BYTES_PER_SAMPLE; // create a lookup table for gr_complex values - for (unsigned int i = 0; i <= 0xff; i++) { - _lut.push_back( float(int8_t(i)) * (1.0f/128.0f) ); + for (unsigned int i = 0; i <= 0xffff; i++) { +#ifdef BOOST_LITTLE_ENDIAN + _lut.push_back( gr_complex( (float(int8_t(i & 0xff))) * (1.0f/128.0f), + (float(int8_t(i >> 8))) * (1.0f/128.0f) ) ); +#else // BOOST_BIG_ENDIAN + _lut.push_back( gr_complex( (float(int8_t(i >> 8))) * (1.0f/128.0f), + (float(int8_t(i & 0xff))) * (1.0f/128.0f) ) ); +#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; @@ -109,15 +211,29 @@ 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") ) { - hackrf_common::set_bias(dict["bias"] == "1"); + bool bias = boost::lexical_cast<bool>( dict["bias"] ); + ret = hackrf_set_antenna_enable(_dev, static_cast<uint8_t>(bias)); + if ( ret != HACKRF_SUCCESS ) + { + std::cerr << "Failed to apply antenna bias voltage state: " << bias << HACKRF_FORMAT_ERROR(ret, "") << std::endl; + } + else + { + std::cerr << (bias ? "Enabled" : "Disabled") << " antenna bias voltage" << std::endl; + } } - _buf = (unsigned char **) malloc(_buf_num * sizeof(unsigned char *)); + _buf = (unsigned short **) malloc(_buf_num * sizeof(unsigned short *)); if (_buf) { for(unsigned int i = 0; i < _buf_num; ++i) - _buf[i] = (unsigned char *) malloc(_buf_len); + _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") } /* @@ -125,6 +241,30 @@ 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]); @@ -144,7 +284,7 @@ int hackrf_source_c::_hackrf_rx_callback(hackrf_transfer *transfer) int hackrf_source_c::hackrf_rx_callback(unsigned char *buf, uint32_t len) { { - std::lock_guard<std::mutex> lock(_buf_mutex); + boost::mutex::scoped_lock lock( _buf_mutex ); int buf_tail = (_buf_head + _buf_used) % _buf_num; memcpy(_buf[buf_tail], buf, len); @@ -162,31 +302,40 @@ 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.get() ) + if ( ! _dev ) return false; - - hackrf_common::start(); - int ret = hackrf_start_rx( _dev.get(), _hackrf_rx_callback, (void *)this ); +#if 0 + int ret = hackrf_start_rx( _dev, _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.get() ) + if ( ! _dev ) return false; - - hackrf_common::stop(); - int ret = hackrf_stop_rx( _dev.get() ); +#if 0 + int ret = hackrf_stop_rx( _dev ); if ( ret != HACKRF_SUCCESS ) { std::cerr << "Failed to stop RX streaming (" << ret << ")" << std::endl; return false; } +#endif return true; } @@ -198,41 +347,33 @@ int hackrf_source_c::work( int noutput_items, bool running = false; - if ( _dev.get() ) - running = (hackrf_is_streaming( _dev.get() ) == HACKRF_TRUE); + if ( _dev ) + running = (hackrf_is_streaming( _dev ) == HACKRF_TRUE); { - std::unique_lock<std::mutex> lock(_buf_mutex); + boost::mutex::scoped_lock lock( _buf_mutex ); - while (_buf_used < 3 && running) { // collect at least 3 buffers - _buf_cond.wait_for( lock , std::chrono::milliseconds(100)); - - // Re-check whether the device has closed or stopped streaming - if ( _dev.get() ) - running = (hackrf_is_streaming( _dev.get() ) == HACKRF_TRUE); - else - running = false; - } + while (_buf_used < 3 && running) // collect at least 3 buffers + _buf_cond.wait( lock ); } if ( ! running ) return WORK_DONE; - const uint8_t *buf = _buf[_buf_head] + _buf_offset * BYTES_PER_SAMPLE; -#define TO_COMPLEX(p) gr_complex( _lut[(p)[0]], _lut[(p)[1]] ) + unsigned short *buf = _buf[_buf_head] + _buf_offset; if (noutput_items <= _samp_avail) { for (int i = 0; i < noutput_items; ++i) - *out++ = TO_COMPLEX( buf + i*BYTES_PER_SAMPLE ); + *out++ = _lut[ *(buf + i) ]; _buf_offset += noutput_items; _samp_avail -= noutput_items; } else { for (int i = 0; i < _samp_avail; ++i) - *out++ = TO_COMPLEX( buf + i*BYTES_PER_SAMPLE ); + *out++ = _lut[ *(buf + i) ]; { - std::lock_guard<std::mutex> lock(_buf_mutex); + boost::mutex::scoped_lock lock( _buf_mutex ); _buf_head = (_buf_head + 1) % _buf_num; _buf_used--; @@ -243,7 +384,7 @@ int hackrf_source_c::work( int noutput_items, int remaining = noutput_items - _samp_avail; for (int i = 0; i < remaining; ++i) - *out++ = TO_COMPLEX( buf + i*BYTES_PER_SAMPLE ); + *out++ = _lut[ *(buf + i) ]; _buf_offset = remaining; _samp_avail = (_buf_len / BYTES_PER_SAMPLE) - remaining; @@ -254,7 +395,78 @@ int hackrf_source_c::work( int noutput_items, std::vector<std::string> hackrf_source_c::get_devices() { - return hackrf_common::get_devices(); + std::vector<std::string> 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; } size_t hackrf_source_c::get_num_channels() @@ -264,47 +476,99 @@ size_t hackrf_source_c::get_num_channels() osmosdr::meta_range_t hackrf_source_c::get_sample_rates() { - return 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_source_c::set_sample_rate( double rate ) { - return hackrf_common::set_sample_rate(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(); } double hackrf_source_c::get_sample_rate() { - return hackrf_common::get_sample_rate(); + return _sample_rate; } osmosdr::freq_range_t hackrf_source_c::get_freq_range( size_t chan ) { - return hackrf_common::get_freq_range(chan); + osmosdr::freq_range_t range; + + range += osmosdr::range_t( _sample_rate / 2, 7250e6 - _sample_rate / 2 ); + + return range; } double hackrf_source_c::set_center_freq( double freq, size_t chan ) { - return hackrf_common::set_center_freq(freq, 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 ); } double hackrf_source_c::get_center_freq( size_t chan ) { - return hackrf_common::get_center_freq(chan); + return _center_freq; } double hackrf_source_c::set_freq_corr( double ppm, size_t chan ) { - return hackrf_common::set_freq_corr(ppm, chan); + _freq_corr = ppm; + + set_center_freq( _center_freq ); + + return get_freq_corr( chan ); } double hackrf_source_c::get_freq_corr( size_t chan ) { - return hackrf_common::get_freq_corr(chan); + return _freq_corr; } std::vector<std::string> hackrf_source_c::get_gain_names( size_t chan ) { - return { "RF", "IF", "BB" }; + std::vector< std::string > names; + + names += "RF"; + names += "IF"; + names += "BB"; + + return names; } osmosdr::gain_range_t hackrf_source_c::get_gain_range( size_t chan ) @@ -331,17 +595,34 @@ 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 ) { - return hackrf_common::set_gain_mode(automatic, chan); + _auto_gain = automatic; + + return get_gain_mode(chan); } bool hackrf_source_c::get_gain_mode( size_t chan ) { - return hackrf_common::get_gain_mode(chan); + return _auto_gain; } double hackrf_source_c::set_gain( double gain, size_t chan ) { - return hackrf_common::set_gain(gain, 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; } double hackrf_source_c::set_gain( double gain, const std::string & name, size_t chan) @@ -363,7 +644,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 hackrf_common::get_gain(chan); + return _amp_gain; } double hackrf_source_c::get_gain( const std::string & name, size_t chan ) @@ -388,10 +669,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.get()) { + if (_dev) { double clip_gain = rf_gains.clip( gain, true ); - ret = hackrf_set_lna_gain( _dev.get(), uint32_t(clip_gain) ); + ret = hackrf_set_lna_gain( _dev, uint32_t(clip_gain) ); if ( HACKRF_SUCCESS == ret ) { _lna_gain = clip_gain; } else { @@ -407,10 +688,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.get()) { + if (_dev) { double clip_gain = if_gains.clip( gain, true ); - ret = hackrf_set_vga_gain( _dev.get(), uint32_t(clip_gain) ); + ret = hackrf_set_vga_gain( _dev, uint32_t(clip_gain) ); if ( HACKRF_SUCCESS == ret ) { _vga_gain = clip_gain; } else { @@ -423,30 +704,72 @@ double hackrf_source_c::set_bb_gain( double gain, size_t chan ) std::vector< std::string > hackrf_source_c::get_antennas( size_t chan ) { - return hackrf_common::get_antennas(chan); + std::vector< std::string > antennas; + + antennas += get_antenna( chan ); + + return antennas; } std::string hackrf_source_c::set_antenna( const std::string & antenna, size_t chan ) { - return hackrf_common::set_antenna(antenna, chan); + return get_antenna( chan ); } std::string hackrf_source_c::get_antenna( size_t chan ) { - return hackrf_common::get_antenna(chan); + return "TX/RX"; } double hackrf_source_c::set_bandwidth( double bandwidth, size_t chan ) { - return hackrf_common::set_bandwidth(bandwidth, 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; } double hackrf_source_c::get_bandwidth( size_t chan ) { - return hackrf_common::get_bandwidth(chan); + return _bandwidth; } osmosdr::freq_range_t hackrf_source_c::get_bandwidth_range( size_t chan ) { - return hackrf_common::get_bandwidth_range(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; } diff --git a/lib/hackrf/hackrf_source_c.h b/lib/hackrf/hackrf_source_c.h index 0d38ac0..6ae81d2 100644 --- a/lib/hackrf/hackrf_source_c.h +++ b/lib/hackrf/hackrf_source_c.h @@ -1,35 +1,36 @@ /* -*- c++ -*- */ /* * Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net> - * Copyright 2020 Clayton Smith <argilo@gmail.com> * - * gr-osmosdr is free software; you can redistribute it and/or modify + * 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. * - * gr-osmosdr is distributed in the hope that it will be useful, + * 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 gr-osmosdr; see the file COPYING. If not, write to + * 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_HACKRF_SOURCE_C_H #define INCLUDED_HACKRF_SOURCE_C_H +#include <gnuradio/thread/thread.h> #include <gnuradio/sync_block.h> -#include <condition_variable> -#include <mutex> +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition_variable.hpp> #include <libhackrf/hackrf.h> #include "source_iface.h" -#include "hackrf_common.h" class hackrf_source_c; @@ -61,12 +62,12 @@ hackrf_source_c_sptr make_hackrf_source_c (const std::string & args = ""); */ class hackrf_source_c : public gr::sync_block, - public source_iface, - protected hackrf_common + public source_iface { 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); /*! @@ -122,22 +123,35 @@ 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<float> _lut; + std::vector<gr_complex> _lut; - unsigned char **_buf; + 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; - std::mutex _buf_mutex; - std::condition_variable _buf_cond; + boost::mutex _buf_mutex; + boost::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 */ |