bladerf: add support for MIMO

Squashed commit of rtucker-bladerf-hierarchy branch:

commit 35442da7d390919f6f9cbae3f69d6dc32ca595bb
through
commit 9026136cfdbc7116f55af18cb06d1731330fa13f
This commit is contained in:
Rey Tucker 2017-09-18 13:17:48 -04:00 committed by Dimitri Stolnikov
parent c4a0781367
commit 8f8b137cee
6 changed files with 1794 additions and 1252 deletions

File diff suppressed because it is too large Load Diff

View File

@ -21,21 +21,14 @@
#ifndef INCLUDED_BLADERF_COMMON_H
#define INCLUDED_BLADERF_COMMON_H
#include <vector>
#include <list>
#include <map>
#include <string>
#include <vector>
#include <boost/circular_buffer.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/shared_mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/assign.hpp>
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/weak_ptr.hpp>
#include <gnuradio/thread/thread.h>
#include <gnuradio/gr_complex.h>
#include <libbladeRF.h>
#include "osmosdr/ranges.h"
@ -44,103 +37,256 @@
#ifdef _MSC_VER
#include <cstddef>
typedef ptrdiff_t ssize_t;
#endif //_MSC_VER
#endif //_MSC_VER
typedef boost::shared_ptr < struct bladerf >bladerf_sptr;
#define BLADERF_DEBUG_ENABLE
typedef boost::shared_ptr<struct bladerf> bladerf_sptr;
/* Identification of the bladeRF hardware in use */
typedef enum {
BLADERF_REV_INVALID,
BLADERF_REV_1,
BLADERF_REV_2
BOARD_TYPE_UNKNOWN, /**< Board type is unknown */
BOARD_TYPE_NONE, /**< Uninitialized or no board present */
BOARD_TYPE_BLADERF_1, /**< bladeRF 1 (LMS6002D-based, 1RX/1TX) */
BOARD_TYPE_BLADERF_2, /**< bladeRF 2 (AD9361-based, 2RX/2TX) */
} bladerf_board_type;
/* Mapping of bladerf_channel to bool */
typedef std::map<bladerf_channel, bool> bladerf_channel_enable_map;
/* Mapping of bladerf_channel to gnuradio port/chan */
typedef std::map<bladerf_channel, int> bladerf_channel_map;
/* Convenience macros for throwing a runtime error */
#define BLADERF_THROW(message) \
{ \
throw std::runtime_error(std::string(__FUNCTION__) + ": " + message); \
}
#define BLADERF_THROW_STATUS(status, message) \
{ \
BLADERF_THROW(boost::str(boost::format("%s: %s (%d)") % message \
% bladerf_strerror(status) % status)); \
}
/* Convenience macros for printing a warning message to stderr */
#define BLADERF_WARNING(message) \
{ \
std::cerr << _pfx << __FUNCTION__ << ": " << message << std::endl; \
}
#define BLADERF_WARN_STATUS(status, message) \
{ \
BLADERF_WARNING(message << ": " << bladerf_strerror(status)); \
} \
/* Convenience macro for printing an informational message to stdout */
#define BLADERF_INFO(message) \
{ \
std::cout << _pfx << __FUNCTION__ << ": " << message << std::endl; \
}
/* Convenience macro for printing a debug message to stdout */
#ifdef BLADERF_DEBUG_ENABLE
#define BLADERF_DEBUG(message) BLADERF_INFO("DEBUG: " << message)
#else
#define BLADERF_DEBUG(message)
#endif // BLADERF_DEBUG_ENABLE
/* Given a bladerf_channel_layout, calculate the number of streams */
size_t num_streams(bladerf_channel_layout layout);
/**
* Common class for bladeRF interaction
*/
class bladerf_common
{
public:
/*****************************************************************************
* Public methods
****************************************************************************/
bladerf_common();
virtual ~ bladerf_common();
protected:
/*****************************************************************************
* Protected methods
****************************************************************************/
/* Handle initialized and parameters common to both source & sink */
void init(dict_t &dict, bladerf_direction direction);
/**
* Handle initialization and parameters common to both source & sink
*
* Specify arguments in key=value,key=value format, e.g.
* bladerf=0,buffers=512
*
* Recognized arguments:
* Key Allowed values
* ---------------------------------------------------------------------------
* REQUIRED:
* bladerf a valid instance or serial number
* USB INTERFACE CONTROL:
* buffers (default: NUM_BUFFERS)
* buflen (default: NUM_SAMPLES_PER_BUFFER)
* stream_timeout valid time in milliseconds (default: 3000)
* transfers (default: NUM_TRANSFERS)
* FPGA CONTROL:
* enable_metadata 1 to enable metadata
* fpga a path to a valid .rbf file
* fpga-reload 1 to force reloading the FPGA unconditionally
* RF CONTROL:
* agc 1 to enable, 0 to disable (default: hardware-dependent)
* agc_mode default, manual, fast, slow, hybrid (default: default)
* loopback bb_txlpf_rxvga2, bb_txlpf_rxlpf, bb_txvga1_rxvga2,
* bb_txvga1_rxlpf, rf_lna1, rf_lna2, rf_lna3, firmware,
* ad9361_bist, none (default: none)
* ** Note: valid on receive channels only
* rxmux baseband, 12bit, 32bit, digital (default: baseband)
* ** Note: valid on receive channels only
* smb a valid frequency
* tamer internal, external_1pps, external (default: internal)
* xb200 auto, auto3db, 50M, 144M, 222M, custom (default: auto)
* MISC:
* verbosity verbose, debug, info, warning, error, critical, silent
* (default: info)
* ** Note: applies only to libbladeRF logging
*/
void init(dict_t const &dict, bladerf_direction direction);
bool start(bladerf_direction direction);
bool stop(bladerf_direction direction);
/* Get a vector of available devices */
static std::vector<std::string> devices();
/* Get the type of the open bladeRF board */
bladerf_board_type get_board_type();
/* Get the maximum number of channels supported in a given direction */
size_t get_max_channels(bladerf_direction direction);
bladerf_board_type get_board_type(struct bladerf *dev);
void set_channel_enable(bladerf_channel ch, bool enable);
bool get_channel_enable(bladerf_channel ch);
double set_sample_rate(bladerf_direction direction, double rate);
double get_sample_rate(bladerf_direction direction);
/* Set libbladeRF verbosity */
void set_verbosity(std::string const &verbosity);
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);
/* Convert an antenna/channel name (e.g. "RX2") to a bladerf_channel */
bladerf_channel str2channel(std::string const &ch);
/* Convert a bladerf_channel to an antenna/channel name (e.g. "RX2") */
std::string channel2str(bladerf_channel ch);
/* Convert a bladerf_channel to a hardware port identifier */
int channel2rfport(bladerf_channel ch);
std::vector < std::string > get_gain_names(size_t chan = 0);
osmosdr::gain_range_t get_gain_range(size_t chan = 0);
osmosdr::gain_range_t get_gain_range(const std::string &name, size_t chan =
0);
bool set_gain_mode(bool automatic, size_t chan = 0);
bool get_gain_mode(size_t chan = 0);
double set_gain(double gain, size_t chan = 0);
double set_gain(double gain, const std::string &name, size_t chan = 0);
double get_gain(size_t chan = 0);
double get_gain(const std::string &name, size_t chan = 0);
/* Using the channel map, get the bladerf_channel for a gnuradio chan */
bladerf_channel chan2channel(bladerf_direction direction, size_t chan = 0);
int set_dc_offset(bladerf_direction direction,
const std::complex < double > &offset, size_t chan);
int set_iq_balance(bladerf_direction direction,
const std::complex < double > &balance, size_t chan);
/* Get range of supported sampling rates for channel ch */
osmosdr::meta_range_t sample_rates(bladerf_channel ch);
/* Set sampling rate on channel ch to rate */
double set_sample_rate(double rate, bladerf_channel ch);
/* Get the current sampling rate on channel ch */
double get_sample_rate(bladerf_channel ch);
void set_clock_source(const std::string &source, const size_t mboard = 0);
std::string get_clock_source(const size_t mboard = 0);
std::vector < std::string > get_clock_sources(const size_t mboard = 0);
/* Get range of supported RF frequencies for channel ch */
osmosdr::freq_range_t freq_range(bladerf_channel ch);
/* Set center RF frequency of channel ch to freq */
double set_center_freq(double freq, bladerf_channel ch);
/* Get the center RF frequency of channel ch */
double get_center_freq(bladerf_channel ch);
/* Get range of supported bandwidths for channel ch */
osmosdr::freq_range_t filter_bandwidths(bladerf_channel ch);
/* Set the bandwidth on channel ch to bandwidth */
double set_bandwidth(double bandwidth, bladerf_channel ch);
/* Get the current bandwidth of channel ch */
double get_bandwidth(bladerf_channel ch);
/* Get the names of gain stages on channel ch */
std::vector<std::string> get_gain_names(bladerf_channel ch);
/* Get range of supported overall gain values on channel ch */
osmosdr::gain_range_t get_gain_range(bladerf_channel ch);
/* Get range of supported gain values for gain stage 'name' on channel ch */
osmosdr::gain_range_t get_gain_range(std::string const &name,
bladerf_channel ch);
/* Enable or disable the automatic gain control on channel ch */
bool set_gain_mode(bool automatic, bladerf_channel ch,
bladerf_gain_mode agc_mode = BLADERF_GAIN_DEFAULT);
/* Get the current automatic gain control status on channel ch */
bool get_gain_mode(bladerf_channel ch);
/* Set the overall gain value on channel ch */
double set_gain(double gain, bladerf_channel ch);
/* Set the gain of stage 'name' on channel ch */
double set_gain(double gain, std::string const &name, bladerf_channel ch);
/* Get the overall gain value on channel ch */
double get_gain(bladerf_channel ch);
/* Get the gain of stage 'name' on channel ch */
double get_gain(std::string const &name, bladerf_channel ch);
/* Get the list of antennas supported by a channel */
std::vector<std::string> get_antennas(bladerf_direction dir);
bool set_antenna(bladerf_direction dir, size_t chan, const std::string &antenna);
/* Set the DC offset on channel ch */
int set_dc_offset(std::complex<double> const &offset, bladerf_channel ch);
/* Set the IQ balance on channel ch */
int set_iq_balance(std::complex<double> const &balance, bladerf_channel ch);
/* Get the list of supported clock sources */
std::vector<std::string> get_clock_sources(size_t mboard = 0);
/* Set the clock source to */
void set_clock_source(std::string const &source, size_t mboard = 0);
/* Get the name of the current clock source */
std::string get_clock_source(size_t mboard = 0);
/* Set the SMB frequency */
void set_smb_frequency(double frequency);
/* Get the current SMB frequency */
double get_smb_frequency();
osmosdr::freq_range_t freq_range(bladerf_channel chan);
osmosdr::meta_range_t sample_rates();
osmosdr::freq_range_t filter_bandwidths();
/*****************************************************************************
* Protected members
****************************************************************************/
bladerf_sptr _dev; /**< shared pointer for the active device */
std::string _pfx; /**< prefix for console messages */
unsigned int _failures; /**< counter for consecutive rx/tx failures */
static std::vector < std::string > devices();
size_t _num_buffers; /**< number of buffers to allocate */
size_t _samples_per_buffer; /**< how many samples per buffer */
size_t _num_transfers; /**< number of active backend transfers */
unsigned int _stream_timeout; /**< timeout for backend transfers */
bladerf_sptr _dev;
bladerf_format _format; /**< sample format to use */
size_t _num_buffers;
size_t _samples_per_buffer;
size_t _num_transfers;
unsigned int _stream_timeout_ms;
bladerf_channel_map _chanmap; /**< map of antennas to channels */
bladerf_channel_enable_map _enables; /**< enabled channels */
int16_t *_conv_buf;
int _conv_buf_size; /* In units of samples */
bool _use_metadata;
bool _use_mimo;
std::string _pfx;
bool _xb_200_attached;
unsigned int _consecutive_failures;
/*****************************************************************************
* Protected constants
****************************************************************************/
/* Maximum bladerf_sync_{rx,tx} failures to allow before giving up */
static const unsigned int MAX_CONSECUTIVE_FAILURES = 3;
/* BladeRF IQ correction parameters */
static const int16_t DCOFF_SCALE = 2048;
static const int16_t GAIN_SCALE = 4096;
static const int16_t PHASE_SCALE = 4096;
static const unsigned int MAX_CONSECUTIVE_FAILURES = 3;
private:
/*****************************************************************************
* Private methods
****************************************************************************/
/* Open the bladeRF described by device_name. Returns a sptr if successful */
bladerf_sptr open(const std::string &device_name);
static void close(void *dev); /* called by shared_ptr */
/* Called by shared_ptr when a bladerf_sptr hits a refcount of 0 */
static void close(void *dev);
/* If a device described by devinfo is open, this returns a sptr to it */
static bladerf_sptr get_cached_device(struct bladerf_devinfo devinfo);
/* Prints a summary of device information */
void print_device_info();
void set_verbosity(const std::string &verbosity);
void set_loopback_mode(const std::string &loopback);
void set_rx_mux_mode(const std::string &rxmux);
bool is_antenna_valid(bladerf_direction dir, const std::string &antenna);
static boost::mutex _devs_mutex;
static std::list < boost::weak_ptr < struct bladerf >>_devs;
/*****************************************************************************
* Private members
****************************************************************************/
static boost::mutex _devs_mutex; /**< mutex for access to _devs */
static std::list<boost::weak_ptr<struct bladerf>> _devs; /**< dev cache */
};
#endif

View File

@ -35,23 +35,19 @@
#include <boost/lexical_cast.hpp>
#include <gnuradio/io_signature.h>
#include <gnuradio/tags.h>
#include <gnuradio/sync_block.h>
#include <volk/volk.h>
#include "arg_helpers.h"
#include "bladerf_sink_c.h"
//#define DEBUG_BLADERF_SINK
#ifdef DEBUG_BLADERF_SINK
#define DBG(input) std::cerr << _pfx << input << std::endl
#else
#define DBG(input)
#endif
#include "osmosdr/sink.h"
using namespace boost::assign;
/******************************************************************************
* Functions
******************************************************************************/
/*
* Create a new instance of bladerf_sink_c and return
* a boost shared_ptr. This is effectively the public constructor.
@ -61,54 +57,233 @@ bladerf_sink_c_sptr make_bladerf_sink_c(const std::string &args)
return gnuradio::get_initial_sptr(new bladerf_sink_c(args));
}
/******************************************************************************
* Private methods
******************************************************************************/
/*
* The private constructor
*/
bladerf_sink_c::bladerf_sink_c(const std::string &args)
:gr::sync_block("bladerf_sink_c",
bladerf_sink_c::bladerf_sink_c(const std::string &args) :
gr::sync_block( "bladerf_sink_c",
args_to_io_signature(args),
gr::io_signature::make(0, 0, 0))
gr::io_signature::make(0, 0, 0)),
_16icbuf(NULL),
_32fcbuf(NULL),
_in_burst(false),
_running(false)
{
dict_t dict = params_to_dict(args);
/* Perform src/sink agnostic initializations */
init(dict, BLADERF_TX);
/* Bounds-checking input signature depending on our underlying hardware */
size_t max_nchan = 1;
if (get_board_type(_dev.get()) == BLADERF_REV_2) {
max_nchan = 2;
/* Check for RX-only params */
if (dict.count("loopback")) {
BLADERF_WARNING("Warning: 'loopback' has been specified on a bladeRF "
"sink, and will have no effect. This parameter should be "
"specified on the associated bladeRF source.");
}
if (get_num_channels() > max_nchan) {
std::cerr << _pfx
<< "Warning: number of channels specified on command line ("
<< get_num_channels() << ") is greater than the maximum number "
<< "supported by this device (" << max_nchan << "). Resetting "
<< "to " << max_nchan << "."
<< std::endl;
set_input_signature( gr::io_signature::make(max_nchan, max_nchan, sizeof(gr_complex) ) );
if (dict.count("rxmux")) {
BLADERF_WARNING("Warning: 'rxmux' has been specified on a bladeRF sink, "
"and will have no effect.");
}
_use_mimo = get_num_channels() > 1;
/* Initialize channel <-> antenna map */
BOOST_FOREACH(std::string ant, get_antennas()) {
_chanmap[str2channel(ant)] = -1;
}
/* Bounds-checking output signature depending on our underlying hardware */
if (get_num_channels() > get_max_channels()) {
BLADERF_WARNING("Warning: number of channels specified on command line ("
<< get_num_channels() << ") is greater than the maximum "
"number supported by this device (" << get_max_channels()
<< "). Resetting to " << get_max_channels() << ".");
set_input_signature(gr::io_signature::make(get_max_channels(),
get_max_channels(),
sizeof(gr_complex)));
}
/* Set up constraints */
int const alignment_multiple = volk_get_alignment() / sizeof(gr_complex);
set_alignment(std::max(1,alignment_multiple));
set_max_noutput_items(_samples_per_buffer);
set_output_multiple(get_num_channels());
/* Set channel layout */
_layout = (get_num_channels() > 1) ? BLADERF_TX_X2 : BLADERF_TX_X1;
/* Initial wiring of antennas to channels */
for (size_t ch = 0; ch < get_num_channels(); ++ch) {
set_channel_enable(BLADERF_CHANNEL_TX(ch), true);
_chanmap[BLADERF_CHANNEL_TX(ch)] = ch;
}
BLADERF_DEBUG("initialization complete");
}
/******************************************************************************
* Public methods
******************************************************************************/
std::string bladerf_sink_c::name()
{
return "bladeRF transmitter";
}
std::vector<std::string> bladerf_sink_c::get_devices()
{
return bladerf_common::devices();
}
size_t bladerf_sink_c::get_max_channels()
{
return bladerf_common::get_max_channels(BLADERF_TX);
}
size_t bladerf_sink_c::get_num_channels()
{
return input_signature()->max_streams();
}
bool bladerf_sink_c::start()
{
int status;
BLADERF_DEBUG("starting sink");
gr::thread::scoped_lock guard(d_mutex);
_in_burst = false;
return bladerf_common::start(BLADERF_TX);
status = bladerf_sync_config(_dev.get(), _layout, _format, _num_buffers,
_samples_per_buffer, _num_transfers,
_stream_timeout);
if (status != 0) {
BLADERF_THROW_STATUS(status, "bladerf_sync_config failed");
}
for (size_t ch = 0; ch < get_max_channels(); ++ch) {
bladerf_channel brfch = BLADERF_CHANNEL_TX(ch);
if (get_channel_enable(brfch)) {
status = bladerf_enable_module(_dev.get(), brfch, true);
if (status != 0) {
BLADERF_THROW_STATUS(status, "bladerf_enable_module failed");
}
}
}
/* Allocate memory for conversions in work() */
size_t alignment = volk_get_alignment();
_16icbuf = reinterpret_cast<int16_t *>(volk_malloc(2*_samples_per_buffer*sizeof(int16_t), alignment));
_32fcbuf = reinterpret_cast<gr_complex *>(volk_malloc(_samples_per_buffer*sizeof(gr_complex), alignment));
_running = true;
return true;
}
bool bladerf_sink_c::stop()
{
return bladerf_common::stop(BLADERF_TX);
int status;
BLADERF_DEBUG("stopping sink");
gr::thread::scoped_lock guard(d_mutex);
if (!_running) {
BLADERF_WARNING("sink already stopped, nothing to do here");
return true;
}
_running = false;
for (size_t ch = 0; ch < get_max_channels(); ++ch) {
bladerf_channel brfch = BLADERF_CHANNEL_TX(ch);
if (get_channel_enable(brfch)) {
status = bladerf_enable_module(_dev.get(), brfch, false);
if (status != 0) {
BLADERF_THROW_STATUS(status, "bladerf_enable_module failed");
}
}
}
/* Deallocate conversion memory */
volk_free(_16icbuf);
volk_free(_32fcbuf);
_16icbuf = NULL;
_32fcbuf = NULL;
return true;
}
#define INVALID_IDX -1
int bladerf_sink_c::work(int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
int status;
size_t nstreams = num_streams(_layout);
int bladerf_sink_c::transmit_with_tags(int noutput_items)
gr::thread::scoped_lock guard(d_mutex);
// if we aren't running, nothing to do here
if (!_running) {
return 0;
}
// copy the samples from input_items
gr_complex const **in = reinterpret_cast<gr_complex const **>(&input_items[0]);
if (nstreams > 1) {
// we need to interleave the streams as we copy
gr_complex *intl_out = _32fcbuf;
for (size_t i = 0; i < (noutput_items/nstreams); ++i) {
for (size_t n = 0; n < nstreams; ++n) {
memcpy(intl_out++, in[n]++, sizeof(gr_complex));
}
}
} else {
// no interleaving to do: simply copy everything
memcpy(_32fcbuf, in[0], noutput_items * sizeof(gr_complex));
}
// convert floating point to fixed point and scale
// input_items is gr_complex (2x float), so num_points is 2*noutput_items
volk_32f_s32f_convert_16i(_16icbuf, reinterpret_cast<float const *>(_32fcbuf),
SCALING_FACTOR, 2*noutput_items);
// transmit the samples from the temp buffer
if (BLADERF_FORMAT_SC16_Q11_META == _format) {
status = transmit_with_tags(_16icbuf, noutput_items);
} else {
status = bladerf_sync_tx(_dev.get(), static_cast<void const *>(_16icbuf),
noutput_items, NULL, _stream_timeout);
}
// handle failure
if (status != 0) {
BLADERF_WARNING("bladerf_sync_tx error: " << bladerf_strerror(status));
++_failures;
if (_failures >= MAX_CONSECUTIVE_FAILURES) {
BLADERF_WARNING("Consecutive error limit hit. Shutting down.");
return WORK_DONE;
}
} else {
_failures = 0;
}
return noutput_items;
}
int bladerf_sink_c::transmit_with_tags(int16_t const *samples,
int noutput_items)
{
int status;
int count = 0;
@ -120,13 +295,14 @@ int bladerf_sink_c::transmit_with_tags(int noutput_items)
int end_idx = (noutput_items - 1);
struct bladerf_metadata meta;
std::vector < gr::tag_t > tags;
std::vector<gr::tag_t> tags;
int16_t zeros[8] = { 0 };
int const INVALID_IDX = -1;
int16_t const zeros[8] = { 0 };
memset(&meta, 0, sizeof(meta));
DBG("transmit_with_tags(" << noutput_items << ")");
BLADERF_DEBUG("transmit_with_tags(" << noutput_items << ")");
// Important Note: We assume that these tags are ordered by their offsets.
// This is true for GNU Radio 3.7.7.x, since the GR runtime libs store
@ -138,57 +314,56 @@ int bladerf_sink_c::transmit_with_tags(int noutput_items)
if (tags.size() == 0) {
if (_in_burst) {
DBG("TX'ing " << noutput_items << " samples in within a burst...");
BLADERF_DEBUG("TX'ing " << noutput_items << " samples within a burst...");
return bladerf_sync_tx(_dev.get(),
static_cast < void *>(_conv_buf),
noutput_items, &meta, _stream_timeout_ms);
return bladerf_sync_tx(_dev.get(), samples, noutput_items,
&meta, _stream_timeout);
} else {
std::cerr << _pfx
<< "Dropping " << noutput_items << " samples not in a burst."
<< std::endl;
BLADERF_WARNING("Dropping " << noutput_items
<< " samples not in a burst.");
}
}
BOOST_FOREACH(gr::tag_t tag, tags) {
// Upon seeing an SOB tag, update our offset. We'll TX the start of the
// burst when we see an EOB or at the end of this function - whichever
// occurs first.
if (pmt::symbol_to_string(tag.key) == "tx_sob") {
if (_in_burst) {
std::cerr << ("Got SOB while already within a burst") << std::endl;
BLADERF_WARNING("Got SOB while already within a burst");
return BLADERF_ERR_INVAL;
} else {
start_idx = static_cast < int >(tag.offset - nitems_read(0));
DBG("Got SOB " << start_idx << " samples into work payload");
start_idx = static_cast<int>(tag.offset - nitems_read(0));
meta.flags |=
(BLADERF_META_FLAG_TX_NOW | BLADERF_META_FLAG_TX_BURST_START);
BLADERF_DEBUG("Got SOB " << start_idx << " samples into work payload");
meta.flags |= (BLADERF_META_FLAG_TX_NOW | BLADERF_META_FLAG_TX_BURST_START);
_in_burst = true;
}
} else if (pmt::symbol_to_string(tag.key) == "tx_eob") {
if (!_in_burst) {
std::cerr << _pfx << "Got EOB while not in burst" << std::endl;
BLADERF_WARNING("Got EOB while not in burst");
return BLADERF_ERR_INVAL;
}
// Upon seeing an EOB, transmit what we have and reset our state
end_idx = static_cast < int >(tag.offset - nitems_read(0));
DBG("Got EOB " << end_idx << " samples into work payload");
end_idx = static_cast<int>(tag.offset - nitems_read(0));
BLADERF_DEBUG("Got EOB " << end_idx << " samples into work payload");
if ((start_idx == INVALID_IDX) || (start_idx > end_idx)) {
DBG("Buffer indicies are in an invalid state!");
BLADERF_DEBUG("Buffer indicies are in an invalid state!");
return BLADERF_ERR_INVAL;
}
count = end_idx - start_idx + 1;
DBG("TXing @ EOB [" << start_idx << ":" << end_idx << "]");
BLADERF_DEBUG("TXing @ EOB [" << start_idx << ":" << end_idx << "]");
status = bladerf_sync_tx(_dev.get(),
static_cast < void *>(&_conv_buf[2 * start_idx]),
count, &meta, _stream_timeout_ms);
static_cast<void const *>(&samples[2 * start_idx]),
count, &meta, _stream_timeout);
if (status != 0) {
return status;
}
@ -197,16 +372,15 @@ int bladerf_sink_c::transmit_with_tags(int noutput_items)
* as of the libbladeRF version that includes the
* TX_UPDATE_TIMESTAMP flag. Verify this potentially remove this.
* (The meta.flags changes would then be applied to the previous
* bladerf_sync_tx() call.)
* bladerf_sync_tx() call.)
*/
DBG("TXing Zeros with burst end flag");
BLADERF_DEBUG("TXing Zeros with burst end flag");
meta.flags &= ~(BLADERF_META_FLAG_TX_NOW | BLADERF_META_FLAG_TX_BURST_START);
meta.flags |= BLADERF_META_FLAG_TX_BURST_END;
status = bladerf_sync_tx(_dev.get(),
static_cast < void *>(zeros),
4, &meta, _stream_timeout_ms);
status = bladerf_sync_tx(_dev.get(), static_cast<void const *>(zeros),
4, &meta, _stream_timeout);
/* Reset our state */
start_idx = INVALID_IDX;
@ -215,7 +389,7 @@ int bladerf_sink_c::transmit_with_tags(int noutput_items)
_in_burst = false;
if (status != 0) {
DBG("Failed to send zero samples to flush EOB");
BLADERF_DEBUG("Failed to send zero samples to flush EOB");
return status;
}
}
@ -225,113 +399,51 @@ int bladerf_sink_c::transmit_with_tags(int noutput_items)
if (_in_burst) {
count = end_idx - start_idx + 1;
DBG("TXing SOB [" << start_idx << ":" << end_idx << "]");
BLADERF_DEBUG("TXing SOB [" << start_idx << ":" << end_idx << "]");
status = bladerf_sync_tx(_dev.get(),
static_cast < void *>(&_conv_buf[2 * start_idx]),
count, &meta, _stream_timeout_ms);
static_cast<void const *>(&samples[2 * start_idx]),
count, &meta, _stream_timeout);
}
return status;
}
int bladerf_sink_c::work(int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
const gr_complex *in = (const gr_complex *) input_items[0];
const float scaling = 2000.0f;
int status;
if (noutput_items > _conv_buf_size) {
void *tmp;
_conv_buf_size = noutput_items;
tmp = realloc(_conv_buf, _conv_buf_size * 2 * sizeof(int16_t));
if (tmp == NULL) {
throw std::runtime_error(_pfx + "Failed to realloc _conv_buf");
} else {
DBG("Resized _conv_buf to " << _conv_buf_size << " samples");
}
_conv_buf = static_cast < int16_t * >(tmp);
}
/* Convert floating point samples into fixed point */
volk_32f_s32f_convert_16i(_conv_buf, (float *) in, scaling,
2 * noutput_items);
if (_use_metadata) {
status = transmit_with_tags(noutput_items);
} else {
status = bladerf_sync_tx(_dev.get(), static_cast < void *>(_conv_buf),
noutput_items, NULL, _stream_timeout_ms);
}
if (status != 0) {
std::cerr << _pfx
<< "bladerf_sync_tx error: " << bladerf_strerror(status)
<< std::endl;
_consecutive_failures++;
if (_consecutive_failures >= MAX_CONSECUTIVE_FAILURES) {
noutput_items = WORK_DONE;
std::cerr << _pfx
<< "Consecutive error limit hit. Shutting down."
<< std::endl;
}
} else {
_consecutive_failures = 0;
}
return noutput_items;
}
std::vector < std::string > bladerf_sink_c::get_devices()
{
return bladerf_common::devices();
}
size_t bladerf_sink_c::get_num_channels()
{
return input_signature()->max_streams();
}
osmosdr::meta_range_t bladerf_sink_c::get_sample_rates()
{
return sample_rates();
return sample_rates(chan2channel(BLADERF_TX, 0));
}
double bladerf_sink_c::set_sample_rate(double rate)
{
return bladerf_common::set_sample_rate(BLADERF_TX, rate);
return bladerf_common::set_sample_rate(rate, chan2channel(BLADERF_TX, 0));
}
double bladerf_sink_c::get_sample_rate()
{
return bladerf_common::get_sample_rate(BLADERF_TX);
return bladerf_common::get_sample_rate(chan2channel(BLADERF_TX, 0));
}
osmosdr::freq_range_t bladerf_sink_c::get_freq_range(size_t chan)
{
return bladerf_common::get_freq_range(BLADERF_CHANNEL_TX(chan));
return bladerf_common::freq_range(chan2channel(BLADERF_TX, chan));
}
double bladerf_sink_c::set_center_freq(double freq, size_t chan)
{
return bladerf_common::set_center_freq(freq, BLADERF_CHANNEL_TX(chan));
return bladerf_common::set_center_freq(freq, chan2channel(BLADERF_TX, chan));
}
double bladerf_sink_c::get_center_freq(size_t chan)
{
return bladerf_common::get_center_freq(BLADERF_CHANNEL_TX(chan));
return bladerf_common::get_center_freq(chan2channel(BLADERF_TX, chan));
}
double bladerf_sink_c::set_freq_corr(double ppm, size_t chan)
{
/* TODO: Write the VCTCXO with a correction value (also changes RX ppm value!) */
return get_freq_corr(BLADERF_CHANNEL_TX(chan));
BLADERF_WARNING("Frequency correction is not implemented.");
return get_freq_corr(chan2channel(BLADERF_TX, chan));
}
double bladerf_sink_c::get_freq_corr(size_t chan)
@ -340,77 +452,80 @@ double bladerf_sink_c::get_freq_corr(size_t chan)
return 0;
}
std::vector < std::string > bladerf_sink_c::get_gain_names(size_t chan)
std::vector<std::string> bladerf_sink_c::get_gain_names(size_t chan)
{
return bladerf_common::get_gain_names(BLADERF_CHANNEL_TX(chan));
return bladerf_common::get_gain_names(chan2channel(BLADERF_TX, chan));
}
osmosdr::gain_range_t bladerf_sink_c::get_gain_range(size_t chan)
{
return bladerf_common::get_gain_range(BLADERF_CHANNEL_TX(chan));
return bladerf_common::get_gain_range(chan2channel(BLADERF_TX, chan));
}
osmosdr::gain_range_t bladerf_sink_c::get_gain_range(const std::string &name,
size_t chan)
{
return bladerf_common::get_gain_range(name, BLADERF_CHANNEL_TX(chan));
return bladerf_common::get_gain_range(name, chan2channel(BLADERF_TX, chan));
}
bool bladerf_sink_c::set_gain_mode(bool automatic, size_t chan)
{
return bladerf_common::set_gain_mode(automatic, BLADERF_CHANNEL_TX(chan));
return bladerf_common::set_gain_mode(automatic,
chan2channel(BLADERF_TX, chan));
}
bool bladerf_sink_c::get_gain_mode(size_t chan)
{
return bladerf_common::get_gain_mode(BLADERF_CHANNEL_TX(chan));
return bladerf_common::get_gain_mode(chan2channel(BLADERF_TX, chan));
}
double bladerf_sink_c::set_gain(double gain, size_t chan)
{
return bladerf_common::set_gain(gain, BLADERF_CHANNEL_TX(chan));
return bladerf_common::set_gain(gain, chan2channel(BLADERF_TX, chan));
}
double bladerf_sink_c::set_gain(double gain, const std::string &name,
size_t chan)
{
return bladerf_common::set_gain(gain, name, BLADERF_CHANNEL_TX(chan));
return bladerf_common::set_gain(gain, name, chan2channel(BLADERF_TX, chan));
}
double bladerf_sink_c::get_gain(size_t chan)
{
return bladerf_common::get_gain(BLADERF_CHANNEL_TX(chan));
return bladerf_common::get_gain(chan2channel(BLADERF_TX, chan));
}
double bladerf_sink_c::get_gain(const std::string &name, size_t chan)
{
return bladerf_common::get_gain(name, BLADERF_CHANNEL_TX(chan));
return bladerf_common::get_gain(name, chan2channel(BLADERF_TX, chan));
}
std::vector < std::string > bladerf_sink_c::get_antennas(size_t chan)
std::vector<std::string> bladerf_sink_c::get_antennas(size_t chan)
{
std::vector < std::string > antennas;
antennas += "TX0";
if (BLADERF_REV_2 == get_board_type(_dev.get())) {
antennas += "TX1";
}
return antennas;
return bladerf_common::get_antennas(BLADERF_TX);
}
std::string bladerf_sink_c::set_antenna(const std::string &antenna,
size_t chan)
{
return get_antenna(BLADERF_CHANNEL_TX(chan));
bool _was_running = _running;
if (_was_running) {
stop();
}
bladerf_common::set_antenna(BLADERF_TX, chan, antenna);
if (_was_running) {
start();
}
return get_antenna(chan);
}
std::string bladerf_sink_c::get_antenna(size_t chan)
{
/* We only have a single transmit antenna here */
// TODO: the above is a lie
return "TX0";
return channel2str(chan2channel(BLADERF_TX, chan));
}
void bladerf_sink_c::set_dc_offset(const std::complex < double > &offset,
@ -418,11 +533,10 @@ void bladerf_sink_c::set_dc_offset(const std::complex < double > &offset,
{
int status;
status = bladerf_common::set_dc_offset(BLADERF_TX, offset, chan);
status = bladerf_common::set_dc_offset(offset, chan2channel(BLADERF_TX, chan));
if (status != 0) {
throw std::runtime_error(_pfx + "could not set dc offset: " +
bladerf_strerror(status));
BLADERF_THROW_STATUS(status, "could not set dc offset");
}
}
@ -431,66 +545,40 @@ void bladerf_sink_c::set_iq_balance(const std::complex < double > &balance,
{
int status;
status = bladerf_common::set_iq_balance(BLADERF_TX, balance, chan);
status = bladerf_common::set_iq_balance(balance, chan2channel(BLADERF_TX, chan));
if (status != 0) {
throw std::runtime_error(_pfx + "could not set iq balance: " +
bladerf_strerror(status));
BLADERF_THROW_STATUS(status, "could not set iq balance");
}
}
double bladerf_sink_c::set_bandwidth(double bandwidth, size_t chan)
{
int status;
uint32_t actual;
if (bandwidth == 0.0) {
/* bandwidth of 0 means automatic filter selection */
/* select narrower filters to prevent aliasing */
bandwidth = get_sample_rate() * 0.75;
}
status = bladerf_set_bandwidth(_dev.get(), BLADERF_TX, (uint32_t) bandwidth,
&actual);
if (status != 0) {
throw std::runtime_error(_pfx + "could not set bandwidth:" +
bladerf_strerror(status));
}
return get_bandwidth();
}
double bladerf_sink_c::get_bandwidth(size_t chan)
{
int status;
uint32_t bandwidth;
status = bladerf_get_bandwidth(_dev.get(), BLADERF_TX, &bandwidth);
if (status != 0) {
throw std::runtime_error(_pfx + "could not get bandwidth: " +
bladerf_strerror(status));
}
return (double) bandwidth;
}
osmosdr::freq_range_t bladerf_sink_c::get_bandwidth_range(size_t chan)
{
return filter_bandwidths();
return filter_bandwidths(chan2channel(BLADERF_TX, chan));
}
double bladerf_sink_c::set_bandwidth(double bandwidth, size_t chan)
{
return bladerf_common::set_bandwidth(bandwidth, chan2channel(BLADERF_TX, chan));
}
double bladerf_sink_c::get_bandwidth(size_t chan)
{
return bladerf_common::get_bandwidth(chan2channel(BLADERF_TX, chan));
}
std::vector < std::string > bladerf_sink_c::get_clock_sources(size_t mboard)
{
return bladerf_common::get_clock_sources(mboard);
}
void bladerf_sink_c::set_clock_source(const std::string &source,
const size_t mboard)
size_t mboard)
{
bladerf_common::set_clock_source(source, mboard);
}
std::string bladerf_sink_c::get_clock_source(const size_t mboard)
std::string bladerf_sink_c::get_clock_source(size_t mboard)
{
return bladerf_common::get_clock_source(mboard);
}
std::vector < std::string > bladerf_sink_c::get_clock_sources(const size_t mboard)
{
return bladerf_common::get_clock_sources(mboard);
}

View File

@ -21,14 +21,12 @@
#ifndef INCLUDED_BLADERF_SINK_C_H
#define INCLUDED_BLADERF_SINK_C_H
#include <gnuradio/thread/thread.h>
#include <gnuradio/block.h>
#include <gnuradio/sync_block.h>
#include "osmosdr/ranges.h"
#include "sink_iface.h"
#include "bladerf_common.h"
#include "osmosdr/ranges.h"
class bladerf_sink_c;
/*
@ -42,7 +40,7 @@ class bladerf_sink_c;
*
* As a convention, the _sptr suffix indicates a boost::shared_ptr
*/
typedef boost::shared_ptr < bladerf_sink_c > bladerf_sink_c_sptr;
typedef boost::shared_ptr<bladerf_sink_c> bladerf_sink_c_sptr;
/*!
* \brief Return a shared_ptr to a new instance of bladerf_sink_c.
@ -53,25 +51,28 @@ typedef boost::shared_ptr < bladerf_sink_c > bladerf_sink_c_sptr;
*/
bladerf_sink_c_sptr make_bladerf_sink_c(const std::string &args = "");
class bladerf_sink_c:public gr::sync_block,
public sink_iface, protected bladerf_common
class bladerf_sink_c :
public gr::sync_block,
public sink_iface,
protected bladerf_common
{
private:
// The friend declaration allows bladerf_make_sink_c to
// access the private constructor.
friend bladerf_sink_c_sptr make_bladerf_sink_c(const std::string &args);
bladerf_sink_c(const std::string &args); // private constructor
bladerf_sink_c(const std::string &args);
// Transmit converted samples stored in _conv_buf, applying SOB and EOB
// based upon the provided tags
//
// Returns bladeRF error code
int transmit_with_tags(int noutput_items);
bool _in_burst;
bool is_antenna_valid(const std::string &antenna);
public:
std::string name();
static std::vector<std::string> get_devices();
size_t get_max_channels(void);
size_t get_num_channels(void);
bool start();
bool stop();
@ -79,10 +80,6 @@ public:
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items);
static std::vector < std::string > get_devices();
size_t get_num_channels(void);
osmosdr::meta_range_t get_sample_rates(void);
double set_sample_rate(double rate);
double get_sample_rate(void);
@ -94,10 +91,10 @@ public:
double set_freq_corr(double ppm, size_t chan = 0);
double get_freq_corr(size_t chan = 0);
std::vector < std::string > get_gain_names(size_t chan = 0);
std::vector<std::string> get_gain_names(size_t chan = 0);
osmosdr::gain_range_t get_gain_range(size_t chan = 0);
osmosdr::gain_range_t get_gain_range(const std::string &name, size_t chan =
0);
osmosdr::gain_range_t get_gain_range(const std::string &name,
size_t chan = 0);
bool set_gain_mode(bool automatic, size_t chan = 0);
bool get_gain_mode(size_t chan = 0);
double set_gain(double gain, size_t chan = 0);
@ -105,20 +102,36 @@ public:
double get_gain(size_t chan = 0);
double get_gain(const std::string &name, size_t chan = 0);
std::vector < std::string > get_antennas(size_t chan = 0);
std::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);
void set_dc_offset(const std::complex < double > &offset, size_t chan);
void set_iq_balance(const std::complex < double > &balance, size_t chan);
void set_dc_offset(const std::complex<double> &offset, size_t chan);
void set_iq_balance(const std::complex<double> &balance, size_t chan);
osmosdr::freq_range_t get_bandwidth_range(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);
void set_clock_source(const std::string &source, const size_t mboard = 0);
std::string get_clock_source(const size_t mboard);
std::vector < std::string > get_clock_sources(const size_t mboard);
std::vector<std::string> get_clock_sources(size_t mboard);
void set_clock_source(const std::string &source, size_t mboard = 0);
std::string get_clock_source(size_t mboard);
private:
int transmit_with_tags(int16_t const *samples, int noutput_items);
// Sample-handling buffers
int16_t *_16icbuf; /**< raw samples to bladeRF */
gr_complex *_32fcbuf; /**< intermediate buffer for conversions */
bool _in_burst; /**< are we currently in a burst? */
bool _running; /**< is the sink running? */
bladerf_channel_layout _layout; /**< channel layout */
gr::thread::mutex d_mutex; /**< mutex to protect set/work access */
/* Scaling factor used when converting from float to int16_t */
const float SCALING_FACTOR = 2048.0f;
};
#endif /* INCLUDED_BLADERF_SINK_C_H */
#endif // INCLUDED_BLADERF_SINK_C_H

View File

@ -44,6 +44,10 @@
using namespace boost::assign;
/******************************************************************************
* Functions
******************************************************************************/
/*
* Create a new instance of bladerf_source_c and return
* a boost shared_ptr. This is effectively the public constructor.
@ -53,195 +57,326 @@ bladerf_source_c_sptr make_bladerf_source_c(const std::string &args)
return gnuradio::get_initial_sptr(new bladerf_source_c(args));
}
/******************************************************************************
* Private methods
******************************************************************************/
/*
* The private constructor
*/
bladerf_source_c::bladerf_source_c(const std::string &args)
:gr::sync_block("bladerf_source_c",
bladerf_source_c::bladerf_source_c(const std::string &args) :
gr::sync_block( "bladerf_source_c",
gr::io_signature::make(0, 0, 0),
args_to_io_signature(args))
args_to_io_signature(args)),
_16icbuf(NULL),
_32fcbuf(NULL),
_running(false),
_agcmode(BLADERF_GAIN_DEFAULT)
{
int status;
std::string device_name;
struct bladerf_version fpga_version;
dict_t dict = params_to_dict(args);
/* Perform src/sink agnostic initializations */
init(dict, BLADERF_RX);
/* Handle setting of sampling mode */
if (dict.count("sampling")) {
std::string sampling = dict["sampling"];
std::cerr << _pfx
<< "Setting bladerf sampling to " << sampling
<< std::endl;
if (sampling == "internal") {
status = bladerf_set_sampling(_dev.get(), BLADERF_SAMPLING_INTERNAL);
if (status != 0) {
std::cerr << _pfx
<< "Problem while setting sampling mode: "
<< bladerf_strerror(status)
<< std::endl;
}
} else if (sampling == "external") {
status = bladerf_set_sampling(_dev.get(), BLADERF_SAMPLING_EXTERNAL);
if (status != 0) {
std::cerr << _pfx
<< "Problem while setting sampling mode: "
<< bladerf_strerror(status)
<< std::endl;
}
bladerf_sampling sampling = BLADERF_SAMPLING_UNKNOWN;
if (dict["sampling"] == "internal") {
sampling = BLADERF_SAMPLING_INTERNAL;
} else if (dict["sampling"] == "external") {
sampling = BLADERF_SAMPLING_EXTERNAL;
} else {
std::cerr << _pfx << "Invalid sampling mode " << sampling << std::endl;
BLADERF_WARNING("Invalid sampling mode: " + dict["sampling"]);
}
if (sampling != BLADERF_SAMPLING_UNKNOWN) {
status = bladerf_set_sampling(_dev.get(), sampling);
if (status != 0) {
BLADERF_WARNING("Problem while setting sampling mode: " <<
bladerf_strerror(status));
}
}
}
/* Loopback */
set_loopback_mode(dict.count("loopback") ? dict["loopback"] : "none");
/* RX Mux */
set_rx_mux_mode(dict.count("rxmux") ? dict["rxmux"] : "baseband");
/* AGC mode */
if (dict.count("agc_mode")) {
set_agc_mode(dict["agc_mode"]);
}
/* Specify initial gain mode */
if (dict.count("agc")) {
for (size_t i = 0; i < get_max_channels(); ++i) {
set_gain_mode(boost::lexical_cast<bool>(dict["agc"]), BLADERF_CHANNEL_RX(i));
BLADERF_INFO(boost::str(boost::format("%s gain mode set to '%s'")
% channel2str(BLADERF_CHANNEL_RX(i))
% get_gain_mode(BLADERF_CHANNEL_RX(i))));
}
}
/* Warn user about using an old FPGA version, as we no longer strip off the
* markers that were pressent in the pre-v0.0.1 FPGA */
if (bladerf_fpga_version(_dev.get(), &fpga_version) != 0) {
std::cerr << _pfx << "Failed to get FPGA version" << std::endl;
} else if (fpga_version.major <= 0 &&
fpga_version.minor <= 0 && fpga_version.patch < 1) {
{
struct bladerf_version fpga_version;
std::cerr << _pfx
<< "Warning: FPGA version v0.0.1 or later is required. Using an "
<< "earlier FPGA version will result in misinterpeted samples."
<< std::endl;
if (bladerf_fpga_version(_dev.get(), &fpga_version) != 0) {
BLADERF_WARNING("Failed to get FPGA version");
} else if (fpga_version.major <= 0 &&
fpga_version.minor <= 0 &&
fpga_version.patch < 1) {
BLADERF_WARNING("Warning: FPGA version v0.0.1 or later is required. "
"Using an earlier FPGA version will result in "
"misinterpeted samples.");
}
}
/* Initialize channel <-> antenna map */
BOOST_FOREACH(std::string ant, get_antennas()) {
_chanmap[str2channel(ant)] = -1;
}
/* Bounds-checking output signature depending on our underlying hardware */
size_t max_nchan = 1;
if (get_num_channels() > get_max_channels()) {
BLADERF_WARNING("Warning: number of channels specified on command line ("
<< get_num_channels() << ") is greater than the maximum "
"number supported by this device (" << get_max_channels()
<< "). Resetting to " << get_max_channels() << ".");
if (get_board_type(_dev.get()) == BLADERF_REV_2) {
max_nchan = 2;
set_output_signature(gr::io_signature::make(get_max_channels(),
get_max_channels(),
sizeof(gr_complex)));
}
if (get_num_channels() > max_nchan) {
std::cerr << _pfx
<< "Warning: number of channels specified on command line ("
<< get_num_channels() << ") is greater than the maximum number "
<< "supported by this device (" << max_nchan << "). Resetting "
<< "to " << max_nchan << "."
<< std::endl;
/* Set up constraints */
int const alignment_multiple = volk_get_alignment() / sizeof(gr_complex);
set_alignment(std::max(1,alignment_multiple));
set_max_noutput_items(_samples_per_buffer);
set_output_multiple(get_num_channels());
set_output_signature( gr::io_signature::make(max_nchan, max_nchan, sizeof(gr_complex) ) );
/* Set channel layout */
_layout = (get_num_channels() > 1) ? BLADERF_RX_X2 : BLADERF_RX_X1;
/* Initial wiring of antennas to channels */
for (size_t ch = 0; ch < get_num_channels(); ++ch) {
set_channel_enable(BLADERF_CHANNEL_RX(ch), true);
_chanmap[BLADERF_CHANNEL_RX(ch)] = ch;
}
_use_mimo = get_num_channels() > 1;
BLADERF_DEBUG("initialization complete");
}
bool bladerf_source_c::start()
bool bladerf_source_c::is_antenna_valid(const std::string &antenna)
{
return bladerf_common::start(BLADERF_RX);
}
bool bladerf_source_c::stop()
{
return bladerf_common::stop(BLADERF_RX);
}
int bladerf_source_c::work(int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
const float scaling = 2048.0f;
int status;
gr_complex *out = static_cast<gr_complex *>(output_items[0]);
struct bladerf_metadata meta;
struct bladerf_metadata *meta_ptr = NULL;
if (noutput_items > _conv_buf_size) {
void *tmp;
_conv_buf_size = noutput_items;
tmp = realloc(_conv_buf, _conv_buf_size * 2 * sizeof(int16_t));
if (tmp == NULL) {
throw std::runtime_error(_pfx + "Failed to realloc _conv_buf");
BOOST_FOREACH(std::string ant, get_antennas()) {
if (antenna == ant) {
return true;
}
_conv_buf = static_cast<int16_t *>(tmp);
}
if (_use_metadata) {
memset(&meta, 0, sizeof(meta));
meta.flags = BLADERF_META_FLAG_RX_NOW;
meta_ptr = &meta;
}
/* Grab all the samples into the temporary buffer */
status = bladerf_sync_rx(_dev.get(), static_cast<void *>(_conv_buf),
noutput_items, meta_ptr, _stream_timeout_ms);
if (status != 0) {
std::cerr << _pfx
<< "bladerf_sync_rx error: " << bladerf_strerror(status)
<< std::endl;
_consecutive_failures++;
if (_consecutive_failures >= MAX_CONSECUTIVE_FAILURES) {
std::cerr << _pfx
<< "Consecutive error limit hit. Shutting down."
<< std::endl;
return WORK_DONE;
}
} else {
_consecutive_failures = 0;
}
/* Convert them from fixed to floating point */
volk_16i_s32f_convert_32f((float *) out, _conv_buf, scaling,
2 * noutput_items);
return noutput_items;
return false;
}
std::vector < std::string > bladerf_source_c::get_devices()
/******************************************************************************
* Public methods
******************************************************************************/
std::string bladerf_source_c::name()
{
return "bladeRF receiver";
}
std::vector<std::string> bladerf_source_c::get_devices()
{
return bladerf_common::devices();
}
size_t bladerf_source_c::get_max_channels()
{
return bladerf_common::get_max_channels(BLADERF_RX);
}
size_t bladerf_source_c::get_num_channels()
{
return output_signature()->max_streams();
}
bool bladerf_source_c::start()
{
int status;
BLADERF_DEBUG("starting source");
gr::thread::scoped_lock guard(d_mutex);
status = bladerf_sync_config(_dev.get(), _layout, _format, _num_buffers,
_samples_per_buffer, _num_transfers,
_stream_timeout);
if (status != 0) {
BLADERF_THROW_STATUS(status, "bladerf_sync_config failed");
}
for (size_t ch = 0; ch < get_max_channels(); ++ch) {
bladerf_channel brfch = BLADERF_CHANNEL_RX(ch);
if (get_channel_enable(brfch)) {
status = bladerf_enable_module(_dev.get(), brfch, true);
if (status != 0) {
BLADERF_THROW_STATUS(status, "bladerf_enable_module failed");
}
}
}
/* Allocate memory for conversions in work() */
size_t alignment = volk_get_alignment();
_16icbuf = reinterpret_cast<int16_t *>(volk_malloc(2*_samples_per_buffer*sizeof(int16_t), alignment));
_32fcbuf = reinterpret_cast<gr_complex *>(volk_malloc(_samples_per_buffer*sizeof(gr_complex), alignment));
_running = true;
return true;
}
bool bladerf_source_c::stop()
{
int status;
BLADERF_DEBUG("stopping source");
gr::thread::scoped_lock guard(d_mutex);
if (!_running) {
BLADERF_WARNING("source already stopped, nothing to do here");
return true;
}
_running = false;
for (size_t ch = 0; ch < get_max_channels(); ++ch) {
bladerf_channel brfch = BLADERF_CHANNEL_RX(ch);
if (get_channel_enable(brfch)) {
status = bladerf_enable_module(_dev.get(), brfch, false);
if (status != 0) {
BLADERF_THROW_STATUS(status, "bladerf_enable_module failed");
}
}
}
/* Deallocate conversion memory */
volk_free(_16icbuf);
volk_free(_32fcbuf);
_16icbuf = NULL;
_32fcbuf = NULL;
return true;
}
int bladerf_source_c::work(int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
int status;
struct bladerf_metadata meta;
struct bladerf_metadata *meta_ptr = NULL;
size_t nstreams = num_streams(_layout);
gr::thread::scoped_lock guard(d_mutex);
// if we aren't running, nothing to do here
if (!_running) {
return 0;
}
// set up metadata
if (BLADERF_FORMAT_SC16_Q11_META == _format) {
memset(&meta, 0, sizeof(meta));
meta.flags = BLADERF_META_FLAG_RX_NOW;
meta_ptr = &meta;
}
// grab samples into temp buffer
status = bladerf_sync_rx(_dev.get(), static_cast<void *>(_16icbuf),
noutput_items, meta_ptr, _stream_timeout);
if (status != 0) {
BLADERF_WARNING(boost::str(boost::format("bladerf_sync_rx error: %s")
% bladerf_strerror(status)));
++_failures;
if (_failures >= MAX_CONSECUTIVE_FAILURES) {
BLADERF_WARNING("Consecutive error limit hit. Shutting down.");
return WORK_DONE;
}
} else {
_failures = 0;
}
// convert from int16_t to float
// output_items is gr_complex (2x float), so num_points is 2*noutput_items
volk_16i_s32f_convert_32f(reinterpret_cast<float *>(_32fcbuf), _16icbuf,
SCALING_FACTOR, 2*noutput_items);
// copy the samples into output_items
gr_complex **out = reinterpret_cast<gr_complex **>(&output_items[0]);
if (nstreams > 1) {
// we need to deinterleave the multiplex as we copy
gr_complex const *deint_in = _32fcbuf;
for (size_t i = 0; i < (noutput_items/nstreams); ++i) {
for (size_t n = 0; n < nstreams; ++n) {
memcpy(out[n]++, deint_in++, sizeof(gr_complex));
}
}
} else {
// no deinterleaving to do: simply copy everything
memcpy(out[0], _32fcbuf, sizeof(gr_complex) * noutput_items);
}
return noutput_items;
}
osmosdr::meta_range_t bladerf_source_c::get_sample_rates()
{
return sample_rates();
return sample_rates(chan2channel(BLADERF_RX, 0));
}
double bladerf_source_c::set_sample_rate(double rate)
{
return bladerf_common::set_sample_rate(BLADERF_RX, rate);
return bladerf_common::set_sample_rate(rate, chan2channel(BLADERF_RX, 0));
}
double bladerf_source_c::get_sample_rate()
{
return bladerf_common::get_sample_rate(BLADERF_RX);
return bladerf_common::get_sample_rate(chan2channel(BLADERF_RX, 0));
}
osmosdr::freq_range_t bladerf_source_c::get_freq_range(size_t chan)
{
return bladerf_common::get_freq_range(BLADERF_CHANNEL_RX(chan));
return bladerf_common::freq_range(chan2channel(BLADERF_RX, chan));
}
double bladerf_source_c::set_center_freq(double freq, size_t chan)
{
return bladerf_common::set_center_freq(freq, BLADERF_CHANNEL_RX(chan));
return bladerf_common::set_center_freq(freq, chan2channel(BLADERF_RX, chan));
}
double bladerf_source_c::get_center_freq(size_t chan)
{
return bladerf_common::get_center_freq(BLADERF_CHANNEL_RX(chan));
return bladerf_common::get_center_freq(chan2channel(BLADERF_RX, chan));
}
double bladerf_source_c::set_freq_corr(double ppm, size_t chan)
{
/* TODO: Write the VCTCXO with a correction value (also changes TX ppm value!) */
return get_freq_corr(BLADERF_CHANNEL_RX(chan));
BLADERF_WARNING("Frequency correction is not implemented.");
return get_freq_corr(chan2channel(BLADERF_RX, chan));
}
double bladerf_source_c::get_freq_corr(size_t chan)
@ -250,77 +385,81 @@ double bladerf_source_c::get_freq_corr(size_t chan)
return 0;
}
std::vector < std::string > bladerf_source_c::get_gain_names(size_t chan)
std::vector<std::string> bladerf_source_c::get_gain_names(size_t chan)
{
return bladerf_common::get_gain_names(BLADERF_CHANNEL_RX(chan));
return bladerf_common::get_gain_names(chan2channel(BLADERF_RX, chan));
}
osmosdr::gain_range_t bladerf_source_c::get_gain_range(size_t chan)
{
return bladerf_common::get_gain_range(BLADERF_CHANNEL_RX(chan));
return bladerf_common::get_gain_range(chan2channel(BLADERF_RX, chan));
}
osmosdr::gain_range_t bladerf_source_c::get_gain_range(const std::string &name,
size_t chan)
{
return bladerf_common::get_gain_range(name, BLADERF_CHANNEL_RX(chan));
return bladerf_common::get_gain_range(name, chan2channel(BLADERF_RX, chan));
}
bool bladerf_source_c::set_gain_mode(bool automatic, size_t chan)
{
return bladerf_common::set_gain_mode(automatic, BLADERF_CHANNEL_RX(chan));
return bladerf_common::set_gain_mode(automatic,
chan2channel(BLADERF_RX, chan),
_agcmode);
}
bool bladerf_source_c::get_gain_mode(size_t chan)
{
return bladerf_common::get_gain_mode(BLADERF_CHANNEL_RX(chan));
return bladerf_common::get_gain_mode(chan2channel(BLADERF_RX, chan));
}
double bladerf_source_c::set_gain(double gain, size_t chan)
{
return bladerf_common::set_gain(gain, BLADERF_CHANNEL_RX(chan));
return bladerf_common::set_gain(gain, chan2channel(BLADERF_RX, chan));
}
double bladerf_source_c::set_gain(double gain, const std::string &name,
size_t chan)
{
return bladerf_common::set_gain(gain, name, BLADERF_CHANNEL_RX(chan));
return bladerf_common::set_gain(gain, name, chan2channel(BLADERF_RX, chan));
}
double bladerf_source_c::get_gain(size_t chan)
{
return bladerf_common::get_gain(BLADERF_CHANNEL_RX(chan));
return bladerf_common::get_gain(chan2channel(BLADERF_RX, chan));
}
double bladerf_source_c::get_gain(const std::string &name, size_t chan)
{
return bladerf_common::get_gain(name, BLADERF_CHANNEL_RX(chan));
return bladerf_common::get_gain(name, chan2channel(BLADERF_RX, chan));
}
std::vector < std::string > bladerf_source_c::get_antennas(size_t chan)
std::vector<std::string> bladerf_source_c::get_antennas(size_t chan)
{
std::vector < std::string > antennas;
antennas += "RX0";
if (BLADERF_REV_2 == get_board_type(_dev.get())) {
antennas += "RX1";
}
return antennas;
return bladerf_common::get_antennas(BLADERF_RX);
}
std::string bladerf_source_c::set_antenna(const std::string &antenna,
size_t chan)
{
return get_antenna(BLADERF_CHANNEL_RX(chan));
bool _was_running = _running;
if (_was_running) {
stop();
}
bladerf_common::set_antenna(BLADERF_RX, chan, antenna);
if (_was_running) {
start();
}
return get_antenna(chan);
}
std::string bladerf_source_c::get_antenna(size_t chan)
{
/* We only have a single receive antenna here */
// TODO: this is a lie
return "RX0";
return channel2str(chan2channel(BLADERF_RX, chan));
}
void bladerf_source_c::set_dc_offset_mode(int mode, size_t chan)
@ -328,28 +467,25 @@ void bladerf_source_c::set_dc_offset_mode(int mode, size_t chan)
if (osmosdr::source::DCOffsetOff == mode) {
//_src->set_auto_dc_offset( false, chan );
/* reset to default for off-state */
set_dc_offset(std::complex < double >(0.0, 0.0), chan);
set_dc_offset(std::complex<double>(0.0, 0.0), chan);
} else if (osmosdr::source::DCOffsetManual == mode) {
/* disable auto mode, but keep correcting with last known values */
//_src->set_auto_dc_offset( false, chan );
} else if (osmosdr::source::DCOffsetAutomatic == mode) {
//_src->set_auto_dc_offset( true, chan );
std::cerr << _pfx
<< "Automatic DC correction mode is not implemented."
<< std::endl;
BLADERF_WARNING("Automatic DC correction mode is not implemented.");
}
}
void bladerf_source_c::set_dc_offset(const std::complex < double > &offset,
void bladerf_source_c::set_dc_offset(const std::complex<double> &offset,
size_t chan)
{
int status;
status = bladerf_common::set_dc_offset(BLADERF_RX, offset, chan);
status = bladerf_common::set_dc_offset(offset, chan2channel(BLADERF_RX, chan));
if (status != 0) {
throw std::runtime_error(_pfx + "could not set dc offset: " +
bladerf_strerror(status));
BLADERF_THROW_STATUS(status, "could not set dc offset");
}
}
@ -358,83 +494,161 @@ void bladerf_source_c::set_iq_balance_mode(int mode, size_t chan)
if (osmosdr::source::IQBalanceOff == mode) {
//_src->set_auto_iq_balance( false, chan );
/* reset to default for off-state */
set_iq_balance(std::complex < double >(0.0, 0.0), chan);
set_iq_balance(std::complex<double>(0.0, 0.0), chan);
} else if (osmosdr::source::IQBalanceManual == mode) {
/* disable auto mode, but keep correcting with last known values */
//_src->set_auto_iq_balance( false, chan );
} else if (osmosdr::source::IQBalanceAutomatic == mode) {
//_src->set_auto_iq_balance( true, chan );
std::cerr << _pfx
<< "Automatic IQ correction mode is not implemented."
<< std::endl;
BLADERF_WARNING("Automatic IQ correction mode is not implemented.");
}
}
void bladerf_source_c::set_iq_balance(const std::complex < double > &balance,
void bladerf_source_c::set_iq_balance(const std::complex<double> &balance,
size_t chan)
{
int status;
status = bladerf_common::set_iq_balance(BLADERF_RX, balance, chan);
status = bladerf_common::set_iq_balance(balance, chan2channel(BLADERF_RX, chan));
if (status != 0) {
throw std::runtime_error(_pfx + "could not set iq balance: " +
bladerf_strerror(status));
BLADERF_THROW_STATUS(status, "could not set iq balance");
}
}
double bladerf_source_c::set_bandwidth(double bandwidth, size_t chan)
{
int status;
uint32_t actual;
if (bandwidth == 0.0) {
/* bandwidth of 0 means automatic filter selection */
/* select narrower filters to prevent aliasing */
bandwidth = get_sample_rate() * 0.75;
}
status = bladerf_set_bandwidth(_dev.get(), BLADERF_RX, (uint32_t) bandwidth,
&actual);
if (status != 0) {
throw std::runtime_error(_pfx + "could not set bandwidth: " +
bladerf_strerror(status));
}
return get_bandwidth();
}
double bladerf_source_c::get_bandwidth(size_t chan)
{
int status;
uint32_t bandwidth;
status = bladerf_get_bandwidth(_dev.get(), BLADERF_RX, &bandwidth);
if (status != 0) {
throw std::runtime_error(_pfx + "could not get bandwidth: " +
bladerf_strerror(status));
}
return (double) bandwidth;
}
osmosdr::freq_range_t bladerf_source_c::get_bandwidth_range(size_t chan)
{
return filter_bandwidths();
return filter_bandwidths(chan2channel(BLADERF_RX, chan));
}
double bladerf_source_c::set_bandwidth(double bandwidth, size_t chan)
{
return bladerf_common::set_bandwidth(bandwidth,
chan2channel(BLADERF_RX, chan));
}
double bladerf_source_c::get_bandwidth(size_t chan)
{
return bladerf_common::get_bandwidth(chan2channel(BLADERF_RX, chan));
}
std::vector<std::string> bladerf_source_c::get_clock_sources(size_t mboard)
{
return bladerf_common::get_clock_sources(mboard);
}
void bladerf_source_c::set_clock_source(const std::string &source,
const size_t mboard)
size_t mboard)
{
bladerf_common::set_clock_source(source, mboard);
}
std::string bladerf_source_c::get_clock_source(const size_t mboard)
std::string bladerf_source_c::get_clock_source(size_t mboard)
{
return bladerf_common::get_clock_source(mboard);
}
std::vector < std::string > bladerf_source_c::get_clock_sources(const size_t mboard)
void bladerf_source_c::set_loopback_mode(const std::string &loopback)
{
return bladerf_common::get_clock_sources(mboard);
int status;
bladerf_loopback mode;
if (loopback == "bb_txlpf_rxvga2") {
mode = BLADERF_LB_BB_TXLPF_RXVGA2;
} else if (loopback == "bb_txlpf_rxlpf") {
mode = BLADERF_LB_BB_TXLPF_RXLPF;
} else if (loopback == "bb_txvga1_rxvga2") {
mode = BLADERF_LB_BB_TXVGA1_RXVGA2;
} else if (loopback == "bb_txvga1_rxlpf") {
mode = BLADERF_LB_BB_TXVGA1_RXLPF;
} else if (loopback == "rf_lna1") {
mode = BLADERF_LB_RF_LNA1;
} else if (loopback == "rf_lna2") {
mode = BLADERF_LB_RF_LNA2;
} else if (loopback == "rf_lna3") {
mode = BLADERF_LB_RF_LNA3;
} else if (loopback == "firmware") {
mode = BLADERF_LB_FIRMWARE;
} else if (loopback == "ad9361_bist") {
mode = BLADERF_LB_AD9361_BIST;
} else if (loopback == "none") {
mode = BLADERF_LB_NONE;
} else {
BLADERF_THROW("Unknown loopback mode: " + loopback);
}
status = bladerf_set_loopback(_dev.get(), mode);
if (BLADERF_ERR_UNSUPPORTED == status) {
// unsupported, but not worth crashing out
BLADERF_WARNING("Loopback mode not supported by device: " + loopback);
} else if (status != 0) {
BLADERF_THROW_STATUS(status, "Failed to set loopback mode");
}
}
void bladerf_source_c::set_rx_mux_mode(const std::string &rxmux)
{
int status;
bladerf_rx_mux mode;
if (rxmux == "baseband") {
mode = BLADERF_RX_MUX_BASEBAND;
} else if (rxmux == "12bit") {
mode = BLADERF_RX_MUX_12BIT_COUNTER;
} else if (rxmux == "32bit") {
mode = BLADERF_RX_MUX_32BIT_COUNTER;
} else if (rxmux == "digital") {
mode = BLADERF_RX_MUX_DIGITAL_LOOPBACK;
} else {
BLADERF_THROW("Unknown RX mux mode: " + rxmux);
}
status = bladerf_set_rx_mux(_dev.get(), mode);
if (BLADERF_ERR_UNSUPPORTED == status) {
// unsupported, but not worth crashing out
BLADERF_WARNING("RX mux mode not supported by device: " + rxmux);
} else if (status != 0) {
BLADERF_THROW_STATUS(status, "Failed to set RX mux mode");
}
}
void bladerf_source_c::set_agc_mode(const std::string &agcmode)
{
int status;
bladerf_gain_mode mode;
bool ok = false;
struct bladerf_gain_modes const *modes = NULL;
/* Get the list of AGC modes */
status = bladerf_get_gain_modes(_dev.get(), BLADERF_CHANNEL_RX(0), &modes);
if (status < 0) {
BLADERF_THROW_STATUS(status, "failed to get gain modes");
}
size_t count = status;
/* Compare... */
for (size_t i = 0; i < count; ++i) {
if (agcmode == std::string(modes[i].name)) {
mode = modes[i].mode;
ok = true;
BLADERF_DEBUG("Setting gain mode to " << mode << " (" << agcmode << ")");
break;
}
}
if (!ok) {
BLADERF_WARNING("Unknown gain mode \"" << agcmode << "\"");
return;
}
_agcmode = mode;
for (size_t i = 0; i < get_num_channels(); ++i) {
if (bladerf_common::get_gain_mode(BLADERF_CHANNEL_RX(i))) {
/* Refresh this */
bladerf_common::set_gain_mode(true, BLADERF_CHANNEL_RX(i), _agcmode);
}
}
}

View File

@ -21,14 +21,12 @@
#ifndef INCLUDED_BLADERF_SOURCE_C_H
#define INCLUDED_BLADERF_SOURCE_C_H
#include <gnuradio/thread/thread.h>
#include <gnuradio/block.h>
#include <gnuradio/sync_block.h>
#include "osmosdr/ranges.h"
#include "source_iface.h"
#include "bladerf_common.h"
#include "osmosdr/ranges.h"
class bladerf_source_c;
/*
@ -42,7 +40,7 @@ class bladerf_source_c;
*
* As a convention, the _sptr suffix indicates a boost::shared_ptr
*/
typedef boost::shared_ptr < bladerf_source_c > bladerf_source_c_sptr;
typedef boost::shared_ptr<bladerf_source_c> bladerf_source_c_sptr;
/*!
* \brief Return a shared_ptr to a new instance of bladerf_source_c.
@ -53,17 +51,28 @@ typedef boost::shared_ptr < bladerf_source_c > bladerf_source_c_sptr;
*/
bladerf_source_c_sptr make_bladerf_source_c(const std::string &args = "");
class bladerf_source_c:public gr::sync_block,
public source_iface, protected bladerf_common
class bladerf_source_c :
public gr::sync_block,
public source_iface,
protected bladerf_common
{
private:
// The friend declaration allows bladerf_make_source_c to
// access the private constructor.
friend bladerf_source_c_sptr make_bladerf_source_c(const std::string &args);
bladerf_source_c(const std::string &args); // private constructor
bladerf_source_c(const std::string &args);
bool is_antenna_valid(const std::string &antenna);
public:
std::string name();
static std::vector<std::string> get_devices();
size_t get_max_channels(void);
size_t get_num_channels(void);
bool start();
bool stop();
@ -71,10 +80,6 @@ public:
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items);
static std::vector < std::string > get_devices();
size_t get_num_channels(void);
osmosdr::meta_range_t get_sample_rates(void);
double set_sample_rate(double rate);
double get_sample_rate(void);
@ -86,10 +91,10 @@ public:
double set_freq_corr(double ppm, size_t chan = 0);
double get_freq_corr(size_t chan = 0);
std::vector < std::string > get_gain_names(size_t chan = 0);
std::vector<std::string> get_gain_names(size_t chan = 0);
osmosdr::gain_range_t get_gain_range(size_t chan = 0);
osmosdr::gain_range_t get_gain_range(const std::string &name, size_t chan =
0);
osmosdr::gain_range_t get_gain_range(const std::string &name,
size_t chan = 0);
bool set_gain_mode(bool automatic, size_t chan = 0);
bool get_gain_mode(size_t chan = 0);
double set_gain(double gain, size_t chan = 0);
@ -97,25 +102,41 @@ public:
double get_gain(size_t chan = 0);
double get_gain(const std::string &name, size_t chan = 0);
std::vector < std::string > get_antennas(size_t chan = 0);
std::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);
void set_dc_offset_mode(int mode, size_t chan = 0);
void set_dc_offset(const std::complex < double > &offset, size_t chan = 0);
void set_dc_offset(const std::complex<double> &offset, size_t chan = 0);
void set_iq_balance_mode(int mode, size_t chan = 0);
void set_iq_balance(const std::complex < double > &balance, size_t chan = 0);
void set_iq_balance(const std::complex<double> &balance, size_t chan = 0);
osmosdr::freq_range_t get_bandwidth_range(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);
void set_clock_source(const std::string &source, const size_t mboard = 0);
std::string get_clock_source(const size_t mboard);
std::vector < std::string > get_clock_sources(const size_t mboard);
std::vector<std::string> get_clock_sources(size_t mboard);
void set_clock_source(const std::string &source, size_t mboard = 0);
std::string get_clock_source(size_t mboard);
void set_loopback_mode(const std::string &loopback);
void set_rx_mux_mode(const std::string &rxmux);
void set_agc_mode(const std::string &agcmode);
private:
// Sample-handling buffers
int16_t *_16icbuf; /**< raw samples from bladeRF */
gr_complex *_32fcbuf; /**< intermediate buffer to gnuradio */
bool _running; /**< is the source running? */
bladerf_channel_layout _layout; /**< channel layout */
bladerf_gain_mode _agcmode; /**< gain mode when AGC is enabled */
gr::thread::mutex d_mutex; /**< mutex to protect set/work access */
/* Scaling factor used when converting from int16_t to float */
const float SCALING_FACTOR = 2048.0f;
};
#endif /* INCLUDED_BLADERF_SOURCE_C_H */
#endif // INCLUDED_BLADERF_SOURCE_C_H