Run a Release-17 5G Non-Terrestrial Network — a gNB whose radio link goes through a satellite — entirely in software on one node. The Duranta gNB (nr-softmodem) broadcasts the satellite's position and timing in SIB19, OAI's soft UE (nr-uesoftmodem) uses it to pre-compensate the delay and the Doppler shift, and the RF simulator (RFsim) puts a geostationary (GEO) or low-earth-orbit (LEO) channel between them. You attach the UE, ping through a half-second satellite round trip, and measure what TCP, UDP and HARQ make of it, next to a terrestrial baseline on the same cell.
A terrestrial 5G cell assumes the UE is a few kilometres from the gNB: the round trip is microseconds, the timing advance (TA) the UE needs is small and the gNB measures it during random access, and a lost transport block is retransmitted by HARQ a few milliseconds later. Put a satellite in the path and all three assumptions break. A GEO satellite sits 35,786 km up, so the gateway → satellite → UE path takes about 239 ms one way and the radio round trip is about 477 ms. A LEO satellite at 600 km is much closer (a few ms to ~20 ms one way) but moves at 7.6 km/s, so the delay changes by tens of microseconds every second and the carrier arrives shifted by tens of kilohertz of Doppler.
3GPP Release 17 adds NR NTN to cope with this, without changing the waveform. The gNB tells every UE, in a new system information block (SIB19), the satellite's position and velocity (its ephemeris), the common part of the delay (ta-Common) and how fast it drifts, and a scheduling offset (Koffset) that pushes every uplink timing relation far enough into the future to cover the round trip. The UE combines that with its own position (from GNSS on a real device) to compute its full timing advance and to pre-correct its uplink frequency, before it sends its first preamble. On top of that the gNB may run more HARQ processes (32 instead of 16) or switch HARQ feedback off entirely, because waiting half a second for an ACK per transport block would leave the link idle most of the time.
This tutorial makes those mechanisms visible. It runs four scenarios on one 5 MHz cell (band n254, an NTN S-band, FDD, 15 kHz SCS) taken unchanged from the configurations that ship in Duranta's source tree: a terrestrial baseline, GEO with HARQ feedback disabled, GEO with 32 HARQ processes, and a LEO pass with the moving delay and Doppler. Each run records the attach time, the RTT, TCP and UDP throughput in both directions, and what the UE decoded from SIB19.
It needs no radio and no reservation beyond one node: the satellite is simulated. That is also its limitation — read The RFsim clock below before you compare any number on this page with a real satellite link.
SAT_LEO_REGEN model for it; it is not part of this tutorial). Architecture: TS 38.300 §16.14, TR 38.821.ta-Common with its drift (ta-CommonDrift, ta-CommonDriftVariant) — and a UE-specific TA that the UE computes from its own GNSS position and the satellite ephemeris. The total is T_TA = (N_TA + N_TA,offset + N_TA,adj^common + N_TA,adj^UE) × T_c (TS 38.213 §4.2, TS 38.211 §4.3.1). In this run the GEO UE reports N_Common_Ta: 238.74 ms and a total timing_advance_ntn of 3,667,039 samples at 7.68 Msample/s = 477.5 ms — the full round trip.cellSpecificKoffset (in ms-equivalent slots at 15 kHz) to them (TS 38.213 §4.2, §8.3, §9.2.3; TS 38.214 §6.1.2.1). It must be at least the round trip: 478 for this GEO cell, 40 for the LEO cell.SIB19-r17 carries ntn-Config-r17 (ephemeris, ta-Info, cellSpecificKoffset, kmac, epoch time, ntn-UlSyncValidityDuration) plus the satellite's coverage information (TS 38.331 §6.3.1, NTN-Config-r17). A UE in an NTN cell must have read it before random access. ntn-UlSyncValidityDuration (240 s for GEO, 5 s for LEO here) says how long the UE may extrapolate before it must read SIB19 again.nrofHARQ-ProcessesForPDSCH-v1700, TS 38.331; TS 38.321 §5.3.2) and lets the network disable HARQ feedback per process (downlinkHARQ-FeedbackDisabled-r17, TS 38.321 §5.3.2.1, TS 38.213 §9.1), so the process is free again immediately and reliability falls back to RLC ARQ. Doppler: the UE compensates the DL and pre-compensates the UL using the ephemeris (TS 38.101-5 sets the accuracy requirements).Full study material: TR 38.811 (channel and deployment models for NTN) and TR 38.821 (solutions for NR to support NTN).
After completing this tutorial you will be able to:
ntn-Config-r17 in servingCellConfigCommon, SIB19 at the DU, extended timers, and the HARQ options (disable_harq, num_dlharq / num_ulharq).position0), the RFsim satellite delay, and for LEO the channel model plus the drift and Doppler options.| Learning path | An optional branch after step 1 — Duranta soft-only. Do that one first: this tutorial assumes its core and UE workflow |
| Difficulty | Intermediate |
| Estimated time | 30–40 min (the scripted run of all four scenarios takes about 10 min) |
| Domain / sandbox | any COSMOS server node (no radio) |
| Topic group | Cellular (4G/5G/O-RAN) |
| Image to load | duranta.ndz → duranta-w42-20261021 (Duranta 2026.w42). Verified on duranta-w42-20261021 (2026-10-22), duranta-w39-20261009 (2026-10-10) and duranta-w39-20261005b (2026-10-08) |
| Last verified | 2026-10-22 — three-mode parity PASS on 2× Xeon Gold 6126, duranta-w42-20261021 (Duranta 2026.w42), all four scenarios (see The parity check) |
| Upstream source | Duranta tag 2026.w42 (commit f80a883268); NTN guide doc/ntn-configuration.md |
Background knowledge
LD_LIBRARY_PATH, the dynamic AMF address, the UE's carrier arguments.Account & access
Devices / nodes
| Resource | Role | Qty | Notes |
|---|---|---|---|
| any COSMOS server node | gNB + OAI CN5G + nr-uesoftmodem (all-in-one) |
1 | no SDR. The LEO channel model is CPU-heavy: use a server-class node |
Disk images
| Image | Load onto | Provides |
|---|---|---|
duranta.ndz |
your node | Duranta 2026.w42 nr-softmodem + nr-uesoftmodem (/opt/duranta), the upstream NTN configs (/opt/duranta/ci-scripts/conf_files/*ntn*), OAI CN5G (/opt/oai-cn5g), the soft UE's test USIM (/root/duranta/ue-rfsim.conf) |
Software components
| Component | Version | Source |
|---|---|---|
| Duranta gNB / nrUE | 2026.w42 | /opt/duranta/cmake_targets/ran_build/build/ |
| gNB NTN configs (GEO, LEO) | 2026.w42 | ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf, …ntn-leo.conf |
| LEO channel model | 2026.w42 | SAT_LEO_TRANS in radio/rfsimulator/apply_channelmod.c |
| OAI CN5G | develop (Docker) |
/opt/oai-cn5g/oai-cn5g-fed/docker-compose |
| Tutorial bundle | this page | tutorials/duranta-ntn/ (see Same experiment, three ways) |
Spectrum / RF / special
00101, DNN oai, IMSI 001010123456780 (the image's soft-UE test subscriber), UE pool 192.168.100.0/22. one COSMOS node (duranta image)
┌──────────────────────────────────────────────────────────────────────────┐
│ OAI CN5G (Docker): AMF / SMF / UPF + oai-ext-dn (iperf3 server) │
│ ▲ N2 / N3 over the demo-oai bridge │
│ nr-softmodem ── RFsim TCP :4043 ──────────────────────▶ nr-uesoftmodem │
│ (gNB, "on the ground") │ │ (UE, oaitun_ue1) │
│ SIB19: ephemeris, │ the "satellite": │ position0, │
│ ta-Common, Koffset │ prop_delay and/or │ reads SIB19, │
│ │ SAT_LEO_TRANS │ pre-compensates │
│ │ (delay + Doppler) │ TA + Doppler │
└──────────────────────────────────────────────────────────────────────────┘
Read it the way a satellite operator would. The gNB is the ground segment: it schedules as if the UE were far away (Koffset) and announces where the satellite is (SIB19). RFsim is the space segment: on the downlink it is applied on the UE side and on the uplink on the gNB side, so each direction gets its own delay — a fixed prop_delay for GEO, or the SAT_LEO_TRANS model that recomputes both delay and Doppler every slot from a circular 600 km orbit. The UE is the user terminal: it knows its own position (position0, here on the Earth's surface directly below the satellite's track) and corrects its timing and frequency before transmitting. The core is unchanged — NTN is entirely a RAN feature.
RFsim is not real time. The gNB and UE exchange samples as fast as the CPU allows; a light 5 MHz cell runs several times faster than real time, and the LEO channel model is expensive enough to run slower than real time. Everything inside the RAN — frames, HARQ timers, Koffset, the satellite delay — runs on this simulated clock. ping and iperf3 run on the wall clock. So a wall-clock RTT is the simulated RTT divided by the speed-up.
The bundle measures the speed-up for every measurement window: the gNB prints [NR_MAC] Frame.Slot <f>.0 every 128 frames (1.28 s of simulated time), and counting those lines across a window gives scale = simulated s / wall s. The tables below report both clocks:
goodput DL … UL … Mbps) is already on the simulated clock and is reported as an independent check.What this does not fix: TCP's congestion control runs in the host kernel on the wall clock, so in the GEO scenario TCP sees an ~89 ms RTT instead of ~550 ms. The TCP-vs-UDP gap you measure here is therefore smaller than it would be over a real GEO link; the Instructions explain the real-link numbers from first principles.
-r 0 gives the build tree and logs the whole disk):ssh <username>@console.<domain>.cosmos-lab.org
omf load -i duranta.ndz -t <node> -r 0 -o 2400 -b 600
omf tell -a on -t <node>
The steps run on the node as root. They are what duranta-ntn_manual.sh does, one scenario at a time; the commands are shown for GEO and the differences for the other scenarios are listed at the end of each step.
Exactly as in the soft-only tutorial: the core is NTN-agnostic.
ssh root@<node>
cd /opt/oai-cn5g/oai-cn5g-fed/docker-compose
docker compose -f docker-compose-basic-nrf.yaml up -d
docker exec -i mysql sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" oai_db' < /root/duranta/provision.sql
docker ps --format '{{.Names}} {{.Status}}' | grep -c healthy # 9
Duranta ships a GEO and a LEO gNB configuration in its CI directory. They use a test PLMN (208/99) and CI addresses, so duranta-ntn_mkconf.py copies them with four lines changed — the PLMN, the AMF address, and the N2/N3 bind address — and builds the UE config from the image's own test USIM plus the UE position and the RFsim channel. Nothing else is altered; diff shows it:
mkdir -p /root/duranta-ntn && cd /root/duranta-ntn # copy duranta-ntn_mkconf.py + duranta-ntn_node.sh here (scp from the console)
AMF=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' oai-amf)
python3 duranta-ntn_mkconf.py --scenario geo --out-dir conf --amf $AMF --ng-addr 192.168.70.129
diff /opt/duranta/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf conf/gnb-geo.conf
16c16
< plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) });
---
> plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1 }) });
185c185
< amf_ip_address = ({ ipv4 = "192.168.71.132"; });
---
> amf_ip_address = ({ ipv4 = "192.168.70.137"; });
189,190c189,190
< GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.140/26";
< GNB_IPV4_ADDRESS_FOR_NGU = "192.168.71.140/26";
---
> GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/26";
> GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/26";
The NTN part of the gNB file is worth reading line by line — it is what turns an ordinary cell into a satellite cell:
min_rxtxtime = 6;
disable_harq = 1; # Rel-17 downlinkHARQ-FeedbackDisabled: no HARQ ACK/NACK, no retransmissions
cu_sibs = [2];
du_sibs = [19]; # the DU broadcasts SIB19
...
ntn-UlSyncValidityDuration-r17 = 240; # s; re-read SIB19 at least this often
cellSpecificKoffset_r17 = 478; # >= the 477.5 ms round trip
ta-Common-r17 = 58629666; # x 4.072 ns = 238.74 ms
positionX-r17 = 0; positionY-r17 = 0;
positionZ-r17 = 32433846; # x 1.3 m = 42,164 km from the Earth's centre: GEO
velocityVX-r17 = 0; velocityVY-r17 = 0; velocityVZ-r17 = 0;
TIMERS : { sr_ProhibitTimer_v1700 = 512; t300 = 2000; t301 = 2000; t319 = 2000; ... };
rlc = { srb = { t_poll_retransmit = "ms2000" } drb_am = { t_poll_retransmit = "ms2000" } }
MACRLCs = ({ ... dl_max_mcs = 9; ul_max_mcs = 9; });
rfsimulator = ({ ... prop_delay = 238.74; }); # the UL leg of the simulated satellite (DL leg is set on the UE)
and the UE file (keys elided; they are the image's test USIM):
uicc0 = { imsi = "001010123456780"; key = "…"; opc = "…"; pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; }); }
position0 = { x = 0.0; y = 0.0; z = 6377900.0; } # ECEF, metres: on the surface, under the satellite
rfsimulator = ({ serveraddr = "127.0.0.1"; prop_delay = 238.74; });
Other scenarios. --scenario tn drops the nine *-r17 lines, du_sibs and disable_harq, and sets prop_delay = 0 — the same cell on the ground. geo-harq32 replaces disable_harq = 1 with num_dlharq = 32; num_ulharq = 32;. leo uses …rfsim.ntn-leo.conf (Koffset 40, ta-Common 18.84 ms, ta-CommonDrift −46 µs/s, a moving ephemeris, 32 HARQ processes, a channelmod block with SAT_LEO_TRANS, options = ("chanmod")) and gives the UE the same channel model.
B=/opt/duranta/cmake_targets/ran_build/build; mkdir -p /tmp/duranta-ntn/geo
systemd-run --unit=duranta-ntn-gnb --collect --working-directory=$B --setenv=LD_LIBRARY_PATH=$B \
bash -c "exec $B/nr-softmodem -O /root/duranta-ntn/conf/gnb-geo.conf --rfsim > /tmp/duranta-ntn/geo/gnb.log 2>&1"
grep -aE 'activate SIB19|is in service|Command line parameters for OAI UE' /tmp/duranta-ntn/geo/gnb.log
[NR_MAC] Command line parameters for OAI UE: -C 2488400000 --CO -873500000 -r 25 --numerology 0 --band 254 --ssb 60
[GNB_APP] activate SIB19 at DU
[NR_RRC] cell PLMN 001.01 Cell ID 12345678 is in service
What to look for: activate SIB19 at DU — the cell is an NTN cell. --CO -873500000 is the FDD uplink offset (UL 1614.9 MHz); the UE needs it because n254 is not a band the UE can derive the UL from on its own.
ARGS=$(grep -a 'Command line parameters for OAI UE' /tmp/duranta-ntn/geo/gnb.log | head -1 | sed -E 's/.*OAI UE: *//; s/\x1b\[[0-9;]*m//g')
systemd-run --unit=duranta-ntn-ue --collect --working-directory=$B --setenv=LD_LIBRARY_PATH=$B \
bash -c "exec $B/nr-uesoftmodem -O /root/duranta-ntn/conf/ue-geo.conf --rfsim $ARGS > /tmp/duranta-ntn/geo/ue.log 2>&1"
until ip -br addr show oaitun_ue1 2>/dev/null | grep -q 192.168.100; do sleep 0.25; done
grep -aE 'Found SIB19|k_offset|TA_COMMAND|PDU Session Establishment Accept' /tmp/duranta-ntn/geo/ue.log
[NR_RRC] Found SIB19
[PHY] k_offset: 478ms, N_Common_Ta: 238.740000ms, drift: 0.000000µs/s, variant: 0.000000µs/s², timing_advance_ntn: 3667039 samples, DL Doppler shift: 0.000000kHz, UL Doppler shift: 0.000000kHz
[NR_MAC] [791.0] Received TA_COMMAND 30 TAGID 0 CC_id 0
[NAS] Received PDU Session Establishment Accept, UE IPv4: 192.168.100.2
What to look for: the UE found SIB19 before random access and set its TA to 3,667,039 samples (477.5 ms at 7.68 Msample/s) — the whole round trip, pre-computed from ta-Common plus its own position. The gNB's closed-loop TA_COMMAND 30 afterwards is one step from the neutral value 31 (TS 38.321 §6.1.3.4): the open-loop estimate was right to within a fraction of a symbol.
For LEO the UE needs three things SIB19 cannot give it before it has synchronised, so they go on its command line (exactly what upstream CI passes in 2026.w39):
... $ARGS --time-sync-I 0.1 --ntn-initial-time-drift -46 --initial-fo 57340 --cont-fo-comp 3
Option (executables/nr-uesoftmodem.h) |
What it does |
|---|---|
--ntn-initial-time-drift -46 |
the DL timing drift (feeder + service link) in µs/s, used during initial sync before SIB19 is read |
--initial-fo 57340 |
the DL frequency offset (Hz) to remove up front — the Doppler at the start of the pass |
--cont-fo-comp 3 |
continuous FO estimation; 2 = treat the offset as Doppler (pre-compensate UL with it), 3 = like 2 but do not fold the residual DL FO into the UL pre-compensation. The NTN guide shows 2; CI runs 3 |
--time-sync-I 0.1 |
the integral gain of the UE's time-tracking PI loop, needed to follow a delay that drifts continuously |
--autonomous-ta (not used) |
update the TA from the observed DL drift instead of from SIB19 |
and its log shows the moving satellite:
[HW] Downlink delay 18.859594 ms, Doppler shift SAT->UE 57.341449 kHz
[PHY] k_offset: 40ms, N_Common_Ta: 18.826412ms, drift: -46.086200µs/s, variant: 0.000160µs/s², timing_advance_ntn: 289142 samples, DL Doppler shift: 57.341116kHz, UL Doppler shift: 37.211877kHz
[NR_MAC] NTN Config Rxd. Epoch HFN: 0, Epoch SFN: 529, Epoch Subframe: 0, k_offset: 40ms, N_Common_Ta: 18.708429ms, drift: -46.086000µs/s, …
The second NTN Config Rxd a few seconds later carries a smaller N_Common_Ta (18.708 ms): the RFsim LEO model updates SIB19 every frame as the satellite moves (nr_update_sib19() in radio/rfsimulator/apply_channelmod.c), the way an operator's ephemeris feed would.
DN=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' oai-ext-dn)
docker exec -d oai-ext-dn iperf3 -s -p 5201
ping -I oaitun_ue1 -c 10 -W 5 $DN # -W 5: a GEO reply takes ~0.5 s of simulated time
iperf3 -c $DN -B 192.168.100.2 -t 10 -i 0 -R # TCP DL
iperf3 -c $DN -B 192.168.100.2 -t 10 -i 0 -u -b 40M -R # UDP DL, offered above capacity
duranta-ntn_node.sh measure runs this set (TCP and UDP, DL and UL) and brackets every command with the gNB's frame counter, so each window has its own time scale. The receiver line of each iperf3 run is the measurement.
systemctl stop duranta-ntn-ue duranta-ntn-gnb
Measured 2026-10-08: four runs of every scenario (one manual-mode run, then the three modes of the parity harness), on 2× Intel Xeon Gold 6126 @ 2.60 GHz, 24 cores / 48 threads, image duranta-w39-20261005b (Duranta 2026.w39, 29d5fa7d38). Ranges are min–max over the four runs. Same 5 MHz n254 cell for all four scenarios; MCS capped at 9 by the upstream file; iperf3 windows of 10 s wall clock, receiver line. "sim" = simulated clock (see The RFsim clock).
| terrestrial | LEO (600 km) | GEO, HARQ off | GEO, 32 HARQ | |
|---|---|---|---|---|
| HARQ | 16 proc., feedback on | 32 proc. | feedback disabled | 32 proc. |
| SIB19 Koffset / common TA | — | 40 ms / 18.82 ms | 478 ms / 238.74 ms | 478 ms / 238.74 ms |
| SIB19 DL Doppler | — | 57.34 kHz | 0 | 0 |
| attach (UE start → PDU address), wall s | 2.4 | 4.2–4.5 | 2.9–3.2 | 3.4 |
| attach, sim s (±1.3 s) | 2.6 | 3.8 | 6.4–7.7 | 7.7–9.0 |
| time scale during ping (sim s per wall s) | 8.1–8.4 | 0.69–0.84 | 6.18–6.19 | 5.7–6.1 |
| ping RTT, wall ms | 5.5–6.2 | 178–187 | 87–91 | 165–182 |
| ping RTT, sim ms | 46–52 | 124–157 | 539–565 | 1012–1047 |
| TCP DL, sim Mbit/s | 3.69–3.75 | 2.15–2.20 | 2.95–3.03 | 0.03–0.17 |
| UDP DL, sim Mbit/s | 3.79–3.82 | 2.44–2.55 | 3.75–3.83 | 0.05–0.26 |
| TCP UL, sim Mbit/s | 3.97–4.00 | 2.25–2.51 | 3.02–3.34 | 0.05–0.12 |
| UDP UL, sim Mbit/s | 4.13–4.14 | 2.40–2.41 (3 of 4 runs; one UL UDP run returned no result) | 3.97–4.02 | 0.14–0.25 |
| gNB MAC goodput during TCP DL, sim Mbit/s | 3.92–3.94 | 2.25–2.36 | 3.10–3.14 | 0.05–0.17 |
Re-run 2026-10-10 on duranta-w39-20261009 (same server platform, three modes): every value lies inside the ranges above. Simulated ping RTT: terrestrial 49.8–50.6 ms, LEO 124–153 ms, GEO 547–558 ms, GEO with 32 HARQ 1005–1022 ms. UDP DL: 3.82, 2.37–2.55, 3.77 and 0.05–0.26 Mbit/s. The LEO uplink TCP and UDP tests returned a result in only one of the three modes (TCP UL 2.50, UDP UL 2.40 Mbit/s); see Troubleshooting.
Re-run 2026-10-22 on duranta-w42-20261021 (Duranta 2026.w42, same server platform, three modes): parity PASS. Simulated ping RTT: terrestrial 44.5–52.8 ms, LEO 119–158 ms, GEO 522–542 ms, GEO with 32 HARQ 1007–1033 ms. UDP DL: 3.73–3.81, 2.44–2.51, 3.77–3.80 and 0.05–0.26 Mbit/s.
How to read it:
min_rxtxtime, a single UL-capable path); the LEO cell sits in between. The lower bound 2 × 238.74 ms is the parity check for GEO: a run under it means the delay was not applied.downlinkHARQ-FeedbackDisabled removes — with it the GEO cell matches the terrestrial UDP rate (3.75–3.83 vs 3.79–3.82 Mbit/s). The price is that a lost block is now only recovered by RLC ARQ, a full round trip later. Even the ping doubles (~1.0 s) with HARQ on: the ICMP echo and reply queue behind processes still waiting for ACKs. Note that this cell is noiseless and BLER is 0, so the cost of losing HARQ (no soft combining) does not show here.SAT_LEO_TRANS model recomputes delay and Doppler for every block of samples). Its RTT is the moving ~19–38 ms one-way path plus scheduling, and its throughput is below the terrestrial cell's with the same MCS cap. The LEO UE tracked a 57 kHz Doppler and a −46 µs/s drift throughout with no RLF.From the source tree of tag 2026.w39 (29d5fa7d38) — the same feature set as OAI develop of that week; Duranta's develop and OAI's are the same commit (f8f769592, 2026.w40):
| Feature | Where (2026.w39) | Shown here |
|---|---|---|
ntn-Config-r17 in SIB19, Koffset in gNB scheduling |
openair2/GNB_APP/RRC_nr_paramsvalues.h:101-110; doc/FEATURE_SET.md:163-167 |
yes |
downlinkHARQ-FeedbackDisabled-r17 (disable_harq) |
openair2/GNB_APP/gnb_config.c:1579-1593 (warns: Rel-17 UEs only) |
yes |
32 DL/UL HARQ processes (num_dlharq, num_ulharq) |
openair2/GNB_APP/gnb_paramdef.h:96-97 |
yes |
UE: SIB19 reception, ntn-UlSyncValidityDuration |
doc/FEATURE_SET.md:514-516 |
yes |
UE: TA from SIB19 (epoch-time based) or autonomous (--autonomous-ta) |
doc/FEATURE_SET.md:403-407; executables/nr-uesoftmodem.h:76-77 |
SIB19-based |
UE: DL Doppler compensation, UL pre-compensation (--initial-fo, --cont-fo-comp) |
doc/FEATURE_SET.md:408-409; executables/nr-uesoftmodem.h:78-81 |
LEO |
RFsim GEO delay (prop_delay) |
radio/rfsimulator/simulator.cpp:73,518 |
GEO |
RFsim LEO channel SAT_LEO_TRANS / SAT_LEO_REGEN (600 km circular orbit, delay + Doppler, live SIB19 updates) |
radio/rfsimulator/apply_channelmod.c:40-200; openair1/SIMULATION/TOOLS/sim.h:222-223,259-260 |
LEO (transparent) |
| Other NTN bands (n255, n256, n512 FR2-NTN) sample configs | targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band{254,255,256,512}.ntn.*.conf |
no |
Newer than 2026.w39 / not yet available: 2026.w40 adds only documentation of an OAI-nrUE ↔ OCUDU-gNB GEO interop test over ZMQ (doc/ntn-configuration.md, commit 6987ccb30). Open upstream work at the time of writing: runtime SIB19 update over telnet for external channel emulators (PR #645), trace-based NTN emulation in RFsim (#654), TLE-based LEO over ZMQ (#529), a blind CFO/PSS receiver for NTN initial sync (#612), and NTN/GEO handover (#252). Known issues that affect what you see here: #123 — with disable_harq = 1 the BLER-driven MCS adaptation is inoperative (MCS stays at its initial value; irrelevant here because this cell caps MCS at 9 and BLER is 0) and #513 — the UE applies the RAR timing-advance command one Koffset after Msg3 (harmless when the open-loop TA is accurate, as it is here; it breaks random access when the delay the UE assumes is off by more than the RAR TA can fix).
The step-by-step above is Mode 1. The bundle runs the whole four-scenario experiment as a script, an Ansible playbook and a notebook. All three copy the same two node-side helpers to the node, read one parameter file and build the same JSON contract with the same parser.
cd ~ && wget -q https://www.cosmos-lab.org/files/cosmos-tutorials-duranta-ntn.zip
unzip -o cosmos-tutorials-duranta-ntn.zip # extracts into ~/tutorials/
| File | Purpose |
|---|---|
duranta-ntn_params.yml |
the single parameter source: scenarios, LEO UE options, UDP offered loads, timeouts |
duranta-ntn_mkconf.py |
runs on the node: renders gnb-/ue-<scenario>.conf from the image's upstream NTN files (no SIM key ever leaves the node) |
duranta-ntn_node.sh |
runs on the node: conf, gnb, ue, measure, stop, platform — systemd units duranta-ntn-gnb / duranta-ntn-ue, frame-counter time scale per window |
duranta-ntn_manual.sh |
Mode 1 scripted |
duranta-ntn.yml |
Mode 2 — Ansible (cosmos_preflight first) |
duranta-ntn_tutorial.ipynb |
Mode 3 — Jupyter, adds the RTT and throughput plots |
duranta-ntn_lib.py |
shared parser, result contract, the comparison table, the parity spec |
duranta-ntn_parity.sh |
runs all three modes and asserts they agree; tears down on every exit |
duranta-ntn_teardown.yml |
stops the two units and the iperf3 server; the core only with -e ntn_stop_core=true |
cd ~/tutorials
export OMF_NODES="srv19" # your node
ansible-playbook duranta-ntn/duranta-ntn.yml
export OMF_NODES="srv19"
/opt/cosmos-jupyter/venv/bin/papermill duranta-ntn/duranta-ntn_tutorial.ipynb /tmp/duranta-ntn-out.ipynb --cwd duranta-ntn
bash duranta-ntn/duranta-ntn_parity.sh srv19
=== duranta-ntn parity ===
mode tn.ping_rtt_sim_ms tn.udp_dl_sim_mbps geo.ping_rtt_sim_ms geo.udp_dl_sim_mbps geo_harq32.ping_rtt_sim_ms geo_harq32.udp_dl_sim_mbps leo.ping_rtt_sim_ms leo.udp_dl_sim_mbps
ansible 44.5 3.81 542.3 3.78 1016.6 0.05 158.4 2.44
manual 52.1 3.75 536.5 3.77 1033.1 0.26 120.3 2.51
jupyter 52.8 3.73 521.7 3.8 1007.1 0.05 119.3 2.45
...
geo.ping_rtt_sim_ms spread max/min = 1.04 (tol 1.40)
leo.ping_rtt_sim_ms spread max/min = 1.33 (tol 1.40)
PARITY: PASS (same experiment in all modes; all checks green)
(2026-10-22, 2× Xeon Gold 6126, duranta-w42-20261021.) Notes:
The checks: in every mode and scenario the cell is in service and the UE attached; the NTN scenarios have SIB19 on both sides; the simulated ping RTT sits in a band set by geometry (GEO ≥ 477 ms — two 238.74 ms legs; LEO 40–400 ms; terrestrial ≤ 200 ms) and the modes agree within 40 %; the simulated UDP DL rate is above a floor per scenario and the modes agree within 80 % (RFsim is CPU-bound and the modes run minutes apart) — except GEO with 32 HARQ, which must only stay below 1 Mbit/s.
| Check | Command | Expected |
|---|---|---|
| NTN cell | grep -a 'activate SIB19' /tmp/duranta-ntn/geo/gnb.log |
activate SIB19 at DU |
| UE used SIB19 | grep -a 'k_offset' /tmp/duranta-ntn/geo/ue.log |
k_offset: 478ms, N_Common_Ta: 238.740000ms … |
| UE attached | ip -br addr show oaitun_ue1 |
an address in 192.168.100.0/22 |
| Delay applied | simulated ping RTT (duranta-ntn_lib.py table) |
GEO > 477 ms |
| LEO tracking | grep -a 'NTN Config Rxd' /tmp/duranta-ntn/leo/ue.log |
repeated, N_Common_Ta changing |
cd ~/tutorials && ansible-playbook duranta-ntn/duranta-ntn_teardown.yml # add -e ntn_stop_core=true to stop the core
omf tell -a offh -t <node>
By hand on the node: systemctl stop duranta-ntn-ue duranta-ntn-gnb. The units have exact names, so nothing else on the host is touched.
| Symptom | Cause | Fix |
|---|---|---|
| GEO ping shows 100 % loss but the UE is attached | ping -W 2 gives up before a ~0.5 s (simulated) reply arrives when RFsim runs near real time |
-W 5 or more |
GEO UE logs synch Failed for minutes and never attaches; the frame counter barely moves |
RFsim fell to a crawl (seen once in 16 GEO runs, 2026-10-10: 0.08× real time instead of ~6×) | stop the scenario (duranta-ntn_node.sh stop) and start it again |
UE never finds the cell (no Found SIB19, no RRC) in LEO |
the LEO UE started without the drift / Doppler options | add --ntn-initial-time-drift -46 --initial-fo 57340 --cont-fo-comp 3 --time-sync-I 0.1 |
| UE asserts at start about the frequency range | carrier arguments missing (Duranta ≥ 2026.w36) | pass the gNB's Command line parameters for OAI UE line, including --CO |
iperf3 -u UL never returns in GEO-HARQ / LEO |
the offered UDP load fills the UE's buffers and the iperf3 control messages queue behind it | offer less (the bundle uses 3M / 6M) and allow timeout ≥ test + 60 s |
Random access failed / Msg3 never decoded after changing the delay |
the UE's assumed delay differs from the channel's by more than the RAR TA corrects; upstream issue #513 makes this worse | keep ta-Common + the UE position consistent with prop_delay; do not change one alone |
| Wall-clock numbers look "too good" for a satellite | RFsim runs faster than real time | use the simulated-clock columns (… _sim_… in the contract) |
disable_harq cell stuck at one MCS |
upstream issue #123 (AMC inoperative with HARQ off) | expected in 2026.w39 |
lteue can act as an NR NTN UE (sample ue-nr-ntn.cfg in the Amarisoft install on the sb1 SDR nodes). Against this gNB it would need a radio or an IQ bridge, a matching ephemeris and position, and an Amarisoft licence — see the Amarisoft overview. Not verified here.doc/ntn-configuration.md); see the OCUDU overview. Not verified here.gnb.sa.band{254,255,256,512}.ntn.*.rfsim.conf (n512 is FR2 NTN, 120 kHz SCS). A MEO case is a matter of ta-Common, Koffset and prop_delay chosen consistently.On this wiki
Upstream
doc/ntn-configuration.md; RFsimulator — radio/rfsimulator/README.md; CI scenarios ci-scripts/xml_files/container_5g_rfsim_ntn_{geo,leo}.xml.3GPP Release 17
NTN-Config-r17, HARQ process and feedback-disabling IEs. 38331.htmN_TA,adj. 38211.htmAuthor(s): COSMOS team · Last verified: 2026-10-22 — three-mode parity PASS on 2× Xeon Gold 6126 with duranta-w42-20261021 (Duranta 2026.w42); first measured 2026-10-08 on duranta-w39-20261005b · Tested image/release: duranta-w42-20261021 (Duranta tag 2026.w42, f80a883268) · Bundle: cosmos-tutorials-duranta-ntn.zip · Tags: duranta, oai, ntn, satellite, geo, leo, sib19, rfsim, 5g, rel-17