|
|
|
/* -*- c++ -*- */
|
|
|
|
/*
|
|
|
|
* Copyright 2013-2017 Nuand LLC
|
|
|
|
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
|
|
|
|
*
|
|
|
|
* GNU Radio is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 3, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* GNU Radio is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with GNU Radio; see the file COPYING. If not, write to
|
|
|
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* config.h is generated by configure. It contains the results
|
|
|
|
* of probing for features, options etc. It should be the first
|
|
|
|
* file included in your .cc file.
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <iomanip>
|
|
|
|
#include <iostream>
|
|
|
|
#include <map>
|
|
|
|
#include <sstream>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include <boost/assign.hpp>
|
|
|
|
#include <boost/format.hpp>
|
|
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
|
|
|
|
#include "bladerf_common.h"
|
|
|
|
|
|
|
|
/* Defaults for these values. */
|
|
|
|
static size_t const NUM_BUFFERS = 512;
|
|
|
|
static size_t const NUM_SAMPLES_PER_BUFFER = (4 * 1024);
|
|
|
|
static size_t const NUM_TRANSFERS = 32;
|
|
|
|
static size_t const STREAM_TIMEOUT_MS = 3000;
|
|
|
|
|
|
|
|
using namespace boost::assign;
|
|
|
|
|
|
|
|
std::mutex bladerf_common::_devs_mutex;
|
|
|
|
std::list<std::weak_ptr<struct bladerf> > bladerf_common::_devs;
|
|
|
|
|
|
|
|
/* name for system-wide gain (which is not its own libbladeRF gain stage) */
|
|
|
|
static const char *SYSTEM_GAIN_NAME = "system";
|
|
|
|
|
|
|
|
/* Determines if bladerf_version is greater or equal to major.minor.patch */
|
|
|
|
static bool _version_greater_or_equal(const struct bladerf_version *version,
|
|
|
|
unsigned int major,
|
|
|
|
unsigned int minor, unsigned int patch)
|
|
|
|
{
|
|
|
|
if (version->major > major) {
|
|
|
|
// 2.0.0 > 1.9.9
|
|
|
|
return true;
|
|
|
|
} else if ((version->major == major) && (version->minor > minor)) {
|
|
|
|
// 1.9.9 > 1.8.9
|
|
|
|
return true;
|
|
|
|
} else if ((version->major == major) &&
|
|
|
|
(version->minor == minor) &&
|
|
|
|
(version->patch >= patch)) {
|
|
|
|
// 1.8.9 > 1.8.8
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns TRUE if an expansion board is attached, FALSE otherwise */
|
|
|
|
static bool _is_xb_attached(bladerf_sptr _dev)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
bladerf_xb xb = BLADERF_XB_NONE;
|
|
|
|
|
|
|
|
status = bladerf_expansion_get_attached(_dev.get(), &xb);
|
|
|
|
if (status != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (xb != BLADERF_XB_NONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Gets a value from a const dict */
|
|
|
|
static std::string const _get(dict_t const &dict, std::string key)
|
bladerf: Added 'verbosity' and 'loopback' device parameters
The 'verbosity' parameter may be used to increase or suppress output from
libbladeRF. The available log levels are, in order of decreasing
verbosity are:
verbose, debug, info, warning, critical, silent
The 'loopback' parameter may be used to put the bladeRF into one of the
supported loopback modes. The valid modes are listed below. Their
descriptions may be found in the libbladeRF documentation:
bb_txlpf_rxvga2, bb_txlpf_rxlpf bb_txvga1_rxvga2, bb_txvga1_rxlpf
rf_lna1, rf_lna2, rf_lna3
9 years ago
|
|
|
{
|
|
|
|
std::string rv("");
|
bladerf: Added 'verbosity' and 'loopback' device parameters
The 'verbosity' parameter may be used to increase or suppress output from
libbladeRF. The available log levels are, in order of decreasing
verbosity are:
verbose, debug, info, warning, critical, silent
The 'loopback' parameter may be used to put the bladeRF into one of the
supported loopback modes. The valid modes are listed below. Their
descriptions may be found in the libbladeRF documentation:
bb_txlpf_rxvga2, bb_txlpf_rxlpf bb_txvga1_rxvga2, bb_txvga1_rxlpf
rf_lna1, rf_lna2, rf_lna3
9 years ago
|
|
|
|
|
|
|
dict_t::const_iterator it = dict.find(key);
|
|
|
|
|
|
|
|
if (it != dict.end()) {
|
|
|
|
rv = it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool _is_tx(bladerf_channel ch)
|
|
|
|
{
|
|
|
|
return (1 == (ch & BLADERF_DIRECTION_MASK));
|
bladerf: Added 'verbosity' and 'loopback' device parameters
The 'verbosity' parameter may be used to increase or suppress output from
libbladeRF. The available log levels are, in order of decreasing
verbosity are:
verbose, debug, info, warning, critical, silent
The 'loopback' parameter may be used to put the bladeRF into one of the
supported loopback modes. The valid modes are listed below. Their
descriptions may be found in the libbladeRF documentation:
bb_txlpf_rxvga2, bb_txlpf_rxlpf bb_txvga1_rxvga2, bb_txvga1_rxlpf
rf_lna1, rf_lna2, rf_lna3
9 years ago
|
|
|
}
|
|
|
|
|
|
|
|
size_t num_streams(bladerf_channel_layout layout)
|
|
|
|
{
|
|
|
|
#ifdef BLADERF_COMPATIBILITY
|
|
|
|
return 1;
|
|
|
|
#else
|
|
|
|
|
|
|
|
switch (layout) {
|
|
|
|
case BLADERF_RX_X1:
|
|
|
|
case BLADERF_TX_X1:
|
|
|
|
return 1;
|
|
|
|
case BLADERF_RX_X2:
|
|
|
|
case BLADERF_TX_X2:
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(false);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* Public methods
|
|
|
|
******************************************************************************/
|
|
|
|
bladerf_common::bladerf_common() :
|
|
|
|
_dev(std::shared_ptr<struct bladerf>()),
|
|
|
|
_pfx("[bladeRF common] "),
|
|
|
|
_failures(0),
|
|
|
|
_num_buffers(NUM_BUFFERS),
|
|
|
|
_samples_per_buffer(NUM_SAMPLES_PER_BUFFER),
|
|
|
|
_num_transfers(NUM_TRANSFERS),
|
|
|
|
_stream_timeout(STREAM_TIMEOUT_MS),
|
|
|
|
_format(BLADERF_FORMAT_SC16_Q11)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* Protected methods
|
|
|
|
******************************************************************************/
|
|
|
|
void bladerf_common::init(dict_t const &dict, bladerf_direction direction)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
std::string device_name("");
|
|
|
|
struct bladerf_version ver;
|
|
|
|
|
|
|
|
BLADERF_DEBUG("entering initialization");
|
|
|
|
|
|
|
|
_pfx = boost::str(boost::format("[bladeRF %s] ")
|
|
|
|
% (direction == BLADERF_TX ? "sink" : "source"));
|
|
|
|
|
|
|
|
/* libbladeRF verbosity */
|
|
|
|
if (dict.count("verbosity")) {
|
|
|
|
set_verbosity(_get(dict, "verbosity"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Board identifier */
|
|
|
|
if (dict.count("bladerf")) {
|
|
|
|
std::string const value = _get(dict, "bladerf");
|
|
|
|
if (value.length() > 0) {
|
|
|
|
if (value.length() <= 2) {
|
|
|
|
/* If the value is two digits or less, we'll assume the user is
|
|
|
|
* providing an instance number */
|
|
|
|
unsigned int device_number = 0;
|
|
|
|
|
|
|
|
try {
|
|
|
|
device_number = boost::lexical_cast<unsigned int>(value);
|
|
|
|
device_name = boost::str(boost::format("*:instance=%d")
|
|
|
|
% device_number);
|
|
|
|
} catch (std::exception &ex) {
|
|
|
|
BLADERF_THROW(boost::str(boost::format("Failed to use '%s' as "
|
|
|
|
"device number: %s") % value % ex.what()));
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* Otherwise, we'll assume it's a serial number. libbladeRF v1.4.1
|
|
|
|
* supports matching a subset of a serial number. For earlier versions,
|
|
|
|
* we require the entire serial number.
|
|
|
|
*
|
|
|
|
* libbladeRF is responsible for rejecting bad serial numbers, so we
|
|
|
|
* may just pass whatever the user has provided.
|
|
|
|
*/
|
|
|
|
bladerf_version(&ver);
|
|
|
|
if (_version_greater_or_equal(&ver, 1, 4, 1) ||
|
|
|
|
value.length() == (BLADERF_SERIAL_LENGTH - 1)) {
|
|
|
|
device_name = std::string("*:serial=") + value;
|
|
|
|
} else {
|
|
|
|
BLADERF_THROW(boost::str(boost::format("A full serial number must "
|
|
|
|
"be supplied with libbladeRF %s. libbladeRF >= v1.4.1 "
|
|
|
|
"supports opening a device via a subset of its serial "
|
|
|
|
"#.") % ver.describe));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open the board! */
|
|
|
|
try {
|
|
|
|
BLADERF_INFO(boost::str(boost::format("Opening Nuand bladeRF with "
|
|
|
|
"device identifier string '%s'") % device_name));
|
|
|
|
|
|
|
|
_dev = open(device_name);
|
|
|
|
} catch (std::exception &ex) {
|
|
|
|
BLADERF_THROW(boost::str(boost::format("Failed to open bladeRF device "
|
|
|
|
"'%s': %s") % device_name % ex.what()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL == _dev) {
|
|
|
|
BLADERF_THROW(boost::str(boost::format("Failed to get device handle for "
|
|
|
|
"'%s': _dev is NULL") % device_name));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Load a FPGA */
|
|
|
|
if (dict.count("fpga")) {
|
|
|
|
if (dict.count("fpga-reload") == 0 &&
|
|
|
|
bladerf_is_fpga_configured(_dev.get()) == 1) {
|
|
|
|
|
|
|
|
BLADERF_WARNING("FPGA is already loaded. Set fpga-reload=1 to force a "
|
|
|
|
"reload.");
|
|
|
|
} else {
|
|
|
|
std::string fpga = _get(dict, "fpga");
|
|
|
|
|
|
|
|
BLADERF_INFO("Loading FPGA bitstream from " << fpga);
|
|
|
|
|
|
|
|
status = bladerf_load_fpga(_dev.get(), fpga.c_str());
|
|
|
|
if (status != 0) {
|
|
|
|
BLADERF_WARNING("Could not load FPGA bitstream: "
|
|
|
|
<< bladerf_strerror(status));
|
|
|
|
} else {
|
|
|
|
BLADERF_INFO("The FPGA bitstream was loaded successfully");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bladerf_is_fpga_configured(_dev.get()) != 1) {
|
|
|
|
BLADERF_THROW("The FPGA is not configured! Provide device argument "
|
|
|
|
"fpga=/path/to/the/bitstream.rbf to load it.");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XB-200 Transverter Board */
|
|
|
|
if (dict.count("xb200")) {
|
|
|
|
status = bladerf_expansion_attach(_dev.get(), BLADERF_XB_200);
|
|
|
|
if (status != 0) {
|
|
|
|
BLADERF_WARNING("Could not attach XB-200: " << bladerf_strerror(status));
|
|
|
|
} else {
|
|
|
|
bladerf_xb200_filter filter = BLADERF_XB200_AUTO_1DB;
|
|
|
|
|
|
|
|
if (_get(dict, "xb200") == "custom") {
|
|
|
|
filter = BLADERF_XB200_CUSTOM;
|
|
|
|
} else if (_get(dict, "xb200") == "50M") {
|
|
|
|
filter = BLADERF_XB200_50M;
|
|
|
|
} else if (_get(dict, "xb200") == "144M") {
|
|
|
|
filter = BLADERF_XB200_144M;
|
|
|
|
} else if (_get(dict, "xb200") == "222M") {
|
|
|
|
filter = BLADERF_XB200_222M;
|
|
|
|
} else if (_get(dict, "xb200") == "auto3db") {
|
|
|
|
filter = BLADERF_XB200_AUTO_3DB;
|
|
|
|
} else if (_get(dict, "xb200") == "auto") {
|
|
|
|
filter = BLADERF_XB200_AUTO_1DB;
|
|
|
|
} else {
|
|
|
|
filter = BLADERF_XB200_AUTO_1DB;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = bladerf_xb200_set_filterbank(_dev.get(), direction, filter);
|
|
|
|
if (status != 0) {
|
|
|
|
BLADERF_WARNING("Could not set XB-200 filter: "
|
|
|
|
<< bladerf_strerror(status));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
bladerf: Added 'verbosity' and 'loopback' device parameters
The 'verbosity' parameter may be used to increase or suppress output from
libbladeRF. The available log levels are, in order of decreasing
verbosity are:
verbose, debug, info, warning, critical, silent
The 'loopback' parameter may be used to put the bladeRF into one of the
supported loopback modes. The valid modes are listed below. Their
descriptions may be found in the libbladeRF documentation:
bb_txlpf_rxvga2, bb_txlpf_rxlpf bb_txvga1_rxvga2, bb_txvga1_rxlpf
rf_lna1, rf_lna2, rf_lna3
9 years ago
|
|
|
|
|
|
|
/* Show some info about the device we've opened */
|
|
|
|
print_device_info();
|
|
|
|
|
|
|
|
if (dict.count("tamer")) {
|
|
|
|
set_clock_source(_get(dict, "tamer"));
|
|
|
|
BLADERF_INFO(boost::str(boost::format("Tamer mode set to '%s'")
|
|
|
|
% get_clock_source()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dict.count("smb")) {
|
|
|
|
set_smb_frequency(boost::lexical_cast<double>(_get(dict, "smb")));
|
|
|
|
BLADERF_INFO(boost::str(boost::format("SMB frequency set to %f Hz")
|
|
|
|
% get_smb_frequency()));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize buffer and sample configuration */
|
|
|
|
if (dict.count("buffers")) {
|
|
|
|
_num_buffers = boost::lexical_cast<size_t>(_get(dict, "buffers"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dict.count("buflen")) {
|
|
|
|
_samples_per_buffer = boost::lexical_cast<size_t>(_get(dict, "buflen"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dict.count("transfers")) {
|
|
|
|
_num_transfers = boost::lexical_cast<size_t>(_get(dict, "transfers"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dict.count("stream_timeout")) {
|
|
|
|
_stream_timeout = boost::lexical_cast<unsigned int>(_get(dict, "stream_timeout"));
|
|
|
|
} else if (dict.count("stream_timeout_ms")) {
|
|
|
|
// reverse compatibility
|
|
|
|
_stream_timeout = boost::lexical_cast<unsigned int>(_get(dict, "stream_timeout_ms"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dict.count("enable_metadata") > 0) {
|
|
|
|
_format = BLADERF_FORMAT_SC16_Q11_META;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Require value to be >= 2 so we can ensure we have twice as many
|
|
|
|
* buffers as transfers */
|
|
|
|
if (_num_buffers <= 1) {
|
|
|
|
_num_buffers = NUM_BUFFERS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (0 == _samples_per_buffer) {
|
|
|
|
_samples_per_buffer = NUM_SAMPLES_PER_BUFFER;
|
|
|
|
} else {
|
|
|
|
if ((_samples_per_buffer < 1024) || (_samples_per_buffer % 1024 != 0)) {
|
|
|
|
BLADERF_WARNING(boost::str(boost::format("Invalid \"buflen\" value "
|
|
|
|
"(%d). A multiple of 1024 is required. Defaulting "
|
|
|
|
"to %d")
|
|
|
|
% _samples_per_buffer % NUM_SAMPLES_PER_BUFFER));
|
|
|
|
_samples_per_buffer = NUM_SAMPLES_PER_BUFFER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the user hasn't specified the desired number of transfers, set it to
|
|
|
|
* at least num_buffers/2 */
|
|
|
|
if (0 == _num_transfers) {
|
|
|
|
_num_transfers = std::min(NUM_TRANSFERS, _num_buffers / 2);
|
|
|
|
} else if (_num_transfers >= _num_buffers) {
|
|
|
|
_num_transfers = std::min(NUM_TRANSFERS, _num_buffers / 2);
|
|
|
|
BLADERF_WARNING(boost::str(boost::format("Clamping \"transfers\" to %d. "
|
|
|
|
"Try using a smaller \"transfers\" value if timeouts "
|
|
|
|
"occur.") % _num_transfers));
|
|
|
|
}
|
|
|
|
|
|
|
|
BLADERF_INFO(boost::str(boost::format("Buffers: %d, samples per buffer: "
|
|
|
|
"%d, active transfers: %d")
|
|
|
|
% _num_buffers
|
|
|
|
% _samples_per_buffer
|
|
|
|
% _num_transfers));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> bladerf_common::devices()
|
|
|
|
{
|
|
|
|
struct bladerf_devinfo *devices;
|
|
|
|
ssize_t n_devices;
|
|
|
|
std::vector<std::string> ret;
|
|
|
|
|
|
|
|
n_devices = bladerf_get_device_list(&devices);
|
|
|
|
|
|
|
|
if (n_devices > 0) {
|
|
|
|
for (ssize_t i = 0; i < n_devices; i++) {
|
|
|
|
std::string serial(devices[i].serial);
|
|
|
|
std::string devstr;
|
|
|
|
|
|
|
|
if (serial.length() == 32) {
|
|
|
|
serial.replace(4, 24, "...");
|
|
|
|
}
|
|
|
|
|
|
|
|
devstr = boost::str(boost::format("bladerf=%s,label='Nuand bladeRF%s%s'")
|
|
|
|
% devices[i].instance
|
|
|
|
% (serial.length() > 0 ? " SN " : "")
|
|
|
|
% serial);
|
|
|
|
|
|
|
|
ret.push_back(devstr);
|
|
|
|
}
|
|
|
|
|
|
|
|
bladerf_free_device_list(devices);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bladerf_board_type bladerf_common::get_board_type()
|
|
|
|
{
|
|
|
|
if (NULL == _dev || NULL == _dev.get()) {
|
|
|
|
BLADERF_WARNING("no bladeRF device is open");
|
|
|
|
return BOARD_TYPE_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string boardname = std::string(bladerf_get_board_name(_dev.get()));
|
|
|
|
|
|
|
|
if (boardname == "bladerf1") {
|
|
|
|
return BOARD_TYPE_BLADERF_1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (boardname == "bladerf2") {
|
|
|
|
return BOARD_TYPE_BLADERF_2;
|
|
|
|
}
|
|
|
|
|
|
|
|
BLADERF_WARNING(boost::str(boost::format("model '%s' is not recognized")
|
|
|
|
% boardname));
|
|
|
|
|
|
|
|
return BOARD_TYPE_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t bladerf_common::get_max_channels(bladerf_direction direction)
|
|
|
|
{
|
|
|
|
#ifdef BLADERF_COMPATIBILITY
|
|
|
|
return 1;
|
|
|
|
#else
|
|
|
|
return bladerf_get_channel_count(_dev.get(), direction);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void bladerf_common::set_channel_enable(bladerf_channel ch, bool enable)
|
|
|
|
{
|
|
|
|
_enables[ch] = enable;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bladerf_common::get_channel_enable(bladerf_channel ch)
|
|
|
|
{
|
|
|
|
return _enables[ch];
|
|
|
|
}
|
|
|
|
|
|
|
|
void bladerf_common::set_verbosity(std::string const &verbosity)
|
|
|
|
{
|
|
|
|
bladerf_log_level l;
|
|
|
|
|
|
|
|
if (verbosity == "verbose") {
|
|
|
|
l = BLADERF_LOG_LEVEL_VERBOSE;
|
|
|
|
} else if (verbosity == "debug") {
|
|
|
|
l = BLADERF_LOG_LEVEL_DEBUG;
|
|
|
|
} else if (verbosity == "info") {
|
|
|
|
l = BLADERF_LOG_LEVEL_INFO;
|
|
|
|
} else if (verbosity == "warning") {
|
|
|
|
l = BLADERF_LOG_LEVEL_WARNING;
|
|
|
|
} else if (verbosity == "error") {
|
|
|
|
l = BLADERF_LOG_LEVEL_ERROR;
|
|
|
|
} else if (verbosity == "critical") {
|
|
|
|
l = BLADERF_LOG_LEVEL_CRITICAL;
|
|
|
|
} else if (verbosity == "silent") {
|
|
|
|
l = BLADERF_LOG_LEVEL_SILENT;
|
|
|
|
} else {
|
|
|
|
BLADERF_THROW(boost::str(boost::format("Invalid log level: %s")
|
|
|
|
% verbosity));
|
|
|
|
}
|
|
|
|
|
|
|
|
bladerf_log_set_verbosity(l);
|
|
|
|
}
|
|
|
|
|
|
|
|
bladerf_channel bladerf_common::str2channel(std::string const &ch)
|
|
|
|
{
|
|
|
|
std::string prefix, numstr;
|
|
|
|
unsigned int numint;
|
|
|
|
|
|
|
|
/* We expect strings like "RX1" or "TX2" */
|
|
|
|
if (ch.length() < 3) {
|
|
|
|
/* It's too short */
|
|
|
|
return BLADERF_CHANNEL_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
prefix = ch.substr(0,2);
|
|
|
|
numstr = ch.substr(2,std::string::npos);
|
|
|
|
numint = boost::lexical_cast<unsigned int>(numstr) - 1;
|
|
|
|
|
|
|
|
if (prefix == "RX") {
|
|
|
|
return BLADERF_CHANNEL_RX(numint);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prefix == "TX") {
|
|
|
|
return BLADERF_CHANNEL_TX(numint);
|
|
|
|
}
|
|
|
|
|
|
|
|
return BLADERF_CHANNEL_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string bladerf_common::channel2str(bladerf_channel ch)
|
|
|
|
{
|
|
|
|
if (ch == BLADERF_CHANNEL_INVALID) {
|
|
|
|
return "OFF";
|
|
|
|
}
|
|
|
|
|
|
|
|
return boost::str(boost::format("%s%d")
|
|
|
|
% (_is_tx(ch) ? "TX" : "RX")
|
|
|
|
% (channel2rfport(ch) + 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
int bladerf_common::channel2rfport(bladerf_channel ch)
|
|
|
|
{
|
|
|
|
return (ch >> 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
bladerf_channel bladerf_common::chan2channel(bladerf_direction direction,
|
|
|
|
size_t chan)
|
|
|
|
{
|
|
|
|
for (bladerf_channel_map::value_type &i : _chanmap) {
|
|
|
|
bladerf_channel ch = i.first;
|
|
|
|
if (
|
|
|
|
(i.second == (int)chan) && (
|
|
|
|
(direction == BLADERF_TX && _is_tx(ch)) ||
|
|
|
|
(direction == BLADERF_RX && !_is_tx(ch))
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
return i.first;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return BLADERF_CHANNEL_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
osmosdr::meta_range_t bladerf_common::sample_rates(bladerf_channel ch)
|
|
|
|
{
|
|
|
|
osmosdr::meta_range_t sample_rates;
|
|
|
|
|
|
|
|
#ifdef BLADERF_COMPATIBILITY
|
|
|
|
/* assuming the same for RX & TX */
|
|
|
|
sample_rates += osmosdr::range_t( 160e3, 200e3, 40e3 );
|
|
|
|
sample_rates += osmosdr::range_t( 300e3, 900e3, 100e3 );
|
|
|
|
sample_rates += osmosdr::range_t( 1e6, 40e6, 1e6 );
|
|
|
|
#else
|
|
|
|
|
|
|
|
int status;
|
|
|
|
const bladerf_range *brf_sample_rates;
|
|
|
|
|
|
|
|
status = bladerf_get_sample_rate_range(_dev.get(), ch, &brf_sample_rates);
|
|
|
|
if (status != 0) {
|
|
|
|
BLADERF_THROW_STATUS(status, "bladerf_get_sample_rate_range failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Suggest a variety of sample rates */
|
|
|
|
sample_rates += osmosdr::range_t(brf_sample_rates->min,
|
|
|
|
brf_sample_rates->max / 4.0,
|
|
|
|
brf_sample_rates->max / 16.0);
|
|
|
|
sample_rates += osmosdr::range_t(brf_sample_rates->max / 4.0,
|
|
|
|
brf_sample_rates->max / 2.0,
|
|
|
|
brf_sample_rates->max / 8.0);
|
|
|
|
sample_rates += osmosdr::range_t(brf_sample_rates->max / 2.0,
|
|
|
|
brf_sample_rates->max,
|
|
|
|
brf_sample_rates->max / 4.0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return sample_rates;
|
|
|
|
}
|
|
|
|
|
|
|
|
double bladerf_common::set_sample_rate(double rate, bladerf_channel ch)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
struct bladerf_rational_rate rational_rate, actual;
|
|
|
|
|
|
|
|
rational_rate.integer = static_cast<uint32_t>(rate);
|
|
|
|
rational_rate.den = 10000;
|
|
|
|
rational_rate.num = (rate - rational_rate.integer) * rational_rate.den;
|
|
|
|
|
|
|
|
status = bladerf_set_rational_sample_rate(_dev.get(), ch,
|
|
|
|
&rational_rate, &actual);
|
|
|
|
if (status != 0) {
|
|
|
|
BLADERF_THROW_STATUS(status, "Failed to set sample rate");
|
|
|
|
}
|
|
|
|
|
|
|
|
return actual.integer + (actual.num / static_cast<double>(actual.den));
|
|
|
|
}
|
|
|
|
|
|
|
|
double bladerf_common::get_sample_rate(bladerf_channel ch)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
struct bladerf_rational_rate rate;
|
|
|
|
|
|
|
|
status = bladerf_get_rational_sample_rate(_dev.get(), ch, &rate);
|
|
|
|
if (status != 0) {
|
|
|
|
BLADERF_THROW_STATUS(status, "Failed to get sample rate");
|
|
|
|
}
|
|
|
|
|
|
|
|
return rate.integer + rate.num / static_cast<double>(rate.den);
|
|
|
|
}
|
|
|
|
|
|
|
|
osmosdr::freq_range_t bladerf_common::freq_range(bladerf_channel ch)
|
|
|
|
{
|
|
|
|
#ifdef BLADERF_COMPATIBILITY
|
|
|
|
return osmosdr::freq_range_t( _is_xb_attached(_dev) ? 0 : 280e6,
|
|
|
|
BLADERF_FREQUENCY_MAX );
|
|
|
|
#else
|
|
|
|
|
|
|
|
int status;
|
|
|
|
const struct bladerf_range *range;
|
|
|
|
|
|
|
|
status = bladerf_get_frequency_range(_dev.get(), ch, &range);
|
|
|
|
if (status != 0) {
|
|
|
|
BLADERF_THROW_STATUS(status, "bladerf_get_frequency_range failed");
|
|
|
|
};
|
|
|
|
|
|
|
|
return osmosdr::freq_range_t(static_cast<double>(range->min),
|
|
|
|
static_cast<double>(range->max),
|
|
|
|
static_cast<double>(range->step));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
double bladerf_common::set_center_freq(double freq, bladerf_channel ch)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
uint64_t freqint = static_cast<uint64_t>(freq + 0.5);
|
|
|
|
|
|
|
|
/* Check frequency range */
|
|
|
|
if (freqint < freq_range(ch).start() || freqint > freq_range(ch).stop()) {
|
|
|
|
BLADERF_WARNING(boost::str(boost::format("Frequency %d Hz is outside "
|
|
|
|
"range, ignoring") % freqint));
|
|
|
|
} else {
|
|
|
|
status = bladerf_set_frequency(_dev.get(), ch, freqint);
|
|
|
|
if (status != 0) {
|
|
|
|
BLADERF_THROW_STATUS(status, boost::str(boost::format("Failed to set center "
|
|
|
|
"frequency to %d Hz") % freqint));
|
|