bladerf: add support for nuand LLC bladeRF (WIP)

This is based on the original work (https://github.com/Nuand/gr-osmosdr)
done by folks at nuand LLC for the gr3.6 branch of gr-osmosdr.

The following modifications have been done in this commit:

* port to gr-osmosdr master codebase (gr3.7)
* moved shared properties to bladerf_common
* added & verified IF filter bandwidth setters
* set LMS6002D registers with values taken from FAQ 5.27
* print device information (serial/versions) on startup
* added fpga= and fw= device arguments to program MCU/FPGA
* added bladerf=# dev. arg. to select a specific bladeRF
* grc gain field controls RF path VGA for RX/TX
* grc BB gain field controls BB path VGA for RX/TX

Usage example:

osmocom_fft -a "bladerf,fpga=/tmp/hostedx115.rbf"

The following RX named gain stages are available:

LNA: 0 to 6 dB, in 3dB steps
VGA1: 5 to 30 dB, in 1dB steps; nonlinear mapping done inside the lib
VGA2: 0 to 60 dB, in 3dB steps; not recommended to be used above 30dB

The following TX named gain stages are available:

VGA1: -35 to -4 dB, in 1dB steps, BB side
VGA2: 0 to 25 dB, in 1dB steps, RF side

Thanks a lot to the team of nuand LLC for this major contribution.
standalone
Dimitri Stolnikov 10 years ago
parent 0edfcfcba0
commit e5f7b28093
  1. 1
      AUTHORS
  2. 1
      CMakeLists.txt
  3. 1
      README
  4. 27
      cmake/Modules/FindLibbladeRF.cmake
  5. 2
      grc/gen_osmosdr_blocks.py
  6. 8
      lib/CMakeLists.txt
  7. 40
      lib/bladerf/CMakeLists.txt
  8. 168
      lib/bladerf/bladerf_common.cc
  9. 80
      lib/bladerf/bladerf_common.h
  10. 566
      lib/bladerf/bladerf_sink_c.cc
  11. 120
      lib/bladerf/bladerf_sink_c.h
  12. 584
      lib/bladerf/bladerf_source_c.cc
  13. 121
      lib/bladerf/bladerf_source_c.h
  14. 1
      lib/config.h.in
  15. 11
      lib/device.cc
  16. 20
      lib/sink_impl.cc
  17. 22
      lib/source_impl.cc

@ -1,3 +1,4 @@
Dimitri Stolnikov <horiz0n@gmx.net>
Steve Markgraf <steve@steve-m.de>
Hoernchen <la@tfc-server.de>
Nuand LLC folks

@ -154,6 +154,7 @@ find_package(LibOsmoSDR)
find_package(LibRTLSDR)
find_package(LibMiriSDR)
find_package(LibHackRF)
find_package(LibbladeRF)
find_package(Doxygen)
if(NOT GNURADIO_RUNTIME_FOUND)

@ -4,6 +4,7 @@ as well supports:
* FUNcube Dongle through libgnuradio-fcd
* FUNcube Dongle Pro+ through gr-fcdproplus
* sysmocom OsmoSDR Devices through libosmosdr
* Nuand LLC bladeRF through libbladeRF library
* Great Scott Gadgets HackRF through libhackrf
* Ettus USRP Devices through Ettus UHD library
* RTL2832U based DVB-T dongles through librtlsdr

@ -0,0 +1,27 @@
if(NOT LIBBLADERF_FOUND)
pkg_check_modules (LIBBLADERF_PKG libbladeRF)
find_path(LIBBLADERF_INCLUDE_DIR NAMES libbladeRF.h
PATHS
${LIBBLADERF_PKG_INCLUDE_DIRS}
/usr/include
/usr/local/include
)
find_library(LIBBLADERF_LIBRARIES NAMES bladeRF
PATHS
${LIBBLADERF_PKG_LIBRARY_DIRS}
/usr/lib
/usr/local/lib
)
if(LIBBLADERF_INCLUDE_DIR AND LIBBLADERF_LIBRARIES)
set(LIBBLADERF_FOUND TRUE CACHE INTERNAL "libbladeRF found")
message(STATUS "Found libbladeRF: ${LIBBLADERF_INCLUDE_DIR}, ${LIBBLADERF_LIBRARIES}")
else(LIBBLADERF_INCLUDE_DIR AND LIBBLADERF_LIBRARIES)
set(LIBBLADERF_FOUND FALSE CACHE INTERNAL "libbladeRF found")
message(STATUS "libbladeRF not found.")
endif(LIBBLADERF_INCLUDE_DIR AND LIBBLADERF_LIBRARIES)
mark_as_advanced(LIBBLADERF_INCLUDE_DIR LIBBLADERF_LIBRARIES)
endif(NOT LIBBLADERF_FOUND)

@ -119,6 +119,7 @@ While primarily being developed for the OsmoSDR hardware, this block as well sup
* FUNcube Dongle Pro+ through gr-fcdproplus
* sysmocom OsmoSDR Devices through libosmosdr
#end if
* Nuand LLC bladeRF through libbladeRF library
* Great Scott Gadgets HackRF through libhackrf
* Ettus USRP Devices through Ettus UHD library
#if $sourk == 'source':
@ -152,6 +153,7 @@ Lines ending with ... mean it's possible to bind devices together by specifying
rtl=2[,direct_samp=0|1|2][,offset_tune=0|1] ...
rtl_tcp=127.0.0.1:1234[,psize=16384][,direct_samp=0|1|2][,offset_tune=0|1] ...
uhd[,serial=...][,lo_offset=0][,mcr=52e6][,nchan=2][,subdev='\\\\'B:0 A:0\\\\''] ...
bladerf=0[,fpga='/path/to/the/bitstream.rbf'][,fw='/path/to/the/firmware.img']
osmosdr=0[,buffers=32][,buflen=N*512] ...
file='/path/to/your file',rate=1e6[,freq=100e6][,repeat=true][,throttle=true] ...
#end if

@ -130,6 +130,14 @@ if(ENABLE_HACKRF)
GR_INCLUDE_SUBDIRECTORY(hackrf)
endif(ENABLE_HACKRF)
########################################################################
# Setup bladeRF component
########################################################################
GR_REGISTER_COMPONENT("nuand bladeRF" ENABLE_BLADERF LIBBLADERF_FOUND)
if(ENABLE_BLADERF)
GR_INCLUDE_SUBDIRECTORY(bladerf)
endif(ENABLE_BLADERF)
########################################################################
# Setup configuration file
########################################################################

@ -0,0 +1,40 @@
# Copyright 2013 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# GNU Radio is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
########################################################################
# This file included, use CMake directory variables
########################################################################
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${LIBBLADERF_INCLUDE_DIRS}
)
set(bladerf_srcs
${CMAKE_CURRENT_SOURCE_DIR}/bladerf_source_c.cc
${CMAKE_CURRENT_SOURCE_DIR}/bladerf_sink_c.cc
${CMAKE_CURRENT_SOURCE_DIR}/bladerf_common.cc
)
########################################################################
# Append gnuradio-osmosdr library sources
########################################################################
list(APPEND gr_osmosdr_srcs ${bladerf_srcs})
list(APPEND gr_osmosdr_libs ${LIBBLADERF_LIBRARIES})

@ -0,0 +1,168 @@
/* -*- c++ -*- */
/*
* Copyright 2013 Nuand LLC
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
*
* GNU Radio is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* GNU Radio is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Radio; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
/*
* config.h is generated by configure. It contains the results
* of probing for features, options etc. It should be the first
* file included in your .cc file.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <boost/lexical_cast.hpp>
#include <boost/assign.hpp>
#include <boost/foreach.hpp>
#include "bladerf_common.h"
#define BLADERF_FIFO_SIZE_ENV "BLADERF_SAMPLE_FIFO_SIZE"
using namespace boost::assign;
bladerf_common::bladerf_common() : running(true)
{
const char *env_fifo_size;
size_t fifo_size;
/* 1 Sample = i,q (2 int16_t's) */
this->raw_sample_buf = new int16_t[2 * BLADERF_SAMPLE_BLOCK_SIZE];
if (!raw_sample_buf) {
throw std::runtime_error( std::string(__FUNCTION__) +
" has failed to allocate a raw sample buffer!" );
}
env_fifo_size = getenv(BLADERF_FIFO_SIZE_ENV);
fifo_size = BLADERF_SAMPLE_FIFO_SIZE;
if (env_fifo_size != NULL) {
try {
fifo_size = boost::lexical_cast<size_t>(env_fifo_size);
} catch (const boost::bad_lexical_cast &e) {
std::cerr << "Warning: \"" << BLADERF_FIFO_SIZE_ENV
<< "\" is invalid" << "... defaulting to "
<< fifo_size;
}
if (fifo_size < BLADERF_SAMPLE_FIFO_MIN_SIZE) {
fifo_size = BLADERF_SAMPLE_FIFO_MIN_SIZE;
std::cerr << "Warning: \"" << BLADERF_FIFO_SIZE_ENV
<< "\" is too small" << "... defaulting to "
<< BLADERF_SAMPLE_FIFO_MIN_SIZE;
}
}
this->sample_fifo = new boost::circular_buffer<gr_complex>(fifo_size);
if (!this->sample_fifo)
throw std::runtime_error( std::string(__FUNCTION__) +
" has failed to allocate a sample FIFO!" );
}
bladerf_common::~bladerf_common()
{
delete[] this->raw_sample_buf;
delete this->sample_fifo;
}
void bladerf_common::setup_device()
{
gpio_write( this->dev, 0x57 ); /* enable LMS RX & TX, select lower band */
lms_spi_write( this->dev, 0x5a, 0xa0 ); /* polarity of the IQSel signal */
/* values are taken from LMS6002D FAQ 5.27 */
lms_spi_write( this->dev, 0x05, 0x3e ); /* enable the tx and rx modules */
lms_spi_write( this->dev, 0x47, 0x40 ); /* Improving Tx spurious emission performance */
lms_spi_write( this->dev, 0x59, 0x29 ); /* Improving ADC's performance */
lms_spi_write( this->dev, 0x64, 0x36 ); /* Common Mode Voltage For ADC's */
lms_spi_write( this->dev, 0x79, 0x37 ); /* Higher LNA Gain */
return;
}
osmosdr::freq_range_t bladerf_common::freq_range()
{
/* assuming the same for RX & TX */
return osmosdr::freq_range_t( 300e6, 3.8e9 );
}
osmosdr::meta_range_t bladerf_common::sample_rates()
{
/* assuming the same for RX & TX */
return osmosdr::meta_range_t( 160e3, 40e6, 1e6 );
}
osmosdr::freq_range_t bladerf_common::filter_bandwidths()
{
/* the same for RX & TX according to the datasheet */
osmosdr::freq_range_t bandwidths;
std::vector<double> half_bandwidths;
half_bandwidths += \
0.75, 0.875, 1.25, 1.375, 1.5, 1.92, 2.5,
2.75, 3, 3.5, 4.375, 5, 6, 7, 10, 14;
BOOST_FOREACH( double half_bws, half_bandwidths )
bandwidths += osmosdr::range_t( half_bws * 2.e6 );
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 dev(devices[i].path);
s << "bladerf=" << dev.substr(dev.find_first_of("01234567890")) << ","
<< "label='nuand bladeRF SN " << std::setfill('0') << std::setw(16)
<< devices[i].serial << "'";
ret.push_back(s.str());
}
bladerf_free_device_list(devices, n_devices);
}
return ret;
}
bool bladerf_common::is_running()
{
boost::shared_lock<boost::shared_mutex> lock(this->state_lock);
return this->running;
}
void bladerf_common::set_running(bool is_running)
{
boost::unique_lock<boost::shared_mutex> lock(this->state_lock);
this->running = is_running;
}

@ -0,0 +1,80 @@
/* -*- c++ -*- */
/*
* Copyright 2013 Nuand LLC
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
*
* GNU Radio is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* GNU Radio is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Radio; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDED_BLADERF_COMMON_H
#define INCLUDED_BLADERF_COMMON_H
#include <vector>
#include <string>
#include <boost/circular_buffer.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/shared_mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <gnuradio/gr_complex.h>
#include <libbladeRF.h>
#include "osmosdr/ranges.h"
/* We currently read/write 1024 samples (pairs of 16-bit signed ints) */
#define BLADERF_SAMPLE_BLOCK_SIZE (1024)
/*
* Default size of sample FIFO, in entries.
* This can be overridden by the environment variable BLADERF_SAMPLE_FIFO_SIZE.
*/
#ifndef BLADERF_SAMPLE_FIFO_SIZE
# define BLADERF_SAMPLE_FIFO_SIZE (2 * 1024 * 1024)
#endif
#define BLADERF_SAMPLE_FIFO_MIN_SIZE (3 * BLADERF_SAMPLE_BLOCK_SIZE)
class bladerf_common
{
public:
bladerf_common();
~bladerf_common();
protected:
void setup_device();
osmosdr::freq_range_t freq_range();
osmosdr::meta_range_t sample_rates();
osmosdr::freq_range_t filter_bandwidths();
static std::vector< std::string > devices();
bool is_running();
void set_running(bool is_running);
bladerf *dev;
int16_t *raw_sample_buf;
boost::circular_buffer<gr_complex> *sample_fifo;
boost::mutex sample_fifo_lock;
boost::condition_variable samples_available;
private:
bool running;
boost::shared_mutex state_lock;
};
#endif

@ -0,0 +1,566 @@
/* -*- c++ -*- */
/*
* Copyright 2013 Nuand LLC
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
*
* GNU Radio is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* GNU Radio is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Radio; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
/*
* config.h is generated by configure. It contains the results
* of probing for features, options etc. It should be the first
* file included in your .cc file.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <iostream>
#include <boost/assign.hpp>
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <gnuradio/io_signature.h>
#include "arg_helpers.h"
#include "bladerf_sink_c.h"
using namespace boost::assign;
/*
* Create a new instance of bladerf_source_c and return
* a boost shared_ptr. This is effectively the public constructor.
*/
bladerf_sink_c_sptr make_bladerf_sink_c (const std::string &args)
{
return gnuradio::get_initial_sptr(new bladerf_sink_c (args));
}
/*
* Specify constraints on number of input and output streams.
* This info is used to construct the input and output signatures
* (2nd & 3rd args to gr_block's constructor). The input and
* output signatures are used by the runtime system to
* check that a valid number and type of inputs and outputs
* are connected to this block. In this case, we accept
* only 0 input and 1 output.
*/
static const int MIN_IN = 1; // mininum number of input streams
static const int MAX_IN = 1; // maximum number of input streams
static const int MIN_OUT = 0; // minimum number of output streams
static const int MAX_OUT = 0; // maximum number of output streams
/*
* The private constructor
*/
bladerf_sink_c::bladerf_sink_c (const std::string &args)
: gr::sync_block ("bladerf_sink_c",
gr::io_signature::make (MIN_IN, MAX_IN, sizeof (gr_complex)),
gr::io_signature::make (MIN_OUT, MAX_OUT, sizeof (gr_complex)))
{
unsigned int device_number = 0;
std::string device_name;
dict_t dict = params_to_dict(args);
if (dict.count("bladerf"))
{
std::string value = dict["bladerf"];
if ( value.length() )
{
try {
device_number = boost::lexical_cast< unsigned int >( value );
} catch ( std::exception &ex ) {
throw std::runtime_error(
"Failed to use '" + value + "' as device number: " + ex.what());
}
}
}
device_name = boost::str(boost::format( "/dev/bladerf%d" ) % device_number);
/* Open a handle to the device */
this->dev = bladerf_open( device_name.c_str() );
if( NULL == this->dev ) {
throw std::runtime_error( std::string(__FUNCTION__) + " " +
"failed to open bladeRF device " + device_name );
}
if (dict.count("fpga"))
{
std::string fpga = dict["fpga"];
std::cerr << "Loading FPGA bitstream " << fpga << "..." << std::endl;
int ret = bladerf_load_fpga( this->dev, fpga.c_str() );
if ( ret != 0 )
std::cerr << "bladerf_load_fpga has returned with " << ret << std::endl;
else
std::cerr << "The FPGA bitstream has been successfully loaded." << std::endl;
}
if (dict.count("fw"))
{
std::string fw = dict["fw"];
std::cerr << "Flashing firmware image " << fw << "..., "
<< "DO NOT INTERRUPT!"
<< std::endl;
int ret = bladerf_flash_firmware( this->dev, fw.c_str() );
if ( ret != 0 )
std::cerr << "bladerf_flash_firmware has failed with " << ret << std::endl;
else
std::cerr << "The firmare has been successfully flashed, "
<< "please power cycle the bladeRF before using it."
<< std::endl;
}
std::cerr << "Using nuand LLC bladeRF #" << device_number;
u_int64_t serial;
if ( bladerf_get_serial( this->dev, &serial ) == 0 )
std::cerr << " SN " << std::setfill('0') << std::setw(16) << serial;
unsigned int major, minor;
if ( bladerf_get_fw_version( this->dev, &major, &minor) == 0 )
std::cerr << " FW v" << major << "." << minor;
if ( bladerf_get_fpga_version( this->dev, &major, &minor) == 0 )
std::cerr << " FPGA v" << major << "." << minor;
std::cerr << std::endl;
if ( bladerf_is_fpga_configured( this->dev ) != 1 )
{
std::cerr << "ERROR: The FPGA is not configured! "
<< "Use the device argument fpga=/path/to/the/bitstream.rbf to load it."
<< std::endl;
}
/* Set the range of VGA1, VGA1GAINT[7:0] */
this->vga1_range = osmosdr::gain_range_t( -35, -4, 1 );
/* Set the range of VGA2, VGA2GAIN[4:0] */
this->vga2_range = osmosdr::gain_range_t( 0, 25, 1 );
this->setup_device();
this->thread = gr::thread::thread(write_task_dispatch, this);
}
/*
* Our virtual destructor.
*/
bladerf_sink_c::~bladerf_sink_c ()
{
this->set_running(false);
this->thread.join();
/* Close the device */
bladerf_close( this->dev );
}
void bladerf_sink_c::write_task_dispatch(bladerf_sink_c *obj)
{
obj->write_task();
}
void bladerf_sink_c::write_task()
{
int i, n_samples_avail, n_samples;
int16_t *p;
gr_complex sample;
while ( this->is_running() )
{
{
/* Lock the circular buffer */
boost::unique_lock<boost::mutex> lock(this->sample_fifo_lock);
/* Check to make sure we have samples available */
n_samples_avail = this->sample_fifo->size();
while( n_samples_avail < BLADERF_SAMPLE_BLOCK_SIZE ) {
/* Wait until there is at least a block size of samples ready */
this->samples_available.wait(lock);
n_samples_avail = this->sample_fifo->size();
}
/* Pop samples from circular buffer, write samples to outgoing buffer */
int16_t *p = this->raw_sample_buf;
for( i = 0; i < BLADERF_SAMPLE_BLOCK_SIZE; ++i ) {
sample = this->sample_fifo->at(0);
this->sample_fifo->pop_front();
*p++ = 0xa000 | (int16_t)(real(sample)*2000);
*p++ = 0x5000 | (int16_t)(imag(sample)*2000);
}
} /* Give up the lock by leaving the scope ...*/
/* Notify that we've just popped some samples */
this->samples_available.notify_one();
/* Samples are available to write out */
n_samples = bladerf_send_c16(this->dev, this->raw_sample_buf,
BLADERF_SAMPLE_BLOCK_SIZE);
/* Check n_samples return value */
if( n_samples < 0 ) {
std::cerr << "Failed to write samples: "
<< bladerf_strerror(n_samples) << std::endl;
this->set_running(false);
} else {
if(n_samples != BLADERF_SAMPLE_BLOCK_SIZE) {
if(n_samples > BLADERF_SAMPLE_BLOCK_SIZE) {
std::cerr << "Warning: sent bloated sample block of "
<< n_samples << " samples!" << std::endl;
} else {
std::cerr << "Warning: sent truncated sample block of "
<< n_samples << " samples!" << std::endl;
}
}
}
}
}
int bladerf_sink_c::work( int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items )
{
int n_space_avail, to_copy, limit, i;
const gr_complex *in = (const gr_complex *) input_items[0];
if ( ! this->is_running() )
return WORK_DONE;
if( noutput_items >= 0 ) {
/* Total samples we want to process */
to_copy = noutput_items;
/* While there are still samples to copy out ... */
while( to_copy > 0 ) {
{
/* Acquire the circular buffer lock */
boost::unique_lock<boost::mutex> lock(this->sample_fifo_lock);
/* Check to see how much space is available */
n_space_avail = this->sample_fifo->capacity() - this->sample_fifo->size();
while (n_space_avail == 0) {
this->samples_available.wait(lock);
n_space_avail = this->sample_fifo->capacity() - this->sample_fifo->size();
}
/* Limit ourselves to either the number of output items ...
... or whatever space is available */
limit = (n_space_avail < noutput_items ? n_space_avail : noutput_items);
/* Consume! */
for( i = 0; i < limit; i++ ) {
this->sample_fifo->push_back(*in++);
}
/* Decrement the amount we need to copy */
to_copy -= limit;
} /* Unlock by leaving the scope */
/* Notify that we've just added some samples */
this->samples_available.notify_one();
}
}
return noutput_items;
}
std::vector<std::string> bladerf_sink_c::get_devices()
{
return bladerf_common::devices();
}
size_t bladerf_sink_c::get_num_channels()
{
/* We only support a single channel for each bladeRF */
return 1;
}
osmosdr::meta_range_t bladerf_sink_c::get_sample_rates()
{
return this->sample_rates();
}
double bladerf_sink_c::set_sample_rate(double rate)
{
int ret;
uint32_t actual;
/* Set the Si5338 to be 2x this sample rate */
/* Check to see if the sample rate is an integer */
if( (uint32_t)round(rate) == (uint32_t)rate )
{
ret = bladerf_set_sample_rate( this->dev, TX, (uint32_t)rate, &actual );
if( ret ) {
throw std::runtime_error( std::string(__FUNCTION__) + " " +
"has failed to set integer rate, error " +
boost::lexical_cast<std::string>(ret) );
}
} else {
/* TODO: Fractional sample rate */
ret = bladerf_set_sample_rate( this->dev, TX, (uint32_t)rate, &actual );
if( ret ) {
throw std::runtime_error( std::string(__FUNCTION__) + " " +
"has failed to set fractional rate, error " +
boost::lexical_cast<std::string>(ret) );
}
}
return get_sample_rate();
}
double bladerf_sink_c::get_sample_rate()
{
int ret;
unsigned int rate = 0;
ret = bladerf_get_sample_rate( this->dev, TX, &rate );
if( ret ) {
throw std::runtime_error( std::string(__FUNCTION__) + " " +
"has failed to get sample rate, error " +
boost::lexical_cast<std::string>(ret) );
}
return (double)rate;
}
osmosdr::freq_range_t bladerf_sink_c::get_freq_range( size_t chan )
{
return this->freq_range();
}
double bladerf_sink_c::set_center_freq( double freq, size_t chan )
{
int ret;
/* 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;
} else {
ret = bladerf_set_frequency( this->dev, TX, (uint32_t)freq );
if( ret ) {
throw std::runtime_error( std::string(__FUNCTION__) + " " +
"failed to set center frequency " +
boost::lexical_cast<std::string>(freq) +
", error " +
boost::lexical_cast<std::string>(ret) );
}
}
return get_center_freq( chan );
}
double bladerf_sink_c::get_center_freq( size_t chan )
{
uint32_t freq;
int ret;
ret = bladerf_get_frequency( this->dev, TX, &freq );
if( ret ) {
throw std::runtime_error( std::string(__FUNCTION__) + " " +
"failed to get center frequency, error " +
boost::lexical_cast<std::string>(ret) );
}
return (double)freq;
}
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( chan );
}
double bladerf_sink_c::get_freq_corr( size_t chan )
{
/* TODO: Return back the frequency correction in ppm */
return 0;
}
std::vector<std::string> bladerf_sink_c::get_gain_names( size_t chan )
{
std::vector< std::string > names;
names += "VGA1", "VGA2";
return names;
}
osmosdr::gain_range_t bladerf_sink_c::get_gain_range( size_t chan )
{
/* TODO: This is an overall system gain range. Given the VGA1 and VGA2
how much total gain can we have in the system */
return get_gain_range( "VGA2", chan ); /* we use only VGA2 here for now */
}
osmosdr::gain_range_t bladerf_sink_c::get_gain_range( const std::string & name, size_t chan )
{
osmosdr::gain_range_t range;
if( name == "VGA1" ) {
range = this->vga1_range;
} else if( name == "VGA2" ) {
range = this->vga2_range;
} else {
throw std::runtime_error( std::string(__FUNCTION__) + " " +
"requested an invalid gain element " + name );
}
return range;
}
bool bladerf_sink_c::set_gain_mode( bool automatic, size_t chan )
{
return false;
}
bool bladerf_sink_c::get_gain_mode( size_t chan )
{
return false;
}
double bladerf_sink_c::set_gain( double gain, size_t chan )
{
return set_gain( gain, "VGA2", chan ); /* we use only VGA2 here for now */
}
double bladerf_sink_c::set_gain( double gain, const std::string & name, size_t chan)
{
int ret = 0;
if( name == "VGA1" ) {
ret = bladerf_set_txvga1( this->dev, (int)gain );
} else if( name == "VGA2" ) {
ret = bladerf_set_txvga2( this->dev, (int)gain );
} else {
throw std::runtime_error( std::string(__FUNCTION__) + " " +
"requested to set the gain "
"of an unknown gain element " + name );
}
/* Check for errors */
if( ret ) {
throw std::runtime_error( std::string(__FUNCTION__) + " " +
"could not set " + name + " gain, error " +
boost::lexical_cast<std::string>(ret) );
}
return get_gain( name, chan );
}
double bladerf_sink_c::get_gain( size_t chan )
{
return get_gain( "VGA2", chan ); /* we use only VGA2 here for now */
}
double bladerf_sink_c::get_gain( const std::string & name, size_t chan )
{
int g;
int ret = 0;
if( name == "VGA1" ) {
ret = bladerf_get_txvga1( this->dev, &g );
} else if( name == "VGA2" ) {
ret = bladerf_get_txvga2( this->dev, &g );
} else {
throw std::runtime_error( std::string(__FUNCTION__) + " " +
"requested to get the gain "
"of an unknown gain element " + name );
}
/* Check for errors */
if( ret ) {
throw std::runtime_error( std::string(__FUNCTION__) + " " +
"could not get " + name + " gain, error " +
boost::lexical_cast<std::string>(ret) );
}
return (double)g;
}
double bladerf_sink_c::set_bb_gain( double gain, size_t chan )
{
/* for TX, only VGA1 is in the BB path */
osmosdr::gain_range_t bb_gains = get_gain_range( "VGA1", chan );
double clip_gain = bb_gains.clip( gain, true );
gain = set_gain( clip_gain, "VGA1", chan );
return gain;
}
std::vector< std::string > bladerf_sink_c::get_antennas( size_t chan )
{
std::vector< std::string > antennas;
antennas += get_antenna( chan );
return antennas;
}
std::string bladerf_sink_c::set_antenna( const std::string & antenna, size_t chan )
{
return get_antenna( chan );
}
std::string bladerf_sink_c::get_antenna( size_t chan )
{
/* We only have a single transmit antenna here */
return "TX";
}
double bladerf_sink_c::set_bandwidth( double bandwidth, size_t chan )
{
int ret;
uint32_t actual;
ret = bladerf_set_bandwidth( this->dev, TX, (uint32_t)bandwidth, &actual );
if( ret ) {
throw std::runtime_error( std::string(__FUNCTION__) + " " +
"could not set bandwidth, error " +
boost::lexical_cast<std::string>(ret) );
}
return this->get_bandwidth();
}
double bladerf_sink_c::get_bandwidth( size_t chan )
{
uint32_t bandwidth;
int ret;
ret = bladerf_get_bandwidth( this->dev, TX, &bandwidth );
if( ret ) {
throw std::runtime_error( std::string(__FUNCTION__) + " " +
"could not get bandwidth, error " +
boost::lexical_cast<std::string>(ret) );
}
return (double)bandwidth;
}
osmosdr::freq_range_t bladerf_sink_c::get_bandwidth_range( size_t chan )
{
return this->filter_bandwidths();
}

@ -0,0 +1,120 @@
/* -*- c++ -*- */
/*
* Copyright 2013 Nuand LLC
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
*
* GNU Radio is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* GNU Radio is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Radio; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDED_BLADERF_SINK_C_H
#define INCLUDED_BLADERF_SINK_C_H
#include <gnuradio/thread/thread.h>
#include <gnuradio/block.h>
#include <gnuradio/sync_block.h>
#include <libbladeRF.h>
#include "osmosdr/ranges.h"
#include "sink_iface.h"
#include "bladerf_common.h"
class bladerf_sink_c;
/*
* We use boost::shared_ptr's instead of raw pointers for all access
* to gr_blocks (and many other data structures). The shared_ptr gets
* us transparent reference counting, which greatly simplifies storage
* management issues. This is especially helpful in our hybrid
* C++ / Python system.
*
* See http://www.boost.org/libs/smart_ptr/smart_ptr.htm
*
* As a convention, the _sptr suffix indicates a boost::shared_ptr
*/
typedef boost::shared_ptr<bladerf_sink_c> bladerf_sink_c_sptr;
/*!
* \brief Return a shared_ptr to a new instance of bladerf_sink_c.
*
* To avoid accidental use of raw pointers, bladerf_sink_c's
* constructor is private. make_bladerf_sink_c is the public
* interface for creating new instances.
*/
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
{
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
public:
~bladerf_sink_c (); // public destructor
int work( int noutput_items,
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 );
osmosdr::freq_range_t get_freq_range( size_t chan = 0 );
double set_center_freq( double freq, size_t chan = 0 );
double get_center_freq( size_t chan = 0 );
double set_freq_corr( double ppm, size_t chan = 0 );
double get_freq_corr( size_t chan = 0 );
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 );
double set_bb_gain( double gain, size_t chan = 0 );
std::vector< std::string > get_antennas( size_t chan = 0 );
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
std::string get_antenna( size_t chan = 0 );
double set_bandwidth( double bandwidth, size_t chan = 0 );
double get_bandwidth( size_t chan = 0 );
osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 );
private:
static void write_task_dispatch(bladerf_sink_c *obj);
void write_task();
gr::thread::thread thread;
osmosdr::gain_range_t vga1_range;
osmosdr::gain_range_t vga2_range;
};
#endif /* INCLUDED_BLADERF_SINK_C_H */

@ -0,0 +1,584 @@
/* -*- c++ -*- */
/*
* Copyright 2013 Nuand LLC
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
*
* GNU Radio is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* GNU Radio is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Radio; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
/*
* config.h is generated by configure. It contains the results
* of probing for features, options etc. It should be the first
* file included in your .cc file.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <iostream>
#include <boost/assign.hpp>
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <gnuradio/io_signature.h>
#include "arg_helpers.h"
#include "bladerf_source_c.h"
using namespace boost::assign;
/*
* Create a new instance of bladerf_source_c and return
* a boost shared_ptr. This is effectively the public constructor.
*/
bladerf_source_c_sptr make_bladerf_source_c (const std::string &args)
{
return gnuradio::get_initial_sptr(new bladerf_source_c (args));
}
/*
* Specify constraints on number of input and output streams.
* This info is used to construct the input and output signatures
* (2nd & 3rd args to gr_block's constructor). The input and
* output signatures are used by the runtime system to
* check that a valid number and type of inputs and outputs
* are connected to this block. In this case, we accept
* only 0 input and 1 output.
*/
static const int MIN_IN = 0; // mininum number of input streams
static const int MAX_IN = 0; // maximum number of input streams
static const int MIN_OUT = 1; // minimum number of output streams
static const int MAX_OUT = 1; // maximum number of output streams
/*
* The private constructor
*/
bladerf_source_c::bladerf_source_c (const std::string &args)
: gr::sync_block ("bladerf_source_c",
gr::io_signature::make (MIN_IN, MAX_IN, sizeof (gr_complex)),
gr::io_signature::make (MIN_OUT, MAX_OUT, sizeof (gr_complex)))
{
unsigned int device_number = 0;
std::string device_name;
dict_t dict = params_to_dict(args);
if (dict.count("bladerf"))
{
std::string value = dict["bladerf"];
if ( value.length() )
{
try {
device_number = boost::lexical_cast< unsigned int >( value );
} catch ( std::exception &ex ) {
throw std::runtime_error(
"Failed to use '" + value + "' as device number: " + ex.what());
}
}
}
device_name = boost::str(boost::format( "/dev/bladerf%d" ) % device_number);
/* Open a handle to the device */
this->dev = bladerf_open( device_name.c_str() );
if( NULL == this->dev ) {
throw std::runtime_error( std::string(__FUNCTION__) + " " +
"failed to open bladeRF device " + device_name );
}
if (dict.count("fpga"))
{
std::string fpga = dict["fpga"];
std::cerr << "Loading FPGA bitstream " << fpga << "..." << std::endl;
int ret = bladerf_load_fpga( this->dev, fpga.c_str() );
if ( ret != 0 )
std::cerr << "bladerf_load_fpga has returned with " << ret << std::endl;
else
std::cerr << "The FPGA bitstream has been successfully loaded." << std::endl;
}
if (dict.count("fw"))
{
std::string fw = dict["fw"];
std::cerr << "Flashing firmware image " << fw << "..., "
<< "DO NOT INTERRUPT!"
<< std::endl;
int ret = bladerf_flash_firmware( this->dev, fw.c_str() );
if ( ret != 0 )
std::cerr << "bladerf_flash_firmware has failed with " << ret << std::endl;
else
std::cerr << "The firmare has been successfully flashed, "
<< "please power cycle the bladeRF before using it."
<< std::endl;
}
std::cerr << "Using nuand LLC bladeRF #" << device_number;
u_int64_t serial;
if ( bladerf_get_serial( this->dev, &serial ) == 0 )
std::cerr << " SN " << std::setfill('0') << std::setw(16) << serial;
unsigned int major, minor;
if ( bladerf_get_fw_version( this->dev, &major, &minor) == 0 )
std::cerr << " FW v" << major << "." << minor;
if ( bladerf_get_fpga_version( this->dev, &major, &minor) == 0 )
std::cerr << " FPGA v" << major << "." << minor;
std::cerr << std::endl;
if ( bladerf_is_fpga_configured( this->dev ) != 1 )
{
std::cerr << "ERROR: The FPGA is not configured! "
<< "Use the device argument fpga=/path/to/the/bitstream.rbf to load it."
<< std::endl;
}
/* Set the range of LNA, G_LNA_RXFE[1:0] */
this->lna_range = osmosdr::gain_range_t( 0, 6, 3 );
/* Set the range of VGA1, RFB_TIA_RXFE[6:0], nonlinear mapping done inside the lib */
this->vga1_range = osmosdr::gain_range_t( 5, 30, 1 );
/* Set the range of VGA2 VGA2GAIN[4:0], not recommended to be used above 30dB */
this->vga2_range = osmosdr::gain_range_t( 0, 60, 3 );
this->setup_device();
this->thread = gr::thread::thread(read_task_dispatch, this);
}
/*
* Our virtual destructor.
*/
bladerf_source_c::~bladerf_source_c ()
{
this->set_running(false);
this->thread.join();
/* Close the device */
bladerf_close( this->dev );
}
void bladerf_source_c::read_task_dispatch(bladerf_source_c *obj)
{
obj->read_task();
}
void bladerf_source_c::read_task()
{
int16_t si, sq, *next_val;
ssize_t n_samples;
size_t n_avail, to_copy;
while ( this->is_running() )
{
n_samples = bladerf_read_c16(this->dev, this->raw_sample_buf,
BLADERF_SAMPLE_BLOCK_SIZE);
if (n_samples < 0) {
std::cerr << "Failed to read samples: "
<< bladerf_strerror(n_samples) << std::endl;
this->set_running(false);
} else {
if (n_samples != BLADERF_SAMPLE_BLOCK_SIZE) {
if (n_samples > BLADERF_SAMPLE_BLOCK_SIZE) {
std::cerr << "Warning: received bloated sample block of "
<< n_samples << " bytes!"<< std::endl;
} else {
std::cerr << "Warning: received truncated sample block of "
<< n_samples << " bytes!"<< std::endl;
}
} else {
//std::cerr << "+" << std::flush;
next_val = this->raw_sample_buf;
this->sample_fifo_lock.lock();
n_avail = this->sample_fifo->capacity() - this->sample_fifo->size();
to_copy = (n_avail < (size_t)n_samples ? n_avail : (size_t)n_samples);
for (size_t i = 0; i < to_copy; ++i) {
si = *next_val++ & 0xfff;
sq = *next_val++ & 0xfff;
/* Sign extend the 12-bit IQ values, if needed */
if( si & 0x800 ) si |= 0xf000;
if( sq & 0x800 ) sq |= 0xf000;
gr_complex sample((float)si * (1.0f/2048.0f),
(float)sq * (1.0f/2048.0f));
this->sample_fifo->push_back(sample);
}
this->sample_fifo_lock.unlock();
/* We have made some new samples available to the consumer in work() */
if (to_copy) {
this->samples_available.notify_one();
}
/* Indicate overrun, if neccesary */
if (to_copy < (size_t)n_samples) {