From c798ac56af4e6c90f5af930c2c3c438e29fa3902 Mon Sep 17 00:00:00 2001 From: Alexander Rulkens Date: Sun, 4 Oct 2026 09:24:01 +0200 Subject: [PATCH 01/14] Accurate planet positions: spec + plan --- .../2026-10-04-accurate-planet-positions.md | 331 ++++++++++++++++++ ...-10-04-accurate-planet-positions-design.md | 188 ++++++++++ 2 files changed, 519 insertions(+) create mode 100644 docs/superpowers/plans/2026-10-04-accurate-planet-positions.md create mode 100644 docs/superpowers/specs/2026-10-04-accurate-planet-positions-design.md diff --git a/docs/superpowers/plans/2026-10-04-accurate-planet-positions.md b/docs/superpowers/plans/2026-10-04-accurate-planet-positions.md new file mode 100644 index 000000000..1fd853f5c --- /dev/null +++ b/docs/superpowers/plans/2026-10-04-accurate-planet-positions.md @@ -0,0 +1,331 @@ +# Accurate planet positions — implementation plan + +> **For agentic workers:** REQUIRED SUB-SKILL: use superpowers:subagent-driven-development under the +> lean protocol in `docs/superpowers/conventions/sdd-execution.md`. Steps use checkbox (`- [ ]`) syntax. + +**Goal:** The eight planets match JPL DE (Horizons) to ≤ 1,000 km over 1900–2100, Earth wobbles about +the Earth–Moon barycentre, and every orbit trail stays through its body. + +**Architecture:** Each planet's position is today's Keplerian position plus a generated correction +series: a cubic polynomial plus fitted sinusoids, about 17 kB of constants. The correction is +evaluated in `deriveBodyStates`. A one-row pair table subtracts the Moon's reflex from Earth. Orbit +trails anchor on the snapshot body position, so they follow both corrections without needing to know +about either. + +**Tech stack:** TypeScript, Vitest, tsx tools, the JPL Horizons API. + +**Spec:** `docs/superpowers/specs/2026-10-04-accurate-planet-positions-design.md` + +## Global constraints + +- Accuracy: ≤ **1,000 km** from Horizons for every planet over **1900-01-01 → 2100-01-01**. The fit + stops at ≤ **900 km** on its sample grid, and the tool verifies every planet on the dense 1-day grid + and throws above 1,000 km. +- Outside the span the correction is **frozen at its edge value**: clamp `simDays` to + `[startJd, endJd]`. No fade and no new setting. +- Horizons query: `EPHEM_TYPE='VECTORS'`, `CENTER='500@10'` (Sun centre), `REF_PLANE='FRAME'` + (equatorial ICRF), `TIME_TYPE='UT'`, `OUT_UNITS='KM-S'`, `CSV_FORMAT='YES'`, `VEC_TABLE='1'`. + - Targets: Mercury `199`, Venus `299`, Earth–Moon barycentre `3`, then system barycentres `4`–`8` + for Mars through Neptune. + - Fetch 1-day steps in 50-year chunks, retrying each chunk up to 5 times. +- `OBLIQUITY_DEG = 23.4392911`. +- Earth–Moon pair: `secondaryMassFraction = 1 / 82.30057`, the Moon's share of the pair mass. +- `simDays` stays a UTC Julian date. No runtime TDB conversion: the UT query absorbs the offset. +- Conventions: one symbol per `utils/` and `@types/` file, `type` and never `interface`, and pass and + frame files export only their one symbol. +- Prep (Tasks 1–2) is a **separate PR** on branch `accurate-planets-prep` off `main`. This spec and + plan are committed there too. Features (Tasks 3–6) go on `accurate-planets`, stacked on the prep + branch. Never commit on `main`. + +## Review focus + +1. **Dates outside 1900–2100** (a tour in 1850, a scrub to 2200). Positions stay finite and equal + Kepler plus the frozen edge correction, with no jump at either boundary. Tests are in Task 3. +2. **A camera parked at an Earth surface site with the clock running.** The site keeps its offset from + Earth while Earth wobbles. Tested in Task 6. +3. **Moon, Hubble and the easter eggs after the Earth reflex.** Each still sits on its own trail, + because its trail is centred on the reflexed Earth. Tested in Task 6. +4. **Any element-row edit or frame change without regenerating.** The Horizons check fails loudly + instead of drifting silently. Tested in Task 6. +5. **Chunk seams in the Horizons fetch.** The duplicated boundary row is dropped exactly once, with + no gap and no duplicate. Tested in Task 4. + +--- + +## PR 1: prep (`accurate-planets-prep`) + +### Task 1: Orbit trails anchor on the snapshot body position (P1) + +**Files:** `src/services/engine/frame/passes/orbitTrailsPass.ts:96-109` (modify), +`src/data/bodies/orbitalElements.ts:1-9` (header, modify), +`tests/services/engine/frame/passes/orbitTrailsPass.test.ts` (modify) + +**Before → after** (`orbitTrailsPass.ts:105-109`): + +```ts +// before: centre = focus + centerOffset (focus re-read, conic re-derived: a mirror of deriveBodyStates) +// after: centre = state.positionMpc − keplerianPositionMpc(propagated) + centerOffsetMpc +``` + +The body is now on its trail by construction: any later change to the snapshot position moves the +trail with it. Keep `meanAnomalyRad` as the fade anchor, unchanged. + +- [ ] Add the test `trail follows a snapshot position that differs from raw Kepler`. Stub the states + map so one row's `positionMpc` is the Kepler position plus a 1e6 km vector (converted with + `SCALE_UNITS.KM_TO_MPC`). Read the packed eye-relative km basis (floats 34..45, written by + `eyeRelativeOrbitBasisKm`): the body point `C + A·cosE + B·sinE` at the row's propagated E must + equal the stubbed position, eye-relative in km, to ≤ 1 m. Use km tolerances throughout: + 1e-15 Mpc is 30,000 km. +- [ ] Run that test and confirm it fails against the current code. +- [ ] Implement the after-line. Rewrite the `orbitalElements.ts` header sentence about "a body sitting + on its own trail is structural" so it names the snapshot anchoring. +- [ ] The comparisons against `keplerianEllipse` (`orbitTrailsPass.test.ts:345-378`, `:511-573`) were + exact to 18–20 digits. Loosen them to `toBeCloseTo(…, 15)`, or the relative tolerance f64 can + hold for `(a + b) − b`, and give the reason in one line. +- [ ] `npm test -- orbitTrailsPass`, then commit "Orbit trails anchor on the snapshot body position". + +### Task 2: Exact obliquity (P2) + +**Files:** +- `src/data/bodies/orbitPlaneFrames.ts:72` (and the header comments at `:25`, `:29`, `:77-78`) +- `src/utils/orbit/rotationLookAt.ts:19` +- `src/data/defaults.ts:138-141` +- `tests/data/bodies/orbitPlaneFrames.test.ts:50-53` +- `tests/data/orientation/orientationFrames.test.ts:54` +- `tests/utils/orbit/propagateElements.test.ts:94` +- `tests/services/engine/frame/earthTerminator.test.ts:17` +- `tests/fixtures/bodyStatesJ2000.json` +- `tests/fixtures/camera/settleGoldenTrace.json` +- `tests/fixtures/camera/driverGoldenTrace.json` +- `tools/perf/perfScenarios.ts:52` + +Set `OBLIQUITY_DEG = 23.4392911` and say "IAU J2000 mean obliquity" in its comment. Comments that +say `23.44°` become `23.439°`. Test literals use the exact value; in `orientationFrames.test.ts:54`, +66.56° becomes `90 − 23.4392911`. No new test: this is a constant, and the existing frame tests pin it. + +- [ ] Change the constant, comments and test literals. +- [ ] Regenerate `bodyStatesJ2000.json` with a one-off tsx snippet that is not committed. It + serialises `deriveBodyStates(2451545.0)` for every `ORBITAL_ELEMENTS` id in the fixture's + existing shape (`positionMpc`, `orientation`, `meanAnomalyRad`), leaves anchors out, and keeps + the key order. Diff it: only rotated values may change. +- [ ] Re-record the camera traces with `SETTLE_GOLDEN_RECORD=1` and `DRIVER_GOLDEN_RECORD=1` + (`npm test -- settleGoldenTrace driverGoldenTrace`). The spec rules this. Then run once more + without the flags. +- [ ] Update `EARTH_TARGET` in `perfScenarios.ts:52` to the new J2000 Earth position, taken from the + regenerated fixture. +- [ ] `npm run typecheck:fast`, then `npm test -- orbitPlaneFrames orientationFrames propagateElements earthTerminator deriveBodyStates`. + Commit "Exact J2000 obliquity". + +--- + +## PR 2: feature (`accurate-planets`, stacked on prep) + +### Task 3: Correction type and evaluator + +**Files:** +- `src/@types/scene/EphemerisCorrection.d.ts` (create) +- `src/utils/orbit/ephemerisCorrectionMpc.ts` (create) +- `tests/utils/orbit/ephemerisCorrectionMpc.test.ts` (create) + +**Interfaces, produced:** + +```ts +export type EphemerisCorrection = { + startJd: number; + endJd: number; // fitted span (UTC JD); outside it the edge value is held + polyKm: readonly [Vec3, Vec3, Vec3, Vec3]; // coefficients of τ⁰..τ³, τ = 2(t − startJd)/(endJd − startJd) − 1 + terms: readonly number[]; // flat, 7 per term: ω (rad/day), cos x,y,z (km), sin x,y,z (km); phase ω·(t − startJd) +}; +export function ephemerisCorrectionMpc(c: EphemerisCorrection, simDays: number): Vec3; +``` + +Sum in f64 km, then multiply by `SCALE_UNITS.KM_TO_MPC`. + +- [ ] Test `evaluates polynomial and terms at a known instant`: a hand-built two-term correction, + checked against values worked out by hand. +- [ ] Test `holds the edge value outside the span`: `simDays` 100 years before `startJd` returns + exactly (`toEqual`) the value at `startJd`, and likewise at `endJd`. +- [ ] Test `is continuous across both span edges`: evaluations at edge ± 1e-6 day differ by < 1 m. +- [ ] Implement, `npm test -- ephemerisCorrectionMpc`, then commit "Ephemeris correction evaluator". + +### Task 4: Horizons planet fetcher + +**Files:** +- `tools/parsers/horizonsVectorsCsv.ts` (create) +- `tests/tools/parsers/horizonsVectorsCsv.test.ts` (create) +- `tools/fetch/fetchHorizonsPlanets.ts` (create) +- `tools/utils/io/rawDataRegistry.ts` (add `horizons.planets` and `horizons.planets.readme`, using the + entry shape at `:17-25`) +- `data/raw/horizons/planets/README.md` (create, committed; it records the exact query in Global + constraints) +- `package.json` (add `"fetch-horizons-planets": "tsx tools/fetch/fetchHorizonsPlanets.ts"`) +- `docs/DATA.md` (add a Horizons entry wherever the fetchers are listed) + +**Interfaces, produced:** + +```ts +export type HorizonsVectorRow = { jd: number; xKm: number; yKm: number; zKm: number }; +export function parseHorizonsVectorsCsv(result: string): HorizonsVectorRow[]; // rows between $$SOE/$$EOE +``` + +The output goes to `data/raw/horizons/planets/.csv` with the columns `jd,x_km,y_km,z_km`, +written through `rawDataPath('horizons.planets')`. Chunks are concatenated and a row whose `jd` is ≤ +the previous row's is dropped (Review focus 5). + +- [ ] Test `parses the SOE/EOE block of a Horizons vectors result`: use a trimmed real response + pasted as a string literal, three rows. +- [ ] Test `concatenating two chunks drops the shared boundary row once`, through a small exported + merge step, or by asserting the fetcher's `mergeChunks` if you extract it. Keep it + one-symbol-per-file if extracted. +- [ ] Implement the parser and the fetcher, which writes all eight CSVs. Announce the download size + before running it: 8 × ~3 MB. +- [ ] Run `npm run fetch-horizons-planets`. Commit "Horizons planet fetcher", without the gitignored + CSVs. + +### Task 5: Fit tool and generated corrections · `review: yes` + +**Files:** +- `tools/utils/math/fft.ts` (create) +- `tools/utils/math/fitSinusoidSeries.ts` (create) +- `tests/tools/utils/math/fft.test.ts` (create) +- `tests/tools/utils/math/fitSinusoidSeries.test.ts` (create) +- `tools/bodies/buildPlanetEphemeris.ts` (create, using the `tools/bodies/buildPlanetFacts.ts` + template: banner, prettier, CLI guard) +- `src/data/bodies/planetEphemerisCorrections.generated.ts` (generated) +- `package.json` (add `"build-planet-ephemeris": "tsx tools/bodies/buildPlanetEphemeris.ts"`) + +**Interfaces, produced:** + +```ts +export function fft(re: Float64Array, im: Float64Array): void; // in-place radix-2, length a power of two +export type FitResult = { polyKm: [Vec3, Vec3, Vec3, Vec3]; terms: number[]; maxErrKm: number }; +export function fitSinusoidSeries(tJd: Float64Array, residualKm: [Float64Array, Float64Array, Float64Array], + startJd: number, endJd: number, stopKm: number, maxTerms: number): FitResult; +export const PLANET_EPHEMERIS_CORRECTIONS: Readonly>; // keys: planet ids +``` + +**Method.** The spike is `planetSeriesFit.mts` in the brainstorm scratchpad; it measured ~590 terms. +1. Fit the cubic in τ first. +2. Then loop: + - take the FFT of the residual, zero-padded ×8, with power summed over x, y and z; + - find the peak and refine it parabolically; + - add a cos column and a sin column; + - Gram–Schmidt each new column against the kept ones, which makes it an exact least-squares refit; + - stop when the max 3D error is ≤ `stopKm`. +3. Recover the coefficients of the original basis from the orthogonal one. Keep R, or refit by normal + equations once at the end, so the generated `terms` are plain (ω, cos, sin) amplitudes. +4. The tool computes the residual as `Horizons − keplerianPositionMpc(propagateElements(row, jd))`. + It converts km with the same `MPC_TO_KM` the app uses, imports the `src/` modules directly, and + uses the app's own element rows. + +**Sample steps (days) for fitting:** Mercury 2, Venus 4, Earth 4, Mars 6, Jupiter through Neptune 10. +The tool then **verifies on every 1-day row** with `ephemerisCorrectionMpc` (from Task 3) and throws +if any planet exceeds 1,000 km. That way the shipped evaluator is exactly what gets verified. + +- [ ] Test `fft finds a pure sinusoid's bin`: N = 64, a sinusoid in bin 5, and the power peaks at k = 5. +- [ ] Test `fitSinusoidSeries recovers a synthetic two-term signal`: a cubic plus two sinusoids at + non-bin frequencies. With `stopKm` 1, it should use ≤ 4 terms, and evaluating the result through + `ephemerisCorrectionMpc` reproduces the signal to ≤ 1 km. +- [ ] Implement, then run `npm run build-planet-ephemeris`. Report each planet's term count and + verified max error; expect ~35–180 terms and a total of ~17 kB of numbers. +- [ ] Commit "Planet ephemeris fit tool + generated corrections". + +### Task 6: Wire corrections and the Earth–Moon pair into `deriveBodyStates` · `review: yes` + +**Files:** +- `src/@types/scene/BarycentricPair.d.ts` (create) +- `src/data/bodies/barycentricPairs.ts` (create) +- `src/services/engine/frame/deriveBodyStates.ts:60-68` (modify; the header gains one clause) +- `src/data/bodies/scenePlanets.ts:3` (fix the stale "via keplerianPositionMpc" header) +- `tests/fixtures/horizonsPlanets.json` (create) +- `tests/data/bodies/planetEphemeris.test.ts` (create) +- `tests/services/engine/frame/deriveBodyStates.test.ts` (modify) +- `tests/fixtures/bodyStatesJ2000.json` +- `tests/fixtures/camera/{settle,driver}GoldenTrace.json` +- `tools/perf/perfScenarios.ts:52` +- `docs/backlog/2026-08-16-barycentric-orbit-pairs.md` +- `docs/BACKLOG.md` (the "Barycentric orbit pairs" line) + +**Interfaces:** + +```ts +export type BarycentricPair = { primaryId: string; secondaryId: string; secondaryMassFraction: number }; +export const BARYCENTRIC_PAIRS: readonly BarycentricPair[]; // [{ primaryId: 'earth', secondaryId: 'moon', secondaryMassFraction: 1 / 82.30057 }] +``` + +**1b loop, after this task:** +`position = focus + kepler(el) + correction(el.id) − k · kepler(propagate(secondaryRow))`. +- `correction` is `ephemerisCorrectionMpc(PLANET_EPHEMERIS_CORRECTIONS[el.id], simDays)` when a + correction exists, and zero otherwise. +- The reflex term applies only when `el.id` is a pair's `primaryId`. +- The secondary row comes from `ORBITAL_ELEMENTS` and is propagated inline, because `FOCUS_ORDER` + resolves `earth` before `moon`. +- Resolve pair and correction lookups into id-keyed maps at module load, beside `FOCUS_ORDER`, never + per frame. + +**Fixture.** `tests/fixtures/horizonsPlanets.json` uses the Global constraints query, except with +target `399` (Earth centre) for Earth. +- Dates: 1900-06-01, 1979-03-05, 1980-11-12, 1986-01-24, 1989-08-25 and 2099-06-01 (Voyager flybys + plus both ends). +- Shape: `{ [id]: { [jd]: [xKm, yKm, zKm] } }`. +- The test header records the query URL. + +- [ ] Test `every planet is within 1,000 km of Horizons at the fixture dates`, from `deriveBodyStates` + positions. Earth is compared with Horizons `399` and gets its own tolerance: measure the max, + round up to the next 500 km, and write the measured number in a one-line comment, since it + includes the app Moon's error. This test is Review focus 4. +- [ ] Test `Earth–Moon reflex keeps the barycentre and the separation`. At three dates, with + k = 1/82.30057, (1−k)·Earth + k·Moon equals the corrected EMB (Earth-row Kepler plus the + correction) to ≤ 1 m. Moon − Earth equals the Moon's raw Kepler offset to ≤ 1 m. +- [ ] Test `an Earth surface site keeps its offset from the wobbling Earth` (Review focus 2): a site + hosted on `earth` minus Earth's position is independent of the reflex. Compare with the site + offset computed from `sitePointBodyFixed` and Earth's orientation. +- [ ] Test `Moon, Hubble and Saturn sit on their trails at a non-J2000 date` (Review focus 3): drive + `orbitTrailsPass` packing at a 1989 date. Each body's position lies on its packed conic at its + propagated E, to ≤ 1 m, reading the eye-relative km basis as Task 1's test does. +- [ ] Implement the pair table and the 1b term. Fix the `scenePlanets.ts:3` header. +- [ ] Regenerate `bodyStatesJ2000.json` with Task 2's one-off snippet. Re-record the golden traces + with the record flags, then rerun without them. Update `EARTH_TARGET`. +- [ ] Backlog: in the detail file, add that `BARYCENTRIC_PAIRS` exists, so Pluto's wobble is one row, + while the minor moons still need the invisible node. Update the BACKLOG line's clause to match. +- [ ] `npm run typecheck:fast`, then `npm test -- planetEphemeris deriveBodyStates orbitTrailsPass`. + Commit "Accurate planet positions: corrections + Earth–Moon pair". + +--- + +## Dispatch grouping (controller) + +| PR | Dispatch | Tasks | Model | +|---|---|---|---| +| 1 | A, or inline (< 50 lines of source) | 1, 2 | Sonnet | +| 2 | B | 3, 4, 5 (the tool chain; Task 5 has `review: yes`) | Opus | +| 2 | C | 6 (`review: yes`) | Opus | + +Ask at plan start: parallelism, and whether to run the perf gate. Recommended: no perf gate, because +the per-frame cost is about 590 sinusoids per new `simDays` and trails add nothing. + +## Definition of done + +**Deliverables:** +- `PLANET_EPHEMERIS_CORRECTIONS` (generated) +- `ephemerisCorrectionMpc` +- `BARYCENTRIC_PAIRS` +- `npm run fetch-horizons-planets` and `npm run build-planet-ephemeris` +- `OBLIQUITY_DEG = 23.4392911` +- trails anchored on the snapshot + +**Manual smoke** (the user looks; the dev server stays running): +- The clock set to 1980-11-12: Saturn is where Horizons puts it, and the inspector's position + matches JPL to within ~1,000 km. +- Zoom close to Saturn and Jupiter with time running fast: each stays on its trail. +- Park at Earth with a 1-day/s clock: the Earth–Moon wobble is visible against the trail, and the + Moon stays on its trail. +- Set the clock to 1850 and to 2200: the planets are finite, there is no jump as the clock crosses + 1900 and 2100, and the trails are intact. + +**Out of scope (deferral boundary):** +- moon accuracy +- the Pluto–Charon pair row and Pluto's minor moons +- spacecraft and Voyager (the mission-trails spec is next) +- runtime TDB conversion +- any margin in `orbitReachByRegion` diff --git a/docs/superpowers/specs/2026-10-04-accurate-planet-positions-design.md b/docs/superpowers/specs/2026-10-04-accurate-planet-positions-design.md new file mode 100644 index 000000000..9ec3c8750 --- /dev/null +++ b/docs/superpowers/specs/2026-10-04-accurate-planet-positions-design.md @@ -0,0 +1,188 @@ +# Accurate planet positions — design spec + +Child of the Voyager mission-trails brainstorm (2026-09-28 → 2026-10-03). Voyager's flybys +are only as honest as the planets they pass: today's Keplerian planets drift from JPL by +98k km at Jupiter up to 1.6M km at Saturn at the flyby dates, 9–16× the miss distances. On +2026-10-03 the user ruled that **the accurate physical planet position is the source of +truth**, independent of any flyby window, and that it ships as its own spec and PR before the +mission-trails spec. + +## 1. What this is + +Heliocentric positions of the eight planets match JPL DE (Horizons) to **≤ 1,000 km over +1900–2100**. That is 3% of the closest planet flyby (Voyager 2 at Neptune, ~29,000 km from +centre). Four parts: + +- **A correction series per planet**, added on top of today's Keplerian position. It is a + cubic polynomial plus fitted sinusoids, generated offline from Horizons and shipped as + constants. All eight planets together take ~590 terms, about 17 kB. A build tool fits it + and checks it against JPL. +- **Earth wobbles around the Earth–Moon barycentre (EMB).** The `earth` element row already + describes the EMB, so Earth = EMB − k·(Moon's geocentric offset), with k the Moon's share of + the pair's mass. +- **Orbit trails follow the corrected body.** Each trail is the same Keplerian conic, + translated so it passes through the body's snapshot position. +- **Exact obliquity.** `OBLIQUITY_DEG` becomes 23.4392911° instead of the rounded 23.44°. + +**Out of scope:** +- Moon accuracy. Moons keep their own element rows. +- Pluto–Charon. That becomes one more pair row later (§6). +- Spacecraft. Mission trails is the next spec and reuses the fetch tool. +- Any runtime TDB time scale. + +## 2. Ground preparation + +Refactor-ground ran on 2026-10-03. It sent an Explore trace over every reader of a planet's +position and over the trail pass, and ran a greenfield cross-check. The user signed off on the +checkpoint. + +| Touchpoint | Verdict | Seam | +|---|---|---| +| Body position (`deriveBodyStates.ts:63-67`) | growth | one added term in the 1b loop; it is the only place positions are composed | +| Orbit trail (`orbitTrailsPass.ts:101-109`) | **bolt-on → P1** | the pass re-propagates each conic and re-folds the focus itself, a mirror of `deriveBodyStates`. The body sits on its trail only because both happen to agree, so adding the correction here would make a second copy | +| Obliquity (`orbitPlaneFrames.ts:72`) | **wrong constant → P2** | the fit is generated against the app's frame, so the constant must be right before the first generation | +| Earth wobble | growth | a new pair table read in the same 1b loop. No focus-graph node: the backlog's invisible-node design is needed only for Pluto's minor moons | +| Synchronous readers at module load (`bodyRegions.ts:29`, `starCatalog/create.ts:63`) | growth | the generated `.ts` is a static import, as with `bodyFacts.generated.ts` | +| Generated data | growth | `tools/bodies/buildPlanetFacts.ts` precedent: banner, prettier, committed output | + +**The greenfield cross-check diverged on three points, all resolved by measurement or ruling:** +- **Residual table → fitted series.** A table needs ~140 kB in f32 (Mercury needs a 13-day + step); the series needs ~17 kB. +- **Sampled trail with a geometric tail → translated conic.** With a translated conic the + worst case is Saturn: 4 px with the whole orbit 1,000 px wide, and 0.34° tangent error close + up. Mercury through Earth stay under 0.1 px. +- **Spacecraft in planet-relative legs → rejected.** With accurate planets, heliocentric + samples are enough. That decision belongs to the mission-trails spec. + +**Prep, as a separate PR (ruled 2026-10-04):** + +- **P1: trails anchor on the snapshot body.** `orbitTrailsPass` takes + `centreMpc = state.positionMpc − keplerianPositionMpc(propagated) + centerOffsetMpc` instead + of `focus + centerOffsetMpc`. + - This is behaviour-neutral today, apart from f64 rounding. + - Once this lands, the body sits on its own trail by construction rather than by coincidence. + - Any later change to a body's position (correction, pair reflex, a moon's parent) moves the + trail with it. + - The 18–20-digit trail tests (`orbitTrailsPass.test.ts:345-378`, `:511-573`) move to a + tolerance that f64 can actually hold. + - The `orbitalElements.ts` header line about the invariant is rewritten to name the new + mechanism. +- **P2: exact obliquity.** `OBLIQUITY_DEG = 23.4392911` (IAU 1980 / J2000 mean obliquity). + - The obliquity literals in tests follow: `orbitPlaneFrames.test.ts:53`, + `orientationFrames.test.ts:54`, and the comments in `propagateElements.test.ts:94` and + `earthTerminator.test.ts:17`. So do the `23.44°` comments in `rotationLookAt.ts` and + `defaults.ts`. + - The fixture `tests/fixtures/bodyStatesJ2000.json` and the camera golden traces are + re-recorded, under this ruling, with `SETTLE_GOLDEN_RECORD=1` and `DRIVER_GOLDEN_RECORD=1`. + +## 3. Data shapes + +```ts +// src/@types/scene/EphemerisCorrection.d.ts — one planet's DE − Kepler correction, km +export type EphemerisCorrection = { + startJd: number; endJd: number; // fitted span; outside it the edge value is held + polyKm: readonly [Vec3, Vec3, Vec3, Vec3]; // cubic in τ = 2(t−start)/(end−start) − 1 + // flat (ω rad/day, cos x,y,z, sin x,y,z) per term; phase measured from startJd + terms: readonly number[]; +}; + +// src/@types/scene/BarycentricPair.d.ts — the primary's element row describes the pair barycentre +export type BarycentricPair = { primaryId: string; secondaryId: string; secondaryMassFraction: number }; + +// src/data/bodies/planetEphemerisCorrections.generated.ts (GENERATED — DO NOT EDIT) +export const PLANET_EPHEMERIS_CORRECTIONS: Readonly>; + +// src/data/bodies/barycentricPairs.ts +export const BARYCENTRIC_PAIRS: readonly BarycentricPair[] = + [{ primaryId: 'earth', secondaryId: 'moon', secondaryMassFraction: 1 / 82.30057 }]; +``` + +**Frame and time are pinned by construction, not converted at runtime.** +- **Frame.** The fit tool computes `DE − app Kepler` in the app's own equatorial frame, so any + residual frame bias is absorbed into the correction. +- **Time.** Horizons is fetched with `TIME_TYPE='UT'`, which is verified to be accepted for + vector tables, so the TDB−UTC gap (~69 s, ~2,000 km at Earth) is absorbed as well. `simDays` + stays a UTC Julian date. +- **Planet targets.** Mercury and Venus use their planet centres (199, 299). Earth through + Neptune use their system barycentres (3–8), because the planet-centre wobble caused by moons + is ≤ 300 km. + +## 4. Runtime + +- **`src/utils/orbit/ephemerisCorrectionMpc.ts`** — + `(c: EphemerisCorrection, simDays: number) => Vec3`. It clamps `simDays` to + `[startJd, endJd]` (the ruled freeze-at-edge), sums the polynomial and the terms in f64 km, + then converts to Mpc. The conversion happens before the sum joins an au-scale position, as in + the site-offset precedent at `deriveBodyStates.ts:88-94`. +- **`deriveBodyStates` 1b:** + `position = focus + kepler(el) + correction(el.id) − pairReflex(el.id)`. + - `pairReflex` is `secondaryMassFraction · keplerianPositionMpc(propagateElements(secondary))` + for a primary listed in `BARYCENTRIC_PAIRS`. + - The secondary's offset is computed inline, because `FOCUS_ORDER` resolves `earth` before + `moon`. + - Dependants of `earth` (the Moon, Hubble, the easter eggs, Earth sites) follow the + reflexed Earth automatically. So Moon = EMB + (1−k)·geo, which is physically right. +- **Cost:** about 590 sinusoid evaluations per new `simDays`, a few µs. The existing one-deep + memo makes paused frames free. Trails add nothing, because P1 reads the snapshot. + +## 5. Tools + +- **`tools/fetch/fetchHorizonsPlanets.ts`** (`npm run fetch-horizons-planets`). + - Fetches 1-day equatorial heliocentric vectors, 1900–2100, in 50-year chunks with retry. + - Writes them to `data/raw/horizons/planets/.csv`. + - The output is gitignored, registered in `rawDataRegistry.ts` with a README next to it + that records the exact API query. +- **`tools/bodies/buildPlanetEphemeris.ts`** (`npm run build-planet-ephemeris`). + - Subtracts the app's Kepler position using the `src/` propagation, imported as + `buildPlanetFacts` imports `src` types. + - Fits each planet greedily: cubic polynomial, then sinusoids. Each frequency is the + zero-padded FFT peak of the remaining residual, and a Gram–Schmidt step refits all + coefficients exactly by least squares. + - Stops once a planet is ≤ 900 km, leaving margin. + - **Verifies every planet on the dense 1-day grid** and throws above 1,000 km. + - Writes the generated file with a banner plus prettier. + - Helpers go one function per file under `tools/utils/math/` (`fft.ts`, + `fitSinusoidSeries.ts`). +- **Regeneration rule:** any edit to a planet's element row or to the frame requires + re-running the build tool. The test in §7 fails until it is re-run. + +## 6. Docs and backlog + +- `scenePlanets.ts:3`: rewrite the stale "positions via keplerianPositionMpc" header. +- `docs/backlog/2026-08-16-barycentric-orbit-pairs.md`: note that the pair table now exists. + Pluto's wobble becomes one `BARYCENTRIC_PAIRS` row; the minor moons still need the invisible + node. The BACKLOG line stays and its clause is updated. +- `docs/DATA.md`: add a Horizons fetch entry. +- `orbitReachByRegion` gets no margin for the correction. ≤ 8M km is negligible against + Pluto's ~50 au reach. Recorded here, not backlogged. + +## 7. Tests + +- **`tests/data/bodies/planetEphemeris.test.ts`.** + - Fixture: a committed Horizons fixture, `tests/fixtures/horizonsPlanets.json`, with 6 dates + × 8 planets, UT, equatorial. + - `deriveBodyStates` must be ≤ 1,000 km from each fixture vector. + - It fails if anyone edits a planet's elements or the frame without regenerating. + - Earth is checked against Horizons 399 (Earth centre) to ≤ 1,000 km plus the Moon-model + error. The tolerance is stated in the test after measurement. +- **`ephemerisCorrectionMpc.test.ts`:** a clamped date returns exactly the edge value + (freeze), with continuity across `startJd` and `endJd`. +- **Pair reflex:** + - Earth–Moon distance is unchanged. + - The EMB, computed as (1−k)·Earth + k·Moon, equals the old Earth position. +- **Re-recorded:** + - `tests/fixtures/bodyStatesJ2000.json` + - the camera golden traces + - `tools/perf/perfScenarios.ts:52` `EARTH_TARGET` + - the 2024-04-08 eclipse test keeps its 2° tolerance, and should tighten. + +## 8. PR sequence + +1. **Prep PR** (separate, ruled): P1 and P2, each its own commit. They are behaviour-neutral + except for the obliquity. +2. **Feature PR**, built on top of it: + - fetch tool and raw-registry entry + - fit tool and helpers + - generated corrections + - pair table and the `deriveBodyStates` term + - tests, docs and backlog edits From 2183ed6b7abe6bf931857133a4d3074bba1302e1 Mon Sep 17 00:00:00 2001 From: Alexander Rulkens Date: Sun, 4 Oct 2026 09:26:29 +0200 Subject: [PATCH 02/14] Orbit trails anchor on the snapshot body position --- src/data/bodies/orbitalElements.ts | 4 +- .../engine/frame/passes/orbitTrailsPass.ts | 17 +++-- .../frame/passes/orbitTrailsPass.test.ts | 68 +++++++++++++++++++ 3 files changed, 80 insertions(+), 9 deletions(-) diff --git a/src/data/bodies/orbitalElements.ts b/src/data/bodies/orbitalElements.ts index 024a69fb8..bdd15c2da 100644 --- a/src/data/bodies/orbitalElements.ts +++ b/src/data/bodies/orbitalElements.ts @@ -1,7 +1,7 @@ /** * orbitalElements — the J2000 Keplerian table, single source of truth for BOTH body - * positions (`keplerianPositionMpc`) and their trails (`keplerianEllipse`), so a body sitting - * on its own trail is structural rather than an invariant to remember. Per row: `focusId` (the + * positions (`keplerianPositionMpc`) and their trails (`keplerianEllipse`); the trail pass + * anchors each trail on the snapshot body position, so a body sits on its own trail by construction. Per row: `focusId` (the * Sun for planets, the parent planet for a moon) and `plane` (omitted = ecliptic, which is how * JPL publishes the planets and the Moon; a planet's own moons ride their OWN Laplace pole — * see `orbitPlaneFrames.ts`). No buried Mpc/radian literals, and JPL's `L`/`ϖ` columns are diff --git a/src/services/engine/frame/passes/orbitTrailsPass.ts b/src/services/engine/frame/passes/orbitTrailsPass.ts index dd77c0d80..5ff7f1fb1 100644 --- a/src/services/engine/frame/passes/orbitTrailsPass.ts +++ b/src/services/engine/frame/passes/orbitTrailsPass.ts @@ -15,6 +15,7 @@ import { ORBIT_REACH_BY_REGION } from '../../../../data/bodies/orbitReachByRegio import { CULL_PX, FULL_PX } from '../../../../data/bodies/orbitTrailConstants'; import { regionRelativeDistanceMpc } from '../../../../utils/regions/regionRelativeDistanceMpc'; import { propagateElements } from '../../../../utils/orbit/propagateElements'; +import { keplerianPositionMpc } from '../../../../utils/orbit/keplerianPositionMpc'; import { keplerianEllipse } from '../../../../utils/orbit/keplerianEllipse'; import { composeOrbitConic } from '../../../../utils/camera/composeOrbitConic'; import { sampledDepthBinding } from '../sampledDepthBinding'; @@ -96,17 +97,19 @@ export const orbitTrailsPass: ContentPass = { for (let i = 0; i < limit; i++) { const elements = rows[i]!; // Re-derived at the frame instant, never baked. `keplerianEllipse` returns - // FRESH vectors per call, so the in-place focus fold below cannot alias a + // FRESH vectors per call, so the in-place anchoring below cannot alias a // shared scratch across orbits. const propagated = propagateElements(elements, ctx.snapshot.simDays); const { centerOffsetMpc, semiMajorMpc, semiMinorMpc } = keplerianEllipse(propagated); - // The snapshot seeds anchors (the Sun) alongside every element row, so a - // heliocentric focus and a moving parent are the same lookup. - const focus = states.get(elements.focusId)!.positionMpc; + // Centre = the snapshot body position, less its raw Kepler offset, plus the + // ellipse centre offset: the body sits on its trail by construction, so any + // later correction to the snapshot position moves the trail with it. + const bodyMpc = states.get(elements.id)!.positionMpc; + const keplerMpc = keplerianPositionMpc(propagated); const centerMpc = centerOffsetMpc; - centerMpc[0] += focus[0]; - centerMpc[1] += focus[1]; - centerMpc[2] += focus[2]; + centerMpc[0] += bodyMpc[0] - keplerMpc[0]; + centerMpc[1] += bodyMpc[1] - keplerMpc[1]; + centerMpc[2] += bodyMpc[2] - keplerMpc[2]; const dx = centerMpc[0] - camPos[0]; const dy = centerMpc[1] - camPos[1]; diff --git a/tests/services/engine/frame/passes/orbitTrailsPass.test.ts b/tests/services/engine/frame/passes/orbitTrailsPass.test.ts index 683b3b32e..0ad439b24 100644 --- a/tests/services/engine/frame/passes/orbitTrailsPass.test.ts +++ b/tests/services/engine/frame/passes/orbitTrailsPass.test.ts @@ -33,6 +33,7 @@ import { ORBITAL_ELEMENTS } from '../../../../../src/data/bodies/orbitalElements import { CORE_TRAIL_ELEMENTS } from '../../../../../src/data/bodies/coreTrailElements'; import { SCENE_ANCHORS } from '../../../../../src/data/bodies/sceneAnchors'; import { deriveBodyStates } from '../../../../../src/services/engine/frame/deriveBodyStates'; +import { eccentricAnomalyFromMean } from '../../../../../src/utils/orbit/eccentricAnomalyFromMean'; import { propagateElements } from '../../../../../src/utils/orbit/propagateElements'; import { keplerianEllipse } from '../../../../../src/utils/orbit/keplerianEllipse'; import { keplerianPositionMpc } from '../../../../../src/utils/orbit/keplerianPositionMpc'; @@ -76,6 +77,21 @@ vi.mock('../../../../../src/utils/camera/composeOrbitConic', () => ({ })); import { composeOrbitConic } from '../../../../../src/utils/camera/composeOrbitConic'; +// Lets one test hand the pass a snapshot that differs from raw Kepler; unset, the +// real map passes through untouched. +const snapshotOverride = vi.hoisted(() => ({ states: undefined as unknown })); +vi.mock('../../../../../src/services/engine/frame/sceneBodyStates', async (importOriginal) => { + const actual = + await importOriginal< + typeof import('../../../../../src/services/engine/frame/sceneBodyStates') + >(); + return { + sceneBodyStates: ((...args: Parameters) => + (snapshotOverride.states as ReturnType | undefined) ?? + actual.sceneBodyStates(...args)) as typeof actual.sceneBodyStates, + }; +}); + const composeMock = composeOrbitConic as unknown as ReturnType; // The J2000 oracle `SCENE_ORBIT_CONICS` used to provide, hand-derived here with @@ -585,6 +601,58 @@ describe('orbitTrailsPass.draw', () => { expect(drift).toBeGreaterThan(1e-13); }); + it('trail follows a snapshot position that differs from raw Kepler', () => { + const simDays = CONST_J2000 + 100; + const real = deriveBodyStates(simDays); + const moonEl = CORE_TRAIL_ELEMENTS.find((e) => e.id === 'moon')!; + const moonState = real.get('moon')!; + // 1e6 km off raw Kepler, asymmetric so a dropped axis shows. + const shiftMpc = [1e6, -4e5, 2.5e5].map((km) => km * SCALE_UNITS.KM_TO_MPC); + const stubbed: Vec3 = [ + moonState.positionMpc[0] + shiftMpc[0]!, + moonState.positionMpc[1] + shiftMpc[1]!, + moonState.positionMpc[2] + shiftMpc[2]!, + ]; + snapshotOverride.states = new Map(real).set('moon', { ...moonState, positionMpc: stubbed }); + try { + // Eye 100 km off the body. The basis is packed in f32, whose ulp at the Moon's + // ~4e5 km semi-major axis is 30 m, so 0.1 km is the tightest honest bound; the + // injected 1e6 km offset is four orders above it. + const eye: Vec3 = [stubbed[0] + 100 * SCALE_UNITS.KM_TO_MPC, stubbed[1], stubbed[2]]; + const ctx = { + snapshot: { + simDays, + focusBlend: 0, + nowMs: 0, + renderTargets: { farDepthView: () => FAR_DEPTH_VIEW_STUB }, + }, + bodyPose: () => null, + drawCamPos: eye, + drawPxPerRad: FIXTURE_PX_PER_RAD, + cam: { distance: 1e-13 }, + } as unknown as FrameView; + const renderer = makeRendererSpy(); + orbitTrailsPass.draw(PASS_STUB, makeNear0View(), ctx, { + ...makeState(renderer), + orbitTrailRows: [moonEl], + } as unknown as EngineState); + + const [, { instances: staging, count }] = renderer.draw.mock.calls[0]!; + expect(count).toBe(1); + const { eccentricity, meanAnomalyRad } = propagateElements(moonEl, simDays); + const eAnom = eccentricAnomalyFromMean(meanAnomalyRad, eccentricity); + const [cosE, sinE] = [Math.cos(eAnom), Math.sin(eAnom)]; + const kmPerMpc = 1 / SCALE_UNITS.KM_TO_MPC; + for (let axis = 0; axis < 3; axis++) { + const bodyKm = + staging[34 + axis]! + staging[38 + axis]! * cosE + staging[42 + axis]! * sinE; + expect(Math.abs(bodyKm - (stubbed[axis]! - eye[axis]!) * kmPerMpc)).toBeLessThan(0.1); + } + } finally { + snapshotOverride.states = undefined; + } + }); + it('stages no conic for the mesh bodies (whale, petunias) even when the Moon trail is emitted', () => { // Same pose as "rides a moon trail on its propagated parent" above — parking // the camera at Earth is what makes the Moon's tiny geocentric orbit survive From 7edf35cb5d6f1118e12162aa17adcce7544cab42 Mon Sep 17 00:00:00 2001 From: Alexander Rulkens Date: Sun, 4 Oct 2026 09:29:05 +0200 Subject: [PATCH 03/14] Exact J2000 obliquity --- src/data/bodies/orbitPlaneFrames.ts | 12 +- src/data/defaults.ts | 4 +- src/utils/orbit/rotationLookAt.ts | 2 +- tests/data/bodies/orbitPlaneFrames.test.ts | 4 +- .../orientation/orientationFrames.test.ts | 4 +- tests/fixtures/bodyStatesJ2000.json | 70 +- tests/fixtures/camera/driverGoldenTrace.json | 1313 +++++++++++++---- tests/fixtures/camera/settleGoldenTrace.json | 769 +--------- .../engine/frame/earthTerminator.test.ts | 2 +- tests/utils/orbit/propagateElements.test.ts | 2 +- tools/perf/perfScenarios.ts | 2 +- 11 files changed, 1097 insertions(+), 1087 deletions(-) diff --git a/src/data/bodies/orbitPlaneFrames.ts b/src/data/bodies/orbitPlaneFrames.ts index 2fc9c2591..5dc8c9f39 100644 --- a/src/data/bodies/orbitPlaneFrames.ts +++ b/src/data/bodies/orbitPlaneFrames.ts @@ -20,13 +20,13 @@ * sits ~15° off Saturn's equator) rides its true plane rather than the shared * equatorial one — hence `planeFrameFromPole` is exported. The ecliptic is * simply the frame of the ecliptic pole, so `planeFrameFromPole` reproduces - * `ECLIPTIC_FRAME` exactly (it equals `planeFrameFromPole(270, 66.56)` to + * `ECLIPTIC_FRAME` exactly (it equals `planeFrameFromPole(270, 66.5607089)` to * floating-point noise); `ECLIPTIC_FRAME` is written out directly from the - * obliquity so the 23.44° has a single, legible source. + * obliquity so the 23.439° has a single, legible source. * * The ecliptic — the plane of Earth's orbit, which every OTHER solar-system * body (the Moon, the planets) also orbits near — is tilted from the scene's - * equatorial xy-plane by Earth's axial tilt, the obliquity ε ≈ 23.44°. The two + * equatorial xy-plane by Earth's axial tilt, the obliquity ε ≈ 23.439°. The two * planes share one line: the equinox, where the Sun crosses the equatorial * plane heading north each spring. That shared line is exactly frame +x, so +x * needs no transform to land in the ecliptic; rotating equatorial +y by ε about @@ -68,14 +68,14 @@ export function planeFrameFromPole(poleRaDeg: number, poleDecDeg: number): Orbit return { xAxis, yAxis, normal }; } -// Earth's axial tilt: the angle between the equatorial and ecliptic planes. -const OBLIQUITY_DEG = 23.44; +// IAU J2000 mean obliquity: the angle between the equatorial and ecliptic planes. +const OBLIQUITY_DEG = 23.4392911; /** * The ecliptic frame: the equinox +x is the shared node, and the y/z axes carry * the obliquity ε — `yAxis` is equatorial +y rotated by ε about +x, `normal` is * equatorial +z rotated the same. Written directly from `OBLIQUITY_DEG` so the - * 23.44° has one source. + * 23.439° has one source. */ const eps = degToRad(OBLIQUITY_DEG); export const ECLIPTIC_FRAME: OrbitPlaneFrame = { diff --git a/src/data/defaults.ts b/src/data/defaults.ts index 101f150d2..fbbc6e769 100644 --- a/src/data/defaults.ts +++ b/src/data/defaults.ts @@ -135,9 +135,9 @@ export const DEFAULT_FOV_DEG = 60; * * Ecliptic, not equatorial: the descent lands in the solar system, and the * ecliptic frame puts Earth's orbital plane flat so the planets read as a disk - * and Earth's 23.44° obliquity is *desired* — the tilt between the equatorial + * and Earth's 23.439° obliquity is *desired* — the tilt between the equatorial * and ecliptic poles is exactly what makes the seasons legible in that view. * Booting equatorial would instead flatten Earth's equator and rake the orbital - * plane at that same 23.44°, which is the wrong "up" for the arrival scene. + * plane at that same 23.439°, which is the wrong "up" for the arrival scene. */ export const DEFAULT_ORIENTATION: OrientationFrameId = 'ecliptic'; diff --git a/src/utils/orbit/rotationLookAt.ts b/src/utils/orbit/rotationLookAt.ts index fa395fe08..861d3fff1 100644 --- a/src/utils/orbit/rotationLookAt.ts +++ b/src/utils/orbit/rotationLookAt.ts @@ -16,7 +16,7 @@ import { mat3FromColumns } from '../math/mat3FromColumns'; import { normalize3 } from '../math/normalize3'; // Positions are equatorial-world, so the ecliptic pole is the obliquity-tilted -// frame normal rather than +z; `ECLIPTIC_FRAME` owns the 23.44°. +// frame normal rather than +z; `ECLIPTIC_FRAME` owns the 23.439°. // Below this residual length (≈ the boresight-to-pole angle in radians) the // projection has no direction left to carry the roll, so a second, always // non-parallel reference takes over — the equinox, the frame's own +X. diff --git a/tests/data/bodies/orbitPlaneFrames.test.ts b/tests/data/bodies/orbitPlaneFrames.test.ts index 39aee8f5e..89ae6b0ce 100644 --- a/tests/data/bodies/orbitPlaneFrames.test.ts +++ b/tests/data/bodies/orbitPlaneFrames.test.ts @@ -47,9 +47,9 @@ describe('orbit plane frames', () => { it('tilts the ecliptic normal from frame +z by the obliquity', () => { // Both are unit vectors, so the dot product is cos(angle) directly. The - // expected obliquity is written as the literal 23.44° in radians rather than + // expected obliquity is written as the literal 23.4392911° in radians rather than // through degToRad, so a broken conversion would not hide behind a mirror. const cosAngle = dot(ECLIPTIC_FRAME.normal, [0, 0, 1]); - expect(Math.acos(cosAngle)).toBeCloseTo((23.44 * Math.PI) / 180, 12); + expect(Math.acos(cosAngle)).toBeCloseTo((23.4392911 * Math.PI) / 180, 12); }); }); diff --git a/tests/data/orientation/orientationFrames.test.ts b/tests/data/orientation/orientationFrames.test.ts index 588230975..787bdd6ed 100644 --- a/tests/data/orientation/orientationFrames.test.ts +++ b/tests/data/orientation/orientationFrames.test.ts @@ -51,8 +51,8 @@ describe('ORIENTATION_FRAMES', () => { }); it('ecliptic pole matches the obliquity pole from planeFrameFromPole', () => { - // 66.56° = 90° − 23.44°: the ecliptic north pole in equatorial RA/Dec. - const pole = planeFrameFromPole(270, 66.56).normal; + // 66.5607089° = 90° − 23.4392911°: the ecliptic north pole in equatorial RA/Dec. + const pole = planeFrameFromPole(270, 90 - 23.4392911).normal; const mid = col(ORIENTATION_FRAMES.ecliptic, 1); expect(mid[0]).toBeCloseTo(pole[0], 4); expect(mid[1]).toBeCloseTo(pole[1], 4); diff --git a/tests/fixtures/bodyStatesJ2000.json b/tests/fixtures/bodyStatesJ2000.json index 260236bb7..938fd8f0a 100644 --- a/tests/fixtures/bodyStatesJ2000.json +++ b/tests/fixtures/bodyStatesJ2000.json @@ -1,6 +1,6 @@ { "callisto": { - "positionMpc": [1.9395074114394832e-11, 1.3353048102062752e-11, 5.254328189844694e-12], + "positionMpc": [1.9395074114394832e-11, 1.3353112790826594e-11, 5.254163647580489e-12], "orientation": [ -0.20189790956390807, -0.8856344217249613, -0.4181972084670308, 0.9793604914627385, -0.1867008108423695, -0.07743277725010955, -0.009500625010351567, -0.42519933947114147, @@ -9,7 +9,7 @@ "meanAnomalyRad": 1.5254177662430441 }, "charon": { - "positionMpc": [-4.791450444930355e-11, -1.3566802983502213e-10, -2.790299055313828e-11], + "positionMpc": [-4.791450444930355e-11, -1.3566837505179883e-10, -2.7901311982952056e-11], "orientation": [ 0.3334893355513799, 0.43442810728528536, 0.8366941392611901, 0.65509112052934, 0.5314492493346875, -0.5370449880458906, -0.6779679099169169, 0.7272096774535675, @@ -18,24 +18,24 @@ "meanAnomalyRad": 5.307546255314756 }, "deimos": { - "positionMpc": [6.7424822969448034e-12, 6.384457595165361e-15, -1.795630494941746e-13], + "positionMpc": [6.7424822969448034e-12, 6.3822415228828266e-15, -1.7956313479591653e-13], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 3.5779249665883754 }, "dione": { - "positionMpc": [3.1097913153309065e-11, 2.9839498609464e-11, 1.0980813348118515e-11], + "positionMpc": [3.1097913153309065e-11, 2.983963447795244e-11, 1.0980444303639635e-11], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 3.7000980142279785 }, "earth": { - "positionMpc": [-8.589504546518782e-13, 4.302223432556485e-12, 1.865304005019885e-12], + "positionMpc": [-8.589504546518782e-13, 4.302246510960393e-12, 1.8652507750193955e-12], "orientation": [ 0.1761742596326785, -0.9843589945964214, 0, 0.9843589945964214, 0.1761742596326785, 0, 0, 0, 1 ], "meanAnomalyRad": -0.04316391697638581 }, "enceladus": { - "positionMpc": [3.1104454125073664e-11, 2.982129424379336e-11, 1.0981597288866233e-11], + "positionMpc": [3.1104454125073664e-11, 2.9821430112281805e-11, 1.0981228244387354e-11], "orientation": [ -0.7305820977667332, 0.6827118412761586, 0.012423373296998674, -0.6774366704874311, -0.7269782211842862, 0.11217051039660496, 0.08561165751031312, 0.07353371815057558, @@ -44,7 +44,7 @@ "meanAnomalyRad": 0.9948376736367678 }, "europa": { - "positionMpc": [1.9366208544821226e-11, 1.3288486688770912e-11, 5.223331413650205e-12], + "positionMpc": [1.9366208544821226e-11, 1.3288551377534754e-11, 5.2231668713859995e-12], "orientation": [ 0.8261228244491918, 0.5034550279258321, 0.2530891419644264, -0.5633055860404016, 0.7493575544597507, 0.34806618955276497, -0.014418587301454018, -0.43011195104333155, @@ -53,7 +53,7 @@ "meanAnomalyRad": 6.028367236388414 }, "ganymede": { - "positionMpc": [1.935782125182158e-11, 1.327889867944624e-11, 5.2186285300575744e-12], + "positionMpc": [1.935782125182158e-11, 1.3278963368210082e-11, 5.218463987793369e-12], "orientation": [ 0.7379372685045305, 0.6051980492902703, 0.29863675073125895, -0.674734511281878, 0.6704669763077579, 0.3085569201394417, -0.013488033113756969, -0.42919617288133416, @@ -62,12 +62,12 @@ "meanAnomalyRad": 5.668829410477583 }, "iapetus": { - "positionMpc": [3.112403542854821e-11, 2.9938815769420106e-11, 1.0993574885840525e-11], + "positionMpc": [3.112403542854821e-11, 2.993895163790855e-11, 1.0993205841361646e-11], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 1.3055062804917585 }, "io": { - "positionMpc": [1.9397356297216674e-11, 1.3302582071640613e-11, 5.2304387922836454e-12], + "positionMpc": [1.9397356297216674e-11, 1.3302646760404455e-11, 5.23027425001944e-12], "orientation": [ -0.9475005237879973, -0.2823909444270978, -0.14999370629143904, 0.31941847123727335, -0.8573973102852636, -0.4035364823012149, -0.014649151986168738, -0.4302617887067911, @@ -76,7 +76,7 @@ "meanAnomalyRad": 5.775294494849236 }, "jupiter": { - "positionMpc": [1.938440693074091e-11, 1.3298849430765319e-11, 5.228454608751198e-12], + "positionMpc": [1.938440693074091e-11, 1.3298914119529161e-11, 5.2282900664869925e-12], "orientation": [ 0.22825610082668613, -0.8802468632436776, -0.41601035105519185, 0.9734916178676621, 0.19993790151295504, 0.1110806260335145, -0.014602136035502334, -0.4303374202742101, @@ -85,7 +85,7 @@ "meanAnomalyRad": 0.34327067101878284 }, "mars": { - "positionMpc": [6.7421474995208884e-12, 6.8954934330600985e-15, -1.7911067654834298e-13], + "positionMpc": [6.7421474995208884e-12, 6.893277360777564e-15, -1.791107618500849e-13], "orientation": [ -0.7067491138500313, -0.7065745401448309, 0.035469836358746884, 0.5490428766969101, -0.5794164477979991, -0.6023524712072909, 0.4461587269353554, -0.4062376142607543, @@ -94,7 +94,7 @@ "meanAnomalyRad": 0.3384227896851049 }, "mercury": { - "positionMpc": [-6.306874292609911e-13, -1.942141141157702e-12, -9.72034974689076e-13], + "positionMpc": [-6.306874292609911e-13, -1.942153167646951e-12, -9.720109452033183e-13], "orientation": [ 0.9314951469471263, -0.27146527694401956, -0.24212268507444376, 0.3520893147390993, 0.8400821208129223, 0.4126683229144372, 0.09137781820826621, -0.46964735036425953, @@ -103,12 +103,12 @@ "meanAnomalyRad": 3.050705107870811 }, "mimas": { - "positionMpc": [3.109980743972915e-11, 2.982140995496761e-11, 1.0982152797121401e-11], + "positionMpc": [3.109980743972915e-11, 2.982154582345605e-11, 1.0981783752642522e-11], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 4.804891430740389 }, "moon": { - "positionMpc": [-8.684282394279855e-13, 4.293715726229506e-12, 1.8628749417615773e-12], + "positionMpc": [-8.684282394279855e-13, 4.293738774580142e-12, 1.8628218170240537e-12], "orientation": [ 0.7845965380655952, 0.5687413064909984, 0.24686352251070906, -0.6200066702864299, 0.7197447865505031, 0.3123446350326911, -0.000035437512628813665, -0.3981215499370551, @@ -117,7 +117,7 @@ "meanAnomalyRad": 2.3609068791727297 }, "neptune": { - "positionMpc": [8.147178919019027e-11, -1.1141464182571051e-10, -4.763257192058855e-11], + "positionMpc": [8.147178919019027e-11, -1.1141523115780291e-10, -4.7631193423792595e-11], "orientation": [ 0.018461554711798778, -0.731133128996666, -0.6819849842050542, 0.9343480499713713, 0.2554057808141049, -0.24851882955106883, 0.3558832568586489, -0.6326232961330795, @@ -126,12 +126,12 @@ "meanAnomalyRad": -1.746809150875549 }, "phobos": { - "positionMpc": [6.742083325040046e-12, 6.612080728136197e-15, -1.7921372187202975e-13], + "positionMpc": [6.742083325040046e-12, 6.609864655853662e-15, -1.7921380717377167e-13], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 3.3108895910332428 }, "pluto": { - "positionMpc": [-4.791428248023062e-11, -1.3566774569531757e-10, -2.790246761511439e-11], + "positionMpc": [-4.791428248023062e-11, -1.3566809091209427e-10, -2.7900789044928168e-11], "orientation": [ -0.3334893355513804, -0.4344281072852858, -0.8366941392611896, -0.6550911205293397, -0.5314492493346873, 0.5370449880458912, -0.6779679099169169, 0.7272096774535675, @@ -150,7 +150,7 @@ "meanAnomalyRad": 0.08515672394345941 }, "rhea": { - "positionMpc": [3.1115776493412146e-11, 2.98215761066705e-11, 1.0980684148716999e-11], + "positionMpc": [3.1115776493412146e-11, 2.9821711975158945e-11, 1.098031510423812e-11], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 0.5497787143782138 }, @@ -340,7 +340,7 @@ "meanAnomalyRad": 5.630129826433361 }, "saturn": { - "positionMpc": [3.1099752807904804e-11, 2.9827398693151044e-11, 1.0981551367914625e-11], + "positionMpc": [3.1099752807904804e-11, 2.9827534561639487e-11, 1.0981182323435745e-11], "orientation": [ -0.9801892579246128, 0.18502086117224825, 0.07068450735682215, -0.1786683742380381, -0.9800019411711303, 0.08760027025045486, 0.0854788318610716, 0.07323575787752883, @@ -349,17 +349,17 @@ "meanAnomalyRad": -0.7442892730041829 }, "tethys": { - "positionMpc": [3.1108738218666247e-11, 2.9830450110316054e-11, 1.0980470694683058e-11], + "positionMpc": [3.1108738218666247e-11, 2.9830585978804496e-11, 1.0980101650204179e-11], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 0 }, "titan": { - "positionMpc": [3.111648567869407e-11, 2.9792764657318893e-11, 1.0982498512163092e-11], + "positionMpc": [3.111648567869407e-11, 2.9792900525807336e-11, 1.0982129467684212e-11], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 0.20420352248333654 }, "uranus": { - "positionMpc": [6.993663216219453e-11, -6.064674486105524e-11, -2.7552288916504907e-11], + "positionMpc": [6.993663216219453e-11, -6.06470857509395e-11, -2.755153855411126e-11], "orientation": [ -0.8693317262684063, 0.3040596708473847, -0.3896281127785479, 0.44645103553580323, 0.1449623050604645, -0.8829877705724841, -0.2119995815377986, -0.9415591572895124, @@ -368,7 +368,7 @@ "meanAnomalyRad": 2.48332127460649 }, "venus": { - "positionMpc": [-3.4824959657808e-12, -2.2458100936506426e-13, 1.1936386084274154e-13], + "positionMpc": [-3.4824959657808e-12, -2.2457953250190632e-13, 1.1936663949337553e-13], "orientation": [ -0.954821497429634, 0.2665104385944917, 0.13148419740182907, -0.296591573568662, -0.8824137725799873, -0.36521140788482953, 0.018690814168902045, -0.38770880836179883, @@ -382,12 +382,12 @@ "meanAnomalyRad": 2.1093550674103057 }, "whale": { - "positionMpc": [-8.589504546518782e-13, 4.302442865722174e-12, 1.865304005019885e-12], + "positionMpc": [-8.589504546518782e-13, 4.3024659441260814e-12, 1.8652507750193955e-12], "orientation": [-1, 1.2246467991473532e-16, 0, -1.2246467991473532e-16, -1, 0, 0, 0, 1], "meanAnomalyRad": 0 }, "petunias": { - "positionMpc": [-8.589504533555665e-13, 4.30244286572217e-12, 1.865304005019885e-12], + "positionMpc": [-8.589504533555665e-13, 4.3024659441260774e-12, 1.8652507750193955e-12], "orientation": [ 0.31895930929807, -0.9477684100095857, 0, -0.5730199417863159, -0.19284251607872754, 0.7965299180241964, -0.7549258939308582, -0.25406063248824595, -0.6045991148623748 @@ -395,25 +395,25 @@ "meanAnomalyRad": -0.000005907546891153448 }, "voyager1": { - "positionMpc": [-9.849911903784252e-11, -3.472694889308621e-10, 7.700732265421764e-11], + "positionMpc": [-9.849911903784252e-11, -3.47268536120473e-10, 7.701161928836898e-11], "orientation": [ - 0.26209637816188736, 0.9437270931800531, -0.20170439299219164, -0.9486015462831995, - 0.2903566657747011, 0.12588928877915567, 0.1773713475833796, 0.1583419724461146, - 0.971322410334658 + 0.2620963781618873, 0.9437245974921663, -0.2017160693714547, -0.9486015462831995, + 0.29035822333520933, 0.1258856962912936, 0.17737134758337958, 0.15835399025559568, + 0.9713204511522379 ], "meanAnomalyRad": 21.812722680769564 }, "voyager2": { - "positionMpc": [7.68472828852993e-11, -1.8208692576490295e-10, -2.1201749638295566e-10], + "positionMpc": [7.68472828852993e-11, -1.820895489669245e-10, -2.120152434709163e-10], "orientation": [ - -0.26417090791453673, 0.6336504634512483, 0.7271181620477015, -0.9569166485951118, - -0.2664011872494653, -0.11550296564839521, 0.12051663392705755, -0.7263039980614822, - 0.6767261213717097 + -0.26417090791453673, 0.6336594597733262, 0.7271103220635683, -0.956916648595112, + -0.2664026163055799, -0.11549966955381757, 0.12051663392705765, -0.7262956251178432, + 0.6767351076171333 ], "meanAnomalyRad": 12.997179255974443 }, "hubble": { - "positionMpc": [-8.5896919141719e-13, 4.3024188450404305e-12, 1.8652002002526575e-12], + "positionMpc": [-8.5896919141719e-13, 4.302441923444338e-12, 1.865146970252168e-12], "orientation": [ -6.123233995736766e-17, 6.123233995736766e-17, 1, -6.123233995736766e-17, -1, 6.123233995736766e-17, 1, -6.123233995736766e-17, 6.123233995736766e-17 diff --git a/tests/fixtures/camera/driverGoldenTrace.json b/tests/fixtures/camera/driverGoldenTrace.json index 825918f1c..9257d51b8 100644 --- a/tests/fixtures/camera/driverGoldenTrace.json +++ b/tests/fixtures/camera/driverGoldenTrace.json @@ -3,15 +3,31 @@ "label": "boot 0", "winner": "resting", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.7, 0.3, 1.23882029118e-15, 0 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.7, + 0.3, + 1.23882029118e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.7, 0.3, 1.23882029118e-15, 0 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.7, + 0.3, + 1.23882029118e-15, + 0 ], "follow": { "fromDistance": null, "distanceTarget": null, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -35,21 +51,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "boot 3", "winner": "resting", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.7, 0.3, 1.23882029118e-15, 0 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.7, + 0.3, + 1.23882029118e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.7, 0.3, 1.23882029118e-15, 0 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.7, + 0.3, + 1.23882029118e-15, + 0 ], "follow": { "fromDistance": null, "distanceTarget": null, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -73,21 +107,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "drag 0", "winner": "orbitDrag", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.7, 0.3, 1.23882029118e-15, 0 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.7, + 0.3, + 1.23882029118e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.7, 0.3, 1.23882029118e-15, 0 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.7, + 0.3, + 1.23882029118e-15, + 0 ], "follow": { "fromDistance": null, "distanceTarget": null, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -111,21 +163,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "drag end", "winner": "resting", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 1.23882029118e-15, 0 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 1.23882029118e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 1.23882029118e-15, 0 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 1.23882029118e-15, + 0 ], "follow": { "fromDistance": null, "distanceTarget": null, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -149,21 +219,41 @@ "refNull": true } }, - "actions": ["camera/commitCameraPose", "camera/endDrag", "engine/engineScaleChanged"] + "actions": [ + "camera/commitCameraPose", + "camera/endDrag", + "engine/engineScaleChanged" + ] }, { "label": "follow 0", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 1.23882029118e-15, 0 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 1.23882029118e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 1.23882029118e-15, 0 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 1.23882029118e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.95283314668e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -187,21 +277,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "follow 7", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 1.08011618333e-15, 0 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 1.08011618333e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 1.08011618333e-15, 0 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 1.08011618333e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.95283314668e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -225,21 +333,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "follow 9", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 1.04608780247e-15, 0 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 1.04608780247e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 1.04608780247e-15, 0 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 1.04608780247e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.95283314668e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -263,23 +389,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "notch under follow a", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 9.91065552e-16, - 2.94167982154e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 9.91065552e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 9.91065552e-16, - 2.94167982154e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 9.91065552e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -303,23 +445,39 @@ "refNull": true } }, - "actions": ["camera/commitCameraPose", "engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "notch under follow b", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 9.74097988688e-16, - 3.14314171398e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 9.74097988688e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 9.74097988688e-16, - 3.14314171398e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 9.74097988688e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -343,23 +501,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "follow settle 0", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 9.583638658e-16, - 3.32995853114e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 9.583638658e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 9.583638658e-16, - 3.32995853114e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 9.583638658e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -383,23 +557,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "follow settle 3", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 9.1809669046e-16, - 3.80806497446e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 9.1809669046e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 9.1809669046e-16, - 3.80806497446e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 9.1809669046e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -423,23 +613,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "follow settle 11", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 8.53383734397e-16, - 4.5764248358e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 8.53383734397e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 8.53383734397e-16, - 4.5764248358e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 8.53383734397e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -463,23 +669,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "follow settle 19", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 8.31864465622e-16, - 4.8319307403e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 8.31864465622e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 8.31864465622e-16, - 4.8319307403e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 8.31864465622e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -503,23 +725,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "follow settle 23", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 8.29855276505e-16, - 4.85578655474e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 8.29855276505e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 8.29855276505e-16, - 4.85578655474e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 8.29855276505e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -543,23 +781,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "autoRotate 0", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 8.2976024729e-16, - 4.85691487029e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 8.2976024729e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 8.2976024729e-16, - 4.85691487029e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 8.2976024729e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -583,23 +837,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "autoRotate 2", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 8.29734065771e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 8.29734065771e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 8.29734065771e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 8.29734065771e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -623,23 +893,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "notch under autoRotate a", "winner": "autoRotate", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 8.29734065771e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 8.29734065771e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 8.29734065771e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 8.29734065771e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -663,23 +949,40 @@ "refNull": true } }, - "actions": ["camera/commitCameraPose", "camera/commitCameraPose", "engine/engineScaleChanged"] + "actions": [ + "camera/commitCameraPose", + "engine/engineScaleChanged" + ] }, { "label": "notch under autoRotate b", "winner": "autoRotate", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 8.29734065771e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 8.29734065771e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61, 0.3, 8.29734065771e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61, + 0.3, + 8.29734065771e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -703,23 +1006,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "autoRotate settle 0", "winner": "autoRotate", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61083808, 0.3, 8.29734065771e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61083808, + 0.3, + 8.29734065771e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61083808, 0.3, 8.29734065771e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61083808, + 0.3, + 8.29734065771e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -743,23 +1062,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "autoRotate settle 2", "winner": "autoRotate", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61251424, 0.3, 8.29734065771e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61251424, + 0.3, + 8.29734065771e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61251424, 0.3, 8.29734065771e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61251424, + 0.3, + 8.29734065771e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -783,23 +1118,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "tween 0", "winner": "tween", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61251424, 0.3, 8.29734065771e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61251424, + 0.3, + 8.29734065771e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.61251424, 0.3, 8.29734065771e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.61251424, + 0.3, + 8.29734065771e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -823,23 +1174,40 @@ "refNull": true } }, - "actions": ["camera/commitCameraPose", "engine/engineScaleChanged"] + "actions": [ + "camera/commitCameraPose", + "engine/engineScaleChanged" + ] }, { "label": "tween 1", "winner": "tween", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.66956424, 0.3, 8.88904476337e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.66956424, + 0.3, + 8.88904476337e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.66956424, 0.3, 8.88904476337e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.66956424, + 0.3, + 8.88904476337e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -863,23 +1231,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "tween 3", "winner": "tween", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.76686424, 0.3, 9.89820882086e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.76686424, + 0.3, + 9.89820882086e-16, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.76686424, 0.3, 9.89820882086e-16, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.76686424, + 0.3, + 9.89820882086e-16, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -903,23 +1287,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "tween 11", "winner": "tween", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.97606424, 0.3, 1.20679634029e-15, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.97606424, + 0.3, + 1.20679634029e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.97606424, 0.3, 1.20679634029e-15, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.97606424, + 0.3, + 1.20679634029e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -943,23 +1343,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "tween 19", "winner": "tween", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01246424, 0.3, 1.24454924028e-15, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01246424, + 0.3, + 1.24454924028e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01246424, 0.3, 1.24454924028e-15, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01246424, + 0.3, + 1.24454924028e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -983,23 +1399,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "tween 20", "winner": "tween", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01251424, 0.3, 1.24460109866e-15, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01251424, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01251424, 0.3, 1.24460109866e-15, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01251424, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1023,23 +1455,40 @@ "refNull": true } }, - "actions": ["camera/cancelCameraTween", "engine/engineScaleChanged"] + "actions": [ + "camera/cancelCameraTween", + "engine/engineScaleChanged" + ] }, { "label": "tween 21", "winner": "autoRotate", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01251424, 0.3, 1.24460109866e-15, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01251424, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01251424, 0.3, 1.24460109866e-15, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01251424, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1063,23 +1512,40 @@ "refNull": true } }, - "actions": ["camera/commitCameraPose", "engine/engineScaleChanged"] + "actions": [ + "camera/commitCameraPose", + "engine/engineScaleChanged" + ] }, { "label": "tween 22", "winner": "autoRotate", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01251424, 0.3, 1.24460109866e-15, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01251424, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01251424, 0.3, 1.24460109866e-15, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01251424, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1103,23 +1569,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "tween 23", "winner": "autoRotate", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01335232, 0.3, 1.24460109866e-15, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01335232, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01335232, 0.3, 1.24460109866e-15, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01335232, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1143,23 +1625,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "clip 0", "winner": "clip", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01335232, 0.3, 1.24460109866e-15, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01335232, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01335232, 0.3, 1.24460109866e-15, - 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01335232, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1183,23 +1681,40 @@ "refNull": false } }, - "actions": ["camera/commitCameraPose", "engine/engineScaleChanged"] + "actions": [ + "camera/commitCameraPose", + "engine/engineScaleChanged" + ] }, { "label": "clip 1", "winner": "clip", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01375444386, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01375444386, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01375444386, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01375444386, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1223,23 +1738,39 @@ "refNull": false } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "clip 3", "winner": "clip", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.02420966421, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.02420966421, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.02420966421, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.02420966421, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1263,23 +1794,39 @@ "refNull": false } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "clip 11", "winner": "clip", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.54857917721, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.54857917721, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.54857917721, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.54857917721, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1303,23 +1850,39 @@ "refNull": false } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "clip 19", "winner": "clip", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 2.49728989311, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 2.49728989311, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 2.49728989311, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 2.49728989311, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1343,23 +1906,39 @@ "refNull": false } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "clip 27", "winner": "clip", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01656931088, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01656931088, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.01656931088, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.01656931088, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1383,23 +1962,39 @@ "refNull": false } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "clip 35", "winner": "clip", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.41547617966, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.41547617966, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.41547617966, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.41547617966, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1423,23 +2018,39 @@ "refNull": false } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "clip 37", "winner": "clip", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.70822234949, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.70822234949, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.70822234949, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.70822234949, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1463,23 +2074,39 @@ "refNull": false } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "teardown 0", "winner": "resting", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.70822234949, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.70822234949, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.70822234949, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.70822234949, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1503,23 +2130,40 @@ "refNull": true } }, - "actions": ["camera/commitCameraPose", "engine/engineScaleChanged"] + "actions": [ + "camera/commitCameraPose", + "engine/engineScaleChanged" + ] }, { "label": "teardown 1", "winner": "resting", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.70822234949, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.70822234949, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.70822234949, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.70822234949, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1543,23 +2187,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "teardown 5", "winner": "resting", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.70822234949, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.70822234949, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.70822234949, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.70822234949, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.23882029118e-15, "distanceTarget": 8.29734065771e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1583,23 +2243,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "refocus 0", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.70822234949, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.70822234949, + 0.3, + 1.24460109866e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.70822234949, 0.3, - 1.24460109866e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.70822234949, + 0.3, + 1.24460109866e-15, + 0 ], "follow": { "fromDistance": 1.24460109866e-15, "distanceTarget": 8.95283314668e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1623,23 +2299,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "refocus 3", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.70822234949, 0.3, - 1.16729288125e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.70822234949, + 0.3, + 1.16729288125e-15, + 0 ], "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 1.70822234949, 0.3, - 1.16729288125e-15, 4.85722573274e-17 + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 1.70822234949, + 0.3, + 1.16729288125e-15, + 0 ], "follow": { "fromDistance": 1.24460109866e-15, "distanceTarget": 8.95283314668e-16, - "panOffset": [0, 0, 0] + "panOffset": [ + 0, + 0, + 0 + ] }, "epochs": { "tween": { @@ -1663,23 +2355,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "pan 0", "winner": "orbitDrag", "displayed": [ - -8.58953059185e-13, 4.30214531476e-12, 1.86531224046e-12, 1.70822234949, 0.3, - 1.16729288125e-15, 4.85722573274e-17 + -8.58953059185e-13, + 4.30216839327e-12, + 1.86525901142e-12, + 1.70822234949, + 0.3, + 1.16729288125e-15, + 0 ], "register": [ - -8.58953059185e-13, 4.30214531476e-12, 1.86531224046e-12, 1.70822234949, 0.3, - 1.16729288125e-15, 4.85722573274e-17 + -8.58953059185e-13, + 4.30216839327e-12, + 1.86525901142e-12, + 1.70822234949, + 0.3, + 1.16729288125e-15, + 0 ], "follow": { "fromDistance": 1.24460109866e-15, "distanceTarget": 8.95283314668e-16, - "panOffset": [-2.60453313039e-18, -7.81177960581e-17, 8.23543884596e-18] + "panOffset": [ + -2.60453313039e-18, + -7.81176941581e-17, + 8.23640536814e-18 + ] }, "epochs": { "tween": { @@ -1703,23 +2411,40 @@ "refNull": true } }, - "actions": ["camera/commitCameraPose", "engine/engineScaleChanged"] + "actions": [ + "camera/commitCameraPose", + "engine/engineScaleChanged" + ] }, { "label": "pan 1", "winner": "orbitDrag", "displayed": [ - -8.58955663718e-13, 4.30206719696e-12, 1.8653204759e-12, 1.70822234949, 0.3, - 1.16729288125e-15, 4.85722573274e-17 + -8.58955663718e-13, + 4.30209027557e-12, + 1.86526724783e-12, + 1.70822234949, + 0.3, + 1.16729288125e-15, + 0 ], "register": [ - -8.58955663718e-13, 4.30206719696e-12, 1.8653204759e-12, 1.70822234949, 0.3, - 1.16729288125e-15, 4.85722573274e-17 + -8.58955663718e-13, + 4.30209027557e-12, + 1.86526724783e-12, + 1.70822234949, + 0.3, + 1.16729288125e-15, + 0 ], "follow": { "fromDistance": 1.24460109866e-15, "distanceTarget": 8.95283314668e-16, - "panOffset": [-5.20906626077e-18, -1.56235592116e-16, 1.64708776919e-17] + "panOffset": [ + -5.20906626077e-18, + -1.56235388316e-16, + 1.64728107363e-17 + ] }, "epochs": { "tween": { @@ -1743,23 +2468,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "pan end", "winner": "followApproach", "displayed": [ - -8.58958268251e-13, 4.30198907917e-12, 1.86532871134e-12, 1.70822234949, 0.3, - 1.20888694702e-15, 4.85722573274e-17 + -8.58958268251e-13, + 4.30201215788e-12, + 1.86527548424e-12, + 1.70822234949, + 0.3, + 1.20888694702e-15, + 0 ], "register": [ - -8.58958268251e-13, 4.30198907917e-12, 1.86532871134e-12, 1.70822234949, 0.3, - 1.20888694702e-15, 4.85722573274e-17 + -8.58958268251e-13, + 4.30201215788e-12, + 1.86527548424e-12, + 1.70822234949, + 0.3, + 1.20888694702e-15, + 0 ], "follow": { "fromDistance": 1.24460109866e-15, "distanceTarget": 1.16729288125e-15, - "panOffset": [-7.81359939116e-18, -2.34353388174e-16, 2.47063165379e-17] + "panOffset": [ + -7.81359939116e-18, + -2.34353082474e-16, + 2.47092161044e-17 + ] }, "epochs": { "tween": { @@ -1783,23 +2524,41 @@ "refNull": true } }, - "actions": ["camera/commitCameraPose", "camera/endDrag", "engine/engineScaleChanged"] + "actions": [ + "camera/commitCameraPose", + "camera/endDrag", + "engine/engineScaleChanged" + ] }, { "label": "pan settle 0", "winner": "followApproach", "displayed": [ - -8.58958268251e-13, 4.30198907917e-12, 1.86532871134e-12, 1.70822234949, 0.3, - 1.20492839979e-15, 4.85722573274e-17 + -8.58958268251e-13, + 4.30201215788e-12, + 1.86527548424e-12, + 1.70822234949, + 0.3, + 1.20492839979e-15, + 0 ], "register": [ - -8.58958268251e-13, 4.30198907917e-12, 1.86532871134e-12, 1.70822234949, 0.3, - 1.20492839979e-15, 4.85722573274e-17 + -8.58958268251e-13, + 4.30201215788e-12, + 1.86527548424e-12, + 1.70822234949, + 0.3, + 1.20492839979e-15, + 0 ], "follow": { "fromDistance": 1.24460109866e-15, "distanceTarget": 1.16729288125e-15, - "panOffset": [-7.81359939116e-18, -2.34353388174e-16, 2.47063165379e-17] + "panOffset": [ + -7.81359939116e-18, + -2.34353082474e-16, + 2.47092161044e-17 + ] }, "epochs": { "tween": { @@ -1823,23 +2582,39 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] }, { "label": "pan settle 3", "winner": "followApproach", "displayed": [ - -8.58958268251e-13, 4.30198907917e-12, 1.86532871134e-12, 1.70822234949, 0.3, - 1.19457445869e-15, 4.85722573274e-17 + -8.58958268251e-13, + 4.30201215788e-12, + 1.86527548424e-12, + 1.70822234949, + 0.3, + 1.19457445869e-15, + 0 ], "register": [ - -8.58958268251e-13, 4.30198907917e-12, 1.86532871134e-12, 1.70822234949, 0.3, - 1.19457445869e-15, 4.85722573274e-17 + -8.58958268251e-13, + 4.30201215788e-12, + 1.86527548424e-12, + 1.70822234949, + 0.3, + 1.19457445869e-15, + 0 ], "follow": { "fromDistance": 1.24460109866e-15, "distanceTarget": 1.16729288125e-15, - "panOffset": [-7.81359939116e-18, -2.34353388174e-16, 2.47063165379e-17] + "panOffset": [ + -7.81359939116e-18, + -2.34353082474e-16, + 2.47092161044e-17 + ] }, "epochs": { "tween": { @@ -1863,6 +2638,8 @@ "refNull": true } }, - "actions": ["engine/engineScaleChanged"] + "actions": [ + "engine/engineScaleChanged" + ] } ] diff --git a/tests/fixtures/camera/settleGoldenTrace.json b/tests/fixtures/camera/settleGoldenTrace.json index 7f4c6293e..19db51a8c 100644 --- a/tests/fixtures/camera/settleGoldenTrace.json +++ b/tests/fixtures/camera/settleGoldenTrace.json @@ -1,768 +1 @@ -{ - "northUp": [ - { - "label": "start", - "arm": "absolute", - "hr": 5, - "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.7, 0.3, 1.23882029118e-15, 0 - ], - "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.7, 0.3, 1.23882029118e-15, 0 - ], - "memory": 0 - }, - { - "label": "dive 0", - "arm": "absolute", - "hr": 4.69959329559, - "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.7, 0.3, 1.17679530434e-15, - 1.57809567882e-17 - ], - "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.7, 0.3, 1.17679530434e-15, - 1.57809567882e-17 - ], - "memory": 0 - }, - { - "label": "dive 20", - "arm": "earth", - "hr": 0.408531953689, - "displayed": [ - -8.58823383762e-13, 4.30206074691e-12, 1.86529997395e-12, 0.7, 0.300000000001, - 8.43496123045e-17, -0.0199078845974 - ], - "register": [ - 0, 0, 0, 7933156.7443, 4190780.91606, -175201.198913, -0.449691619429, 0.861174146953, - 0.236973703258, 0.127481010064, -0.200714484013, 0.971319869034, -0.884039614208, - -0.467004051939, 0.0195237287359 - ], - "memory": 0 - }, - { - "label": "dive 39", - "arm": "earth", - "hr": 0.0615681608777, - "displayed": [ - -8.58815842396e-13, 4.30206705766e-12, 1.86531150914e-12, 0.763865797113, 0.340617140441, - 1.27119833966e-17, -0.255763657587 - ], - "register": [ - 0, 0, 0, 5819025.68697, 3438051.22141, 245569.950366, -0.509248289938, 0.860308071321, - 0.023156027636, -0.0189715330061, -0.038121513736, 0.999093004242, -0.860410518375, - -0.508347098557, -0.0357346786504 - ], - "memory": 0 - }, - { - "label": "dive brisk 0", - "arm": "earth", - "hr": 0.0456277801718, - "displayed": [ - -8.5881543796e-13, 4.30206742725e-12, 1.86531192452e-12, 0.767221244995, 0.342024345681, - 9.42077070426e-18, -0.270229276841 - ], - "register": [ - 0, 0, 0, 5722204.69758, 3401369.82706, 255404.532694, -0.511247578567, 0.859378454033, - 0.00972554130965, -0.0276544886274, -0.0277600193841, 0.999232010387, -0.858988441522, - -0.510585990865, -0.0379579143748 - ], - "memory": 0 - }, - { - "label": "dive brisk 35", - "arm": "earth", - "hr": 0.00000239999873797, - "displayed": [ - -8.58814244798e-13, 4.30206852826e-12, 1.86531302813e-12, 0.776946766818, 0.345361094401, - 4.95528126563e-22, -0.282917596025 - ], - "register": [ - 0, 0, 0, 5445558.39002, 3295179.717, 278424.647066, -0.517708829899, 0.855556875634, - -3.88623138878e-9, -0.0373893521034, -0.0226247890665, 0.99904462126, -0.854739494696, - -0.517214222035, -0.0437017703418 - ], - "memory": 0 - }, - { - "label": "tilt 1 move 0", - "arm": "earth", - "hr": 0.00000248496732813, - "displayed": [ - -8.58814244798e-13, 4.30206852826e-12, 1.86531302813e-12, 0.752741251018, 0.26482712059, - 5.1487696487e-22, -0.275640450238 - ], - "register": [ - 0, 0, 0, 5445558.90184, 3295180.0267, 278423.358061, -0.517708829899, 0.855556875634, - -3.8862327037e-9, 0.0342645351442, 0.0207339298939, 0.999197701049, -0.854870463331, - -0.517293472515, 0.0400493972176 - ], - "memory": 0.0837755978624 - }, - { - "label": "tilt 1 move 3", - "arm": "earth", - "hr": 0.00000263277105872, - "displayed": [ - -8.58814244798e-13, 4.30206852826e-12, 1.86531302814e-12, 0.684987943457, 0.0225617062629, - 5.75605631392e-22, -0.265862865903 - ], - "register": [ - 0, 0, 0, 5445559.86179, 3295180.60758, 278419.255521, -0.517708829899, 0.855556875634, - -3.88623658254e-9, 0.245785690831, 0.14872819067, 0.9578461878, -0.81949189235, - -0.495885428154, 0.287281535288 - ], - "memory": 0.33510235913 - }, - { - "label": "tilt 1 end", - "arm": "earth", - "hr": 0.00000240000085916, - "displayed": [ - -8.58814244839e-13, 4.30206852831e-12, 1.86531302955e-12, 0.304150654493, -0.931252262711, - 2.17004580495e-21, -0.455729267585 - ], - "register": [ - 0, 0, 0, 5445559.18802, 3295180.19988, 278403.323597, -0.517708829899, 0.855556875634, - -3.88625155667e-9, 0.823618448403, 0.49838246457, 0.270679830167, -0.231581991732, - -0.140133334952, 0.962669429005 - ], - "memory": 1.34040951538 - }, - { - "label": "pan move 0", - "arm": "earth", - "hr": 0.00000239999799412, - "displayed": [ - -8.58814244934e-13, 4.30206852823e-12, 1.86531302955e-12, 0.304150405293, -0.931252189813, - 2.17004580504e-21, -0.455728878458 - ], - "register": [ - 0, 0, 0, 5445561.20441, 3295176.86763, 278403.323592, -0.517708306366, 0.85555719243, - -3.8862556645e-9, 0.823618753374, 0.498381960581, 0.270679830166, -0.231582077482, - -0.140133193242, 0.962669429005 - ], - "memory": 1.34040951538 - }, - { - "label": "pan end", - "arm": "earth", - "hr": 0.00000239999932394, - "displayed": [ - -8.58814245295e-13, 4.30206852789e-12, 1.8653130293e-12, 0.304148562276, -0.93125297358, - 2.17004580513e-21, -0.455728071278 - ], - "register": [ - 0, 0, 0, 5445569.31207, 3295164.10778, 278395.762756, -0.517706274756, 0.85555842178, - -3.8862557894e-9, 0.823620211924, 0.498380171274, 0.270678686621, -0.231581431872, - -0.140132051306, 0.962669750542 - ], - "memory": 1.34040951538 - }, - { - "label": "tilt 2 move 0", - "arm": "earth", - "hr": 0.00000239999899798, - "displayed": [ - -8.58814245329e-13, 4.30206852793e-12, 1.8653130305e-12, 0.235348859851, -1.00544185973, - 3.39220446184e-21, -0.512461799769 - ], - "register": [ - 0, 0, 0, 5445569.35779, 3295164.13545, 278394.540928, -0.517706274756, 0.85555842178, - -3.88625580328e-9, 0.840109894691, 0.508358229701, 0.189175249802, -0.161850480136, - -0.0979372105863, 0.981943340963 - ], - "memory": 1.42418512751 - }, - { - "label": "tilt 2 end", - "arm": "earth", - "hr": 0.00000239999918339, - "displayed": [ - -8.58814244602e-13, 4.30206852711e-12, 1.86531302683e-12, -2.18873158028, -0.782393844366, - 4.95528116513e-22, -2.76218929169 - ], - "register": [ - 0, 0, 0, 5445570.02468, 3295164.53899, 278376.719336, -0.517706274756, 0.85555842178, - -3.88625581715e-9, 0.413537188401, 0.250235154524, -0.875425245952, 0.74897744084, - 0.453213144516, 0.483353533917 - ], - "memory": 2.68081938898 - }, - { - "label": "look move 0", - "arm": "earth", - "hr": 0.00000240000252694, - "displayed": [ - -8.58814244611e-13, 4.3020685271e-12, 1.86531302683e-12, -2.21974316641, -0.79026709551, - 4.95528116513e-22, -2.83104060575 - ], - "register": [ - 0, 0, 0, 5445570.02468, 3295164.53899, 278376.719336, -0.518953250488, 0.853202028398, - 0.052285968923, 0.389222376494, 0.290314069469, -0.874198880521, 0.761047610498, - 0.433317481538, 0.482744751136 - ], - "memory": 2.68081938898 - }, - { - "label": "look end", - "arm": "earth", - "hr": 0.00000240000220453, - "displayed": [ - -8.5881424461e-13, 4.30206852708e-12, 1.86531302681e-12, -2.26867632381, -0.745682551694, - 4.95528116513e-22, -2.91255621627 - ], - "register": [ - 0, 0, 0, 5445570.02468, 3295164.53899, 278376.719336, -0.518777811432, 0.848507067561, - 0.104428629526, 0.323919861772, 0.30813390666, -0.894499535336, 0.791167179246, - 0.430220004027, 0.434701325762 - ], - "memory": 2.73317926654 - }, - { - "label": "recede brisk 0", - "arm": "earth", - "hr": 0.00000323966090598, - "displayed": [ - -8.58814244386e-13, 4.30206852684e-12, 1.86531302689e-12, -2.23720591448, -0.739328320116, - 6.68892992463e-22, -2.83571247526 - ], - "register": [ - 0, 0, 0, 5445574.5971, 3295167.3058, 278376.953078, -0.518852132056, 0.853724607628, - 0.0441220964097, 0.352754925775, 0.26082999103, -0.898627663786, 0.77868891568, - 0.450690592436, 0.436487757546 - ], - "memory": 2.73317926654 - }, - { - "label": "recede brisk 35", - "arm": "earth", - "hr": 0.117649922739, - "displayed": [ - -8.58790612528e-13, 4.30203996958e-12, 1.86533471318e-12, -1.73703326324, -1.24763716596, - 2.42911852572e-17, -2.40939779165 - ], - "register": [ - 0, 0, 0, 6086226.3104, 3682831.55361, 311126.972153, -0.518981479156, 0.85299727638, - 0.0552618383795, 0.711370360268, 0.466848482509, -0.525361499266, 0.473930833377, - 0.233341254104, 0.8490826958 - ], - "memory": 2.73317926654 - }, - { - "label": "recede 0", - "arm": "earth", - "hr": 0.130023273124, - "displayed": [ - -8.58790902539e-13, 4.30203938782e-12, 1.86533885729e-12, -1.30184995608, -1.35141730292, - 2.68459115118e-17, -1.99678524698 - ], - "register": [ - 0, 0, 0, 6153606.09465, 3723603.68118, 314571.416574, -0.519053726585, 0.851821695684, - 0.0705905636771, 0.771980311407, 0.502646834336, -0.389091966932, 0.366919102415, - 0.147465110091, 0.918490290416 - ], - "memory": 2.73317926654 - }, - { - "label": "recede 3", - "arm": "earth", - "hr": 0.175513060413, - "displayed": [ - -8.58796269796e-13, 4.30204137664e-12, 1.86535035948e-12, 0.394300847072, -1.13068063703, - 3.62381901016e-17, -0.359797730373 - ], - "register": [ - 0, 0, 0, 6401323.32224, 3873499.6554, 327234.683932, -0.518386381167, 0.846342728004, - 0.122390957895, 0.854626085316, 0.507744829759, 0.108671257242, -0.0298297522298, - -0.160932205002, 0.986514577325 - ], - "memory": 2.73317926654 - }, - { - "label": "recede 19", - "arm": "absolute", - "hr": 0.904220833927, - "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.77203029021, 0.26566625834, - 3.93164567994e-16, 0 - ], - "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.77203029021, 0.26566625834, - 3.93164567994e-16, 0 - ], - "memory": 2.73317926654 - }, - { - "label": "recede world 0", - "arm": "absolute", - "hr": 0.999318569173, - "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.77203029021, 0.26566625834, - 4.12799402005e-16, 1.38777878078e-17 - ], - "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.77203029021, 0.26566625834, - 4.12799402005e-16, 1.38777878078e-17 - ], - "memory": 2.73317926654 - }, - { - "label": "recede world 7", - "arm": "absolute", - "hr": 2.0123804743, - "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.77203029021, 0.26566625834, - 6.21966342721e-16, 4.99537680643e-17 - ], - "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.77203029021, 0.26566625834, - 6.21966342721e-16, 4.99537680643e-17 - ], - "memory": 2.73317926654 - }, - { - "label": "re-dive 0", - "arm": "absolute", - "hr": 1.82087715247, - "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.77203029021, 0.26566625834, - 5.82426642569e-16, -4.1680373752e-18 - ], - "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.77203029021, 0.26566625834, - 5.82426642569e-16, -4.1680373752e-18 - ], - "memory": 2.73317926654 - }, - { - "label": "re-dive 14", - "arm": "earth", - "hr": 0.44902277751, - "displayed": [ - -8.58813615039e-13, 4.30206886461e-12, 1.8653076863e-12, 0.770526712133, 0.146645684916, - 9.36724038836e-17, -0.0125262597088 - ], - "register": [ - 0, 0, 0, 7807991.34246, 4915356.66425, -315071.744698, -0.506029067466, 0.820749077459, - 0.265151908782, 0.268425335891, -0.142294341754, 0.952732994787, -0.819684342861, - -0.553284079073, 0.148304773735 - ], - "memory": 2.73317926654 - }, - { - "label": "re-dive engaged 0", - "arm": "earth", - "hr": 0.404990357301, - "displayed": [ - -8.5881413465e-13, 4.30206838331e-12, 1.8653065484e-12, 0.766060103541, 0.0569265529058, - 8.56332553321e-17, -0.014682372201 - ], - "register": [ - 0, 0, 0, 7548597.89319, 4780284.00485, -539834.180337, -0.504219599434, 0.822849971614, - 0.262069684934, 0.339586908599, -0.090096931112, 0.936249579179, -0.794004614001, - -0.561070821928, 0.234000439585 - ], - "memory": 2.73317926654 - }, - { - "label": "re-dive engaged 3", - "arm": "earth", - "hr": 0.268610129073, - "displayed": [ - -8.58815551839e-13, 4.30206713078e-12, 1.86530493509e-12, 0.730400579092, -0.381240123956, - 6.74071830062e-17, -0.0418810656223 - ], - "register": [ - 0, 0, 0, 6703603.91623, 4337565.20366, -1253411.55901, -0.498419255637, 0.834725313401, - 0.234119407094, 0.637186519562, 0.169602057168, 0.751816787184, -0.587853370236, - -0.523897693612, 0.616408648325 - ], - "memory": 2.73317926654 - } - ], - "northUpOff": [ - { - "label": "start", - "arm": "absolute", - "hr": 5, - "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.7, 0.3, 1.23882029118e-15, 0 - ], - "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.7, 0.3, 1.23882029118e-15, 0 - ], - "memory": 0 - }, - { - "label": "dive 0", - "arm": "absolute", - "hr": 4.69959329559, - "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.7, 0.3, 1.17679530434e-15, 0 - ], - "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.7, 0.3, 1.17679530434e-15, 0 - ], - "memory": 0 - }, - { - "label": "dive 20", - "arm": "earth", - "hr": 0.408531953689, - "displayed": [ - -8.58823383762e-13, 4.30206074691e-12, 1.86529997395e-12, 0.7, 0.300000000001, - 8.43496123045e-17, 0 - ], - "register": [ - 0, 0, 0, 7933156.7443, 4190780.91606, -175201.198913, -0.447064801007, 0.857007963802, - 0.256262392246, 0.136407566548, -0.217817734475, 0.966410063243, -0.884039614208, - -0.467004051939, 0.0195237287359 - ], - "memory": 0 - }, - { - "label": "dive 39", - "arm": "earth", - "hr": 0.0615621239591, - "displayed": [ - -8.58815721031e-13, 4.30206715141e-12, 1.86531128077e-12, 0.763746321349, 0.339489149202, - 1.27107388114e-17, -0.0200470287365 - ], - "register": [ - 0, 0, 0, 5816777.47103, 3442303.88859, 238084.637298, -0.499523362162, 0.827670508433, - 0.255808405111, 0.101435192445, -0.237376038054, 0.966107405153, -0.86034139293, - -0.508541194033, -0.0346199593216 - ], - "memory": 0 - }, - { - "label": "dive brisk 0", - "arm": "earth", - "hr": 0.045625139939, - "displayed": [ - -8.58815389035e-13, 4.30206746308e-12, 1.865311795e-12, 0.767005933745, 0.341447327806, - 9.42022602833e-18, -0.0211355075588 - ], - "register": [ - 0, 0, 0, 5721378.84037, 3403036.63475, 251229.96033, -0.50212952764, 0.826125774875, - 0.255699318653, 0.0996977487931, -0.238405860426, 0.966034680847, -0.859026365345, - -0.510567184415, -0.0373477421075 - ], - "memory": 0 - }, - { - "label": "dive brisk 35", - "arm": "earth", - "hr": 0.0000023999980725, - "displayed": [ - -8.58814448428e-13, 4.30206836336e-12, 1.86531326421e-12, 0.775991810209, 0.346814025077, - 4.95528126624e-22, -0.0241671776287 - ], - "register": [ - 0, 0, 0, 5449460.18477, 3288098.1732, 285709.5177, -0.509274526104, 0.821846932352, - 0.255356763852, 0.094934416484, -0.241258422108, 0.96580631098, -0.855351923847, - -0.516102696704, -0.0448452096255 - ], - "memory": 0 - }, - { - "label": "tilt 1 move 0", - "arm": "earth", - "hr": 0.00000248496687472, - "displayed": [ - -8.58814448428e-13, 4.30206836336e-12, 1.86531326421e-12, 0.773897707549, 0.263062019846, - 5.14876964911e-22, -0.0235379046179 - ], - "register": [ - 0, 0, 0, 5449460.52306, 3288098.76957, 285708.272988, -0.509274526107, 0.821846932291, - 0.255356764042, 0.16617547239, -0.197225934497, 0.96617164269, -0.84440817703, - -0.534480636361, 0.0361286578788 - ], - "memory": 0.0837755979029 - }, - { - "label": "tilt 1 move 3", - "arm": "earth", - "hr": 0.00000263277163493, - "displayed": [ - -8.58814448431e-13, 4.30206836336e-12, 1.86531326422e-12, 0.76804416842, 0.0118011306383, - 5.75605631355e-22, -0.0227296004181 - ], - "register": [ - 0, 0, 0, 5449460.93475, 3288100.25622, 285704.309385, -0.509274526113, 0.821846932138, - 0.255356764524, 0.37095053859, -0.0581097905024, 0.926832751993, -0.77655338192, - -0.566737039891, 0.275270726095 - ], - "memory": 0.335102358841 - }, - { - "label": "tilt 1 end", - "arm": "earth", - "hr": 0.00000239999983642, - "displayed": [ - -8.58814448742e-13, 4.30206836317e-12, 1.86531326558e-12, 0.732896838001, -0.993109045967, - 2.17004579673e-21, -0.0416281368215 - ], - "register": [ - 0, 0, 0, 5449458.15847, 3288103.32257, 285688.903665, -0.509274526102, 0.82184693181, - 0.255356765599, 0.854430944198, 0.447375126055, 0.264203062404, -0.102894211013, - -0.352736611739, 0.930048205245 - ], - "memory": 1.34040951447 - }, - { - "label": "pan move 0", - "arm": "earth", - "hr": 0.00000240000079321, - "displayed": [ - -8.58814448833e-13, 4.30206836309e-12, 1.86531326555e-12, 0.732896535328, -0.993109039083, - 2.17004579682e-21, -0.0416278013131 - ], - "register": [ - 0, 0, 0, 5449460.14201, 3288100.12161, 285687.909089, -0.509274005764, 0.821847253697, - 0.255356767375, 0.854431244195, 0.447374641933, 0.264202911982, -0.102894295261, - -0.352736475781, 0.930048247488 - ], - "memory": 1.34040951447 - }, - { - "label": "pan end", - "arm": "earth", - "hr": 0.00000239999977647, - "displayed": [ - -8.58814449131e-13, 4.3020683628e-12, 1.86531326519e-12, 0.732895294537, -0.993110199781, - 2.17004579699e-21, -0.0416265515011 - ], - "register": [ - 0, 0, 0, 5449467.12076, 3288089.5259, 285676.740295, -0.509271985061, 0.821848499612, - 0.255356787489, 0.854432532278, 0.447373179523, 0.264201222609, -0.102893600465, - -0.352735427662, 0.930048721872 - ], - "memory": 1.34040951447 - }, - { - "label": "tilt 2 move 0", - "arm": "earth", - "hr": 0.00000239999905194, - "displayed": [ - -8.58814449397e-13, 4.30206836264e-12, 1.86531326635e-12, 0.725551047161, -1.07680251242, - 3.39220444085e-21, -0.0479469701107 - ], - "register": [ - 0, 0, 0, 5449467.00465, 3288089.82096, 285675.559116, -0.509271985061, 0.821848499612, - 0.255356787489, 0.860045831705, 0.475320318668, 0.185450160495, -0.0310356665523, - -0.314063112043, 0.948894698613 - ], - "memory": 1.42418512661 - }, - { - "label": "tilt 2 end", - "arm": "earth", - "hr": 0.00000239999997409, - "displayed": [ - -8.58814447958e-13, 4.30206836244e-12, 1.86531326281e-12, -2.35006327372, -0.807403215039, - 4.95528116483e-22, -3.10871731778 - ], - "register": [ - 0, 0, 0, 5449465.31106, 3288094.12462, 285658.33045, -0.509271985061, 0.821848499612, - 0.255356787489, 0.29528545085, 0.445573825476, -0.845145235191, 0.808361644159, - 0.355005647469, 0.46959817133 - ], - "memory": 2.68081938808 - }, - { - "label": "look move 0", - "arm": "earth", - "hr": 0.00000239999988616, - "displayed": [ - -8.58814447966e-13, 4.30206836243e-12, 1.86531326281e-12, -2.38373364598, -0.807339449685, - 4.95528116483e-22, 3.10323965376 - ], - "register": [ - 0, 0, 0, 5449465.31106, 3288094.12462, 285658.33045, -0.503605228001, 0.808095902874, - 0.305553245912, 0.271528452232, 0.483804112839, -0.831989110523, 0.820154908513, - 0.336027665763, 0.463067310316 - ], - "memory": 2.68081938808 - }, - { - "label": "look end", - "arm": "earth", - "hr": 0.00000239999997409, - "displayed": [ - -8.5881444796e-13, 4.30206836242e-12, 1.86531326278e-12, -2.40944022527, -0.753557836751, - 4.95528116483e-22, 3.03763473241 - ], - "register": [ - 0, 0, 0, 5449465.31106, 3288094.12462, 285658.33045, -0.496558124016, 0.792128371437, - 0.354912204127, 0.204196630132, 0.504011632198, -0.839211541181, 0.843643150696, - 0.344245432466, 0.412021014646 - ], - "memory": 2.73317926564 - }, - { - "label": "recede brisk 0", - "arm": "earth", - "hr": 0.00000323966149196, - "displayed": [ - -8.58814447761e-13, 4.30206836216e-12, 1.86531326286e-12, -2.40944022527, -0.753557836751, - 6.68892992433e-22, 3.03763473241 - ], - "register": [ - 0, 0, 0, 5449469.88675, 3288096.88549, 285658.570306, -0.496558124016, 0.792128371437, - 0.354912204127, 0.204196630132, 0.504011632198, -0.839211541181, 0.843643150696, - 0.344245432466, 0.412021014646 - ], - "memory": 2.73317926564 - }, - { - "label": "recede brisk 35", - "arm": "earth", - "hr": 0.117649922728, - "displayed": [ - -8.58795358288e-13, 4.3020357795e-12, 1.86533371378e-12, -2.62627933297, -1.30978913525, - 2.42911852551e-17, 2.84396199283 - ], - "register": [ - 0, 0, 0, 6090579.86642, 3674929.32447, 319265.244717, -0.496558124016, 0.792128371437, - 0.354912204127, 0.62387405745, 0.60998688806, -0.488566430319, 0.603499121717, - 0.0211811132692, 0.79708228592 - ], - "memory": 2.73317926564 - }, - { - "label": "recede 0", - "arm": "earth", - "hr": 0.130023273112, - "displayed": [ - -8.58796303391e-13, 4.3020346161e-12, 1.86533761691e-12, -3.00081954901, -1.44805394845, - 2.68459115096e-17, 2.47527892143 - ], - "register": [ - 0, 0, 0, 6158007.84829, 3715613.96753, 322799.786848, -0.496558124016, 0.792128371437, - 0.354912204127, 0.70873235831, 0.606064735209, -0.361087220794, 0.501127203171, - -0.0722369704604, 0.86235337672 - ], - "memory": 2.73317926564 - }, - { - "label": "recede 3", - "arm": "earth", - "hr": 0.1755130604, - "displayed": [ - -8.58802801727e-13, 4.30203560122e-12, 1.86534854787e-12, 0.974192521948, -1.15078859777, - 3.62381900986e-17, 0.186667782595 - ], - "register": [ - 0, 0, 0, 6405902.27118, 3865188.31086, 335794.29235, -0.496558124016, 0.792128371437, - 0.354912204127, 0.863198945858, 0.493605329488, 0.106025273267, 0.0912009284136, - -0.359007551266, 0.928868111623 - ], - "memory": 2.73317926564 - }, - { - "label": "recede 19", - "arm": "absolute", - "hr": 0.904220856109, - "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.782643157474, 0.267207821923, - 3.93164572574e-16, 0.0785409051511 - ], - "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.782643157474, 0.267207821923, - 3.93164572574e-16, 0.0785409051511 - ], - "memory": 2.73317926564 - }, - { - "label": "recede world 0", - "arm": "absolute", - "hr": 0.999318593689, - "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.782643157474, 0.267207821923, - 4.12799407067e-16, 0.0785409051511 - ], - "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.782643157474, 0.267207821923, - 4.12799407067e-16, 0.0785409051511 - ], - "memory": 2.73317926564 - }, - { - "label": "recede world 7", - "arm": "absolute", - "hr": 2.01238052367, - "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.782643157474, 0.267207821923, - 6.21966352914e-16, 0.0785409051511 - ], - "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.782643157474, 0.267207821923, - 6.21966352914e-16, 0.0785409051511 - ], - "memory": 2.73317926564 - }, - { - "label": "re-dive 0", - "arm": "absolute", - "hr": 1.82087719714, - "displayed": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.782643157474, 0.267207821923, - 5.82426651792e-16, 0.0785409051511 - ], - "register": [ - -8.58950454652e-13, 4.30222343256e-12, 1.86530400502e-12, 0.782643157474, 0.267207821923, - 5.82426651792e-16, 0.0785409051511 - ], - "memory": 2.73317926564 - }, - { - "label": "re-dive 14", - "arm": "earth", - "hr": 0.449022788525, - "displayed": [ - -8.58812915346e-13, 4.30206950158e-12, 1.86530825661e-12, 0.792064079893, 0.148535184229, - 9.36724058737e-17, 0.0785409051511 - ], - "register": [ - 0, 0, 0, 7762490.13457, 4989271.48472, -275042.547948, -0.495946113036, 0.791703341311, - 0.356711749627, 0.314704012642, -0.218991606864, 0.923582189386, -0.809319984549, - -0.570305615859, 0.140544182121 - ], - "memory": 2.73317926564 - }, - { - "label": "re-dive engaged 0", - "arm": "earth", - "hr": 0.404742861533, - "displayed": [ - -8.58813375988e-13, 4.30206906302e-12, 1.86530706557e-12, 0.801544273355, 0.0594168216069, - 8.55816312919e-17, 0.0775559827398 - ], - "register": [ - 0, 0, 0, 7485120.5034, 4881334.78478, -491102.955744, -0.497894230725, 0.790278686588, - 0.357156733852, 0.38579097349, -0.167005848978, 0.907344681574, -0.776702426792, - -0.589549526276, 0.221731135123 - ], - "memory": 2.73317926564 - }, - { - "label": "re-dive engaged 3", - "arm": "earth", - "hr": 0.266757755343, - "displayed": [ - -8.58814922354e-13, 4.30206767835e-12, 1.86530523177e-12, 0.863342306848, -0.376693539653, - 6.70660395683e-17, 0.0879707615574 - ], - "register": [ - 0, 0, 0, 6556743.55339, 4560834.9573, -1158054.05414, -0.514400153683, 0.774691173384, - 0.367758164793, 0.677083432359, 0.103729462365, 0.728559005341, -0.526260874013, - -0.62377382481, 0.577889010075 - ], - "memory": 2.73317926564 - } - ] -} +{"northUp":[{"label":"start","arm":"absolute","hr":5,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.23882029118e-15,0],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.23882029118e-15,0],"memory":0},{"label":"dive 0","arm":"absolute","hr":4.69959329559,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.17679530435e-15,7.36444650118e-18],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.17679530435e-15,7.36444650118e-18],"memory":0},{"label":"dive 20","arm":"earth","hr":0.408531953689,"displayed":[-8.58823383762e-13,4.30208382526e-12,1.86524674597e-12,0.7,0.299999999999,8.43496123044e-17,-0.0199073133356],"register":[0,0,0,7933158.87757,4190780.53426,-175113.714869,-0.449694432696,0.861174548827,0.236966904135,0.127469437126,-0.200712858741,0.971321723702,-0.88403985193,-0.467004009393,0.01951397986],"memory":0},{"label":"dive 39","arm":"earth","hr":0.0615681608771,"displayed":[-8.58815842393e-13,4.30209013615e-12,1.86525828107e-12,0.763865807331,0.340617091539,1.27119833965e-17,-0.255756288997],"register":[0,0,0,5819022.64155,3438051.86992,245633.027437,-0.509248730504,0.860307829721,0.0231553147304,-0.0189799082836,-0.0381256553536,0.999092687134,-0.860410072908,-0.508347196829,-0.0357440052922],"memory":0},{"label":"dive brisk 0","arm":"earth","hr":0.0456277801724,"displayed":[-8.58815437959e-13,4.30209050575e-12,1.86525869645e-12,0.767221245664,0.342024316219,9.42077070425e-18,-0.270221482753],"register":[0,0,0,5722201.57312,3401370.42056,255466.623119,-0.51124791987,0.859378254518,0.0097252295442,-0.0276626493212,-0.0277645251701,0.999231659314,-0.858987975621,-0.510586081678,-0.0379672350284],"memory":0},{"label":"dive brisk 35","arm":"earth","hr":0.00000240000070995,"displayed":[-8.58814244804e-13,4.30209160677e-12,1.86525980005e-12,0.77694673987,0.34536113635,4.95528126503e-22,-0.282909410527],"register":[0,0,0,5445555.10972,3295180.12262,278483.997844,-0.517709104819,0.855556709276,-3.87938092761e-9,-0.0373973149811,-0.0226296239348,0.999044213713,-0.854738979817,-0.517214285701,-0.0437110860907],"memory":0},{"label":"tilt 1 move 0","arm":"earth","hr":0.00000248496508704,"displayed":[-8.58814244804e-13,4.30209160677e-12,1.86525980005e-12,0.752741906691,0.264826976113,5.1487696481e-22,-0.275632471139],"register":[0,0,0,5445555.62154,3295180.43233,278482.708845,-0.517709104819,0.855556709276,-3.87938117741e-9,0.0342565571094,0.0207291173096,0.999198074453,-0.854870616574,-0.517293940529,0.0400400800377],"memory":0.0837755978623},{"label":"tilt 1 move 3","arm":"earth","hr":0.00000263277160673,"displayed":[-8.58814244804e-13,4.30209160677e-12,1.86525980006e-12,0.684990514444,0.02256104076,5.7560563135e-22,-0.265855190469],"register":[0,0,0,5445556.58153,3295181.01323,278478.606315,-0.517709104819,0.855556709276,-3.87938191293e-9,0.245778001551,0.148723645672,0.957848866561,-0.819494024835,-0.495887078306,0.287272603686],"memory":0.33510235913},{"label":"tilt 1 end","arm":"earth","hr":0.0000023999998191,"displayed":[-8.58814244845e-13,4.30209160682e-12,1.86525980147e-12,0.304162643125,-0.931255732717,2.17004580513e-21,-0.455717746089],"register":[0,0,0,5445555.90789,3295180.6056,278462.674384,-0.517709104819,0.855556709276,-3.87938481339e-9,0.823616128798,0.498381422508,0.27068880672,-0.231589626648,-0.140138056616,0.962666904966],"memory":1.34040951538},{"label":"pan move 0","arm":"earth","hr":0.00000239999969298,"displayed":[-8.5881424494e-13,4.30209160674e-12,1.86525980147e-12,0.304162393914,-0.931255659819,2.17004580504e-21,-0.455717356973],"register":[0,0,0,5445557.92428,3295177.27335,278462.674378,-0.517708581286,0.855557026073,-3.8793856183e-9,0.823616433768,0.49838091852,0.270688806719,-0.231589712401,-0.140137914902,0.962666904966],"memory":1.34040951538},{"label":"pan end","arm":"earth","hr":0.00000240000053964,"displayed":[-8.588142453e-13,4.30209160641e-12,1.86525980122e-12,0.304160550871,-0.931256443589,2.17004580513e-21,-0.455716549823],"register":[0,0,0,5445566.032,3295164.51355,278455.113546,-0.517706549676,0.855558255423,-3.87938579871e-9,0.823617892326,0.498379129223,0.270687663177,-0.231589066805,-0.140136772948,0.962667226514],"memory":1.34040951538},{"label":"tilt 2 move 0","arm":"earth","hr":0.00000240000208973,"displayed":[-8.58814245335e-13,4.30209160644e-12,1.86525980243e-12,0.23536204091,-1.00544579425,3.39220446157e-21,-0.512449439596],"register":[0,0,0,5445566.07774,3295164.54122,278453.891718,-0.517706549676,0.855558255423,-3.87938578483e-9,0.840108222102,0.508357586402,0.189184406081,-0.161858282392,-0.0979420028656,0.981941576926],"memory":1.42418512751},{"label":"tilt 2 end","arm":"earth","hr":0.0000023999991996,"displayed":[-8.58814244608e-13,4.30209160562e-12,1.86525979876e-12,-2.18873636672,-0.782395233589,4.95528116513e-22,-2.76219997364],"register":[0,0,0,5445566.74476,3295164.94484,278436.070134,-0.517706549676,0.855558255423,-3.87938620117e-9,0.413544091929,0.250239513462,-0.875420738807,0.748973439084,0.453211051807,0.483361696938],"memory":2.68081938898},{"label":"look move 0","arm":"earth","hr":0.00000239999928509,"displayed":[-8.58814244617e-13,4.30209160561e-12,1.86525979876e-12,-2.21974811218,-0.790268249161,4.95528116513e-22,-2.83105142834],"register":[0,0,0,5445566.74476,3295164.94484,278436.070134,-0.518953941777,0.853201609233,0.0522859476041,0.38922925736,0.290318414674,-0.874194373876,0.761043619991,0.433315395645,0.482752914405],"memory":2.68081938898},{"label":"look end","arm":"earth","hr":0.00000240000213503,"displayed":[-8.58814244616e-13,4.3020916056e-12,1.86525979873e-12,-2.26868065666,-0.74568333147,4.95528116513e-22,-2.91256664702],"register":[0,0,0,5445566.74476,3295164.94484,278436.070134,-0.518778917195,0.848506396737,0.10442858694,0.323926898868,0.308138328882,-0.894495463635,0.791163573014,0.43021815974,0.434709714368],"memory":2.73317926654},{"label":"recede brisk 0","arm":"earth","hr":0.00000323966126414,"displayed":[-8.58814244392e-13,4.30209160535e-12,1.86525979882e-12,-2.23721013181,-0.739329340615,6.68892992494e-22,-2.83572280702],"register":[0,0,0,5445571.31718,3295167.71166,278436.303925,-0.518852758383,0.853724227906,0.0441220784167,0.352762011006,0.260834442443,-0.898623590402,0.778685288617,0.450688735516,0.436496145424],"memory":2.73317926654},{"label":"recede brisk 35","arm":"earth","hr":0.11764992274,"displayed":[-8.58790612634e-13,4.30206304827e-12,1.86528148545e-12,-1.73704603665,-1.24764085932,2.42911852576e-17,-2.40941359841],"register":[0,0,0,6086222.64462,3682832.00722,311193.305409,-0.51898218053,0.852996952088,0.0552602571646,0.711374857916,0.466850497001,-0.525353618979,0.473923314279,0.233338409138,0.849087674509],"memory":2.73317926654},{"label":"recede 0","arm":"earth","hr":0.130023273126,"displayed":[-8.58790902658e-13,4.30206246655e-12,1.86528562957e-12,-1.30186041478,-1.35142240492,2.68459115123e-17,-1.99679807879],"register":[0,0,0,6153602.38829,3723604.13981,314638.484196,-0.519054562022,0.851821354531,0.0705885373798,0.77198370136,0.502648226471,-0.389083442541,0.366910788174,0.147462335498,0.918494057211],"memory":2.73317926654},{"label":"recede 3","arm":"earth","hr":0.175513060418,"displayed":[-8.58796269929e-13,4.3020644555e-12,1.86529713172e-12,0.394311083513,-1.13068223763,3.62381901023e-17,-0.359788132177],"register":[0,0,0,6401319.46668,3873500.13249,327304.451403,-0.518387722124,0.846342418415,0.122387419055,0.854624935456,0.507744775192,0.108680554666,-0.0298393908959,-0.160934005283,0.986513992143],"memory":2.73317926654},{"label":"recede 19","arm":"absolute","hr":0.904220833938,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,3.93164567996e-16,-5.55111512313e-17],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,3.93164567996e-16,-5.55111512313e-17],"memory":2.73317926654},{"label":"recede world 0","arm":"absolute","hr":0.999318569187,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,4.12799402008e-16,1.38777878078e-17],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,4.12799402008e-16,1.38777878078e-17],"memory":2.73317926654},{"label":"recede world 7","arm":"absolute","hr":2.01238047432,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,6.21966342726e-16,-5.55738316694e-18],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,6.21966342726e-16,-5.55738316694e-18],"memory":2.73317926654},{"label":"re-dive 0","arm":"absolute","hr":1.82087715249,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,5.82426642574e-16,-5.96791886065e-17],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,5.82426642574e-16,-5.96791886065e-17],"memory":2.73317926654},{"label":"re-dive 14","arm":"earth","hr":0.449022777516,"displayed":[-8.58813615026e-13,4.30209194307e-12,1.86525445823e-12,0.770526869984,0.146645730242,9.36724036408e-17,-0.0125259023887],"register":[0,0,0,7807994.71433,4915356.7519,-314986.806499,-0.506032322158,0.820749515208,0.265144342232,0.268413886155,-0.142292603399,0.952736480217,-0.819686082992,-0.553283876779,0.148295910413],"memory":2.73317926654},{"label":"re-dive engaged 0","arm":"earth","hr":0.404990357704,"displayed":[-8.58814134638e-13,4.30209146175e-12,1.86525332033e-12,0.766060308078,0.0569265954535,8.56332551382e-17,-0.0146819552961],"register":[0,0,0,7548603.9725,4780283.67862,-539752.097825,-0.504222807269,0.822850390028,0.26206219922,0.339575685799,-0.0900952685041,0.936253809716,-0.794007376694,-0.561070475273,0.233991896297],"memory":2.73317926654},{"label":"re-dive engaged 3","arm":"earth","hr":0.268610130415,"displayed":[-8.58815551831e-13,4.3020902092e-12,1.86525170703e-12,0.730401394984,-0.381240094609,6.74071828526e-17,-0.0418799467016],"register":[0,0,0,6703618.31588,4337564.0694,-1253338.52376,-0.498422031517,0.834725566235,0.234112595928,0.637177733564,0.169602983375,0.751824024543,-0.587860539881,-0.523896990928,0.616402407967],"memory":2.73317926654}],"northUpOff":[{"label":"start","arm":"absolute","hr":5,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.23882029118e-15,0],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.23882029118e-15,0],"memory":0},{"label":"dive 0","arm":"absolute","hr":4.69959329559,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.17679530435e-15,0],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.17679530435e-15,0],"memory":0},{"label":"dive 20","arm":"earth","hr":0.408531953689,"displayed":[-8.58823383762e-13,4.30208382526e-12,1.86524674597e-12,0.7,0.299999999999,8.43496123044e-17,0],"register":[0,0,0,7933158.87757,4190780.53426,-175113.714869,-0.447067922013,0.85700852238,0.256255079316,0.136395796513,-0.217815627948,0.966412199279,-0.88403985193,-0.467004009393,0.01951397986],"memory":0},{"label":"dive 39","arm":"earth","hr":0.0615621239606,"displayed":[-8.58815721031e-13,4.30209022991e-12,1.86525805271e-12,0.763746321348,0.339489149201,1.27107388113e-17,-0.0200470287363],"register":[0,0,0,5816774.57098,3442304.40762,238147.977164,-0.499526477634,0.82767106602,0.25580051723,0.101423426099,-0.237373932188,0.966109157888,-0.860340971232,-0.508541269506,-0.0346293290295],"memory":0},{"label":"dive brisk 0","arm":"earth","hr":0.0456251399424,"displayed":[-8.58815389035e-13,4.30209054158e-12,1.86525856693e-12,0.767005933745,0.341447327804,9.42022602832e-18,-0.0211355075587],"register":[0,0,0,5721375.78023,3403037.18244,251292.223917,-0.502132641784,0.826126332225,0.255691402398,0.0996859833328,-0.238403754719,0.966036414667,-0.859025910425,-0.510567265834,-0.0373570913832],"memory":0},{"label":"dive brisk 35","arm":"earth","hr":0.0000023999987333,"displayed":[-8.58814448428e-13,4.30209144188e-12,1.86526003613e-12,0.775991805555,0.346814022309,4.9552812602e-22,-0.0241671760466],"register":[0,0,0,5449456.70472,3288098.79604,285768.720091,-0.50927763239,0.821847491179,0.255348770098,0.0949226562623,-0.241256315416,0.965807993133,-0.855351379537,-0.516102791617,-0.0448544982087],"memory":0},{"label":"tilt 1 move 0","arm":"earth","hr":0.0000024849657001,"displayed":[-8.58814448428e-13,4.30209144188e-12,1.86526003613e-12,0.773897707698,0.263062016837,5.14876964263e-22,-0.023537904874],"register":[0,0,0,5449457.04302,3288099.39241,285767.475381,-0.509277636295,0.821847488822,0.255348769893,0.166163707683,-0.197223826706,0.966174096335,-0.844408616381,-0.534480558389,0.0361195416064],"memory":0.0837756009065},{"label":"tilt 1 move 3","arm":"earth","hr":0.00000263277086576,"displayed":[-8.58814448431e-13,4.30209144188e-12,1.86526003614e-12,0.768044168272,0.0118011273059,5.75605630761e-22,-0.0227296011328],"register":[0,0,0,5449457.45477,3288100.87905,285763.51178,-0.509277636295,0.821847488822,0.255348769893,0.370939252781,-0.0581077677636,0.926837395702,-0.776556733224,-0.566736440016,0.275262506787],"memory":0.335102362174},{"label":"tilt 1 end","arm":"earth","hr":0.0000023999997163,"displayed":[-8.58814448742e-13,4.30209144169e-12,1.8652600375e-12,0.732896834484,-0.993109049844,2.17004583329e-21,-0.0416281402392],"register":[0,0,0,5449454.67867,3288103.94536,285748.10602,-0.509277636295,0.821847488822,0.255348769893,0.854427726547,0.447375703741,0.264212489882,-0.102905535695,-0.352734581263,0.93004772238],"memory":1.34040951843},{"label":"pan move 0","arm":"earth","hr":0.00000239999935414,"displayed":[-8.58814448833e-13,4.3020914416e-12,1.86526003747e-12,0.7328965318,-0.99310904296,2.17004583338e-21,-0.0416278046814],"register":[0,0,0,5449456.66222,3288100.7444,285747.111475,-0.509277115907,0.821847810737,0.255348771684,0.854428026575,0.447375219571,0.264212339449,-0.102905619947,-0.3527344453,0.930047764624],"memory":1.34040951843},{"label":"pan end","arm":"earth","hr":0.00000240000002849,"displayed":[-8.58814449131e-13,4.30209144132e-12,1.86526003711e-12,0.732895290976,-0.993110203657,2.17004583347e-21,-0.0416265549024],"register":[0,0,0,5449463.64111,3288090.14867,285735.942789,-0.509275095207,0.821849056657,0.255348791801,0.854429314677,0.447373757161,0.264210650097,-0.102904925166,-0.352733397165,0.930048239018],"memory":1.34040951843},{"label":"tilt 2 move 0","arm":"earth","hr":0.00000240000012441,"displayed":[-8.58814449397e-13,4.30209144116e-12,1.86526003827e-12,0.725551042947,-1.07680251628,3.39220453137e-21,-0.0479469740881],"register":[0,0,0,5449463.52501,3288090.44372,285734.761608,-0.509275095207,0.821849056657,0.255348791801,0.860043573015,0.475320724373,0.185459595323,-0.0310472207774,-0.314061040331,0.948895006325],"memory":1.42418513056},{"label":"tilt 2 end","arm":"earth","hr":0.00000239999946028,"displayed":[-8.58814447958e-13,4.30209144096e-12,1.86526003473e-12,-2.35006327273,-0.807403211062,4.95528116483e-22,-3.10871731543],"register":[0,0,0,5449461.83163,3288094.74734,285717.532912,-0.509275095207,0.821849056657,0.255348791801,0.295295741598,0.445571980531,-0.845142612321,0.808355925565,0.355006673512,0.469607239473],"memory":2.68081939203},{"label":"look move 0","arm":"earth","hr":0.00000240000239859,"displayed":[-8.58814447966e-13,4.30209144095e-12,1.86526003472e-12,-2.38373364458,-0.807339445766,4.95528116483e-22,3.10323965641],"register":[0,0,0,5449461.83163,3288094.74734,285717.532912,-0.503608949544,0.808096569413,0.305545349241,0.271538582696,0.483802296668,-0.831986860381,0.820149269369,0.336028677709,0.463076563551],"memory":2.68081939203},{"label":"look end","arm":"earth","hr":0.00000239999978224,"displayed":[-8.5881444796e-13,4.30209144094e-12,1.8652600347e-12,-2.40944022372,-0.753557832895,4.95528116483e-22,3.03763473516],"register":[0,0,0,5449461.83163,3288094.74734,285717.532912,-0.496562446757,0.792129145641,0.354904428116,0.20420684848,0.504009800245,-0.839210155021,0.843638133032,0.344246333143,0.412030536019],"memory":2.73317926959},{"label":"recede brisk 0","arm":"earth","hr":0.0000032396613292,"displayed":[-8.58814447761e-13,4.30209144068e-12,1.86526003478e-12,-2.40944022372,-0.753557832895,6.68892992433e-22,3.03763473516],"register":[0,0,0,5449466.40732,3288097.50822,285717.772817,-0.496562446757,0.792129145641,0.354904428116,0.20420684848,0.504009800245,-0.839210155021,0.843638133032,0.344246333143,0.412030536019],"memory":2.73317926959},{"label":"recede brisk 35","arm":"earth","hr":0.117649922735,"displayed":[-8.58795358288e-13,4.30205885827e-12,1.8652804861e-12,-2.62627932389,-1.30978913277,2.42911852565e-17,2.84396200263],"register":[0,0,0,6090575.97768,3674930.02048,319331.412187,-0.496562446757,0.792129145641,0.354904428116,0.623880006388,0.609985822238,-0.48856016446,0.603489415069,0.0211828537717,0.797089588821],"memory":2.73317926959},{"label":"recede 0","arm":"earth","hr":0.130023273121,"displayed":[-8.58796303391e-13,4.30205769492e-12,1.86528438925e-12,-3.00081952413,-1.44805394725,2.6845911511e-17,2.47527894691],"register":[0,0,0,6158003.91651,3715614.67124,322866.686849,-0.496562446757,0.792129145641,0.354904428116,0.708736754964,0.606063947721,-0.361079912812,0.501116701612,-0.0722350877246,0.862359636965],"memory":2.73317926959},{"label":"recede 3","arm":"earth","hr":0.175513060412,"displayed":[-8.58802801726e-13,4.30205868017e-12,1.86529532019e-12,0.974192518494,-1.15078860015,3.62381901006e-17,0.186667779321],"register":[0,0,0,6405898.18113,3865189.04291,335863.885453,-0.496562446757,0.792129145641,0.354904428116,0.863197654257,0.493605561447,0.10603470844,0.0911896164954,-0.359005524099,0.928870005711],"memory":2.73317926959},{"label":"recede 19","arm":"absolute","hr":0.904220834056,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,3.9316456802e-16,0.0785409033636],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,3.9316456802e-16,0.0785409033636],"memory":2.73317926959},{"label":"recede world 0","arm":"absolute","hr":0.999318569316,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,4.12799402035e-16,0.0785409033636],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,4.12799402035e-16,0.0785409033636],"memory":2.73317926959},{"label":"recede world 7","arm":"absolute","hr":2.01238047459,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,6.2196634278e-16,0.0785409033636],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,6.2196634278e-16,0.0785409033636],"memory":2.73317926959},{"label":"re-dive 0","arm":"absolute","hr":1.82087715273,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,5.82426642623e-16,0.0785409033636],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,5.82426642623e-16,0.0785409033636],"memory":2.73317926959},{"label":"re-dive 14","arm":"earth","hr":0.449022777574,"displayed":[-8.58812915346e-13,4.30209258004e-12,1.86525502852e-12,0.79206408103,0.148535164831,9.36724038973e-17,0.0785409033636],"register":[0,0,0,7762493.42556,4989270.84689,-274958.882602,-0.495950457609,0.791704119425,0.356703982153,0.314692779118,-0.218989581328,0.923586497325,-0.809321690287,-0.570305313456,0.14053558651],"memory":2.73317926959},{"label":"re-dive engaged 0","arm":"earth","hr":0.404742850815,"displayed":[-8.58813375989e-13,4.30209214146e-12,1.86525383748e-12,0.801544274338,0.0594167972848,8.55816295471e-17,0.0775559811744],"register":[0,0,0,7485126.427,4881333.67662,-491022.434865,-0.49789858041,0.790279465874,0.357148945741,0.385779940944,-0.167003855664,0.907349739273,-0.776705118299,-0.589549046315,0.221722983015],"memory":2.73317926959},{"label":"re-dive engaged 3","arm":"earth","hr":0.266757742279,"displayed":[-8.58814922355e-13,4.30209075677e-12,1.8652520037e-12,0.863342306569,-0.376693585659,6.70660385912e-17,0.0879707620004],"register":[0,0,0,6556757.59107,4560832.38104,-1157984.13863,-0.514404629972,0.774691977732,0.367750209111,0.67707458349,0.103731078593,0.72856699879,-0.526267883359,-0.623772557084,0.577883995256],"memory":2.73317926959}]} diff --git a/tests/services/engine/frame/earthTerminator.test.ts b/tests/services/engine/frame/earthTerminator.test.ts index a87a0cec9..9c46cb06f 100644 --- a/tests/services/engine/frame/earthTerminator.test.ts +++ b/tests/services/engine/frame/earthTerminator.test.ts @@ -14,7 +14,7 @@ import { cubeSphereMesh } from '../../../../src/utils/math/cubeSphereMesh'; * Sub-solar longitude at 14:00 UTC: the Sun crosses the Greenwich meridian near * 12:00 UTC and the sub-solar point moves 15°/hour westward, so at 14:00 it sits * near -30°. Cross-checked via sidereal time: GMST ≈ 149.3°, the Sun's apparent - * right ascension ≈ 120.6° (ecliptic longitude ~118.5°, obliquity 23.44°), so the + * right ascension ≈ 120.6° (ecliptic longitude ~118.5°, obliquity 23.439°), so the * sub-solar geographic longitude = RA - GMST ≈ -28.7°. Sub-solar latitude ≈ +20.5° * (late-July solar declination). Denmark at (10°E, 56°N) is ~39° of longitude east * of that meridian — well inside the 90° day hemisphere — so it MUST be lit. diff --git a/tests/utils/orbit/propagateElements.test.ts b/tests/utils/orbit/propagateElements.test.ts index 123152deb..f769e06e5 100644 --- a/tests/utils/orbit/propagateElements.test.ts +++ b/tests/utils/orbit/propagateElements.test.ts @@ -91,7 +91,7 @@ describe('propagateElements', () => { // elements through ECLIPTIC_FRAME), matching Horizons' ICRF frame to sub- // arcsecond bias. Tolerance is arcminute-class: the mean-element model differs // from the true DE ephemeris by ~1 arcmin (~2e-4 au at 1 au), and the scene's - // rounded 23.44° obliquity adds ~2 arcsec. A frame swap, unit slip, or sign + // scene's J2000 obliquity is exact, so it adds none. A frame swap, unit slip, or sign // error in a rate would miss by ≥0.05 au and fail loudly. const simDays = 2_460_676.5; const horizonsAu: Vec3 = [-1.786710910310161e-1, 8.871846912692936e-1, 3.845832338744293e-1]; diff --git a/tools/perf/perfScenarios.ts b/tools/perf/perfScenarios.ts index 1e201da97..0e652fb78 100644 --- a/tools/perf/perfScenarios.ts +++ b/tools/perf/perfScenarios.ts @@ -49,7 +49,7 @@ export type PerfScenario = { readonly name: string; readonly pose: PerfPose }; // time). // Factored out so those poses differ only in the axes that actually vary // (distance/yaw/pitch) and the target can never drift between them. -const EARTH_TARGET: Vec3 = [-8.5895045e-13, 4.3022234e-12, 1.865304e-12]; +const EARTH_TARGET: Vec3 = [-8.5895045e-13, 4.3022465e-12, 1.8652508e-12]; // The shared look-at point for `milky-way-outside` and `milky-way-close`: the // galactic centre (~8 kpc off the origin — contrast `EARTH_TARGET`'s ~1 AU). From 973bdb389432e3d2a74f23237f0f6e339e2150d5 Mon Sep 17 00:00:00 2001 From: Alexander Rulkens Date: Sun, 4 Oct 2026 09:31:46 +0200 Subject: [PATCH 04/14] Ephemeris correction evaluator --- src/@types/scene/EphemerisCorrection.d.ts | 19 +++++++ src/utils/orbit/ephemerisCorrectionMpc.ts | 31 +++++++++++ .../orbit/ephemerisCorrectionMpc.test.ts | 54 +++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 src/@types/scene/EphemerisCorrection.d.ts create mode 100644 src/utils/orbit/ephemerisCorrectionMpc.ts create mode 100644 tests/utils/orbit/ephemerisCorrectionMpc.test.ts diff --git a/src/@types/scene/EphemerisCorrection.d.ts b/src/@types/scene/EphemerisCorrection.d.ts new file mode 100644 index 000000000..7fb3c3192 --- /dev/null +++ b/src/@types/scene/EphemerisCorrection.d.ts @@ -0,0 +1,19 @@ +/** + * EphemerisCorrection — one planet's fitted `Horizons − Kepler` residual over a span, + * in km: a cubic in normalised time plus a sum of sinusoids. `ephemerisCorrectionMpc` + * evaluates it; `tools/bodies/buildPlanetEphemeris.ts` generates it. Outside the span + * the edge value is held (simDays is clamped), so dates past either end stay finite. + */ + +import type { Vec3 } from '../math/Vec3'; + +export type EphemerisCorrection = { + /** Fitted span start, UTC Julian date. */ + readonly startJd: number; + /** Fitted span end, UTC Julian date. */ + readonly endJd: number; + /** Coefficients of τ⁰..τ³ in km, τ = 2(t − startJd)/(endJd − startJd) − 1. */ + readonly polyKm: readonly [Vec3, Vec3, Vec3, Vec3]; + /** Flat, 7 per term: ω (rad/day), cos x,y,z (km), sin x,y,z (km); phase ω·(t − startJd). */ + readonly terms: readonly number[]; +}; diff --git a/src/utils/orbit/ephemerisCorrectionMpc.ts b/src/utils/orbit/ephemerisCorrectionMpc.ts new file mode 100644 index 000000000..2f66def42 --- /dev/null +++ b/src/utils/orbit/ephemerisCorrectionMpc.ts @@ -0,0 +1,31 @@ +/** + * ephemerisCorrectionMpc — evaluate a planet's fitted `Horizons − Kepler` correction + * at `simDays` as an equatorial-world offset in Mpc. Summed in f64 km and converted + * once at the end. `simDays` is clamped to the span, so the edge value is held outside it. + */ + +import type { EphemerisCorrection } from '../../@types/scene/EphemerisCorrection'; +import type { Vec3 } from '../../@types/math/Vec3'; +import { SCALE_UNITS } from '../../data/scaleUnits'; + +const FLOATS_PER_TERM = 7; + +export function ephemerisCorrectionMpc(c: EphemerisCorrection, simDays: number): Vec3 { + const dt = Math.min(Math.max(simDays, c.startJd), c.endJd) - c.startJd; + const tau = (2 * dt) / (c.endJd - c.startJd) - 1; + const [p0, p1, p2, p3] = c.polyKm; + let x = p0[0] + tau * (p1[0] + tau * (p2[0] + tau * p3[0])); + let y = p0[1] + tau * (p1[1] + tau * (p2[1] + tau * p3[1])); + let z = p0[2] + tau * (p1[2] + tau * (p2[2] + tau * p3[2])); + const terms = c.terms; + for (let i = 0; i < terms.length; i += FLOATS_PER_TERM) { + const phase = terms[i] * dt; + const cos = Math.cos(phase); + const sin = Math.sin(phase); + x += terms[i + 1] * cos + terms[i + 4] * sin; + y += terms[i + 2] * cos + terms[i + 5] * sin; + z += terms[i + 3] * cos + terms[i + 6] * sin; + } + const k = SCALE_UNITS.KM_TO_MPC; + return [x * k, y * k, z * k]; +} diff --git a/tests/utils/orbit/ephemerisCorrectionMpc.test.ts b/tests/utils/orbit/ephemerisCorrectionMpc.test.ts new file mode 100644 index 000000000..e245952ee --- /dev/null +++ b/tests/utils/orbit/ephemerisCorrectionMpc.test.ts @@ -0,0 +1,54 @@ +import { describe, it, expect } from 'vitest'; +import { ephemerisCorrectionMpc } from '../../../src/utils/orbit/ephemerisCorrectionMpc'; +import { SCALE_UNITS } from '../../../src/data/scaleUnits'; +import type { EphemerisCorrection } from '../../../src/@types/scene/EphemerisCorrection'; + +// A 400-day span, so dt = 100 sits at τ = −0.5 with round phases. +const CORRECTION: EphemerisCorrection = { + startJd: 2_400_000, + endJd: 2_400_400, + polyKm: [ + [100, -50, 7], + [10, 0, 0], + [0, 4, 0], + [0, 0, 2], + ], + // prettier-ignore + terms: [ + Math.PI / 100, 1000, 0, 0, 0, 500, 0, // phase π at dt = 100 + Math.PI / 400, 0, 0, 30, 0, 0, -20, // phase π/4 at dt = 100 + ], +}; + +function km(simDays: number): number[] { + return ephemerisCorrectionMpc(CORRECTION, simDays).map((v) => v / SCALE_UNITS.KM_TO_MPC); +} + +describe('ephemerisCorrectionMpc', () => { + it('evaluates polynomial and terms at a known instant', () => { + // poly at τ = −0.5: x = 100 − 5, y = −50 + 1, z = 7 − 0.25. + // term 1 (π): x −= 1000; term 2 (π/4): z += (30 − 20)/√2. + const [x, y, z] = km(2_400_100); + expect(x).toBeCloseTo(95 - 1000, 6); + expect(y).toBeCloseTo(-49, 6); + expect(z).toBeCloseTo(6.75 + 10 / Math.SQRT2, 6); + }); + + it('holds the edge value outside the span', () => { + const century = 36_525; + expect(ephemerisCorrectionMpc(CORRECTION, CORRECTION.startJd - century)).toEqual( + ephemerisCorrectionMpc(CORRECTION, CORRECTION.startJd), + ); + expect(ephemerisCorrectionMpc(CORRECTION, CORRECTION.endJd + century)).toEqual( + ephemerisCorrectionMpc(CORRECTION, CORRECTION.endJd), + ); + }); + + it('is continuous across both span edges', () => { + for (const edge of [CORRECTION.startJd, CORRECTION.endJd]) { + const a = km(edge - 1e-6); + const b = km(edge + 1e-6); + expect(Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2])).toBeLessThan(1e-3); + } + }); +}); From 428f92fdcbf9e95da62352c43dfc47cb83e161a2 Mon Sep 17 00:00:00 2001 From: Alexander Rulkens Date: Sun, 4 Oct 2026 09:38:20 +0200 Subject: [PATCH 05/14] Ephemeris correction evaluator: strict index access --- src/utils/orbit/ephemerisCorrectionMpc.ts | 8 ++++---- tests/utils/orbit/ephemerisCorrectionMpc.test.ts | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/utils/orbit/ephemerisCorrectionMpc.ts b/src/utils/orbit/ephemerisCorrectionMpc.ts index 2f66def42..f1f3e7ec4 100644 --- a/src/utils/orbit/ephemerisCorrectionMpc.ts +++ b/src/utils/orbit/ephemerisCorrectionMpc.ts @@ -19,12 +19,12 @@ export function ephemerisCorrectionMpc(c: EphemerisCorrection, simDays: number): let z = p0[2] + tau * (p1[2] + tau * (p2[2] + tau * p3[2])); const terms = c.terms; for (let i = 0; i < terms.length; i += FLOATS_PER_TERM) { - const phase = terms[i] * dt; + const phase = terms[i]! * dt; const cos = Math.cos(phase); const sin = Math.sin(phase); - x += terms[i + 1] * cos + terms[i + 4] * sin; - y += terms[i + 2] * cos + terms[i + 5] * sin; - z += terms[i + 3] * cos + terms[i + 6] * sin; + x += terms[i + 1]! * cos + terms[i + 4]! * sin; + y += terms[i + 2]! * cos + terms[i + 5]! * sin; + z += terms[i + 3]! * cos + terms[i + 6]! * sin; } const k = SCALE_UNITS.KM_TO_MPC; return [x * k, y * k, z * k]; diff --git a/tests/utils/orbit/ephemerisCorrectionMpc.test.ts b/tests/utils/orbit/ephemerisCorrectionMpc.test.ts index e245952ee..143e0e6c5 100644 --- a/tests/utils/orbit/ephemerisCorrectionMpc.test.ts +++ b/tests/utils/orbit/ephemerisCorrectionMpc.test.ts @@ -1,6 +1,7 @@ import { describe, it, expect } from 'vitest'; import { ephemerisCorrectionMpc } from '../../../src/utils/orbit/ephemerisCorrectionMpc'; import { SCALE_UNITS } from '../../../src/data/scaleUnits'; +import type { Vec3 } from '../../../src/@types/math/Vec3'; import type { EphemerisCorrection } from '../../../src/@types/scene/EphemerisCorrection'; // A 400-day span, so dt = 100 sits at τ = −0.5 with round phases. @@ -20,8 +21,10 @@ const CORRECTION: EphemerisCorrection = { ], }; -function km(simDays: number): number[] { - return ephemerisCorrectionMpc(CORRECTION, simDays).map((v) => v / SCALE_UNITS.KM_TO_MPC); +function km(simDays: number): Vec3 { + const [x, y, z] = ephemerisCorrectionMpc(CORRECTION, simDays); + const k = SCALE_UNITS.KM_TO_MPC; + return [x / k, y / k, z / k]; } describe('ephemerisCorrectionMpc', () => { From e79ff43f2405b1aacaa74ba5a88f788ce766b43e Mon Sep 17 00:00:00 2001 From: Alexander Rulkens Date: Sun, 4 Oct 2026 09:38:20 +0200 Subject: [PATCH 06/14] Horizons planet fetcher --- data/raw/horizons/planets/README.md | 34 ++++++++ docs/DATA.md | 5 ++ package.json | 1 + .../tools/parsers/horizonsVectorsCsv.test.ts | 34 ++++++++ .../utils/data/mergeHorizonsChunks.test.ts | 14 ++++ tools/fetch/fetchHorizonsPlanets.ts | 78 +++++++++++++++++++ tools/parsers/@types/HorizonsVectorRow.d.ts | 11 +++ tools/parsers/horizonsVectorsCsv.ts | 32 ++++++++ tools/utils/data/mergeHorizonsChunks.ts | 18 +++++ tools/utils/io/rawDataRegistry.ts | 19 +++++ 10 files changed, 246 insertions(+) create mode 100644 data/raw/horizons/planets/README.md create mode 100644 tests/tools/parsers/horizonsVectorsCsv.test.ts create mode 100644 tests/tools/utils/data/mergeHorizonsChunks.test.ts create mode 100644 tools/fetch/fetchHorizonsPlanets.ts create mode 100644 tools/parsers/@types/HorizonsVectorRow.d.ts create mode 100644 tools/parsers/horizonsVectorsCsv.ts create mode 100644 tools/utils/data/mergeHorizonsChunks.ts diff --git a/data/raw/horizons/planets/README.md b/data/raw/horizons/planets/README.md new file mode 100644 index 000000000..adc2b94e5 --- /dev/null +++ b/data/raw/horizons/planets/README.md @@ -0,0 +1,34 @@ +# JPL Horizons planet vectors + +Input to `npm run build-planet-ephemeris`, which fits each planet's `Horizons − Kepler` +residual into `src/data/bodies/planetEphemerisCorrections.generated.ts`. The CSVs are +gitignored; regenerate with `npm run fetch-horizons-planets` (8 files, ~3 MB each). + +- **Upstream:** JPL Horizons API, `https://ssd.jpl.nasa.gov/api/horizons.api` (JPL DE ephemeris; public domain, NASA/JPL). +- **Files:** `.csv`, columns `jd,x_km,y_km,z_km`, one row per day from JD 2415020.5 (1900-01-01) to 2488069.5 (2100-01-01). +- **Targets:** Mercury `199`, Venus `299`, Earth–Moon barycentre `3`, then the system barycentres `4`–`8` (Mars through Neptune). + +## Query + +Every target is fetched in four 50-year chunks (1900–1950, 1950–2000, 2000–2050, 2050–2100), +each retried up to five times. Each chunk includes both endpoints, so the shared boundary row is +dropped once when the chunks are merged. + +``` +format=json +COMMAND='' +OBJ_DATA='NO' +MAKE_EPHEM='YES' +EPHEM_TYPE='VECTORS' +CENTER='500@10' # Sun body centre +REF_PLANE='FRAME' # equatorial ICRF, the scene's world frame +TIME_TYPE='UT' # UT1 before 1962, UTC after: the app's simDays is UTC +OUT_UNITS='KM-S' +CSV_FORMAT='YES' +VEC_TABLE='1' # position only +START_TIME='' +STOP_TIME='' +STEP_SIZE='1 d' +``` + +Fetched 2026-10-04. diff --git a/docs/DATA.md b/docs/DATA.md index f5ba57cd9..55695e049 100644 --- a/docs/DATA.md +++ b/docs/DATA.md @@ -192,6 +192,10 @@ Planet/moon/ring textures and Earth's imagery are gitignored raw pulls with comm Earth's whole-globe base texture and its surface tile pyramid are two publications of one Blue Marble month (a 21600×10800 equirect and eight 21600×21600 quadrants, ~421 MB). The month is chosen once in [`bmngVintage.ts`](../tools/utils/io/bmngVintage.ts) and every registry path, upstream URL, and attribution string reads it from there. The tile layer falls back to the base outside its baked window, so a vintage split between the two would draw a visible seasonal seam along the tile frontier. +### Planet positions (JPL Horizons) + +Each planet's position is its Keplerian row plus a fitted correction series that matches JPL DE to ≤ 1,000 km over 1900–2100. `npm run fetch-horizons-planets` pulls 1-day heliocentric ICRF vectors (8 × ~3 MB, gitignored) into `data/raw/horizons/planets/`, and `npm run build-planet-ephemeris` fits them into the committed `src/data/bodies/planetEphemerisCorrections.generated.ts`. Regenerate after any planet element-row or frame change. The exact query is in [`data/raw/horizons/planets/README.md`](../data/raw/horizons/planets/README.md). + ### Earth surface tile pyramid The bake emits `public/data/images/earth-tiles/v10/albedo///.webp`: 512 px lossy WebP tiles whose alpha channel doubles as the land mask, plus two sidecars, `index.txt` (one path per line) and `manifest.json` (the flat band list every product's planner filters by `builtFrom`). `npm run build-surface-tiles` is idempotent per tile (a tile whose output already exists is skipped, byte-identical, on a re-run) and refuses to write `index.txt`/`manifest.json` at all if any tile the prior index promised is missing on disk, leaving both untouched — an interrupted or partially-deleted bake never advances the pointer to half-baked data ([`buildSurfaceTiles.ts`](../tools/textures/buildSurfaceTiles.ts)). @@ -216,6 +220,7 @@ Every refresh shares one shape: fetch, build, then `npm run sync-r2-secure` from | Clusters/superclusters | `fetch-structures` | `build-structures` (after `build-tiers`) | | DESI | `fetch-desi` | `build-tiers` (`desi-{deep,wedge,sgw}.bin`) | | Planet textures | `fetch-textures` (`--dev` for a subset) | `build-textures` | +| Planet positions | `fetch-horizons-planets` | `build-planet-ephemeris` (commits a generated `.ts`, no R2 sync) | | Earth surface tiles | `fetch-textures` + `fetch-eox` + `fetch-height` | `build-surface-tiles` (`--dev` for a quick z5 pass, albedo only) | Raw files and built artefacts are gitignored; only provenance READMEs and `.sha256` sidecars are committed. Two small deterministic bakes are the exceptions and live in git under `public/`: the MSDF font atlases in `public/fonts/` (`npm run build-fonts`) and the split-sum environment-BRDF LUT in `public/lut/` (`npm run build-env-brdf-lut`). Full-resolution texture and tile builds run post-merge from the main worktree. diff --git a/package.json b/package.json index e16b0b2b7..ff9285a75 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,7 @@ "fetch-famous-images": "tsx tools/famous/fetchFamousImages.ts", "fetch-gaia": "tsx tools/fetch/fetchGaia.ts", "fetch-height": "tsx tools/fetch/fetchHeightSources.ts", + "fetch-horizons-planets": "tsx tools/fetch/fetchHorizonsPlanets.ts", "fetch-hyperleda": "tsx tools/fetch/fetchHyperLeda.ts", "fetch-milliquas": "tsx tools/fetch/fetchMilliquas.ts", "fetch-skraafoto": "tsx tools/fetch/fetchSkraafoto.ts", diff --git a/tests/tools/parsers/horizonsVectorsCsv.test.ts b/tests/tools/parsers/horizonsVectorsCsv.test.ts new file mode 100644 index 000000000..1ae9ed635 --- /dev/null +++ b/tests/tools/parsers/horizonsVectorsCsv.test.ts @@ -0,0 +1,34 @@ +import { describe, it, expect } from 'vitest'; +import { parseHorizonsVectorsCsv } from '../../../tools/parsers/horizonsVectorsCsv'; + +// Trimmed from a real Horizons response (Mercury, 1900-01-01..03, CENTER 500@10). +const RESULT = `Reference frame : ICRF +******************************************************************************* + JDUT , Calendar Date (UT ), X, Y, Z, +************************************************************************************************************************** +$$SOE +2415020.500000000, A.D. 1900-Jan-01 00:00:00.0000, -5.795103211328162E+07, -2.365718743126438E+07, -6.605861464973872E+06, +2415021.500000000, A.D. 1900-Jan-02 00:00:00.0000, -5.707774547111462E+07, -2.685144548110793E+07, -8.402394181703608E+06, +2415022.500000000, A.D. 1900-Jan-03 00:00:00.0000, -5.598502057837061E+07, -2.994254589329407E+07, -1.016666302505421E+07, +$$EOE +************************************************************************************************************************** + Times PRIOR to 1962 are UT1, a mean-solar time closely related to the +`; + +describe('parseHorizonsVectorsCsv', () => { + it('parses the SOE/EOE block of a Horizons vectors result', () => { + const rows = parseHorizonsVectorsCsv(RESULT); + expect(rows).toHaveLength(3); + expect(rows[0]).toEqual({ + jd: 2415020.5, + xKm: -5.795103211328162e7, + yKm: -2.365718743126438e7, + zKm: -6.605861464973872e6, + }); + expect(rows[2]).toMatchObject({ jd: 2415022.5, zKm: -1.016666302505421e7 }); + }); + + it('throws when the result carries no data block', () => { + expect(() => parseHorizonsVectorsCsv('No ephemeris for target')).toThrow(/SOE/); + }); +}); diff --git a/tests/tools/utils/data/mergeHorizonsChunks.test.ts b/tests/tools/utils/data/mergeHorizonsChunks.test.ts new file mode 100644 index 000000000..3b5f303ca --- /dev/null +++ b/tests/tools/utils/data/mergeHorizonsChunks.test.ts @@ -0,0 +1,14 @@ +import { describe, it, expect } from 'vitest'; +import { mergeHorizonsChunks } from '../../../../tools/utils/data/mergeHorizonsChunks'; + +const row = (jd: number) => ({ jd, xKm: jd, yKm: 0, zKm: 0 }); + +describe('mergeHorizonsChunks', () => { + it('concatenating two chunks drops the shared boundary row once', () => { + const merged = mergeHorizonsChunks([ + [row(1), row(2), row(3)], + [row(3), row(4), row(5)], + ]); + expect(merged.map((r) => r.jd)).toEqual([1, 2, 3, 4, 5]); + }); +}); diff --git a/tools/fetch/fetchHorizonsPlanets.ts b/tools/fetch/fetchHorizonsPlanets.ts new file mode 100644 index 000000000..d63a79020 --- /dev/null +++ b/tools/fetch/fetchHorizonsPlanets.ts @@ -0,0 +1,78 @@ +#!/usr/bin/env node +/** + * fetchHorizonsPlanets — pull 1-day heliocentric equatorial (ICRF) positions for the eight + * planets over 1900–2100 from the JPL Horizons API into data/raw/horizons/planets/.csv + * (`jd,x_km,y_km,z_km`, ~3 MB each). Mars–Neptune are their system barycentres and Earth is + * the Earth–Moon barycentre: those are the points the app's Kepler rows describe. UT time tags, + * so the fit absorbs TDB−UT and the app's UTC `simDays` needs no conversion. Query in README. + */ +import { mkdirSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; + +import { parseHorizonsVectorsCsv } from '../parsers/horizonsVectorsCsv'; +import { mergeHorizonsChunks } from '../utils/data/mergeHorizonsChunks'; +import { rawDataPath } from '../utils/io/rawDataRegistry'; +import type { HorizonsVectorRow } from '../parsers/@types/HorizonsVectorRow'; + +const API = 'https://ssd.jpl.nasa.gov/api/horizons.api'; +const TARGETS = ['199', '299', '3', '4', '5', '6', '7', '8']; +const CHUNKS: readonly [string, string][] = [ + ['1900-01-01', '1950-01-01'], + ['1950-01-01', '2000-01-01'], + ['2000-01-01', '2050-01-01'], + ['2050-01-01', '2100-01-01'], +]; +const MAX_ATTEMPTS = 5; + +function queryUrl(naif: string, start: string, stop: string): string { + const params: Record = { + format: 'json', + COMMAND: `'${naif}'`, + OBJ_DATA: "'NO'", + MAKE_EPHEM: "'YES'", + EPHEM_TYPE: "'VECTORS'", + CENTER: "'500@10'", + REF_PLANE: "'FRAME'", + TIME_TYPE: "'UT'", + OUT_UNITS: "'KM-S'", + CSV_FORMAT: "'YES'", + VEC_TABLE: "'1'", + START_TIME: `'${start}'`, + STOP_TIME: `'${stop}'`, + STEP_SIZE: "'1 d'", + }; + return `${API}?${new URLSearchParams(params).toString()}`; +} + +async function fetchChunk(naif: string, start: string, stop: string): Promise { + for (let attempt = 1; ; attempt++) { + try { + const response = await fetch(queryUrl(naif, start, stop)); + if (!response.ok) throw new Error(`${response.status} ${response.statusText}`); + const body = (await response.json()) as { result?: string; error?: string }; + if (body.error || !body.result) throw new Error(body.error ?? 'empty result'); + return parseHorizonsVectorsCsv(body.result); + } catch (err) { + if (attempt >= MAX_ATTEMPTS) throw err; + console.warn(`fetchHorizonsPlanets: ${naif} ${start} attempt ${attempt} failed: ${err}`); + await new Promise((r) => setTimeout(r, 2000 * attempt)); + } + } +} + +async function main(): Promise { + const outDir = rawDataPath('horizons.planets'); + mkdirSync(outDir, { recursive: true }); + console.log('fetchHorizonsPlanets: downloading 8 × ~3 MB of 1-day vectors from JPL Horizons'); + for (const naif of TARGETS) { + const chunks: HorizonsVectorRow[][] = []; + for (const [start, stop] of CHUNKS) chunks.push(await fetchChunk(naif, start, stop)); + const rows = mergeHorizonsChunks(chunks); + const csv = ['jd,x_km,y_km,z_km', ...rows.map((r) => `${r.jd},${r.xKm},${r.yKm},${r.zKm}`)]; + const outPath = join(outDir, `${naif}.csv`); + writeFileSync(outPath, `${csv.join('\n')}\n`); + console.log(`fetchHorizonsPlanets: wrote ${outPath} (${rows.length} rows)`); + } +} + +await main(); diff --git a/tools/parsers/@types/HorizonsVectorRow.d.ts b/tools/parsers/@types/HorizonsVectorRow.d.ts new file mode 100644 index 000000000..19e38bd13 --- /dev/null +++ b/tools/parsers/@types/HorizonsVectorRow.d.ts @@ -0,0 +1,11 @@ +/** + * HorizonsVectorRow — one JPL Horizons state-vector row (VEC_TABLE=1: position only), + * heliocentric equatorial ICRF in km, time-tagged by its UT Julian date. + */ + +export type HorizonsVectorRow = { + readonly jd: number; + readonly xKm: number; + readonly yKm: number; + readonly zKm: number; +}; diff --git a/tools/parsers/horizonsVectorsCsv.ts b/tools/parsers/horizonsVectorsCsv.ts new file mode 100644 index 000000000..5ce648240 --- /dev/null +++ b/tools/parsers/horizonsVectorsCsv.ts @@ -0,0 +1,32 @@ +/** + * parseHorizonsVectorsCsv — the data rows of a Horizons VECTORS result (CSV_FORMAT=YES, + * VEC_TABLE=1): everything between `$$SOE` and `$$EOE`, columns `JDUT, Calendar, X, Y, Z,`. + * The calendar column carries no comma of its own, so a plain split is safe. + */ + +import type { HorizonsVectorRow } from './@types/HorizonsVectorRow'; + +export function parseHorizonsVectorsCsv(result: string): HorizonsVectorRow[] { + const start = result.indexOf('$$SOE'); + const end = result.indexOf('$$EOE'); + if (start < 0 || end < start) throw new Error('parseHorizonsVectorsCsv: no $$SOE/$$EOE block'); + + return result + .slice(start + '$$SOE'.length, end) + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.length > 0) + .map((line) => { + const cols = line.split(',').map((c) => c.trim()); + const row = { + jd: Number(cols[0]), + xKm: Number(cols[2]), + yKm: Number(cols[3]), + zKm: Number(cols[4]), + }; + if (![row.jd, row.xKm, row.yKm, row.zKm].every(Number.isFinite)) { + throw new Error(`parseHorizonsVectorsCsv: unparseable row "${line}"`); + } + return row; + }); +} diff --git a/tools/utils/data/mergeHorizonsChunks.ts b/tools/utils/data/mergeHorizonsChunks.ts new file mode 100644 index 000000000..36d21dbbe --- /dev/null +++ b/tools/utils/data/mergeHorizonsChunks.ts @@ -0,0 +1,18 @@ +/** + * mergeHorizonsChunks — concatenate per-chunk Horizons rows into one strictly increasing + * series. Adjacent chunks share their boundary instant (each query is inclusive at both + * ends), so a row whose `jd` is not past the previous row's is dropped. + */ + +import type { HorizonsVectorRow } from '../../parsers/@types/HorizonsVectorRow'; + +export function mergeHorizonsChunks( + chunks: readonly (readonly HorizonsVectorRow[])[], +): HorizonsVectorRow[] { + const merged: HorizonsVectorRow[] = []; + for (const row of chunks.flat()) { + const last = merged.at(-1); + if (!last || row.jd > last.jd) merged.push(row); + } + return merged; +} diff --git a/tools/utils/io/rawDataRegistry.ts b/tools/utils/io/rawDataRegistry.ts index 17f6107a3..cf13b4757 100644 --- a/tools/utils/io/rawDataRegistry.ts +++ b/tools/utils/io/rawDataRegistry.ts @@ -1545,6 +1545,25 @@ export const RAW_DATA = { description: 'What `meshes.sha256` pins, the R2 backup, and the restore command.', }, + // ─── JPL Horizons planet vectors (ephemeris-correction fit input) ───── + + 'horizons.planets': { + path: 'data/raw/horizons/planets', + kind: 'directory', + source: 'gitignored', + description: + 'Holds `.csv` (`jd,x_km,y_km,z_km`): 1-day heliocentric ICRF positions of the eight planets, 1900–2100, UT time tags. Input to `build-planet-ephemeris`.', + upstream: 'https://ssd.jpl.nasa.gov/api/horizons.api', + fetcher: 'tools/fetch/fetchHorizonsPlanets.ts', + readme: 'horizons.planets.readme', + }, + 'horizons.planets.readme': { + path: 'data/raw/horizons/planets/README.md', + kind: 'file', + source: 'committed', + description: 'Provenance for the Horizons planet vectors — the exact query, targets and span.', + }, + // ─── StarNet++ weights (famous-galaxy curator) ──────────────────────── 'starnet.weights': { From 2f0c8f0d314e4e25d3a0c8717e71daa0452ffb2b Mon Sep 17 00:00:00 2001 From: Alexander Rulkens Date: Sun, 4 Oct 2026 09:57:34 +0200 Subject: [PATCH 07/14] Planet ephemeris fit tool + generated corrections --- package.json | 1 + .../planetEphemerisCorrections.generated.ts | 472 ++++++++++++++++++ tests/tools/utils/math/fft.test.ts | 18 + .../utils/math/fitSinusoidSeries.test.ts | 39 ++ tools/@types/math/FitResult.d.ts | 13 + tools/bodies/buildPlanetEphemeris.ts | 161 ++++++ tools/utils/math/fft.ts | 45 ++ tools/utils/math/fitSinusoidSeries.ts | 155 ++++++ 8 files changed, 904 insertions(+) create mode 100644 src/data/bodies/planetEphemerisCorrections.generated.ts create mode 100644 tests/tools/utils/math/fft.test.ts create mode 100644 tests/tools/utils/math/fitSinusoidSeries.test.ts create mode 100644 tools/@types/math/FitResult.d.ts create mode 100644 tools/bodies/buildPlanetEphemeris.ts create mode 100644 tools/utils/math/fft.ts create mode 100644 tools/utils/math/fitSinusoidSeries.ts diff --git a/package.json b/package.json index ff9285a75..0f0026f33 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "build-stars-rs": "cargo run --manifest-path tools/stars-rs/Cargo.toml --release -- --out public/data && npm run build-data-manifest", "build-famous": "tsx tools/famous/buildFamous.ts && npm run build-data-manifest", "build-famous-stars": "tsx tools/famous/buildFamousStars.ts && npm run build-data-manifest", + "build-planet-ephemeris": "tsx tools/bodies/buildPlanetEphemeris.ts", "build-planet-facts": "tsx tools/bodies/buildPlanetFacts.ts", "build-famous-hires": "tsx tools/famous/copyHiResToPublic.ts", "build-famous-thumbs": "tsx tools/famous/buildFamousThumbs.ts", diff --git a/src/data/bodies/planetEphemerisCorrections.generated.ts b/src/data/bodies/planetEphemerisCorrections.generated.ts new file mode 100644 index 000000000..7aa064e9f --- /dev/null +++ b/src/data/bodies/planetEphemerisCorrections.generated.ts @@ -0,0 +1,472 @@ +// src/data/bodies/planetEphemerisCorrections.generated.ts +// !!! GENERATED FILE — DO NOT EDIT BY HAND !!! +// Regenerate with: npm run fetch-horizons-planets && npm run build-planet-ephemeris +// Source of truth: data/raw/horizons/planets/ (JPL Horizons) minus orbitalElements.ts +import type { EphemerisCorrection } from '../../@types/scene/EphemerisCorrection'; + +export const PLANET_EPHEMERIS_CORRECTIONS: Readonly> = { + // mercury: 46 terms, max 893 km vs Horizons (1-day grid) + mercury: { + startJd: 2415020.5, + endJd: 2488069.5, + polyKm: [ + [-434.9, 402, 269.9], + [-511.4, -15.9, 42.8], + [2.9, 6.7, -0.4], + [-3.6, 9.1, 4.7], + ], + terms: [ + 0.0714251270098, -679.6, -2487, -1250.9, 2717, -462.6, -518.4, 0.0683854652548, 445.6, 871.9, + 409.6, -948.2, 358.8, 276.5, 0.0713720514238, -283.4, 1265.1, 717.6, -1414.1, -333.7, -33.4, + 0.0744629154721, -458.2, 722.5, 430, -812.4, -483.5, -154.6, 0.142844334985, 524, 491.9, + 210.7, -522.6, 443.2, 290, 0.00290083422122, -513.1, -401.3, -160.9, 429.5, -445.4, -264.8, + 0.0559244267028, 166.1, 522.5, 252.7, -573, 125.1, 119.5, 0.0714978857854, 212.4, 433, 207.2, + -468.7, 172, 140.7, 0.0869248224424, -134.3, 357.7, 199, -392.6, -150.4, -36, 0.0124627998028, + -154.4, -223.1, -99.5, 246.4, -129.4, -90.2, 0.142899897771, -278.9, -111, -30, 109.4, -244.4, + -142, 0.145888347963, -200.9, -187.2, -77.5, 200.5, -169.9, -111.7, 0.0154999917092, 245.9, + -116.5, -94.3, -144.1, -200.7, -96.3, 0.00303792367598, 189.2, -168.8, -106.6, -194.3, -172.7, + -70.8, 0.139949181914, 176.6, -117.7, -83.8, 141.4, 168.8, 70.2, 0.139811347792, -199.8, 32.2, + 37.5, -46.3, -180.4, -91, 0.214267665718, -176.4, 58.1, 49.1, -74.3, -160.6, -78.3, + 0.158349646233, -142.1, -92.4, -34.2, 95.1, -123, -75.6, 0.0713061381868, 81.1, 412.6, 215.3, + -447.9, 49.8, 78.3, 0.0014530606217, -125.2, -78.6, -25.9, 68.8, -87.5, -50.6, + 0.0712342328374, 234.8, 162.4, 62.4, -164.2, 202.8, 130.1, 0.0279621812362, 121.1, -23.9, + -23.1, 29.9, 110, 49.8, 0.14277406341, -50.9, -125.7, -62.3, 138.1, -39.1, -35, + 0.127349950883, -110.9, 0.1, 11, -5.6, -99.6, -51.2, 0.0589620607687, 95.2, 37.4, 22.7, -18.9, + 97.6, 60, 0.0743219807864, 99.3, -44.6, -29.4, 52.8, 91, 41.4, 0.0688071069515, 94.6, 31.1, + 10.4, -31.2, 82.5, 49, 0.071162327488, 154.8, 77.3, 23.5, -75.7, 136.3, 83.6, 0.130386564911, + 53.8, -63.7, -38.6, 73.3, 52.3, 18.6, 0.0740405697951, -82.6, 6.5, 17.9, -17.5, -81.7, -40.2, + 0.0404253811571, 73.7, -37.6, -25.9, 44.6, 67.5, 30, 0.0344042079595, 48.6, -57.2, -32.9, + 65.9, 45.9, 18.1, 0.072867587625, 14.9, -73.5, -33.2, 78.6, 15.5, 2.6, 0.00116867733822, 8.7, + -66.9, -34.8, 75, 9.9, -1.8, 0.0699747129679, 28.4, -70.5, -26.7, 66.5, 22.8, 7.3, + 0.211374008935, 21.1, 62.8, 30.7, -68.8, 15.4, 16, 0.00434991596319, -10.6, 63.3, 33.2, -71.2, + -12.1, 0.1, 0.217313670371, 60.6, -30.9, -22.8, 37.7, 55.4, 25.4, 0.214325878207, 42.2, -62.9, + -38, 72.8, 40.9, 14.3, 0.0716529123301, -61.5, 34.1, 24.9, -41.5, -56.9, -25.9, + 0.114887310031, -63.1, -21, -4.5, 19.8, -57.1, -29.5, 0.14139626632, 52, -13.7, -13, 18, 48.4, + 22.5, 0.00261669932628, -4.8, -42.8, -21.7, -49.6, 3.7, 5.9, 0.0710904221385, 70, 11.9, -3.1, + -8.5, 63.4, 36.1, 0.229774401504, 35.5, -26.8, -18, 31.8, 33.2, 14.4, 0.285691769153, -13.4, + -41.4, -20.7, 45.8, -9.9, -10, + ], + }, + // venus: 40 terms, max 893 km vs Horizons (1-day grid) + venus: { + startJd: 2415020.5, + endJd: 2488069.5, + polyKm: [ + [-1077.2, 107.4, 116.9], + [-626.1, 695.1, 352.8], + [-19.3, -59.5, -24.8], + [-98.8, 75.1, 41], + ], + terms: [ + 0.00644182944683, -3292.2, -2411.7, -908.6, 2587.7, -2951.1, -1451.4, 0.0279612088002, -500.1, + 2571.9, 1294.4, -2870.8, -518.8, -81.2, 0.00431861454861, -2640.1, 1056.6, 682.4, 1213.4, + 2422.9, 1004.8, 0.0494830514863, 646.2, -1462.1, -689.8, 1614, 623.7, 190.9, 0.0280191178287, + -436.7, -1551.8, -695.4, 1695.3, -336, -318.1, 0.0172022263825, -308.9, 1397.5, 618.9, + -1534.3, -290.5, -97.2, 0.00145163189158, -631.7, -881.5, -357.1, 950.1, -568.9, -312.1, + 0.0278812992423, 930.2, -778.5, -410.3, 881.9, 851.6, 389, 0.0387225731649, 659, 696.6, 292.3, + -753.8, 598.2, 278, 0.0236434768626, -653.9, -750.8, -301, 697, -543.5, -269.6, + 0.0602435218972, 833.3, -41.5, -78.3, 69.8, 762.3, 339.6, 0.0322814533423, 141.6, -723.2, + -332.6, 803.4, 148.5, 23.5, 0.00212241136848, -130.8, 542.4, 250.2, -590.1, -151.9, -32.4, + 0.015078635528, 7.2, -409.4, -196, -444.2, -34.2, 56.7, 0.0258354982168, -194.1, 313.3, 9.3, + -185.7, -400.7, -264.1, 0.0544719027545, 47.6, -366.5, -167.1, 401.9, 52.1, -1, + 0.0265148258282, -159.8, 339.2, 148.9, -390.3, -151.9, -49.2, 0.0559439027846, 67.6, -367.1, + -169.3, 402.7, 71.2, 6, 0.0250615219382, 265.8, -242, -215.5, -270.6, -266.7, -69.8, + 0.0294112550092, 321.9, 212.9, 81.1, -227.5, 293.3, 134.1, 0.0300903971788, 364.7, 29.9, + -15.9, -16.9, 334.7, 155, 0.0284914414884, 18.8, -255.2, -110.3, 280.3, 19.7, -9.4, + 0.0274398237049, -214, -186.6, -70.4, 199.2, -190.7, -98.1, 0.000525007370005, 259.2, 133.7, + 43.6, 106, -184.2, -89.5, 0.0128833976039, 223.1, -47.2, -31.3, 57, 205.4, 88.5, + 0.0538032829881, 156.5, 142.8, 55.6, -154.2, 138.2, 71.4, 0.00290015086141, -137.2, 103.1, + 53.5, -111.2, -140.8, -56.5, 0.0278213781178, 42.8, -271.9, -156.6, 309.9, 46.1, 16.9, + 0.0558824407337, -124.3, 63.5, 36.6, -73.4, -115.8, -47.5, 0.0430412903343, -120, 25.7, 17.4, + -32.2, -107.5, -46.8, 0.0277554648808, -124.6, -80.9, -43.9, 87.9, -107.9, -59.8, + 0.0249290139256, 85.7, 59.7, 21.3, -65.2, 77.3, 38.5, 0.0309958853169, -37.2, 81.7, 39, -85.2, + -42.9, -12, 0.0710037289443, -47.7, -74.5, -29.4, 79.8, -40.3, -26.6, 0.0124631273415, 51.7, + 69.8, 27.7, -74.4, 45.6, 24.7, 0.0564460007775, -54.4, 62.5, 31.7, -70.3, -51.7, -18.8, + 0.00863452863139, 65.4, 55, 17.7, 34.4, -70.4, -36.5, 0.0365988985048, -19.5, 58.3, 2.3, 25.5, + 77.4, 44.2, 0.0281738805631, 59.6, -50.4, -28, 57, 56.2, 21.9, 0.0809866299714, -64.7, -19.5, + -4.6, 19.3, -58.8, -27.7, + ], + }, + // earth: 50 terms, max 900 km vs Horizons (1-day grid) + earth: { + startJd: 2415020.5, + endJd: 2488069.5, + polyKm: [ + [-184.2, 540.7, 232.8], + [-76.7, -359.5, -169.4], + [60.2, 64.7, 24.8], + [27.8, -12, -7.3], + ], + terms: [ + 0.00144870071357, -1842.4, -3160.4, -1363.8, 3413.1, -1681.9, -738.7, 0.00431854489581, + 2870.3, -1170.6, -558.3, -1430.7, -2696.4, -1158.1, 0.00644196744377, 1706.6, 1196.8, 563.5, + -1324.9, 1580.1, 629.6, 0.014302000003, -1549.8, -424.7, -313.9, -523.5, 1491.1, 629.2, + 0.0329556786067, -1094.9, 792.4, 345.4, -865.1, -1004.8, -433.1, 0.0172364219834, -525.3, + 1119.4, -339.3, -891.4, 86.5, -1528.5, 0.0279620534614, 1304.1, -311, -111.7, 331.9, 1200.3, + 520.7, 0.00109037104064, -665.8, 796.1, 347.7, -872.1, -638.8, -276.4, 0.0128818827333, + -1129.1, 314.7, 123.5, -40.1, -818.3, -344.8, 0.0171733942893, 296.8, -614.2, 388.5, 408.8, + 839.4, -1193.5, 0.0157511739741, -617.4, -701.6, -296.5, 809.2, -512.2, -233.7, + 0.0186529628599, -809.1, 370.2, 175.6, -407.8, -745.4, -313.2, 0.00289982860786, -846.6, 70.9, + 35.4, -77.4, -774.5, -334.4, 0.0387225972858, 536.6, 569.5, 238.3, -617.2, 486.3, 227, + 0.0215201330636, -608.3, -434.4, -195.6, 483.4, -541.3, -229.3, 0.00211673392891, 360.5, + -624.7, -269.8, 683.8, 313.9, 136.3, 0.0161124421161, -227.7, 585.8, 248.8, -636.8, -215.4, + -95.6, 0.018291926389, 25.1, -559.7, -239.1, 610, 21.8, 12.1, 0.0150808568346, 64.6, 696.7, + 356.4, 80, 194.1, -70.3, 0.0171074810524, -63.6, -422.3, -117, 437.2, -60.2, -15.7, + 0.0333131144225, -95, -337.6, -147, 367.1, -87.2, -38.6, 0.0315042298381, -280.7, -155.1, + -68.7, 159.9, -259.8, -113.5, 0.0173052207634, -185.2, 405, 1.6, -372.1, -152.9, -114.3, + 0.0193183026304, 248.8, -177.5, -77.1, 193.1, 220.8, 93, 0.0487059811967, -298.8, -30.1, + -13.5, 32.5, -273.1, -118.5, 0.0322875379937, 20.5, 264.5, 115, -293, 18.4, 4.6, + 0.000583088558062, -3.1, -211.9, -91.7, 228.8, 0.8, -0.2, 0.00218712104525, 14, -216.8, -95.6, + 192.6, -115.1, -50.4, 0.0343949732731, 67.5, -175.7, -74, 190.1, 63.8, 26.3, 0.0102364239006, + -10.6, 175.4, 77, -193.4, -10.2, -3.2, 0.0128231206487, 197.2, 43.1, 27.5, -6.2, -192.2, + -85.3, 0.0150162338064, 156.5, -98.5, -41.4, 71.7, 96.1, 45.9, 0.0258384966022, -94.7, -74.5, + -50.7, -36, 146.6, 48.1, 0.0173831182252, -122.9, 79.6, -11.2, -69.3, -113.9, -44, + 0.00914454452992, 62.6, -102.5, -47, 112.4, 56.3, 23.3, 0.0241681687367, 39.9, -101.8, -44.9, + 110.9, 38.7, 16.4, 0.0193844837844, 42.7, 89.1, 47.3, -137.1, 17.5, -3.9, 0.0300538924293, + 125.4, -56.9, -28.4, -23, -66.6, -26.1, 0.0170175993656, -49.2, -98.1, -38.6, 106.8, -48.8, + -8.9, 0.0166133134871, 47.5, -75, -35.5, 80.9, 48.6, 15, 0.0086297749702, -68.6, -54.2, -18.1, + -11.8, 82.9, 39.6, 0.0113260583903, -32.1, -74.9, -32.6, 87.1, -23.8, -10.7, 0.00139616220145, + -114.3, 25.5, 10.9, -28.4, -103.1, -44.8, 0.017788972563, 6.3, 83.3, 36.1, -88.9, 6.3, 4.2, + 0.00326823425569, -1.7, 74.1, 32.4, -83.1, -0.5, 0.1, 0.032219588658, -46, 62.5, 27.1, -67.2, + -43.3, -18.7, 0.03382588328, -16.7, 68.6, 29, -75.6, -15.2, -7.1, 0.0252591206341, 5.9, 68.1, + 30.5, -74.4, 5, 3.1, 0.00696603693656, -56.8, 41.4, 15.4, 43.6, 54, 23.5, 0.0160302702607, + -29.7, -40, -35.5, -68.5, 28.9, -5.5, + ], + }, + // mars: 118 terms, max 899 km vs Horizons (1-day grid) + mars: { + startJd: 2415020.5, + endJd: 2488069.5, + polyKm: [ + [-3343.6, -375.8, -88.4], + [-3354.3, 1438.1, 721.9], + [88.4, 2.3, -19.7], + [-227.9, -54.8, -15.6], + ], + terms: [ + 0.00145100844461, -10317, -15869.6, -6897.3, 17584.4, -9091.8, -4556.5, 0.00290053832111, + 14319.3, -8338.9, -4147.1, 9276.3, 13104.7, 5669.1, 0.00624543425113, 12358.4, 842, 44, 613.2, + -11180, -5653.9, 0.00805648582603, -4596.8, 6048.5, 2805.3, -6554.3, -4057.8, -1698, + 0.0168401602261, -7903.3, 944, 633.2, -1102.6, -7186.9, -3229.7, 0.0153909949761, 639.9, 6630, + 3045.9, -7436.1, 575.2, 432.3, 0.0102359478356, 1862, -6216.1, -2854.7, 6828.6, 1754.1, 669.3, + 0.00108716080645, 3742.6, -3510.1, -1754.1, 3958.4, 3722.3, 1602.3, 0.00217991329399, -1736.1, + 4653.4, 2193.7, -5166.4, -1568.2, -573.7, 0.00917622357607, 4017.8, 2551.3, 909.7, -2590.2, + 3774.5, 1638.2, 0.00861523311441, 3685.4, 517, 140.2, -558.3, 3357.2, 1551.9, + 0.00968057540251, 2816.6, 1897.7, 782.7, -2121.9, 2560.5, 1170.9, 0.0245371266418, 2297.9, + 2278.1, 989, -2496.3, 2058.3, 1015.5, 0.0172029623381, -373.3, 2901.3, 1338.6, -3202.8, + -362.4, -82.2, 0.0069663424904, 3504.4, -1540.7, -716.2, 519.7, 1738.1, 780.3, + 0.0161115334366, 1389, -1894.2, -915, 2097.1, 1309.9, 542.8, 0.00434951222514, -2282.3, 752.7, + 404.1, -899.4, -2098.1, -923.6, 0.0113260060732, 756.5, 2125.5, 948.8, -2329.2, 667.9, 358.5, + 0.0105955354042, -573.9, -1958, -835.6, 2116.2, -518.2, -272.1, 0.00769343411896, -1084.3, + 1621.1, 633.3, -1617.1, -1194.8, -469.9, 0.00479642429938, -1719.6, -671.4, -271, -697.1, + 1775, 871.4, 0.00326896413177, -0.9, -1716.9, -790.3, 1960.2, -67.8, -88.5, 0.0139423810603, + 502.4, -1949, -862.7, 270, -1104.5, -472.6, 0.0182928178298, -1052.4, -843.7, -366.2, 918.6, + -946.7, -461.2, 0.00117210459728, 1187.1, 568, 221.9, -598.5, 1111.4, 513.3, 0.0120467381966, + 332.7, -1064.6, -491, 1173.1, 317, 110.8, 0.000548074359739, 473.2, -883.9, -414.8, -773.1, + 678.5, 340.1, 0.0259864441085, -692.9, 741.9, 357.2, -824.1, -637.1, -269.3, 0.00922785317716, + -786.5, 189.6, 109, -195.4, -757.2, -297.4, 0.0150236028713, -919.8, 587.1, 308.7, -544, + -553.8, -236.3, 0.00587002080729, -778.3, 437.5, 191.9, 745.2, -320.3, -144.8, + 0.0193761088243, -184, -652.8, -293.2, 676.4, -137.5, -77.5, 0.00872942301714, 619.8, -283.9, + -148.3, 313.1, 547.6, 241.1, 0.000607200728573, -658.2, -507.2, -212.3, 324.5, -217.4, -104.4, + 0.00910801092804, 548.8, -906.7, -324.8, 870.2, 574.2, 117.4, 0.0182399903059, 174, 601.1, + 271.6, -664.6, 150.4, 80.1, 0.0230881419012, -413, -283.8, -122.2, 310.3, -507.7, -242.4, + 0.00954708437422, -213.6, 533, 254.8, -589, -193.6, -70, 0.0177309116271, 256.2, 390.9, 171, + -427.3, 226.3, 114.9, 0.0336833285081, 523, 64, 15.2, -66, 472.4, 219.1, 0.0171208633679, + 450.8, 222.5, 93.6, -242.9, 403.6, 193.1, 0.0124276706873, -390.3, -166.6, -67, 180.3, -392.3, + -180.9, 0.0079835113051, 179.3, 234, 188.1, 433.6, -60.9, -35.4, 0.00545227325922, -314.9, + -219.8, -92.8, 247.7, -277, -134.8, 0.00904409506187, -550.4, -345.2, -94.9, 326.9, -515.3, + -220.6, 0.0263537387111, 228.2, 229.5, 99.3, -249.5, 202.7, 102, 0.0128461960632, -282.8, + 42.4, 35.1, 54.2, -191.6, -92.8, 0.00333327463491, 262.8, -35.5, -19.9, -63, -258.6, -121.3, + 0.00151001233811, 209.1, -211.2, -97.3, 323.1, 16.4, -2.1, 0.0279591067929, 245.3, -31.8, + -29.5, 38.9, 225.4, 99, 0.00850084209861, 15.7, -246.4, -102.9, 263.5, -11.5, -12.9, + 0.0167859043507, -64.4, 219.5, 101.8, -246.5, -64.1, -22.9, 0.00579960515101, 160.5, 159, + 64.8, -105.5, 225.2, 105.6, 0.0124955518347, -42, 111.4, 42.4, 114.8, 302.5, 120.8, + 0.0204709363984, 200.6, 124, 51.5, -132.4, 188.3, 88.7, 0.0134967288135, -33.6, 207.2, 95, + -228.5, -31.1, -8.3, 0.00897218971244, -284.9, 105.7, 66.1, -137.1, -273.7, -104.2, + 0.00174816578803, -206.1, -69.8, -24.7, 85.1, -189.5, -87.6, 0.00654138292466, 216.9, 58.5, + 21.1, -83.8, 208.9, 99.4, 0.00048735847945, -124.6, 97.9, 49.8, 443.4, 291.8, 118.4, + 0.0036961860031, -1.3, 159.8, 71.2, 243.7, -65, -30.8, 0.0197429192877, -153.4, -145.8, -62.4, + 156.3, -145.3, -68.3, 0.018359832555, 273.6, 6.9, 1.1, -12.1, 249.1, 117.8, 0.00945502297169, + -132.1, 116, 56.6, -129.6, -119.5, -51.1, 0.0216326432121, -14.5, -91.3, -35.6, -208.7, 142.8, + 66.4, 0.00123038042365, -164.3, 55.2, 29, -47.8, -140.1, -63.4, 0.000407463646747, -91.2, + 13.3, 7.8, 174.2, 244.6, 106.3, 0.0274443002553, -180.8, 74.2, 38.8, -84.9, -166.8, -75.6, + 0.0322326218518, -122.6, 127.1, 61.6, -148.2, -107.3, -47.1, 0.00761381724941, -175.6, -54.6, + -24.6, 58.3, -156.3, -72.2, 0.0252579046161, -40.3, -158, -76.5, 178.2, -32, -25.8, + 0.00974716958972, -223.9, 20.9, 22.5, -40.7, -203.4, -95.3, 0.01174830039, 134.3, 72.4, 26.5, + -140.5, 122.1, 65, 0.00981907493916, -132.5, 105.6, 48.5, -125.9, -118.4, -54.1, + 0.0106717373863, -152.5, -50.2, -17.5, 71.4, -132.7, -67.2, 0.0153398078788, 160.1, 14.6, 2.8, + -20.5, 139.6, 64.8, 0.00295610881002, 111.8, 54.7, 20.6, -83, 104.7, 48.1, 0.0088763159132, + -158, 21.9, 17.7, -42.5, -146.5, -61.8, 0.0081173150026, -173.5, 1.4, 7.4, -15, -203.3, -96.7, + 0.00101466437529, 131.8, -98.3, -51.1, 127.2, -56.9, -30.3, 0.00261026141863, -68.5, -95.8, + -41.8, -142.4, 41.4, 20.8, 0.0351317441273, 0.5, 123, 56.3, -136, -1.9, 2.7, 0.00135821215591, + 30.9, 126, 56.1, -135.7, 29.5, 16.5, 0.017680726477, -98.6, -90, -37.8, 101, -87.2, -42.4, + 0.010170612203, -68, -119.5, -53.5, 151.1, -74.7, -38.4, 0.00440220528194, 162.9, -54.8, + -30.6, 20.2, 190.7, 88.5, 0.0262606802683, 127, -0.9, -3.7, 2.3, 113.7, 52.2, 0.0181680849565, + 96.1, 76.6, 33.5, -84.5, 83.5, 39.3, 0.0169137360831, 137.2, 52.2, 20.2, -55.3, 125, 58.4, + 0.00933171645967, -126.9, -21.1, -8.6, 29.5, -122.4, -48.8, 0.0211892072124, -36, -95.6, + -42.5, 102.5, -31.4, -17.5, 0.00739122568528, -63.8, -45.5, -29.4, -72.5, 88.5, 47.6, + 0.0103144229019, 100.4, 25.1, 5.3, -32.2, 103.2, 46.1, 0.00844488381661, -21, -108.3, -42.4, + 116.8, -41.1, -15.8, 0.0061918495344, 38.4, 88.2, 42, 112.2, -31.7, -16.5, 0.0273959381336, + 121.4, 19.4, 6.2, -17.7, 109, 50.3, 0.0285220152107, -70.1, -60.5, -26, 31.6, -62.6, -29.5, + 0.016559411117, -76.7, 11.4, 5.6, 23.5, -81.3, -38.6, 0.0178005687261, -88.3, -62.1, -26.8, + 65.7, -81.1, -39.1, 0.0028362665609, -26.8, 92.8, 42.4, -87.6, -51.5, -20.9, 0.0188529572306, + -7.6, -58.8, -24, 62.3, -13, -7.7, 0.0138857219236, -11.7, 57.3, 27.5, -101.4, 43.1, 23.6, + 0.0428289279859, 60.1, -34.1, -17.4, 38, 54.9, 24.2, 0.0268739216821, 59.1, 38.1, 15.8, -39.3, + 52.7, 25.1, 0.00776577773871, 71.2, 1, 2.4, -1.2, 78.1, 36.1, 0.000687095561275, -113.9, + -11.8, -1.2, -22.3, 1.3, 2.1, 0.00474575306249, -95.9, 26.2, 14.1, -14.1, 76.2, 33.9, + 0.0123677201023, 81, -15.2, -9.6, 35.9, 62.3, 27.9, 0.0184317379045, 76.5, 41.7, 18.8, -48, + 71.4, 33.9, 0.00158990717081, 25.7, -58.7, -26.5, 59, 29.3, 10.7, 0.000311589847504, -57.1, + -6.9, -2.1, 61.1, 50.1, 21, 0.0154596501279, 35.4, -58.3, -28.5, 65.7, 33.5, 14.2, + 0.00724346556203, -16.9, -53, -23.2, 47.8, -20.1, -9.1, 0.00447411063137, 10.3, 4.2, 1.7, -35, + 76.7, 37.3, 0.0145949144483, -52.8, -4.5, -0.8, 4.8, -49.4, -22.7, 0.0244877662232, 61.5, + -30.1, -16.4, 40.1, 56.6, 25.5, 0.0194304233133, -60.5, 39.5, 18.6, -40.8, -35.3, -15.4, + 0.0219864607679, -14.2, 18, 12, 32.4, 50.8, 22.5, + ], + }, + // jupiter: 80 terms, max 882 km vs Horizons (1-day grid) + jupiter: { + startJd: 2415020.5, + endJd: 2488069.5, + polyKm: [ + [-18645.8, -52869, -22112.5], + [-26938.3, 87395.7, 37736.1], + [-79340.8, 90455.1, 40451.4], + [-78987.3, -111404.6, -45999.3], + ], + terms: [ + 0.000282980942627, -340245.5, 599165.6, 265277.6, 365996.9, 175744, 69500.6, 0.00139255620629, + 307831.8, -57886, -29481.7, 58524.5, 295615.5, 122177.4, 0.00116813757, 218938.9, 14908.8, + -2754.5, -19838.5, 268490.1, 117167.8, 0.00150477382803, 187193, -173938, -79784.3, 201405.8, + 182960.7, 71792.4, 0.000580193985381, 52570.7, -200494.9, -85827.6, 162705.7, -17256.1, + -13697.9, 0.00173051708038, 88428.6, -167192.2, -73879, 193962.5, 83469.1, 30101.3, + 0.00318018737319, -170075.3, -12666.2, -1560.2, 13954, -158490.2, -68355.8, 0.00231873291867, + 96141.1, 40928.3, 13950.2, -40061.9, 82420.5, 36255.9, 0.00259581844637, -58918.6, -63710.7, + -25807, 69004.1, -52108.5, -24189.4, 0.000335558297392, 78508.3, 20198.3, 6657.7, -12516.6, + 51507.1, 22159.9, 0.00134223318936, -54432, -85180.8, -33907.3, 84832.3, -42693.8, -18290.6, + 0.00155315554778, 24263.7, 45540.5, 17621.5, -39531.8, 16057.4, 8561.3, 0.00126553414996, + -39888.8, -11377.2, -4200.6, 4795.7, -27179.8, -10859.3, 0.000912825962563, -23201.8, 1275.5, + 1136.7, -3624, -23694.3, -9837.9, 0.00199972371397, -17732.2, 16630.5, 7909.8, -2081.9, 472.4, + -719.1, 0.00111213607118, -17214.1, 19500.2, 9878.3, 12016, -31073.2, -12393.1, + 0.0028885099642, -7657.4, 14385.8, 6553.4, -20994.4, -3056.3, -763.3, 0.000201334978368, + 1497.5, 19332.1, 8241.9, -21544, -13619.3, -5315.5, 0.000632767075045, -12193.2, -12844.1, + -5136.5, -4409.8, -6799.7, -2862.4, 0.00405390704165, -5477.2, 12754.1, 5547, -12326.2, + -5547.6, -2180.4, 0.00463013579091, 8110.8, 9009.6, 3680, -10369.6, 7678.4, 3547.4, + 0.00346153060936, 9777.1, 912.5, 233.5, -2727.3, 9670.6, 4241.5, 0.000843689433295, -9992.9, + 3471.1, 2306.1, 6609.8, -13645.1, -5761.3, 0.00297597683529, -11026.4, -2565.1, -828.1, + 1991.6, -10246.1, -4428.8, 0.00102584965186, -6648.9, 8506.7, 3849.3, -3345.6, -11228, + -4486.4, 0.000488956376097, -8438.4, -2631.1, -888, 12735, 1220.7, 259.1, 0.00207976638541, + 5880.7, 6816.5, 2479.8, -8841.5, 881.5, 1152.5, 0.00375667638974, -1450.3, -6448.5, -2655.4, + 4018.6, -608.8, -334, 0.000766990393901, -2070.8, 1938.7, 965.3, -1327.1, -6094.9, -2495, + 0.00237767022126, 4755.5, 2745.6, 1093.8, -4606.7, 2362.6, 1251.5, 0.00311589847535, -1161.1, + 5626, 2418.4, -5828.3, -1681.3, -567.3, 0.00268604833724, 3257.6, 6164.2, 2521.1, -5378.3, + 2705.3, 1348.2, 0.00183068562755, 2496.2, 4118.8, 1649.6, -2364.8, 3233.3, 1457.1, + 0.00247793142667, 3684.5, -521.4, -315, 361.7, 2452.5, 1090.5, 0.00163944196709, -212.2, 4802, + 1613.7, -1245.6, -2931.4, -928, 0.00432835889004, 1204.3, -1515.4, -640.5, 68, 1276.1, 573.5, + 0.00192706336474, -744.7, 2896.6, 1259.5, -750.5, 2684.6, 1085.5, 0.00325012179437, 414.7, + 3519, 1512.7, -4231.4, -1461.8, -473.7, 0.00222934144546, -474.3, -1299.5, -429.6, 11.8, + -1069.7, -275.4, 0.00276116541824, 2890.3, 3240.4, 1287.3, -1431.7, 1563, 806.6, + 0.00491882166518, -1669.5, 635.5, 318.6, -507.7, -1267.1, -574, 0.00410339860764, -1894.4, + 323.1, 165.2, -847.5, -3022.8, -1247.8, 0.00549833280722, 1104.9, -4.3, -26.8, -157.3, 870.8, + 381.4, 0.00440627631133, -1341, -319.1, -86.8, -500.5, -806.3, -333.4, 0.00333640821369, + -704.3, 2121.9, 939.6, -2743.9, -2564.1, -979.1, 0.00607386326377, 350.3, -718.9, -321.8, + 737.8, 149.9, 52.7, 0.00381577720991, -1651.7, 56.4, 82.2, -532.8, -1032.4, -432.6, + 0.00281868969778, -679.3, 1849.6, 889.3, -1297, -3112.7, -1248.9, 0.00419314091311, -870.1, + 768.5, 341.1, -1384.6, -2157, -861.5, 0.00214757310308, 487.4, 1198.2, 567.7, -2993.7, + -1672.5, -297.3, 0.0172057522834, -29.4, 433, 187.6, -469.6, -29.3, -12.7, 0.00457318022384, + -379.9, -945.1, -378.3, 452.7, 405.1, 158.4, 0.00364019347223, 126.8, -587.6, -246.7, 225.6, + -13.2, -12.3, 0.00578060508244, -257.3, 213.2, 103.9, -165.1, -139.7, -70.9, 0.00356650533179, + -451.4, -686.3, -268.6, 232.5, -653.6, -281.7, 0.00523814668532, -18.1, 122.6, 51, -91, 83.4, + 36.2, 0.00339393249324, -1798.8, -1293.9, -464.5, -431.3, -2297.1, -959.9, 0.0279627814202, + 255.7, -58.6, -42.6, 72.1, 234.6, 101, 0.00635717786915, 189, 51, 18.7, -136.1, 71.6, 36.4, + 0.00497585018075, -158.3, -165.8, -70.3, 295.1, -54.6, -35.3, 0.0042472093065, -1407.2, + -1269.3, -484.5, -194.2, -1319.9, -543.2, 0.00448689380461, -653.9, -857.1, -337.6, 344.8, + -101.9, -54.3, 0.00471699092279, -104.2, -317.1, -129.9, 262.8, 150.3, 55.8, 0.005186772539, + -61.3, -198.2, -75.3, -119.4, -68.5, -15.6, 0.00481286472203, -57.8, -201.1, -81, 68.8, 92.4, + 38.6, 0.00392123838907, -181.4, -78.9, -20.8, -193.6, -133.4, -50, 0.00612633577166, -195.1, + -85.1, -29.6, -37.9, -150.1, -61.4, 0.00664337986861, -33.8, 86.4, 42.4, -70.4, 28.3, 8.7, + 0.00694872645844, -39.4, -43.7, -20.6, 90.5, -68, -31.4, 0.00507172397999, -88, -79.1, -31.3, + 93.6, -13.6, -9.9, 0.00914231509312, 38.9, -53, -25.4, 60.9, 36.3, 15, 0.00544563179695, + -97.5, -87.2, -32, -42.9, 53.6, 23.3, 0.00586276614487, 40.3, -23, -10.7, 27.9, 63.8, 25.8, + 0.00753551498163, -26.9, 35.7, 18.1, -62, -18, -4.8, 0.00721281908909, 66.1, 11.1, 3.6, -35.5, + 1, 2.2, 0.00533058323794, -54.8, -30.5, -11.7, 37.4, 14.2, 4.1, 0.00619344743113, -75.4, + -65.5, -24.6, 28.3, 5.9, 1.7, 0.00571407843483, 13.4, -27.9, -13, 13.9, -52.7, -22.4, + 0.00627014647052, -34.4, -19.5, -6.6, 22.2, 34.6, 13.7, 0.0172572838638, -32.3, 8.3, 3.6, + -10.1, -31, -13.4, + ], + }, + // saturn: 86 terms, max 803 km vs Horizons (1-day grid) + saturn: { + startJd: 2415020.5, + endJd: 2488069.5, + polyKm: [ + [-333592.3, -445783.5, -173403.2], + [-33212.6, -1082781, -446731.5], + [145469.7, -629502.6, -262318.4], + [-79519.6, -159545, -56433.6], + ], + terms: [ + 0.00063414090514, -1885474.6, -222690, -5203, 6405, -1861611.5, -763310.3, 0.000304781660106, + -1049328.1, -881703, -304242.9, 835813.4, -1824986.9, -808315.8, 0.000528276165939, + -1003145.4, 50252.8, 57764.9, 55777.2, -1404432, -569424.3, 0.000865075522692, -749929.4, + 558412, 265150.5, -689069.9, -727552.8, -265562.2, 0.00145142968614, -270049.2, -601026.7, + -249853.2, 593216.3, -189930.3, -100450.2, 0.000680703974666, 203460.1, -525763, -224265.3, + 426779.5, 285675.1, 94363.5, 0.0011320829133, 62835.4, -628322.7, -261323.2, 282465.1, + -168447.2, -80749.3, 0.00118883511065, 463325.3, -10591.5, -28233.6, -17257.1, 356379.9, + 146074.6, 0.000469781616248, 193420.3, 405709.4, 152728, -195943.6, -42842.1, -16191.5, + 0.000239684498065, -218745.5, -14481.8, -2670.3, -79115.4, 245313.3, 103026.5, + 0.000393082576854, 162123.5, 77169.9, 25507.2, 87363, -34908.6, -25124.9, 0.00106419917155, + -84920.1, -72131.3, -25400.4, 63476.8, -194292.3, -83599.7, 0.000977912752235, -54018.4, + -30567.7, -9639.8, 30242.1, -103550, -44630.5, 0.000766990393985, 59039.3, -65842.1, -29585.6, + 18351, 76486.7, 29155.8, 0.00126553415005, 101601.7, 5171.7, -3815.8, -67864.1, 92450.1, + 41043.1, 0.00200807496445, 40949.3, -13251, -7863.3, -16860.5, -30916.9, -11322.6, + 0.00174378512402, -8564.1, -5650.4, -2715.8, 7912.2, 4229.2, 2271.9, 0.00135182056937, + 69209.6, -22528.5, -13184.2, -24772.9, 59931.1, 25745.1, 0.00288078320629, 4303.1, 10524.6, + 3807.9, -3746.2, -15635.8, -6416.1, 0.00151480602808, 44109.3, -5253.3, -5219.1, -31427.4, + 86984.4, 37676.3, 0.00230867510923, -601.8, -8878.7, -2063.5, -9451.5, 22495.1, 9252.6, + 0.00159150506747, 14863.3, 16751.1, 5558.5, -41390.1, 55330.9, 25199.1, 0.00166820410687, + -7691.7, 19741.5, 8205.3, -32422.6, 33082.6, 15672.2, 0.00208784450663, -5566.9, 12325.9, + 5359.6, -9187.5, 7373, 3415.2, 0.00182160218566, -2866.7, 21449.9, 8873.6, -17987.2, 9404.8, + 5128, 0.00293373825687, 3218.8, 11200.3, 4806.7, -8242.2, 6833.8, 3293.2, 0.00373292423462, + 3685.4, 1744.5, 505.7, 333.7, -2054.9, -823.4, 0.00257190956628, -2235, 4939.8, 2158, 223, + -8279, -3043.5, 0.00316566188727, -5836.3, 80.7, 1000.7, -496.5, 237.8, 100.6, + 0.00235849546142, 813.7, 12194.7, 4736.2, -8764.4, 22231.7, 10125.3, 0.00190788860497, + -3721.4, 9945.7, 4203.8, -9931.5, 2613.8, 1814.6, 0.00216674786293, -3986.8, 6837.9, 2982.7, + -4413.7, 2626.6, 1308.3, 0.00242560712089, -6474.2, 17282.1, 7401.5, -7090, 5857.1, 3083.4, + 0.00402665405003, -2376.2, -25.9, 331.1, -356.9, -242.7, -12.3, 0.00250230616028, -6519.2, + 13021.9, 5664.1, -3145.6, -720.5, 119.3, 0.00345601547463, 1105.3, 1969.8, 639.4, 836.5, + -720.6, -332.8, 0.00460456485812, 899.9, 1075.7, 390.2, 956.1, -390.5, -200, 0.00300084991634, + -2609.1, 5589.6, 2600.3, -1771.2, -1971, -722.6, 0.0043416728863, 362.6, 230.5, 79.8, 893.8, + 665.7, 207.5, 0.00265570423907, -3003.5, 3147.1, 1504.3, 1307, -1719.3, -772.7, + 0.00379660245006, -1978.1, 2424.7, 1097, -381.9, 1718.2, 736, 0.00309288970149, -1806.8, + 3641.3, 1748.7, -826.1, -965.4, -278.5, 0.00276116541824, -2013.8, 2026.7, 936.9, -386, -28.9, + 50.6, 0.00488337145341, -1238.8, 587.3, 344.2, -46, -382.7, -130.7, 0.00547784031254, -129.1, + 664.4, 274, 650.3, 76.2, 4.6, 0.00519244403834, -175.7, 287.2, 118.6, 510.5, 128.9, 2.6, + 0.00386371410953, -1191.8, 1845.5, 840.7, 298, -400.3, -155.6, 0.0171990175034, -133.6, 377.3, + 163.5, -415.2, -124.3, -53.8, 0.00324053441445, -925.7, 954.8, 363.6, 242.1, -1207.1, -466.5, + 0.00395703069263, -986.3, 1458.5, 679.6, -140.9, -100.3, 16.4, 0.00333640821369, -478.2, + 583.7, 253.2, 45, -445.6, -177.8, 0.00574673898199, -193.5, -159.4, -40.4, -225.7, 0.6, 25.8, + 0.0279632069525, 254, -64.4, -45, 76, 232.7, 99.9, 0.00465946664324, -618.9, 558.6, 256.2, + -360.5, 791.7, 339.4, 0.0060554368568, -166.1, 97.6, 48.7, 165.3, 175.2, 50.3, + 0.0063489230162, -202.8, 206.5, 89.9, 181.9, 187.8, 70.3, 0.00428555882611, 121, 373.5, 129.9, + 133.3, -612.3, -239.1, 0.00365279175111, 357.8, 246.4, 75.3, -303.6, 512.7, 233.4, + 0.00492791328112, 251.7, -409.9, -200.7, 370.5, -939.2, -350.9, 0.00661026412655, -29.8, + -78.9, -28, -109.5, -12.9, 6.8, 0.00692550384149, -113.7, -10.9, 5.1, 15.8, 106.8, 37.7, + 0.00352815581218, -239.4, 454.9, 205.7, -216.8, -6, 9.2, 0.00473616568264, -643.4, 842.4, + 376.2, -282.8, 392.1, 183.3, 0.00410339860764, -247.9, 603.2, 204.1, 225.1, -658.7, -268.9, + 0.00418968502695, -159.8, 321.3, 127, 177, -496.7, -202.9, 0.00721235499402, -101.3, 51.2, + 22.8, 38.6, 101.3, 40.2, 0.00480327734211, -727.5, 899.6, 414.1, 129.7, -299.8, -117.6, + 0.00914150243825, 37.1, -53.2, -25.3, 57.1, 44.5, 18.6, 0.00778504688769, -60.1, -19.3, -2.1, + -9, 57.5, 21.9, 0.00747164100558, 0, -43.3, -16.9, -62.8, -18.8, 0.7, 0.00580036485423, 47.8, + 17.9, -17.6, -17.9, -119.4, -39.3, 0.00444854428491, -125, 97.5, 47.5, 19.2, -101.2, -44, + 0.00501419970044, 75, -192.6, -80.1, 203.5, -310, -136.7, 0.00510048611976, 31.2, -127.7, + -51.7, 99.2, -165.4, -73.7, 0.00808166599056, -44.3, -2.5, -0.6, -5.3, 49.6, 19.6, + 0.00666322904742, 58.4, 27.2, -3.3, -5.8, -29, -10.8, 0.00526347157847, 37.9, -58.4, -23.8, + 71.9, -70.3, -32.8, 0.00864454101401, -30.1, -16.6, -3.6, -13.1, 29.6, 12.2, 0.00833824545498, + 13.1, -15.1, -7.6, -24.7, -15, -2.8, 0.00567572891513, -32, 22.2, 12.1, -34.4, -12.6, 1.1, + 0.00535934537772, 1.4, -15.1, -5.2, 35.3, -39.5, -17.7, 0.0172476964838, 31.6, 2.3, 1.1, -4.8, + 30.9, 13.4, 0.00894690975299, -16.1, -8.6, -3.3, -7.3, 21.7, 8.2, 0.0055510929762, -16.4, + -5.3, 2, 29.6, -27.1, -14, 0.00641395716939, -12.1, 17.2, 9.8, 29.6, -9.2, -5.1, + 0.00587706389363, 8.6, -23.6, -11, 10.3, -34.2, -14, + ], + }, + // uranus: 60 terms, max 709 km vs Horizons (1-day grid) + uranus: { + startJd: 2415020.5, + endJd: 2488069.5, + polyKm: [ + [-19445.5, -178220.5, -61713.5], + [1358199.4, -1667903.7, -745205], + [898237, 216795.3, 39509.2], + [-1435565.5, 2085998, 915201.7], + ], + terms: [ + 0.00145332579776, -480836.4, -568634.8, -231930.3, 615431.9, -419603.7, -194919.3, + 0.000267280927189, -377901, 462476.9, 212301.5, -672472.8, -13216.6, 22523.7, + 0.000591547725313, -202726.6, -414908, -166567.5, 346973, -155145.1, -81687.8, + 0.000325970917468, -430657.5, -78923.3, -18618, 299410.9, -129541.7, -55678.4, + 0.000191747598444, 97804, -55069.4, -23035.1, -244567.1, 363147.3, 172746.1, 0.00046977021653, + -96134.5, 77688.3, 37869, 15059, -150373.1, -67150, 0.000651941834893, -17868.6, -67047.9, + -28615.6, 84245.6, 27192.6, 8267, 0.000402669956862, -11746.6, 43733.6, 22498, 129761.8, + 40984.5, 15221.5, 0.00150521864815, 23999.4, -38990.4, -17327.5, 59551.9, 27425.3, 10108.9, + 0.0028946246843, -8968.3, 13924.3, 6168, -13649.1, -7627.3, -2980, 0.00138058270906, 5252, + 6011.5, 2372.7, -11459.9, 12740.1, 5738, 0.000931288145943, -26889.3, -1826.4, 68.2, 6606.1, + 10439.6, 4694.2, 0.000728640874288, -6595.9, -17770.1, -7720.4, 29456.7, 18460.9, 6928.5, + 0.00116708018075, 523.1, 10349.7, 4200.7, -8015.8, -453.4, 152.3, 0.00158191768755, 14070.8, + -8074.7, -3849.5, 21412.9, 12090, 4523.6, 0.000814927293606, -3017.9, -7741.6, -3386.1, + 13246.9, 3334.6, 682.2, 0.000987500132243, -3894.8, -747.5, -800.9, 15878.8, 2239.4, 646, + 0.00130388366966, -2558.9, 2471.6, 1334, -2217.1, 4444.9, 2032.8, 0.00165861672694, 12210.3, + -1386, -935, 11154.2, 7231.8, 2740.3, 0.00107378655156, -1429.2, 415.1, 201.8, 7026.1, 365.8, + -113.3, 0.00227432600011, -1509.5, -2042.8, -873.3, -1047.3, 1902.2, 823.9, 0.00173531576634, + 9319.2, -453.4, -499.2, 4523.1, 5120.2, 2110.4, 0.00295291301672, 2608.4, -508.3, -321.5, + 2044.5, 1629.8, 639.1, 0.00181201480573, 6274.2, 1211.6, 316, 529.2, 2312.9, 968.6, + 0.00188871384513, 3272.1, 955.3, 300.9, -728.5, 927.2, 427.5, 0.00283786445755, -761.1, 904.2, + 428.2, -859.2, 141.5, 49.9, 0.0020858675289, 1679.6, -245.1, -117.4, 488.4, -141.1, -84.9, + 0.00233932070157, 1830.3, 275.2, 78.4, 752.8, -245.3, -147.4, 0.00435734539012, 499.7, -500.3, + -226.8, 624.3, 484.5, 191.2, 0.00198458764437, 1594.7, 332.6, 93.9, 273.7, 572.3, 229.7, + 0.00272934667227, -660.5, 171.1, 71.2, 42.8, -447.3, -227.2, 0.0172021009591, -93.8, 408.4, + 177.1, -445.3, -84.1, -36.4, 0.00351825883705, 620.6, -201.3, -116.6, -587.4, 240.8, 122.4, + 0.00302961205612, 1528.4, -72.6, -93.6, 887.2, 599.3, 235.9, 0.00241601974096, 1094.2, 187.3, + 52.1, 138.2, -105.6, -66.8, 0.00216674786293, 750.9, 51.9, -11.2, 508.5, 436.3, 166.4, + 0.00310631109551, 1202.3, 41.2, -28, 286.9, 277.6, 114.9, 0.0279611366859, 257, -45.5, -36.8, + 56.1, 237.2, 103.2, 0.00250230616028, 504.7, 221.2, 85.4, 40.8, -93.5, -50, 0.00336629140148, + 673.5, 93.4, 16.5, 327.1, 24.9, 2.6, 0.00328551765363, 507, -89, -60.8, 260.5, 158.1, 60.3, + 0.00346104415263, 684.2, -25.9, -38.5, 534.2, 73.4, 13.5, 0.00319259751483, 594.1, 54.1, -3.2, + 149.1, 34.6, 11.5, 0.00259817995952, 197.2, 126.8, 57.6, 148.9, -27.9, -25.6, + 0.00359526747165, 510.4, 69.2, 13.1, -304.9, -54.8, -11, 0.00368155389097, 337.6, 7.2, -9.3, + -233.1, -18.7, 2, 0.00441019476521, 285.9, 59.3, 16.6, 65.2, 171.9, 71.5, 0.00915258769, 15.3, + -64.1, -29.8, 71.9, 14.4, 4.6, 0.00375825293036, 198.4, 74.4, 26.2, -168.9, -49.5, -14.4, + 0.0047731996548, -20.2, -50.3, -21.6, -95.6, 3.6, 3.6, 0.00384453934968, 125.9, 6.6, -2.6, + -140.3, -21.7, -3, 0.00446771904476, 179.2, 89.6, 32.6, -127.3, 21.5, 13.4, 0.00454441808415, + 95.4, 24, 7, -125.8, -5.5, 1.4, 0.00462111712355, 51.3, 47.3, 18.8, -84.3, -11.9, -2, + 0.00580396491257, -23.8, -8.5, -3.1, 7, -26.2, -11.5, 0.0040144059292, 44.5, 5.2, 1.5, -68.6, + -13.1, -3.9, 0.00392123838907, 48, -41.2, -22.3, -73, -12.9, 1.7, 0.00499379517518, 20.4, 18, + 7.3, 14.9, -9.1, -4.5, 0.00409381122771, 25.5, -1.1, -1.4, -50.6, -1.4, 0.9, 0.00469781616294, + 13.6, 10.4, 4.1, -62.3, -14.2, -3.9, + ], + }, + // neptune: 45 terms, max 862 km vs Horizons (1-day grid) + neptune: { + startJd: 2415020.5, + endJd: 2488069.5, + polyKm: [ + [70963, -352191.5, -150630.5], + [177011.8, 174437.3, 70374.1], + [-274060.7, 1356524.7, 558708.7], + [-268644.9, 365379.5, 153073.8], + ], + terms: [ + 0.00145181845597, -424116.5, -584543.9, -240067.7, 626774.7, -379579.2, -177993.5, + 0.000585979638308, -58812.4, -388455.8, -157935.7, 404607.5, -25834.8, -27980.1, + 0.000191107892144, 111463.1, -257721, -108758.3, 100235.1, -10680.1, -5065.1, + 0.000352310937894, -28486.8, -31762.2, -11773.9, -69550.4, -19710.2, -6773.6, + 0.00150521864815, 13482.4, -21162.1, -9360.5, 26676.1, 19514.5, 7522, 0.00289483862923, + -7595.2, 13929.8, 6158.3, -15283.5, -6807.4, -2553.6, 0.00116474872088, -719.4, 11065.7, + 4597.2, -11511.4, 889.8, 847.1, 0.000268446637922, 7743.5, -13952.3, -6764, -48338.2, -3874.6, + 462.3, 0.000412257336786, -33533.9, -23017.7, -8211.8, -8982.4, 14929.5, 6769.6, + 0.000651941834893, -4385.5, -6609.1, -2694.2, 14193.5, 17485.1, 6683.1, 0.00138058270906, + 6717.7, 1431.3, 500.7, -4765.1, 6149.3, 2784.4, 0.000498543756105, -21031.1, -6750.7, -2216, + -10677.1, 13596.6, 5730.9, 0.00157233030762, 9554.7, -102.4, -342.5, 5906.5, 11066.8, 4494.3, + 0.000738228254212, -4036.2, -1834.9, -573.1, 6152.6, 9492.8, 3670.3, 0.000881694795214, + -1922.9, 4476.7, 1878.9, 3553.5, 2981.1, 1097.3, 0.00163944196709, 5059.3, 3454.1, 1289.9, + -53.4, 3562.9, 1502.7, 0.0010833739314, -625.9, 1400.2, 629.2, -804.6, 1566.7, 626.9, + 0.00295291301672, 1813.8, -28.9, -60.4, 309.9, 1864.5, 786.3, 0.000805339913682, 1442.6, + 3673.7, 1478.8, 4621.2, 3418.2, 1249.3, 0.00172572838641, 2551.5, 1316.6, 465, 652.1, 1706.5, + 693, 0.00128470890981, -131.6, 100.6, 77.1, -1179.3, 1101.7, 503.3, 0.000987500132243, + -1143.8, 1030, 457.2, 641.8, 1284.1, 523, 0.00282827707762, 129, 440.9, 192.9, -640.5, 225.8, + 105.4, 0.00435737360743, 483.6, -452.1, -205.5, 501.9, 471.9, 189.8, 0.0172008658057, -112.6, + 403, 174.8, -440.6, -102.1, -44.3, 0.00180242742581, 1145.5, 1377.4, 537.8, -188, 95.6, 50.3, + 0.00259543302188, -385.5, 65.9, 34.9, 274.5, 232.7, 86.3, 0.00302961205612, 576, 241.1, 85.9, + -35.5, 596, 253.6, 0.0279616204375, 256.6, -50.5, -39, 60.8, 236.3, 102.5, 0.00188871384513, + 674.1, 709.6, 272.7, -61.5, 61.4, 28.7, 0.00311589847543, 342.8, 164.1, 59.2, 21.1, 372.7, + 156.8, 0.00196541288452, 364.2, 393.3, 149.9, -129, -130.6, -49.2, 0.0031830101349, 48.1, + 274.2, 113.1, -130.6, -20, -5.6, 0.00205169930384, 210.6, 206.1, 79.4, -50.5, -75.6, -29.9, + 0.0027419906583, -14.6, 56.2, 24.8, -131.8, 11.6, 9.3, 0.00233930095801, 134.3, 22.4, 4.9, 54, + 78.1, 31.6, 0.00441019476521, 91.8, 49.5, 18.7, -28, 87.3, 38, 0.0091477930909, 26.2, -62.2, + -29.2, 66.3, 22.8, 8.8, 0.0032597091743, 94.4, 174.2, 70.4, -103.1, 12, 8.2, 0.00213798572316, + 111.9, 92.6, 34.4, -3.6, -52.1, -21.7, 0.00254065567989, -21.8, 47.1, 20.1, 72.8, -34.4, + -16.5, 0.00333640821369, 34.5, 93.6, 38.2, -68.1, -26.7, -9.1, 0.0022338595224, 29.2, 49.7, + 19.1, 43.6, -23, -11.2, 0.00242560712089, 47.6, 11.2, 3.1, 41.9, -5.5, -3.9, 0.00342269463301, + 20.4, 52.9, 21.6, -37.8, -13.7, -4.6, + ], + }, +}; diff --git a/tests/tools/utils/math/fft.test.ts b/tests/tools/utils/math/fft.test.ts new file mode 100644 index 000000000..ff4bef6e9 --- /dev/null +++ b/tests/tools/utils/math/fft.test.ts @@ -0,0 +1,18 @@ +import { describe, it, expect } from 'vitest'; +import { fft } from '../../../../tools/utils/math/fft'; + +describe('fft', () => { + it("fft finds a pure sinusoid's bin", () => { + const n = 64; + const re = Float64Array.from({ length: n }, (_, i) => + Math.cos((2 * Math.PI * 5 * i) / n + 0.3), + ); + const im = new Float64Array(n); + fft(re, im); + const power = Array.from({ length: n / 2 }, (_, k) => re[k]! ** 2 + im[k]! ** 2); + expect(power.indexOf(Math.max(...power))).toBe(5); + // A pure in-bin tone carries amplitude N/2 and leaks nowhere. + expect(Math.sqrt(power[5]!)).toBeCloseTo(n / 2, 9); + expect(Math.max(...power.filter((_, k) => k !== 5))).toBeLessThan(1e-18); + }); +}); diff --git a/tests/tools/utils/math/fitSinusoidSeries.test.ts b/tests/tools/utils/math/fitSinusoidSeries.test.ts new file mode 100644 index 000000000..c8116add7 --- /dev/null +++ b/tests/tools/utils/math/fitSinusoidSeries.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect } from 'vitest'; +import { fitSinusoidSeries } from '../../../../tools/utils/math/fitSinusoidSeries'; +import { ephemerisCorrectionMpc } from '../../../../src/utils/orbit/ephemerisCorrectionMpc'; +import { SCALE_UNITS } from '../../../../src/data/scaleUnits'; + +const START = 2_415_020.5; +const END = START + 20_000; + +// Cubic plus two sinusoids whose periods (333.3 d, 1777.7 d) fall between FFT bins. +function signal(t: number): [number, number, number] { + const tau = (2 * (t - START)) / (END - START) - 1; + const p1 = (2 * Math.PI * (t - START)) / 333.3; + const p2 = (2 * Math.PI * (t - START)) / 1777.7; + return [ + 5000 + 800 * tau - 300 * tau ** 3 + 2000 * Math.cos(p1 + 0.4), + -1200 * tau ** 2 + 1500 * Math.sin(p1 + 0.4) + 700 * Math.cos(p2), + 40 * tau + 900 * Math.sin(p2 - 1.1), + ]; +} + +describe('fitSinusoidSeries', () => { + it('fitSinusoidSeries recovers a synthetic two-term signal', () => { + const step = 5; + const tJd = Float64Array.from( + { length: Math.floor((END - START) / step) + 1 }, + (_, i) => START + i * step, + ); + const axes = [0, 1, 2].map((a) => Float64Array.from(tJd, (t) => signal(t)[a]!)); + const fit = fitSinusoidSeries(tJd, [axes[0]!, axes[1]!, axes[2]!], START, END, 1, 50); + + expect(fit.terms.length / 7).toBeLessThanOrEqual(4); + const correction = { startJd: START, endJd: END, polyKm: fit.polyKm, terms: fit.terms }; + for (let t = START; t <= END; t += 1) { + const [x, y, z] = ephemerisCorrectionMpc(correction, t).map((v) => v / SCALE_UNITS.KM_TO_MPC); + const [sx, sy, sz] = signal(t); + expect(Math.hypot(x! - sx, y! - sy, z! - sz)).toBeLessThanOrEqual(1); + } + }); +}); diff --git a/tools/@types/math/FitResult.d.ts b/tools/@types/math/FitResult.d.ts new file mode 100644 index 000000000..322eb50b8 --- /dev/null +++ b/tools/@types/math/FitResult.d.ts @@ -0,0 +1,13 @@ +/** + * FitResult — `fitSinusoidSeries`' output, in the plain basis `EphemerisCorrection` stores: + * cubic coefficients in τ (km) and flat 7-per-term sinusoids (ω rad/day, cos xyz, sin xyz km). + * `maxErrKm` is the worst 3D residual on the fit's own sample grid. + */ + +import type { Vec3 } from '../../../src/@types/math/Vec3'; + +export type FitResult = { + polyKm: [Vec3, Vec3, Vec3, Vec3]; + terms: number[]; + maxErrKm: number; +}; diff --git a/tools/bodies/buildPlanetEphemeris.ts b/tools/bodies/buildPlanetEphemeris.ts new file mode 100644 index 000000000..34cc192f0 --- /dev/null +++ b/tools/bodies/buildPlanetEphemeris.ts @@ -0,0 +1,161 @@ +#!/usr/bin/env node +/** + * buildPlanetEphemeris — fit each planet's `Horizons − Kepler` residual (1900–2100) into the + * committed `PLANET_EPHEMERIS_CORRECTIONS` table. Kepler is the app's own path + * (`keplerianPositionMpc(propagateElements(row, jd))`), so any element-row or frame edit needs a + * rerun. Fits on a coarse grid to 900 km, then verifies the ROUNDED values, as they will be + * parsed from the emitted file, through `ephemerisCorrectionMpc` on every 1-day row; throws + * above 1,000 km. Reads `data/raw/horizons/planets/` (`npm run fetch-horizons-planets`). + */ +import { readFileSync, writeFileSync } from 'node:fs'; +import { join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { format, resolveConfig } from 'prettier'; + +import { elementsById } from '../../src/data/bodies/orbitalElements'; +import { SCALE_UNITS } from '../../src/data/scaleUnits'; +import { ephemerisCorrectionMpc } from '../../src/utils/orbit/ephemerisCorrectionMpc'; +import { keplerianPositionMpc } from '../../src/utils/orbit/keplerianPositionMpc'; +import { propagateElements } from '../../src/utils/orbit/propagateElements'; +import type { EphemerisCorrection } from '../../src/@types/scene/EphemerisCorrection'; +import type { Vec3 } from '../../src/@types/math/Vec3'; +import { fitSinusoidSeries } from '../utils/math/fitSinusoidSeries'; +import { rawDataPath } from '../utils/io/rawDataRegistry'; + +const GENERATED_PATH = 'src/data/bodies/planetEphemerisCorrections.generated.ts'; +const GENERATED_BANNER = + `// ${GENERATED_PATH}\n` + + '// !!! GENERATED FILE — DO NOT EDIT BY HAND !!!\n' + + '// Regenerate with: npm run fetch-horizons-planets && npm run build-planet-ephemeris\n' + + '// Source of truth: data/raw/horizons/planets/ (JPL Horizons) minus orbitalElements.ts\n'; + +// [planet id, Horizons target, fit sample step in days]. The step keeps ≥ ~20 samples +// across the shortest period the residual carries. +const PLANETS: readonly [string, string, number][] = [ + ['mercury', '199', 2], + ['venus', '299', 4], + ['earth', '3', 4], + ['mars', '4', 6], + ['jupiter', '5', 10], + ['saturn', '6', 10], + ['uranus', '7', 10], + ['neptune', '8', 10], +]; +const FIT_STOP_KM = 900; +const VERIFY_MAX_KM = 1000; +const MAX_TERMS = 400; +const KM_TO_MPC = SCALE_UNITS.KM_TO_MPC; + +// ω to 12 significant digits: over the 73,000-day span its phase error stays < 1e-7 rad. +// Amplitudes to 0.1 km: hundreds of 0.05 km roundings sum to well under a kilometre. +const fmtOmega = (x: number): string => String(Number(x.toPrecision(12))); +const fmtKm = (x: number): string => String(Math.round(x * 10) / 10); + +type HorizonsSeries = { jd: Float64Array; km: [Float64Array, Float64Array, Float64Array] }; + +function readSeries(naif: string): HorizonsSeries { + const lines = readFileSync(join(rawDataPath('horizons.planets'), `${naif}.csv`), 'utf8') + .trim() + .split('\n') + .slice(1); + const cols = [0, 1, 2, 3].map(() => new Float64Array(lines.length)); + lines.forEach((line, i) => line.split(',').forEach((v, c) => (cols[c]![i] = Number(v)))); + return { jd: cols[0]!, km: [cols[1]!, cols[2]!, cols[3]!] }; +} + +function keplerKm(id: string, jd: number): Vec3 { + const [x, y, z] = keplerianPositionMpc(propagateElements(elementsById(id), jd)); + return [x / KM_TO_MPC, y / KM_TO_MPC, z / KM_TO_MPC]; +} + +/** Emit one correction's literal, and the correction those exact digits parse back to. */ +function emit( + startJd: number, + endJd: number, + polyKm: readonly Vec3[], + terms: readonly number[], +): { text: string; shipped: EphemerisCorrection } { + const polyText = polyKm.map((v) => v.map(fmtKm)); + const termText = terms.map((v, i) => (i % 7 === 0 ? fmtOmega(v) : fmtKm(v))); + const parse = (s: readonly string[]): number[] => s.map(Number); + const shipped: EphemerisCorrection = { + startJd, + endJd, + polyKm: polyText.map(parse) as unknown as EphemerisCorrection['polyKm'], + terms: parse(termText), + }; + const text = + `{ startJd: ${startJd}, endJd: ${endJd}, ` + + `polyKm: [${polyText.map((v) => `[${v.join(', ')}]`).join(', ')}], ` + + `terms: [${termText.join(', ')}] }`; + return { text, shipped }; +} + +function buildPlanet(id: string, naif: string, step: number): { text: string; summary: string } { + const { jd, km } = readSeries(naif); + const startJd = jd[0]!; + const endJd = jd[jd.length - 1]!; + + // Every `step`-th row, plus the last row so the fit is never extrapolating at the end. + const last = jd.length - 1; + const sampleIdx = Array.from({ length: Math.ceil(last / step) + 1 }, (_, i) => + Math.min(i * step, last), + ); + const tJd = Float64Array.from(sampleIdx, (i) => jd[i]!); + const kepler = sampleIdx.map((i) => keplerKm(id, jd[i]!)); + const residual = [0, 1, 2].map((axis) => + Float64Array.from(sampleIdx, (i, s) => km[axis]![i]! - kepler[s]![axis]!), + ); + const fit = fitSinusoidSeries( + tJd, + [residual[0]!, residual[1]!, residual[2]!], + startJd, + endJd, + FIT_STOP_KM, + MAX_TERMS, + ); + const { text, shipped } = emit(startJd, endJd, fit.polyKm, fit.terms); + + let maxKm = 0; + for (let i = 0; i < jd.length; i++) { + const kepler = keplerianPositionMpc(propagateElements(elementsById(id), jd[i]!)); + const corr = ephemerisCorrectionMpc(shipped, jd[i]!); + const err = [0, 1, 2].map((a) => km[a]![i]! - (kepler[a]! + corr[a]!) / KM_TO_MPC); + maxKm = Math.max(maxKm, Math.hypot(err[0]!, err[1]!, err[2]!)); + } + const nTerms = fit.terms.length / 7; + const summary = `${id}: ${nTerms} terms, max ${Math.round(maxKm)} km vs Horizons (1-day grid)`; + if (maxKm > VERIFY_MAX_KM) + throw new Error(`buildPlanetEphemeris: ${summary} exceeds ${VERIFY_MAX_KM} km`); + return { text: ` // ${summary}\n ${id}: ${text},`, summary }; +} + +async function main(): Promise { + const rows: string[] = []; + for (const [id, naif, step] of PLANETS) { + const { text, summary } = buildPlanet(id, naif, step); + process.stderr.write(`${summary}\n`); + rows.push(text); + } + const source = + GENERATED_BANNER + + "import type { EphemerisCorrection } from '../../@types/scene/EphemerisCorrection';\n\n" + + 'export const PLANET_EPHEMERIS_CORRECTIONS: Readonly> = {\n' + + `${rows.join('\n')}\n};\n`; + const outPath = resolve(GENERATED_PATH); + const formatted = await format(source, { + ...(await resolveConfig(outPath)), + parser: 'typescript', + }); + writeFileSync(outPath, formatted); + process.stderr.write(`wrote ${GENERATED_PATH} (${(formatted.length / 1024).toFixed(1)} kB)\n`); +} + +// Allow the script to be both executed (CLI) and imported (tests). +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch((err: unknown) => { + process.stderr.write(`error: ${err instanceof Error ? err.message : String(err)}\n`); + process.exit(1); + }); +} diff --git a/tools/utils/math/fft.ts b/tools/utils/math/fft.ts new file mode 100644 index 000000000..6b67d9bd5 --- /dev/null +++ b/tools/utils/math/fft.ts @@ -0,0 +1,45 @@ +/** + * fft — in-place iterative radix-2 Cooley–Tukey transform of a complex signal held as two + * real arrays. Forward sign convention (e^{−2πikn/N}), unnormalised. Length must be a power + * of two; the caller zero-pads. + */ + +export function fft(re: Float64Array, im: Float64Array): void { + const n = re.length; + if (n !== im.length || (n & (n - 1)) !== 0) + throw new Error(`fft: length ${n} not a power of two`); + + // Bit-reversal permutation. + for (let i = 1, j = 0; i < n; i++) { + let bit = n >> 1; + for (; j & bit; bit >>= 1) j ^= bit; + j ^= bit; + if (i < j) { + const tr = re[i]!; + re[i] = re[j]!; + re[j] = tr; + const ti = im[i]!; + im[i] = im[j]!; + im[j] = ti; + } + } + + for (let len = 2; len <= n; len <<= 1) { + const half = len >> 1; + const ang = (-2 * Math.PI) / len; + for (let k = 0; k < half; k++) { + // Twiddle from a direct cos/sin per k, not a running product: no drift at 2^19 points. + const wr = Math.cos(ang * k); + const wi = Math.sin(ang * k); + for (let i = k; i < n; i += len) { + const b = i + half; + const xr = re[b]! * wr - im[b]! * wi; + const xi = re[b]! * wi + im[b]! * wr; + re[b] = re[i]! - xr; + im[b] = im[i]! - xi; + re[i] = re[i]! + xr; + im[i] = im[i]! + xi; + } + } + } +} diff --git a/tools/utils/math/fitSinusoidSeries.ts b/tools/utils/math/fitSinusoidSeries.ts new file mode 100644 index 000000000..9a0280d52 --- /dev/null +++ b/tools/utils/math/fitSinusoidSeries.ts @@ -0,0 +1,155 @@ +/** + * fitSinusoidSeries — fit a 3-axis residual (km, near-uniform time grid) as a cubic in τ plus + * sinusoids, greedily: FFT the remaining residual (×8 zero-pad, power summed over xyz) to + * bracket the strongest tone, refine its ω, add its cos and sin columns, stop at `stopKm`. + * Columns are orthonormalised incrementally (Gram–Schmidt, two passes), so every step is an + * exact least-squares refit; the kept R turns the orthogonal coefficients back into plain + * (ω, cos, sin) amplitudes. τ and phase conventions match `ephemerisCorrectionMpc`. + */ + +import type { FitResult } from '../../@types/math/FitResult'; +import type { Vec3 } from '../../../src/@types/math/Vec3'; +import { fft } from './fft'; + +const ZERO_PAD = 8; +const GOLDEN = (Math.sqrt(5) - 1) / 2; +// Shrinks the two-bin bracket by 0.618^40 ≈ 4e-9: far below any phase error that matters. +const GOLDEN_ITERATIONS = 40; + +const dot = (a: Float64Array, b: Float64Array): number => { + let s = 0; + for (let i = 0; i < a.length; i++) s += a[i]! * b[i]!; + return s; +}; + +export function fitSinusoidSeries( + tJd: Float64Array, + residualKm: [Float64Array, Float64Array, Float64Array], + startJd: number, + endJd: number, + stopKm: number, + maxTerms: number, +): FitResult { + const n = tJd.length; + // Uniform, except the last gap may be short so the grid can end exactly on `endJd`: an + // unsampled tail is extrapolated, and drifts past the stop. The FFT only brackets ω, so + // one sample slightly off its index slot is harmless; the refine and refit use true times. + const stepDays = tJd[1]! - tJd[0]!; + for (let i = 1; i < n; i++) { + const gap = tJd[i]! - tJd[i - 1]!; + const ok = i === n - 1 ? gap > 0 && gap <= stepDays : Math.abs(gap - stepDays) < 1e-6; + if (!ok) throw new Error('fit: grid not uniform'); + } + const dt = Float64Array.from(tJd, (t) => t - startJd); + const tau = Float64Array.from(dt, (d) => (2 * d) / (endJd - startJd) - 1); + + const residual = residualKm.map((a) => Float64Array.from(a)); + const q: Float64Array[] = []; + const rCols: Float64Array[] = []; // column j of the upper-triangular R + const proj: number[][] = [[], [], []]; // q_j · y, per axis + + const addColumn = (a: Float64Array): void => { + const c = Float64Array.from(a); + const rc = new Float64Array(q.length + 1); + for (let pass = 0; pass < 2; pass++) { + q.forEach((qj, j) => { + const d = dot(qj, c); + for (let i = 0; i < n; i++) c[i]! -= d * qj[i]!; + rc[j]! += d; + }); + } + const norm = Math.sqrt(dot(c, c)); + if (norm < 1e-9 * Math.sqrt(dot(a, a))) throw new Error('fit: degenerate column'); + for (let i = 0; i < n; i++) c[i]! /= norm; + rc[q.length] = norm; + q.push(c); + rCols.push(rc); + residual.forEach((r, axis) => { + const d = dot(c, r); + for (let i = 0; i < n; i++) r[i]! -= d * c[i]!; + proj[axis]!.push(d); + }); + }; + const maxErr = (): number => { + let m = 0; + for (let i = 0; i < n; i++) + m = Math.max(m, Math.hypot(residual[0]![i]!, residual[1]![i]!, residual[2]![i]!)); + return m; + }; + + // Residual energy (xyz) a cos/sin pair at ω would remove, net of what the kept columns + // already span. The bare periodogram ignores that overlap and is biased wherever a tone + // correlates with the cubic or a neighbour, which costs extra terms to mop up. + const capturedEnergy = (omega: number): number => { + const c = Float64Array.from(dt, (d) => Math.cos(omega * d)); + const s = Float64Array.from(dt, (d) => Math.sin(omega * d)); + let gcc = dot(c, c); + let gcs = dot(c, s); + let gss = dot(s, s); + for (const qj of q) { + const qc = dot(qj, c); + const qs = dot(qj, s); + gcc -= qc * qc; + gcs -= qc * qs; + gss -= qs * qs; + } + const det = gcc * gss - gcs * gcs; + let total = 0; + for (const r of residual) { + const bc = dot(r, c); + const bs = dot(r, s); + total += (gss * bc * bc - 2 * gcs * bc * bs + gcc * bs * bs) / det; + } + return total; + }; + + for (let p = 0; p <= 3; p++) addColumn(Float64Array.from(tau, (u) => u ** p)); + + let m = 1; + while (m < n * ZERO_PAD) m <<= 1; + const re = new Float64Array(m); + const im = new Float64Array(m); + const power = new Float64Array(m / 2); + const omegas: number[] = []; + while (omegas.length < maxTerms && maxErr() > stopKm) { + power.fill(0); + for (const r of residual) { + re.fill(0); + im.fill(0); + re.set(r); + fft(re, im); + for (let k = 1; k < m / 2; k++) power[k]! += re[k]! ** 2 + im[k]! ** 2; + } + let kb = 1; + for (let k = 2; k < m / 2 - 1; k++) if (power[k]! > power[kb]!) kb = k; + // The padded FFT only brackets the peak; a golden-section search inside the bracket + // lands on it, so one term absorbs a tone instead of a cluster of neighbours. + let lo = (2 * Math.PI * (kb - 1)) / (m * stepDays); + let hi = (2 * Math.PI * (kb + 1)) / (m * stepDays); + for (let it = 0; it < GOLDEN_ITERATIONS; it++) { + const w1 = hi - GOLDEN * (hi - lo); + const w2 = lo + GOLDEN * (hi - lo); + if (capturedEnergy(w1) < capturedEnergy(w2)) lo = w1; + else hi = w2; + } + const omega = (lo + hi) / 2; + addColumn(Float64Array.from(dt, (d) => Math.cos(omega * d))); + addColumn(Float64Array.from(dt, (d) => Math.sin(omega * d))); + omegas.push(omega); + } + + // Back-substitute R·x = qᵀy per axis: x are the plain-basis coefficients. + const k = q.length; + const coef = proj.map((d) => { + const x = new Float64Array(k); + for (let j = k - 1; j >= 0; j--) { + let s = d[j]!; + for (let l = j + 1; l < k; l++) s -= rCols[l]![j]! * x[l]!; + x[j] = s / rCols[j]![j]!; + } + return x; + }); + const at = (j: number): Vec3 => [coef[0]![j]!, coef[1]![j]!, coef[2]![j]!]; + const terms = omegas.flatMap((omega, t) => [omega, ...at(4 + 2 * t), ...at(5 + 2 * t)]); + return { polyKm: [at(0), at(1), at(2), at(3)], terms, maxErrKm: maxErr() }; +} From 309d53be8a7abc1bac8cf34f72937d6c54d7659b Mon Sep 17 00:00:00 2001 From: Alexander Rulkens Date: Sun, 4 Oct 2026 09:58:40 +0200 Subject: [PATCH 08/14] Prep review fixes: readable settle trace, obliquity-tolerant disc bearing, comment wraps --- src/data/bodies/orbitalElements.ts | 13 +- tests/fixtures/camera/settleGoldenTrace.json | 1575 ++++++++++++++++- .../engine/camera/cameraFraming.test.ts | 6 +- .../engine/frame/settleGoldenTrace.test.ts | 2 +- tests/utils/orbit/propagateElements.test.ts | 6 +- 5 files changed, 1589 insertions(+), 13 deletions(-) diff --git a/src/data/bodies/orbitalElements.ts b/src/data/bodies/orbitalElements.ts index bdd15c2da..103a0a113 100644 --- a/src/data/bodies/orbitalElements.ts +++ b/src/data/bodies/orbitalElements.ts @@ -1,12 +1,13 @@ /** * orbitalElements — the J2000 Keplerian table, single source of truth for BOTH body * positions (`keplerianPositionMpc`) and their trails (`keplerianEllipse`); the trail pass - * anchors each trail on the snapshot body position, so a body sits on its own trail by construction. Per row: `focusId` (the - * Sun for planets, the parent planet for a moon) and `plane` (omitted = ecliptic, which is how - * JPL publishes the planets and the Moon; a planet's own moons ride their OWN Laplace pole — - * see `orbitPlaneFrames.ts`). No buried Mpc/radian literals, and JPL's `L`/`ϖ` columns are - * converted inline (`ω = ϖ − Ω`, `M = L − ϖ`, likewise the rates) so every transcription stays - * checkable against the source: JPL SSD approx_pos.html Table 1 (planets), sats/elem (moons). + * anchors each trail on the snapshot body position, so a body sits on its own trail by + * construction. Per row: `focusId` (the Sun for planets, the parent planet for a moon) and + * `plane` (omitted = ecliptic, which is how JPL publishes the planets and the Moon; a planet's + * own moons ride their OWN Laplace pole — see `orbitPlaneFrames.ts`). No buried Mpc/radian + * literals, and JPL's `L`/`ϖ` columns are converted inline (`ω = ϖ − Ω`, `M = L − ϖ`, + * likewise the rates) so every transcription stays checkable against the source: JPL SSD + * approx_pos.html Table 1 (planets), sats/elem (moons). */ import { SCALE_UNITS } from '../scaleUnits'; diff --git a/tests/fixtures/camera/settleGoldenTrace.json b/tests/fixtures/camera/settleGoldenTrace.json index 19db51a8c..a92851fe3 100644 --- a/tests/fixtures/camera/settleGoldenTrace.json +++ b/tests/fixtures/camera/settleGoldenTrace.json @@ -1 +1,1574 @@ -{"northUp":[{"label":"start","arm":"absolute","hr":5,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.23882029118e-15,0],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.23882029118e-15,0],"memory":0},{"label":"dive 0","arm":"absolute","hr":4.69959329559,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.17679530435e-15,7.36444650118e-18],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.17679530435e-15,7.36444650118e-18],"memory":0},{"label":"dive 20","arm":"earth","hr":0.408531953689,"displayed":[-8.58823383762e-13,4.30208382526e-12,1.86524674597e-12,0.7,0.299999999999,8.43496123044e-17,-0.0199073133356],"register":[0,0,0,7933158.87757,4190780.53426,-175113.714869,-0.449694432696,0.861174548827,0.236966904135,0.127469437126,-0.200712858741,0.971321723702,-0.88403985193,-0.467004009393,0.01951397986],"memory":0},{"label":"dive 39","arm":"earth","hr":0.0615681608771,"displayed":[-8.58815842393e-13,4.30209013615e-12,1.86525828107e-12,0.763865807331,0.340617091539,1.27119833965e-17,-0.255756288997],"register":[0,0,0,5819022.64155,3438051.86992,245633.027437,-0.509248730504,0.860307829721,0.0231553147304,-0.0189799082836,-0.0381256553536,0.999092687134,-0.860410072908,-0.508347196829,-0.0357440052922],"memory":0},{"label":"dive brisk 0","arm":"earth","hr":0.0456277801724,"displayed":[-8.58815437959e-13,4.30209050575e-12,1.86525869645e-12,0.767221245664,0.342024316219,9.42077070425e-18,-0.270221482753],"register":[0,0,0,5722201.57312,3401370.42056,255466.623119,-0.51124791987,0.859378254518,0.0097252295442,-0.0276626493212,-0.0277645251701,0.999231659314,-0.858987975621,-0.510586081678,-0.0379672350284],"memory":0},{"label":"dive brisk 35","arm":"earth","hr":0.00000240000070995,"displayed":[-8.58814244804e-13,4.30209160677e-12,1.86525980005e-12,0.77694673987,0.34536113635,4.95528126503e-22,-0.282909410527],"register":[0,0,0,5445555.10972,3295180.12262,278483.997844,-0.517709104819,0.855556709276,-3.87938092761e-9,-0.0373973149811,-0.0226296239348,0.999044213713,-0.854738979817,-0.517214285701,-0.0437110860907],"memory":0},{"label":"tilt 1 move 0","arm":"earth","hr":0.00000248496508704,"displayed":[-8.58814244804e-13,4.30209160677e-12,1.86525980005e-12,0.752741906691,0.264826976113,5.1487696481e-22,-0.275632471139],"register":[0,0,0,5445555.62154,3295180.43233,278482.708845,-0.517709104819,0.855556709276,-3.87938117741e-9,0.0342565571094,0.0207291173096,0.999198074453,-0.854870616574,-0.517293940529,0.0400400800377],"memory":0.0837755978623},{"label":"tilt 1 move 3","arm":"earth","hr":0.00000263277160673,"displayed":[-8.58814244804e-13,4.30209160677e-12,1.86525980006e-12,0.684990514444,0.02256104076,5.7560563135e-22,-0.265855190469],"register":[0,0,0,5445556.58153,3295181.01323,278478.606315,-0.517709104819,0.855556709276,-3.87938191293e-9,0.245778001551,0.148723645672,0.957848866561,-0.819494024835,-0.495887078306,0.287272603686],"memory":0.33510235913},{"label":"tilt 1 end","arm":"earth","hr":0.0000023999998191,"displayed":[-8.58814244845e-13,4.30209160682e-12,1.86525980147e-12,0.304162643125,-0.931255732717,2.17004580513e-21,-0.455717746089],"register":[0,0,0,5445555.90789,3295180.6056,278462.674384,-0.517709104819,0.855556709276,-3.87938481339e-9,0.823616128798,0.498381422508,0.27068880672,-0.231589626648,-0.140138056616,0.962666904966],"memory":1.34040951538},{"label":"pan move 0","arm":"earth","hr":0.00000239999969298,"displayed":[-8.5881424494e-13,4.30209160674e-12,1.86525980147e-12,0.304162393914,-0.931255659819,2.17004580504e-21,-0.455717356973],"register":[0,0,0,5445557.92428,3295177.27335,278462.674378,-0.517708581286,0.855557026073,-3.8793856183e-9,0.823616433768,0.49838091852,0.270688806719,-0.231589712401,-0.140137914902,0.962666904966],"memory":1.34040951538},{"label":"pan end","arm":"earth","hr":0.00000240000053964,"displayed":[-8.588142453e-13,4.30209160641e-12,1.86525980122e-12,0.304160550871,-0.931256443589,2.17004580513e-21,-0.455716549823],"register":[0,0,0,5445566.032,3295164.51355,278455.113546,-0.517706549676,0.855558255423,-3.87938579871e-9,0.823617892326,0.498379129223,0.270687663177,-0.231589066805,-0.140136772948,0.962667226514],"memory":1.34040951538},{"label":"tilt 2 move 0","arm":"earth","hr":0.00000240000208973,"displayed":[-8.58814245335e-13,4.30209160644e-12,1.86525980243e-12,0.23536204091,-1.00544579425,3.39220446157e-21,-0.512449439596],"register":[0,0,0,5445566.07774,3295164.54122,278453.891718,-0.517706549676,0.855558255423,-3.87938578483e-9,0.840108222102,0.508357586402,0.189184406081,-0.161858282392,-0.0979420028656,0.981941576926],"memory":1.42418512751},{"label":"tilt 2 end","arm":"earth","hr":0.0000023999991996,"displayed":[-8.58814244608e-13,4.30209160562e-12,1.86525979876e-12,-2.18873636672,-0.782395233589,4.95528116513e-22,-2.76219997364],"register":[0,0,0,5445566.74476,3295164.94484,278436.070134,-0.517706549676,0.855558255423,-3.87938620117e-9,0.413544091929,0.250239513462,-0.875420738807,0.748973439084,0.453211051807,0.483361696938],"memory":2.68081938898},{"label":"look move 0","arm":"earth","hr":0.00000239999928509,"displayed":[-8.58814244617e-13,4.30209160561e-12,1.86525979876e-12,-2.21974811218,-0.790268249161,4.95528116513e-22,-2.83105142834],"register":[0,0,0,5445566.74476,3295164.94484,278436.070134,-0.518953941777,0.853201609233,0.0522859476041,0.38922925736,0.290318414674,-0.874194373876,0.761043619991,0.433315395645,0.482752914405],"memory":2.68081938898},{"label":"look end","arm":"earth","hr":0.00000240000213503,"displayed":[-8.58814244616e-13,4.3020916056e-12,1.86525979873e-12,-2.26868065666,-0.74568333147,4.95528116513e-22,-2.91256664702],"register":[0,0,0,5445566.74476,3295164.94484,278436.070134,-0.518778917195,0.848506396737,0.10442858694,0.323926898868,0.308138328882,-0.894495463635,0.791163573014,0.43021815974,0.434709714368],"memory":2.73317926654},{"label":"recede brisk 0","arm":"earth","hr":0.00000323966126414,"displayed":[-8.58814244392e-13,4.30209160535e-12,1.86525979882e-12,-2.23721013181,-0.739329340615,6.68892992494e-22,-2.83572280702],"register":[0,0,0,5445571.31718,3295167.71166,278436.303925,-0.518852758383,0.853724227906,0.0441220784167,0.352762011006,0.260834442443,-0.898623590402,0.778685288617,0.450688735516,0.436496145424],"memory":2.73317926654},{"label":"recede brisk 35","arm":"earth","hr":0.11764992274,"displayed":[-8.58790612634e-13,4.30206304827e-12,1.86528148545e-12,-1.73704603665,-1.24764085932,2.42911852576e-17,-2.40941359841],"register":[0,0,0,6086222.64462,3682832.00722,311193.305409,-0.51898218053,0.852996952088,0.0552602571646,0.711374857916,0.466850497001,-0.525353618979,0.473923314279,0.233338409138,0.849087674509],"memory":2.73317926654},{"label":"recede 0","arm":"earth","hr":0.130023273126,"displayed":[-8.58790902658e-13,4.30206246655e-12,1.86528562957e-12,-1.30186041478,-1.35142240492,2.68459115123e-17,-1.99679807879],"register":[0,0,0,6153602.38829,3723604.13981,314638.484196,-0.519054562022,0.851821354531,0.0705885373798,0.77198370136,0.502648226471,-0.389083442541,0.366910788174,0.147462335498,0.918494057211],"memory":2.73317926654},{"label":"recede 3","arm":"earth","hr":0.175513060418,"displayed":[-8.58796269929e-13,4.3020644555e-12,1.86529713172e-12,0.394311083513,-1.13068223763,3.62381901023e-17,-0.359788132177],"register":[0,0,0,6401319.46668,3873500.13249,327304.451403,-0.518387722124,0.846342418415,0.122387419055,0.854624935456,0.507744775192,0.108680554666,-0.0298393908959,-0.160934005283,0.986513992143],"memory":2.73317926654},{"label":"recede 19","arm":"absolute","hr":0.904220833938,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,3.93164567996e-16,-5.55111512313e-17],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,3.93164567996e-16,-5.55111512313e-17],"memory":2.73317926654},{"label":"recede world 0","arm":"absolute","hr":0.999318569187,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,4.12799402008e-16,1.38777878078e-17],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,4.12799402008e-16,1.38777878078e-17],"memory":2.73317926654},{"label":"recede world 7","arm":"absolute","hr":2.01238047432,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,6.21966342726e-16,-5.55738316694e-18],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,6.21966342726e-16,-5.55738316694e-18],"memory":2.73317926654},{"label":"re-dive 0","arm":"absolute","hr":1.82087715249,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,5.82426642574e-16,-5.96791886065e-17],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.772030404994,0.265666289276,5.82426642574e-16,-5.96791886065e-17],"memory":2.73317926654},{"label":"re-dive 14","arm":"earth","hr":0.449022777516,"displayed":[-8.58813615026e-13,4.30209194307e-12,1.86525445823e-12,0.770526869984,0.146645730242,9.36724036408e-17,-0.0125259023887],"register":[0,0,0,7807994.71433,4915356.7519,-314986.806499,-0.506032322158,0.820749515208,0.265144342232,0.268413886155,-0.142292603399,0.952736480217,-0.819686082992,-0.553283876779,0.148295910413],"memory":2.73317926654},{"label":"re-dive engaged 0","arm":"earth","hr":0.404990357704,"displayed":[-8.58814134638e-13,4.30209146175e-12,1.86525332033e-12,0.766060308078,0.0569265954535,8.56332551382e-17,-0.0146819552961],"register":[0,0,0,7548603.9725,4780283.67862,-539752.097825,-0.504222807269,0.822850390028,0.26206219922,0.339575685799,-0.0900952685041,0.936253809716,-0.794007376694,-0.561070475273,0.233991896297],"memory":2.73317926654},{"label":"re-dive engaged 3","arm":"earth","hr":0.268610130415,"displayed":[-8.58815551831e-13,4.3020902092e-12,1.86525170703e-12,0.730401394984,-0.381240094609,6.74071828526e-17,-0.0418799467016],"register":[0,0,0,6703618.31588,4337564.0694,-1253338.52376,-0.498422031517,0.834725566235,0.234112595928,0.637177733564,0.169602983375,0.751824024543,-0.587860539881,-0.523896990928,0.616402407967],"memory":2.73317926654}],"northUpOff":[{"label":"start","arm":"absolute","hr":5,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.23882029118e-15,0],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.23882029118e-15,0],"memory":0},{"label":"dive 0","arm":"absolute","hr":4.69959329559,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.17679530435e-15,0],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.7,0.3,1.17679530435e-15,0],"memory":0},{"label":"dive 20","arm":"earth","hr":0.408531953689,"displayed":[-8.58823383762e-13,4.30208382526e-12,1.86524674597e-12,0.7,0.299999999999,8.43496123044e-17,0],"register":[0,0,0,7933158.87757,4190780.53426,-175113.714869,-0.447067922013,0.85700852238,0.256255079316,0.136395796513,-0.217815627948,0.966412199279,-0.88403985193,-0.467004009393,0.01951397986],"memory":0},{"label":"dive 39","arm":"earth","hr":0.0615621239606,"displayed":[-8.58815721031e-13,4.30209022991e-12,1.86525805271e-12,0.763746321348,0.339489149201,1.27107388113e-17,-0.0200470287363],"register":[0,0,0,5816774.57098,3442304.40762,238147.977164,-0.499526477634,0.82767106602,0.25580051723,0.101423426099,-0.237373932188,0.966109157888,-0.860340971232,-0.508541269506,-0.0346293290295],"memory":0},{"label":"dive brisk 0","arm":"earth","hr":0.0456251399424,"displayed":[-8.58815389035e-13,4.30209054158e-12,1.86525856693e-12,0.767005933745,0.341447327804,9.42022602832e-18,-0.0211355075587],"register":[0,0,0,5721375.78023,3403037.18244,251292.223917,-0.502132641784,0.826126332225,0.255691402398,0.0996859833328,-0.238403754719,0.966036414667,-0.859025910425,-0.510567265834,-0.0373570913832],"memory":0},{"label":"dive brisk 35","arm":"earth","hr":0.0000023999987333,"displayed":[-8.58814448428e-13,4.30209144188e-12,1.86526003613e-12,0.775991805555,0.346814022309,4.9552812602e-22,-0.0241671760466],"register":[0,0,0,5449456.70472,3288098.79604,285768.720091,-0.50927763239,0.821847491179,0.255348770098,0.0949226562623,-0.241256315416,0.965807993133,-0.855351379537,-0.516102791617,-0.0448544982087],"memory":0},{"label":"tilt 1 move 0","arm":"earth","hr":0.0000024849657001,"displayed":[-8.58814448428e-13,4.30209144188e-12,1.86526003613e-12,0.773897707698,0.263062016837,5.14876964263e-22,-0.023537904874],"register":[0,0,0,5449457.04302,3288099.39241,285767.475381,-0.509277636295,0.821847488822,0.255348769893,0.166163707683,-0.197223826706,0.966174096335,-0.844408616381,-0.534480558389,0.0361195416064],"memory":0.0837756009065},{"label":"tilt 1 move 3","arm":"earth","hr":0.00000263277086576,"displayed":[-8.58814448431e-13,4.30209144188e-12,1.86526003614e-12,0.768044168272,0.0118011273059,5.75605630761e-22,-0.0227296011328],"register":[0,0,0,5449457.45477,3288100.87905,285763.51178,-0.509277636295,0.821847488822,0.255348769893,0.370939252781,-0.0581077677636,0.926837395702,-0.776556733224,-0.566736440016,0.275262506787],"memory":0.335102362174},{"label":"tilt 1 end","arm":"earth","hr":0.0000023999997163,"displayed":[-8.58814448742e-13,4.30209144169e-12,1.8652600375e-12,0.732896834484,-0.993109049844,2.17004583329e-21,-0.0416281402392],"register":[0,0,0,5449454.67867,3288103.94536,285748.10602,-0.509277636295,0.821847488822,0.255348769893,0.854427726547,0.447375703741,0.264212489882,-0.102905535695,-0.352734581263,0.93004772238],"memory":1.34040951843},{"label":"pan move 0","arm":"earth","hr":0.00000239999935414,"displayed":[-8.58814448833e-13,4.3020914416e-12,1.86526003747e-12,0.7328965318,-0.99310904296,2.17004583338e-21,-0.0416278046814],"register":[0,0,0,5449456.66222,3288100.7444,285747.111475,-0.509277115907,0.821847810737,0.255348771684,0.854428026575,0.447375219571,0.264212339449,-0.102905619947,-0.3527344453,0.930047764624],"memory":1.34040951843},{"label":"pan end","arm":"earth","hr":0.00000240000002849,"displayed":[-8.58814449131e-13,4.30209144132e-12,1.86526003711e-12,0.732895290976,-0.993110203657,2.17004583347e-21,-0.0416265549024],"register":[0,0,0,5449463.64111,3288090.14867,285735.942789,-0.509275095207,0.821849056657,0.255348791801,0.854429314677,0.447373757161,0.264210650097,-0.102904925166,-0.352733397165,0.930048239018],"memory":1.34040951843},{"label":"tilt 2 move 0","arm":"earth","hr":0.00000240000012441,"displayed":[-8.58814449397e-13,4.30209144116e-12,1.86526003827e-12,0.725551042947,-1.07680251628,3.39220453137e-21,-0.0479469740881],"register":[0,0,0,5449463.52501,3288090.44372,285734.761608,-0.509275095207,0.821849056657,0.255348791801,0.860043573015,0.475320724373,0.185459595323,-0.0310472207774,-0.314061040331,0.948895006325],"memory":1.42418513056},{"label":"tilt 2 end","arm":"earth","hr":0.00000239999946028,"displayed":[-8.58814447958e-13,4.30209144096e-12,1.86526003473e-12,-2.35006327273,-0.807403211062,4.95528116483e-22,-3.10871731543],"register":[0,0,0,5449461.83163,3288094.74734,285717.532912,-0.509275095207,0.821849056657,0.255348791801,0.295295741598,0.445571980531,-0.845142612321,0.808355925565,0.355006673512,0.469607239473],"memory":2.68081939203},{"label":"look move 0","arm":"earth","hr":0.00000240000239859,"displayed":[-8.58814447966e-13,4.30209144095e-12,1.86526003472e-12,-2.38373364458,-0.807339445766,4.95528116483e-22,3.10323965641],"register":[0,0,0,5449461.83163,3288094.74734,285717.532912,-0.503608949544,0.808096569413,0.305545349241,0.271538582696,0.483802296668,-0.831986860381,0.820149269369,0.336028677709,0.463076563551],"memory":2.68081939203},{"label":"look end","arm":"earth","hr":0.00000239999978224,"displayed":[-8.5881444796e-13,4.30209144094e-12,1.8652600347e-12,-2.40944022372,-0.753557832895,4.95528116483e-22,3.03763473516],"register":[0,0,0,5449461.83163,3288094.74734,285717.532912,-0.496562446757,0.792129145641,0.354904428116,0.20420684848,0.504009800245,-0.839210155021,0.843638133032,0.344246333143,0.412030536019],"memory":2.73317926959},{"label":"recede brisk 0","arm":"earth","hr":0.0000032396613292,"displayed":[-8.58814447761e-13,4.30209144068e-12,1.86526003478e-12,-2.40944022372,-0.753557832895,6.68892992433e-22,3.03763473516],"register":[0,0,0,5449466.40732,3288097.50822,285717.772817,-0.496562446757,0.792129145641,0.354904428116,0.20420684848,0.504009800245,-0.839210155021,0.843638133032,0.344246333143,0.412030536019],"memory":2.73317926959},{"label":"recede brisk 35","arm":"earth","hr":0.117649922735,"displayed":[-8.58795358288e-13,4.30205885827e-12,1.8652804861e-12,-2.62627932389,-1.30978913277,2.42911852565e-17,2.84396200263],"register":[0,0,0,6090575.97768,3674930.02048,319331.412187,-0.496562446757,0.792129145641,0.354904428116,0.623880006388,0.609985822238,-0.48856016446,0.603489415069,0.0211828537717,0.797089588821],"memory":2.73317926959},{"label":"recede 0","arm":"earth","hr":0.130023273121,"displayed":[-8.58796303391e-13,4.30205769492e-12,1.86528438925e-12,-3.00081952413,-1.44805394725,2.6845911511e-17,2.47527894691],"register":[0,0,0,6158003.91651,3715614.67124,322866.686849,-0.496562446757,0.792129145641,0.354904428116,0.708736754964,0.606063947721,-0.361079912812,0.501116701612,-0.0722350877246,0.862359636965],"memory":2.73317926959},{"label":"recede 3","arm":"earth","hr":0.175513060412,"displayed":[-8.58802801726e-13,4.30205868017e-12,1.86529532019e-12,0.974192518494,-1.15078860015,3.62381901006e-17,0.186667779321],"register":[0,0,0,6405898.18113,3865189.04291,335863.885453,-0.496562446757,0.792129145641,0.354904428116,0.863197654257,0.493605561447,0.10603470844,0.0911896164954,-0.359005524099,0.928870005711],"memory":2.73317926959},{"label":"recede 19","arm":"absolute","hr":0.904220834056,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,3.9316456802e-16,0.0785409033636],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,3.9316456802e-16,0.0785409033636],"memory":2.73317926959},{"label":"recede world 0","arm":"absolute","hr":0.999318569316,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,4.12799402035e-16,0.0785409033636],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,4.12799402035e-16,0.0785409033636],"memory":2.73317926959},{"label":"recede world 7","arm":"absolute","hr":2.01238047459,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,6.2196634278e-16,0.0785409033636],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,6.2196634278e-16,0.0785409033636],"memory":2.73317926959},{"label":"re-dive 0","arm":"absolute","hr":1.82087715273,"displayed":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,5.82426642623e-16,0.0785409033636],"register":[-8.58950454652e-13,4.30224651096e-12,1.86525077502e-12,0.782643157335,0.267207821768,5.82426642623e-16,0.0785409033636],"memory":2.73317926959},{"label":"re-dive 14","arm":"earth","hr":0.449022777574,"displayed":[-8.58812915346e-13,4.30209258004e-12,1.86525502852e-12,0.79206408103,0.148535164831,9.36724038973e-17,0.0785409033636],"register":[0,0,0,7762493.42556,4989270.84689,-274958.882602,-0.495950457609,0.791704119425,0.356703982153,0.314692779118,-0.218989581328,0.923586497325,-0.809321690287,-0.570305313456,0.14053558651],"memory":2.73317926959},{"label":"re-dive engaged 0","arm":"earth","hr":0.404742850815,"displayed":[-8.58813375989e-13,4.30209214146e-12,1.86525383748e-12,0.801544274338,0.0594167972848,8.55816295471e-17,0.0775559811744],"register":[0,0,0,7485126.427,4881333.67662,-491022.434865,-0.49789858041,0.790279465874,0.357148945741,0.385779940944,-0.167003855664,0.907349739273,-0.776705118299,-0.589549046315,0.221722983015],"memory":2.73317926959},{"label":"re-dive engaged 3","arm":"earth","hr":0.266757742279,"displayed":[-8.58814922355e-13,4.30209075677e-12,1.8652520037e-12,0.863342306569,-0.376693585659,6.70660385912e-17,0.0879707620004],"register":[0,0,0,6556757.59107,4560832.38104,-1157984.13863,-0.514404629972,0.774691977732,0.367750209111,0.67707458349,0.103731078593,0.72856699879,-0.526267883359,-0.623772557084,0.577883995256],"memory":2.73317926959}]} +{ + "northUp": [ + { + "label": "start", + "arm": "absolute", + "hr": 5, + "displayed": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.7, + 0.3, + 1.23882029118e-15, + 0 + ], + "register": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.7, + 0.3, + 1.23882029118e-15, + 0 + ], + "memory": 0 + }, + { + "label": "dive 0", + "arm": "absolute", + "hr": 4.69959329559, + "displayed": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.7, + 0.3, + 1.17679530435e-15, + 7.36444650118e-18 + ], + "register": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.7, + 0.3, + 1.17679530435e-15, + 7.36444650118e-18 + ], + "memory": 0 + }, + { + "label": "dive 20", + "arm": "earth", + "hr": 0.408531953689, + "displayed": [ + -8.58823383762e-13, + 4.30208382526e-12, + 1.86524674597e-12, + 0.7, + 0.299999999999, + 8.43496123044e-17, + -0.0199073133356 + ], + "register": [ + 0, + 0, + 0, + 7933158.87757, + 4190780.53426, + -175113.714869, + -0.449694432696, + 0.861174548827, + 0.236966904135, + 0.127469437126, + -0.200712858741, + 0.971321723702, + -0.88403985193, + -0.467004009393, + 0.01951397986 + ], + "memory": 0 + }, + { + "label": "dive 39", + "arm": "earth", + "hr": 0.0615681608771, + "displayed": [ + -8.58815842393e-13, + 4.30209013615e-12, + 1.86525828107e-12, + 0.763865807331, + 0.340617091539, + 1.27119833965e-17, + -0.255756288997 + ], + "register": [ + 0, + 0, + 0, + 5819022.64155, + 3438051.86992, + 245633.027437, + -0.509248730504, + 0.860307829721, + 0.0231553147304, + -0.0189799082836, + -0.0381256553536, + 0.999092687134, + -0.860410072908, + -0.508347196829, + -0.0357440052922 + ], + "memory": 0 + }, + { + "label": "dive brisk 0", + "arm": "earth", + "hr": 0.0456277801724, + "displayed": [ + -8.58815437959e-13, + 4.30209050575e-12, + 1.86525869645e-12, + 0.767221245664, + 0.342024316219, + 9.42077070425e-18, + -0.270221482753 + ], + "register": [ + 0, + 0, + 0, + 5722201.57312, + 3401370.42056, + 255466.623119, + -0.51124791987, + 0.859378254518, + 0.0097252295442, + -0.0276626493212, + -0.0277645251701, + 0.999231659314, + -0.858987975621, + -0.510586081678, + -0.0379672350284 + ], + "memory": 0 + }, + { + "label": "dive brisk 35", + "arm": "earth", + "hr": 0.00000240000070995, + "displayed": [ + -8.58814244804e-13, + 4.30209160677e-12, + 1.86525980005e-12, + 0.77694673987, + 0.34536113635, + 4.95528126503e-22, + -0.282909410527 + ], + "register": [ + 0, + 0, + 0, + 5445555.10972, + 3295180.12262, + 278483.997844, + -0.517709104819, + 0.855556709276, + -3.87938092761e-9, + -0.0373973149811, + -0.0226296239348, + 0.999044213713, + -0.854738979817, + -0.517214285701, + -0.0437110860907 + ], + "memory": 0 + }, + { + "label": "tilt 1 move 0", + "arm": "earth", + "hr": 0.00000248496508704, + "displayed": [ + -8.58814244804e-13, + 4.30209160677e-12, + 1.86525980005e-12, + 0.752741906691, + 0.264826976113, + 5.1487696481e-22, + -0.275632471139 + ], + "register": [ + 0, + 0, + 0, + 5445555.62154, + 3295180.43233, + 278482.708845, + -0.517709104819, + 0.855556709276, + -3.87938117741e-9, + 0.0342565571094, + 0.0207291173096, + 0.999198074453, + -0.854870616574, + -0.517293940529, + 0.0400400800377 + ], + "memory": 0.0837755978623 + }, + { + "label": "tilt 1 move 3", + "arm": "earth", + "hr": 0.00000263277160673, + "displayed": [ + -8.58814244804e-13, + 4.30209160677e-12, + 1.86525980006e-12, + 0.684990514444, + 0.02256104076, + 5.7560563135e-22, + -0.265855190469 + ], + "register": [ + 0, + 0, + 0, + 5445556.58153, + 3295181.01323, + 278478.606315, + -0.517709104819, + 0.855556709276, + -3.87938191293e-9, + 0.245778001551, + 0.148723645672, + 0.957848866561, + -0.819494024835, + -0.495887078306, + 0.287272603686 + ], + "memory": 0.33510235913 + }, + { + "label": "tilt 1 end", + "arm": "earth", + "hr": 0.0000023999998191, + "displayed": [ + -8.58814244845e-13, + 4.30209160682e-12, + 1.86525980147e-12, + 0.304162643125, + -0.931255732717, + 2.17004580513e-21, + -0.455717746089 + ], + "register": [ + 0, + 0, + 0, + 5445555.90789, + 3295180.6056, + 278462.674384, + -0.517709104819, + 0.855556709276, + -3.87938481339e-9, + 0.823616128798, + 0.498381422508, + 0.27068880672, + -0.231589626648, + -0.140138056616, + 0.962666904966 + ], + "memory": 1.34040951538 + }, + { + "label": "pan move 0", + "arm": "earth", + "hr": 0.00000239999969298, + "displayed": [ + -8.5881424494e-13, + 4.30209160674e-12, + 1.86525980147e-12, + 0.304162393914, + -0.931255659819, + 2.17004580504e-21, + -0.455717356973 + ], + "register": [ + 0, + 0, + 0, + 5445557.92428, + 3295177.27335, + 278462.674378, + -0.517708581286, + 0.855557026073, + -3.8793856183e-9, + 0.823616433768, + 0.49838091852, + 0.270688806719, + -0.231589712401, + -0.140137914902, + 0.962666904966 + ], + "memory": 1.34040951538 + }, + { + "label": "pan end", + "arm": "earth", + "hr": 0.00000240000053964, + "displayed": [ + -8.588142453e-13, + 4.30209160641e-12, + 1.86525980122e-12, + 0.304160550871, + -0.931256443589, + 2.17004580513e-21, + -0.455716549823 + ], + "register": [ + 0, + 0, + 0, + 5445566.032, + 3295164.51355, + 278455.113546, + -0.517706549676, + 0.855558255423, + -3.87938579871e-9, + 0.823617892326, + 0.498379129223, + 0.270687663177, + -0.231589066805, + -0.140136772948, + 0.962667226514 + ], + "memory": 1.34040951538 + }, + { + "label": "tilt 2 move 0", + "arm": "earth", + "hr": 0.00000240000208973, + "displayed": [ + -8.58814245335e-13, + 4.30209160644e-12, + 1.86525980243e-12, + 0.23536204091, + -1.00544579425, + 3.39220446157e-21, + -0.512449439596 + ], + "register": [ + 0, + 0, + 0, + 5445566.07774, + 3295164.54122, + 278453.891718, + -0.517706549676, + 0.855558255423, + -3.87938578483e-9, + 0.840108222102, + 0.508357586402, + 0.189184406081, + -0.161858282392, + -0.0979420028656, + 0.981941576926 + ], + "memory": 1.42418512751 + }, + { + "label": "tilt 2 end", + "arm": "earth", + "hr": 0.0000023999991996, + "displayed": [ + -8.58814244608e-13, + 4.30209160562e-12, + 1.86525979876e-12, + -2.18873636672, + -0.782395233589, + 4.95528116513e-22, + -2.76219997364 + ], + "register": [ + 0, + 0, + 0, + 5445566.74476, + 3295164.94484, + 278436.070134, + -0.517706549676, + 0.855558255423, + -3.87938620117e-9, + 0.413544091929, + 0.250239513462, + -0.875420738807, + 0.748973439084, + 0.453211051807, + 0.483361696938 + ], + "memory": 2.68081938898 + }, + { + "label": "look move 0", + "arm": "earth", + "hr": 0.00000239999928509, + "displayed": [ + -8.58814244617e-13, + 4.30209160561e-12, + 1.86525979876e-12, + -2.21974811218, + -0.790268249161, + 4.95528116513e-22, + -2.83105142834 + ], + "register": [ + 0, + 0, + 0, + 5445566.74476, + 3295164.94484, + 278436.070134, + -0.518953941777, + 0.853201609233, + 0.0522859476041, + 0.38922925736, + 0.290318414674, + -0.874194373876, + 0.761043619991, + 0.433315395645, + 0.482752914405 + ], + "memory": 2.68081938898 + }, + { + "label": "look end", + "arm": "earth", + "hr": 0.00000240000213503, + "displayed": [ + -8.58814244616e-13, + 4.3020916056e-12, + 1.86525979873e-12, + -2.26868065666, + -0.74568333147, + 4.95528116513e-22, + -2.91256664702 + ], + "register": [ + 0, + 0, + 0, + 5445566.74476, + 3295164.94484, + 278436.070134, + -0.518778917195, + 0.848506396737, + 0.10442858694, + 0.323926898868, + 0.308138328882, + -0.894495463635, + 0.791163573014, + 0.43021815974, + 0.434709714368 + ], + "memory": 2.73317926654 + }, + { + "label": "recede brisk 0", + "arm": "earth", + "hr": 0.00000323966126414, + "displayed": [ + -8.58814244392e-13, + 4.30209160535e-12, + 1.86525979882e-12, + -2.23721013181, + -0.739329340615, + 6.68892992494e-22, + -2.83572280702 + ], + "register": [ + 0, + 0, + 0, + 5445571.31718, + 3295167.71166, + 278436.303925, + -0.518852758383, + 0.853724227906, + 0.0441220784167, + 0.352762011006, + 0.260834442443, + -0.898623590402, + 0.778685288617, + 0.450688735516, + 0.436496145424 + ], + "memory": 2.73317926654 + }, + { + "label": "recede brisk 35", + "arm": "earth", + "hr": 0.11764992274, + "displayed": [ + -8.58790612634e-13, + 4.30206304827e-12, + 1.86528148545e-12, + -1.73704603665, + -1.24764085932, + 2.42911852576e-17, + -2.40941359841 + ], + "register": [ + 0, + 0, + 0, + 6086222.64462, + 3682832.00722, + 311193.305409, + -0.51898218053, + 0.852996952088, + 0.0552602571646, + 0.711374857916, + 0.466850497001, + -0.525353618979, + 0.473923314279, + 0.233338409138, + 0.849087674509 + ], + "memory": 2.73317926654 + }, + { + "label": "recede 0", + "arm": "earth", + "hr": 0.130023273126, + "displayed": [ + -8.58790902658e-13, + 4.30206246655e-12, + 1.86528562957e-12, + -1.30186041478, + -1.35142240492, + 2.68459115123e-17, + -1.99679807879 + ], + "register": [ + 0, + 0, + 0, + 6153602.38829, + 3723604.13981, + 314638.484196, + -0.519054562022, + 0.851821354531, + 0.0705885373798, + 0.77198370136, + 0.502648226471, + -0.389083442541, + 0.366910788174, + 0.147462335498, + 0.918494057211 + ], + "memory": 2.73317926654 + }, + { + "label": "recede 3", + "arm": "earth", + "hr": 0.175513060418, + "displayed": [ + -8.58796269929e-13, + 4.3020644555e-12, + 1.86529713172e-12, + 0.394311083513, + -1.13068223763, + 3.62381901023e-17, + -0.359788132177 + ], + "register": [ + 0, + 0, + 0, + 6401319.46668, + 3873500.13249, + 327304.451403, + -0.518387722124, + 0.846342418415, + 0.122387419055, + 0.854624935456, + 0.507744775192, + 0.108680554666, + -0.0298393908959, + -0.160934005283, + 0.986513992143 + ], + "memory": 2.73317926654 + }, + { + "label": "recede 19", + "arm": "absolute", + "hr": 0.904220833938, + "displayed": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.772030404994, + 0.265666289276, + 3.93164567996e-16, + -5.55111512313e-17 + ], + "register": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.772030404994, + 0.265666289276, + 3.93164567996e-16, + -5.55111512313e-17 + ], + "memory": 2.73317926654 + }, + { + "label": "recede world 0", + "arm": "absolute", + "hr": 0.999318569187, + "displayed": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.772030404994, + 0.265666289276, + 4.12799402008e-16, + 1.38777878078e-17 + ], + "register": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.772030404994, + 0.265666289276, + 4.12799402008e-16, + 1.38777878078e-17 + ], + "memory": 2.73317926654 + }, + { + "label": "recede world 7", + "arm": "absolute", + "hr": 2.01238047432, + "displayed": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.772030404994, + 0.265666289276, + 6.21966342726e-16, + -5.55738316694e-18 + ], + "register": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.772030404994, + 0.265666289276, + 6.21966342726e-16, + -5.55738316694e-18 + ], + "memory": 2.73317926654 + }, + { + "label": "re-dive 0", + "arm": "absolute", + "hr": 1.82087715249, + "displayed": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.772030404994, + 0.265666289276, + 5.82426642574e-16, + -5.96791886065e-17 + ], + "register": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.772030404994, + 0.265666289276, + 5.82426642574e-16, + -5.96791886065e-17 + ], + "memory": 2.73317926654 + }, + { + "label": "re-dive 14", + "arm": "earth", + "hr": 0.449022777516, + "displayed": [ + -8.58813615026e-13, + 4.30209194307e-12, + 1.86525445823e-12, + 0.770526869984, + 0.146645730242, + 9.36724036408e-17, + -0.0125259023887 + ], + "register": [ + 0, + 0, + 0, + 7807994.71433, + 4915356.7519, + -314986.806499, + -0.506032322158, + 0.820749515208, + 0.265144342232, + 0.268413886155, + -0.142292603399, + 0.952736480217, + -0.819686082992, + -0.553283876779, + 0.148295910413 + ], + "memory": 2.73317926654 + }, + { + "label": "re-dive engaged 0", + "arm": "earth", + "hr": 0.404990357704, + "displayed": [ + -8.58814134638e-13, + 4.30209146175e-12, + 1.86525332033e-12, + 0.766060308078, + 0.0569265954535, + 8.56332551382e-17, + -0.0146819552961 + ], + "register": [ + 0, + 0, + 0, + 7548603.9725, + 4780283.67862, + -539752.097825, + -0.504222807269, + 0.822850390028, + 0.26206219922, + 0.339575685799, + -0.0900952685041, + 0.936253809716, + -0.794007376694, + -0.561070475273, + 0.233991896297 + ], + "memory": 2.73317926654 + }, + { + "label": "re-dive engaged 3", + "arm": "earth", + "hr": 0.268610130415, + "displayed": [ + -8.58815551831e-13, + 4.3020902092e-12, + 1.86525170703e-12, + 0.730401394984, + -0.381240094609, + 6.74071828526e-17, + -0.0418799467016 + ], + "register": [ + 0, + 0, + 0, + 6703618.31588, + 4337564.0694, + -1253338.52376, + -0.498422031517, + 0.834725566235, + 0.234112595928, + 0.637177733564, + 0.169602983375, + 0.751824024543, + -0.587860539881, + -0.523896990928, + 0.616402407967 + ], + "memory": 2.73317926654 + } + ], + "northUpOff": [ + { + "label": "start", + "arm": "absolute", + "hr": 5, + "displayed": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.7, + 0.3, + 1.23882029118e-15, + 0 + ], + "register": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.7, + 0.3, + 1.23882029118e-15, + 0 + ], + "memory": 0 + }, + { + "label": "dive 0", + "arm": "absolute", + "hr": 4.69959329559, + "displayed": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.7, + 0.3, + 1.17679530435e-15, + 0 + ], + "register": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.7, + 0.3, + 1.17679530435e-15, + 0 + ], + "memory": 0 + }, + { + "label": "dive 20", + "arm": "earth", + "hr": 0.408531953689, + "displayed": [ + -8.58823383762e-13, + 4.30208382526e-12, + 1.86524674597e-12, + 0.7, + 0.299999999999, + 8.43496123044e-17, + 0 + ], + "register": [ + 0, + 0, + 0, + 7933158.87757, + 4190780.53426, + -175113.714869, + -0.447067922013, + 0.85700852238, + 0.256255079316, + 0.136395796513, + -0.217815627948, + 0.966412199279, + -0.88403985193, + -0.467004009393, + 0.01951397986 + ], + "memory": 0 + }, + { + "label": "dive 39", + "arm": "earth", + "hr": 0.0615621239606, + "displayed": [ + -8.58815721031e-13, + 4.30209022991e-12, + 1.86525805271e-12, + 0.763746321348, + 0.339489149201, + 1.27107388113e-17, + -0.0200470287363 + ], + "register": [ + 0, + 0, + 0, + 5816774.57098, + 3442304.40762, + 238147.977164, + -0.499526477634, + 0.82767106602, + 0.25580051723, + 0.101423426099, + -0.237373932188, + 0.966109157888, + -0.860340971232, + -0.508541269506, + -0.0346293290295 + ], + "memory": 0 + }, + { + "label": "dive brisk 0", + "arm": "earth", + "hr": 0.0456251399424, + "displayed": [ + -8.58815389035e-13, + 4.30209054158e-12, + 1.86525856693e-12, + 0.767005933745, + 0.341447327804, + 9.42022602832e-18, + -0.0211355075587 + ], + "register": [ + 0, + 0, + 0, + 5721375.78023, + 3403037.18244, + 251292.223917, + -0.502132641784, + 0.826126332225, + 0.255691402398, + 0.0996859833328, + -0.238403754719, + 0.966036414667, + -0.859025910425, + -0.510567265834, + -0.0373570913832 + ], + "memory": 0 + }, + { + "label": "dive brisk 35", + "arm": "earth", + "hr": 0.0000023999987333, + "displayed": [ + -8.58814448428e-13, + 4.30209144188e-12, + 1.86526003613e-12, + 0.775991805555, + 0.346814022309, + 4.9552812602e-22, + -0.0241671760466 + ], + "register": [ + 0, + 0, + 0, + 5449456.70472, + 3288098.79604, + 285768.720091, + -0.50927763239, + 0.821847491179, + 0.255348770098, + 0.0949226562623, + -0.241256315416, + 0.965807993133, + -0.855351379537, + -0.516102791617, + -0.0448544982087 + ], + "memory": 0 + }, + { + "label": "tilt 1 move 0", + "arm": "earth", + "hr": 0.0000024849657001, + "displayed": [ + -8.58814448428e-13, + 4.30209144188e-12, + 1.86526003613e-12, + 0.773897707698, + 0.263062016837, + 5.14876964263e-22, + -0.023537904874 + ], + "register": [ + 0, + 0, + 0, + 5449457.04302, + 3288099.39241, + 285767.475381, + -0.509277636295, + 0.821847488822, + 0.255348769893, + 0.166163707683, + -0.197223826706, + 0.966174096335, + -0.844408616381, + -0.534480558389, + 0.0361195416064 + ], + "memory": 0.0837756009065 + }, + { + "label": "tilt 1 move 3", + "arm": "earth", + "hr": 0.00000263277086576, + "displayed": [ + -8.58814448431e-13, + 4.30209144188e-12, + 1.86526003614e-12, + 0.768044168272, + 0.0118011273059, + 5.75605630761e-22, + -0.0227296011328 + ], + "register": [ + 0, + 0, + 0, + 5449457.45477, + 3288100.87905, + 285763.51178, + -0.509277636295, + 0.821847488822, + 0.255348769893, + 0.370939252781, + -0.0581077677636, + 0.926837395702, + -0.776556733224, + -0.566736440016, + 0.275262506787 + ], + "memory": 0.335102362174 + }, + { + "label": "tilt 1 end", + "arm": "earth", + "hr": 0.0000023999997163, + "displayed": [ + -8.58814448742e-13, + 4.30209144169e-12, + 1.8652600375e-12, + 0.732896834484, + -0.993109049844, + 2.17004583329e-21, + -0.0416281402392 + ], + "register": [ + 0, + 0, + 0, + 5449454.67867, + 3288103.94536, + 285748.10602, + -0.509277636295, + 0.821847488822, + 0.255348769893, + 0.854427726547, + 0.447375703741, + 0.264212489882, + -0.102905535695, + -0.352734581263, + 0.93004772238 + ], + "memory": 1.34040951843 + }, + { + "label": "pan move 0", + "arm": "earth", + "hr": 0.00000239999935414, + "displayed": [ + -8.58814448833e-13, + 4.3020914416e-12, + 1.86526003747e-12, + 0.7328965318, + -0.99310904296, + 2.17004583338e-21, + -0.0416278046814 + ], + "register": [ + 0, + 0, + 0, + 5449456.66222, + 3288100.7444, + 285747.111475, + -0.509277115907, + 0.821847810737, + 0.255348771684, + 0.854428026575, + 0.447375219571, + 0.264212339449, + -0.102905619947, + -0.3527344453, + 0.930047764624 + ], + "memory": 1.34040951843 + }, + { + "label": "pan end", + "arm": "earth", + "hr": 0.00000240000002849, + "displayed": [ + -8.58814449131e-13, + 4.30209144132e-12, + 1.86526003711e-12, + 0.732895290976, + -0.993110203657, + 2.17004583347e-21, + -0.0416265549024 + ], + "register": [ + 0, + 0, + 0, + 5449463.64111, + 3288090.14867, + 285735.942789, + -0.509275095207, + 0.821849056657, + 0.255348791801, + 0.854429314677, + 0.447373757161, + 0.264210650097, + -0.102904925166, + -0.352733397165, + 0.930048239018 + ], + "memory": 1.34040951843 + }, + { + "label": "tilt 2 move 0", + "arm": "earth", + "hr": 0.00000240000012441, + "displayed": [ + -8.58814449397e-13, + 4.30209144116e-12, + 1.86526003827e-12, + 0.725551042947, + -1.07680251628, + 3.39220453137e-21, + -0.0479469740881 + ], + "register": [ + 0, + 0, + 0, + 5449463.52501, + 3288090.44372, + 285734.761608, + -0.509275095207, + 0.821849056657, + 0.255348791801, + 0.860043573015, + 0.475320724373, + 0.185459595323, + -0.0310472207774, + -0.314061040331, + 0.948895006325 + ], + "memory": 1.42418513056 + }, + { + "label": "tilt 2 end", + "arm": "earth", + "hr": 0.00000239999946028, + "displayed": [ + -8.58814447958e-13, + 4.30209144096e-12, + 1.86526003473e-12, + -2.35006327273, + -0.807403211062, + 4.95528116483e-22, + -3.10871731543 + ], + "register": [ + 0, + 0, + 0, + 5449461.83163, + 3288094.74734, + 285717.532912, + -0.509275095207, + 0.821849056657, + 0.255348791801, + 0.295295741598, + 0.445571980531, + -0.845142612321, + 0.808355925565, + 0.355006673512, + 0.469607239473 + ], + "memory": 2.68081939203 + }, + { + "label": "look move 0", + "arm": "earth", + "hr": 0.00000240000239859, + "displayed": [ + -8.58814447966e-13, + 4.30209144095e-12, + 1.86526003472e-12, + -2.38373364458, + -0.807339445766, + 4.95528116483e-22, + 3.10323965641 + ], + "register": [ + 0, + 0, + 0, + 5449461.83163, + 3288094.74734, + 285717.532912, + -0.503608949544, + 0.808096569413, + 0.305545349241, + 0.271538582696, + 0.483802296668, + -0.831986860381, + 0.820149269369, + 0.336028677709, + 0.463076563551 + ], + "memory": 2.68081939203 + }, + { + "label": "look end", + "arm": "earth", + "hr": 0.00000239999978224, + "displayed": [ + -8.5881444796e-13, + 4.30209144094e-12, + 1.8652600347e-12, + -2.40944022372, + -0.753557832895, + 4.95528116483e-22, + 3.03763473516 + ], + "register": [ + 0, + 0, + 0, + 5449461.83163, + 3288094.74734, + 285717.532912, + -0.496562446757, + 0.792129145641, + 0.354904428116, + 0.20420684848, + 0.504009800245, + -0.839210155021, + 0.843638133032, + 0.344246333143, + 0.412030536019 + ], + "memory": 2.73317926959 + }, + { + "label": "recede brisk 0", + "arm": "earth", + "hr": 0.0000032396613292, + "displayed": [ + -8.58814447761e-13, + 4.30209144068e-12, + 1.86526003478e-12, + -2.40944022372, + -0.753557832895, + 6.68892992433e-22, + 3.03763473516 + ], + "register": [ + 0, + 0, + 0, + 5449466.40732, + 3288097.50822, + 285717.772817, + -0.496562446757, + 0.792129145641, + 0.354904428116, + 0.20420684848, + 0.504009800245, + -0.839210155021, + 0.843638133032, + 0.344246333143, + 0.412030536019 + ], + "memory": 2.73317926959 + }, + { + "label": "recede brisk 35", + "arm": "earth", + "hr": 0.117649922735, + "displayed": [ + -8.58795358288e-13, + 4.30205885827e-12, + 1.8652804861e-12, + -2.62627932389, + -1.30978913277, + 2.42911852565e-17, + 2.84396200263 + ], + "register": [ + 0, + 0, + 0, + 6090575.97768, + 3674930.02048, + 319331.412187, + -0.496562446757, + 0.792129145641, + 0.354904428116, + 0.623880006388, + 0.609985822238, + -0.48856016446, + 0.603489415069, + 0.0211828537717, + 0.797089588821 + ], + "memory": 2.73317926959 + }, + { + "label": "recede 0", + "arm": "earth", + "hr": 0.130023273121, + "displayed": [ + -8.58796303391e-13, + 4.30205769492e-12, + 1.86528438925e-12, + -3.00081952413, + -1.44805394725, + 2.6845911511e-17, + 2.47527894691 + ], + "register": [ + 0, + 0, + 0, + 6158003.91651, + 3715614.67124, + 322866.686849, + -0.496562446757, + 0.792129145641, + 0.354904428116, + 0.708736754964, + 0.606063947721, + -0.361079912812, + 0.501116701612, + -0.0722350877246, + 0.862359636965 + ], + "memory": 2.73317926959 + }, + { + "label": "recede 3", + "arm": "earth", + "hr": 0.175513060412, + "displayed": [ + -8.58802801726e-13, + 4.30205868017e-12, + 1.86529532019e-12, + 0.974192518494, + -1.15078860015, + 3.62381901006e-17, + 0.186667779321 + ], + "register": [ + 0, + 0, + 0, + 6405898.18113, + 3865189.04291, + 335863.885453, + -0.496562446757, + 0.792129145641, + 0.354904428116, + 0.863197654257, + 0.493605561447, + 0.10603470844, + 0.0911896164954, + -0.359005524099, + 0.928870005711 + ], + "memory": 2.73317926959 + }, + { + "label": "recede 19", + "arm": "absolute", + "hr": 0.904220834056, + "displayed": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.782643157335, + 0.267207821768, + 3.9316456802e-16, + 0.0785409033636 + ], + "register": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.782643157335, + 0.267207821768, + 3.9316456802e-16, + 0.0785409033636 + ], + "memory": 2.73317926959 + }, + { + "label": "recede world 0", + "arm": "absolute", + "hr": 0.999318569316, + "displayed": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.782643157335, + 0.267207821768, + 4.12799402035e-16, + 0.0785409033636 + ], + "register": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.782643157335, + 0.267207821768, + 4.12799402035e-16, + 0.0785409033636 + ], + "memory": 2.73317926959 + }, + { + "label": "recede world 7", + "arm": "absolute", + "hr": 2.01238047459, + "displayed": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.782643157335, + 0.267207821768, + 6.2196634278e-16, + 0.0785409033636 + ], + "register": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.782643157335, + 0.267207821768, + 6.2196634278e-16, + 0.0785409033636 + ], + "memory": 2.73317926959 + }, + { + "label": "re-dive 0", + "arm": "absolute", + "hr": 1.82087715273, + "displayed": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.782643157335, + 0.267207821768, + 5.82426642623e-16, + 0.0785409033636 + ], + "register": [ + -8.58950454652e-13, + 4.30224651096e-12, + 1.86525077502e-12, + 0.782643157335, + 0.267207821768, + 5.82426642623e-16, + 0.0785409033636 + ], + "memory": 2.73317926959 + }, + { + "label": "re-dive 14", + "arm": "earth", + "hr": 0.449022777574, + "displayed": [ + -8.58812915346e-13, + 4.30209258004e-12, + 1.86525502852e-12, + 0.79206408103, + 0.148535164831, + 9.36724038973e-17, + 0.0785409033636 + ], + "register": [ + 0, + 0, + 0, + 7762493.42556, + 4989270.84689, + -274958.882602, + -0.495950457609, + 0.791704119425, + 0.356703982153, + 0.314692779118, + -0.218989581328, + 0.923586497325, + -0.809321690287, + -0.570305313456, + 0.14053558651 + ], + "memory": 2.73317926959 + }, + { + "label": "re-dive engaged 0", + "arm": "earth", + "hr": 0.404742850815, + "displayed": [ + -8.58813375989e-13, + 4.30209214146e-12, + 1.86525383748e-12, + 0.801544274338, + 0.0594167972848, + 8.55816295471e-17, + 0.0775559811744 + ], + "register": [ + 0, + 0, + 0, + 7485126.427, + 4881333.67662, + -491022.434865, + -0.49789858041, + 0.790279465874, + 0.357148945741, + 0.385779940944, + -0.167003855664, + 0.907349739273, + -0.776705118299, + -0.589549046315, + 0.221722983015 + ], + "memory": 2.73317926959 + }, + { + "label": "re-dive engaged 3", + "arm": "earth", + "hr": 0.266757742279, + "displayed": [ + -8.58814922355e-13, + 4.30209075677e-12, + 1.8652520037e-12, + 0.863342306569, + -0.376693585659, + 6.70660385912e-17, + 0.0879707620004 + ], + "register": [ + 0, + 0, + 0, + 6556757.59107, + 4560832.38104, + -1157984.13863, + -0.514404629972, + 0.774691977732, + 0.367750209111, + 0.67707458349, + 0.103731078593, + 0.72856699879, + -0.526267883359, + -0.623772557084, + 0.577883995256 + ], + "memory": 2.73317926959 + } + ] +} diff --git a/tests/services/engine/camera/cameraFraming.test.ts b/tests/services/engine/camera/cameraFraming.test.ts index 224d39667..243b7c5d8 100644 --- a/tests/services/engine/camera/cameraFraming.test.ts +++ b/tests/services/engine/camera/cameraFraming.test.ts @@ -73,7 +73,9 @@ describe('GALACTIC_DISC_FORWARD', () => { GALACTIC_DISC_FORWARD, ORIENTATION_FRAMES.ecliptic, ); - expect(yaw).toBeCloseTo(-1.4208, 6); - expect(pitch).toBeCloseTo(-0.1783, 6); + // 4 places, not 6: the exact J2000 obliquity re-tilts the ecliptic frame by + // ~2e-6 rad, still well inside a mistyped fourth decimal. + expect(yaw).toBeCloseTo(-1.4208, 4); + expect(pitch).toBeCloseTo(-0.1783, 4); }); }); diff --git a/tests/services/engine/frame/settleGoldenTrace.test.ts b/tests/services/engine/frame/settleGoldenTrace.test.ts index 9cd61162c..ce8c85e4b 100644 --- a/tests/services/engine/frame/settleGoldenTrace.test.ts +++ b/tests/services/engine/frame/settleGoldenTrace.test.ts @@ -231,7 +231,7 @@ describe('settle golden trace (byte bar for the orientation settles)', () => { const trace = thin(runScript(northUp)); if (process.env['SETTLE_GOLDEN_RECORD']) { recorded[key] = trace; - writeFileSync(FIXTURE_PATH, `${JSON.stringify(recorded)}\n`); + writeFileSync(FIXTURE_PATH, `${JSON.stringify(recorded, null, 2)}\n`); return; } expectTraceMatches(trace, (GOLDEN as Record)[key]!); diff --git a/tests/utils/orbit/propagateElements.test.ts b/tests/utils/orbit/propagateElements.test.ts index f769e06e5..b4cbade8e 100644 --- a/tests/utils/orbit/propagateElements.test.ts +++ b/tests/utils/orbit/propagateElements.test.ts @@ -90,9 +90,9 @@ describe('propagateElements', () => { // The scene frame is equatorial J2000 (keplerianEllipse rotates the ecliptic // elements through ECLIPTIC_FRAME), matching Horizons' ICRF frame to sub- // arcsecond bias. Tolerance is arcminute-class: the mean-element model differs - // from the true DE ephemeris by ~1 arcmin (~2e-4 au at 1 au), and the scene's - // scene's J2000 obliquity is exact, so it adds none. A frame swap, unit slip, or sign - // error in a rate would miss by ≥0.05 au and fail loudly. + // from the true DE ephemeris by ~1 arcmin (~2e-4 au at 1 au); the scene's J2000 + // obliquity is exact, so it adds none. A frame swap, unit slip, or sign error in a + // rate would miss by ≥0.05 au and fail loudly. const simDays = 2_460_676.5; const horizonsAu: Vec3 = [-1.786710910310161e-1, 8.871846912692936e-1, 3.845832338744293e-1]; const expectedMpc = horizonsAu.map((au) => au * SCALE_UNITS.AU_TO_MPC) as Vec3; From b707c020b8b3ace114c8915cba4b9ac255986ef7 Mon Sep 17 00:00:00 2001 From: Alexander Rulkens Date: Sun, 4 Oct 2026 10:09:49 +0200 Subject: [PATCH 09/14] =?UTF-8?q?Accurate=20planet=20positions:=20correcti?= =?UTF-8?q?ons=20+=20Earth=E2=80=93Moon=20pair?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/BACKLOG.md | 2 +- .../2026-08-16-barycentric-orbit-pairs.md | 10 +- src/@types/scene/BarycentricPair.d.ts | 12 + src/data/bodies/barycentricPairs.ts | 25 + src/data/bodies/scenePlanets.ts | 6 +- src/services/engine/frame/deriveBodyStates.ts | 22 +- .../animation/clips/earthUniverseLoop.test.ts | 5 +- tests/data/bodies/planetEphemeris.test.ts | 35 ++ tests/fixtures/bodyStatesJ2000.json | 62 +- tests/fixtures/camera/driverGoldenTrace.json | 564 +++++++++--------- tests/fixtures/camera/settleGoldenTrace.json | 384 ++++++------ tests/fixtures/horizonsPlanets.json | 258 ++++++++ .../engine/frame/deriveBodyStates.test.ts | 53 ++ .../frame/passes/orbitTrailsPass.test.ts | 76 ++- tools/bodies/@types/HorizonsSeries.d.ts | 3 + tools/bodies/buildPlanetEphemeris.ts | 3 +- tools/perf/perfScenarios.ts | 2 +- 17 files changed, 991 insertions(+), 531 deletions(-) create mode 100644 src/@types/scene/BarycentricPair.d.ts create mode 100644 src/data/bodies/barycentricPairs.ts create mode 100644 tests/data/bodies/planetEphemeris.test.ts create mode 100644 tests/fixtures/horizonsPlanets.json create mode 100644 tools/bodies/@types/HorizonsSeries.d.ts diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index c76270a44..46fde993b 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -182,7 +182,7 @@ Items with a **→ details** link have a full write-up in [`backlog/`](backlog/) - [ ] **Galactic Center place labels** `needs-design` — four POI markers (central cluster, Arches, Quintuplet, CMZ) around the shipped Sgr A\*; category fit is the open question. → [details](backlog/2026-07-30-galactic-center-place-labels.md) - [ ] **Filaments + flow field lack scale fade bands** `ready` — both layers gate on user intent alone, with no zoom-based fade like the survey point clouds. → [details](backlog/2026-07-24-filaments-flow-scale-bands.md) - [ ] **Flow field does not reseed on a particle-count change** `needs-verification` — confirmed on the deployed build; the latch arms and the seed pass encodes, so the suspect is a stale trail ring or index-hashed seed positions. → [details](backlog/2026-09-15-flow-field-no-reseed-on-count-change.md) -- [ ] **Barycentric orbit pairs** `needs-design` — one-hop `focusId` pins Pluto (and Earth) instead of wobbling around the pair barycentre; needs an invisible focus-graph node. → [details](backlog/2026-08-16-barycentric-orbit-pairs.md) +- [ ] **Barycentric orbit pairs** `needs-design` — Pluto's wobble is one `BARYCENTRIC_PAIRS` row (Earth–Moon already uses it); the minor moons still need an invisible focus-graph node. → [details](backlog/2026-08-16-barycentric-orbit-pairs.md) - [ ] **Pluto's minor moons (Styx/Nix/Kerberos/Hydra)** `blocked` — they orbit the barycentre, so honest placement waits on barycentric pairs. → [details](backlog/2026-08-16-barycentric-orbit-pairs.md) - [ ] **ZoA label em-height not radius-invariant** `needs-design` — `LABEL_EM_MPC` is baked at construction while `LABEL_RADIUS_MPC` is per-frame; a tunable label radius would change apparent glyph size with nothing re-deriving to match. → [details](backlog/2026-08-17-zone-of-avoidance-shape-constants.md) - [ ] **Consolidate the renderer hygiene-basket duplications** `ready` — grow-on-demand instance buffer (×7+), 16-byte fade-scratch (×4), fullscreen-triangle (×5 + a second `.wesl` copy), sub-pixel cull (×3). → [details](backlog/2026-08-20-renderer-hygiene-basket.md) diff --git a/docs/backlog/2026-08-16-barycentric-orbit-pairs.md b/docs/backlog/2026-08-16-barycentric-orbit-pairs.md index 708b61591..ecad1dd35 100644 --- a/docs/backlog/2026-08-16-barycentric-orbit-pairs.md +++ b/docs/backlog/2026-08-16-barycentric-orbit-pairs.md @@ -6,14 +6,18 @@ From the [add-Pluto/Charon grill session](../grill-sessions/add-pluto-charon-202 The focus graph is strictly one-hop: `OrbitalElements.focusId` makes a body orbit its parent's *center* (`src/services/engine/frame/deriveBodyStates.ts` resolves positions along `focusResolveOrder`). Real binary-ish systems orbit a shared barycentre: -- **Pluto–Charon**: Charon is ~12% of Pluto's mass; the barycentre sits ~2,130 km from Pluto's centre — 1.8 Pluto radii, i.e. ~940 km *above* the surface. The shipped model (Charon as a Moon-style satellite of Pluto) pins Pluto at its heliocentric position — an error of ~10% of the pair separation, visible only when parked near the barycentre with time running. The same approximation already applies to Earth–Moon (Earth doesn't wobble around the EMB), just less visibly. +- **Pluto–Charon**: Charon is ~12% of Pluto's mass; the barycentre sits ~2,130 km from Pluto's centre — 1.8 Pluto radii, i.e. ~940 km *above* the surface. The shipped model (Charon as a Moon-style satellite of Pluto) pins Pluto at its heliocentric position — an error of ~10% of the pair separation, visible only when parked near the barycentre with time running. Earth–Moon no longer has this error: see the pair table below. - **Pluto's minor moons** — Styx, Nix, Kerberos, Hydra — orbit the *barycentre*, not Pluto, at wider radii where the approximation grows proportionally worse. They are deliberately absent until this lands (grill Q5): four sub-pixel irregular rocks drawn dishonestly around Pluto's center add clutter without honesty. -## Shape of a fix +## What exists now + +`BARYCENTRIC_PAIRS` (`src/data/bodies/barycentricPairs.ts`) treats a primary's element row as the pair barycentre, and `deriveBodyStates` subtracts the secondary's mass-weighted reflex from the primary. Earth–Moon is its one row. Pluto's wobble is therefore one more row (`pluto`/`charon`, Charon's mass fraction ≈ 0.1086), once Pluto's row is re-fitted to the Pluto-system barycentre. The minor moons still need the invisible node below, because they orbit the barycentre rather than either body. + +## Shape of a fix (minor moons) An invisible barycentre node in the focus graph that both partners orbit. Touchpoints that currently assume focus-graph nodes are visible bodies: pick tables (`resolvePickTable.ts` maps source → seed table by index), region extents (`bodyRegions.ts` maxes over members), labels/captions (every `SCENE_PLANETS` row gets one), glint partition. Either a `visible: false` body kind or a separate node table that only `deriveBodyStates` sees. ## Follow-on once landed -- Let Pluto (and optionally Earth) wobble: re-parent Pluto → `pluto-barycentre` with a ~2,130 km semi-major element row; delete the "looks wrong, don't fix" comment on Charon's row. +- Let Pluto wobble: add the `pluto`/`charon` row to `BARYCENTRIC_PAIRS`; delete the "looks wrong, don't fix" comment on Charon's row. - Add Styx/Nix/Kerberos/Hydra as ordinary satellite rows parented on the barycentre node (no textures — no usable maps exist; flat albedo + glint only). diff --git a/src/@types/scene/BarycentricPair.d.ts b/src/@types/scene/BarycentricPair.d.ts new file mode 100644 index 000000000..be675fbfe --- /dev/null +++ b/src/@types/scene/BarycentricPair.d.ts @@ -0,0 +1,12 @@ +/** + * BarycentricPair — a primary whose element row describes the pair's barycentre, + * not the primary itself: `deriveBodyStates` subtracts the secondary's reflex, + * `secondaryMassFraction` × the secondary's primary-relative offset. + */ + +export type BarycentricPair = { + readonly primaryId: string; + readonly secondaryId: string; + /** The secondary's share of the pair mass, m₂ / (m₁ + m₂). */ + readonly secondaryMassFraction: number; +}; diff --git a/src/data/bodies/barycentricPairs.ts b/src/data/bodies/barycentricPairs.ts new file mode 100644 index 000000000..552342af5 --- /dev/null +++ b/src/data/bodies/barycentricPairs.ts @@ -0,0 +1,25 @@ +/** + * BARYCENTRIC_PAIRS — primaries whose element row is the pair barycentre. Earth's + * row is fitted to the Horizons Earth–Moon barycentre (target 3), so Earth itself + * sits k·(Moon offset) the other way. k = 1 / (1 + M⊕/M☾), M⊕/M☾ = 81.30057 (DE440). + */ + +import type { BarycentricPair } from '../../@types/scene/BarycentricPair'; +import { ORBITAL_ELEMENTS } from './orbitalElements'; +import { findByIdOrThrow } from '../../utils/object/findByIdOrThrow'; + +export const BARYCENTRIC_PAIRS: readonly BarycentricPair[] = [ + { primaryId: 'earth', secondaryId: 'moon', secondaryMassFraction: 1 / 82.30057 }, +]; + +// Primary id → its secondary's element row and mass fraction, resolved at import so +// `deriveBodyStates` does no lookup per instant. A misspelt secondary throws here. +export const BARYCENTRIC_REFLEX_BY_PRIMARY = new Map( + BARYCENTRIC_PAIRS.map((pair) => [ + pair.primaryId, + { + secondary: findByIdOrThrow(ORBITAL_ELEMENTS, pair.secondaryId, 'BARYCENTRIC_PAIRS'), + k: pair.secondaryMassFraction, + }, + ]), +); diff --git a/src/data/bodies/scenePlanets.ts b/src/data/bodies/scenePlanets.ts index ca1174715..e0d1e37df 100644 --- a/src/data/bodies/scenePlanets.ts +++ b/src/data/bodies/scenePlanets.ts @@ -1,7 +1,7 @@ /** - * scenePlanets — planet + moon seeds at their real J2000 mean positions, DERIVED - * from `ORBITAL_ELEMENTS` via `keplerianPositionMpc`, so a body sits exactly on the - * ellipse its trail draws. `datumRadiusM` is authored in SI metres and resolved + * scenePlanets — planet + moon identity rows; positions are not baked here but + * derived per instant by `deriveBodyStates`, and each trail anchors on that snapshot + * position. `datumRadiusM` is authored in SI metres and resolved * into a draw-space sphere at render time. Albedos are plausible * flat linear-RGB colours, inline rather than in `palette.ts` because each is * per-body data read once at its seed site. diff --git a/src/services/engine/frame/deriveBodyStates.ts b/src/services/engine/frame/deriveBodyStates.ts index 639fc8815..b0525fa7a 100644 --- a/src/services/engine/frame/deriveBodyStates.ts +++ b/src/services/engine/frame/deriveBodyStates.ts @@ -2,6 +2,7 @@ * deriveBodyStates — derives every scene body's time-varying `BodyState` from * the three authored position tables — anchors, Keplerian elements, surface * sites (`positionDrivers.ts` reads the same three as a union) — keyed by id. + * A planet is Kepler plus its fitted Horizons correction, less any pair reflex. * `meanAnomalyRad` is the PROPAGATED `M` at `t` (not epoch) — the * orbit-trail falloff anchor, so a trail fading behind the body must * track where it actually is. Memoized on `simDays`: every pass (draw, @@ -25,6 +26,9 @@ import { sitePointBodyFixed } from '../../../utils/camera/sitePointBodyFixed'; import { addVec3 } from '../../../utils/math/addVec3'; import { rotateVec3ByTightMat3 } from '../../../utils/math/rotateVec3ByTightMat3'; import { findByIdOrThrow } from '../../../utils/object/findByIdOrThrow'; +import { PLANET_EPHEMERIS_CORRECTIONS } from '../../../data/bodies/planetEphemerisCorrections.generated'; +import { BARYCENTRIC_REFLEX_BY_PRIMARY } from '../../../data/bodies/barycentricPairs'; +import { ephemerisCorrectionMpc } from '../../../utils/orbit/ephemerisCorrectionMpc'; // The focus graph is authored, static data, so its order is resolved once at // module load and replayed every instant: the per-frame cost stays one linear @@ -63,7 +67,23 @@ export function deriveBodyStates(simDays: number): ReadonlyMap { // sin(30°) up the ecliptic pole — the basis's second column, since a // column-major Mat3 stores column c at basis[c*3 + r]. const pole: Vec3 = [basis[3], basis[4], basis[5]]; + // The lift is from the sun line, and the Moon's reflex puts Earth ~1″ off the + // ecliptic, so the sun line's own latitude β is folded in. const eyeAlongPole = dirWorld[0] * pole[0] + dirWorld[1] * pole[1] + dirWorld[2] * pole[2]; - expect(eyeAlongPole).toBeCloseTo(Math.sin((30 * Math.PI) / 180), 6); + const beta = Math.asin(sunward[0] * pole[0] + sunward[1] * pole[1] + sunward[2] * pole[2]); + expect(eyeAlongPole).toBeCloseTo(Math.sin((30 * Math.PI) / 180 - beta), 6); }); it('loops seamlessly: pose(durationSec) equals pose(0) with yaw offset by exactly 2π', () => { diff --git a/tests/data/bodies/planetEphemeris.test.ts b/tests/data/bodies/planetEphemeris.test.ts new file mode 100644 index 000000000..41da7d0eb --- /dev/null +++ b/tests/data/bodies/planetEphemeris.test.ts @@ -0,0 +1,35 @@ +/** + * Planet positions vs JPL Horizons at six dates (both span ends + the Voyager flybys). + * Fixture query (one per target; Earth is 399, the rest as in fetchHorizonsPlanets): + * https://ssd.jpl.nasa.gov/api/horizons.api?format=json&COMMAND='199'&OBJ_DATA='NO'&MAKE_EPHEM='YES'&EPHEM_TYPE='VECTORS'&CENTER='500@10'&REF_PLANE='FRAME'&TIME_TYPE='UT'&OUT_UNITS='KM-S'&CSV_FORMAT='YES'&VEC_TABLE='1'&TLIST_TYPE='JD'&TLIST='2415171.5' '2443937.5' '2444555.5' '2446454.5' '2447763.5' '2487855.5' + * Fails when an element row or the frame changes without `npm run build-planet-ephemeris`. + */ + +import { describe, it, expect } from 'vitest'; +import { deriveBodyStates } from '../../../src/services/engine/frame/deriveBodyStates'; +import { SCALE_UNITS } from '../../../src/data/scaleUnits'; +import HORIZONS from '../../fixtures/horizonsPlanets.json'; + +// Earth is compared with 399 (Earth centre), so its error also carries the app Moon's +// error through the reflex: measured max 621 km, inside the planets' own bound. +const TOLERANCE_KM = 1000; + +function heliocentricKm(id: string, jd: number): [number, number, number] { + const states = deriveBodyStates(jd); + const body = states.get(id)!.positionMpc; + const sun = states.get('sun')!.positionMpc; + const k = 1 / SCALE_UNITS.KM_TO_MPC; + return [(body[0] - sun[0]) * k, (body[1] - sun[1]) * k, (body[2] - sun[2]) * k]; +} + +describe('planet ephemeris', () => { + it('every planet is within 1,000 km of Horizons at the fixture dates', () => { + for (const [id, rows] of Object.entries(HORIZONS)) { + for (const [jd, expected] of Object.entries(rows)) { + const [x, y, z] = heliocentricKm(id, Number(jd)); + const errKm = Math.hypot(x - expected[0]!, y - expected[1]!, z - expected[2]!); + expect(errKm, `${id} at JD ${jd}`).toBeLessThan(TOLERANCE_KM); + } + } + }); +}); diff --git a/tests/fixtures/bodyStatesJ2000.json b/tests/fixtures/bodyStatesJ2000.json index 938fd8f0a..15fdbeaeb 100644 --- a/tests/fixtures/bodyStatesJ2000.json +++ b/tests/fixtures/bodyStatesJ2000.json @@ -1,6 +1,6 @@ { "callisto": { - "positionMpc": [1.9395074114394832e-11, 1.3353112790826594e-11, 5.254163647580489e-12], + "positionMpc": [1.9408901677315712e-11, 1.3321528986092854e-11, 5.2401142604567205e-12], "orientation": [ -0.20189790956390807, -0.8856344217249613, -0.4181972084670308, 0.9793604914627385, -0.1867008108423695, -0.07743277725010955, -0.009500625010351567, -0.42519933947114147, @@ -18,24 +18,24 @@ "meanAnomalyRad": 5.307546255314756 }, "deimos": { - "positionMpc": [6.7424822969448034e-12, 6.3822415228828266e-15, -1.7956313479591653e-13], + "positionMpc": [6.742719404912348e-12, 6.34331143373209e-15, -1.7961210376951156e-13], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 3.5779249665883754 }, "dione": { - "positionMpc": [3.1097913153309065e-11, 2.983963447795244e-11, 1.0980444303639635e-11], + "positionMpc": [3.1057293500772036e-11, 2.9947698055196037e-11, 1.1027667569334394e-11], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 3.7000980142279785 }, "earth": { - "positionMpc": [-8.589504546518782e-13, 4.302246510960393e-12, 1.8652507750193955e-12], + "positionMpc": [-8.58825881805149e-13, 4.302365851478245e-12, 1.8652815217339254e-12], "orientation": [ 0.1761742596326785, -0.9843589945964214, 0, 0.9843589945964214, 0.1761742596326785, 0, 0, 0, 1 ], "meanAnomalyRad": -0.04316391697638581 }, "enceladus": { - "positionMpc": [3.1104454125073664e-11, 2.9821430112281805e-11, 1.0981228244387354e-11], + "positionMpc": [3.1063834472536635e-11, 2.99294936895254e-11, 1.1028451510082113e-11], "orientation": [ -0.7305820977667332, 0.6827118412761586, 0.012423373296998674, -0.6774366704874311, -0.7269782211842862, 0.11217051039660496, 0.08561165751031312, 0.07353371815057558, @@ -44,7 +44,7 @@ "meanAnomalyRad": 0.9948376736367678 }, "europa": { - "positionMpc": [1.9366208544821226e-11, 1.3288551377534754e-11, 5.2231668713859995e-12], + "positionMpc": [1.9380036107742107e-11, 1.3256967572801014e-11, 5.209117484262231e-12], "orientation": [ 0.8261228244491918, 0.5034550279258321, 0.2530891419644264, -0.5633055860404016, 0.7493575544597507, 0.34806618955276497, -0.014418587301454018, -0.43011195104333155, @@ -53,7 +53,7 @@ "meanAnomalyRad": 6.028367236388414 }, "ganymede": { - "positionMpc": [1.935782125182158e-11, 1.3278963368210082e-11, 5.218463987793369e-12], + "positionMpc": [1.937164881474246e-11, 1.3247379563476342e-11, 5.2044146006696005e-12], "orientation": [ 0.7379372685045305, 0.6051980492902703, 0.29863675073125895, -0.674734511281878, 0.6704669763077579, 0.3085569201394417, -0.013488033113756969, -0.42919617288133416, @@ -62,12 +62,12 @@ "meanAnomalyRad": 5.668829410477583 }, "iapetus": { - "positionMpc": [3.112403542854821e-11, 2.993895163790855e-11, 1.0993205841361646e-11], + "positionMpc": [3.108341577601118e-11, 3.0047015215152144e-11, 1.1040429107056405e-11], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 1.3055062804917585 }, "io": { - "positionMpc": [1.9397356297216674e-11, 1.3302646760404455e-11, 5.23027425001944e-12], + "positionMpc": [1.9411183860137555e-11, 1.3271062955670715e-11, 5.2162248628956716e-12], "orientation": [ -0.9475005237879973, -0.2823909444270978, -0.14999370629143904, 0.31941847123727335, -0.8573973102852636, -0.4035364823012149, -0.014649151986168738, -0.4302617887067911, @@ -76,7 +76,7 @@ "meanAnomalyRad": 5.775294494849236 }, "jupiter": { - "positionMpc": [1.938440693074091e-11, 1.3298914119529161e-11, 5.2282900664869925e-12], + "positionMpc": [1.939823449366179e-11, 1.3267330314795421e-11, 5.2142406793632244e-12], "orientation": [ 0.22825610082668613, -0.8802468632436776, -0.41601035105519185, 0.9734916178676621, 0.19993790151295504, 0.1110806260335145, -0.014602136035502334, -0.4303374202742101, @@ -85,7 +85,7 @@ "meanAnomalyRad": 0.34327067101878284 }, "mars": { - "positionMpc": [6.7421474995208884e-12, 6.893277360777564e-15, -1.791107618500849e-13], + "positionMpc": [6.742384607488433e-12, 6.854347271626827e-15, -1.7915973082367994e-13], "orientation": [ -0.7067491138500313, -0.7065745401448309, 0.035469836358746884, 0.5490428766969101, -0.5794164477979991, -0.6023524712072909, 0.4461587269353554, -0.4062376142607543, @@ -94,7 +94,7 @@ "meanAnomalyRad": 0.3384227896851049 }, "mercury": { - "positionMpc": [-6.306874292609911e-13, -1.942153167646951e-12, -9.720109452033183e-13], + "positionMpc": [-6.306351560005334e-13, -1.9421570611633886e-12, -9.72018854428298e-13], "orientation": [ 0.9314951469471263, -0.27146527694401956, -0.24212268507444376, 0.3520893147390993, 0.8400821208129223, 0.4126683229144372, 0.09137781820826621, -0.46964735036425953, @@ -103,12 +103,12 @@ "meanAnomalyRad": 3.050705107870811 }, "mimas": { - "positionMpc": [3.109980743972915e-11, 2.982154582345605e-11, 1.0981783752642522e-11], + "positionMpc": [3.105918778719212e-11, 2.992960940069965e-11, 1.102900701833728e-11], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 4.804891430740389 }, "moon": { - "positionMpc": [-8.684282394279855e-13, 4.293738774580142e-12, 1.8628218170240537e-12], + "positionMpc": [-8.683036665812563e-13, 4.293858115097994e-12, 1.8628525637385836e-12], "orientation": [ 0.7845965380655952, 0.5687413064909984, 0.24686352251070906, -0.6200066702864299, 0.7197447865505031, 0.3123446350326911, -0.000035437512628813665, -0.3981215499370551, @@ -117,7 +117,7 @@ "meanAnomalyRad": 2.3609068791727297 }, "neptune": { - "positionMpc": [8.147178919019027e-11, -1.1141523115780291e-10, -4.7631193423792595e-11], + "positionMpc": [8.150711165197627e-11, -1.1141066748051168e-10, -4.763016806201879e-11], "orientation": [ 0.018461554711798778, -0.731133128996666, -0.6819849842050542, 0.9343480499713713, 0.2554057808141049, -0.24851882955106883, 0.3558832568586489, -0.6326232961330795, @@ -126,7 +126,7 @@ "meanAnomalyRad": -1.746809150875549 }, "phobos": { - "positionMpc": [6.742083325040046e-12, 6.609864655853662e-15, -1.7921380717377167e-13], + "positionMpc": [6.742320433007591e-12, 6.5709345667029255e-15, -1.792627761473667e-13], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 3.3108895910332428 }, @@ -150,7 +150,7 @@ "meanAnomalyRad": 0.08515672394345941 }, "rhea": { - "positionMpc": [3.1115776493412146e-11, 2.9821711975158945e-11, 1.098031510423812e-11], + "positionMpc": [3.107515684087512e-11, 2.992977555240254e-11, 1.1027538369932879e-11], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 0.5497787143782138 }, @@ -340,7 +340,7 @@ "meanAnomalyRad": 5.630129826433361 }, "saturn": { - "positionMpc": [3.1099752807904804e-11, 2.9827534561639487e-11, 1.0981182323435745e-11], + "positionMpc": [3.1059133155367775e-11, 2.993559813888308e-11, 1.1028405589130504e-11], "orientation": [ -0.9801892579246128, 0.18502086117224825, 0.07068450735682215, -0.1786683742380381, -0.9800019411711303, 0.08760027025045486, 0.0854788318610716, 0.07323575787752883, @@ -349,17 +349,17 @@ "meanAnomalyRad": -0.7442892730041829 }, "tethys": { - "positionMpc": [3.1108738218666247e-11, 2.9830585978804496e-11, 1.0980101650204179e-11], + "positionMpc": [3.106811856612922e-11, 2.993864955604809e-11, 1.1027324915898938e-11], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 0 }, "titan": { - "positionMpc": [3.111648567869407e-11, 2.9792900525807336e-11, 1.0982129467684212e-11], + "positionMpc": [3.107586602615704e-11, 2.990096410305093e-11, 1.1029352733378971e-11], "orientation": [1, 0, 0, 0, 1, 0, 0, 0, 1], "meanAnomalyRad": 0.20420352248333654 }, "uranus": { - "positionMpc": [6.993663216219453e-11, -6.06470857509395e-11, -2.755153855411126e-11], + "positionMpc": [6.996762625183395e-11, -6.063208073582132e-11, -2.7545606911649655e-11], "orientation": [ -0.8693317262684063, 0.3040596708473847, -0.3896281127785479, 0.44645103553580323, 0.1449623050604645, -0.8829877705724841, -0.2119995815377986, -0.9415591572895124, @@ -368,7 +368,7 @@ "meanAnomalyRad": 2.48332127460649 }, "venus": { - "positionMpc": [-3.4824959657808e-12, -2.2457953250190632e-13, 1.1936663949337553e-13], + "positionMpc": [-3.482424126157874e-12, -2.2440510278642365e-13, 1.1943343188733745e-13], "orientation": [ -0.954821497429634, 0.2665104385944917, 0.13148419740182907, -0.296591573568662, -0.8824137725799873, -0.36521140788482953, 0.018690814168902045, -0.38770880836179883, @@ -382,12 +382,12 @@ "meanAnomalyRad": 2.1093550674103057 }, "whale": { - "positionMpc": [-8.589504546518782e-13, 4.3024659441260814e-12, 1.8652507750193955e-12], + "positionMpc": [-8.58825881805149e-13, 4.3025852846439334e-12, 1.8652815217339254e-12], "orientation": [-1, 1.2246467991473532e-16, 0, -1.2246467991473532e-16, -1, 0, 0, 0, 1], "meanAnomalyRad": 0 }, "petunias": { - "positionMpc": [-8.589504533555665e-13, 4.3024659441260774e-12, 1.8652507750193955e-12], + "positionMpc": [-8.588258805088373e-13, 4.302585284643929e-12, 1.8652815217339254e-12], "orientation": [ 0.31895930929807, -0.9477684100095857, 0, -0.5730199417863159, -0.19284251607872754, 0.7965299180241964, -0.7549258939308582, -0.25406063248824595, -0.6045991148623748 @@ -397,23 +397,23 @@ "voyager1": { "positionMpc": [-9.849911903784252e-11, -3.47268536120473e-10, 7.701161928836898e-11], "orientation": [ - 0.2620963781618873, 0.9437245974921663, -0.2017160693714547, -0.9486015462831995, - 0.29035822333520933, 0.1258856962912936, 0.17737134758337958, 0.15835399025559568, - 0.9713204511522379 + 0.26209661470961676, 0.9437245655336025, -0.20171591153444463, -0.948601464583778, + 0.2903584480148159, 0.12588579370182462, 0.17737143498114238, 0.15835376874214452, + 0.9713204713058865 ], "meanAnomalyRad": 21.812722680769564 }, "voyager2": { "positionMpc": [7.68472828852993e-11, -1.820895489669245e-10, -2.120152434709163e-10], "orientation": [ - -0.26417090791453673, 0.6336594597733262, 0.7271103220635683, -0.956916648595112, - -0.2664026163055799, -0.11549966955381757, 0.12051663392705765, -0.7262956251178432, - 0.6767351076171333 + -0.26417042597844803, 0.6336597253131357, 0.7271102657468445, -0.9569168162798556, + -0.26640210928592933, -0.11549944973389652, 0.12051635888621277, -0.7262955794193499, + 0.6767352056430346 ], "meanAnomalyRad": 12.997179255974443 }, "hubble": { - "positionMpc": [-8.5896919141719e-13, 4.302441923444338e-12, 1.865146970252168e-12], + "positionMpc": [-8.588446185704608e-13, 4.30256126396219e-12, 1.865177716966698e-12], "orientation": [ -6.123233995736766e-17, 6.123233995736766e-17, 1, -6.123233995736766e-17, -1, 6.123233995736766e-17, 1, -6.123233995736766e-17, 6.123233995736766e-17 diff --git a/tests/fixtures/camera/driverGoldenTrace.json b/tests/fixtures/camera/driverGoldenTrace.json index 9257d51b8..1cf955732 100644 --- a/tests/fixtures/camera/driverGoldenTrace.json +++ b/tests/fixtures/camera/driverGoldenTrace.json @@ -3,18 +3,18 @@ "label": "boot 0", "winner": "resting", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.7, 0.3, 1.23882029118e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.7, 0.3, 1.23882029118e-15, @@ -59,18 +59,18 @@ "label": "boot 3", "winner": "resting", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.7, 0.3, 1.23882029118e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.7, 0.3, 1.23882029118e-15, @@ -115,18 +115,18 @@ "label": "drag 0", "winner": "orbitDrag", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.7, 0.3, 1.23882029118e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.7, 0.3, 1.23882029118e-15, @@ -171,18 +171,18 @@ "label": "drag end", "winner": "resting", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 1.23882029118e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 1.23882029118e-15, @@ -229,18 +229,18 @@ "label": "follow 0", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 1.23882029118e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 1.23882029118e-15, @@ -285,18 +285,18 @@ "label": "follow 7", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 1.08011618333e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 1.08011618333e-15, @@ -341,18 +341,18 @@ "label": "follow 9", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 1.04608780247e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 1.04608780247e-15, @@ -397,18 +397,18 @@ "label": "notch under follow a", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 9.91065552e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 9.91065552e-16, @@ -453,18 +453,18 @@ "label": "notch under follow b", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 9.74097988688e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 9.74097988688e-16, @@ -509,18 +509,18 @@ "label": "follow settle 0", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 9.583638658e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 9.583638658e-16, @@ -565,18 +565,18 @@ "label": "follow settle 3", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 9.1809669046e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 9.1809669046e-16, @@ -621,18 +621,18 @@ "label": "follow settle 11", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 8.53383734397e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 8.53383734397e-16, @@ -677,18 +677,18 @@ "label": "follow settle 19", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 8.31864465622e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 8.31864465622e-16, @@ -733,18 +733,18 @@ "label": "follow settle 23", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 8.29855276505e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 8.29855276505e-16, @@ -789,18 +789,18 @@ "label": "autoRotate 0", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 8.2976024729e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 8.2976024729e-16, @@ -845,18 +845,18 @@ "label": "autoRotate 2", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 8.29734065771e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 8.29734065771e-16, @@ -901,18 +901,18 @@ "label": "notch under autoRotate a", "winner": "autoRotate", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 8.29734065771e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 8.29734065771e-16, @@ -958,18 +958,18 @@ "label": "notch under autoRotate b", "winner": "autoRotate", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 8.29734065771e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61, 0.3, 8.29734065771e-16, @@ -1014,18 +1014,18 @@ "label": "autoRotate settle 0", "winner": "autoRotate", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61083808, 0.3, 8.29734065771e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61083808, 0.3, 8.29734065771e-16, @@ -1070,18 +1070,18 @@ "label": "autoRotate settle 2", "winner": "autoRotate", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61251424, 0.3, 8.29734065771e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61251424, 0.3, 8.29734065771e-16, @@ -1126,18 +1126,18 @@ "label": "tween 0", "winner": "tween", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61251424, 0.3, 8.29734065771e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.61251424, 0.3, 8.29734065771e-16, @@ -1183,18 +1183,18 @@ "label": "tween 1", "winner": "tween", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.66956424, 0.3, 8.88904476337e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.66956424, 0.3, 8.88904476337e-16, @@ -1239,18 +1239,18 @@ "label": "tween 3", "winner": "tween", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.76686424, 0.3, 9.89820882086e-16, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.76686424, 0.3, 9.89820882086e-16, @@ -1295,18 +1295,18 @@ "label": "tween 11", "winner": "tween", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.97606424, 0.3, 1.20679634029e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.97606424, 0.3, 1.20679634029e-15, @@ -1351,18 +1351,18 @@ "label": "tween 19", "winner": "tween", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01246424, 0.3, 1.24454924028e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01246424, 0.3, 1.24454924028e-15, @@ -1407,18 +1407,18 @@ "label": "tween 20", "winner": "tween", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01251424, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01251424, 0.3, 1.24460109866e-15, @@ -1464,18 +1464,18 @@ "label": "tween 21", "winner": "autoRotate", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01251424, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01251424, 0.3, 1.24460109866e-15, @@ -1521,18 +1521,18 @@ "label": "tween 22", "winner": "autoRotate", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01251424, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01251424, 0.3, 1.24460109866e-15, @@ -1577,18 +1577,18 @@ "label": "tween 23", "winner": "autoRotate", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01335232, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01335232, 0.3, 1.24460109866e-15, @@ -1633,18 +1633,18 @@ "label": "clip 0", "winner": "clip", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01335232, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01335232, 0.3, 1.24460109866e-15, @@ -1690,18 +1690,18 @@ "label": "clip 1", "winner": "clip", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01375444386, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01375444386, 0.3, 1.24460109866e-15, @@ -1746,18 +1746,18 @@ "label": "clip 3", "winner": "clip", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.02420966421, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.02420966421, 0.3, 1.24460109866e-15, @@ -1802,18 +1802,18 @@ "label": "clip 11", "winner": "clip", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.54857917721, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.54857917721, 0.3, 1.24460109866e-15, @@ -1858,18 +1858,18 @@ "label": "clip 19", "winner": "clip", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 2.49728989311, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 2.49728989311, 0.3, 1.24460109866e-15, @@ -1914,18 +1914,18 @@ "label": "clip 27", "winner": "clip", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01656931088, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.01656931088, 0.3, 1.24460109866e-15, @@ -1970,18 +1970,18 @@ "label": "clip 35", "winner": "clip", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.41547617966, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.41547617966, 0.3, 1.24460109866e-15, @@ -2026,18 +2026,18 @@ "label": "clip 37", "winner": "clip", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.70822234949, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.70822234949, 0.3, 1.24460109866e-15, @@ -2082,18 +2082,18 @@ "label": "teardown 0", "winner": "resting", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.70822234949, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.70822234949, 0.3, 1.24460109866e-15, @@ -2139,18 +2139,18 @@ "label": "teardown 1", "winner": "resting", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.70822234949, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.70822234949, 0.3, 1.24460109866e-15, @@ -2195,18 +2195,18 @@ "label": "teardown 5", "winner": "resting", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.70822234949, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.70822234949, 0.3, 1.24460109866e-15, @@ -2251,18 +2251,18 @@ "label": "refocus 0", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.70822234949, 0.3, 1.24460109866e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.70822234949, 0.3, 1.24460109866e-15, @@ -2307,18 +2307,18 @@ "label": "refocus 3", "winner": "followApproach", "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.70822234949, 0.3, 1.16729288125e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 1.70822234949, 0.3, 1.16729288125e-15, @@ -2363,18 +2363,18 @@ "label": "pan 0", "winner": "orbitDrag", "displayed": [ - -8.58953059185e-13, - 4.30216839327e-12, - 1.86525901142e-12, + -8.58828486338e-13, + 4.30228773378e-12, + 1.86528975814e-12, 1.70822234949, 0.3, 1.16729288125e-15, 0 ], "register": [ - -8.58953059185e-13, - 4.30216839327e-12, - 1.86525901142e-12, + -8.58828486338e-13, + 4.30228773378e-12, + 1.86528975814e-12, 1.70822234949, 0.3, 1.16729288125e-15, @@ -2420,18 +2420,18 @@ "label": "pan 1", "winner": "orbitDrag", "displayed": [ - -8.58955663718e-13, - 4.30209027557e-12, - 1.86526724783e-12, + -8.58831090871e-13, + 4.30220961609e-12, + 1.86529799454e-12, 1.70822234949, 0.3, 1.16729288125e-15, 0 ], "register": [ - -8.58955663718e-13, - 4.30209027557e-12, - 1.86526724783e-12, + -8.58831090871e-13, + 4.30220961609e-12, + 1.86529799454e-12, 1.70822234949, 0.3, 1.16729288125e-15, @@ -2476,18 +2476,18 @@ "label": "pan end", "winner": "followApproach", "displayed": [ - -8.58958268251e-13, - 4.30201215788e-12, - 1.86527548424e-12, + -8.58833695405e-13, + 4.3021314984e-12, + 1.86530623095e-12, 1.70822234949, 0.3, 1.20888694702e-15, 0 ], "register": [ - -8.58958268251e-13, - 4.30201215788e-12, - 1.86527548424e-12, + -8.58833695405e-13, + 4.3021314984e-12, + 1.86530623095e-12, 1.70822234949, 0.3, 1.20888694702e-15, @@ -2534,18 +2534,18 @@ "label": "pan settle 0", "winner": "followApproach", "displayed": [ - -8.58958268251e-13, - 4.30201215788e-12, - 1.86527548424e-12, + -8.58833695405e-13, + 4.3021314984e-12, + 1.86530623095e-12, 1.70822234949, 0.3, 1.20492839979e-15, 0 ], "register": [ - -8.58958268251e-13, - 4.30201215788e-12, - 1.86527548424e-12, + -8.58833695405e-13, + 4.3021314984e-12, + 1.86530623095e-12, 1.70822234949, 0.3, 1.20492839979e-15, @@ -2590,18 +2590,18 @@ "label": "pan settle 3", "winner": "followApproach", "displayed": [ - -8.58958268251e-13, - 4.30201215788e-12, - 1.86527548424e-12, + -8.58833695405e-13, + 4.3021314984e-12, + 1.86530623095e-12, 1.70822234949, 0.3, 1.19457445869e-15, 0 ], "register": [ - -8.58958268251e-13, - 4.30201215788e-12, - 1.86527548424e-12, + -8.58833695405e-13, + 4.3021314984e-12, + 1.86530623095e-12, 1.70822234949, 0.3, 1.19457445869e-15, diff --git a/tests/fixtures/camera/settleGoldenTrace.json b/tests/fixtures/camera/settleGoldenTrace.json index a92851fe3..89ecd70fa 100644 --- a/tests/fixtures/camera/settleGoldenTrace.json +++ b/tests/fixtures/camera/settleGoldenTrace.json @@ -5,18 +5,18 @@ "arm": "absolute", "hr": 5, "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.7, 0.3, 1.23882029118e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.7, 0.3, 1.23882029118e-15, @@ -29,18 +29,18 @@ "arm": "absolute", "hr": 4.69959329559, "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.7, 0.3, 1.17679530435e-15, 7.36444650118e-18 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.7, 0.3, 1.17679530435e-15, @@ -53,9 +53,9 @@ "arm": "earth", "hr": 0.408531953689, "displayed": [ - -8.58823383762e-13, - 4.30208382526e-12, - 1.86524674597e-12, + -8.58698810916e-13, + 4.30220316578e-12, + 1.86527749268e-12, 0.7, 0.299999999999, 8.43496123044e-17, @@ -85,9 +85,9 @@ "arm": "earth", "hr": 0.0615681608771, "displayed": [ - -8.58815842393e-13, - 4.30209013615e-12, - 1.86525828107e-12, + -8.58691269547e-13, + 4.30220947667e-12, + 1.86528902778e-12, 0.763865807331, 0.340617091539, 1.27119833965e-17, @@ -117,9 +117,9 @@ "arm": "earth", "hr": 0.0456277801724, "displayed": [ - -8.58815437959e-13, - 4.30209050575e-12, - 1.86525869645e-12, + -8.58690865113e-13, + 4.30220984627e-12, + 1.86528944316e-12, 0.767221245664, 0.342024316219, 9.42077070425e-18, @@ -149,9 +149,9 @@ "arm": "earth", "hr": 0.00000240000070995, "displayed": [ - -8.58814244804e-13, - 4.30209160677e-12, - 1.86525980005e-12, + -8.58689671957e-13, + 4.30221094729e-12, + 1.86529054676e-12, 0.77694673987, 0.34536113635, 4.95528126503e-22, @@ -181,9 +181,9 @@ "arm": "earth", "hr": 0.00000248496508704, "displayed": [ - -8.58814244804e-13, - 4.30209160677e-12, - 1.86525980005e-12, + -8.58689671957e-13, + 4.30221094729e-12, + 1.86529054676e-12, 0.752741906691, 0.264826976113, 5.1487696481e-22, @@ -213,9 +213,9 @@ "arm": "earth", "hr": 0.00000263277160673, "displayed": [ - -8.58814244804e-13, - 4.30209160677e-12, - 1.86525980006e-12, + -8.58689671957e-13, + 4.30221094729e-12, + 1.86529054678e-12, 0.684990514444, 0.02256104076, 5.7560563135e-22, @@ -245,9 +245,9 @@ "arm": "earth", "hr": 0.0000023999998191, "displayed": [ - -8.58814244845e-13, - 4.30209160682e-12, - 1.86525980147e-12, + -8.58689671998e-13, + 4.30221094734e-12, + 1.86529054818e-12, 0.304162643125, -0.931255732717, 2.17004580513e-21, @@ -277,9 +277,9 @@ "arm": "earth", "hr": 0.00000239999969298, "displayed": [ - -8.5881424494e-13, - 4.30209160674e-12, - 1.86525980147e-12, + -8.58689672093e-13, + 4.30221094725e-12, + 1.86529054818e-12, 0.304162393914, -0.931255659819, 2.17004580504e-21, @@ -309,9 +309,9 @@ "arm": "earth", "hr": 0.00000240000053964, "displayed": [ - -8.588142453e-13, - 4.30209160641e-12, - 1.86525980122e-12, + -8.58689672454e-13, + 4.30221094692e-12, + 1.86529054794e-12, 0.304160550871, -0.931256443589, 2.17004580513e-21, @@ -341,9 +341,9 @@ "arm": "earth", "hr": 0.00000240000208973, "displayed": [ - -8.58814245335e-13, - 4.30209160644e-12, - 1.86525980243e-12, + -8.58689672488e-13, + 4.30221094696e-12, + 1.86529054914e-12, 0.23536204091, -1.00544579425, 3.39220446157e-21, @@ -373,9 +373,9 @@ "arm": "earth", "hr": 0.0000023999991996, "displayed": [ - -8.58814244608e-13, - 4.30209160562e-12, - 1.86525979876e-12, + -8.58689671761e-13, + 4.30221094614e-12, + 1.86529054547e-12, -2.18873636672, -0.782395233589, 4.95528116513e-22, @@ -405,9 +405,9 @@ "arm": "earth", "hr": 0.00000239999928509, "displayed": [ - -8.58814244617e-13, - 4.30209160561e-12, - 1.86525979876e-12, + -8.5868967177e-13, + 4.30221094613e-12, + 1.86529054547e-12, -2.21974811218, -0.790268249161, 4.95528116513e-22, @@ -437,9 +437,9 @@ "arm": "earth", "hr": 0.00000240000213503, "displayed": [ - -8.58814244616e-13, - 4.3020916056e-12, - 1.86525979873e-12, + -8.58689671769e-13, + 4.30221094611e-12, + 1.86529054545e-12, -2.26868065666, -0.74568333147, 4.95528116513e-22, @@ -469,9 +469,9 @@ "arm": "earth", "hr": 0.00000323966126414, "displayed": [ - -8.58814244392e-13, - 4.30209160535e-12, - 1.86525979882e-12, + -8.58689671545e-13, + 4.30221094587e-12, + 1.86529054553e-12, -2.23721013181, -0.739329340615, 6.68892992494e-22, @@ -501,9 +501,9 @@ "arm": "earth", "hr": 0.11764992274, "displayed": [ - -8.58790612634e-13, - 4.30206304827e-12, - 1.86528148545e-12, + -8.58666039788e-13, + 4.30218238878e-12, + 1.86531223217e-12, -1.73704603665, -1.24764085932, 2.42911852576e-17, @@ -533,9 +533,9 @@ "arm": "earth", "hr": 0.130023273126, "displayed": [ - -8.58790902658e-13, - 4.30206246655e-12, - 1.86528562957e-12, + -8.58666329811e-13, + 4.30218180707e-12, + 1.86531637628e-12, -1.30186041478, -1.35142240492, 2.68459115123e-17, @@ -565,9 +565,9 @@ "arm": "earth", "hr": 0.175513060418, "displayed": [ - -8.58796269929e-13, - 4.3020644555e-12, - 1.86529713172e-12, + -8.58671697082e-13, + 4.30218379602e-12, + 1.86532787843e-12, 0.394311083513, -1.13068223763, 3.62381901023e-17, @@ -597,18 +597,18 @@ "arm": "absolute", "hr": 0.904220833938, "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.772030404994, 0.265666289276, 3.93164567996e-16, -5.55111512313e-17 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.772030404994, 0.265666289276, 3.93164567996e-16, @@ -621,18 +621,18 @@ "arm": "absolute", "hr": 0.999318569187, "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.772030404994, 0.265666289276, 4.12799402008e-16, 1.38777878078e-17 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.772030404994, 0.265666289276, 4.12799402008e-16, @@ -645,18 +645,18 @@ "arm": "absolute", "hr": 2.01238047432, "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.772030404994, 0.265666289276, 6.21966342726e-16, -5.55738316694e-18 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.772030404994, 0.265666289276, 6.21966342726e-16, @@ -669,18 +669,18 @@ "arm": "absolute", "hr": 1.82087715249, "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.772030404994, 0.265666289276, 5.82426642574e-16, -5.96791886065e-17 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.772030404994, 0.265666289276, 5.82426642574e-16, @@ -693,9 +693,9 @@ "arm": "earth", "hr": 0.449022777516, "displayed": [ - -8.58813615026e-13, - 4.30209194307e-12, - 1.86525445823e-12, + -8.5868904218e-13, + 4.30221128359e-12, + 1.86528520494e-12, 0.770526869984, 0.146645730242, 9.36724036408e-17, @@ -725,9 +725,9 @@ "arm": "earth", "hr": 0.404990357704, "displayed": [ - -8.58814134638e-13, - 4.30209146175e-12, - 1.86525332033e-12, + -8.58689561792e-13, + 4.30221080227e-12, + 1.86528406705e-12, 0.766060308078, 0.0569265954535, 8.56332551382e-17, @@ -757,9 +757,9 @@ "arm": "earth", "hr": 0.268610130415, "displayed": [ - -8.58815551831e-13, - 4.3020902092e-12, - 1.86525170703e-12, + -8.58690978984e-13, + 4.30220954972e-12, + 1.86528245375e-12, 0.730401394984, -0.381240094609, 6.74071828526e-17, @@ -791,18 +791,18 @@ "arm": "absolute", "hr": 5, "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.7, 0.3, 1.23882029118e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.7, 0.3, 1.23882029118e-15, @@ -815,18 +815,18 @@ "arm": "absolute", "hr": 4.69959329559, "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.7, 0.3, 1.17679530435e-15, 0 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.7, 0.3, 1.17679530435e-15, @@ -839,9 +839,9 @@ "arm": "earth", "hr": 0.408531953689, "displayed": [ - -8.58823383762e-13, - 4.30208382526e-12, - 1.86524674597e-12, + -8.58698810916e-13, + 4.30220316578e-12, + 1.86527749268e-12, 0.7, 0.299999999999, 8.43496123044e-17, @@ -871,9 +871,9 @@ "arm": "earth", "hr": 0.0615621239606, "displayed": [ - -8.58815721031e-13, - 4.30209022991e-12, - 1.86525805271e-12, + -8.58691148184e-13, + 4.30220957043e-12, + 1.86528879942e-12, 0.763746321348, 0.339489149201, 1.27107388113e-17, @@ -903,9 +903,9 @@ "arm": "earth", "hr": 0.0456251399424, "displayed": [ - -8.58815389035e-13, - 4.30209054158e-12, - 1.86525856693e-12, + -8.58690816189e-13, + 4.3022098821e-12, + 1.86528931365e-12, 0.767005933745, 0.341447327804, 9.42022602832e-18, @@ -935,9 +935,9 @@ "arm": "earth", "hr": 0.0000023999987333, "displayed": [ - -8.58814448428e-13, - 4.30209144188e-12, - 1.86526003613e-12, + -8.58689875581e-13, + 4.3022107824e-12, + 1.86529078285e-12, 0.775991805555, 0.346814022309, 4.9552812602e-22, @@ -967,9 +967,9 @@ "arm": "earth", "hr": 0.0000024849657001, "displayed": [ - -8.58814448428e-13, - 4.30209144188e-12, - 1.86526003613e-12, + -8.58689875582e-13, + 4.3022107824e-12, + 1.86529078285e-12, 0.773897707698, 0.263062016837, 5.14876964263e-22, @@ -999,9 +999,9 @@ "arm": "earth", "hr": 0.00000263277086576, "displayed": [ - -8.58814448431e-13, - 4.30209144188e-12, - 1.86526003614e-12, + -8.58689875584e-13, + 4.3022107824e-12, + 1.86529078286e-12, 0.768044168272, 0.0118011273059, 5.75605630761e-22, @@ -1031,9 +1031,9 @@ "arm": "earth", "hr": 0.0000023999997163, "displayed": [ - -8.58814448742e-13, - 4.30209144169e-12, - 1.8652600375e-12, + -8.58689875895e-13, + 4.3022107822e-12, + 1.86529078422e-12, 0.732896834484, -0.993109049844, 2.17004583329e-21, @@ -1063,9 +1063,9 @@ "arm": "earth", "hr": 0.00000239999935414, "displayed": [ - -8.58814448833e-13, - 4.3020914416e-12, - 1.86526003747e-12, + -8.58689875986e-13, + 4.30221078212e-12, + 1.86529078419e-12, 0.7328965318, -0.99310904296, 2.17004583338e-21, @@ -1095,9 +1095,9 @@ "arm": "earth", "hr": 0.00000240000002849, "displayed": [ - -8.58814449131e-13, - 4.30209144132e-12, - 1.86526003711e-12, + -8.58689876284e-13, + 4.30221078184e-12, + 1.86529078382e-12, 0.732895290976, -0.993110203657, 2.17004583347e-21, @@ -1127,9 +1127,9 @@ "arm": "earth", "hr": 0.00000240000012441, "displayed": [ - -8.58814449397e-13, - 4.30209144116e-12, - 1.86526003827e-12, + -8.5868987655e-13, + 4.30221078168e-12, + 1.86529078499e-12, 0.725551042947, -1.07680251628, 3.39220453137e-21, @@ -1159,9 +1159,9 @@ "arm": "earth", "hr": 0.00000239999946028, "displayed": [ - -8.58814447958e-13, - 4.30209144096e-12, - 1.86526003473e-12, + -8.58689875111e-13, + 4.30221078148e-12, + 1.86529078144e-12, -2.35006327273, -0.807403211062, 4.95528116483e-22, @@ -1191,9 +1191,9 @@ "arm": "earth", "hr": 0.00000240000239859, "displayed": [ - -8.58814447966e-13, - 4.30209144095e-12, - 1.86526003472e-12, + -8.58689875119e-13, + 4.30221078147e-12, + 1.86529078144e-12, -2.38373364458, -0.807339445766, 4.95528116483e-22, @@ -1223,9 +1223,9 @@ "arm": "earth", "hr": 0.00000239999978224, "displayed": [ - -8.5881444796e-13, - 4.30209144094e-12, - 1.8652600347e-12, + -8.58689875113e-13, + 4.30221078146e-12, + 1.86529078141e-12, -2.40944022372, -0.753557832895, 4.95528116483e-22, @@ -1255,9 +1255,9 @@ "arm": "earth", "hr": 0.0000032396613292, "displayed": [ - -8.58814447761e-13, - 4.30209144068e-12, - 1.86526003478e-12, + -8.58689874915e-13, + 4.30221078119e-12, + 1.86529078149e-12, -2.40944022372, -0.753557832895, 6.68892992433e-22, @@ -1287,9 +1287,9 @@ "arm": "earth", "hr": 0.117649922735, "displayed": [ - -8.58795358288e-13, - 4.30205885827e-12, - 1.8652804861e-12, + -8.58670785441e-13, + 4.30217819879e-12, + 1.86531123281e-12, -2.62627932389, -1.30978913277, 2.42911852565e-17, @@ -1319,9 +1319,9 @@ "arm": "earth", "hr": 0.130023273121, "displayed": [ - -8.58796303391e-13, - 4.30205769492e-12, - 1.86528438925e-12, + -8.58671730545e-13, + 4.30217703544e-12, + 1.86531513596e-12, -3.00081952413, -1.44805394725, 2.6845911511e-17, @@ -1351,9 +1351,9 @@ "arm": "earth", "hr": 0.175513060412, "displayed": [ - -8.58802801726e-13, - 4.30205868017e-12, - 1.86529532019e-12, + -8.5867822888e-13, + 4.30217802069e-12, + 1.86532606691e-12, 0.974192518494, -1.15078860015, 3.62381901006e-17, @@ -1383,18 +1383,18 @@ "arm": "absolute", "hr": 0.904220834056, "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.782643157335, 0.267207821768, 3.9316456802e-16, 0.0785409033636 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.782643157335, 0.267207821768, 3.9316456802e-16, @@ -1407,18 +1407,18 @@ "arm": "absolute", "hr": 0.999318569316, "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.782643157335, 0.267207821768, 4.12799402035e-16, 0.0785409033636 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.782643157335, 0.267207821768, 4.12799402035e-16, @@ -1431,18 +1431,18 @@ "arm": "absolute", "hr": 2.01238047459, "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.782643157335, 0.267207821768, 6.2196634278e-16, 0.0785409033636 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.782643157335, 0.267207821768, 6.2196634278e-16, @@ -1455,18 +1455,18 @@ "arm": "absolute", "hr": 1.82087715273, "displayed": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.782643157335, 0.267207821768, 5.82426642623e-16, 0.0785409033636 ], "register": [ - -8.58950454652e-13, - 4.30224651096e-12, - 1.86525077502e-12, + -8.58825881805e-13, + 4.30236585148e-12, + 1.86528152173e-12, 0.782643157335, 0.267207821768, 5.82426642623e-16, @@ -1479,9 +1479,9 @@ "arm": "earth", "hr": 0.449022777574, "displayed": [ - -8.58812915346e-13, - 4.30209258004e-12, - 1.86525502852e-12, + -8.586883425e-13, + 4.30221192056e-12, + 1.86528577523e-12, 0.79206408103, 0.148535164831, 9.36724038973e-17, @@ -1511,9 +1511,9 @@ "arm": "earth", "hr": 0.404742850815, "displayed": [ - -8.58813375989e-13, - 4.30209214146e-12, - 1.86525383748e-12, + -8.58688803142e-13, + 4.30221148198e-12, + 1.86528458419e-12, 0.801544274338, 0.0594167972848, 8.55816295471e-17, @@ -1543,9 +1543,9 @@ "arm": "earth", "hr": 0.266757742279, "displayed": [ - -8.58814922355e-13, - 4.30209075677e-12, - 1.8652520037e-12, + -8.58690349508e-13, + 4.30221009729e-12, + 1.86528275041e-12, 0.863342306569, -0.376693585659, 6.70660385912e-17, diff --git a/tests/fixtures/horizonsPlanets.json b/tests/fixtures/horizonsPlanets.json new file mode 100644 index 000000000..c52881de2 --- /dev/null +++ b/tests/fixtures/horizonsPlanets.json @@ -0,0 +1,258 @@ +{ + "mercury": { + "2415171.500000000": [ + 7483679.109714671, + 40369654.7213906, + 20778815.08579103 + ], + "2443937.500000000": [ + 7271709.769125367, + 40389445.90468683, + 20818386.63973618 + ], + "2444555.500000000": [ + -3894809.945644011, + 40590467.52391278, + 22084711.39517884 + ], + "2446454.500000000": [ + 19287679.52797054, + -56257872.2745742, + -32050509.08493746 + ], + "2447763.500000000": [ + -15664836.37926049, + -60674673.01552974, + -30783765.71038574 + ], + "2487855.500000000": [ + -59055866.45641895, + -11661944.9384968, + -123154.8931291581 + ] + }, + "venus": { + "2415171.500000000": [ + -72836657.81070568, + -74706497.65053442, + -28968751.72078097 + ], + "2443937.500000000": [ + -62822235.17691786, + -81948931.54033257, + -32885003.16269746 + ], + "2444555.500000000": [ + -86852340.72349352, + 55518089.06172591, + 30471310.43065875 + ], + "2446454.500000000": [ + 64516137.5418477, + -78459101.33648193, + -39377696.22862676 + ], + "2447763.500000000": [ + -47097520.37902651, + -90204823.7986849, + -37597272.99954098 + ], + "2487855.500000000": [ + 88168918.65270643, + 59217129.08197042, + 21087913.36805414 + ] + }, + "earth": { + "2415171.500000000": [ + -48348631.81548293, + -131945643.2686936, + -57241745.96787091 + ], + "2443937.500000000": [ + -142697670.355659, + 37256203.87297093, + 16154266.09699155 + ], + "2444555.500000000": [ + 95093680.20101847, + 104120219.1506324, + 45147175.09116362 + ], + "2446454.500000000": [ + -82129362.62129314, + 112135018.8593458, + 48620025.19905417 + ], + "2447763.500000000": [ + 133419545.3729756, + -65293501.62586283, + -28309887.57258326 + ], + "2487855.500000000": [ + -53112342.11036406, + -130343415.1897614, + -56474094.94749723 + ] + }, + "mars": { + "2415171.500000000": [ + 196925556.7603641, + 73918660.63203643, + 28530351.565069 + ], + "2443937.500000000": [ + 174122538.9992633, + -99593847.57765982, + -50395029.28909588 + ], + "2444555.500000000": [ + 54158028.14729189, + -187131636.9467229, + -87296875.91222645 + ], + "2446454.500000000": [ + -227503865.0092814, + -77708428.77508338, + -29483890.21139638 + ], + "2447763.500000000": [ + -245144581.5686254, + 33384597.23238907, + 21945342.175897 + ], + "2487855.500000000": [ + 95077986.17608264, + -169996236.9923828, + -80526902.74801362 + ] + }, + "jupiter": { + "2415171.500000000": [ + -303074464.8736803, + -682523895.0824546, + -285220581.0110855 + ], + "2443937.500000000": [ + -481720407.7906085, + 572731470.4252973, + 257257551.944829 + ], + "2444555.500000000": [ + -810875218.2413923, + 50473174.92184998, + 41403168.88808651 + ], + "2446454.500000000": [ + 633801879.8291223, + -366855759.752545, + -172695134.4249972 + ], + "2447763.500000000": [ + 62556278.61500947, + 700180918.6058778, + 298605219.6573165 + ], + "2487855.500000000": [ + -807255779.4990433, + 79079101.18609643, + 53513560.91675908 + ] + }, + "saturn": { + "2415171.500000000": [ + 64289641.37288647, + -1390193954.016424, + -576631345.547019 + ], + "2443937.500000000": [ + -1318558724.196886, + 400085094.9888089, + 221885350.8344445 + ], + "2444555.500000000": [ + -1421314829.154814, + -72167730.39596327, + 31282074.89120226 + ], + "2446454.500000000": [ + -681345123.5676914, + -1237147580.369312, + -481634368.5321383 + ], + "2447763.500000000": [ + 321639827.9425938, + -1350418209.921785, + -571543197.5011016 + ], + "2487855.500000000": [ + -1406012907.725283, + -288209808.7923881, + -58413568.19796775 + ] + }, + "uranus": { + "2415171.500000000": [ + -885809426.3931122, + -2481472506.995965, + -1074272566.638308 + ], + "2443937.500000000": [ + -1852864077.381997, + -1924864309.810483, + -816777509.6868061 + ], + "2444555.500000000": [ + -1568454233.97813, + -2143220747.819155, + -916430021.4497007 + ], + "2446454.500000000": [ + -549164855.0818605, + -2574471743.83951, + -1119753799.40533 + ], + "2447763.500000000": [ + 211411118.737716, + -2644337440.975036, + -1161130851.457387 + ], + "2487855.500000000": [ + 2861970833.861998, + 808048839.0802308, + 313493471.9391013 + ] + }, + "neptune": { + "2415171.500000000": [ + 155543083.5274187, + 4134808604.015062, + 1688525096.936738 + ], + "2443937.500000000": [ + -877168112.8228146, + -4120930858.357338, + -1664884573.591237 + ], + "2444555.500000000": [ + -593009878.0742743, + -4161036668.169079, + -1688382261.066192 + ], + "2446454.500000000": [ + 292824206.7088943, + -4176519072.444312, + -1716778984.965878 + ], + "2447763.500000000": [ + 897558488.791832, + -4091867133.224503, + -1697168074.23832 + ], + "2487855.500000000": [ + -4318175095.830819, + 1189662641.724081, + 594460174.6205162 + ] + } +} \ No newline at end of file diff --git a/tests/services/engine/frame/deriveBodyStates.test.ts b/tests/services/engine/frame/deriveBodyStates.test.ts index df215d74b..c8ec831f2 100644 --- a/tests/services/engine/frame/deriveBodyStates.test.ts +++ b/tests/services/engine/frame/deriveBodyStates.test.ts @@ -8,10 +8,25 @@ import { SURFACE_FIXED_SITES } from '../../../../src/data/bodies/surfaceFixedSit import { IDENTITY_MAT3 } from '../../../../src/utils/math/identityMat3'; import { propagateElements } from '../../../../src/utils/orbit/propagateElements'; import { keplerianPositionMpc } from '../../../../src/utils/orbit/keplerianPositionMpc'; +import { PLANET_EPHEMERIS_CORRECTIONS } from '../../../../src/data/bodies/planetEphemerisCorrections.generated'; +import { SCENE_EARTH } from '../../../../src/data/bodies/sceneEarth'; +import { SCALE_UNITS } from '../../../../src/data/scaleUnits'; +import { ephemerisCorrectionMpc } from '../../../../src/utils/orbit/ephemerisCorrectionMpc'; +import { siteGroundRadiusM } from '../../../../src/utils/camera/siteGroundRadiusM'; +import { sitePointBodyFixed } from '../../../../src/utils/camera/sitePointBodyFixed'; +import { rotateVec3ByTightMat3 } from '../../../../src/utils/math/rotateVec3ByTightMat3'; +import { findByIdOrThrow } from '../../../../src/utils/object/findByIdOrThrow'; import BODY_STATES_J2000 from '../../../fixtures/bodyStatesJ2000.json'; const states = deriveBodyStates(CONST_J2000); const ANCHOR_IDS = new Set(SCENE_ANCHORS.map((anchor) => anchor.id)); +const MPC_TO_KM = 1 / SCALE_UNITS.KM_TO_MPC; +// 1900, a Voyager flyby, 2099: the reflex and correction are checked across the span. +const SPAN_DATES = [2415171.5, 2447763.5, 2487855.5]; + +function distanceKm(a: readonly number[], b: readonly number[]): number { + return Math.hypot(a[0]! - b[0]!, a[1]! - b[1]!, a[2]! - b[2]!) * MPC_TO_KM; +} describe('deriveBodyStates', () => { it('returns a state for every driver id, and nothing else', () => { @@ -128,6 +143,44 @@ describe('deriveBodyStates', () => { expect(io.positionMpc[2] - jupiter.positionMpc[2]).toBeCloseTo(ioRelative[2], 18); }); + it('Earth–Moon reflex keeps the barycentre and the separation', () => { + // Earth's row is the barycentre: weighting Earth and Moon by mass must land back + // on the corrected row position, and the Moon's offset from Earth stays raw Kepler. + const k = 1 / 82.30057; + for (const t of SPAN_DATES) { + const snap = deriveBodyStates(t); + const earth = snap.get('earth')!.positionMpc; + const moon = snap.get('moon')!.positionMpc; + const sun = snap.get('sun')!.positionMpc; + const rowKepler = keplerianPositionMpc(propagateElements(elementsById('earth'), t)); + const correction = ephemerisCorrectionMpc(PLANET_EPHEMERIS_CORRECTIONS['earth']!, t); + const emb = [0, 1, 2].map((i) => sun[i]! + rowKepler[i]! + correction[i]!); + const weighted = [0, 1, 2].map((i) => (1 - k) * earth[i]! + k * moon[i]!); + expect(distanceKm(weighted, emb)).toBeLessThan(1e-3); + + const moonKepler = keplerianPositionMpc(propagateElements(elementsById('moon'), t)); + const separation = [0, 1, 2].map((i) => moon[i]! - earth[i]!); + expect(distanceKm(separation, moonKepler)).toBeLessThan(1e-3); + } + }); + + it('an Earth surface site keeps its offset from the wobbling Earth', () => { + // The site rides the reflexed Earth, so its offset is the body-fixed point under + // Earth's spin alone — the ~4,700 km wobble must not leak into it. + const site = findByIdOrThrow(SURFACE_FIXED_SITES, 'soendermarken', 'test'); + for (const t of SPAN_DATES) { + const snap = deriveBodyStates(t); + const earth = snap.get('earth')!; + const offsetM = rotateVec3ByTightMat3( + sitePointBodyFixed(site, siteGroundRadiusM(site, SCENE_EARTH.surface.datumRadiusM)), + earth.orientation, + ); + const offsetMpc = offsetM.map((m) => m * SCALE_UNITS.M_TO_MPC); + const actual = snap.get(site.id)!.positionMpc.map((v, i) => v - earth.positionMpc[i]!); + expect(distanceKm(actual, offsetMpc)).toBeLessThan(1e-3); + } + }); + it('orientation is identity iff the body is untextured', () => { // Matches orientationForBody's texture-gate contract: a textured body (Earth) // carries a baked IAU rotation; an untextured one (Titan) carries identity. diff --git a/tests/services/engine/frame/passes/orbitTrailsPass.test.ts b/tests/services/engine/frame/passes/orbitTrailsPass.test.ts index 0ad439b24..b2a0f7a73 100644 --- a/tests/services/engine/frame/passes/orbitTrailsPass.test.ts +++ b/tests/services/engine/frame/passes/orbitTrailsPass.test.ts @@ -31,13 +31,11 @@ import { makeSlab } from '../../../../fixtures/makeSlab'; import { CONST_J2000 } from '../../../../../src/data/time/constJ2000'; import { ORBITAL_ELEMENTS } from '../../../../../src/data/bodies/orbitalElements'; import { CORE_TRAIL_ELEMENTS } from '../../../../../src/data/bodies/coreTrailElements'; -import { SCENE_ANCHORS } from '../../../../../src/data/bodies/sceneAnchors'; import { deriveBodyStates } from '../../../../../src/services/engine/frame/deriveBodyStates'; import { eccentricAnomalyFromMean } from '../../../../../src/utils/orbit/eccentricAnomalyFromMean'; import { propagateElements } from '../../../../../src/utils/orbit/propagateElements'; import { keplerianEllipse } from '../../../../../src/utils/orbit/keplerianEllipse'; import { keplerianPositionMpc } from '../../../../../src/utils/orbit/keplerianPositionMpc'; -import { addVec3 } from '../../../../../src/utils/math/addVec3'; import type { SlabView } from '../../../../../src/@types/engine/frame/SlabView'; import type { Slab } from '../../../../../src/@types/engine/frame/Slab'; import type { FrameView } from '../../../../../src/@types/engine/frame/FrameView'; @@ -95,13 +93,17 @@ vi.mock('../../../../../src/services/engine/frame/sceneBodyStates', async (impor const composeMock = composeOrbitConic as unknown as ReturnType; // The J2000 oracle `SCENE_ORBIT_CONICS` used to provide, hand-derived here with -// its own two helpers (`keplerianEllipse` for the ellipse, `keplerianPositionMpc` -// to place a focus that is itself an orbiting body) so the expected values never -// come from the layer under test. -function conicAtJ2000(el: OrbitalElements, focusPositionMpc: Readonly) { +// its own helpers (`keplerianEllipse` for the ellipse, the snapshot body less its +// raw Kepler offset for the focus the trail anchors on) so the expected values +// never come from the layer under test. +function conicAtJ2000(el: OrbitalElements) { const { centerOffsetMpc, semiMajorMpc, semiMinorMpc } = keplerianEllipse(el); + const bodyMpc = deriveBodyStates(CONST_J2000).get(el.id)!.positionMpc; + const keplerMpc = keplerianPositionMpc(el); return { - centerMpc: addVec3(focusPositionMpc, centerOffsetMpc), + centerMpc: [0, 1, 2].map( + (i) => bodyMpc[i]! - keplerMpc[i]! + centerOffsetMpc[i]!, + ) as unknown as Vec3, semiMajorMpc, semiMinorMpc, eccentricity: el.eccentricity, @@ -110,14 +112,9 @@ function conicAtJ2000(el: OrbitalElements, focusPositionMpc: Readonly) { }; } -const SUN_POS_MPC = SCENE_ANCHORS.find((a) => a.id === 'sun')!.positionMpc; -const EARTH_POS_MPC = addVec3( - SUN_POS_MPC, - keplerianPositionMpc(CORE_TRAIL_ELEMENTS.find((e) => e.id === 'earth')!), -); // Mercury is CORE_TRAIL_ELEMENTS[0] — the first ORBITAL_ELEMENTS row, unfiltered. -const MERCURY_CONIC = conicAtJ2000(CORE_TRAIL_ELEMENTS[0]!, SUN_POS_MPC); -const MOON_CONIC = conicAtJ2000(CORE_TRAIL_ELEMENTS.find((e) => e.id === 'moon')!, EARTH_POS_MPC); +const MERCURY_CONIC = conicAtJ2000(CORE_TRAIL_ELEMENTS[0]!); +const MOON_CONIC = conicAtJ2000(CORE_TRAIL_ELEMENTS.find((e) => e.id === 'moon')!); const PASS_STUB = { setPipeline: vi.fn(), @@ -653,6 +650,57 @@ describe('orbitTrailsPass.draw', () => { } }); + it('Moon, Hubble and Saturn sit on their trails at a non-J2000 date', () => { + // 1989: Earth carries its correction and the Moon's reflex, Saturn its own + // correction; each body must still lie on its conic at its propagated E. + const simDays = 2447763.5; + const states = deriveBodyStates(simDays); + const kmPerMpc = 1 / SCALE_UNITS.KM_TO_MPC; + for (const id of ['moon', 'hubble', 'saturn']) { + const el = ORBITAL_ELEMENTS.find((e) => e.id === id)!; + const bodyMpc = states.get(id)!.positionMpc; + const eye: Vec3 = [bodyMpc[0] + 100 * SCALE_UNITS.KM_TO_MPC, bodyMpc[1], bodyMpc[2]]; + const ctx = { + snapshot: { + simDays, + focusBlend: 0, + nowMs: 0, + renderTargets: { farDepthView: () => FAR_DEPTH_VIEW_STUB }, + }, + bodyPose: () => null, + drawCamPos: eye, + drawPxPerRad: FIXTURE_PX_PER_RAD, + cam: { distance: 1e-13 }, + } as unknown as FrameView; + composeMock.mockClear(); + const renderer = makeRendererSpy(); + orbitTrailsPass.draw(PASS_STUB, makeNear0View(), ctx, { + ...makeState(renderer), + orbitTrailRows: [el], + } as unknown as EngineState); + + const [, { instances: staging, count }] = renderer.draw.mock.calls[0]!; + expect(count, id).toBe(1); + const { eccentricity, meanAnomalyRad } = propagateElements(el, simDays); + const eAnom = eccentricAnomalyFromMean(meanAnomalyRad, eccentricity); + const [cosE, sinE] = [Math.cos(eAnom), Math.sin(eAnom)]; + // f32's ulp at Saturn's 1.4e9 km semi-major axis is 128 km, so Saturn reads the + // f64 conic handed to composeOrbitConic; the small orbits read the packed basis. + const [, centre, major, minor] = composeMock.mock.calls[0]! as Vec3[]; + for (let axis = 0; axis < 3; axis++) { + const rel = (bodyMpc[axis]! - eye[axis]!) * kmPerMpc; + if (id === 'saturn') { + const onConic = centre![axis]! + major![axis]! * cosE + minor![axis]! * sinE; + expect(Math.abs((onConic - bodyMpc[axis]!) * kmPerMpc), id).toBeLessThan(1e-3); + } else { + const packed = + staging[34 + axis]! + staging[38 + axis]! * cosE + staging[42 + axis]! * sinE; + expect(Math.abs(packed - rel), id).toBeLessThan(0.1); + } + } + } + }); + it('stages no conic for the mesh bodies (whale, petunias) even when the Moon trail is emitted', () => { // Same pose as "rides a moon trail on its propagated parent" above — parking // the camera at Earth is what makes the Moon's tiny geocentric orbit survive diff --git a/tools/bodies/@types/HorizonsSeries.d.ts b/tools/bodies/@types/HorizonsSeries.d.ts new file mode 100644 index 000000000..3c9ea7e8c --- /dev/null +++ b/tools/bodies/@types/HorizonsSeries.d.ts @@ -0,0 +1,3 @@ +/** One planet's Horizons heliocentric series: UT Julian dates and equatorial km per axis. */ + +export type HorizonsSeries = { jd: Float64Array; km: [Float64Array, Float64Array, Float64Array] }; diff --git a/tools/bodies/buildPlanetEphemeris.ts b/tools/bodies/buildPlanetEphemeris.ts index 34cc192f0..bbf2b7dc2 100644 --- a/tools/bodies/buildPlanetEphemeris.ts +++ b/tools/bodies/buildPlanetEphemeris.ts @@ -20,6 +20,7 @@ import { keplerianPositionMpc } from '../../src/utils/orbit/keplerianPositionMpc import { propagateElements } from '../../src/utils/orbit/propagateElements'; import type { EphemerisCorrection } from '../../src/@types/scene/EphemerisCorrection'; import type { Vec3 } from '../../src/@types/math/Vec3'; +import type { HorizonsSeries } from './@types/HorizonsSeries'; import { fitSinusoidSeries } from '../utils/math/fitSinusoidSeries'; import { rawDataPath } from '../utils/io/rawDataRegistry'; @@ -52,8 +53,6 @@ const KM_TO_MPC = SCALE_UNITS.KM_TO_MPC; const fmtOmega = (x: number): string => String(Number(x.toPrecision(12))); const fmtKm = (x: number): string => String(Math.round(x * 10) / 10); -type HorizonsSeries = { jd: Float64Array; km: [Float64Array, Float64Array, Float64Array] }; - function readSeries(naif: string): HorizonsSeries { const lines = readFileSync(join(rawDataPath('horizons.planets'), `${naif}.csv`), 'utf8') .trim() diff --git a/tools/perf/perfScenarios.ts b/tools/perf/perfScenarios.ts index 0e652fb78..299f2ff04 100644 --- a/tools/perf/perfScenarios.ts +++ b/tools/perf/perfScenarios.ts @@ -49,7 +49,7 @@ export type PerfScenario = { readonly name: string; readonly pose: PerfPose }; // time). // Factored out so those poses differ only in the axes that actually vary // (distance/yaw/pitch) and the target can never drift between them. -const EARTH_TARGET: Vec3 = [-8.5895045e-13, 4.3022465e-12, 1.8652508e-12]; +const EARTH_TARGET: Vec3 = [-8.5882588e-13, 4.3023659e-12, 1.8652815e-12]; // The shared look-at point for `milky-way-outside` and `milky-way-close`: the // galactic centre (~8 kpc off the origin — contrast `EARTH_TARGET`'s ~1 AU). From 8eea5fe6924553d2cf1ab181196dc45e1f0e01de Mon Sep 17 00:00:00 2001 From: Alexander Rulkens Date: Sun, 4 Oct 2026 10:19:32 +0200 Subject: [PATCH 10/14] Final review fixes --- src/data/bodies/barycentricPairs.ts | 2 +- .../engine/frame/deriveBodyStates.test.ts | 3 +- .../tools/parsers/horizonsVectorsCsv.test.ts | 4 --- tools/@types/math/FitResult.d.ts | 2 -- tools/utils/math/dot.ts | 5 +++ tools/utils/math/fitSinusoidSeries.ts | 33 ++++++++++++------- 6 files changed, 29 insertions(+), 20 deletions(-) create mode 100644 tools/utils/math/dot.ts diff --git a/src/data/bodies/barycentricPairs.ts b/src/data/bodies/barycentricPairs.ts index 552342af5..09ec79046 100644 --- a/src/data/bodies/barycentricPairs.ts +++ b/src/data/bodies/barycentricPairs.ts @@ -8,7 +8,7 @@ import type { BarycentricPair } from '../../@types/scene/BarycentricPair'; import { ORBITAL_ELEMENTS } from './orbitalElements'; import { findByIdOrThrow } from '../../utils/object/findByIdOrThrow'; -export const BARYCENTRIC_PAIRS: readonly BarycentricPair[] = [ +const BARYCENTRIC_PAIRS: readonly BarycentricPair[] = [ { primaryId: 'earth', secondaryId: 'moon', secondaryMassFraction: 1 / 82.30057 }, ]; diff --git a/tests/services/engine/frame/deriveBodyStates.test.ts b/tests/services/engine/frame/deriveBodyStates.test.ts index c8ec831f2..ff8676d74 100644 --- a/tests/services/engine/frame/deriveBodyStates.test.ts +++ b/tests/services/engine/frame/deriveBodyStates.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest'; import { deriveBodyStates } from '../../../../src/services/engine/frame/deriveBodyStates'; import { CONST_J2000 } from '../../../../src/data/time/constJ2000'; import { ORBITAL_ELEMENTS, elementsById } from '../../../../src/data/bodies/orbitalElements'; +import { BARYCENTRIC_REFLEX_BY_PRIMARY } from '../../../../src/data/bodies/barycentricPairs'; import { SCENE_ANCHORS } from '../../../../src/data/bodies/sceneAnchors'; import { SCENE_STARS } from '../../../../src/data/bodies/sceneStars'; import { SURFACE_FIXED_SITES } from '../../../../src/data/bodies/surfaceFixedSites'; @@ -146,7 +147,7 @@ describe('deriveBodyStates', () => { it('Earth–Moon reflex keeps the barycentre and the separation', () => { // Earth's row is the barycentre: weighting Earth and Moon by mass must land back // on the corrected row position, and the Moon's offset from Earth stays raw Kepler. - const k = 1 / 82.30057; + const k = BARYCENTRIC_REFLEX_BY_PRIMARY.get('earth')!.k; for (const t of SPAN_DATES) { const snap = deriveBodyStates(t); const earth = snap.get('earth')!.positionMpc; diff --git a/tests/tools/parsers/horizonsVectorsCsv.test.ts b/tests/tools/parsers/horizonsVectorsCsv.test.ts index 1ae9ed635..92d194f17 100644 --- a/tests/tools/parsers/horizonsVectorsCsv.test.ts +++ b/tests/tools/parsers/horizonsVectorsCsv.test.ts @@ -27,8 +27,4 @@ describe('parseHorizonsVectorsCsv', () => { }); expect(rows[2]).toMatchObject({ jd: 2415022.5, zKm: -1.016666302505421e7 }); }); - - it('throws when the result carries no data block', () => { - expect(() => parseHorizonsVectorsCsv('No ephemeris for target')).toThrow(/SOE/); - }); }); diff --git a/tools/@types/math/FitResult.d.ts b/tools/@types/math/FitResult.d.ts index 322eb50b8..179a2181b 100644 --- a/tools/@types/math/FitResult.d.ts +++ b/tools/@types/math/FitResult.d.ts @@ -1,7 +1,6 @@ /** * FitResult — `fitSinusoidSeries`' output, in the plain basis `EphemerisCorrection` stores: * cubic coefficients in τ (km) and flat 7-per-term sinusoids (ω rad/day, cos xyz, sin xyz km). - * `maxErrKm` is the worst 3D residual on the fit's own sample grid. */ import type { Vec3 } from '../../../src/@types/math/Vec3'; @@ -9,5 +8,4 @@ import type { Vec3 } from '../../../src/@types/math/Vec3'; export type FitResult = { polyKm: [Vec3, Vec3, Vec3, Vec3]; terms: number[]; - maxErrKm: number; }; diff --git a/tools/utils/math/dot.ts b/tools/utils/math/dot.ts new file mode 100644 index 000000000..522827ba4 --- /dev/null +++ b/tools/utils/math/dot.ts @@ -0,0 +1,5 @@ +export const dot = (a: Float64Array, b: Float64Array): number => { + let s = 0; + for (let i = 0; i < a.length; i++) s += a[i]! * b[i]!; + return s; +}; diff --git a/tools/utils/math/fitSinusoidSeries.ts b/tools/utils/math/fitSinusoidSeries.ts index 9a0280d52..eadafa685 100644 --- a/tools/utils/math/fitSinusoidSeries.ts +++ b/tools/utils/math/fitSinusoidSeries.ts @@ -9,6 +9,7 @@ import type { FitResult } from '../../@types/math/FitResult'; import type { Vec3 } from '../../../src/@types/math/Vec3'; +import { dot } from './dot'; import { fft } from './fft'; const ZERO_PAD = 8; @@ -16,12 +17,6 @@ const GOLDEN = (Math.sqrt(5) - 1) / 2; // Shrinks the two-bin bracket by 0.618^40 ≈ 4e-9: far below any phase error that matters. const GOLDEN_ITERATIONS = 40; -const dot = (a: Float64Array, b: Float64Array): number => { - let s = 0; - for (let i = 0; i < a.length; i++) s += a[i]! * b[i]!; - return s; -}; - export function fitSinusoidSeries( tJd: Float64Array, residualKm: [Float64Array, Float64Array, Float64Array], @@ -124,13 +119,27 @@ export function fitSinusoidSeries( for (let k = 2; k < m / 2 - 1; k++) if (power[k]! > power[kb]!) kb = k; // The padded FFT only brackets the peak; a golden-section search inside the bracket // lands on it, so one term absorbs a tone instead of a cluster of neighbours. - let lo = (2 * Math.PI * (kb - 1)) / (m * stepDays); + // Bin 0 is DC, where the sin column vanishes: keep the bracket at one padded bin or above. + let lo = (2 * Math.PI * Math.max(kb - 1, 1)) / (m * stepDays); let hi = (2 * Math.PI * (kb + 1)) / (m * stepDays); + let w1 = hi - GOLDEN * (hi - lo); + let w2 = lo + GOLDEN * (hi - lo); + let e1 = capturedEnergy(w1); + let e2 = capturedEnergy(w2); for (let it = 0; it < GOLDEN_ITERATIONS; it++) { - const w1 = hi - GOLDEN * (hi - lo); - const w2 = lo + GOLDEN * (hi - lo); - if (capturedEnergy(w1) < capturedEnergy(w2)) lo = w1; - else hi = w2; + if (e1 < e2) { + lo = w1; + w1 = w2; + e1 = e2; + w2 = lo + GOLDEN * (hi - lo); + e2 = capturedEnergy(w2); + } else { + hi = w2; + w2 = w1; + e2 = e1; + w1 = hi - GOLDEN * (hi - lo); + e1 = capturedEnergy(w1); + } } const omega = (lo + hi) / 2; addColumn(Float64Array.from(dt, (d) => Math.cos(omega * d))); @@ -151,5 +160,5 @@ export function fitSinusoidSeries( }); const at = (j: number): Vec3 => [coef[0]![j]!, coef[1]![j]!, coef[2]![j]!]; const terms = omegas.flatMap((omega, t) => [omega, ...at(4 + 2 * t), ...at(5 + 2 * t)]); - return { polyKm: [at(0), at(1), at(2), at(3)], terms, maxErrKm: maxErr() }; + return { polyKm: [at(0), at(1), at(2), at(3)], terms }; } From 60a10eec941c26ebbf735356fdecfdfa1221d133 Mon Sep 17 00:00:00 2001 From: Alexander Rulkens Date: Sun, 4 Oct 2026 10:23:22 +0200 Subject: [PATCH 11/14] Regenerate planet corrections from the final fit tool; re-bless snapshot and camera traces --- .../planetEphemerisCorrections.generated.ts | 572 +++++++++--------- tests/fixtures/bodyStatesJ2000.json | 62 +- tests/fixtures/camera/driverGoldenTrace.json | 548 ++++++++--------- tests/fixtures/camera/settleGoldenTrace.json | 328 +++++----- 4 files changed, 755 insertions(+), 755 deletions(-) diff --git a/src/data/bodies/planetEphemerisCorrections.generated.ts b/src/data/bodies/planetEphemerisCorrections.generated.ts index 7aa064e9f..2bd809cbd 100644 --- a/src/data/bodies/planetEphemerisCorrections.generated.ts +++ b/src/data/bodies/planetEphemerisCorrections.generated.ts @@ -16,34 +16,34 @@ export const PLANET_EPHEMERIS_CORRECTIONS: Readonly Date: Sun, 4 Oct 2026 23:54:39 +0200 Subject: [PATCH 12/14] Deletion audit: drop surplus tool types, import guard and a redundant test --- .../utils/orbit/ephemerisCorrectionMpc.test.ts | 8 -------- tools/@types/math/FitResult.d.ts | 11 ----------- tools/bodies/@types/HorizonsSeries.d.ts | 3 --- tools/bodies/buildPlanetEphemeris.ts | 18 +++++------------- tools/utils/math/fitSinusoidSeries.ts | 4 ++-- 5 files changed, 7 insertions(+), 37 deletions(-) delete mode 100644 tools/@types/math/FitResult.d.ts delete mode 100644 tools/bodies/@types/HorizonsSeries.d.ts diff --git a/tests/utils/orbit/ephemerisCorrectionMpc.test.ts b/tests/utils/orbit/ephemerisCorrectionMpc.test.ts index 143e0e6c5..22d0ef8e2 100644 --- a/tests/utils/orbit/ephemerisCorrectionMpc.test.ts +++ b/tests/utils/orbit/ephemerisCorrectionMpc.test.ts @@ -46,12 +46,4 @@ describe('ephemerisCorrectionMpc', () => { ephemerisCorrectionMpc(CORRECTION, CORRECTION.endJd), ); }); - - it('is continuous across both span edges', () => { - for (const edge of [CORRECTION.startJd, CORRECTION.endJd]) { - const a = km(edge - 1e-6); - const b = km(edge + 1e-6); - expect(Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2])).toBeLessThan(1e-3); - } - }); }); diff --git a/tools/@types/math/FitResult.d.ts b/tools/@types/math/FitResult.d.ts deleted file mode 100644 index 179a2181b..000000000 --- a/tools/@types/math/FitResult.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * FitResult — `fitSinusoidSeries`' output, in the plain basis `EphemerisCorrection` stores: - * cubic coefficients in τ (km) and flat 7-per-term sinusoids (ω rad/day, cos xyz, sin xyz km). - */ - -import type { Vec3 } from '../../../src/@types/math/Vec3'; - -export type FitResult = { - polyKm: [Vec3, Vec3, Vec3, Vec3]; - terms: number[]; -}; diff --git a/tools/bodies/@types/HorizonsSeries.d.ts b/tools/bodies/@types/HorizonsSeries.d.ts deleted file mode 100644 index 3c9ea7e8c..000000000 --- a/tools/bodies/@types/HorizonsSeries.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -/** One planet's Horizons heliocentric series: UT Julian dates and equatorial km per axis. */ - -export type HorizonsSeries = { jd: Float64Array; km: [Float64Array, Float64Array, Float64Array] }; diff --git a/tools/bodies/buildPlanetEphemeris.ts b/tools/bodies/buildPlanetEphemeris.ts index bbf2b7dc2..efb1ef441 100644 --- a/tools/bodies/buildPlanetEphemeris.ts +++ b/tools/bodies/buildPlanetEphemeris.ts @@ -9,7 +9,6 @@ */ import { readFileSync, writeFileSync } from 'node:fs'; import { join, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; import { format, resolveConfig } from 'prettier'; @@ -20,7 +19,6 @@ import { keplerianPositionMpc } from '../../src/utils/orbit/keplerianPositionMpc import { propagateElements } from '../../src/utils/orbit/propagateElements'; import type { EphemerisCorrection } from '../../src/@types/scene/EphemerisCorrection'; import type { Vec3 } from '../../src/@types/math/Vec3'; -import type { HorizonsSeries } from './@types/HorizonsSeries'; import { fitSinusoidSeries } from '../utils/math/fitSinusoidSeries'; import { rawDataPath } from '../utils/io/rawDataRegistry'; @@ -53,14 +51,14 @@ const KM_TO_MPC = SCALE_UNITS.KM_TO_MPC; const fmtOmega = (x: number): string => String(Number(x.toPrecision(12))); const fmtKm = (x: number): string => String(Math.round(x * 10) / 10); -function readSeries(naif: string): HorizonsSeries { +function readSeries(naif: string) { const lines = readFileSync(join(rawDataPath('horizons.planets'), `${naif}.csv`), 'utf8') .trim() .split('\n') .slice(1); const cols = [0, 1, 2, 3].map(() => new Float64Array(lines.length)); lines.forEach((line, i) => line.split(',').forEach((v, c) => (cols[c]![i] = Number(v)))); - return { jd: cols[0]!, km: [cols[1]!, cols[2]!, cols[3]!] }; + return { jd: cols[0]!, km: [cols[1]!, cols[2]!, cols[3]!] as const }; } function keplerKm(id: string, jd: number): Vec3 { @@ -118,9 +116,9 @@ function buildPlanet(id: string, naif: string, step: number): { text: string; su let maxKm = 0; for (let i = 0; i < jd.length; i++) { - const kepler = keplerianPositionMpc(propagateElements(elementsById(id), jd[i]!)); + const kepler = keplerKm(id, jd[i]!); const corr = ephemerisCorrectionMpc(shipped, jd[i]!); - const err = [0, 1, 2].map((a) => km[a]![i]! - (kepler[a]! + corr[a]!) / KM_TO_MPC); + const err = [0, 1, 2].map((a) => km[a]![i]! - (kepler[a]! + corr[a]! / KM_TO_MPC)); maxKm = Math.max(maxKm, Math.hypot(err[0]!, err[1]!, err[2]!)); } const nTerms = fit.terms.length / 7; @@ -151,10 +149,4 @@ async function main(): Promise { process.stderr.write(`wrote ${GENERATED_PATH} (${(formatted.length / 1024).toFixed(1)} kB)\n`); } -// Allow the script to be both executed (CLI) and imported (tests). -if (process.argv[1] === fileURLToPath(import.meta.url)) { - main().catch((err: unknown) => { - process.stderr.write(`error: ${err instanceof Error ? err.message : String(err)}\n`); - process.exit(1); - }); -} +await main(); diff --git a/tools/utils/math/fitSinusoidSeries.ts b/tools/utils/math/fitSinusoidSeries.ts index eadafa685..0872b538e 100644 --- a/tools/utils/math/fitSinusoidSeries.ts +++ b/tools/utils/math/fitSinusoidSeries.ts @@ -7,7 +7,7 @@ * (ω, cos, sin) amplitudes. τ and phase conventions match `ephemerisCorrectionMpc`. */ -import type { FitResult } from '../../@types/math/FitResult'; +import type { EphemerisCorrection } from '../../../src/@types/scene/EphemerisCorrection'; import type { Vec3 } from '../../../src/@types/math/Vec3'; import { dot } from './dot'; import { fft } from './fft'; @@ -24,7 +24,7 @@ export function fitSinusoidSeries( endJd: number, stopKm: number, maxTerms: number, -): FitResult { +): Pick { const n = tJd.length; // Uniform, except the last gap may be short so the grid can end exactly on `endJd`: an // unsampled tail is extrapolated, and drifts past the stop. The FFT only brackets ω, so From 3ff4005a5848c36f06f9c05a98a09979a7fb93f2 Mon Sep 17 00:00:00 2001 From: Alexander Rulkens Date: Mon, 5 Oct 2026 00:11:50 +0200 Subject: [PATCH 13/14] Deletion audit: drop the real-data on-trail test (the snapshot-offset test covers the mechanism) --- .../frame/passes/orbitTrailsPass.test.ts | 51 ------------------- 1 file changed, 51 deletions(-) diff --git a/tests/services/engine/frame/passes/orbitTrailsPass.test.ts b/tests/services/engine/frame/passes/orbitTrailsPass.test.ts index b2a0f7a73..855100d48 100644 --- a/tests/services/engine/frame/passes/orbitTrailsPass.test.ts +++ b/tests/services/engine/frame/passes/orbitTrailsPass.test.ts @@ -650,57 +650,6 @@ describe('orbitTrailsPass.draw', () => { } }); - it('Moon, Hubble and Saturn sit on their trails at a non-J2000 date', () => { - // 1989: Earth carries its correction and the Moon's reflex, Saturn its own - // correction; each body must still lie on its conic at its propagated E. - const simDays = 2447763.5; - const states = deriveBodyStates(simDays); - const kmPerMpc = 1 / SCALE_UNITS.KM_TO_MPC; - for (const id of ['moon', 'hubble', 'saturn']) { - const el = ORBITAL_ELEMENTS.find((e) => e.id === id)!; - const bodyMpc = states.get(id)!.positionMpc; - const eye: Vec3 = [bodyMpc[0] + 100 * SCALE_UNITS.KM_TO_MPC, bodyMpc[1], bodyMpc[2]]; - const ctx = { - snapshot: { - simDays, - focusBlend: 0, - nowMs: 0, - renderTargets: { farDepthView: () => FAR_DEPTH_VIEW_STUB }, - }, - bodyPose: () => null, - drawCamPos: eye, - drawPxPerRad: FIXTURE_PX_PER_RAD, - cam: { distance: 1e-13 }, - } as unknown as FrameView; - composeMock.mockClear(); - const renderer = makeRendererSpy(); - orbitTrailsPass.draw(PASS_STUB, makeNear0View(), ctx, { - ...makeState(renderer), - orbitTrailRows: [el], - } as unknown as EngineState); - - const [, { instances: staging, count }] = renderer.draw.mock.calls[0]!; - expect(count, id).toBe(1); - const { eccentricity, meanAnomalyRad } = propagateElements(el, simDays); - const eAnom = eccentricAnomalyFromMean(meanAnomalyRad, eccentricity); - const [cosE, sinE] = [Math.cos(eAnom), Math.sin(eAnom)]; - // f32's ulp at Saturn's 1.4e9 km semi-major axis is 128 km, so Saturn reads the - // f64 conic handed to composeOrbitConic; the small orbits read the packed basis. - const [, centre, major, minor] = composeMock.mock.calls[0]! as Vec3[]; - for (let axis = 0; axis < 3; axis++) { - const rel = (bodyMpc[axis]! - eye[axis]!) * kmPerMpc; - if (id === 'saturn') { - const onConic = centre![axis]! + major![axis]! * cosE + minor![axis]! * sinE; - expect(Math.abs((onConic - bodyMpc[axis]!) * kmPerMpc), id).toBeLessThan(1e-3); - } else { - const packed = - staging[34 + axis]! + staging[38 + axis]! * cosE + staging[42 + axis]! * sinE; - expect(Math.abs(packed - rel), id).toBeLessThan(0.1); - } - } - } - }); - it('stages no conic for the mesh bodies (whale, petunias) even when the Moon trail is emitted', () => { // Same pose as "rides a moon trail on its propagated parent" above — parking // the camera at Earth is what makes the Moon's tiny geocentric orbit survive From c239404f8e0e8cff2a0320023857903eca3d4de5 Mon Sep 17 00:00:00 2001 From: Alexander Rulkens Date: Mon, 5 Oct 2026 00:12:13 +0200 Subject: [PATCH 14/14] docs(plan): mark accurate-planet-positions complete DoD audit READY: full suite + typecheck green locally (stacked PR, no GitHub CI), 31/31 boxes, smoke passed, deletion audit applied (~85 LOC incl. ruled test drop), no new TODOs. --- ...-10-04-accurate-planet-positions.ledger.md | 51 ++++++++++++++ .../2026-10-04-accurate-planet-positions.md | 66 ++++++++++--------- ...-10-04-accurate-planet-positions-design.md | 0 3 files changed, 85 insertions(+), 32 deletions(-) create mode 100644 docs/superpowers/plans/completed/2026-10-04-accurate-planet-positions.ledger.md rename docs/superpowers/plans/{ => completed}/2026-10-04-accurate-planet-positions.md (87%) rename docs/superpowers/specs/{ => completed}/2026-10-04-accurate-planet-positions-design.md (100%) diff --git a/docs/superpowers/plans/completed/2026-10-04-accurate-planet-positions.ledger.md b/docs/superpowers/plans/completed/2026-10-04-accurate-planet-positions.ledger.md new file mode 100644 index 000000000..466de1929 --- /dev/null +++ b/docs/superpowers/plans/completed/2026-10-04-accurate-planet-positions.ledger.md @@ -0,0 +1,51 @@ +# SDD ledger — plan: docs/superpowers/plans/2026-10-04-accurate-planet-positions.md + +Asks (2026-10-04): parallelism = stack (feature starts right away on `accurate-planets` atop `accurate-planets-prep`); perf gate = SKIP. Lean protocol: one final review per PR; T5/T6 tagged review: yes. +Base: prep branch off origin/main d435719c0 (main moved from a600d661f; #827 touches no planned file). + +## Pre-flight scan +| Rows | Produces → consumes | Finding | +|---|---|---| +| T1 ↔ T6 | T1 test reads eye-relative km basis (floats 34..45); T6 on-trail test reuses it | consistent | +| T2 ↔ T6 | both regenerate bodyStatesJ2000 + golden traces + EARTH_TARGET | T6 re-does after T2; consistent (sequential) | +| T2 ↔ T5 | T5 fit must run AFTER T2's obliquity (residual in app frame) | stacked branch guarantees order | +| T3 ↔ T5 | `EphemerisCorrection`, `ephemerisCorrectionMpc` → T5 verifies with it | consistent | +| T4 ↔ T5 | `data/raw/horizons/planets/.csv` via rawDataPath('horizons.planets') | consistent | +| T5 ↔ T6 | `PLANET_EPHEMERIS_CORRECTIONS` keyed by planet id | consistent | +| T1 self | test fails today, passes after | ok | +| T2 self | no new test (constant) | ok per testing.md | +| T3–T6 self | tests match code | ok | +Dispatch A (Tasks 1–2, Sonnet) launched on accurate-planets-prep; draft PR #834; base c798ac56a +Task 1: complete (commits c798ac56a..2183ed6b7, final review pending) +Task 2: complete (commits 2183ed6b7..7edf35cb5, final review pending) +Ruling: Task 1 on-trail tolerance 0.1 km, not 1 m — the eye-relative basis is packed f32 (~30 m step at the Moon's 4e5 km); injected offset 1e6 km is 4 orders above — costs: a sub-100 m anchoring regression would pass. Same tolerance for Task 6's on-trail test. +Ruling: 18–20-digit keplerianEllipse comparisons left unloosened — they still pass, loosening would be churn — costs nothing. +Golden traces checked: same structure (26/26, 47 entries), only J2000 Earth coordinates rotated by the obliquity fix. +Prep pushed 7edf35cb5; CI watch on #834; final review (Opus) dispatched. Feature branch accurate-planets created at 7edf35cb5; Dispatch B (Tasks 3–5, Opus) launched. +Prep final review: 3 Minor (propagateElements.test.ts:93-94 garbled comment; orbitalElements.ts:4 header not re-wrapped; settleGoldenTrace.json minified by re-record). No Critical/Important. +Ruling: controller fixes the 3 Minors inline on accurate-planets-prep (<50 lines, lean protocol) AFTER Dispatch B finishes (worktree is on accurate-planets), then merges prep into accurate-planets — avoids switching branches under a live agent — costs: prep PR waits ~1 dispatch. +Prep CI RED (1/1426): cameraFraming.test.ts:76 GALACTIC_DISC_FORWARD decodes to yaw −1.4208018 vs −1.4208 @1e-6 — the exact-obliquity ecliptic frame shifts the decode ~2e-6 rad. +Ruling: relax yaw/pitch to toBeCloseTo(…, 4) with a one-line why (still catches a mistyped 4th decimal, 1e-4 > 5e-5); do NOT re-derive GALACTIC_DISC_FORWARD (a 1e-5 rad view change nobody sees) — costs: the pin is 50× looser. Folded into the controller's prep fix commit after Dispatch B. +Prep fix commit 309d53be8 (3 review Minors + cameraFraming 4-place ruling; settle recorder now writes 2-space JSON and trace re-recorded) pushed; CI re-watch running. +Task 3: complete (commits 7edf35cb5..428f92fdc, final review pending) +Task 4: complete (commits 428f92fdc..e79ff43f2, final review pending) +Task 5: complete (commits e79ff43f2..2f0c8f0d3, final review pending) — 525 terms, max verified 900 km (Earth), 36.2 kB text +Ruling (Dispatch B deviations accepted): golden-section frequency refine instead of parabolic (2 vs 21 terms on two-tone test); fit grid always includes the final row (Uranus failed 1,012 km at 2100 otherwise); generated file 36 kB text not ~17 kB (plan assumed f32 binary; text with 12-digit ω is the honest size) — costs: bundle +36 kB raw (~less gzipped). +Prep merged into accurate-planets (948758534); feature draft PR #835 (base accurate-planets-prep). Dispatch C (Task 6, Opus) launched. +Prep PR #834 CI GREEN at 309d53be8; review clean after fixes → ready for user smoke/merge ruling. +Task 6: complete (commits 948758534..b707c020b, final review pending) — vs Horizons fixture: Me 593, V 616, E 621 (vs 399), Ma 868, J 307, S 333, U 279, N 300 km; k=0 mutation → Earth 4,830 km, reflex test fails. +Ruling (accepted): BARYCENTRIC_REFLEX_BY_PRIMARY as 2nd export of data/bodies/barycentricPairs.ts (frameFilePurity bans the module-level map in deriveBodyStates) — costs: data file with two exports. Saturn on-trail check at 1 m on the f64 conic (packed f32 step 128 km at Saturn); Moon/Hubble 0.1 km on packed basis. HorizonsSeries type moved to tools/bodies/@types (noInlineTypes). +Feature final review: clean of Critical/Important; 6 Minors (unused maxErrKm; BARYCENTRIC_PAIRS export unused + test restates k; inline dot helper; golden-section double eval; lo=0 NaN probe; parser throw-case test). Ruling: fix all 6 in ONE fix dispatch after local CI finishes (stacked PR gets no GitHub CI — base ≠ main; local typecheck+lint+test run as the gate). +Local CI on b707c020b: typecheck+lint+test GREEN (1432 files / 14950 tests). Fix dispatch (6 Minors, Sonnet) launched. +Fix dispatch 8eea5fe69: 6 Minors fixed; fixer reverted the regenerated file (1e-10 rounding noise broke exact fixtures). +Ruling: controller regenerated corrections from the final tool + re-blessed bodyStatesJ2000 and both golden traces (60a10eec9) so the committed generated file equals tool output — costs: fixture churn of rounding noise only (same term counts, same max errors). +Local CI on 60a10eec9: 1429/1429 files, 14943 tests pass; 3 worker-start timeouts (load) re-run green (3 files, 7 tests). Both PRs review-clean. NEXT: user smoke (plan DoD) → /feature-done → merge on user's word (prep #834 first, then retarget #835 to main). + +## /feature-done IN PROGRESS (2026-10-04) — user smoke PASSED ("looks good") +DoD findings so far: tests+typecheck GREEN (local full run on 60a10eec9, no change since); checkboxes 0/31 ticked (tick all on READY); DoD section PRESENT; spec Ground preparation PRESENT; unexpected files (all explained): earthUniverseLoop.test.ts, settleGoldenTrace.test.ts (recorder), mergeHorizonsChunks(.test).ts, tools/@types/math/FitResult.d.ts, tools/bodies/@types/HorizonsSeries.d.ts, tools/parsers/@types/HorizonsVectorRow.d.ts, tools/utils/math/dot.ts; untouched planned: none; new TODOs: 0; comment density OK; test parity grew; smoke FOUND; deferred = plan "Out of scope" list (moons, Pluto pair row → backlog detail updated, Voyager → mission-trails spec next, runtime TDB, orbitReachByRegion margin). +IN-FLIGHT: deletion-audit agent (opus, GREENFIELD/--justify, read-only) → report at scratchpad `deletion-audit-accurate-planets.md`. ON RESULT: apply SAFE-NOW bin as its own deletion commit on `accurate-planets` (dispatch Sonnet if >2 files), run targeted tests, push; NEEDS-RULING items → ask user. Then READY → tick plan checkboxes; git mv plan → plans/completed/, spec → specs/completed/; copy this ledger → plans/completed/2026-10-04-accurate-planet-positions.ledger.md; backlog sweep (barycentric-pairs item STAYS, only its clause was updated); commit "docs(plan): mark accurate-planet-positions complete" (NO Co-Authored-By per user memory); push accurate-planets. +THEN: merge ONLY on user's "merge this PR": #834 first (squash), then retarget #835 base → main, rebase/merge main, GitHub CI, merge. After both land: Voyager mission-trails spec (memory project_mission_trails.md). +Dev server for this wt: localhost:5176 (bg, started this session) — leave running. +Deletion audit done (report: scratchpad deletion-audit-accurate-planets.md): safe-now ~27 LOC (FitResult.d.ts, import guard, keplerKm reuse, edge-continuity test, HorizonsSeries.d.ts) → Sonnet dispatch applying. NEEDS-RULING asked as dash ask 4UUQ (site test keep(rec) · real-data on-trail test delete(rec) · pair table keep(rec) · mergeHorizonsChunks keep(rec)). +Safe-now deletion commit 788c5f627 pushed (typecheck both tsconfigs + tests/tools tests/utils/orbit green). Waiting on ask 4UUQ. +DoD audit READY 2026-10-05: deletion audit ~27 LOC safe-now (788c5f627) + ruled on-trail test drop (3ff4005a5); 4UUQ: keep site test, keep pair table, keep mergeHorizonsChunks. Plan/spec moved to completed/. diff --git a/docs/superpowers/plans/2026-10-04-accurate-planet-positions.md b/docs/superpowers/plans/completed/2026-10-04-accurate-planet-positions.md similarity index 87% rename from docs/superpowers/plans/2026-10-04-accurate-planet-positions.md rename to docs/superpowers/plans/completed/2026-10-04-accurate-planet-positions.md index 1fd853f5c..5978c2820 100644 --- a/docs/superpowers/plans/2026-10-04-accurate-planet-positions.md +++ b/docs/superpowers/plans/completed/2026-10-04-accurate-planet-positions.md @@ -1,7 +1,7 @@ # Accurate planet positions — implementation plan > **For agentic workers:** REQUIRED SUB-SKILL: use superpowers:subagent-driven-development under the -> lean protocol in `docs/superpowers/conventions/sdd-execution.md`. Steps use checkbox (`- [ ]`) syntax. +> lean protocol in `docs/superpowers/conventions/sdd-execution.md`. Steps use checkbox (`- [x]`) syntax. **Goal:** The eight planets match JPL DE (Horizons) to ≤ 1,000 km over 1900–2100, Earth wobbles about the Earth–Moon barycentre, and every orbit trail stays through its body. @@ -14,7 +14,9 @@ about either. **Tech stack:** TypeScript, Vitest, tsx tools, the JPL Horizons API. -**Spec:** `docs/superpowers/specs/2026-10-04-accurate-planet-positions-design.md` +**Spec:** `docs/superpowers/specs/completed/2026-10-04-accurate-planet-positions-design.md` + +**Completed 2026-10-05** (#834 prep, #835 feature). Deviations and rulings: see the archived ledger `2026-10-04-accurate-planet-positions.ledger.md`. The real-data on-trail test (Task 6) was dropped by the deletion audit (user ruling): the snapshot-offset test covers the mechanism. ## Global constraints @@ -70,19 +72,19 @@ about either. The body is now on its trail by construction: any later change to the snapshot position moves the trail with it. Keep `meanAnomalyRad` as the fade anchor, unchanged. -- [ ] Add the test `trail follows a snapshot position that differs from raw Kepler`. Stub the states +- [x] Add the test `trail follows a snapshot position that differs from raw Kepler`. Stub the states map so one row's `positionMpc` is the Kepler position plus a 1e6 km vector (converted with `SCALE_UNITS.KM_TO_MPC`). Read the packed eye-relative km basis (floats 34..45, written by `eyeRelativeOrbitBasisKm`): the body point `C + A·cosE + B·sinE` at the row's propagated E must equal the stubbed position, eye-relative in km, to ≤ 1 m. Use km tolerances throughout: 1e-15 Mpc is 30,000 km. -- [ ] Run that test and confirm it fails against the current code. -- [ ] Implement the after-line. Rewrite the `orbitalElements.ts` header sentence about "a body sitting +- [x] Run that test and confirm it fails against the current code. +- [x] Implement the after-line. Rewrite the `orbitalElements.ts` header sentence about "a body sitting on its own trail is structural" so it names the snapshot anchoring. -- [ ] The comparisons against `keplerianEllipse` (`orbitTrailsPass.test.ts:345-378`, `:511-573`) were +- [x] The comparisons against `keplerianEllipse` (`orbitTrailsPass.test.ts:345-378`, `:511-573`) were exact to 18–20 digits. Loosen them to `toBeCloseTo(…, 15)`, or the relative tolerance f64 can hold for `(a + b) − b`, and give the reason in one line. -- [ ] `npm test -- orbitTrailsPass`, then commit "Orbit trails anchor on the snapshot body position". +- [x] `npm test -- orbitTrailsPass`, then commit "Orbit trails anchor on the snapshot body position". ### Task 2: Exact obliquity (P2) @@ -103,17 +105,17 @@ Set `OBLIQUITY_DEG = 23.4392911` and say "IAU J2000 mean obliquity" in its comme say `23.44°` become `23.439°`. Test literals use the exact value; in `orientationFrames.test.ts:54`, 66.56° becomes `90 − 23.4392911`. No new test: this is a constant, and the existing frame tests pin it. -- [ ] Change the constant, comments and test literals. -- [ ] Regenerate `bodyStatesJ2000.json` with a one-off tsx snippet that is not committed. It +- [x] Change the constant, comments and test literals. +- [x] Regenerate `bodyStatesJ2000.json` with a one-off tsx snippet that is not committed. It serialises `deriveBodyStates(2451545.0)` for every `ORBITAL_ELEMENTS` id in the fixture's existing shape (`positionMpc`, `orientation`, `meanAnomalyRad`), leaves anchors out, and keeps the key order. Diff it: only rotated values may change. -- [ ] Re-record the camera traces with `SETTLE_GOLDEN_RECORD=1` and `DRIVER_GOLDEN_RECORD=1` +- [x] Re-record the camera traces with `SETTLE_GOLDEN_RECORD=1` and `DRIVER_GOLDEN_RECORD=1` (`npm test -- settleGoldenTrace driverGoldenTrace`). The spec rules this. Then run once more without the flags. -- [ ] Update `EARTH_TARGET` in `perfScenarios.ts:52` to the new J2000 Earth position, taken from the +- [x] Update `EARTH_TARGET` in `perfScenarios.ts:52` to the new J2000 Earth position, taken from the regenerated fixture. -- [ ] `npm run typecheck:fast`, then `npm test -- orbitPlaneFrames orientationFrames propagateElements earthTerminator deriveBodyStates`. +- [x] `npm run typecheck:fast`, then `npm test -- orbitPlaneFrames orientationFrames propagateElements earthTerminator deriveBodyStates`. Commit "Exact J2000 obliquity". --- @@ -141,12 +143,12 @@ export function ephemerisCorrectionMpc(c: EphemerisCorrection, simDays: number): Sum in f64 km, then multiply by `SCALE_UNITS.KM_TO_MPC`. -- [ ] Test `evaluates polynomial and terms at a known instant`: a hand-built two-term correction, +- [x] Test `evaluates polynomial and terms at a known instant`: a hand-built two-term correction, checked against values worked out by hand. -- [ ] Test `holds the edge value outside the span`: `simDays` 100 years before `startJd` returns +- [x] Test `holds the edge value outside the span`: `simDays` 100 years before `startJd` returns exactly (`toEqual`) the value at `startJd`, and likewise at `endJd`. -- [ ] Test `is continuous across both span edges`: evaluations at edge ± 1e-6 day differ by < 1 m. -- [ ] Implement, `npm test -- ephemerisCorrectionMpc`, then commit "Ephemeris correction evaluator". +- [x] Test `is continuous across both span edges`: evaluations at edge ± 1e-6 day differ by < 1 m. +- [x] Implement, `npm test -- ephemerisCorrectionMpc`, then commit "Ephemeris correction evaluator". ### Task 4: Horizons planet fetcher @@ -172,14 +174,14 @@ The output goes to `data/raw/horizons/planets/.csv` with the columns `jd,x written through `rawDataPath('horizons.planets')`. Chunks are concatenated and a row whose `jd` is ≤ the previous row's is dropped (Review focus 5). -- [ ] Test `parses the SOE/EOE block of a Horizons vectors result`: use a trimmed real response +- [x] Test `parses the SOE/EOE block of a Horizons vectors result`: use a trimmed real response pasted as a string literal, three rows. -- [ ] Test `concatenating two chunks drops the shared boundary row once`, through a small exported +- [x] Test `concatenating two chunks drops the shared boundary row once`, through a small exported merge step, or by asserting the fetcher's `mergeChunks` if you extract it. Keep it one-symbol-per-file if extracted. -- [ ] Implement the parser and the fetcher, which writes all eight CSVs. Announce the download size +- [x] Implement the parser and the fetcher, which writes all eight CSVs. Announce the download size before running it: 8 × ~3 MB. -- [ ] Run `npm run fetch-horizons-planets`. Commit "Horizons planet fetcher", without the gitignored +- [x] Run `npm run fetch-horizons-planets`. Commit "Horizons planet fetcher", without the gitignored CSVs. ### Task 5: Fit tool and generated corrections · `review: yes` @@ -222,13 +224,13 @@ export const PLANET_EPHEMERIS_CORRECTIONS: Readonly