Engineering Documentation · v3.0

The physics of NEO Radar.

Every orbital trajectory in NEO Radar is integrated from real JPL Horizons ephemeris with full gravitational perturbation from the outer planets. This document describes the math, the integrators, the data pipeline, and the strict separation between the physics engine and the rendering layer.

Author · Davi Martins Revision · 3.0 · 2026·06·04 Reading time · ~14 min Citation · doi:placeholder
01 · Reference frame

Heliocentric ecliptic J2000, AU · M☉ · days.

All state vectors live in a heliocentric ecliptic reference frame at J2000, with position in astronomical units, mass in solar masses, and time in days since J2000.0 (Julian Date 2451545.0). This makes the gravitational constant clean: G = 4π² in these units, exactly. Every orbital integration and every render call uses these conventions — no mid-pipeline conversions, no silent unit drift.

Position
AU
Mass
M
Time
days

The frame is rotated to the J2000 ecliptic so we never need to chain equator-to-ecliptic transforms during integration. The penalty is a single one-time rotation when ingesting JPL Horizons vectors (which arrive in ICRF) — a 23.4° obliquity rotation about the x-axis, applied once at cache load.

02 · Kepler solver

Newton-Raphson, four iterations, ε < 10⁻¹².

For two-body propagation we solve Kepler's equation numerically. Given mean anomaly M and eccentricity e, we want eccentric anomaly E such that:

M = E − e · sin(E) Kepler's equation

Newton-Raphson with seed E₀ = M + e · sin(M) converges in three to four iterations for nearly every NEO in the catalog. The update step is:

En+1 = En − (En − e · sin(En) − M) / (1 − e · cos(En)) Update
physics/kepler.tsTypeScript · 17 LOC
export function solveKepler(M: number, e: number): number {
  // Seed approximation — accurate to ~e² for moderate eccentricity
  let E = M + e * Math.sin(M);
  const tol = 1e-12;
  const maxIter = 20;

  for (let i = 0; i < maxIter; i++) {
    const f  = E - e * Math.sin(E) - M;
    const fp = 1 - e * Math.cos(E);
    const dE = f / fp;
    E -= dE;
    if (Math.abs(dE) < tol) return E;
  }
  throw new Error(`Kepler failed to converge: e=${e}, M=${M}`);
}
Convergence · |ΔE| per iteration · Apophis (e=0.191)
10⁰ 10⁻³ 10⁻⁶ 10⁻⁹ 10⁻¹² i=1 i=2 i=3 i=4 i=5 ε = 10⁻¹² converged (3 iters)
03 · N-body integration

RK4 with adaptive timestep based on local truncation error.

Pure Kepler propagation ignores everything except the Sun. To get JPL-grade accuracy near Jupiter flybys, we run a 4th-order Runge-Kutta integrator over the full Newtonian N-body acceleration:

ai = −G · Σj≠i mj · (ri − rj) / |ri − rj Newton

Timestep Δt is chosen per-step from local truncation error: we run one RK4 step at Δt, two at Δt/2, compare position residuals, and accept if below tolerance — else halve and retry. In practice Δt stays at 1.0 d in interplanetary space and contracts toward 10⁻³ d within a planet's Hill sphere.

Δtnew = 0.9 · Δt · (εtol / εlocal)1/5 PI step controller
Order
4
Δt range
10⁻³ – 1.0 d
εtol
10⁻¹⁰ AU
04 · Gravitational bodies

Six perturbers — four dynamic, two for visual completeness.

We include the Sun and five perturbing bodies in every integration. Earth, Jupiter, and Saturn cover > 99.6% of cumulative Δv for 50-year propagation of typical NEO orbits. Uranus and Neptune are now included for visual completeness and honest solar-system representation; their combined gravitational Δv contribution to typical NEO trajectories is < 0.1% over 50 years. Mercury, Venus, and Mars contribute below the 0.4% threshold and are omitted.

Sun
Primary · always-on
1.000
M
Earth
Required · close-approach geometry
3.00 × 10⁻⁶
M
Jupiter
Dominant perturber · 99% of outer-planet Δv
9.55 × 10⁻⁴
M
Saturn
Secondary · resonance contributions
2.86 × 10⁻⁴
M
Uranus
Visual completeness · < 0.1% Δv over 50yr
4.37 × 10⁻⁵
M
Neptune
Visual completeness · < 0.1% Δv over 50yr
5.15 × 10⁻⁵
M
05 · Uncertainty propagation

3σ covariance projected forward via Monte Carlo.

The uncertainty cone visible in the Radar is not decorative — it is the 3σ envelope of position deviation, sampled by Monte-Carlo propagation of the JPL Horizons covariance matrix. We draw N = 256 particles from a multivariate Gaussian over the six orbital elements, integrate each forward through the same RK4 engine, and at every output epoch fit a position ellipsoid to the cloud.

The cone widens dramatically near planetary close approaches — this is the gravitational keyhole effect, where small initial position errors get amplified by the close encounter. NEO Radar shows this honestly: far from a flyby the cone is invisible, near one it blooms.

06 · Data pipeline

NASA NeoWs → JPL Horizons → local cache → renderer.

Discovery and classification metadata come from NASA's Near-Earth Object Web Service (NeoWs); precision ephemeris and covariance come from JPL Horizons. Both are pulled and pinned to a local SQLite cache, keyed by SPK-ID, with a content hash so we can detect upstream re-solutions and invalidate.

data/pipeline.tsflow
NeoWs /* classification, threat metadata */
  └─► CatalogEntry { spkId, name, designation, classification, riskLevel }
       │
       ▼
Horizons /* state vectors + covariance, K224/56 solution */
  └─► EphemerisRecord { r[3], v[3], cov[6×6], epoch, arc, condition }
       │
       ▼
LocalCache /* SQLite, content-hashed, 14h TTL */
  └─► get(spkId) ─► EphemerisRecord  /* O(1) on hit */
       │
       ▼
PhysicsEngine /* pure, no I/O, no rendering */
  └─► propagate(epoch, dt, perturbers) ─► State[]
       │
       ▼
Renderer /* canvas, never touches physics */
07 · Architecture

Physics and rendering live in different worlds.

The single most important architectural decision in NEO Radar is the strict separation of the physics engine from the rendering layer. The physics engine has zero dependencies on the DOM, on canvas, on any rendering primitive. The renderer has zero dependencies on integrators, solvers, or covariance math. They communicate exclusively through plain state arrays — frozen, immutable, re-emitted every frame.

PHYSICS · PURE Kepler Solver Newton-Raphson · ε < 1e-12 RK4 Integrator Adaptive Δt · 6-body Covariance Propagation Monte Carlo · N=256 no DOM · no canvas · no React STATE[] frozen · per-frame RENDERER · PRESENTATION Canvas Layer orbits · planets · cones UI Shell React · sidebar · controls Camera · Pan/Zoom view-space transforms no integrators · no solvers
08 · Known limitations

What NEO Radar does not yet model.

Honesty about scope is the only way to earn trust about accuracy. These effects are not modeled in v3.0:

  • Outgassing Long-period comet activity — volatile sublimation imparts a non-gravitational acceleration of order 10⁻⁸ AU/d² on comets but is negligible for the asteroidal NEOs in the current catalog.
  • GR Relativistic corrections — solar Schwarzschild precession contributes ~43"/century to Mercury but is below the position-uncertainty floor for any current NEO target over the 50-year window.
  • Yarkovsky Radiation-pressure thermal drag — the dominant non-gravitational force on small NEOs (e.g. Bennu, ~5×10⁻¹⁴ AU/d²). Currently approximated from JPL's per-object A2 coefficient at cache load; full thermal model is roadmap.
  • Lunar Earth–Moon barycenter modeled as point mass at EMB. Close approaches inside the lunar distance see ~1500 km position error from this approximation; acceptable for visualization, not for impact prediction. Full Earth–Moon system (two-body sub-integration) is planned for v3.