aboutsummaryrefslogtreecommitdiffstats
path: root/lib/bladerf
diff options
context:
space:
mode:
authorDimitri Stolnikov <horiz0n@gmx.net>2013-07-20 18:14:20 +0200
committerDimitri Stolnikov <horiz0n@gmx.net>2013-07-21 11:59:22 +0200
commite5f7b28093c10f05d272bcf12c6c4b6583af7021 (patch)
treeb82370206b2fe91aae99e78f7c4d67e47ed649b2 /lib/bladerf
parent0edfcfcba0ddf0fd5002dab70331d75a376bcf3f (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.txt40
-rw-r--r--lib/bladerf/bladerf_common.cc168
-rw-r--r--lib/bladerf/bladerf_common.h80
-rw-r--r--lib/bladerf/bladerf_sink_c.cc566
-rw-r--r--lib/bladerf/bladerf_sink_c.h120
-rw-r--r--lib/bladerf/bladerf_source_c.cc584
-rw-r--r--lib/bladerf/bladerf_source_c.h121
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 */