[gr-osmosdr] Patch: Update SDRplay Support

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/osmocom-sdr@lists.osmocom.org/.

Jeff Long willcode4 at gmail.com
Sun Jan 14 13:51:44 UTC 2018


Latest version of diff.

On Fri, Jan 12, 2018 at 12:48 PM, Jeff Long <willcode4 at gmail.com> 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: <http://lists.osmocom.org/pipermail/osmocom-sdr/attachments/20180114/b3559779/attachment.htm>
-------------- 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 <support at sdrplay.com>
- * Copyright 2012 Dimitri Stolnikov <horiz0n at gmx.net>
- * Copyright 2012 Steve Markgraf <steve at steve-m.de>
+ * Copyright 2018 Jeff Long <willcode4 at gmail.com>
  *
  * 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 <gnuradio/io_signature.h>
 #include "osmosdr/source.h"
+#include "arg_helpers.h"
 
 #include <boost/assign.hpp>
 #include <boost/format.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition_variable.hpp>
+#include <boost/chrono.hpp>
 
-#include <stdexcept>
 #include <iostream>
-#include <stdio.h>
-#include <math.h>
-
-#include <mirsdrapi-rsp.h>
-
-#include "arg_helpers.h"
+#include <mutex>
 
 #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<double> 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<unsigned int>(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<int>( 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<std::string> 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<std::string> 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<std::string> sdrplay_source_c::get_devices()
 {
-   osmosdr::freq_range_t range;
+  unsigned int numDevices;
+  mir_sdr_DeviceT mirDevices[MAX_SUPPORTED_DEVICES];
+  std::vector<std::string> 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; i<numDevices; i++) {
+    mir_sdr_DeviceT *dev = &mirDevices[i];
+    if (!dev->devAvail)
+      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<std::string> 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<std::string> sdrplay_source_c::get_gain_names(size_t chan)
 {
-   return _auto_gain;
+  std::vector<std::string> 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<std::string> 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<std::string> 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<double> &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<double> &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<bandwidths.size(); i++)
+    range += osmosdr::range_t(bandwidths[i]);
 
-   return range;
+  return range;
 }
diff --git a/lib/sdrplay/sdrplay_source_c.h b/lib/sdrplay/sdrplay_source_c.h
index 2e4631e..056a748 100644
--- a/lib/sdrplay/sdrplay_source_c.h
+++ b/lib/sdrplay/sdrplay_source_c.h
@@ -1,5 +1,6 @@
 /* -*- c++ -*- */
 /*
+ * Copyright 2018 Jeff Long <willcode4 at gmail.com>
  * Copyright 2015 SDRplay Ltd <support at sdrplay.com>
  * Copyright 2012 Dimitri Stolnikov <horiz0n at gmx.net>
  *
@@ -22,17 +23,21 @@
 #define INCLUDED_SDRPLAY_SOURCE_C_H
 
 #include <gnuradio/sync_block.h>
-
 #include <gnuradio/thread/thread.h>
-#include <boost/thread/mutex.hpp>
-#include <boost/thread/condition_variable.hpp>
 
 #include "osmosdr/ranges.h"
 
 #include "source_iface.h"
 
+#include <mirsdrapi-rsp.h>
+
 class sdrplay_source_c;
-typedef struct sdrplay_dev sdrplay_dev_t;
+
+template <typename T>
+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 */


More information about the osmocom-sdr mailing list