Set up two RFSoC-backed mmWave nodes on COSMOS SB1, configure the RF switches and Sivers PAAM arrays, and run basic single-node, two-node, and channel-sounder demos at 60 GHz.
This tutorial walks through experimentation with COSMOS SB1's millimeter-wave (mmWave) radio infrastructure. Each experimental node consists of a support server (srv1-inN) connected to a Xilinx RFSoC ZCU111 evaluation board (sdr2-inN) and a Sivers IMA phased-array antenna module (PAAM). The two nodes are at opposite corners of SB1 and are coordinated via RF switches (rfsw1/rfsw2) that route the signal path between the RFSoC baseband and the Sivers array.
The tutorial covers FPGA firmware download over JTAG, network interface configuration for the high-speed data planes, FTDI driver setup for the Sivers control interface, and a set of progressive demos: single-node loopback, two-node 60 GHz link, frequency-domain channel sounder, IQ calibration, antenna array pattern measurement, and beam tracking.
This hardware is part of the advanced programmable, open-source mmWave radio infrastructure integrated into the COSMOS PAWR testbed. See the Publications section for citable references.
After completing this tutorial you will be able to:
rfsoc_sivers_sb1.ndz image and power on all required SB1 resources.mmwsdr Python demos: single-node, two-node 60 GHz link, channel sounder, calibration, array pattern, and beam tracking.| Difficulty | Advanced |
| Estimated time | 90–120 min |
| Domain / sandbox | sb1.cosmos-lab.org |
| Topic group | mmWave & PAAM |
| Last verified | Not re-tested (migrated 2026-06-20) |
Background knowledge
mmwsdr Python library).Account & access
Devices / nodes
| Resource | Role | Qty | Notes |
|---|---|---|---|
| srv1-in1 | Support server — corner 1 (1-1) | 1 | Hosts RFSoC firmware download; runs mmwsdr demos |
| srv1-in2 | Support server — corner 2 (20-20) | 1 | Second experiment node |
| sdr2-in1 | Xilinx RFSoC ZCU111 — corner 1 | 1 | Connected to srv1-in1 via JTAG and data network |
| sdr2-in2 | Xilinx RFSoC ZCU111 — corner 2 | 1 | Connected to srv1-in2 via JTAG and data network |
| rfdev3-in1 | Sivers IMA PAAM — corner 1 | 1 | Unit SN0240; controlled via FTDI from srv1-in1 |
| rfdev3-in2 | Sivers IMA PAAM — corner 2 | 1 | Unit SN0243; controlled via FTDI from srv1-in2 |
| rfsw1.sb1.cosmos-lab.org | RF switch — node 1 | 1 | 4-port; set to port 2 to route RFSoC↔Sivers |
| rfsw2.sb1.cosmos-lab.org | RF switch — node 2 | 1 | 4-port; set to port 2 to route RFSoC↔Sivers |
Disk images
| Image | Load onto | Provides |
|---|---|---|
| rfsoc_sivers_sb1.ndz | srv1-in1, srv1-in2 | mmwsdr Python library, Xilinx XSCT tool, JTAG firmware scripts, Sivers FTDI drivers |
Software components
| Component | Version | Source |
|---|---|---|
mmwsdr Python library |
TODO: verify | Preinstalled in rfsoc_sivers_sb1.ndz; also on GitHub (git pull to update) |
| Xilinx XSCT | TODO: verify | Preinstalled in rfsoc_sivers_sb1.ndz |
| Sivers IMA Python drivers (Python 2) | TODO: verify | Preinstalled in rfsoc_sivers_sb1.ndz |
| OpenCV | TODO: verify | Used in video.py demo; preinstalled in image |
Spectrum / RF / special
Two SB1 nodes at opposite corners of the indoor environment (corner 1-1 and corner 20-20). Each node consists of:
srv1-inN) on the control network (10.37.1.X).sdr2-inN) connected to the support server via JTAG (firmware) and two high-speed Ethernet data links (data network 1: 10.38.X.X, data network 2: 10.39.X.X).rfdev3-inN) controlled from the support server via FTDI USB bridge.rfsw1/rfsw2) routing the IF/RF signal between the RFSoC and the Sivers array. All four switch ports must be set to port 2 for the RFSoC↔Sivers path.Network address table:
Corner 1 (1-1)
| Device | Control Network | Data Network 1 | Data Network 2 |
|---|---|---|---|
| Support Server | 10.37.1.3 (srv1-in1) | 10.38.1.3 (srv1a-in1) | 10.39.1.3 (srv1b-in1) |
| RFSoC Device | 10.37.6.3 (sdr2-in1) | 10.38.6.3 (sdr2-in1a) | 10.39.6.3 (sdr2-in1b) |
Corner 2 (20-20)
| Device | Control Network | Data Network 1 | Data Network 2 |
|---|---|---|---|
| Support Server | 10.37.1.4 (srv1-in2) | 10.38.1.4 (srv1a-in2) | 10.39.1.4 (srv1b-in2) |
| RFSoC Device | 10.37.6.4 (sdr2-in2) | 10.38.6.4 (sdr2-in2a) | 10.39.6.4 (sdr2-in2b) |
Reserve the resources and log into the SB1 console:
ssh -Y your_username@console.sb1.cosmos-lab.org
Make sure all nodes and devices of this reservation are turned off:
your_username@console:~$ omf tell -a offh -t system:topo:allres
Load the rfsoc_sivers_sb1.ndz image on srv1-in1 and srv1-in2:
your_username@console:~$ omf load -i rfsoc_sivers_sb1.ndz -t srv1-in1,srv1-in2
Power on all required resources (support servers, RF devices, RFSoC boards):
your_username@console:~$ omf tell -a on -t srv1-in1,srv1-in2,rfdev3-in1,rfdev3-in2,sdr2-in1,sdr2-in2
Check the status of all nodes and devices:
your_username@console:~$ omf stat -t system:topo:allres
Configure the RF switches to connect each RFSoC to its Sivers PAAM (all four switches on both rfsw1 and rfsw2 set to port 2):
your_username@console:~$ curl "am1.cosmos-lab.org:5054/rf_switch/set?name=rfsw1.sb1.cosmos-lab.org,rfsw2.sb1.cosmos-lab.org&switch=1,2,3,4&port=2"
Verify the RF switch configuration (all switches should show port 2):
your_username@console:~# curl am1.cosmos-lab.org:5054/rf_switch/status?name=rfsw1.sb1.cosmos-lab.org,rfsw2.sb1.cosmos-lab.org
Expected output:
<response status="OK">
<rf_switch name="rfsw1.sb1.cosmos-lab.org" num_of_switches="4">
<switch number="1" port="2"/>
<switch number="2" port="2"/>
<switch number="3" port="2"/>
<switch number="4" port="2"/>
</rf_switch>
<rf_switch name="rfsw2.sb1.cosmos-lab.org" num_of_switches="4">
<switch number="1" port="2"/>
<switch number="2" port="2"/>
<switch number="3" port="2"/>
<switch number="4" port="2"/>
</rf_switch>
</response>
Download the FPGA firmware to sdr2-in1 via JTAG from srv1-in1 (takes 5–10 minutes):
your_username@console:~$ ssh -Y root@srv1-in1
root@srv1-in1:~$ cd mmwsdr/fpga/nonrt-ch1/jtag/sb1_sdr2_in1/
root@srv1-in1:~/mmwsdr/fpga/nonrt-ch1/jtag/sb1_sdr2_in1$ xsct download_firmware.tcl
In a second SSH session, download the firmware to sdr2-in2 via JTAG from srv1-in2 (takes 5–10 minutes):
your_username@console:~$ ssh -Y root@srv1-in2
root@srv1-in2:~$ cd mmwsdr/fpga/nonrt-ch1/jtag/sb1_sdr2_in2/
root@srv1-in2:~/mmwsdr/fpga/nonrt-ch1/jtag/sb1_sdr2_in2$ xsct download_firmware.tcl
Configure the high-speed data-plane Ethernet interfaces on srv1-in1 (MTU 9000, static IPs for data networks 1 and 2):
root@srv1-in1:~# ip link set enp1s0 mtu 9000 up
root@srv1-in1:~# ip link set enp3s0 mtu 9000 up
root@srv1-in1:~# ip addr add 10.38.1.3/16 dev enp1s0
root@srv1-in1:~# ip addr add 10.39.1.3/16 dev enp3s0
Configure the data-plane interfaces on srv1-in2:
root@srv1-in2:~# ip link set enp1s0 mtu 9000 up
root@srv1-in2:~# ip link set enp3s0 mtu 9000 up
root@srv1-in2:~# ip addr add 10.38.1.4/16 dev enp1s0
root@srv1-in2:~# ip addr add 10.39.1.4/16 dev enp3s0
Set up the FTDI drivers for Sivers array control on srv1-in1 (required for every new SSH session):
root@srv1-in1:~$ source ~/mmwsdr/host/scripts/sivers_ftdi.sh
Update the mmwsdr software on both nodes:
root@srv1-in1:~$ cd mmwsdr
root@srv1-in1:~/mmwsdr$ git pull
root@srv1-in2:~$ cd mmwsdr
root@srv1-in2:~/mmwsdr$ git pull
video.py)This script demonstrates control of the SDR XY-Table mount. Each RFSoC node is mounted on an XY-Table allowing independent horizontal movement and vertical-axis rotation. Motion is visualized via a live camera stream using OpenCV.
On srv1-in1:
root@srv1-in1:~/mmwsdr/host/demos/basic$ python video.py --node srv1-in1
The XY-Table HTTP API reference is available at: https://wiki.cosmos-lab.org/wiki/Resources/Services/XYTable
onenode.py)This demo creates a single SDR object and XY-Table object using the mmwsdr library to configure and control the Xilinx RFSoC ZCU111 and Sivers IMA transceiver. The script defaults to a local connection at 60.48 GHz in receive mode.
Start the transmitter on srv1-in1:
root@srv1-in1:~/mmwsdr/host/demos/basic$ python onenode.py --freq 60.48e9 --node srv1-in1 --mode tx
Start the receiver on srv1-in2:
root@srv1-in2:~/mmwsdr/host/demos/basic$ python onenode.py --freq 60.48e9 --node srv1-in2 --mode rx
Observe the received signal in the frequency domain.
ederarray.py)The Sivers IMA Python drivers require Python 2. To remove this dependency from the main experiment scripts, ederarray.py instantiates the Sivers array object and starts an HTTP server that listens for control commands.
Start the HTTP server on srv1-in1 (Sivers unit SN0240):
root@srv1-in1:~/mmwsdr/host/mmwsdr/array$ python ederarray.py -u SN0240
Start the HTTP server on srv1-in2 (Sivers unit SN0243):
root@srv1-in2:~/mmwsdr/host/mmwsdr/array$ python ederarray.py -u SN0243
twonode.py)This demo controls both SDR nodes from a single script. twonode.py creates an SDR object and XY-Table object for each node. It connects directly to its local Sivers front-end (SN0240) and connects to the remote Sivers front-end (SN0243) via the HTTP server running on the peer node.
Start the remote Eder server on srv1-in1:
root@srv1-in1:~/mmwsdr/host/mmwsdr/array$ python ederarray.py -u SN0240
Start the experiment on srv1-in2 (this node acts as the local controller):
root@srv1-in2:~/mmwsdr/host/demos/basic$ python twonode.py --freq 60.48e9 --mode tx --node srv1-in2
This demo implements a frequency-domain channel sounder at 60 GHz. The transmitter generates N_FFT symbols in the frequency domain: a wideband sequence spanning [sc_min, sc_max] subcarriers with random 4-QAM symbols, converted to the time domain via IFFT and transmitted with cyclic repeat. The receiver captures 100 frames of N_FFT samples. The user can select to save or process the captured data using the mmwave.utils.waveform.wideband function.
IQ calibration corrects for TX and RX IQ imbalance in the Sivers array. The base calibration uses Sivers' built-in calibration routines (invoked via the same ederarray.py entry point). For additional IQ calibration techniques applicable to phased arrays, see the papers by Dhananjay et al. in the References section.
root@srv1-in1:~/mmwsdr/host/mmwsdr/array$ python ederarray.py --unit SN0240
root@srv1-in1:~/mmwsdr/host/mmwsdr/array$ python ederarray.py --unit SN0240
Measure the antenna array radiation pattern.
On srv1-in1:
root@srv1-in1:~/mmwsdr/host/mmwsdr/array$ python ederarray.py --unit SN0240
On srv1-in2:
root@srv1-in2:~/mmwsdr/host/demos/basic$ python twonode.py --freq 60.48e9 --mode tx
Not documented in the source. See the Brooklyn 6G Summit presentation linked in References for context.
omf stat -t system:topo:allres should show all six resources (srv1-in1, srv1-in2, rfdev3-in1, rfdev3-in2, sdr2-in1, sdr2-in2) as online.rfsw1 and rfsw2 set to port="2".onenode.py or twonode.py in receive mode shows a signal peak in the frequency-domain display when the transmitter is active at 60.48 GHz.omf tell -a offh -t system:topo:allres
No omf save is needed unless you modified the image. Note that the FPGA firmware loaded via JTAG is volatile and must be re-downloaded after every power cycle.
| Symptom | Likely cause | Fix |
|---|---|---|
| XSCT firmware download fails or times out | JTAG connection issue / node not fully booted | Confirm srv1-inN is reachable via SSH; check JTAG cable; retry xsct download_firmware.tcl |
No signal in onenode.py receiver |
RF switches not configured, or firmware not loaded | Re-run the curl switch command and verify all ports show 2; re-run XSCT firmware download |
| FTDI / Sivers control errors | FTDI driver not sourced in current session | Run source ~/mmwsdr/host/scripts/sivers_ftdi.sh at the start of each new SSH session |
ederarray.py fails with Python version error |
Script invoked with Python 3 | Sivers drivers require Python 2; ensure you are running python (Python 2), not python3 |
| Data-plane ping to RFSoC fails | enp1s0/enp3s0 not configured |
Re-run the ip link set / ip addr add commands; verify interface names with ip link |
omf stat shows resources offline |
Power-on sequence incomplete | Confirm all six targets were included in omf tell -a on; check CMC status |
Author(s): COSMOS team (Panagiotis Skrimponis, NYU; Prasanthi Maddala, Rutgers University) · Last verified: Not re-tested (migrated 2026-06-20) · Tested image/release: rfsoc_sivers_sb1.ndz · Tags: mmwave, rfsoc, sivers, paam, 60ghz, beam-tracking, channel-sounder, sb1