[PATCH] gr-osmosdr: Microtelecom Perseus HF receiver support
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@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@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
On Fri, Feb 2, 2018 at 11:33 PM, Sylvain Munaut <246tnt@gmail.com> wrote:
Hi,
Can't comment about the patch it self but :
lib/airspy/airspy_source_c.cc | 5 +- lib/rtl/rtl_source_c.cc | 2 +-
Those looks like mistakes that have nothing to do in this patch ....
Yes, I forgot to remove that. Going to send another email with the fixed patch (cannot send it from here as GMAIL is breaking lines at column 70 or so). -- 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
participants (2)
-
andrea montefusco -
Sylvain Munaut