Split the Amarisoft gNB at the O-RAN 7.2 fronthaul with Amarisoft's trx_s72 driver, drive the commercial LiteOn FlexFi O-RU, and attach a Quectel modem end to end: Amarisoft core, registration, PDU session, ping and iperf3. Measured on 2026-10-10: DL 161–168 / UL 4.7–5.1 Mbit/s on a 30 MHz n78 cell with 4 DL and 4 UL antenna ports. Later runs (2026-10-11) reached DL 344–349 / UL 12 Mbit/s on a 60 MHz cell with 2 ports and a USB 3 Quectel; see Performance by CPU platform.
The other Amarisoft tutorials drive a USRP through the trx_uhd driver, so the gNB produces time-domain samples and the radio is a simple converter. This tutorial replaces the USRP with a commercial O-RAN Radio Unit. lteenb loads trx_s72, Amarisoft's O-RAN 7.2 split driver. It sends frequency-domain IQ as eCPRI frames over VLAN 4 of the server's E810 to the LiteOn, which radiates the cell. It is the same radio, fabric and modem as the OCUDU and Duranta O-RU tutorials. With the same rig under three stacks you can compare them directly.
This is the first Amarisoft O-RAN 7.2 cell on COSMOS. Getting there took four non-default settings, and each one fails silently. The page explains each so you can recognise it:
bandwidth=120.0/120.0). The LiteOn needs 4 uplink ports: with one, it sends no uplink at all. Four ports therefore means a 30 MHz cell, and the LiteOn is set to 30 MHz to match. With 4 downlink ports as well, the cell is a full 4T4R cell.ta3_max: 300, not the vendor example's 1000. trx_s72 delays its own slot clock by ta3_max, and lteenb builds the uplink C-plane only two slots ahead of that clock. At 1000 µs, every C-plane message for a full uplink slot and for PRACH misses its deadline and is dropped. The only trace is one line in the gNB console: TRX port #0 underflow=9%. The LiteOn's uplink arrives 82–100 µs after air time (measured), so 300 µs is ample.tai_offset: 0. COSMOS hosts run phc2sys -O 0, so the system clock already runs on TAI. Adding the 37-second offset again misaligns every frame.ss_pbch_block_power: -30) fixes attach, and p0_nominal_with_grant: -70 (from −84) brings the PUSCH SNR from −3 to +6…+9 dB.The uplink stays small (about 5 Mbit/s) for a structural reason. The Amarisoft TDD template used here, pattern 2 at 30 kHz, gives the uplink one slot plus two symbols in every 5 ms. A pattern with more uplink slots is the next experiment.
Warning: Status: manual procedure, verified once (2026-10-10). The current Amarisoft image does not yet contain the O-RAN host tooling (CPU isolation tool, PTP guard, PTP profile, the O-RU tool's credentials), so some Setup steps need files from the COSMOS team. The next image rebake will include them. There is no Ansible or Jupyter mode yet.
After completing this tutorial you will be able to:
trx_s72, and give it a fronthaul VLAN interface with the DU's MAC address.t1a_*, ta3_max) against the measured arrival time of the O-RU's uplink, and recognise C-plane dropped at the source.cosmos-oru and restore it for the next user.| Learning path | Step 4 of 7: O-RAN 7.2 with a commercial O-RU. Same step: OCUDU O-RU, Duranta O-RU. Previous Amarisoft step: over the air on a USRP |
| Difficulty | Advanced |
| Estimated time | 60–90 min the first time, including one host reboot and one O-RU cold cycle |
| Domain / sandbox | osc (gNB + core on srv18.osc, UE node1.osc); O-RU power from your fronthaul console (console.osc when your LiteOn reservation targets osc) |
| Topic group | Cellular (4G/5G/O-RAN) |
| Last verified | 2026-10-10, manual procedure on srv18.osc (amarisoft-20261009, Amarisoft 2026-09-11, 2× Xeon Gold 6126): DL 161–168 / UL 4.7–5.1 Mbit/s, ping 0 % loss |
Background knowledge
Account & access
srv18.osc, node1.osc and the LiteOn. The O-RU is a shared instrument, so coordinate before powering it. See Make a reservation.console.osc.cosmos-lab.org. With an O-RU reservation for the LiteOn that targets osc, it is also where you power the O-RU (full name, no -d). See SSH access.gNB and 5gC must be free testbed-wide (see the licence section of Amarisoft on COSMOS).Devices / nodes
| Resource | Role | Qty | Notes |
|---|---|---|---|
srv18.osc |
gNB (lteenb + trx_s72) and core (ltemme) |
1 | Intel E810, fronthaul on DATA1a, VLAN 4 |
| LiteOn FlexFi | commercial O-RU, n78 | 1 | management 10.3.14.1, fronthaul MAC e8:c7:4f:25:89:39 |
node1.osc |
UE host | 1 | Quectel RM5xx modem, AT port /dev/ttyUSB2 |
Danger: The Benetel and the LiteOn share one RF space. Since 2026-10-16 every O-RU reservation carries its band, centre frequency and bandwidth, and the shared scheduler refuses overlapping bookings, so both may be powered only on non-overlapping channels.
cosmos-oru resetstops if a co-channel O-RU is being driven by a DU, and if any DU is already streaming at your O-RU (a forgotten one elsewhere breaks every other user's cell). An unreserved O-RU that is powered is still switched off for you. Confirm by ping, not by theomf tellreply.
Channel: the cell runs on the channel of your O-RU reservation (band, centre, bandwidth from the shared scheduler);
cosmos-oru rf <oru>prints it. Experienced users override it with--center-freq <MHz> --bandwidth <MHz>oncosmos-oru resetand the bring-up scripts; stay inside your reservation, because the scheduler cleared only that block. For this gNB, put the same channel intognb-s72-liteon.cfg:ORU_RF_DL_ARFCNfromcosmos-oru rf liteongoes intodl_nr_arfcn, and the bandwidth intoNR_BANDWIDTH. The licence caps bandwidth × antenna ports at 120 MHz per direction, so 100 MHz needs the 1T1R configuration and 60 MHz the 2T2R one; the 30 MHz default is 4T4R.
Disk images
| Image | Load onto | Provides |
|---|---|---|
amarisoft.ndz (currently amarisoft-20261009) |
srv18.osc |
lteenb, ltemme, the Amarisoft 2026-09-11 release tarball with trx_s72, cosmos-sim-db |
| the UE host's current image | node1.osc |
the Quectel tooling and ue-prep.sh |
Software components
| Component | Version | Source |
|---|---|---|
Amarisoft lteenb / ltemme |
2026-09-11 | preinstalled (/opt/amarisoft/enb, /opt/amarisoft/mme) |
trx_s72 |
2026-09-11 | inside /opt/amarisoft/amarisoft-2026-09-11.tar.gz |
| gNB configuration | this tutorial | /files/amarisoft-gnb-s72-liteon.cfg |
cosmos-oru-tools |
≥ 1.6 (adds the amarisoft LiteOn golden and the bandwidth setter) |
apt install cosmos-oru-tools |
| LiteOn FlexFi firmware | 02.00.10 | on the O-RU |
console.osc --ssh--> srv18.osc lteenb + trx_s72 (O-DU) + ltemme (core, tun 192.168.100.1)
| DATA1a.4 (VLAN 4, MAC 00:11:22:33:44:66): eCPRI C/U-plane
| DATA1a: PTP G.8275.1 slave (domain 24)
v
fronthaul switch (PTP grandmaster)
|
v
LiteOn FlexFi 10.3.14.1, 30 MHz n78 @ 3750 MHz, 4T4R ) ) ) air ( ( ( Quectel on node1.osc
trx_s72 binds the VLAN sub-interface DATA1a.4. It sends C-plane and U-plane for downlink eAxC 0–3 and requests uplink on eAxC 0–3 plus PRACH on eAxC 4–7. The LiteOn sends to MAC 00:11:22:33:44:66, which differs from the NIC's own address, so both interfaces run promiscuous.ptp4l disciplines the E810's clock and phc2sys -O 0 copies it to the system clock, which trx_s72 reads, hence tai_offset: 0.ltemme runs on the same host. The UE pool 192.168.100.0/22 (APN internet) terminates on its tun interface at 192.168.100.1.lteenb never talks to the O-RU. cosmos-oru sets the LiteOn over its CLI before the gNB starts.Setup prepares srv18.osc once per image load. Steps 2–4 cover what the current image lacks; ask the COSMOS team for the three files they name.
console.osc. The image is large, so use -r 0:omf load -i amarisoft.ndz -t srv18.osc.cosmos-lab.org -r 0 -o 2400 -b 600
omf tell -a on -t srv18.osc.cosmos-lab.org
apt-get install -y cosmos-oru-tools (version 1.6 or later). Put the O-RU credentials file you receive from the COSMOS team at /etc/cosmos/oru-credentials (root:adm, mode 0640). It is never published.cosmos-ofh-isolation --nic DATA1a --apply, then reboot. Check cat /sys/devices/system/cpu/isolated, which shows 3-11,27-35 on srv18.osc. See Real-time host preparation./etc/linuxptp/ptp4l-g8275.conf (domain 24, L2, clientOnly 1, hardware time stamping) and enable cosmos-fh-ptp-guard, which keeps PTP management messages off the fabric. Then start the two daemons:systemctl stop systemd-timesyncd
setsid chrt -f 10 taskset -c 1 ptp4l -f /etc/linuxptp/ptp4l-g8275.conf -i DATA1a -m > /root/ptp4l.log 2>&1 < /dev/null &
setsid chrt -f 10 taskset -c 2 phc2sys -s DATA1a -n 24 -O 0 -S 0.01 -m > /root/phc2sys.log 2>&1 < /dev/null &
grep rms /root/ptp4l.log | tail -1 # rms in the tens of ns once locked
ip link add link DATA1a name DATA1a.4 address 00:11:22:33:44:66 mtu 9000 type vlan id 4 \
egress-qos-map 0:7 1:7 2:7 3:7 4:7 5:7 6:7 7:7
ip link set DATA1a.4 up
ip link set DATA1a promisc on; ip link set DATA1a.4 promisc on
ethtool -C DATA1a adaptive-tx off rx-usecs 0
trx_s72 next to lteenb:mkdir -p /root/s72 && cd /root/s72
tar xzf /opt/amarisoft/amarisoft-2026-09-11.tar.gz 2026-09-11/trx_s72-linux-2026-09-11.tar.gz
tar xzf 2026-09-11/trx_s72-linux-2026-09-11.tar.gz
cp trx_s72-linux-2026-09-11/trx_s72.so /opt/amarisoft/enb/
wget -q -O /opt/amarisoft/enb/config/gnb-s72-liteon.cfg https://www.cosmos-lab.org/files/amarisoft-gnb-s72-liteon.cfg
ue_db in /opt/amarisoft/mme/config/mme-amari.cfg, using the entry that cosmos-sim-gen amarisoft prints for it. Delete any multi_sim: true line from a milenage SIM's entry: ltemme refuses it ("multi_sim can only be used with the XOR authentication algorithm") and exits with code 10, printing the error before its banner.The LiteOn keeps its configuration across power cycles. OCUDU and Duranta use it at 100 MHz, so the Amarisoft state differs from theirs in one value, Band Width 30000000. cosmos-oru switches the Benetel off, restores the amarisoft golden, cold-cycles the RU if the stored configuration changed, re-applies jumboframe (reset on every boot), and waits for sync. Run it on srv18.osc before the gNB starts:
cosmos-oru diff liteon amarisoft # read-only: what would change
cosmos-oru reset liteon amarisoft # 3-6 min with a cold cycle
It ends with:
golden diff: EMPTY - the RU matches the golden
[cosmos-oru] live O-RU == golden
[cosmos-oru] Sync State SYNCHRONIZED RF State Ready
[cosmos-oru] READY: LiteOn FlexFi is in the amarisoft state. Start (or restart) your DU now - the DU locks onto the O-RU only when it starts.
These lines in gnb-s72-liteon.cfg differ from the Amarisoft defaults and from the vendor's trx_s72 example:
#define NR_BANDWIDTH 30 // licence: 120 MHz x antennas per direction
#define N_ANTENNA_DL 4
#define N_ANTENNA_UL 4 // with 1 UL port the LiteOn sends no uplink at all
rf_driver: {
name: "s72",
tai_offset: 0, // phc2sys -O 0: CLOCK_REALTIME is already TAI
rf_ports: [{
t1a_max_cp_dl: 400, t1a_min_cp_dl: 250, t1a_max_up_dl: 200, t1a_min_up_dl: 100,
t1a_max_cp_ul: 400, t1a_min_cp_ul: 250,
ta3_max: 300, // 1000 drops every full-UL-slot and PRACH C-plane
transport: "ethernet",
bind_interface: "DATA1a.4",
remote_addr: "e8:c7:4f:25:89:39",
}],
},
rf_ports: [{ s72: { ud_comp_hdr: 0x91, port_mapping_prach: [4, 5, 6, 7] } }], // BFP-9; LiteOn PRACH eAxC 4-7
nr_cell_list: [{
manual_ref_signal_power: true,
ss_pbch_block_power: -30, // automatic value is -44: UE under-powers its UL with 4 DL ports
...
pusch: { p0_nominal_with_grant: -70, ... }, // was -84: PHR 38 dB, PUSCH SNR -3 dB
Both programs exit when their standard input closes. Start each in its own process group with stdin held open, and stop them later by that group, never by name (see Amarisoft on COSMOS). The gNB runs on the isolated cores:
cd /opt/amarisoft/mme && setsid sh -c "tail -f /dev/null | ./ltemme config/mme-amari.cfg > /root/ltemme.out 2>&1" < /dev/null > /dev/null 2>&1 &
cd /opt/amarisoft/enb && setsid sh -c "tail -f /dev/null | taskset -c $(cat /sys/devices/system/cpu/isolated) ./lteenb config/gnb-s72-liteon.cfg > /root/lteenb.out 2>&1" < /dev/null > /dev/null 2>&1 &
sleep 20; tail -2 /root/lteenb.out
iperf3 -s -B 192.168.100.1 -p 5299 -D -I /root/iperf3-5299.pid
A healthy start prints the licence and the radio line, and no underflow:
License server: 10.50.0.8 (0b-b4-46-dc-2f-83-58-cb)
RF0: sample_rate=61.440 MHz dl_freq=3750.000 MHz ul_freq=3750.000 MHz (band n78) dl_ant=4 ul_ant=4
The gNB log /tmp/gnb0.log begins with # SMP C=9 P=normal bandwidth=120.0/120.0 PRACH=1 DRBs=1 RF0=4/4, followed by NG setup response.
On node1.osc, bring the modem up with the standard tool. It reboots the modem, checks its settings, waits for registration and starts a data call:
bash /root/ue-prep.sh quectel up internet
AT+QENG="servingcell" +QENG: "servingcell","NOCONN","NR5G-SA","TDD",001,01,001234501,500,1,649632,78,5,-60,-10
UE_IP=192.168.100.2 IF=wwan0
UP
Then measure. The iperf3 receiver line is the result:
ping -c10 -i 0.3 -I wwan0 192.168.100.1 | tail -2 # wwan0 = the IF that ue-prep printed; use yours
iperf3 -c 192.168.100.1 -B 192.168.100.2 -p 5299 -P 4 -R -t 20 | grep "SUM.*receiver" # downlink
iperf3 -c 192.168.100.1 -B 192.168.100.2 -p 5299 -P 4 -t 20 | grep "SUM.*receiver" # uplink
10 packets transmitted, 10 received, 0% packet loss, time 2708ms
[SUM] 0.00-20.00 sec 384 MBytes 161 Mbits/sec receiver
[SUM] 0.00-20.86 sec 11.6 MBytes 4.67 Mbits/sec receiver
| Check | Where | Expected |
|---|---|---|
| Licence and antennas | /root/lteenb.out |
dl_ant=4 ul_ant=4, no underflow= line |
| PRACH | grep PRACH: /tmp/gnb0.log (needs PHY logging, see Troubleshooting) |
a line per attempt, snr around 10 dB |
| Registration | /tmp/gnb0.log |
RRC setup request → RRC setup → PDU session resource setup response |
| UE | AT+QENG="servingcell" |
NR5G-SA, PCI 500, RSRP about −60 dBm |
| Throughput | iperf3 receiver lines | DL ≥ 150, UL ≈ 5 Mbit/s |
One Quectel modem, iperf3 TCP with 4 streams, receiver line, n78 at 3750 MHz, TDD pattern 2 (30 kHz).
| Cell | gNB CPU platform | Date / image | DL Mbit/s | UL Mbit/s | Notes |
|---|---|---|---|---|---|
| 30 MHz, 4 DL / 4 UL ports | 2× Intel Xeon Gold 6126 @ 2.60 GHz, 24 cores / 48 threads | 2026-10-10 / amarisoft-20261009 |
152–168 (3 runs) | 4.7–5.1 (2 runs) | rank 2–4, CQI 15, DL MCS ≈ 25; PUSCH SNR +6…+9 dB; ping 0 % loss |
| 30 MHz, 1 DL / 4 UL ports | same | 2026-10-10 / amarisoft-20261009 |
54.6 | 2.5 | single DL layer; p0_nominal_with_grant −84 |
30 MHz, 4 DL / 4 UL ports, SIMCom SIM8200 on USB 3 (node1.rrail) |
same | 2026-10-11 / amarisoft-20261009 |
247–277 (DDDSU; UDP 323) / 155–186 (DDSUU) | 10–19 (DDDSU) / 41 (DDSUU) | p0_nominal_with_grant −60; DDDSU at 30 MHz is the cell's ceiling, not the USB link |
60 MHz, 2 DL / 2 UL ports, Quectel RM520N-GL on USB 3 (sdr1-in3.sb1) |
same | 2026-10-11 / amarisoft-20261009 |
344–349 | 12 | rank 2, CQI 15, retransmissions 8 %; the LiteOn is re-set to 60 MHz 2T2R for this cell |
100 MHz, 1 DL / 1 UL port, Quectel (node1.osc) |
same | 2026-10-11 / amarisoft-20261009 |
216 (DDDSU) / 150 (DDSUU) | 17 (DDDSU) / 34 (DDSUU) | needs gen_prb0: true in the s72 block (273 PRBs exceed one C-plane section) and p0_nominal_with_grant −50 |
The gNB licence allows 120 MHz of bandwidth × antennas per direction, so 30 MHz × 4, 60 MHz × 2 and 100 MHz × 1 are the possible cells. A modem on a USB 2 port stops near 270–300 Mbit/s DL; use a USB 3 port to see the 60 MHz figure.
Stop your own processes by process group (never pkill -f on a shared host), then return the LiteOn to the state the next stack expects and remove the interface:
for p in lteenb-avx2 ltemme; do kill -TERM -- -$(ps -o pgid= -p $(pgrep -x $p) | tr -d ' '); done
kill $(cat /root/iperf3-5299.pid) # only the server you started
cosmos-oru reset liteon ocudu # back to 100 MHz; cold-cycles the RU
ip link del DATA1a.4
Run bash /root/ue-prep.sh quectel down on node1.osc before the gNB stops, so the core does not keep a stale session. Power the LiteOn off only if nobody else needs it: omf tell -a offh -t liteon-oru.instrument.orbit-lab.org from your fronthaul console.
Turn on the gNB's PHY and MAC logs at run time through its remote API on port 9001 (message config_set with "logs":{"layers":{"PHY":{"level":"debug"},"MAC":{"level":"debug"}}}). The default configuration logs only RRC, NAS and NGAP.
| Symptom | Cause | Fix |
|---|---|---|
lteenb exits right after License found |
bandwidth × antennas exceeds the licence (for example 100 MHz with 4 ports) | 30 MHz with 4 ports; check the bandwidth=x/120.0 header |
TRX port #0 underflow=9%; gNB log UL ... SLOT: skip slot; no PRACH; the LiteOn's RX_LATE_C about half its C-plane |
ta3_max too large: the uplink C-plane is built after its deadline and dropped at the source |
ta3_max: 300 |
The LiteOn's TX_TOTAL stays 0, no uplink U-plane at all |
1 uplink port configured | N_ANTENNA_UL 4 and port_mapping_prach: [4, 5, 6, 7] |
No fronthaul frames reach lteenb |
NIC not promiscuous: the LiteOn sends to 00:11:22:33:44:66 |
ip link set DATA1a promisc on and the same for DATA1a.4 |
| Frames arrive but every symbol is late or early by tens of seconds | tai_offset left at 37 on a host whose clock already runs on TAI |
tai_offset: 0 |
Repeated RRC setup request / RRC setup, never Setup Complete; the PHY log shows Msg4 ack=1, then every PUSCH crc=FAIL at SNR about 0 |
the UE sets its uplink power from RSRP and the advertised SSB power; with 4 DL ports the RSRP rose about 14 dB | manual_ref_signal_power: true, ss_pbch_block_power: -30 |
Attached, but UL ≈ 0–2 Mbit/s and ue_get shows pusch_snr below 0 and ul_phr about 38 |
closed-loop power control does not raise the UE enough | p0_nominal_with_grant: -70 |
ltemme exits with code 10 straight away |
multi_sim: true on a milenage SIM (from cosmos-sim-gen amarisoft) |
delete that line |
| The UE attaches, but a second gNB or core elsewhere segfaults at start | one licence seat per tag, testbed-wide | see Amarisoft on COSMOS |
| Capturing the fronthaul shows only the RU's packets | capturing on DATA1a.4 misses the gNB's transmit ring |
capture on the parent: tcpdump -i DATA1a ether host e8:c7:4f:25:89:39 |
On this wiki
External
trx_s72 manual: 2026-09-11/doc/trx_s72-2026-09-11/trx_s72.pdf inside the release tarball; Amarisoft Tech AcademyAuthor(s): Ivan Seskar · Last verified: 2026-10-10 (manual procedure, srv18.osc) · Tested image/release: amarisoft-20261009.ndz, Amarisoft 2026-09-11 (lteenb, ltemme, trx_s72), LiteOn FlexFi 02.00.10, cosmos-oru-tools 1.6 · Tags: amarisoft, oran, 7.2, fronthaul, liteon, 5g, sa