pothosware/SoapySDRPlay3

Bandwidth stuck at 200kHz

vk4jbe opened this issue · 22 comments

People,

I am wondering of someone can help me with my SoapySDR / SDRPlay3 / SDRPlay RSPdx install. I have successfully installed the SDRPlay 3.07 API, Ryan Volz's radioconda and compiled the pothosware SoapySDRPlay3 0.8 library on a Ubuntu 22.04 system. The SDR is receiving signals fine with both GQRX and GNURadio but the bandwidth seems stuck on 200kHz and the device seems to be stuck in AGC = on mode.

Looking at the pothosware/SoapySDRPlay3 github site I have checked out the SoapySDRPlay.hpp and settings.cpp files and it appears from the comments that the default bandwidth is 200khz (although I can't actually see where this is set in code). The code to change that is in internal functions that appear to be part of the Sample Rate API in the settings.cpp.

I have also noticed that the I get a "Not updating IFGR gain because AGC is enabled" warning when I try to change the IFGR in GQRX. Once again AGC = on seems to be the default. In GQRX the AGC flag is not ticked but the error still occurs.

I have been digging through the c++ source code to see if I can figure it out but unfortunately I am not familiar enough with c++ to debug this stuff.

I am wondering if anybody else has noticed this and has a fix or has enough familiarity with the code to track the issue through.

John - VK4JBE

John, let's start from the problem with the bandwidth.

In SoapySDRPlay3 the bandwidth is determined by the sample rate; this is to avoid problems with aliasing if you set the bandwidth wider than the sample rate (because of the Nyquist theorem).
If you look at the function getBwEnumForRate (https://github.com/pothosware/SoapySDRPlay3/blob/master/Settings.cpp#L996-L1006), you'll see that if the output sample rate (which is the sample rate divided by the decimation factor) is less than 300kHz, then the bandwidth is fixed at 200kHz.

I am not sure what value of sample rate and decimation you are using, but I would suggest to try a quick test with sample rate = 2MHz and decimation = 1 to see if the bandwidth gets set correctly at 1536kHz (instead of 200kHz).

Franco

John, it's about 10:30pm here in Florida, so I am about to go to bed now LOL

Anyway since you are saying the problem happens with GNU Radio too, do you mind attaching here the .grc file you used and the screenshot that shows that hump? This way I can try to reproduce it here with my RSPdx.
Also please run these tests with all the antennas disconnected from the RSPdx, so we can rule out any external cause (plus this way my results should be identical to yours).

As per the bandwidth when the sample rate is set to 2MHz, you are 100% correct: it should be 2MHz, not 1536kHz.

Franco K4VZ

Excellent detective work John!

I read your comment and looked at the code, and I think you're right that the function setBandwidth() should be using the failsafe getBwEnumForRate() instead of sdrPlayGetBwMhzEnum().

Unfortunately I have to start work in a few minutes, so perhaps in the meanwhile you could change just that line in Settings.cpp, rebuild SoapySDRPlay3, and run GNU Radio (and/or gqrx) again to see if the problem goes away.

Tonight after work I'll try the same thing (before and after the change), and if everything looks OK, I'll commit this change in the code.

Franco K4VZ

@vk4jbe - thanks for making the change and confirming that it solves the problem.

Tonight I too ran your GNU Radio flowgraph with the original code from the master branch, and I saw the issue with the IF bandwidth stuck at 200kHz. I then changed the code in the setBandwidth() function to use getBwEnumForRate(), and I confirmed that the bandwidth is now set to the right value (1536kHz for a sample rate of 2Msps).

I created a new branch called use-getBwEnumForRate (https://github.com/pothosware/SoapySDRPlay3/tree/use-getBwEnumForRate) with the new code; I changed just that one line and I removed the function sdrPlayGetBwMhzEnum() because it is no longer used.

When you have time, try building SoapySDRPlay3 using the code from the new branch use-getBwEnumForRate to make sure it work for you too; once we are all good with it, I'll merge it into the master branch.

Finally, if you are doing advanced work in GNU Radio using your RSPdx's, you may want to consider the 'gr-sdrplay3' OOT module that I wrote specifically for the SDRplay RSPs (https://github.com/fventuri/gr-sdrplay3) to use them as native GNU Radio sources.
The instructions to run 'gr-sdrplay3' on Windows using conda (or radioconda) are in this message I sent about a year ago to the GNU Radio mailing list: https://lists.gnu.org/archive/html/discuss-gnuradio/2022-03/msg00144.html - if you decide to try 'gr-sdrplay3' let me know if you run into any problems and I'll be happy to help you.

Franco K4VZ

Great information. I have some sdrplay equipment, but hadn’t noticed this before. Once it’s in main I’d like to build it into a deb package and test a deployment upgrade over apt.

John, thanks for your feedback and hard work!

Tonight I took a quick look at getBwEnumForRate, and I see it is called like this (https://github.com/pothosware/SoapySDRPlay3/blob/master/Settings.cpp#L739):

       sdrplay_api_Bw_MHzT bwType = getBwEnumForRate(output_sample_rate);

The variable output_sample_rate is the output sample rate from the RSP + SDRplay API going in to the client application (GNU Radio or gqrx in this case). This sample rate is after decimation, and therefore it can be < 200kHz (for instance a sample rate of 2MHz decimated by 16 becomes an output sample rate of 125kHz, and if it is decimated by 32 it becomes 62.5kHz), so these valid values of the sample rate would trigger the warning you suggested.

If I remember correctly the problem you observed with gqrx was that the application was attempting to set a bandwidth of 0Hz, which is definitely an invalid value. Perhaps we may want to consider to have a similar warning if the argument for getBwEnumForRate is 0, and return from that function without changing the current bandwidth.

I might not be able to work on this issue for the next couple of days due to my schedule, but Wednesday night I should be able to answer your comments and discuss this with you.

Franco K4VZ

John, this morning I ran gqrx (all my tests were done with GNU Radio so far), and I noticed that if you hover on the bandwidth selection box it says "Analog bandwidth (leave at 0 for default)". I then added a log statement in the setBandwidth() function, and I saw that gqrx calls it with the bw_in argument set to 0, which causes the 200kHz bandwidth problem.

Because of the special meaning that gqrx gives to the value 0, I thought that it might be a good idea to change the function setBandwidth() so that a value of 0 sets the bandwidth to the highest value compatible with the sample rate (for instance in the case of a sample rate of 2Msps it would set it to 1536kHz), which I think is a reasonable default.

I also refactored setBandwidth() so that now it uses just getBwEnumForRate() for everything (instead of getBwValueFromEnum() one way and getBwEnumForRate() the other), since I think it is more consistent with this approach.

I left temporarily that log statement in setBandwidth() that shows both the value of the bw_in argument and the bandwidth value that is actually set by the function (bwType), so you can run it with gqrx and see the calls it makes to setBandwidth().

In the next few days I also want to validate these changes with CubicSDR, to make sure they don't introduce problems or unexpected behavior there; CubicSDR is kind of how the whole SoapySDRPlay3 module started, and I use CubicSDR as the reference SDR client application for SoapySDRPlay3 (I also think there's a significant number of users running CubicSDR).

Please try the latest changes from the use-getBwEnumForRate branch when you have time, and let me know what you think.

Franco K4VZ

Franco….Interestingly enough I have done some similar things. I actually put log statements in various places in both setSampleRate() and setBandwidth() and I noticed some really weird stuff coming out of GQRX.

For some odd reason the setSampleRate() function in GQRX is always called twice; first with the default Sample Rate of 2Msps and secondly with the actual Sample Rate in the menu (unless it is already 2Msps). This correctly sets the bwEnum value using the function getBwEnumForRate().

The next thing I observed was that if the Bandwidth = 0.0MHz in the menu then setBandwidth() was called twice, first with a bw_in value = (SR-(SR/4)) which proceeds to set the wrong bwEnum value because of the -(SR/4) factor and secondly with the bw_in value = 0.0. This is not valid and so results in a 200kHz bandwidth. Very strange behaviour. The bw_in=(SR-(SR/4)) is obviously coming from GQRX. It is not observable in GNURadio.

If the Bandwidth value in the menu is a valid value then the setBandwidth() is only called once and the bwEnum is set correctly. If the Bandwidth is not a valid value then bwEnum is set to 200 (ie 200kHz).

I am not sure if you have noticed this behaviour.

GNURadio on the other hand is much simpler. It calls both setSampleRate() and setBandwidth() once and sets the value appropriatly. If the Bandwidth = 0.0Mhz it sets the bwEnum = 200 (ie 200kHz). You can change Sample Rates and Bandwidths on the fly here and it just calls the appropriate function once. I might add I am not sure if that is good practice, but it seems to work fine.

I have played with the Settings.cpp code to make it a bit more foolproof with the only area that is still not resolved in GQRX is the Bandwidth = 0.0MHz. It sets an incorrect value due to the odd calculation of the bw_in on the first pass.

I will have a look at your refactored code and run it up to see if it does what I expect.

I might also add I have never used CubicSDR, but have heard it mentioned plenty of times. I guess I am used to the tight integration between GQRX and GPredict for satellite work. I actually use SDR-Console on Windows for most of my satellite voice comms. That is a great program. I am trying to develop a setup for digital satellite work and experimentation using Linux…..hence getting the RSPdx working.

Thanks for your efforts on this.

John – VK4JBE

John, whe I ran gqrx this morning I too noticed the call to setBandwidth() with the bw_in argument = 75% of the sample rate:

[INFO] setBandwidth() - bw_in=1500000.000000 bwType=600
[INFO] setBandwidth() - bw_in=0.000000 bwType=1536

Since gqrx uses the GNU Radio OOT module gr-osmocom to talk to SoapySDR and therefore to this module SoapySDRplay3, I suspect that that 75% comes from this line (https://github.com/osmocom/gr-osmosdr/blob/master/lib/soapy/soapy_source_c.cc#L326):

        set_bandwidth(get_sample_rate() * 0.75, chan); /* select narrower filters to prevent aliasing */

Also since 0 as a bandwidth selection does not make much sense per-se (an ideal filter with a bandwidth = 0Hz wouldn't let any signal through), I think it is OK to accept that that specific value actually means "set the bandwidth to a reasonable default value", as gqrx seems to expect (but I need to double check that other programs like CubicSDR don't make different assumptions for that value).

Franco K4VZ

@franco I have tested your latest new branch and it is giving the bandwidth settings you would expect in both GNURadio and GQRX for all combinations of sample rate and bandwidth. Perfect. Thank you.

I had played with trying to use the existing bwEnum setting when the bandwidth = 0.0MHz (ie don’t update bwEnum) and it was fine in GNURadio but GQRX was still problematic. I had concluded that we needed to go and get the sample rate when the BW = 0.0MHz and substitute that. Your coding to do that is very neat. I was not familiar with that c construct previously …..but then I am not a c programmer.…… Actually I have learned a lot through this process.

Your SoapySDR_logf() statement on the setBandwidth() function shows GQRX calculating that odd bw_in = (SR-(SR/4)) and using it to calculate the wrong bwEnum, but the second call to setBandwidth() with the new algorithm fixes it. If you had a similar statement in setSampleRate() you would see the double setting there with a 2Msps sample rate first and then the correct sample rate.

I am not sure if leaving the log statement in will confuse GQRX users and cause questions or not. Perhaps it might be more instructive if it showed the Sample Rate and the Bandwidth. I am happy to see you merge that into the main/master branch.
One last thing, it may well be worth adding something into the wiki to explain the standard IF Bandwidth settings (and perhaps the preferred Sample Rate settings).

Thanks for all your work on this.

John – VK4JBE

Franco. That explains the weird bw_in setting. Makes sense in a certain context.

John - VK4JBE

John, I just removed that log statement; since I didn't know know you already had done something similar, I added it temporarily to show you what happens.

As per SoapySDRPlay3, since it is a general purpose module that can be used by many different SDR applications in the context of the SoapySDR framework, it is sometimes difficult for me to 'forecast' how an application will be using a method like setBandwidth(); the comments in the SoapySDR include file Device.hpp (https://github.com/pothosware/SoapySDR/blob/master/include/SoapySDR/Device.hpp) are more or less what I refer to as the 'standard' for what a given method should do, but they don't cover corner cases like this one.

On the other side, it is also hard if not impossible for a client application like gr-osmosdr to know the best thing to do; for instance the author of gr-osmosdr had the good intention of making the bandwidth a little less of the sample rate to prevent aliasing problems; they didn't know/realize that in the specific case of the SDRplay RSPs the selectable bandwidths are only a limited set of values, and in the case of a sample rate of 2Msps, 75% of that is 1500kHz, and the setBandwidth() function ended up with an actual bandwidth of 600kHz; had they used a slightly higher percentage, for instance 77%, then setBandwidth() would have set the bandwidth at 1536kHz, which is a much better value for that sample rate.

I plan to run some tests with CubicSDR tonight after work; if everything works OK, then I'll merge these changes into the master branch. As per the Wiki, it is not part of the actual code I commit and push; I know there's a way to update it (I made an update a while ago; I just have to remember how I did it).

Franco K4VZ

Franco, I am happy to test the master when you upload that.

If I am reading this right it seems like updating the wiki is really just an update to the README.md file with the appropriate markup (or is it markdown) for formatting. Can it be that simple!!

John - VK4JBE

John, good news; it looks like in CubiCSDR the method setBandwidth() is currently only called for a specific device, the bladrRF (https://github.com/cjcliffe/CubicSDR/blob/master/src/sdr/SoapySDRThread.cpp#L487-L489):

        if (device->getDriverKey() == "bladeRF") {
            device->setBandwidth(SOAPY_SDR_RX, 0, sampleRate.load());
        }

so these changes to setBandwidth() do not affect CubicSDR.

I just merged the use-getBwEnumForRate branch into master ; please give it a try, and let me know how if you see any problems.

Franco K4VZ

Will do Franco.

Edit: Just downloaded the new SoapySDRPlay3 from master. Install worked fine. Running GNURadio and GQRX with a variety of combinations of Sample Rate and Bandwidth including bandwidth = 0.0MHz and non-standard values (ie 3Mhz) as well as standard values (300kHz, etc) and it all seems to be working as expected.

Great work.

John - VK4JBE