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.comLatest 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 <willcode4 at gmail.com> wrote: > 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/20180115/26d115c3/attachment.htm> -------------- 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 @@ +<?xml version="1.0"?> +<block> + <name>SDRplay Source</name> + <key>sdrplay_source</key> + <category>Sources</category> + <import>import osmosdr</import> + <make>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) + </make> + <callback>set_sample_rate($sample_rate)</callback> + <callback>set_center_freq($freq, 0)</callback> + <callback>set_freq_corr($corr, 0)</callback> + <callback>set_dc_offset_mode($dc_offset_mode, 0)</callback> + <callback>set_iq_balance_mode($iq_balance_mode, 0)</callback> + <callback>set_gain_mode($gain_mode, 0)</callback> + <callback>set_gain($lna, "LNA_ATTEN_STEP", 0)</callback> + <callback>set_gain($grdb, "IF_ATTEN_DB", 0)</callback> + <callback>set_antenna($ant, 0)</callback> + <callback>set_bandwidth($bw, 0)</callback> + <param> + <name>Output Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>Complex float32</name> + <key>fc32</key> + <opt>type:fc32</opt> + </option> + </param> + <param> + <name>Device Arguments</name> + <key>args</key> + <value></value> + <type>string</type> + </param> + <param> + <name>Num Channels</name> + <key>nchan</key> + <value>1</value> + <type>int</type> + <option> + <name>1</name> + <key>1</key> + </option> + </param> + <param> + <name>Sample Rate (sps)</name> + <key>sample_rate</key> + <value>samp_rate</value> + <type>real</type> + </param> + + <param> + <name>Frequency (Hz)</name> + <key>freq</key> + <value>100e6</value> + <type>real</type> + </param> + <param> + <name>Freq. Corr. (ppm)</name> + <key>corr</key> + <value>0</value> + <type>real</type> + </param> + <param> + <name>DC Offset Mode</name> + <key>dc_offset_mode</key> + <value>0</value> + <type>int</type> + <option><name>Off</name><key>0</key></option> + <option><name>Automatic</name><key>2</key></option> + </param> + <param> + <name>IQ Balance Mode</name> + <key>iq_balance_mode</key> + <value>0</value> + <type>int</type> + <option><name>Off</name><key>0</key></option> + <option><name>Automatic</name><key>2</key></option> + </param> + <param> + <name>Gain Mode</name> + <key>gain_mode</key> + <value>False</value> + <type>bool</type> + <option><name>Manual</name><key>False</key></option> + <option><name>Automatic</name><key>True</key></option> + </param> + <param> + <name>LNA Atten Step</name> + <key>lna</key> + <value>0</value> + <type>real</type> + </param> + <param> + <name>IF Atten (dB)</name> + <key>grdb</key> + <value>40</value> + <type>real</type> + </param> + <param> + <name>Antenna</name> + <key>ant</key> + <value></value> + <type>string</type> + <option><name>Default</name><key></key></option> + <option><name>RX</name><key>RX</key></option> + <option><name>A</name><key>A</key></option> + <option><name>B</name><key>B</key></option> + <option><name>High Z</name><key>HIGHZ</key></option> + </param> + <param> + <name>Bandwidth (Hz)</name> + <key>bw</key> + <value>1536e3</value> + <type>real</type> + </param> + <param> + <name>Bias Voltage</name> + <key>bias</key> + <value>0</value> + <type>int</type> + <option><name>Off</name><key>0</key></option> + <option><name>On</name><key>1</key></option> + </param> + <param> + <name>AM LO Freq</name> + <key>loFreq</key> + <value>0</value> + <type>int</type> + <option><name>Auto</name><key>0</key></option> + <option><name>120 MHz</name><key>120</key></option> + <option><name>144 MHz</name><key>144</key></option> + <option><name>168 MHz</name><key>168</key></option> + </param> + <source> + <name>out</name> + <type>$type.type</type> + <nports>1</nports> + </source> +</block> 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 <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,738 @@ #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), + _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<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"; + } + + // bias=[0|1] to turn [off|on] bias tee. Default is off. + _biasT = 0; + if (dict.count("bias")) { + _biasT = boost::lexical_cast<int>(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<int>(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<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]; - 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<std::string> sdrplay_source_c::get_devices() { - if (_running) - { - return _dev->rfHz; - } + unsigned int numDevices; + mir_sdr_DeviceT mirDevices[MAX_SUPPORTED_DEVICES]; + std::vector<std::string> devices; -// return 0; - return _dev->rfHz; + mir_sdr_GetDevices(mirDevices, &numDevices, MAX_SUPPORTED_DEVICES); + + 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_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<std::string> 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<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, 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<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; } -void sdrplay_source_c::set_dc_offset( const std::complex<double> &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<double> &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<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..7022af4 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 ); @@ -112,25 +120,58 @@ public: void set_dc_offset_mode( int mode, size_t chan = 0 ); void set_dc_offset( const std::complex<double> &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 */