Skip to content

SoapySDR::Device::setGain has undesired behavior for SDRPlay #60

@dlaw

Description

@dlaw

SoapySDR assumes that gains are additive in dB. When Device::setGain is called without a name, it automatically distributes the requested gain between all available gain stages:
https://github.com/pothosware/SoapySDR/blob/master/lib/Device.cpp#L281

Unfortunately, SDRPlay gains are unconventional. There are two gains exposed via SoapySDR, called "RFGR" and "IFGR".

  1. "IFGR" stands for "intermediate frequency gain reduction". It takes a value between 20 and 59. Contrary to what SoapySDR expects, the greatest amplification is obtained with the smallest numerical value. The IFGR has units of dB.
  2. "RFGR" stands for "radio frequency gain reduction". It takes a value between 0 and 9. As with the IFGR, the greatest amplification is obtained with the smallest value. In addition, the RFGR does not have units of dB! The actual gain reduction is nonlinear and frequency-dependent, but tends to be about 7 dB per count.
    (a) Note that RFGR is also settable via the "rxgain_sel" menu item.
    (b) RFGR attenuation tables in dB are available in the SDRPlay API documentation, section 5.3.
    (c) Most SDRPlay technical documents refer to the RFGR as the LNA state. I am not sure which terminology is preferred.

When we put these unconventional gain definitions together with SoapySDR's default gain distribution algorithm, disaster follows. Nearly any call to
void SoapySDR::Device::setGain(const int, const size_t, const double)
will force the RFGR to 9 (more than 60 dB of attenuation!) before allocating the remainder of the requested gain to the IFGR. Now you might argue that SDRPlay users should always set their gains by name -- but unfortunately, certain unenlightened applications do not expose the individual named gains, and there may be no way to avoid a call to the automatic gain distribution algorithm.

Proposed solution 1: quick and easy

  • Override the default SoapySDR implementation of SoapySDR::Device::setGain(const int, const size_t, const double). Rather than attempting to distribute gain between stages, the requested gain will be assigned directly to the IFGR and the RFGR will not be changed.
  • For consistency, also override SoapySDR::Device::getGainRange(const int dir, const size_t channel) to exclude the RFGR.
  • SDRPlay's automatic gain control works in much the same way -- it varies the IFGR but does not touch the RFGR.

Proposed solution 2: fix the abstraction, break some existing configurations

  • Remove RFGR from the list of available gains. It will remain available through the "rfgain_sel" menu item (which perhaps should be renamed to "LNA_state").
  • Invert the sign of IFGR in SoapySDR, so that it spans from -20 to -59 rather than +20 to +59. This will match the SoapySDR sign convention of a bigger number representing more amplification, while preserving a clear correspondence with the numerical IFGR values present in SDRPlay documentation.
  • With these abstractions fixed, setGain and getGainRange will now do the right thing without any further changes.

I am very new to SoapySDR (and SDR in general, for that matter). I would be happy to send in a pull request, but first I'd appreciate some feedback from the maintainers regarding the preferred approach.

Huge thanks to SDRPlay support for helping me understand what is going on in the hardware... responding in 15 minutes at midnight on Saturday!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions