diff --git a/AUTHORS b/AUTHORS index e5aafba..8bcfb94 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,4 @@ Dimitri Stolnikov Steve Markgraf Hoernchen +Nuand LLC folks diff --git a/CMakeLists.txt b/CMakeLists.txt index 49dd315..644cc88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/README b/README index c384cc4..34e3397 100644 --- a/README +++ b/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 diff --git a/cmake/Modules/FindLibbladeRF.cmake b/cmake/Modules/FindLibbladeRF.cmake new file mode 100644 index 0000000..36da9fb --- /dev/null +++ b/cmake/Modules/FindLibbladeRF.cmake @@ -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) diff --git a/grc/gen_osmosdr_blocks.py b/grc/gen_osmosdr_blocks.py index a579aa6..dd12cd3 100644 --- a/grc/gen_osmosdr_blocks.py +++ b/grc/gen_osmosdr_blocks.py @@ -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 diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index ffbf7a8..ad0ec96 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -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 ######################################################################## diff --git a/lib/bladerf/CMakeLists.txt b/lib/bladerf/CMakeLists.txt new file mode 100644 index 0000000..23f7e3f --- /dev/null +++ b/lib/bladerf/CMakeLists.txt @@ -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}) + diff --git a/lib/bladerf/bladerf_common.cc b/lib/bladerf/bladerf_common.cc new file mode 100644 index 0000000..d12dd33 --- /dev/null +++ b/lib/bladerf/bladerf_common.cc @@ -0,0 +1,168 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Nuand LLC + * Copyright 2013 Dimitri Stolnikov + * + * 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 +#include +#include +#include + +#include +#include +#include + +#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(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(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 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 lock(this->state_lock); + return this->running; +} + +void bladerf_common::set_running(bool is_running) +{ + boost::unique_lock lock(this->state_lock); + this->running = is_running; +} diff --git a/lib/bladerf/bladerf_common.h b/lib/bladerf/bladerf_common.h new file mode 100644 index 0000000..ce3464c --- /dev/null +++ b/lib/bladerf/bladerf_common.h @@ -0,0 +1,80 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Nuand LLC + * Copyright 2013 Dimitri Stolnikov + * + * 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 +#include + +#include +#include +#include +#include + +#include + +#include + +#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 *sample_fifo; + boost::mutex sample_fifo_lock; + boost::condition_variable samples_available; +private: + bool running; + boost::shared_mutex state_lock; +}; + +#endif diff --git a/lib/bladerf/bladerf_sink_c.cc b/lib/bladerf/bladerf_sink_c.cc new file mode 100644 index 0000000..7958a70 --- /dev/null +++ b/lib/bladerf/bladerf_sink_c.cc @@ -0,0 +1,566 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Nuand LLC + * Copyright 2013 Dimitri Stolnikov + * + * 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 + +#include +#include +#include + +#include + +#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 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 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 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(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(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(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(freq) + + ", error " + + boost::lexical_cast(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(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 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(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(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(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(ret) ); + } + + return (double)bandwidth; +} + +osmosdr::freq_range_t bladerf_sink_c::get_bandwidth_range( size_t chan ) +{ + return this->filter_bandwidths(); +} diff --git a/lib/bladerf/bladerf_sink_c.h b/lib/bladerf/bladerf_sink_c.h new file mode 100644 index 0000000..8db1a8e --- /dev/null +++ b/lib/bladerf/bladerf_sink_c.h @@ -0,0 +1,120 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Nuand LLC + * Copyright 2013 Dimitri Stolnikov + * + * 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 +#include +#include + +#include + +#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_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 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 */ diff --git a/lib/bladerf/bladerf_source_c.cc b/lib/bladerf/bladerf_source_c.cc new file mode 100644 index 0000000..5d95fbb --- /dev/null +++ b/lib/bladerf/bladerf_source_c.cc @@ -0,0 +1,584 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Nuand LLC + * Copyright 2013 Dimitri Stolnikov + * + * 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 + +#include +#include +#include + +#include + +#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 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 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(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(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 " + + boost::lexical_cast(ret) ); + } + + return (double)rate; +} + +osmosdr::freq_range_t bladerf_source_c::get_freq_range( size_t chan ) +{ + return this->freq_range(); +} + +double bladerf_source_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, RX, (uint32_t)freq ); + if( ret ) { + throw std::runtime_error( std::string(__FUNCTION__) + " " + + "failed to set center frequency " + + boost::lexical_cast(freq) + + ", error " + + boost::lexical_cast(ret) ); + } + } + + return get_center_freq( chan ); +} + +double bladerf_source_c::get_center_freq( size_t chan ) +{ + uint32_t freq; + int ret; + + ret = bladerf_get_frequency( this->dev, RX, &freq ); + if( ret ) { + throw std::runtime_error( std::string(__FUNCTION__) + " " + + "failed to get center frequency, error " + + boost::lexical_cast(ret) ); + } + + return (double)freq; +} + +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( chan ); +} + +double bladerf_source_c::get_freq_corr( size_t chan ) +{ + /* TODO: Return back the frequency correction in ppm */ + return 0; +} + +std::vector bladerf_source_c::get_gain_names( size_t chan ) +{ + std::vector< std::string > names; + + names += "LNA", "VGA1", "VGA2"; + + return names; +} + +osmosdr::gain_range_t bladerf_source_c::get_gain_range( size_t chan ) +{ + /* TODO: This is an overall system gain range. Given the LNA, VGA1 and VGA2 + how much total gain can we have in the system */ + return get_gain_range( "LNA", chan ); /* we use only LNA here for now */ +} + +osmosdr::gain_range_t bladerf_source_c::get_gain_range( const std::string & name, size_t chan ) +{ + osmosdr::gain_range_t range; + + if( name == "LNA" ) { + range = this->lna_range; + } else 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_source_c::set_gain_mode( bool automatic, size_t chan ) +{ + /* TODO: Implement AGC in the FPGA */ + return false; +} + +bool bladerf_source_c::get_gain_mode( size_t chan ) +{ + /* TODO: Read back AGC mode */ + return false; +} + +double bladerf_source_c::set_gain( double gain, size_t chan ) +{ + /* TODO: This is an overall system gain that has to be set */ + return set_gain( gain, "LNA", chan ); /* we use only LNA here for now */ +} + +double bladerf_source_c::set_gain( double gain, const std::string & name, size_t chan ) +{ + int ret = 0; + + if( name == "LNA" ) { + bladerf_lna_gain g; + if( gain == 0.0 ) { + g = LNA_BYPASS; + } else if( gain == 3.0 ) { + g = LNA_MID; + } else if( gain == 6.0 ) { + g = LNA_MAX; + } else { + std::cerr << "Invalid LNA gain requested: " << gain << ", " + << "setting to LNA_MAX (6dB)" << std::endl; + g = LNA_MAX; + } + ret = bladerf_set_lna_gain( this->dev, g ); + } else if( name == "VGA1" ) { + ret = bladerf_set_rxvga1( this->dev, (int)gain ); + } else if( name == "VGA2" ) { + ret = bladerf_set_rxvga2( 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(ret) ); + } + + return get_gain( name, chan ); +} + +double bladerf_source_c::get_gain( size_t chan ) +{ + /* TODO: This is an overall system gain that has to be set */ + return get_gain( "LNA", chan ); /* we use only LNA here for now */ +} + +double bladerf_source_c::get_gain( const std::string & name, size_t chan ) +{ + int g; + int ret = 0; + + if( name == "LNA" ) { + bladerf_lna_gain lna_g; + ret = bladerf_get_lna_gain( this->dev, &lna_g ); + g = lna_g == LNA_BYPASS ? 0 : lna_g == LNA_MID ? 3 : 6; + } else if( name == "VGA1" ) { + ret = bladerf_get_rxvga1( this->dev, &g ); + } else if( name == "VGA2" ) { + ret = bladerf_get_rxvga2( 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(ret) ); + } + + return (double)g; +} + +double bladerf_source_c::set_bb_gain( double gain, size_t chan ) +{ + /* TODO: for RX, we should combine VGA1 & VGA2 which both are in BB path */ + osmosdr::gain_range_t bb_gains = get_gain_range( "VGA2", chan ); + + double clip_gain = bb_gains.clip( gain, true ); + gain = set_gain( clip_gain, "VGA2", chan ); + + return gain; +} + +std::vector< std::string > bladerf_source_c::get_antennas( size_t chan ) +{ + std::vector< std::string > antennas; + + antennas += get_antenna( chan ); + + return antennas; +} + +std::string bladerf_source_c::set_antenna( const std::string & antenna, size_t chan ) +{ + return get_antenna( chan ); +} + +std::string bladerf_source_c::get_antenna( size_t chan ) +{ + /* We only have a single receive antenna here */ + return "RX"; +} + +double bladerf_source_c::set_bandwidth( double bandwidth, size_t chan ) +{ + int ret; + uint32_t actual; + + ret = bladerf_set_bandwidth( this->dev, RX, (uint32_t)bandwidth, &actual ); + if( ret ) { + throw std::runtime_error( std::string(__FUNCTION__) + " " + + "could not set bandwidth, error " + + boost::lexical_cast(ret) ); + } + + return this->get_bandwidth(); +} + +double bladerf_source_c::get_bandwidth( size_t chan ) +{ + uint32_t bandwidth; + int ret; + + ret = bladerf_get_bandwidth( this->dev, RX, &bandwidth ); + if( ret ) { + throw std::runtime_error( std::string(__FUNCTION__) + " " + + "could not get bandwidth, error " + + boost::lexical_cast(ret) ); + } + + return (double)bandwidth; +} + +osmosdr::freq_range_t bladerf_source_c::get_bandwidth_range( size_t chan ) +{ + return this->filter_bandwidths(); +} diff --git a/lib/bladerf/bladerf_source_c.h b/lib/bladerf/bladerf_source_c.h new file mode 100644 index 0000000..8074ccf --- /dev/null +++ b/lib/bladerf/bladerf_source_c.h @@ -0,0 +1,121 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Nuand LLC + * Copyright 2013 Dimitri Stolnikov + * + * 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_SOURCE_C_H +#define INCLUDED_BLADERF_SOURCE_C_H + +#include +#include +#include + +#include + +#include "osmosdr/ranges.h" +#include "source_iface.h" +#include "bladerf_common.h" + +class bladerf_source_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_source_c_sptr; + +/*! + * \brief Return a shared_ptr to a new instance of bladerf_source_c. + * + * To avoid accidental use of raw pointers, bladerf_source_c's + * constructor is private. bladerf_make_source_c is the public + * interface for creating new instances. + */ +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 +{ +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 + +public: + ~bladerf_source_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 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 read_task_dispatch(bladerf_source_c *obj); + void read_task(); + + gr::thread::thread thread; + osmosdr::gain_range_t lna_range; + osmosdr::gain_range_t vga1_range; + osmosdr::gain_range_t vga2_range; +}; + +#endif /* INCLUDED_BLADERF_SOURCE_C_H */ diff --git a/lib/config.h.in b/lib/config.h.in index 1f9b147..5ef2233 100644 --- a/lib/config.h.in +++ b/lib/config.h.in @@ -12,5 +12,6 @@ #cmakedefine ENABLE_UHD #cmakedefine ENABLE_MIRI #cmakedefine ENABLE_HACKRF +#cmakedefine ENABLE_BLADERF #endif // CONFIG_H_IN diff --git a/lib/device.cc b/lib/device.cc index 0126053..108dd36 100644 --- a/lib/device.cc +++ b/lib/device.cc @@ -62,6 +62,10 @@ #include #endif +#ifdef ENABLE_BLADERF +#include +#endif + #include "arg_helpers.h" using namespace osmosdr; @@ -133,11 +137,14 @@ devices_t device::find(const device_t &hint) BOOST_FOREACH( std::string dev, miri_source_c::get_devices() ) devices.push_back( device_t(dev) ); #endif +#ifdef ENABLE_BLADERF + BOOST_FOREACH( std::string dev, bladerf_source_c::get_devices() ) + devices.push_back( device_t(dev) ); +#endif #ifdef ENABLE_HACKRF BOOST_FOREACH( std::string dev, hackrf_source_c::get_devices() ) - devices.push_back( device_t(dev) ); + devices.push_back( device_t(dev) ); #endif - /* software-only sources should be appended at the very end, * hopefully resulting in hardware sources to be shown first * in a graphical interface etc... */ diff --git a/lib/sink_impl.cc b/lib/sink_impl.cc index ef85b05..40decfc 100644 --- a/lib/sink_impl.cc +++ b/lib/sink_impl.cc @@ -38,6 +38,9 @@ #ifdef ENABLE_HACKRF #include "hackrf_sink_c.h" #endif +#ifdef ENABLE_BLADERF +#include "bladerf_sink_c.h" +#endif #include "arg_helpers.h" #include "sink_impl.h" @@ -77,9 +80,11 @@ sink_impl::sink_impl( const std::string &args ) #ifdef ENABLE_HACKRF dev_types.push_back("hackrf"); #endif - +#ifdef ENABLE_BLADERF + dev_types.push_back("bladerf"); +#endif std::cerr << "gr-osmosdr " - << GR_OSMOSDR_VERSION " (" GR_OSMOSDR_LIBVER ") " + << GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") " << "gnuradio " << gr::version() << std::endl; std::cerr << "built-in sink types: "; BOOST_FOREACH(std::string dev_type, dev_types) @@ -103,10 +108,15 @@ sink_impl::sink_impl( const std::string &args ) BOOST_FOREACH( std::string dev, uhd_sink_c::get_devices() ) dev_list.push_back( dev ); #endif +#ifdef ENABLE_BLADERF + BOOST_FOREACH( std::string dev, bladerf_sink_c::get_devices() ) + dev_list.push_back( dev ); +#endif #ifdef ENABLE_HACKRF BOOST_FOREACH( std::string dev, hackrf_sink_c::get_devices() ) dev_list.push_back( dev ); #endif + // std::cerr << std::endl; // BOOST_FOREACH( std::string dev, dev_list ) // std::cerr << "'" << dev << "'" << std::endl; @@ -141,6 +151,12 @@ sink_impl::sink_impl( const std::string &args ) block = sink; iface = sink.get(); } #endif +#ifdef ENABLE_BLADERF + if ( dict.count("bladerf") ) { + bladerf_sink_c_sptr sink = make_bladerf_sink_c( arg ); + block = sink; iface = sink.get(); + } +#endif if ( iface != NULL && long(block.get()) != 0 ) { _devs.push_back( iface ); diff --git a/lib/source_impl.cc b/lib/source_impl.cc index 3ffc890..9bd7df5 100644 --- a/lib/source_impl.cc +++ b/lib/source_impl.cc @@ -64,6 +64,10 @@ #include #endif +#ifdef ENABLE_BLADERF +#include +#endif + #include "arg_helpers.h" #include "source_impl.h" @@ -120,9 +124,11 @@ source_impl::source_impl( const std::string &args ) #ifdef ENABLE_HACKRF dev_types.push_back("hackrf"); #endif - +#ifdef ENABLE_BLADERF + dev_types.push_back("bladerf"); +#endif std::cerr << "gr-osmosdr " - << GR_OSMOSDR_VERSION " (" GR_OSMOSDR_LIBVER ") " + << GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") " << "gnuradio " << gr::version() << std::endl; std::cerr << "built-in source types: "; BOOST_FOREACH(std::string dev_type, dev_types) @@ -162,10 +168,15 @@ source_impl::source_impl( const std::string &args ) BOOST_FOREACH( std::string dev, miri_source_c::get_devices() ) dev_list.push_back( dev ); #endif +#ifdef ENABLE_BLADERF + BOOST_FOREACH( std::string dev, bladerf_source_c::get_devices() ) + dev_list.push_back( dev ); +#endif #ifdef ENABLE_HACKRF BOOST_FOREACH( std::string dev, hackrf_source_c::get_devices() ) dev_list.push_back( dev ); #endif + // std::cerr << std::endl; // BOOST_FOREACH( std::string dev, dev_list ) // std::cerr << "'" << dev << "'" << std::endl; @@ -244,6 +255,13 @@ source_impl::source_impl( const std::string &args ) } #endif +#ifdef ENABLE_BLADERF + if ( dict.count("bladerf") ) { + bladerf_source_c_sptr src = make_bladerf_source_c( arg ); + block = src; iface = src.get(); + } +#endif + if ( iface != NULL && long(block.get()) != 0 ) { _devs.push_back( iface );