Bring up a 5G standalone network on COSMOS SB1 from a single baremetal image that
carries all three 5G SA gNB stacks — Amarisoft, OCUDU (srsRAN-Project fork) and
Duranta (OpenAirInterface fork) — plus a self-contained Open5GS core, each
driving the same USRP X310 and attaching the same real Quectel UE over the
air on band n41. Switch stacks back-to-back to compare them on identical hardware, RF, and UE.
The goal is a single, reproducible node image to compare three very different 5G SA
RAN implementations on identical hardware, identical RF, and an identical UE. One
image (ubuntu2404-uhd4.9-RT-triostack.ndz) carries all three RANs so they can be
brought up back-to-back without re-imaging, making side-by-side measurement
(throughput, attach behaviour, UL/DL quality) trivial.
Only one RAN stack runs at a time — they share the single X310 and the cell
frequency. You switch stacks with the stop/start scripts plus an X310 power-cycle.
The UE and core are common to all three, so the same SIM and the same attach
procedure work everywhere.
| Stack | Lineage | gNB binary | Core it uses | Fronthaul here |
|---|---|---|---|---|
| Amarisoft | commercial (Amarisoft Callbox SW) | lteenb (lteenb-avx2 at runtime) |
its own ltemme |
USRP (trx_uhd) |
| OCUDU | srsRAN-Project fork (gitlab.com/ocudu/ocudu) |
gnb |
Open5GS | USRP (UHD) |
| Duranta | OpenAirInterface fork (github.com/duranta-project/openairinterface5g) |
nr-softmodem |
Open5GS | USRP (UHD) |
After completing this tutorial you will be able to:
| Difficulty | Advanced |
| Estimated time | 2–3 hours |
| Domain / sandbox | sb1.cosmos-lab.org (SB1) |
| Topic group | 4G/5G & O-RAN |
| Last verified | 2026-06-22 (X310, n41, 20 & 40 MHz; OAI 2026.w25) |
Background knowledge
Account & access
console.sb1.cosmos-lab.org — see SSH access.Devices / nodes
| Resource | Role | Qty | Notes |
|---|---|---|---|
| srv1-lg1.sb1 | gNB + Open5GS core host | 1 | Xeon Gold 6226 (AVX-512 required); DATA1 eno1np0=10.38.250.5/16 (jumbo), DATA2=10.39.250.7/16, CTRL=10.37.1.1/16 |
| sdr2-md1.sb1 | USRP X310 | 1 | NI USRP-2974, serial 318DD00; addr=10.38.3.2 on DATA1 (also 10.39.3.2 on DATA2); UBX daughterboard, internal GPSDO |
| sdr1-in3.sb1 | UE host | 1 | Quectel RM520N-GL (2c7c:0801); AT on /dev/ttyUSB2, QMI cdc-wdm0/wwan0 raw-ip. Note: a container — use nohup/setsid, not systemd-run |
| console.sb1 | omf control + iperf server |
1 | CTRL 10.37.0.10, DATA1 10.38.0.10, DATA2 10.39.0.10 |
Disk images
| Image | Load onto | Provides |
|---|---|---|
| ubuntu2404-uhd4.9-RT-triostack.ndz | srv1-lg1 | all 3 RAN stacks + Open5GS, RT kernel, UHD 4.9, DPDK 24.11.4 (/opt/{ocudu,duranta,amarisoft,open5gs}) |
The shared image registry (/mnt/images, frisbee NFS) is cross-domain, so this image
(saved from sb1/osc) loads in any domain.
Software components (as built into the image)
| Component | Version |
|---|---|
| OS | Ubuntu 24.04.4 LTS (noble) |
| Kernel | 6.8.1-1015-realtime (PREEMPT_RT; generic 6.8.0-110 fallback) |
| UHD | 4.9.0.0+ds1-1~noble2 |
| DPDK | 24.11.4 (/opt/dpdk/24.11.4) |
| OCUDU (srsRAN) | gNB commit 6b3eb5e |
| Duranta (OAI) | tag 2026.w25 (commit 31eb466a7d) — the latest Duranta weekly; /opt/duranta/rebuild.sh tracks it (rebuild.sh <tag> to pin another) |
| Amarisoft | release 2026-06-12 (lteenb/ltemme/lteue/trx_uhd/trx_sdr) |
| Open5GS | source build, 11 NFs, SCP-indirect |
| MongoDB | 8.0.26 (subscriber DB) |
OCUDU/OAI binaries are compiled
-march=nativewith AVX-512: they run on
AVX-512 hosts (Xeon Gold 6226 in srv1-lg1.sb1) but SIGILL on non-AVX-512 CPUs
(e.g. the i7 inside the NI-2974). Rebuild with-DMARCH=x86-64-v3for portability.
Spectrum / RF — band n41 (FR1 TDD, ~2.5 GHz), 20 MHz (51 PRB) or 40 MHz
(106 PRB), 30 kHz SCS, TDD pattern DDDSU. Over the air; stay within your reservation's allowed spectrum.
Everything must live in one COSMOS domain so the gNB host reaches the X310
L2-direct on a DATA network — this is what enables jumbo frames (below), the
difference between a usable and an unusable cell at 20/40 MHz.
| Role | Host | Key addresses |
|---|---|---|
| gNB + Open5GS core | srv1-lg1.sb1 | DATA1 10.38.250.5/16 (jumbo), DATA2 10.39.250.7/16, CTRL 10.37.1.1/16 |
| USRP X310 | sdr2-md1.sb1 | addr=10.38.3.2 (DATA1), 10.39.3.2 (DATA2) |
| UE host | sdr1-in3.sb1 | Quectel RM520N-GL on wwan0 |
| Console / iperf | console.sb1 | CTRL 10.37.0.10 |
COSMOS data networks: DATA1 = 10.38.0.0/16, DATA2 = 10.39.0.0/16,
CTRL = 10.37.0.0/16. The X310 sits on the DATA nets — always reach it over DATA, never CTRL.
Jumbo frames to the X310 — the single most important operational detail.
At 20 MHz the X310 streams ~23 Msps; at 40 MHz ~46 Msps. UHD wants 8000-byte
frames. At the default MTU 1500, UHD falls back to ~1472-byte frames and OCUDU
floodsReceived late DL/UL_TTI.request(unusable at 20 MHz) while OAI exits with
rx_rf … ERROR_CODE_TIMEOUT. The fix is MTU 9000 end-to-end to the X310.
/root/common/sdr-net.sh(sourced by everystart.sh) auto-detects the
DATA interface sharing the X310's /16, sets MTU 9000, pins the USRP route, and
raises UDP socket buffers to 512 MiB / NIC rings to 4096.
All omf commands run from console.sb1.
SSH note: after
omf loada node's host key changes — use
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null …or clear the stale key.
ssh <username>@console.sb1.cosmos-lab.org
-r 0 = full-disk resize, important) and boot it:omf load -t srv1-lg1 -i ubuntu2404-uhd4.9-RT-triostack.ndz -r 0
omf tell -a on -t srv1-lg1
After boot, confirm: uname -r = 6.8.1-…-realtime, df -h / shows the full/opt/{ocudu,duranta,amarisoft,open5gs} present, and a DATA interface holds10.38.x address at MTU 9000.ltemme):ssh root@srv1-lg1.sb1 /root/open5gs/start.sh
Brings up mongod, ogstun (10.45.0.1/16) + NAT, provisions the subscriber fromsubscriber_db.csv, and starts all 11 NFs. Healthy = every NF up andAMF NGAP … 127.0.0.5:38412.Each start.sh takes an optional bandwidth arg: no arg / 20 = 20 MHz, 40 =
40 MHz (106 PRB). Each prints the sdr-net: jumbo banner first.
# OCUDU (srsRAN) + Open5GS
/root/ocudu/start.sh # 20 MHz (or: /root/ocudu/start.sh 40)
# Duranta (OAI) + Open5GS
/root/duranta/start.sh # 20 MHz (or: /root/duranta/start.sh 40)
# Amarisoft (self-contained ltemme core)
/root/amarisoft/start.sh # 20 MHz (or: /root/amarisoft/start.sh 40)
Expected: the gNB acquires the X310 (Maximum frame size: 8000 bytes), NG setup
completes (Number of gNBs is now 1 in the AMF, or NG setup response in ltemme),
and the cell is on air at n41.
The three gNBs share one X310; a previous UHD client can leave its streaming channel
wedged (Unable to create radio session, recv buff timeout, or Amarisoft
TRX discontinuity too wide). Power-cycle the X310 on every switch:
# on console.sb1
omf tell -a off -t sdr2-md1 ; sleep 7 ; omf tell -a on -t sdr2-md1
# wait until `ping 10.38.3.2` succeeds (~10 s), then start the next gNB
ssh root@sdr1-in3.sb1 /root/modem-attach.sh internet # OCUDU / Duranta (DNN internet → 10.45.0.x)
ssh root@sdr1-in3.sb1 /root/modem-attach.sh test123 # Amarisoft (DNN test123 → 192.168.3.x)
modem-attach.sh resets the modem (AT+CFUN=1,1), forces SA registration to PLMN
310/014, brings up the QMI raw-ip PDU session (held in a transient scope so it
survives the SSH exit), configures wwan0, and pings. Success = +C5GREG: 0,1, an
allocated IP, and ping 8.8.8.8 0% loss.
Ping/iperf must bind the DEVICE, not the address:
ping -I wwan0 8.8.8.8.
sdr1-in3's control-NIC default route outrankswwan0, soping -I <ue-ip>
(source-address form) silently leaves via the control NIC and shows 100% loss with
a perfectly healthy RAN. For iperf, add an explicit route
ip route replace <server> dev wwan0 src <ue-ip>(avia <gw>route fails on the Open5GS /30).
Shared 5G parameters (all three stacks):
| Parameter | Value |
|---|---|
| PLMN | 310 / 014 (single-PLMN broadcast — the Quectel data CM rejects multi-PLMN SIB1) |
| TAC | 1 · S-NSSAI sst 1 |
| Band | n41 (FR1 TDD, ~2.5 GHz) |
| Channel BW / SCS | 20 MHz (51 PRB) or 40 MHz (106 PRB) · 30 kHz (μ=1) |
| Carrier center | ~2593–2595 MHz (per stack, see RF specs) |
| TDD pattern | DDDSU (DL-heavy, ~80% DL) |
| AMF NGAP | 127.0.0.5:38412 · gNB binds loopback 127.0.0.1 (gNB + core co-located) |
| UE pool | Open5GS 10.45.0.0/16 (DNN internet) · Amarisoft 192.168.3.0/24 (DNN test123) |
Test SIM / subscriber: IMSI 310014000000032, Ki 9E53736BC519AB701CCAA585BDD529EC,
OP 11111111111111111111111111111111 (milenage, OP not OPc), AMF 9001.
Per-stack config files:
| Stack | 20 MHz | 40 MHz | Notes |
|---|---|---|---|
| OCUDU | /root/ocudu/gnb-ocudu.yml |
gnb-ocudu-40.yml |
srate 23.04→46.08, channel_bandwidth_MHz 20→40 |
| OAI | /root/duranta/gnb-oai.conf |
gnb-oai-40.conf |
dl/ul_carrierBandwidth 51→106, PointA 516834→514854, BWP RIV 13750→28875 |
| Amarisoft | /opt/amarisoft/enb/config/gnb-amari.cfg |
gnb-amari-40.cfg |
NR_BANDWIDTH 20→40 and rf_ports.sample_rate 23.04→46.08 |
RF channel specs (for spectrum/PDCCH monitoring) — n41 TDD, 30 kHz SCS. Only one
cell radiates at a time; PCI and SSB placement differ per stack:
| Stack | PCI | Carrier center | SSB center | SSB ARFCN / GSCN |
|---|---|---|---|---|
| Amarisoft @ 40 MHz | 500 | 2593.35 MHz | 2582.55 MHz | 516510 / GSCN 6456 |
| Amarisoft @ 20 MHz | 500 | 2593.35 MHz | 2588.55 MHz | 517710 |
| OCUDU | 1 | 2595.0 MHz (dl_arfcn 519000) | ~2589.75 MHz @ 20 MHz | 517950 |
| Duranta/OAI | 1 | (PointA-based) | 2593.35 MHz (absoluteFrequencySSB 518670) | 518670 |
Health checks (from console.sb1):
SSH="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
G=root@srv1-lg1.sb1.cosmos-lab.org ; U=root@sdr1-in3.sb1.cosmos-lab.org
$SSH $G 'grep -a "Number of gNBs" /tmp/open5gs/amf.log | tail -1' # gNB ↔ AMF
$SSH $G 'grep -a "UE SUPI.*IPv4" /tmp/open5gs/smf.log | tail -1' # PDU + IP
$SSH $U 'ping -I wwan0 -c4 8.8.8.8' # data plane
# throughput (server on console CTRL IP; device-routed)
$SSH root@console.sb1.cosmos-lab.org 'iperf3 -s -B 10.37.0.10 -p 5201 -D'
$SSH $U 'ip route replace 10.37.0.10 dev wwan0 src $(ip -br -4 a show wwan0|awk "{print \$3}"|cut -d/ -f1); \
iperf3 -c 10.37.0.10 -p 5201 -t 10 -P4 -R' # DL
Performance observed (srv1-lg1.sb1, n41, X310, jumbo path; UL reported as UDP
capacity because TCP uplink is bufferbloat-limited on every cellular stack; DL is TCP):
20 MHz:
| Stack | DL | UL | Notes |
|---|---|---|---|
| OCUDU | 53 Mbit/s | 26 Mbit/s (0% loss) | full performance; UL PUSCH crc=OK, SINR ~37 dB |
| Amarisoft | 25 Mbit/s | ~0.8 Mbit/s | DL good; UL grant-limited (see Troubleshooting) |
| Duranta/OAI | ~46 Mbit/s (UDP) | ~0 | 2026.w25 build; register + PDU + IP + ping 0% loss, DL BLER 2e-4. UL = wideband-PUSCH limit (see Troubleshooting) |
40 MHz:
| Stack | DL | Notes |
|---|---|---|
| OCUDU | ~102 Mbit/s (TCP, ≈2× of 20 MHz) | cell 106 PRB, 0 ongoing late TTIs |
| Duranta/OAI | cell + attach OK (2026.w25) | UL = wideband-PUSCH limit |
| Amarisoft | ~77 Mbit/s (raw DL UDP ceiling, 0% loss) | RF0 46.08 MHz; cell + attach + data OK |
A long-running half-capacity downlink (UDP -b 38M) holds 38 Mbit/s at 0% loss indefinitely.
# stop the running stack and core on the gNB host
ssh root@srv1-lg1.sb1 /root/<stack>/stop.sh # amarisoft | ocudu | duranta
ssh root@srv1-lg1.sb1 /root/open5gs/stop.sh
# free the resources
omf tell -a offh -t system:topo:allres
First
omf savemay time out (transient frisbee stall) — just retry; the disk is read-only during save, so nothing is lost.
| Symptom | Likely cause | Fix |
|---|---|---|
OCUDU floods Received late DL/UL_TTI.request; OAI rx_rf … ERROR_CODE_TIMEOUT |
host↔X310 not at jumbo MTU (routed/cross-domain path caps at 1472) | co-locate on one domain; sdr-net.sh sets MTU 9000 — confirm UHD Maximum frame size: 8000 bytes |
Unable to create radio session / recv buff timeout / Amarisoft TRX discontinuity too wide on stack switch |
previous UHD client left the X310 wedged | power-cycle the X310 (Step 2) before starting the next gNB |
| UE shows 100% loss with a healthy RAN | ping -I <ue-ip> left via the control NIC |
bind the device: ping -I wwan0 …; for iperf add ip route replace … dev wwan0 src <ue-ip> |
OAI bulk uplink collapses under load (ulsch BLER climbs to ~1.0 @ NPRB 39–51, SNR ~22 dB, UL goodput ~0.5 Mbit/s) |
OAI wideband-PUSCH limitation — confirmed on 2026.w25 on both the L2-direct (sb1) and routed (osc) paths, so it is purely OAI software, not RF/MTU (OCUDU does 26 Mbit/s UL on the same X310+UE) | narrow allocations (registration / ping / DL) are clean; DL is fine (~46 Mbit/s); no fix via simple config knobs |
| Amarisoft UL stuck ~0.8 Mbit/s | conservative auto UL link-adaptation; USE_SRS 1 breaks this Quectel's DRB and forcing PUSCH MCS breaks initial access |
DL unaffected (25 Mbit/s) |
pdn-ipv4-call-throttled on rapid re-attach |
QMI session throttling | full AT+CFUN=1,1 reset; modem-attach.sh re-probes /dev/ttyUSB* for the live AT port |
| SIGILL on a different node | binaries are AVX-512 | rebuild with -DMARCH=x86-64-v3 |
This single image reproduces several of the other 4G/5G & O-RAN tutorials — each confirmed end-to-end on srv1-lg1.sb1 (X310 sdr2-md1.sb1, Quectel UE on sdr1-in3.sb1), 2026-06-22:
| Tutorial | Reproduced by | Status |
|---|---|---|
| 5G NR SA with srsRAN gNB + Open5GS | OCUDU (srsRAN-based) + Open5GS | ✅ register + PDU + IP (10.45.0.x), 0 late TTIs |
| 5G NR SA with Amarisoft · Amarisoft + COTS UEs · Amarisoft 5G SA + COTS UE | Amarisoft (lteenb + ltemme) | ✅ register + PDU + IP (192.168.3.x) |
| 5G SA with OAI (this page) | Duranta (OAI nr-softmodem) + Open5GS |
✅ register + PDU + IP (10.45.0.x); bulk UL limited (see Troubleshooting) |
Not covered by this image (different binary / hardware — would need a separate build or testbed):
lte-softmodem; this image ships the 5G nr-softmodem only.triostack-scripts/ repo (common/, {amarisoft,duranta,ocudu,open5gs}/, ue/modem-attach.sh, configs-srv1/)./opt/{ocudu,duranta,open5gs}/rebuild.sh, /opt/amarisoft/update.sh.Author(s): COSMOS team · Last verified: 2026-06-22 (X310, n41, 20 & 40 MHz; OAI rebuilt from 2026.w25) · Tested image/release: ubuntu2404-uhd4.9-RT-triostack.ndz · Tags: 5g, sa, amarisoft, srsran, ocudu, oai, duranta, open5gs, x310, n41