Terminal 1

May 5, 2026 · View on GitHub

flexric-bridge

FlexRIC E2 Real-Wire Integration: NTN E2 Agent, Three xApps, and a Reproducible Docker Stack


module live demo

Why this module

In-process xApp stubs are a fine way to prototype a Near-RT RIC integration; they do not let you publish results that survive review against a real O-RAN deployment. The reviewer's question is always: "does the same xApp logic work over the actual wire?" flexric-bridge answers that question by replacing the in-memory E2 stubs in oran-ntn with a real Near-RT RIC stack based on EURECOM's FlexRIC. Three NTN-aware xApps (Conditional Handover, multi-beam selection, slice orchestration) run as separate processes and exchange real E2AP messages with an ns-3 E2 agent. Two operating modes — same code, different wire: a stub TCP/JSON mode for CI and a live SCTP/E2AP mode driven by FlexRIC under Docker.

At a glance

┌─────────────────────── stub mode (CI) ────────────────────────┐
│  Python e2-agent-ns3 ──TCP loopback (JSON frames)──► xApp     │
│  (no FlexRIC, no asn1c, no Docker required)                    │
└────────────────────────────────────────────────────────────────┘
┌─────────────────────── live mode (Docker) ────────────────────┐
│  ns-3 ──KPM push──► ntn-e2-agent ──SCTP/E2AP──► nearRT-RIC     │
│                                              │                  │
│                                              └──► cho / beam /  │
│                                                   slice xApps   │
└────────────────────────────────────────────────────────────────┘

Stub mode uses JSON-on-the-wire framed by 4-byte big-endian length, with exactly the same procedure-code / RIC-request-id / RAN-function-id structure as real E2AP. Switching to live mode is a one-line change (use FlexRIC's asn1c-generated codec via the supplied Docker image).

MetricValue
xApps shipped3 (CHO · beam-mgmt · slice-orch)
E2AP procedure codes implemented7 (E2 Setup · Subscription · Indication · Control + 3 NTN extensions)
E2SM-KPM RAN-function id147 (NTN extensions)
E2SM-RC RAN-function id148 (NTN extensions)
Stub-mode E2E throughput30 000 IND / s (90 000 indications, 0 % loss, 3.0 s wallclock)
30-step CHO oracle equivalencebit-identical vs hand-coded reference
Test suite (pytest tests/, 7 tests)PASS in 2.34 s

What it does

  • E2AP codec (src/ntn_flexric_bridge/e2ap_codec.py) — frame encoder + 7 message constructors (E2 Setup Req/Resp, Subscription Req/Resp, Indication, Control Req/Ack); 4-byte big-endian length prefix; same procedure-code / RIC-request-id structure as live FlexRIC.
  • E2SM-KPM with NTN extensions (e2sm_kpm_ntn.py) — Key Performance Measurement service model carrying rsrp_dbm, elevation_deg, tte_total_us, sat_norad, plus standard 5G KPIs.
  • E2SM-RC with NTN extensions (e2sm_rc_ntn.py) — RAN Control service model carrying CHO triggers, multi-beam selections, and slice-PRB-share commands with NTN-specific IEs (NORAD ID, expected TTA in milliseconds, slice S-NSSAI).
  • NTN E2 agent (e2_agent_ns3.py) — TCP server + state machine; one-call submit_kpm_measurement(header, message) to push live ns-3 simulation data; default CHO handler logs handovers and returns control-acks.
  • xApps (src/ntn_flexric_bridge/xapps/) — xapp_base provides the connect / subscribe / send / read loop; cho_xapp is a TTE-aware Conditional Handover xApp that's bit-identical to the in-memory CHO oracle on a 30-step pass; beam_mgmt_xapp does multi-beam selection; slice_orch_xapp orchestrates eMBB / URLLC / mMTC PRB shares.
  • Docker stack (docker/) — 2-stage Dockerfile (asn1c + SWIG 4.2 + GCC 12 + FlexRIC master), docker-compose.yml brings up RIC + e2-agent + 3 xApps + a tcpdump container that captures the live SCTP/E2AP wire to captures/e2ap.pcap.

Install & run

Stub mode (no Docker)

git clone https://github.com/Muhammaduazir69/flexric-bridge.git contrib/oran-ntn/flexric-bridge
cd contrib/oran-ntn/flexric-bridge
pip install -e .[test]

# Terminal 1 — RIC stub
ntn-e2-agent --gnb-id 1 --port 36421

# Terminal 2 — CHO xApp (similarly: ntn-beam-xapp / ntn-slice-xapp)
ntn-cho-xapp --host 127.0.0.1 --port 36421 --seconds 30

Driven from a Python ns-3 example:

from ntn_flexric_bridge.e2_agent_ns3 import E2AgentNs3
from ntn_flexric_bridge import e2sm_kpm_ntn

agent = E2AgentNs3(gnb_id=1)
threading.Thread(target=agent.run_stub_server, daemon=True).start()

agent.submit_kpm_measurement(
    e2sm_kpm_ntn.KpmIndicationHeader.make(gnb_id=1, sat_norad=44714),
    e2sm_kpm_ntn.KpmIndicationMessage(measurements=[
        e2sm_kpm_ntn.KpmMeasurement("rsrp_dbm",       -97.5, ue_imsi="100001"),
        e2sm_kpm_ntn.KpmMeasurement("elevation_deg",  47.3,  ue_imsi="100001"),
        e2sm_kpm_ntn.KpmMeasurement("tte_total_us",   3669,  ue_imsi="100001"),
    ]),
)

Live mode (Docker)

docker compose -f docker/docker-compose.yml -p ntn-flexric up -d
# Wait ~30 s for FlexRIC + agent + 3 xApps to come up
wireshark -d sctp.port==36421,e2ap captures/e2ap.pcap

The Dockerfile builds a clean image from Ubuntu 22.04 with asn1c@HEAD, SWIG 4.2.1, GCC 12 (FlexRIC's macro-heavy code triggers an internal compiler error on GCC 11), and FlexRIC master against E2AP_V2 / KPM_V2_03. See docs/BUILD_FLEXRIC.md for the full recipe.

Verification

Test suite (pytest tests/, 7 cases, 2.34 s, all passing):

TestAsserts
test_e2_setup_request_round_tripE2AP Setup encodes + decodes with procedure_code = 1, criticality REJECT, PLMN round-trips
test_subscription_request_round_tripRIC Subscription frames carry the action list intact
test_kpm_indication_payload_round_tripHeader + message survive serialise → parse with all 3 NTN measurements
test_rc_cho_trigger_round_tripRC ControlRequest with CHO trigger preserves NORAD + expected_tta_ms
test_setup_subscribe_indicate_flow100 IND sent / 100 received over the loopback (no loss)
test_cho_xapp_triggers_on_low_serving_elevationCHO emitted from sat A → B at the right moment, with correct TTE
test_cho_xapp_matches_inmemory_oracleAcross a 30-step synthetic pass with 3 sats, the xApp's HO sequence is bit-identical to a hand-coded reference

Stub-mode E2E performance smoke (30 000 ticks × 3 sats = 90 000 indications):

MetricValue
Indications sent90 000
Indications received90 000
Loss rate0.00 %
Wallclock3.00 s
Throughput30 k IND / s
Handovers triggered15
Controls executed15

In stub mode, every E2 procedure is exercised end-to-end with the same message taxonomy as live FlexRIC, and the CHO xApp produces a handover sequence bit-identical to the in-memory CHO reference — so the wire-level trace is the only thing changing when you switch modes.

Documentation

  • docs/BUILD_FLEXRIC.md — full live-mode bring-up recipe (asn1c, SWIG, GCC 12, FlexRIC tag).
  • INSTALL.md — pip install + dev environment notes.
  • O-RAN.WG3.E2AP-v02.03 — E2 Application Protocol.
  • O-RAN.WG3.E2SM-KPM-v03.00 — E2 Service Model: Key Performance Measurement.
  • O-RAN.WG3.E2SM-RC-v01.03 — E2 Service Model: RAN Control.

Cite this work

@misc{uzair2026flexricbridge,
  author = {Uzair, Muhammad},
  title  = {flexric-bridge: FlexRIC E2 Real-Wire Integration with NTN-Aware xApps},
  year   = {2026},
  url    = {https://github.com/Muhammaduazir69/flexric-bridge}
}

Part of the ns3-ntn-toolkit

ModuleRepo
Toolkit (umbrella)ns3-ntn-toolkit
ntn-constellationntn-constellation
ntn-rrcntn-rrc
ntn-observabilityntn-observability
ns3-ai (fork)ns3-ai
ntn-saginntn-sagin
ntn-slicentn-slice
ntn-v2xntn-v2x
flexric-bridgethis repo
ntn-sionnantn-sionna
ntn-digital-twinntn-digital-twin
ntn-chontn-cho-framework
oran-ntnoran-ntn
thz-ntnns3-thz-ntn

License

GPL-2.0-only — same as the umbrella ns3-ntn-toolkit and FlexRIC itself.

Acknowledgements

EURECOM Mosaic5G / FlexRIC team · O-RAN Alliance WG3 specifications · mouse07410/asn1c · ns-3 core team.