Add support for FreeSRP

This patch adds support for both receiving and transmitting using
the FreeSRP. More information on the FreeSRP can be found at:

http://freesrp.org

The gr-osmosdr blocks added make use of libfreesrp, the library
required for interfacing with this device. The libfreesrp source
code is freely available at

https://github.com/freesrp/libfreesrp

Usage example:

osmocom_fft -a "freesrp"
soapy_support
Lukas Lao Beyer 2017-04-07 23:08:01 -04:00 committed by Dimitri Stolnikov
parent e9dde9afd7
commit 5ecfa255d2
21 changed files with 2716 additions and 0 deletions

View File

@ -6,3 +6,4 @@ Josh Blum
SDRplay Ltd.
Pavel Demin
Marcus Müller
Lukas Lao Beyer

View File

@ -172,6 +172,7 @@ find_package(LibAIRSPY)
find_package(Volk)
find_package(LibbladeRF)
find_package(SoapySDR NO_MODULE)
find_package(LibFreeSRP)
find_package(Doxygen)
if(NOT GNURADIO_RUNTIME_FOUND)

1
README
View File

@ -17,6 +17,7 @@ as well supports:
* Ettus USRP Devices through Ettus UHD library
* Fairwaves UmTRX through Fairwaves' fork of UHD
* Red Pitaya SDR transceiver (http://bazaar.redpitaya.com)
* FreeSRP through libfreesrp
By using the OsmoSDR block you can take advantage of a common software api in
your application(s) independent of the underlying radio hardware.

View File

@ -0,0 +1,27 @@
if(NOT LIBFREESRP_FOUND)
pkg_check_modules (LIBFREESRP_PKG libfreesrp)
find_path(LIBFREESRP_INCLUDE_DIRS NAMES freesrp.hpp
PATHS
${LIBFREESRP_PKG_INCLUDE_DIRS}
/usr/include
/usr/local/include
)
find_library(LIBFREESRP_LIBRARIES NAMES freesrp
PATHS
${LIBFREESRP_PKG_LIBRARY_DIRS}
/usr/lib
/usr/local/lib
)
if(LIBFREESRP_INCLUDE_DIRS AND LIBFREESRP_LIBRARIES)
set(LIBFREESRP_FOUND TRUE CACHE INTERNAL "libfreesrp found")
message(STATUS "Found libfreesrp: ${LIBFREESRP_INCLUDE_DIRS}, ${LIBFREESRP_LIBRARIES}")
else(LIBFREESRP_INCLUDE_DIRS AND LIBFREESRP_LIBRARIES)
set(LIBFREESRP_FOUND FALSE CACHE INTERNAL "libfreesrp found")
message(STATUS "libfreesrp not found.")
endif(LIBFREESRP_INCLUDE_DIRS AND LIBFREESRP_LIBRARIES)
mark_as_advanced(LIBFREESRP_LIBRARIES LIBFREESRP_INCLUDE_DIRS)
endif(NOT LIBFREESRP_FOUND)

View File

@ -229,6 +229,7 @@ While primarily being developed for the OsmoSDR hardware, this block as well sup
* Ettus USRP Devices through Ettus UHD library
* Fairwaves UmTRX through Fairwaves' fork of UHD
* Red Pitaya SDR transceiver (http://bazaar.redpitaya.com)
* FreeSRP through libfreesrp library
By using the osmocom $sourk block you can take advantage of a common software api in your application(s) independent of the underlying radio hardware.
@ -264,6 +265,7 @@ Lines ending with ... mean it's possible to bind devices together by specifying
file='/path/to/your file',rate=1e6[,freq=100e6][,append=true][,throttle=true] ...
#end if
redpitaya=192.168.1.100[:1001]
freesrp=0[,fx3='path/to/fx3.img',fpga='path/to/fpga.bin',loopback]
hackrf=0[,buffers=32][,bias=0|1][,bias_tx=0|1]
bladerf=0[,tamer=internal|external|external_1pps][,smb=25e6]
uhd[,serial=...][,lo_offset=0][,mcr=52e6][,nchan=2][,subdev='\\\\'B:0 A:0\\\\''] ...

View File

@ -240,6 +240,14 @@ if(ENABLE_REDPITAYA)
GR_INCLUDE_SUBDIRECTORY(redpitaya)
endif(ENABLE_REDPITAYA)
########################################################################
# Setup FreeSRP component
########################################################################
GR_REGISTER_COMPONENT("FreeSRP support" ENABLE_FREESRP LIBFREESRP_FOUND)
if(ENABLE_FREESRP)
GR_INCLUDE_SUBDIRECTORY(freesrp)
endif(ENABLE_FREESRP)
########################################################################
# Setup configuration file
########################################################################

View File

@ -18,6 +18,7 @@
#cmakedefine ENABLE_AIRSPY
#cmakedefine ENABLE_SOAPY
#cmakedefine ENABLE_REDPITAYA
#cmakedefine ENABLE_FREESRP
//provide NAN define for MSVC older than VC12
#if defined(_MSC_VER) && (_MSC_VER < 1800)

View File

@ -86,6 +86,10 @@
#include <redpitaya_source_c.h>
#endif
#ifdef ENABLE_FREESRP
#include <freesrp_source_c.h>
#endif
#include "arg_helpers.h"
using namespace osmosdr;
@ -182,6 +186,10 @@ devices_t device::find(const device_t &hint)
BOOST_FOREACH( std::string dev, airspy_source_c::get_devices() )
devices.push_back( device_t(dev) );
#endif
#ifdef ENABLE_FREESRP
BOOST_FOREACH( std::string dev, freesrp_source_c::get_devices() )
devices.push_back( device_t(dev) );
#endif
#ifdef ENABLE_SOAPY
BOOST_FOREACH( std::string dev, soapy_source_c::get_devices() )
devices.push_back( device_t(dev) );

View File

@ -0,0 +1,39 @@
# Copyright 2012 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}
${LIBFREESRP_INCLUDE_DIRS}
)
set(freesrp_srcs
${CMAKE_CURRENT_SOURCE_DIR}/freesrp_common.cc
${CMAKE_CURRENT_SOURCE_DIR}/freesrp_source_c.cc
${CMAKE_CURRENT_SOURCE_DIR}/freesrp_sink_c.cc
)
########################################################################
# Append gnuradio-osmosdr library sources
########################################################################
list(APPEND gr_osmosdr_srcs ${freesrp_srcs})
list(APPEND gr_osmosdr_libs ${LIBFREESRP_LIBRARIES})

View File

@ -0,0 +1,199 @@
#include "freesrp_common.h"
#include <cstdlib>
#include <boost/make_shared.hpp>
#include <boost/assign.hpp>
#include <arg_helpers.h>
using namespace FreeSRP;
using namespace std;
using namespace boost::assign;
boost::shared_ptr<::FreeSRP::FreeSRP> freesrp_common::_srp;
freesrp_common::freesrp_common(const string &args)
{
dict_t dict = params_to_dict(args);
if(!_srp)
{
try
{
string serial = "";
if(dict.count("freesrp"))
{
serial = dict["freesrp"];
}
if(dict.count("fx3"))
{
if(Util::find_fx3())
{
// Upload firmware to FX3
string firmware_path = string(getenv("HOME")) + "/.freesrp/fx3.img";
if(dict["fx3"].length() > 0)
{
firmware_path = dict["fx3"];
}
Util::find_fx3(true, firmware_path);
cout << "FX3 programmed with '" << firmware_path << "'" << endl;
// Give FX3 time to re-enumerate
this_thread::sleep_for(chrono::milliseconds(600));
}
else
{
cout << "No FX3 in bootloader mode found" << endl;
}
}
_srp.reset(new ::FreeSRP::FreeSRP(serial));
if(dict.count("fpga") || !_srp->fpga_loaded())
{
string bitstream_path = string(getenv("HOME")) + "/.freesrp/fpga.bin";
if(dict["fpga"].length() > 0)
{
bitstream_path = dict["fpga"];
}
fpga_status stat = _srp->load_fpga(bitstream_path);
switch(stat)
{
case FPGA_CONFIG_ERROR:
throw runtime_error("Could not load FPGA configuration!");
case FPGA_CONFIG_SKIPPED:
cout << "FPGA already configured. Restart the FreeSRP to load a new bitstream." << endl;
break;
case FPGA_CONFIG_DONE:
cout << "FPGA configured with '" << bitstream_path << "'" << endl;
break;
}
}
cout << "Connected to FreeSRP" << endl;
if(dict.count("loopback"))
{
response res = _srp->send_cmd({SET_LOOPBACK_EN, 1});
if(res.error == CMD_OK)
{
cout << "AD9364 in loopback mode" << endl;
}
else
{
throw runtime_error("Could not put AD9364 into loopback mode!");
}
}
else
{
response res = _srp->send_cmd({SET_LOOPBACK_EN, 0});
if(res.error != CMD_OK)
{
throw runtime_error("Error disabling AD9364 loopback mode!");
}
}
if(dict.count("ignore_overflow"))
{
_ignore_overflow = true;
}
else
{
_ignore_overflow = false;
}
}
catch(const runtime_error& e)
{
cerr << "FreeSRP Error: " << e.what() << endl;
throw runtime_error(e.what());
}
}
}
vector<string> freesrp_common::get_devices()
{
vector<string> devices;
try
{
::FreeSRP::FreeSRP srp;
string str;
str = "freesrp=0,label='FreeSRP'";
devices.push_back(str);
}
catch(const ConnectionError &err)
{
// No FreeSRP found.
}
return devices;
}
size_t freesrp_common::get_num_channels( void )
{
return 1;
}
osmosdr::meta_range_t freesrp_common::get_sample_rates( void )
{
osmosdr::meta_range_t range;
// Any sample rate between 1e6 and 61.44e6 can be requested.
// This list of some integer values is used instead of
// range += osmosdr::range_t(1e6, 61.44e6);
// because SoapyOsmo seems to handle the range object differently.
range += osmosdr::range_t(1e6);
range += osmosdr::range_t(8e6);
range += osmosdr::range_t(16e6);
range += osmosdr::range_t(20e6);
range += osmosdr::range_t(40e6);
range += osmosdr::range_t(50e6);
range += osmosdr::range_t(61.44e6);
return range;
}
osmosdr::freq_range_t freesrp_common::get_freq_range(size_t chan)
{
osmosdr::meta_range_t freq_ranges;
freq_ranges.push_back(osmosdr::range_t(7e7, 6e9, 2.4));
return freq_ranges;
}
osmosdr::freq_range_t freesrp_common::get_bandwidth_range(size_t chan)
{
osmosdr::meta_range_t range;
//range += osmosdr::range_t(2e5, 56e6);
range += osmosdr::range_t(2e5);
range += osmosdr::range_t(1e6);
range += osmosdr::range_t(8e6);
range += osmosdr::range_t(16e6);
range += osmosdr::range_t(20e6);
range += osmosdr::range_t(40e6);
range += osmosdr::range_t(50e6);
range += osmosdr::range_t(56e6);
return range;
}
double freesrp_common::set_freq_corr( double ppm, size_t chan )
{
// TODO: Set DCXO tuning
return 0;
}
double freesrp_common::get_freq_corr( size_t chan )
{
// TODO: Get DCXO tuning
return 0;
}

View File

@ -0,0 +1,29 @@
#ifndef INCLUDED_FREESRP_COMMON_H
#define INCLUDED_FREESRP_COMMON_H
#include <vector>
#include <string>
#include "osmosdr/ranges.h"
#include <freesrp.hpp>
class freesrp_common
{
protected:
freesrp_common(const std::string &args);
public:
static std::vector<std::string> get_devices();
size_t get_num_channels( void );
osmosdr::meta_range_t get_sample_rates( void );
osmosdr::freq_range_t get_freq_range( size_t chan = 0 );
osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 );
double set_freq_corr( double ppm, size_t chan = 0 );
double get_freq_corr( size_t chan = 0 );
protected:
static boost::shared_ptr<::FreeSRP::FreeSRP> _srp;
bool _ignore_overflow = false;
};
#endif

View File

@ -0,0 +1,280 @@
#include "freesrp_sink_c.h"
using namespace FreeSRP;
using namespace std;
freesrp_sink_c_sptr make_freesrp_sink_c (const string &args)
{
return gnuradio::get_initial_sptr(new freesrp_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 1 input and 0 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
freesrp_sink_c::freesrp_sink_c (const string & args) : gr::sync_block("freesrp_sink_c",
gr::io_signature::make (MIN_IN, MAX_IN, sizeof (gr_complex)),
gr::io_signature::make (MIN_OUT, MAX_OUT, sizeof (gr_complex))),
freesrp_common(args)
{
if(_srp == nullptr)
{
throw runtime_error("FreeSRP not initialized!");
}
}
bool freesrp_sink_c::start()
{
response res = _srp->send_cmd({SET_DATAPATH_EN, 1});
if(res.error != CMD_OK)
{
return false;
}
_srp->start_tx(std::bind(&freesrp_sink_c::freesrp_tx_callback, this, std::placeholders::_1));
return true;
}
bool freesrp_sink_c::stop()
{
_srp->send_cmd({SET_DATAPATH_EN, 0});
_srp->stop_tx();
return true;
}
void freesrp_sink_c::freesrp_tx_callback(vector<sample>& samples)
{
unique_lock<std::mutex> lk(_buf_mut);
for(sample &s : samples)
{
if(!_buf_queue.try_dequeue(s))
{
s.i = 0;
s.q = 0;
}
else
{
_buf_available_space++;
}
}
_buf_cond.notify_one();
}
int freesrp_sink_c::work(int noutput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items)
{
const gr_complex *in = (const gr_complex *) input_items[0];
unique_lock<std::mutex> lk(_buf_mut);
// Wait until enough space is available
while(_buf_available_space < (unsigned int) noutput_items)
{
_buf_cond.wait(lk);
}
for(int i = 0; i < noutput_items; ++i)
{
sample s;
s.i = (int16_t) (real(in[i]) * 2047.0f);
s.q = (int16_t) (imag(in[i]) * 2047.0f);
if(!_buf_queue.try_enqueue(s))
{
throw runtime_error("Failed to add sample to buffer. This should never happen. Available space reported to be " + to_string(_buf_available_space) + " samples, noutput_items=" + to_string(noutput_items) + ", i=" + to_string(i));
}
else
{
_buf_available_space--;
}
}
return noutput_items;
}
double freesrp_sink_c::set_sample_rate( double rate )
{
command cmd = _srp->make_command(SET_TX_SAMP_FREQ, rate);
response r = _srp->send_cmd(cmd);
if(r.error != CMD_OK)
{
cerr << "Could not set TX sample rate, error: " << r.error << endl;
return 0;
}
else
{
return static_cast<double>(r.param);
}
}
double freesrp_sink_c::get_sample_rate( void )
{
response r = _srp->send_cmd({GET_TX_SAMP_FREQ, 0});
if(r.error != CMD_OK)
{
cerr << "Could not get TX sample rate, error: " << r.error << endl;
return 0;
}
else
{
return r.param;
}
}
double freesrp_sink_c::set_center_freq( double freq, size_t chan )
{
command cmd = _srp->make_command(SET_TX_LO_FREQ, freq);
response r = _srp->send_cmd(cmd);
if(r.error != CMD_OK)
{
cerr << "Could not set TX LO frequency, error: " << r.error << endl;
return 0;
}
else
{
return static_cast<double>(r.param);
}
}
double freesrp_sink_c::get_center_freq( size_t chan )
{
response r = _srp->send_cmd({GET_TX_LO_FREQ, 0});
if(r.error != CMD_OK)
{
cerr << "Could not get TX LO frequency, error: " << r.error << endl;
return 0;
}
else
{
return static_cast<double>(r.param);
}
}
vector<string> freesrp_sink_c::get_gain_names( size_t chan )
{
vector<string> names;
names.push_back("TX_RF");
return names;
}
osmosdr::gain_range_t freesrp_sink_c::get_gain_range(size_t chan)
{
osmosdr::meta_range_t gain_ranges;
gain_ranges.push_back(osmosdr::range_t(0, 89.75, 0.25));
return gain_ranges;
}
osmosdr::gain_range_t freesrp_sink_c::get_gain_range(const string& name, size_t chan)
{
return get_gain_range(chan);
}
double freesrp_sink_c::set_gain(double gain, size_t chan)
{
gain = get_gain_range().clip(gain);
double atten = 89.75 - gain;
command cmd = _srp->make_command(SET_TX_ATTENUATION, atten * 1000);
response r = _srp->send_cmd(cmd);
if(r.error != CMD_OK)
{
cerr << "Could not set TX attenuation, error: " << r.error << endl;
return 0;
}
else
{
return 89.75 - (((double) r.param) / 1000.0);
}
}
double freesrp_sink_c::set_gain(double gain, const string& name, size_t chan)
{
return set_gain(gain, chan);
}
double freesrp_sink_c::get_gain(size_t chan)
{
response r = _srp->send_cmd({GET_TX_ATTENUATION, 0});
if(r.error != CMD_OK)
{
cerr << "Could not get TX RF attenuation, error: " << r.error << endl;
return 0;
}
else
{
return 89.75 - (((double) r.param) / 1000.0);
}
}
double freesrp_sink_c::get_gain(const string& name, size_t chan)
{
return get_gain(chan);
}
double freesrp_sink_c::set_bb_gain(double gain, size_t chan)
{
return set_gain(gain, chan);
}
vector<string> freesrp_sink_c::get_antennas(size_t chan)
{
vector<string> antennas;
antennas.push_back(get_antenna(chan));
return antennas;
}
string freesrp_sink_c::set_antenna(const string& antenna, size_t chan)
{
return get_antenna(chan);
}
string freesrp_sink_c::get_antenna(size_t chan)
{
return "TX";
}
double freesrp_sink_c::set_bandwidth(double bandwidth, size_t chan)
{
command cmd = _srp->make_command(SET_TX_RF_BANDWIDTH, bandwidth);
response r = _srp->send_cmd(cmd);
if(r.error != CMD_OK)
{
cerr << "Could not set TX RF bandwidth, error: " << r.error << endl;
return 0;
}
else
{
return static_cast<double>(r.param);
}
}
double freesrp_sink_c::get_bandwidth(size_t chan)
{
response r = _srp->send_cmd({GET_TX_RF_BANDWIDTH, 0});
if(r.error != CMD_OK)
{
cerr << "Could not get TX RF bandwidth, error: " << r.error << endl;
return 0;
}
else
{
return r.param;
}
}

View File

@ -0,0 +1,130 @@
/* -*- c++ -*- */
/*
* Copyright 2015 Lukas Lao Beyer
* 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_FREESRP_SINK_C_H
#define INCLUDED_FREESRP_SINK_C_H
#include <gnuradio/thread/thread.h>
#include <gnuradio/block.h>
#include <gnuradio/sync_block.h>
#include "osmosdr/ranges.h"
#include "sink_iface.h"
#include "freesrp_common.h"
#include "readerwriterqueue/readerwriterqueue.h"
#include <mutex>
#include <condition_variable>
#include <freesrp.hpp>
class freesrp_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<freesrp_sink_c> freesrp_sink_c_sptr;
/*!
* \brief Return a shared_ptr to a new instance of freesrp_sink_c.
*
* To avoid accidental use of raw pointers, freesrp_sink_c's
* constructor is private. make_freesrp_sink_c is the public
* interface for creating new instances.
*/
freesrp_sink_c_sptr make_freesrp_sink_c (const std::string & args = "");
class freesrp_sink_c :
public gr::sync_block,
public sink_iface,
public freesrp_common
{
private:
// The friend declaration allows freesrp_make_sink_c to
// access the private constructor.
friend freesrp_sink_c_sptr make_freesrp_sink_c (const std::string & args);
freesrp_sink_c (const std::string & args); // private constructor
public:
// From freesrp_common:
static std::vector<std::string> get_devices() { return freesrp_common::get_devices(); };
size_t get_num_channels( void ) { return freesrp_common::get_num_channels(); }
osmosdr::meta_range_t get_sample_rates( void ) { return freesrp_common::get_sample_rates(); }
osmosdr::freq_range_t get_freq_range( size_t chan = 0 ) { return freesrp_common::get_freq_range(chan); }
osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 ) { return freesrp_common::get_bandwidth_range(chan); }
double set_freq_corr( double ppm, size_t chan = 0 ) { return freesrp_common::set_freq_corr(ppm, chan); }
double get_freq_corr( size_t chan = 0 ) { return freesrp_common::get_freq_corr(chan); }
bool start();
bool stop();
int work( int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items );
double set_sample_rate( double rate );
double get_sample_rate( void );
double set_center_freq( double freq, size_t chan = 0 );
double get_center_freq( 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 );
//TODO: implement this: bool set_gain_mode( bool automatic, size_t chan = 0 );
//TODO: implement this: 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 );
private:
void freesrp_tx_callback(std::vector<::FreeSRP::sample> &samples);
bool _running = false;
std::mutex _buf_mut{};
std::condition_variable _buf_cond{};
size_t _buf_available_space = FREESRP_RX_TX_QUEUE_SIZE;
moodycamel::ReaderWriterQueue<::FreeSRP::sample> _buf_queue{FREESRP_RX_TX_QUEUE_SIZE};
};
#endif /* INCLUDED_FREESRP_SINK_C_H */

View File

@ -0,0 +1,341 @@
#include "freesrp_source_c.h"
using namespace FreeSRP;
using namespace std;
freesrp_source_c_sptr make_freesrp_source_c (const string &args)
{
return gnuradio::get_initial_sptr(new freesrp_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
freesrp_source_c::freesrp_source_c (const string & args) : gr::sync_block ("freesrp_source_c",
gr::io_signature::make (MIN_IN, MAX_IN, sizeof (gr_complex)),
gr::io_signature::make (MIN_OUT, MAX_OUT, sizeof (gr_complex))),
freesrp_common(args)
{
if(_srp == nullptr)
{
throw runtime_error("FreeSRP not initialized!");
}
}
bool freesrp_source_c::start()
{
response res = _srp->send_cmd({SET_DATAPATH_EN, 1});
if(res.error != CMD_OK)
{
return false;
}
_srp->start_rx(std::bind(&freesrp_source_c::freesrp_rx_callback, this, std::placeholders::_1));
_running = true;
return true;
}
bool freesrp_source_c::stop()
{
_srp->send_cmd({SET_DATAPATH_EN, 0});
_srp->stop_rx();
_running = false;
return true;
}
void freesrp_source_c::freesrp_rx_callback(const vector<sample> &samples)
{
unique_lock<std::mutex> lk(_buf_mut);
for(const sample &s : samples)
{
if(!_buf_queue.try_enqueue(s))
{
if(!_ignore_overflow)
{
throw runtime_error("RX buffer overflow");
}
}
else
{
_buf_num_samples++;
}
}
_buf_cond.notify_one();
}
int freesrp_source_c::work(int noutput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items)
{
gr_complex *out = static_cast<gr_complex *>(output_items[0]);
unique_lock<std::mutex> lk(_buf_mut);
if(!_running)
{
return WORK_DONE;
}
// Wait until enough samples collected
while(_buf_num_samples < (unsigned int) noutput_items)
{
_buf_cond.wait(lk);
}
for(int i = 0; i < noutput_items; ++i)
{
sample s;
if(!_buf_queue.try_dequeue(s))
{
// This should not be happening
throw runtime_error("Failed to get sample from buffer. This should never happen. Number of available samples reported to be " + to_string(_buf_num_samples) + ", noutput_items=" + to_string(noutput_items) + ", i=" + to_string(i));
}
else
{
_buf_num_samples--;
}
out[i] = gr_complex(((float) s.i) / 2048.0f, ((float) s.q) / 2048.0f);
}
return noutput_items;
}
double freesrp_source_c::set_sample_rate( double rate )
{
command cmd = _srp->make_command(SET_RX_SAMP_FREQ, rate);
response r = _srp->send_cmd(cmd);
if(r.error != CMD_OK)
{
cerr << "Could not set RX sample rate, error: " << r.error << endl;
return 0;
}
else
{
return static_cast<double>(r.param);
}
}
double freesrp_source_c::get_sample_rate( void )
{
response r = _srp->send_cmd({GET_RX_SAMP_FREQ, 0});
if(r.error != CMD_OK)
{
cerr << "Could not get RX sample rate, error: " << r.error << endl;
return 0;
}
else
{
return static_cast<double>(r.param);
}
}
double freesrp_source_c::set_center_freq( double freq, size_t chan )
{
command cmd = _srp->make_command(SET_RX_LO_FREQ, freq);
response r = _srp->send_cmd(cmd);
if(r.error != CMD_OK)
{
cerr << "Could not set RX LO frequency, error: " << r.error << endl;
return 0;
}
else
{
return static_cast<double>(r.param);
}
}
double freesrp_source_c::get_center_freq( size_t chan )
{
response r = _srp->send_cmd({GET_RX_LO_FREQ, 0});
if(r.error != CMD_OK)
{
cerr << "Could not get RX LO frequency, error: " << r.error << endl;
return 0;
}
else
{
return static_cast<double>(r.param);
}
}
vector<string> freesrp_source_c::get_gain_names( size_t chan )
{
vector<string> names;
names.push_back("RF");
return names;
}
osmosdr::gain_range_t freesrp_source_c::get_gain_range(size_t chan)
{
osmosdr::meta_range_t gain_ranges;
gain_ranges.push_back(osmosdr::range_t(0, 74, 1));
return gain_ranges;
}
bool freesrp_source_c::set_gain_mode( bool automatic, size_t chan )
{
uint8_t gc_mode = RF_GAIN_SLOWATTACK_AGC;
if(!automatic)
{
gc_mode = RF_GAIN_MGC;
}
command cmd = _srp->make_command(SET_RX_GC_MODE, gc_mode);
response r = _srp->send_cmd(cmd);
if(r.error != CMD_OK)
{
cerr << "Could not set RX RF gain control mode, error: " << r.error << endl;
return false;
}
else
{
return r.param != RF_GAIN_MGC;
}
}
bool freesrp_source_c::get_gain_mode( size_t chan )
{
response r = _srp->send_cmd({GET_RX_GC_MODE, 0});
if(r.error != CMD_OK)
{
cerr << "Could not get RX RF gain control mode, error: " << r.error << endl;
return false;
}
else
{
return r.param != RF_GAIN_MGC;
}
}
osmosdr::gain_range_t freesrp_source_c::get_gain_range(const string& name, size_t chan)
{
return get_gain_range(chan);
}
double freesrp_source_c::set_gain(double gain, size_t chan)
{
gain = get_gain_range().clip(gain);
command cmd = _srp->make_command(SET_RX_RF_GAIN, gain);
response r = _srp->send_cmd(cmd);
if(r.error != CMD_OK)
{
cerr << "Could not set RX RF gain, error: " << r.error << endl;
return 0;
}
else
{
return r.param;
}
}
double freesrp_source_c::set_gain(double gain, const string& name, size_t chan)
{
if(name == "RF")
{
return set_gain(gain, chan);
}
else
{
return 0;
}
}
double freesrp_source_c::get_gain(size_t chan)
{
response r = _srp->send_cmd({GET_RX_RF_GAIN, 0});
if(r.error != CMD_OK)
{
cerr << "Could not get RX RF gain, error: " << r.error << endl;
return 0;
}
else
{
return (static_cast<double>(r.param));
}
}
double freesrp_source_c::get_gain(const string& name, size_t chan)
{
if(name == "RF")
{
return get_gain(chan);
}
else
{
return 0;
}
}
double freesrp_source_c::set_bb_gain(double gain, size_t chan)
{
return set_gain(gain, chan);
}
vector<string> freesrp_source_c::get_antennas(size_t chan)
{
vector<string> antennas;
antennas.push_back(get_antenna(chan));
return antennas;
}
string freesrp_source_c::set_antenna(const string& antenna, size_t chan)
{
return get_antenna(chan);
}
string freesrp_source_c::get_antenna(size_t chan)
{
return "RX";
}
double freesrp_source_c::set_bandwidth(double bandwidth, size_t chan)
{
command cmd = _srp->make_command(SET_RX_RF_BANDWIDTH, bandwidth);
response r = _srp->send_cmd(cmd);
if(r.error != CMD_OK)
{
cerr << "Could not set RX RF bandwidth, error: " << r.error << endl;
return 0;
}
else
{
return static_cast<double>(r.param);
}
}
double freesrp_source_c::get_bandwidth(size_t chan)
{
response r = _srp->send_cmd({GET_RX_RF_BANDWIDTH, 0});
if(r.error != CMD_OK)
{
cerr << "Could not get RX RF bandwidth, error: " << r.error << endl;
return 0;
}
else
{
return static_cast<double>(r.param);
}
}

View File

@ -0,0 +1,131 @@
/* -*- c++ -*- */
/*
* Copyright 2015 Lukas Lao Beyer
* 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_FREESRP_SOURCE_C_H
#define INCLUDED_FREESRP_SOURCE_C_H
#include <gnuradio/thread/thread.h>
#include <gnuradio/block.h>
#include <gnuradio/sync_block.h>
#include "osmosdr/ranges.h"
#include "source_iface.h"
#include "freesrp_common.h"
#include "readerwriterqueue/readerwriterqueue.h"
#include <freesrp.hpp>
#include <mutex>
#include <condition_variable>
class freesrp_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<freesrp_source_c> freesrp_source_c_sptr;
/*!
* \brief Return a shared_ptr to a new instance of freesrp_source_c.
*
* To avoid accidental use of raw pointers, freesrp_source_c's
* constructor is private. freesrp_make_source_c is the public
* interface for creating new instances.
*/
freesrp_source_c_sptr make_freesrp_source_c (const std::string & args = "");
class freesrp_source_c :
public gr::sync_block,
public source_iface,
public freesrp_common
{
private:
// The friend declaration allows freesrp_make_source_c to
// access the private constructor.
friend freesrp_source_c_sptr make_freesrp_source_c (const std::string & args);
freesrp_source_c (const std::string & args); // private constructor
public:
// From freesrp_common:
static std::vector<std::string> get_devices() { return freesrp_common::get_devices(); };
size_t get_num_channels( void ) { return freesrp_common::get_num_channels(); }
osmosdr::meta_range_t get_sample_rates( void ) { return freesrp_common::get_sample_rates(); }
osmosdr::freq_range_t get_freq_range( size_t chan = 0 ) { return freesrp_common::get_freq_range(chan); }
osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 ) { return freesrp_common::get_bandwidth_range(chan); }
double set_freq_corr( double ppm, size_t chan = 0 ) { return freesrp_common::set_freq_corr(ppm, chan); }
double get_freq_corr( size_t chan = 0 ) { return freesrp_common::get_freq_corr(chan); }
bool start();
bool stop();
int work( int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items );
double set_sample_rate( double rate );
double get_sample_rate( void );
double set_center_freq( double freq, size_t chan = 0 );
double get_center_freq( 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 );
private:
void freesrp_rx_callback(const std::vector<FreeSRP::sample> &samples);
bool _running = false;
std::mutex _buf_mut{};
std::condition_variable _buf_cond{};
size_t _buf_num_samples = 0;
moodycamel::ReaderWriterQueue<FreeSRP::sample> _buf_queue{FREESRP_RX_TX_QUEUE_SIZE};
};
#endif /* INCLUDED_FREESRP_SOURCE_C_H */

View File

@ -0,0 +1,28 @@
This license applies to all the code in this repository except that written by third
parties, namely the files in benchmarks/ext, which have their own licenses, and Jeff
Preshing's semaphore implementation (used in the blocking queue) which has a zlib
license (embedded in atomicops.h).
Simplified BSD License:
Copyright (c) 2013-2015, Cameron Desrochers
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,114 @@
# A single-producer, single-consumer lock-free queue for C++
This mini-repository has my very own implementation of a lock-free queue (that I designed from scratch) for C++.
It only supports a two-thread use case (one consuming, and one producing). The threads can't switch roles, though
you could use this queue completely from a single thread if you wish (but that would sort of defeat the purpose!).
Note: If you need a general purpse multi-producer, multi-consumer lock free queue, I have [one of those too][mpmc].
## Features
- [Blazing fast][benchmarks]
- Compatible with C++11 (supports moving objects instead of making copies)
- Fully generic (templated container of any type) -- just like `std::queue`, you never need to allocate memory for elements yourself
(which saves you the hassle of writing a lock-free memory manager to hold the elements you're queueing)
- Allocates memory up front, in contiguous blocks
- Provides a `try_enqueue` method which is guaranteed never to allocate memory (the queue starts with an initial capacity)
- Also provides an `enqueue` method which can dynamically grow the size of the queue as needed
- Also provides a blocking version with `wait_dequeue`
- Completely "wait-free" (no compare-and-swap loop). Enqueue and dequeue are always O(1) (not counting memory allocation)
- On x86, the memory barriers compile down to no-ops, meaning enqueue and dequeue are just a simple series of loads and stores (and branches)
## Use
Simply drop the readerwriterqueue.h and atomicops.h files into your source code and include them :-)
A modern compiler is required (MSVC2010+, GCC 4.7+, ICC 13+, or any C++11 compliant compiler should work).
Note: If you're using GCC, you really do need GCC 4.7 or above -- [4.6 has a bug][gcc46bug] that prevents the atomic fence primitives
from working correctly.
Example:
```cpp
using namespace moodycamel;
ReaderWriterQueue<int> q(100); // Reserve space for at least 100 elements up front
q.enqueue(17); // Will allocate memory if the queue is full
bool succeeded = q.try_enqueue(18); // Will only succeed if the queue has an empty slot (never allocates)
assert(succeeded);
int number;
succeeded = q.try_dequeue(number); // Returns false if the queue was empty
assert(succeeded && number == 17);
// You can also peek at the front item of the queue (consumer only)
int* front = q.peek();
assert(*front == 18);
succeeded = q.try_dequeue(number);
assert(succeeded && number == 18);
front = q.peek();
assert(front == nullptr); // Returns nullptr if the queue was empty
```
The blocking version has the exact same API, with the addition of a `wait_dequeue` method:
```cpp
BlockingReaderWriterQueue<int> q;
std::thread reader([&]() {
int item;
for (int i = 0; i != 100; ++i) {
q.wait_dequeue(item);
}
});
std::thread writer([&]() {
for (int i = 0; i != 100; ++i) {
q.enqueue(i);
}
});
writer.join();
reader.join();
assert(q.size_approx() == 0);
```
Note that `wait_dequeue` will block indefinitely while the queue is empty; this
means care must be taken to only call `wait_dequeue` if you're sure another element
will come along eventually, or if the queue has a static lifetime. This is because
destroying the queue while a thread is waiting on it will invoke undefined behaviour.
## Disclaimers
The queue should only be used on platforms where aligned integer and pointer access is atomic; fortunately, that
includes all modern processors (e.g. x86/x86-64, ARM, and PowerPC). *Not* for use with a DEC Alpha processor (which has very weak memory ordering) :-)
Note that it's only been tested on x86(-64); if someone has access to other processors I'd love to run some tests on
anything that's not x86-based.
Finally, I am not an expert. This is my first foray into lock-free programming, and though I'm confident in the code,
it's possible that there are bugs despite the effort I put into designing and testing this data structure.
Use this code at your own risk; in particular, lock-free programming is a patent minefield, and this code may very
well violate a pending patent (I haven't looked). It's worth noting that I came up with this algorithm and
implementation from scratch, independent of any existing lock-free queues.
## More info
See the [LICENSE.md][license] file for the license (simplified BSD).
My [blog post][blog] introduces the context that led to this code, and may be of interest if you're curious
about lock-free programming.
[blog]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++
[license]: LICENSE.md
[benchmarks]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++#benchmarks
[gcc46bug]: http://stackoverflow.com/questions/16429669/stdatomic-thread-fence-has-undefined-reference
[mpmc]: https://github.com/cameron314/concurrentqueue

View File

@ -0,0 +1,577 @@
// ©2013-2015 Cameron Desrochers.
// Distributed under the simplified BSD license (see the license file that
// should have come with this header).
// Uses Jeff Preshing's semaphore implementation (under the terms of its