diff options
author | Dimitri Stolnikov <horiz0n@gmx.net> | 2013-07-20 18:14:20 +0200 |
---|---|---|
committer | Dimitri Stolnikov <horiz0n@gmx.net> | 2013-07-21 11:59:22 +0200 |
commit | e5f7b28093c10f05d272bcf12c6c4b6583af7021 (patch) | |
tree | b82370206b2fe91aae99e78f7c4d67e47ed649b2 /lib/bladerf | |
parent | 0edfcfcba0ddf0fd5002dab70331d75a376bcf3f (diff) |
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.
Diffstat (limited to 'lib/bladerf')
-rw-r--r-- | lib/bladerf/CMakeLists.txt | 40 | ||||
-rw-r--r-- | lib/bladerf/bladerf_common.cc | 168 | ||||
-rw-r--r-- | lib/bladerf/bladerf_common.h | 80 | ||||
-rw-r--r-- | lib/bladerf/bladerf_sink_c.cc | 566 | ||||
-rw-r--r-- | lib/bladerf/bladerf_sink_c.h | 120 | ||||
-rw-r--r-- | lib/bladerf/bladerf_source_c.cc | 584 | ||||
-rw-r--r-- | lib/bladerf/bladerf_source_c.h | 121 |
7 files changed, 1679 insertions, 0 deletions
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 <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; +} 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 <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 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 <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(); +} 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 <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 */ 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 <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 " + + boost::lexical_cast<std::string>(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<std::string>(freq) + + ", error " + + boost::lexical_cast<std::string>(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<std::string>(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<std::string> 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<std::string>(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<std::string>(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<std::string>(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<std::string>(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 <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_SOURCE_C_H +#define INCLUDED_BLADERF_SOURCE_C_H + +#include <gnuradio/thread/thread.h> +#include <gnuradio/block.h> +#include <gnuradio/sync_block.h> + +#include <libbladeRF.h> + +#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> 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<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 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 */ |