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

RSPduo support #62

Closed
SDRplay opened this issue Dec 17, 2019 · 95 comments
Closed

RSPduo support #62

SDRplay opened this issue Dec 17, 2019 · 95 comments
Assignees

Comments

@SDRplay
Copy link
Contributor

SDRplay commented Dec 17, 2019

I made a fork under my account and created a branch called 'RSPduo_fixes' (https://github.com/fventuri/SoapySDRPlay/tree/RSPduo_fixes).
I cleaned up a couple of warnings during compile and I also straightened up the code for the RSPduo mode/tunes and antennas (hopefully).
I tested my changes this afternoon using 'SoapySDRUtil' with the following commands:

SoapySDRUtil --probe="driver=sdrPlay, rspduo_mode=Tuner A (Single Tuner)"
SoapySDRUtil --probe="driver=sdrPlay, rspduo_mode=Tuner B (Single Tuner)"
SoapySDRUtil --probe="driver=sdrPlay, rspduo_mode=Tuner A (Master/Slave)"
SoapySDRUtil --probe="driver=sdrPlay, rspduo_mode=Tuner B (Master/Slave)"
SoapySDRUtil --probe="driver=sdrPlay, rspduo_mode=Dual Tuner"

and in each case 'SoapySDRUtil' returned what I expected (including 2 RX channels in the last case with 'Dual Tuner').

Please give it a try and if it looks OK to you, I'll go ahead and send a pull request.

Franco

Originally posted by @fventuri in pothosware/SoapySDRPlay3#58 (comment)

@SDRplay
Copy link
Contributor Author

SDRplay commented Dec 17, 2019

Hi @fventuri, this is a good start and only works for probing and not in an application - master mode has certain limits... must be only Low-IF mode, sample rate must be either 6 or 8 MHz only - IF Freq must be either 1.62 MHz or 2.048 correspondingly.

Once the first tuner is in master mode, then probe should only return or allow slave mode. If I start one instance of CubicSDR and then run probe again I get a map::at error

Slave mode cannot have any sample rate control, or IF frequency. It can have independent bw and decimation control however.

@SDRplay SDRplay self-assigned this Dec 17, 2019
@fventuri
Copy link
Contributor

Thanks for your feedback.

I committed several changes to the 'RSPduo_fixes' branch in my repo that should address some of the problems with the application.
The application I used for my tests is CubicSDR (version 0.2.4) - I was able to have the driver run in single tuner, dual tuner, and master mode; while in master mode, I was able to launch a second instance of CubicSDR and use the slave instance.

While I was testing the code for SoapySDRPlay, I noticed that CubicSDR has a couple of quirks:

  1. it ignores the return value when calling activateStream() (in CubicSDR source code look at the file src/sdr/SoapySDRThread.cpp line 119); therefore it would ignore when the activateStream() in SoapySDRPlay returned the error SOAPY_SDR_NOT_SUPPORTED. I changed the behavior of activateStream() in the driver to throw an exception instead; this makes CubicSDR crash, but at least this way I could see there was a problem with sdrplay_api_Init()

  2. somehow CubicSDR seems to be unable to deal with a list of antennas dependent on the rspDuoMode and tuner (i.e. only 'Tuner 1 50 ohm' and 'Tuner 1 Hi-Z' should be listed if the tuner selected is Tuner A, while only 'Tuner 2 50 ohm' should be listed for Tuner B). In order for listAntennas() to list all the antennas (independent of the tuner) when using CubicSDR, I added (for now) a boolean called 'CubicSDR', which is set to true to address this quirk. In the final version of the driver, I'd like to find a better way to handle this issue.

Also the code for the driver has become more messy to handle the dual tuner and master/slave cases, but at least I was able to make it run without crashing for the most common use cases I could think of.

Please give it a try when you get a chance and let me know how it goes for you.

Franco

@nmaster2042
Copy link

nmaster2042 commented Dec 26, 2019

Hi, I tried the new Franco's branch for API v3 & RSP Duo support with a fresh built CubicSDR.

When I start CubicSDR it scans for Soapy devices and finds my RSP Duo.

I select it and the app hang and exits.

On the console windo I can see:

SDR enumerator starting.
SoapySDR init..
API Version: v0.8.0
ABI Version: v0.8
Install root: /usr/local
Loading modules...
Available factories...netsdr, null, sdrPlay
driver = sdrPlay
label = SDRplay Dev0 RSPduo 1809XXXXXX
Make device 0
[INFO] devIdx: 0
[INFO] hwVer: 3
sdrplay_api_api_version=3,060000
sdrplay_api_hw_version=3

Reporting enumeration complete.
SDR enumerator done.
[INFO] devIdx: 0
[INFO] hwVer: 3
[ERROR] Invalid RSPduo mode change: 7 -> 8
Terminating SDR thread..
ERROR: thread '9SDRThread' has not terminated in time ! (> 3000 ms)

I don't know if it's a CubicSDR issue or a SoapySDRPlay one.

@fventuri
Copy link
Contributor

Thanks for the feedback, nmaster2042.
What this line:

     [ERROR] Invalid RSPduo mode change: 7 -> 8

means is more or less the following.

RSPduo mode 7 (7=1+2+4) means that the RSPduo can run in 'single tuner', 'dual tuner' or 'master' mode (these codes are described in the enum 'sdrplay_api_RspDuoModeT' in the include file 'sdrplay_api_rspDuo.h') - this is typically the list of modes that the RSPduo accepts when no other application is using it.

On the other hand mode 8 means CubicSDR tries to run that RSPduo in 'slave' mode, which is the mode you would choose when another application is already using the same RSPduo in 'master' mode. Typically this happens because CubicSDR on exit saves its last used configuration in a config file called $HOME/.CubicSDR/config.xml.

If you look at that contents of that file, you'll see a stanza that looks more or less like this:

        <device>
            <id>SDRplay Dev0 RSPduo SERIALNUM</id>
...
            <settings>
                <agc_setpoint>-30</agc_setpoint>
                <biasT_ctrl>false</biasT_ctrl>
                <dabnotch_ctrl>false</dabnotch_ctrl>
                <extref_ctrl>false</extref_ctrl>
                <if_mode>1620kHz</if_mode>
                <iqcorr_ctrl>true</iqcorr_ctrl>
                <rfgain_sel>4</rfgain_sel>
                <rfnotch_ctrl>false</rfnotch_ctrl>
                <rspduo_mode>Tuner A (Master)</rspduo_mode>
            </settings>
...
        </device>

When you restart CubicSDR it reads that file and tries to ask the driver for the mode previously saved in the 'rspduo_mode' element. If in your case, you might have something like 'Tuner B (Slave)', that can trigger the error you saw.

A quick workaround (for now) is to edit that config file and change the 'rspduo_mode' line to something like 'Tuner A (Master)' - after that CubicSDR should work.
A better way would be to exit the CubicSDR in slave mode first and then exit the CubicSDR in master mode last - this way the config file should contain an entry like mine (i.e. 'Tuner A (Master)').

Finally I should probably address this issue by making a simple change in the driver, such that in a scenario like yours it would select 'single tuner' or 'master' mode (with the same tuner in the config, or the other tuner, or always 'Tuner A'?).

Hope this helps,
Franco

@vsonnier
Copy link
Contributor

it ignores the return value when calling activateStream() (in CubicSDR source code look at the file src/sdr/SoapySDRThread.cpp line 119); therefore it would ignore when the activateStream() in SoapySDRPlay returned the error SOAPY_SDR_NOT_SUPPORTED. I changed the behavior of activateStream() in the driver to throw an exception instead; this makes CubicSDR crash, but at least this way I could see there was a problem with sdrplay_api_Init()

somehow CubicSDR seems to be unable to deal with a list of antennas dependent on the rspDuoMode and tuner (i.e. only 'Tuner 1 50 ohm' and 'Tuner 1 Hi-Z' should be listed if the tuner selected is Tuner A, while only 'Tuner 2 50 ohm' should be listed for Tuner B). In order for listAntennas() to list all the antennas (independent of the tuner) when using CubicSDR, I added (for now) a boolean called 'CubicSDR', which is set to true to address this quirk. In the final version of the driver, I'd like to find a better way to handle this issue.

That is the problem with the "pull the rug under the SDR feet" problem of the Duo. Cubic expects that Settings are obeyed and don't change dynamically because of another instance in particular.
That is why I suggested in #58 that the device only present "automagically" settings that stay relatively static afterwards depending of the starting order.

Maybe presenting a reduced capability "CubicSDR"

@SDRplay suggested in #58 presenting different devices in the selection panel:

It's not as simple as always starting the first instance as the master. In master mode, Low-IF is only possible and so therefore only 2 MHz of bandwidth. So what we do in SDRuno is to always start in single tuner mode, but have a switch to allow the changing of the first instance into master mode. Then the 2nd instance would automatically start in slave mode. Then there is dual tuner mode, where both tuners are controlled by the same instance. I guess this could all be handled by a mode option in the device selection panel.

Whichever instance is using tuner 1 will still be able to switch between the HiZ port and port A in master/slave mode.

I'm not even sure other API layers using Soapy underneath like gr-osmo or such in GQRX and others would interface that dynamic settings any more kindly, so presenting different capability classes as pseudo-"Devices" may be the solution.

@SDRplay
Copy link
Contributor Author

SDRplay commented Dec 27, 2019

I must admit I'm a bit confused as to what the intention is with the driver. In SDRuno, we always start the RSPduo in single tuner mode EXCEPT when the RSPduo is already being used in master mode, then slave mode must be used. It's very simple, there are no variables required.

Why can't it be done like that?...

scenario 1: RSPduo connected, but neither tuner in use

  • in the CubicSDR device selection panel you can select between single tuner (default) or master (ignore dual for now, can CubicSDR deal with multiple streams from a single driver instance?)

scenario 2: RSPduo connected and master mode is already in use by CubicSDR

  • in the 2nd CubicSDR instance ONLY slave is available and therefore the sample rate and IF mode should be hidden.

scenario 3: RSPduo connected and single tuner is in use by CubicSDR

  • then the 2nd CubicSDR instance should see NO RSPduo available.

This to me seems very simple - is there a problem with implementing it this way? By allowing variables to be passed, there seems to be an assumption that they override existing behaviour - for example if the RSPduo is already being used in master mode, would you expect that another instance of CubicSDR where "single tuner" is being passed in via the rspduo_mode variable would work - because it won't and shouldn't be expected to either.

Unless I've missed something about the way CubicSDR and the SoapySDR framework behave?

@vsonnier
Copy link
Contributor

@SDRplay summurizes it right I think, the SoapySDR driver should only present the settings appropriate to each of the 3 scenarios above.
Once in a given "mode", the list of settings is indeed frozen and CubicSDR or others should be able to run normally, being either Single, Master, or Slave exclusively. We should not try to override the auto-detected or manually chosen mode by other means like this persistent rspduo_mode.

can CubicSDR deal with multiple streams from a single driver instance ?

Alas no. Only 1 device, 1 readStream thread active at any given time. The Devices menu is always available though, and allows switching of devices without restarting CubicSDR.

@fventuri
Copy link
Contributor

Thanks for your comments, vsonnier and SDRplay.

My perspective on the SoapySDRplay driver was to write a "general purpose" SoapySDR driver, following more or less the guidelines here: https://github.com/pothosware/SoapySDR/wiki/DriverGuide (and more specifically the API contract specified here: https://github.com/pothosware/SoapySDR/blob/master/include/SoapySDR/Device.hpp). From this point of view CubicSDR is just one of the applications using the SoapySDRplay driver (for instance there could be other applications where they would want the 'Dual Tuner' option, since the API shows that a device could have multiple channels).
This point also might help explain the confusion for SDRplay, where I think the application (SDRuno) and the driver are one thing and therefore there's no 'line of demarcation' between the two.

This said, I have no problem implementing the driver as presenting multiple "virtual devices" in the RSPduo case (I don't think it should be too hard) - I have just a few high level questions to avoid possible misunderstandings in the future:

  • what should be the naming convention for the RSPduo virtual devices? Would something like 'SDRplay Dev0 RSPduo <SERIAL NUMBER> - Single Tuner', 'SDRplay Dev0 RSPduo <SERIAL NUMBER> - Master', and 'SDRplay Dev0 RSPduo <SERIAL NUMBER> - Slave' work?
  • also is it possible that the user might want to use tuner B as master and tuner A as slave? in this case should the list of virtual devices above have something like '... - Tuner A Master', and '.... - Tuner B Master'?
  • in a similar way what about sampling rates in the case of Master (or Dual Tuner)? Should the 6MHz/8Mhz sampling rate be specified at the virtual device level too?

As per my initial comment about CubicSDR ignoring the return value from 'activateStream()', there could be several reasons why the call to 'sdrplay_api_Init()' fails besides the case of a wrong RSPduo mode (see the list at the bottom of page 26 in https://www.sdrplay.com/docs/SDRplay_API_Specification_v3.06.pdf) - I am not sure how's CubicSDR expects the driver to communicate this failure.

Franco

@SDRplay
Copy link
Contributor Author

SDRplay commented Dec 27, 2019

I don't understand the significance of the naming. If that means something to the end application then I don't have a problem with whatever it needs to be. Note that once the RSPduo is in use, then the driver (for the 2nd instance of the end application) gets all the information it needs from the API about what that instance can or can't do. So the driver will not need to query any virtual device name for that information.

It's possible for the master tuner to be either tuner, the slave is just whichever one isn't in use. Again, you can get that information from the API.

The importance about the sample rate is...

Single Tuner - sample rate can be 2 - 10 MHz in Zero-IF mode and 6 MHz (which produces a final sample rate of 2 MHz) in Low-IF mode. Other sample rates can be used in Low-IF mode but with less efficiency so we tend not to recommend them now. Delivers 1 IQ stream.

Master Tuner - Low-IF mode only either 6 MHz or 8 MHz (both produce a final sample rate of 2 MHz) - you would ONLY use 8 MHz if the other tuner was using an application like dump1090 which expects 8 MHz sample rate. Delivers 1 IQ stream.

Slave Tuner - Neither sample rate or IF mode can be set - follows whatever the Master Tuner is using. Delivers 1 IQ stream.

Dual Tuner - Low-IF mode only and only 6 MHz sample rate (produces a final sample rate of 2 MHz) - delivers 2 IQ streams.

Note that for all of these scenarios, parameters like decimation and IF Bandwidth are independent and can be used by either tuner (except dual tuner mode where you would want those parameters to be consistent).

Gain control for each tuner is also independent of these scenarios.

@SDRplay
Copy link
Contributor Author

SDRplay commented Dec 27, 2019

Forgot to mention that when using 6 MHz Low-IF, the IF mode should be set to 1.62 MHz and when using the 8 MHz Low-IF, the IF mode should be set to 2.048 MHz - if not this could cause the incorrect tuner mode to be selected.

@fventuri
Copy link
Contributor

I made the changes you suggested - now the SoapySDRPlay driver can present multiple 'virtual devices' for the RSPduo depending on the available RSPduo modes.
I quickly ran a couple of tests using CubicSDR a few minutes ago and I was able to run two instances, one as a master, and one as a slave.
I'll run a few tests tomorrow morning, but in the meanwhile you can give it a try to see if it works as you described above.

Franco

@nmaster2042
Copy link

Hi Franco.

I just rebuilt your SoapySDRPlay api3 branch.

I removed the .CubicSDR folder in order to reinitialize CubicSDR user configuration, as you explained.

Now when I start CubicSDR I can see 5 devices. From a user point of view it's more understandable like this.

But, I can't start a SDR session with any of these devices: CubicSDR crashes and I get this error in the console:

SDR thread starting.
device init()
[INFO] Using format CF32.
[ERROR] *** error in activateStream() - Init() failed: sdrplay_api_Fail
terminate called after throwing an instance of 'std::runtime_error'
what(): *** error in activateStream() - Init() failed
Abandon (core dumped)

One other detail, When selecting Tuner A single or master, Bias tee option has to be removed.

@nmaster2042
Copy link

OK, it seems I have a small issue with the sdrplay 3 API on my system.

I'm investigating this issue and I'll make more tests.

@SDRplay
Copy link
Contributor Author

SDRplay commented Dec 28, 2019

5 devices?? I'm not at my desk but will take a look as soon as I can. Why is it not just 1 device and then deal with the different modes like another IF mode?...

i.e.

RSPduo = 1 device
Mode (dropdown like IF mode) = Single Tuner, Master Tuner, Dual Tuner

The thing I didn't like about the rspduo_mode variable was that it was required to be set to do anything. What should happen is that the API should be queried and then the functionality presented as dropdown options to the user.

Same goes for Tuner #

Is that not possible? I'll take a look asap and provide some feedback.

@vsonnier
Copy link
Contributor

Mode (dropdown like IF mode) = Single Tuner, Master Tuner, Dual Tuner

Can't work. Make the Mode a setting that is RW with readSetting/writeSetting like the others (RF notch, antenna choice, IF modes....etc.) will make it possible to change the Mode at any time at runtime, which we don't want.
Presenting different "pseudo-Device " (ex RSPDuo-Single, RSPDuo-Master, RSPDuo-Slave) that stay stable once selected is the only way, unless I'm missing something there.

@nmaster2042
Copy link

These are virtual devices.

When starting the firs CubicSDR, and no tuners are actually in use you get 5 devices (Tuner A single, Tuner B single, Dual tuner, Tuner A master and tuner B master).

When you chose device tuner A Master for example, when you start another instance of CubicSDR you only get Tnuer B slave with good IF and sample rate settings.

Let's give a try.

It's probably different than on SDRUno I never used but this way is better than the rspduo_mode option.

@SDRplay
Copy link
Contributor Author

SDRplay commented Dec 28, 2019

My point was I don't understand why there is 5, but there should be 3. Why is Tuner in the virtual name?

Are you saying that when you select the device, that you then can't customise the other options??

If so, then what is to stop me from selecting Tuner A master and 10 MHz sample rate with Zero IF - this is obviously not a valid combination.

If you can deal with that, then why can't the tuner also be like antenna select?

@nmaster2042
Copy link

In the actual implementation, when you select RSP Duo Tuner A master you simply can't select 10 mhz sample rate and zero-if because sample rate is fixed to 6 mhz and if mode = 1620 khz to prevent user to select wrong values as I made and crashing the app.

Instead you can select Tuner A single to put 10 Mhz sample rate and Zero IF.

I think the reason why there is 5 virtual devices and not 3 is because it seems to be the way to deal with options of each tuner considering the mode (single, dual, master/slave).

In Cubic SDR, when you select a device, there is different options, but one can't be dynamically changed depending of the value of an other option.

In the first implementation with the rspduo_mode, you could select the mode but you had to manually set sample rate and if mode correctly because options couldn't be automatically modified.

It's my understanding.

I'm testing this, for now it seems to work ok.

@SDRplay
Copy link
Contributor Author

SDRplay commented Dec 28, 2019

In master mode, the sample rate can be either 6 MHz (1.62 MHz IF) or 8 MHz (2.048 MHz IF) - so is 8 MHz not an option?

I guess what I'm not understanding is that if you can select sample rate depending on whether you've selected master or single (as you've stated above), then I don't understand why the same is also not true of what antenna/tuner is used.

I'll try it myself later today when I'm at my desk and I'll probably get a better feel for it and get back to you.

@nmaster2042
Copy link

nmaster2042 commented Dec 28, 2019

For now, in master mode, only 6 Mhz SR with IF mode of 1620 khz is selectable.
But 8 Mhz / 2048 khz can probably can be added.

The master / slave mode is actually working I can use 2 instances of CubicSDR.

Yes, the best way is to get a try by yourself to give a feedback.

@nmaster2042
Copy link

nmaster2042 commented Dec 28, 2019

But if 8 Mhz / 2048 khz is added in master mode, user will need to pay attention to adjust the 2 parameters correctly because it will be possible to select for example 6 Mhz SR and 2048 khz IF mod witch will be incorrect.

OR, for master mode it would be better to have 2 options combining the 2 parameters:

  • 6 mhz / 1620 khz
  • 8 mhz / 2048 khz

This would prevent users (like me lol) to chose incorrect combination.

@fventuri
Copy link
Contributor

Thanks everyone for trying it and especially their feedback.

A couple of comments:

  • if the tuner (A or B) was selectable as a dropdown like IF mode, then we would go back to the problem with the antenna selection; i.e. if tuner A was selected, only antennas 'Tuner 1 50ohm' and 'Tuner 1 Hi-Z' should be selectable, while if the user had switched to tuner B, only the 'Tuner 2 50ohm' option should be presented. If I understand correctly, CubicSDR doesn't dynamically retrieve the list of available antennas every time, and therefore the driver would have to present all the antennas regardless of the selected tuner (which is is what the previous version was doing).
  • a similar reasoning also applies to the choice of the sample rate for dual tuner and master mode (I'll talk about slave mode in a moment) - if the user was allowed to choose between 6Ms/s and 8Ms/s, then the IF mode choice would be dependent on that . True, I could add another couple of pseudo devices to allow for that, as nmaster2042 suggested, but I also based my decision on this sentence by SDRplay:

Master Tuner - Low-IF mode only either 6 MHz or 8 MHz (both produce a final sample rate of 2 MHz) - you would ONLY use 8 MHz if the other tuner was using an application like dump1090 which expects 8 MHz sample rate. Delivers 1 IQ stream.

As per when the driver is used in slave mode, in this case the driver uses the sample rate already set by the master (be it 6Ms/s or 8Ms/s), so if someone were to start say dump1090 in master mode (which would set the sample rate to 8Ms/s) and then run CubicSDR in slave mode, CubicSDR would use an 8Ms/s sample rate (and an IF mode of 2048 kHz).

Franco

@nmaster2042
Copy link

You are right Franco, the simplest is the best I think.

In the particular case of app such as dump1090, the only thing to remember is to start it in master mode so it can get its 8 Mhz SR.

As we can chose witch tuner will be the master, it can serve all uses cases I think.

@fventuri
Copy link
Contributor

OK, I think I figured out the issue with CubicSDR and the list of antennas shown. I took a look at the source code for CubicSDR and I found this code(see https://github.com/cjcliffe/CubicSDR/blob/master/src/forms/SDRDevices/SDRDevices.cpp#L136):

        //A-3) Antennas, is there are more than 1 RX antenna, else do not expose the setting.
        //get the saved setting
        const std::string& currentSetAntenna = devConfig->getAntennaName();

        //compare to the list of existing antennas
        SoapySDR::ArgInfo antennasArg;
        std::vector<std::string> antennaOpts = selDev->getAntennaNames(SOAPY_SDR_RX, 0);

        //only do something if there is more than 1 antenna
        if (antennaOpts.size() > 1) {

            //by default, choose the first of the list.
            std::string antennaToSelect = antennaOpts.front();

This explains why when I was selecting any of the tuner B cases (either in single tuner mode or in master mode), the antenna entry was not there (because there's only one choice). After understanding this, I may have to revisit my comments about the way CubicSDR handles the antenna selection (and therefore the tuner choice discussion above).

Franco

@vsonnier
Copy link
Contributor

vsonnier commented Dec 28, 2019

This is the initial Device dialog choosing. The other place where list of antennas are read and displayed is this: https://github.com/cjcliffe/CubicSDR/blob/57454e4b32040e98a6c296cbfeb1335b108a940b/src/AppFrame.cpp#L1036

But the principle is the same: there is no point, in principle, of displaying a list of antennas made of 1 entry because there is no choice. This is to have a cleaner UX, nothing more, nothing less.

For information, here is the original PR I made to support multiple antennas easily when I bought my RSP2 : cjcliffe/CubicSDR#571

@fventuri
Copy link
Contributor

Thanks vsonnier for the clarification about the antenna thing.

I fixed another bug I found this afternoon where in CubicSDR switching from one pseudo device (for instance Tuner A - Single) to a different pseudo device (for instance Tuner B - Single), and then back again to the first pseudo device (Tuner A - Single) causes some problems because CubicSDR tries to re-use the initial instance of that pseudo device (instead of reinitializing it), while I think the SDRplay 3.x API expects that the client goes through a full ReleaseDevice/SelectDevice cycle (+ GetDeviceParams). I added a check in readSettings() so that, if the device is not the one selected (i.e. if CubicSDR didn't initialize it), then a new function called 'reselectDevice()' is invoked, that does exactly that.

I also found out this afternoon that, when streaming in Single Tuner mode, there are quite a number of times where the time elapsed between two successive calls to 'rx_callback' is above 100,000us, which then causes timeouts in 'acquireReadBuffer()', which show up in the console as messages: 'SoapySDR read failed with code: -1'.
To understand better this problem, I lowered the threshold, and I logged all the cases where the time elapsed between two successive calls to 'rx_callback' is above 90,000us - there were a lot of them, so it seems this issue needs some more research.
I am not sure if you too see these messages in Single Tuner mode (I didn't try to see if it happens in Dual Tuner mode), or if it just something going on on my computer.

Franco

@vsonnier
Copy link
Contributor

I fixed another bug I found this afternoon where in CubicSDR switching from one pseudo device (for instance Tuner A - Single) to a different pseudo device (for instance Tuner B - Single), and then back again to the first pseudo device (Tuner A - Single) causes some problems because CubicSDR tries to re-use the initial instance of that pseudo device (instead of reinitializing it), while I think the SDRplay 3.x API expects that the client goes through a full ReleaseDevice/SelectDevice cycle (+ GetDeviceParams). I added a check in readSettings() so that, if the device is not the one selected (i.e. if CubicSDR didn't initialize it), then a new function called 'reselectDevice()' is invoked, that does exactly that.

Truth be told, the Devices selection is rarely used after the intial CubicSDR startup, so it is small wonder a clean Device switch is probably not done. Now the changes you made are surely interesting for all, so a PR for CubicSDR would be greatly appreciated once the Duo problems are sorted out 👍

I'll have a look (later) at the Streaming code of the SoapySDR driver to see if there are obvious concurrency or performance problems, it has become my specialty for Cubic or Soapy drivers, so to speak (pothosware/SoapyPlutoSDR#14).

@SDRplay
Copy link
Contributor Author

SDRplay commented Jan 16, 2020

Comment on Low-IF 8MHz sample rate...

In Registration.cpp there is a duplication of sdrplay_api_RspDuoMode_Master in the for loop at 142 - is this deliberate to support both 6 and 8 MHz sample rates?
This also appears in the for loop on line 106 in Settings.cpp

On line 148 of Settings,cpp, 8 MHz sample rate seems to be set as true if master is selected - I'm confused by this, surely if rspDuoMode == sdrplay_api_RspDuoMode_Master doesn't it get dealt with at line 132?

I just want to make sure that 8 MHz sample rate is ONLY selected if the user specifically selects the 8 MHz device option. It's only a dump1090 compatibility option and the default should be 6 MHz

@fventuri
Copy link
Contributor

Andy, Vincent, first of all thanks for all your feedback and your patience; I think I am starting to see the light on how the input sample rate, output sample rate, decimation, and IF mode all work together for the RSPs (all this knowledge will turn very useful for the GNURadio module).

Anyway, I renamed the branch to ''API3+RSPduo' (https://github.com/fventuri/SoapySDRPlay/tree/API3+RSPduo) as discussed yesterday, and I pushed all the suggested changes and fixes there (hopefully I didn't miss anything); I was able to run a couple of quick tests here using CubicSDR (single tuner mode, and master/slave) with no crashes.

A few comments:

  • the issue with IFAGC off/on should be fixed; the method setGainMode() was missing a call to sdrplay_api_Update() when the device is streaming
  • I tried a couple of times to start CubicSDR with tuner 2 and I saw the device was streaming; since here at my desk I just have a piece of wire and it's late I couldn't check much else, but if the tester still sees that problem with the latest commit, I'll double check
  • a couple of times in the past I saw CubicSDR/SoapySDRPlay freeze while playing around; if this happens again to you, please do the following:
  1. do not try to exit out of the frozen CubicSDR (no control-C)
  2. open another terminal and obtain the PID of the CubicSDR process with 'ps -ef | grep CubicSDR'
  3. send the ABORT signal to that PID with 'kill -ABRT <PID>'
  4. if things work like here with Linux, CubicSDR wil exit and create a 'core dump' file with the exact status of the process at the time of the ABORT signal; that core dump file will possibly/hopefully be written to the same directory where you ran CubicSDR (but not always; here on Fedora Linux they get written to '/var/lib/systemd/coredump' in some sort of compressed format)
  5. once you find it, run this command: gdb <path of the CubicSDR executable> <path of the core file>
  6. finally if this command was successful, you should get a '(gdb)' prompt; type 'thread apply all backtrace', and you should get a list of what each thread was doing while CubicSDR was frozen; usually there's enough information there to figure out something
  • finally I rewrote those for range loops in Registration.cpp and Settings.cpp that go over all RSPduo modes; instead of the 'hack' I had before of having the Master mode repeated twice, I am now using a structure with the RSPduo mode and the input sample frequency flag (6 or 8MHz). This hopefully should make more clear my intent, and you'll see that the 8MHz sample rate is only selected when the user explicitly says so.

Franco

@SDRplay
Copy link
Contributor Author

SDRplay commented Jan 17, 2020

Thanks Franco, we've restarted testing and things look a lot better. Couple of comments (that you may or may not be able to do anything about)...

  1. There is no visual indication which CubicSDR instance is the master and which is the slave. Is there something that can be added to the title bar?
  2. You cannot close the master before the slave, the slave must be closed first. We are finding, not surprisingly, that when we close the master, the slave crashes. There is a mechanism by which the slave instance can catch this event and send a signal to the application to say that the slave is still running, so now must be closed. Here's a snippet from SDRuno from the event callback...
switch(eventId)
{
...
    case sdrplay_api_RspDuoModeChange:
        if(params->rspDuoModeParams.modeChangeType == sdrplay_api_MasterDllDisappeared)
        {
            // Display error saying that the master stream has been removed before the
            // slave stream and force the slave application to close
        }
        break;
...
}

can either of these be done?

Andy.

@vsonnier
Copy link
Contributor

vsonnier commented Jan 17, 2020

  1. There is no visual indication which CubicSDR instance is the master and which is the slave. Is there something that can be added to the title bar?
    ...
    There is a mechanism by which the slave instance can catch this event and send a signal to the application to say that the slave is still running, so now must be closed.

No RSP-specific code in Clubic please. Still, here is an idea :

a) For the first case, the name of the device must suffice. So to stay generic, displaying the name of the device on the titlebar would fit our need. We already use wxWidgets SetTitle to change it to append the name of the current Session, so this is definitely possible.

b) For the second case, make the slave stop streaming, i.e readStream returning 0 or another code < 0 would be enough for the final user to see that nothing is moving.

@fventuri
Copy link
Contributor

I added the following code to the event callback function in Streaming.cpp

    else if (eventId == sdrplay_api_RspDuoModeChange)
    {
        if (params->rspDuoModeParams.modeChangeType == sdrplay_api_MasterDllDisappeared)
        {
            // Display error saying that the master stream has been removed
            // before the slave stream and force the slave application to close
            SoapySDR_log(SOAPY_SDR_ERROR, "*** master stream has been removed. Aborting.");
            throw std::runtime_error("*** master stream has been removed. Aborting.");
        }
    }

However for some reason in the CubicSDR running as a slave I never see the eventId sdrplay_api_RspDuoModeChange (but I do see a few sdrplay_api_GainChange events) when I close the CubicSDR running in master mode first - what I see here is that the rx_callback in the slave stops being called, which causes the method acquireReadBuffer() to timeout while waiting for a buffer to become available, returning SOAPY_SDR_TIMEOUT.

I just pushed my code with the change above to the API3+RSPduo branch - please give it a try there to see if that's what happens to you too.

Franco

@fventuri
Copy link
Contributor

As per adding the device name for the CubicSDR title bar, I added the two lines below to src/AppFrame.cpp and they seem to work for me:

git diff src/AppFrame.cpp
diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp
index 2dec8b2..61c619d 100644
--- a/src/AppFrame.cpp
+++ b/src/AppFrame.cpp
@@ -979,6 +979,9 @@ void AppFrame::handleUpdateDeviceParams() {
     
     int i = 0;
     SoapySDR::Device *soapyDev = devInfo->getSoapyDevice();
+
+    // Display device name in app title
+    SetTitle(wxString::Format(wxT("%s - %s"), CUBICSDR_TITLE, devInfo->getName().c_str()));
     
     // Build settings menu
     wxMenu *newSettingsMenu = new wxMenu;

If you are interested I can create a pull request with CubicSDR.

Franco

@vsonnier
Copy link
Contributor

@fventuri Thanks, but this is a bit too simple: if you look at the usage of CUBICSDR_TITLE, you'll see that the current saved or load session file is appended to it. We need to keep that information while adding (before it) the Device name.
If anything, the device name should be set in void AppFrame::initDeviceParams(SDRDeviceInfo *devInfo) once, then set it also in AppFrame::saveSession(std::string fileName) and AppFrame::loadSession(std::string fileName) appending the Session name correctly to it.

@fventuri
Copy link
Contributor

Thanks Vincent.
What about something like this:

git diff
diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp
index 2dec8b2..2f3dcaa 100644
--- a/src/AppFrame.cpp
+++ b/src/AppFrame.cpp
@@ -979,6 +979,9 @@ void AppFrame::handleUpdateDeviceParams() {
     
     int i = 0;
     SoapySDR::Device *soapyDev = devInfo->getSoapyDevice();
+
+    // Display device name in app title
+    SetTitle(wxString::Format(wxT("%s - %s"), CUBICSDR_TITLE, devInfo->getName().c_str()));
     
     // Build settings menu
     wxMenu *newSettingsMenu = new wxMenu;
@@ -1404,7 +1407,8 @@ bool AppFrame::actionOnMenuReset(wxCommandEvent& event) {
         wxGetApp().getSpectrumProcessor()->setFFTAverageRate(0.65f);
         spectrumAvgMeter->setLevel(0.65f);
 
-        SetTitle(CUBICSDR_TITLE);
+        // Display device name in app title
+        SetTitle(wxString::Format(wxT("%s - %s"), CUBICSDR_TITLE, wxGetApp().getDevice()->getName().c_str()));
         currentSessionFile = "";
                currentBookmarkFile = "";
         bookmarkSplitter->Unsplit(bookmarkView);
@@ -2642,7 +2646,8 @@ void AppFrame::saveSession(std::string fileName) {
     currentSessionFile = fileName;
     std::string filePart = fileName.substr(fileName.find_last_of(filePathSeparator) + 1);
     GetStatusBar()->SetStatusText(wxString::Format(wxT("Saved session: %s"), currentSessionFile.c_str()));
-    SetTitle(wxString::Format(wxT("%s: %s"), CUBICSDR_TITLE, filePart.c_str()));
+    // Display device name in app title
+    SetTitle(wxString::Format(wxT("%s - %s: %s"), CUBICSDR_TITLE, wxGetApp().getDevice()->getName().c_str(), filePart.c_str()));
 }
 
 bool AppFrame::loadSession(std::string fileName) {
@@ -2677,7 +2682,8 @@ bool AppFrame::loadSession(std::string fileName) {
     std::string filePart = fileName.substr(fileName.find_last_of(filePathSeparator) + 1);
 
     GetStatusBar()->SetStatusText(wxString::Format(wxT("Loaded session file: %s"), currentSessionFile.c_str()));
-    SetTitle(wxString::Format(wxT("%s: %s"), CUBICSDR_TITLE, filePart.c_str()));
+    // Display device name in app title
+    SetTitle(wxString::Format(wxT("%s - %s: %s"), CUBICSDR_TITLE, wxGetApp().getDevice()->getName().c_str(), filePart.c_str()));
 
     wxGetApp().getBookmarkMgr().updateActiveList();
 

Franco

@vsonnier
Copy link
Contributor

I've made a PR for Cubic, please test it with the driver: cjcliffe/CubicSDR#786.

@fventuri
Copy link
Contributor

Thanks Vincent.
I ran a couple of quick tests now (single tuner and master/slave) and no problems so far.

Franco

@fventuri
Copy link
Contributor

Today I spent sometime cleaning up the code with the SelectDevice() and ReleaseDevice() API calls.

I noticed that the SoapySDRPlay driver freezes in this scenario (even when using the master branch from CubicSDR, so this is not due to any of the changes Vincent made yersterday):

  • start CubicSDR with the RSPduo in single tuner mode and let it run for a few seconds to make sure the waterfall shows up
  • go in the 'SDR Devices' menu in CubicSDR and try to change the device to the RSPduo in master mode (with the default 6MHz input sample rate)

I was able to track down the problem to the 'sdrplay_api_ReleaseDevice()' API and more exactly to the 'sdrplay_api_LockDevice()' API call inside ReleaseDevice.
To further troubleshoot this issue, I put this code right before ReleaseDevice:

      sdrplay_api_ErrT err;
      err = sdrplay_api_Uninit(deviceSelected->dev);
SoapySDR_logf(SOAPY_SDR_INFO, "after Uninit() - err=%d", err);
      err = sdrplay_api_UnlockDeviceApi();
SoapySDR_logf(SOAPY_SDR_INFO, "after UnlockDeviceApi() - err=%d", err);
      err = sdrplay_api_LockDeviceApi();
SoapySDR_logf(SOAPY_SDR_INFO, "after LockDeviceApi() - err=%d", err);
SoapySDR_logf(SOAPY_SDR_INFO, "before ReleaseDevice() - deviceSelected=%p", deviceSelected);
      sdrplay_api_ReleaseDevice(deviceSelected);

and the output looks like this:

[INFO] after Uninit() - err=0
[INFO] after UnlockDeviceApi() - err=0

i.e. sdrplay_api_LockDevice() never returns (notice that the two previous API calls are successful), and that causes CubicSDR to freeze.

Franco

@fventuri
Copy link
Contributor

I just pushed a few code changes to the 'API3+RSPduo' branch to take care of the following:

  • refactor reselectDevice() into deselectDevice() and selectDevice(); this way the code to select the device, get the device parameters, select the channel parameters pointer based on the active tuner, etc, is all in one place
  • I also found out that attempting to select a different mode for the RSPduo (through the 'SDR Devices' menu in CubicSDR) while streaming is active, causes CubicSDR to freeze (I think because the internal SDRplay API code is waiting for a mutex) - in order to notify the user about this issue, I added a warning message in findSDRPlay(), that lets the user know that the device is streaming.

One last thing - I am still trying to figure out how to prevent sdrplay_api_Init() to fail in the following scenario:

  • the user selects single tuner mode, and starts streaming from the RSPduo
  • after some time they stop streaming, switch to master mode, and stream again for some time
  • finally they stop streaming in master mode, go back to single tuner mode, and try to stream again

when they try to stream again in single tuner mode, I see that sdrplay_api_Init() fails and the logs show a message like this:

Feb 16 18:22:25 fvdesktop CubicSDR[10979]: [11050]: sdrplay_api: sdrplay_api_Init: ERROR: Failed to lock mutex

It might be just a corner case that no user will actually encounter, but I wanted to let you know in case you think it is worth investigating.

Franco

@SDRplay
Copy link
Contributor Author

SDRplay commented Feb 17, 2020

@fventuri I haven't checked the code, but are you releasing the device and selecting it again when switching between modes, because you need to. In SDRuno, when switching from single tuner to master, I release the device, set rspDuoMode to master and then select the device again. Same when going back to single tuner - of course you need to make sure the slave isn't in use before allowing the master mode to switch back to single tuner mode.

Does that help?

Andy

@fventuri
Copy link
Contributor

Good catch, Andy.
I wasn't reselecting the device (i.e. calling the API functions ReleaseDevice() + SelectDevice()) when switching back to the previous RSPduo mode.
I just pushed out the code with the fix to the API3+RSPduo branch; I ran a couple of quick tests and I was able to switch mode back and forth without any issue.

Thanks,
Franco

@nmaster2042
Copy link

nmaster2042 commented Feb 17, 2020 via email

@nmaster2042
Copy link

This issue occurs only when, at first streaming start in single tuner mode I set sample rate = 2048 khz (default option on my setup). The same test starting single tuner mode with sample rate = 2 mhz is ok.

Now if I start the first streaming in signle tuner mode with sample rate = 6 Mhz, when I change to master mode I get this on Cubic:

Screenshot_20200217_113020

To get a proper working stream again, I have to go to the sample rate menu and change it for ex to 1 mhz and again to 2 mhz to fix.

@SDRplay
Copy link
Contributor Author

SDRplay commented Feb 17, 2020

The sample rate for master mode in the dropdown CANNOT be greater than 2 MHz - in fact, the library shouldn't accept or ideally not give any option to select a sample rate greater than 2 MHz in master mode. Internally the SoapySDRPlay library will use 6 MHz, but that is to deliver a final sample rate of 2 MHz - if you are able to attempt to switch from single tuner mode to master mode using a final sample rate of 6 MHz, then that's where the issue is.

@nmaster2042
Copy link

@SDRplay no, there is no issue about master mode and 2 mhz max sample rate.

I start in single tuner, I can chose whatever sample rate (ex: 6 mhz or 2.048 mhz).
I start streaming for few seconds like franco wrote.

Then I stop streaming, select device and chose master mode. Here I only get max of 2 mhz selectable values and I let default 2 mhz.
Finally I start streaming.

The issue is at this stage is that cubic is crashing if in the first start in single tuner mode has been done with sample rate of 2.048 mhz.

@SDRplay
Copy link
Contributor Author

SDRplay commented Feb 17, 2020

ok, well there is nothing fundamentally wrong with doing that, so something wrong in the sequencing - if you are able to capture a debug log, that would help. Maybe @fventuri can help with that?

@nmaster2042
Copy link

Here is a strace while reproducing the issue I mentioned, I hope it can be usable.

cubic-trace.log

@SDRplay
Copy link
Contributor Author

SDRplay commented Feb 18, 2020

Unfortunately not, I need the DebugEnable statement enabled in the SoapySDRPlay library and then the output from that.

@fventuri
Copy link
Contributor

Thanks for the feedback nmaster2042 and Andy.

I just pushed a code change to the 'API3+RSPduo' branch that validates the output sample rate when the RSPduo is in master or slave mode and, if it is > 2MHz, sends a warning message and forces it to 2MHz.
This problem was happening because CubicSDR was calling the SoapySDRPlay driver method 'setSampleRate()' with a value of 2.048MHz for the output sample rate, and I was blindly passing it through to the device parameter structure without checking it first; this issue should be fixed now and CubicSDR should work as expected.

If a similar issue happens again and you are able to repeat it consistently, I'd like you do do the following:

  • edit the source files Settings.cpp and Streaming.cpp; in each of them search for the string 'Debug'; comment out the line with the parameter 'sdrplay_api_DbgLvl_Disable', and uncomment the line below with the parameter 'sdrplay_api_DbgLvl_Verbose' - you should have something like this:
    //sdrplay_api_DebugEnable(device.dev, sdrplay_api_DbgLvl_Disable);
    sdrplay_api_DebugEnable(device.dev, sdrplay_api_DbgLvl_Verbose);
  • build and install the SoapySDRPLay driver with debug enabled:
cd build
make
sudo make install
  • run CubicSDR and make the problem happen again
  • get the lines containing the string 'sdrplay_api' from the log file '/var/log/messages':
sudo grep sdrplay_api /var/log/messages > /tmp/sdrplay_api.txt'
  • the file '/tmp/sdrplay_api.txt' should contain a few hundreds/thousand lines; toward the end you should see something like this (posssibly with a different error message):
Feb 17 20:04:36 fvdesktop sdrplay_apiService[1836]: [2323]: devIdx0: sdrplay_apiService_rsp: checkDeviceParamLimits: ERROR: fsFreq fsHz out of range for master/slave mode(6000000.000000 or 8000000.000000) = 2048000.000000
Feb 17 20:04:36 fvdesktop sdrplay_apiService[1836]: [2323]: devIdx0: sdrplay_apiService_rsp: Init: ERROR: Tuner1: checkDeviceParamLimits returns error 3
  • please cut&paste those lines (and anything you see interesting in that '/tmp/sdrplay_api.txt' file), so we can troubleshoot why the SoapySDRPlay driver failed

  • to disable debug mode in the driver, you just have to revert back the two changes you made above to the source files Settings.cpp and Streaming.cpp, rebuild and reinstall the driver using the same commands I showed above.

Franco

@fventuri
Copy link
Contributor

Since I start seeing some 'git clone' activity on the repo, I just enabled the 'Issues' tab there (https://github.com/fventuri/SoapySDRPlay/issues), to be able to track the remaining issues before the pull request for the 'API3+RSPduo' branch.

Franco

@nmaster2042
Copy link

@fventuri: I rebuilt the SoapySDRPlay with your last changes, the issue I pointed is now gone. I'll continue making other tests.

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

No branches or pull requests

5 participants