From 8f8b137cee314528bc983517a52fd91592a71528 Mon Sep 17 00:00:00 2001 From: Rey Tucker Date: Mon, 18 Sep 2017 13:17:48 -0400 Subject: [PATCH] bladerf: add support for MIMO Squashed commit of rtucker-bladerf-hierarchy branch: commit 35442da7d390919f6f9cbae3f69d6dc32ca595bb through commit 9026136cfdbc7116f55af18cb06d1731330fa13f --- lib/bladerf/bladerf_common.cc | 1512 ++++++++++++++++--------------- lib/bladerf/bladerf_common.h | 286 ++++-- lib/bladerf/bladerf_sink_c.cc | 502 +++++----- lib/bladerf/bladerf_sink_c.h | 73 +- lib/bladerf/bladerf_source_c.cc | 606 +++++++++---- lib/bladerf/bladerf_source_c.h | 67 +- 6 files changed, 1794 insertions(+), 1252 deletions(-) diff --git a/lib/bladerf/bladerf_common.cc b/lib/bladerf/bladerf_common.cc index 22bfc74..c19f8ff 100644 --- a/lib/bladerf/bladerf_common.cc +++ b/lib/bladerf/bladerf_common.cc @@ -28,210 +28,418 @@ #include "config.h" #endif -#include #include #include +#include #include +#include -#include #include #include -#include +#include +#include +#include #include "bladerf_common.h" -#define NUM_BUFFERS 256 -#define NUM_SAMPLES_PER_BUFFER (4 * 1024) -#define NUM_TRANSFERS 16 +/* 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; boost::mutex bladerf_common::_devs_mutex; -std::list < boost::weak_ptr < struct bladerf >> bladerf_common::_devs; +std::list> bladerf_common::_devs; -/* name of system-wide gain - * (internal only, doesn't match any libbladeRF gain stage) - */ +/* name for system-wide gain (which is not its own libbladeRF gain stage) */ static const char *SYSTEM_GAIN_NAME = "system"; -bladerf_common::bladerf_common(): - _conv_buf(NULL), - _conv_buf_size(4096), - _xb_200_attached(false), - _consecutive_failures(0) +typedef std::pair bladerf_rxtx_pair; +typedef std::pair bladerf_chan_rxtx_pair; +typedef std::map bladerf_chan_rxtx_map; + +/* Mapping of bladeRF board model to maximum supported channels for (rx, tx) */ +static bladerf_chan_rxtx_map const _max_chans = { + bladerf_chan_rxtx_pair(BOARD_TYPE_UNKNOWN, bladerf_rxtx_pair(1,1)), + bladerf_chan_rxtx_pair(BOARD_TYPE_NONE, bladerf_rxtx_pair(0,0)), + bladerf_chan_rxtx_pair(BOARD_TYPE_BLADERF_1, bladerf_rxtx_pair(1,1)), + bladerf_chan_rxtx_pair(BOARD_TYPE_BLADERF_2, bladerf_rxtx_pair(2,2)), +}; + +/* 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; + } } -bladerf_common::~bladerf_common() +/* Returns TRUE if an expansion board is attached, FALSE otherwise */ +static bool _is_xb_attached(bladerf_sptr _dev) { - free(_conv_buf); -} + int status; + bladerf_xb xb = BLADERF_XB_NONE; -bladerf_board_type bladerf_common::get_board_type(struct bladerf *dev) -{ - if (NULL == dev) { - throw std::runtime_error(std::string(__FUNCTION__) + ": " + - "null pointer caught: dev"); + status = bladerf_expansion_get_attached(_dev.get(), &xb); + if (status != 0) { + return false; } - std::string boardname = std::string(bladerf_get_board_name(dev)); + return (xb != BLADERF_XB_NONE); +} + +/* Gets a value from a const dict */ +static std::string const _get(dict_t const &dict, std::string key) +{ + std::string rv(""); + + 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)); +} + +size_t num_streams(bladerf_channel_layout layout) +{ + 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; +} + +/****************************************************************************** + * Public methods + ******************************************************************************/ +bladerf_common::bladerf_common() : + _dev(NULL), + _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(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)); + } + } + } + + /* 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(_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(_get(dict, "buffers")); + } + + if (dict.count("buflen")) { + _samples_per_buffer = boost::lexical_cast(_get(dict, "buflen")); + } + + if (dict.count("transfers")) { + _num_transfers = boost::lexical_cast(_get(dict, "transfers")); + } + + if (dict.count("stream_timeout")) { + _stream_timeout = boost::lexical_cast(_get(dict, "stream_timeout")); + } else if (dict.count("stream_timeout_ms")) { + // reverse compatibility + _stream_timeout = boost::lexical_cast(_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 bladerf_common::devices() +{ + struct bladerf_devinfo *devices; + ssize_t n_devices; + std::vector 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 BLADERF_REV_1; - } else if (boardname == "bladerf2") { - return BLADERF_REV_2; + return BOARD_TYPE_BLADERF_1; } - std::cerr << _pfx - << "board name \"" << boardname << "\" unknown" - << std::endl; - return BLADERF_REV_INVALID; + if (boardname == "bladerf2") { + return BOARD_TYPE_BLADERF_2; + } + + BLADERF_WARNING(boost::str(boost::format("model '%s' is not recognized") + % boardname)); + + return BOARD_TYPE_UNKNOWN; } -bladerf_sptr bladerf_common::get_cached_device( struct bladerf_devinfo devinfo ) +size_t bladerf_common::get_max_channels(bladerf_direction direction) { - /* Lock to _devs must be aquired by caller */ - BOOST_FOREACH(boost::weak_ptr < struct bladerf >dev, _devs) { - int status; - struct bladerf_devinfo other_devinfo; - - status = bladerf_get_devinfo(bladerf_sptr(dev).get(), &other_devinfo); - if (status < 0) { - throw std::runtime_error(std::string(__FUNCTION__) + ": " + - "Failed to get devinfo for cached device: " + - bladerf_strerror(status)); - } - - if (bladerf_devinfo_matches(&devinfo, &other_devinfo)) { - return bladerf_sptr(dev); - } + if (BLADERF_RX == direction) { + return _max_chans.find(get_board_type())->second.first; } - return bladerf_sptr(); + if (BLADERF_TX == direction) { + return _max_chans.find(get_board_type())->second.second; + } + + return 0; } -/* This is called when a bladerf_sptr hits a refcount of 0 */ -void bladerf_common::close(void *dev) +void bladerf_common::set_channel_enable(bladerf_channel ch, bool enable) { - boost::unique_lock < boost::mutex > lock(_devs_mutex); - std::list < boost::weak_ptr < struct bladerf >>::iterator it(_devs.begin()); - - /* Prune expired entries from device cache */ - while (it != _devs.end()) { - if ((*it).expired()) { - it = _devs.erase(it); - } else { - ++it; - } - } - - bladerf_close(static_cast(dev)); + _enables[ch] = enable; } -bladerf_sptr bladerf_common::open(const std::string &device_name) +bool bladerf_common::get_channel_enable(bladerf_channel ch) { - int status; - struct bladerf *raw_dev = NULL; - struct bladerf_devinfo devinfo; - - boost::unique_lock < boost::mutex > lock(_devs_mutex); - - /* Initialize the information used to identify the desired device - * to all wildcard (i.e., "any device") values */ - bladerf_init_devinfo(&devinfo); - - /* Populate the devinfo structure from device_name */ - status = bladerf_get_devinfo_from_str(device_name.c_str(), &devinfo); - if (status < 0) { - throw std::runtime_error(_pfx + "Failed to get devinfo for '" + - device_name + "': " + bladerf_strerror(status)); - } - - /* Do we already have this device open? */ - bladerf_sptr cached_dev = get_cached_device(devinfo); - - if (cached_dev) { - return cached_dev; - } - - /* Open the device. */ - status = bladerf_open_with_devinfo(&raw_dev, &devinfo); - if (status < 0) { - throw std::runtime_error(_pfx + "Failed to open device for '" + - device_name + "': " + bladerf_strerror(status)); - } - - /* Add the device handle to our cache */ - bladerf_sptr dev = bladerf_sptr(raw_dev, bladerf_common::close); - - _devs.push_back(boost::weak_ptr < struct bladerf >(dev)); - - return dev; + return _enables[ch]; } -void bladerf_common::set_loopback_mode(const std::string &loopback) -{ - 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 { - throw std::runtime_error(_pfx + "Unknown loopback mode: " + loopback); - } - - status = bladerf_set_loopback(_dev.get(), mode); - if (status != 0) { - // TODO: handle BLADERF_ERR_UNSUPPORTED more gingerly - throw std::runtime_error(_pfx + "Failed to set loopback mode: " + - bladerf_strerror(status)); - } -} - -void bladerf_common::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 { - throw std::runtime_error(_pfx + "Unknown RX mux mode: " + rxmux); - } - - status = bladerf_set_rx_mux(_dev.get(), mode); - if (status != 0) { - // TODO: handle BLADERF_ERR_UNSUPPORTED more gingerly - throw std::runtime_error(_pfx + "Failed to set RX mux mode: " + - bladerf_strerror(status)); - } -} - -void bladerf_common::set_verbosity(const std::string &verbosity) +void bladerf_common::set_verbosity(std::string const &verbosity) { bladerf_log_level l; @@ -250,402 +458,85 @@ void bladerf_common::set_verbosity(const std::string &verbosity) } else if (verbosity == "silent") { l = BLADERF_LOG_LEVEL_SILENT; } else { - throw std::runtime_error(_pfx + "Invalid log level: " + verbosity); + BLADERF_THROW(boost::str(boost::format("Invalid log level: %s") + % verbosity)); } bladerf_log_set_verbosity(l); } -bool bladerf_common::start(bladerf_direction direction) +bladerf_channel bladerf_common::str2channel(std::string const &ch) { - int status; - bladerf_format format; - bladerf_channel_layout layout; + std::string prefix, numstr; + unsigned int numint; - if (_use_metadata) { - format = BLADERF_FORMAT_SC16_Q11_META; - } else { - format = BLADERF_FORMAT_SC16_Q11; + /* We expect strings like "RX1" or "TX2" */ + if (ch.length() < 3) { + /* It's too short */ + return BLADERF_CHANNEL_INVALID; } - switch (direction) { - case BLADERF_RX: - layout = _use_mimo ? BLADERF_RX_X2 : BLADERF_RX_X1; - break; - case BLADERF_TX: - layout = _use_mimo ? BLADERF_TX_X2 : BLADERF_TX_X1; - break; - default: - throw std::runtime_error(_pfx + "Invalid direction: " + - boost::lexical_cast(direction)); + prefix = ch.substr(0,2); + numstr = ch.substr(2,std::string::npos); + numint = boost::lexical_cast(numstr) - 1; + + if (prefix == "RX") { + return BLADERF_CHANNEL_RX(numint); } - status = bladerf_sync_config(_dev.get(), layout, format, - _num_buffers, _samples_per_buffer, - _num_transfers, _stream_timeout_ms); - if (status != 0) { - throw std::runtime_error(_pfx + "bladerf_sync_config failed: " + - bladerf_strerror(status)); + if (prefix == "TX") { + return BLADERF_CHANNEL_TX(numint); } - status = bladerf_enable_module(_dev.get(), direction, true); - if (status != 0) { - throw std::runtime_error(_pfx + "bladerf_enable_module failed: " + - bladerf_strerror(status)); - } - - return true; + return BLADERF_CHANNEL_INVALID; } -bool bladerf_common::stop(bladerf_direction direction) +std::string bladerf_common::channel2str(bladerf_channel ch) { - int status; - - status = bladerf_enable_module(_dev.get(), direction, false); - if (status != 0) { - throw std::runtime_error(_pfx + "bladerf_enable_module failed: " + - bladerf_strerror(status)); + if (ch == BLADERF_CHANNEL_INVALID) { + return "OFF"; } - return true; + return boost::str(boost::format("%s%d") + % (_is_tx(ch) ? "TX" : "RX") + % (channel2rfport(ch) + 1)); } -static bool version_greater_or_equal(const struct bladerf_version *version, - unsigned int major, - unsigned int minor, unsigned int patch) +int bladerf_common::channel2rfport(bladerf_channel ch) { - if (version->major > major) { - return true; - } else if ((version->major == major) && (version->minor > minor)) { - return true; - } else if ((version->major == major) && - (version->minor == minor) && - (version->patch >= patch)) { - return true; - } else { - return false; - } + return (ch >> 1); } -void bladerf_common::init(dict_t &dict, bladerf_direction direction) +bladerf_channel bladerf_common::chan2channel(bladerf_direction direction, + size_t chan) { - int status; - std::string device_name(""); - struct bladerf_version ver; - char serial[BLADERF_SERIAL_LENGTH]; - const char *type = (direction == BLADERF_TX ? "sink" : "source"); - - _pfx = std::string("[bladeRF ") + std::string(type) + std::string("] "); - - if (dict.count("verbosity")) { - set_verbosity(dict["verbosity"]); - } - - if (dict.count("bladerf")) { - const std::string value = 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(value); - device_name = boost::str(boost::format("*:instance=%d") % device_number); - } catch (std::exception &ex) { - throw std::runtime_error(_pfx + "Failed to use '" + value + - "' as device number: " + 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 { - throw std::runtime_error(_pfx + - "A full serial number must be supplied " - "with libbladeRF " + - std::string(ver.describe) + - ". libbladeRF >= v1.4.1 supports opening " - "a device via a subset of its serial #."); - } - } + BOOST_FOREACH(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; } } - try { - std::cerr << _pfx - << "Opening nuand bladeRF with device identifier string: \"" - << device_name << "\"" - << std::endl; - - _dev = open(device_name); - } catch (...) { - throw std::runtime_error(_pfx + "Failed to open bladeRF device " + - device_name); - } - - /* Load an FPGA */ - if (dict.count("fpga")) { - if (dict.count("fpga-reload") == 0 && - bladerf_is_fpga_configured(_dev.get()) == 1) { - - std::cerr << _pfx - << "FPGA is already loaded. Set fpga-reload=1 to force a " - << "reload." - << std::endl; - } else { - std::string fpga = dict["fpga"]; - - std::cerr << _pfx - << "Loading FPGA bitstream " << fpga << "..." - << std::endl; - - status = bladerf_load_fpga(_dev.get(), fpga.c_str()); - if (status != 0) { - std::cerr << _pfx - << "bladerf_load_fpga has failed with " - << bladerf_strerror(status) - << std::endl; - } else { - std::cerr << _pfx - << "The FPGA bitstream was successfully loaded." - << std::endl; - } - } - } - - if (bladerf_is_fpga_configured(_dev.get()) != 1) { - throw std::runtime_error(_pfx + - "The FPGA is not configured! Provide device " - "argument fpga=/path/to/the/bitstream.rbf to " - "load it."); - } - - if (direction == BLADERF_RX) { - if (dict.count("loopback")) { - set_loopback_mode(dict["loopback"]); - } else { - set_loopback_mode("none"); - } - } else if (direction == BLADERF_TX && dict.count("loopback")) { - std::cerr << _pfx - << "Warning: 'loopback' has been specified on a bladeRF " - << "sink, and will have no effect. This parameter should " - << "be specified on the associated bladeRF source." - << std::endl; - } - - if (direction == BLADERF_RX) { - if (dict.count("rxmux")) { - set_rx_mux_mode(dict["rxmux"]); - } else { - set_rx_mux_mode("baseband"); - } - } else if (direction == BLADERF_TX && dict.count("rxmux")) { - std::cerr << _pfx - << "Warning: 'rxmux' has been specified on a bladeRF sink, " - << "and will have no effect." - << std::endl; - } - - if (dict.count("xb200")) { - if (bladerf_expansion_attach(_dev.get(), BLADERF_XB_200)) { - std::cerr << _pfx << "Could not attach XB-200" << std::endl; - } else { - _xb_200_attached = true; - - bladerf_xb200_filter filter = BLADERF_XB200_AUTO_1DB; - - if (dict["xb200"] == "custom") { - filter = BLADERF_XB200_CUSTOM; - } else if (dict["xb200"] == "50M") { - filter = BLADERF_XB200_50M; - } else if (dict["xb200"] == "144M") { - filter = BLADERF_XB200_144M; - } else if (dict["xb200"] == "222M") { - filter = BLADERF_XB200_222M; - } else if (dict["xb200"] == "auto3db") { - filter = BLADERF_XB200_AUTO_3DB; - } else if (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) { - std::cerr << _pfx - << "Could not set XB-200 filter: " - << bladerf_strerror(status) - << std::endl; - } - } - } - - /* Show some info about the device we've opened */ - std::cerr << _pfx; - - if (bladerf_get_serial(_dev.get(), serial) == 0) { - std::string strser(serial); - - if (strser.length() == 32) { - strser.replace(4, 24, "..."); - } - - std::cerr << " Serial # " << strser; - } - - if (bladerf_fw_version(_dev.get(), &ver) == 0) { - std::cerr << " FW v" << ver.major << "." << ver.minor << "." << ver.patch; - } - - if (bladerf_fpga_version(_dev.get(), &ver) == 0) { - std::cerr << " FPGA v" << ver.major << "." << ver.minor << "." << ver.patch; - } - - std::cerr << std::endl; - - if (dict.count("tamer")) { - set_clock_source(dict["tamer"]); - std::cerr << _pfx - << "Tamer mode set to '" << get_clock_source() << "'" - << std::endl; - } - - if (dict.count("smb")) { - set_smb_frequency(boost::lexical_cast(dict["smb"])); - std::cerr << _pfx - << "SMB frequency set to " << get_smb_frequency() << " Hz" - << std::endl; - } - - /* Specify initial gain mode */ - if (direction == BLADERF_RX) { - if (dict.count("agc")) { - set_gain_mode(boost::lexical_cast(dict["agc"])); - std::cerr << _pfx - << "Gain mode set to " << get_gain_mode() - << std::endl; - } - } - - /* Initialize buffer and sample configuration */ - _num_buffers = 0; - if (dict.count("buffers")) { - _num_buffers = boost::lexical_cast(dict["buffers"]); - } - - _samples_per_buffer = 0; - if (dict.count("buflen")) { - _samples_per_buffer = boost::lexical_cast(dict["buflen"]); - } - - _num_transfers = 0; - if (dict.count("transfers")) { - _num_transfers = boost::lexical_cast(dict["transfers"]); - } - - _stream_timeout_ms = 3000; - if (dict.count("stream_timeout_ms")) { - _stream_timeout_ms = boost::lexical_cast(dict["stream_timeout_ms"]); - } - - _use_metadata = dict.count("enable_metadata") != 0; - - /* 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)) { - /* 0 likely implies the user did not specify this, so don't warn */ - if (_samples_per_buffer != 0) { - std::cerr << _pfx - << "Invalid \"buflen\" value. A multiple of 1024 is " - << "required. Defaulting to " << NUM_SAMPLES_PER_BUFFER - << std::endl; - } - - _samples_per_buffer = NUM_SAMPLES_PER_BUFFER; - } - } - - /* If the user hasn't specified the desired number of transfers, set it to - * min(NUM_TRANSFERS, num_buffers / 2) */ - if (_num_transfers == 0) { - _num_transfers = _num_buffers / 2; - - if (_num_transfers > NUM_TRANSFERS) { - _num_transfers = NUM_TRANSFERS; - } - } else if (_num_transfers >= _num_buffers) { - _num_transfers = _num_buffers - 1; - std::cerr << _pfx - << "Clamping num_tranfers to " << _num_transfers << ". " - << "Try using a smaller num_transfers value if timeouts occur." - << std::endl; - } - - std::cerr << _pfx - << "Buffers: " << _num_buffers << ", " - << "Samples per buffer: " << _samples_per_buffer << ", " - << "Transfers: " << _num_transfers - << std::endl; - - _conv_buf = static_cast(malloc(_conv_buf_size * 2 * sizeof(int16_t))); - - if (NULL == _conv_buf) { - throw std::runtime_error(_pfx + "Failed to allocate _conv_buf"); - } + return BLADERF_CHANNEL_INVALID; } -osmosdr::freq_range_t bladerf_common::freq_range(bladerf_channel chan) -{ - int status; - struct bladerf_range range; - - status = bladerf_get_frequency_range(_dev.get(), chan, &range); - - if (status != 0) { - throw std::runtime_error(_pfx + - "bladerf_get_frequency_range failed: " + - bladerf_strerror(status)); - } else { - return osmosdr::freq_range_t(static_cast(range.min), - static_cast(range.max), - static_cast(range.step)); - }; -} - -osmosdr::meta_range_t bladerf_common::sample_rates() +osmosdr::meta_range_t bladerf_common::sample_rates(bladerf_channel ch) { int status; osmosdr::meta_range_t sample_rates; bladerf_range brf_sample_rates; - /* assuming the same for RX & TX */ - status = bladerf_get_sample_rate_range(_dev.get(), BLADERF_CHANNEL_RX(0), - &brf_sample_rates); + status = bladerf_get_sample_rate_range(_dev.get(), ch, &brf_sample_rates); if (status != 0) { - throw std::runtime_error(_pfx + - "bladerf_get_sample_rate_range failed: " + - bladerf_strerror(status)); + 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); @@ -659,61 +550,7 @@ osmosdr::meta_range_t bladerf_common::sample_rates() return sample_rates; } -osmosdr::freq_range_t bladerf_common::filter_bandwidths() -{ - /* the same for RX & TX according to the datasheet */ - int status; - osmosdr::freq_range_t bandwidths; - bladerf_range range; - - status = bladerf_get_bandwidth_range(_dev.get(), BLADERF_CHANNEL_RX(0), - &range); - if (status != 0) { - throw std::runtime_error(_pfx + - "bladerf_get_bandwidth_range failed: " + - bladerf_strerror(status)); - } - - bandwidths += osmosdr::range_t(range.min, range.max, range.step); - - return bandwidths; -} - -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::stringstream s; - std::string serial(devices[i].serial); - - s << "bladerf=" << devices[i].instance << ",label='nuand bladeRF"; - - if (serial.length() == 32) { - serial.replace(4, 24, "..."); - } - - if (serial.length()) { - s << " SN " << serial; - } - - s << "'"; - - ret.push_back(s.str()); - } - - bladerf_free_device_list(devices); - } - - return ret; -} - -double bladerf_common::set_sample_rate(bladerf_direction direction, double rate) +double bladerf_common::set_sample_rate(double rate, bladerf_channel ch) { int status; struct bladerf_rational_rate rational_rate, actual; @@ -722,91 +559,141 @@ double bladerf_common::set_sample_rate(bladerf_direction direction, double rate) rational_rate.den = 10000; rational_rate.num = (rate - rational_rate.integer) * rational_rate.den; - status = bladerf_set_rational_sample_rate(_dev.get(), direction, + status = bladerf_set_rational_sample_rate(_dev.get(), ch, &rational_rate, &actual); if (status != 0) { - throw std::runtime_error(_pfx + "Failed to set sample rate:" + - bladerf_strerror(status)); + BLADERF_THROW_STATUS(status, "Failed to set sample rate"); } - return actual.integer + actual.num / static_cast(actual.den); + return actual.integer + (actual.num / static_cast(actual.den)); } -double bladerf_common::get_sample_rate(bladerf_direction direction) +double bladerf_common::get_sample_rate(bladerf_channel ch) { int status; struct bladerf_rational_rate rate; - status = bladerf_get_rational_sample_rate(_dev.get(), direction, &rate); + status = bladerf_get_rational_sample_rate(_dev.get(), ch, &rate); if (status != 0) { - throw std::runtime_error(_pfx + "Failed to get sample rate:" + - bladerf_strerror(status)); + BLADERF_THROW_STATUS(status, "Failed to get sample rate"); } return rate.integer + rate.num / static_cast(rate.den); } -osmosdr::freq_range_t bladerf_common::get_freq_range(size_t chan) -{ - return freq_range(static_cast(chan)); -} - -double bladerf_common::set_center_freq(double freq, size_t chan) +osmosdr::freq_range_t bladerf_common::freq_range(bladerf_channel ch) { int status; + 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(range.min), + static_cast(range.max), + static_cast(range.step)); +} + +double bladerf_common::set_center_freq(double freq, bladerf_channel ch) +{ + int status; + uint64_t freqint = static_cast(freq + 0.5); /* Check frequency range */ - if (freq < get_freq_range(chan).start() || - freq > get_freq_range(chan).stop()) { - std::cerr << "Failed to set out of bound frequency: " << freq << std::endl; + 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(), - static_cast(chan), - static_cast(freq)); + status = bladerf_set_frequency(_dev.get(), ch, freqint); if (status != 0) { - throw std::runtime_error(_pfx + - "failed to set center frequency " + - boost::lexical_cast(freq) + ": " + - bladerf_strerror(status)); + BLADERF_THROW_STATUS(status, boost::str(boost::format("Failed to set center " + "frequency to %d Hz") % freqint)); } } - return get_center_freq(chan); + return get_center_freq(ch); } -double bladerf_common::get_center_freq(size_t chan) +double bladerf_common::get_center_freq(bladerf_channel ch) { int status; uint64_t freq; - status = bladerf_get_frequency(_dev.get(), - static_cast(chan), - &freq); + status = bladerf_get_frequency(_dev.get(), ch, &freq); if (status != 0) { - throw std::runtime_error(_pfx + "failed to get center frequency: " + - bladerf_strerror(status)); + BLADERF_THROW_STATUS(status, "Failed to get center frequency"); } return static_cast(freq); } -std::vector bladerf_common::get_gain_names(size_t chan) +osmosdr::freq_range_t bladerf_common::filter_bandwidths(bladerf_channel ch) +{ + /* the same for RX & TX according to the datasheet */ + int status; + osmosdr::freq_range_t bandwidths; + bladerf_range range; + + status = bladerf_get_bandwidth_range(_dev.get(), ch, &range); + if (status != 0) { + BLADERF_THROW_STATUS(status, "bladerf_get_bandwidth_range failed"); + } + + bandwidths += osmosdr::range_t(range.min, range.max, range.step); + + return bandwidths; +} + +double bladerf_common::set_bandwidth(double bandwidth, bladerf_channel ch) +{ + int status; + uint32_t bwint; + + if (bandwidth == 0.0) { + /* bandwidth of 0 means automatic filter selection */ + /* select narrower filters to prevent aliasing */ + bandwidth = get_sample_rate(ch) * 0.75; + } + + bwint = static_cast(bandwidth + 0.5); + + status = bladerf_set_bandwidth(_dev.get(), ch, bwint, NULL); + if (status != 0) { + BLADERF_THROW_STATUS(status, "could not set bandwidth"); + } + + return get_bandwidth(ch); +} + +double bladerf_common::get_bandwidth(bladerf_channel ch) +{ + int status; + uint32_t bandwidth; + + status = bladerf_get_bandwidth(_dev.get(), ch, &bandwidth); + if (status != 0) { + BLADERF_THROW_STATUS(status, "could not get bandwidth"); + } + + return static_cast(bandwidth); +} + +std::vector bladerf_common::get_gain_names(bladerf_channel ch) { const size_t max_count = 16; - std::vector < std::string > names; + std::vector names; char *gain_names[max_count]; int count; names += SYSTEM_GAIN_NAME; - count = bladerf_get_gain_stages(_dev.get(), - static_cast(chan), + count = bladerf_get_gain_stages(_dev.get(), ch, reinterpret_cast(&gain_names), max_count); if (count < 0) { - throw std::runtime_error(_pfx + - "failed to get gain stages: " + - bladerf_strerror(count)); + BLADERF_THROW_STATUS(count, "Failed to enumerate gain stages"); } for (int i = 0; i < count; ++i) { @@ -817,143 +704,173 @@ std::vector bladerf_common::get_gain_names(size_t chan) return names; } -osmosdr::gain_range_t bladerf_common::get_gain_range(size_t chan) +osmosdr::gain_range_t bladerf_common::get_gain_range(bladerf_channel ch) { /* This is an overall system gain range. */ - return get_gain_range(SYSTEM_GAIN_NAME, chan); + return get_gain_range(SYSTEM_GAIN_NAME, ch); } -osmosdr::gain_range_t bladerf_common::get_gain_range(const std::string &name, - size_t chan) +osmosdr::gain_range_t bladerf_common::get_gain_range(std::string const &name, + bladerf_channel ch) { int status; struct bladerf_range range; if (name == SYSTEM_GAIN_NAME) { - status = bladerf_get_gain_range(_dev.get(), - static_cast(chan), - &range); + status = bladerf_get_gain_range(_dev.get(), ch, &range); } else { - status = bladerf_get_gain_stage_range(_dev.get(), - static_cast(chan), - name.c_str(), - &range); + status = bladerf_get_gain_stage_range(_dev.get(), ch, name.c_str(), &range); } if (status != 0) { - throw std::runtime_error(_pfx + - "bladerf_get_gain_range " + name + - " failed: " + bladerf_strerror(status)); + BLADERF_THROW_STATUS(status, boost::str(boost::format("Failed to get gain " + "range for stage '%s'") % name)); } return osmosdr::gain_range_t(range.min, range.max, range.step); } -bool bladerf_common::set_gain_mode(bool automatic, size_t chan) +bool bladerf_common::set_gain_mode(bool automatic, bladerf_channel ch, + bladerf_gain_mode agc_mode) { int status; - bladerf_gain_mode mode = automatic ? BLADERF_GAIN_DEFAULT : BLADERF_GAIN_MGC; + bladerf_gain_mode mode = automatic ? agc_mode : BLADERF_GAIN_MGC; - status = bladerf_set_gain_mode(_dev.get(), - static_cast(chan), - mode); + status = bladerf_set_gain_mode(_dev.get(), ch, mode); if (status != 0) { - throw std::runtime_error(_pfx + - "bladerf_set_gain_mode " + - (automatic ? "automatic" : "manual") + - " failed: " + bladerf_strerror(status)); + BLADERF_THROW_STATUS(status, boost::str(boost::format("Setting gain mode " + "to '%s' failed") + % (automatic ? "automatic" : "manual"))); } - return get_gain_mode(chan); + return get_gain_mode(ch); } -bool bladerf_common::get_gain_mode(size_t chan) +bool bladerf_common::get_gain_mode(bladerf_channel ch) { int status; - bladerf_gain_mode gainmode; + bladerf_gain_mode gainmode = BLADERF_GAIN_DEFAULT; - status = bladerf_get_gain_mode(_dev.get(), - static_cast(chan), - &gainmode); + status = bladerf_get_gain_mode(_dev.get(), ch, &gainmode); if (status != 0) { - throw std::runtime_error(_pfx + "bladerf_get_gain_mode failed: " + - bladerf_strerror(status)); + BLADERF_WARN_STATUS(status, "Failed to get gain mode"); } return (gainmode != BLADERF_GAIN_MGC); } -double bladerf_common::set_gain(double gain, size_t chan) +double bladerf_common::set_gain(double gain, bladerf_channel ch) { - return set_gain(gain, SYSTEM_GAIN_NAME, chan); + return set_gain(gain, SYSTEM_GAIN_NAME, ch); } double bladerf_common::set_gain(double gain, - const std::string &name, size_t chan) + std::string const &name, + bladerf_channel ch) { int status; if (name == SYSTEM_GAIN_NAME) { - status = bladerf_set_gain(_dev.get(), - static_cast(chan), - static_cast(gain)); + status = bladerf_set_gain(_dev.get(), ch, static_cast(gain)); } else { - status = bladerf_set_gain_stage(_dev.get(), - static_cast(chan), - name.c_str(), + status = bladerf_set_gain_stage(_dev.get(), ch, name.c_str(), static_cast(gain)); } /* Check for errors */ - if (status != 0) { - std::string errmsg = _pfx + "could not set " + name + " gain: " + - bladerf_strerror(status); - if (BLADERF_ERR_UNSUPPORTED == status) { - // unsupported, but not worth crashing out - std::cerr << errmsg << std::endl; - } else { - throw std::runtime_error(errmsg); - } + if (BLADERF_ERR_UNSUPPORTED == status) { + // unsupported, but not worth crashing out + BLADERF_WARNING(boost::str(boost::format("Gain stage '%s' not supported " + "by device") % name)); + } else if (status != 0) { + BLADERF_THROW_STATUS(status, boost::str(boost::format("Failed to set " + "gain for stage '%s'") % name)); } - return get_gain(name, chan); + return get_gain(name, ch); } -double bladerf_common::get_gain(size_t chan) +double bladerf_common::get_gain(bladerf_channel ch) { - return get_gain(SYSTEM_GAIN_NAME, chan); + return get_gain(SYSTEM_GAIN_NAME, ch); } -double bladerf_common::get_gain(const std::string &name, size_t chan) +double bladerf_common::get_gain(std::string const &name, bladerf_channel ch) { int status; - int g; + int g = 0; if (name == SYSTEM_GAIN_NAME) { - status = bladerf_get_gain(_dev.get(), - static_cast(chan), - &g); + status = bladerf_get_gain(_dev.get(), ch, &g); } else { - status = bladerf_get_gain_stage(_dev.get(), - static_cast(chan), - name.c_str(), - &g); + status = bladerf_get_gain_stage(_dev.get(), ch, name.c_str(), &g); } /* Check for errors */ if (status != 0) { - throw std::runtime_error(_pfx + "could not get " + name + " gain: " + - bladerf_strerror(status)); + BLADERF_WARN_STATUS(status, boost::str(boost::format("Could not get gain " + "for stage '%s'") % name)); } - return (double) g; + return static_cast(g); } -int bladerf_common::set_dc_offset(bladerf_direction direction, - const std::complex < double > &offset, - size_t chan) +std::vector bladerf_common::get_antennas(bladerf_direction dir) +{ + std::vector antennas; + + for (size_t i = 0; i < get_max_channels(dir); ++i) { + switch (dir) { + case BLADERF_RX: + antennas += channel2str(BLADERF_CHANNEL_RX(i)); + break; + case BLADERF_TX: + antennas += channel2str(BLADERF_CHANNEL_TX(i)); + break; + default: + break; + } + } + + return antennas; +} + +bool bladerf_common::set_antenna(bladerf_direction dir, + size_t chan, + const std::string &antenna) +{ + if (!is_antenna_valid(dir, antenna)) { + BLADERF_THROW("Invalid antenna: " + antenna); + } + + // This port's old antenna + bladerf_channel old_channel = chan2channel(dir, chan); + // This port's new antenna + bladerf_channel new_channel = str2channel(antenna); + // The new antenna's old port + int old_chan = _chanmap[new_channel]; + + if (old_channel != new_channel || old_chan != (int)chan) { + // Disable the old antenna, if it's not going to be used + if (old_chan == -1) { + set_channel_enable(old_channel, false); + } + + // Swap antennas + _chanmap[old_channel] = old_chan; + _chanmap[new_channel] = chan; + + // Enable the new antenna + set_channel_enable(new_channel, true); + } + + return true; +} + +int bladerf_common::set_dc_offset(std::complex const &offset, + bladerf_channel ch) { int ret = 0; int16_t val_i, val_q; @@ -961,17 +878,16 @@ int bladerf_common::set_dc_offset(bladerf_direction direction, val_i = static_cast(offset.real() * DCOFF_SCALE); val_q = static_cast(offset.imag() * DCOFF_SCALE); - ret = bladerf_set_correction(_dev.get(), direction, + ret = bladerf_set_correction(_dev.get(), ch, BLADERF_CORR_LMS_DCOFF_I, val_i); - ret |= bladerf_set_correction(_dev.get(), direction, + ret |= bladerf_set_correction(_dev.get(), ch, BLADERF_CORR_LMS_DCOFF_Q, val_q); return ret; } -int bladerf_common::set_iq_balance(bladerf_direction direction, - const std::complex < double > &balance, - size_t chan) +int bladerf_common::set_iq_balance(std::complex const &balance, + bladerf_channel ch) { int ret = 0; int16_t val_gain, val_phase; @@ -979,20 +895,32 @@ int bladerf_common::set_iq_balance(bladerf_direction direction, val_gain = static_cast(balance.real() * GAIN_SCALE); val_phase = static_cast(balance.imag() * PHASE_SCALE); - ret = bladerf_set_correction(_dev.get(), direction, + ret = bladerf_set_correction(_dev.get(), ch, BLADERF_CORR_FPGA_GAIN, val_gain); - ret |= bladerf_set_correction(_dev.get(), direction, + ret |= bladerf_set_correction(_dev.get(), ch, BLADERF_CORR_FPGA_PHASE, val_phase); return ret; } -void bladerf_common::set_clock_source(const std::string &source, - const size_t mboard) +std::vector bladerf_common::get_clock_sources(size_t mboard) +{ + std::vector sources; + + // assumes zero-based 1:1 mapping + sources.push_back("internal"); // BLADERF_VCTCXO_TAMER_DISABLED + sources.push_back("external_1pps"); // BLADERF_VCTCXO_TAMER_1_PPS + sources.push_back("external"); // BLADERF_VCTCXO_TAMER_10_MHZ + + return sources; +} + +void bladerf_common::set_clock_source(std::string const &source, + size_t mboard) { int status; bladerf_vctcxo_tamer_mode tamer_mode; - std::vector < std::string > clock_sources; + std::vector clock_sources; int index; tamer_mode = BLADERF_VCTCXO_TAMER_DISABLED; @@ -1005,23 +933,21 @@ void bladerf_common::set_clock_source(const std::string &source, status = bladerf_set_vctcxo_tamer_mode(_dev.get(), tamer_mode); if (status != 0) { - throw std::runtime_error(_pfx + "Failed to set VCTCXO tamer mode: " + - bladerf_strerror(status)); + BLADERF_THROW_STATUS(status, "Failed to set VCTCXO tamer mode"); } } -std::string bladerf_common::get_clock_source(const size_t mboard) +std::string bladerf_common::get_clock_source(size_t mboard) { int status; bladerf_vctcxo_tamer_mode tamer_mode; - std::vector < std::string > clock_sources; + std::vector clock_sources; tamer_mode = BLADERF_VCTCXO_TAMER_INVALID; status = bladerf_get_vctcxo_tamer_mode(_dev.get(), &tamer_mode); if (status != 0) { - throw std::runtime_error(_pfx + "Failed to get VCTCXO tamer mode: " + - bladerf_strerror(status)); + BLADERF_THROW_STATUS(status, "Failed to get VCTCXO tamer mode"); } clock_sources = get_clock_sources(mboard); @@ -1029,37 +955,28 @@ std::string bladerf_common::get_clock_source(const size_t mboard) return clock_sources.at(tamer_mode); } -std::vector < std::string > bladerf_common::get_clock_sources(const size_t mboard) -{ - std::vector < std::string > sources; - - // assumes zero-based 1:1 mapping - sources.push_back("internal"); // BLADERF_VCTCXO_TAMER_DISABLED - sources.push_back("external_1pps"); // BLADERF_VCTCXO_TAMER_1_PPS - sources.push_back("external"); // BLADERF_VCTCXO_TAMER_10_MHZ - - return sources; -} - void bladerf_common::set_smb_frequency(double frequency) { int status; - uint32_t actual_frequency; - actual_frequency = frequency; + uint32_t freqint = static_cast(frequency + 0.5); + uint32_t actual_frequency = freqint; - status = bladerf_set_smb_frequency(_dev.get(), - static_cast(frequency), - &actual_frequency); - if (status != 0) { - throw std::runtime_error(_pfx + "Failed to set SMB frequency: " + - bladerf_strerror(status)); + if (_is_xb_attached(_dev)) { + BLADERF_WARNING("Cannot use SMB port when expansion board is attached"); + return; } - if (static_cast(frequency) != actual_frequency) { - std::cerr << _pfx - << "Wanted SMB frequency is " << frequency - << ", actual is " << actual_frequency - << std::endl; + status = bladerf_set_smb_frequency(_dev.get(), + freqint, + &actual_frequency); + if (status != 0) { + BLADERF_THROW_STATUS(status, "Failed to set SMB frequency"); + } + + if (freqint != actual_frequency) { + BLADERF_WARNING(boost::str(boost::format("Wanted SMB frequency %f (%d) " + "Hz, actual frequency is %d Hz") + % frequency % freqint % actual_frequency)); } } @@ -1068,11 +985,154 @@ double bladerf_common::get_smb_frequency() int status; unsigned int actual_frequency; + if (_is_xb_attached(_dev)) { + BLADERF_WARNING("Cannot use SMB port when expansion board is attached"); + return 0.0; + } + status = bladerf_get_smb_frequency(_dev.get(), &actual_frequency); if (status != 0) { - throw std::runtime_error(_pfx + "Failed to get SMB frequency: " + - bladerf_strerror(status)); + BLADERF_THROW_STATUS(status, "Failed to get SMB frequency"); } return static_cast(actual_frequency); } + +/****************************************************************************** + * Private methods + ******************************************************************************/ +bladerf_sptr bladerf_common::open(std::string const &device_name) +{ + int status; + struct bladerf *raw_dev = NULL; + struct bladerf_devinfo devinfo; + + boost::unique_lock lock(_devs_mutex); + + /* Initialize the information used to identify the desired device + * to all wildcard (i.e., "any device") values */ + bladerf_init_devinfo(&devinfo); + + /* Populate the devinfo structure from device_name */ + status = bladerf_get_devinfo_from_str(device_name.c_str(), &devinfo); + if (status < 0) { + BLADERF_THROW_STATUS(status, boost::str(boost::format("Failed to get " + "devinfo for '%s'") % device_name)); + } + + /* Do we already have this device open? */ + bladerf_sptr cached_dev = get_cached_device(devinfo); + + if (cached_dev) { + return cached_dev; + } + + /* Open the device. */ + status = bladerf_open_with_devinfo(&raw_dev, &devinfo); + if (status < 0) { + BLADERF_THROW_STATUS(status, boost::str(boost::format("Failed to open " + "device for '%s'") % device_name)); + } + + /* Add the device handle to our cache */ + bladerf_sptr dev = bladerf_sptr(raw_dev, bladerf_common::close); + + _devs.push_back(static_cast>(dev)); + + return dev; +} + +void bladerf_common::close(void *dev) +{ + boost::unique_lock lock(_devs_mutex); + std::list>::iterator it(_devs.begin()); + + /* Prune expired entries from device cache */ + while (it != _devs.end()) { + if ((*it).expired()) { + it = _devs.erase(it); + } else { + ++it; + } + } + + bladerf_close(static_cast(dev)); +} + +bladerf_sptr bladerf_common::get_cached_device(struct bladerf_devinfo devinfo) +{ + /* Lock to _devs must be aquired by caller */ + int status; + struct bladerf_devinfo other_devinfo; + + BOOST_FOREACH(boost::weak_ptr dev, _devs) { + status = bladerf_get_devinfo(bladerf_sptr(dev).get(), &other_devinfo); + if (status < 0) { + BLADERF_THROW_STATUS(status, "Failed to get devinfo for cached device"); + } + + if (bladerf_devinfo_matches(&devinfo, &other_devinfo)) { + return bladerf_sptr(dev); + } + } + + return bladerf_sptr(); +} + +void bladerf_common::print_device_info() +{ + char serial[BLADERF_SERIAL_LENGTH]; + struct bladerf_version ver; + + std::cout << _pfx << "Device: "; + + switch (get_board_type()) { + case BOARD_TYPE_BLADERF_1: + std::cout << "Nuand bladeRF"; + break; + case BOARD_TYPE_BLADERF_2: + std::cout << "Nuand bladeRF 2.0"; + break; + default: + std::cout << "Unknown Device"; + break; + } + + if (bladerf_get_serial(_dev.get(), serial) == 0) { + std::string strser(serial); + + if (strser.length() == 32) { + strser.replace(4, 24, "..."); + } + + std::cout << " Serial # " << strser; + } else { + std::cout << " Serial # UNKNOWN"; + } + + if (bladerf_fw_version(_dev.get(), &ver) == 0) { + std::cout << " FW v" << ver.major << "." << ver.minor << "." << ver.patch; + } else { + std::cout << " FW version UNKNOWN"; + } + + if (bladerf_fpga_version(_dev.get(), &ver) == 0) { + std::cout << " FPGA v" << ver.major << "." << ver.minor << "." << ver.patch; + } else { + std::cout << " FPGA version UNKNOWN"; + } + + std::cout << std::endl; +} + +bool bladerf_common::is_antenna_valid(bladerf_direction dir, + const std::string &antenna) +{ + BOOST_FOREACH(std::string ant, get_antennas(dir)) { + if (antenna == ant) { + return true; + } + } + + return false; +} diff --git a/lib/bladerf/bladerf_common.h b/lib/bladerf/bladerf_common.h index 34a5ea0..1dce8bf 100644 --- a/lib/bladerf/bladerf_common.h +++ b/lib/bladerf/bladerf_common.h @@ -21,21 +21,14 @@ #ifndef INCLUDED_BLADERF_COMMON_H #define INCLUDED_BLADERF_COMMON_H -#include +#include +#include #include +#include -#include #include -#include -#include -#include -#include -#include #include -#include -#include - #include #include "osmosdr/ranges.h" @@ -44,103 +37,256 @@ #ifdef _MSC_VER #include 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 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_enable_map; + +/* Mapping of bladerf_channel to gnuradio port/chan */ +typedef std::map 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 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 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 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 const &offset, bladerf_channel ch); + /* Set the IQ balance on channel ch */ + int set_iq_balance(std::complex const &balance, bladerf_channel ch); + + /* Get the list of supported clock sources */ + std::vector 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> _devs; /**< dev cache */ }; #endif diff --git a/lib/bladerf/bladerf_sink_c.cc b/lib/bladerf/bladerf_sink_c.cc index 377ed99..74a4bc9 100644 --- a/lib/bladerf/bladerf_sink_c.cc +++ b/lib/bladerf/bladerf_sink_c.cc @@ -35,23 +35,19 @@ #include #include -#include -#include #include #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 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(volk_malloc(2*_samples_per_buffer*sizeof(int16_t), alignment)); + _32fcbuf = reinterpret_cast(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(&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(_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(_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 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(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(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(&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(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(&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 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 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); -} diff --git a/lib/bladerf/bladerf_sink_c.h b/lib/bladerf/bladerf_sink_c.h index e76f0ec..fc5b02c 100644 --- a/lib/bladerf/bladerf_sink_c.h +++ b/lib/bladerf/bladerf_sink_c.h @@ -21,14 +21,12 @@ #ifndef INCLUDED_BLADERF_SINK_C_H #define INCLUDED_BLADERF_SINK_C_H -#include -#include #include - -#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_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 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 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 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 &offset, size_t chan); + void set_iq_balance(const std::complex &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 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 diff --git a/lib/bladerf/bladerf_source_c.cc b/lib/bladerf/bladerf_source_c.cc index b0c1de9..b94b24b 100644 --- a/lib/bladerf/bladerf_source_c.cc +++ b/lib/bladerf/bladerf_source_c.cc @@ -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(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(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(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(_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 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(volk_malloc(2*_samples_per_buffer*sizeof(int16_t), alignment)); + _32fcbuf = reinterpret_cast(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(_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(_32fcbuf), _16icbuf, + SCALING_FACTOR, 2*noutput_items); + + // copy the samples into output_items + gr_complex **out = reinterpret_cast(&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 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 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(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 &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(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 &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 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); + } + } } diff --git a/lib/bladerf/bladerf_source_c.h b/lib/bladerf/bladerf_source_c.h index 8eadd74..4bc3af2 100644 --- a/lib/bladerf/bladerf_source_c.h +++ b/lib/bladerf/bladerf_source_c.h @@ -21,14 +21,12 @@ #ifndef INCLUDED_BLADERF_SOURCE_C_H #define INCLUDED_BLADERF_SOURCE_C_H -#include -#include #include - -#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_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 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 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 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 &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 &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 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