Transmit and receive a single tone at 28 GHz using IBM Phased Array Antenna Modules (PAAMs) and USRP N310 SDRs in the COSMOS sandbox (SB1 or SB2), and observe the received frequency response in GNU Radio Companion.
This tutorial demonstrates the basic use of IBM 28 GHz Phased Array Antenna Modules (PAAMs) paired with USRP N310 software-defined radios in the COSMOS sandboxes SB1 and SB2.
One PAAM is configured as a transmitter and one as a receiver. Both are steered to broadside (0°,0°) using the COSMOS RESTful array management service. A GNU Radio Companion flowgraph running on a shared server node (srv1-lg1) drives the N310 SDRs to send a single tone, and the received spectrum is observed in a frequency-sink display.
The tutorial covers both sandbox variants: SB1 (which uses combined IC I/O) and SB2 (which uses split IC I/O), and shows how the array management API differs between them.
If you publish results obtained using the PAAMs deployed in COSMOS, please cite:
After completing this tutorial you will be able to:
omf commands to image and power nodes and RF devices in a mmWave setup.uhd_find_devices.| Difficulty | Intermediate |
| Estimated time | 90 min |
| Domain / sandbox | sb1.cosmos-lab.org or sb2.cosmos-lab.org |
| Topic group | mmwave-paam |
| Last verified | Not re-tested (migrated 2026-06-20) |
Background knowledge
Account & access
Devices / nodes
| Resource | Role | Qty | Notes |
|---|---|---|---|
sdr1-in1 (SB1) or sdr1-s1-lg1 (SB2) |
USRP N310 — RX SDR | 1 | Connected to RX PAAM |
sdr1-in2 (SB1) or sdr1-md1 (SB2) |
USRP N310 — TX SDR | 1 | Connected to TX PAAM |
rfdev4-in1 (SB1) or rfdev2-1 (SB2) |
IBM 28 GHz PAAM — RX | 1 | 16 elements (SB1, all ICs); 4 elements IC0/1 (SB2) |
rfdev4-in2 (SB1) or rfdev2-2 (SB2) |
IBM 28 GHz PAAM — TX | 1 | 16 elements (SB1, all ICs); 4 elements IC0/1 (SB2) |
srv1-lg1 |
Experiment server (runs GNU Radio) | 1 | Shared across SB1 and SB2 |
Disk images
| Image | Load onto | Provides |
|---|---|---|
paam28GHz-tutorial-cosmos-tone-crd.ndz |
srv1-lg1 |
Ubuntu 20.04, UHD 4.1, GNU Radio 3.9, example GRC flowgraph |
Software components
| Component | Version | Source |
|---|---|---|
| UHD (USRP Hardware Driver) | 4.1 | preinstalled in paam28GHz-tutorial-cosmos-tone-crd.ndz |
| GNU Radio / GNU Radio Companion | 3.9 | preinstalled in paam28GHz-tutorial-cosmos-tone-crd.ndz |
| COSMOS Array Management Service | TODO: verify | REST API at http://am1.cosmos-lab.org:5054/array_mgmt/ |
Spectrum / RF / special
28 GHz mmWave band, over-the-air within the shielded sandbox. The tutorial uses V-polarization (SB1) or H-polarization (SB2) with broadside beam steering (θ=0°, φ=0°). An IF carrier of 3 GHz (3e9) is used in the GNU Radio flowgraph; the PAAM up-converts to 28 GHz.


SB1 RF path connections:
sdr1-in1 RF2 TX/RX → rfdev4-in1 all ICs / TX / Hsdr1-in1 RF2 RX2 → rfdev4-in1 all ICs / RX / Hsdr1-in1 RF3 TX/RX → rfdev4-in1 all ICs / TX / Vsdr1-in1 RF3 RX2 → rfdev4-in1 all ICs / RX / Vsdr1-in2 RF2 TX/RX → rfdev4-in2 all ICs / TX / Hsdr1-in2 RF2 RX2 → rfdev4-in2 all ICs / RX / Hsdr1-in2 RF3 TX/RX → rfdev4-in2 all ICs / TX / Vsdr1-in2 RF2 RX2 → rfdev4-in2 all ICs / RX / VSB2 RF path connections:
sdr1-s1-lg1 RF2 TX/RX → rfdev2-1 IC0 / TX / Hsdr1-s1-lg1 RF2 RX2 → rfdev2-1 IC1 / RX / Hsdr1-s1-lg1 RF3 TX/RX → rfdev2-1 IC0 / TX / Vsdr1-s1-lg1 RF3 RX2 → rfdev2-1 IC1 / RX / Vsdr1-md1 RF2 TX/RX → rfdev2-2 IC0 / TX / Hsdr1-md1 RF2 RX2 → rfdev2-2 IC1 / RX / Hsdr1-md1 RF3 TX/RX → rfdev2-2 IC0 / TX / Vsdr1-md1 RF3 RX2 → rfdev2-2 IC1 / RX / VNote: SB1 uses combined IC I/O; SB2 uses split IC I/O.
SB1 full RF path configuration: SB1 RF Path Configurations for mmWave Development Platforms
IP address assignments: SB1 IP addresses · SB2 IP addresses
Reserve SB1 or SB2 and log into the console (open two SSH sessions):
ssh <username>@console.sb1.cosmos-lab.org
or
ssh <username>@console.sb2.cosmos-lab.org
Turn off all nodes and RF devices used in the experiment. Use the command that matches your sandbox:
SB1:
omf tell -a offh -t sdr1-in1,sdr1-in2,rfdev4-in1,rfdev4-in2,srv1-lg1
SB2:
omf tell -a offh -t sdr1-s1-lg1,sdr1-md1,rfdev2-1,rfdev2-2,srv1-lg1
Load the PAAM tutorial image onto the server node:
omf load -i paam28GHz-tutorial-cosmos-tone-crd.ndz -t srv1-lg1
Turn all required resources on and check their status:
SB1:
omf tell -a on -t sdr1-in1,sdr1-in2,rfdev4-in1,rfdev4-in2,srv1-lg1
SB2:
omf tell -a on -t sdr1-s1-lg1,sdr1-md1,rfdev2-1,rfdev2-2,srv1-lg1
omf stat -t all
SSH to the server with X11 forwarding enabled (for GNU Radio Companion GUI):
ssh -Y root@srv1-lg1
As an alternative to X11 forwarding, you can use Chrome Remote Desktop to view GNU Radio Companion. Set up Chrome Remote Desktop access as shown here. Chrome Remote Desktop is already installed in the image. SSH as "native" to set up remote access; skip the installation step.
Upon logging into srv1-lg1, run the eth_config.sh script to configure the 10G data interfaces (eno1, eno2):
root@srv1-lg1:~# ./eth_config.sh
After running the script, verify the data interfaces have the correct IP addresses. Expected output for each sandbox:
SB1:
root@srv1-lg1:~# ifconfig eno1
eno1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9000
inet 10.38.1.1 netmask 255.255.0.0 broadcast 10.38.255.255
inet6 fe80::1e34:daff:fe42:c3c prefixlen 64 scopeid 0x20<link>
ether 1c:34:da:42:0c:3c txqueuelen 1000 (Ethernet)
RX packets 712 bytes 74814 (74.8 KB)
RX errors 0 dropped 595 overruns 0 frame 0
TX packets 42 bytes 9132 (9.1 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
root@srv1-lg1:~# ifconfig eno2
eno2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9000
inet 10.39.1.1 netmask 255.255.0.0 broadcast 10.39.255.255
inet6 fe80::1e34:daff:fe42:c3d prefixlen 64 scopeid 0x20<link>
ether 1c:34:da:42:0c:3d txqueuelen 1000 (Ethernet)
RX packets 643 bytes 51548 (51.5 KB)
RX errors 0 dropped 599 overruns 0 frame 0
TX packets 42 bytes 9132 (9.1 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
SB2:
root@srv1-lg1:~# ifconfig eno1
eno1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9000
inet 10.114.2.11 netmask 255.255.255.0 broadcast 10.114.2.255
inet6 fe80::1e34:daff:fe42:d4c prefixlen 64 scopeid 0x20<link>
ether 1c:34:da:42:0d:4c txqueuelen 1000 (Ethernet)
RX packets 254 bytes 20924 (20.9 KB)
RX errors 0 dropped 185 overruns 0 frame 0
TX packets 25 bytes 4642 (4.6 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
root@srv1-lg1:~# ifconfig eno2
eno2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9000
inet 10.115.2.11 netmask 255.255.255.0 broadcast 10.115.2.255
inet6 fe80::1e34:daff:fe42:d4d prefixlen 64 scopeid 0x20<link>
ether 1c:34:da:42:0d:4d txqueuelen 1000 (Ethernet)
RX packets 218 bytes 22274 (22.2 KB)
RX errors 0 dropped 186 overruns 0 frame 0
TX packets 25 bytes 4642 (4.6 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Verify the IP addresses match the SB1 or SB2 IP address tables.
Run uhd_find_devices to confirm both N310s can be reached:
root@srv1-lg1:~# uhd_find_devices --args "product=n310"
Expected output on SB1:
[INFO] [UHD] linux; GNU C++ version 9.3.0; Boost_107100; UHD_4.1.0.4-release
--------------------------------------------------
-- UHD Device 0
--------------------------------------------------
Device Address:
serial: 3176DEB
addr: 10.39.6.1
claimed: False
fpga: XG
mgmt_addr: 10.37.6.1
mgmt_addr: 10.38.6.1
mgmt_addr: 10.39.6.1
name: ni-n3xx-3176DEB
product: n310
type: n3xx
--------------------------------------------------
-- UHD Device 1
--------------------------------------------------
Device Address:
serial: 3176DFA
addr: 10.39.6.2
claimed: False
fpga: XG
mgmt_addr: 10.37.6.2
mgmt_addr: 10.38.6.2
mgmt_addr: 10.39.6.2
name: ni-n3xx-3176DFA
product: n310
type: n3xx
Expected output on SB2:
[INFO] [UHD] linux; GNU C++ version 9.3.0; Boost_107100; UHD_4.1.0.4-release
--------------------------------------------------
-- UHD Device 0
--------------------------------------------------
Device Address:
serial: 315A35A
addr: 10.114.2.21
claimed: False
fpga: XG
mgmt_addr: 10.113.2.21
mgmt_addr: 10.114.2.21
name: ni-n3xx-315A35A
product: n310
type: n3xx
--------------------------------------------------
-- UHD Device 1
--------------------------------------------------
Device Address:
serial: 3176DF7
addr: 10.115.2.31
claimed: False
fpga: XG
mgmt_addr: 10.113.2.31
mgmt_addr: 10.114.2.31
mgmt_addr: 10.115.2.31
name: ni-n3xx-3176DF7
product: n310
type: n3xx
If either device is missing, see Troubleshooting.
COSMOS provides a RESTful array management service for PAAM control. Two modes are available:
connect → steer → disconnect (can re-steer during the experiment).configure (single call — connect, steer, and initialize in one step).This tutorial uses static configuration. Run the commands from the console (not from srv1-lg1).
Configure PAAM #1 (rfdev4-in1) in RX mode, V-polarization, 16 elements on all ICs, broadside (θ=0, φ=0):
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/configure?dev_name=rfdev4-in1.sb1.cosmos-lab.org&ics=all&num_elements=16&txrx=rx&pol=v&theta=0&phi=0"
Expected response:
<?xml version="1.0" encoding="UTF-8"?>
<response status="OK">
<action service="array_mgmt" name="configure" ipaddr="10.37.7.1">
<step name="open" duration="3.599997"/>
<step name="initializaition" duration="0.071123"/>
<step name="enabling" duration="0.016817"/>
<step name="steering" duration="0.007705"/>
<state PAAM_ID="0x36" LO_switch="PLL" if_sw1="0x0" if_sw2="0x0" if_sw3="0x0" if_sw4="0x0"/>
<adc>
<conv index="0" name="1v2" tADC="120" tVolt="0.293" tCurr="0.147"/>
<conv index="1" name="1v5" tADC="334" tVolt="0.816" tCurr="1.632"/>
<conv index="2" name="1v8" tADC="24" tVolt="0.059" tCurr="0.029"/>
<conv index="3" name="2v7_0" tADC="163" tVolt="0.398" tCurr="0.797"/>
<conv index="4" name="2v7_1" tADC="206" tVolt="0.503" tCurr="1.007"/>
<conv index="5" name="2v7_2" tADC="220" tVolt="0.538" tCurr="1.075"/>
<conv index="6" name="2v7_3" tADC="180" tVolt="0.440" tCurr="0.880"/>
<conv index="7" name="3v3_pll" tADC="408" tVolt="0.997" tCurr="0.499"/>
<conv index="8" name="5v_uzed" tADC="206" tVolt="0.503" tCurr="0.503"/>
<conv index="9" name="12v" tADC="244" tVolt="0.596" tCurr="1.988"/>
<conv index="10" name="0V" tADC="0" tVolt="0.000"/>
<conv index="11" name="1V8" tADC="737" tVolt="1.801"/>
</adc>
<step name="status" duration="0.020882"/>
<step name="close" duration="0.100221"/>
</action>
</response>
Check the 2v7_0, 2v7_1, 2v7_2, 2v7_3 ADC readings — non-zero values confirm all ICs are initialized.
Configure PAAM #2 (rfdev4-in2) in TX mode, V-polarization, 16 elements on all ICs, broadside (θ=0, φ=0):
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/configure?dev_name=rfdev4-in2.sb1.cosmos-lab.org&ics=all&num_elements=16&txrx=tx&pol=v&theta=0&phi=0"
Expected response:
<?xml version="1.0" encoding="UTF-8"?>
<response status="OK">
<action service="array_mgmt" name="configure" ipaddr="10.37.7.2">
<step name="open" duration="3.558891"/>
<step name="initializaition" duration="0.072244"/>
<step name="enabling" duration="0.018709"/>
<step name="steering" duration="0.008254"/>
<state PAAM_ID="0x30" LO_switch="PLL" if_sw1="0x0" if_sw2="0x0" if_sw3="0x0" if_sw4="0x0"/>
<adc>
<conv index="0" name="1v2" tADC="125" tVolt="0.305" tCurr="0.153"/>
<conv index="1" name="1v5" tADC="331" tVolt="0.809" tCurr="1.618"/>
<conv index="2" name="1v8" tADC="22" tVolt="0.054" tCurr="0.027"/>
<conv index="3" name="2v7_0" tADC="255" tVolt="0.623" tCurr="1.246"/>
<conv index="4" name="2v7_1" tADC="250" tVolt="0.611" tCurr="1.222"/>
<conv index="5" name="2v7_2" tADC="266" tVolt="0.650" tCurr="1.300"/>
<conv index="6" name="2v7_3" tADC="264" tVolt="0.645" tCurr="1.290"/>
<conv index="7" name="3v3_pll" tADC="410" tVolt="1.002" tCurr="0.501"/>
<conv index="8" name="5v_uzed" tADC="215" tVolt="0.525" tCurr="0.525"/>
<conv index="9" name="12v" tADC="300" tVolt="0.733" tCurr="2.444"/>
<conv index="10" name="0V" tADC="0" tVolt="0.000"/>
<conv index="11" name="1V8" tADC="734" tVolt="1.794"/>
</adc>
<step name="status" duration="0.021005"/>
<step name="close" duration="0.100240"/>
</action>
</response>
Configure PAAM #1 (rfdev2-1) in TX mode, H-polarization, 4 elements on IC0, broadside:
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/configure?dev_name=rfdev2-1.sb2.cosmos-lab.org&ics=0&num_elements=4&txrx=tx&pol=h&theta=0&phi=0"
Configure PAAM #2 (rfdev2-2) in RX mode, H-polarization, 4 elements on IC1, broadside:
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/configure?dev_name=rfdev2-2.sb2.cosmos-lab.org&ics=1&num_elements=4&txrx=rx&pol=h&theta=0&phi=0"
On srv1-lg1, start GNU Radio Companion and open the example flowgraph:
SB1:
root@srv1-lg1:~# gnuradio-companion example_paam_tone_sb1.grc
SB2:
root@srv1-lg1:~# gnuradio-companion example_paam_tone_sb2.grc
Configure the flowgraph blocks as follows:
sdr1-in2: mgmt_addr=10.37.6.2,addr=10.39.6.2. Set the USRP source (RX) to sdr1-in1: mgmt_addr=10.37.6.1,addr=10.39.6.1.sdr1-s1-lg1: mgmt_addr=10.113.2.21,addr=10.114.2.21. Set the USRP source (RX) to sdr1-md1: mgmt_addr=10.113.2.31,addr=10.114.2.31.On both TX and RX blocks:
3e9)B:1 (RF3)B:0 (RF2)2.5e6)1e6)

Run the flowgraph by clicking the play button in GNU Radio Companion.
uhd_find_devices --args "product=n310" lists two N310 devices with the expected serial numbers and IP addresses for the sandbox.configure API calls return <response status="OK"> with non-zero 2v7_* ADC readings, confirming all PAAM ICs are initialized.

The figure above shows the expected frequency response of the received tone at 1 MHz offset.
Stop the GNU Radio flowgraph, then from the console, clean up and turn off both PAAMs before powering down all nodes.
SB1:
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/connect?dev_name=rfdev4-in1.sb1.cosmos-lab.org"
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/cleanup?dev_name=rfdev4-in1.sb1.cosmos-lab.org"
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/disconnect?dev_name=rfdev4-in1.sb1.cosmos-lab.org"
root@console:~# omf tell -a offh -t rfdev4-in1.sb1.cosmos-lab.org
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/connect?dev_name=rfdev4-in2.sb1.cosmos-lab.org"
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/cleanup?dev_name=rfdev4-in2.sb1.cosmos-lab.org"
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/disconnect?dev_name=rfdev4-in2.sb1.cosmos-lab.org"
root@console:~# omf tell -a offh -t rfdev4-in2.sb1.cosmos-lab.org
SB2:
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/connect?dev_name=rfdev2-1.sb2.cosmos-lab.org"
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/cleanup?dev_name=rfdev2-1.sb2.cosmos-lab.org"
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/disconnect?dev_name=rfdev2-1.sb2.cosmos-lab.org"
root@console:~# omf tell -a offh -t rfdev2-1.sb2.cosmos-lab.org
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/connect?dev_name=rfdev2-1.sb2.cosmos-lab.org"
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/cleanup?dev_name=rfdev2-2.sb2.cosmos-lab.org"
root@console:~# curl "http://am1.orbit-lab.org:5054/array_mgmt/disconnect?dev_name=rfdev2-1.sb2.cosmos-lab.org"
root@console:~# omf tell -a offh -t rfdev2-2.sb2.cosmos-lab.org
Then power off all remaining nodes:
omf tell -a offh -t system:topo:allres
(No omf save needed unless you customized the image on srv1-lg1.)
| Symptom | Likely cause | Fix |
|---|---|---|
uhd_find_devices returns fewer than 2 devices |
eth_config.sh not run, or 10G interface down |
Re-run ./eth_config.sh; verify with ifconfig eno1 eno2 that IPs are assigned; ping the N310 mgmt address |
configure API returns non-OK status or zero 2v7_* ADC readings |
PAAM not powered on, or wrong dev_name |
Confirm rfdev4-in1/rfdev4-in2 (SB1) or rfdev2-1/rfdev2-2 (SB2) are powered (omf stat); check dev_name matches the sandbox |
| GNU Radio flowgraph throws UHD error | Wrong device address in USRP block | Double-check mgmt_addr and addr parameters match the sandbox IP table |
| No tone visible in frequency sink | Wrong subdev setting | SB1 needs B:1 (RF3); SB2 needs B:0 (RF2); confirm both TX and RX blocks have the same carrier frequency |
| X11 forwarding fails / GRC window does not appear | SSH not started with -Y |
Reconnect with ssh -Y root@srv1-lg1; alternatively use Chrome Remote Desktop |
Author(s): COSMOS team (original: Tingjun Chen, Duke University) · Last verified: Not re-tested (migrated 2026-06-20) · Tested image/release: paam28GHz-tutorial-cosmos-tone-crd.ndz · Tags: mmwave, paam, ibm, usrp, n310, gnuradio, 28ghz, beamforming