From mike at spideraerialfilming.com Wed Aug 1 16:32:29 2018 From: mike at spideraerialfilming.com (Mike Bishop) Date: Wed, 1 Aug 2018 17:32:29 +0100 Subject: Creating GPS indoors Message-ID: Hello, I hope you are well. I am looking to commission an array of GPS units that emulate the standard GPS. This is to provide positioning service indoors and underground in a large open space. Can you help with something like this? Kindest regards, Mike *Mike Bishop* Operations Director Spider UAS Operations Ltd CAA Authorised UAV Operator M: 07801 450 392 ***SHOWREEL*** E: Mike at spideraerialfilming.com W: spideraerialfilming.com W: dronebuilders.co.uk Instagram: Spider_Drone -------------- next part -------------- An HTML attachment was scrubbed... URL: From andrescampos45 at hotmail.com Fri Aug 3 02:27:31 2018 From: andrescampos45 at hotmail.com (Andres Campos Santana) Date: Fri, 3 Aug 2018 02:27:31 +0000 Subject: USING MINI DIGITAL TV STICK RTL2832U 820T2 & SDR ON GRC Message-ID: Good afternoon everyone. I write here to see if any of you know that installation errors I may have had to use the r820t2 in gnu radio companion. I'm trying to make an FM receiver and other analog TV receiver but when running the program does not receive any type of signal and the view errors, I have: devices not supported. I believe that this is some driver to be installed but I don't know where or how. The steps that I have followed for installation (Ubuntu 16.04 LTS) have been: 1. Install gnuradio. 2. Install Xenial Xerius specifications (it is supposed to do I need for my version of Ubuntu). I3. nstall specifications of gr-osmosdr to enable some features of the chip. The Stick comes with a CD but i do not know how to install it since, according to the specifications only works for WXP, 2000, Vista, 7 and 8; and it is something that it seems strange to me since I have seen videos where they use this same model of stick to carry out projects in Gnuradio on Ubuntu. I tried with this: sudo apt-get update sudo apt-get install git sudo apt-get install cmake sudo apt-get install build-essential sudo apt-get install libusb-1.0-0-dev git clone git://git.osmocom.org/rtl-sdr.git cd rtl-sdr/ mkdir build cd build cmake ../ -DINSTALL_UDEV_RULES=ON make sudo make install sudo ldconfig sudo cp ../rtl-sdr.rules /etc/udev/rules.d/ Opening /etc/modprobe.d folder Creating a new file 'blacklist-rtl.conf' and add this one line: blacklist dvb_usb_rtl28xxu rtl_test -t I tried those commands but I do not know if I need to install something else because GNRC still says: "FATAL: No supported devices found to pick from"... Please, if any of you know that i need to install It would serve as much help as I am a novice programmer and i need to resolve this problem for my project. Enviado desde Outlook -------------- next part -------------- An HTML attachment was scrubbed... URL: From horiz0n at gmx.net Wed Aug 15 18:30:11 2018 From: horiz0n at gmx.net (horizon) Date: Wed, 15 Aug 2018 20:30:11 +0200 Subject: [PATCH] gr-osmosdr Microtelecom Perseus HF receiver support (II) In-Reply-To: <5a758328.jVmt9CwXV2YCfoTx%andrea.montefusco@gmail.com> References: <5a758328.jVmt9CwXV2YCfoTx%andrea.montefusco@gmail.com> Message-ID: Hi Andrea, could you please provide a signed-off patch with your latest changes ongithub and a proper commit message? Feel free to add information to AUTHORS, README and the grc/gen_osmosdr_blocks.py files just like we did for other radios and ideally include a link where libperseus is hosted (I understand this is https://github.com/Microtelecom/libperseus-sdr ?). Best regards, Dimitri On 02/03/2018 10:38 AM, andrea.montefusco at gmail.com wrote: > Second attempt: I removed all the changes not related to Perseus. > > @Sylvain: thanks for take time to review. > > > --- > CMakeLists.txt | 1 + > cmake/Modules/FindLibPERSEUS.cmake | 24 ++ > lib/CMakeLists.txt | 8 + > lib/config.h.in | 1 + > lib/device.cc | 8 + > lib/perseus/CMakeLists.txt | 37 +++ > lib/perseus/perseus_source_c.cc | 613 +++++++++++++++++++++++++++++++++++++ > lib/perseus/perseus_source_c.h | 135 ++++++++ > lib/source_impl.cc | 18 +- > 9 files changed, 844 insertions(+), 1 deletion(-) > 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/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 > #endif > > +#ifdef ENABLE_PERSEUS > +#include > +#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..bf7c9be > --- /dev/null > +++ b/lib/perseus/perseus_source_c.cc > @@ -0,0 +1,613 @@ > +/* -*- 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 > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > + > +#include > + > +#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), > + _att_value(0), > + _adc_preamp(false), > + _adc_dither(false), > + _preselector(false) > +{ > + dict_t dict = params_to_dict(args); > + > + _dev = NULL; > + > + // Set debug info dumped to stderr to the maximum verbose level > + perseus_set_debug(3); > + > + // 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( 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, _att_value); > + // Enable ADC Dither, Disable ADC Preamp > + 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() ); > + > + _fifo = new boost::circular_buffer(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 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 perseus_source_c::get_devices() > +{ > + std::vector 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))), > + _preselector == true ? 1:0); > + 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 perseus_source_c::get_gain_names( size_t chan ) > +{ > + 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 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_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_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_attenuator( size_t chan ) > +{ > + 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 += "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 > new file mode 100644 > index 0000000..55d3f89 > --- /dev/null > +++ b/lib/perseus/perseus_source_c.h > @@ -0,0 +1,135 @@ > +/* -*- c++ -*- */ > +/* > + * Copyright 2013 Dimitri Stolnikov > + * > + * 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 > +#include > +#include > + > +#include > + > +#include "source_iface.h" > +#include > + > +class perseus_source_c; > + > +typedef boost::shared_ptr 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 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 ) {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( 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 *_fifo; > + boost::mutex _fifo_lock; > + boost::condition_variable _samp_avail; > + > + std::vector< std::pair > _sample_rates; > + double _sample_rate; > + double _center_freq; > + double _freq_corr; > + > + bool _frun; > + int _cnt; > + double _att_value; > + bool _adc_preamp; > + bool _adc_dither; > + bool _preselector; > +}; > + > +#endif /* INCLUDED_PERSEUS_SOURCE_C_H */ > 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 > #endif > > +#ifdef ENABLE_PERSEUS > +#include > +#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 ); > From horiz0n at gmx.net Wed Aug 15 18:45:36 2018 From: horiz0n at gmx.net (horizon) Date: Wed, 15 Aug 2018 20:45:36 +0200 Subject: sdrplay2: Which changes to merge? Message-ID: <6d3a4a8f-fe1f-827b-9d7c-a9139b1c4897@gmx.net> trying to figure out what patches to merge for sdrplay2 suppoort. So far i've received the following sources: Frank's patch by email on 06/30/18 https://github.com/willcode/gr-osmosdr/tree/sdrplay2 https://github.com/SDRplay/gr-osmosdr/tree/sdrplay2 Could you please comment on whats the most recent and correct state to apply? Thanks a lot! Best regards, Dimitri -------------- next part -------------- An HTML attachment was scrubbed... URL: From andrea.montefusco at gmail.com Thu Aug 16 20:48:32 2018 From: andrea.montefusco at gmail.com (andrea montefusco) Date: Thu, 16 Aug 2018 22:48:32 +0200 Subject: [PATCH] gr-osmosdr Microtelecom Perseus HF receiver support (II) In-Reply-To: References: <5a758328.jVmt9CwXV2YCfoTx%andrea.montefusco@gmail.com> Message-ID: Ok, just give me few days, currently I'm on vacation far away from computers and radios. Andrea Montefusco IW0HDV On Wed, Aug 15, 2018, 20:30 horizon wrote: > Hi Andrea, > > could you please provide a signed-off patch with your latest changes > ongithub and a proper commit message? > > Feel free to add information to AUTHORS, README and the > grc/gen_osmosdr_blocks.py files just like we did for other radios and > ideally include a link where libperseus is hosted (I understand this is > https://github.com/Microtelecom/libperseus-sdr ?). > > Best regards, > > Dimitri > > > On 02/03/2018 10:38 AM, andrea.montefusco at gmail.com wrote: > > Second attempt: I removed all the changes not related to Perseus. > > > > @Sylvain: thanks for take time to review. > > > > > > --- > > CMakeLists.txt | 1 + > > cmake/Modules/FindLibPERSEUS.cmake | 24 ++ > > lib/CMakeLists.txt | 8 + > > lib/config.h.in | 1 + > > lib/device.cc | 8 + > > lib/perseus/CMakeLists.txt | 37 +++ > > lib/perseus/perseus_source_c.cc | 613 > +++++++++++++++++++++++++++++++++++++ > > lib/perseus/perseus_source_c.h | 135 ++++++++ > > lib/source_impl.cc | 18 +- > > 9 files changed, 844 insertions(+), 1 deletion(-) > > 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/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 > > #endif > > > > +#ifdef ENABLE_PERSEUS > > +#include > > +#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..bf7c9be > > --- /dev/null > > +++ b/lib/perseus/perseus_source_c.cc > > @@ -0,0 +1,613 @@ > > +/* -*- 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 > > +#include > > +#include > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include > > + > > +#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), > > + _att_value(0), > > + _adc_preamp(false), > > + _adc_dither(false), > > + _preselector(false) > > +{ > > + dict_t dict = params_to_dict(args); > > + > > + _dev = NULL; > > + > > + // Set debug info dumped to stderr to the maximum verbose level > > + perseus_set_debug(3); > > + > > + // 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( > 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, _att_value); > > + // Enable ADC Dither, Disable ADC Preamp > > + 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() ); > > + > > + _fifo = new boost::circular_buffer(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 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 perseus_source_c::get_devices() > > +{ > > + std::vector 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))), > > + _preselector == true ? 1:0); > > + 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 perseus_source_c::get_gain_names( size_t chan ) > > +{ > > + 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 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_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_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_attenuator( size_t chan ) > > +{ > > + 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 += "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 > > new file mode 100644 > > index 0000000..55d3f89 > > --- /dev/null > > +++ b/lib/perseus/perseus_source_c.h > > @@ -0,0 +1,135 @@ > > +/* -*- c++ -*- */ > > +/* > > + * Copyright 2013 Dimitri Stolnikov > > + * > > + * 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 > > +#include > > +#include > > + > > +#include > > + > > +#include "source_iface.h" > > +#include > > + > > +class perseus_source_c; > > + > > +typedef boost::shared_ptr 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 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 ) {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( 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 *_fifo; > > + boost::mutex _fifo_lock; > > + boost::condition_variable _samp_avail; > > + > > + std::vector< std::pair > _sample_rates; > > + double _sample_rate; > > + double _center_freq; > > + double _freq_corr; > > + > > + bool _frun; > > + int _cnt; > > + double _att_value; > > + bool _adc_preamp; > > + bool _adc_dither; > > + bool _preselector; > > +}; > > + > > +#endif /* INCLUDED_PERSEUS_SOURCE_C_H */ > > 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 > > #endif > > > > +#ifdef ENABLE_PERSEUS > > +#include > > +#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 ); > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From frank_lists at superlogical.ch Sat Aug 18 20:42:57 2018 From: frank_lists at superlogical.ch (Frank Werner-Krippendorf) Date: Sat, 18 Aug 2018 22:42:57 +0200 Subject: sdrplay2: Which changes to merge? In-Reply-To: <022001d43531$d8a9bfe0$89fd3fa0$@sdrplay.com> References: <6d3a4a8f-fe1f-827b-9d7c-a9139b1c4897@gmx.net> <022001d43531$d8a9bfe0$89fd3fa0$@sdrplay.com> Message-ID: I can confirm that it is working. Tested today. Cheers Frank On 08/16/2018 09:22 AM, software at sdrplay.com wrote: > > Hello Dimitri, > > Please take https://github.com/willcode/gr-osmosdr/tree/sdrplay2 > > This requires v2.13 of the API and supports all RSPs in single tuner mode. > > Best regards, > > Andy > > *From:*horizon > *Sent:* 15 August 2018 7:46 PM > *To:* software at sdrplay.com; willcode4 at gmail.com; > frank_lists at superlogical.ch; osmocom-sdr at lists.osmocom.org > *Subject:* sdrplay2: Which changes to merge? > > ?trying to figure out what patches to merge for sdrplay2 suppoort. So > far i've received the following sources: > > Frank's patch by email on 06/30/18 > > https://github.com/willcode/gr-osmosdr/tree/sdrplay2 > > https://github.com/SDRplay/gr-osmosdr/tree/sdrplay2 > > Could you please comment on whats the most recent and correct state to > apply? Thanks a lot! > > Best regards, > > Dimitri > -------------- next part -------------- An HTML attachment was scrubbed... URL: From martin.m at suddenlink.net Sun Aug 19 18:47:48 2018 From: martin.m at suddenlink.net (Martin McCormick) Date: Sun, 19 Aug 2018 13:47:48 -0500 Subject: Non Graphical Use of gnuradio Tools Message-ID: If there is a better mailing list for the following question, I will gladly take it there. I want to use as much of the gnu radio project as practical but the graphical way to string various modules together isn't terribly useful for computer users who happen to be blind. Unix and to a lesser degree, DOS handled input and output logic elegantly with <, >, | and even tee when one sends output to multiple files and unix just turned 50 this Summer so it's doing something right. Is there any kind of text-based way to string those python GNU modules together to cause them to work as they do when connected using GRC? This could very well be a dumb question as I have only scratched at python a few years ago and what I am talking about may be similar to a Makefile which is what you use to refer to a bunch of C modules that might be in a program. Anyway, I sure hope there is something else out there that accomplishes the same job. I have played with the Realtek, RTL2838UHIDIR and it does surprisingly well on analog signals so it's time to string some of those modules together and try some digital decoding. Thanks for any and all constructive suggestions. Martin McCormick WB5AGZ From software at sdrplay.com Thu Aug 16 07:22:10 2018 From: software at sdrplay.com (software at sdrplay.com) Date: Thu, 16 Aug 2018 08:22:10 +0100 Subject: sdrplay2: Which changes to merge? In-Reply-To: <6d3a4a8f-fe1f-827b-9d7c-a9139b1c4897@gmx.net> References: <6d3a4a8f-fe1f-827b-9d7c-a9139b1c4897@gmx.net> Message-ID: <022001d43531$d8a9bfe0$89fd3fa0$@sdrplay.com> Hello Dimitri, Please take https://github.com/willcode/gr-osmosdr/tree/sdrplay2 This requires v2.13 of the API and supports all RSPs in single tuner mode. Best regards, Andy From: horizon Sent: 15 August 2018 7:46 PM To: software at sdrplay.com; willcode4 at gmail.com; frank_lists at superlogical.ch; osmocom-sdr at lists.osmocom.org Subject: sdrplay2: Which changes to merge? trying to figure out what patches to merge for sdrplay2 suppoort. So far i've received the following sources: Frank's patch by email on 06/30/18 https://github.com/willcode/gr-osmosdr/tree/sdrplay2 https://github.com/SDRplay/gr-osmosdr/tree/sdrplay2 Could you please comment on whats the most recent and correct state to apply? Thanks a lot! Best regards, Dimitri -------------- next part -------------- An HTML attachment was scrubbed... URL: From mueller at kit.edu Sun Aug 19 22:41:57 2018 From: mueller at kit.edu (=?utf-8?B?TcO8bGxlciwgTWFyY3VzIChDRUwp?=) Date: Sun, 19 Aug 2018 22:41:57 +0000 Subject: Non Graphical Use of gnuradio Tools In-Reply-To: References: Message-ID: <1534718516.6153.60.camel@kit.edu> Hi Martin, arguing that this is unrelated to osmocom, but relevant to GNU Radio: I'm CC'ing the discuss-gnuradio mailing list [1]; I'll recommend following up there (and taking osmocom-sdr out of CC: when doing that), unless someone else sees an argument for keeping this discussion here. So, GNU Radio as a framework doesn't set any constraints on what the scheduler and blocks do to stdout; especially, the default logger writes to stdout, so, no matter how you do it, putting the output onto stdout doesn't work out; it's a shared resource across all components in GNU Radio. > Unix and to a lesser degree, DOS handled input and output > logic elegantly with <, >, | and even tee when one sends output > to multiple files and unix just turned 50 this Summer so it's > doing something right. Yes, but you can only have graphs that are paths this way. GNU Radio allows for arbitrary cycle-free graphs. So, this only solves a small subset. It's a mature solution, but for a much smaller problem space, i.e. not generally applicable to GNU Radio. You put this very nicely! You're saying: > ? kind of text-based way to string those python GNU > modules together and that's the whole point: Unix shell piping can create *strings* of processing steps. GNU Radio can do more than that ? thus, we never implemented something different. > Is there any kind of text-based way to string those python GNU > modules together to cause them to work as they do when > connected using GRC? I'd argue that GNU Radio's python interface allows you to execute top_block.connect(source_block, processing_block1, processing_block2, sink_block) top_block.run() so, that's a pretty text-based approach :) Don't forget that it essentially doesn't matter to your OS whether you run a bash script or a python script ? and in this case, python has the tools that bash is lacking to create complex flow graphs. So, what to recommend? First of all, you don't *have* to use GRC to create your flow graphs. Many of us do, because it gives you kind of a graphical representation of the Python program that GRC generates and thus is kind of self- documenting, but really, just write Python :) You'll find that you can read the flow graph programs that GRC generates pretty well, and base your programs on that. No matter how you set up your GNU Radio flow graph, it's a very valid use case to want something like python flowgraph.py | grep "alert" > warnings.txt you can actually do that by /bin/bash TEMPDIR=$(mktemp -d) mkfifo "${TEMPDIR}/fifo" python flowgraph.py --filename "${TEMPDIR}/fifo" & grep "alert" < "${TEMPDIR}/fifo" > warnings.txt The magic happens in your flowgraph, where you'd add a "Parameter" block to add your "--filename" option, and use that option for the file name in a file sink :) Hope that helps, Marcus [1] https://lists.gnu.org/mailman/listinfo/discuss-gnuradio On Sun, 2018-08-19 at 13:47 -0500, Martin McCormick wrote: > If there is a better mailing list for the following question, I will > gladly take > it there. > > I want to use as much of the gnu radio project as > practical but the graphical way to string various modules > together isn't terribly useful for computer users who happen to > be blind. > > Unix and to a lesser degree, DOS handled input and output > logic elegantly with <, >, | and even tee when one sends output > to multiple files and unix just turned 50 this Summer so it's > doing something right. > > Is there any kind of text-based way to string those python GNU > modules together to cause them to work as they do when > connected using GRC? > > This could very well be a dumb question as I have only > scratched at python a few years ago and what I am talking > about may be similar to a Makefile which is what you use to refer > to a bunch of C modules that might be in a program. > > Anyway, I sure hope there is something else out there > that accomplishes the same job. > > I have played with the Realtek, RTL2838UHIDIR and it does > surprisingly well on analog signals so it's time to string some > of those modules together and try some digital decoding. > > Thanks for any and all constructive suggestions. > > Martin McCormick WB5AGZ -------------- next part -------------- A non-text attachment was scrubbed... Name: smime.p7s Type: application/x-pkcs7-signature Size: 6582 bytes Desc: not available URL: From andrea.montefusco at gmail.com Tue Aug 21 14:33:12 2018 From: andrea.montefusco at gmail.com (Andrea Montefusco IW0HDV) Date: Tue, 21 Aug 2018 16:33:12 +0200 Subject: [PATCH] gr-osmosdr Microtelecom Perseus HF receiver support (II) Message-ID: <1534861992-6994-1-git-send-email-andrew@montefusco.com> Hi Dimitri, >> could you please provide a signed-off patch with your latest changes ongithub and a proper commit message? >> Feel free to add information to AUTHORS, README and the grc/gen_osmosdr_blocks.py files just like we did for other radios and ideally include a link where libperseus is hosted (I understand this is https://github.com/Microtelecom/libperseus-sdr ?). I added the infor requested and even tested the module under GRC (non only under GQRX that is the reason I developed the patch at start). Below you find the patch as per git command. *am* IW0HDV --- 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 #endif +#ifdef ENABLE_PERSEUS +#include +#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 +#include +#include + +#include +#include +#include +#include +#include + +#include + +#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( 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(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 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 perseus_source_c::get_devices() +{ + std::vector 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 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 + * + * 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 +#include +#include + +#include + +#include "source_iface.h" +#include + +class perseus_source_c; + +typedef boost::shared_ptr 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 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 *_fifo; + boost::mutex _fifo_lock; + boost::condition_variable _samp_avail; + + std::vector< std::pair > _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 #endif +#ifdef ENABLE_PERSEUS +#include +#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 Date: Thu, 1 Feb 2018 17:33:39 +0100 Subject: [PATCH 2/6] 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 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 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 >From ff520c9d98d0bdeb9cf59cc092a08d54ced76af1 Mon Sep 17 00:00:00 2001 From: Andrea Montefusco IW0HDV Date: Sat, 3 Feb 2018 08:55:32 +0100 Subject: [PATCH 3/6] files not enclosed in patch restored --- lib/airspy/airspy_source_c.cc | 5 ++--- lib/rtl/rtl_source_c.cc | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/airspy/airspy_source_c.cc b/lib/airspy/airspy_source_c.cc index 02fc6ab..50150e5 100644 --- a/lib/airspy/airspy_source_c.cc +++ b/lib/airspy/airspy_source_c.cc @@ -350,10 +350,9 @@ 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++) { - std::cerr << "SR: [" << i << "]: " << _sample_rates[i].first << std::endl; + for (size_t i = 0; i < _sample_rates.size(); i++) range += osmosdr::range_t( _sample_rates[i].first ); - } + return range; } diff --git a/lib/rtl/rtl_source_c.cc b/lib/rtl/rtl_source_c.cc index eb7042e..a371464 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."); -- 1.9.1 >From a195b8a7eb328ed7c0b31a1e4aa077e11789f648 Mon Sep 17 00:00:00 2001 From: Andrea Montefusco IW0HDV Date: Sat, 3 Feb 2018 10:25:27 +0100 Subject: [PATCH 4/6] Microtelecom Perseus HF receiver support --- CMakeLists.txt | 1 + cmake/Modules/FindLibPERSEUS.cmake | 24 ++ lib/CMakeLists.txt | 8 + lib/config.h.in | 1 + lib/device.cc | 8 + lib/perseus/CMakeLists.txt | 37 +++ lib/perseus/perseus_source_c.cc | 613 +++++++++++++++++++++++++++++++++++++ lib/perseus/perseus_source_c.h | 135 ++++++++ lib/source_impl.cc | 18 +- 9 files changed, 844 insertions(+), 1 deletion(-) 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/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 #endif +#ifdef ENABLE_PERSEUS +#include +#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..bf7c9be --- /dev/null +++ b/lib/perseus/perseus_source_c.cc @@ -0,0 +1,613 @@ +/* -*- 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 +#include +#include + +#include +#include +#include +#include +#include + +#include + +#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), + _att_value(0), + _adc_preamp(false), + _adc_dither(false), + _preselector(false) +{ + dict_t dict = params_to_dict(args); + + _dev = NULL; + + // Set debug info dumped to stderr to the maximum verbose level + perseus_set_debug(3); + + // 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( 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, _att_value); + // Enable ADC Dither, Disable ADC Preamp + 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() ); + + _fifo = new boost::circular_buffer(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 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 perseus_source_c::get_devices() +{ + std::vector 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))), + _preselector == true ? 1:0); + 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 perseus_source_c::get_gain_names( size_t chan ) +{ + 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 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_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_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_attenuator( size_t chan ) +{ + 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 += "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 new file mode 100644 index 0000000..55d3f89 --- /dev/null +++ b/lib/perseus/perseus_source_c.h @@ -0,0 +1,135 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Dimitri Stolnikov + * + * 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 +#include +#include + +#include + +#include "source_iface.h" +#include + +class perseus_source_c; + +typedef boost::shared_ptr 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 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 ) {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( 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 *_fifo; + boost::mutex _fifo_lock; + boost::condition_variable _samp_avail; + + std::vector< std::pair > _sample_rates; + double _sample_rate; + double _center_freq; + double _freq_corr; + + bool _frun; + int _cnt; + double _att_value; + bool _adc_preamp; + bool _adc_dither; + bool _preselector; +}; + +#endif /* INCLUDED_PERSEUS_SOURCE_C_H */ 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 #endif +#ifdef ENABLE_PERSEUS +#include +#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 9be9bfc531d793f0d3d728c361f0b7f520d5c991 Mon Sep 17 00:00:00 2001 From: Andrea Montefusco IW0HDV Date: Tue, 27 Mar 2018 20:35:30 +0200 Subject: [PATCH 5/6] Avoid to throw exception when the new frequency is out of range; it happens when we are coming back to Perseus from wider range receivers --- lib/perseus/perseus_source_c.cc | 76 ++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/lib/perseus/perseus_source_c.cc b/lib/perseus/perseus_source_c.cc index 89674b1..faab51d 100644 --- a/lib/perseus/perseus_source_c.cc +++ b/lib/perseus/perseus_source_c.cc @@ -103,15 +103,15 @@ perseus_source_c::perseus_source_c (const std::string &args) // 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"); @@ -125,19 +125,19 @@ perseus_source_c::perseus_source_c (const std::string &args) 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) + if (flag != 0) fprintf(stderr, "The device is a preserie unit"); else { eeprom_prodid prodid; - if (perseus_get_product_id(_dev,&prodid)<0) + 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.sn, (uint16_t) prodid.signature[5], (uint16_t) prodid.signature[4], (uint16_t) prodid.signature[3], @@ -146,18 +146,18 @@ perseus_source_c::perseus_source_c (const std::string &args) (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( buf[i], i ) ); i++; } @@ -177,14 +177,14 @@ perseus_source_c::perseus_source_c (const std::string &args) std::cerr << "fpga configuration error: " << perseus_errorstr() << std::endl; PERSEUS_THROW_ON_ERROR(ret, "Perseus fpga configuration error"); } - + perseus_set_attenuator_in_db(_dev, _att_value); // Enable ADC Dither, Disable ADC Preamp 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() ); - + _fifo = new boost::circular_buffer(5000000); if (!_fifo) { throw std::runtime_error( std::string(__FUNCTION__) + " " + @@ -237,10 +237,10 @@ 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) + if (buf_size % 6) std::cerr << "FATAL !!!!!!! not on boundary" << std::endl; iq_sample s; @@ -251,7 +251,7 @@ int perseus_source_c::perseus_rx_callback(void *buf, int buf_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++; @@ -259,16 +259,16 @@ int perseus_source_c::perseus_rx_callback(void *buf, int buf_size) 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() */ @@ -290,7 +290,7 @@ 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; } @@ -301,8 +301,8 @@ bool perseus_source_c::start() if ( ! _dev ) return false; - - std::cerr << "***** START COUNTER: " << _cnt + + std::cerr << "***** START COUNTER: " << _cnt << " mod 6 " << (_cnt % 6) << std::endl; _frun = true; @@ -311,15 +311,15 @@ bool perseus_source_c::start() // 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 */, + 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 (" + std::cerr << "Failed to start RX streaming (" << ret << ")" << std::endl; - return false; - } else + return false; + } else return true; } @@ -330,15 +330,15 @@ bool perseus_source_c::stop() _frun = false; - std::cerr << "***** STOP COUNTER: " << _cnt + 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 false; + } else return true; } @@ -375,7 +375,7 @@ std::vector perseus_source_c::get_devices() std::string args = "perseus=0,label='Perseus'"; devices.push_back( args ); - + return devices; } @@ -390,7 +390,7 @@ osmosdr::meta_range_t perseus_source_c::get_sample_rates() for (size_t i = 0; i < _sample_rates.size(); i++) { range += osmosdr::range_t( _sample_rates[i].first ); - std::cerr << "SR: " << i << " : (" + std::cerr << "SR: " << i << " : (" << _sample_rates[i].first << ")" << std::endl; } return range; @@ -400,10 +400,10 @@ 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 + PERSEUS_THROW_ON_ERROR(ret, "Unable to set sample rate."); + } else _sample_rate = rate; - + return get_sample_rate(); } @@ -426,9 +426,13 @@ double perseus_source_c::set_center_freq( double freq, size_t chan ) int ret; if (_dev) { - ret = + // avoid to crash when the frequency is out of range + if (freq > PERSEUS_DDC_FREQ_MAX) freq = PERSEUS_DDC_FREQ_MAX; + if (freq < PERSEUS_DDC_FREQ_MIN) freq = PERSEUS_DDC_FREQ_MIN; + + ret = perseus_set_ddc_center_freq(_dev, - (uint64_t)(freq * (1.0 + (_freq_corr * 10e-6))), + (uint64_t)(freq * (1.0 + (_freq_corr * 10e-6))), _preselector == true ? 1:0); if ( ret == PERSEUS_NOERROR ) { _center_freq = freq; -- 1.9.1 >From 38f121f6d823d7971262e0768ab2f8be9ec35e43 Mon Sep 17 00:00:00 2001 From: Andrea Montefusco IW0HDV Date: Tue, 21 Aug 2018 13:49:17 +0200 Subject: [PATCH 6/6] perseus: gr-osmosdr Microtelecom Perseus HF receiver support --- AUTHORS | 1 + MANIFEST.md | 1 + README | 2 ++ grc/gen_osmosdr_blocks.py | 6 +++++ lib/perseus/perseus_source_c.cc | 57 ++++++++++++++++++++++++++--------------- 5 files changed, 46 insertions(+), 21 deletions(-) diff --git a/AUTHORS b/AUTHORS index 2fd882c..fd51a22 100644 --- a/AUTHORS +++ b/AUTHORS @@ -7,3 +7,4 @@ SDRplay Ltd. Pavel Demin Marcus M?ller Lukas Lao Beyer +Andrea Montefusco IW0HDV diff --git a/MANIFEST.md b/MANIFEST.md index 9e19f5f..ad33c82 100644 --- a/MANIFEST.md +++ b/MANIFEST.md @@ -10,6 +10,7 @@ tags: - sdrplay - uhd - osmocom + - perseus author: - Dimitri Stolnikov copyright_owner: diff --git a/README b/README index 67fa475..9ff3d5c 100644 --- a/README +++ b/README @@ -18,6 +18,8 @@ as well supports: * Fairwaves UmTRX through Fairwaves' fork of UHD * Red Pitaya SDR transceiver (http://bazaar.redpitaya.com) * FreeSRP through libfreesrp + * Microtelecom Perseus HF receiver through libperseus-sdr + available in https://github.com/Microtelecom/libperseus-sdr/ By using the OsmoSDR block you can take advantage of a common software api in your application(s) independent of the underlying radio hardware. diff --git a/grc/gen_osmosdr_blocks.py b/grc/gen_osmosdr_blocks.py index d1f2b1a..b7e33ef 100644 --- a/grc/gen_osmosdr_blocks.py +++ b/grc/gen_osmosdr_blocks.py @@ -219,6 +219,8 @@ While primarily being developed for the OsmoSDR hardware, this block as well sup * gnuradio .cfile input through libgnuradio-blocks * RFSPACE SDR-IQ, SDR-IP, NetSDR (incl. X2 option) * AirSpy Wideband Receiver through libairspy + * Microtelecom Perseus HF Receiver through libperseus-sdr + available in https://github.com/Microtelecom/libperseus-sdr/ #end if #if $sourk == 'sink': * gnuradio .cfile output through libgnuradio-blocks @@ -260,6 +262,7 @@ Lines ending with ... mean it's possible to bind devices together by specifying cloudiq=127.0.0.1[:50000] sdr-iq=/dev/ttyUSB0 airspy=0[,bias=0|1][,linearity][,sensitivity] + perseus=0[,attenuator=0|-10|-20|-30][,wideband[0|1]][,adcpreamp=0|1][,adcdither=0|1] #end if #if $sourk == 'sink': file='/path/to/your file',rate=1e6[,freq=100e6][,append=true][,throttle=true] ... @@ -474,6 +477,9 @@ if __name__ == '__main__': elif tail.startswith('osmosdr'): title = 'osmocom' prefix = 'osmosdr' + elif tail.startswith('perseus'): + title = 'Perseus' + prefix = 'perseus' else: raise Exception, 'file %s has wrong syntax!'%tail if tail.endswith ('source.xml'): diff --git a/lib/perseus/perseus_source_c.cc b/lib/perseus/perseus_source_c.cc index 3a388d5..4a8f12d 100644 --- a/lib/perseus/perseus_source_c.cc +++ b/lib/perseus/perseus_source_c.cc @@ -175,18 +175,36 @@ 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, _att_value); - // Enable ADC Dither, Disable ADC Preamp - 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() ); - _fifo = new boost::circular_buffer(5000000); if (!_fifo) { throw std::runtime_error( std::string(__FUNCTION__) + " " + "Failed to allocate a sample FIFO!" ); } + + int nav = -1; + //perseus=0[,attenuator=0|-10|-20|-30][,wideband[0|1]][,adcpreamp=0|1][,adcdither=0|1] + if ( dict.count( "attenuator" ) ) + nav = -(boost::lexical_cast< int >( dict["attenuator"] )); + if ( dict.count( "preselector" ) ) + _preselector = ( boost::lexical_cast< int > ( dict["preselector"] ) == 1 ); + if ( dict.count( "adcpreamp" ) ) + _adc_preamp = boost::lexical_cast< int >( dict["adcpreamp"] ); + if ( dict.count( "adcdither" ) ) + _adc_dither = boost::lexical_cast< int >( dict["adcdither"] ); + + + // attenuator + if (nav != -1) { + std::cerr << "perseus_set_attenuator_in_db: new value: " << nav << " old value: " << _att_value << std::endl; + perseus_set_attenuator_in_db(_dev, nav); + _att_value = nav; + } + // Enable ADC Dither, Disable ADC Preamp + 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 + // and, implicitly, the preselector status + set_center_freq( (get_freq_range().start() + get_freq_range().stop()) / 2.0 ); + set_sample_rate( get_sample_rates().start() ); } /* @@ -465,12 +483,9 @@ std::vector perseus_source_c::get_gain_names( size_t chan ) return names; } -// -// FIXME -// osmosdr::gain_range_t perseus_source_c::get_gain_range( size_t chan ) { - return get_gain_range( "Attenuator", chan ); + return osmosdr::gain_range_t( -30, +7, 1 ); } osmosdr::gain_range_t perseus_source_c::get_gain_range( const std::string & name, size_t chan ) @@ -491,16 +506,12 @@ osmosdr::gain_range_t perseus_source_c::get_gain_range( const std::string & name 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 ) { + if ( PERSEUS_NOERROR == perseus_set_attenuator_in_db (_dev, gain) ) { _att_value = gain; } else { - std::cerr << "ERROR: perseus_set_attenuator_in_db: " << gain << std::endl; + std::cerr << "ERROR: perseus_set_attenuator_in_db: new value was: " << gain << std::endl; } } @@ -600,21 +611,25 @@ std::vector< std::string > perseus_source_c::get_antennas( size_t chan ) std::string perseus_source_c::set_antenna( const std::string & antenna, size_t chan ) { + std::cerr << "perseus_source_c::set_antenna: preselector: " << _preselector << "ch: " << chan << std::endl; + if ( antenna == "RX" ) { _preselector = false; set_center_freq( get_center_freq() ); - return "RX"; + return get_antenna(chan); } if ( antenna == "RX+PRESELECTOR" ) { _preselector = true; set_center_freq( get_center_freq() ); - return "RX+PRESELECTOR"; + return get_antenna(chan); } - return get_antenna( chan ); + 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"; + + if (_preselector == false ) return "RX"; + else return "RX+PRESELECTOR"; } -- 1.9.1 From rascustoms at gmail.com Thu Aug 23 14:29:47 2018 From: rascustoms at gmail.com (Kieren Rasmussen) Date: Fri, 24 Aug 2018 00:29:47 +1000 Subject: [PATCH 0/3] Adds support for the AirspyHF+ Message-ID: <20180823142950.29027-1-rascustoms@gmail.com> This builds on the work started in gr-osmosdr-gqrx arispyhf branch by Alexandru Csete. Further adds support for all the gain stages within the Airspy. Kieren Rasmussen (3): - Added support for manual gain modes of AirspyHF+ - TODO support for AGC threshold level - Incorrect gain clipping when setting attenuation - Verified working with a git build of GQRX - Split the AirspyHF+ into a separate block in GRC with more relevant parameters exposed README | 1 + grc/CMakeLists.txt | 1 + grc/gen_airspyhf_blocks.py | 281 ++++++++++++++++++++++++++++++ grc/gen_osmosdr_blocks.py | 2 + lib/airspyhf/airspyhf_source_c.cc | 102 ++++++++++- lib/airspyhf/airspyhf_source_c.h | 9 + 6 files changed, 392 insertions(+), 4 deletions(-) create mode 100644 grc/gen_airspyhf_blocks.py -- 2.18.0 From rascustoms at gmail.com Thu Aug 23 14:29:48 2018 From: rascustoms at gmail.com (Kieren Rasmussen) Date: Fri, 24 Aug 2018 00:29:48 +1000 Subject: [PATCH 1/3] - Added support for manual gain modes of AirspyHF+ - TODO support for AGC threshold level In-Reply-To: <20180823142950.29027-1-rascustoms@gmail.com> References: <20180823142950.29027-1-rascustoms@gmail.com> Message-ID: <20180823142950.29027-2-rascustoms@gmail.com> From: Kieren Rasmussen --- README | 1 + grc/gen_osmosdr_blocks.py | 2 + lib/airspyhf/airspyhf_source_c.cc | 102 ++++++++++++++++++++++++++++-- lib/airspyhf/airspyhf_source_c.h | 9 +++ 4 files changed, 110 insertions(+), 4 deletions(-) diff --git a/README b/README index 67fa475..6f8ece3 100644 --- a/README +++ b/README @@ -11,6 +11,7 @@ as well supports: * gnuradio .cfile input through libgnuradio-blocks * RFSPACE SDR-IQ, SDR-IP, NetSDR (incl. X2 option) * AirSpy Wideband Receiver through libairspy + * AirSpy HF+ through libairspyhf * CCCamp 2015 rad1o Badge through libhackrf * Great Scott Gadgets HackRF through libhackrf * Nuand LLC bladeRF through libbladeRF library diff --git a/grc/gen_osmosdr_blocks.py b/grc/gen_osmosdr_blocks.py index d1f2b1a..5932395 100644 --- a/grc/gen_osmosdr_blocks.py +++ b/grc/gen_osmosdr_blocks.py @@ -219,6 +219,7 @@ While primarily being developed for the OsmoSDR hardware, this block as well sup * gnuradio .cfile input through libgnuradio-blocks * RFSPACE SDR-IQ, SDR-IP, NetSDR (incl. X2 option) * AirSpy Wideband Receiver through libairspy + * AirSpy HF+ through libairspyhf #end if #if $sourk == 'sink': * gnuradio .cfile output through libgnuradio-blocks @@ -260,6 +261,7 @@ Lines ending with ... mean it's possible to bind devices together by specifying cloudiq=127.0.0.1[:50000] sdr-iq=/dev/ttyUSB0 airspy=0[,bias=0|1][,linearity][,sensitivity] + airspyhf=0 #end if #if $sourk == 'sink': file='/path/to/your file',rate=1e6[,freq=100e6][,append=true][,throttle=true] ... diff --git a/lib/airspyhf/airspyhf_source_c.cc b/lib/airspyhf/airspyhf_source_c.cc index f4b3533..e4dbe5f 100644 --- a/lib/airspyhf/airspyhf_source_c.cc +++ b/lib/airspyhf/airspyhf_source_c.cc @@ -384,38 +384,132 @@ double airspyhf_source_c::get_freq_corr( size_t chan ) std::vector airspyhf_source_c::get_gain_names( size_t chan ) { - return std::vector(); + std::vector names; + names += "LNA"; + names += "ATT"; + + return names; } osmosdr::gain_range_t airspyhf_source_c::get_gain_range( size_t chan ) { - return osmosdr::gain_range_t(); + // Based on libairspyhf +6db with LNA and 0 to -48dB with Attenuation + return osmosdr::gain_range_t(-48, 6, 6); } osmosdr::gain_range_t airspyhf_source_c::get_gain_range( const std::string & name, size_t chan ) { + if ("LNA" == name) { + return osmosdr::gain_range_t(0, 6, 6); + } else if ("ATT" == name) { + return osmosdr::gain_range_t(-48, 0, 6); + } return osmosdr::gain_range_t(); } +bool airspyhf_source_c::set_gain_mode( bool automatic, size_t chan ) +{ + if ( automatic ) { + airspyhf_set_hf_agc( _dev, 1 ); + } else { + airspyhf_set_hf_agc( _dev, 0 ); + } + _auto_gain = automatic; + + return get_gain_mode(chan); +} + +bool airspyhf_source_c::get_gain_mode( size_t chan ) +{ + return _auto_gain; +} double airspyhf_source_c::set_gain( double gain, size_t chan ) { + osmosdr::gain_range_t gains = get_gain_range(chan); + + if(_dev) { + double clip_gain = gains.clip(gain, true); + + if (clip_gain > 0) { + if (set_lna_gain(clip_gain, chan) == clip_gain && set_att_gain(0, chan) == 0) { + _gain = clip_gain; + } + } else { + if (set_lna_gain(0, chan) == 0 && set_att_gain(clip_gain, chan) == 0) { + _gain = clip_gain; + } + } + } + return gain; } double airspyhf_source_c::set_gain( double gain, const std::string & name, size_t chan) { + if ("LNA" == name) { + return set_lna_gain(gain, chan); + } else if ("ATT" == name) { + return set_att_gain(gain, chan); + } + return gain; } double airspyhf_source_c::get_gain( size_t chan ) { - return 0.0; + return _gain; } double airspyhf_source_c::get_gain( const std::string & name, size_t chan ) { - return 0.0; + if ("LNA" == name) { + return _lna_gain; + } else if ("ATT" == name) { + return _att_gain; + } + + return get_gain(chan); +} + +double airspyhf_source_c::set_lna_gain( double gain, size_t chan ) +{ + int ret = AIRSPYHF_SUCCESS; + osmosdr::gain_range_t gains = get_gain_range( "LNA", chan ); + + if (_dev) { + double clip_gain = gains.clip( gain, true ); + uint8_t value = (uint8_t) ((clip_gain > 0) ? 1 : 0); + + ret = airspyhf_set_hf_lna( _dev, value ); + if ( AIRSPYHF_SUCCESS == ret ) { + _lna_gain = clip_gain; + } else { + AIRSPYHF_THROW_ON_ERROR( ret, AIRSPYHF_FUNC_STR( "airspyhf_set_lna_gain", value ) ) + } + } + + return _lna_gain; +} + +double airspyhf_source_c::set_att_gain( double gain, size_t chan ) +{ + int ret = AIRSPYHF_SUCCESS; + osmosdr::gain_range_t gains = get_gain_range( "LNA", chan ); + + if (_dev) { + double clip_gain = gains.clip( gain, true ); + uint8_t value = (uint8_t) (clip_gain / -6); + + ret = airspyhf_set_hf_att( _dev, value ); + if ( AIRSPYHF_SUCCESS == ret ) { + _att_gain = clip_gain; + } else { + AIRSPYHF_THROW_ON_ERROR( ret, AIRSPYHF_FUNC_STR( "airspyhf_set_att_gain", value ) ) + } + } + + return _lna_gain; } std::vector< std::string > airspyhf_source_c::get_antennas( size_t chan ) diff --git a/lib/airspyhf/airspyhf_source_c.h b/lib/airspyhf/airspyhf_source_c.h index cfb8c89..d464d07 100644 --- a/lib/airspyhf/airspyhf_source_c.h +++ b/lib/airspyhf/airspyhf_source_c.h @@ -88,11 +88,16 @@ public: std::vector get_gain_names( size_t chan = 0 ); osmosdr::gain_range_t get_gain_range( size_t chan = 0 ); osmosdr::gain_range_t get_gain_range( const std::string & name, size_t chan = 0 ); + bool set_gain_mode( bool automatic, size_t chan = 0 ); + bool get_gain_mode( size_t chan = 0 ); double set_gain( double gain, size_t chan = 0 ); double set_gain( double gain, const std::string & name, size_t chan = 0 ); double get_gain( size_t chan = 0 ); double get_gain( const std::string & name, size_t chan = 0 ); + double set_lna_gain( double gain, size_t chan = 0 ); + double set_att_gain( double gain, size_t chan = 0 ); + std::vector< std::string > get_antennas( size_t chan = 0 ); std::string set_antenna( const std::string & antenna, size_t chan = 0 ); std::string get_antenna( size_t chan = 0 ); @@ -112,6 +117,10 @@ private: double _sample_rate; double _center_freq; double _freq_corr; + bool _auto_gain; + double _gain; + double _lna_gain; + double _att_gain; }; #endif /* INCLUDED_AIRSPY_SOURCE_C_H */ -- 2.18.0 From rascustoms at gmail.com Thu Aug 23 14:29:49 2018 From: rascustoms at gmail.com (Kieren Rasmussen) Date: Fri, 24 Aug 2018 00:29:49 +1000 Subject: [PATCH 2/3] - Incorrect gain clipping when setting attenuation - Verified working with a git build of GQRX In-Reply-To: <20180823142950.29027-1-rascustoms@gmail.com> References: <20180823142950.29027-1-rascustoms@gmail.com> Message-ID: <20180823142950.29027-3-rascustoms@gmail.com> From: Kieren Rasmussen --- lib/airspyhf/airspyhf_source_c.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/airspyhf/airspyhf_source_c.cc b/lib/airspyhf/airspyhf_source_c.cc index e4dbe5f..b2965ff 100644 --- a/lib/airspyhf/airspyhf_source_c.cc +++ b/lib/airspyhf/airspyhf_source_c.cc @@ -495,7 +495,7 @@ double airspyhf_source_c::set_lna_gain( double gain, size_t chan ) double airspyhf_source_c::set_att_gain( double gain, size_t chan ) { int ret = AIRSPYHF_SUCCESS; - osmosdr::gain_range_t gains = get_gain_range( "LNA", chan ); + osmosdr::gain_range_t gains = get_gain_range( "ATT", chan ); if (_dev) { double clip_gain = gains.clip( gain, true ); -- 2.18.0 From rascustoms at gmail.com Thu Aug 23 14:29:50 2018 From: rascustoms at gmail.com (Kieren Rasmussen) Date: Fri, 24 Aug 2018 00:29:50 +1000 Subject: [PATCH 3/3] - Split the AirspyHF+ into a separate block in GRC with more relevant parameters exposed In-Reply-To: <20180823142950.29027-1-rascustoms@gmail.com> References: <20180823142950.29027-1-rascustoms@gmail.com> Message-ID: <20180823142950.29027-4-rascustoms@gmail.com> From: Kieren Rasmussen --- grc/CMakeLists.txt | 1 + grc/gen_airspyhf_blocks.py | 281 +++++++++++++++++++++++++++++++++++++ 2 files changed, 282 insertions(+) create mode 100644 grc/gen_airspyhf_blocks.py diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt index 09838c1..92a0e10 100644 --- a/grc/CMakeLists.txt +++ b/grc/CMakeLists.txt @@ -32,6 +32,7 @@ macro(GEN_BLOCK_XML _generator _xml_block) ) endmacro(GEN_BLOCK_XML) +GEN_BLOCK_XML(gen_airspyhf_blocks.py airspyhf_source.xml) GEN_BLOCK_XML(gen_osmosdr_blocks.py rtlsdr_source.xml) GEN_BLOCK_XML(gen_osmosdr_blocks.py osmosdr_source.xml) GEN_BLOCK_XML(gen_osmosdr_blocks.py osmosdr_sink.xml) diff --git a/grc/gen_airspyhf_blocks.py b/grc/gen_airspyhf_blocks.py new file mode 100644 index 0000000..5878731 --- /dev/null +++ b/grc/gen_airspyhf_blocks.py @@ -0,0 +1,281 @@ +""" +Copyright 2012 Free Software Foundation, Inc. + +This file is part of GNU Radio + +GNU Radio Companion 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 2 +of the License, or (at your option) any later version. + +GNU Radio Companion 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 this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +""" + +MAIN_TMPL = """\ + + + $(title) $sourk.title() + $(prefix)_$(sourk) + $($sourk.title())s + 1 + import osmosdr + import time + osmosdr.$(sourk)( args="numchan=" + str(\$nchan) + " " + \$args ) +self.\$(id).set_sample_rate(\$sample_rate) +#for $n in range($max_nchan) +\#if \$nchan() > $n +self.\$(id).set_center_freq(\$freq$(n), $n) +self.\$(id).set_freq_corr(\$corr$(n), $n) +self.\$(id).set_iq_balance_mode(\$iq_balance_mode$(n), $n) +self.\$(id).set_gain_mode(\$gain_mode$(n), $n) +self.\$(id).set_gain(\$lna_gain$(n), "LNA", $n) +self.\$(id).set_gain(\$att_gain$(n), "ATT", $n) +\#end if +#end for + + set_sample_rate(\$sample_rate) + #for $n in range($max_nchan) + set_center_freq(\$freq$(n), $n) + set_freq_corr(\$corr$(n), $n) + set_iq_balance_mode(\$iq_balance_mode$(n), $n) + set_gain_mode(\$gain_mode$(n), $n) + set_gain(\$lna_gain$(n), "LNA", $n) + set_gain(\$att_gain$(n), "ATT", $n) + #end for + + $(dir.title())put Type + type + enum + + + + Device Arguments + args + airspyhf=0 + string + + \#if \$args() + none + \#else + part + \#end if + + + + Num Mboards + num_mboards + 1 + int + part + #for $m in range(1, $max_mboards+1) + + #end for + + + Num Channels + nchan + 1 + int + #for $n in range(1, $max_nchan+1) + + #end for + + + Sample Rate (sps) + sample_rate + samp_rate + real + + $params + $max_nchan >= \$nchan + \$nchan > 0 + $max_mboards >= \$num_mboards + \$num_mboards > 0 + \$nchan >= \$num_mboards + <$sourk> + $dir + \$type.type + \$nchan + + +The AirspyHF $sourk block: + +This block supports: + * AirSpy HF+ through libairspyhf + +Output Type: +This parameter controls the data type of the stream in gnuradio. Only complex float32 samples are supported at the moment. + +Device Arguments: +The device argument is a comma delimited string used to locate devices on your system. Device arguments for multiple devices may be given by separating them with a space. +Use the device id or name/serial (if applicable) to specify a certain device or list of devices. If left blank, the first device found will be used. + +Examples: + +Optional arguments are placed into [] brackets, remove the brackets before using them! Specific variable values are separated with a |, choose one of them. Variable values containing spaces shall be enclosed in '' as demonstrated in examples section below. +Lines ending with ... mean it's possible to bind devices together by specifying multiple device arguments separated with a space. + + airspyhf=0 + +Num Channels: +Selects the total number of channels in this multi-device configuration. Required when specifying multiple device arguments. + +Sample Rate: +The sample rate is the number of samples per second output by this block on each channel. + +Frequency: +The center frequency is the frequency the RF chain is tuned to. + +Freq. Corr.: +The frequency correction factor in parts per million (ppm). Set to 0 if unknown. + +IQ Balance Mode: +Controls the behavior of software IQ imbalance corrrection. + Off: Disable correction algorithm (pass through). + Manual: Keep last estimated correction when switched from Automatic to Manual. + Automatic: Periodically find the best solution to compensate for image signals. + +This functionality depends on http://cgit.osmocom.org/cgit/gr-iqbal/ + +Gain Mode: +Chooses between the manual (default) and automatic gain mode where appropriate. +To allow manual control of LNA and Attenuator stages, manual gain mode must be configured. + +LNA Gain: +LNA gain of the device. Either 0dB or 6dB of gain available. + +ATT Gain: +Attenuator gain of the device. Between -48dB and 0dB of gain available with 6dB steps. + +See the OsmoSDR project page for more detailed documentation: +http://sdr.osmocom.org/trac/wiki/GrOsmoSDR +http://sdr.osmocom.org/trac/wiki/rtl-sdr +http://sdr.osmocom.org/trac/ + + +""" + +PARAMS_TMPL = """ + + Ch$(n): Frequency (Hz) + freq$(n) + 100e6 + real + \#if \$nchan() > $n then 'none' else 'all'# + + + Ch$(n): Freq. Corr. (ppm) + corr$(n) + 0 + real + \#if \$nchan() > $n then 'none' else 'all'# + + + Ch$(n): IQ Balance Mode + iq_balance_mode$(n) + 0 + int + \#if \$nchan() > $n then 'none' else 'all'# + + + + + + Ch$(n): Gain Mode + gain_mode$(n) + False + bool + \#if \$nchan() > $n then 'none' else 'all'# + + + + + Ch$(n): LNA Gain (dB) + lna_gain$(n) + 6 + real + \#if \$nchan() > $n then 'none' else 'all'# + + + Ch$(n): ATT Gain (dB) + att_gain$(n) + 0 + real + \#if \$nchan() > $n then 'none' else 'all'# + +""" + + +def parse_tmpl(_tmpl, **kwargs): + from Cheetah import Template + return str(Template.Template(_tmpl, kwargs)) + + +max_num_mboards = 8 +max_num_channels = max_num_mboards + +import os.path + +if __name__ == '__main__': + import sys + + for file in sys.argv[1:]: + head, tail = os.path.split(file) + + if tail.startswith('airspyhf'): + title = 'AirspyHF' + prefix = 'airspyhf' + else: + raise Exception, 'file %s has wrong syntax!' % tail + + if tail.endswith('source.xml'): + sourk = 'source' + dir = 'out' + elif tail.endswith('sink.xml'): + sourk = 'sink' + dir = 'in' + else: + raise Exception, 'is %s a source or sink?' % file + + params = ''.join([parse_tmpl(PARAMS_TMPL, n=n, sourk=sourk) for n in range(max_num_channels)]) + open(file, 'w').write(parse_tmpl(MAIN_TMPL, + max_nchan=max_num_channels, + max_mboards=max_num_mboards, + params=params, + title=title, + prefix=prefix, + sourk=sourk, + dir=dir, + )) -- 2.18.0