How Interpolation and Dead Reckoning Enable Smooth Flight Tracking in Gods-Eye-View

Gods-Eye-View combines Hermite smoothstep interpolation with velocity-based dead reckoning to eliminate visual jitter caused by sparse 10–30 second ADS-B updates, ensuring aircraft billboards glide smoothly across the Cesium.js globe.

Receiving aircraft positions from the OpenSky Network every 10–30 seconds creates a fundamental rendering challenge: displaying an aircraft at discrete coordinates would produce jarring jumps or frozen frames. The bilawalsidhu/gods-eye-view repository solves this in src/data/flights.js by blending two predictive techniques that maintain fluid motion at 60 frames per second even when the underlying data arrives sporadically.

The Challenge: Sparse ADS-B Updates and Visual Jitter

Raw ADS-B feeds from the OpenSky Network do not arrive frequently enough to drive animation directly. Rendering an aircraft billboard immediately upon receiving a new fix would cause the icon to snap across the map or pause motionless when packets delay. To maintain visual continuity, the system must generate intermediate positions between verified fixes while gracefully handling network dropouts.

Interpolation with Hermite Smoothstep

When a fresh fix arrives, the engine does not instantly teleport the aircraft to the new coordinates. Instead, it executes a 1-second linear interpolation (lerp) that blends the previous dead-reckoned position into the corrected fix using Hermite smoothstep easing.

The easing function applies the polynomial 3t² – 2t³ to the interpolation factor, producing a smooth acceleration curve rather than linear motion. As noted in the source code comments at lines 12–16 of src/data/flights.js, the aircraft "advances smoothly using ENU frame math… a 1-second lerp blends the current dead-reckoned position into the corrected fix to avoid visual snapping."

// Interpolation after a fresh fix arrives (1-second smoothstep lerp)
const lerpFactor = Math.min(1, (now - lastFixTime) / 1000);
const smooth = lerpFactor * lerpFactor * (3 - 2 * lerpFactor); // smoothstep
Cesium.Cartesian3.lerp(prevPos, newPos, smooth, interpolatedPos);

This Cesium.Cartesian3.lerp operation ensures that when a delayed fix finally arrives, the aircraft drifts softly into the corrected trajectory rather than snapping instantly.

Dead Reckoning for Continuous Motion

Between OpenSky polls—or when a fix is missed entirely—the system falls back to dead reckoning. This algorithm predicts the aircraft’s current location by extrapolating from its recent velocity vector.

Velocity Extrapolation in _deadReckon

The private function _deadReckon(icao24, result) defined in src/data/flights.js computes the predicted position by analyzing the delta between the last two stored fixes. It calculates velocity as displacement over time, then projects that vector forward to the current render timestamp.

// Dead-reckoning (simplified) – used in _deadReckon()
function deadReckon(history) {
  const last = history[history.length - 1];
  const prev = history[history.length - 2];
  const dt = Cesium.JulianDate.secondsDifference(last.time, prev.time);
  const vel = Cesium.Cartesian3.subtract(last.position, prev.position, new Cesium.Cartesian3());
  Cesium.Cartesian3.multiplyByScalar(vel, 1 / dt, vel);
  const extrap = Cesium.Cartesian3.multiplyByScalar(vel, deltaSec, new Cesium.Cartesian3());
  return Cesium.Cartesian3.add(last.position, extrap, new Cesium.Cartesian3());
}

Position History Buffer

Each aircraft maintains a short trajectory history defined by POSITION_HISTORY_LIMIT = 5, storing the last five Cartesian positions with timestamps. This buffer provides the recent trajectory data required to compute the velocity vector used in the extrapolation formula.

Performance Architecture: Caching and Callback Properties

To prevent redundant calculations during every render frame, the engine implements a fixed-interval update cycle and a per-frame cache.

The 80-Millisecond Update Cycle

Rather than recomputing positions every frame, dead reckoning runs at a fixed ~12 Hz rate controlled by FLEET_DR_INTERVAL_MS = 80. This interval balancing ensures motion remains fluid without overwhelming the CPU with unnecessary geodetic math.

Per-Frame Position Caching

The system stores the last computed dead-reckoned position in _cachedDRPosition and tracks the last computed frame in _cachedDRFrame. During the render loop, a Cesium.CallbackProperty retrieves the cached value unless the frame has changed, eliminating duplicate calculations for a single frame.

// Per-frame cache – only call dead-reckon once per frame
if (frame !== _cachedDRFrame) {
  _cachedDRPosition = _deadReckon(icao24, _scratchDrRaw);
  _cachedDRFrame = frame;
}
billboard.position = _cachedDRPosition;

This caching strategy allows thousands of aircraft to update smoothly without per-frame garbage collection or redundant trigonometric operations.

Code Architecture and Reusability

The motion system is modularized across several files to support different aircraft categories:

  • src/data/flights.js – Defines the core _deadReckon function and interpolation logic for commercial traffic.
  • src/data/motionModel.js – Provides helper utilities such as arcOffsetEnu and lerpAngleDeg used by the dead-reckoning engine.
  • src/data/militaryFlights.js – Reuses the same dead-reckoning callback pattern for military aircraft, demonstrating the architecture’s flexibility across data layers.

Summary

  • Interpolation uses Hermite smoothstep (3t² – 2t³) during a 1-second window to blend new fixes smoothly, preventing visual snapping when corrected positions arrive.
  • Dead reckoning extrapolates positions using velocity vectors derived from a 5-fix history buffer when real-time data is unavailable.
  • Performance optimization includes an 80-millisecond update interval and per-frame caching (_cachedDRPosition) to minimize CPU overhead.
  • Cesium.js integration leverages CallbackProperty to supply smoothly interpolated coordinates to billboards every frame.
  • Modular design allows the same dead-reckoning algorithm to power both commercial and military flight layers.

Frequently Asked Questions

What is Hermite smoothstep interpolation?

Hermite smoothstep interpolation applies a cubic polynomial (3t² – 2t³) to a normalized time value, creating an ease-in-ease-out curve. In Gods-Eye-View, this function modulates the 1-second lerp between the old and new aircraft positions, ensuring the visual transition accelerates gently rather than moving at a constant mechanical speed.

How does dead reckoning differ from interpolation in this system?

Interpolation blends between two known, received positions (the previous fix and the new fix) over a fixed 1-second duration. Dead reckoning predicts positions during data gaps by calculating a velocity vector from recent history and extrapolating forward, allowing the aircraft to continue moving realistically even when the OpenSky feed drops packets.

Why does Gods-Eye-View cache dead-reckoned positions per frame?

The per-frame cache (_cachedDRFrame and _cachedDRPosition) prevents the _deadReckon function from executing multiple times within a single animation frame. Since Cesium.js may query the CallbackProperty several times per frame for different rendering passes, caching ensures the expensive velocity extrapolation occurs only once per 80-millisecond cycle, maintaining 60 FPS performance with thousands of tracked aircraft.

How does the system handle severely delayed or missed ADS-B packets?

When the OpenSky Network delays beyond the expected 10–30 second window, the dead-reckoning algorithm continues extrapolating using the last known velocity vector. Once a new fix arrives, the interpolation system takes over for 1 second to smoothly correct the aircraft’s path from the predicted dead-reckoned position to the actual received coordinates, eliminating visible jumps or "snap-backs."

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →