Remote RF
A python API to remotely access signal centric hardware.
Courtesy of Wireless Lab @ UCLA. - Ethan Ge
Prerequisites
- Python 3.10: This package works in Python 3.10+. If you don’t have Python installed, you can download it from the official Python website.
To check your current Python version, open a terminal and run:
python --version
- UCLA VPN: Please ensure that you are connected to the UCLA VPN. You can download and configure the VPN client from the following link: UCLA VPN Client Download. If you’re not connected to the VPN, you will not have access to the lab servers.
Installation
Use the package manager pip to install remoteRF. It is recommended that you install this package within a virtual environment.
python3 -m venv venv # Create virtual environment
source venv/bin/activate # Activate virtual environment
pip install remoterf # Install remoteRF
NI USRP-2901, Ettus B205-mini, B200-family, and USRP N210
The packaged client natively includes the shared USRP schema for the qualified
NI USRP-2901, Ettus Research USRP B205-mini, the broader USB/B200 family, and
the Ethernet-connected N210. No custom client driver is required. The B205-mini
uses the same native-style API and reports its live 1 RX/1 TX channel topology
through remoterf_capabilities.
After reserving a server-local usrp inventory entry, fetch or refresh its
Dynamic v2 package with the reservation token:
from remoteRF.drivers import ensure_driver
ensure_driver(token="reservation-token")
Both supported initialization forms then use the same generated runtime:
from remoteRF.drivers.usrp import uhd
with uhd.usrp.MultiUSRP(token="reservation-token") as usrp:
print(usrp.remoterf_capabilities)
usrp.set_rx_rate(1e6)
from remoteRF.drivers.usrp import MultiUSRP
usrp = MultiUSRP("reservation-token")
try:
print(usrp.get_pp_string())
finally:
usrp.close()
Streaming uses NumPy buffers and native-like UHD value objects. Only the leading region reported by native UHD is changed on a partial receive:
import numpy as np
from remoteRF.drivers.usrp import uhd
with uhd.usrp.MultiUSRP(token="reservation-token") as usrp:
stream_args = uhd.usrp.StreamArgs("fc32", "sc16")
stream_args.channels = [0]
with usrp.get_rx_stream(stream_args) as rx:
samples = np.empty(4096, dtype=np.complex64)
metadata = uhd.types.RXMetadata()
count = rx.recv(samples, metadata, 0.25)
valid_samples = samples[:count]
The generated package includes native-like namespaces, aliases, overload
stubs, deterministic close(), context managers, opaque session-bound
handles, and typed errors in remoteRF.core.v2_errors. Streamers are never
silently recreated after a disconnect; reopen a new device session and
configure a new stream explicitly.
Methods marked deferred in the fetched schema remain available through
generic native dispatch, but are not release-qualified as exact UHD overload
parity until the server's UHD 4.10 target-introspection and hardware
differential gates pass.
RTL-SDR
RTL-SDR clients are generated from the server's rtl_sdr schema. The machine
running client code does not need librtlsdr or PyRtlSdr:
from remoteRF.drivers import ensure_driver
token = "reservation-token"
ensure_driver(token=token)
from remoteRF.drivers.rtl_sdr import RtlSdr
sdr = RtlSdr(token)
sdr.sample_rate = 2_048_000
sdr.center_freq = 100_000_000
sdr.gain = "auto"
sdr.agc_mode = True
samples = sdr.read_samples(16_384) # NumPy complex64
raw_iq = sdr.read_bytes(4096) # packed uint8 I/Q bytes
The server owns the USB handle for its lifetime, so the generated client does
not close the physical radio. read_samples and read_bytes are bounded to a
4 MiB response. Call them repeatedly for longer captures. Native asynchronous
callbacks are not transported remotely.
When a Tailscale address differs from the IP or DNS identity in the server certificate, keep TLS verification enabled and set the expected identity:
export REMOTERF_ADDR=rrf2.example.ts.net:61005
export REMOTERF_TLS_SERVER_NAME=certificate-name.example
TI mmWave radar
TI radar clients are generated from the server's ti_mmwave schema. Only the
machine physically connected to the radar needs pyserial or a CP210x driver.
The initial parser profile supports xWR68xx mmWave SDK 3 out-of-box TLV output.
from remoteRF.drivers import ensure_driver
token = "reservation-token"
ensure_driver(token=token)
from remoteRF.drivers.ti_mmwave import TiMmWave, decode_frame
radar = TiMmWave(token)
print(radar.device_info)
print(radar.query_version())
radar.apply_config(open("profile.cfg").read(), start=True)
try:
packet = radar.read_frame(timeout=1.0)
frame = decode_frame(packet)
print(frame.header.frame_number, frame.points, frame.side_info)
finally:
radar.stop()
read_frame() returns one complete binary UART packet. Decoding is client-local
and retains unknown TLVs as raw bytes, allowing custom firmware to be captured
before a dedicated parser profile is added. stream_stats reports dropped
frames, queued frames, resynchronization bytes, invalid headers, and reader
health. Remote firmware flashing is deliberately unsupported.
If pip install doesn't work, you can clone the source directly from github.
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-2.0.11.tar.gz.
File metadata
- Download URL: remoterf-2.0.11.tar.gz
- Upload date:
- Size: 123.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/5.1.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da239cd4a72a76e68f3ba9754ff4c08189f459a6a96d472b1b85b410362618c6
|
|
| MD5 |
e73638f93cb21617777582d9e631ec04
|
|
| BLAKE2b-256 |
f2fcf0e51564eecd17cb39dc4c2a89f77f21003c076ad6deeee48131ff9cc0ee
|
File details
Details for the file remoterf-2.0.11-py3-none-any.whl.
File metadata
- Download URL: remoterf-2.0.11-py3-none-any.whl
- Upload date:
- Size: 129.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/5.1.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e67a3c5376adc1026e0680eb26338fea94599ee7ef83a7480e2f64c621f71ed
|
|
| MD5 |
585dce2ad17838ef305262bb56ea3b3a
|
|
| BLAKE2b-256 |
d6256a14a8a266a05e363e13ce70464dbf916119400908d7773428611c6b296b
|