Merge branch 'zmq-docs' into 'develop'

docs: add a step-by-step guide for OAI ZMQ radio

See merge request oai/openairinterface5g!4105
This commit is contained in:
Bartosz Podrygajlo
2026-06-29 18:14:21 +00:00
3 changed files with 698 additions and 0 deletions

View File

@@ -59,6 +59,7 @@ There is some general information in the [OpenAirInterface Gitlab Wiki](https://
- [How to setup gNB frequency](./gNB_frequency_setup.md)
- [How to use the RT data recording app](./data_recording.md)
- [How to use packages](./packages.md)
- [How to use OAI ZMQ radio](./zmq_tutorial.md)
Legacy unmaintained files:
- [`L2NFAPI_NOS1.md`](./L2NFAPI_NOS1.md), [`L2NFAPI_S1.md`](./L2NFAPI_S1.md):

520
doc/noise_injector.grc Normal file
View File

@@ -0,0 +1,520 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
options:
parameters:
author: ''
catch_exceptions: 'True'
category: '[GRC Hier Blocks]'
cmake_opt: ''
comment: ''
copyright: ''
description: ''
gen_cmake: 'On'
gen_linking: dynamic
generate_options: qt_gui
hier_block_src_path: '.:'
id: untitled
max_nouts: '0'
output_language: python
placement: (0,0)
qt_qss_theme: ''
realtime_scheduling: ''
run: 'True'
run_command: '{python} -u {filename}'
run_options: prompt
sizing_mode: fixed
thread_safe_setters: ''
title: Simple noise injector
window_size: (1000,1000)
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [56, 72.0]
rotation: 0
state: enabled
blocks:
- name: noise_amp
id: variable_qtgui_range
parameters:
comment: ''
gui_hint: ''
label: ''
min_len: '200'
orient: QtCore.Qt.Horizontal
rangeType: float
start: '0'
step: '0.001'
stop: '1'
value: '0'
widget: counter_slider
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [376, 56.0]
rotation: 0
state: enabled
- name: samp_rate
id: variable
parameters:
comment: ''
value: '32000000'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [240, 72.0]
rotation: 0
state: enabled
- name: analog_fastnoise_source_x_0
id: analog_fastnoise_source_x
parameters:
affinity: ''
alias: ''
amp: noise_amp
comment: ''
maxoutbuf: '0'
minoutbuf: '0'
noise_type: analog.GR_GAUSSIAN
samples: '8192'
seed: '0'
type: complex
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [304, 224.0]
rotation: 0
state: enabled
- name: blocks_add_xx_0
id: blocks_add_xx
parameters:
affinity: ''
alias: ''
comment: ''
maxoutbuf: '0'
minoutbuf: '0'
num_inputs: '2'
type: complex
vlen: '1'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [624, 296.0]
rotation: 0
state: enabled
- name: blocks_throttle2_0_0
id: blocks_throttle2
parameters:
affinity: ''
alias: ''
comment: ''
ignoretag: 'True'
limit: auto
maximum: '0.1'
maxoutbuf: '0'
minoutbuf: '0'
samples_per_second: samp_rate
type: complex
vlen: '1'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [328, 384.0]
rotation: 0
state: enabled
- name: qtgui_freq_sink_x_0
id: qtgui_freq_sink_x
parameters:
affinity: ''
alias: ''
alpha1: '1.0'
alpha10: '1.0'
alpha2: '1.0'
alpha3: '1.0'
alpha4: '1.0'
alpha5: '1.0'
alpha6: '1.0'
alpha7: '1.0'
alpha8: '1.0'
alpha9: '1.0'
autoscale: 'False'
average: '1.0'
axislabels: 'True'
bw: samp_rate
color1: '"blue"'
color10: '"dark blue"'
color2: '"red"'
color3: '"green"'
color4: '"black"'
color5: '"cyan"'
color6: '"magenta"'
color7: '"yellow"'
color8: '"dark red"'
color9: '"dark green"'
comment: ''
ctrlpanel: 'False'
fc: '0'
fftsize: '1024'
freqhalf: 'True'
grid: 'False'
gui_hint: ''
label: Relative Gain
label1: ''
label10: ''''''
label2: ''''''
label3: ''''''
label4: ''''''
label5: ''''''
label6: ''''''
label7: ''''''
label8: ''''''
label9: ''''''
legend: 'True'
maxoutbuf: '0'
minoutbuf: '0'
name: '""'
nconnections: '1'
norm_window: 'False'
showports: 'False'
tr_chan: '0'
tr_level: '0.0'
tr_mode: qtgui.TRIG_MODE_FREE
tr_tag: '""'
type: complex
units: dB
update_time: '0.10'
width1: '1'
width10: '1'
width2: '1'
width3: '1'
width4: '1'
width5: '1'
width6: '1'
width7: '1'
width8: '1'
width9: '1'
wintype: window.WIN_BLACKMAN_hARRIS
ymax: '10'
ymin: '-140'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [928, 328.0]
rotation: 0
state: enabled
- name: qtgui_time_sink_x_0
id: qtgui_time_sink_x
parameters:
affinity: ''
alias: ''
alpha1: '1.0'
alpha10: '1.0'
alpha2: '1.0'
alpha3: '1.0'
alpha4: '1.0'
alpha5: '1.0'
alpha6: '1.0'
alpha7: '1.0'
alpha8: '1.0'
alpha9: '1.0'
autoscale: 'True'
axislabels: 'True'
color1: blue
color10: dark blue
color2: red
color3: green
color4: black
color5: cyan
color6: magenta
color7: yellow
color8: dark red
color9: dark green
comment: ''
ctrlpanel: 'False'
entags: 'True'
grid: 'False'
gui_hint: ''
label1: Signal 1
label10: Signal 10
label2: Signal 2
label3: Signal 3
label4: Signal 4
label5: Signal 5
label6: Signal 6
label7: Signal 7
label8: Signal 8
label9: Signal 9
legend: 'True'
marker1: '-1'
marker10: '-1'
marker2: '-1'
marker3: '-1'
marker4: '-1'
marker5: '-1'
marker6: '-1'
marker7: '-1'
marker8: '-1'
marker9: '-1'
name: Output sig
nconnections: '1'
size: '1024'
srate: samp_rate
stemplot: 'False'
style1: '1'
style10: '1'
style2: '1'
style3: '1'
style4: '1'
style5: '1'
style6: '1'
style7: '1'
style8: '1'
style9: '1'
tr_chan: '0'
tr_delay: '0'
tr_level: '0.0'
tr_mode: qtgui.TRIG_MODE_FREE
tr_slope: qtgui.TRIG_SLOPE_POS
tr_tag: '""'
type: complex
update_time: '0.10'
width1: '1'
width10: '1'
width2: '1'
width3: '1'
width4: '1'
width5: '1'
width6: '1'
width7: '1'
width8: '1'
width9: '1'
ylabel: Amplitude
ymax: '1'
ymin: '-1'
yunit: '""'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [928, 192.0]
rotation: 0
state: enabled
- name: qtgui_time_sink_x_1
id: qtgui_time_sink_x
parameters:
affinity: ''
alias: ''
alpha1: '1.0'
alpha10: '1.0'
alpha2: '1.0'
alpha3: '1.0'
alpha4: '1.0'
alpha5: '1.0'
alpha6: '1.0'
alpha7: '1.0'
alpha8: '1.0'
alpha9: '1.0'
autoscale: 'True'
axislabels: 'True'
color1: blue
color10: dark blue
color2: red
color3: green
color4: black
color5: cyan
color6: magenta
color7: yellow
color8: dark red
color9: dark green
comment: ''
ctrlpanel: 'False'
entags: 'True'
grid: 'False'
gui_hint: ''
label1: Signal 1
label10: Signal 10
label2: Signal 2
label3: Signal 3
label4: Signal 4
label5: Signal 5
label6: Signal 6
label7: Signal 7
label8: Signal 8
label9: Signal 9
legend: 'True'
marker1: '-1'
marker10: '-1'
marker2: '-1'
marker3: '-1'
marker4: '-1'
marker5: '-1'
marker6: '-1'
marker7: '-1'
marker8: '-1'
marker9: '-1'
name: Noise
nconnections: '1'
size: '1024'
srate: samp_rate
stemplot: 'False'
style1: '1'
style10: '1'
style2: '1'
style3: '1'
style4: '1'
style5: '1'
style6: '1'
style7: '1'
style8: '1'
style9: '1'
tr_chan: '0'
tr_delay: '0'
tr_level: '0.0'
tr_mode: qtgui.TRIG_MODE_FREE
tr_slope: qtgui.TRIG_SLOPE_POS
tr_tag: '""'
type: complex
update_time: '0.10'
width1: '1'
width10: '1'
width2: '1'
width3: '1'
width4: '1'
width5: '1'
width6: '1'
width7: '1'
width8: '1'
width9: '1'
ylabel: Amplitude
ymax: '1'
ymin: '-1'
yunit: '""'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [744, 104.0]
rotation: 0
state: enabled
- name: qtgui_waterfall_sink_x_0
id: qtgui_waterfall_sink_x
parameters:
affinity: ''
alias: ''
alpha1: '1.0'
alpha10: '1.0'
alpha2: '1.0'
alpha3: '1.0'
alpha4: '1.0'
alpha5: '1.0'
alpha6: '1.0'
alpha7: '1.0'
alpha8: '1.0'
alpha9: '1.0'
axislabels: 'True'
bw: samp_rate
color1: '0'
color10: '0'
color2: '0'
color3: '0'
color4: '0'
color5: '0'
color6: '0'
color7: '0'
color8: '0'
color9: '0'
comment: ''
fc: '0'
fftsize: '1024'
freqhalf: 'True'
grid: 'False'
gui_hint: ''
int_max: '10'
int_min: '-140'
label1: ''
label10: ''
label2: ''
label3: ''
label4: ''
label5: ''
label6: ''
label7: ''
label8: ''
label9: ''
legend: 'True'
maxoutbuf: '0'
minoutbuf: '0'
name: '""'
nconnections: '1'
showports: 'False'
type: complex
update_time: '0.10'
wintype: window.WIN_BLACKMAN_hARRIS
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [1104, 256.0]
rotation: 0
state: enabled
- name: zeromq_rep_sink_0
id: zeromq_rep_sink
parameters:
address: tcp://127.0.0.1:4446
affinity: ''
alias: ''
bind: 'True'
comment: ''
hwm: '-1'
pass_tags: 'False'
timeout: '100'
type: complex
vlen: '1'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [752, 432.0]
rotation: 0
state: enabled
- name: zeromq_req_source_0
id: zeromq_req_source
parameters:
address: tcp://127.0.0.1:4445
affinity: ''
alias: ''
bind: 'False'
comment: ''
hwm: '-1'
maxoutbuf: '0'
minoutbuf: '0'
pass_tags: 'False'
timeout: '100'
type: complex
vlen: '1'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [32, 240.0]
rotation: 0
state: enabled
connections:
- [analog_fastnoise_source_x_0, '0', blocks_add_xx_0, '0']
- [analog_fastnoise_source_x_0, '0', qtgui_time_sink_x_1, '0']
- [blocks_add_xx_0, '0', qtgui_freq_sink_x_0, '0']
- [blocks_add_xx_0, '0', qtgui_time_sink_x_0, '0']
- [blocks_add_xx_0, '0', qtgui_waterfall_sink_x_0, '0']
- [blocks_add_xx_0, '0', zeromq_rep_sink_0, '0']
- [blocks_throttle2_0_0, '0', blocks_add_xx_0, '1']
- [zeromq_req_source_0, '0', blocks_throttle2_0_0, '0']
metadata:
file_format: 1
grc_version: 3.10.9.2

177
doc/zmq_tutorial.md Normal file
View File

@@ -0,0 +1,177 @@
# ZMQ Radio Tutorial
This tutorial provides instructions on how to use the ZeroMQ (ZMQ) radio implementation in OpenAirInterface. The ZMQ
radio allows for simulating the radio link over a network using ZMQ sockets, which is useful for development, testing,
and CI/CD without the need for physical SDR hardware.
It currently supports two main scenarios:
1. Connecting an **OAI gNB** with an **OAI NR UE**.
2. Connecting an **OCUDU gNB** (Split 8) with an **OAI NR UE**.
3. Using GNU Radio companion to inject noise in DL between **OAI gNB** and **OAI NR UE**.
## Prerequisites
Before starting, ensure you have the following dependencies installed on your system:
- **ZeroMQ development libraries**:
```bash
sudo apt-get install libzmq3-dev
```
- **OAI Dependencies**: Ensure you have followed the standard OAI build instructions to install necessary system
dependencies.
## Compilation
To use the ZMQ radio, you need to build the `oai_zmqdevif` library along with the softmodems.
Configure the build:
```bash
cmake ../ -DOAI_ZMQ=ON
```
Compile:
```bash
cmake --build . --target nr-softmodem nr-uesoftmodem ldpc params_libconfig zmq_radio
```
## Scenario 1: OAI gNB <-> OAI NR UE
In this scenario, both the gNB and the UE are running OAI code and communicate over ZMQ.
### 1. Start the gNB
Run the gNB with the `oai_zmqdevif` device name. Note the mapping of TX/RX ports.
```bash
sudo ./nr-softmodem -O ../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --device.name oai_zmqdevif --zmq.[0].tx_channels tcp://127.0.0.1:4556 --zmq.[0].rx_channels tcp://127.0.0.1:4557
```
### 2. Start the UE
Start the UE and connect it to the ports exposed by the gNB. Note that the UE's TX port should match the gNB's RX port,
and vice versa.
```bash
sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --ssb 516 --device.name oai_zmqdevif --zmq.[0].tx_channels tcp://127.0.0.1:4557 --zmq.[0].rx_channels tcp://127.0.0.1:4556
```
## Scenario 2: OCUDU gNB (Split 8) <-> OAI NR UE
This scenario demonstrates interoperability between the OCUDU project's Split 8 gNB and the OAI NR UE.
### 1. Start the Core Network
Deploy the OAI 5G Core Network using Docker Compose.
```bash
cd doc/tutorial_resources/oai-cn5g
docker compose up -d
```
### 2. Configure and Start OCUDU gNB
Create a `config.yml` in your OCUDU directory with the following content:
```yaml
cu_cp:
amf:
addrs: 192.168.70.132
port: 38412
bind_addrs: 0.0.0.0
supported_tracking_areas:
- tac: 1
plmn_list:
- plmn: "00101"
tai_slice_support_list:
- sst: 1
ru_sdr:
device_driver: zmq
device_args: tx_port=tcp://127.0.0.1:4558,rx_port=tcp://127.0.0.1:4556
srate: 23.04
tx_gain: 25
rx_gain: 25
cell_cfg:
dl_arfcn: 632628
band: 78
channel_bandwidth_MHz: 20
common_scs: 30
plmn: "00101"
tac: 1
pci: 1
tdd_ul_dl_cfg:
dl_ul_tx_period: 5
nof_dl_slots: 3
nof_dl_symbols: 10
nof_ul_slots: 1
nof_ul_symbols: 2
csi:
csi_rs_enabled: false
pucch:
formats: f0_and_f2
nof_cell_csi_res: 0
log:
filename: gnb.log
all_level: debug
pcap:
mac_enable: false
mac_filename: /tmp/gnb_mac.pcap
ngap_enable: false
ngap_filename: /tmp/gnb_ngap.pcap
```
Run the OCUDU gNB:
```bash
sudo ./apps/gnb_split_8/gnb -c config.yml
```
### 3. Start OAI NR UE
Connect the OAI NR UE to the OCUDU gNB via ZMQ.
```
sudo ./nr-uesoftmodem -r 51 -E --numerology 1 --band 78 -C 3489420000 --ssb 0 --uecap_file ../targets/PROJECTS/GENERIC-NR-5GC/CONF/uecap_ports1.xml --log_config.global_log_options level,nocolor,time --device.name oai_zmqdevif --zmq.[0].tx_channels tcp://127.0.0.1:4556 --zmq.[0].rx_channels tcp://127.0.0.1:4558 -O ../ci-scripts/conf_files/nrue.uicc.conf --uicc0.imsi 001010000000001
```
> Once connected, you can perform standard network tests like `ping` or `iperf` between the UE and the core network.
## Scenario 3: Inject noise using GNU radio companion
In this scenario, you can inject noise between OAI gNB and OAI NR UE. An example GNU radio companion project
is added in `noise_injector.grc`. The program expects a ZMQ radio stream at tcp://127.0.0.1:4445 and outputs the
same samples with added noise at tcp://127.0.0.1:4446
Architecture
```mermaid
graph TD
nr_softmodem -- "DL - port 4445" --> noise_injector
noise_injector -- "DL+ noise - port 4446" --> nr_uesoftmodem
nr_uesoftmodem -- "UL - port 4557"--> nr_softmodem
```
### 1. Start the gNB
```
sudo ./nr-softmodem -O ../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --device.name oai_zmqdevif --zmq.[0].tx_channels tcp://127.0.0.1:4445 --zmq.[0].rx_channels tcp://127.0.0.1:4557
```
### 2. Start the UE
```
sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --ssb 516 --device.name oai_zmqdevif --zmq.[0].tx_channels tcp://127.0.0.1:4557 --zmq.[0].rx_channels tcp://127.0.0.1:4446
```
### 3. Start the GNU radio companion program
You should see a window with an input field labelled noise_amp and a frequency domain waterfall graph of the gNBs
downlink signal.
Now you should be able to change the injected noise and see the UE loose DL connection. Use either the slider
or the input field to change noise amplitude.