Split the OCUDU (srsRAN Project) gNB at the O-RAN 7.2 fronthaul and drive a real, commercial O-RU: Open5GS core, a commercial Quectel modem, and a working end-to-end path (registration → PDU session → ping → iperf3) on two of the three COSMOS O-RUs. The Benetel path is also given three ways (command by command, Ansible, Jupyter) with a parity check that all three agree.
OCUDU sends frequency-domain IQ as eCPRI over a VLAN-4 Ethernet link to a standalone O-RU. Unlike Duranta it needs no DPDK: its Open Fronthaul (ru_ofh) uses a raw socket on the E810 physical function (network_interface: DATA1a). It also has no NETCONF/M-Plane client, so every O-RU must run standalone from its own on-board configuration.
The three O-RUs behave differently enough that "the O-RU" is not one recipe:
The same radios under OAI are covered in Duranta 5G SA — O-RAN 7.2 split. For a real air interface without the 7.2 split, start with the OCUDU over-the-air tutorial.
After completing this tutorial you will be able to:
TX_TOTAL, DuConnected).| Difficulty | Advanced |
| Estimated time | 60–90 min (Benetel, first run including host preparation); ~25 min per parity run |
| Domain / sandbox | sb1 (O-DU srv1-lg1 + UE sdr1-in3) for the Benetel bundle; any 7.2 server for the LiteOn procedure (run on osc, sb1 and rrail); O-RU power from console.instrument |
| Topic group | Cellular (4G/5G/O-RAN) |
| Last verified | 2026-09-16 — Benetel three-mode parity PASS (srv1-lg1.sb1, ocudu-20260915, 2× Xeon Gold 6226; DL 106–111 / UL 79.3–79.6 Mbit/s). LiteOn end-to-end by the manual procedure 2026-09-14 (srv18.osc) |
Background knowledge
ssh, ip and iperf3.Account & access
console.instrument.cosmos-lab.org — see SSH access.Devices / nodes
| Resource | Role | Qty | Notes |
|---|---|---|---|
srv1-lg1.sb1 (bundle) or srv18.osc |
O-DU (OCUDU gnb) + Open5GS |
1 | Intel E810, fronthaul on DATA1a, VLAN 4; CPU isolation applied per host (Setup) |
| One O-RU | commercial radio | 1 | Benetel RAN650 10.3.12.20, LiteOn FlexFi 10.3.14.1, or Foxconn RPQN 10.3.16.1 |
sdr1-in3.sb1 |
UE host | 1 | Quectel RM520N-GL; AT /dev/ttyUSB2, QMI /dev/cdc-wdm0 |
⚠️ The Benetel and the LiteOn share one RF space — power only one of them at a time. All three O-RUs are powered from console.instrument.cosmos-lab.org:
omf tell -a offh -t liteon-oru.instrument.orbit-lab.org
omf tell -a on -t benetel-oru.instrument.orbit-lab.org
The first omf tell to an instrument is sometimes dropped: confirm by pinging the RU from the O-DU host.
Disk images
| Image | Load onto | Provides |
|---|---|---|
ocudu.ndz (currently ocudu-20260909b) |
the O-DU server | OCUDU gnb, Open5GS, cosmos-ofh-isolation, cosmos-fh-ptp-guard, the Benetel mode script and RU key |
lte5gue-20260905.ndz |
sdr1-in3.sb1 |
Quectel tooling (qmicli, iperf3) |
Software components
| Component | Version | Source |
|---|---|---|
OCUDU gnb |
commit 7238f4f (branch dev) |
preinstalled in the image |
| Open5GS | image build | /root/open5gs/start.sh |
| Tutorial bundle | ocudu-72 |
/files/cosmos-tutorials-ocudu-72.zip |
console.sb1 --ssh--> srv1-lg1.sb1 (O-DU: OCUDU gnb + Open5GS, PTP slave on DATA1a)
| DATA1a, VLAN 4: eCPRI C/U-plane + PTP
v
fronthaul switch (PTP grandmaster)
|
v
O-RU (Benetel / LiteOn / Foxconn) ) ) ) air ( ( ( Quectel on sdr1-in3.sb1
The core (Open5GS) runs on the O-DU host; the UE's data path is wwan0 → gNB → ogstun (192.168.100.1/22), NATed to the Internet.
ssh root@srv1-lg1.sb1.cosmos-lab.org
cosmos-ofh-isolation --nic DATA1a --apply # writes GRUB, then:
reboot
grep -o 'isolcpus=[^ ]*' /proc/cmdline
isolcpus=7,9,11,13,15,17,19,21,23
The bundle reads this and refuses to start without it. ⚠️ Do not leave a host isolated for the OTA tutorial: its gNB then gets only the non-isolated cores, and on a 24-core host it stalls.ssh <username>@console.sb1.cosmos-lab.org
wget -q https://www.cosmos-lab.org/files/cosmos-tutorials-ocudu-72.zip
unzip -o cosmos-tutorials-ocudu-72.zip && cd tutorials
mimo_mode, compression, TDD pattern); each mismatch alone gives a clean fronthaul and a silent radio. The bring-up checks and applies the OCUDU state with an RU profile tool if it is present on the O-DU host at /root/oru-profiles/ (ask the testbed team for it — it is not part of the bundle), and otherwise falls back to the one register that silences the radio when wrong (number of transmitters). The RU management key ships with the image and must never be copied into a bundle.Two nodes in OMF_NODES, in this order: the O-DU host, then the UE host.
export OMF_NODES="srv1-lg1,sdr1-in3"
ansible-playbook ocudu-72/ocudu-72.yml # Ansible mode
bash ocudu-72/ocudu-72_manual.sh srv1-lg1 sdr1-in3 # the same experiment, command by command
Every mode does the same sequence:
ocudu-72 / ocudu-gnb units only).console.instrument and verify it really went down (no ICMP reply) and came back (its sshd, :22). In standalone mode the RU's NETCONF server (:830) is disabled on purpose and never opens.BOOTUP TIME stamp is newer than its own boot), the RU state check, fronthaul NIC jumbo + promiscuous + IRQs off the isolated cores, PTP (step once, then slew), Open5GS, the gNB.COSMOS-O, PLMN 00101; wait for registration; QMI data call; ping the core gateway and 8.8.8.8; iperf3 DL and UL.The notebook runs the same steps: ocudu-72/ocudu-72_tutorial.ipynb. Bring-up output you should see:
=== [1b] O-RU: THIS boot's radio bring-up finished, per-stack state asserted, Tx count set ===
RU bring-up finished this boot: Mon Sep 14 02:21:19 UTC 2026 (RU up 152s)
C0300 (number of Tx) = 0x1010101
ptp4l locked: ptp4l[2139.061]: rms 3149 max 4739 freq -6742 +/- 390 delay
stepped: |offset| = 148 ns
slewing: |offset| = 1 ns (UTC timescale)
AMF OK
GNB-ALIVE, cell up
ether_rx rx_bytes: 892800
The LiteOn works from any 7.2 server; it has been run from all three COSMOS platforms (2× Xeon Gold 6126, 2× Xeon Gold 6226, AMD EPYC 9355P), with its own complete parameter set — never one edited from another RU's. Power the Benetel off and the LiteOn on, then wait until the RU reports Sync State : SYNCHRONIZED (about 4–5 minutes after boot).
jumboframe resets to 0 on each boot; without it the RU drops out of sync under load. The LiteOn CLI is user/user, enable liteon168; the testbed's liteon-ru-config.py applies and verifies the full RU parameter set (center 3750 MHz, BFP-9, DU MAC 00:11:22:33:44:66, slot-id 1, PRACH eAxC 4–7, VLAN 4, jumboframe 1).prach_port_id: [4, 5, 6, 7]):ru_ofh:
ta4_max: 1960 # the reference 500 us closes the uplink window a hair before the LiteOn's PRACH arrives
ta4_min: 0
expert_phy:
max_proc_delay: 10 # at 15 the PRACH reaches the scheduler after the RAR window's last downlink slot
configs/ocudu-liteon-72/ocudu-liteon-bringup.sh from the duranta-cosmos repo, copied to the O-DU host next to gnb-ocudu-liteon.yml, liteon-ru-config.py and the bundle's ocudu-72_bringup.sh. It renders this host's CPU affinities from its isolated cores and sets network_interface (FH=<iface>, default DATA1a). It then re-applies the RU's volatile setting and runs the bundle's bring-up: promiscuous NIC, PTP, Open5GS, and the gNB under taskset over all cores. Attach the modem as above.FH=DATA1a /root/gap-liteon/ocudu-liteon-bringup.sh # stop: systemctl stop ocudu-72 ocudu-iperf
What each fix changed, as the gNB log reported it:
| Setting | Before | After |
|---|---|---|
ta4_max: 500 → 1960 |
every PRACH: missed incoming User-Plane PRACH messages for slot '991.19', then no uplink PRACH context was found for slot '223.19' |
0 misses, rx_late 0; preambles detected |
max_proc_delay: 15 → 10 |
Could not transmit RAR within the window=[833.0..834.0), prach_slot=832.19 |
RAR sent, Msg3 crc=OK, UE registered |
Not attaching yet. What works and the exact latch order are on the OCUDU + Foxconn page.
Run all three modes and compare (a static inventory is only needed if a node's chassis manager is down — see Troubleshooting):
./ocudu-72/ocudu-72_parity.sh srv1-lg1 sdr1-in3
=== ocudu-72 parity ===
mode ue_ipv4 rsrp_dbm sinr_db ofh_drops ping_rtt_ms ping_remote_loss_pct dl_mbps ul_mbps
--------------------------------------------------------------------------------------------
ansible 192.168.100.2-70 25 0 30.07 0.0 105.0 78.4
manual 192.168.100.3-70 25 0 28.97 0.0 101.0 78.4
jupyter 192.168.100.4-70 26 0 35.26 0.0 109.0 79.5
dl_mbps spread max/min = 1.08 (tol 1.60)
ul_mbps spread max/min = 1.01 (tol 1.80)
PARITY: PASS (same experiment in all modes; all checks green)
### releasing the rig (teardown)
rig released
(Real output, 2026-09-14. The table runs the UE address into the RSRP column: 192.168.100.2, -70.) The harness tears the rig down on every exit, including a failure or Ctrl-C.
One Quectel RM520N-GL, one UE, iperf3 receiver line, 10 s each direction, n78 100 MHz TDD.
| O-RU | O-DU CPU platform | Date / image | DL Mbit/s | UL Mbit/s | Notes |
|---|---|---|---|---|---|
| Benetel RAN650 | 2× Xeon Gold 6226 @ 2.70 GHz, 24 cores / 24 threads | 2026-09-16 / ocudu-20260915 |
106–111 | 79.3–79.6 | three-mode parity; RSRP −69, SINR 25 |
| Benetel RAN650 | 2× Xeon Gold 6226 @ 2.70 GHz, 24 cores / 24 threads | 2026-09-14 / ocudu-20260909b |
101–109 | 78–80 | three-mode bundle; RSRP −70, SINR 25 |
| Benetel RAN650 | 2× Xeon Gold 6126 @ 2.60 GHz, 24 cores / 48 threads | 2026-09-09 / ocudu-20260909b |
248 | 77.4 | manual procedure; 18 isolated cores |
| LiteOn FlexFi | 2× Xeon Gold 6126 @ 2.60 GHz, 24 cores / 48 threads | 2026-09-14 / ocudu-20260909b |
154 | 48.6 | manual; CQI 12, rank 4; DL BLER high (open) |
| Benetel RAN650 | 1× AMD EPYC 9355P, 32 cores / 32 threads | 2026-09-14 / ocudu-20260909b |
104 | 79.8 | three-mode bundle, Ansible mode; RSRP −70, SINR 26 |
| LiteOn FlexFi | 2× Intel Xeon Gold 6226 @ 2.70 GHz, 24 cores / 24 threads | 2026-09-14 / ocudu-20260909b |
170–195 | 136–140 | ocudu-liteon-bringup.sh, 3 runs; DL BLER ~1 % |
| LiteOn FlexFi | 1× AMD EPYC 9355P, 32 cores / 32 threads | 2026-09-14 / ocudu-20260909b |
140–155 | 131–145 | ocudu-liteon-bringup.sh, 3 runs |
| Foxconn RPQN | — | — | not measured | not measured | not attaching |
The parity harness releases the rig itself. After running a single mode, tear it down:
export OMF_NODES="srv1-lg1,sdr1-in3"
ansible-playbook ocudu-72/ocudu-72_teardown.yml
Power the O-RU off only if nobody else is using it: omf tell -a offh -t benetel-oru.instrument.orbit-lab.org from console.instrument.cosmos-lab.org. If you isolated a host that others use for non-7.2 work, restore its GRUB (or reload ocudu.ndz).
| Symptom | Likely cause | Fix |
|---|---|---|
Every counter healthy, gNB started, 0 fronthaul drops — and no UE sees a cell (Benetel) |
The RU is still in the other stack's state (compression, mimo_mode, TDD pattern) |
Apply the OCUDU state and reboot the RU from its own OS; a power cycle alone did not pick up a config change on this RU. The bundle refuses to start in the wrong state. |
DU receives nothing (ether_rx rx_bytes 0), no PRACH ever |
Fronthaul NIC not promiscuous — srsRAN's raw socket only sees frames the NIC accepts | ip link set DATA1a promisc on (the bring-up does this) |
Invalid CPU core selected 'N' |
isolcpus hides the isolated cores from srsRAN's affinity check |
Launch inside taskset -c 0-<ncpus-1> (the bring-up does this) |
The RU's TX_TOTAL stays 0 / DuConnected : notReady |
Not a fault by itself. TX_TOTAL counts the RU's uplink frames to the DU (0 with no UE scheduled); the LiteOn reports DuConnected : notReady even with a UE registered |
Judge radiation by a UE scan (AT+QSCAN) and the DU's PRACH/rx_on_time counters instead |
PRACH missed incoming User-Plane PRACH messages, then no uplink PRACH context ... slot 'N.19' a few µs later |
Uplink receive window closes just before the RU's PRACH arrives | Raise ru_ofh.ta4_max (1960 on this fabric) |
Preambles detected, then Could not transmit RAR within the window |
The MAC runs so far ahead that the PRACH reaches the scheduler after the window's last downlink slot | Lower expert_phy.max_proc_delay (10 worked on srv18; watch for late DL_TTI) |
Every uplink symbol dropped: no section was decoded correctly |
Compression-header mode mismatch with the RU | Match enable_ul_static_compr_hdr to what the RU sends (Foxconn needs true) |
| Registers, gets an IP, 100 % loss; the gNB sees no scheduling request | The modem's USB data endpoint is stuck: ip -s link show wwan0 shows TX errors +1 per packet and 0 packets sent |
USB device reset of the modem (the bundle's modem helper does this when it sees TX errors); a qmi_wwan rebind does not help |
Preflight: OMF_NODES selected 1 node(s) |
omf stat shows a node Unknown (UNREACHABLE) (its chassis manager is down) and the inventory keeps only powered-on nodes |
Run with a static inventory: ANSIBLE_INVENTORY=~/static.yml containing group nodes with the O-DU host first, then the UE host |
omf tell ... No such node in 'rrail' |
O-RUs moved to the instrument domain | Use console.instrument.cosmos-lab.org |
gitlab.com/ocudu/ocudu)Author(s): Ivan Seskar · Last verified: 2026-09-16 · Tested image/release: ocudu-20260915 (OCUDU 36128c5 + T1a patch), lte5gue-20260905.ndz, Benetel RAN650 V2.1.0, LiteOn FlexFi 02.00.10 · Tags: ocudu, srsran, oran, 7.2, fronthaul, benetel, liteon, foxconn