osmocom-sdr
Threads by month
- ----- 2026 -----
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- 921 discussions
03 Feb '18
Hi maintainer and all,
Below you find a patch that implements Microtelecom Perseus support in
gr-osmosdr.
I have tested it in gqrx SDR software.
(second attempt as the first one got corrupted)
---
CMakeLists.txt | 1 +
cmake/Modules/FindLibPERSEUS.cmake | 24 ++
lib/CMakeLists.txt | 8 +
lib/airspy/airspy_source_c.cc | 5 +-
lib/config.h.in | 1 +
lib/device.cc | 8 +
lib/perseus/CMakeLists.txt | 37 +++
lib/perseus/perseus_source_c.cc | 507
+++++++++++++++++++++++++++++++++++++
lib/perseus/perseus_source_c.h | 120 +++++++++
lib/rtl/rtl_source_c.cc | 2 +-
lib/source_impl.cc | 18 +-
11 files changed, 727 insertions(+), 4 deletions(-)
create mode 100644 cmake/Modules/FindLibPERSEUS.cmake
create mode 100644 lib/perseus/CMakeLists.txt
create mode 100644 lib/perseus/perseus_source_c.cc
create mode 100644 lib/perseus/perseus_source_c.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 296456d..94acd4c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -173,6 +173,7 @@ find_package(Volk)
find_package(LibbladeRF)
find_package(SoapySDR NO_MODULE)
find_package(LibFreeSRP)
+find_package(LibPERSEUS)
find_package(Doxygen)
if(NOT GNURADIO_RUNTIME_FOUND)
diff --git a/cmake/Modules/FindLibPERSEUS.cmake
b/cmake/Modules/FindLibPERSEUS.cmake
new file mode 100644
index 0000000..5477327
--- /dev/null
+++ b/cmake/Modules/FindLibPERSEUS.cmake
@@ -0,0 +1,24 @@
+INCLUDE(FindPkgConfig)
+PKG_CHECK_MODULES(PC_LIBPERSEUS libperseus-sdr)
+
+FIND_PATH(
+ LIBPERSEUS_INCLUDE_DIRS
+ NAMES perseus-sdr.h
+ HINTS $ENV{LIBPERSEUS_DIR}/include
+ ${PC_LIBPERSEUS_INCLUDEDIR}
+ PATHS /usr/local/include
+ /usr/include
+)
+
+FIND_LIBRARY(
+ LIBPERSEUS_LIBRARIES
+ NAMES perseus-sdr
+ HINTS $ENV{LIBPERSEUS_DIR}/lib
+ ${PC_LIBPERSEUS_LIBDIR}
+ PATHS /usr/local/lib
+ /usr/lib
+)
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBPERSEUS DEFAULT_MSG
LIBPERSEUS_LIBRARIES LIBPERSEUS_INCLUDE_DIRS)
+MARK_AS_ADVANCED(LIBPERSEUS_LIBRARIES LIBPERSEUS_INCLUDE_DIRS)
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index c05b8d9..f555816 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -249,6 +249,14 @@ GR_INCLUDE_SUBDIRECTORY(freesrp)
endif(ENABLE_FREESRP)
########################################################################
+# Setup PERSEUS component
+########################################################################
+GR_REGISTER_COMPONENT("Microtelecom Perseus Receiver" ENABLE_PERSEUS
LIBPERSEUS_FOUND)
+if(ENABLE_PERSEUS)
+GR_INCLUDE_SUBDIRECTORY(perseus)
+endif(ENABLE_PERSEUS)
+
+########################################################################
# Setup configuration file
########################################################################
ADD_DEFINITIONS(-DHAVE_CONFIG_H=1)
diff --git a/lib/airspy/airspy_source_c.cc b/lib/airspy/airspy_source_c.cc
index 50150e5..02fc6ab 100644
--- a/lib/airspy/airspy_source_c.cc
+++ b/lib/airspy/airspy_source_c.cc
@@ -350,9 +350,10 @@ osmosdr::meta_range_t
airspy_source_c::get_sample_rates()
{
osmosdr::meta_range_t range;
- for (size_t i = 0; i < _sample_rates.size(); i++)
+ for (size_t i = 0; i < _sample_rates.size(); i++) {
+ std::cerr << "SR: [" << i << "]: " << _sample_rates[i].first <<
std::endl;
range += osmosdr::range_t( _sample_rates[i].first );
-
+ }
return range;
}
diff --git a/lib/config.h.in b/lib/config.h.in
index 42e72f1..dac7e39 100644
--- a/lib/config.h.in
+++ b/lib/config.h.in
@@ -19,6 +19,7 @@
#cmakedefine ENABLE_SOAPY
#cmakedefine ENABLE_REDPITAYA
#cmakedefine ENABLE_FREESRP
+#cmakedefine ENABLE_PERSEUS
//provide NAN define for MSVC older than VC12
#if defined(_MSC_VER) && (_MSC_VER < 1800)
diff --git a/lib/device.cc b/lib/device.cc
index 025a22b..df0d734 100644
--- a/lib/device.cc
+++ b/lib/device.cc
@@ -90,6 +90,10 @@
#include <freesrp_source_c.h>
#endif
+#ifdef ENABLE_PERSEUS
+#include <perseus_source_c.h>
+#endif
+
#include "arg_helpers.h"
using namespace osmosdr;
@@ -194,6 +198,10 @@ devices_t device::find(const device_t &hint)
BOOST_FOREACH( std::string dev, soapy_source_c::get_devices() )
devices.push_back( device_t(dev) );
#endif
+#ifdef ENABLE_PERSEUS
+ BOOST_FOREACH( std::string dev, perseus_source_c::get_devices() )
+ devices.push_back( device_t(dev) );
+#endif
/* software-only sources should be appended at the very end,
* hopefully resulting in hardware sources to be shown first
diff --git a/lib/perseus/CMakeLists.txt b/lib/perseus/CMakeLists.txt
new file mode 100644
index 0000000..f45a10d
--- /dev/null
+++ b/lib/perseus/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright 2017 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}
+ ${LIBPERSEUS_INCLUDE_DIRS}
+)
+
+set(perseus_srcs
+ ${CMAKE_CURRENT_SOURCE_DIR}/perseus_source_c.cc
+)
+
+########################################################################
+# Append gnuradio-osmosdr library sources
+########################################################################
+list(APPEND gr_osmosdr_srcs ${perseus_srcs})
+list(APPEND gr_osmosdr_libs ${LIBPERSEUS_LIBRARIES}
${GNURADIO_BLOCKS_LIBRARIES})
diff --git a/lib/perseus/perseus_source_c.cc
b/lib/perseus/perseus_source_c.cc
new file mode 100644
index 0000000..0720da4
--- /dev/null
+++ b/lib/perseus/perseus_source_c.cc
@@ -0,0 +1,507 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2018 Andrea Montefusco IW0HDV
+ *
+ * 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 <stdexcept>
+#include <iostream>
+#include <algorithm>
+
+#include <boost/assign.hpp>
+#include <boost/format.hpp>
+#include <boost/detail/endian.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/thread/thread.hpp>
+
+#include <gnuradio/io_signature.h>
+
+#include "perseus_source_c.h"
+#include "arg_helpers.h"
+
+
+
+using namespace boost::assign;
+
+#define PERSEUS_FORMAT_ERROR(ret, msg) \
+ boost::str( boost::format(msg " (%1%)") % ret )
+
+#define PERSEUS_THROW_ON_ERROR(ret, msg) \
+ if ( ret != PERSEUS_NOERROR ) \
+ { \
+ throw std::runtime_error( PERSEUS_FORMAT_ERROR(ret, msg) ); \
+ }
+
+#define PERSEUS_FUNC_STR(func, arg) \
+ boost::str(boost::format(func "(%1%)") % arg) + " has failed"
+
+perseus_source_c_sptr make_perseus_source_c (const std::string & args)
+{
+ return gnuradio::get_initial_sptr(new perseus_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
+ */
+perseus_source_c::perseus_source_c (const std::string &args)
+ : gr::sync_block ("perseus_source_c",
+ gr::io_signature::make(MIN_IN, MAX_IN, sizeof (gr_complex)),
+ gr::io_signature::make(MIN_OUT, MAX_OUT, sizeof (gr_complex))),
+ _dev(NULL),
+ _sample_rate(0),
+ _center_freq(0),
+ _freq_corr(0),
+ _frun(false),
+ _cnt(0)
+{
+ //int ret;
+
+ dict_t dict = params_to_dict(args);
+
+ _dev = NULL;
+
+ // Set debug info dumped to stderr to the maximum verbose level
+ perseus_set_debug(3);
+
+ //fprintf (stderr, "Revision: %s\n", git_revision);
+ //fprintf (stderr, "SAMPLE RATE: %d\n", sr);
+ //fprintf (stderr, "NBUF: %d BUF SIZE: %d TOTAL BUFFER LENGTH: %d\n",
nb, bs, nb*bs);
+
+ // Check how many Perseus receivers are connected to the system
+ int num_perseus = perseus_init();
+ std::cerr << "## Microtelecom Perseus ctor: " << num_perseus << "
Perseus receivers found." << std::endl;
+
+ if (num_perseus==0) {
+ perseus_exit();
+ PERSEUS_THROW_ON_ERROR(PERSEUS_DEVNOTFOUND, "No Perseus receivers
detected");
+ }
+ // Open the first one...
+ if ((_dev = perseus_open(0)) == NULL) {
+ PERSEUS_THROW_ON_ERROR(PERSEUS_DEVNOTFOUND, "Unable to open Perseus
device");
+ }
+ // Download the standard firmware to the unit
+ std::cerr << "Downloading firmware..." << std::endl;
+ if (int rc = perseus_firmware_download(_dev,NULL)<0) {
+ PERSEUS_THROW_ON_ERROR (rc, "firmware download error");
+ }
+
+ // Dump some information about the receiver (S/N and HW rev)
+ int flag;
+ perseus_is_preserie(_dev, &flag);
+ if (flag != 0)
+ fprintf(stderr, "The device is a preserie unit");
+ else {
+ eeprom_prodid prodid;
+ if (perseus_get_product_id(_dev,&prodid)<0)
+ fprintf(stderr, "get product id error: %s", perseus_errorstr());
+ else
+ fprintf(stderr, "Receiver S/N:
%05d-%02hX%02hX-%02hX%02hX-%02hX%02hX - HW Release:%hd.%hd\n",
+ (uint16_t) prodid.sn,
+ (uint16_t) prodid.signature[5],
+ (uint16_t) prodid.signature[4],
+ (uint16_t) prodid.signature[3],
+ (uint16_t) prodid.signature[2],
+ (uint16_t) prodid.signature[1],
+ (uint16_t) prodid.signature[0],
+ (uint16_t) prodid.hwrel,
+ (uint16_t) prodid.hwver);
+ }
+ // Printing all sampling rates available .....
+ {
+ int buf[BUFSIZ];
+
+ if (perseus_get_sampling_rates (_dev, buf,
sizeof(buf)/sizeof(buf[0])) < 0) {
+ fprintf(stderr, "get sampling rates error: %s\n",
perseus_errorstr());
+ } else {
+ int i = 0;
+ while (buf[i]) {
+ fprintf(stderr, "#%d: sample rate: %d\n", i, buf[i]);
+
+ _sample_rates.push_back( std::pair<double, uint32_t>(
buf[i], i ) );
+ i++;
+ }
+ /* since they may (and will) give us an unsorted array we have
to sort it here
+ * to play nice with the monotonic requirement of meta-range
later on */
+ std::sort(_sample_rates.begin(), _sample_rates.end());
+
+ for (size_t i = 0; i < _sample_rates.size(); i++)
+ std::cerr << boost::format("%gM ") %
(_sample_rates[i].first / 1e6);
+
+ std::cerr << std::endl;
+ }
+ }
+ // Configure the receiver for 2 MS/s operations
+ fprintf(stderr, "Configuring FPGA...\n");
+ if (int ret = perseus_set_sampling_rate(_dev, 96000) < 0) { // specify
the sampling rate value in Samples/second
+ std::cerr << "fpga configuration error: " << perseus_errorstr() <<
std::endl;
+ PERSEUS_THROW_ON_ERROR(ret, "Perseus fpga configuration error");
+ }
+
+ perseus_set_attenuator_in_db(_dev, PERSEUS_ATT_0DB);
+ // Enable ADC Dither, Disable ADC Preamp
+ perseus_set_adc(_dev, true, false);
+ // set the NCO frequency in the middle of the range allowed
+ set_center_freq( (get_freq_range().start() + get_freq_range().stop()) /
2.0 );
+ set_sample_rate( get_sample_rates().start() );
+
+ _fifo = new boost::circular_buffer<gr_complex>(5000000);
+ if (!_fifo) {
+ throw std::runtime_error( std::string(__FUNCTION__) + " " +
+ "Failed to allocate a sample FIFO!" );
+ }
+}
+
+/*
+ * Our virtual destructor.
+ */
+perseus_source_c::~perseus_source_c ()
+{
+ if (_dev) {
+ if (_frun) stop();
+ int ret = perseus_close(_dev);
+ if ( ret != PERSEUS_NOERROR ) {
+ std::cerr << PERSEUS_FORMAT_ERROR(ret, "Failed to close Perseus") <<
std::endl;
+ } else
+ _dev = NULL;
+ }
+ perseus_exit();
+
+ if (_fifo) {
+ delete _fifo;
+ _fifo = NULL;
+ }
+}
+
+
+typedef union {
+ struct {
+ int32_t i;
+ int32_t q;
+ } __attribute__((__packed__)) iq;
+ struct {
+ uint8_t i1;
+ uint8_t i2;
+ uint8_t i3;
+ uint8_t i4;
+ uint8_t q1;
+ uint8_t q2;
+ uint8_t q3;
+ uint8_t q4;
+ } __attribute__((__packed__)) ;
+} iq_sample;
+
+
+int perseus_source_c::perseus_rx_callback(void *buf, int buf_size)
+{
+ size_t i, n_avail, to_copy, num_samples;
+
+ uint8_t *samplebuf = (uint8_t*)buf;
+
+ num_samples = buf_size/6;
+
+ if (buf_size % 6)
+ std::cerr << "FATAL !!!!!!! not on boundary" << std::endl;
+
+ iq_sample s;
+
+ _fifo_lock.lock();
+
+ n_avail = _fifo->capacity() - _fifo->size();
+ to_copy = (n_avail < num_samples ? n_avail : num_samples);
+
+ for (i = 0; i < to_copy; i++ ) {
+
+ s.i1 = s.q1 = 0;
+ s.i2 = *samplebuf++;
+ s.i3 = *samplebuf++;
+ s.i4 = *samplebuf++;
+ s.q2 = *samplebuf++;
+ s.q3 = *samplebuf++;
+ s.q4 = *samplebuf++;
+
+ // convert 24 bit two's complements integers to float in [-1.0 -
+1.0] range
+
+ /* Push sample to the fifo */
+ _fifo->push_back( gr_complex( (float)(s.iq.i) / (INT_MAX - 256),
(float)(s.iq.q) / (INT_MAX - 256) ) );
+
+ }
+ // keep a counter of processed bytes
+ _cnt += buf_size;
+
+ _fifo_lock.unlock();
+
+ /* We have made some new samples available to the consumer in work() */
+ if (to_copy) {
+ //std::cerr << "+" << std::flush;
+ _samp_avail.notify_one();
+ }
+
+ /* Indicate overrun, if necessary */
+ if (to_copy < num_samples)
+ std::cerr << "O" << std::flush;
+
+ return 0; // TODO: return -1 on error/stop
+}
+
+// static method used as callback from the libperseus-sdr library
+int perseus_source_c::_perseus_rx_callback(void *buf, int buf_size, void
*extra)
+{
+ perseus_source_c *obj = (perseus_source_c *)extra;
+
+ return obj->perseus_rx_callback((float *)buf, buf_size);
+
+ return 0;
+}
+
+
+bool perseus_source_c::start()
+{
+ int ret;
+
+ if ( ! _dev )
+ return false;
+
+ std::cerr << "***** START COUNTER: " << _cnt
+ << " mod 6 " << (_cnt % 6) << std::endl;
+
+ _frun = true;
+
+ // in order to guarantee samples stream synchronization
+ // re-set the sample rate each time the stream is started
+ set_sample_rate( get_sample_rate() );
+
+ ret = perseus_start_async_input(_dev,
+ 6 * 1024 * 2 /* buffer size */,
+ perseus_source_c::_perseus_rx_callback,
+ (void *)this);
+ if (ret != PERSEUS_NOERROR) {
+ std::cerr << "Failed to start RX streaming ("
+ << ret << ")" << std::endl;
+ return false;
+ } else
+ return true;
+}
+
+bool perseus_source_c::stop()
+{
+ if ( ! _dev )
+ return false;
+
+ _frun = false;
+
+ std::cerr << "***** STOP COUNTER: " << _cnt
+ << " mod 6 " << (_cnt % 6) << std::endl;
+
+ int ret = perseus_stop_async_input(_dev);
+
+ if ( ret != PERSEUS_NOERROR ) {
+ std::cerr << "Failed to stop RX streaming (" << ret << ")" <<
std::endl;
+ return false;
+ } else
+ return true;
+}
+
+int perseus_source_c::work( int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items )
+{
+ gr_complex *out = (gr_complex *)output_items[0];
+
+ if ( _frun == false ) return WORK_DONE;
+
+ boost::unique_lock<boost::mutex> lock(_fifo_lock);
+
+ /* Wait until we have the requested number of samples */
+ int n_samples_avail = _fifo->size();
+
+ while (n_samples_avail < noutput_items) {
+ _samp_avail.wait(lock);
+ n_samples_avail = _fifo->size();
+ }
+
+ for(int i = 0; i < noutput_items; ++i) {
+ out[i] = _fifo->at(0);
+ _fifo->pop_front();
+ }
+
+ return noutput_items;
+}
+
+std::vector<std::string> perseus_source_c::get_devices()
+{
+ std::vector<std::string> devices;
+ std::string label;
+
+ std::string args = "perseus=0,label='Perseus'";
+ devices.push_back( args );
+
+ return devices;
+}
+
+size_t perseus_source_c::get_num_channels()
+{
+ return 1;
+}
+
+osmosdr::meta_range_t perseus_source_c::get_sample_rates()
+{
+ osmosdr::meta_range_t range;
+
+ for (size_t i = 0; i < _sample_rates.size(); i++) {
+ range += osmosdr::range_t( _sample_rates[i].first );
+ std::cerr << "SR: " << i << " : ("
+ << _sample_rates[i].first << ")" << std::endl;
+ }
+ return range;
+}
+
+double perseus_source_c::set_sample_rate( double rate )
+{
+ int ret;
+ if ((ret = perseus_set_sampling_rate(_dev, rate)) < 0) {
+ PERSEUS_THROW_ON_ERROR(ret, "Unable to set sample rate.");
+ } else
+ _sample_rate = rate;
+
+ return get_sample_rate();
+}
+
+double perseus_source_c::get_sample_rate()
+{
+ return _sample_rate;
+}
+
+osmosdr::freq_range_t perseus_source_c::get_freq_range( size_t chan )
+{
+ osmosdr::freq_range_t range;
+
+ range += osmosdr::range_t( PERSEUS_DDC_FREQ_MIN, PERSEUS_DDC_FREQ_MAX );
+
+ return range;
+}
+
+double perseus_source_c::set_center_freq( double freq, size_t chan )
+{
+ int ret;
+
+ if (_dev) {
+ ret =
+ perseus_set_ddc_center_freq(_dev,
+ (uint64_t)(freq * (1.0 + (_freq_corr *
10e-6))),
+ 1);
+ if ( ret == PERSEUS_NOERROR ) {
+ _center_freq = freq;
+ } else {
+ PERSEUS_THROW_ON_ERROR( ret, PERSEUS_FUNC_STR(
"perseus_set_ddc_center_freq", freq ) )
+ }
+ }
+
+ return get_center_freq( chan );
+}
+
+double perseus_source_c::get_center_freq( size_t chan )
+{
+ return _center_freq;
+}
+
+double perseus_source_c::set_freq_corr( double ppm, size_t chan )
+{
+ return _freq_corr = ppm;
+}
+
+double perseus_source_c::get_freq_corr( size_t chan )
+{
+ return _freq_corr;
+}
+
+std::vector<std::string> perseus_source_c::get_gain_names( size_t chan )
+{
+ return {};
+}
+
+osmosdr::gain_range_t perseus_source_c::get_gain_range( size_t chan )
+{
+ return osmosdr::gain_range_t();
+}
+
+osmosdr::gain_range_t perseus_source_c::get_gain_range( const std::string
& name, size_t chan )
+{
+ return osmosdr::gain_range_t();
+}
+
+
+double perseus_source_c::set_gain( double gain, size_t chan )
+{
+ return gain;
+}
+
+double perseus_source_c::set_gain( double gain, const std::string & name,
size_t chan)
+{
+ return gain;
+}
+
+double perseus_source_c::get_gain( size_t chan )
+{
+ return 0.0;
+}
+
+double perseus_source_c::get_gain( const std::string & name, size_t chan )
+{
+ return 0.0;
+}
+
+std::vector< std::string > perseus_source_c::get_antennas( size_t chan )
+{
+ std::vector< std::string > antennas;
+
+ antennas += get_antenna( chan );
+
+ return antennas;
+}
+
+std::string perseus_source_c::set_antenna( const std::string & antenna,
size_t chan )
+{
+ return get_antenna( chan );
+}
+
+std::string perseus_source_c::get_antenna( size_t chan )
+{
+ return "RX";
+}
diff --git a/lib/perseus/perseus_source_c.h b/lib/perseus/perseus_source_c.h
new file mode 100644
index 0000000..9eda23d
--- /dev/null
+++ b/lib/perseus/perseus_source_c.h
@@ -0,0 +1,120 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2013 Dimitri Stolnikov <horiz0n(a)gmx.net>
+ *
+ * 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.
+ */
+#ifndef INCLUDED_PERSEUS_SOURCE_C_H
+#define INCLUDED_PERSEUS_SOURCE_C_H
+
+#include <boost/circular_buffer.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition_variable.hpp>
+
+#include <gnuradio/sync_block.h>
+
+#include "source_iface.h"
+#include <perseus-sdr.h>
+
+class perseus_source_c;
+
+typedef boost::shared_ptr<perseus_source_c> perseus_source_c_sptr;
+
+/*!
+ * \brief Return a shared_ptr to a new instance of perseus_source_c.
+ *
+ * To avoid accidental use of raw pointers, perseus_source_c's
+ * constructor is private. make_perseus_source_c is the public
+ * interface for creating new instances.
+ */
+perseus_source_c_sptr make_perseus_source_c (const std::string & args =
"");
+
+/*!
+ * \brief Provides a stream of complex samples.
+ * \ingroup block
+ */
+class perseus_source_c :
+ public gr::sync_block,
+ public source_iface
+{
+private:
+ // The friend declaration allows make_perseus_source_c to
+ // access the private constructor.
+
+ friend perseus_source_c_sptr make_perseus_source_c (const std::string &
args);
+
+ perseus_source_c (const std::string & args);
+
+public:
+ ~perseus_source_c ();
+
+ bool start();
+ bool stop();
+
+ 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 );
+ 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 );
+
+ 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 );
+
+
+private:
+ static int _perseus_rx_callback(void *buf, int buf_size, void *extra);
+
+ int perseus_rx_callback(void *samples, int sample_count);
+
+ perseus_descr *_dev;
+
+ boost::circular_buffer<gr_complex> *_fifo;
+ boost::mutex _fifo_lock;
+ boost::condition_variable _samp_avail;
+
+ std::vector< std::pair<double, uint32_t> > _sample_rates;
+ double _sample_rate;
+ double _center_freq;
+ double _freq_corr;
+
+ bool _frun;
+ int _cnt;
+};
+
+#endif /* INCLUDED_PERSEUS_SOURCE_C_H */
diff --git a/lib/rtl/rtl_source_c.cc b/lib/rtl/rtl_source_c.cc
index a371464..eb7042e 100644
--- a/lib/rtl/rtl_source_c.cc
+++ b/lib/rtl/rtl_source_c.cc
@@ -221,7 +221,7 @@ rtl_source_c::rtl_source_c (const std::string &args)
throw std::runtime_error("Failed to enable offset tuning.");
}
- ret = rtlsdr_set_bias_tee(_dev, bias_tee);
+// ret = rtlsdr_set_bias_tee(_dev, bias_tee);
if (ret < 0)
throw std::runtime_error("Failed to set bias tee.");
diff --git a/lib/source_impl.cc b/lib/source_impl.cc
index a28f314..ead4ef7 100644
--- a/lib/source_impl.cc
+++ b/lib/source_impl.cc
@@ -92,6 +92,9 @@
#include <freesrp_source_c.h>
#endif
+#ifdef ENABLE_PERSEUS
+#include <perseus_source_c.h>
+#endif
#include "arg_helpers.h"
#include "source_impl.h"
@@ -171,6 +174,9 @@ source_impl::source_impl( const std::string &args )
#ifdef ENABLE_FREESRP
dev_types.push_back("freesrp");
#endif
+#ifdef ENABLE_PERSEUS
+ dev_types.push_back("perseus");
+#endif
std::cerr << "gr-osmosdr "
<< GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") "
<< "gnuradio " << gr::version() << std::endl;
@@ -252,7 +258,10 @@ source_impl::source_impl( const std::string &args )
BOOST_FOREACH( std::string dev, freesrp_source_c::get_devices() )
dev_list.push_back( dev );
#endif
-
+#ifdef ENABLE_PERSEUS
+ BOOST_FOREACH( std::string dev, perseus_source_c::get_devices() )
+ dev_list.push_back( dev );
+#endif
// std::cerr << std::endl;
// BOOST_FOREACH( std::string dev, dev_list )
// std::cerr << "'" << dev << "'" << std::endl;
@@ -383,6 +392,13 @@ source_impl::source_impl( const std::string &args )
}
#endif
+#ifdef ENABLE_PERSEUS
+ if ( dict.count("perseus") ) {
+ perseus_source_c_sptr src = make_perseus_source_c( arg );
+ block = src; iface = src.get();
+ }
+#endif
+
if ( iface != NULL && long(block.get()) != 0 ) {
_devs.push_back( iface );
--
1.9.1
>From 1b6cf50a7a6074754717b0270757931c379561bc Mon Sep 17 00:00:00 2001
From: Andrea Montefusco IW0HDV <andrew(a)montefusco.com>
Date: Thu, 1 Feb 2018 17:33:39 +0100
Subject: More hardware controls added (attenuator, ADC preamp and dither,
preselector)
---
lib/perseus/perseus_source_c.cc | 136
++++++++++++++++++++++++++++++++++++----
lib/perseus/perseus_source_c.h | 19 +++++-
2 files changed, 142 insertions(+), 13 deletions(-)
diff --git a/lib/perseus/perseus_source_c.cc
b/lib/perseus/perseus_source_c.cc
index 0720da4..89674b1 100644
--- a/lib/perseus/perseus_source_c.cc
+++ b/lib/perseus/perseus_source_c.cc
@@ -89,7 +89,11 @@ perseus_source_c::perseus_source_c (const std::string
&args)
_center_freq(0),
_freq_corr(0),
_frun(false),
- _cnt(0)
+ _cnt(0),
+ _att_value(0),
+ _adc_preamp(false),
+ _adc_dither(false),
+ _preselector(false)
{
//int ret;
@@ -174,9 +178,9 @@ perseus_source_c::perseus_source_c (const std::string
&args)
PERSEUS_THROW_ON_ERROR(ret, "Perseus fpga configuration error");
}
- perseus_set_attenuator_in_db(_dev, PERSEUS_ATT_0DB);
+ perseus_set_attenuator_in_db(_dev, _att_value);
// Enable ADC Dither, Disable ADC Preamp
- perseus_set_adc(_dev, true, false);
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ?
1:0);
// set the NCO frequency in the middle of the range allowed
set_center_freq( (get_freq_range().start() + get_freq_range().stop()) /
2.0 );
set_sample_rate( get_sample_rates().start() );
@@ -425,7 +429,7 @@ double perseus_source_c::set_center_freq( double freq,
size_t chan )
ret =
perseus_set_ddc_center_freq(_dev,
(uint64_t)(freq * (1.0 + (_freq_corr *
10e-6))),
- 1);
+ _preselector == true ? 1:0);
if ( ret == PERSEUS_NOERROR ) {
_center_freq = freq;
} else {
@@ -453,55 +457,165 @@ double perseus_source_c::get_freq_corr( size_t chan )
std::vector<std::string> perseus_source_c::get_gain_names( size_t chan )
{
- return {};
+ std::vector< std::string > names;
+
+ names += "Attenuator";
+ names += "ADC preamp";
+ names += "ADC dither";
+
+ return names;
}
+//
+// FIXME
+//
osmosdr::gain_range_t perseus_source_c::get_gain_range( size_t chan )
{
- return osmosdr::gain_range_t();
+ return get_gain_range( "Attenuator", chan );
}
osmosdr::gain_range_t perseus_source_c::get_gain_range( const std::string
& name, size_t chan )
{
+ if ( "Attenuator" == name ) {
+ return osmosdr::gain_range_t( -30, 0, 10 );
+ }
+ if ( "ADC preamp" == name ) {
+ return osmosdr::gain_range_t( 0, 6, 6 );
+ }
+ if ( "ADC dither" == name ) {
+ return osmosdr::gain_range_t( 0, 1, 1 );
+ }
+
return osmosdr::gain_range_t();
}
-double perseus_source_c::set_gain( double gain, size_t chan )
+double perseus_source_c::set_attenuator( double gain, size_t chan )
{
+ int ret;
+
+ gain = - gain;
+ if (_dev && _att_value != gain) {
+ ret = perseus_set_attenuator_in_db (_dev, gain);
+
+ if ( PERSEUS_NOERROR == ret ) {
+ _att_value = gain;
+ } else {
+ std::cerr << "ERROR: perseus_set_attenuator_in_db: " << gain <<
std::endl;
+ }
+ }
+
+ return _att_value;
+}
+
+double perseus_source_c::set_adc_preamp( double gain, size_t chan )
+{
+ std::cerr << "set_adc_preamp: " << gain << std::endl;
+
+ if (_dev) {
+ _adc_preamp = (gain != 0. ? true : false);
+ int ret =
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ?
1:0);
+ if ( PERSEUS_NOERROR != ret ) {
+ std::cerr << "ERROR: perseus_set_adc" << ret << std::endl;
+ }
+ }
return gain;
}
-double perseus_source_c::set_gain( double gain, const std::string & name,
size_t chan)
+double perseus_source_c::set_adc_dither( double gain, size_t chan )
{
+ std::cerr << "set_adc_dither: " << gain << std::endl;
+
+ if (_dev) {
+ _adc_dither = (gain != 0. ? true : false);
+ int ret =
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ?
1:0);
+ if ( PERSEUS_NOERROR != ret ) {
+ std::cerr << "ERROR: perseus_set_adc" << ret << std::endl;
+ }
+ }
return gain;
}
-double perseus_source_c::get_gain( size_t chan )
+double perseus_source_c::get_attenuator( size_t chan )
{
- return 0.0;
+ return _att_value;
}
+double perseus_source_c::get_adc_preamp(size_t chan )
+{
+ double v = _adc_preamp == true ? 6. : 0.;
+ std::cerr << "get_adc_preamp: " << _adc_preamp << " value" << v <<
std::endl;
+ return v;
+}
+
+double perseus_source_c::get_adc_dither(size_t chan )
+{
+ double v = _adc_dither == true ? 1. : 0.;
+ std::cerr << "get_adc_dither: " << _adc_dither << " value" << v <<
std::endl;
+ return v;
+}
+
+
+double perseus_source_c::set_gain( double gain, const std::string & name,
size_t chan)
+{
+ if ( "Attenuator" == name ) {
+ return set_attenuator( gain, chan );
+ }
+ if ( "ADC preamp" == name ) {
+ return set_adc_preamp( gain, chan );
+ }
+ if ( "ADC dither" == name ) {
+ return set_adc_dither( gain, chan );
+ }
+ return gain;
+}
+
+
double perseus_source_c::get_gain( const std::string & name, size_t chan )
{
+ if ( "Attenuator" == name ) {
+ return get_attenuator( chan );
+ }
+ if ( "ADC preamp" == name ) {
+ return get_adc_preamp( chan );
+ }
+ if ( "ADC dither" == name ) {
+ return get_adc_dither( chan );
+ }
return 0.0;
}
+
+
std::vector< std::string > perseus_source_c::get_antennas( size_t chan )
{
std::vector< std::string > antennas;
- antennas += get_antenna( chan );
+ antennas += "RX";
+ antennas += "RX+PRESELECTOR";
return antennas;
}
std::string perseus_source_c::set_antenna( const std::string & antenna,
size_t chan )
{
+ if ( antenna == "RX" ) {
+ _preselector = false;
+ set_center_freq( get_center_freq() );
+ return "RX";
+ }
+ if ( antenna == "RX+PRESELECTOR" ) {
+ _preselector = true;
+ set_center_freq( get_center_freq() );
+ return "RX+PRESELECTOR";
+ }
return get_antenna( chan );
}
std::string perseus_source_c::get_antenna( size_t chan )
{
+ std::cerr << "perseus_source_c::get_antenna: " << chan << std::endl;
return "RX";
}
diff --git a/lib/perseus/perseus_source_c.h b/lib/perseus/perseus_source_c.h
index 9eda23d..934ff2b 100644
--- a/lib/perseus/perseus_source_c.h
+++ b/lib/perseus/perseus_source_c.h
@@ -87,9 +87,20 @@ public:
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 );
- double set_gain( double gain, size_t chan = 0 );
+
+ double set_gain( double gain, size_t chan = 0 ) {return gain; }
+ double get_gain( size_t chan = 0 ) {return _att_value; }
+
+ double set_attenuator( double gain, size_t chan = 0 );
+ double get_attenuator( size_t chan = 0 );
+
+ double set_adc_preamp( double gain, size_t chan = 0 );
+ double get_adc_preamp( size_t chan = 0 );
+
+ double set_adc_dither( double gain, size_t chan = 0 );
+ double get_adc_dither( 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 );
std::vector< std::string > get_antennas( size_t chan = 0 );
@@ -115,6 +126,10 @@ private:
bool _frun;
int _cnt;
+ double _att_value;
+ bool _adc_preamp;
+ bool _adc_dither;
+ bool _preselector;
};
#endif /* INCLUDED_PERSEUS_SOURCE_C_H */
--
1.9.1
--
Andrea Montefusco IW0HDV
------------------------------------------
As my old boss, an Apollo veteran, would often remind us “It’s good to be
smart, but it’s better to be lucky.”
Wayne Hale, Space Shuttle Flight Director
2
2
[PATCH] gr-osmosdr Microtelecom Perseus HF receiver support
by andrea.montefusco@gmail.com 02 Feb '18
by andrea.montefusco@gmail.com 02 Feb '18
02 Feb '18
Hi maintainer and all,
Below you find a patch that implements Microtelecom Perseus support .
I have testes it using gqrx SDR software.
---
CMakeLists.txt | 1 +
cmake/Modules/FindLibPERSEUS.cmake | 24 ++
lib/CMakeLists.txt | 8 +
lib/airspy/airspy_source_c.cc | 5 +-
lib/config.h.in | 1 +
lib/device.cc | 8 +
lib/perseus/CMakeLists.txt | 37 +++
lib/perseus/perseus_source_c.cc | 507 +++++++++++++++++++++++++++++++++++++
lib/perseus/perseus_source_c.h | 120 +++++++++
lib/rtl/rtl_source_c.cc | 2 +-
lib/source_impl.cc | 18 +-
11 files changed, 727 insertions(+), 4 deletions(-)
create mode 100644 cmake/Modules/FindLibPERSEUS.cmake
create mode 100644 lib/perseus/CMakeLists.txt
create mode 100644 lib/perseus/perseus_source_c.cc
create mode 100644 lib/perseus/perseus_source_c.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 296456d..94acd4c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -173,6 +173,7 @@ find_package(Volk)
find_package(LibbladeRF)
find_package(SoapySDR NO_MODULE)
find_package(LibFreeSRP)
+find_package(LibPERSEUS)
find_package(Doxygen)
if(NOT GNURADIO_RUNTIME_FOUND)
diff --git a/cmake/Modules/FindLibPERSEUS.cmake b/cmake/Modules/FindLibPERSEUS.cmake
new file mode 100644
index 0000000..5477327
--- /dev/null
+++ b/cmake/Modules/FindLibPERSEUS.cmake
@@ -0,0 +1,24 @@
+INCLUDE(FindPkgConfig)
+PKG_CHECK_MODULES(PC_LIBPERSEUS libperseus-sdr)
+
+FIND_PATH(
+ LIBPERSEUS_INCLUDE_DIRS
+ NAMES perseus-sdr.h
+ HINTS $ENV{LIBPERSEUS_DIR}/include
+ ${PC_LIBPERSEUS_INCLUDEDIR}
+ PATHS /usr/local/include
+ /usr/include
+)
+
+FIND_LIBRARY(
+ LIBPERSEUS_LIBRARIES
+ NAMES perseus-sdr
+ HINTS $ENV{LIBPERSEUS_DIR}/lib
+ ${PC_LIBPERSEUS_LIBDIR}
+ PATHS /usr/local/lib
+ /usr/lib
+)
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBPERSEUS DEFAULT_MSG LIBPERSEUS_LIBRARIES LIBPERSEUS_INCLUDE_DIRS)
+MARK_AS_ADVANCED(LIBPERSEUS_LIBRARIES LIBPERSEUS_INCLUDE_DIRS)
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index c05b8d9..f555816 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -249,6 +249,14 @@ GR_INCLUDE_SUBDIRECTORY(freesrp)
endif(ENABLE_FREESRP)
########################################################################
+# Setup PERSEUS component
+########################################################################
+GR_REGISTER_COMPONENT("Microtelecom Perseus Receiver" ENABLE_PERSEUS LIBPERSEUS_FOUND)
+if(ENABLE_PERSEUS)
+GR_INCLUDE_SUBDIRECTORY(perseus)
+endif(ENABLE_PERSEUS)
+
+########################################################################
# Setup configuration file
########################################################################
ADD_DEFINITIONS(-DHAVE_CONFIG_H=1)
diff --git a/lib/airspy/airspy_source_c.cc b/lib/airspy/airspy_source_c.cc
index 50150e5..02fc6ab 100644
--- a/lib/airspy/airspy_source_c.cc
+++ b/lib/airspy/airspy_source_c.cc
@@ -350,9 +350,10 @@ osmosdr::meta_range_t airspy_source_c::get_sample_rates()
{
osmosdr::meta_range_t range;
- for (size_t i = 0; i < _sample_rates.size(); i++)
+ for (size_t i = 0; i < _sample_rates.size(); i++) {
+ std::cerr << "SR: [" << i << "]: " << _sample_rates[i].first << std::endl;
range += osmosdr::range_t( _sample_rates[i].first );
-
+ }
return range;
}
diff --git a/lib/config.h.in b/lib/config.h.in
index 42e72f1..dac7e39 100644
--- a/lib/config.h.in
+++ b/lib/config.h.in
@@ -19,6 +19,7 @@
#cmakedefine ENABLE_SOAPY
#cmakedefine ENABLE_REDPITAYA
#cmakedefine ENABLE_FREESRP
+#cmakedefine ENABLE_PERSEUS
//provide NAN define for MSVC older than VC12
#if defined(_MSC_VER) && (_MSC_VER < 1800)
diff --git a/lib/device.cc b/lib/device.cc
index 025a22b..df0d734 100644
--- a/lib/device.cc
+++ b/lib/device.cc
@@ -90,6 +90,10 @@
#include <freesrp_source_c.h>
#endif
+#ifdef ENABLE_PERSEUS
+#include <perseus_source_c.h>
+#endif
+
#include "arg_helpers.h"
using namespace osmosdr;
@@ -194,6 +198,10 @@ devices_t device::find(const device_t &hint)
BOOST_FOREACH( std::string dev, soapy_source_c::get_devices() )
devices.push_back( device_t(dev) );
#endif
+#ifdef ENABLE_PERSEUS
+ BOOST_FOREACH( std::string dev, perseus_source_c::get_devices() )
+ devices.push_back( device_t(dev) );
+#endif
/* software-only sources should be appended at the very end,
* hopefully resulting in hardware sources to be shown first
diff --git a/lib/perseus/CMakeLists.txt b/lib/perseus/CMakeLists.txt
new file mode 100644
index 0000000..f45a10d
--- /dev/null
+++ b/lib/perseus/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright 2017 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}
+ ${LIBPERSEUS_INCLUDE_DIRS}
+)
+
+set(perseus_srcs
+ ${CMAKE_CURRENT_SOURCE_DIR}/perseus_source_c.cc
+)
+
+########################################################################
+# Append gnuradio-osmosdr library sources
+########################################################################
+list(APPEND gr_osmosdr_srcs ${perseus_srcs})
+list(APPEND gr_osmosdr_libs ${LIBPERSEUS_LIBRARIES} ${GNURADIO_BLOCKS_LIBRARIES})
diff --git a/lib/perseus/perseus_source_c.cc b/lib/perseus/perseus_source_c.cc
new file mode 100644
index 0000000..0720da4
--- /dev/null
+++ b/lib/perseus/perseus_source_c.cc
@@ -0,0 +1,507 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2018 Andrea Montefusco IW0HDV
+ *
+ * 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 <stdexcept>
+#include <iostream>
+#include <algorithm>
+
+#include <boost/assign.hpp>
+#include <boost/format.hpp>
+#include <boost/detail/endian.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/thread/thread.hpp>
+
+#include <gnuradio/io_signature.h>
+
+#include "perseus_source_c.h"
+#include "arg_helpers.h"
+
+
+
+using namespace boost::assign;
+
+#define PERSEUS_FORMAT_ERROR(ret, msg) \
+ boost::str( boost::format(msg " (%1%)") % ret )
+
+#define PERSEUS_THROW_ON_ERROR(ret, msg) \
+ if ( ret != PERSEUS_NOERROR ) \
+ { \
+ throw std::runtime_error( PERSEUS_FORMAT_ERROR(ret, msg) ); \
+ }
+
+#define PERSEUS_FUNC_STR(func, arg) \
+ boost::str(boost::format(func "(%1%)") % arg) + " has failed"
+
+perseus_source_c_sptr make_perseus_source_c (const std::string & args)
+{
+ return gnuradio::get_initial_sptr(new perseus_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
+ */
+perseus_source_c::perseus_source_c (const std::string &args)
+ : gr::sync_block ("perseus_source_c",
+ gr::io_signature::make(MIN_IN, MAX_IN, sizeof (gr_complex)),
+ gr::io_signature::make(MIN_OUT, MAX_OUT, sizeof (gr_complex))),
+ _dev(NULL),
+ _sample_rate(0),
+ _center_freq(0),
+ _freq_corr(0),
+ _frun(false),
+ _cnt(0)
+{
+ //int ret;
+
+ dict_t dict = params_to_dict(args);
+
+ _dev = NULL;
+
+ // Set debug info dumped to stderr to the maximum verbose level
+ perseus_set_debug(3);
+
+ //fprintf (stderr, "Revision: %s\n", git_revision);
+ //fprintf (stderr, "SAMPLE RATE: %d\n", sr);
+ //fprintf (stderr, "NBUF: %d BUF SIZE: %d TOTAL BUFFER LENGTH: %d\n", nb, bs, nb*bs);
+
+ // Check how many Perseus receivers are connected to the system
+ int num_perseus = perseus_init();
+ std::cerr << "## Microtelecom Perseus ctor: " << num_perseus << " Perseus receivers found." << std::endl;
+
+ if (num_perseus==0) {
+ perseus_exit();
+ PERSEUS_THROW_ON_ERROR(PERSEUS_DEVNOTFOUND, "No Perseus receivers detected");
+ }
+ // Open the first one...
+ if ((_dev = perseus_open(0)) == NULL) {
+ PERSEUS_THROW_ON_ERROR(PERSEUS_DEVNOTFOUND, "Unable to open Perseus device");
+ }
+ // Download the standard firmware to the unit
+ std::cerr << "Downloading firmware..." << std::endl;
+ if (int rc = perseus_firmware_download(_dev,NULL)<0) {
+ PERSEUS_THROW_ON_ERROR (rc, "firmware download error");
+ }
+
+ // Dump some information about the receiver (S/N and HW rev)
+ int flag;
+ perseus_is_preserie(_dev, &flag);
+ if (flag != 0)
+ fprintf(stderr, "The device is a preserie unit");
+ else {
+ eeprom_prodid prodid;
+ if (perseus_get_product_id(_dev,&prodid)<0)
+ fprintf(stderr, "get product id error: %s", perseus_errorstr());
+ else
+ fprintf(stderr, "Receiver S/N: %05d-%02hX%02hX-%02hX%02hX-%02hX%02hX - HW Release:%hd.%hd\n",
+ (uint16_t) prodid.sn,
+ (uint16_t) prodid.signature[5],
+ (uint16_t) prodid.signature[4],
+ (uint16_t) prodid.signature[3],
+ (uint16_t) prodid.signature[2],
+ (uint16_t) prodid.signature[1],
+ (uint16_t) prodid.signature[0],
+ (uint16_t) prodid.hwrel,
+ (uint16_t) prodid.hwver);
+ }
+ // Printing all sampling rates available .....
+ {
+ int buf[BUFSIZ];
+
+ if (perseus_get_sampling_rates (_dev, buf, sizeof(buf)/sizeof(buf[0])) < 0) {
+ fprintf(stderr, "get sampling rates error: %s\n", perseus_errorstr());
+ } else {
+ int i = 0;
+ while (buf[i]) {
+ fprintf(stderr, "#%d: sample rate: %d\n", i, buf[i]);
+
+ _sample_rates.push_back( std::pair<double, uint32_t>( buf[i], i ) );
+ i++;
+ }
+ /* since they may (and will) give us an unsorted array we have to sort it here
+ * to play nice with the monotonic requirement of meta-range later on */
+ std::sort(_sample_rates.begin(), _sample_rates.end());
+
+ for (size_t i = 0; i < _sample_rates.size(); i++)
+ std::cerr << boost::format("%gM ") % (_sample_rates[i].first / 1e6);
+
+ std::cerr << std::endl;
+ }
+ }
+ // Configure the receiver for 2 MS/s operations
+ fprintf(stderr, "Configuring FPGA...\n");
+ if (int ret = perseus_set_sampling_rate(_dev, 96000) < 0) { // specify the sampling rate value in Samples/second
+ std::cerr << "fpga configuration error: " << perseus_errorstr() << std::endl;
+ PERSEUS_THROW_ON_ERROR(ret, "Perseus fpga configuration error");
+ }
+
+ perseus_set_attenuator_in_db(_dev, PERSEUS_ATT_0DB);
+ // Enable ADC Dither, Disable ADC Preamp
+ perseus_set_adc(_dev, true, false);
+ // set the NCO frequency in the middle of the range allowed
+ set_center_freq( (get_freq_range().start() + get_freq_range().stop()) / 2.0 );
+ set_sample_rate( get_sample_rates().start() );
+
+ _fifo = new boost::circular_buffer<gr_complex>(5000000);
+ if (!_fifo) {
+ throw std::runtime_error( std::string(__FUNCTION__) + " " +
+ "Failed to allocate a sample FIFO!" );
+ }
+}
+
+/*
+ * Our virtual destructor.
+ */
+perseus_source_c::~perseus_source_c ()
+{
+ if (_dev) {
+ if (_frun) stop();
+ int ret = perseus_close(_dev);
+ if ( ret != PERSEUS_NOERROR ) {
+ std::cerr << PERSEUS_FORMAT_ERROR(ret, "Failed to close Perseus") << std::endl;
+ } else
+ _dev = NULL;
+ }
+ perseus_exit();
+
+ if (_fifo) {
+ delete _fifo;
+ _fifo = NULL;
+ }
+}
+
+
+typedef union {
+ struct {
+ int32_t i;
+ int32_t q;
+ } __attribute__((__packed__)) iq;
+ struct {
+ uint8_t i1;
+ uint8_t i2;
+ uint8_t i3;
+ uint8_t i4;
+ uint8_t q1;
+ uint8_t q2;
+ uint8_t q3;
+ uint8_t q4;
+ } __attribute__((__packed__)) ;
+} iq_sample;
+
+
+int perseus_source_c::perseus_rx_callback(void *buf, int buf_size)
+{
+ size_t i, n_avail, to_copy, num_samples;
+
+ uint8_t *samplebuf = (uint8_t*)buf;
+
+ num_samples = buf_size/6;
+
+ if (buf_size % 6)
+ std::cerr << "FATAL !!!!!!! not on boundary" << std::endl;
+
+ iq_sample s;
+
+ _fifo_lock.lock();
+
+ n_avail = _fifo->capacity() - _fifo->size();
+ to_copy = (n_avail < num_samples ? n_avail : num_samples);
+
+ for (i = 0; i < to_copy; i++ ) {
+
+ s.i1 = s.q1 = 0;
+ s.i2 = *samplebuf++;
+ s.i3 = *samplebuf++;
+ s.i4 = *samplebuf++;
+ s.q2 = *samplebuf++;
+ s.q3 = *samplebuf++;
+ s.q4 = *samplebuf++;
+
+ // convert 24 bit two's complements integers to float in [-1.0 - +1.0] range
+
+ /* Push sample to the fifo */
+ _fifo->push_back( gr_complex( (float)(s.iq.i) / (INT_MAX - 256), (float)(s.iq.q) / (INT_MAX - 256) ) );
+
+ }
+ // keep a counter of processed bytes
+ _cnt += buf_size;
+
+ _fifo_lock.unlock();
+
+ /* We have made some new samples available to the consumer in work() */
+ if (to_copy) {
+ //std::cerr << "+" << std::flush;
+ _samp_avail.notify_one();
+ }
+
+ /* Indicate overrun, if necessary */
+ if (to_copy < num_samples)
+ std::cerr << "O" << std::flush;
+
+ return 0; // TODO: return -1 on error/stop
+}
+
+// static method used as callback from the libperseus-sdr library
+int perseus_source_c::_perseus_rx_callback(void *buf, int buf_size, void *extra)
+{
+ perseus_source_c *obj = (perseus_source_c *)extra;
+
+ return obj->perseus_rx_callback((float *)buf, buf_size);
+
+ return 0;
+}
+
+
+bool perseus_source_c::start()
+{
+ int ret;
+
+ if ( ! _dev )
+ return false;
+
+ std::cerr << "***** START COUNTER: " << _cnt
+ << " mod 6 " << (_cnt % 6) << std::endl;
+
+ _frun = true;
+
+ // in order to guarantee samples stream synchronization
+ // re-set the sample rate each time the stream is started
+ set_sample_rate( get_sample_rate() );
+
+ ret = perseus_start_async_input(_dev,
+ 6 * 1024 * 2 /* buffer size */,
+ perseus_source_c::_perseus_rx_callback,
+ (void *)this);
+ if (ret != PERSEUS_NOERROR) {
+ std::cerr << "Failed to start RX streaming ("
+ << ret << ")" << std::endl;
+ return false;
+ } else
+ return true;
+}
+
+bool perseus_source_c::stop()
+{
+ if ( ! _dev )
+ return false;
+
+ _frun = false;
+
+ std::cerr << "***** STOP COUNTER: " << _cnt
+ << " mod 6 " << (_cnt % 6) << std::endl;
+
+ int ret = perseus_stop_async_input(_dev);
+
+ if ( ret != PERSEUS_NOERROR ) {
+ std::cerr << "Failed to stop RX streaming (" << ret << ")" << std::endl;
+ return false;
+ } else
+ return true;
+}
+
+int perseus_source_c::work( int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items )
+{
+ gr_complex *out = (gr_complex *)output_items[0];
+
+ if ( _frun == false ) return WORK_DONE;
+
+ boost::unique_lock<boost::mutex> lock(_fifo_lock);
+
+ /* Wait until we have the requested number of samples */
+ int n_samples_avail = _fifo->size();
+
+ while (n_samples_avail < noutput_items) {
+ _samp_avail.wait(lock);
+ n_samples_avail = _fifo->size();
+ }
+
+ for(int i = 0; i < noutput_items; ++i) {
+ out[i] = _fifo->at(0);
+ _fifo->pop_front();
+ }
+
+ return noutput_items;
+}
+
+std::vector<std::string> perseus_source_c::get_devices()
+{
+ std::vector<std::string> devices;
+ std::string label;
+
+ std::string args = "perseus=0,label='Perseus'";
+ devices.push_back( args );
+
+ return devices;
+}
+
+size_t perseus_source_c::get_num_channels()
+{
+ return 1;
+}
+
+osmosdr::meta_range_t perseus_source_c::get_sample_rates()
+{
+ osmosdr::meta_range_t range;
+
+ for (size_t i = 0; i < _sample_rates.size(); i++) {
+ range += osmosdr::range_t( _sample_rates[i].first );
+ std::cerr << "SR: " << i << " : ("
+ << _sample_rates[i].first << ")" << std::endl;
+ }
+ return range;
+}
+
+double perseus_source_c::set_sample_rate( double rate )
+{
+ int ret;
+ if ((ret = perseus_set_sampling_rate(_dev, rate)) < 0) {
+ PERSEUS_THROW_ON_ERROR(ret, "Unable to set sample rate.");
+ } else
+ _sample_rate = rate;
+
+ return get_sample_rate();
+}
+
+double perseus_source_c::get_sample_rate()
+{
+ return _sample_rate;
+}
+
+osmosdr::freq_range_t perseus_source_c::get_freq_range( size_t chan )
+{
+ osmosdr::freq_range_t range;
+
+ range += osmosdr::range_t( PERSEUS_DDC_FREQ_MIN, PERSEUS_DDC_FREQ_MAX );
+
+ return range;
+}
+
+double perseus_source_c::set_center_freq( double freq, size_t chan )
+{
+ int ret;
+
+ if (_dev) {
+ ret =
+ perseus_set_ddc_center_freq(_dev,
+ (uint64_t)(freq * (1.0 + (_freq_corr * 10e-6))),
+ 1);
+ if ( ret == PERSEUS_NOERROR ) {
+ _center_freq = freq;
+ } else {
+ PERSEUS_THROW_ON_ERROR( ret, PERSEUS_FUNC_STR( "perseus_set_ddc_center_freq", freq ) )
+ }
+ }
+
+ return get_center_freq( chan );
+}
+
+double perseus_source_c::get_center_freq( size_t chan )
+{
+ return _center_freq;
+}
+
+double perseus_source_c::set_freq_corr( double ppm, size_t chan )
+{
+ return _freq_corr = ppm;
+}
+
+double perseus_source_c::get_freq_corr( size_t chan )
+{
+ return _freq_corr;
+}
+
+std::vector<std::string> perseus_source_c::get_gain_names( size_t chan )
+{
+ return {};
+}
+
+osmosdr::gain_range_t perseus_source_c::get_gain_range( size_t chan )
+{
+ return osmosdr::gain_range_t();
+}
+
+osmosdr::gain_range_t perseus_source_c::get_gain_range( const std::string & name, size_t chan )
+{
+ return osmosdr::gain_range_t();
+}
+
+
+double perseus_source_c::set_gain( double gain, size_t chan )
+{
+ return gain;
+}
+
+double perseus_source_c::set_gain( double gain, const std::string & name, size_t chan)
+{
+ return gain;
+}
+
+double perseus_source_c::get_gain( size_t chan )
+{
+ return 0.0;
+}
+
+double perseus_source_c::get_gain( const std::string & name, size_t chan )
+{
+ return 0.0;
+}
+
+std::vector< std::string > perseus_source_c::get_antennas( size_t chan )
+{
+ std::vector< std::string > antennas;
+
+ antennas += get_antenna( chan );
+
+ return antennas;
+}
+
+std::string perseus_source_c::set_antenna( const std::string & antenna, size_t chan )
+{
+ return get_antenna( chan );
+}
+
+std::string perseus_source_c::get_antenna( size_t chan )
+{
+ return "RX";
+}
diff --git a/lib/perseus/perseus_source_c.h b/lib/perseus/perseus_source_c.h
new file mode 100644
index 0000000..9eda23d
--- /dev/null
+++ b/lib/perseus/perseus_source_c.h
@@ -0,0 +1,120 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2013 Dimitri Stolnikov <horiz0n(a)gmx.net>
+ *
+ * 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.
+ */
+#ifndef INCLUDED_PERSEUS_SOURCE_C_H
+#define INCLUDED_PERSEUS_SOURCE_C_H
+
+#include <boost/circular_buffer.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition_variable.hpp>
+
+#include <gnuradio/sync_block.h>
+
+#include "source_iface.h"
+#include <perseus-sdr.h>
+
+class perseus_source_c;
+
+typedef boost::shared_ptr<perseus_source_c> perseus_source_c_sptr;
+
+/*!
+ * \brief Return a shared_ptr to a new instance of perseus_source_c.
+ *
+ * To avoid accidental use of raw pointers, perseus_source_c's
+ * constructor is private. make_perseus_source_c is the public
+ * interface for creating new instances.
+ */
+perseus_source_c_sptr make_perseus_source_c (const std::string & args = "");
+
+/*!
+ * \brief Provides a stream of complex samples.
+ * \ingroup block
+ */
+class perseus_source_c :
+ public gr::sync_block,
+ public source_iface
+{
+private:
+ // The friend declaration allows make_perseus_source_c to
+ // access the private constructor.
+
+ friend perseus_source_c_sptr make_perseus_source_c (const std::string & args);
+
+ perseus_source_c (const std::string & args);
+
+public:
+ ~perseus_source_c ();
+
+ bool start();
+ bool stop();
+
+ 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 );
+ 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 );
+
+ 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 );
+
+
+private:
+ static int _perseus_rx_callback(void *buf, int buf_size, void *extra);
+
+ int perseus_rx_callback(void *samples, int sample_count);
+
+ perseus_descr *_dev;
+
+ boost::circular_buffer<gr_complex> *_fifo;
+ boost::mutex _fifo_lock;
+ boost::condition_variable _samp_avail;
+
+ std::vector< std::pair<double, uint32_t> > _sample_rates;
+ double _sample_rate;
+ double _center_freq;
+ double _freq_corr;
+
+ bool _frun;
+ int _cnt;
+};
+
+#endif /* INCLUDED_PERSEUS_SOURCE_C_H */
diff --git a/lib/rtl/rtl_source_c.cc b/lib/rtl/rtl_source_c.cc
index a371464..eb7042e 100644
--- a/lib/rtl/rtl_source_c.cc
+++ b/lib/rtl/rtl_source_c.cc
@@ -221,7 +221,7 @@ rtl_source_c::rtl_source_c (const std::string &args)
throw std::runtime_error("Failed to enable offset tuning.");
}
- ret = rtlsdr_set_bias_tee(_dev, bias_tee);
+// ret = rtlsdr_set_bias_tee(_dev, bias_tee);
if (ret < 0)
throw std::runtime_error("Failed to set bias tee.");
diff --git a/lib/source_impl.cc b/lib/source_impl.cc
index a28f314..ead4ef7 100644
--- a/lib/source_impl.cc
+++ b/lib/source_impl.cc
@@ -92,6 +92,9 @@
#include <freesrp_source_c.h>
#endif
+#ifdef ENABLE_PERSEUS
+#include <perseus_source_c.h>
+#endif
#include "arg_helpers.h"
#include "source_impl.h"
@@ -171,6 +174,9 @@ source_impl::source_impl( const std::string &args )
#ifdef ENABLE_FREESRP
dev_types.push_back("freesrp");
#endif
+#ifdef ENABLE_PERSEUS
+ dev_types.push_back("perseus");
+#endif
std::cerr << "gr-osmosdr "
<< GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") "
<< "gnuradio " << gr::version() << std::endl;
@@ -252,7 +258,10 @@ source_impl::source_impl( const std::string &args )
BOOST_FOREACH( std::string dev, freesrp_source_c::get_devices() )
dev_list.push_back( dev );
#endif
-
+#ifdef ENABLE_PERSEUS
+ BOOST_FOREACH( std::string dev, perseus_source_c::get_devices() )
+ dev_list.push_back( dev );
+#endif
// std::cerr << std::endl;
// BOOST_FOREACH( std::string dev, dev_list )
// std::cerr << "'" << dev << "'" << std::endl;
@@ -383,6 +392,13 @@ source_impl::source_impl( const std::string &args )
}
#endif
+#ifdef ENABLE_PERSEUS
+ if ( dict.count("perseus") ) {
+ perseus_source_c_sptr src = make_perseus_source_c( arg );
+ block = src; iface = src.get();
+ }
+#endif
+
if ( iface != NULL && long(block.get()) != 0 ) {
_devs.push_back( iface );
--
1.9.1
>From 1b6cf50a7a6074754717b0270757931c379561bc Mon Sep 17 00:00:00 2001
From: Andrea Montefusco IW0HDV <andrew(a)montefusco.com>
Date: Thu, 1 Feb 2018 17:33:39 +0100
Subject: More hardware controls added (attenuator, ADC preamp and dither,
preselector)
---
lib/perseus/perseus_source_c.cc | 136 ++++++++++++++++++++++++++++++++++++----
lib/perseus/perseus_source_c.h | 19 +++++-
2 files changed, 142 insertions(+), 13 deletions(-)
diff --git a/lib/perseus/perseus_source_c.cc b/lib/perseus/perseus_source_c.cc
index 0720da4..89674b1 100644
--- a/lib/perseus/perseus_source_c.cc
+++ b/lib/perseus/perseus_source_c.cc
@@ -89,7 +89,11 @@ perseus_source_c::perseus_source_c (const std::string &args)
_center_freq(0),
_freq_corr(0),
_frun(false),
- _cnt(0)
+ _cnt(0),
+ _att_value(0),
+ _adc_preamp(false),
+ _adc_dither(false),
+ _preselector(false)
{
//int ret;
@@ -174,9 +178,9 @@ perseus_source_c::perseus_source_c (const std::string &args)
PERSEUS_THROW_ON_ERROR(ret, "Perseus fpga configuration error");
}
- perseus_set_attenuator_in_db(_dev, PERSEUS_ATT_0DB);
+ perseus_set_attenuator_in_db(_dev, _att_value);
// Enable ADC Dither, Disable ADC Preamp
- perseus_set_adc(_dev, true, false);
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ? 1:0);
// set the NCO frequency in the middle of the range allowed
set_center_freq( (get_freq_range().start() + get_freq_range().stop()) / 2.0 );
set_sample_rate( get_sample_rates().start() );
@@ -425,7 +429,7 @@ double perseus_source_c::set_center_freq( double freq, size_t chan )
ret =
perseus_set_ddc_center_freq(_dev,
(uint64_t)(freq * (1.0 + (_freq_corr * 10e-6))),
- 1);
+ _preselector == true ? 1:0);
if ( ret == PERSEUS_NOERROR ) {
_center_freq = freq;
} else {
@@ -453,55 +457,165 @@ double perseus_source_c::get_freq_corr( size_t chan )
std::vector<std::string> perseus_source_c::get_gain_names( size_t chan )
{
- return {};
+ std::vector< std::string > names;
+
+ names += "Attenuator";
+ names += "ADC preamp";
+ names += "ADC dither";
+
+ return names;
}
+//
+// FIXME
+//
osmosdr::gain_range_t perseus_source_c::get_gain_range( size_t chan )
{
- return osmosdr::gain_range_t();
+ return get_gain_range( "Attenuator", chan );
}
osmosdr::gain_range_t perseus_source_c::get_gain_range( const std::string & name, size_t chan )
{
+ if ( "Attenuator" == name ) {
+ return osmosdr::gain_range_t( -30, 0, 10 );
+ }
+ if ( "ADC preamp" == name ) {
+ return osmosdr::gain_range_t( 0, 6, 6 );
+ }
+ if ( "ADC dither" == name ) {
+ return osmosdr::gain_range_t( 0, 1, 1 );
+ }
+
return osmosdr::gain_range_t();
}
-double perseus_source_c::set_gain( double gain, size_t chan )
+double perseus_source_c::set_attenuator( double gain, size_t chan )
{
+ int ret;
+
+ gain = - gain;
+ if (_dev && _att_value != gain) {
+ ret = perseus_set_attenuator_in_db (_dev, gain);
+
+ if ( PERSEUS_NOERROR == ret ) {
+ _att_value = gain;
+ } else {
+ std::cerr << "ERROR: perseus_set_attenuator_in_db: " << gain << std::endl;
+ }
+ }
+
+ return _att_value;
+}
+
+double perseus_source_c::set_adc_preamp( double gain, size_t chan )
+{
+ std::cerr << "set_adc_preamp: " << gain << std::endl;
+
+ if (_dev) {
+ _adc_preamp = (gain != 0. ? true : false);
+ int ret =
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ? 1:0);
+ if ( PERSEUS_NOERROR != ret ) {
+ std::cerr << "ERROR: perseus_set_adc" << ret << std::endl;
+ }
+ }
return gain;
}
-double perseus_source_c::set_gain( double gain, const std::string & name, size_t chan)
+double perseus_source_c::set_adc_dither( double gain, size_t chan )
{
+ std::cerr << "set_adc_dither: " << gain << std::endl;
+
+ if (_dev) {
+ _adc_dither = (gain != 0. ? true : false);
+ int ret =
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ? 1:0);
+ if ( PERSEUS_NOERROR != ret ) {
+ std::cerr << "ERROR: perseus_set_adc" << ret << std::endl;
+ }
+ }
return gain;
}
-double perseus_source_c::get_gain( size_t chan )
+double perseus_source_c::get_attenuator( size_t chan )
{
- return 0.0;
+ return _att_value;
}
+double perseus_source_c::get_adc_preamp(size_t chan )
+{
+ double v = _adc_preamp == true ? 6. : 0.;
+ std::cerr << "get_adc_preamp: " << _adc_preamp << " value" << v << std::endl;
+ return v;
+}
+
+double perseus_source_c::get_adc_dither(size_t chan )
+{
+ double v = _adc_dither == true ? 1. : 0.;
+ std::cerr << "get_adc_dither: " << _adc_dither << " value" << v << std::endl;
+ return v;
+}
+
+
+double perseus_source_c::set_gain( double gain, const std::string & name, size_t chan)
+{
+ if ( "Attenuator" == name ) {
+ return set_attenuator( gain, chan );
+ }
+ if ( "ADC preamp" == name ) {
+ return set_adc_preamp( gain, chan );
+ }
+ if ( "ADC dither" == name ) {
+ return set_adc_dither( gain, chan );
+ }
+ return gain;
+}
+
+
double perseus_source_c::get_gain( const std::string & name, size_t chan )
{
+ if ( "Attenuator" == name ) {
+ return get_attenuator( chan );
+ }
+ if ( "ADC preamp" == name ) {
+ return get_adc_preamp( chan );
+ }
+ if ( "ADC dither" == name ) {
+ return get_adc_dither( chan );
+ }
return 0.0;
}
+
+
std::vector< std::string > perseus_source_c::get_antennas( size_t chan )
{
std::vector< std::string > antennas;
- antennas += get_antenna( chan );
+ antennas += "RX";
+ antennas += "RX+PRESELECTOR";
return antennas;
}
std::string perseus_source_c::set_antenna( const std::string & antenna, size_t chan )
{
+ if ( antenna == "RX" ) {
+ _preselector = false;
+ set_center_freq( get_center_freq() );
+ return "RX";
+ }
+ if ( antenna == "RX+PRESELECTOR" ) {
+ _preselector = true;
+ set_center_freq( get_center_freq() );
+ return "RX+PRESELECTOR";
+ }
return get_antenna( chan );
}
std::string perseus_source_c::get_antenna( size_t chan )
{
+ std::cerr << "perseus_source_c::get_antenna: " << chan << std::endl;
return "RX";
}
diff --git a/lib/perseus/perseus_source_c.h b/lib/perseus/perseus_source_c.h
index 9eda23d..934ff2b 100644
--- a/lib/perseus/perseus_source_c.h
+++ b/lib/perseus/perseus_source_c.h
@@ -87,9 +87,20 @@ public:
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 );
- double set_gain( double gain, size_t chan = 0 );
+
+ double set_gain( double gain, size_t chan = 0 ) {return gain; }
+ double get_gain( size_t chan = 0 ) {return _att_value; }
+
+ double set_attenuator( double gain, size_t chan = 0 );
+ double get_attenuator( size_t chan = 0 );
+
+ double set_adc_preamp( double gain, size_t chan = 0 );
+ double get_adc_preamp( size_t chan = 0 );
+
+ double set_adc_dither( double gain, size_t chan = 0 );
+ double get_adc_dither( 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 );
std::vector< std::string > get_antennas( size_t chan = 0 );
@@ -115,6 +126,10 @@ private:
bool _frun;
int _cnt;
+ double _att_value;
+ bool _adc_preamp;
+ bool _adc_dither;
+ bool _preselector;
};
#endif /* INCLUDED_PERSEUS_SOURCE_C_H */
--
1.9.1
.
QUIT
1
0
02 Feb '18
---------- Forwarded message ----------
From: <andrea.montefusco(a)gmail.com>
Date: Fri, Feb 2, 2018 at 8:25 PM
Subject: [PATCH] gr-osmosdr Microtelecom Perseus HF receiver support
To: andrea.montefusco(a)gmail.com
Hi maintainer and all,
Below you find a patch that implements Microtelecom Perseus support .
I have testes it using gqrx SDR software.
---
CMakeLists.txt | 1 +
cmake/Modules/FindLibPERSEUS.cmake | 24 ++
lib/CMakeLists.txt | 8 +
lib/airspy/airspy_source_c.cc | 5 +-
lib/config.h.in | 1 +
lib/device.cc | 8 +
lib/perseus/CMakeLists.txt | 37 +++
lib/perseus/perseus_source_c.cc | 507 ++++++++++++++++++++++++++++++
+++++++
lib/perseus/perseus_source_c.h | 120 +++++++++
lib/rtl/rtl_source_c.cc | 2 +-
lib/source_impl.cc | 18 +-
11 files changed, 727 insertions(+), 4 deletions(-)
create mode 100644 cmake/Modules/FindLibPERSEUS.cmake
create mode 100644 lib/perseus/CMakeLists.txt
create mode 100644 lib/perseus/perseus_source_c.cc
create mode 100644 lib/perseus/perseus_source_c.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 296456d..94acd4c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -173,6 +173,7 @@ find_package(Volk)
find_package(LibbladeRF)
find_package(SoapySDR NO_MODULE)
find_package(LibFreeSRP)
+find_package(LibPERSEUS)
find_package(Doxygen)
if(NOT GNURADIO_RUNTIME_FOUND)
diff --git a/cmake/Modules/FindLibPERSEUS.cmake b/cmake/Modules/
FindLibPERSEUS.cmake
new file mode 100644
index 0000000..5477327
--- /dev/null
+++ b/cmake/Modules/FindLibPERSEUS.cmake
@@ -0,0 +1,24 @@
+INCLUDE(FindPkgConfig)
+PKG_CHECK_MODULES(PC_LIBPERSEUS libperseus-sdr)
+
+FIND_PATH(
+ LIBPERSEUS_INCLUDE_DIRS
+ NAMES perseus-sdr.h
+ HINTS $ENV{LIBPERSEUS_DIR}/include
+ ${PC_LIBPERSEUS_INCLUDEDIR}
+ PATHS /usr/local/include
+ /usr/include
+)
+
+FIND_LIBRARY(
+ LIBPERSEUS_LIBRARIES
+ NAMES perseus-sdr
+ HINTS $ENV{LIBPERSEUS_DIR}/lib
+ ${PC_LIBPERSEUS_LIBDIR}
+ PATHS /usr/local/lib
+ /usr/lib
+)
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBPERSEUS DEFAULT_MSG
LIBPERSEUS_LIBRARIES LIBPERSEUS_INCLUDE_DIRS)
+MARK_AS_ADVANCED(LIBPERSEUS_LIBRARIES LIBPERSEUS_INCLUDE_DIRS)
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index c05b8d9..f555816 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -249,6 +249,14 @@ GR_INCLUDE_SUBDIRECTORY(freesrp)
endif(ENABLE_FREESRP)
########################################################################
+# Setup PERSEUS component
+########################################################################
+GR_REGISTER_COMPONENT("Microtelecom Perseus Receiver" ENABLE_PERSEUS
LIBPERSEUS_FOUND)
+if(ENABLE_PERSEUS)
+GR_INCLUDE_SUBDIRECTORY(perseus)
+endif(ENABLE_PERSEUS)
+
+########################################################################
# Setup configuration file
########################################################################
ADD_DEFINITIONS(-DHAVE_CONFIG_H=1)
diff --git a/lib/airspy/airspy_source_c.cc b/lib/airspy/airspy_source_c.cc
index 50150e5..02fc6ab 100644
--- a/lib/airspy/airspy_source_c.cc
+++ b/lib/airspy/airspy_source_c.cc
@@ -350,9 +350,10 @@ osmosdr::meta_range_t airspy_source_c::get_sample_
rates()
{
osmosdr::meta_range_t range;
- for (size_t i = 0; i < _sample_rates.size(); i++)
+ for (size_t i = 0; i < _sample_rates.size(); i++) {
+ std::cerr << "SR: [" << i << "]: " << _sample_rates[i].first <<
std::endl;
range += osmosdr::range_t( _sample_rates[i].first );
-
+ }
return range;
}
diff --git a/lib/config.h.in b/lib/config.h.in
index 42e72f1..dac7e39 100644
--- a/lib/config.h.in
+++ b/lib/config.h.in
@@ -19,6 +19,7 @@
#cmakedefine ENABLE_SOAPY
#cmakedefine ENABLE_REDPITAYA
#cmakedefine ENABLE_FREESRP
+#cmakedefine ENABLE_PERSEUS
//provide NAN define for MSVC older than VC12
#if defined(_MSC_VER) && (_MSC_VER < 1800)
diff --git a/lib/device.cc b/lib/device.cc
index 025a22b..df0d734 100644
--- a/lib/device.cc
+++ b/lib/device.cc
@@ -90,6 +90,10 @@
#include <freesrp_source_c.h>
#endif
+#ifdef ENABLE_PERSEUS
+#include <perseus_source_c.h>
+#endif
+
#include "arg_helpers.h"
using namespace osmosdr;
@@ -194,6 +198,10 @@ devices_t device::find(const device_t &hint)
BOOST_FOREACH( std::string dev, soapy_source_c::get_devices() )
devices.push_back( device_t(dev) );
#endif
+#ifdef ENABLE_PERSEUS
+ BOOST_FOREACH( std::string dev, perseus_source_c::get_devices() )
+ devices.push_back( device_t(dev) );
+#endif
/* software-only sources should be appended at the very end,
* hopefully resulting in hardware sources to be shown first
diff --git a/lib/perseus/CMakeLists.txt b/lib/perseus/CMakeLists.txt
new file mode 100644
index 0000000..f45a10d
--- /dev/null
+++ b/lib/perseus/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright 2017 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}
+ ${LIBPERSEUS_INCLUDE_DIRS}
+)
+
+set(perseus_srcs
+ ${CMAKE_CURRENT_SOURCE_DIR}/perseus_source_c.cc
+)
+
+########################################################################
+# Append gnuradio-osmosdr library sources
+########################################################################
+list(APPEND gr_osmosdr_srcs ${perseus_srcs})
+list(APPEND gr_osmosdr_libs ${LIBPERSEUS_LIBRARIES}
${GNURADIO_BLOCKS_LIBRARIES})
diff --git a/lib/perseus/perseus_source_c.cc b/lib/perseus/perseus_source_
c.cc
new file mode 100644
index 0000000..0720da4
--- /dev/null
+++ b/lib/perseus/perseus_source_c.cc
@@ -0,0 +1,507 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2018 Andrea Montefusco IW0HDV
+ *
+ * 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 <stdexcept>
+#include <iostream>
+#include <algorithm>
+
+#include <boost/assign.hpp>
+#include <boost/format.hpp>
+#include <boost/detail/endian.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/thread/thread.hpp>
+
+#include <gnuradio/io_signature.h>
+
+#include "perseus_source_c.h"
+#include "arg_helpers.h"
+
+
+
+using namespace boost::assign;
+
+#define PERSEUS_FORMAT_ERROR(ret, msg) \
+ boost::str( boost::format(msg " (%1%)") % ret )
+
+#define PERSEUS_THROW_ON_ERROR(ret, msg) \
+ if ( ret != PERSEUS_NOERROR ) \
+ { \
+ throw std::runtime_error( PERSEUS_FORMAT_ERROR(ret, msg) ); \
+ }
+
+#define PERSEUS_FUNC_STR(func, arg) \
+ boost::str(boost::format(func "(%1%)") % arg) + " has failed"
+
+perseus_source_c_sptr make_perseus_source_c (const std::string & args)
+{
+ return gnuradio::get_initial_sptr(new perseus_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
+ */
+perseus_source_c::perseus_source_c (const std::string &args)
+ : gr::sync_block ("perseus_source_c",
+ gr::io_signature::make(MIN_IN, MAX_IN, sizeof (gr_complex)),
+ gr::io_signature::make(MIN_OUT, MAX_OUT, sizeof (gr_complex))),
+ _dev(NULL),
+ _sample_rate(0),
+ _center_freq(0),
+ _freq_corr(0),
+ _frun(false),
+ _cnt(0)
+{
+ //int ret;
+
+ dict_t dict = params_to_dict(args);
+
+ _dev = NULL;
+
+ // Set debug info dumped to stderr to the maximum verbose level
+ perseus_set_debug(3);
+
+ //fprintf (stderr, "Revision: %s\n", git_revision);
+ //fprintf (stderr, "SAMPLE RATE: %d\n", sr);
+ //fprintf (stderr, "NBUF: %d BUF SIZE: %d TOTAL BUFFER LENGTH: %d\n",
nb, bs, nb*bs);
+
+ // Check how many Perseus receivers are connected to the system
+ int num_perseus = perseus_init();
+ std::cerr << "## Microtelecom Perseus ctor: " << num_perseus << "
Perseus receivers found." << std::endl;
+
+ if (num_perseus==0) {
+ perseus_exit();
+ PERSEUS_THROW_ON_ERROR(PERSEUS_DEVNOTFOUND, "No Perseus receivers
detected");
+ }
+ // Open the first one...
+ if ((_dev = perseus_open(0)) == NULL) {
+ PERSEUS_THROW_ON_ERROR(PERSEUS_DEVNOTFOUND, "Unable to open Perseus
device");
+ }
+ // Download the standard firmware to the unit
+ std::cerr << "Downloading firmware..." << std::endl;
+ if (int rc = perseus_firmware_download(_dev,NULL)<0) {
+ PERSEUS_THROW_ON_ERROR (rc, "firmware download error");
+ }
+
+ // Dump some information about the receiver (S/N and HW rev)
+ int flag;
+ perseus_is_preserie(_dev, &flag);
+ if (flag != 0)
+ fprintf(stderr, "The device is a preserie unit");
+ else {
+ eeprom_prodid prodid;
+ if (perseus_get_product_id(_dev,&prodid)<0)
+ fprintf(stderr, "get product id error: %s", perseus_errorstr());
+ else
+ fprintf(stderr, "Receiver S/N: %05d-%02hX%02hX-%02hX%02hX-%02hX%02hX
- HW Release:%hd.%hd\n",
+ (uint16_t) prodid.sn,
+ (uint16_t) prodid.signature[5],
+ (uint16_t) prodid.signature[4],
+ (uint16_t) prodid.signature[3],
+ (uint16_t) prodid.signature[2],
+ (uint16_t) prodid.signature[1],
+ (uint16_t) prodid.signature[0],
+ (uint16_t) prodid.hwrel,
+ (uint16_t) prodid.hwver);
+ }
+ // Printing all sampling rates available .....
+ {
+ int buf[BUFSIZ];
+
+ if (perseus_get_sampling_rates (_dev, buf,
sizeof(buf)/sizeof(buf[0])) < 0) {
+ fprintf(stderr, "get sampling rates error: %s\n",
perseus_errorstr());
+ } else {
+ int i = 0;
+ while (buf[i]) {
+ fprintf(stderr, "#%d: sample rate: %d\n", i, buf[i]);
+
+ _sample_rates.push_back( std::pair<double, uint32_t>(
buf[i], i ) );
+ i++;
+ }
+ /* since they may (and will) give us an unsorted array we have
to sort it here
+ * to play nice with the monotonic requirement of meta-range
later on */
+ std::sort(_sample_rates.begin(), _sample_rates.end());
+
+ for (size_t i = 0; i < _sample_rates.size(); i++)
+ std::cerr << boost::format("%gM ") %
(_sample_rates[i].first / 1e6);
+
+ std::cerr << std::endl;
+ }
+ }
+ // Configure the receiver for 2 MS/s operations
+ fprintf(stderr, "Configuring FPGA...\n");
+ if (int ret = perseus_set_sampling_rate(_dev, 96000) < 0) { // specify
the sampling rate value in Samples/second
+ std::cerr << "fpga configuration error: " << perseus_errorstr()
<< std::endl;
+ PERSEUS_THROW_ON_ERROR(ret, "Perseus fpga configuration error");
+ }
+
+ perseus_set_attenuator_in_db(_dev, PERSEUS_ATT_0DB);
+ // Enable ADC Dither, Disable ADC Preamp
+ perseus_set_adc(_dev, true, false);
+ // set the NCO frequency in the middle of the range allowed
+ set_center_freq( (get_freq_range().start() + get_freq_range().stop()) /
2.0 );
+ set_sample_rate( get_sample_rates().start() );
+
+ _fifo = new boost::circular_buffer<gr_complex>(5000000);
+ if (!_fifo) {
+ throw std::runtime_error( std::string(__FUNCTION__) + " " +
+ "Failed to allocate a sample FIFO!" );
+ }
+}
+
+/*
+ * Our virtual destructor.
+ */
+perseus_source_c::~perseus_source_c ()
+{
+ if (_dev) {
+ if (_frun) stop();
+ int ret = perseus_close(_dev);
+ if ( ret != PERSEUS_NOERROR ) {
+ std::cerr << PERSEUS_FORMAT_ERROR(ret, "Failed to close Perseus") <<
std::endl;
+ } else
+ _dev = NULL;
+ }
+ perseus_exit();
+
+ if (_fifo) {
+ delete _fifo;
+ _fifo = NULL;
+ }
+}
+
+
+typedef union {
+ struct {
+ int32_t i;
+ int32_t q;
+ } __attribute__((__packed__)) iq;
+ struct {
+ uint8_t i1;
+ uint8_t i2;
+ uint8_t i3;
+ uint8_t i4;
+ uint8_t q1;
+ uint8_t q2;
+ uint8_t q3;
+ uint8_t q4;
+ } __attribute__((__packed__)) ;
+} iq_sample;
+
+
+int perseus_source_c::perseus_rx_callback(void *buf, int buf_size)
+{
+ size_t i, n_avail, to_copy, num_samples;
+
+ uint8_t *samplebuf = (uint8_t*)buf;
+
+ num_samples = buf_size/6;
+
+ if (buf_size % 6)
+ std::cerr << "FATAL !!!!!!! not on boundary" << std::endl;
+
+ iq_sample s;
+
+ _fifo_lock.lock();
+
+ n_avail = _fifo->capacity() - _fifo->size();
+ to_copy = (n_avail < num_samples ? n_avail : num_samples);
+
+ for (i = 0; i < to_copy; i++ ) {
+
+ s.i1 = s.q1 = 0;
+ s.i2 = *samplebuf++;
+ s.i3 = *samplebuf++;
+ s.i4 = *samplebuf++;
+ s.q2 = *samplebuf++;
+ s.q3 = *samplebuf++;
+ s.q4 = *samplebuf++;
+
+ // convert 24 bit two's complements integers to float in [-1.0 -
+1.0] range
+
+ /* Push sample to the fifo */
+ _fifo->push_back( gr_complex( (float)(s.iq.i) / (INT_MAX - 256),
(float)(s.iq.q) / (INT_MAX - 256) ) );
+
+ }
+ // keep a counter of processed bytes
+ _cnt += buf_size;
+
+ _fifo_lock.unlock();
+
+ /* We have made some new samples available to the consumer in work() */
+ if (to_copy) {
+ //std::cerr << "+" << std::flush;
+ _samp_avail.notify_one();
+ }
+
+ /* Indicate overrun, if necessary */
+ if (to_copy < num_samples)
+ std::cerr << "O" << std::flush;
+
+ return 0; // TODO: return -1 on error/stop
+}
+
+// static method used as callback from the libperseus-sdr library
+int perseus_source_c::_perseus_rx_callback(void *buf, int buf_size, void
*extra)
+{
+ perseus_source_c *obj = (perseus_source_c *)extra;
+
+ return obj->perseus_rx_callback((float *)buf, buf_size);
+
+ return 0;
+}
+
+
+bool perseus_source_c::start()
+{
+ int ret;
+
+ if ( ! _dev )
+ return false;
+
+ std::cerr << "***** START COUNTER: " << _cnt
+ << " mod 6 " << (_cnt % 6) << std::endl;
+
+ _frun = true;
+
+ // in order to guarantee samples stream synchronization
+ // re-set the sample rate each time the stream is started
+ set_sample_rate( get_sample_rate() );
+
+ ret = perseus_start_async_input(_dev,
+ 6 * 1024 * 2 /* buffer size */,
+ perseus_source_c::_perseus_rx_callback,
+ (void *)this);
+ if (ret != PERSEUS_NOERROR) {
+ std::cerr << "Failed to start RX streaming ("
+ << ret << ")" << std::endl;
+ return false;
+ } else
+ return true;
+}
+
+bool perseus_source_c::stop()
+{
+ if ( ! _dev )
+ return false;
+
+ _frun = false;
+
+ std::cerr << "***** STOP COUNTER: " << _cnt
+ << " mod 6 " << (_cnt % 6) << std::endl;
+
+ int ret = perseus_stop_async_input(_dev);
+
+ if ( ret != PERSEUS_NOERROR ) {
+ std::cerr << "Failed to stop RX streaming (" << ret << ")" <<
std::endl;
+ return false;
+ } else
+ return true;
+}
+
+int perseus_source_c::work( int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items )
+{
+ gr_complex *out = (gr_complex *)output_items[0];
+
+ if ( _frun == false ) return WORK_DONE;
+
+ boost::unique_lock<boost::mutex> lock(_fifo_lock);
+
+ /* Wait until we have the requested number of samples */
+ int n_samples_avail = _fifo->size();
+
+ while (n_samples_avail < noutput_items) {
+ _samp_avail.wait(lock);
+ n_samples_avail = _fifo->size();
+ }
+
+ for(int i = 0; i < noutput_items; ++i) {
+ out[i] = _fifo->at(0);
+ _fifo->pop_front();
+ }
+
+ return noutput_items;
+}
+
+std::vector<std::string> perseus_source_c::get_devices()
+{
+ std::vector<std::string> devices;
+ std::string label;
+
+ std::string args = "perseus=0,label='Perseus'";
+ devices.push_back( args );
+
+ return devices;
+}
+
+size_t perseus_source_c::get_num_channels()
+{
+ return 1;
+}
+
+osmosdr::meta_range_t perseus_source_c::get_sample_rates()
+{
+ osmosdr::meta_range_t range;
+
+ for (size_t i = 0; i < _sample_rates.size(); i++) {
+ range += osmosdr::range_t( _sample_rates[i].first );
+ std::cerr << "SR: " << i << " : ("
+ << _sample_rates[i].first << ")" << std::endl;
+ }
+ return range;
+}
+
+double perseus_source_c::set_sample_rate( double rate )
+{
+ int ret;
+ if ((ret = perseus_set_sampling_rate(_dev, rate)) < 0) {
+ PERSEUS_THROW_ON_ERROR(ret, "Unable to set sample rate.");
+ } else
+ _sample_rate = rate;
+
+ return get_sample_rate();
+}
+
+double perseus_source_c::get_sample_rate()
+{
+ return _sample_rate;
+}
+
+osmosdr::freq_range_t perseus_source_c::get_freq_range( size_t chan )
+{
+ osmosdr::freq_range_t range;
+
+ range += osmosdr::range_t( PERSEUS_DDC_FREQ_MIN, PERSEUS_DDC_FREQ_MAX );
+
+ return range;
+}
+
+double perseus_source_c::set_center_freq( double freq, size_t chan )
+{
+ int ret;
+
+ if (_dev) {
+ ret =
+ perseus_set_ddc_center_freq(_dev,
+ (uint64_t)(freq * (1.0 + (_freq_corr *
10e-6))),
+ 1);
+ if ( ret == PERSEUS_NOERROR ) {
+ _center_freq = freq;
+ } else {
+ PERSEUS_THROW_ON_ERROR( ret, PERSEUS_FUNC_STR(
"perseus_set_ddc_center_freq", freq ) )
+ }
+ }
+
+ return get_center_freq( chan );
+}
+
+double perseus_source_c::get_center_freq( size_t chan )
+{
+ return _center_freq;
+}
+
+double perseus_source_c::set_freq_corr( double ppm, size_t chan )
+{
+ return _freq_corr = ppm;
+}
+
+double perseus_source_c::get_freq_corr( size_t chan )
+{
+ return _freq_corr;
+}
+
+std::vector<std::string> perseus_source_c::get_gain_names( size_t chan )
+{
+ return {};
+}
+
+osmosdr::gain_range_t perseus_source_c::get_gain_range( size_t chan )
+{
+ return osmosdr::gain_range_t();
+}
+
+osmosdr::gain_range_t perseus_source_c::get_gain_range( const std::string
& name, size_t chan )
+{
+ return osmosdr::gain_range_t();
+}
+
+
+double perseus_source_c::set_gain( double gain, size_t chan )
+{
+ return gain;
+}
+
+double perseus_source_c::set_gain( double gain, const std::string & name,
size_t chan)
+{
+ return gain;
+}
+
+double perseus_source_c::get_gain( size_t chan )
+{
+ return 0.0;
+}
+
+double perseus_source_c::get_gain( const std::string & name, size_t chan )
+{
+ return 0.0;
+}
+
+std::vector< std::string > perseus_source_c::get_antennas( size_t chan )
+{
+ std::vector< std::string > antennas;
+
+ antennas += get_antenna( chan );
+
+ return antennas;
+}
+
+std::string perseus_source_c::set_antenna( const std::string & antenna,
size_t chan )
+{
+ return get_antenna( chan );
+}
+
+std::string perseus_source_c::get_antenna( size_t chan )
+{
+ return "RX";
+}
diff --git a/lib/perseus/perseus_source_c.h b/lib/perseus/perseus_source_c.h
new file mode 100644
index 0000000..9eda23d
--- /dev/null
+++ b/lib/perseus/perseus_source_c.h
@@ -0,0 +1,120 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2013 Dimitri Stolnikov <horiz0n(a)gmx.net>
+ *
+ * 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.
+ */
+#ifndef INCLUDED_PERSEUS_SOURCE_C_H
+#define INCLUDED_PERSEUS_SOURCE_C_H
+
+#include <boost/circular_buffer.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition_variable.hpp>
+
+#include <gnuradio/sync_block.h>
+
+#include "source_iface.h"
+#include <perseus-sdr.h>
+
+class perseus_source_c;
+
+typedef boost::shared_ptr<perseus_source_c> perseus_source_c_sptr;
+
+/*!
+ * \brief Return a shared_ptr to a new instance of perseus_source_c.
+ *
+ * To avoid accidental use of raw pointers, perseus_source_c's
+ * constructor is private. make_perseus_source_c is the public
+ * interface for creating new instances.
+ */
+perseus_source_c_sptr make_perseus_source_c (const std::string & args =
"");
+
+/*!
+ * \brief Provides a stream of complex samples.
+ * \ingroup block
+ */
+class perseus_source_c :
+ public gr::sync_block,
+ public source_iface
+{
+private:
+ // The friend declaration allows make_perseus_source_c to
+ // access the private constructor.
+
+ friend perseus_source_c_sptr make_perseus_source_c (const std::string &
args);
+
+ perseus_source_c (const std::string & args);
+
+public:
+ ~perseus_source_c ();
+
+ bool start();
+ bool stop();
+
+ 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 );
+ 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 );
+
+ 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 );
+
+
+private:
+ static int _perseus_rx_callback(void *buf, int buf_size, void *extra);
+
+ int perseus_rx_callback(void *samples, int sample_count);
+
+ perseus_descr *_dev;
+
+ boost::circular_buffer<gr_complex> *_fifo;
+ boost::mutex _fifo_lock;
+ boost::condition_variable _samp_avail;
+
+ std::vector< std::pair<double, uint32_t> > _sample_rates;
+ double _sample_rate;
+ double _center_freq;
+ double _freq_corr;
+
+ bool _frun;
+ int _cnt;
+};
+
+#endif /* INCLUDED_PERSEUS_SOURCE_C_H */
diff --git a/lib/rtl/rtl_source_c.cc b/lib/rtl/rtl_source_c.cc
index a371464..eb7042e 100644
--- a/lib/rtl/rtl_source_c.cc
+++ b/lib/rtl/rtl_source_c.cc
@@ -221,7 +221,7 @@ rtl_source_c::rtl_source_c (const std::string &args)
throw std::runtime_error("Failed to enable offset tuning.");
}
- ret = rtlsdr_set_bias_tee(_dev, bias_tee);
+// ret = rtlsdr_set_bias_tee(_dev, bias_tee);
if (ret < 0)
throw std::runtime_error("Failed to set bias tee.");
diff --git a/lib/source_impl.cc b/lib/source_impl.cc
index a28f314..ead4ef7 100644
--- a/lib/source_impl.cc
+++ b/lib/source_impl.cc
@@ -92,6 +92,9 @@
#include <freesrp_source_c.h>
#endif
+#ifdef ENABLE_PERSEUS
+#include <perseus_source_c.h>
+#endif
#include "arg_helpers.h"
#include "source_impl.h"
@@ -171,6 +174,9 @@ source_impl::source_impl( const std::string &args )
#ifdef ENABLE_FREESRP
dev_types.push_back("freesrp");
#endif
+#ifdef ENABLE_PERSEUS
+ dev_types.push_back("perseus");
+#endif
std::cerr << "gr-osmosdr "
<< GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") "
<< "gnuradio " << gr::version() << std::endl;
@@ -252,7 +258,10 @@ source_impl::source_impl( const std::string &args )
BOOST_FOREACH( std::string dev, freesrp_source_c::get_devices() )
dev_list.push_back( dev );
#endif
-
+#ifdef ENABLE_PERSEUS
+ BOOST_FOREACH( std::string dev, perseus_source_c::get_devices() )
+ dev_list.push_back( dev );
+#endif
// std::cerr << std::endl;
// BOOST_FOREACH( std::string dev, dev_list )
// std::cerr << "'" << dev << "'" << std::endl;
@@ -383,6 +392,13 @@ source_impl::source_impl( const std::string &args )
}
#endif
+#ifdef ENABLE_PERSEUS
+ if ( dict.count("perseus") ) {
+ perseus_source_c_sptr src = make_perseus_source_c( arg );
+ block = src; iface = src.get();
+ }
+#endif
+
if ( iface != NULL && long(block.get()) != 0 ) {
_devs.push_back( iface );
--
1.9.1
>From 1b6cf50a7a6074754717b0270757931c379561bc Mon Sep 17 00:00:00 2001
From: Andrea Montefusco IW0HDV <andrew(a)montefusco.com>
Date: Thu, 1 Feb 2018 17:33:39 +0100
Subject: More hardware controls added (attenuator, ADC preamp and dither,
preselector)
---
lib/perseus/perseus_source_c.cc | 136 ++++++++++++++++++++++++++++++
++++++----
lib/perseus/perseus_source_c.h | 19 +++++-
2 files changed, 142 insertions(+), 13 deletions(-)
diff --git a/lib/perseus/perseus_source_c.cc b/lib/perseus/perseus_source_
c.cc
index 0720da4..89674b1 100644
--- a/lib/perseus/perseus_source_c.cc
+++ b/lib/perseus/perseus_source_c.cc
@@ -89,7 +89,11 @@ perseus_source_c::perseus_source_c (const std::string
&args)
_center_freq(0),
_freq_corr(0),
_frun(false),
- _cnt(0)
+ _cnt(0),
+ _att_value(0),
+ _adc_preamp(false),
+ _adc_dither(false),
+ _preselector(false)
{
//int ret;
@@ -174,9 +178,9 @@ perseus_source_c::perseus_source_c (const std::string
&args)
PERSEUS_THROW_ON_ERROR(ret, "Perseus fpga configuration error");
}
- perseus_set_attenuator_in_db(_dev, PERSEUS_ATT_0DB);
+ perseus_set_attenuator_in_db(_dev, _att_value);
// Enable ADC Dither, Disable ADC Preamp
- perseus_set_adc(_dev, true, false);
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ?
1:0);
// set the NCO frequency in the middle of the range allowed
set_center_freq( (get_freq_range().start() + get_freq_range().stop()) /
2.0 );
set_sample_rate( get_sample_rates().start() );
@@ -425,7 +429,7 @@ double perseus_source_c::set_center_freq( double freq,
size_t chan )
ret =
perseus_set_ddc_center_freq(_dev,
(uint64_t)(freq * (1.0 + (_freq_corr *
10e-6))),
- 1);
+ _preselector == true ? 1:0);
if ( ret == PERSEUS_NOERROR ) {
_center_freq = freq;
} else {
@@ -453,55 +457,165 @@ double perseus_source_c::get_freq_corr( size_t chan )
std::vector<std::string> perseus_source_c::get_gain_names( size_t chan )
{
- return {};
+ std::vector< std::string > names;
+
+ names += "Attenuator";
+ names += "ADC preamp";
+ names += "ADC dither";
+
+ return names;
}
+//
+// FIXME
+//
osmosdr::gain_range_t perseus_source_c::get_gain_range( size_t chan )
{
- return osmosdr::gain_range_t();
+ return get_gain_range( "Attenuator", chan );
}
osmosdr::gain_range_t perseus_source_c::get_gain_range( const std::string
& name, size_t chan )
{
+ if ( "Attenuator" == name ) {
+ return osmosdr::gain_range_t( -30, 0, 10 );
+ }
+ if ( "ADC preamp" == name ) {
+ return osmosdr::gain_range_t( 0, 6, 6 );
+ }
+ if ( "ADC dither" == name ) {
+ return osmosdr::gain_range_t( 0, 1, 1 );
+ }
+
return osmosdr::gain_range_t();
}
-double perseus_source_c::set_gain( double gain, size_t chan )
+double perseus_source_c::set_attenuator( double gain, size_t chan )
{
+ int ret;
+
+ gain = - gain;
+ if (_dev && _att_value != gain) {
+ ret = perseus_set_attenuator_in_db (_dev, gain);
+
+ if ( PERSEUS_NOERROR == ret ) {
+ _att_value = gain;
+ } else {
+ std::cerr << "ERROR: perseus_set_attenuator_in_db: " << gain <<
std::endl;
+ }
+ }
+
+ return _att_value;
+}
+
+double perseus_source_c::set_adc_preamp( double gain, size_t chan )
+{
+ std::cerr << "set_adc_preamp: " << gain << std::endl;
+
+ if (_dev) {
+ _adc_preamp = (gain != 0. ? true : false);
+ int ret =
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ?
1:0);
+ if ( PERSEUS_NOERROR != ret ) {
+ std::cerr << "ERROR: perseus_set_adc" << ret << std::endl;
+ }
+ }
return gain;
}
-double perseus_source_c::set_gain( double gain, const std::string & name,
size_t chan)
+double perseus_source_c::set_adc_dither( double gain, size_t chan )
{
+ std::cerr << "set_adc_dither: " << gain << std::endl;
+
+ if (_dev) {
+ _adc_dither = (gain != 0. ? true : false);
+ int ret =
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ?
1:0);
+ if ( PERSEUS_NOERROR != ret ) {
+ std::cerr << "ERROR: perseus_set_adc" << ret << std::endl;
+ }
+ }
return gain;
}
-double perseus_source_c::get_gain( size_t chan )
+double perseus_source_c::get_attenuator( size_t chan )
{
- return 0.0;
+ return _att_value;
}
+double perseus_source_c::get_adc_preamp(size_t chan )
+{
+ double v = _adc_preamp == true ? 6. : 0.;
+ std::cerr << "get_adc_preamp: " << _adc_preamp << " value" << v <<
std::endl;
+ return v;
+}
+
+double perseus_source_c::get_adc_dither(size_t chan )
+{
+ double v = _adc_dither == true ? 1. : 0.;
+ std::cerr << "get_adc_dither: " << _adc_dither << " value" << v <<
std::endl;
+ return v;
+}
+
+
+double perseus_source_c::set_gain( double gain, const std::string & name,
size_t chan)
+{
+ if ( "Attenuator" == name ) {
+ return set_attenuator( gain, chan );
+ }
+ if ( "ADC preamp" == name ) {
+ return set_adc_preamp( gain, chan );
+ }
+ if ( "ADC dither" == name ) {
+ return set_adc_dither( gain, chan );
+ }
+ return gain;
+}
+
+
double perseus_source_c::get_gain( const std::string & name, size_t chan )
{
+ if ( "Attenuator" == name ) {
+ return get_attenuator( chan );
+ }
+ if ( "ADC preamp" == name ) {
+ return get_adc_preamp( chan );
+ }
+ if ( "ADC dither" == name ) {
+ return get_adc_dither( chan );
+ }
return 0.0;
}
+
+
std::vector< std::string > perseus_source_c::get_antennas( size_t chan )
{
std::vector< std::string > antennas;
- antennas += get_antenna( chan );
+ antennas += "RX";
+ antennas += "RX+PRESELECTOR";
return antennas;
}
std::string perseus_source_c::set_antenna( const std::string & antenna,
size_t chan )
{
+ if ( antenna == "RX" ) {
+ _preselector = false;
+ set_center_freq( get_center_freq() );
+ return "RX";
+ }
+ if ( antenna == "RX+PRESELECTOR" ) {
+ _preselector = true;
+ set_center_freq( get_center_freq() );
+ return "RX+PRESELECTOR";
+ }
return get_antenna( chan );
}
std::string perseus_source_c::get_antenna( size_t chan )
{
+ std::cerr << "perseus_source_c::get_antenna: " << chan << std::endl;
return "RX";
}
diff --git a/lib/perseus/perseus_source_c.h b/lib/perseus/perseus_source_c.h
index 9eda23d..934ff2b 100644
--- a/lib/perseus/perseus_source_c.h
+++ b/lib/perseus/perseus_source_c.h
@@ -87,9 +87,20 @@ public:
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 );
- double set_gain( double gain, size_t chan = 0 );
+
+ double set_gain( double gain, size_t chan = 0 ) {return gain; }
+ double get_gain( size_t chan = 0 ) {return _att_value; }
+
+ double set_attenuator( double gain, size_t chan = 0 );
+ double get_attenuator( size_t chan = 0 );
+
+ double set_adc_preamp( double gain, size_t chan = 0 );
+ double get_adc_preamp( size_t chan = 0 );
+
+ double set_adc_dither( double gain, size_t chan = 0 );
+ double get_adc_dither( 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 );
std::vector< std::string > get_antennas( size_t chan = 0 );
@@ -115,6 +126,10 @@ private:
bool _frun;
int _cnt;
+ double _att_value;
+ bool _adc_preamp;
+ bool _adc_dither;
+ bool _preselector;
};
#endif /* INCLUDED_PERSEUS_SOURCE_C_H */
--
1.9.1
.
QUIT
--
Andrea Montefusco IW0HDV
------------------------------------------
As my old boss, an Apollo veteran, would often remind us “It’s good to be
smart, but it’s better to be lucky.”
Wayne Hale, Space Shuttle Flight Director
1
0
[PATCH] gr-osmosdr Microtelecom Perseus HF receiver support
by andrea.montefusco@gmail.com 02 Feb '18
by andrea.montefusco@gmail.com 02 Feb '18
02 Feb '18
Hi maintainer and all,
Below you find a patch that implements Microtelecom Perseus support .
I have testes it using gqrx SDR software.
---
CMakeLists.txt | 1 +
cmake/Modules/FindLibPERSEUS.cmake | 24 ++
lib/CMakeLists.txt | 8 +
lib/airspy/airspy_source_c.cc | 5 +-
lib/config.h.in | 1 +
lib/device.cc | 8 +
lib/perseus/CMakeLists.txt | 37 +++
lib/perseus/perseus_source_c.cc | 507 +++++++++++++++++++++++++++++++++++++
lib/perseus/perseus_source_c.h | 120 +++++++++
lib/rtl/rtl_source_c.cc | 2 +-
lib/source_impl.cc | 18 +-
11 files changed, 727 insertions(+), 4 deletions(-)
create mode 100644 cmake/Modules/FindLibPERSEUS.cmake
create mode 100644 lib/perseus/CMakeLists.txt
create mode 100644 lib/perseus/perseus_source_c.cc
create mode 100644 lib/perseus/perseus_source_c.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 296456d..94acd4c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -173,6 +173,7 @@ find_package(Volk)
find_package(LibbladeRF)
find_package(SoapySDR NO_MODULE)
find_package(LibFreeSRP)
+find_package(LibPERSEUS)
find_package(Doxygen)
if(NOT GNURADIO_RUNTIME_FOUND)
diff --git a/cmake/Modules/FindLibPERSEUS.cmake b/cmake/Modules/FindLibPERSEUS.cmake
new file mode 100644
index 0000000..5477327
--- /dev/null
+++ b/cmake/Modules/FindLibPERSEUS.cmake
@@ -0,0 +1,24 @@
+INCLUDE(FindPkgConfig)
+PKG_CHECK_MODULES(PC_LIBPERSEUS libperseus-sdr)
+
+FIND_PATH(
+ LIBPERSEUS_INCLUDE_DIRS
+ NAMES perseus-sdr.h
+ HINTS $ENV{LIBPERSEUS_DIR}/include
+ ${PC_LIBPERSEUS_INCLUDEDIR}
+ PATHS /usr/local/include
+ /usr/include
+)
+
+FIND_LIBRARY(
+ LIBPERSEUS_LIBRARIES
+ NAMES perseus-sdr
+ HINTS $ENV{LIBPERSEUS_DIR}/lib
+ ${PC_LIBPERSEUS_LIBDIR}
+ PATHS /usr/local/lib
+ /usr/lib
+)
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBPERSEUS DEFAULT_MSG LIBPERSEUS_LIBRARIES LIBPERSEUS_INCLUDE_DIRS)
+MARK_AS_ADVANCED(LIBPERSEUS_LIBRARIES LIBPERSEUS_INCLUDE_DIRS)
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index c05b8d9..f555816 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -249,6 +249,14 @@ GR_INCLUDE_SUBDIRECTORY(freesrp)
endif(ENABLE_FREESRP)
########################################################################
+# Setup PERSEUS component
+########################################################################
+GR_REGISTER_COMPONENT("Microtelecom Perseus Receiver" ENABLE_PERSEUS LIBPERSEUS_FOUND)
+if(ENABLE_PERSEUS)
+GR_INCLUDE_SUBDIRECTORY(perseus)
+endif(ENABLE_PERSEUS)
+
+########################################################################
# Setup configuration file
########################################################################
ADD_DEFINITIONS(-DHAVE_CONFIG_H=1)
diff --git a/lib/airspy/airspy_source_c.cc b/lib/airspy/airspy_source_c.cc
index 50150e5..02fc6ab 100644
--- a/lib/airspy/airspy_source_c.cc
+++ b/lib/airspy/airspy_source_c.cc
@@ -350,9 +350,10 @@ osmosdr::meta_range_t airspy_source_c::get_sample_rates()
{
osmosdr::meta_range_t range;
- for (size_t i = 0; i < _sample_rates.size(); i++)
+ for (size_t i = 0; i < _sample_rates.size(); i++) {
+ std::cerr << "SR: [" << i << "]: " << _sample_rates[i].first << std::endl;
range += osmosdr::range_t( _sample_rates[i].first );
-
+ }
return range;
}
diff --git a/lib/config.h.in b/lib/config.h.in
index 42e72f1..dac7e39 100644
--- a/lib/config.h.in
+++ b/lib/config.h.in
@@ -19,6 +19,7 @@
#cmakedefine ENABLE_SOAPY
#cmakedefine ENABLE_REDPITAYA
#cmakedefine ENABLE_FREESRP
+#cmakedefine ENABLE_PERSEUS
//provide NAN define for MSVC older than VC12
#if defined(_MSC_VER) && (_MSC_VER < 1800)
diff --git a/lib/device.cc b/lib/device.cc
index 025a22b..df0d734 100644
--- a/lib/device.cc
+++ b/lib/device.cc
@@ -90,6 +90,10 @@
#include <freesrp_source_c.h>
#endif
+#ifdef ENABLE_PERSEUS
+#include <perseus_source_c.h>
+#endif
+
#include "arg_helpers.h"
using namespace osmosdr;
@@ -194,6 +198,10 @@ devices_t device::find(const device_t &hint)
BOOST_FOREACH( std::string dev, soapy_source_c::get_devices() )
devices.push_back( device_t(dev) );
#endif
+#ifdef ENABLE_PERSEUS
+ BOOST_FOREACH( std::string dev, perseus_source_c::get_devices() )
+ devices.push_back( device_t(dev) );
+#endif
/* software-only sources should be appended at the very end,
* hopefully resulting in hardware sources to be shown first
diff --git a/lib/perseus/CMakeLists.txt b/lib/perseus/CMakeLists.txt
new file mode 100644
index 0000000..f45a10d
--- /dev/null
+++ b/lib/perseus/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright 2017 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}
+ ${LIBPERSEUS_INCLUDE_DIRS}
+)
+
+set(perseus_srcs
+ ${CMAKE_CURRENT_SOURCE_DIR}/perseus_source_c.cc
+)
+
+########################################################################
+# Append gnuradio-osmosdr library sources
+########################################################################
+list(APPEND gr_osmosdr_srcs ${perseus_srcs})
+list(APPEND gr_osmosdr_libs ${LIBPERSEUS_LIBRARIES} ${GNURADIO_BLOCKS_LIBRARIES})
diff --git a/lib/perseus/perseus_source_c.cc b/lib/perseus/perseus_source_c.cc
new file mode 100644
index 0000000..0720da4
--- /dev/null
+++ b/lib/perseus/perseus_source_c.cc
@@ -0,0 +1,507 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2018 Andrea Montefusco IW0HDV
+ *
+ * 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 <stdexcept>
+#include <iostream>
+#include <algorithm>
+
+#include <boost/assign.hpp>
+#include <boost/format.hpp>
+#include <boost/detail/endian.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/thread/thread.hpp>
+
+#include <gnuradio/io_signature.h>
+
+#include "perseus_source_c.h"
+#include "arg_helpers.h"
+
+
+
+using namespace boost::assign;
+
+#define PERSEUS_FORMAT_ERROR(ret, msg) \
+ boost::str( boost::format(msg " (%1%)") % ret )
+
+#define PERSEUS_THROW_ON_ERROR(ret, msg) \
+ if ( ret != PERSEUS_NOERROR ) \
+ { \
+ throw std::runtime_error( PERSEUS_FORMAT_ERROR(ret, msg) ); \
+ }
+
+#define PERSEUS_FUNC_STR(func, arg) \
+ boost::str(boost::format(func "(%1%)") % arg) + " has failed"
+
+perseus_source_c_sptr make_perseus_source_c (const std::string & args)
+{
+ return gnuradio::get_initial_sptr(new perseus_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
+ */
+perseus_source_c::perseus_source_c (const std::string &args)
+ : gr::sync_block ("perseus_source_c",
+ gr::io_signature::make(MIN_IN, MAX_IN, sizeof (gr_complex)),
+ gr::io_signature::make(MIN_OUT, MAX_OUT, sizeof (gr_complex))),
+ _dev(NULL),
+ _sample_rate(0),
+ _center_freq(0),
+ _freq_corr(0),
+ _frun(false),
+ _cnt(0)
+{
+ //int ret;
+
+ dict_t dict = params_to_dict(args);
+
+ _dev = NULL;
+
+ // Set debug info dumped to stderr to the maximum verbose level
+ perseus_set_debug(3);
+
+ //fprintf (stderr, "Revision: %s\n", git_revision);
+ //fprintf (stderr, "SAMPLE RATE: %d\n", sr);
+ //fprintf (stderr, "NBUF: %d BUF SIZE: %d TOTAL BUFFER LENGTH: %d\n", nb, bs, nb*bs);
+
+ // Check how many Perseus receivers are connected to the system
+ int num_perseus = perseus_init();
+ std::cerr << "## Microtelecom Perseus ctor: " << num_perseus << " Perseus receivers found." << std::endl;
+
+ if (num_perseus==0) {
+ perseus_exit();
+ PERSEUS_THROW_ON_ERROR(PERSEUS_DEVNOTFOUND, "No Perseus receivers detected");
+ }
+ // Open the first one...
+ if ((_dev = perseus_open(0)) == NULL) {
+ PERSEUS_THROW_ON_ERROR(PERSEUS_DEVNOTFOUND, "Unable to open Perseus device");
+ }
+ // Download the standard firmware to the unit
+ std::cerr << "Downloading firmware..." << std::endl;
+ if (int rc = perseus_firmware_download(_dev,NULL)<0) {
+ PERSEUS_THROW_ON_ERROR (rc, "firmware download error");
+ }
+
+ // Dump some information about the receiver (S/N and HW rev)
+ int flag;
+ perseus_is_preserie(_dev, &flag);
+ if (flag != 0)
+ fprintf(stderr, "The device is a preserie unit");
+ else {
+ eeprom_prodid prodid;
+ if (perseus_get_product_id(_dev,&prodid)<0)
+ fprintf(stderr, "get product id error: %s", perseus_errorstr());
+ else
+ fprintf(stderr, "Receiver S/N: %05d-%02hX%02hX-%02hX%02hX-%02hX%02hX - HW Release:%hd.%hd\n",
+ (uint16_t) prodid.sn,
+ (uint16_t) prodid.signature[5],
+ (uint16_t) prodid.signature[4],
+ (uint16_t) prodid.signature[3],
+ (uint16_t) prodid.signature[2],
+ (uint16_t) prodid.signature[1],
+ (uint16_t) prodid.signature[0],
+ (uint16_t) prodid.hwrel,
+ (uint16_t) prodid.hwver);
+ }
+ // Printing all sampling rates available .....
+ {
+ int buf[BUFSIZ];
+
+ if (perseus_get_sampling_rates (_dev, buf, sizeof(buf)/sizeof(buf[0])) < 0) {
+ fprintf(stderr, "get sampling rates error: %s\n", perseus_errorstr());
+ } else {
+ int i = 0;
+ while (buf[i]) {
+ fprintf(stderr, "#%d: sample rate: %d\n", i, buf[i]);
+
+ _sample_rates.push_back( std::pair<double, uint32_t>( buf[i], i ) );
+ i++;
+ }
+ /* since they may (and will) give us an unsorted array we have to sort it here
+ * to play nice with the monotonic requirement of meta-range later on */
+ std::sort(_sample_rates.begin(), _sample_rates.end());
+
+ for (size_t i = 0; i < _sample_rates.size(); i++)
+ std::cerr << boost::format("%gM ") % (_sample_rates[i].first / 1e6);
+
+ std::cerr << std::endl;
+ }
+ }
+ // Configure the receiver for 2 MS/s operations
+ fprintf(stderr, "Configuring FPGA...\n");
+ if (int ret = perseus_set_sampling_rate(_dev, 96000) < 0) { // specify the sampling rate value in Samples/second
+ std::cerr << "fpga configuration error: " << perseus_errorstr() << std::endl;
+ PERSEUS_THROW_ON_ERROR(ret, "Perseus fpga configuration error");
+ }
+
+ perseus_set_attenuator_in_db(_dev, PERSEUS_ATT_0DB);
+ // Enable ADC Dither, Disable ADC Preamp
+ perseus_set_adc(_dev, true, false);
+ // set the NCO frequency in the middle of the range allowed
+ set_center_freq( (get_freq_range().start() + get_freq_range().stop()) / 2.0 );
+ set_sample_rate( get_sample_rates().start() );
+
+ _fifo = new boost::circular_buffer<gr_complex>(5000000);
+ if (!_fifo) {
+ throw std::runtime_error( std::string(__FUNCTION__) + " " +
+ "Failed to allocate a sample FIFO!" );
+ }
+}
+
+/*
+ * Our virtual destructor.
+ */
+perseus_source_c::~perseus_source_c ()
+{
+ if (_dev) {
+ if (_frun) stop();
+ int ret = perseus_close(_dev);
+ if ( ret != PERSEUS_NOERROR ) {
+ std::cerr << PERSEUS_FORMAT_ERROR(ret, "Failed to close Perseus") << std::endl;
+ } else
+ _dev = NULL;
+ }
+ perseus_exit();
+
+ if (_fifo) {
+ delete _fifo;
+ _fifo = NULL;
+ }
+}
+
+
+typedef union {
+ struct {
+ int32_t i;
+ int32_t q;
+ } __attribute__((__packed__)) iq;
+ struct {
+ uint8_t i1;
+ uint8_t i2;
+ uint8_t i3;
+ uint8_t i4;
+ uint8_t q1;
+ uint8_t q2;
+ uint8_t q3;
+ uint8_t q4;
+ } __attribute__((__packed__)) ;
+} iq_sample;
+
+
+int perseus_source_c::perseus_rx_callback(void *buf, int buf_size)
+{
+ size_t i, n_avail, to_copy, num_samples;
+
+ uint8_t *samplebuf = (uint8_t*)buf;
+
+ num_samples = buf_size/6;
+
+ if (buf_size % 6)
+ std::cerr << "FATAL !!!!!!! not on boundary" << std::endl;
+
+ iq_sample s;
+
+ _fifo_lock.lock();
+
+ n_avail = _fifo->capacity() - _fifo->size();
+ to_copy = (n_avail < num_samples ? n_avail : num_samples);
+
+ for (i = 0; i < to_copy; i++ ) {
+
+ s.i1 = s.q1 = 0;
+ s.i2 = *samplebuf++;
+ s.i3 = *samplebuf++;
+ s.i4 = *samplebuf++;
+ s.q2 = *samplebuf++;
+ s.q3 = *samplebuf++;
+ s.q4 = *samplebuf++;
+
+ // convert 24 bit two's complements integers to float in [-1.0 - +1.0] range
+
+ /* Push sample to the fifo */
+ _fifo->push_back( gr_complex( (float)(s.iq.i) / (INT_MAX - 256), (float)(s.iq.q) / (INT_MAX - 256) ) );
+
+ }
+ // keep a counter of processed bytes
+ _cnt += buf_size;
+
+ _fifo_lock.unlock();
+
+ /* We have made some new samples available to the consumer in work() */
+ if (to_copy) {
+ //std::cerr << "+" << std::flush;
+ _samp_avail.notify_one();
+ }
+
+ /* Indicate overrun, if necessary */
+ if (to_copy < num_samples)
+ std::cerr << "O" << std::flush;
+
+ return 0; // TODO: return -1 on error/stop
+}
+
+// static method used as callback from the libperseus-sdr library
+int perseus_source_c::_perseus_rx_callback(void *buf, int buf_size, void *extra)
+{
+ perseus_source_c *obj = (perseus_source_c *)extra;
+
+ return obj->perseus_rx_callback((float *)buf, buf_size);
+
+ return 0;
+}
+
+
+bool perseus_source_c::start()
+{
+ int ret;
+
+ if ( ! _dev )
+ return false;
+
+ std::cerr << "***** START COUNTER: " << _cnt
+ << " mod 6 " << (_cnt % 6) << std::endl;
+
+ _frun = true;
+
+ // in order to guarantee samples stream synchronization
+ // re-set the sample rate each time the stream is started
+ set_sample_rate( get_sample_rate() );
+
+ ret = perseus_start_async_input(_dev,
+ 6 * 1024 * 2 /* buffer size */,
+ perseus_source_c::_perseus_rx_callback,
+ (void *)this);
+ if (ret != PERSEUS_NOERROR) {
+ std::cerr << "Failed to start RX streaming ("
+ << ret << ")" << std::endl;
+ return false;
+ } else
+ return true;
+}
+
+bool perseus_source_c::stop()
+{
+ if ( ! _dev )
+ return false;
+
+ _frun = false;
+
+ std::cerr << "***** STOP COUNTER: " << _cnt
+ << " mod 6 " << (_cnt % 6) << std::endl;
+
+ int ret = perseus_stop_async_input(_dev);
+
+ if ( ret != PERSEUS_NOERROR ) {
+ std::cerr << "Failed to stop RX streaming (" << ret << ")" << std::endl;
+ return false;
+ } else
+ return true;
+}
+
+int perseus_source_c::work( int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items )
+{
+ gr_complex *out = (gr_complex *)output_items[0];
+
+ if ( _frun == false ) return WORK_DONE;
+
+ boost::unique_lock<boost::mutex> lock(_fifo_lock);
+
+ /* Wait until we have the requested number of samples */
+ int n_samples_avail = _fifo->size();
+
+ while (n_samples_avail < noutput_items) {
+ _samp_avail.wait(lock);
+ n_samples_avail = _fifo->size();
+ }
+
+ for(int i = 0; i < noutput_items; ++i) {
+ out[i] = _fifo->at(0);
+ _fifo->pop_front();
+ }
+
+ return noutput_items;
+}
+
+std::vector<std::string> perseus_source_c::get_devices()
+{
+ std::vector<std::string> devices;
+ std::string label;
+
+ std::string args = "perseus=0,label='Perseus'";
+ devices.push_back( args );
+
+ return devices;
+}
+
+size_t perseus_source_c::get_num_channels()
+{
+ return 1;
+}
+
+osmosdr::meta_range_t perseus_source_c::get_sample_rates()
+{
+ osmosdr::meta_range_t range;
+
+ for (size_t i = 0; i < _sample_rates.size(); i++) {
+ range += osmosdr::range_t( _sample_rates[i].first );
+ std::cerr << "SR: " << i << " : ("
+ << _sample_rates[i].first << ")" << std::endl;
+ }
+ return range;
+}
+
+double perseus_source_c::set_sample_rate( double rate )
+{
+ int ret;
+ if ((ret = perseus_set_sampling_rate(_dev, rate)) < 0) {
+ PERSEUS_THROW_ON_ERROR(ret, "Unable to set sample rate.");
+ } else
+ _sample_rate = rate;
+
+ return get_sample_rate();
+}
+
+double perseus_source_c::get_sample_rate()
+{
+ return _sample_rate;
+}
+
+osmosdr::freq_range_t perseus_source_c::get_freq_range( size_t chan )
+{
+ osmosdr::freq_range_t range;
+
+ range += osmosdr::range_t( PERSEUS_DDC_FREQ_MIN, PERSEUS_DDC_FREQ_MAX );
+
+ return range;
+}
+
+double perseus_source_c::set_center_freq( double freq, size_t chan )
+{
+ int ret;
+
+ if (_dev) {
+ ret =
+ perseus_set_ddc_center_freq(_dev,
+ (uint64_t)(freq * (1.0 + (_freq_corr * 10e-6))),
+ 1);
+ if ( ret == PERSEUS_NOERROR ) {
+ _center_freq = freq;
+ } else {
+ PERSEUS_THROW_ON_ERROR( ret, PERSEUS_FUNC_STR( "perseus_set_ddc_center_freq", freq ) )
+ }
+ }
+
+ return get_center_freq( chan );
+}
+
+double perseus_source_c::get_center_freq( size_t chan )
+{
+ return _center_freq;
+}
+
+double perseus_source_c::set_freq_corr( double ppm, size_t chan )
+{
+ return _freq_corr = ppm;
+}
+
+double perseus_source_c::get_freq_corr( size_t chan )
+{
+ return _freq_corr;
+}
+
+std::vector<std::string> perseus_source_c::get_gain_names( size_t chan )
+{
+ return {};
+}
+
+osmosdr::gain_range_t perseus_source_c::get_gain_range( size_t chan )
+{
+ return osmosdr::gain_range_t();
+}
+
+osmosdr::gain_range_t perseus_source_c::get_gain_range( const std::string & name, size_t chan )
+{
+ return osmosdr::gain_range_t();
+}
+
+
+double perseus_source_c::set_gain( double gain, size_t chan )
+{
+ return gain;
+}
+
+double perseus_source_c::set_gain( double gain, const std::string & name, size_t chan)
+{
+ return gain;
+}
+
+double perseus_source_c::get_gain( size_t chan )
+{
+ return 0.0;
+}
+
+double perseus_source_c::get_gain( const std::string & name, size_t chan )
+{
+ return 0.0;
+}
+
+std::vector< std::string > perseus_source_c::get_antennas( size_t chan )
+{
+ std::vector< std::string > antennas;
+
+ antennas += get_antenna( chan );
+
+ return antennas;
+}
+
+std::string perseus_source_c::set_antenna( const std::string & antenna, size_t chan )
+{
+ return get_antenna( chan );
+}
+
+std::string perseus_source_c::get_antenna( size_t chan )
+{
+ return "RX";
+}
diff --git a/lib/perseus/perseus_source_c.h b/lib/perseus/perseus_source_c.h
new file mode 100644
index 0000000..9eda23d
--- /dev/null
+++ b/lib/perseus/perseus_source_c.h
@@ -0,0 +1,120 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2013 Dimitri Stolnikov <horiz0n(a)gmx.net>
+ *
+ * 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.
+ */
+#ifndef INCLUDED_PERSEUS_SOURCE_C_H
+#define INCLUDED_PERSEUS_SOURCE_C_H
+
+#include <boost/circular_buffer.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition_variable.hpp>
+
+#include <gnuradio/sync_block.h>
+
+#include "source_iface.h"
+#include <perseus-sdr.h>
+
+class perseus_source_c;
+
+typedef boost::shared_ptr<perseus_source_c> perseus_source_c_sptr;
+
+/*!
+ * \brief Return a shared_ptr to a new instance of perseus_source_c.
+ *
+ * To avoid accidental use of raw pointers, perseus_source_c's
+ * constructor is private. make_perseus_source_c is the public
+ * interface for creating new instances.
+ */
+perseus_source_c_sptr make_perseus_source_c (const std::string & args = "");
+
+/*!
+ * \brief Provides a stream of complex samples.
+ * \ingroup block
+ */
+class perseus_source_c :
+ public gr::sync_block,
+ public source_iface
+{
+private:
+ // The friend declaration allows make_perseus_source_c to
+ // access the private constructor.
+
+ friend perseus_source_c_sptr make_perseus_source_c (const std::string & args);
+
+ perseus_source_c (const std::string & args);
+
+public:
+ ~perseus_source_c ();
+
+ bool start();
+ bool stop();
+
+ 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 );
+ 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 );
+
+ 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 );
+
+
+private:
+ static int _perseus_rx_callback(void *buf, int buf_size, void *extra);
+
+ int perseus_rx_callback(void *samples, int sample_count);
+
+ perseus_descr *_dev;
+
+ boost::circular_buffer<gr_complex> *_fifo;
+ boost::mutex _fifo_lock;
+ boost::condition_variable _samp_avail;
+
+ std::vector< std::pair<double, uint32_t> > _sample_rates;
+ double _sample_rate;
+ double _center_freq;
+ double _freq_corr;
+
+ bool _frun;
+ int _cnt;
+};
+
+#endif /* INCLUDED_PERSEUS_SOURCE_C_H */
diff --git a/lib/rtl/rtl_source_c.cc b/lib/rtl/rtl_source_c.cc
index a371464..eb7042e 100644
--- a/lib/rtl/rtl_source_c.cc
+++ b/lib/rtl/rtl_source_c.cc
@@ -221,7 +221,7 @@ rtl_source_c::rtl_source_c (const std::string &args)
throw std::runtime_error("Failed to enable offset tuning.");
}
- ret = rtlsdr_set_bias_tee(_dev, bias_tee);
+// ret = rtlsdr_set_bias_tee(_dev, bias_tee);
if (ret < 0)
throw std::runtime_error("Failed to set bias tee.");
diff --git a/lib/source_impl.cc b/lib/source_impl.cc
index a28f314..ead4ef7 100644
--- a/lib/source_impl.cc
+++ b/lib/source_impl.cc
@@ -92,6 +92,9 @@
#include <freesrp_source_c.h>
#endif
+#ifdef ENABLE_PERSEUS
+#include <perseus_source_c.h>
+#endif
#include "arg_helpers.h"
#include "source_impl.h"
@@ -171,6 +174,9 @@ source_impl::source_impl( const std::string &args )
#ifdef ENABLE_FREESRP
dev_types.push_back("freesrp");
#endif
+#ifdef ENABLE_PERSEUS
+ dev_types.push_back("perseus");
+#endif
std::cerr << "gr-osmosdr "
<< GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") "
<< "gnuradio " << gr::version() << std::endl;
@@ -252,7 +258,10 @@ source_impl::source_impl( const std::string &args )
BOOST_FOREACH( std::string dev, freesrp_source_c::get_devices() )
dev_list.push_back( dev );
#endif
-
+#ifdef ENABLE_PERSEUS
+ BOOST_FOREACH( std::string dev, perseus_source_c::get_devices() )
+ dev_list.push_back( dev );
+#endif
// std::cerr << std::endl;
// BOOST_FOREACH( std::string dev, dev_list )
// std::cerr << "'" << dev << "'" << std::endl;
@@ -383,6 +392,13 @@ source_impl::source_impl( const std::string &args )
}
#endif
+#ifdef ENABLE_PERSEUS
+ if ( dict.count("perseus") ) {
+ perseus_source_c_sptr src = make_perseus_source_c( arg );
+ block = src; iface = src.get();
+ }
+#endif
+
if ( iface != NULL && long(block.get()) != 0 ) {
_devs.push_back( iface );
--
1.9.1
>From 1b6cf50a7a6074754717b0270757931c379561bc Mon Sep 17 00:00:00 2001
From: Andrea Montefusco IW0HDV <andrew(a)montefusco.com>
Date: Thu, 1 Feb 2018 17:33:39 +0100
Subject: More hardware controls added (attenuator, ADC preamp and dither,
preselector)
---
lib/perseus/perseus_source_c.cc | 136 ++++++++++++++++++++++++++++++++++++----
lib/perseus/perseus_source_c.h | 19 +++++-
2 files changed, 142 insertions(+), 13 deletions(-)
diff --git a/lib/perseus/perseus_source_c.cc b/lib/perseus/perseus_source_c.cc
index 0720da4..89674b1 100644
--- a/lib/perseus/perseus_source_c.cc
+++ b/lib/perseus/perseus_source_c.cc
@@ -89,7 +89,11 @@ perseus_source_c::perseus_source_c (const std::string &args)
_center_freq(0),
_freq_corr(0),
_frun(false),
- _cnt(0)
+ _cnt(0),
+ _att_value(0),
+ _adc_preamp(false),
+ _adc_dither(false),
+ _preselector(false)
{
//int ret;
@@ -174,9 +178,9 @@ perseus_source_c::perseus_source_c (const std::string &args)
PERSEUS_THROW_ON_ERROR(ret, "Perseus fpga configuration error");
}
- perseus_set_attenuator_in_db(_dev, PERSEUS_ATT_0DB);
+ perseus_set_attenuator_in_db(_dev, _att_value);
// Enable ADC Dither, Disable ADC Preamp
- perseus_set_adc(_dev, true, false);
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ? 1:0);
// set the NCO frequency in the middle of the range allowed
set_center_freq( (get_freq_range().start() + get_freq_range().stop()) / 2.0 );
set_sample_rate( get_sample_rates().start() );
@@ -425,7 +429,7 @@ double perseus_source_c::set_center_freq( double freq, size_t chan )
ret =
perseus_set_ddc_center_freq(_dev,
(uint64_t)(freq * (1.0 + (_freq_corr * 10e-6))),
- 1);
+ _preselector == true ? 1:0);
if ( ret == PERSEUS_NOERROR ) {
_center_freq = freq;
} else {
@@ -453,55 +457,165 @@ double perseus_source_c::get_freq_corr( size_t chan )
std::vector<std::string> perseus_source_c::get_gain_names( size_t chan )
{
- return {};
+ std::vector< std::string > names;
+
+ names += "Attenuator";
+ names += "ADC preamp";
+ names += "ADC dither";
+
+ return names;
}
+//
+// FIXME
+//
osmosdr::gain_range_t perseus_source_c::get_gain_range( size_t chan )
{
- return osmosdr::gain_range_t();
+ return get_gain_range( "Attenuator", chan );
}
osmosdr::gain_range_t perseus_source_c::get_gain_range( const std::string & name, size_t chan )
{
+ if ( "Attenuator" == name ) {
+ return osmosdr::gain_range_t( -30, 0, 10 );
+ }
+ if ( "ADC preamp" == name ) {
+ return osmosdr::gain_range_t( 0, 6, 6 );
+ }
+ if ( "ADC dither" == name ) {
+ return osmosdr::gain_range_t( 0, 1, 1 );
+ }
+
return osmosdr::gain_range_t();
}
-double perseus_source_c::set_gain( double gain, size_t chan )
+double perseus_source_c::set_attenuator( double gain, size_t chan )
{
+ int ret;
+
+ gain = - gain;
+ if (_dev && _att_value != gain) {
+ ret = perseus_set_attenuator_in_db (_dev, gain);
+
+ if ( PERSEUS_NOERROR == ret ) {
+ _att_value = gain;
+ } else {
+ std::cerr << "ERROR: perseus_set_attenuator_in_db: " << gain << std::endl;
+ }
+ }
+
+ return _att_value;
+}
+
+double perseus_source_c::set_adc_preamp( double gain, size_t chan )
+{
+ std::cerr << "set_adc_preamp: " << gain << std::endl;
+
+ if (_dev) {
+ _adc_preamp = (gain != 0. ? true : false);
+ int ret =
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ? 1:0);
+ if ( PERSEUS_NOERROR != ret ) {
+ std::cerr << "ERROR: perseus_set_adc" << ret << std::endl;
+ }
+ }
return gain;
}
-double perseus_source_c::set_gain( double gain, const std::string & name, size_t chan)
+double perseus_source_c::set_adc_dither( double gain, size_t chan )
{
+ std::cerr << "set_adc_dither: " << gain << std::endl;
+
+ if (_dev) {
+ _adc_dither = (gain != 0. ? true : false);
+ int ret =
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ? 1:0);
+ if ( PERSEUS_NOERROR != ret ) {
+ std::cerr << "ERROR: perseus_set_adc" << ret << std::endl;
+ }
+ }
return gain;
}
-double perseus_source_c::get_gain( size_t chan )
+double perseus_source_c::get_attenuator( size_t chan )
{
- return 0.0;
+ return _att_value;
}
+double perseus_source_c::get_adc_preamp(size_t chan )
+{
+ double v = _adc_preamp == true ? 6. : 0.;
+ std::cerr << "get_adc_preamp: " << _adc_preamp << " value" << v << std::endl;
+ return v;
+}
+
+double perseus_source_c::get_adc_dither(size_t chan )
+{
+ double v = _adc_dither == true ? 1. : 0.;
+ std::cerr << "get_adc_dither: " << _adc_dither << " value" << v << std::endl;
+ return v;
+}
+
+
+double perseus_source_c::set_gain( double gain, const std::string & name, size_t chan)
+{
+ if ( "Attenuator" == name ) {
+ return set_attenuator( gain, chan );
+ }
+ if ( "ADC preamp" == name ) {
+ return set_adc_preamp( gain, chan );
+ }
+ if ( "ADC dither" == name ) {
+ return set_adc_dither( gain, chan );
+ }
+ return gain;
+}
+
+
double perseus_source_c::get_gain( const std::string & name, size_t chan )
{
+ if ( "Attenuator" == name ) {
+ return get_attenuator( chan );
+ }
+ if ( "ADC preamp" == name ) {
+ return get_adc_preamp( chan );
+ }
+ if ( "ADC dither" == name ) {
+ return get_adc_dither( chan );
+ }
return 0.0;
}
+
+
std::vector< std::string > perseus_source_c::get_antennas( size_t chan )
{
std::vector< std::string > antennas;
- antennas += get_antenna( chan );
+ antennas += "RX";
+ antennas += "RX+PRESELECTOR";
return antennas;
}
std::string perseus_source_c::set_antenna( const std::string & antenna, size_t chan )
{
+ if ( antenna == "RX" ) {
+ _preselector = false;
+ set_center_freq( get_center_freq() );
+ return "RX";
+ }
+ if ( antenna == "RX+PRESELECTOR" ) {
+ _preselector = true;
+ set_center_freq( get_center_freq() );
+ return "RX+PRESELECTOR";
+ }
return get_antenna( chan );
}
std::string perseus_source_c::get_antenna( size_t chan )
{
+ std::cerr << "perseus_source_c::get_antenna: " << chan << std::endl;
return "RX";
}
diff --git a/lib/perseus/perseus_source_c.h b/lib/perseus/perseus_source_c.h
index 9eda23d..934ff2b 100644
--- a/lib/perseus/perseus_source_c.h
+++ b/lib/perseus/perseus_source_c.h
@@ -87,9 +87,20 @@ public:
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 );
- double set_gain( double gain, size_t chan = 0 );
+
+ double set_gain( double gain, size_t chan = 0 ) {return gain; }
+ double get_gain( size_t chan = 0 ) {return _att_value; }
+
+ double set_attenuator( double gain, size_t chan = 0 );
+ double get_attenuator( size_t chan = 0 );
+
+ double set_adc_preamp( double gain, size_t chan = 0 );
+ double get_adc_preamp( size_t chan = 0 );
+
+ double set_adc_dither( double gain, size_t chan = 0 );
+ double get_adc_dither( 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 );
std::vector< std::string > get_antennas( size_t chan = 0 );
@@ -115,6 +126,10 @@ private:
bool _frun;
int _cnt;
+ double _att_value;
+ bool _adc_preamp;
+ bool _adc_dither;
+ bool _preselector;
};
#endif /* INCLUDED_PERSEUS_SOURCE_C_H */
--
1.9.1
.
QUIT
1
0
02 Feb '18
Hi maintainer and all,
Below you find a patch that implements Microtelecom Perseus support in
gr-osmosdr.
I have tested it in gqrx SDR software.
---
CMakeLists.txt | 1 +
cmake/Modules/FindLibPERSEUS.cmake | 24 ++
lib/CMakeLists.txt | 8 +
lib/airspy/airspy_source_c.cc | 5 +-
lib/config.h.in | 1 +
lib/device.cc | 8 +
lib/perseus/CMakeLists.txt | 37 +++
lib/perseus/perseus_source_c.cc | 507 ++++++++++++++++++++++++++++++
+++++++
lib/perseus/perseus_source_c.h | 120 +++++++++
lib/rtl/rtl_source_c.cc | 2 +-
lib/source_impl.cc | 18 +-
11 files changed, 727 insertions(+), 4 deletions(-)
create mode 100644 cmake/Modules/FindLibPERSEUS.cmake
create mode 100644 lib/perseus/CMakeLists.txt
create mode 100644 lib/perseus/perseus_source_c.cc
create mode 100644 lib/perseus/perseus_source_c.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 296456d..94acd4c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -173,6 +173,7 @@ find_package(Volk)
find_package(LibbladeRF)
find_package(SoapySDR NO_MODULE)
find_package(LibFreeSRP)
+find_package(LibPERSEUS)
find_package(Doxygen)
if(NOT GNURADIO_RUNTIME_FOUND)
diff --git a/cmake/Modules/FindLibPERSEUS.cmake b/cmake/Modules/
FindLibPERSEUS.cmake
new file mode 100644
index 0000000..5477327
--- /dev/null
+++ b/cmake/Modules/FindLibPERSEUS.cmake
@@ -0,0 +1,24 @@
+INCLUDE(FindPkgConfig)
+PKG_CHECK_MODULES(PC_LIBPERSEUS libperseus-sdr)
+
+FIND_PATH(
+ LIBPERSEUS_INCLUDE_DIRS
+ NAMES perseus-sdr.h
+ HINTS $ENV{LIBPERSEUS_DIR}/include
+ ${PC_LIBPERSEUS_INCLUDEDIR}
+ PATHS /usr/local/include
+ /usr/include
+)
+
+FIND_LIBRARY(
+ LIBPERSEUS_LIBRARIES
+ NAMES perseus-sdr
+ HINTS $ENV{LIBPERSEUS_DIR}/lib
+ ${PC_LIBPERSEUS_LIBDIR}
+ PATHS /usr/local/lib
+ /usr/lib
+)
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBPERSEUS DEFAULT_MSG
LIBPERSEUS_LIBRARIES LIBPERSEUS_INCLUDE_DIRS)
+MARK_AS_ADVANCED(LIBPERSEUS_LIBRARIES LIBPERSEUS_INCLUDE_DIRS)
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index c05b8d9..f555816 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -249,6 +249,14 @@ GR_INCLUDE_SUBDIRECTORY(freesrp)
endif(ENABLE_FREESRP)
########################################################################
+# Setup PERSEUS component
+########################################################################
+GR_REGISTER_COMPONENT("Microtelecom Perseus Receiver" ENABLE_PERSEUS
LIBPERSEUS_FOUND)
+if(ENABLE_PERSEUS)
+GR_INCLUDE_SUBDIRECTORY(perseus)
+endif(ENABLE_PERSEUS)
+
+########################################################################
# Setup configuration file
########################################################################
ADD_DEFINITIONS(-DHAVE_CONFIG_H=1)
diff --git a/lib/airspy/airspy_source_c.cc b/lib/airspy/airspy_source_c.cc
index 50150e5..02fc6ab 100644
--- a/lib/airspy/airspy_source_c.cc
+++ b/lib/airspy/airspy_source_c.cc
@@ -350,9 +350,10 @@ osmosdr::meta_range_t airspy_source_c::get_sample_
rates()
{
osmosdr::meta_range_t range;
- for (size_t i = 0; i < _sample_rates.size(); i++)
+ for (size_t i = 0; i < _sample_rates.size(); i++) {
+ std::cerr << "SR: [" << i << "]: " << _sample_rates[i].first <<
std::endl;
range += osmosdr::range_t( _sample_rates[i].first );
-
+ }
return range;
}
diff --git a/lib/config.h.in b/lib/config.h.in
index 42e72f1..dac7e39 100644
--- a/lib/config.h.in
+++ b/lib/config.h.in
@@ -19,6 +19,7 @@
#cmakedefine ENABLE_SOAPY
#cmakedefine ENABLE_REDPITAYA
#cmakedefine ENABLE_FREESRP
+#cmakedefine ENABLE_PERSEUS
//provide NAN define for MSVC older than VC12
#if defined(_MSC_VER) && (_MSC_VER < 1800)
diff --git a/lib/device.cc b/lib/device.cc
index 025a22b..df0d734 100644
--- a/lib/device.cc
+++ b/lib/device.cc
@@ -90,6 +90,10 @@
#include <freesrp_source_c.h>
#endif
+#ifdef ENABLE_PERSEUS
+#include <perseus_source_c.h>
+#endif
+
#include "arg_helpers.h"
using namespace osmosdr;
@@ -194,6 +198,10 @@ devices_t device::find(const device_t &hint)
BOOST_FOREACH( std::string dev, soapy_source_c::get_devices() )
devices.push_back( device_t(dev) );
#endif
+#ifdef ENABLE_PERSEUS
+ BOOST_FOREACH( std::string dev, perseus_source_c::get_devices() )
+ devices.push_back( device_t(dev) );
+#endif
/* software-only sources should be appended at the very end,
* hopefully resulting in hardware sources to be shown first
diff --git a/lib/perseus/CMakeLists.txt b/lib/perseus/CMakeLists.txt
new file mode 100644
index 0000000..f45a10d
--- /dev/null
+++ b/lib/perseus/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright 2017 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}
+ ${LIBPERSEUS_INCLUDE_DIRS}
+)
+
+set(perseus_srcs
+ ${CMAKE_CURRENT_SOURCE_DIR}/perseus_source_c.cc
+)
+
+########################################################################
+# Append gnuradio-osmosdr library sources
+########################################################################
+list(APPEND gr_osmosdr_srcs ${perseus_srcs})
+list(APPEND gr_osmosdr_libs ${LIBPERSEUS_LIBRARIES}
${GNURADIO_BLOCKS_LIBRARIES})
diff --git a/lib/perseus/perseus_source_c.cc b/lib/perseus/perseus_source_
c.cc
new file mode 100644
index 0000000..0720da4
--- /dev/null
+++ b/lib/perseus/perseus_source_c.cc
@@ -0,0 +1,507 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2018 Andrea Montefusco IW0HDV
+ *
+ * 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 <stdexcept>
+#include <iostream>
+#include <algorithm>
+
+#include <boost/assign.hpp>
+#include <boost/format.hpp>
+#include <boost/detail/endian.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/thread/thread.hpp>
+
+#include <gnuradio/io_signature.h>
+
+#include "perseus_source_c.h"
+#include "arg_helpers.h"
+
+
+
+using namespace boost::assign;
+
+#define PERSEUS_FORMAT_ERROR(ret, msg) \
+ boost::str( boost::format(msg " (%1%)") % ret )
+
+#define PERSEUS_THROW_ON_ERROR(ret, msg) \
+ if ( ret != PERSEUS_NOERROR ) \
+ { \
+ throw std::runtime_error( PERSEUS_FORMAT_ERROR(ret, msg) ); \
+ }
+
+#define PERSEUS_FUNC_STR(func, arg) \
+ boost::str(boost::format(func "(%1%)") % arg) + " has failed"
+
+perseus_source_c_sptr make_perseus_source_c (const std::string & args)
+{
+ return gnuradio::get_initial_sptr(new perseus_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
+ */
+perseus_source_c::perseus_source_c (const std::string &args)
+ : gr::sync_block ("perseus_source_c",
+ gr::io_signature::make(MIN_IN, MAX_IN, sizeof (gr_complex)),
+ gr::io_signature::make(MIN_OUT, MAX_OUT, sizeof (gr_complex))),
+ _dev(NULL),
+ _sample_rate(0),
+ _center_freq(0),
+ _freq_corr(0),
+ _frun(false),
+ _cnt(0)
+{
+ //int ret;
+
+ dict_t dict = params_to_dict(args);
+
+ _dev = NULL;
+
+ // Set debug info dumped to stderr to the maximum verbose level
+ perseus_set_debug(3);
+
+ //fprintf (stderr, "Revision: %s\n", git_revision);
+ //fprintf (stderr, "SAMPLE RATE: %d\n", sr);
+ //fprintf (stderr, "NBUF: %d BUF SIZE: %d TOTAL BUFFER LENGTH: %d\n",
nb, bs, nb*bs);
+
+ // Check how many Perseus receivers are connected to the system
+ int num_perseus = perseus_init();
+ std::cerr << "## Microtelecom Perseus ctor: " << num_perseus << "
Perseus receivers found." << std::endl;
+
+ if (num_perseus==0) {
+ perseus_exit();
+ PERSEUS_THROW_ON_ERROR(PERSEUS_DEVNOTFOUND, "No Perseus receivers
detected");
+ }
+ // Open the first one...
+ if ((_dev = perseus_open(0)) == NULL) {
+ PERSEUS_THROW_ON_ERROR(PERSEUS_DEVNOTFOUND, "Unable to open Perseus
device");
+ }
+ // Download the standard firmware to the unit
+ std::cerr << "Downloading firmware..." << std::endl;
+ if (int rc = perseus_firmware_download(_dev,NULL)<0) {
+ PERSEUS_THROW_ON_ERROR (rc, "firmware download error");
+ }
+
+ // Dump some information about the receiver (S/N and HW rev)
+ int flag;
+ perseus_is_preserie(_dev, &flag);
+ if (flag != 0)
+ fprintf(stderr, "The device is a preserie unit");
+ else {
+ eeprom_prodid prodid;
+ if (perseus_get_product_id(_dev,&prodid)<0)
+ fprintf(stderr, "get product id error: %s", perseus_errorstr());
+ else
+ fprintf(stderr, "Receiver S/N: %05d-%02hX%02hX-%02hX%02hX-%02hX%02hX
- HW Release:%hd.%hd\n",
+ (uint16_t) prodid.sn,
+ (uint16_t) prodid.signature[5],
+ (uint16_t) prodid.signature[4],
+ (uint16_t) prodid.signature[3],
+ (uint16_t) prodid.signature[2],
+ (uint16_t) prodid.signature[1],
+ (uint16_t) prodid.signature[0],
+ (uint16_t) prodid.hwrel,
+ (uint16_t) prodid.hwver);
+ }
+ // Printing all sampling rates available .....
+ {
+ int buf[BUFSIZ];
+
+ if (perseus_get_sampling_rates (_dev, buf,
sizeof(buf)/sizeof(buf[0])) < 0) {
+ fprintf(stderr, "get sampling rates error: %s\n",
perseus_errorstr());
+ } else {
+ int i = 0;
+ while (buf[i]) {
+ fprintf(stderr, "#%d: sample rate: %d\n", i, buf[i]);
+
+ _sample_rates.push_back( std::pair<double, uint32_t>(
buf[i], i ) );
+ i++;
+ }
+ /* since they may (and will) give us an unsorted array we have
to sort it here
+ * to play nice with the monotonic requirement of meta-range
later on */
+ std::sort(_sample_rates.begin(), _sample_rates.end());
+
+ for (size_t i = 0; i < _sample_rates.size(); i++)
+ std::cerr << boost::format("%gM ") %
(_sample_rates[i].first / 1e6);
+
+ std::cerr << std::endl;
+ }
+ }
+ // Configure the receiver for 2 MS/s operations
+ fprintf(stderr, "Configuring FPGA...\n");
+ if (int ret = perseus_set_sampling_rate(_dev, 96000) < 0) { // specify
the sampling rate value in Samples/second
+ std::cerr << "fpga configuration error: " << perseus_errorstr() <<
std::endl;
+ PERSEUS_THROW_ON_ERROR(ret, "Perseus fpga configuration error");
+ }
+
+ perseus_set_attenuator_in_db(_dev, PERSEUS_ATT_0DB);
+ // Enable ADC Dither, Disable ADC Preamp
+ perseus_set_adc(_dev, true, false);
+ // set the NCO frequency in the middle of the range allowed
+ set_center_freq( (get_freq_range().start() + get_freq_range().stop()) /
2.0 );
+ set_sample_rate( get_sample_rates().start() );
+
+ _fifo = new boost::circular_buffer<gr_complex>(5000000);
+ if (!_fifo) {
+ throw std::runtime_error( std::string(__FUNCTION__) + " " +
+ "Failed to allocate a sample FIFO!" );
+ }
+}
+
+/*
+ * Our virtual destructor.
+ */
+perseus_source_c::~perseus_source_c ()
+{
+ if (_dev) {
+ if (_frun) stop();
+ int ret = perseus_close(_dev);
+ if ( ret != PERSEUS_NOERROR ) {
+ std::cerr << PERSEUS_FORMAT_ERROR(ret, "Failed to close Perseus") <<
std::endl;
+ } else
+ _dev = NULL;
+ }
+ perseus_exit();
+
+ if (_fifo) {
+ delete _fifo;
+ _fifo = NULL;
+ }
+}
+
+
+typedef union {
+ struct {
+ int32_t i;
+ int32_t q;
+ } __attribute__((__packed__)) iq;
+ struct {
+ uint8_t i1;
+ uint8_t i2;
+ uint8_t i3;
+ uint8_t i4;
+ uint8_t q1;
+ uint8_t q2;
+ uint8_t q3;
+ uint8_t q4;
+ } __attribute__((__packed__)) ;
+} iq_sample;
+
+
+int perseus_source_c::perseus_rx_callback(void *buf, int buf_size)
+{
+ size_t i, n_avail, to_copy, num_samples;
+
+ uint8_t *samplebuf = (uint8_t*)buf;
+
+ num_samples = buf_size/6;
+
+ if (buf_size % 6)
+ std::cerr << "FATAL !!!!!!! not on boundary" << std::endl;
+
+ iq_sample s;
+
+ _fifo_lock.lock();
+
+ n_avail = _fifo->capacity() - _fifo->size();
+ to_copy = (n_avail < num_samples ? n_avail : num_samples);
+
+ for (i = 0; i < to_copy; i++ ) {
+
+ s.i1 = s.q1 = 0;
+ s.i2 = *samplebuf++;
+ s.i3 = *samplebuf++;
+ s.i4 = *samplebuf++;
+ s.q2 = *samplebuf++;
+ s.q3 = *samplebuf++;
+ s.q4 = *samplebuf++;
+
+ // convert 24 bit two's complements integers to float in [-1.0 -
+1.0] range
+
+ /* Push sample to the fifo */
+ _fifo->push_back( gr_complex( (float)(s.iq.i) / (INT_MAX - 256),
(float)(s.iq.q) / (INT_MAX - 256) ) );
+
+ }
+ // keep a counter of processed bytes
+ _cnt += buf_size;
+
+ _fifo_lock.unlock();
+
+ /* We have made some new samples available to the consumer in work() */
+ if (to_copy) {
+ //std::cerr << "+" << std::flush;
+ _samp_avail.notify_one();
+ }
+
+ /* Indicate overrun, if necessary */
+ if (to_copy < num_samples)
+ std::cerr << "O" << std::flush;
+
+ return 0; // TODO: return -1 on error/stop
+}
+
+// static method used as callback from the libperseus-sdr library
+int perseus_source_c::_perseus_rx_callback(void *buf, int buf_size, void
*extra)
+{
+ perseus_source_c *obj = (perseus_source_c *)extra;
+
+ return obj->perseus_rx_callback((float *)buf, buf_size);
+
+ return 0;
+}
+
+
+bool perseus_source_c::start()
+{
+ int ret;
+
+ if ( ! _dev )
+ return false;
+
+ std::cerr << "***** START COUNTER: " << _cnt
+ << " mod 6 " << (_cnt % 6) << std::endl;
+
+ _frun = true;
+
+ // in order to guarantee samples stream synchronization
+ // re-set the sample rate each time the stream is started
+ set_sample_rate( get_sample_rate() );
+
+ ret = perseus_start_async_input(_dev,
+ 6 * 1024 * 2 /* buffer size */,
+ perseus_source_c::_perseus_rx_callback,
+ (void *)this);
+ if (ret != PERSEUS_NOERROR) {
+ std::cerr << "Failed to start RX streaming ("
+ << ret << ")" << std::endl;
+ return false;
+ } else
+ return true;
+}
+
+bool perseus_source_c::stop()
+{
+ if ( ! _dev )
+ return false;
+
+ _frun = false;
+
+ std::cerr << "***** STOP COUNTER: " << _cnt
+ << " mod 6 " << (_cnt % 6) << std::endl;
+
+ int ret = perseus_stop_async_input(_dev);
+
+ if ( ret != PERSEUS_NOERROR ) {
+ std::cerr << "Failed to stop RX streaming (" << ret << ")" <<
std::endl;
+ return false;
+ } else
+ return true;
+}
+
+int perseus_source_c::work( int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items )
+{
+ gr_complex *out = (gr_complex *)output_items[0];
+
+ if ( _frun == false ) return WORK_DONE;
+
+ boost::unique_lock<boost::mutex> lock(_fifo_lock);
+
+ /* Wait until we have the requested number of samples */
+ int n_samples_avail = _fifo->size();
+
+ while (n_samples_avail < noutput_items) {
+ _samp_avail.wait(lock);
+ n_samples_avail = _fifo->size();
+ }
+
+ for(int i = 0; i < noutput_items; ++i) {
+ out[i] = _fifo->at(0);
+ _fifo->pop_front();
+ }
+
+ return noutput_items;
+}
+
+std::vector<std::string> perseus_source_c::get_devices()
+{
+ std::vector<std::string> devices;
+ std::string label;
+
+ std::string args = "perseus=0,label='Perseus'";
+ devices.push_back( args );
+
+ return devices;
+}
+
+size_t perseus_source_c::get_num_channels()
+{
+ return 1;
+}
+
+osmosdr::meta_range_t perseus_source_c::get_sample_rates()
+{
+ osmosdr::meta_range_t range;
+
+ for (size_t i = 0; i < _sample_rates.size(); i++) {
+ range += osmosdr::range_t( _sample_rates[i].first );
+ std::cerr << "SR: " << i << " : ("
+ << _sample_rates[i].first << ")" << std::endl;
+ }
+ return range;
+}
+
+double perseus_source_c::set_sample_rate( double rate )
+{
+ int ret;
+ if ((ret = perseus_set_sampling_rate(_dev, rate)) < 0) {
+ PERSEUS_THROW_ON_ERROR(ret, "Unable to set sample rate.");
+ } else
+ _sample_rate = rate;
+
+ return get_sample_rate();
+}
+
+double perseus_source_c::get_sample_rate()
+{
+ return _sample_rate;
+}
+
+osmosdr::freq_range_t perseus_source_c::get_freq_range( size_t chan )
+{
+ osmosdr::freq_range_t range;
+
+ range += osmosdr::range_t( PERSEUS_DDC_FREQ_MIN, PERSEUS_DDC_FREQ_MAX );
+
+ return range;
+}
+
+double perseus_source_c::set_center_freq( double freq, size_t chan )
+{
+ int ret;
+
+ if (_dev) {
+ ret =
+ perseus_set_ddc_center_freq(_dev,
+ (uint64_t)(freq * (1.0 + (_freq_corr *
10e-6))),
+ 1);
+ if ( ret == PERSEUS_NOERROR ) {
+ _center_freq = freq;
+ } else {
+ PERSEUS_THROW_ON_ERROR( ret, PERSEUS_FUNC_STR(
"perseus_set_ddc_center_freq", freq ) )
+ }
+ }
+
+ return get_center_freq( chan );
+}
+
+double perseus_source_c::get_center_freq( size_t chan )
+{
+ return _center_freq;
+}
+
+double perseus_source_c::set_freq_corr( double ppm, size_t chan )
+{
+ return _freq_corr = ppm;
+}
+
+double perseus_source_c::get_freq_corr( size_t chan )
+{
+ return _freq_corr;
+}
+
+std::vector<std::string> perseus_source_c::get_gain_names( size_t chan )
+{
+ return {};
+}
+
+osmosdr::gain_range_t perseus_source_c::get_gain_range( size_t chan )
+{
+ return osmosdr::gain_range_t();
+}
+
+osmosdr::gain_range_t perseus_source_c::get_gain_range( const std::string
& name, size_t chan )
+{
+ return osmosdr::gain_range_t();
+}
+
+
+double perseus_source_c::set_gain( double gain, size_t chan )
+{
+ return gain;
+}
+
+double perseus_source_c::set_gain( double gain, const std::string & name,
size_t chan)
+{
+ return gain;
+}
+
+double perseus_source_c::get_gain( size_t chan )
+{
+ return 0.0;
+}
+
+double perseus_source_c::get_gain( const std::string & name, size_t chan )
+{
+ return 0.0;
+}
+
+std::vector< std::string > perseus_source_c::get_antennas( size_t chan )
+{
+ std::vector< std::string > antennas;
+
+ antennas += get_antenna( chan );
+
+ return antennas;
+}
+
+std::string perseus_source_c::set_antenna( const std::string & antenna,
size_t chan )
+{
+ return get_antenna( chan );
+}
+
+std::string perseus_source_c::get_antenna( size_t chan )
+{
+ return "RX";
+}
diff --git a/lib/perseus/perseus_source_c.h b/lib/perseus/perseus_source_c.h
new file mode 100644
index 0000000..9eda23d
--- /dev/null
+++ b/lib/perseus/perseus_source_c.h
@@ -0,0 +1,120 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2013 Dimitri Stolnikov <horiz0n(a)gmx.net>
+ *
+ * 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.
+ */
+#ifndef INCLUDED_PERSEUS_SOURCE_C_H
+#define INCLUDED_PERSEUS_SOURCE_C_H
+
+#include <boost/circular_buffer.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition_variable.hpp>
+
+#include <gnuradio/sync_block.h>
+
+#include "source_iface.h"
+#include <perseus-sdr.h>
+
+class perseus_source_c;
+
+typedef boost::shared_ptr<perseus_source_c> perseus_source_c_sptr;
+
+/*!
+ * \brief Return a shared_ptr to a new instance of perseus_source_c.
+ *
+ * To avoid accidental use of raw pointers, perseus_source_c's
+ * constructor is private. make_perseus_source_c is the public
+ * interface for creating new instances.
+ */
+perseus_source_c_sptr make_perseus_source_c (const std::string & args =
"");
+
+/*!
+ * \brief Provides a stream of complex samples.
+ * \ingroup block
+ */
+class perseus_source_c :
+ public gr::sync_block,
+ public source_iface
+{
+private:
+ // The friend declaration allows make_perseus_source_c to
+ // access the private constructor.
+
+ friend perseus_source_c_sptr make_perseus_source_c (const std::string &
args);
+
+ perseus_source_c (const std::string & args);
+
+public:
+ ~perseus_source_c ();
+
+ bool start();
+ bool stop();
+
+ 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 );
+ 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 );
+
+ 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 );
+
+
+private:
+ static int _perseus_rx_callback(void *buf, int buf_size, void *extra);
+
+ int perseus_rx_callback(void *samples, int sample_count);
+
+ perseus_descr *_dev;
+
+ boost::circular_buffer<gr_complex> *_fifo;
+ boost::mutex _fifo_lock;
+ boost::condition_variable _samp_avail;
+
+ std::vector< std::pair<double, uint32_t> > _sample_rates;
+ double _sample_rate;
+ double _center_freq;
+ double _freq_corr;
+
+ bool _frun;
+ int _cnt;
+};
+
+#endif /* INCLUDED_PERSEUS_SOURCE_C_H */
diff --git a/lib/rtl/rtl_source_c.cc b/lib/rtl/rtl_source_c.cc
index a371464..eb7042e 100644
--- a/lib/rtl/rtl_source_c.cc
+++ b/lib/rtl/rtl_source_c.cc
@@ -221,7 +221,7 @@ rtl_source_c::rtl_source_c (const std::string &args)
throw std::runtime_error("Failed to enable offset tuning.");
}
- ret = rtlsdr_set_bias_tee(_dev, bias_tee);
+// ret = rtlsdr_set_bias_tee(_dev, bias_tee);
if (ret < 0)
throw std::runtime_error("Failed to set bias tee.");
diff --git a/lib/source_impl.cc b/lib/source_impl.cc
index a28f314..ead4ef7 100644
--- a/lib/source_impl.cc
+++ b/lib/source_impl.cc
@@ -92,6 +92,9 @@
#include <freesrp_source_c.h>
#endif
+#ifdef ENABLE_PERSEUS
+#include <perseus_source_c.h>
+#endif
#include "arg_helpers.h"
#include "source_impl.h"
@@ -171,6 +174,9 @@ source_impl::source_impl( const std::string &args )
#ifdef ENABLE_FREESRP
dev_types.push_back("freesrp");
#endif
+#ifdef ENABLE_PERSEUS
+ dev_types.push_back("perseus");
+#endif
std::cerr << "gr-osmosdr "
<< GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") "
<< "gnuradio " << gr::version() << std::endl;
@@ -252,7 +258,10 @@ source_impl::source_impl( const std::string &args )
BOOST_FOREACH( std::string dev, freesrp_source_c::get_devices() )
dev_list.push_back( dev );
#endif
-
+#ifdef ENABLE_PERSEUS
+ BOOST_FOREACH( std::string dev, perseus_source_c::get_devices() )
+ dev_list.push_back( dev );
+#endif
// std::cerr << std::endl;
// BOOST_FOREACH( std::string dev, dev_list )
// std::cerr << "'" << dev << "'" << std::endl;
@@ -383,6 +392,13 @@ source_impl::source_impl( const std::string &args )
}
#endif
+#ifdef ENABLE_PERSEUS
+ if ( dict.count("perseus") ) {
+ perseus_source_c_sptr src = make_perseus_source_c( arg );
+ block = src; iface = src.get();
+ }
+#endif
+
if ( iface != NULL && long(block.get()) != 0 ) {
_devs.push_back( iface );
--
1.9.1
>From 1b6cf50a7a6074754717b0270757931c379561bc Mon Sep 17 00:00:00 2001
From: Andrea Montefusco IW0HDV <andrew(a)montefusco.com>
Date: Thu, 1 Feb 2018 17:33:39 +0100
Subject: More hardware controls added (attenuator, ADC preamp and dither,
preselector)
---
lib/perseus/perseus_source_c.cc | 136 ++++++++++++++++++++++++++++++
++++++----
lib/perseus/perseus_source_c.h | 19 +++++-
2 files changed, 142 insertions(+), 13 deletions(-)
diff --git a/lib/perseus/perseus_source_c.cc b/lib/perseus/perseus_source_
c.cc
index 0720da4..89674b1 100644
--- a/lib/perseus/perseus_source_c.cc
+++ b/lib/perseus/perseus_source_c.cc
@@ -89,7 +89,11 @@ perseus_source_c::perseus_source_c (const std::string
&args)
_center_freq(0),
_freq_corr(0),
_frun(false),
- _cnt(0)
+ _cnt(0),
+ _att_value(0),
+ _adc_preamp(false),
+ _adc_dither(false),
+ _preselector(false)
{
//int ret;
@@ -174,9 +178,9 @@ perseus_source_c::perseus_source_c (const std::string
&args)
PERSEUS_THROW_ON_ERROR(ret, "Perseus fpga configuration error");
}
- perseus_set_attenuator_in_db(_dev, PERSEUS_ATT_0DB);
+ perseus_set_attenuator_in_db(_dev, _att_value);
// Enable ADC Dither, Disable ADC Preamp
- perseus_set_adc(_dev, true, false);
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ?
1:0);
// set the NCO frequency in the middle of the range allowed
set_center_freq( (get_freq_range().start() + get_freq_range().stop()) /
2.0 );
set_sample_rate( get_sample_rates().start() );
@@ -425,7 +429,7 @@ double perseus_source_c::set_center_freq( double freq,
size_t chan )
ret =
perseus_set_ddc_center_freq(_dev,
(uint64_t)(freq * (1.0 + (_freq_corr *
10e-6))),
- 1);
+ _preselector == true ? 1:0);
if ( ret == PERSEUS_NOERROR ) {
_center_freq = freq;
} else {
@@ -453,55 +457,165 @@ double perseus_source_c::get_freq_corr( size_t chan )
std::vector<std::string> perseus_source_c::get_gain_names( size_t chan )
{
- return {};
+ std::vector< std::string > names;
+
+ names += "Attenuator";
+ names += "ADC preamp";
+ names += "ADC dither";
+
+ return names;
}
+//
+// FIXME
+//
osmosdr::gain_range_t perseus_source_c::get_gain_range( size_t chan )
{
- return osmosdr::gain_range_t();
+ return get_gain_range( "Attenuator", chan );
}
osmosdr::gain_range_t perseus_source_c::get_gain_range( const std::string
& name, size_t chan )
{
+ if ( "Attenuator" == name ) {
+ return osmosdr::gain_range_t( -30, 0, 10 );
+ }
+ if ( "ADC preamp" == name ) {
+ return osmosdr::gain_range_t( 0, 6, 6 );
+ }
+ if ( "ADC dither" == name ) {
+ return osmosdr::gain_range_t( 0, 1, 1 );
+ }
+
return osmosdr::gain_range_t();
}
-double perseus_source_c::set_gain( double gain, size_t chan )
+double perseus_source_c::set_attenuator( double gain, size_t chan )
{
+ int ret;
+
+ gain = - gain;
+ if (_dev && _att_value != gain) {
+ ret = perseus_set_attenuator_in_db (_dev, gain);
+
+ if ( PERSEUS_NOERROR == ret ) {
+ _att_value = gain;
+ } else {
+ std::cerr << "ERROR: perseus_set_attenuator_in_db: " << gain <<
std::endl;
+ }
+ }
+
+ return _att_value;
+}
+
+double perseus_source_c::set_adc_preamp( double gain, size_t chan )
+{
+ std::cerr << "set_adc_preamp: " << gain << std::endl;
+
+ if (_dev) {
+ _adc_preamp = (gain != 0. ? true : false);
+ int ret =
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ?
1:0);
+ if ( PERSEUS_NOERROR != ret ) {
+ std::cerr << "ERROR: perseus_set_adc" << ret << std::endl;
+ }
+ }
return gain;
}
-double perseus_source_c::set_gain( double gain, const std::string & name,
size_t chan)
+double perseus_source_c::set_adc_dither( double gain, size_t chan )
{
+ std::cerr << "set_adc_dither: " << gain << std::endl;
+
+ if (_dev) {
+ _adc_dither = (gain != 0. ? true : false);
+ int ret =
+ perseus_set_adc(_dev, _adc_dither == true ? 1:0, _adc_preamp == true ?
1:0);
+ if ( PERSEUS_NOERROR != ret ) {
+ std::cerr << "ERROR: perseus_set_adc" << ret << std::endl;
+ }
+ }
return gain;
}
-double perseus_source_c::get_gain( size_t chan )
+double perseus_source_c::get_attenuator( size_t chan )
{
- return 0.0;
+ return _att_value;
}
+double perseus_source_c::get_adc_preamp(size_t chan )
+{
+ double v = _adc_preamp == true ? 6. : 0.;
+ std::cerr << "get_adc_preamp: " << _adc_preamp << " value" << v <<
std::endl;
+ return v;
+}
+
+double perseus_source_c::get_adc_dither(size_t chan )
+{
+ double v = _adc_dither == true ? 1. : 0.;
+ std::cerr << "get_adc_dither: " << _adc_dither << " value" << v <<
std::endl;
+ return v;
+}
+
+
+double perseus_source_c::set_gain( double gain, const std::string & name,
size_t chan)
+{
+ if ( "Attenuator" == name ) {
+ return set_attenuator( gain, chan );
+ }
+ if ( "ADC preamp" == name ) {
+ return set_adc_preamp( gain, chan );
+ }
+ if ( "ADC dither" == name ) {
+ return set_adc_dither( gain, chan );
+ }
+ return gain;
+}
+
+
double perseus_source_c::get_gain( const std::string & name, size_t chan )
{
+ if ( "Attenuator" == name ) {
+ return get_attenuator( chan );
+ }
+ if ( "ADC preamp" == name ) {
+ return get_adc_preamp( chan );
+ }
+ if ( "ADC dither" == name ) {
+ return get_adc_dither( chan );
+ }
return 0.0;
}
+
+
std::vector< std::string > perseus_source_c::get_antennas( size_t chan )
{
std::vector< std::string > antennas;
- antennas += get_antenna( chan );
+ antennas += "RX";
+ antennas += "RX+PRESELECTOR";
return antennas;
}
std::string perseus_source_c::set_antenna( const std::string & antenna,
size_t chan )
{
+ if ( antenna == "RX" ) {
+ _preselector = false;
+ set_center_freq( get_center_freq() );
+ return "RX";
+ }
+ if ( antenna == "RX+PRESELECTOR" ) {
+ _preselector = true;
+ set_center_freq( get_center_freq() );
+ return "RX+PRESELECTOR";
+ }
return get_antenna( chan );
}
std::string perseus_source_c::get_antenna( size_t chan )
{
+ std::cerr << "perseus_source_c::get_antenna: " << chan << std::endl;
return "RX";
}
diff --git a/lib/perseus/perseus_source_c.h b/lib/perseus/perseus_source_c.h
index 9eda23d..934ff2b 100644
--- a/lib/perseus/perseus_source_c.h
+++ b/lib/perseus/perseus_source_c.h
@@ -87,9 +87,20 @@ public:
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 );
- double set_gain( double gain, size_t chan = 0 );
+
+ double set_gain( double gain, size_t chan = 0 ) {return gain; }
+ double get_gain( size_t chan = 0 ) {return _att_value; }
+
+ double set_attenuator( double gain, size_t chan = 0 );
+ double get_attenuator( size_t chan = 0 );
+
+ double set_adc_preamp( double gain, size_t chan = 0 );
+ double get_adc_preamp( size_t chan = 0 );
+
+ double set_adc_dither( double gain, size_t chan = 0 );
+ double get_adc_dither( 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 );
std::vector< std::string > get_antennas( size_t chan = 0 );
@@ -115,6 +126,10 @@ private:
bool _frun;
int _cnt;
+ double _att_value;
+ bool _adc_preamp;
+ bool _adc_dither;
+ bool _preselector;
};
#endif /* INCLUDED_PERSEUS_SOURCE_C_H */
--
1.9.1
--
Andrea Montefusco IW0HDV
------------------------------------------
As my old boss, an Apollo veteran, would often remind us “It’s good to be
smart, but it’s better to be lucky.”
Wayne Hale, Space Shuttle Flight Director
1
0
Here is a PyBOMBS recipe file that pulls gr-osmosdr from my sdrplay2
branch. If anyone would like to try out this support with a PyBOMBS build,
this would be the easiest way. Best to build everything from scratch, but
you can remove the src/gr-osmosdr directory, do a pybombs fetch/rebuild if
you're comfortable with that.
The SDRplay SDK v2.11 is required, and should be installed before
gr-osmosdr is rebuilt. The ENABLE_NONFREE flag is turned on for this build.
This is just for testing, so I can get some feedback. Hopefully, this will
go into the gr-osmosdr master at some point. It "works for me".
Note that the gains on SDRplay are actually attenuations, so they appear
backward. The LNA attenuation steps on RSPs are frequency dependent, so I
gave up trying to make them look like gains. My main use case is GQRX, and
that way was even more confusing.
There is a GRC sdrplay source that is a little less generic than the
osmocom source.
There are probably ways to repackage this so it can be distributed freely,
but I'll address that if there's more interest. The SDK include file is
redistributable and its terms seem to be compatible with GPL. The shared
object can be dynamically loaded over a placeholder library that does
nothing. Yes, that's hackish and it would be better to have everything free.
1
0
SDRplay support, rewritten, supporting RSP1, RSP1A, and RSP2
Requires ENABLE_NONFREE=yes. Requires SDRplay's SDK v2.11 to be installed
by the user. In the future, if there is a way to redistribute
mirsdrapi-rsp.h from the SDK, that problem could be addressed using a shim
library that looks for /usr/local/lib/libmirsdrapi-rsp.so at runtime.
1
2
Good afternoon,
I am a complete noob playing with my YardStick one and HackRf one.
I am working a signal that I am fairly certain is using pulse interval encoding and I am having a heck of a time understanding how to turn the NRZ 1's and 0's into the Pulse Interval.
The closest thing I have seen is how RFID uses pulse interval encoding but I don't think that is the same thing.
I started by recording in osmocom_fft
I then used inspectrum to extract symbols and found that most symbols were 1 unit in inspectrum, but there were a few places where there were 2 or 4 in a row of high or low.
Once I exported the symbols I was able to generate a bit string of 1's and 0's representing highs and lows.
I was able to identify the preamble and what I think is the sync word, but that's where I get lost.
I have seen many examples where people take 3 bits and assume that say 001 is a 0 and 011 is a 1n a PWM model but nothing I seem to do seems to work to uncover the PIE encoding and was hoping someone out there could point me in the right direction.
Thanks,
Adam
Sent from my iPad
1
0
Hello dear RTL-SDR community,
I just whant to ask if it's possible to modify rtl-sdr.h to lower the
admissible sample rate.
I am currently using 8 dongle successfully but would like to use 12
dongle simultaneously with software (rtlsdr-wsprd) to decode wspr signals.
This one uses the library rtl-sdr.
I would therefore like to be able to use sampling frequencies lower than
250000.
Thanks in advance!
1
0
08 Jan '18
Hello dear RTL-SDR users,
I whant to use multiples dongles as array for decode wspr.
My problem is that I can not use more than 4 USB dongle simultaneously.
I check with usbmon, each dongle uses 500kbits / s around. So I am far
from the maximum bandwidth allowed by usb 2.
I tried using a USB switch with external power supply but it does not
work better.
The default test performed is:
#!/bin/bash
for ((i=0;i<=4;i++))
do
rtl_test -d $i -s 250000 &
done
First 4 is starting but last 5 doesent. And i whant to use more...
Is this a limitation of the RTL-SDR library or the kernel driver?
Do we have a solution to get around the problem?
Thank you in advance for your valuable feedback.
3
4