Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

SoapyMultiSDR #70

Closed
cjcliffe opened this issue May 25, 2016 · 9 comments
Closed

SoapyMultiSDR #70

cjcliffe opened this issue May 25, 2016 · 9 comments

Comments

@cjcliffe
Copy link
Contributor

I'm seeing some interesting stuff done using multiple RTL-SDR's combined in the last few months; reviewing liquid's oversampled firpfbch2 and regular firpfbch (polyphase filterbank channelizer) it seems possible to utilize them to combine multiple streams in this manner.

I'm thinking instead of dirtying up the SoapyRTLSDR repo a SoapyMultiRTL repo would be a good one to set up and try.

Thoughts?

@guruofquality
Copy link
Contributor

I was thinking about the same thing actually, though not for RTLs only. I'm not a big fan of trying to cram multiple devices into a single wrapper for MIMO reasons because its a pain from the development end. That puts the burden on the API user, which often just means more for loops. But I can see where the existing interface would be nice to just re-use for many devices, so...

What if there was a SoapyMulti support driver that worked kind of like the Osmo block top level does now. It would take a bunch of device args to list the internal devices, and then it would summon the underlying SoapySDR device pointers for the internal devices, and reimplement all of the settings calls to distribute them to the correct destination device. Streaming as well, which might get a little hairy with non-homogenous devices and different stream MTUs, but we can memcpy....

Hows that sound?

@cjcliffe
Copy link
Contributor Author

That's a more robust plan than what I had in mind -- If I read that correctly it would be simplest (configuration-wise) to only support devices of the same module so there's one configuration proxied to the devices with some sort of management for bandwidth, frequency, etc. between them?

Mixing devices would be neat but I'm guessing that would increase the complexity significantly :)

@cjcliffe cjcliffe changed the title SoapyMultiRTL? SoapyMultiSDR? May 26, 2016
@guruofquality
Copy link
Contributor

For the most part I imagine only supporting the same type of device. I think it would pretty much just work with heterogeneous devices, but some stream configurations might be awkward. I think the default args should be simple to reflect using the same type of device, but there could be an advanced mode to support heterogeneous stuff or added later. But basically the settings and streaming are going to look pretty generic, and I think there is a straightforward way to implement them.

The constructor
Make the device 0, 1, and ... N-1 according to args specific.

The settings*
All of the calls take channel indexes, so if the first device has two channels, then 0 and 1 get mapped to device 0, if the second device has 3 channels, then 2, 3, 4 gets mapped to device 1, etc... A vector of internal device pointers for each possible channel would handle this mapping pretty easily.

Streaming
A stream is created with a list of channels. So we make streams with the internal devices converting the external channel number to the internal one for each device. A SoapyMultiSDR Stream object imply has pointers to N internal Stream objects.

The readStream call itself calls each internal stream readStream() call sequentially (could be in parallel with threads) on each of the buffers passed in. Even with homogeneous devices, we need to make sure the same amount is read off each channel or do some accounting. Same basic idea with writeStream.

Some hand-wavy fixes for the variable read amounts

  • We could work with memcpy to save remainders when one stream gets more than the others and copy it back in at the start of the next read -- this involves some annoying accounting of course.
  • Always fully fill the entire presented buffer, calling each stream until we get all the requested samples.
  • If the input stream is at least MTU size, we can read with SOAPY_SDR_ONE_PACKET, though not always implemented by all devices.

@guruofquality
Copy link
Contributor

SoapyMultiSDR sounds like a good name. I can go ahead and make the project. It sounds like its going to be more or less on-par with how I maintain SoapyRemote -- which is to add new matching calls when SoapySDR gets additions. Its going to be a bit annoying to go through all the calls, but I think I can plow through it in a weekend once I get some time :-)

Also global settings
I was also thinking that some of the calls for global settings (like GPIO) for example might need to have a device index suffix appended to the available names. So device 0 GPIO bank foo will be renamed to foo0, so it doesnt conflict with device 1's foo GPIO which is foo1 now.

@cjcliffe cjcliffe changed the title SoapyMultiSDR? SoapyMultiSDR May 26, 2016
@cjcliffe
Copy link
Contributor Author

Nice, that all sounds good to me; I'll leave it to you to get the project rolling 👍

@jynik
Copy link

jynik commented May 26, 2016

This sounds really cool!

I recently pulled in support for synchronized TRX triggers on the bladeRF, allowing N devices to be sample-synchronized (not phase-aligned, but a constant phase-offset). I could help in getting this up and running in SoapyMultiSDR as well.

To make this happen, I will need to think about how to integrate the following into Soapy:

  • Configuring the reference clock master/slave
  • Configuring the trigger signal master/slave
  • Firing the synchronization trigger once the streams have been started on all devices

@guruofquality - I realize each of these is probably going to require some more thought on my end (and likely separate issue tracker items)... just wanted to chime in so you could delegate some tasks. ;)

@guruofquality
Copy link
Contributor

I started the repo, its empty now, but it will be a good place to put any relevant issues: https://github.com/pothosware/SoapyMultiSDR

@guruofquality
Copy link
Contributor

Presenting the argument naming convention and inception support, feel free to comment before I do something terrible :-)

@guruofquality
Copy link
Contributor

Other than documenting this basic usage on the wiki, SoapyMultiSDR is basically functional from a settings standpoint. The streaming is currently very dumb about heterogeneous stuff happening. So I will have to do something smarter there. But if you wanted to start experimenting with using the driver, I think its in a good place. Here is an example probe:

  • You can see that the find arguments specify the driver as multi, and specify indexed serials to identify the devices: driver=multi,serial[0]=1234567890,serial[1]=etc
  • Calls that worked with keys for unordered maps, or device global component names are indexed to match their respective device, key[index]
  • And calls that worked with single strings for device-wide arguments (like clock source, time source, or driver key) use a CSV format: dev0Thing, dev1Thing
SoapySDRUtil --find
######################################################
## Soapy SDR -- the SDR abstraction library
######################################################

Found device 0
  backend = libusb
  device = 0x02:0x0D
  driver = bladerf
  instance = 0
  serial = fc73b8339f9faed4e577b516adc19104

Found device 1
  backend = libusb
  device = 0x02:0x0F
  driver = bladerf
  instance = 1
  serial = 7db5b78c31a3c05fa40055e813f91dc2


jblum@bigbear:~/build/SoapyMultiSDR$ SoapySDRUtil --probe="driver=multi,serial[0]=fc73b8339f9faed4e577b516adc19104,serial[1]=7db5b78c31a3c05fa40055e813f91dc2"
######################################################
## Soapy SDR -- the SDR abstraction library
######################################################

Probe device driver=multi,serial[0]=fc73b8339f9faed4e577b516adc19104,serial[1]=7db5b78c31a3c05fa40055e813f91dc2
[INFO] Making device 0...
[INFO] bladerf_open_with_devinfo()
[INFO] bladerf_get_serial() = fc73b8339f9faed4e577b516adc19104
[INFO] setSampleRate(1, 1.000000 MHz), actual = 1.000000 MHz
[INFO] setSampleRate(0, 1.000000 MHz), actual = 1.000000 MHz
[INFO] Making device 1...
[INFO] bladerf_open_with_devinfo()
[INFO] bladerf_get_serial() = 7db5b78c31a3c05fa40055e813f91dc2
[INFO] setSampleRate(1, 1.000000 MHz), actual = 1.000000 MHz
[INFO] setSampleRate(0, 1.000000 MHz), actual = 1.000000 MHz

----------------------------------------------------
-- Device identification
----------------------------------------------------
  driver=bladeRF, bladeRF
  hardware=bladeRF, bladeRF
  fpga_size[0]=40
  fpga_size[1]=40
  fpga_version[0]=0.5.0
  fpga_version[1]=0.5.0
  fw_version[0]=1.9.0
  fw_version[1]=1.9.0
  serial[0]=fc73b8339f9faed4e577b516adc19104
  serial[1]=7db5b78c31a3c05fa40055e813f91dc2

----------------------------------------------------
-- Peripheral summary
----------------------------------------------------
  Channels: 2 Rx, 2 Tx
  Timestamps: YES
  Other Settings:
     * XB200 Transverter - Device0 - bladeRF XB200 Transverter Board
       [key=xb200[0], default=disabled, type=string, options=(disabled, 50M, 144M, 222M, auto1db, auto3db, auto, custom)]
     * Sampling Mode - Device0 - Internal = Via RX/TX connectors, External = Direct sampling from J60/J61 connectors
       [key=sampling_mode[0], default=internal, type=string, options=(internal, external)]
     * Loopback Mode - Device0 - Enable/disable internal loopback
       [key=loopback[0], default=disabled, type=string, options=(disabled, firmware, bb_txlpf_rxvga2, bb_txvga1_rxvga2, bb_txlpf_rxlpf, bb_txvga1_rxlpf, rf_lna1, rf_lna2, rf_lna3)]
     * XB200 Transverter - Device1 - bladeRF XB200 Transverter Board
       [key=xb200[1], default=disabled, type=string, options=(disabled, 50M, 144M, 222M, auto1db, auto3db, auto, custom)]
     * Sampling Mode - Device1 - Internal = Via RX/TX connectors, External = Direct sampling from J60/J61 connectors
       [key=sampling_mode[1], default=internal, type=string, options=(internal, external)]
     * Loopback Mode - Device1 - Enable/disable internal loopback
       [key=loopback[1], default=disabled, type=string, options=(disabled, firmware, bb_txlpf_rxvga2, bb_txvga1_rxvga2, bb_txlpf_rxlpf, bb_txvga1_rxlpf, rf_lna1, rf_lna2, rf_lna3)]
  GPIOs: CONFIG[0], EXPANSION[0], CONFIG[1], EXPANSION[1]

----------------------------------------------------
-- RX Channel 0
----------------------------------------------------
  Full-duplex: YES
  Supports AGC: NO
  Stream formats: CS16, CF32
  Native format: CS16 [full-scale=2048]
  Stream args:
     * Buffer Count - Number of async USB buffers.
       [key=buffers, units=buffers, default=32, type=int]
     * Buffer Length - Number of bytes per USB buffer, the number must be a multiple of 1024.
       [key=buflen, units=bytes, default=4096, type=int]
     * Num Transfers - Number of async USB transfers. Use 0 for automatic
       [key=transfers, units=bytes, default=0, type=int, range=[0, 32]]
  Antennas: RX
  Full gain range: [0, 61] dB
    LNA gain range: [0, 6] dB
    VGA1 gain range: [5, 30] dB
    VGA2 gain range: [0, 30] dB
  Full freq range: [237.5, 3800] MHz
    RF freq range: [237.5, 3800] MHz
  Sample rates: [0.16, 40] MHz
  Filter bandwidths: [1.5, 28] MHz

----------------------------------------------------
-- RX Channel 1
----------------------------------------------------
  Full-duplex: YES
  Supports AGC: NO
  Stream formats: CS16, CF32
  Native format: CS16 [full-scale=2048]
  Stream args:
     * Buffer Count - Number of async USB buffers.
       [key=buffers, units=buffers, default=32, type=int]
     * Buffer Length - Number of bytes per USB buffer, the number must be a multiple of 1024.
       [key=buflen, units=bytes, default=4096, type=int]
     * Num Transfers - Number of async USB transfers. Use 0 for automatic
       [key=transfers, units=bytes, default=0, type=int, range=[0, 32]]
  Antennas: RX
  Full gain range: [0, 61] dB
    LNA gain range: [0, 6] dB
    VGA1 gain range: [5, 30] dB
    VGA2 gain range: [0, 30] dB
  Full freq range: [237.5, 3800] MHz
    RF freq range: [237.5, 3800] MHz
  Sample rates: [0.16, 40] MHz
  Filter bandwidths: [1.5, 28] MHz

----------------------------------------------------
-- TX Channel 0
----------------------------------------------------
  Full-duplex: YES
  Supports AGC: NO
  Stream formats: CS16, CF32
  Native format: CS16 [full-scale=2048]
  Stream args:
     * Buffer Count - Number of async USB buffers.
       [key=buffers, units=buffers, default=32, type=int]
     * Buffer Length - Number of bytes per USB buffer, the number must be a multiple of 1024.
       [key=buflen, units=bytes, default=4096, type=int]
     * Num Transfers - Number of async USB transfers. Use 0 for automatic
       [key=transfers, units=bytes, default=0, type=int, range=[0, 32]]
  Antennas: TX
  Full gain range: [0, 56] dB
    VGA1 gain range: [-35, -4] dB
    VGA2 gain range: [0, 25] dB
  Full freq range: [237.5, 3800] MHz
    RF freq range: [237.5, 3800] MHz
  Sample rates: [0.16, 40] MHz
  Filter bandwidths: [1.5, 28] MHz

----------------------------------------------------
-- TX Channel 1
----------------------------------------------------
  Full-duplex: YES
  Supports AGC: NO
  Stream formats: CS16, CF32
  Native format: CS16 [full-scale=2048]
  Stream args:
     * Buffer Count - Number of async USB buffers.
       [key=buffers, units=buffers, default=32, type=int]
     * Buffer Length - Number of bytes per USB buffer, the number must be a multiple of 1024.
       [key=buflen, units=bytes, default=4096, type=int]
     * Num Transfers - Number of async USB transfers. Use 0 for automatic
       [key=transfers, units=bytes, default=0, type=int, range=[0, 32]]
  Antennas: TX
  Full gain range: [0, 56] dB
    VGA1 gain range: [-35, -4] dB
    VGA2 gain range: [0, 25] dB
  Full freq range: [237.5, 3800] MHz
    RF freq range: [237.5, 3800] MHz
  Sample rates: [0.16, 40] MHz
  Filter bandwidths: [1.5, 28] MHz

[INFO] bladerf_close()
[INFO] bladerf_close()

@ncorgan ncorgan closed this as completed Nov 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants