Skip to content

rsba1server — a standalone RS-BA1 network radio server

rsba1server is an open-source, from-scratch clone of the server side of Icom's RS-BA1 "Remote Utility". It attaches to a radio (or an SDR) and serves it over the network using Icom's RS-BA1 UDP protocol, so any RS-BA1 client can control it remotely:

  • Icom's own RS-BA1 software,
  • wfview,
  • the Rafe web app in this repo.

It exists so a radio that only has USB (like the IC-7300) — or a bare SDR — can be operated over the network exactly the way a networked IC-705 is. It's pure Python 3, no GUI, and runs happily on a Raspberry Pi.


What it speaks

Three UDP streams:

Port Stream Purpose
50001 control login/passcode handshake, token/auth, capabilities + connection-info exchange, pkt7 keepalives, retransmit
50002 CI-V transparent CI-V serial bridge between client and radio
50003 audio RX audio radio→client and TX audio client→radio

The protocol was implemented from this repo's own RS-BA1 client (app/radio/) plus wfview's server as a reference, and is verified end-to-end against that client (test_rsba1.py): login, CI-V request/reply, and RX+TX audio. Audio is uncompressed 16-bit LPCM (what the IC-705 path uses); the Opus/µ-law codec options some clients offer are not implemented — request LPCM on the client if asked.

One authenticated control client per source IP.


Backends

rsba1server can serve three kinds of source:

  • usbUsbRadio: a real USB Icom radio. CI-V over pyserial, audio via ALSA arecord/aplay (resampled to/from the device rate).
  • soapySoapyRadio: any SoapySDR receiver (RTL-SDR, LimeSDR Mini, HackRF, Airspy…). CI-V frequency/mode retunes the SDR and switches the demodulator (FM / AM / USB / LSB / CW); demodulated audio is streamed to the client. RX only. --backend rtlsdr / --backend lime are shortcuts.
  • fakeFakeRadio: answers common CI-V polls and emits a test tone; no hardware. Used by test_rsba1.py and for trying a client.

Running it independently

The server is a self-contained package — copy rsba1server/ to the machine next to the radio and run it with the system Python.

A USB radio (e.g. IC-7300)

python3 -m rsba1server --backend usb \
    --serial /dev/ttyUSB0 --civ-address 0x94 --device IC-7300 \
    --audio-in plughw:1,0 --audio-out plughw:1,0 \
    --user patrick --password secret

Find the serial port with ls /dev/serial/by-id/ and the USB-audio ALSA device with arecord -l / aplay -l (then plughw:<card>,0).

CI-V addresses: IC-7300 0x94, IC-705 0xA4, IC-9700 0xA2, IC-7610 0x98, IC-7100 0x88, IC-7851 0x8E, IC-905 0xAC.

An SDR (RTL-SDR / LimeSDR Mini)

# RTL-SDR
python3 -m rsba1server --backend rtlsdr --device "RTL-SDR" \
    --civ-address 0x94 --user patrick --password secret
# LimeSDR Mini
python3 -m rsba1server --backend lime --device "LimeSDR Mini" ...
# any SoapySDR device by driver key
python3 -m rsba1server --backend soapy --driver hackrf --samp-rate 2000000 ...

Options: --driver (SoapySDR key), --samp-rate (IQ rate, default 240000), --sdr-gain (dB; omit for AGC), --ppm (frequency correction).

A simulator (no hardware)

python3 -m rsba1server --backend fake --device "IC-705 (sim)" \
    --user patrick --password secret

Then point a client at the host's IP, port 50001, with those credentials.

From a config file

python3 -m rsba1server --config /etc/rsba1-server.json

See rsba1server/rsba1-server.example.json. As a systemd service, use deploy/rsba1-server.service (it runs from /opt, SupplementaryGroups=dialout audio for serial + USB-audio access).


Multiple remote SDRs

There are two independent ways to add remote receivers, and you can mix them.

1. Several SDRs each served as a radio (switch between them)

Run one rsba1server instance per SDR — on the same host (different ports) or on separate Raspberry Pis around the site — and add each as a radio in the web app's radio switcher (⚙ dialog). Each becomes a selectable "radio":

# Pi in the loft, wideband discone -> RTL-SDR
python3 -m rsba1server --backend rtlsdr --device "Loft RTL" \
    --user patrick --password secret          # (host: 192.168.1.20)

# Pi in the shack, LimeSDR on a beam
python3 -m rsba1server --backend lime --device "Beam Lime" \
    --user patrick --password secret          # (host: 192.168.1.21)

In the app, add two radios pointing at 192.168.1.20 and 192.168.1.21; the switcher flips the whole panadapter/audio/CI-V head between them. To run more than one instance on a single host, give each its own bind/ports via the config file.

2. Remote data collectors (feed decoders, not a radio head)

For data rather than a tunable head — AIS ship positions today — a Pi with a cheap RTL-SDR runs in collector mode and forwards decoded traffic to the central Rafe server, where every collector's output is merged onto one map:

python3 -m rsba1server --collect ais \
    --feed <server-ip>:10110 \
    --serial 00000001 --sdr-gain 40
  • --collect ais runs AIS-catcher on the local dongle and forwards AIVDM NMEA over UDP to --feed HOST:PORT (the server's AIS ingest port, default 10110).
  • --serial selects the dongle; --sdr-gain sets the tuner gain; --driver selects a SoapySDR device instead (e.g. a LimeSDR on the Pi).
  • It supervises the decoder and restarts it if it dies.

Point as many collectors at the server's port as you like — no per-collector setup on the server. The server also has its own local SDR path (see the AIS and satellite-decode sections of the install guide); the local SDR and a satellite decode are mutually exclusive because they share one SDR, but remote collectors are independent and always on.


Status / limitations

  • Verified against this repo's RS-BA1 client (control + CI-V + audio, both directions). Testing against Icom's own client / wfview is recommended before relying on it on the air.
  • The SoapySDR demodulator is verified against synthetic IQ; on-air reception against a physical dongle may need gain/sample-rate and per-mode audio-gain tuning in demodulate().
  • SDR backends are receive-only; TX is not implemented for SDR.