A tutorial on how to deploy and use LibIQ, with a use case for spectrum classification.
The O-RAN architecture enables flexible, data-driven management through RAN softwarization and xApps/rApps. However, latency and privacy concerns limit real-time RF monitoring. To overcome these limitations, we adopt dApps, real-time microservices co-located with the RAN nodes, for spectrum classification. This tutorial guides you through deploying LibIQ, a modular and extensible C++/Python library specifically designed for RF signal analysis in O-RAN environments. LibIQ provides a complete pipeline for reading, processing, visualizing, and labeling I/Q time-series data collected from wireless environments. It supports multiple input formats (e.g., binary, CSV, SigMF), and enables fast conversion of raw I/Q samples into structured time-series ready for classification. LibIQ also includes utilities to generate scatterplots, spectrograms and perform real-time spectrum analysis directly on-device. A core feature of LibIQ is its built-in Convolutional Neural Network (CNN) classifier, which achieves high accuracy predictions on various RF signal types. This makes it ideal for tasks like spectrum classification, anomaly detection, and signal fingerprinting in dynamic wireless networks.
Since this tutorial uses the dApp framework, please refer to the dapps tutorial for detailed instructions on setting up the gNB.
Prerequisites: Make sure that python3
, hatch
and libiq
are installed on your system.
pip3 install hatch libiq
For more details on how to install hatch
please refer to the official website.
A stable version of the dapp
package should already be loaded on the Python Package Index (PyPI), thus these steps are intended to be conducted only to test the example Spectrum Classification dApp presented in this tutorial.
git clone https://github.com/wineslab/dApp-library dApp
cd dApp
hatch build
pip3 install dist/*.tar.gz
python3 examples/spectrum_dapp.py --control --link zmq --transport tcp --demo-gui --model <PATH_TO_MODEL>
The options available for this example include:
--ota
: Enable OTA configuration or specify if running on Colosseum.--link
: Specify the link layer to be used (default: zmq
).--transport
: Specify the transport layer to be used (default: ipc
).--save-iqs
: Enable data collection mode to save I/Q samples for offline analysis.--control
: Enable PRB blocking for spectrum management.--energy-gui
: Enable the Energy GUI through X11.--iq-plotter-gui
: Enable the I/Q Plotter GUI through X11.--demo-gui
: Enable the Demo GUI website.--timed
: Run for 5 minutes and exits.--time-window
: Number of input vectors to pass to the CNN model.--input-vector
: Number of I/Q samples per input vector.--moving-avg-window
: Window size (in samples) for the moving average used to detect energy peaks in the spectrum.--extraction-window
: Number of samples to retain after detecting an energy peak.--model
: Path to the CNN model file to be used.The integration of LibIQ into a dApp that is presented in this tutorial is also available as a Linux container on Colosseum.
The name of the container is oai-dapp
with password dapps
.
To replicate this tutorial on Colosseum, create a reservation with at least three different nodes in the same order as presented here:
oai-5gcore-2024
: Core for OAI that allows the identification and pairing of the UE to the network. Password for this container is pass
.oai-dapp
: our container that will act as a gNB with a dApp embedded; Password for this container is dapps
.cast
: container from [1], used on this case as an external incumbent. Password for this container is cast123
;Once the reservation has been deployed, start a Radio Frequency (RF) scenario on Colosseum, e.g., scenario 10011
(All Paths 0 dB, 3.6 GHz as center frequency, 10 nodes max).
colosseumcli rf start 10011 -c
Run the OAI 5G Core-network as indicated in the original tutorial:
cd oai-cn5g
docker compose up -d
Access the first oai-dapp
container, create a tmux
session and start the gNB operations with the aforementioned commands.
In the openairinterface5g
folder, we have also a utility script perform the commands automatically:
cd ~/openairinterface5g
./run_oai.sh
Please edit this file and the configuration according to your needs.
Split the tmux
session or open a new terminal and create a new window that will be dedicated to the dApp.
For Colosseum, we use a different noise floor threshold by not including the --ota
parameter.
cd ~/dApp
taskset -ca 0-45 python3 examples/spectrum_dapp.py --control --link zmq --transport tcp --demo-gui --model <PATH_TO_MODEL>
With the flag --demo-gui
, you should be able to access a webpage on your browser that shows the I/Q samples sensed by the gNB and delivered by to the dApp.
To make the dApp faster the taskset
utility can be used, please check the above command and the number of available cores on the SRN with nproc
.
Since the sensed I/Qs are extracted from a reserved OFDM slot, the UE communication will not be shown in the web interface.
Finally, access the incumbent container (i.e., cast
) and start a uniform noise with the command:
uhd_siggen --freq 3.6292e9 --samp-rate 1e6 --gain 20 -m 1 --uniform
The expected output is that the dApp, thanks to LibIQ, is able to detect the presence of a signal on the incumbent and classify it. The supported RF signal types are No RFI
, Radar
, Jammer
, Square
and Triangular
If you use LibIQ in your work, please cite our paper:
Filippo Olimpieri and Noemi Giustini and Andrea Lacava and Salvatore D’Oro and Tommaso Melodia and Francesca Cuomo, "LibIQ: Toward Real-Time Spectrum Classification in O-RAN dApps," in Proceedings of IEEE Mediterranean Communication and Computer Networking Conference (MedComNet), Cagliari, Italy, June 2025. [pdf] [bibtex]