forked from sdr/gr-osmosdr
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
parent
e9dde9afd7
commit
5ecfa255d2
|
@ -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
1
README
|
@ -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.
|
||||
|
|
|
@ -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)
|
|
@ -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\\\\''] ...
|
||||
|
|
|
@ -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
|
||||
########################################################################
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) );
|
||||
|
|
|
@ -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})
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
||||