Code Monkey home page Code Monkey logo

Comments (95)

SDRplay avatar SDRplay commented on July 28, 2024

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.

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

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.

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

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.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

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?

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

@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.

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

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.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

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.

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

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.

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

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.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

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.

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

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.

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

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.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

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?

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

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.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

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.

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

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.

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

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.

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

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.

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

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

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

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

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

The code won't build for me...

/usr/include/c++/5/bits/hashtable_policy.h:85:34: error: no match for call to ‘(const std::hash<sdrplay_api_RspDuoModeT>) (const sdrplay_api_RspDuoModeT&)’
noexcept(declval<const _Hash&>()(declval<const _Key&>()))>

Amongst many other errors, which I wasn't getting before. Someone point me in the right direction please.

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

@vsonnier - I'll definitely plan to submit these changes as a PR once the design part is sorted out - so far everything is very 'experimental'

@SDRplay - I just tried pulling the code directly from github and recompiling and it worked for me:

$ git clone --single-branch --branch RSPduo_fixes https://github.com/fventuri/SoapySDRPlay.git
Cloning into 'SoapySDRPlay'...
remote: Enumerating objects: 39, done.
remote: Counting objects: 100% (39/39), done.
remote: Compressing objects: 100% (29/29), done.
remote: Total 459 (delta 24), reused 23 (delta 10), pack-reused 420
Receiving objects: 100% (459/459), 147.69 KiB | 1.64 MiB/s, done.
Resolving deltas: 100% (306/306), done.
$ cd SoapySDRPlay/
$ mkdir build
$ cd build
$ cmake ../
-- The CXX compiler identification is GNU 9.2.1
-- Check for working CXX compiler: /bin/c++
-- Check for working CXX compiler: /bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Build type not specified: defaulting to release.
-- Found libsdrplay: /usr/local/include, /usr/local/lib64/libsdrplay_api.so
-- LIBSDRPLAY_INCLUDE_DIRS - /usr/local/include
-- LIBSDRPLAY_LIBRARIES - /usr/local/lib64/libsdrplay_api.so
-- Performing Test HAS_STD_CXX11
-- Performing Test HAS_STD_CXX11 - Success
-- Found Git: /bin/git (found version "2.21.0") 
-- Module sdrPlaySupport configured with version: 0.3.0-5e8a451
-- Configuring done
-- Generating done
-- Build files have been written to: /tmp/SoapySDRPlay/build
$ make
Scanning dependencies of target sdrPlaySupport
[ 20%] Building CXX object CMakeFiles/sdrPlaySupport.dir/Registration.cpp.o
[ 40%] Building CXX object CMakeFiles/sdrPlaySupport.dir/Settings.cpp.o
[ 60%] Building CXX object CMakeFiles/sdrPlaySupport.dir/Streaming.cpp.o
[ 80%] Building CXX object CMakeFiles/sdrPlaySupport.dir/Version.cpp.o
[100%] Linking CXX shared module libsdrPlaySupport.so
[100%] Built target sdrPlaySupport

Here I am running Linux Fedora 30 with gcc/g++ version 9.2.1:

$ cat /etc/os-release 
NAME=Fedora
VERSION="30 (Thirty)"
ID=fedora
VERSION_ID=30
VERSION_CODENAME=""
PLATFORM_ID="platform:f30"
PRETTY_NAME="Fedora 30 (Thirty)"
ANSI_COLOR="0;34"
LOGO=fedora-logo-icon
CPE_NAME="cpe:/o:fedoraproject:fedora:30"
HOME_URL="https://fedoraproject.org/"
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f30/system-administrators-guide/"
SUPPORT_URL="https://fedoraproject.org/wiki/Communicating_and_getting_help"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_BUGZILLA_PRODUCT="Fedora"
REDHAT_BUGZILLA_PRODUCT_VERSION=30
REDHAT_SUPPORT_PRODUCT="Fedora"
REDHAT_SUPPORT_PRODUCT_VERSION=30
PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy"

$ g++ --version
g++ (GCC) 9.2.1 20190827 (Red Hat 9.2.1-1)
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Do you mind running the same commands (git clone, ..., cmake, and make) on your system?
If the make step still fails for you, you can email me (my username at comcast dot net) the full output from make so I can take a look.

Franco

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

The code won't build for me...

/usr/include/c++/5/bits/hashtable_policy.h:85:34: error: no match for call to ‘(const std::hash<sdrplay_api_RspDuoModeT>) (const sdrplay_api_RspDuoModeT&)’
noexcept(declval<const _Hash&>()(declval<const _Key&>()))>

I would bet a C++ compatibility level. @fventuri, try to stick to C++11 only, maybe you used C++14 or C++17 contructs and recent GCCs maybe default to these recent standards. @guruofquality, what is the expected C++ standard supposed to be supported in the Pothosware ecosystem ?

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

@SDRplay I rebuilt with success too the rspduo_fixes branch from Franco's git repo.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

Nope, won't build for me on Ubuntu 18.04.3 LTS - I've done everything you suggested @fventuri

gcc -v
gcc version 5.5.0 20171010 (Ubuntu 5.5.0-12ubuntu1)

Updated to latest version using apt update apt upgrade etc.

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

gcc version 5.5.0

I suspect this GCC version is way too old, to support modern C++ features properly.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

what do you do to update gcc?

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

bear in mind that almost all of our customers that want to use Linux, don't want to build anything, so to tell them that they first have to get a new compiler is going to be interesting lol

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

Let's try with this: https://linuxize.com/post/how-to-install-gcc-compiler-on-ubuntu-18-04/ installing GCC 7.xx as bare minimum.

bear in mind that almost all of our customers that want to use Linux, don't want to build anything, so to tell them that they first have to get a new compiler is going to be interesting lol

Well GCC 5.0 is an antique version w.r.t recent Ubuntus like v18.04, so they won't have to install anything most likely.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

Interestingly build-essential has been marked for "kept back" - no idea why

Going to brute force upgrade it now

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

@SDRplay On my ubuntu 19.10 I have:

gcc version 9.2.1 20191008 (Ubuntu 9.2.1-9ubuntu2)

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

Just managed to get updated to gcc 7.4 - that wasn't pretty - had to uninstall packages, remove files and the contents of the status file just to get it to install build-essential correctly - nasty

Anyway, I've now built it and I can see it working.

Couple of things...

Master 6 MHz - the starting sample rate is 6 MHz, but it is 2 MHz by the time it gets to the application - CubicSDR is showing a 6 MHz sample rate, which is wrong - it should be 2 MHz

Now that there is only a single sample rate, decimation should be an option so that smaller sample rates can be used.

Master 8 MHz sample rate should also be an option.

Not sure I like the virtual devices but I understand it's a way to get this done.

I'll keep playing with it.

from soapysdrplay2.

guruofquality avatar guruofquality commented on July 28, 2024

std::hash<sdrplay_api_RspDuoModeT>

Isnt this just a matter of the hash not being defined for a custom type, and not a feature greater than c++11.

I would bet a C++ compatibility level. @fventuri, try to stick to C++11 only, maybe you used C++14 or C++17 contructs and recent GCCs maybe default to these recent standards. @guruofquality, what is the expected C++ standard supposed to be supported in the Pothosware ecosystem ?

Ive tried not to use any C++14 and up in the common libraries, but its not really a big deal. Honestly when I started this, C++11 was just as recent as C++14 and 17 are now to the current date. Its pretty easy to have C++14 support in the latest ubuntu LTS and C++17 is mostly there.

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

Thanks SDRplay and guruofquality - I just pushed a new commit that removes any use of hashes/unordered_maps from the code and uses instead 'stringTo...' and '...toString' methods (similar to the existing methods 'stringToIF' and IFtoString').
Hopefully this latest code should compile without warnings or errors on any version of gcc/g++ compiler.

As per the sample rate, I see that the exiting code for the the SoapySDRplay driver has (and I think it comes from the original code) this method called 'getInputSampleRateAndDecimation', that selects a decimation factor based on the IF mode and on the input sample rate.
I assume this code is still valid when the RSPduo is running in single tuner mode, but I suspect it might need to be revisited for master (and slave mode).
To make sure I understand how the RSPduo works, you are saying that in master mode, in case of a 'rspDuoSampleFreq' of 6MHz, the sample rate is actually 2 MHz with no decimation, but the user should be allowed to select a sample rate of 1 MHz with decimation factor of 2; or a sample rate of 500 kHz with a decimation factor of 4; and son on?

Finally I am also wrote (and I currently debugging) an 'hybrid' version of the driver where in the RSPduo case there's a pseudo device for each of the RSPduo modes (i.e. typically three pseudo devices - single tuner, dual tuner, master - when started first, or just one pseudo device - slave - when started as a slave), and the tuner choice (for the single tuner and master modes) is a drop down like the IF mode selection. Do you think you would like this approach better?

Franco

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

As mentioned earlier, I created a new branch called 'pseudo_devices_by_RSPduo_mode' (https://github.com/fventuri/SoapySDRPlay/tree/pseudo_devices_by_RSPduo_mode), which presents a new pseudo device for each of the RSPduo modes that are available when the client application (CubicSDR in this case) is run, while the selection of the tuner is via dropdown.
I ran only a couple of tests (single tuner, and one instance of CubicSDR as a master, and another instance as a slave), and they worked fine on my computer.

Please give it a try and let me know if this way looks better for you.

Franco

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

Hi @fventuri, the pseudo branch is much more what I thought it would look like - I think it looks better to the end user. Some comments...

  1. By definition, if single or master modes are available, then ALL 3 antennas should be available to be selected. Then depending on what antenna is selected, you can then automatically select the tuner - so I would be in favour of having all 3 antennas available, but removing tuner as an option. I think, again, this makes it easier for the end user.

  2. In dual tuner mode, in practice you wouldn't have any antenna select and just make tuner 1 50 Ohm and Tuner 2 50 ohm ports the ones to use. This is the way we have it set for SDRuno. For diversity reception you would want the two tuner inputs to be the same otherwise there is a mismatch in the gain control.

  3. In master mode, dual tuner mode and single tuner Low-IF mode, these are the options for sample rate...

Input: 6 MHz, Output: 2 MHz, decimation = 1, dec_enabled = false, IFBW = 1.536 MHz
Input: 6 MHz, Output: 1 MHz, decimation = 2, dec_enabled = true, IFBW = 0.6 MHz
Input: 6 MHz, Output: 500 kHz, decimation = 4, dec_enabled = true, IFBW = 0.6 MHz
Input: 6 MHz, Output: 250 kHz, decimation = 8, dec_enabled = true, IFBW = 0.3 MHz
Input: 6 MHz, Output: 125 kHz, decimation = 16, dec_enabled = true, IFBW = 0.2 MHz
Input: 6 MHz, Output: 62.5 kHz, decimation = 32, dec_enabled = true, IFBW = 0.2 MHz

For master mode ONLY, you can also have:

Input: 8 MHz, Output: 2 MHz, decimation = 1, dec_enabled = false, IFBW = 1.536 MHz
Input: 8 MHz, Output: 1 MHz, decimation = 2, dec_enabled = true, IFBW = 0.6 MHz
Input: 8 MHz, Output: 500 kHz, decimation = 4, dec_enabled = true, IFBW = 0.6 MHz
Input: 8 MHz, Output: 250 kHz, decimation = 8, dec_enabled = true, IFBW = 0.3 MHz
Input: 8 MHz, Output: 125 kHz, decimation = 16, dec_enabled = true, IFBW = 0.2 MHz
Input: 8 MHz, Output: 62.5 kHz, decimation = 32, dec_enabled = true, IFBW = 0.2 MHz

Note: this is for compatibility with dump1090 ONLY and shouldn't be used in normal circumstances.

For slave mode, the input sample rate is fixed by the master, but the output sample rate can be...

Output: 2 MHz, decimation = 1, dec_enabled = false, IFBW = 1.536 MHz
Output: 1 MHz, decimation = 2, dec_enabled = true, IFBW = 0.6 MHz
Output: 500 kHz, decimation = 4, dec_enabled = true, IFBW = 0.6 MHz
Output: 250 kHz, decimation = 8, dec_enabled = true, IFBW = 0.3 MHz
Output: 125 kHz, decimation = 16, dec_enabled = true, IFBW = 0.2 MHz
Output: 62.5 kHz, decimation = 32, dec_enabled = true, IFBW = 0.2 MHz

Note: this is independent of what decimation is being used in the master.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

By the way, the output sample rate should be what CubicSDR uses - not the input sample rate.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

I've edited the comment above to add in the relevant IFBW values for each output sample rate.

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

By the way, the output sample rate should be what CubicSDR uses - not the input sample rate.

Yes indeed, it is crucial that the Sample rate the user has access to really corresponds to the final I/Q samples per second the whole processing is expecting.
Now to achieve the correct behaviour of setSampleRate and getSampleRate API the right combinations of "input rate + decimation" as listed by @SDRplay has to be used for instance, and of course Decimation must not be exposed as a setting for the final user.

  1. By definition, if single or master modes are available, then ALL 3 antennas should be available to be selected. Then depending on what antenna is selected, you can then automatically select the tuner - so I would be in favour of having all 3 antennas available, but removing tuner as an option. I think, again, this makes it easier for the end user.

I agree 100% here. If the 3 antennas can be switched at runtime, it is a perfect candidate for an Antenna selection setting, as it exists for the RSP2. At the driver level, calling setAntenna API could indeed also be used to switch to the appropriate tuner if necessary, hiding all the complexity behind the SoapySDR API.

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

Thanks for the very useful feedback SDRplay and vsonnier.

I just implemented all your recommendations to the SoapySDRPlay driver (branch 'pseudo_devices_by_RSPduo_mode' - https://github.com/fventuri/SoapySDRPlay/tree/pseudo_devices_by_RSPduo_mode).

A couple of notes:

  • I added another pseudo device, so that the user can choose between running in master mode with a sample rate of 6MHz (the default), and 8Mhz (if they want to run it at the same time as say dump1090).
  • I used the API function 'sdrplay_api_SwapRspDuoActiveTuner' to switch between the tuners when the RSPduo is in single tuner mode, but I am not sure what is the right way to switch between the tuners when the RSPduo is in master mode (assuming no other client application is using the slave instance, of course).

I just ran a couple of tests with CubicSDR (single user and master/slave case), and it seems to work here; give it a try and let me know how it goes.

Franco

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

Happy new year to all !

I made some tests on your last pseudo_devices_by_RSPduo_mode branch Franco.

It's working for most, I noticed 2 small things:

In single tuner mode now, if I select Zero IF mode I can't use sample rate more than 2 Mhz.
It's only while starting because during a SDR session in CubicSDR I can actually change and select other sample rate directly in CubicSDR menu (not in device selection dialog box).

So in Cubic it's not an issue but it may be for other Apps using SoapySDR ?

Second thing is about gain control. When I disable AGC I get IFGR and RFGR.
I can change RFGR value is changed and it modify IFGR value.

If I change IFGR it doesn't stay at the selected level and cursor is going to another value.
It's like it's reversed, I don't remember this was working like this on older soapy driver but I may be wrong.

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

Happy new year to you too!

I looked into the issue about the Zero IF mode and I made a couple of changes to the SoapySDRPlay driver that should address it.
I also noticed in CubicSDR these lines (https://github.com/cjcliffe/CubicSDR/blob/master/src/sdr/SDREnumerator.cpp#L281):

                for (size_t j = 0; j < settingsInfo.size(); j++) {
                    if (deviceArgs.find(settingsInfo[j].key) != deviceArgs.end()) {
                        settingsInfo[j].value = deviceArgs[settingsInfo[j].key];
                    }
                }

If my understanding is correct, this means that whatever CubicSDR finds in its config file ($HOME/.CubicSDR/config.xml) prevails over the values returned by the call to the function 'getSettinInfo()' from the SoapySDR driver - in other words, even after I changed the driver to return 'Zero-IF' for the argument 'if_mode' when in single tuner mode, I kept seeing 1,620kHz under the 'SoapySDR Device Options' header in CubicSDR; it wasn't until I manually edited the config file, found the entry for the RSPduo device in single user mode, and changed the value for 'if_mode' there, that I was able to see 'Zero-IF' under the 'SoapySDR Device Options' header in CubicSDR.
Not sure if this is something that affects you too, but I thought you may want to know, just in case.

As per the question about gain control, I looked at the code for the SoapySDR driver (adapting the code for the various RSPduo modes didn't require any changes there, so what it does should be identical to the original driver), and I noticed the following:

  • 'IFGR' affects the value of tunerParams.gain.gRdB, while 'RFGR' affects the value of 'tuneParams.gain.LNAstate'
  • while the LNAstate (i.e. RFGR) is pretty straighforward, for gRdB (i.e. IFGR) I noticed that the 'setGain()' method assigns the requested value to tunerParams.gain.gRdB, while the 'getGain()' method reads the value from tunerParams.gain.gainVals.curr - I am not exactly sure how these values are related ('gRdB' is the input value from the user, 'gainVals.curr' is the output value from the RSP device), but perhaps what you are seeing in CubicSDR is related to that (and I am fairly confident it was like that in the original driver).

During the holidays I also found out about another Linux SDR client: 'linhpsdr' by John Melton, G0ORX/N6LYT (his presentation at the SDR Academy 2018 is here: https://www.youtube.com/watch?v=GP0OgxfyCm0).
Since it looks like linhpsdr can make use of the SoapySDR driver (https://github.com/g0orx/linhpsdr/blob/master/Makefile#L20), I thought it would be a good candidate to make sure the SoapySDRPlay driver changes I am making play nice with other clients.
I made a few changes to the driver to also support linhpsdr (mainly I had the getHardwareKey() method return a good description of the RSPduo mode, not just the serial number), and I also made some changes to the linhpsdr source code itself to be able to support the 'sdrPlay' SoapySDR driver.
I still have some issues to figure out with the sdrplay_api_Init() function and the value for gRdB (I am sure I am passing 0, however sdrplay_api_Init() fails complaining that gRdB is set to 59 and therefore outside its allowed range). I plan to put my changes to the linhpsdr source code out on github tonight or tomorrow in case you want to give it a try.

Franco

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

Thank you very much Franco, no more issue with Zero IF mode.

I'll get a try with linhpsdr I tried when soapysdr was added, but it was not a great success.
I never managed to get it work with sdrplay.

I'll be happy to test your changes on it.

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

Hi @fventuri , @SDRplay and all, I wish you a very Happy New Year ! I've not had time to test @fventuri module, which don't stop me at delivering generic remarks.

I looked into the issue about the Zero IF mode and I made a couple of changes to the SoapySDRPlay driver that should address it.
I also noticed in CubicSDR these lines ...etc....
Not sure if this is something that affects you too, but I thought you may want to know, just in case.

The Devices initial dialog reads the config, checks it is within ranges, and try to apply it, that is all it can do.
At the risk of repeating myself, there is some core concept to understand :

Soapy settings are meant to be essentially independent options, where changing one setting don't affect the others.

If it is not the case, an application can only work properly if it has this feedback loop at its core:

change setting A (set) ==> Re-read all settings (get) ==> refresh all UI / inform user ==> repeat.

Else, the application is victim of the "pull the rug from under the feet" I mentionned earlier where a given setting change can affect other settings (or worse, the sample rate !!!) unknowingly of the application.

For Cubic we are lucky because the runtime Settings menu is actually completely refreshed each time a user change one setting. On the other hand, the other place it doesn't happen for a variety of reasons is the initial Devices. Do not even try to fix it, It has a chicken-and-egg problem because it manage the first initialization, I've been there.

My personnal opinion is to only expose 'Sample rate' and 'Antenna' and such super-generic parameters on the initial Devices dialog to prevent such problems, but I have no time available to work on that and should discuss it with @cjcliffe on such big change.

Now what about other apps than Cubic ? Could we expect them to re-scan their settings at a single option change to work properly ? Probably not.

To sum up IMHO Soapy API should make a best effort to only expose independent settings. Which means for instance removing access to Low IF / Zero-IF settings from the driver because it affects the sample rate. (on all RSP models).

I there drawbacks using the following ?

  • Sample rate < 2048 MHz => Make the driver use the best appropriate Low-IF mode
  • Sample rate > 2048 MHz ==> Zero-IF is the only option anyway (?)

As for the 'Devices' initial dialog, just admit that all the settings you put there won't necessarily be applied as is, because they may be inconsistent and the driver will eventually apply other things. Who cares ? Once started, the 'Sample rate' and 'Settings' menus will show the user the actual settings anyway.

Sorry if it sounds like a rant :)

'IFGR' affects the value of tunerParams.gain.gRdB, while 'RFGR' affects the value of 'tuneParams.gain.LNAstate'
while the LNAstate (i.e. RFGR) is pretty straighforward, for gRdB (i.e. IFGR) I noticed that the 'setGain()' method assigns the requested value to tunerParams.gain.gRdB, while the 'getGain()' method reads the value from tunerParams.gain.gainVals.curr - I am not exactly sure how these values are related ('gRdB' is the input value from the user, 'gainVals.curr' is the output value from the RSP device), but perhaps what you are seeing in CubicSDR is related to that (and I am fairly confident it was like that in the original driver).

RF Gain Select is also exposed as an artificial 'gain' using the listGains / setGain / getGain API so that a 'RFGR' gain slider is displayed on Cubic, because it is waaaay cooler than switching to the Settings menu. It is guarded by proper #ifdef for users who want only 'true' gains controls in dB, see here: pothosware/SoapySDRPlay3#61

Speaking of a 'feedback loop', this a perfect example: Because I had to make Cubic generic, I couldn't hardcode the reciprocal update between 'RFGR' and 'RF Select setting', so I could only make the hypothesis that somehow changing any Settings could affect Gains, so I made the code to re-read the current gains using setGain and that it why you see it updating in realtime.
The fun part was that the Gain controls were indeed used as both command and control at the same time. Yes that was hell. Don't do this at home.

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

Thanks for your comments, nmaster2042 and vsonnier.

I am perfectly OK with the way CubicSDR works and my comment earlier was just in the spirit of understanding why it behaves the way it does.
As suggested by you and SDRplay, I removed any reference to the tuner A or B in the public interface and public settings of the SoapySDRPlay driver - the user just chooses an antenna in CubicSDR, and the driver takes care of selecting the appropriate tuner.

As per the choice of the IF mode, I don't have any problem removing that too, and making it dependent on the sample rate, if SDRplay is OK with it.
When I started working on this SoapySDR driver, I thought it would be a good idea to make it "as close as possible" to the old SoapySDRPlay driver, and just add the code to make it work with the SDRplay API version 3.x and the various modes of the RSPduo, to avoid 'surprises' to the user; since the 'if_mode' was one of the settings in the old driver, I just kept it (and made the changes for the RSPduo).

As per my initial work on making the SoapySDRplay driver work with 'linhpsdr', if you want to try it, you can find my fork of 'linhpsdr' with a few changes to make it work here: https://github.com/fventuri/linhpsdr/tree/SDRplay-RSPs-with-SoaypSDR - I found that in order to avoid those problems I was having yesterday with 'sdrplay_api_Init()' and the value of gRdB being out of range, you have to create a linhpsdr property file called 'sdrPlay.props' and copy it under '$HOME/.local/share/linhpsdr'; the file in the github repo above is the one I created tonight, and the good news is that linhpsdr doesn't crash here now with that file. I saw another of those .props file in the same directory and it has an additional 700 or so properties, so I'll probably have to understand better this file in the next days, but at least it seems that linhpsdr could be another Linux client for the RSPs.

Franco

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

Hi @fventuri,

As per the choice of the IF mode, I don't have any problem removing that too, and making it dependent on the sample rate, if SDRplay is OK with it.
When I started working on this SoapySDR driver, I thought it would be a good idea to make it "as close as possible" to the old SoapySDRPlay driver, and just add the code to make it work with the SDRplay API version 3.x and the various modes of the RSPduo, to avoid 'surprises' to the user; since the 'if_mode' was one of the settings in the old driver, I just kept it (and made the changes for the RSPduo).

Well that was a mistake in the first place to introduce IF-control. A mistake I only fully understood by thinking about your recent hard work you made on this RSPDuo can of worms. I think we would better fix this mistake by removing the IF-control from the module entirely.

As for the Low-IF mode as implemented in the API v2.x, I switched to it once, Cubic did 'something' (wrong) and I quickly get back to "Zero-IF" never to touch IF again. I'm just taking care of not puting a demod in the Central Frequency because it get muffled because of DC-removal, a drawback the Low-IF modes don't have if I remember correctly.

As to 'avoid surprises', the IF modes control is by itself, a suprise in what it does related to the sample rate.

I suppose that if we want to plug the square of RSP into the round of a Soapy module, we must cut corners eventually.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

The point of the API is to make all of the parameters available to the developer because the RSP chipset is so flexible. It's ok to simplify this to fit the SoapySDR framework from my point of view.

For API 3 you could offer just sample rate (as per my post above) - <= 2 MHz (not 2.048 MHz) can be Low-IF, > 2 MHz can be Zero-IF

Apply the IFBW as I suggested above for Low-IF and then for Zero-IF (2 - 10 MHz)...

2 MHz < 5 MHz sample rate, use 1.536 MHz
= 5 MHz < 6 MHz sample rate, use 5 MHz
= 6 MHz < 7 MHz sample rate, use 6 MHz
= 7 MHz < 8 MHz sample rate, use 7 MHz
= 8 MHz use 8 MHz

So that way you can just provide Sample Rate and hide IFBW and IFMode

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

I just want to clarify the gain control system. There are 2 independent gain control systems that combine to give an overall gain value. We use IFGR (IF gain reduction) and RFGR (RF gain reduction) because that is actually what is happening in the system. Depending on the GR setting, a certain amount of attenuation is applied from the maximum signal gain.

The IFGR has a range (by default) of 20 to 59 dB (in SDRuno we allow the user to flip the sense between IFGR or IF attenuation to IF gain) - The IFGR is the only gain system in the RSP to be controlled by AGC.

The RFGR has a varying range dependent on RSP type and LO frequency. It always goes from 0 (min RFGR / max RF gain) to N. We pad out the ranges so that a single maximum value (max RFGR / min RF gain) can be used for each RSP. The RFGR range is referred to as LNAstate. It has a varying step size and each state represents a certain dB value.

Again, in SDRuno, the vast majority of people will leave the IFGR to be controlled by the IFAGC and just control the RF gain slider. For each RF gain step, the IFGR has a 40 dB control range.

As all RSPs are calibrated back to the antenna port, there is a currGain parameter that is available in the event callback. Whenever there is a gain update (either by manual change or by the IFAGC) the event callback will be called with an updated currGain value. This is the total system GAIN. It is the combination of the current RFGR AND IFGR values and then the gain is calculated from the maximum system gain at the specified LO frequency for the current RSP.

I understand the desire to reduce everything down to a simplified version so that it can be compared to how existing SDR products work, but I would say that just because something works in a different way to what has gone before it, it doesn't mean that it's wrong. The gain system works the way it does for a reason.

I hope that helps.

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

Thanks for all your useful commends, vsonnier and SDRplay.

I created a new branch called 'IF_mode_dependent_on_sample_rate' (https://github.com/fventuri/SoapySDRPlay/tree/IF_mode_dependent_on_sample_rate), and I made the changes as requested.

Couple of comments:

  • to select the IF type based on the sample rate, I wrote this simple method that should choose it according to your criteria listed above:
sdrplay_api_If_kHzT SoapySDRPlay::getIfEnumForRate(const double rate) const
{
    // 1. RSPduo in dual tuner/master/slave mode
    if (device.hwVer == SDRPLAY_RSPduo_ID) {
        if (fabs(device.rspDuoSampleFreq - 6000000) < 10) {
            return sdrplay_api_IF_1_620;
        } else if (fabs(device.rspDuoSampleFreq - 8000000) < 10) {
            return sdrplay_api_IF_2_048;
        }
    }

    // everything else
    if (rate <= 2000000) {
        return sdrplay_api_IF_0_450;
    } else {
        return sdrplay_api_IF_Zero;
    }
}

Please review it and make sure it matches your requirements (I tried it quickly with the RSPduo both in single tuner mode and in master/slave configuration, and sdrplay_api_Init() didn't complain, which is a good sign). With this method I was able to remove the 'if_mode' argument, and now the user shouldn't have to worry about it in CubicSDR.

  • as per the automatic selection of the bandwidth, the original SoapySDRplay driver contained the method getBwEnumForRate(), which is already doing that (to this point, I ran CubicSDR and I couldn't see that the user can change/set the bandwidth anywhere, although the SoapySDR driver guide does mention a 'Bandwidth API' - https://github.com/pothosware/SoapySDR/wiki/DriverGuide#bandwidth-api). What I did tonight is simply add the case for the RSPduo in Dual Tuner/Master/Slave mode, and for that case I used the same values that the original driver was using for the Zero-IF case. Anyhow this is the code for that method with my change:
sdrplay_api_Bw_MHzT SoapySDRPlay::getBwEnumForRate(double rate, sdrplay_api_If_kHzT ifType, bool rspDuoNonSingleMode)
{
   if (ifType == sdrplay_api_IF_Zero || rspDuoNonSingleMode)
   {
      if      ((rate >= 200000)  && (rate < 300000))  return sdrplay_api_BW_0_200;
      else if ((rate >= 300000)  && (rate < 600000))  return sdrplay_api_BW_0_300;
      else if ((rate >= 600000)  && (rate < 1536000)) return sdrplay_api_BW_0_600;
      else if ((rate >= 1536000) && (rate < 5000000)) return sdrplay_api_BW_1_536;
      else if ((rate >= 5000000) && (rate < 6000000)) return sdrplay_api_BW_5_000;
      else if ((rate >= 6000000) && (rate < 7000000)) return sdrplay_api_BW_6_000;
      else if ((rate >= 7000000) && (rate < 8000000)) return sdrplay_api_BW_7_000;
      else                                            return sdrplay_api_BW_8_000;
   }
   else if ((ifType == sdrplay_api_IF_0_450) || (ifType == sdrplay_api_IF_1_620))
   {
      if      ((rate >= 200000)  && (rate < 500000))  return sdrplay_api_BW_0_200;
      else if ((rate >= 500000)  && (rate < 1000000)) return sdrplay_api_BW_0_300;
      else                                            return sdrplay_api_BW_0_600;
   }
   else
   {
      if      ((rate >= 200000)  && (rate < 500000))  return sdrplay_api_BW_0_200;
      else if ((rate >= 500000)  && (rate < 1000000)) return sdrplay_api_BW_0_300;
      else if ((rate >= 1000000) && (rate < 1536000)) return sdrplay_api_BW_0_600;
      else                                            return sdrplay_api_BW_1_536;
   }
}

Please review this one too to make sure it follows your recommendations.

Franco

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

Hi Franco,

Thanks for this - I think we can make this easier because in the new API we only need...

sdrplay_api_IF_Zero = Zero-IF
sdrplay_api_IF_1_620 = 6 MHz Low-IF
sdrplay_api_IF_2_048 = 8 MHz Low-IF

rspDuoSampleFreq is only important for Low-IF master/slave mode and it must be exactly 6 or 8, so I would say...

if master/slave then
if rspDuoSampleFreq == 6000000 then sdrplay_api_IF_1_620
else sdrplay_api_IF_2_048

For the IFBW this can also be made easier as it's not dependent on IFmode...

Note, rate is the FINAL sample rate, not the starting sample rate (also after any decimation, down conversion, etc.)

if ((rate < 300000)) return sdrplay_api_BW_0_200;
else if ((rate >= 300000) && (rate < 600000)) return sdrplay_api_BW_0_300;
else if ((rate >= 600000) && (rate < 1536000)) return sdrplay_api_BW_0_600;
else if ((rate >= 1536000) && (rate < 5000000)) return sdrplay_api_BW_1_536;
else if ((rate >= 5000000) && (rate < 6000000)) return sdrplay_api_BW_5_000;
else if ((rate >= 6000000) && (rate < 7000000)) return sdrplay_api_BW_6_000;
else if ((rate >= 7000000) && (rate < 8000000)) return sdrplay_api_BW_7_000;
else return sdrplay_api_BW_8_000;

Best regards,

Andy

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

Thanks Andy - I simplified those two methods as you suggested and this is what they reduced to:

sdrplay_api_If_kHzT SoapySDRPlay::getIfEnumForRate(const double rate) const
{
    // 1. RSPduo in dual tuner/master/slave mode
    if (device.hwVer == SDRPLAY_RSPduo_ID &&
            (device.rspDuoMode == sdrplay_api_RspDuoMode_Dual_Tuner ||
             device.rspDuoMode == sdrplay_api_RspDuoMode_Master ||
             device.rspDuoMode == sdrplay_api_RspDuoMode_Slave)) {
        if (device.rspDuoSampleFreq == 6000000) {
            return sdrplay_api_IF_1_620;
        } else {
            return sdrplay_api_IF_2_048;
        }
    }

    // everything else
    return sdrplay_api_IF_Zero;
}

sdrplay_api_Bw_MHzT SoapySDRPlay::getBwEnumForRate(double rate)
{
   if                            (rate < 300000)   return sdrplay_api_BW_0_200;
   else if ((rate >= 300000)  && (rate < 600000))  return sdrplay_api_BW_0_300;
   else if ((rate >= 600000)  && (rate < 1536000)) return sdrplay_api_BW_0_600;
   else if ((rate >= 1536000) && (rate < 5000000)) return sdrplay_api_BW_1_536;
   else if ((rate >= 5000000) && (rate < 6000000)) return sdrplay_api_BW_5_000;
   else if ((rate >= 6000000) && (rate < 7000000)) return sdrplay_api_BW_6_000;
   else if ((rate >= 7000000) && (rate < 8000000)) return sdrplay_api_BW_7_000;
   else                                            return sdrplay_api_BW_8_000;
}

Let me know if this what you meant.

If you are satisfied with the driver changes to handle the RSPduo and use the SDRplay API version 3.x, I would like to post something to the SDRplay Users Forum to see if a couple of people want to build it and use it, to try to get the major bugs/problems out of the way; after that I'll submit a PR to get the code in the api3 branch of the SoapySDRplay driver.

Franco

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

Hello Franco,

We'll be running internal tests with this starting today and I'll come back to you with feedback.

Thank you and to all for your efforts.

Best regards,

Andy

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

Hi @fventuri (and @SDRplay) I noticed on your latest proposed change:

sdrplay_api_If_kHzT SoapySDRPlay::getIfEnumForRate(const double rate) const
{
    // 1. RSPduo in dual tuner/master/slave mode
  ...(etc.)

    // everything else
    return sdrplay_api_IF_Zero;
}

Does it mean the IF-Mode won't be used ever internally for the other RSP models ? I wondered if we could keep it in case of the final requested Sample Rate < 2MHz not to have the DC removal problem as a bonus for such lower sample rates.

Now, if it is more trouble than its worth in terms of driver complexity I understand completly.

Either way, for the final user we already had to explain in case of Zero-IF not ot stick a VFO in the Central Frequency so it won't change much in terms of explanation.

@fventuri I really want to test your driver, but don't have time yet. I'll be able test it on Windows10 with a RSP2 and Cubic of course.

Edit: BTW would the CubicSDR changes you made to adapt the RSPDuo still relevant or not ?

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

Very good point, vsonnier.
If I understand correctly the part after the comment 'everything else' should be replaced by something like this:

    if (rate < 2000000) {
        return sdrplay_api_IF_1_620;
    } else {
        return sdrplay_api_IF_Zero;
    }

I have no problem with that - one thing I noticed is that, if I run the RSPduo in single tuner mode with Zero-IF, I see no warnings, however if I run it in Low-IF mode, every few seconds I see warnings coming from acquireReadBuffer returning SOAPY_SDR_TIMEOUT (meaning that it takes more than 100ms for a new buffer to become available); I am not sure if it is my computer or something else ('top' doesn't show the CPU being very busy), but I thought I would mention it.

As per CubicSDR related changes, the only issue that I think is left is that CubicSDR ignores the return value from activateStream (https://github.com/cjcliffe/CubicSDR/blob/master/src/sdr/SoapySDRThread.cpp - line 455), which makes it impossible for a SoapySDR driver to inform CubicSDR that activateStream failed (this can happen in the SoapySDRPlay driver if there's any problem with sdrplay_api_Init()) - for now the driver will throw a runtime error to make sure this problem is addressed by the user, but I think it would be much better if CubicSDR were to handle failures coming from activateStream.

Finally I spent the last several days getting linhpsdr work with the SoapySDRPlay driver - I had to make several changes to linhpsdr and my modified version is here: https://github.com/fventuri/linhpsdr/tree/SDRplay-RSPs-with-SoaypSDR
One thing I was able to do with linhpsdr is to run the RSPduo in dual tuner mode and have linhpsdr display one receiver for each of the channels; it was while working on this task that I realized that I had the understanding of how streams work in SoapySDR all wrong; in other words I realized that client applications like linhsdr expect to have a different stream for each channel (while before I thought the usage for multiple receivers was to have one stream with multiple sets of buffers). Based on this assumption (one stream <-> one channel), I was able to clean up a lot of the code in the Streaming API section of the SoapySDRPlay driver.

I ran a few tests with both CubicSDR and linhpsdr and I didn't see any major problem (linhpsdr probably needs some more changes to work 100% right with two receivers), so I pushed these changes to the 'IF_mode_dependent_on_sample_rate' branch (https://github.com/fventuri/SoapySDRPlay/tree/IF_mode_dependent_on_sample_rate).

Franco

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

Hi @fventuri ,

I have no problem with that - one thing I noticed is that, if I run the RSPduo in single tuner mode with Zero-IF, I see no warnings, however if I run it in Low-IF mode, every few seconds I see warnings coming from acquireReadBuffer returning SOAPY_SDR_TIMEOUT (meaning that it takes more than 100ms for a new buffer to become available); I am not sure if it is my computer or something else

The important point is that in Zero-IF only, can you properly stream without timeouts between the whole range of advertised Sample rate 250Khz - 10 MHz ? (if applicable of course). If yes, then a suggest leaving Zero-IF as you did with a TODO comment in the code that explains this situation, so that Low-IF is not re-activated afterwards.
On the other hand, if not this is a real show-stopper IMO if the driver can't stream properly with high sample rates in particular. (I really, really need to start testing your driver !)

@SDRplay I suppose you can test it on the whole range of RSPs devices and various sample rates ? Did you observe such behaviour ?

As per CubicSDR related changes, the only issue that I think is left is that CubicSDR ignores the return value from activateStream (https://github.com/cjcliffe/CubicSDR/blob/master/src/sdr/SoapySDRThread.cpp - line 455), which makes it impossible for a SoapySDR driver to inform CubicSDR that activateStream failed

Well at that point Cubic is supposed to have properly setup the device, and if it refuses to stream not much can't be done, return value or not. The best behaviour would be simply returning 0 I/Q samples down the road of DSP (like a 'blocking' situation) so the user can see that nothing is happening on the Waterfall and Spectrum side.

Throwing runtime execptions is certainly a bit harsh. I'll see what I can do on Cubic side.

@fventuri Thank you again for your investment on this, I can easily imagine how time-consuming it can be.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

It's taken me a bit of time to get a proper installer sorted out for the Mac platform and now we've started testing so I'll get back to you shortly with feedback.

Just one comment on the sample rate note above. It should be <= 2 MHz and note this is the final sample rate, not the starting sample rate.

if (rate <= 2000000) {
    return sdrplay_api_IF_1_620;
} else {
    return sdrplay_api_IF_Zero;
}

We'll try all RSPs in varying modes, etc. including the RSPdx and RSPduo master/slave mode. Thanks for your efforts guys. If anyone is interested in testing with the Mac Installers I've built for the API, Soapy and CubicSDR (0.2.6) let me know here and I'll post the links to them. The usual health warnings apply :-)

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

I changed the code for the IF mode selection (for the single tuner case) as shown in SDRplay post, and sdrplay_api_Init() failed with the RSPduo in single tuner mode with this error:

devIdx0: sdrplay_api Service_rsp: checkDeviceParamLimits: ERROR: fsFreq fsHz out of range (2000000.000000:10000000.000000) = 1000000.000000

I added a print statement right before calling sdrplay_api_Init() and these are some of the settings:

deviceParams->devParams->fsFreq.fsHz=1000000.000000 
chParams->tunerParams.ifType=1620
chParams->tunerParams.bwType=600

Also the API Specification version 3.06 document has this paragraph on page 26:

Conditions for LIF down-conversion to be enabled for all RSPs in single tuner mode:
(fsHz == 8192000) && (bwType == sdrplay_api_BW_1_536) && (ifType == sdrplay_api_IF_2_048)
(fsHz == 8000000) && (bwType == sdrplay_api_BW_1_536) && (ifType == sdrplay_api_IF_2_048)
(fsHz == 8000000) && (bwType == sdrplay_api_BW_5_000) && (ifType == sdrplay_api_IF_2_048)
(fsHz == 2000000) && (bwType <= sdrplay_api_BW_0_300) && (ifType == sdrplay_api_IF_0_450)
(fsHz == 2000000) && (bwType == sdrplay_api_BW_0_600) && (ifType == sdrplay_api_IF_0_450)
(fsHz == 6000000) && (bwType <= sdrplay_api_BW_1_536) && (ifType == sdrplay_api_IF_1_620)

I suspect that more changes are required to be able to use Low-IF than just those 5 lines in the getIfEnumFromRate() method; I'll have some time to look into that tomorrow.

Franco

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

For Low-IF, the INPUT sample rate should ALWAYS be 6 MHz or 8 MHz, those other modes are just for backwards compatibility to the old API.

rate in the above example is the OUTPUT sample rate (i.e. what is used in CubicSDR)

If you look above, this is what I said about Low-IF mode...

Input: 6 MHz, Output: 2 MHz, decimation = 1, dec_enabled = false, IFBW = 1.536 MHz
Input: 6 MHz, Output: 1 MHz, decimation = 2, dec_enabled = true, IFBW = 0.6 MHz
Input: 6 MHz, Output: 500 kHz, decimation = 4, dec_enabled = true, IFBW = 0.6 MHz
Input: 6 MHz, Output: 250 kHz, decimation = 8, dec_enabled = true, IFBW = 0.3 MHz
Input: 6 MHz, Output: 125 kHz, decimation = 16, dec_enabled = true, IFBW = 0.2 MHz
Input: 6 MHz, Output: 62.5 kHz, decimation = 32, dec_enabled = true, IFBW = 0.2 MHz

For an INPUT sample rate of 6 MHz, the IFmode should be 1620

For compatability with dump1090, the INPUT sample rate can be 8 MHz (IFmode = 2048) but ONLY in master mode - for single tuner mode the INPUT sample rate for Low-IF should ALWAYS be 6 MHz

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

I've just had a look at the code in the IF_mode_dependent_on_sample_rate branch and have these comments...

remove lines 888 - 891, no sample rate below 2 MHz should use Zero-IF

getInputSampleRateAndDecimation can be simplified for all RSPs...

only fixed sample rate values are allowed to be selected, so...

if ifType == sdrplay_api_IF_1_620 || ifType == sdrplay_api_IF_2_048
{
if rate == 62500 decM=32;decEnable=1
else if rate ==  125000 decM=16;decEnable=1
else if rate == 250000 decM=8;decEnable=1
else if rate == 500000 decM=4;decEnable=1
else if rate == 1000000 decM=2;decEnable=1
else decM=1;decEnable=0
if ifType == sdrplay_api_IF_1_620 return 6000000;
else return 8000000;
}
else if ifType == sdrplay_api_IF_ZeroIF
{
// rate should be > 2 MHz so just return rate
decM=1;decEnable=0;
return rate;
}

I don't understand the get IfEnumForRate - you can't assume the IF type for a given input sample rate. What this function really does is just say am I a RSPduo in non-single tuner mode - it has an input of rate but never uses it. I would either change this function or put a health warning on it saying that it doesn't really do what it says in the function name.

in getBwEnumForRate all of the rate >= cases are irrelevant and can be removed, it should be...

if rate < 300000
else if rate < 600000
etc.

We are still running tests, but I fear that because of the code the way it is, we are not actually seeing Low-IF mode in single tuner mode.

Also, please make the default sample rate 2 MHz and NOT 2.048 MHz

More comments as we get through testing...

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

Andy, thanks for reviewing the code and suggesting those changes to make it much simpler - I just pushed all these changes to the 'IF_mode_dependent_on_sample_rate' branch (since this is the only one branch where we are working on, I should probably change its name to something more descriptive, like 'API3+RSPduo').

I do have a couple of comments:

  • it is true that right now the IF type selection does not depend on the sample rate (and I followed your suggestion to rename that method to just 'getIfEnum()' and remove the 'rate' argument); however in the past we discussed having code like this in there:
if (rate <= 2000000) {
    return sdrplay_api_IF_1_620;
} else {
    return sdrplay_api_IF_Zero;
}

If we decide that that if statement above should go back in that method, then the IF selection will be dependent on the sample rate - since if I remember correctly we decided to remove the IF mode from the user's choices, I am not sure what the IF mode should be based on in single tuner mode at this point.

  • I couldn't find anywhere in the code where the default sample rate is set to 2.048MHz (although it might have been in the past); I am wondering if you see a default of 2.048MHz because that value is read from your $HOME/.CubicSDR/config.xml (in that case you should be able to just edit that file and fix it).

Franco

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

Hi @fventuri , @SDRplay,
(Still not had time to test, which still not stop at commenting...)

I think there is a quid pro quo in what rate and 'sample rate' means here for both of you.

Using the naming conventions of @SDRplay in the previous coments:

  • Output sample rate : this is what the device finally outputs in terms of number of I/Q Samples/s and in reverse, this is what the user requests to the API as Sample rate in a final application like Cubic.
  • Input sample rate : this what the driver requests to the hardware with a combination of decimation and IF-mode in order to achieve Ouput sample rate.

So it is up to the driver to choose the best (or only) combination of Input sample rate, decim, IF-mode according to the model, RSPDuo mode etc... to achieve the desired Output sample rate requested through the SoapyAPI. So yes, the IF-mode is to be hidden from the Soapy API interface.

In addition, since all 250KHz-10MHz is not always possible in some RSPDuo modes, the driver should adapt the list of possible Output sample rate depending of such mode.
(Soapy API : virtual std::vector<double> listSampleRates(const int direction, const size_t channel) const;)

My previous remarks were on the opportunity to use low IF-mode with output_sample_rate < 2000000 automatically or sticking to Zero-IF all the time for the non-RSPDuo or single (?) Duo mode.

I suggest using the convention input_sample_rate and output_sample_rate in the code itself, comenting on what it means to avoid further confusion.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

I like the idea of using input_sample_rate and output_sample_rate - this will help clarify some of the communication.

Whilst it is true that the RSPduo in either dual tuner mode or master/slave mode only works in Low-IF mode, this should not be seen as just an RSPduo mode. The same Low-IF mode rules should apply to all RSPs - it's just that this is the ONLY mode that can be used with that particular RSPduo hardware setup.

I agree that the IF mode should be hidden from the user. However, you cannot isolate the sample rate from the IF mode - they are linked. This statement can be used for ALL RSPs...

if (output_sample_rate <= 2000000) {
    if RSPduo_master_8MHz_mode
        return sdrplay_api_IF_2_048; // input_sample_rate = 8000000
    else
        return sdrplay_api_IF_1_620; // input_sample_rate = 6000000
} else {
    return sdrplay_api_IF_Zero; // 2000000 < input_sample rate <= 10000000
}

If you do that, then you just have to say that RSPduo master and RSPduo dual tuner modes have a maximum output_sample_rate of 2000000

We've been testing on Mac and can confirm that Low-IF mode isn't working correctly in the build of the SoapySDRPlay library we have. We'll wait for updated library and restart testing. We've also noticed some issues with AGC which I'll post in the next note...

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

Comments from internal tester...

When IFAGC turned off, gain sliders appear but the IFAGC still behaves as if it is still turned on. With IFAGC turned on, the RF gain slider should still be displayed (feature request). Maybe both sliders could be displayed when IFAGC is enabled but the IF gain slider could be greyed out?

Whilst playing with the RF gain settings from the menu (not clear if RF gain or RF gain reduction) and switching IFAGC on/off, CubicSDR froze.

If tuner 2 is selected at startup, no signals are shown. If tuner 1 is then selected and then back to tuner 2, then the signals are seen.

I haven't had a chance to check these out myself, but I'll do that today. btw I agree about the branch name change. Thanks again.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

More comments on Settings.cpp...

line 631 should be:
chParams->ctrlParams.agc.enable = sdrplay_api_AGC_CTRL_EN;

lines 648 - 658 should only be run if IFAGC is disabled...

if (name == "IFGR" && chParams->ctrlParams.agc.enable == sdrplay_api_AGC_DISABLE)
   {
      //Depending of the previously used AGC context, the real applied 
      // gain may be either gRdB or current_gRdB, so apply the change if required value is different 
      //from one of them.
      if ((chParams->tunerParams.gain.gRdB != (int)value) || ((int)chParams->tunerParams.gain.gainVals.curr != (int)value))
      {
         chParams->tunerParams.gain.gRdB = (int)value;
         doUpdate = true;
      }
   }

getGain (line 673) isn't right - if name is IFGR then it returns the gain value - this is the total system gain and not specific to the IFGR range. So technically it should be...

if (name == "IFGR")
   {
       return chParams->tunerParams.gain.gRdB;
   }

line 899 should be deleted (2 MHz freq in Zero-IF mode - 2 MHz is Low-IF as previously discussed)

as previously mentioned, getIfEnum is wrong - it assumes LowIF mode is RSPduo ONLY, which it is not.

listBandwidths equally is wrong as it again assumes that LowIF (i.e. BW < 2 MHz) is RSPduo only, which it is not.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

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

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

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.

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024
  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.

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

@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.

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

vsonnier avatar vsonnier commented on July 28, 2024

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

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

Franco

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

@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

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

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.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

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.

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

@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.

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

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?

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

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

cubic-trace.log

from soapysdrplay2.

SDRplay avatar SDRplay commented on July 28, 2024

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

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

fventuri avatar fventuri commented on July 28, 2024

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

from soapysdrplay2.

nmaster2042 avatar nmaster2042 commented on July 28, 2024

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

from soapysdrplay2.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.