Demonstrate TDD 2×2 MIMO channel sounding on two USRP-2974 radios in COSMOS Sandbox 2 (sb2) using the RENEW Sounder software, and analyze the resulting 2×2 channel matrix H.
This tutorial demonstrates the TDD 2×2 MIMO channel sounding procedure using two USRP-2974 (NI-2974 / X310-based) software-defined radios in the COSMOS Sandbox 2 (sb2). One USRP acts as the base station (BS), the other as the user equipment (UE). Both radios use dual-channel (AB) operation, enabling the experiment to sound all four elements of the 2×2 channel matrix H = [h00, h01; h10, h11].
The experiment uses the RENEW Sounder software (C++ Sounder, develop branch), which was extended by the COSMOS team to support UHD-based radios. A server node (srv2-lg1) drives both radios over a 10G Ethernet link, collects pilot frames, and saves them to an HDF5 log file. A Python analysis script then plots the estimated channel responses.
The RENEW Platform Sounder software is used under the RENEW license. The UHD support was contributed by the COSMOS team under the UHD license.
After completing this tutorial you will be able to:
uhd_find_devices.| Difficulty | Intermediate |
| Estimated time | 90 min |
| Domain / sandbox | sb2.cosmos-lab.org |
| Topic group | channel-sounding-mimo |
| Last verified | Not re-tested (migrated 2026-06-20) |
Background knowledge
Account & access
Devices / nodes
| Resource | Role | Qty | Notes |
|---|---|---|---|
| sdr2-s1-lg1 | Base station (BS), dual-channel | 1 | USRP-2974 (NI-2974); 10G port hard-coded to 10.115.2.22 |
| sdr2-md1 | User equipment (UE), dual-channel | 1 | USRP-2974 (X310); 10G port hard-coded to 10.115.2.32 |
| srv2-lg1 | Control / compute server | 1 | Runs Sounder software; connects to USRPs over 10G via eno2 |
Disk images
| Image | Load onto | Provides |
|---|---|---|
| baseline-gr.ndz | srv2-lg1 | Ubuntu 18.04, UHD 3.15, GNU Radio 3.8 |
Software components
| Component | Version | Source |
|---|---|---|
| UHD (USRP Hardware Driver) | 3.15 | Preinstalled in baseline-gr.ndz |
| GNU Radio | 3.8 | Preinstalled in baseline-gr.ndz |
| RENEW Sounder (C++) | develop branch |
github.com/renew-wireless/RENEWLab — build on node |
| Python 3 + HDF5 / h5py | TODO: verify | Used for post-processing (plot_hdf5.py) |
Spectrum / RF / special
rfsw-s1-lg1.sb2.cosmos-lab.org and rfsw-md1.sb2.cosmos-lab.org must be set to port 1 (USRP-2974 path) before running the experiment.srv2-lg1 is the single compute node. It connects to both USRP-2974 radios over a 10G Ethernet interface (eno2), configured with IP 10.115.2.12/24. The two USRPs have fixed IPs on the same subnet:
sdr2-s1-lg1 (BS): 10.115.2.22sdr2-md1 (UE): 10.115.2.32The server drives both radios simultaneously: Terminal 1 runs the UE (client) process and Terminal 2 runs the BS process. All data collection and analysis also happen on srv2-lg1.
Reserve the resources and log into the sb2 console (open two SSH sessions — you will need them simultaneously in the Experiment Execution stage):
ssh <username>@console.sb2.cosmos-lab.org
Make sure all nodes and devices used in the experiment are turned off:
omf tell -a offh -t sdr2-s1-lg1,sdr2-md1,srv2-lg1
Load the baseline-gr.ndz image on the server node (see available disk images for the full list):
omf load -i baseline-gr.ndz -t srv2-lg1
Turn all required resources on and check their status:
omf tell -a on -t sdr2-s1-lg1,sdr2-md1,srv2-lg1
omf stat -t all
Make sure the RF switch ports are set to use the USRP-2974s. If any ports are set to 2, reset them to 1:
curl "am1.cosmos-lab.org:5054/rf_switch/set?name=rfsw-s1-lg1.sb2.cosmos-lab.org,rfsw-md1.sb2.cosmos-lab.org&switch=1,2,3,4&port=1"
Open two SSH sessions to srv2-lg1 from the console, using -Y to forward X11 (needed for GUI plots):
# Terminal 1
ssh -Y root@srv2-lg1
# Terminal 2
ssh -Y root@srv2-lg1
On srv2-lg1, configure eno2 with an IP on the USRP subnet, set jumbo frames (MTU 9000), and increase socket buffer sizes:
root@srv2-lg1:~# ifconfig eno2 10.115.2.12 netmask 255.255.255.0 mtu 9000 up
root@srv2-lg1:~# sudo sysctl -w net.core.wmem_max=24862979
net.core.wmem_max = 24862979
root@srv2-lg1:~# sudo sysctl -w net.core.rmem_max=24862979
net.core.rmem_max = 24862979
root@srv2-lg1:~# ifconfig
eno2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9000
inet 10.115.2.12 netmask 255.255.255.0 broadcast 10.115.2.255
inet6 fe80::9a03:9bff:fe61:9609 prefixlen 64 scopeid 0x20<link>
ether 98:03:9b:61:96:09 txqueuelen 1000 (Ethernet)
RX packets 4053772 bytes 17770502341 (17.7 GB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1727950 bytes 2972166223 (2.9 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Verify that both USRP-2974s are reachable:
root@srv2-lg1:~# uhd_find_devices --args="addr=10.115.2.22"
[INFO] [UHD] linux; GNU C++ version 7.5.0; Boost_106501; UHD_3.15.0.HEAD-0-gaea0e2de
--------------------------------------------------
-- UHD Device 0
--------------------------------------------------
Device Address:
serial: 318F20F
addr: 10.115.2.22
fpga: XG
name:
product: NI-2974
type: x300
root@srv2-lg1:~# uhd_find_devices --args="addr=10.115.2.32"
[INFO] [UHD] linux; GNU C++ version 7.5.0; Boost_106501; UHD_3.15.0.HEAD-0-gaea0e2de
--------------------------------------------------
-- UHD Device 0
--------------------------------------------------
Device Address:
serial: 3165747
addr: 10.115.2.32
fpga: XG
name:
product: X310
type: x300
If either device is not found, see Troubleshooting.
Download and build the RENEW Sounder on the develop branch (the master branch does not include UHD support):
root@srv2-lg1:~# git clone https://github.com/renew-wireless/RENEWLab.git ~/git/renew-software
root@srv2-lg1:~# cd ~/git/renew-software
root@srv2-lg1:~/git/renew-software# git checkout develop
Then follow the build instructions for the C++ Sounder.
Edit the configuration files for the BS (base station) and UE (client).
Base station — files/usrp-bs-serials.txt
Set the BS radio IP address to sdr2-s1-lg1:
10.115.2.22
Base station — files/usrp-bs-only.json
Enable dual-channel on the BS:
"channel" : "AB",
Client (UE) — files/usrp-client-only.json
Set the UE radio IP address to sdr2-md1 and enable dual-channel:
"sdr_id" : [ "10.115.2.32" ]
"channel" : "AB",
In Terminal 1, start the client (UE) process first:
root@srv2-lg1:~/git/renew-software/CC/Sounder# ./sounder files/usrp-client-only.json
...
...
pinning client thread 0 to core 1
scheduling TX 13 Frames (41.08 ms) in the future!
1 uplink symbols will be sent per frame...
Beacon detected at Time 32476200, sync_index: 13100
Start main client txrx loop...
In Terminal 2, start the BS process:
root@srv2-lg1:~/git/renew-software/CC/Sounder# ./sounder files/usrp-bs-only.json
...
...
Set HDF5 File to logs/trace-2020-9-21-18-46-29_1x2x2.hdf5 and group to /
task thread 0 starts
pinning thread 0 to core 1
Recv Thread 0: waiting for release
receiver thread 0 has 1 radios
Sync BS host and FPGA timestamp...
Start BS main recv loop...
Saving HDF5, 2000 frames saved.
...
...
The BS process saves the sounding data to an HDF5 file (e.g., logs/trace-2020-9-21-18-46-29_1x2x2.hdf5).
Once the HDF5 file is recorded, follow the Analyze the HDF5 dataset step in the RENEW Sounder documentation to post-process the data.
To plot the pilot sequence used to compute the 2×2 channel matrix H = [h00, h01; h10, h11]:
root@srv2-lg1:~/git/renew-software/PYTHON/IrisUtils# python3 plot_hdf5.py ../../CC/Sounder/logs/trace-2020-9-21-18-46-29_1x2x2.hdf5 --ref-ant 0 --ref-user 0 --ref-frame 1500
Where:
--ref-ant 0 or 1 selects the BS antenna index.--ref-user 0 or 1 selects the user index.Expected channel matrix plots:




| h00 | h01 |
|---|---|
![]() |
![]() |
| h10 | h11 |
![]() |
![]() |
uhd_find_devices --args="addr=10.115.2.22" returns the NI-2974 device with serial 318F20F.uhd_find_devices --args="addr=10.115.2.32" returns the X310 device with serial 3165747.Beacon detected and Start main client txrx loop....Saving HDF5, 2000 frames saved. and the HDF5 file appears in logs/.plot_hdf5.py produces four channel response plots (h00, h01, h10, h11) without errors.omf tell -a offh -t sdr2-s1-lg1,sdr2-md1,srv2-lg1
(No omf save needed unless you modified the image.)
| Symptom | Likely cause | Fix |
|---|---|---|
uhd_find_devices returns No UHD Devices Found for a USRP |
eno2 not configured or wrong subnet |
Run ifconfig eno2 10.115.2.12 netmask 255.255.255.0 mtu 9000 up and retry; confirm with ping 10.115.2.22 / ping 10.115.2.32 |
| Client does not detect beacon / times out | RF switch ports not set to USRP-2974 path | Re-run the curl RF switch command from Setup step 5 to set all ports to 1 |
| Sounder build fails with UHD errors | Wrong branch (master) checked out |
Switch to develop branch: git checkout develop |
| HDF5 file not created / BS exits immediately | JSON config file has wrong IP or channel settings | Verify usrp-bs-serials.txt contains 10.115.2.22 and usrp-bs-only.json has "channel" : "AB" |
plot_hdf5.py fails with missing module |
Python dependencies not installed | Install with pip3 install h5py matplotlib numpy (TODO: verify exact requirements) |
develop branch for UHD support)Author(s): COSMOS team (original: Tingjun Chen, Duke University) · Last verified: Not re-tested (migrated 2026-06-20) · Tested image/release: baseline-gr.ndz / UHD 3.15 · Tags: mimo, channel-sounding, usrp-2974, renew, uhd, tdd