Quickstart
The bindings run the supported UE and gNB owners in one Python process from one terminal. Private child processes isolate selected OAI state and failure paths, but the package starts, checks, stops, and reaps them for you.
Run a persistent PHY link
"""Run a persistent OAI full-PHY link from one Python process."""
from oai_python.bindings.nr.phy.link import NrPhyLink
payload = bytes(range(64))
with NrPhyLink() as link:
slot = link.step(downlink_payload=payload)
link.run_slots(100)
(received,) = slot.transmissions
assert received.crc_ok and received.decoded_payload == payload
print(f"decoded {len(received.decoded_payload)} downlink bytes")
Expected output:
decoded 64 downlink bytes
step() advances one explicit slot. run_slots(count) advances as many more
slots as requested without returning to Python for every slot. The owner and
its OAI state remain alive until close() or the end of the with block.
Choose the right owner
| Owner | PHY work | MAC work | Users | Best for |
|---|---|---|---|---|
NrPhyLink |
Real reviewed SISO TX/RX chain | None | Link endpoints | Link-level and waveform algorithms |
NrPhyStack |
Real supported PRACH, PDCCH, PDSCH, PUSCH, and PUCCH paths | OAI grants and state | 1 UE | Causal access and traffic through MAC plus PHY |
UeGnbLink |
Abstracted as copied FAPI outcomes | Persistent OAI UE/gNB MAC | 1โ16 UEs | Fast scheduler and protocol experiments |
Granular classes under oai_python.bindings.nr.phy call individual OAI blocks
without requiring any MAC owner. PHY is a first-class mode, not an internal
detail of the MAC simulation.
Run UE and gNB together
For a complete one-UE supported stack:
"""Attach one UE and exchange SDUs through the complete PHY stack mode."""
from oai_python.bindings.nr.phy.stack import NrPhyStack
def main() -> None:
with NrPhyStack(history_limit=64) as simulation:
simulation.start()
attached = simulation.run_until_connected(140)
assert attached.terminal_state == "connected"
simulation.enqueue_downlink_sdu(0, b"hello from the gNB")
simulation.enqueue_uplink_sdu(0, b"hello from the UE")
for _ in range(500):
simulation.step()
result = simulation.result()
if result.downlink_sdus and result.uplink_sdus:
break
else:
raise AssertionError("full-PHY stack traffic did not complete")
print(f"connected RNTI: {result.connected_rntis[0]:#06x}")
print(f"downlink SDU: {result.downlink_sdus[-1]!r}")
print(f"uplink SDU: {result.uplink_sdus[-1]!r}")
if __name__ == "__main__":
main()
The exact RNTI is deterministic for a given configuration. Successful output reports one connected RNTI and the two decoded payloads:
connected RNTI: 0x....
downlink SDU: b'hello from the gNB'
uplink SDU: b'hello from the UE'
Attach multiple UEs
Use UeGnbLink(ue_count=4) for four UE MAC owners attached to one gNB owner.
Each delivery includes both ue_id and rnti, so results cannot silently lose
user attribution. Supported ue_count values are 1 through 16.
"""Run four UEs through the abstract MAC/FAPI link mode."""
from oai_python.bindings.nr.mac.link import UeGnbLink
def main() -> None:
with UeGnbLink(ue_count=4, history_limit=64) as simulation:
simulation.start()
attached = simulation.run_until_connected(1_000)
assert attached.terminal_state == "connected"
for ue_id in range(4):
simulation.enqueue_downlink_sdu(ue_id, f"downlink-{ue_id}".encode())
simulation.enqueue_uplink_sdu(ue_id, f"uplink-{ue_id}".encode())
for _ in range(1_000):
simulation.step()
result = simulation.result()
if len(result.downlink_sdus) == len(result.uplink_sdus) == 4:
break
else:
raise AssertionError("MAC/FAPI traffic did not complete")
for delivery in result.deliveries:
print(
f"UE {delivery.ue_id} RNTI {delivery.rnti:#06x} "
f"{delivery.direction} SDU: {delivery.payload!r}"
)
if __name__ == "__main__":
main()
Expected output contains eight attributed deliveries: uplink and downlink for each of UE 0 through UE 3.
Cleanup and errors
Prefer context managers. Calling close() twice is safe, and using an owner
after close raises a Python exception. Timeouts and child-process failures are
reported as exceptions rather than leaving a worker behind. For a support
report, run oai-python-diagnose --json and include the redacted output.