forked from sdr/gr-osmosdr
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
parent
0edfcfcba0
commit
e5f7b28093
1
AUTHORS
1
AUTHORS
|
@ -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)
|
||||
|
|
1
README
1
README
|
@ -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) {
|
||||
std::cerr << "O" << std::flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Main work function, pull samples from the driver */
|
||||
int bladerf_source_c::work( int noutput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items )
|
||||
{
|
||||
int n_samples_avail;
|
||||
|
||||
if ( ! this->is_running() )
|
||||
return WORK_DONE;
|
||||
|
||||
if( noutput_items >= 0 ) {
|
||||
gr_complex *out = (gr_complex *)output_items[0];
|
||||
boost::unique_lock<boost::mutex> lock(this->sample_fifo_lock);
|
||||
|
||||
/* Wait until we have the requested number of samples */
|
||||
n_samples_avail = this->sample_fifo->size();
|
||||
|
||||
while (n_samples_avail < noutput_items) {
|
||||
this->samples_available.wait(lock);
|
||||
n_samples_avail = this->sample_fifo->size();
|
||||
}
|
||||
|
||||
for(int i = 0; i < noutput_items; ++i) {
|
||||
out[i] = this->sample_fifo->at(0);
|
||||
this->sample_fifo->pop_front();
|
||||
}
|
||||
|
||||
//std::cerr << "-" << std::flush;
|
||||
}
|
||||
|
||||
return noutput_items;
|
||||
}
|
||||
|
||||
std::vector<std::string> bladerf_source_c::get_devices()
|
||||
{
|
||||
return bladerf_common::devices();
|
||||
}
|
||||
|
||||
size_t bladerf_source_c::get_num_channels()
|
||||
{
|
||||
/* We only support a single channel for each bladeRF */
|
||||
return 1;
|
||||
}
|
||||
|
||||
osmosdr::meta_range_t bladerf_source_c::get_sample_rates()
|
||||
{
|
||||
return this->sample_rates();
|
||||
}
|
||||
|
||||
double bladerf_source_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, RX, (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, RX, (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_source_c::get_sample_rate()
|
||||
{
|
||||
int ret;
|
||||
unsigned int rate = 0;
|
||||
|
||||
ret = bladerf_get_sample_rate( this->dev, RX, &rate );
|
||||
if( ret ) {
|
||||
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||
"has failed to get sample rate, error " +
|
||||