From willcode4 at gmail.com Tue Jan 2 02:08:23 2018 From: willcode4 at gmail.com (Jeff Long) Date: Mon, 1 Jan 2018 21:08:23 -0500 Subject: SDRplay rewrite w/RSP2 In-Reply-To: References: Message-ID: <92727aeb-5e72-6a58-cc36-d5a6556ffaf7@gmail.com> The RSP1A and RSP2 work well for me using this code, and there are many improvements since my last post, so I'm looking for other people to help test it. Something along the lines of the following commands will allow installation and test using a temporary directory: git clone https://github.com/willcode/gr-osmosdr cd gr-osmosdr git checkout sdrplay2 mkdir build cd build cmake -DCMAKE_INSTALL_PREFIX=/tmp/test -DENABLE_NONFREE=yes .. make mkdir /tmp/test make install LD_LIBRARY_PATH=/tmp/test/lib64:$LD_LIBRARY_PATH gqrx This product does require a non-free library that gets compiled in to gr-osmosdr. This limits inclusion on distributions, but it's still easy to use from a PyBOMBs installation or a manual install, as above. On 12/16/2017 04:54 PM, Jeff Long wrote: > I picked up a SDRplay RSP2 recently, and the gr-osmosdr support needed > some work. Here's a rewrite that works with RSP2 and has other > improvements. When the RSP1A comes in, I'll finish support for that. No > plans to pick up a RSP1, but someone can see if this works if > interested. This was tested against the Nov 2017 binary blob. > > https://github.com/willcode/gr-osmosdr/tree/sdrplay2 > > ===== > > Complete rewrite of sdrplay support > > - Supports RSP2 > - Will support RSP1A when hardware arrives > - RSP1 is untested > - Uses streaming callbacks instead of polling > - Tuning, gain control imporoved, AGC supported > - Better performance (one less layer of buffers) From michael.patrick.morris at gmail.com Thu Jan 4 06:31:28 2018 From: michael.patrick.morris at gmail.com (Mike Morris) Date: Thu, 4 Jan 2018 01:31:28 -0500 Subject: [PATCH] rtl_tcp: rtl_tcp_source_c.cc: Adds explicit cast Message-ID: <20180104063128.68874-1-michael.patrick.morris@gmail.com> To prevent build errors on macOS 10.13.2 --- lib/rtl_tcp/rtl_tcp_source_c.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rtl_tcp/rtl_tcp_source_c.cc b/lib/rtl_tcp/rtl_tcp_source_c.cc index ecdeee0..c962dd8 100644 --- a/lib/rtl_tcp/rtl_tcp_source_c.cc +++ b/lib/rtl_tcp/rtl_tcp_source_c.cc @@ -298,17 +298,17 @@ rtl_tcp_source_c::rtl_tcp_source_c(const std::string &args) : // set direct sampling struct command cmd; - cmd = { 0x09, htonl(direct_samp) }; + cmd = (struct command){ 0x09, htonl(direct_samp) }; send(d_socket, (const char*)&cmd, sizeof(cmd), 0); if (direct_samp) _no_tuner = true; // set offset tuning - cmd = { 0x0a, htonl(offset_tune) }; + cmd = (struct command){ 0x0a, htonl(offset_tune) }; send(d_socket, (const char*)&cmd, sizeof(cmd), 0); // set bias tee - cmd = { 0x0e, htonl(bias_tee) }; + cmd = (struct command){ 0x0e, htonl(bias_tee) }; send(d_socket, (const char*)&cmd, sizeof(cmd), 0); } @@ -567,7 +567,7 @@ bool rtl_tcp_source_c::set_gain_mode( bool automatic, size_t chan ) send(d_socket, (const char*)&cmd, sizeof(cmd), 0); // AGC mode - cmd = { 0x08, htonl(automatic) }; + cmd = (struct command){ 0x08, htonl(automatic) }; send(d_socket, (const char*)&cmd, sizeof(cmd), 0); _auto_gain = automatic; -- 2.15.1 From sc.olivier at gmail.com Mon Jan 8 21:37:18 2018 From: sc.olivier at gmail.com (Olivier SCHMITT) Date: Mon, 08 Jan 2018 22:37:18 +0100 Subject: limitations of using multiple RTL-SDR dongles simultaneously Message-ID: <5A53E48E.2020509@gmail.com> Hello dear RTL-SDR users, I whant to use multiples dongles as array for decode wspr. My problem is that I can not use more than 4 USB dongle simultaneously. I check with usbmon, each dongle uses 500kbits / s around. So I am far from the maximum bandwidth allowed by usb 2. I tried using a USB switch with external power supply but it does not work better. The default test performed is: #!/bin/bash for ((i=0;i<=4;i++)) do rtl_test -d $i -s 250000 & done First 4 is starting but last 5 doesent. And i whant to use more... Is this a limitation of the RTL-SDR library or the kernel driver? Do we have a solution to get around the problem? Thank you in advance for your valuable feedback. From steve at steve-m.de Mon Jan 8 21:42:47 2018 From: steve at steve-m.de (Steve Markgraf) Date: Mon, 8 Jan 2018 22:42:47 +0100 Subject: limitations of using multiple RTL-SDR dongles simultaneously In-Reply-To: <5A53E48E.2020509@gmail.com> References: <5A53E48E.2020509@gmail.com> Message-ID: Hi Oliver, On 08.01.2018 22:37, Olivier SCHMITT wrote: > Is this a limitation of the RTL-SDR library or the kernel driver? > Do we have a solution to get around the problem? This is a limitation of the Kernel, as the maximum usbfs buffer size is set to 16 MB by default. In order to disable this limit, do the following: echo 0 > /sys/module/usbcore/parameters/usbfs_memory_mb Regards, Steve From sc.olivier at gmail.com Mon Jan 8 22:00:20 2018 From: sc.olivier at gmail.com (Olivier SCHMITT) Date: Mon, 08 Jan 2018 23:00:20 +0100 Subject: limitations of using multiple RTL-SDR dongles simultaneously In-Reply-To: References: <5A53E48E.2020509@gmail.com> Message-ID: <5A53E9F4.2080005@gmail.com> Nice MR Markgraf ! It works perfectly! Thanks you very much! :-) Thanks also to MR Muller for the Linux distrib that I did not know GNU_Radio-Live-Hamradio-binary.iso Good continuation! Le 08/01/2018 22:42, Steve Markgraf a ?crit : > Hi Oliver, > > On 08.01.2018 22:37, Olivier SCHMITT wrote: >> Is this a limitation of the RTL-SDR library or the kernel driver? >> Do we have a solution to get around the problem? > This is a limitation of the Kernel, as the maximum usbfs buffer size > is set to 16 MB by default. > In order to disable this limit, do the following: > > echo 0 > /sys/module/usbcore/parameters/usbfs_memory_mb > > Regards, > Steve From marcus.mueller2 at kit.edu Mon Jan 8 21:50:15 2018 From: marcus.mueller2 at kit.edu (=?utf-8?B?TcO8bGxlciwgTWFyY3VzIChDRUwp?=) Date: Mon, 8 Jan 2018 21:50:15 +0000 Subject: limitations of using multiple RTL-SDR dongles simultaneously In-Reply-To: References: <5A53E48E.2020509@gmail.com> Message-ID: <1515448214.28141.22.camel@kit.edu> Ah, didn't know that! Interesting. This voids my previous mail. On Mon, 2018-01-08 at 22:42 +0100, Steve Markgraf wrote: > Hi Oliver, > > On 08.01.2018 22:37, Olivier SCHMITT wrote: > > Is this a limitation of the RTL-SDR library or the kernel driver? > > Do we have a solution to get around the problem? > > This is a limitation of the Kernel, as the maximum usbfs buffer size > is set to 16 MB by default. > In order to disable this limit, do the following: > > echo 0 > /sys/module/usbcore/parameters/usbfs_memory_mb > > Regards, > Steve From marcus.mueller2 at kit.edu Mon Jan 8 21:49:43 2018 From: marcus.mueller2 at kit.edu (=?utf-8?B?TcO8bGxlciwgTWFyY3VzIChDRUwp?=) Date: Mon, 8 Jan 2018 21:49:43 +0000 Subject: limitations of using multiple RTL-SDR dongles simultaneously In-Reply-To: <5A53E48E.2020509@gmail.com> References: <5A53E48E.2020509@gmail.com> Message-ID: <1515448182.28141.21.camel@kit.edu> Hi Olivier, at a sampling rate of 250 kHz, and with the usual 8 bit complex (meaning 8bit I + 8bit Q = 16 bit per sample), a single dongle will send 4 Mbit/s, not 500 kbit/s. Are you perhaps confusing bytes with bits? Anyway, you're right, the hypothetical transfer rate of USB2 HiSpeed would be 480 Mbit/s, so plenty enough for more than 4 dongles. You say the fifth RTL dongle doesn't even start. Is there a specific error? That very much doesn't sound like a data rate problem, but more like a hardware issue. Can you see (without using them) more than four dongles on your USB bus? What operating system are you using? Does it work under e.g. Linux?? (I see you're using bash, but I'm not sure this implies this is Linux.) Best regards, Marcus ? you could download a USB image and try on your own. Trying in a VM doesn't make sense, so really write the image to a USB dongle or DVD: http://inpha.se/GNU_Radio-Live-Hamradio-binary.iso It's half a year old already, but I built it to include rtl_sdr tooling On Mon, 2018-01-08 at 22:37 +0100, Olivier SCHMITT wrote: > Hello dear RTL-SDR users, > > I whant to use multiples dongles as array for decode wspr. > My problem is that I can not use more than 4 USB dongle > simultaneously. > > I check with usbmon, each dongle uses 500kbits / s around. So I am > far > from the maximum bandwidth allowed by usb 2. > I tried using a USB switch with external power supply but it does > not > work better. > > The default test performed is: > > #!/bin/bash > for ((i=0;i<=4;i++)) > do > rtl_test -d $i -s 250000 & > done > > First 4 is starting but last 5 doesent. And i whant to use more... > > Is this a limitation of the RTL-SDR library or the kernel driver? > Do we have a solution to get around the problem? > > Thank you in advance for your valuable feedback. From sc.olivier at gmail.com Tue Jan 9 00:06:14 2018 From: sc.olivier at gmail.com (Olivier SCHMITT) Date: Tue, 09 Jan 2018 01:06:14 +0100 Subject: modify rtl-sdr.h to lower the sample rate at 125000 Message-ID: <5A540776.2090206@gmail.com> Hello dear RTL-SDR community, I just whant to ask if it's possible to modify rtl-sdr.h to lower the admissible sample rate. I am currently using 8 dongle successfully but would like to use 12 dongle simultaneously with software (rtlsdr-wsprd) to decode wspr signals. This one uses the library rtl-sdr. I would therefore like to be able to use sampling frequencies lower than 250000. Thanks in advance! From willcode4 at gmail.com Fri Jan 12 17:48:29 2018 From: willcode4 at gmail.com (Jeff Long) Date: Fri, 12 Jan 2018 12:48:29 -0500 Subject: [gr-osmosdr] Patch: Update SDRplay Support Message-ID: SDRplay support, rewritten, supporting RSP1, RSP1A, and RSP2 Requires ENABLE_NONFREE=yes. Requires SDRplay's SDK v2.11 to be installed by the user. In the future, if there is a way to redistribute mirsdrapi-rsp.h from the SDK, that problem could be addressed using a shim library that looks for /usr/local/lib/libmirsdrapi-rsp.so at runtime. -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- diff --git a/lib/sdrplay/sdrplay_source_c.cc b/lib/sdrplay/sdrplay_source_c.cc index 9a9ee40..dbafaaa 100644 --- a/lib/sdrplay/sdrplay_source_c.cc +++ b/lib/sdrplay/sdrplay_source_c.cc @@ -1,15 +1,13 @@ /* -*- c++ -*- */ /* - * Copyright 2015 SDRplay Ltd - * Copyright 2012 Dimitri Stolnikov - * Copyright 2012 Steve Markgraf + * Copyright 2018 Jeff Long * * 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, + * 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. @@ -20,11 +18,6 @@ * 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 @@ -32,569 +25,747 @@ #include "sdrplay_source_c.h" #include #include "osmosdr/source.h" +#include "arg_helpers.h" #include #include +#include +#include +#include -#include #include -#include -#include - -#include - -#include "arg_helpers.h" +#include #define MAX_SUPPORTED_DEVICES 4 -struct sdrplay_dev -{ - int gRdB; - double gain_dB; - double fsHz; - double rfHz; - mir_sdr_Bw_MHzT bwType; - mir_sdr_If_kHzT ifType; - int samplesPerPacket; - int maxGain; - int minGain; - int dcMode; -}; - using namespace boost::assign; -#define BYTES_PER_SAMPLE 4 // sdrplay device delivers 16 bit signed IQ data - // containing 12 bits of information +// Index by mir_sdr_Bw_MHzT +static std::vector bandwidths = { + 0, // Dummy + 200e3, + 300e3, + 600e3, + 1536e3, + 5000e3, + 6000e3, + 7000e3, + 8000e3 +}; -#define SDRPLAY_AM_MIN 150e3 -#define SDRPLAY_AM_MAX 30e6 -#define SDRPLAY_FM_MIN 64e6 -#define SDRPLAY_FM_MAX 108e6 -#define SDRPLAY_B3_MIN 162e6 -#define SDRPLAY_B3_MAX 240e6 -#define SDRPLAY_B45_MIN 470e6 -#define SDRPLAY_B45_MAX 960e6 -#define SDRPLAY_L_MIN 1450e6 -#define SDRPLAY_L_MAX 1675e6 +// TODO - RSP1 lower freq is 10e3. +#define SDRPLAY_FREQ_MIN 1e3 +#define SDRPLAY_FREQ_MAX 2000e6 -#define SDRPLAY_MAX_BUF_SIZE 504 +static std::string hwName(int hwVer) +{ + if (hwVer == 1) + return "RSP1"; + if (hwVer == 2) + return "RSP2"; + if (hwVer ==255) + return "RSP1A"; + return "UNK"; +} -/* - * Create a new instance of sdrplay_source_c and return - * a boost shared_ptr. This is effectively the public constructor. - */ sdrplay_source_c_sptr make_sdrplay_source_c (const std::string &args) { return gnuradio::get_initial_sptr(new sdrplay_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 +// 0 inputs, 1 output +static const int MIN_IN = 0; +static const int MAX_IN = 0; +static const int MIN_OUT = 1; +static const int MAX_OUT = 1; -/* - * The private constructor - */ sdrplay_source_c::sdrplay_source_c (const std::string &args) : gr::sync_block ("sdrplay_source_c", - gr::io_signature::make(MIN_IN, MAX_IN, sizeof (gr_complex)), - gr::io_signature::make(MIN_OUT, MAX_OUT, sizeof (gr_complex))), - _running(false), - _uninit(false), - _auto_gain(false) -{ - _dev = (sdrplay_dev_t *)malloc(sizeof(sdrplay_dev_t)); - if (_dev == NULL) - { - return; + gr::io_signature::make(MIN_IN, MAX_IN, sizeof (gr_complex)), + gr::io_signature::make(MIN_OUT, MAX_OUT, sizeof (gr_complex))), + _auto_gain(false), + _gain(40), + _gRdB(40), + _lna(0), + _bcastNotch(0), + _dabNotch(0), + _band(0), + _fsHz(8e6), + _decim(1), + _rfHz(100e6), + _bwType(mir_sdr_BW_6_000), + _ifType(mir_sdr_IF_Zero), + _dcMode(1), + _buffer(NULL), + _streaming(false), + _flowgraphRunning(false), + _reinit(false) +{ + dict_t dict = params_to_dict(args); + if (dict.count("sdrplay")) { + _devIndex = boost::lexical_cast(dict["sdrplay"]); + } + else { + _devIndex = 0; + } + + mir_sdr_DebugEnable(1); + + unsigned int numDevices; + mir_sdr_DeviceT mirDevices[MAX_SUPPORTED_DEVICES]; + mir_sdr_GetDevices(mirDevices, &numDevices, MAX_SUPPORTED_DEVICES); + _hwVer = mirDevices[_devIndex].hwVer; + + if (_hwVer == 2) { + _antenna = "A"; + } + else { + _antenna = "RX"; + } + + _biasT = 0; + if ( dict.count("bias") ) { + _biasT = boost::lexical_cast( dict["bias"] ); + } } - _dev->fsHz = 2048e3; - _dev->rfHz = 200e6; - _dev->bwType = mir_sdr_BW_1_536; - _dev->ifType = mir_sdr_IF_Zero; - _dev->samplesPerPacket = 0; - _dev->dcMode = 0; - _dev->gRdB = 60; - set_gain_limits(_dev->rfHz); - _dev->gain_dB = _dev->maxGain - _dev->gRdB; - - _bufi.reserve(SDRPLAY_MAX_BUF_SIZE); - _bufq.reserve(SDRPLAY_MAX_BUF_SIZE); - _buf_mutex.lock(); - _buf_offset = 0; - _buf_mutex.unlock(); +sdrplay_source_c::~sdrplay_source_c () +{ + if (_streaming) { + stopStreaming(); + } } -/* - * Our virtual destructor. - */ -sdrplay_source_c::~sdrplay_source_c () +bool sdrplay_source_c::start(void) { - free(_dev); - _dev = NULL; - _buf_mutex.lock(); - if (_running) - { - _running = false; - } - _uninit = true; - _buf_mutex.unlock(); -} - -void sdrplay_source_c::reinit_device() -{ - std::cerr << "reinit_device started" << std::endl; - _buf_mutex.lock(); - std::cerr << "after mutex.lock" << std::endl; - if (_running) - { - std::cerr << "mir_sdr_Uninit started" << std::endl; - mir_sdr_Uninit(); - } - - std::cerr << "mir_sdr_Init started" << std::endl; - mir_sdr_Init(_dev->gRdB, _dev->fsHz / 1e6, _dev->rfHz / 1e6, _dev->bwType, _dev->ifType, &_dev->samplesPerPacket); - - if (_dev->dcMode) - { - std::cerr << "mir_sdr_SetDcMode started" << std::endl; - mir_sdr_SetDcMode(4, 1); - } - - _buf_offset = 0; - _buf_mutex.unlock(); - std::cerr << "reinit_device end" << std::endl; -} - -void sdrplay_source_c::set_gain_limits(double freq) -{ - if (freq <= SDRPLAY_AM_MAX) - { - _dev->minGain = -4; - _dev->maxGain = 98; - } - else if (freq <= SDRPLAY_FM_MAX) - { - _dev->minGain = 1; - _dev->maxGain = 103; - } - else if (freq <= SDRPLAY_B3_MAX) - { - _dev->minGain = 5; - _dev->maxGain = 107; - } - else if (freq <= SDRPLAY_B45_MAX) - { - _dev->minGain = 9; - _dev->maxGain = 94; - } - else if (freq <= SDRPLAY_L_MAX) - { - _dev->minGain = 24; - _dev->maxGain = 105; - } -} - -int sdrplay_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]; - int cnt = noutput_items; - unsigned int sampNum; - int grChanged; - int rfChanged; - int fsChanged; - - if (_uninit) - { - return WORK_DONE; - } - - if (!_running) - { - reinit_device(); - _running = true; - } - - _buf_mutex.lock(); - - if (_buf_offset) - { - for (int i = _buf_offset; i < _dev->samplesPerPacket; i++) - { - *out++ = gr_complex( float(_bufi[i]) * (1.0f/2048.0f), float(_bufq[i]) * (1.0f/2048.0f) ); - } - cnt -= (_dev->samplesPerPacket - _buf_offset); - } - - while ((cnt - _dev->samplesPerPacket) >= 0) - { - mir_sdr_ReadPacket(_bufi.data(), _bufq.data(), &sampNum, &grChanged, &rfChanged, &fsChanged); - for (int i = 0; i < _dev->samplesPerPacket; i++) - { - *out++ = gr_complex( float(_bufi[i]) * (1.0f/2048.0f), float(_bufq[i]) * (1.0f/2048.0f) ); - } - cnt -= _dev->samplesPerPacket; - } - - _buf_offset = 0; - if (cnt) - { - mir_sdr_ReadPacket(_bufi.data(), _bufq.data(), &sampNum, &grChanged, &rfChanged, &fsChanged); - for (int i = 0; i < cnt; i++) - { - *out++ = gr_complex( float(_bufi[i]) * (1.0f/2048.0f), float(_bufq[i]) * (1.0f/2048.0f) ); - } - _buf_offset = cnt; - } - _buf_mutex.unlock(); + _flowgraphRunning = true; + return true; +} - return noutput_items; +bool sdrplay_source_c::stop(void) +{ + _flowgraphRunning = false; + return true; } -std::vector sdrplay_source_c::get_devices() +int sdrplay_source_c::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) { - std::vector devices; - std::cerr << "get_devices started" << std::endl; + gr_complex *out = (gr_complex *)output_items[0]; - unsigned int dev_cnt = 0; - int samplesPerPacket; - while(mir_sdr_Init(60, 2.048, 200.0, mir_sdr_BW_1_536, mir_sdr_IF_Zero, &samplesPerPacket) == mir_sdr_Success) - { - dev_cnt++; - } + if (!_streaming) + startStreaming(); - std::cerr << "Device count: " << dev_cnt << std::endl; + { + boost::mutex::scoped_lock lock(_bufferMutex); + _buffer = out; + _bufferSpaceRemaining = noutput_items; + _bufferOffset = 0; + _bufferReady.notify_one(); - for (unsigned int i = 0; i < dev_cnt; i++) - { - mir_sdr_Uninit(); - std::string args = "sdrplay=" + boost::lexical_cast< std::string >( i ); - args += ",label='" + std::string("SDRplay RSP") + "'"; - std::cerr << args << std::endl; - devices.push_back( args ); - } + while (_buffer && _streaming) { + _bufferReady.wait(lock); + } + } - std::cerr << "get_devices end" << std::endl; - return devices; + if (!_streaming) { + return 0; + } + + return noutput_items - _bufferSpaceRemaining; } -size_t sdrplay_source_c::get_num_channels() +// Called by sdrplay streamer thread when data is available +void sdrplay_source_c::streamCallback(short *xi, short *xq, + unsigned int firstSampleNum, + int grChanged, int rfChanged, int fsChanged, + unsigned int numSamples, unsigned int reset, + void *cbContext) +{ + unsigned int i = 0; + _reinit = false; + + boost::mutex::scoped_lock lock(_bufferMutex); + + while (i < numSamples) { + + // Discard samples if not streaming, if flowgraph not running, or reinit needed. + if (!_streaming || _reinit || !_flowgraphRunning) + return; + + // If buffer is not ready for write, wait a short time. Discard samples on timeout. + if (!_buffer) + if (boost::cv_status::timeout == + _bufferReady.wait_for(lock, boost::chrono::milliseconds(250))) + return; + + // Copy until out of samples or buffer is full + while ((i < numSamples) && (_bufferSpaceRemaining > 0)) { + _buffer[_bufferOffset] = + gr_complex(float(xi[i]) / 32768.0, float(xq[i]) / 32768.0); + + i++; + _bufferOffset++; + _bufferSpaceRemaining--; + } + + if (_bufferSpaceRemaining == 0) { + _buffer = NULL; + _bufferReady.notify_one(); + } + } +} + +// Callback wrapper +void sdrplay_source_c::streamCallbackWrap(short *xi, short *xq, + unsigned int firstSampleNum, + int grChanged, int rfChanged, int fsChanged, + unsigned int numSamples, unsigned int reset, + void *cbContext) { - std::cerr << "get_num_channels: 1" << std::endl; - return 1; + sdrplay_source_c *obj = (sdrplay_source_c *)cbContext; + obj->streamCallback(xi, xq, + firstSampleNum, + grChanged, rfChanged, fsChanged, + numSamples, reset, + cbContext); } -osmosdr::meta_range_t sdrplay_source_c::get_sample_rates() +// Called by strplay streamer thread when gain reduction is changed. +void sdrplay_source_c::gainChangeCallback(unsigned int gRdB, + unsigned int lnaGRdB, + void *cbContext) +{ + std::cerr << "GR change, BB+MIX -" << gRdB << "dB, LNA -" << lnaGRdB + << "dB, band " << _band << std::endl; +} + +// Callback wrapper +void sdrplay_source_c::gainChangeCallbackWrap(unsigned int gRdB, + unsigned int lnaGRdB, + void *cbContext) { - osmosdr::meta_range_t range; + sdrplay_source_c *obj = (sdrplay_source_c *)cbContext; + obj->gainChangeCallback(gRdB, + lnaGRdB, + cbContext); +} + +void sdrplay_source_c::startStreaming(void) +{ + if (_streaming) { + std::cerr << "startStreaming(): already streaming." << std::endl; + return; + } + + unsigned int numDevices; + mir_sdr_DeviceT mirDevices[MAX_SUPPORTED_DEVICES]; + mir_sdr_ReleaseDeviceIdx(); + mir_sdr_GetDevices(mirDevices, &numDevices, MAX_SUPPORTED_DEVICES); + mir_sdr_SetDeviceIdx(_devIndex); + + std::cerr << "Using SDRplay " << hwName(_hwVer) << " " + << mirDevices[_devIndex].SerNo << std::endl; + + // Set bias voltage on/off (RSP1A/RSP2). + std::cerr << "Bias voltage: " << _biasT << std::endl; + if (_hwVer == 2) + mir_sdr_RSPII_BiasTControl(_biasT); + else if (_hwVer == 255) + mir_sdr_rsp1a_BiasT(_biasT); + + _streaming = true; - range += osmosdr::range_t( 2000e3, 12000e3 ); + updateGains(); - return range; + int gRdB = _gRdB; + int gRdBsystem = 0; + + mir_sdr_StreamInit(&gRdB, + _fsHz / 1e6, + _rfHz / 1e6, + _bwType, + _ifType, + checkLNA(_lna), + &gRdBsystem, + mir_sdr_USE_RSP_SET_GR, + &_samplesPerPacket, + &streamCallbackWrap, + &gainChangeCallbackWrap, + this); + + // Set decimation with halfband filter + mir_sdr_DecimateControl(_decim != 1, _decim, 1); + + // Note that gqrx never calls set_dc_offset_mode() if the IQ balance + // module is available. + set_dc_offset_mode(osmosdr::source::DCOffsetOff, 0); + + // Model-specific initialization + if (_hwVer == 2) { + set_antenna(get_antenna(), 0); + mir_sdr_RSPII_RfNotchEnable(_bcastNotch); + } + + else if (_hwVer == 255) { + mir_sdr_rsp1a_BroadcastNotch(_bcastNotch); + mir_sdr_rsp1a_DabNotch(_dabNotch); + } } -double sdrplay_source_c::set_sample_rate(double rate) +void sdrplay_source_c::stopStreaming(void) { - std::cerr << "set_sample_rate start" << std::endl; - double diff = rate - _dev->fsHz; - _dev->fsHz = rate; - - std::cerr << "rate = " << rate << std::endl; - std::cerr << "diff = " << diff << std::endl; - if (_running) - { - if (fabs(diff) < 10000.0) - { - std::cerr << "mir_sdr_SetFs started" << std::endl; - mir_sdr_SetFs(diff, 0, 0, 0); - } - else - { - std::cerr << "reinit_device started" << std::endl; - reinit_device(); - } - } - std::cerr << "set_sample_rate end" << std::endl; + if (!_streaming) { + std::cerr << "stopStreaming(): already stopped." << std::endl; + return; + } + + _streaming = false; - return get_sample_rate(); + mir_sdr_StreamUninit(); + mir_sdr_ReleaseDeviceIdx(); } -double sdrplay_source_c::get_sample_rate() +void sdrplay_source_c::reinitDevice(int reason) { - if (_running) - { - return _dev->fsHz; - } + // If no reason given, reinit everything + if (reason == (int)mir_sdr_CHANGE_NONE) + reason = (mir_sdr_CHANGE_GR | + mir_sdr_CHANGE_FS_FREQ | + mir_sdr_CHANGE_RF_FREQ | + mir_sdr_CHANGE_BW_TYPE | + mir_sdr_CHANGE_IF_TYPE | + mir_sdr_CHANGE_LO_MODE | + mir_sdr_CHANGE_AM_PORT); + + int gRdB; + int gRdBsystem; // Returned overall system gain reduction + + updateGains(); + gRdB = _gRdB; + + // Tell stream CB to return + _reinit = true; + + mir_sdr_Reinit(&gRdB, + _fsHz / 1e6, + _rfHz / 1e6, + _bwType, + _ifType, + mir_sdr_LO_Auto, + checkLNA(_lna), + &gRdBsystem, + mir_sdr_USE_RSP_SET_GR, + &_samplesPerPacket, + (mir_sdr_ReasonForReinitT)reason + ); -// return 0; - return _dev->fsHz; + // Set decimation with halfband filter + mir_sdr_DecimateControl(_decim != 1, _decim, 1); + + _bufferReady.notify_one(); } -osmosdr::freq_range_t sdrplay_source_c::get_freq_range( size_t chan ) +std::vector sdrplay_source_c::get_devices() { - osmosdr::freq_range_t range; + unsigned int numDevices; + mir_sdr_DeviceT mirDevices[MAX_SUPPORTED_DEVICES]; + std::vector devices; + + mir_sdr_GetDevices(mirDevices, &numDevices, MAX_SUPPORTED_DEVICES); - range += osmosdr::range_t( SDRPLAY_AM_MIN, SDRPLAY_AM_MAX ); /* LW/MW/SW (150 kHz - 30 MHz) */ - range += osmosdr::range_t( SDRPLAY_FM_MIN, SDRPLAY_FM_MAX ); /* VHF Band II (64 - 108 MHz) */ - range += osmosdr::range_t( SDRPLAY_B3_MIN, SDRPLAY_B3_MAX ); /* Band III (162 - 240 MHz) */ - range += osmosdr::range_t( SDRPLAY_B45_MIN, SDRPLAY_B45_MAX ); /* Band IV/V (470 - 960 MHz) */ - range += osmosdr::range_t( SDRPLAY_L_MIN, SDRPLAY_L_MAX ); /* L-Band (1450 - 1675 MHz) */ + for (unsigned int i=0; idevAvail) + continue; + std::string args = boost::str(boost::format("sdrplay=%d,label='SDRplay %s %s'") + % i % hwName((int)dev->hwVer) % dev->SerNo ); + std::cerr << args << std::endl; + devices.push_back( args ); + } - return range; + return devices; } -double sdrplay_source_c::set_center_freq( double freq, size_t chan ) +size_t sdrplay_source_c::get_num_channels() { - std::cerr << "set_center_freq start" << std::endl; - std::cerr << "freq = " << freq << std::endl; - double diff = freq - _dev->rfHz; - std::cerr << "diff = " << diff << std::endl; - _dev->rfHz = freq; - set_gain_limits(freq); - if (_running) - { - if (fabs(diff) < 10000.0) - { - std::cerr << "mir_sdr_SetRf started" << std::endl; - mir_sdr_SetRf(diff, 0, 0); - } - else - { - std::cerr << "reinit_device started" << std::endl; - reinit_device(); - } - } + return 1; +} - std::cerr << "set_center_freq end" << std::endl; - return get_center_freq( chan ); +osmosdr::meta_range_t sdrplay_source_c::get_sample_rates() +{ + osmosdr::meta_range_t range; + range += osmosdr::range_t( 62.5e3, 10e6 ); + return range; } -double sdrplay_source_c::get_center_freq( size_t chan ) +double sdrplay_source_c::set_sample_rate(double rate) { - if (_running) - { - return _dev->rfHz; - } + rate = std::min( std::max(rate,62.5e3), 10e6 ); + _fsHz = rate; -// return 0; - return _dev->rfHz; + // Decimation is required for rates below 2MS/s + _decim = 1; + while (_fsHz < 2e6) { + _decim *= 2; + _fsHz *= 2; + } + + if (_streaming) + reinitDevice((int)mir_sdr_CHANGE_FS_FREQ); + + return get_sample_rate(); } -double sdrplay_source_c::set_freq_corr( double ppm, size_t chan ) +double sdrplay_source_c::get_sample_rate() { - return get_freq_corr( chan ); + return _fsHz/_decim; } -double sdrplay_source_c::get_freq_corr( size_t chan ) +osmosdr::freq_range_t sdrplay_source_c::get_freq_range(size_t chan) { - return 0; + osmosdr::freq_range_t range; + range += osmosdr::range_t(SDRPLAY_FREQ_MIN, SDRPLAY_FREQ_MAX); + return range; } -std::vector sdrplay_source_c::get_gain_names( size_t chan ) +double sdrplay_source_c::set_center_freq(double freq, size_t chan) { - std::vector< std::string > gains; + _rfHz = freq; - gains += "LNA_MIX_BB"; + if (_streaming) { + reinitDevice((int)mir_sdr_CHANGE_RF_FREQ); + } - return gains; + return get_center_freq( chan ); } -osmosdr::gain_range_t sdrplay_source_c::get_gain_range( size_t chan ) +double sdrplay_source_c::get_center_freq(size_t chan) { - osmosdr::gain_range_t range; - - for (int i = _dev->minGain; i < _dev->maxGain; i++) - { - range += osmosdr::range_t( (float)i ); - } - - return range; + return _rfHz; } -osmosdr::gain_range_t sdrplay_source_c::get_gain_range( const std::string & name, size_t chan ) +double sdrplay_source_c::set_freq_corr(double ppm, size_t chan) { - return get_gain_range( chan ); + return get_freq_corr( chan ); } -bool sdrplay_source_c::set_gain_mode( bool automatic, size_t chan ) +double sdrplay_source_c::get_freq_corr(size_t chan) { - std::cerr << "set_gain_mode started" << std::endl; - _auto_gain = automatic; - std::cerr << "automatic = " << automatic << std::endl; - if (automatic) - { - /* Start AGC */ - std::cerr << "AGC not yet implemented" << std::endl; - } - - std::cerr << "set_gain_mode end" << std::endl; - return get_gain_mode(chan); + return 0; } -bool sdrplay_source_c::get_gain_mode( size_t chan ) +std::vector sdrplay_source_c::get_gain_names(size_t chan) { - return _auto_gain; + std::vector gains; + + gains += "LNA_ATTEN_STEP"; + gains += "SYS_ATTEN_DB"; + + // RSP1A and RSP2 have broadcast notch filters, and RSP1A has a DAB + // notch filter. Show all controls for all models, mainly because + // gqrx gets confused when switching between sources with different + // sets of gains. + gains += "BCAST_NOTCH"; + gains += "DAB_NOTCH"; + + return gains; } -double sdrplay_source_c::set_gain( double gain, size_t chan ) +osmosdr::gain_range_t sdrplay_source_c::get_gain_range(size_t chan) { - std::cerr << "set_gain started" << std::endl; - _dev->gain_dB = gain; - std::cerr << "gain = " << gain << std::endl; - if (gain < _dev->minGain) - { - _dev->gain_dB = _dev->minGain; - } - if (gain > _dev->maxGain) - { - _dev->gain_dB = _dev->maxGain; - } - _dev->gRdB = (int)(_dev->maxGain - gain); + osmosdr::gain_range_t range; - if (_running) - { - std::cerr << "mir_sdr_SetGr started" << std::endl; - mir_sdr_SetGr(_dev->gRdB, 1, 0); - } + for (int i = 20; i <= 59; i++) + range += osmosdr::range_t((float)i); -std::cerr << "set_gain end" << std::endl; -return get_gain( chan ); + return range; } -double sdrplay_source_c::set_gain( double gain, const std::string & name, size_t chan) +osmosdr::gain_range_t sdrplay_source_c::get_gain_range(const std::string & name, size_t chan) { - return set_gain( gain, chan ); + osmosdr::gain_range_t range; + int maxLnaState; + + if (name == "LNA_ATTEN_STEP") { + if (_hwVer == 2) + maxLnaState = 8; + else if (_hwVer == 255) + maxLnaState = 9; + else + maxLnaState = 3; + for (int i = 0; i <= maxLnaState; i++) + range += osmosdr::range_t((float)i); + } + // RSP1A, RSP2 + else if (name == "BCAST_NOTCH") { + range += osmosdr::range_t((float)0); + if (_hwVer == 2 || _hwVer == 255) + range += osmosdr::range_t((float)1); + } + // RSP1A + else if (name == "DAB_NOTCH") { + range += osmosdr::range_t((float)0); + if (_hwVer == 255) + range += osmosdr::range_t((float)1); + } + else { + for (int i = 20; i <= 59; i++) + range += osmosdr::range_t((float)i); + } + + return range; } -double sdrplay_source_c::get_gain( size_t chan ) +bool sdrplay_source_c::set_gain_mode(bool automatic, size_t chan) { - if ( _running ) - { - return _dev->gain_dB; - } + _auto_gain = automatic; + if (_streaming) { + if (automatic) { + mir_sdr_AgcControl(mir_sdr_AGC_5HZ, -30, 0, 0, 0, 0, 0); + } + else { + mir_sdr_AgcControl(mir_sdr_AGC_DISABLE, 0, 0, 0, 0, 0, 0); + set_gain(get_gain(0)); + } + } -// return 0; - return _dev->gain_dB; + return get_gain_mode(chan); } -double sdrplay_source_c::get_gain( const std::string & name, size_t chan ) +bool sdrplay_source_c::get_gain_mode(size_t chan) { - return get_gain( chan ); + return _auto_gain; } -std::vector< std::string > sdrplay_source_c::get_antennas( size_t chan ) +int sdrplay_source_c::checkLNA(int lna) { - std::vector< std::string > antennas; + // Clip LNA reduction step. See table in API section 5.3. + if (_hwVer == 1) { + lna = std::min(3, lna); + } + else if (_hwVer == 255) { + if (_rfHz < 60000000) + lna = std::min(6, lna); + else if (_rfHz >= 1000000000) + lna = std::min(8, lna); + else + lna = std::min(9, lna); + } + else if (_hwVer == 2) { + if (_rfHz >= 420000000) + lna = std::min(5, lna); + else if (_rfHz < 60000000 && _antenna == "HIGHZ") + lna = std::min(4, lna); + else + lna = std::min(8, lna); + } + + return lna; +} + +void sdrplay_source_c::updateGains(void) +{ + int gRdBsystem = 0; + _gRdB = _gain; + int gRdB = _gRdB; + int lna = checkLNA(_lna); + + mir_sdr_GetGrByFreq(_rfHz/1e6, (mir_sdr_BandT *)&_band, &gRdB, lna, &gRdBsystem, + mir_sdr_USE_RSP_SET_GR); + if (_streaming) + mir_sdr_RSP_SetGr(gRdB, lna, 1 /*absolute*/, 0 /*immediate*/); +} + +double sdrplay_source_c::set_gain(double gain, size_t chan) +{ + _gain = (int)gain; + + if (_streaming) + updateGains(); + + return gain; +} + +double sdrplay_source_c::set_gain(double gain, const std::string & name, size_t chan) +{ + int bcastNotchChanged = 0; + int dabNotchChanged = 0; + + if (name == "LNA_ATTEN_STEP") { + _lna = int(gain); + } + // RSP1A, RSP2 + else if (name == "BCAST_NOTCH" && (_hwVer == 2 || _hwVer == 255)) { + if (int(gain) != _bcastNotch) + bcastNotchChanged = 1; + _bcastNotch = int(gain); + } + // RSP1A + else if (name == "DAB_NOTCH" && _hwVer == 255) { + if (int(gain) != _dabNotch) + dabNotchChanged = 1; + _dabNotch = int(gain); + } + else { + _gain = int(gain); + } + + if (_streaming) { + updateGains(); + + if (bcastNotchChanged) { + if (_hwVer == 255 ) { + mir_sdr_rsp1a_BroadcastNotch(_bcastNotch); + } + else if (_hwVer == 2) { + mir_sdr_RSPII_RfNotchEnable(_bcastNotch); + } + } - antennas += get_antenna( chan ); + if (dabNotchChanged) { + mir_sdr_rsp1a_DabNotch(_dabNotch); + } + } - return antennas; + return gain; } -std::string sdrplay_source_c::set_antenna( const std::string & antenna, size_t chan ) +double sdrplay_source_c::get_gain(size_t chan) { - return get_antenna( chan ); + return _gain; } -std::string sdrplay_source_c::get_antenna( size_t chan ) +double sdrplay_source_c::get_gain(const std::string & name, size_t chan) { - return "RX"; + if (name == "LNA_ATTEN_STEP") + return _lna; + else if (name == "BCAST_NOTCH") + return _bcastNotch; + else if (name == "DAB_NOTCH") + return _dabNotch; + else + return _gain; } -void sdrplay_source_c::set_dc_offset_mode( int mode, size_t chan ) +std::vector sdrplay_source_c::get_antennas(size_t chan) { - if ( osmosdr::source::DCOffsetOff == mode ) - { - _dev->dcMode = 0; - if (_running) - { - mir_sdr_SetDcMode(4, 1); - } - } - else if ( osmosdr::source::DCOffsetManual == mode ) - { - std::cerr << "Manual DC correction mode is not implemented." << std::endl; - _dev->dcMode = 0; - if (_running) - { - mir_sdr_SetDcMode(4, 1); + std::vector antennas; + + if (_hwVer == 2) { + antennas += "A"; + antennas += "B"; + antennas += "HIGHZ"; + } + else { + antennas += "RX"; + } + + return antennas; +} + +std::string sdrplay_source_c::set_antenna(const std::string & antenna, size_t chan) +{ + _antenna = antenna; + + if (_streaming) { + if (_hwVer == 2) { + // HIGHZ is ANTENNA_B with AmPortSelect + if (antenna == "HIGHZ") { + mir_sdr_RSPII_AntennaControl(mir_sdr_RSPII_ANTENNA_B); + mir_sdr_AmPortSelect(1); } - } - else if ( osmosdr::source::DCOffsetAutomatic == mode ) - { - _dev->dcMode = 1; - if (_running) - { - mir_sdr_SetDcMode(4, 1); + else { + if (antenna == "A") + mir_sdr_RSPII_AntennaControl(mir_sdr_RSPII_ANTENNA_A); + else + mir_sdr_RSPII_AntennaControl(mir_sdr_RSPII_ANTENNA_B); + mir_sdr_AmPortSelect(0); } - } + + reinitDevice((int)mir_sdr_CHANGE_AM_PORT); + } + } + + return antenna; +} + +std::string sdrplay_source_c::get_antenna(size_t chan) +{ + return _antenna.c_str(); } -void sdrplay_source_c::set_dc_offset( const std::complex &offset, size_t chan ) +// NOTE: DC offset controlled here, IQ balance always on. +void sdrplay_source_c::set_dc_offset_mode(int mode, size_t chan) { - std::cerr << "Manual DC correction mode is not implemented." << std::endl; + if (osmosdr::source::DCOffsetOff == mode) { + _dcMode = 0; + if (_streaming) { + mir_sdr_SetDcMode(0, 0); + mir_sdr_DCoffsetIQimbalanceControl(0, 0); + } + } + else if (osmosdr::source::DCOffsetManual == mode) { + _dcMode = 0; + if (_streaming) { + mir_sdr_SetDcMode(0, 1); + mir_sdr_DCoffsetIQimbalanceControl(0, 1); + } + } + else if (osmosdr::source::DCOffsetAutomatic == mode) { + _dcMode = 1; + if (_streaming) { + mir_sdr_SetDcMode(4, 1); + mir_sdr_DCoffsetIQimbalanceControl(1, 1); + mir_sdr_SetDcTrackTime(63); + } + } +} + +void sdrplay_source_c::set_dc_offset(const std::complex &offset, size_t chan) +{ + std::cerr << "set_dc_offset(): not implemented" << std::endl; } -double sdrplay_source_c::set_bandwidth( double bandwidth, size_t chan ) +double sdrplay_source_c::set_bandwidth(double bandwidth, size_t chan) { - if (bandwidth <= 200e3) _dev->bwType = mir_sdr_BW_0_200; - else if (bandwidth <= 300e3) _dev->bwType = mir_sdr_BW_0_300; - else if (bandwidth <= 600e3) _dev->bwType = mir_sdr_BW_0_600; - else if (bandwidth <= 1536e3) _dev->bwType = mir_sdr_BW_1_536; - else if (bandwidth <= 5000e3) _dev->bwType = mir_sdr_BW_5_000; - else if (bandwidth <= 6000e3) _dev->bwType = mir_sdr_BW_6_000; - else if (bandwidth <= 7000e3) _dev->bwType = mir_sdr_BW_7_000; - else _dev->bwType = mir_sdr_BW_8_000; + _bwType = mir_sdr_BW_8_000; + + for (double bw : bandwidths) { + // Skip dummy value at index 0 + if (bw == 0) + continue; + if (bandwidth <= bw) { + _bwType = (mir_sdr_Bw_MHzT)(bw/1e3); + break; + } + } + + int actual = get_bandwidth(chan); + std::cerr << "SDRplay bandwidth requested=" << bandwidth + << " actual=" << actual << std::endl; - if (_running) - { - reinit_device(); - } + if (_streaming) { + reinitDevice((int)mir_sdr_CHANGE_BW_TYPE); + } - return get_bandwidth( chan ); + return actual; } -double sdrplay_source_c::get_bandwidth( size_t chan ) +double sdrplay_source_c::get_bandwidth(size_t chan) { - double tmpbw=0.0f; - if (_dev->bwType == mir_sdr_BW_0_200) tmpbw = 200e3; - else if (_dev->bwType == mir_sdr_BW_0_300) tmpbw = 300e3; - else if (_dev->bwType == mir_sdr_BW_0_600) tmpbw = 600e3; - else if (_dev->bwType == mir_sdr_BW_1_536) tmpbw = 1536e3; - else if (_dev->bwType == mir_sdr_BW_5_000) tmpbw = 5000e3; - else if (_dev->bwType == mir_sdr_BW_6_000) tmpbw = 6000e3; - else if (_dev->bwType == mir_sdr_BW_7_000) tmpbw = 7000e3; - else tmpbw = 8000e3; - - return (double)tmpbw; + return (double)_bwType * 1e3; } -osmosdr::freq_range_t sdrplay_source_c::get_bandwidth_range( size_t chan ) +osmosdr::freq_range_t sdrplay_source_c::get_bandwidth_range(size_t chan) { - osmosdr::freq_range_t range; + osmosdr::freq_range_t range; - range += osmosdr::range_t( 200e3 ); - range += osmosdr::range_t( 300e3 ); - range += osmosdr::range_t( 600e3 ); - range += osmosdr::range_t( 1536e3 ); - range += osmosdr::range_t( 5000e3 ); - range += osmosdr::range_t( 6000e3 ); - range += osmosdr::range_t( 7000e3 ); - range += osmosdr::range_t( 8000e3 ); + // bandwidths[0] is a dummy + for (unsigned int i=1; i * Copyright 2015 SDRplay Ltd * Copyright 2012 Dimitri Stolnikov * @@ -22,17 +23,21 @@ #define INCLUDED_SDRPLAY_SOURCE_C_H #include - #include -#include -#include #include "osmosdr/ranges.h" #include "source_iface.h" +#include + class sdrplay_source_c; -typedef struct sdrplay_dev sdrplay_dev_t; + +template +struct Range { + T min; + T max; +}; /* * We use boost::shared_ptr's instead of raw pointers for all access @@ -82,6 +87,9 @@ public: gr_vector_const_void_star &input_items, gr_vector_void_star &output_items ); + bool start( void ); + bool stop( void ); + static std::vector< std::string > get_devices(); size_t get_num_channels( void ); @@ -118,19 +126,53 @@ public: osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 ); private: - void reinit_device(void); - void set_gain_limits(double freq); - - sdrplay_dev_t *_dev; - - std::vector< short > _bufi; - std::vector< short > _bufq; - int _buf_offset; - boost::mutex _buf_mutex; - - bool _running; - bool _uninit; - bool _auto_gain; + void startStreaming(void); + void stopStreaming(void); + void reallocateBuffers(int size, int num); + void reinitDevice(int reason); + int checkLNA(int lna); + void updateGains(void); + void streamCallback(short *xi, short *xq, unsigned int firstSampleNum, + int grChanged, int rfChanged, int fsChanged, + unsigned int numSamples, unsigned int reset, + void *cbContext); + void gainChangeCallback(unsigned int gRdB, unsigned int lnaGRdB, void *cbContext); + + static void streamCallbackWrap(short *xi, short *xq, unsigned int firstSampleNum, + int grChanged, int rfChanged, int fsChanged, + unsigned int numSamples, unsigned int reset, + void *cbContext); + static void gainChangeCallbackWrap(unsigned int gRdB, unsigned int lnaGRdB, void *cbContext); + + bool _auto_gain; + + int _gain; + int _gRdB; + int _lna; + int _bcastNotch; + int _dabNotch; + int _band; + double _fsHz; + int _decim; + double _rfHz; + mir_sdr_Bw_MHzT _bwType; + mir_sdr_If_kHzT _ifType; + int _samplesPerPacket; + int _dcMode; + unsigned char _hwVer; + int _devIndex; + std::string _antenna; + int _biasT; + + gr_complex *_buffer; + int _bufferOffset; + int _bufferSpaceRemaining; + boost::mutex _bufferMutex; + boost::condition_variable _bufferReady; // buffer is ready to move to other thread + + bool _streaming; + bool _flowgraphRunning; + bool _reinit; // signal streamer to return after a reinit }; #endif /* INCLUDED_SDRPLAY_SOURCE_C_H */ From willcode4 at gmail.com Sun Jan 14 13:51:44 2018 From: willcode4 at gmail.com (Jeff Long) Date: Sun, 14 Jan 2018 08:51:44 -0500 Subject: [gr-osmosdr] Patch: Update SDRplay Support In-Reply-To: References: Message-ID: Latest version of diff. On Fri, Jan 12, 2018 at 12:48 PM, Jeff Long wrote: > SDRplay support, rewritten, supporting RSP1, RSP1A, and RSP2 > > Requires ENABLE_NONFREE=yes. Requires SDRplay's SDK v2.11 to be installed > by the user. In the future, if there is a way to redistribute > mirsdrapi-rsp.h from the SDK, that problem could be addressed using a shim > library that looks for /usr/local/lib/libmirsdrapi-rsp.so at runtime. > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- diff --git a/lib/sdrplay/sdrplay_source_c.cc b/lib/sdrplay/sdrplay_source_c.cc index 9a9ee40..de00a7c 100644 --- a/lib/sdrplay/sdrplay_source_c.cc +++ b/lib/sdrplay/sdrplay_source_c.cc @@ -1,15 +1,13 @@ /* -*- c++ -*- */ /* - * Copyright 2015 SDRplay Ltd - * Copyright 2012 Dimitri Stolnikov - * Copyright 2012 Steve Markgraf + * Copyright 2018 Jeff Long * * 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, + * 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. @@ -20,11 +18,6 @@ * 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 @@ -32,569 +25,748 @@ #include "sdrplay_source_c.h" #include #include "osmosdr/source.h" +#include "arg_helpers.h" #include #include +#include +#include +#include -#include #include -#include -#include - -#include - -#include "arg_helpers.h" +#include #define MAX_SUPPORTED_DEVICES 4 -struct sdrplay_dev -{ - int gRdB; - double gain_dB; - double fsHz; - double rfHz; - mir_sdr_Bw_MHzT bwType; - mir_sdr_If_kHzT ifType; - int samplesPerPacket; - int maxGain; - int minGain; - int dcMode; -}; - using namespace boost::assign; -#define BYTES_PER_SAMPLE 4 // sdrplay device delivers 16 bit signed IQ data - // containing 12 bits of information +// Index by mir_sdr_Bw_MHzT +static std::vector bandwidths = { + 0, // Dummy + 200e3, + 300e3, + 600e3, + 1536e3, + 5000e3, + 6000e3, + 7000e3, + 8000e3 +}; -#define SDRPLAY_AM_MIN 150e3 -#define SDRPLAY_AM_MAX 30e6 -#define SDRPLAY_FM_MIN 64e6 -#define SDRPLAY_FM_MAX 108e6 -#define SDRPLAY_B3_MIN 162e6 -#define SDRPLAY_B3_MAX 240e6 -#define SDRPLAY_B45_MIN 470e6 -#define SDRPLAY_B45_MAX 960e6 -#define SDRPLAY_L_MIN 1450e6 -#define SDRPLAY_L_MAX 1675e6 +// TODO - RSP1 lower freq is 10e3. +#define SDRPLAY_FREQ_MIN 1e3 +#define SDRPLAY_FREQ_MAX 2000e6 -#define SDRPLAY_MAX_BUF_SIZE 504 +static std::string hwName(int hwVer) +{ + if (hwVer == 1) + return "RSP1"; + if (hwVer == 2) + return "RSP2"; + if (hwVer ==255) + return "RSP1A"; + return "UNK"; +} -/* - * Create a new instance of sdrplay_source_c and return - * a boost shared_ptr. This is effectively the public constructor. - */ sdrplay_source_c_sptr make_sdrplay_source_c (const std::string &args) { return gnuradio::get_initial_sptr(new sdrplay_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 +// 0 inputs, 1 output +static const int MIN_IN = 0; +static const int MAX_IN = 0; +static const int MIN_OUT = 1; +static const int MAX_OUT = 1; -/* - * The private constructor - */ sdrplay_source_c::sdrplay_source_c (const std::string &args) : gr::sync_block ("sdrplay_source_c", - gr::io_signature::make(MIN_IN, MAX_IN, sizeof (gr_complex)), - gr::io_signature::make(MIN_OUT, MAX_OUT, sizeof (gr_complex))), - _running(false), - _uninit(false), - _auto_gain(false) -{ - _dev = (sdrplay_dev_t *)malloc(sizeof(sdrplay_dev_t)); - if (_dev == NULL) - { - return; + gr::io_signature::make(MIN_IN, MAX_IN, sizeof (gr_complex)), + gr::io_signature::make(MIN_OUT, MAX_OUT, sizeof (gr_complex))), + _auto_gain(false), + _gRdB(40), + _lna(0), + _bcastNotch(0), + _dabNotch(0), + _band(0), + _fsHz(8e6), + _decim(1), + _rfHz(100e6), + _bwType(mir_sdr_BW_6_000), + _ifType(mir_sdr_IF_Zero), + _dcMode(1), + _buffer(NULL), + _streaming(false), + _flowgraphRunning(false), + _reinit(false) +{ + dict_t dict = params_to_dict(args); + if (dict.count("sdrplay")) { + _devIndex = boost::lexical_cast(dict["sdrplay"]); + } + else { + _devIndex = 0; + } + + mir_sdr_DebugEnable(1); + + unsigned int numDevices; + mir_sdr_DeviceT mirDevices[MAX_SUPPORTED_DEVICES]; + mir_sdr_GetDevices(mirDevices, &numDevices, MAX_SUPPORTED_DEVICES); + _hwVer = mirDevices[_devIndex].hwVer; + + if (_hwVer == 2) { + _antenna = "A"; + } + else { + _antenna = "RX"; + } + + _biasT = 0; + if ( dict.count("bias") ) { + _biasT = boost::lexical_cast( dict["bias"] ); + } } - _dev->fsHz = 2048e3; - _dev->rfHz = 200e6; - _dev->bwType = mir_sdr_BW_1_536; - _dev->ifType = mir_sdr_IF_Zero; - _dev->samplesPerPacket = 0; - _dev->dcMode = 0; - _dev->gRdB = 60; - set_gain_limits(_dev->rfHz); - _dev->gain_dB = _dev->maxGain - _dev->gRdB; - - _bufi.reserve(SDRPLAY_MAX_BUF_SIZE); - _bufq.reserve(SDRPLAY_MAX_BUF_SIZE); - _buf_mutex.lock(); - _buf_offset = 0; - _buf_mutex.unlock(); +sdrplay_source_c::~sdrplay_source_c () +{ + if (_streaming) { + stopStreaming(); + } } -/* - * Our virtual destructor. - */ -sdrplay_source_c::~sdrplay_source_c () +bool sdrplay_source_c::start(void) { - free(_dev); - _dev = NULL; - _buf_mutex.lock(); - if (_running) - { - _running = false; - } - _uninit = true; - _buf_mutex.unlock(); -} - -void sdrplay_source_c::reinit_device() -{ - std::cerr << "reinit_device started" << std::endl; - _buf_mutex.lock(); - std::cerr << "after mutex.lock" << std::endl; - if (_running) - { - std::cerr << "mir_sdr_Uninit started" << std::endl; - mir_sdr_Uninit(); - } - - std::cerr << "mir_sdr_Init started" << std::endl; - mir_sdr_Init(_dev->gRdB, _dev->fsHz / 1e6, _dev->rfHz / 1e6, _dev->bwType, _dev->ifType, &_dev->samplesPerPacket); - - if (_dev->dcMode) - { - std::cerr << "mir_sdr_SetDcMode started" << std::endl; - mir_sdr_SetDcMode(4, 1); - } - - _buf_offset = 0; - _buf_mutex.unlock(); - std::cerr << "reinit_device end" << std::endl; -} - -void sdrplay_source_c::set_gain_limits(double freq) -{ - if (freq <= SDRPLAY_AM_MAX) - { - _dev->minGain = -4; - _dev->maxGain = 98; - } - else if (freq <= SDRPLAY_FM_MAX) - { - _dev->minGain = 1; - _dev->maxGain = 103; - } - else if (freq <= SDRPLAY_B3_MAX) - { - _dev->minGain = 5; - _dev->maxGain = 107; - } - else if (freq <= SDRPLAY_B45_MAX) - { - _dev->minGain = 9; - _dev->maxGain = 94; - } - else if (freq <= SDRPLAY_L_MAX) - { - _dev->minGain = 24; - _dev->maxGain = 105; - } -} - -int sdrplay_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]; - int cnt = noutput_items; - unsigned int sampNum; - int grChanged; - int rfChanged; - int fsChanged; - - if (_uninit) - { - return WORK_DONE; - } - - if (!_running) - { - reinit_device(); - _running = true; - } - - _buf_mutex.lock(); - - if (_buf_offset) - { - for (int i = _buf_offset; i < _dev->samplesPerPacket; i++) - { - *out++ = gr_complex( float(_bufi[i]) * (1.0f/2048.0f), float(_bufq[i]) * (1.0f/2048.0f) ); - } - cnt -= (_dev->samplesPerPacket - _buf_offset); - } - - while ((cnt - _dev->samplesPerPacket) >= 0) - { - mir_sdr_ReadPacket(_bufi.data(), _bufq.data(), &sampNum, &grChanged, &rfChanged, &fsChanged); - for (int i = 0; i < _dev->samplesPerPacket; i++) - { - *out++ = gr_complex( float(_bufi[i]) * (1.0f/2048.0f), float(_bufq[i]) * (1.0f/2048.0f) ); - } - cnt -= _dev->samplesPerPacket; - } - - _buf_offset = 0; - if (cnt) - { - mir_sdr_ReadPacket(_bufi.data(), _bufq.data(), &sampNum, &grChanged, &rfChanged, &fsChanged); - for (int i = 0; i < cnt; i++) - { - *out++ = gr_complex( float(_bufi[i]) * (1.0f/2048.0f), float(_bufq[i]) * (1.0f/2048.0f) ); - } - _buf_offset = cnt; - } - _buf_mutex.unlock(); + _flowgraphRunning = true; + return true; +} - return noutput_items; +bool sdrplay_source_c::stop(void) +{ + _flowgraphRunning = false; + return true; } -std::vector sdrplay_source_c::get_devices() +int sdrplay_source_c::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) { - std::vector devices; - std::cerr << "get_devices started" << std::endl; + gr_complex *out = (gr_complex *)output_items[0]; + + if (!_streaming) + startStreaming(); - unsigned int dev_cnt = 0; - int samplesPerPacket; - while(mir_sdr_Init(60, 2.048, 200.0, mir_sdr_BW_1_536, mir_sdr_IF_Zero, &samplesPerPacket) == mir_sdr_Success) - { - dev_cnt++; - } + { + boost::mutex::scoped_lock lock(_bufferMutex); + _buffer = out; + _bufferSpaceRemaining = noutput_items; + _bufferOffset = 0; + _bufferReady.notify_one(); - std::cerr << "Device count: " << dev_cnt << std::endl; + while (_buffer && _streaming) { + _bufferReady.wait(lock); + } + } - for (unsigned int i = 0; i < dev_cnt; i++) - { - mir_sdr_Uninit(); - std::string args = "sdrplay=" + boost::lexical_cast< std::string >( i ); - args += ",label='" + std::string("SDRplay RSP") + "'"; - std::cerr << args << std::endl; - devices.push_back( args ); - } + if (!_streaming) { + return 0; + } - std::cerr << "get_devices end" << std::endl; - return devices; + return noutput_items - _bufferSpaceRemaining; } -size_t sdrplay_source_c::get_num_channels() +// Called by sdrplay streamer thread when data is available +void sdrplay_source_c::streamCallback(short *xi, short *xq, + unsigned int firstSampleNum, + int grChanged, int rfChanged, int fsChanged, + unsigned int numSamples, unsigned int reset, + void *cbContext) +{ + unsigned int i = 0; + _reinit = false; + + boost::mutex::scoped_lock lock(_bufferMutex); + + while (i < numSamples) { + + // Discard samples if not streaming, if flowgraph not running, or reinit needed. + if (!_streaming || _reinit || !_flowgraphRunning) + return; + + // If buffer is not ready for write, wait a short time. Discard samples on timeout. + if (!_buffer) + if (boost::cv_status::timeout == + _bufferReady.wait_for(lock, boost::chrono::milliseconds(250))) + return; + + // Copy until out of samples or buffer is full + while ((i < numSamples) && (_bufferSpaceRemaining > 0)) { + _buffer[_bufferOffset] = + gr_complex(float(xi[i]) / 32768.0, float(xq[i]) / 32768.0); + + i++; + _bufferOffset++; + _bufferSpaceRemaining--; + } + + if (_bufferSpaceRemaining == 0) { + _buffer = NULL; + _bufferReady.notify_one(); + } + } +} + +// Callback wrapper +void sdrplay_source_c::streamCallbackWrap(short *xi, short *xq, + unsigned int firstSampleNum, + int grChanged, int rfChanged, int fsChanged, + unsigned int numSamples, unsigned int reset, + void *cbContext) { - std::cerr << "get_num_channels: 1" << std::endl; - return 1; + sdrplay_source_c *obj = (sdrplay_source_c *)cbContext; + obj->streamCallback(xi, xq, + firstSampleNum, + grChanged, rfChanged, fsChanged, + numSamples, reset, + cbContext); } -osmosdr::meta_range_t sdrplay_source_c::get_sample_rates() +// Called by strplay streamer thread when gain reduction is changed. +void sdrplay_source_c::gainChangeCallback(unsigned int gRdB, + unsigned int lnaGRdB, + void *cbContext) +{ + std::cerr << "GR change, BB+MIX -" << gRdB << "dB, LNA -" << lnaGRdB + << "dB, band " << _band << std::endl; +} + +// Callback wrapper +void sdrplay_source_c::gainChangeCallbackWrap(unsigned int gRdB, + unsigned int lnaGRdB, + void *cbContext) { - osmosdr::meta_range_t range; + sdrplay_source_c *obj = (sdrplay_source_c *)cbContext; + obj->gainChangeCallback(gRdB, + lnaGRdB, + cbContext); +} + +void sdrplay_source_c::startStreaming(void) +{ + if (_streaming) { + std::cerr << "startStreaming(): already streaming." << std::endl; + return; + } + + unsigned int numDevices; + mir_sdr_DeviceT mirDevices[MAX_SUPPORTED_DEVICES]; + mir_sdr_ReleaseDeviceIdx(); + mir_sdr_GetDevices(mirDevices, &numDevices, MAX_SUPPORTED_DEVICES); + mir_sdr_SetDeviceIdx(_devIndex); + + std::cerr << "Using SDRplay " << hwName(_hwVer) << " " + << mirDevices[_devIndex].SerNo << std::endl; + + // Set bias voltage on/off (RSP1A/RSP2). + std::cerr << "Bias voltage: " << _biasT << std::endl; + if (_hwVer == 2) + mir_sdr_RSPII_BiasTControl(_biasT); + else if (_hwVer == 255) + mir_sdr_rsp1a_BiasT(_biasT); - range += osmosdr::range_t( 2000e3, 12000e3 ); + _streaming = true; - return range; + updateGains(); + set_gain_mode(get_gain_mode(/*channel*/ 0), /*channel*/ 0); + + int gRdB = _gRdB; + int gRdBsystem = 0; + + mir_sdr_StreamInit(&gRdB, + _fsHz / 1e6, + _rfHz / 1e6, + _bwType, + _ifType, + checkLNA(_lna), + &gRdBsystem, + mir_sdr_USE_RSP_SET_GR, + &_samplesPerPacket, + &streamCallbackWrap, + &gainChangeCallbackWrap, + this); + + // Set decimation with halfband filter + mir_sdr_DecimateControl(_decim != 1, _decim, 1); + + // Note that gqrx never calls set_dc_offset_mode() if the IQ balance + // module is available. + set_dc_offset_mode(osmosdr::source::DCOffsetOff, 0); + + // Model-specific initialization + if (_hwVer == 2) { + set_antenna(get_antenna(), 0); + mir_sdr_RSPII_RfNotchEnable(_bcastNotch); + } + + else if (_hwVer == 255) { + mir_sdr_rsp1a_BroadcastNotch(_bcastNotch); + mir_sdr_rsp1a_DabNotch(_dabNotch); + } } -double sdrplay_source_c::set_sample_rate(double rate) +void sdrplay_source_c::stopStreaming(void) { - std::cerr << "set_sample_rate start" << std::endl; - double diff = rate - _dev->fsHz; - _dev->fsHz = rate; - - std::cerr << "rate = " << rate << std::endl; - std::cerr << "diff = " << diff << std::endl; - if (_running) - { - if (fabs(diff) < 10000.0) - { - std::cerr << "mir_sdr_SetFs started" << std::endl; - mir_sdr_SetFs(diff, 0, 0, 0); - } - else - { - std::cerr << "reinit_device started" << std::endl; - reinit_device(); - } - } - std::cerr << "set_sample_rate end" << std::endl; + if (!_streaming) { + std::cerr << "stopStreaming(): already stopped." << std::endl; + return; + } - return get_sample_rate(); + _streaming = false; + + mir_sdr_StreamUninit(); + mir_sdr_ReleaseDeviceIdx(); } -double sdrplay_source_c::get_sample_rate() +void sdrplay_source_c::reinitDevice(int reason) { - if (_running) - { - return _dev->fsHz; - } + // If no reason given, reinit everything + if (reason == (int)mir_sdr_CHANGE_NONE) + reason = (mir_sdr_CHANGE_GR | + mir_sdr_CHANGE_FS_FREQ | + mir_sdr_CHANGE_RF_FREQ | + mir_sdr_CHANGE_BW_TYPE | + mir_sdr_CHANGE_IF_TYPE | + mir_sdr_CHANGE_LO_MODE | + mir_sdr_CHANGE_AM_PORT); + + int gRdB; + int gRdBsystem; // Returned overall system gain reduction + + updateGains(); + gRdB = _gRdB; + + // Tell stream CB to return + _reinit = true; -// return 0; - return _dev->fsHz; + mir_sdr_Reinit(&gRdB, + _fsHz / 1e6, + _rfHz / 1e6, + _bwType, + _ifType, + mir_sdr_LO_Auto, + checkLNA(_lna), + &gRdBsystem, + mir_sdr_USE_RSP_SET_GR, + &_samplesPerPacket, + (mir_sdr_ReasonForReinitT)reason + ); + + // Set decimation with halfband filter + mir_sdr_DecimateControl(_decim != 1, _decim, 1); + + _bufferReady.notify_one(); } -osmosdr::freq_range_t sdrplay_source_c::get_freq_range( size_t chan ) +std::vector sdrplay_source_c::get_devices() { - osmosdr::freq_range_t range; + unsigned int numDevices; + mir_sdr_DeviceT mirDevices[MAX_SUPPORTED_DEVICES]; + std::vector devices; - range += osmosdr::range_t( SDRPLAY_AM_MIN, SDRPLAY_AM_MAX ); /* LW/MW/SW (150 kHz - 30 MHz) */ - range += osmosdr::range_t( SDRPLAY_FM_MIN, SDRPLAY_FM_MAX ); /* VHF Band II (64 - 108 MHz) */ - range += osmosdr::range_t( SDRPLAY_B3_MIN, SDRPLAY_B3_MAX ); /* Band III (162 - 240 MHz) */ - range += osmosdr::range_t( SDRPLAY_B45_MIN, SDRPLAY_B45_MAX ); /* Band IV/V (470 - 960 MHz) */ - range += osmosdr::range_t( SDRPLAY_L_MIN, SDRPLAY_L_MAX ); /* L-Band (1450 - 1675 MHz) */ + mir_sdr_GetDevices(mirDevices, &numDevices, MAX_SUPPORTED_DEVICES); - return range; + for (unsigned int i=0; idevAvail) + continue; + std::string args = boost::str(boost::format("sdrplay=%d,label='SDRplay %s %s'") + % i % hwName((int)dev->hwVer) % dev->SerNo ); + std::cerr << args << std::endl; + devices.push_back( args ); + } + + return devices; } -double sdrplay_source_c::set_center_freq( double freq, size_t chan ) +size_t sdrplay_source_c::get_num_channels() { - std::cerr << "set_center_freq start" << std::endl; - std::cerr << "freq = " << freq << std::endl; - double diff = freq - _dev->rfHz; - std::cerr << "diff = " << diff << std::endl; - _dev->rfHz = freq; - set_gain_limits(freq); - if (_running) - { - if (fabs(diff) < 10000.0) - { - std::cerr << "mir_sdr_SetRf started" << std::endl; - mir_sdr_SetRf(diff, 0, 0); - } - else - { - std::cerr << "reinit_device started" << std::endl; - reinit_device(); - } - } + return 1; +} - std::cerr << "set_center_freq end" << std::endl; - return get_center_freq( chan ); +osmosdr::meta_range_t sdrplay_source_c::get_sample_rates() +{ + osmosdr::meta_range_t range; + range += osmosdr::range_t( 62.5e3, 10e6 ); + return range; } -double sdrplay_source_c::get_center_freq( size_t chan ) +double sdrplay_source_c::set_sample_rate(double rate) { - if (_running) - { - return _dev->rfHz; - } + rate = std::min( std::max(rate,62.5e3), 10e6 ); + _fsHz = rate; + + // Decimation is required for rates below 2MS/s + _decim = 1; + while (_fsHz < 2e6) { + _decim *= 2; + _fsHz *= 2; + } -// return 0; - return _dev->rfHz; + if (_streaming) + reinitDevice((int)mir_sdr_CHANGE_FS_FREQ); + + return get_sample_rate(); } -double sdrplay_source_c::set_freq_corr( double ppm, size_t chan ) +double sdrplay_source_c::get_sample_rate() { - return get_freq_corr( chan ); + return _fsHz/_decim; } -double sdrplay_source_c::get_freq_corr( size_t chan ) +osmosdr::freq_range_t sdrplay_source_c::get_freq_range(size_t chan) { - return 0; + osmosdr::freq_range_t range; + range += osmosdr::range_t(SDRPLAY_FREQ_MIN, SDRPLAY_FREQ_MAX); + return range; } -std::vector sdrplay_source_c::get_gain_names( size_t chan ) +double sdrplay_source_c::set_center_freq(double freq, size_t chan) { - std::vector< std::string > gains; + _rfHz = freq; - gains += "LNA_MIX_BB"; + if (_streaming) { + reinitDevice((int)mir_sdr_CHANGE_RF_FREQ); + } - return gains; + return get_center_freq( chan ); } -osmosdr::gain_range_t sdrplay_source_c::get_gain_range( size_t chan ) +double sdrplay_source_c::get_center_freq(size_t chan) { - osmosdr::gain_range_t range; - - for (int i = _dev->minGain; i < _dev->maxGain; i++) - { - range += osmosdr::range_t( (float)i ); - } - - return range; + return _rfHz; } -osmosdr::gain_range_t sdrplay_source_c::get_gain_range( const std::string & name, size_t chan ) +double sdrplay_source_c::set_freq_corr(double ppm, size_t chan) { - return get_gain_range( chan ); + return get_freq_corr( chan ); } -bool sdrplay_source_c::set_gain_mode( bool automatic, size_t chan ) +double sdrplay_source_c::get_freq_corr(size_t chan) { - std::cerr << "set_gain_mode started" << std::endl; - _auto_gain = automatic; - std::cerr << "automatic = " << automatic << std::endl; - if (automatic) - { - /* Start AGC */ - std::cerr << "AGC not yet implemented" << std::endl; - } - - std::cerr << "set_gain_mode end" << std::endl; - return get_gain_mode(chan); + return 0; } -bool sdrplay_source_c::get_gain_mode( size_t chan ) +std::vector sdrplay_source_c::get_gain_names(size_t chan) { - return _auto_gain; + std::vector gains; + + gains += "LNA_ATTEN_STEP"; + gains += "IF_ATTEN_DB"; + + // RSP1A and RSP2 have broadcast notch filters, and RSP1A has a DAB + // notch filter. Show all controls for all models, mainly because + // gqrx gets confused when switching between sources with different + // sets of gains. + gains += "BCAST_NOTCH"; + gains += "DAB_NOTCH"; + + return gains; } -double sdrplay_source_c::set_gain( double gain, size_t chan ) +osmosdr::gain_range_t sdrplay_source_c::get_gain_range(size_t chan) { - std::cerr << "set_gain started" << std::endl; - _dev->gain_dB = gain; - std::cerr << "gain = " << gain << std::endl; - if (gain < _dev->minGain) - { - _dev->gain_dB = _dev->minGain; - } - if (gain > _dev->maxGain) - { - _dev->gain_dB = _dev->maxGain; - } - _dev->gRdB = (int)(_dev->maxGain - gain); + osmosdr::gain_range_t range; - if (_running) - { - std::cerr << "mir_sdr_SetGr started" << std::endl; - mir_sdr_SetGr(_dev->gRdB, 1, 0); - } + for (int i = 20; i <= 59; i++) + range += osmosdr::range_t((float)i); -std::cerr << "set_gain end" << std::endl; -return get_gain( chan ); + return range; } -double sdrplay_source_c::set_gain( double gain, const std::string & name, size_t chan) +osmosdr::gain_range_t sdrplay_source_c::get_gain_range(const std::string & name, size_t chan) { - return set_gain( gain, chan ); + osmosdr::gain_range_t range; + int maxLnaState; + + if (name == "LNA_ATTEN_STEP") { + if (_hwVer == 2) + maxLnaState = 8; + else if (_hwVer == 255) + maxLnaState = 9; + else + maxLnaState = 3; + for (int i = 0; i <= maxLnaState; i++) + range += osmosdr::range_t((float)i); + } + // RSP1A, RSP2 + else if (name == "BCAST_NOTCH") { + range += osmosdr::range_t((float)0); + if (_hwVer == 2 || _hwVer == 255) + range += osmosdr::range_t((float)1); + } + // RSP1A + else if (name == "DAB_NOTCH") { + range += osmosdr::range_t((float)0); + if (_hwVer == 255) + range += osmosdr::range_t((float)1); + } + else { + for (int i = 20; i <= 59; i++) + range += osmosdr::range_t((float)i); + } + + return range; } -double sdrplay_source_c::get_gain( size_t chan ) +bool sdrplay_source_c::set_gain_mode(bool automatic, size_t chan) { - if ( _running ) - { - return _dev->gain_dB; - } + _auto_gain = automatic; + if (_streaming) { + if (automatic) { + mir_sdr_AgcControl(mir_sdr_AGC_5HZ, -30, 0, 0, 0, 0, 0); + } + else { + mir_sdr_AgcControl(mir_sdr_AGC_DISABLE, -30, 0, 0, 0, 0, 0); + updateGains(); + } + } -// return 0; - return _dev->gain_dB; + return _auto_gain; } -double sdrplay_source_c::get_gain( const std::string & name, size_t chan ) +bool sdrplay_source_c::get_gain_mode(size_t chan) { - return get_gain( chan ); + return _auto_gain; } -std::vector< std::string > sdrplay_source_c::get_antennas( size_t chan ) +int sdrplay_source_c::checkLNA(int lna) { - std::vector< std::string > antennas; + // Clip LNA reduction step. See table in API section 5.3. + if (_hwVer == 1) { + lna = std::min(3, lna); + } + else if (_hwVer == 255) { + if (_rfHz < 60000000) + lna = std::min(6, lna); + else if (_rfHz >= 1000000000) + lna = std::min(8, lna); + else + lna = std::min(9, lna); + } + else if (_hwVer == 2) { + if (_rfHz >= 420000000) + lna = std::min(5, lna); + else if (_rfHz < 60000000 && _antenna == "HIGHZ") + lna = std::min(4, lna); + else + lna = std::min(8, lna); + } + + return lna; +} + +void sdrplay_source_c::updateGains(void) +{ + int gRdB = _gRdB; + + if (_streaming && !_auto_gain) + mir_sdr_RSP_SetGr(gRdB, checkLNA(_lna), 1 /*absolute*/, 0 /*immediate*/); +} + +double sdrplay_source_c::set_gain(double gain, size_t chan) +{ + set_gain(gain, "IF_ATTEN_DB"); + return get_gain("IF_ATTEN_DB"); +} + +double sdrplay_source_c::set_gain(double gain, const std::string & name, size_t chan) +{ + bool bcastNotchChanged = false; + bool dabNotchChanged = false; + bool gainChanged = false; + + if (name == "LNA_ATTEN_STEP") { + if (gain != _lna) + gainChanged = true; + _lna = int(gain); + } + else if (name == "IF_ATTEN_DB") { + // Ignore out-of-bounds values, since caller knows limits. (GQRX spurious calls). + if (gain >= 20.0 && gain <= 59.0 && gain != _gRdB) { + gainChanged = true; + _gRdB = int(gain); + } + } + // RSP1A, RSP2 + else if (name == "BCAST_NOTCH" && (_hwVer == 2 || _hwVer == 255)) { + if (int(gain) != _bcastNotch) + bcastNotchChanged = true; + _bcastNotch = int(gain); + } + // RSP1A + else if (name == "DAB_NOTCH" && _hwVer == 255) { + if (int(gain) != _dabNotch) + dabNotchChanged = true; + _dabNotch = int(gain); + } + + if (_streaming) { + if (gainChanged) + updateGains(); + + if (bcastNotchChanged) { + if (_hwVer == 255 ) { + mir_sdr_rsp1a_BroadcastNotch(_bcastNotch); + } + else if (_hwVer == 2) { + mir_sdr_RSPII_RfNotchEnable(_bcastNotch); + } + } - antennas += get_antenna( chan ); + if (dabNotchChanged) { + mir_sdr_rsp1a_DabNotch(_dabNotch); + } + } - return antennas; + return get_gain(chan); } -std::string sdrplay_source_c::set_antenna( const std::string & antenna, size_t chan ) +double sdrplay_source_c::get_gain(size_t chan) { - return get_antenna( chan ); + return get_gain("IF_ATTEN_DB"); } -std::string sdrplay_source_c::get_antenna( size_t chan ) +double sdrplay_source_c::get_gain(const std::string & name, size_t chan) { - return "RX"; + if (name == "LNA_ATTEN_STEP") + return _lna; + else if (name == "BCAST_NOTCH") + return _bcastNotch; + else if (name == "DAB_NOTCH") + return _dabNotch; + else if (name == "IF_ATTEN_DB") + return _gRdB; + else + return 0; } -void sdrplay_source_c::set_dc_offset_mode( int mode, size_t chan ) +std::vector sdrplay_source_c::get_antennas(size_t chan) { - if ( osmosdr::source::DCOffsetOff == mode ) - { - _dev->dcMode = 0; - if (_running) - { - mir_sdr_SetDcMode(4, 1); - } - } - else if ( osmosdr::source::DCOffsetManual == mode ) - { - std::cerr << "Manual DC correction mode is not implemented." << std::endl; - _dev->dcMode = 0; - if (_running) - { - mir_sdr_SetDcMode(4, 1); + std::vector antennas; + + if (_hwVer == 2) { + antennas += "A"; + antennas += "B"; + antennas += "HIGHZ"; + } + else { + antennas += "RX"; + } + + return antennas; +} + +std::string sdrplay_source_c::set_antenna(const std::string & antenna, size_t chan) +{ + _antenna = antenna; + + if (_streaming) { + if (_hwVer == 2) { + // HIGHZ is ANTENNA_B with AmPortSelect + if (antenna == "HIGHZ") { + mir_sdr_RSPII_AntennaControl(mir_sdr_RSPII_ANTENNA_B); + mir_sdr_AmPortSelect(1); } - } - else if ( osmosdr::source::DCOffsetAutomatic == mode ) - { - _dev->dcMode = 1; - if (_running) - { - mir_sdr_SetDcMode(4, 1); + else { + if (antenna == "A") + mir_sdr_RSPII_AntennaControl(mir_sdr_RSPII_ANTENNA_A); + else + mir_sdr_RSPII_AntennaControl(mir_sdr_RSPII_ANTENNA_B); + mir_sdr_AmPortSelect(0); } - } + + reinitDevice((int)mir_sdr_CHANGE_AM_PORT); + } + } + + return antenna; +} + +std::string sdrplay_source_c::get_antenna(size_t chan) +{ + return _antenna.c_str(); } -void sdrplay_source_c::set_dc_offset( const std::complex &offset, size_t chan ) +// NOTE: DC offset controlled here, IQ balance always on. +void sdrplay_source_c::set_dc_offset_mode(int mode, size_t chan) { - std::cerr << "Manual DC correction mode is not implemented." << std::endl; + if (osmosdr::source::DCOffsetOff == mode) { + _dcMode = 0; + if (_streaming) { + mir_sdr_SetDcMode(0, 0); + mir_sdr_DCoffsetIQimbalanceControl(0, 0); + } + } + else if (osmosdr::source::DCOffsetManual == mode) { + _dcMode = 0; + if (_streaming) { + mir_sdr_SetDcMode(0, 1); + mir_sdr_DCoffsetIQimbalanceControl(0, 1); + } + } + else if (osmosdr::source::DCOffsetAutomatic == mode) { + _dcMode = 1; + if (_streaming) { + mir_sdr_SetDcMode(4, 1); + mir_sdr_DCoffsetIQimbalanceControl(1, 1); + mir_sdr_SetDcTrackTime(63); + } + } +} + +void sdrplay_source_c::set_dc_offset(const std::complex &offset, size_t chan) +{ + std::cerr << "set_dc_offset(): not implemented" << std::endl; } -double sdrplay_source_c::set_bandwidth( double bandwidth, size_t chan ) +double sdrplay_source_c::set_bandwidth(double bandwidth, size_t chan) { - if (bandwidth <= 200e3) _dev->bwType = mir_sdr_BW_0_200; - else if (bandwidth <= 300e3) _dev->bwType = mir_sdr_BW_0_300; - else if (bandwidth <= 600e3) _dev->bwType = mir_sdr_BW_0_600; - else if (bandwidth <= 1536e3) _dev->bwType = mir_sdr_BW_1_536; - else if (bandwidth <= 5000e3) _dev->bwType = mir_sdr_BW_5_000; - else if (bandwidth <= 6000e3) _dev->bwType = mir_sdr_BW_6_000; - else if (bandwidth <= 7000e3) _dev->bwType = mir_sdr_BW_7_000; - else _dev->bwType = mir_sdr_BW_8_000; + _bwType = mir_sdr_BW_8_000; + + for (double bw : bandwidths) { + // Skip dummy value at index 0 + if (bw == 0) + continue; + if (bandwidth <= bw) { + _bwType = (mir_sdr_Bw_MHzT)(bw/1e3); + break; + } + } + + int actual = get_bandwidth(chan); + std::cerr << "SDRplay bandwidth requested=" << bandwidth + << " actual=" << actual << std::endl; - if (_running) - { - reinit_device(); - } + if (_streaming) { + reinitDevice((int)mir_sdr_CHANGE_BW_TYPE); + } - return get_bandwidth( chan ); + return actual; } -double sdrplay_source_c::get_bandwidth( size_t chan ) +double sdrplay_source_c::get_bandwidth(size_t chan) { - double tmpbw=0.0f; - if (_dev->bwType == mir_sdr_BW_0_200) tmpbw = 200e3; - else if (_dev->bwType == mir_sdr_BW_0_300) tmpbw = 300e3; - else if (_dev->bwType == mir_sdr_BW_0_600) tmpbw = 600e3; - else if (_dev->bwType == mir_sdr_BW_1_536) tmpbw = 1536e3; - else if (_dev->bwType == mir_sdr_BW_5_000) tmpbw = 5000e3; - else if (_dev->bwType == mir_sdr_BW_6_000) tmpbw = 6000e3; - else if (_dev->bwType == mir_sdr_BW_7_000) tmpbw = 7000e3; - else tmpbw = 8000e3; - - return (double)tmpbw; + return (double)_bwType * 1e3; } -osmosdr::freq_range_t sdrplay_source_c::get_bandwidth_range( size_t chan ) +osmosdr::freq_range_t sdrplay_source_c::get_bandwidth_range(size_t chan) { - osmosdr::freq_range_t range; + osmosdr::freq_range_t range; - range += osmosdr::range_t( 200e3 ); - range += osmosdr::range_t( 300e3 ); - range += osmosdr::range_t( 600e3 ); - range += osmosdr::range_t( 1536e3 ); - range += osmosdr::range_t( 5000e3 ); - range += osmosdr::range_t( 6000e3 ); - range += osmosdr::range_t( 7000e3 ); - range += osmosdr::range_t( 8000e3 ); + // bandwidths[0] is a dummy + for (unsigned int i=1; i * Copyright 2015 SDRplay Ltd * Copyright 2012 Dimitri Stolnikov * @@ -22,17 +23,21 @@ #define INCLUDED_SDRPLAY_SOURCE_C_H #include - #include -#include -#include #include "osmosdr/ranges.h" #include "source_iface.h" +#include + class sdrplay_source_c; -typedef struct sdrplay_dev sdrplay_dev_t; + +template +struct Range { + T min; + T max; +}; /* * We use boost::shared_ptr's instead of raw pointers for all access @@ -82,6 +87,9 @@ public: gr_vector_const_void_star &input_items, gr_vector_void_star &output_items ); + bool start( void ); + bool stop( void ); + static std::vector< std::string > get_devices(); size_t get_num_channels( void ); @@ -118,19 +126,53 @@ public: osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 ); private: - void reinit_device(void); - void set_gain_limits(double freq); - - sdrplay_dev_t *_dev; - - std::vector< short > _bufi; - std::vector< short > _bufq; - int _buf_offset; - boost::mutex _buf_mutex; - - bool _running; - bool _uninit; - bool _auto_gain; + void startStreaming(void); + void stopStreaming(void); + void reallocateBuffers(int size, int num); + void reinitDevice(int reason); + int checkLNA(int lna); + void updateGains(void); + void streamCallback(short *xi, short *xq, unsigned int firstSampleNum, + int grChanged, int rfChanged, int fsChanged, + unsigned int numSamples, unsigned int reset, + void *cbContext); + void gainChangeCallback(unsigned int gRdB, unsigned int lnaGRdB, void *cbContext); + + static void streamCallbackWrap(short *xi, short *xq, unsigned int firstSampleNum, + int grChanged, int rfChanged, int fsChanged, + unsigned int numSamples, unsigned int reset, + void *cbContext); + static void gainChangeCallbackWrap(unsigned int gRdB, unsigned int lnaGRdB, void *cbContext); + + bool _auto_gain; + + int _gain; + int _gRdB; + int _lna; + int _bcastNotch; + int _dabNotch; + int _band; + double _fsHz; + int _decim; + double _rfHz; + mir_sdr_Bw_MHzT _bwType; + mir_sdr_If_kHzT _ifType; + int _samplesPerPacket; + int _dcMode; + unsigned char _hwVer; + int _devIndex; + std::string _antenna; + int _biasT; + + gr_complex *_buffer; + int _bufferOffset; + int _bufferSpaceRemaining; + boost::mutex _bufferMutex; + boost::condition_variable _bufferReady; // buffer is ready to move to other thread + + bool _streaming; + bool _flowgraphRunning; + bool _reinit; // signal streamer to return after a reinit }; #endif /* INCLUDED_SDRPLAY_SOURCE_C_H */ From adam.callis at gmail.com Sun Jan 14 22:14:41 2018 From: adam.callis at gmail.com (Adam Callis) Date: Sun, 14 Jan 2018 17:14:41 -0500 Subject: Looking for help decoding Pulse Interval Width Modulation Message-ID: Good afternoon, I am a complete noob playing with my YardStick one and HackRf one. I am working a signal that I am fairly certain is using pulse interval encoding and I am having a heck of a time understanding how to turn the NRZ 1's and 0's into the Pulse Interval. The closest thing I have seen is how RFID uses pulse interval encoding but I don't think that is the same thing. I started by recording in osmocom_fft I then used inspectrum to extract symbols and found that most symbols were 1 unit in inspectrum, but there were a few places where there were 2 or 4 in a row of high or low. Once I exported the symbols I was able to generate a bit string of 1's and 0's representing highs and lows. I was able to identify the preamble and what I think is the sync word, but that's where I get lost. I have seen many examples where people take 3 bits and assume that say 001 is a 0 and 011 is a 1n a PWM model but nothing I seem to do seems to work to uncover the PIE encoding and was hoping someone out there could point me in the right direction. Thanks, Adam Sent from my iPad From willcode4 at gmail.com Mon Jan 15 14:52:52 2018 From: willcode4 at gmail.com (Jeff Long) Date: Mon, 15 Jan 2018 09:52:52 -0500 Subject: [gr-osmosdr] Patch: Update SDRplay Support In-Reply-To: References: Message-ID: Latest version. Added a couple new features, but also fixed a buffer locking problem. Diff is against master. On Sun, Jan 14, 2018 at 8:51 AM, Jeff Long wrote: > Latest version of diff. > > On Fri, Jan 12, 2018 at 12:48 PM, Jeff Long wrote: > >> SDRplay support, rewritten, supporting RSP1, RSP1A, and RSP2 >> >> Requires ENABLE_NONFREE=yes. Requires SDRplay's SDK v2.11 to be installed >> by the user. In the future, if there is a way to redistribute >> mirsdrapi-rsp.h from the SDK, that problem could be addressed using a shim >> library that looks for /usr/local/lib/libmirsdrapi-rsp.so at runtime. >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt index 09838c1..4229eea 100644 --- a/grc/CMakeLists.txt +++ b/grc/CMakeLists.txt @@ -40,6 +40,7 @@ add_custom_target(osmosdr_grc_xml_blocks ALL DEPENDS ${xml_blocks}) install(FILES ${xml_blocks} + sdrplay_source.xml # DESTINATION ${GRC_BLOCKS_DIR} DESTINATION share/gnuradio/grc/blocks ) diff --git a/grc/sdrplay_source.xml b/grc/sdrplay_source.xml new file mode 100644 index 0000000..0f4e264 --- /dev/null +++ b/grc/sdrplay_source.xml @@ -0,0 +1,150 @@ + + + SDRplay Source + sdrplay_source + Sources + import osmosdr + osmosdr.source(args = $args + ",bias=" + str($bias) + ",lo=" + str($loFreq)) +self.$(id).set_sample_rate($sample_rate) +self.$(id).set_center_freq($freq, 0) +self.$(id).set_freq_corr($corr, 0) +self.$(id).set_dc_offset_mode($dc_offset_mode, 0) +self.$(id).set_iq_balance_mode($iq_balance_mode, 0) +self.$(id).set_gain_mode($gain_mode, 0) +self.$(id).set_gain($lna, "LNA_ATTEN_STEP", 0) +self.$(id).set_gain($grdb, "IF_ATTEN_DB", 0) +self.$(id).set_antenna($ant, 0) +self.$(id).set_bandwidth($bw, 0) + + set_sample_rate($sample_rate) + set_center_freq($freq, 0) + set_freq_corr($corr, 0) + set_dc_offset_mode($dc_offset_mode, 0) + set_iq_balance_mode($iq_balance_mode, 0) + set_gain_mode($gain_mode, 0) + set_gain($lna, "LNA_ATTEN_STEP", 0) + set_gain($grdb, "IF_ATTEN_DB", 0) + set_antenna($ant, 0) + set_bandwidth($bw, 0) + + Output Type + type + enum + + + + Device Arguments + args + + string + + + Num Channels + nchan + 1 + int + + + + Sample Rate (sps) + sample_rate + samp_rate + real + + + + Frequency (Hz) + freq + 100e6 + real + + + Freq. Corr. (ppm) + corr + 0 + real + + + DC Offset Mode + dc_offset_mode + 0 + int + + + + + IQ Balance Mode + iq_balance_mode + 0 + int + + + + + Gain Mode + gain_mode + False + bool + + + + + LNA Atten Step + lna + 0 + real + + + IF Atten (dB) + grdb + 40 + real + + + Antenna + ant + + string + + + + + + + + Bandwidth (Hz) + bw + 1536e3 + real + + + Bias Voltage + bias + 0 + int + + + + + AM LO Freq + loFreq + 0 + int + + + + + + + out + $type.type + 1 + + diff --git a/lib/sdrplay/sdrplay_source_c.cc b/lib/sdrplay/sdrplay_source_c.cc index 9a9ee40..8c3d6fd 100644 --- a/lib/sdrplay/sdrplay_source_c.cc +++ b/lib/sdrplay/sdrplay_source_c.cc @@ -1,15 +1,13 @@ /* -*- c++ -*- */ /* - * Copyright 2015 SDRplay Ltd - * Copyright 2012 Dimitri Stolnikov - * Copyright 2012 Steve Markgraf + * Copyright 2018 Jeff Long * * 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, + * 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. @@ -20,11 +18,6 @@ * 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 @@ -32,569 +25,738 @@ #include "sdrplay_source_c.h" #include #include "osmosdr/source.h" +#include "arg_helpers.h" #include #include +#include +#include +#include -#include #include -#include -#include - -#include - -#include "arg_helpers.h" +#include #define MAX_SUPPORTED_DEVICES 4 -struct sdrplay_dev -{ - int gRdB; - double gain_dB; - double fsHz; - double rfHz; - mir_sdr_Bw_MHzT bwType; - mir_sdr_If_kHzT ifType; - int samplesPerPacket; - int maxGain; - int minGain; - int dcMode; -}; - using namespace boost::assign; -#define BYTES_PER_SAMPLE 4 // sdrplay device delivers 16 bit signed IQ data - // containing 12 bits of information +// Index by mir_sdr_Bw_MHzT +static std::vector bandwidths = { + 0, // Dummy + 200e3, + 300e3, + 600e3, + 1536e3, + 5000e3, + 6000e3, + 7000e3, + 8000e3 +}; -#define SDRPLAY_AM_MIN 150e3 -#define SDRPLAY_AM_MAX 30e6 -#define SDRPLAY_FM_MIN 64e6 -#define SDRPLAY_FM_MAX 108e6 -#define SDRPLAY_B3_MIN 162e6 -#define SDRPLAY_B3_MAX 240e6 -#define SDRPLAY_B45_MIN 470e6 -#define SDRPLAY_B45_MAX 960e6 -#define SDRPLAY_L_MIN 1450e6 -#define SDRPLAY_L_MAX 1675e6 +// TODO - RSP1 lower freq is 10e3. +#define SDRPLAY_FREQ_MIN 1e3 +#define SDRPLAY_FREQ_MAX 2000e6 -#define SDRPLAY_MAX_BUF_SIZE 504 +static std::string hwName(int hwVer) +{ + if (hwVer == 1) + return "RSP1"; + if (hwVer == 2) + return "RSP2"; + if (hwVer ==255) + return "RSP1A"; + return "UNK"; +} -/* - * Create a new instance of sdrplay_source_c and return - * a boost shared_ptr. This is effectively the public constructor. - */ sdrplay_source_c_sptr make_sdrplay_source_c (const std::string &args) { return gnuradio::get_initial_sptr(new sdrplay_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 +// 0 inputs, 1 output +static const int MIN_IN = 0; +static const int MAX_IN = 0; +static const int MIN_OUT = 1; +static const int MAX_OUT = 1; -/* - * The private constructor - */ sdrplay_source_c::sdrplay_source_c (const std::string &args) : gr::sync_block ("sdrplay_source_c", - gr::io_signature::make(MIN_IN, MAX_IN, sizeof (gr_complex)), - gr::io_signature::make(MIN_OUT, MAX_OUT, sizeof (gr_complex))), - _running(false), - _uninit(false), - _auto_gain(false) -{ - _dev = (sdrplay_dev_t *)malloc(sizeof(sdrplay_dev_t)); - if (_dev == NULL) - { - return; + gr::io_signature::make(MIN_IN, MAX_IN, sizeof (gr_complex)), + gr::io_signature::make(MIN_OUT, MAX_OUT, sizeof (gr_complex))), + _auto_gain(false), + _gRdB(40), + _lna(0), + _bcastNotch(0), + _dabNotch(0), + _fsHz(8e6), + _decim(1), + _rfHz(100e6), + _bwType(mir_sdr_BW_6_000), + _ifType(mir_sdr_IF_Zero), + _loMode(mir_sdr_LO_Auto), + _dcMode(false), + _iqMode(false), + _buffer(NULL), + _streaming(false), + _flowgraphRunning(false), + _reinit(false) +{ + dict_t dict = params_to_dict(args); + if (dict.count("sdrplay")) { + _devIndex = boost::lexical_cast(dict["sdrplay"]); + } + else { + _devIndex = 0; + } + + mir_sdr_DebugEnable(1); + + unsigned int numDevices; + mir_sdr_DeviceT mirDevices[MAX_SUPPORTED_DEVICES]; + mir_sdr_GetDevices(mirDevices, &numDevices, MAX_SUPPORTED_DEVICES); + _hwVer = mirDevices[_devIndex].hwVer; + + if (_hwVer == 2) { + _antenna = "A"; + } + else { + _antenna = "RX"; + } + + // bias=[0|1] to turn [off|on] bias tee. Default is off. + _biasT = 0; + if (dict.count("bias")) { + _biasT = boost::lexical_cast(dict["bias"]); + } + + // lo=[120|144|168] to set first LO to 120/144/168 MHz. Default is Auto. + if (dict.count("lo")) { + int loMode = boost::lexical_cast(dict["lo"]); + if (loMode == 120) + _loMode = mir_sdr_LO_120MHz; + else if (loMode == 144) + _loMode = mir_sdr_LO_144MHz; + else if (loMode == 168) + _loMode = mir_sdr_LO_168MHz; + } } - _dev->fsHz = 2048e3; - _dev->rfHz = 200e6; - _dev->bwType = mir_sdr_BW_1_536; - _dev->ifType = mir_sdr_IF_Zero; - _dev->samplesPerPacket = 0; - _dev->dcMode = 0; - _dev->gRdB = 60; - set_gain_limits(_dev->rfHz); - _dev->gain_dB = _dev->maxGain - _dev->gRdB; - - _bufi.reserve(SDRPLAY_MAX_BUF_SIZE); - _bufq.reserve(SDRPLAY_MAX_BUF_SIZE); - _buf_mutex.lock(); - _buf_offset = 0; - _buf_mutex.unlock(); +sdrplay_source_c::~sdrplay_source_c () +{ + if (_streaming) { + stopStreaming(); + } } -/* - * Our virtual destructor. - */ -sdrplay_source_c::~sdrplay_source_c () +bool sdrplay_source_c::start(void) { - free(_dev); - _dev = NULL; - _buf_mutex.lock(); - if (_running) - { - _running = false; - } - _uninit = true; - _buf_mutex.unlock(); -} - -void sdrplay_source_c::reinit_device() -{ - std::cerr << "reinit_device started" << std::endl; - _buf_mutex.lock(); - std::cerr << "after mutex.lock" << std::endl; - if (_running) - { - std::cerr << "mir_sdr_Uninit started" << std::endl; - mir_sdr_Uninit(); - } - - std::cerr << "mir_sdr_Init started" << std::endl; - mir_sdr_Init(_dev->gRdB, _dev->fsHz / 1e6, _dev->rfHz / 1e6, _dev->bwType, _dev->ifType, &_dev->samplesPerPacket); - - if (_dev->dcMode) - { - std::cerr << "mir_sdr_SetDcMode started" << std::endl; - mir_sdr_SetDcMode(4, 1); - } - - _buf_offset = 0; - _buf_mutex.unlock(); - std::cerr << "reinit_device end" << std::endl; -} - -void sdrplay_source_c::set_gain_limits(double freq) -{ - if (freq <= SDRPLAY_AM_MAX) - { - _dev->minGain = -4; - _dev->maxGain = 98; - } - else if (freq <= SDRPLAY_FM_MAX) - { - _dev->minGain = 1; - _dev->maxGain = 103; - } - else if (freq <= SDRPLAY_B3_MAX) - { - _dev->minGain = 5; - _dev->maxGain = 107; - } - else if (freq <= SDRPLAY_B45_MAX) - { - _dev->minGain = 9; - _dev->maxGain = 94; - } - else if (freq <= SDRPLAY_L_MAX) - { - _dev->minGain = 24; - _dev->maxGain = 105; - } -} - -int sdrplay_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]; - int cnt = noutput_items; - unsigned int sampNum; - int grChanged; - int rfChanged; - int fsChanged; - - if (_uninit) - { - return WORK_DONE; - } - - if (!_running) - { - reinit_device(); - _running = true; - } - - _buf_mutex.lock(); - - if (_buf_offset) - { - for (int i = _buf_offset; i < _dev->samplesPerPacket; i++) - { - *out++ = gr_complex( float(_bufi[i]) * (1.0f/2048.0f), float(_bufq[i]) * (1.0f/2048.0f) ); - } - cnt -= (_dev->samplesPerPacket - _buf_offset); - } - - while ((cnt - _dev->samplesPerPacket) >= 0) - { - mir_sdr_ReadPacket(_bufi.data(), _bufq.data(), &sampNum, &grChanged, &rfChanged, &fsChanged); - for (int i = 0; i < _dev->samplesPerPacket; i++) - { - *out++ = gr_complex( float(_bufi[i]) * (1.0f/2048.0f), float(_bufq[i]) * (1.0f/2048.0f) ); - } - cnt -= _dev->samplesPerPacket; - } - - _buf_offset = 0; - if (cnt) - { - mir_sdr_ReadPacket(_bufi.data(), _bufq.data(), &sampNum, &grChanged, &rfChanged, &fsChanged); - for (int i = 0; i < cnt; i++) - { - *out++ = gr_complex( float(_bufi[i]) * (1.0f/2048.0f), float(_bufq[i]) * (1.0f/2048.0f) ); - } - _buf_offset = cnt; - } - _buf_mutex.unlock(); + boost::mutex::scoped_lock lock(_bufferMutex); + _flowgraphRunning = true; + return true; +} - return noutput_items; +bool sdrplay_source_c::stop(void) +{ + boost::mutex::scoped_lock lock(_bufferMutex); + _flowgraphRunning = false; + // FG may be modified, so assume copied pointer is invalid + _buffer = NULL; + return true; } -std::vector sdrplay_source_c::get_devices() +int sdrplay_source_c::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) { - std::vector devices; - std::cerr << "get_devices started" << std::endl; + gr_complex *out = (gr_complex *)output_items[0]; - unsigned int dev_cnt = 0; - int samplesPerPacket; - while(mir_sdr_Init(60, 2.048, 200.0, mir_sdr_BW_1_536, mir_sdr_IF_Zero, &samplesPerPacket) == mir_sdr_Success) - { - dev_cnt++; - } + if (!_streaming) + startStreaming(); - std::cerr << "Device count: " << dev_cnt << std::endl; + { + boost::mutex::scoped_lock lock(_bufferMutex); + _buffer = out; + _bufferSpaceRemaining = noutput_items; + _bufferOffset = 0; + _bufferReady.notify_one(); - for (unsigned int i = 0; i < dev_cnt; i++) - { - mir_sdr_Uninit(); - std::string args = "sdrplay=" + boost::lexical_cast< std::string >( i ); - args += ",label='" + std::string("SDRplay RSP") + "'"; - std::cerr << args << std::endl; - devices.push_back( args ); - } + while (_buffer && _streaming) { + _bufferReady.wait(lock); + } + } - std::cerr << "get_devices end" << std::endl; - return devices; -} + if (!_streaming) { + return 0; + } -size_t sdrplay_source_c::get_num_channels() -{ - std::cerr << "get_num_channels: 1" << std::endl; - return 1; + return noutput_items - _bufferSpaceRemaining; } -osmosdr::meta_range_t sdrplay_source_c::get_sample_rates() +// Called by sdrplay streamer thread when data is available +void sdrplay_source_c::streamCallback(short *xi, short *xq, + unsigned int firstSampleNum, + int grChanged, int rfChanged, int fsChanged, + unsigned int numSamples, unsigned int reset) { - osmosdr::meta_range_t range; + unsigned int i = 0; + _reinit = false; + + boost::mutex::scoped_lock lock(_bufferMutex); + + while (i < numSamples) { - range += osmosdr::range_t( 2000e3, 12000e3 ); + // Discard samples if not streaming, if flowgraph not running, or reinit needed. + if (!_streaming || _reinit || !_flowgraphRunning) + return; + + // While buffer is not ready for write, wait a short time. + while (!_buffer) { + if (boost::cv_status::timeout == + _bufferReady.wait_for(lock, boost::chrono::milliseconds(250))) + return; + } + + // Copy until out of samples or buffer is full + while ((i < numSamples) && (_bufferSpaceRemaining > 0)) { + _buffer[_bufferOffset] = + gr_complex(float(xi[i]) / 32768.0, float(xq[i]) / 32768.0); + + i++; + _bufferOffset++; + _bufferSpaceRemaining--; + } - return range; + if (_bufferSpaceRemaining == 0) { + _buffer = NULL; + _bufferReady.notify_one(); + } + } } -double sdrplay_source_c::set_sample_rate(double rate) +// Callback wrapper +void sdrplay_source_c::streamCallbackWrap(short *xi, short *xq, + unsigned int firstSampleNum, + int grChanged, int rfChanged, int fsChanged, + unsigned int numSamples, unsigned int reset, + void *cbContext) { - std::cerr << "set_sample_rate start" << std::endl; - double diff = rate - _dev->fsHz; - _dev->fsHz = rate; - - std::cerr << "rate = " << rate << std::endl; - std::cerr << "diff = " << diff << std::endl; - if (_running) - { - if (fabs(diff) < 10000.0) - { - std::cerr << "mir_sdr_SetFs started" << std::endl; - mir_sdr_SetFs(diff, 0, 0, 0); - } - else - { - std::cerr << "reinit_device started" << std::endl; - reinit_device(); - } - } - std::cerr << "set_sample_rate end" << std::endl; + sdrplay_source_c *obj = (sdrplay_source_c *)cbContext; + obj->streamCallback(xi, xq, + firstSampleNum, + grChanged, rfChanged, fsChanged, + numSamples, reset); +} - return get_sample_rate(); +// Called by strplay streamer thread when gain reduction is changed. +void sdrplay_source_c::gainChangeCallback(unsigned int gRdB, + unsigned int lnaGRdB) +{ + std::cerr << "GR change, BB+MIX -" << gRdB << "dB, LNA -" << lnaGRdB << std::endl; } -double sdrplay_source_c::get_sample_rate() +// Callback wrapper +void sdrplay_source_c::gainChangeCallbackWrap(unsigned int gRdB, + unsigned int lnaGRdB, + void *cbContext) { - if (_running) - { - return _dev->fsHz; - } + sdrplay_source_c *obj = (sdrplay_source_c *)cbContext; + obj->gainChangeCallback(gRdB, lnaGRdB); +} + +void sdrplay_source_c::startStreaming(void) +{ + if (_streaming) + return; + + unsigned int numDevices; + mir_sdr_DeviceT mirDevices[MAX_SUPPORTED_DEVICES]; + mir_sdr_ReleaseDeviceIdx(); + mir_sdr_GetDevices(mirDevices, &numDevices, MAX_SUPPORTED_DEVICES); + mir_sdr_SetDeviceIdx(_devIndex); + + std::cerr << "Using SDRplay " << hwName(_hwVer) << " " + << mirDevices[_devIndex].SerNo << std::endl; + + // Set bias voltage on/off (RSP1A/RSP2). + std::cerr << "Bias voltage: " << _biasT << std::endl; + if (_hwVer == 2) + mir_sdr_RSPII_BiasTControl(_biasT); + else if (_hwVer == 255) + mir_sdr_rsp1a_BiasT(_biasT); + + // Set first LO frequency + mir_sdr_SetLoMode(_loMode); + + _streaming = true; + + set_gain_mode(get_gain_mode(/*channel*/ 0), /*channel*/ 0); + + int gRdB = _gRdB; + int gRdBsystem = 0; + + mir_sdr_StreamInit(&gRdB, + _fsHz / 1e6, + _rfHz / 1e6, + _bwType, + _ifType, + checkLNA(_lna), + &gRdBsystem, + mir_sdr_USE_RSP_SET_GR, + &_samplesPerPacket, + &streamCallbackWrap, + &gainChangeCallbackWrap, + this); -// return 0; - return _dev->fsHz; + // Set decimation with halfband filter + mir_sdr_DecimateControl(_decim != 1, _decim, 1); + + mir_sdr_DCoffsetIQimbalanceControl(_dcMode, _iqMode); + + // Model-specific initialization + if (_hwVer == 2) { + set_antenna(get_antenna(), 0); + mir_sdr_RSPII_RfNotchEnable(_bcastNotch); + } + + else if (_hwVer == 255) { + mir_sdr_rsp1a_BroadcastNotch(_bcastNotch); + mir_sdr_rsp1a_DabNotch(_dabNotch); + } } -osmosdr::freq_range_t sdrplay_source_c::get_freq_range( size_t chan ) +void sdrplay_source_c::stopStreaming(void) { - osmosdr::freq_range_t range; + if (!_streaming) + return; - range += osmosdr::range_t( SDRPLAY_AM_MIN, SDRPLAY_AM_MAX ); /* LW/MW/SW (150 kHz - 30 MHz) */ - range += osmosdr::range_t( SDRPLAY_FM_MIN, SDRPLAY_FM_MAX ); /* VHF Band II (64 - 108 MHz) */ - range += osmosdr::range_t( SDRPLAY_B3_MIN, SDRPLAY_B3_MAX ); /* Band III (162 - 240 MHz) */ - range += osmosdr::range_t( SDRPLAY_B45_MIN, SDRPLAY_B45_MAX ); /* Band IV/V (470 - 960 MHz) */ - range += osmosdr::range_t( SDRPLAY_L_MIN, SDRPLAY_L_MAX ); /* L-Band (1450 - 1675 MHz) */ + _streaming = false; - return range; + mir_sdr_StreamUninit(); + mir_sdr_ReleaseDeviceIdx(); } -double sdrplay_source_c::set_center_freq( double freq, size_t chan ) +void sdrplay_source_c::reinitDevice(int reason) { - std::cerr << "set_center_freq start" << std::endl; - std::cerr << "freq = " << freq << std::endl; - double diff = freq - _dev->rfHz; - std::cerr << "diff = " << diff << std::endl; - _dev->rfHz = freq; - set_gain_limits(freq); - if (_running) - { - if (fabs(diff) < 10000.0) - { - std::cerr << "mir_sdr_SetRf started" << std::endl; - mir_sdr_SetRf(diff, 0, 0); - } - else - { - std::cerr << "reinit_device started" << std::endl; - reinit_device(); - } - } + // If no reason given, reinit everything + if (reason == (int)mir_sdr_CHANGE_NONE) + reason = (mir_sdr_CHANGE_GR | + mir_sdr_CHANGE_FS_FREQ | + mir_sdr_CHANGE_RF_FREQ | + mir_sdr_CHANGE_BW_TYPE | + mir_sdr_CHANGE_IF_TYPE | + mir_sdr_CHANGE_LO_MODE | + mir_sdr_CHANGE_AM_PORT); + + int gRdB; + int gRdBsystem; // Returned overall system gain reduction + + gRdB = _gRdB; - std::cerr << "set_center_freq end" << std::endl; - return get_center_freq( chan ); + // Tell stream CB to return + _reinit = true; + + mir_sdr_Reinit(&gRdB, + _fsHz / 1e6, + _rfHz / 1e6, + _bwType, + _ifType, + _loMode, + checkLNA(_lna), + &gRdBsystem, + mir_sdr_USE_RSP_SET_GR, + &_samplesPerPacket, + (mir_sdr_ReasonForReinitT)reason + ); + + // Set decimation with halfband filter + if (reason && (int)mir_sdr_CHANGE_FS_FREQ) + mir_sdr_DecimateControl(_decim != 1, _decim, 1); + + _bufferReady.notify_one(); } -double sdrplay_source_c::get_center_freq( size_t chan ) +std::vector sdrplay_source_c::get_devices() { - if (_running) - { - return _dev->rfHz; - } + unsigned int numDevices; + mir_sdr_DeviceT mirDevices[MAX_SUPPORTED_DEVICES]; + std::vector devices; -// return 0; - return _dev->rfHz; + mir_sdr_GetDevices(mirDevices, &numDevices, MAX_SUPPORTED_DEVICES); + + for (unsigned int i=0; idevAvail) + continue; + std::string args = boost::str(boost::format("sdrplay=%d,label='SDRplay %s %s'") + % i % hwName((int)dev->hwVer) % dev->SerNo ); + std::cerr << args << std::endl; + devices.push_back( args ); + } + + return devices; } -double sdrplay_source_c::set_freq_corr( double ppm, size_t chan ) +size_t sdrplay_source_c::get_num_channels() { - return get_freq_corr( chan ); + return 1; } -double sdrplay_source_c::get_freq_corr( size_t chan ) +osmosdr::meta_range_t sdrplay_source_c::get_sample_rates() { - return 0; + osmosdr::meta_range_t range; + range += osmosdr::range_t( 62.5e3, 10e6 ); + return range; } -std::vector sdrplay_source_c::get_gain_names( size_t chan ) +double sdrplay_source_c::set_sample_rate(double rate) { - std::vector< std::string > gains; + rate = std::min( std::max(rate,62.5e3), 10e6 ); + _fsHz = rate; + + // Decimation is required for rates below 2MS/s + _decim = 1; + while (_fsHz < 2e6) { + _decim *= 2; + _fsHz *= 2; + } + + if (_streaming) + reinitDevice((int)mir_sdr_CHANGE_FS_FREQ); - gains += "LNA_MIX_BB"; + return get_sample_rate(); +} + +double sdrplay_source_c::get_sample_rate() +{ + return _fsHz/_decim; +} - return gains; +osmosdr::freq_range_t sdrplay_source_c::get_freq_range(size_t chan) +{ + osmosdr::freq_range_t range; + range += osmosdr::range_t(SDRPLAY_FREQ_MIN, SDRPLAY_FREQ_MAX); + return range; } -osmosdr::gain_range_t sdrplay_source_c::get_gain_range( size_t chan ) +double sdrplay_source_c::set_center_freq(double freq, size_t chan) { - osmosdr::gain_range_t range; + _rfHz = freq; - for (int i = _dev->minGain; i < _dev->maxGain; i++) - { - range += osmosdr::range_t( (float)i ); - } + if (_streaming) { + reinitDevice((int)mir_sdr_CHANGE_RF_FREQ); + } - return range; + return get_center_freq( chan ); } -osmosdr::gain_range_t sdrplay_source_c::get_gain_range( const std::string & name, size_t chan ) +double sdrplay_source_c::get_center_freq(size_t chan) { - return get_gain_range( chan ); + return _rfHz; } -bool sdrplay_source_c::set_gain_mode( bool automatic, size_t chan ) +double sdrplay_source_c::set_freq_corr(double ppm, size_t chan) { - std::cerr << "set_gain_mode started" << std::endl; - _auto_gain = automatic; - std::cerr << "automatic = " << automatic << std::endl; - if (automatic) - { - /* Start AGC */ - std::cerr << "AGC not yet implemented" << std::endl; - } + return get_freq_corr( chan ); +} - std::cerr << "set_gain_mode end" << std::endl; - return get_gain_mode(chan); +double sdrplay_source_c::get_freq_corr(size_t chan) +{ + return 0; } -bool sdrplay_source_c::get_gain_mode( size_t chan ) +std::vector sdrplay_source_c::get_gain_names(size_t chan) { - return _auto_gain; + std::vector gains; + + gains += "LNA_ATTEN_STEP"; + gains += "IF_ATTEN_DB"; + + // RSP1A and RSP2 have broadcast notch filters, and RSP1A has a DAB + // notch filter. Show all controls for all models, mainly because + // gqrx gets confused when switching between sources with different + // sets of gains. + gains += "BCAST_NOTCH"; + gains += "DAB_NOTCH"; + + return gains; } -double sdrplay_source_c::set_gain( double gain, size_t chan ) +osmosdr::gain_range_t sdrplay_source_c::get_gain_range(size_t chan) { - std::cerr << "set_gain started" << std::endl; - _dev->gain_dB = gain; - std::cerr << "gain = " << gain << std::endl; - if (gain < _dev->minGain) - { - _dev->gain_dB = _dev->minGain; - } - if (gain > _dev->maxGain) - { - _dev->gain_dB = _dev->maxGain; - } - _dev->gRdB = (int)(_dev->maxGain - gain); + osmosdr::gain_range_t range; - if (_running) - { - std::cerr << "mir_sdr_SetGr started" << std::endl; - mir_sdr_SetGr(_dev->gRdB, 1, 0); - } + for (int i = 20; i <= 59; i++) + range += osmosdr::range_t((float)i); -std::cerr << "set_gain end" << std::endl; -return get_gain( chan ); + return range; } -double sdrplay_source_c::set_gain( double gain, const std::string & name, size_t chan) +osmosdr::gain_range_t sdrplay_source_c::get_gain_range(const std::string & name, size_t chan) { - return set_gain( gain, chan ); + osmosdr::gain_range_t range; + int maxLnaState; + + if (name == "LNA_ATTEN_STEP") { + if (_hwVer == 2) + maxLnaState = 8; + else if (_hwVer == 255) + maxLnaState = 9; + else + maxLnaState = 3; + for (int i = 0; i <= maxLnaState; i++) + range += osmosdr::range_t((float)i); + } + // RSP1A, RSP2 + else if (name == "BCAST_NOTCH") { + range += osmosdr::range_t((float)0); + if (_hwVer == 2 || _hwVer == 255) + range += osmosdr::range_t((float)1); + } + // RSP1A + else if (name == "DAB_NOTCH") { + range += osmosdr::range_t((float)0); + if (_hwVer == 255) + range += osmosdr::range_t((float)1); + } + else { + for (int i = 20; i <= 59; i++) + range += osmosdr::range_t((float)i); + } + + return range; } -double sdrplay_source_c::get_gain( size_t chan ) +bool sdrplay_source_c::set_gain_mode(bool automatic, size_t chan) { - if ( _running ) - { - return _dev->gain_dB; - } + _auto_gain = automatic; + if (_streaming) { + if (automatic) { + mir_sdr_AgcControl(mir_sdr_AGC_5HZ, -30, 0, 0, 0, 0, checkLNA(_lna)); + } + else { + mir_sdr_AgcControl(mir_sdr_AGC_DISABLE, -30, 0, 0, 0, 0, checkLNA(_lna)); + } + } -// return 0; - return _dev->gain_dB; + return _auto_gain; } -double sdrplay_source_c::get_gain( const std::string & name, size_t chan ) +bool sdrplay_source_c::get_gain_mode(size_t chan) { - return get_gain( chan ); + return _auto_gain; } -std::vector< std::string > sdrplay_source_c::get_antennas( size_t chan ) +int sdrplay_source_c::checkLNA(int lna) { - std::vector< std::string > antennas; + // Clip LNA reduction step. See table in API section 5.3. + if (_hwVer == 1) { + lna = std::min(3, lna); + } + else if (_hwVer == 255) { + if (_rfHz < 60000000) + lna = std::min(6, lna); + else if (_rfHz >= 1000000000) + lna = std::min(8, lna); + else + lna = std::min(9, lna); + } + else if (_hwVer == 2) { + if (_rfHz >= 420000000) + lna = std::min(5, lna); + else if (_rfHz < 60000000 && _antenna == "HIGHZ") + lna = std::min(4, lna); + else + lna = std::min(8, lna); + } + + return lna; +} + +double sdrplay_source_c::set_gain(double gain, size_t chan) +{ + set_gain(gain, "IF_ATTEN_DB"); + return get_gain("IF_ATTEN_DB"); +} + +double sdrplay_source_c::set_gain(double gain, const std::string & name, size_t chan) +{ + bool bcastNotchChanged = false; + bool dabNotchChanged = false; + bool gainChanged = false; + + if (name == "LNA_ATTEN_STEP") { + if (gain != _lna) + gainChanged = true; + _lna = int(gain); + } + else if (name == "IF_ATTEN_DB") { + // Ignore out-of-bounds values, since caller knows limits. (GQRX spurious calls). + if (gain >= 20.0 && gain <= 59.0 && gain != _gRdB) { + gainChanged = true; + _gRdB = int(gain); + } + } + // RSP1A, RSP2 + else if (name == "BCAST_NOTCH" && (_hwVer == 2 || _hwVer == 255)) { + if (int(gain) != _bcastNotch) + bcastNotchChanged = true; + _bcastNotch = int(gain); + } + // RSP1A + else if (name == "DAB_NOTCH" && _hwVer == 255) { + if (int(gain) != _dabNotch) + dabNotchChanged = true; + _dabNotch = int(gain); + } + + if (_streaming) { + if (gainChanged) + mir_sdr_RSP_SetGr(_gRdB, checkLNA(_lna), 1 /*absolute*/, 0 /*immediate*/); + + if (bcastNotchChanged) { + if (_hwVer == 255 ) { + mir_sdr_rsp1a_BroadcastNotch(_bcastNotch); + } + else if (_hwVer == 2) { + mir_sdr_RSPII_RfNotchEnable(_bcastNotch); + } + } - antennas += get_antenna( chan ); + if (dabNotchChanged) { + mir_sdr_rsp1a_DabNotch(_dabNotch); + } + } - return antennas; + return get_gain(chan); } -std::string sdrplay_source_c::set_antenna( const std::string & antenna, size_t chan ) +double sdrplay_source_c::get_gain(size_t chan) { - return get_antenna( chan ); + return get_gain("IF_ATTEN_DB"); } -std::string sdrplay_source_c::get_antenna( size_t chan ) +double sdrplay_source_c::get_gain(const std::string & name, size_t chan) { - return "RX"; + if (name == "LNA_ATTEN_STEP") + return _lna; + else if (name == "BCAST_NOTCH") + return _bcastNotch; + else if (name == "DAB_NOTCH") + return _dabNotch; + else if (name == "IF_ATTEN_DB") + return _gRdB; + else + return 0; } -void sdrplay_source_c::set_dc_offset_mode( int mode, size_t chan ) +std::vector sdrplay_source_c::get_antennas(size_t chan) { - if ( osmosdr::source::DCOffsetOff == mode ) - { - _dev->dcMode = 0; - if (_running) - { - mir_sdr_SetDcMode(4, 1); - } - } - else if ( osmosdr::source::DCOffsetManual == mode ) - { - std::cerr << "Manual DC correction mode is not implemented." << std::endl; - _dev->dcMode = 0; - if (_running) - { - mir_sdr_SetDcMode(4, 1); + std::vector antennas; + + if (_hwVer == 2) { + antennas += "A"; + antennas += "B"; + antennas += "HIGHZ"; + } + else { + antennas += "RX"; + } + + return antennas; +} + +std::string sdrplay_source_c::set_antenna(const std::string & antenna, size_t chan) +{ + _antenna = antenna; + + if (_streaming) { + if (_hwVer == 2) { + // HIGHZ is ANTENNA_B with AmPortSelect + if (antenna == "HIGHZ") { + mir_sdr_RSPII_AntennaControl(mir_sdr_RSPII_ANTENNA_B); + mir_sdr_AmPortSelect(1); } - } - else if ( osmosdr::source::DCOffsetAutomatic == mode ) - { - _dev->dcMode = 1; - if (_running) - { - mir_sdr_SetDcMode(4, 1); + else { + if (antenna == "A") + mir_sdr_RSPII_AntennaControl(mir_sdr_RSPII_ANTENNA_A); + else + mir_sdr_RSPII_AntennaControl(mir_sdr_RSPII_ANTENNA_B); + mir_sdr_AmPortSelect(0); } - } + + reinitDevice((int)mir_sdr_CHANGE_AM_PORT); + } + } + + return antenna; } -void sdrplay_source_c::set_dc_offset( const std::complex &offset, size_t chan ) +std::string sdrplay_source_c::get_antenna(size_t chan) { - std::cerr << "Manual DC correction mode is not implemented." << std::endl; + return _antenna.c_str(); } -double sdrplay_source_c::set_bandwidth( double bandwidth, size_t chan ) +void sdrplay_source_c::set_dc_offset_mode(int mode, size_t chan) { - if (bandwidth <= 200e3) _dev->bwType = mir_sdr_BW_0_200; - else if (bandwidth <= 300e3) _dev->bwType = mir_sdr_BW_0_300; - else if (bandwidth <= 600e3) _dev->bwType = mir_sdr_BW_0_600; - else if (bandwidth <= 1536e3) _dev->bwType = mir_sdr_BW_1_536; - else if (bandwidth <= 5000e3) _dev->bwType = mir_sdr_BW_5_000; - else if (bandwidth <= 6000e3) _dev->bwType = mir_sdr_BW_6_000; - else if (bandwidth <= 7000e3) _dev->bwType = mir_sdr_BW_7_000; - else _dev->bwType = mir_sdr_BW_8_000; + _dcMode = (osmosdr::source::DCOffsetAutomatic == mode); + + if (_dcMode) { + mir_sdr_SetDcMode(4, 1); + mir_sdr_SetDcTrackTime(63); + } + mir_sdr_DCoffsetIQimbalanceControl(_dcMode, _iqMode); +} + +void sdrplay_source_c::set_dc_offset(const std::complex &offset, size_t chan) +{ + std::cerr << "set_dc_offset(): not implemented" << std::endl; +} + +void sdrplay_source_c::set_iq_balance_mode(int mode, size_t chan) +{ + _iqMode = (osmosdr::source::IQBalanceAutomatic == mode); + + mir_sdr_DCoffsetIQimbalanceControl(_dcMode, _iqMode); +} + +double sdrplay_source_c::set_bandwidth(double bandwidth, size_t chan) +{ + _bwType = mir_sdr_BW_8_000; + + for (double bw : bandwidths) { + // Skip dummy value at index 0 + if (bw == 0) + continue; + if (bandwidth <= bw) { + _bwType = (mir_sdr_Bw_MHzT)(bw/1e3); + break; + } + } + + int actual = get_bandwidth(chan); + std::cerr << "SDRplay bandwidth requested=" << bandwidth + << " actual=" << actual << std::endl; - if (_running) - { - reinit_device(); - } + if (_streaming) { + reinitDevice((int)mir_sdr_CHANGE_BW_TYPE); + } - return get_bandwidth( chan ); + return actual; } -double sdrplay_source_c::get_bandwidth( size_t chan ) +double sdrplay_source_c::get_bandwidth(size_t chan) { - double tmpbw=0.0f; - if (_dev->bwType == mir_sdr_BW_0_200) tmpbw = 200e3; - else if (_dev->bwType == mir_sdr_BW_0_300) tmpbw = 300e3; - else if (_dev->bwType == mir_sdr_BW_0_600) tmpbw = 600e3; - else if (_dev->bwType == mir_sdr_BW_1_536) tmpbw = 1536e3; - else if (_dev->bwType == mir_sdr_BW_5_000) tmpbw = 5000e3; - else if (_dev->bwType == mir_sdr_BW_6_000) tmpbw = 6000e3; - else if (_dev->bwType == mir_sdr_BW_7_000) tmpbw = 7000e3; - else tmpbw = 8000e3; - - return (double)tmpbw; + return (double)_bwType * 1e3; } -osmosdr::freq_range_t sdrplay_source_c::get_bandwidth_range( size_t chan ) +osmosdr::freq_range_t sdrplay_source_c::get_bandwidth_range(size_t chan) { - osmosdr::freq_range_t range; + osmosdr::freq_range_t range; - range += osmosdr::range_t( 200e3 ); - range += osmosdr::range_t( 300e3 ); - range += osmosdr::range_t( 600e3 ); - range += osmosdr::range_t( 1536e3 ); - range += osmosdr::range_t( 5000e3 ); - range += osmosdr::range_t( 6000e3 ); - range += osmosdr::range_t( 7000e3 ); - range += osmosdr::range_t( 8000e3 ); + // bandwidths[0] is a dummy + for (unsigned int i=1; i * Copyright 2015 SDRplay Ltd * Copyright 2012 Dimitri Stolnikov * @@ -22,17 +23,21 @@ #define INCLUDED_SDRPLAY_SOURCE_C_H #include - #include -#include -#include #include "osmosdr/ranges.h" #include "source_iface.h" +#include + class sdrplay_source_c; -typedef struct sdrplay_dev sdrplay_dev_t; + +template +struct Range { + T min; + T max; +}; /* * We use boost::shared_ptr's instead of raw pointers for all access @@ -82,6 +87,9 @@ public: gr_vector_const_void_star &input_items, gr_vector_void_star &output_items ); + bool start( void ); + bool stop( void ); + static std::vector< std::string > get_devices(); size_t get_num_channels( void ); @@ -112,25 +120,58 @@ public: void set_dc_offset_mode( int mode, size_t chan = 0 ); void set_dc_offset( const std::complex &offset, size_t chan = 0 ); + void set_iq_balance_mode( int mode, size_t chan = 0 ); double set_bandwidth( double bandwidth, size_t chan = 0 ); double get_bandwidth( size_t chan = 0 ); osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 ); private: - void reinit_device(void); - void set_gain_limits(double freq); - - sdrplay_dev_t *_dev; - - std::vector< short > _bufi; - std::vector< short > _bufq; - int _buf_offset; - boost::mutex _buf_mutex; + void startStreaming(void); + void stopStreaming(void); + void reallocateBuffers(int size, int num); + void reinitDevice(int reason); + int checkLNA(int lna); + void streamCallback(short *xi, short *xq, unsigned int firstSampleNum, + int grChanged, int rfChanged, int fsChanged, + unsigned int numSamples, unsigned int reset); + void gainChangeCallback(unsigned int gRdB, unsigned int lnaGRdB); + + static void streamCallbackWrap(short *xi, short *xq, unsigned int firstSampleNum, + int grChanged, int rfChanged, int fsChanged, + unsigned int numSamples, unsigned int reset, + void *cbContext); + static void gainChangeCallbackWrap(unsigned int gRdB, unsigned int lnaGRdB, void *cbContext); - bool _running; - bool _uninit; bool _auto_gain; + int _gain; + int _gRdB; + int _lna; + int _bcastNotch; + int _dabNotch; + double _fsHz; + int _decim; + double _rfHz; + mir_sdr_Bw_MHzT _bwType; + mir_sdr_If_kHzT _ifType; + mir_sdr_LoModeT _loMode; + int _samplesPerPacket; + bool _dcMode; + bool _iqMode; + unsigned char _hwVer; + int _devIndex; + std::string _antenna; + int _biasT; + + gr_complex *_buffer; + int _bufferOffset; + int _bufferSpaceRemaining; + boost::mutex _bufferMutex; + boost::condition_variable _bufferReady; // buffer is ready to move to other thread + + bool _streaming; + bool _flowgraphRunning; + bool _reinit; // signal streamer to return after a reinit }; #endif /* INCLUDED_SDRPLAY_SOURCE_C_H */ From willcode4 at gmail.com Fri Jan 26 13:27:08 2018 From: willcode4 at gmail.com (Jeff Long) Date: Fri, 26 Jan 2018 08:27:08 -0500 Subject: SDRPlay in gr-osmosdr Message-ID: Here is a PyBOMBS recipe file that pulls gr-osmosdr from my sdrplay2 branch. If anyone would like to try out this support with a PyBOMBS build, this would be the easiest way. Best to build everything from scratch, but you can remove the src/gr-osmosdr directory, do a pybombs fetch/rebuild if you're comfortable with that. The SDRplay SDK v2.11 is required, and should be installed before gr-osmosdr is rebuilt. The ENABLE_NONFREE flag is turned on for this build. This is just for testing, so I can get some feedback. Hopefully, this will go into the gr-osmosdr master at some point. It "works for me". Note that the gains on SDRplay are actually attenuations, so they appear backward. The LNA attenuation steps on RSPs are frequency dependent, so I gave up trying to make them look like gains. My main use case is GQRX, and that way was even more confusing. There is a GRC sdrplay source that is a little less generic than the osmocom source. There are probably ways to repackage this so it can be distributed freely, but I'll address that if there's more interest. The SDK include file is redistributable and its terms seem to be compatible with GPL. The shared object can be dynamically loaded over a placeholder library that does nothing. Yes, that's hackish and it would be better to have everything free. -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: gr-osmosdr.lwr Type: application/octet-stream Size: 1158 bytes Desc: not available URL: