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 <argilo@gmail.com>
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
gr3.8
Clayton Smith 3 years ago committed by Sylvain Munaut
parent 96dc33adf1
commit ae2253c516
  1. 10
      lib/hackrf/CMakeLists.txt
  2. 432
      lib/hackrf/hackrf_common.cc
  3. 111
      lib/hackrf/hackrf_common.h
  4. 414
      lib/hackrf/hackrf_sink_c.cc
  5. 37
      lib/hackrf/hackrf_sink_c.h
  6. 405
      lib/hackrf/hackrf_source_c.cc
  7. 36
      lib/hackrf/hackrf_source_c.h

@ -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)

@ -0,0 +1,432 @@
/* -*- 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 <boost/assign.hpp>
#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<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),
_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 += 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<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_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;
}

@ -0,0 +1,111 @@
/* -*- 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 <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 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_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 _bandwidth;
bool _bias;
bool _started;
};
#endif /* INCLUDED_HACKRF_COMMON_H */

@ -2,19 +2,20 @@
/*
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
* Copyright 2014 Hoernchen <la@tfc-server.de>
* Copyright 2020 Clayton Smith <argilo@gmail.com>
*
* 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 <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>
@ -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<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;
}
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<std::mutex> 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<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 ( 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<std::mutex> 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<std::mutex> 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<std::string> hackrf_sink_c::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;
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<std::string> 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);
}

@ -1,34 +1,35 @@
/* -*- c++ -*- */
/*
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
* Copyright 2020 Clayton Smith <argilo@gmail.com>
*
* 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 <gnuradio/thread/thread.h>
#include <gnuradio/sync_block.h>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <condition_variable>
#include <mutex>
#include <libhackrf/hackrf.h>
#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;
double _sample_rate;
double _center_freq;
double _freq_corr;
bool _auto_gain;
double _amp_gain;
bool _stopping;
std::mutex _buf_mutex;
std::condition_variable _buf_cond;
double _vga_gain;
double _bandwidth;
};
#endif /* INCLUDED_HACKRF_SINK_C_H */

@ -1,19 +1,20 @@
/* -*- c++ -*- */
/*
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
* Copyright 2020 Clayton Smith <argilo@gmail.com>
*
* 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 <iostream>
#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>
@ -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