Remote RF Server Guide (Linux)
Environment and setup
This guide installs Miniconda, installs mamba, creates a conda env named remoterf, installs dependencies, and verifies the install on Ubuntu Server 24.04 LTS.
- This guide is done APT-based distro (Ubuntu/Debian/Raspberry Pi OS 64-bit).
1) System Prerequisites
sudo apt update
sudo apt install -y curl ca-certificates bzip2 git build-essential
sudo apt install -y libusb-1.0-0 udev
Optional: confirm architecture:
uname -m
x86_64→ Intel/AMDaarch64→ ARM64 (Raspberry Pi 64-bit, some servers)
2) Install Miniconda
2.1 Download the installer
x86_64
cd /tmp
curl -fsSLO https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
ARM64 (aarch64)
cd /tmp
curl -fsSLO https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh
2.2 Install (non-interactive, recommended)
x86_64
bash Miniconda3-latest-Linux-x86_64.sh -b -p "$HOME/miniconda3"
ARM64 (aarch64)
bash Miniconda3-latest-Linux-aarch64.sh -b -p "$HOME/miniconda3"
2.3 Enable conda in your current shell
source "$HOME/miniconda3/etc/profile.d/conda.sh"
conda --version
If you want conda available automatically in new terminals:
"$HOME/miniconda3/bin/conda" init bash source ~/.bashrc
2.4 Install mamba (default solver)
conda install -n base -c conda-forge -y mamba
mamba --version
Might have to accept anaconda TOS.
conda tos accept --override-channels --channel https://repo.anaconda.com/pkgs/main
conda tos accept --override-channels --channel https://repo.anaconda.com/pkgs/r
3) Create the Environment
mamba create -n remoterf -y -c conda-forge -c defaults python=3.10 pip setuptools wheel grpcio protobuf python-dotenv numpy scipy libiio pylibiio libusb
conda activate remoterf
python -m pip install -U pip
python -m pip install remoterf-server
The base remoterf-server package installs the Python runtimes for every
packaged device driver, including ADALM-Pluto, RTL-SDR, and HackRF. No device
extras are required.
Server Config
Depending on your IT/ISP setup, this will vary, but regardless, you will need a static dial name, whether that be a IP address or a DNS.
Generate CA Certificates
If direct IP connection: (force overrides existing)
serverrf --gen-certs 192.168.1.50 --days 3650 --force
If using DNS: (todo: DNS on client side)
serverrf --gen-certs 192.168.1.50 --dns remoterf.local --days 3650 --force
Confirm/View:
serverrf --show-certs
Specify Outward facing Ports
By default, the users expect to adjacent ports. Thus, it is recommended to do the following:
# examples
serverrf --config --main-port 61000 --cert-port 61001
serverrf --config --main-port 20000 --cert-port 20001
serverrf --config --main-port 32000 --cert-port 32001
Confirm/View:
serverrf --config --show
Testing Connection
The server now should be functional in its most basic form. In the same conda env:
serverrf --serve
If you see something along the lines of: self.socket.bind(self.server_address) PermissionError: [Errno 13] Permission Denied, make sure the ports you are using are not OS reserved (<1024)
Confirm it works locally
Start the server:
serverrf -s
For a loopback-only development server, explicitly bind both services to localhost:
REMOTERF_BIND_HOST=127.0.0.1 GRPC_PORT=55051 CERT_PORT=55052 \
serverrf --serve --headless
Headless and interactive shutdown both stop accepting RPCs, close all Dynamic v2 sessions/handles and native devices, stop the certificate provider, and terminate the reservation-update worker.
Take note of the Local IP and Local Port, for example, if you see:
Local IP: 164.67.195.210
Local Port: 61000
then run the below on a seperate terminal (make sure to keep serverrf -s RUNNING!)
Confirm that its reachable:
nc -vz 164.67.195.210 61000
nc -vz 164.67.195.210 61001
Network Testing
Same test, but on a different computer. Works for UNIX based machines:
Confirm that the Server is reachable:
nc -vz 164.67.195.210 61000
nc -vz 164.67.195.210 61001
You most likely will need to port forward, etc.
Troubleshooting: Some distros come with default firewall settings that block traffic on specific ports. Make sure that your firewall settings permit traffic to and from the server. Depending on the setup, you may also have ISP firewalls.
Server Device Configuration
ADALM-Pluto, RTL-SDR, TI mmWave, and local or HostRF-owned UHD inventories are supported.
Every USRP remains registered as device type usrp; a structured profile
selects the UHD transport and runtime capabilities report the model actually
opened.
To connect plutos to the server:
iio_info -s
If the pluto doesn't show up, yet the below works:
sudo iio_info -s
Run the below and reboot after:
sudo groupadd -f plugdev
sudo usermod -aG plugdev "$USER"
sudo tee /etc/udev/rules.d/53-adi-usb.rules >/dev/null <<'EOF'
# Type the below in
SUBSYSTEM=="usb", ATTR{idVendor}=="0456", MODE="0660", GROUP="plugdev"
EOF
sudo udevadm control --reload-rules
sudo udevadm trigger
sudo reboot now
The below should work as intended now:
iio_info -s
Look for 'serial='. Take note of this serial.
serverrf --device --add --pluto <device_id:name:serial>
# example
serverrf --device --add --pluto 0:pluto_0:104473f6
serverrf --device --add --pluto "1:Pluto SDR (OTA):58472j"
Run the below to check if this new device exists:
serverrf --device --show
Run the below to edit names of existing devices:
serverrf -d --edit-name 0 "New Name"
Note that all device and server config parameters need a 'restart' to take affect (ctrl + c -> serverrf -s).
RTL-SDR
RTL-SDR is a packaged receive-only schema driver. Its Python wrapper and
packaged native library are required dependencies of remoterf-server; clients
do not need either dependency. Install the optional command-line tools only if
you want to run the local rtl_test diagnostic:
conda install -c conda-forge rtl-sdr
pyrtlsdrlib supplies a matching native library for PyRtlSdr. This avoids
loading an incompatible system librtlsdr that is missing optional symbols.
For RTL-SDR Blog V4 hardware, use a native library with V4/R828D support.
Confirm discovery before adding the inventory entry:
rtl_test -t
Add by stable USB serial when possible, or by zero-based index when a device has no useful serial:
serverrf --device --add --rtl-sdr 4:rtl_fm:serial=00000001
serverrf --device --add --rtl-sdr 4:rtl_fm:index=0
serverrf --device --show
The canonical YAML form is:
devices:
- device_id: 4
device_type: rtl_sdr
name: rtl_fm
init:
serial: "00000001"
The schema exposes center frequency (center_freq/fc), sample rate
(sample_rate/rs), gain, valid gains, frequency correction, bandwidth, AGC,
bias tee, direct sampling, offset tuning, dithering, bounded read_samples,
bounded read_bytes, buffer reset, and identity.
Optional native controls are probed at runtime. An unsupported control remains
discoverable but raises a clear NotImplementedError. Remote async callbacks
are intentionally not exposed because callbacks cannot cross an RPC boundary;
clients perform repeated bounded synchronous reads instead.
Ubuntu may load the DVB kernel driver for the dongle. A compatible
librtlsdr can detach it while opening the device. If that is disabled in the
installed native build, blacklist dvb_usb_rtl28xxu or unload it before
starting RemoteRF.
HackRF
HackRF is a packaged schema driver backed by pyhackrf2, which is installed by
the base remoterf-server package. Install the native HackRF runtime and tools
on the server that owns the radio:
sudo apt-get install hackrf
hackrf_info
Add each device to ~/.config/remoterf/devices.yml by stable serial when
possible:
devices:
- device_id: 10
device_type: hackrf
name: HackRF One
init:
serial: "0000000000000000719031ac235bb14a"
The generated HackRF client exposes center frequency, sample rate, filter
bandwidth, LNA/VGA/TXVGA gains, front-end amplifier state, bias tee state,
receive sample-count limit, bounded synchronous IQ reads, TX-buffer loading,
enumeration, and serial-number access. The wrapper also compensates for the
recursive LNA getter and stale amplifier state in pyhackrf2 1.0.3.
TI mmWave radar
The packaged ti_mmwave v1 schema initially targets xWR68xx out-of-box demo
firmware using a Silicon Labs CP2105 dual-UART bridge. Runtime control uses the
Enhanced interface at 115200 baud. Binary TLV output uses the Standard
interface at 921600 baud. pyserial is installed with remoterf-server.
On macOS, install the official Silicon Labs CP210x VCP driver first. Reconnect the radar and verify that two serial device nodes exist:
ls /dev/cu.*
python -m serial.tools.list_ports -v
Add the device by the stable CP2105 bridge serial printed by the second command:
serverrf --device --add --ti-mmwave 20:ti_radar:00DF4F69
serverrf --device --show
The canonical inventory supports explicit port overrides when an operating system does not publish the Enhanced/Standard interface names:
devices:
- device_id: 20
device_type: ti_mmwave
name: ti_radar
init:
serial: "00DF4F69"
firmware_profile: xwr68xx_oob_sdk3
cli_baud: 115200
data_baud: 921600
frame_queue_depth: 8
# cli_port: /dev/cu.SLAB_USBtoUART-enhanced
# data_port: /dev/cu.SLAB_USBtoUART-standard
RemoteRF owns both UART handles while the server is running. The schema exposes
runtime CLI commands, ordered .cfg application, sensor start/stop, version
query, queue health, and bounded complete-frame reads. Firmware flashing and
board SOP-mode changes are intentionally outside the remote API.
Unit tests do not require hardware. After both UARTs enumerate, run the opt-in
hardware smoke test without starting serverrf or another serial application:
REMOTERF_TI_MMWAVE_SERIAL=00DF4F69 \
python -m unittest tests.test_ti_mmwave_hardware
If interface-name discovery is unavailable, also set
REMOTERF_TI_MMWAVE_CLI_PORT and REMOTERF_TI_MMWAVE_DATA_PORT.
To extend the smoke test through configuration, sensor start, and one complete
binary frame, set REMOTERF_TI_MMWAVE_CONFIG to a board-compatible TI .cfg
file. REMOTERF_TI_MMWAVE_FRAME_TIMEOUT optionally changes the five-second
frame deadline.
Structured USRP profiles
The NI USRP-2901, Ettus Research USRP B205-mini, and USRP N210 are
hardware-qualified native profiles in the packaged RemoteRF server and client.
They do not require a custom schema in
~/.config/remoterf/drivers/. A fresh server installs one shared
usrp schema for all of them and for generic UHD families.
Profiles are data, not separate drivers. Each entry declares its canonical ID, aliases, UHD family/type, transport, selector, qualification level, and live identity match terms. The current registry contains:
| Profile | Transport | Support level | Examples |
|---|---|---|---|
usrp2901 |
USB | qualified_native |
NI USRP-2901 |
b205mini |
USB | qualified_native |
Ettus Research USRP B205-mini/B205mini-i |
n210 |
Ethernet | qualified_native |
USRP N210 |
b2xx |
USB | generic_uhd |
B200, B210, B200mini |
n2xx |
Ethernet | generic_uhd |
N200 and unqualified N2xx variants |
x3xx |
Ethernet | generic_uhd |
X300, X310 |
n3xx |
Ethernet | generic_uhd |
N300, N310, N320, N321 |
x4xx |
Ethernet | generic_uhd |
X410, X440 |
e3xx |
Embedded | generic_uhd |
E310, E320 |
generic_usrp |
Runtime-detected | generic_uhd |
UHD fallback |
generic_uhd means the device uses the same native uhd.usrp.MultiUSRP
control and streaming path, but RemoteRF has not yet run that model's physical
qualification suite. Frequency ranges, gains, channels, sensors, clocks, and
OTW support are still read/probed from the live device instead of copied from
the profile.
The server automatically installs and registers its complete USRP v1/v2
schema on first startup, so clients can discover the API even when UHD is not
installed locally. Existing custom usrp drivers in
~/.config/remoterf/drivers/ take precedence over that default.
Opening a real USRP session requires the server or HostRF machine that owns the hardware to
have the exact supported UHD Python API, UHD 4.10.0.0, installed. The
server does not open the device while loading inventory. It opens the
reservation's exact type=b200,serial=... device only when the authenticated
client opens a v2 session.
Add a qualified serial-bound NI USRP-2901:
serverrf --device --add --usrp-2901 7:usrp2901_lab:31A2B3C
serverrf --device --show
The older --usrp2901 and --usrp spellings remain accepted as aliases for
--usrp-2901 so existing provisioning scripts continue to work.
Add a qualified serial-bound Ettus Research USRP B205-mini:
serverrf --device --add --usrp-b205-mini 9:b205mini_lab:31E9F48
serverrf --device --show
The --usrp-b205mini and --b205mini spellings are accepted as aliases.
Registration stores the explicit b205mini profile, UHD type=b200, and the
device serial, so it remains distinct from an NI USRP-2901 on the same server.
Add a qualified address-bound N210 on a dedicated Ethernet interface:
serverrf --device --add --usrp-n210 8:n210_lab:192.168.137.21
serverrf --device --show
Add another registered family through the generic profile command:
serverrf --device --add --usrp-profile \
10:x310_lab:x3xx:192.168.10.2
The final value is interpreted according to the profile's selector: serial
for USB profiles, address for network profiles, or raw UHD device arguments for
the generic/embedded profiles. Alias names such as x310, b210, and usrp2
are normalized to their canonical family profile.
The canonical inventory is ~/.config/remoterf/devices.yml:
devices:
- device_id: 7
device_type: usrp
name: usrp2901_lab
init:
profile: usrp2901
type: b200
serial: 31A2B3C
- device_id: 8
device_type: usrp
name: n210_lab
init:
profile: n210
type: usrp2
addr: 192.168.137.21
- device_id: 9
device_type: usrp
name: b205mini_lab
init:
profile: b205mini
type: b200
serial: 31E9F48
The NI USRP-2901 and B205-mini inventories each use an explicit profile plus
their unique type=b200,serial=... selector. The N210 inventory is independently
constrained to type=usrp2,addr=..., preventing a reservation from opening a
different UHD transport. All profiles publish the same generated
uhd.usrp.MultiUSRP API. device_profile contains the resolved structured
definition, while profile_resolution shows whether it came from live native
identity or inventory and flags a configured/live profile mismatch.
To add a newly qualified USRP model, add one USRPProfile entry in
remoteRF_server/drivers/usrp/profiles.py, give it identity terms and the
correct UHD selector/type, and add a read-only hardware qualification test.
No new server driver, gRPC service, or client API implementation is required.
When a USRP session opens, RemoteRF asks native UHD to construct inactive RX
streamers for UHD 4.10's default, sc16, sc12, and sc8 wire formats. No
stream command is issued and no samples are received. Successful probes are
cached for that hardware session, populate stream_format_capabilities.rx,
and determine the backward-compatible supported_otw_formats list. TX formats
are not probed during capability discovery. Set
REMOTERF_USRP_PROBE_RX_OTW_FORMATS=0 before starting the server to disable
the RX probe; clients may still leave StreamArgs.otw_format empty and let UHD
select its default.
All USRP operations exposed by the Dynamic v2 schema are available without a
separate policy file. This includes transmit controls, transmit streaming,
GPIO/register methods, and property-tree writes. usrp_policy.yml is not read
or required by the current server.
RemoteRF forces UHD_LOG_CONSOLE_LEVEL=off before importing UHD so native UHD
messages do not overwrite the interactive serverrf prompt. To temporarily
restore UHD diagnostics, explicitly set REMOTERF_UHD_CONSOLE_LEVEL to a level
such as warning, info, or debug before starting the server:
REMOTERF_UHD_CONSOLE_LEVEL=warning serverrf -s
On every qualified target, generate the introspection record and enforce the pinned API gate:
python -m remoteRF_server.tools.introspect_uhd \
--output uhd-4.10.0.0-target.json
The command exits nonzero for a version mismatch, a missing required member,
an unclassified public MultiUSRP member, or any still-deferred schema entry.
Hardware differential,
two-channel/full-duplex, USB 2/3, external clock/PPS, policy-bypass, and
qualified-LAN performance tests remain mandatory release gates; they cannot
be completed on a checkout without UHD and a physical NI USRP-2901.
To qualify the actual two-channel RX path through both checkouts on the Linux USRP host, run the opt-in localhost client/server test from the server repo:
REMOTERF_TEST_USRP_SERIAL=31A2B3C \
REMOTERF_CLIENT_SRC=../RemoteRF-Client/src \
python -m unittest discover -s tests \
-p test_usrp_client_server_hardware.py -v
This opens the serial-bound device, negotiates the v2 schema over a real localhost gRPC socket, configures both RX channels through the client API, receives raw binary IQ, restores the prior RX settings, and closes the remote stream/session. It is RX-only and does not enable transmit.
Dynamic v2 control and SampleDataV1.SampleStream run on the existing TLS
gRPC channel. For HostRF-owned devices, the server proxies the same protobuf
operations through the authenticated host tunnel and releases the remote
session if that tunnel disconnects. IQ samples are carried as bounded raw binary payloads with
dtype/shape/channel metadata; JSON/base64 and repeated-float sample transport
are not used.
Server Runtime Configuration
After running serverrf --serve, you’ll enter the RemoteRF Server Shell. This interactive shell is used for runtime administration: users, devices, reservations, user groups, enrollment codes, and live host-tunnel state.
Server
help/h/?— Show helpclear/cls— Clear the screenstatus/server status— Show server status (start time / uptime / bind + cert ports)quit/exit/q— Exit the server shell
Users
users list— List all accountsusers manage— Manage a specific user (perms / delete / reservations)users purge— Delete all usersusers perms— Show permissions table
Devices
devices list— List all devicesdevices status— Show live device routing/online/last_seen from the Host Tunnel
Reservations
reservations list— List all reservationsreservations purge— Delete all reservations
Groups
groups list— List user groupsgroups create— Create a user group (interactive)groups edit— Edit a user group (interactive)groups delete— Delete a user group (interactive)groups csv— Export all groups as a CSV
Enrollment Codes
codes list— List enrollment codescodes create— Create enrollment codes (interactive)codes delete— Delete an enrollment code (interactive)codes csv— Export all codes as a CSV
Host Tunnel
hosts status— Show live host online/last_seen and associated deviceshosts wipe— Wipe persisted host directory state and clear in-memory registry (prompts for confirmation)hosts wipe -y/hosts wipe --yes— Same as above, no prompt
Database
db purge— Remove all database entries
Adding Hosts
You will need, for each host, to run this first on the server CLI:
serverrf --host --token-create host-name --length 8
Which will return the corresponding command to run on the respective host to give that host access to the server. Below are some other related commands (ie: 'whitelists' hosts)
serverrf --host --show
serverrf --host --delete host-name
serverrf --host --wipe
Status (Optional)
If you desire to pull the server information (ie: live updates of reservation, server usage parameters, etc.)
It uses GitHub Gists to push the status updates.
Create public gist on your github. Keep note of the file name: (example) https://gist.github.com/ethange1/2a35e08a90bf88a70dfe7f42a55685ed
The last one is the "GIST_ID" part of the URL, e.g. "2a35e08a90bf88a70dfe7f42a55685ed"
Creating the GitHub PAT Token
- GitHub → Settings
- Developer settings
- Personal access tokens → Fine-grained tokens
- Generate new token
- Set repository access to "Public Respositories"
- Permissions: enable Gists: Read and write
- Generate + copy the token (you only see it once)
serverrf --gist --set --id <gist_id> --file <filename>
Run the server normally, and you should see your gist change every minute!
serverrf -s
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file remoterf_server-1.0.15.tar.gz.
File metadata
- Download URL: remoterf_server-1.0.15.tar.gz
- Upload date:
- Size: 229.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4aea6efa928bc7ca942166f4df9880c505b28cb042281295d5fd74f40b71f9c9
|
|
| MD5 |
a6d0bcf1d7e5fe71671b707e4bfab019
|
|
| BLAKE2b-256 |
b48e46e1bd68d66ea16245bd4aed84f1dcce3c494c839535e5c564157ec021ea
|
File details
Details for the file remoterf_server-1.0.15-py3-none-any.whl.
File metadata
- Download URL: remoterf_server-1.0.15-py3-none-any.whl
- Upload date:
- Size: 189.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65f3ed12bd7c6777a7f6d4c96d1fbcf3c711c99a6190996630a162c88a0e3dc6
|
|
| MD5 |
b2730e03613e3227565c2545eb6fc4b6
|
|
| BLAKE2b-256 |
cfd2b6522b7211890c2d835b476e43144695b583fd4be25025f11645921d8fa4
|