Skip to content

title: ntn-digital-twin - live LEO constellation mirror with handover prediction API | ns3-ntn-toolkit description: ns-3 NTN digital twin: pulls live CelesTrak TLEs, propagates with SGP4, and serves a FastAPI handover-prediction REST API for any satellite ground location.


W10 - ntn-digital-twin

ntn-digital-twin is the ns3-ntn-toolkit module that mirrors a real LEO constellation in near-real time: a refresher loop pulls live CelesTrak TLEs, propagates them with SGP4, emits CesiumJS CZML and InfluxDB telemetry, and a FastAPI service answers handover-prediction queries for any UE location. It reuses the toolkit's SGP4 propagator and metric schema so the live twin shares dashboards with simulated runs.

Why it matters. Validating NTN handover logic and coverage models against the constellation as it actually flies (not a synthetic shell) closes the loop between simulation and reality. Researchers and operators need a service that tracks today's Starlink (or any CelesTrak group), predicts the next handovers for a ground terminal, and feeds the same Grafana dashboards and Cesium viewer used for ns-3 runs. This module is that always-on twin.

What it simulates

  • Refresher loop (twin_loop.py, CLI ntn-twin-loop): on a cron / systemd timer it fetches CelesTrak TLEs (6-hour on-disk cache), propagates with the ntn-constellation SGP4-direct path, writes CesiumJS CZML, and emits InfluxDB line protocol. It is crash-tolerant: exceptions are logged and the loop continues on schedule.
  • Handover-prediction REST API (api/server.py, CLI ntn-twin-api, FastAPI / Uvicorn): closed-form ECEF→ENU elevation on the hot path (no Skyfield), with a constellation cache refreshed from CZML file mtime.
    • GET /health - uptime, loaded constellation size, last refresh timestamp.
    • GET /constellation/state?at=<ISO> - per-satellite NORAD id, geodetic lat/lon/alt, and ECI velocity at a given epoch.
    • POST /predict/handover - given ue_lat_deg, ue_lon_deg, optional ue_alt_m, horizon_min, step_sec, and min_elevation_deg, returns the ordered handover events (incoming / outgoing satellite, elevations, time) over the horizon plus server elapsed_ms.
  • InfluxDB telemetry (ntn_sat_pos measurement: sat_x_m, sat_y_m, sat_z_m, tagged sat_norad / run_id): interoperates with the canonical ntn-observability schema, so the existing Grafana dashboards consume the live twin directly.
  • CesiumJS Live viewer: a viewer patch adds a Live toggle so the Cesium globe shows current satellite positions.
  • systemd units (ntn-twin.service, ntn-twin-api.service): production deployment of the loop and API with restart-on-failure and state directories.
Live LEO constellation mirror refreshed from CelesTrak with a POST /predict/handover response for a ground UE
Live CelesTrak-driven constellation mirror with a /predict/handover response (p99 ≤ 30 ms)

Standards & references

  • CelesTrak (Dr. T. S. Kelso): live TLE feed driving the twin.
  • SGP4: orbital propagation via the sgp4 library, reused from ntn-constellation.
  • WGS-84: geodetic and ECEF/ECI reference frames for satellite and UE positions.
  • InfluxDB line protocol: shared telemetry format with ntn-observability.

Use cases

  • Live handover rehearsal: query /predict/handover for a ground station to anticipate the next serving-satellite changes minutes ahead.
  • Constellation tracking dashboards: stream ntn_sat_pos into the existing Grafana stack to monitor the real fleet alongside simulated runs.
  • Coverage and visibility studies: call /constellation/state to snapshot which satellites are overhead a location at any epoch.
  • CI-friendly validation: run a bounded loop (--max-iterations) to verify propagation fidelity against the SGP4-direct reference.
  • Always-on deployment: install the systemd units for a continuously refreshing twin tied to a chosen CelesTrak group.

Run it

ntn-twin-loop --max-iterations=3 --czml /tmp/twin.czml --lp /tmp/twin.lp --max-sats=50 --interval=2

This runs three loop iterations against the live Starlink TLE feed, writing CesiumJS CZML and InfluxDB line protocol. Then start the API with ntn-twin-api --host 0.0.0.0 --port 8090 and query it:

curl http://localhost:8090/predict/handover \
    -H 'content-type: application/json' \
    -d '{"ue_lat_deg":33.6844,"ue_lon_deg":73.0479,"horizon_min":10}'

Scope

Cron pulls TLEs, propagator regenerates state, CesiumJS shows current positions, REST API answers /predict/handover for a UE over the next N minutes.

Component What it does
twin_loop.py Cron entry; refresh TLE + push to InfluxDB + emit CZML
api/server.py FastAPI: /health, /constellation/state, /predict/handover
systemd/ntn-twin.service systemd unit for the loop
viewer-patches/index.html.patch Adds Live toggle to the CesiumJS viewer

Validation gates

  • 6/6 tests green
  • 24-h continuous loop without crash; 144-iter loop, 0 errors
  • API answers /predict/handover p99 = 29.9 ms over 100 calls (gate 500 ms)
  • CesiumJS "Live" toggle works against running loop

Quickstart

pip install -e contrib/ntn-digital-twin

# One-shot loop iteration
ntn-twin-loop

# Long-running API
ntn-twin-api &
curl http://localhost:8090/health
curl http://localhost:8090/predict/handover \
    -H 'content-type: application/json' \
    -d '{"ue_lat_deg":33.6844,"ue_lon_deg":73.0479,"horizon_min":10}'

Source