Skip to content
Josh Blum edited this page Mar 2, 2019 · 47 revisions

Remote support for Soapy SDR

https://raw.githubusercontent.com/wiki/pothosware/SoapyRemote/images/soapy_sdr_remote_logo.png

Use any SoapySDR supported device transparently over a local network link. The remote support feature can turn any SDR into a network peripheral.

Potential use-cases for Soapy Remote:

  • share the SDR device over a network
  • use the device in multiple processes
  • or use the device on multiple hosts
  • a multi-threaded abstraction layer
  • aid in embedded-device development
  • or a work-around for software issues
  • adapt an IPv4 SDR for an IPv6 network

Pre-built installers for various systems are available through the Pothos SDR environment.

The remote device support has no additional dependencies other than SoapySDR itself:

Recommend installing avahi on Ubuntu (used for device discovery):

sudo apt-get install avahi-daemon libavahi-client-dev

The CMake build system will locate SoapySDR development files on your system. The plugin module and SoapySDRServer application will be built and installed into the SoapySDR modules directory and bin/ directory (respectively). The SoapyRemote project must be installed on both the remote and local system.

git clone https://github.com/pothosware/SoapyRemote.git
cd SoapyRemote
mkdir build
cd build
cmake ..
make
sudo make install

Run the server on the remote machine (the machine with the SDR hardware):

SoapySDRServer --bind

Or specify a custom bind address and port:

SoapySDRServer --bind="0.0.0.0:1234"

IPv6 address URLs are also supported:

SoapySDRServer --bind="[::]:1234"

Start the server in the background:

sudo systemctl start SoapySDRServer

#is it running?
journalctl -u SoapySDRServer

Start the server automatically on startup:

sudo systemctl enable SoapySDRServer

The device should be supported transparently over the network using the standard SoapySDR API, SoapySDRUtil, and any applications built on top of the SoapySDR API. However, users will need to specify an additional device argument to engage the remote support module:

By default, the remote module will attempt to automatically discover and enumerate servers on the local network. Specify the "remote" key to engage the support module at a specific address. The value of the "remote" key should be the remote server's hostname or IP address. If a custom port was selected, the value should be specified as "myServer:portNum":

SoapySDRUtil --find="remote=myServer"

Filter out local devices with the "driver" key set to "remote". When the "driver" key specifies the "remote" plugin module, all other plugin modules will be ignored by the device enumeration routine:

SoapySDRUtil --find="remote=myServer, driver=remote"

Specify the "remote:driver" key to filter plugins on the remote system. The args will be rewritten to strip out the "remote:" key prefix. Use the "remote:" prefix to apply keys to the remote device only that would otherwise conflict with local keys:

SoapySDRUtil --find="remote=myServer, remote:driver=bladerf"

The default connection timeout is 50 milliseconds. In rare cases it may be useful to modify this timeout. Specify a new timeout value in units of microseconds using the "remote:timeout" device argument:

SoapySDRUtil --find="remote=myServer, remote:timeout=1000000"

Use UHD with a remote device when SoapyUHD is installed. The same instructions for "driver" and "remote" keys apply. We can even use SoapyRemote to share the device locally to several processes:

uhd_find_devices --args="driver=remote,remote=myServer"

#use only SoapyRemote when UHD devices are available locally
uhd_find_devices --args="driver=remote,remote=myServer,type=soapy"

#example use of explicit type filter on remote server
uhd_find_devices --args="driver=remote,remote=myServer,remote:type=b200"

Using the Soapy SDR support included in the GrOsmoSDR project, most devices can be used remotely through the GrOsmoSDR blocks and API.

First make sure that "Soapy" is one of the configured components when building GrOsmoSDR. Next, with the SoapySDRServer running, the following args may be used with the gr-osmosdr source or sink block to work with a remote device.

Key Value Required? Description
remote <myServer> required Specifies the server's hostname or address to SoapyRemote.
soapy 0 optional Instructs GrOsmoSDR to use the Soapy support. Required if GrOsmoSDR might find other devices on the local machine.
driver remote optional Instructs SoapySDR to use the remote module. Required if SoapySDR might find other devices on the local machine.
remote:driver <driverKey> optional Required if SoapySDR might find other devices on the remote machine. Example: "remote:driver=rtlsdr"

Putting it all together:

#No devices locally, only one desired device on the remote server
remoteArgs="remote=myServer"

#One or more local devices, and multiple devices on the remote server
remoteArgs="remote=myServer,soapy=0,driver=remote,remote:driver=rtlsdr"

SoapyRemote streams can use either udp or tcp (udp is default). The "remote:prot" key is typically a stream-specific argument, however it can also be passed in as a device argument to set the default for all streams when "remote:prot" is not specified. See the Remote stream args section for more information...

Advanced use: By default, the Soapy SDR server will bind to both IPv4 and IPv6 by default. When a remote address is not specified, the client will be able to discover both IPv4 and IPv6 services. When both services are discovered, IPv4 will be the preference. Use the "remote:ipver" key to selected the desired IP protocol version:

SoapySDRUtil --find="remote:ipver=6"
  • Notice that the resulting remote addresses field will be in IPv6 format.
  • If only one protocol is discovered, a result is yielded regardless of ipver.

There are several optional stream arguments for setupStream() that can be used to tweak the performance of the remote stream forwarding. Regardless, we recommend that linux users follow the instructions for sysctrl and limits.conf, as SoapyRemote can automatically take advantage of these reconfigured settings.

The remote format key specifies the stream format that should be used on the remote device. The default behaviour uses the device's native format over the link when the conversion to the local format is supported. Otherwise the remote and local endpoints use the same data format.

However, users can manually override the remote format setting: Specify {"remote:format":"CS16"} to use complex 16-bit integers over the link, or specify {"remote:format":"CS8"} to use complex 8-bit integers over the link; and the remote stream driver will perform the conversion to/from floating point.

The remote scale specifies the floating point scale factor used to convert between local and remote formats specified above. By default, the device's native full-scale value is used to convert between floats locally and integers over the link. Users can manually override this scaling by specifying the remote scale. For example: {"remote:format":"CS16", "remote:scale":"2048"}

Example with Python bindings:

sdr_device.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CS16, [0], {"remote:scale": "2048"})

The remote MTU specifies the maximum datagram transfer size in bytes. By default the MTU is just under 1500 bytes (the default ethernet MTU). Users may increase the MTU to improve throughput. Not all hardware supports jumbo-frames. Check your ethernet configuration and hardware support before using this option.

On linux, use ifconfig to set the ethernet MTU:

sudo ifconfig eth0 mtu 4096

The remote window specifies the size of the kernel socket buffer in bytes. For applications that can tolerate large queues of samples, a large socket buffer (10s of megabytes) is recommended. The operating system may limit the upper bounds of the socket buffer size. The driver will print a warning if the buffer limit is exceeded.

On linux, use sysctrl to set the maximum socket buffer size:

#I like big buffers and I can not lie
#You other developers can't deny
#That when a socket walks in with an itty bitty SO_RCVBUF
#And a sysctl limit in your face
#You must run,
sudo sysctl -w net.core.rmem_max=104857600
sudo sysctl -w net.core.wmem_max=104857600

The remote priority specifies the scheduling priority of the server forwarding threads. Its recommended to use an elevated priority for the forwarding threads to reduce latency and to avoid potential hiccups like overflows and underflows. The priority is a floating point value between -1.0 (low), 0.0 (normal), and 1.0 (high). By default, the server will try to elevate the priority for the forwarder threads. Users may need to tweak their system configuration to allow elevated thread priority. The driver will print a warning if the thread priority setting is not possible.

To allow elevated priority on linux, edit /etc/security/limits.conf and add the following line for your username:

<username> hard rtprio 99

The remote protocol specifies using the tcp or udp protocol for streams. By default SoapyRemote streams are based around udp to preference throughput over reliability. However, some applications will require reliability and cannot afford the chance of packet loss. Example: {"remote:prot":"udp"} or {"remote:prot":"tcp"}

SoapyRemote is composed of two major pieces: The server, which is an executable that runs on the remote machine. And the client, which is just a regular SoapySDR plugin module that knows how to communicate with the server.

The server operates a simple RPC protocol over the reliable TCP socket layer. The client connects to the server to perform device discovery, creation, and configuration settings. Stream configuration and controls are also implemented through the RPC, however the streams themselves use a separate protocol for high bandwidth.

Streaming operates through a windowed-datagram protcol over the unreliable UDP socket layer (although it can be switched to TCP for reliability at the expense of maximum throughput). A stream receiver endpoint socket is responsible for buffering the incoming stream data, and sending out flow control messages to the sender endpoint. The sender endpoint waits on flow control messages and forwards the stream data to the receiver. The goal of the flow control is to never send more data into the link, than there is space within the socket buffers.

Clone this wiki locally