rsp_dev.cc 17.7 KB
Newer Older
1 2 3 4
/* -*- c++ -*- */
/*
 * gr-sdrplay Copyright 2018 HB9FXQ, Frank Werner-Krippendorf.
 *
HB9FXQ's avatar
HB9FXQ committed
5
 * Credits for the rsp_dev class goes go to:
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
 * gr-osmosdr Copyright 2018 Jeff Long <[email protected]> of the gr-osmosdr-fork!
 *
 * This 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.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#include "rsp_dev.h"
#include <gnuradio/io_signature.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 <iostream>
#include <string>
#include <mutex>
#include <mirsdrapi-rsp.h>

38 39 40 41 42 43
namespace gr
{
namespace sdrplay
{

#define MAX_SUPPORTED_DEVICES 4
44 45 46 47 48

using namespace boost::assign;

// Index by mir_sdr_Bw_MHzT
static std::vector<double> bandwidths = {
49 50 51 52 53 54 55 56 57 58 59 60
    0, // Dummy
    200e3,
    300e3,
    600e3,
    1536e3,
    5000e3,
    6000e3,
    7000e3,
    8000e3};

static std::string hwName(int hwVer)
{
61 62 63 64 65 66 67 68 69 70 71
    if (hwVer == 1)
        return "RSP1";
    if (hwVer == 2)
        return "RSP2";
    if (hwVer == 255)
        return "RSP1A";
    if (hwVer == 3)
        return "RSPduo";
    return "UNK";
}

72 73 74
void rsp_dev::
    list_available_rsp_devices()
{
75 76 77 78 79 80 81

    unsigned int _devIndex = 0;
    unsigned int numDevices;

    mir_sdr_DeviceT mirDevices[MAX_SUPPORTED_DEVICES];
    mir_sdr_GetDevices(mirDevices, &numDevices, MAX_SUPPORTED_DEVICES);

82 83
    if (((_devIndex + 1 > numDevices) || !mirDevices[_devIndex].devAvail))
    {
84 85 86 87
        std::cerr << "Failed to open SDRplay device " << std::endl;
        throw std::runtime_error("Failed to open SDRplay device ");
    }

88 89
    for (int i = 0; i < numDevices; i++)
    {
90 91 92 93 94 95

        std::cerr << "RSP devIndex: [" << i << "] " << hwName(mirDevices[i].hwVer) << " " << mirDevices[i].SerNo
                  << "\r\n";
    }
}

96 97 98 99 100 101 102
rsp_dev::rsp_dev()
{
    _samplesPerPacket = -1;
    _hwVer = -1;
    _biasT = 0;
    _bufferOffset = 0;
    _bufferSpaceRemaining = 0;
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
    _auto_gain = true;
    _gRdB = 40;
    _lna = 0;
    _bcastNotch = 0;
    _dabNotch = 0;
    _fsHz = 2e6;
    _decim = 1;
    _rfHz = 100e6;
    _bwType = mir_sdr_BW_0_300;
    _ifType = mir_sdr_IF_Zero;
    _loMode = mir_sdr_LO_Auto;
    _dcMode = true;
    _iqMode = true;
    _buffer = NULL;
    _streaming = false;
    _reinit = false;
    _devIndex = 0;
    _debug = 0;
}

123 124 125 126
rsp_dev::~rsp_dev()
{
    if (_streaming)
    {
127 128 129 130 131
        stopStreaming();
    }
}

// Called by sdrplay streamer thread when data is available
132
void rsp_dev::streamCallback(short *xi, short *xq,
133 134
                                          unsigned int firstSampleNum,
                                          int grChanged, int rfChanged, int fsChanged,
135 136
                                          unsigned int numSamples, unsigned int reset)
{
137 138 139 140 141
    unsigned int i = 0;
    _reinit = false;

    boost::mutex::scoped_lock lock(_bufferMutex);

142 143 144 145
    while (i < numSamples)
    {
        if (!_streaming || _reinit)
        {
146 147 148
            return;
        }

149 150
        while (!_buffer)
        {
151 152 153 154 155
            if (boost::cv_status::timeout ==
                _bufferReady.wait_for(lock, boost::chrono::milliseconds(250)))
                return;
        }

156 157
        while ((i < numSamples) && (_bufferSpaceRemaining > 0))
        {
158
            _buffer[_bufferOffset] =
159
                gr_complex(float(xi[i]) / 32768.0, float(xq[i]) / 32768.0);
160 161 162 163 164
            i++;
            _bufferOffset++;
            _bufferSpaceRemaining--;
        }

165 166
        if (_bufferSpaceRemaining == 0)
        {
167 168 169 170 171 172 173
            _buffer = NULL;
            _bufferReady.notify_one();
        }
    }
}

// Callback wrapper
174
void rsp_dev::streamCallbackWrap(short *xi, short *xq,
175 176 177
                                              unsigned int firstSampleNum,
                                              int grChanged, int rfChanged, int fsChanged,
                                              unsigned int numSamples, unsigned int reset, unsigned int hwRemoved,
178 179 180
                                              void *cbContext)
{
    rsp_dev *obj = (rsp_dev *)cbContext;
181 182 183 184 185 186 187
    obj->streamCallback(xi, xq,
                        firstSampleNum,
                        grChanged, rfChanged, fsChanged,
                        numSamples, reset);
}

// Called by strplay streamer thread when gain reduction is changed.
188 189 190
void rsp_dev::gainChangeCallback(unsigned int gRdB,
                                              unsigned int lnaGRdB)
{
191 192 193
    mir_sdr_GainValuesT gainVals;
    mir_sdr_GetCurrentGain(&gainVals);

194 195
    if (gRdB < 200 && _debug)
    {
196 197 198
        std::cerr << "GR change, BB+MIX -" << gRdB << "dB, LNA -" << lnaGRdB << std::endl;
    }

199 200
    if (gRdB < mir_sdr_GAIN_MESSAGE_START_ID)
    {
201
        // gainVals.curr is a calibrated gain value
202 203 204
    }
    else if (gRdB == mir_sdr_ADC_OVERLOAD_DETECTED)
    {
205 206
        mir_sdr_GainChangeCallbackMessageReceived();
        // OVERLOAD DETECTED
207 208 209
    }
    else
    {
210 211 212 213 214 215
        mir_sdr_GainChangeCallbackMessageReceived();
        // OVERLOAD CORRECTED
    }
}

// Callback wrapper
216
void rsp_dev::gainChangeCallbackWrap(unsigned int gRdB,
217
                                                  unsigned int lnaGRdB,
218 219 220
                                                  void *cbContext)
{
    rsp_dev *obj = (rsp_dev *)cbContext;
221 222 223
    obj->gainChangeCallback(gRdB, lnaGRdB);
}

224 225 226 227
void rsp_dev::startStreaming(void)
{
    if (_streaming)
    {
228 229 230 231 232 233 234 235
        return;
    }

    unsigned int numDevices;
    mir_sdr_DeviceT mirDevices[MAX_SUPPORTED_DEVICES];
    mir_sdr_ReleaseDeviceIdx();
    mir_sdr_GetDevices(mirDevices, &numDevices, MAX_SUPPORTED_DEVICES);

236 237
    if (_deviceIndexOrSerial.length() > 2 /*It's a SerialNo*/)
    {
238 239 240

        bool match = false;

241 242 243 244
        for (int i = 0; i < numDevices; i++)
        {
            if (_deviceIndexOrSerial.compare(std::string(mirDevices[i].SerNo)) == 0)
            {
245 246 247 248 249 250 251
                std::cerr << "Found requested RSP with SerialNO: " << mirDevices[i].SerNo << "\r\n";
                _devIndex = i;
                match = true;
                break;
            }
        }

252 253
        if (!match)
        {
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
            std::cerr << "FALLBACK TO DEV INDEX = !!!! Could NOT find RSP SerialNO: " << _deviceIndexOrSerial << "\r\n";
        }
    }

    _hwVer = mirDevices[_devIndex].hwVer;
    mir_sdr_SetDeviceIdx(_devIndex);

    std::cerr << "Using SDRplay " << hwName(_hwVer) << " "
              << mirDevices[_devIndex].SerNo << std::endl;

    set_biasT(_biasT);

    // Set first LO frequency
    mir_sdr_SetLoMode(_loMode);

    _streaming = true;

    set_gain_mode(get_gain_mode());

    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);

    mir_sdr_DCoffsetIQimbalanceControl(_dcMode, _iqMode);

    // Model-specific initialization
295 296
    if (_hwVer == 2)
    {
297 298
        set_antenna(get_antenna());
        mir_sdr_RSPII_RfNotchEnable(_bcastNotch);
299 300 301
    }
    else if (_hwVer == 3)
    {
302 303 304 305 306 307
        set_antenna(get_antenna());
        if (_antenna == "HIGHZ")
            mir_sdr_rspDuo_Tuner1AmNotch(_bcastNotch);
        else
            mir_sdr_rspDuo_BroadcastNotch(_bcastNotch);
        mir_sdr_rspDuo_DabNotch(_dabNotch);
308 309 310
    }
    else if (_hwVer == 255)
    {
311 312 313 314 315
        mir_sdr_rsp1a_BroadcastNotch(_bcastNotch);
        mir_sdr_rsp1a_DabNotch(_dabNotch);
    }
}

316 317
void rsp_dev::stopStreaming(void)
{
318 319 320 321 322 323 324 325 326
    if (!_streaming)
        return;

    _streaming = false;

    mir_sdr_StreamUninit();
    mir_sdr_ReleaseDeviceIdx();
}

327 328
void rsp_dev::reinitDevice(int reason)
{
329
    // If no reason given, reinit everything
330
    if (reason == (int)mir_sdr_CHANGE_NONE)
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
        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;

    // 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,
357
                   (mir_sdr_ReasonForReinitT)reason);
358 359

    // Set decimation with halfband filter
360
    if (reason & (int)mir_sdr_CHANGE_FS_FREQ)
361 362 363 364 365
        mir_sdr_DecimateControl(_decim != 1, _decim, 1);

    _bufferReady.notify_one();
}

366 367
double rsp_dev::set_sample_rate(double rate)
{
368 369 370 371 372
    rate = std::min(std::max(rate, 62.5e3), 10e6);
    _fsHz = rate;

    // Decimation is required for rates below 2MS/s
    _decim = 1;
373 374
    while (_fsHz < 2e6)
    {
375 376 377 378 379
        _decim *= 2;
        _fsHz *= 2;
    }

    if (_streaming)
380
        reinitDevice((int)mir_sdr_CHANGE_FS_FREQ);
381 382 383 384

    return get_sample_rate();
}

385 386
double rsp_dev::get_sample_rate() const
{
387 388 389
    return _fsHz / _decim;
}

390 391
double rsp_dev::set_center_freq(double freq)
{
392 393
    _rfHz = freq;

394 395 396
    if (_streaming)
    {
        reinitDevice((int)mir_sdr_CHANGE_RF_FREQ);
397 398 399 400 401
    }

    return get_center_freq();
}

402 403
double rsp_dev::get_center_freq() const
{
404 405 406
    return _rfHz;
}

407 408
bool rsp_dev::set_gain_mode(bool automatic)
{
409
    _auto_gain = automatic;
410 411 412 413
    if (_streaming)
    {
        if (automatic)
        {
414 415
            mir_sdr_AgcControl(mir_sdr_AGC_5HZ /*TODO: expose argument */, -30 /*TODO: magic number */, 0, 0, 0, 0,
                               checkLNA(_lna));
416 417 418
        }
        else
        {
419 420 421
            mir_sdr_AgcControl(mir_sdr_AGC_DISABLE, -30 /*TODO: magic number */, 0, 0, 0, 0, checkLNA(_lna));
        }

422
        reinitDevice((int)mir_sdr_CHANGE_GR);
423 424 425 426 427
    }

    return _auto_gain;
}

428 429
bool rsp_dev::get_gain_mode() const
{
430 431 432
    return _auto_gain;
}

433 434
int rsp_dev::checkLNA(int lna)
{
435
    // Maaaagic - see gain tables in API doc
436 437
    if (_hwVer == 1)
    {
438
        lna = std::min(3, lna);
439 440 441
    }
    else if (_hwVer == 255)
    {
442 443 444 445 446 447
        if (_rfHz < 60000000)
            lna = std::min(6, lna);
        else if (_rfHz >= 1000000000)
            lna = std::min(8, lna);
        else
            lna = std::min(9, lna);
448 449 450
    }
    else if (_hwVer == 2)
    {
451 452 453 454 455 456
        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);
457 458 459
    }
    else if (_hwVer == 3)
    {
460 461 462 463 464 465 466 467 468 469 470 471 472
        if (_rfHz >= 1000000000)
            lna = std::min(8, lna);
        else if (_rfHz < 60000000 && _antenna == "HIGHZ")
            lna = std::min(4, lna);
        else if (_rfHz < 60000000)
            lna = std::min(6, lna);
        else
            lna = std::min(9, lna);
    }

    return lna;
}

473 474
double rsp_dev::set_gain(double gain)
{
475 476 477 478
    set_gain(gain, "IF_ATTEN_DB");
    return get_gain("IF_ATTEN_DB");
}

479 480
double rsp_dev::set_gain(double gain, const std::string &name)
{
481 482 483 484
    bool bcastNotchChanged = false;
    bool dabNotchChanged = false;
    bool gainChanged = false;

485 486 487
    if (name == "LNA_ATTEN_STEP")
    {
        if (gain != _lna) //RSP1 will only send bool / 0||1
488 489
            gainChanged = true;
        _lna = int(gain);
490 491 492
    }
    else if (name == "IF_ATTEN_DB")
    {
493
        // Ignore out-of-bounds values, since caller knows limits. (GQRX spurious calls).
494 495
        if (gain >= 20.0 && gain <= 59.0 && gain != _gRdB)
        {
496 497 498 499
            gainChanged = true;
            _gRdB = int(gain);
        }
    }
500 501 502
    // RSP1A, RSP2
    else if (name == "BCAST_NOTCH" && (_hwVer == 2 || _hwVer == 3 || _hwVer == 255))
    {
503 504 505 506
        if (int(gain) != _bcastNotch)
            bcastNotchChanged = true;
        _bcastNotch = int(gain);
    }
507 508 509
    // RSP1A
    else if (name == "DAB_NOTCH" && (_hwVer == 3 || _hwVer == 255))
    {
510 511 512 513 514
        if (int(gain) != _dabNotch)
            dabNotchChanged = true;
        _dabNotch = int(gain);
    }

515 516
    if (_streaming)
    {
517 518 519
        if (gainChanged)
            mir_sdr_RSP_SetGr(_gRdB, checkLNA(_lna), 1 /*absolute*/, 0 /*immediate*/);

520 521 522 523
        if (bcastNotchChanged)
        {
            if (_hwVer == 255)
            {
524
                mir_sdr_rsp1a_BroadcastNotch(_bcastNotch);
525 526 527
            }
            else if (_hwVer == 2)
            {
528
                mir_sdr_RSPII_RfNotchEnable(_bcastNotch);
529 530 531
            }
            else if (_hwVer == 3)
            {
532 533 534 535 536 537 538
                if (_antenna == "HIGHZ")
                    mir_sdr_rspDuo_Tuner1AmNotch(_bcastNotch);
                else
                    mir_sdr_rspDuo_BroadcastNotch(_bcastNotch);
            }
        }

539 540 541 542
        if (dabNotchChanged)
        {
            if (_hwVer == 255)
            {
543
                mir_sdr_rsp1a_DabNotch(_dabNotch);
544 545 546
            }
            else if (_hwVer == 3)
            {
547 548 549 550 551
                mir_sdr_rspDuo_DabNotch(_dabNotch);
            }
        }

        if (_streaming)
552
            reinitDevice((int)mir_sdr_CHANGE_GR);
553 554 555 556 557
    }

    return get_gain();
}

558 559
double rsp_dev::get_gain() const
{
560 561 562
    return get_gain("IF_ATTEN_DB");
}

563 564
double rsp_dev::get_gain(const std::string &name) const
{
565 566 567 568 569 570 571 572 573 574 575 576
    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;
}

577 578
std::string rsp_dev::set_antenna(const std::string &antenna)
{
579 580
    _antenna = antenna;

581 582 583 584
    if (_streaming)
    {
        if (_hwVer == 2)
        {
585
            // HIGHZ is ANTENNA_B with AmPortSelect
586 587
            if (antenna == "HIGHZ")
            {
588 589
                mir_sdr_RSPII_AntennaControl(mir_sdr_RSPII_ANTENNA_B);
                mir_sdr_AmPortSelect(1);
590 591 592
            }
            else
            {
593 594 595 596 597 598 599
                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);
            }

600 601 602 603 604 605
            reinitDevice((int)mir_sdr_CHANGE_AM_PORT);
        }
        else if (_hwVer == 3)
        {
            if (antenna == "HIGHZ")
            {
606 607
                mir_sdr_rspDuo_TunerSel(mir_sdr_rspDuo_Tuner_1);
                mir_sdr_AmPortSelect(1);
608 609 610
            }
            else if (antenna == "T1_50ohm")
            {
611 612
                mir_sdr_rspDuo_TunerSel(mir_sdr_rspDuo_Tuner_1);
                mir_sdr_AmPortSelect(0);
613 614 615
            }
            else
            {
616 617 618 619
                mir_sdr_rspDuo_TunerSel(mir_sdr_rspDuo_Tuner_2);
                mir_sdr_AmPortSelect(0);
            }

620
            reinitDevice((int)mir_sdr_CHANGE_AM_PORT);
621 622 623 624 625
        }
    }
    return antenna;
}

626 627 628
std::string rsp_dev::get_antenna() const
{
    return _antenna;
629 630
}

631 632
void rsp_dev::set_dc_offset_mode(int mode)
{
633 634 635 636
    _dcMode = mode == 1;
    mir_sdr_DCoffsetIQimbalanceControl(_dcMode, _iqMode);
}

637 638
void rsp_dev::set_iq_balance_mode(int mode)
{
639 640 641 642
    _iqMode = mode == 1;
    mir_sdr_DCoffsetIQimbalanceControl(_dcMode, _iqMode);
}

643 644
void rsp_dev::set_debug_mode(int mode)
{
645 646 647 648
    _debug = mode == 1;
    mir_sdr_DebugEnable(_debug);
}

649 650
double rsp_dev::set_bandwidth(double bandwidth)
{
651 652
    _bwType = mir_sdr_BW_8_000;

653 654
    for (double bw : bandwidths)
    {
655 656
        if (bw == 0)
            continue;
657 658
        if (bandwidth <= bw)
        {
659 660 661 662 663 664 665 666 667
            _bwType = (mir_sdr_Bw_MHzT)(bw / 1e3);
            break;
        }
    }

    int actual = get_bandwidth();
    std::cerr << "SDRplay bandwidth requested=" << bandwidth
              << " actual=" << actual << std::endl;

668 669 670
    if (_streaming)
    {
        reinitDevice((int)mir_sdr_CHANGE_BW_TYPE);
671 672 673 674 675
    }

    return actual;
}

676 677 678
double rsp_dev::get_bandwidth() const
{
    return (double)_bwType * 1e3;
679 680
}

681 682
int rsp_dev::fetch_work_buffer(gr_complex *grWorkBuffer, int noutput_items)
{
683 684 685 686 687 688 689 690 691 692
    if (!_streaming)
        startStreaming();

    {
        boost::mutex::scoped_lock lock(_bufferMutex);
        _buffer = grWorkBuffer;
        _bufferSpaceRemaining = noutput_items;
        _bufferOffset = 0;
        _bufferReady.notify_one();

693 694
        while (_buffer && _streaming)
        {
695 696 697 698
            _bufferReady.wait(lock);
        }
    }

699 700
    if (_streaming)
    {
701 702 703 704
        return 0;
    }
}

705 706 707
void rsp_dev::set_if_type(int ifType)
{
    _ifType = (mir_sdr_If_kHzT)ifType;
708

709 710 711
    if (_streaming)
    {
        reinitDevice((int)mir_sdr_CHANGE_IF_TYPE);
712 713 714
    }
}

715 716 717
void rsp_dev::set_lo_mode(int lo_mode)
{
    _loMode = (mir_sdr_LoModeT)lo_mode;
718

719 720 721
    if (_streaming)
    {
        reinitDevice((int)mir_sdr_CHANGE_LO_MODE);
722 723 724
    }
}

725 726
void rsp_dev::set_biasT(bool biasT)
{
727 728 729 730 731 732 733 734
    if (_hwVer == 2)
        mir_sdr_RSPII_BiasTControl(biasT);
    else if (_hwVer == 3)
        mir_sdr_rspDuo_BiasT(biasT);
    else if (_hwVer == 255)
        mir_sdr_rsp1a_BiasT(biasT);
}

735 736
void rsp_dev::set_deviceIndexOrSerial(const std::string &deviceIndexOrSerial)
{
737
    _deviceIndexOrSerial = deviceIndexOrSerial;
738 739 740 741
}

} // namespace sdrplay
} // namespace gr