Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2 Mhz samplerate + Bandwidth auto mismatch #84

Open
nmaster2042 opened this issue Mar 30, 2024 · 13 comments
Open

2 Mhz samplerate + Bandwidth auto mismatch #84

nmaster2042 opened this issue Mar 30, 2024 · 13 comments
Assignees

Comments

@nmaster2042
Copy link

Hi.

Usig SDR++ with Soapy source I found there is a mismatch when selecting 2Mhz samplerate while letting bandwidth as Auto.

In logs on console we can read:

[31/03/2024 00:22:18.000] [INFO] Bandwidth for samplerate 2000000.000000 is 5000000.000000

But I think it should be 1.54 Mhz, isn't it ?

To have good waterfall I must select manually BW of 1.54 Mhz.

If I chose sample rate of 2.05 Mhz (actually 2048 khz) while letting bandwidth as Auto, things are going well, we have in logs:

[31/03/2024 00:22:36.000] [INFO] Bandwidth for samplerate 2048000.000000 is 5000000.000000

This seems to be the correct combination in this case.

I used a RSP2, the last SDRPlay 3.14 API and the last SoapySDRPlay driver.

@fventuri
Copy link
Collaborator

@nmaster2042 - that's very odd.

If you don't mind, please add this line immediately after line 995 in Settings.cpp (https://github.com/pothosware/SoapySDRPlay3/blob/master/Settings.cpp#L995):

    SoapySDR_logf(SOAPY_SDR_INFO, "bw_in=%lf getSampleRate()=%lf bwType=%d", bw_in, getSampleRate(direction, channel), bwType);

After that, please rebuild and reinstall this SoapySDRPlay3 module.

Then run SDR++ again with the same configuration that is giving you this problem; it should print one or more debug lines in the terminal with the information about requested bandwidth and selected bandwidth.

Please post them here, so we can try to figure out what's going on.

Franco

@fventuri fventuri self-assigned this Mar 31, 2024
@nmaster2042
Copy link
Author

nmaster2042 commented Mar 31, 2024

@fventuri

I added the log line you provided.

Here is the printed log on the console when selecting 2 Mhz samplerate and Auto for Bandwidth:

[INFO] bw_in=5000000.000000 getSampleRate()=2000000.000000 bwType=5000

If I chose 2.05Mhz samplerate, no issue, and the log on the console is like this:

[INFO] bw_in=5000000.000000 getSampleRate()=2000000.000000 bwType=5000

It is coherent with previous logs coming from SDR++ soapy_source.

My question is that I think when 2 Mhz samplerate is selected, and badwidth is set to auto, it should be 1.54Mhz, not 5 Mhz. Shouldn't it be ?

I hope my tests can help.

@fventuri
Copy link
Collaborator

@nmaster2042 - thanks for your logs.

From what I see there, it looks like SDR++ is calling the setBandwidth() function with the argument bw_in set to 5MHz in both cases, when the sample rates (2MHz or 2.05MHz) are significantly lower than the requested bandwidth.

I don't know how SDR++ comes up with that value of the requested bandwidth (5MHz), but I don't think it is a good value because of the aliasing problem with a sample rate of just 2MHz.

In the GNU Radio OOT module I wrote for the SDRplay RSPs (gr-sdrplay3), I put a warning for this scenario: https://github.com/fventuri/gr-sdrplay3/blob/main/lib/rsp_impl.cc#L238 - perhaps I should addd something similar here.

Franco

@nmaster2042
Copy link
Author

nmaster2042 commented Mar 31, 2024

@fventuri I don't know how the value of 5 Mhz is selected.

The soapy_source is not specific to sdrplay.

Here is the code in the source module about BW and SR.

If my understanding is good, if Auto is selected for BW, it passes 0 as value.

txtBwList = "";
        bandwidthList.clear();
        bandwidthList.push_back(-1);
        txtBwList += "Auto";
        txtBwList += '\0';

        for (auto bwr : bandwidthRange) {
            float bw = bwr.minimum();
            bandwidthList.push_back(bw);
            if (bw > 1.0e3 && bw <= 1.0e6) {
                txtBwList += to_string_with_precision((bw / 1.0e3), 2) + " kHz";
            }
            else if (bw > 1.0e6) {
                txtBwList += to_string_with_precision((bw / 1.0e6), 2) + " MHz";
            }
            else {
                txtBwList += to_string_with_precision(bw, 0);
            }
            txtBwList += '\0';
        }

        sampleRates = dev->listSampleRates(SOAPY_SDR_RX, channelId);
        txtSrList = "";
        for (double sr : sampleRates) {
            if (sr > 1.0e3 && sr <= 1.0e6) {
                txtSrList += to_string_with_precision((sr / 1.0e3), 2) + " kHz";
            }
            else if (sr > 1.0e6) {
                txtSrList += to_string_with_precision((sr / 1.0e6), 2) + " MHz";
            }
            else {
                txtSrList += to_string_with_precision(sr, 0);
            }
            txtSrList += '\0';
        }

@nmaster2042
Copy link
Author

nmaster2042 commented Mar 31, 2024

I made a try with GQRX.

I select Input rate = 2000000 and bandwidth = 0,000000 MHz (to let driver select the matching BW).

Here is the log from the line you asked me to insert

[INFO] bw_in=1500000,000000 getSampleRate()=2000000,000000 bwType=600

[INFO] bw_in=0,000000 getSampleRate()=2000000,000000 bwType=1536

There is 2 calls but the second one give the good values and good work in gqrx.

@fventuri
Copy link
Collaborator

@nmaster2042 - thanks for your research work on SDR++ and gqrx.

In the source code for SDR++ can you find the call to dev->setBandwith(), and add a print right before that call to see what arguments are passed?
After that we can try to figure out where that value of 5MHz comes from.

Franco

@nmaster2042
Copy link
Author

@fventuri I had to add 2 points because there is 2 case with dev->setBandwith(), see below

if (_this->bandwidthList.size() > 2) {
            if (_this->bandwidthList[_this->uiBandwidthId] == -1) {
                _this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->selectBwBySr(_this->sampleRates[_this->srId]));
                printf("%s%f\n ", "Set bandwidth IF: ", _this->selectBwBySr(_this->sampleRates[_this->srId]));
            }
            else {
                _this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->bandwidthList[_this->uiBandwidthId]);
                printf("%s%f\n ", "Set bandwidth ELSE: ", _this->bandwidthList[_this->uiBandwidthId]);
            }
        }

Now go back to the tests.

  1. SR 2Mhz, BW: auto
[31/03/2024 19:55:48.000] [INFO] Bandwidth for samplerate 2000000.000000 is 5000000.000000
[INFO] bw_in=5000000.000000 getSampleRate()=2000000.000000 bwType=5000
[31/03/2024 19:55:48.000] [INFO] Bandwidth for samplerate 2000000.000000 is 5000000.000000
Set bandwidth IF: 5000000.000000

Last line is my printf, the 2 lines with date is log from soapy_source and the one begining by [INFO] is the log you asked me to add in the soapySDRPlay driver.

  1. SR 2 Mhz, BW manually select of the value of 1.54 Mhz (real 1536 khz)
[INFO] bw_in=1536000.000000 getSampleRate()=2000000.000000 bwType=1536
Set bandwidth ELSE: 1536000.000000

First line is log from SSoapySDRPlay driver
second one is my second printf (the else part).

I don't understand where the 5 Mhz BW is coming from.

Moreover there is no issue with other soapy devices.

Hopes this can help.

@fventuri
Copy link
Collaborator

@nmaster2042 - thanks for your diligent work and your help debugging this issue.

Looking at that code from SDR++, I have the impression that the problem might be in the SDR++ function selectBwBySr(). That function name seems to indicate that it selects the bandwidth (Bw) given the sample rate (Sr).

At this point I think you may want to create an issue in the SDR++ repository about that function.
Before you do that, I would also add a print of the value of _this->sampleRates[_this->srId] to make sure it is 2MHz:

    printf("%s%f (SR=%f)\n ", "Set bandwidth IF: ", _this->selectBwBySr(_this->sampleRates[_this->srId]), _this->sampleRates[_this->srId]);

Franco

@nmaster2042
Copy link
Author

@fventuri I added a point before _this->sampleRates

Now the code looks like this:

printf("%s%f (SR=%f)\n ", "Set SampleRate: ", _this->selectBwBySr(_this->sampleRates[_this->srId]), _this->sampleRates[_this->srId]);
        _this->dev->setSampleRate(SOAPY_SDR_RX, _this->channelId, _this->sampleRate);

        _this->dev->setAntenna(SOAPY_SDR_RX, _this->channelId, _this->antennaList[_this->uiAntennaId]);

        if (_this->bandwidthList.size() > 2) {
            if (_this->bandwidthList[_this->uiBandwidthId] == -1) {
                printf("%s%f\n ", "Set bandwidth IF: ", _this->selectBwBySr(_this->sampleRates[_this->srId]));
                _this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->selectBwBySr(_this->sampleRates[_this->srId]));
            }
            else {
                printf("%s%f\n ", "Set bandwidth ELSE: ", _this->bandwidthList[_this->uiBandwidthId]);
                _this->dev->setBandwidth(SOAPY_SDR_RX, _this->channelId, _this->bandwidthList[_this->uiBandwidthId]);
            }
        }

Now, starting SDR++, on console I see:

Set SampleRate: 5000000.000000 (SR=2000000.000000)
 [01/04/2024 16:06:04.000] [INFO] Bandwidth for samplerate 2000000.000000 is 5000000.000000
Set bandwidth IF: 5000000.000000
 [01/04/2024 16:06:04.000] [INFO] Bandwidth for samplerate 2000000.000000 is 5000000.000000

The first log is the point I just added before setting SR.
Second and fourth is original soapy_source logs
Third one is the point I added yesterdeay befor BW setting

It's the IF part where it passes when Auto is selected for bandwidth.

@nmaster2042
Copy link
Author

Now same test but selecting SR = 1 Mhz, BW = auto

[01/04/2024 16:14:24.000] [INFO] Bandwidth for samplerate 1000000.000000 is 1536000.000000
Set SampleRate: 1536000.000000 (SR=1000000.000000)
 [01/04/2024 16:14:24.000] [INFO] Bandwidth for samplerate 1000000.000000 is 1536000.000000
Set bandwidth IF: 1536000.000000
 [01/04/2024 16:14:24.000] [INFO] Bandwidth for samplerate 1000000.000000 is 1536000.000000
[INFO] bw_in=1536000.000000 getSampleRate()=1000000.000000 bwType=1536

And this working fine.

@nmaster2042
Copy link
Author

More exhaustive tests.

SR 62.5 khz, BW = auto,

[01/04/2024 16:26:32.000] [INFO] Bandwidth for samplerate 62500.000000 is 200000.000000
Set SampleRate: 200000.000000 (SR=62500.000000)
 [01/04/2024 16:26:32.000] [INFO] Bandwidth for samplerate 62500.000000 is 200000.000000
Set bandwidth IF: 200000.000000
 [01/04/2024 16:26:32.000] [INFO] Bandwidth for samplerate 62500.000000 is 200000.000000
[INFO] bw_in=200000.000000 getSampleRate()=62500.000000 bwType=200

SR 96 khz, BW = auto,

[01/04/2024 16:27:53.000] [INFO] Bandwidth for samplerate 96000.000000 is 200000.000000
Set SampleRate: 200000.000000 (SR=96000.000000)
 [01/04/2024 16:27:53.000] [INFO] Bandwidth for samplerate 96000.000000 is 200000.000000
Set bandwidth IF: 200000.000000
 [01/04/2024 16:27:53.000] [INFO] Bandwidth for samplerate 96000.000000 is 200000.000000
[INFO] bw_in=200000.000000 getSampleRate()=96000.000000 bwType=200

SR 125 khz, BW = auto,

[01/04/2024 16:29:00.000] [INFO] Bandwidth for samplerate 125000.000000 is 200000.000000
Set SampleRate: 200000.000000 (SR=125000.000000)
 [01/04/2024 16:29:00.000] [INFO] Bandwidth for samplerate 125000.000000 is 200000.000000
Set bandwidth IF: 200000.000000
 [01/04/2024 16:29:00.000] [INFO] Bandwidth for samplerate 125000.000000 is 200000.000000
[INFO] bw_in=200000.000000 getSampleRate()=125000.000000 bwType=200

SR 192 khz, BW = auto,

[01/04/2024 16:30:01.000] [INFO] Bandwidth for samplerate 192000.000000 is 200000.000000
Set SampleRate: 200000.000000 (SR=192000.000000)
 [01/04/2024 16:30:01.000] [INFO] Bandwidth for samplerate 192000.000000 is 200000.000000
Set bandwidth IF: 200000.000000
 [01/04/2024 16:30:01.000] [INFO] Bandwidth for samplerate 192000.000000 is 200000.000000
[INFO] bw_in=200000.000000 getSampleRate()=192000.000000 bwType=200

SR 250 khz, BW = auto,

[01/04/2024 16:31:01.000] [INFO] Bandwidth for samplerate 250000.000000 is 300000.000000
Set SampleRate: 300000.000000 (SR=250000.000000)
 [01/04/2024 16:31:01.000] [INFO] Bandwidth for samplerate 250000.000000 is 300000.000000
Set bandwidth IF: 300000.000000
 [01/04/2024 16:31:01.000] [INFO] Bandwidth for samplerate 250000.000000 is 300000.000000
[INFO] bw_in=300000.000000 getSampleRate()=250000.000000 bwType=300

SR 384 khz, BW = auto

Set SampleRate: 600000.000000 (SR=384000.000000)
 [01/04/2024 16:32:04.000] [INFO] Bandwidth for samplerate 384000.000000 is 600000.000000
Set bandwidth IF: 600000.000000
 [01/04/2024 16:32:04.000] [INFO] Bandwidth for samplerate 384000.000000 is 600000.000000
[INFO] bw_in=600000.000000 getSampleRate()=384000.000000 bwType=600

SR 500 khz, BW = auto

Set SampleRate: 600000.000000 (SR=500000.000000)
 [01/04/2024 16:33:18.000] [INFO] Bandwidth for samplerate 500000.000000 is 600000.000000
Set bandwidth IF: 600000.000000
 [01/04/2024 16:33:18.000] [INFO] Bandwidth for samplerate 500000.000000 is 600000.000000
[INFO] bw_in=600000.000000 getSampleRate()=500000.000000 bwType=600

SR 768 khz, BW = auto


[01/04/2024 16:34:31.000] [INFO] Bandwidth for samplerate 768000.000000 is 1536000.000000
Set SampleRate: 1536000.000000 (SR=768000.000000)
 [01/04/2024 16:34:31.000] [INFO] Bandwidth for samplerate 768000.000000 is 1536000.000000
Set bandwidth IF: 1536000.000000
 [01/04/2024 16:34:31.000] [INFO] Bandwidth for samplerate 768000.000000 is 1536000.000000
[INFO] bw_in=1536000.000000 getSampleRate()=768000.000000 bwType=1536

SR 1Mhz, BW = auto


[01/04/2024 16:35:42.000] [INFO] Bandwidth for samplerate 1000000.000000 is 1536000.000000
Set SampleRate: 1536000.000000 (SR=1000000.000000)
 [01/04/2024 16:35:42.000] [INFO] Bandwidth for samplerate 1000000.000000 is 1536000.000000
Set bandwidth IF: 1536000.000000
 [01/04/2024 16:35:42.000] [INFO] Bandwidth for samplerate 1000000.000000 is 1536000.000000
[INFO] bw_in=1536000.000000 getSampleRate()=1000000.000000 bwType=1536

SR 2Mhz, BW = auto, BAD

01/04/2024 16:36:25.000] [INFO] Bandwidth for samplerate 2000000.000000 is 5000000.000000
Set SampleRate: 5000000.000000 (SR=2000000.000000)
 [01/04/2024 16:36:25.000] [INFO] Bandwidth for samplerate 2000000.000000 is 5000000.000000
Set bandwidth IF: 5000000.000000
 [01/04/2024 16:36:25.000] [INFO] Bandwidth for samplerate 2000000.000000 is 5000000.000000
[INFO] bw_in=5000000.000000 getSampleRate()=2000000.000000 bwType=5000

SR 2.05Mhz, BW = auto,

[01/04/2024 16:38:12.000] [INFO] Bandwidth for samplerate 2048000.000000 is 5000000.000000
Set SampleRate: 5000000.000000 (SR=2048000.000000)
 [01/04/2024 16:38:12.000] [INFO] Bandwidth for samplerate 2048000.000000 is 5000000.000000
Set bandwidth IF: 5000000.000000
 [01/04/2024 16:38:12.000] [INFO] Bandwidth for samplerate 2048000.000000 is 5000000.000000
[INFO] bw_in=5000000.000000 getSampleRate()=2048000.000000 bwType=5000

SR 3Mhz, BW = auto,

Set SampleRate: 5000000.000000 (SR=3000000.000000)
 [01/04/2024 16:17:44.000] [INFO] Bandwidth for samplerate 3000000.000000 is 5000000.000000
Set bandwidth IF: 5000000.000000
 [01/04/2024 16:17:44.000] [INFO] Bandwidth for samplerate 3000000.000000 is 5000000.000000
[INFO] bw_in=5000000.000000 getSampleRate()=3000000.000000 bwType=5000

SR 4Mhz, BW = auto,

[01/04/2024 16:19:44.000] [INFO] Bandwidth for samplerate 4000000.000000 is 5000000.000000
Set SampleRate: 5000000.000000 (SR=4000000.000000)
 [01/04/2024 16:19:44.000] [INFO] Bandwidth for samplerate 4000000.000000 is 5000000.000000
Set bandwidth IF: 5000000.000000
 [01/04/2024 16:19:44.000] [INFO] Bandwidth for samplerate 4000000.000000 is 5000000.000000
[INFO] bw_in=5000000.000000 getSampleRate()=4000000.000000 bwType=5000

SR 5Mhz, BW = auto,

[01/04/2024 16:21:03.000] [INFO] Bandwidth for samplerate 5000000.000000 is 5000000.000000
Set SampleRate: 5000000.000000 (SR=5000000.000000)
 [01/04/2024 16:21:03.000] [INFO] Bandwidth for samplerate 5000000.000000 is 5000000.000000
Set bandwidth IF: 5000000.000000
 [01/04/2024 16:21:03.000] [INFO] Bandwidth for samplerate 5000000.000000 is 5000000.000000
[INFO] bw_in=5000000.000000 getSampleRate()=5000000.000000 bwType=5000

SR 6Mhz, BW = auto,

[01/04/2024 16:22:01.000] [INFO] Bandwidth for samplerate 6000000.000000 is 6000000.000000
Set SampleRate: 6000000.000000 (SR=6000000.000000)
 [01/04/2024 16:22:01.000] [INFO] Bandwidth for samplerate 6000000.000000 is 6000000.000000
Set bandwidth IF: 6000000.000000
 [01/04/2024 16:22:01.000] [INFO] Bandwidth for samplerate 6000000.000000 is 6000000.000000
[INFO] bw_in=6000000.000000 getSampleRate()=6000000.000000 bwType=6000

SR 7Mhz, BW = auto,

[01/04/2024 16:22:44.000] [INFO] Bandwidth for samplerate 7000000.000000 is 7000000.000000
Set SampleRate: 7000000.000000 (SR=7000000.000000)
 [01/04/2024 16:22:44.000] [INFO] Bandwidth for samplerate 7000000.000000 is 7000000.000000
Set bandwidth IF: 7000000.000000
 [01/04/2024 16:22:44.000] [INFO] Bandwidth for samplerate 7000000.000000 is 7000000.000000
[INFO] bw_in=7000000.000000 getSampleRate()=7000000.000000 bwType=7000

SR 8Mhz, BW = auto,

[01/04/2024 16:23:59.000] [INFO] Bandwidth for samplerate 8000000.000000 is 8000000.000000
Set SampleRate: 8000000.000000 (SR=8000000.000000)
 [01/04/2024 16:23:59.000] [INFO] Bandwidth for samplerate 8000000.000000 is 8000000.000000
Set bandwidth IF: 8000000.000000
 [01/04/2024 16:23:59.000] [INFO] Bandwidth for samplerate 8000000.000000 is 8000000.000000
[INFO] bw_in=8000000.000000 getSampleRate()=8000000.000000 bwType=8000

SR 9Mhz, BW = auto, BAD

[01/04/2024 16:24:56.000] [INFO] Bandwidth for samplerate 9000000.000000 is 200000.000000
Set SampleRate: 200000.000000 (SR=9000000.000000)
 [01/04/2024 16:24:56.000] [INFO] Bandwidth for samplerate 9000000.000000 is 200000.000000
Set bandwidth IF: 200000.000000
 [01/04/2024 16:24:56.000] [INFO] Bandwidth for samplerate 9000000.000000 is 200000.000000
[INFO] bw_in=200000.000000 getSampleRate()=9000000.000000 bwType=200

SR 10Mhz, BW = auto, BAD

[01/04/2024 16:26:02.000] [INFO] Bandwidth for samplerate 10000000.000000 is 200000.000000
Set SampleRate: 200000.000000 (SR=10000000.000000)
 [01/04/2024 16:26:02.000] [INFO] Bandwidth for samplerate 10000000.000000 is 200000.000000
Set bandwidth IF: 200000.000000
 [01/04/2024 16:26:02.000] [INFO] Bandwidth for samplerate 10000000.000000 is 200000.000000
[INFO] bw_in=200000.000000 getSampleRate()=10000000.000000 bwType=200

The real BW used by soapySDRPlay driver is the last line of each log block.

@fventuri
Copy link
Collaborator

fventuri commented Apr 2, 2024

@nmaster2042 - thanks for your extensive tests and the detailed logging.

It looks like the SDR++ method selectBwBySr() in most cases selects the next bandwidth above the sample rate (instead of below); for instance for a sample rate of 2MHz it selects a bandwidth of 5MHz, and for a sample rate of 1MHz it selects a bandwidth of 1.536MHz.
I think this choice of bandwidth could case problems with aliasing due to the Nyquist theorem.
Also in a couple of cases (SR=9MHz and SR=10MHz) I see it selects a bandwidth of just 200kHz, which is wrong, as you noticed in your comments.

If you haven't already created an issue with SDR++, perhaps it is time to take a look at the internals of that selectBwBySr() method to understand how it works and hopefully figure out why in most cases it selects the bandwidth above the sample rate instead of the one below the sample rate, as one would have expected.

One more thing: SDR++ also supports the SDRplay API natively (instead of using SoapySDR); I think using the native API is probably a better choice because it skips the SoapySDR middleman. I wonder if you already tried this approach instead of SoapySDR, and if you had the same problems with it.

Franco

@nmaster2042
Copy link
Author

@fventuri: There will be no issue opened on SDR++ repository because the author said long time ago he doesn't want to maintain this because he wrote support for a lot of SDRs.

He also noticed this make a lot of issues (conflict between soapy and native sources sometimes.

on the official repository, soapy_source has never be finished: string args was never implemented this was introduced in a fork. But it the author choice, and I totally respect this.

SDR++ Author is adding support for new devices given to him, but he can't have all SDRs arround, even if his SDR collection is already large.

In my opinion, soapy_source is sill an option to get support for other SDRs, even if ti's not the best one compared to native support.

Here is the last saopy_source development:

sannysanoff/SDRPlusPlusBrown#11
It was made by made by ericek111, alexander-sholohov, integrated in the sannysanoff's fork of SDR++.

Now on this fork, for soapy _source we have:

  • custum args
  • dynamic args depending of soapy devices (bias-tee, filters, etc)
  • try / catch when make the device to prevent SDR++ from crashing

That's why I still have interest in this source module even if it's no more an official one.
I have SDRs without native SDR++ support, and this is the only way to use it whth SDR++, my faforite SDR prog now ;-)

Ex: the new RSP1B I just bought.

I will make tests for the BW selection, the main issue will be not to break other soapy device where BW selection in auto mode has no issue.

I think this module would be worth to be maintaining in Out of tree module.
But if anytime SDR++ change source part, this will fail, or will need to make appropriate changes.

I hope this will be the case.

Thank you for your share, and advises about this issue I discovered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants