Take the orbits at TDB as the spins already were, so a locked moon faces the planet it is drawn round
Every element set here runs on TDB: Standish's T_eph, the SSD satellite and SBDB epochs, the IAU's d and T. bodyOrientation already took the clock's UTC to TDB, but SystemOrbitsRenderer.update and the body page's heliocentricPosition fed the UTC date straight to meanElementsAt, so in one frame each body's place was 69.184 s behind its spin. That is n x 69 s of orbit: Phobos 0.90 degrees, Mimas 0.31, Deimos 0.23, Enceladus 0.21, Miranda 0.20, Io 0.16, Tethys 0.15, Europa 0.08, the Moon 0.011. 48319c3's table measured the app at a UTC date against Horizons at the same number read as TDB, which hid it, and its "nothing for anything else" was wrong: Io's 0.16 is four to five times Io's worst model error there (0.035). tdbFromUtc, in constants.ts, is now the one conversion, and positions and spins both go through it. In the running app, clock pinned to 2025-06-01 12:00 UTC, Io's face towards Jupiter is at 0.024 E, latitude -0.009, where Horizons (observer quantity 14 from Jupiter's centre) has 0.036 E and -0.003: 0.012 degrees apart, where it was 0.175. The renderer spec checks that point, and now hands its frozen Horizons vectors, which are TDB, to update() as the UTC dates that name them, 69.184 s earlier; the same frozen rows fed at the UTC date fail for Io and Europa. A body-page test checks that the Sun lights the point it stands over at the same TDB instant the body is turned for. Controls: taking the renderer's orbits at the clock's UTC fails "faces jupiter and the Sun with the points Horizons gives on io"; taking the page's Sun there fails "takes the Sun where it stands at the same TDB instant". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import { planetTexture } from '../../shared/rendering/procedural-planet-texture'
|
||||
import { bodyTexturePath, loadCachedTexture, saturnRing } from '../../shared/rendering/texture-catalog';
|
||||
import { isPropagatableOrbit, keplerRates, meanElementsAt, orbitEllipsePoints, positionAtEpoch, resolveGravitationalParameter, resolveOrbitalElements } from '../../shared/astro/kepler';
|
||||
import { CartesianCoordinates, OBLIQUITY_J2000_DEG } from '../../shared/astro/coordinates';
|
||||
import { tdbFromUtc } from '../../shared/astro/constants';
|
||||
import { BodyRecord, MeanElementRates, OrbitalElements, RotationalElements } from '../../shared/models/body.model';
|
||||
import { bodyOrientation, poleFrame } from '../../shared/rendering/body-orientation';
|
||||
import { bodyMarkerRadiusAu, systemGridRingsAu } from './system-framing';
|
||||
@@ -466,10 +467,14 @@ export class SystemOrbitsRenderer {
|
||||
this.object.add(starLight());
|
||||
}
|
||||
|
||||
/** Recomputes every marker's position for the given Julian date. Call once per tick. */
|
||||
/**
|
||||
* Recomputes every marker's position for the given Julian date, UTC as the map's clock gives it:
|
||||
* the orbits are taken at its TDB, as the spins are. Call once per tick.
|
||||
*/
|
||||
update(epochJd: number): void {
|
||||
const jdTdb = tdbFromUtc(epochJd);
|
||||
for (const body of this.topLevelBodies) {
|
||||
const current = meanElementsAt(body.elements, body.rates, epochJd);
|
||||
const current = meanElementsAt(body.elements, body.rates, jdTdb);
|
||||
const orbital = positionAtEpoch(current);
|
||||
body.position.set(orbital.x, orbital.y, orbital.z).applyQuaternion(body.frame);
|
||||
body.marker.position.copy(body.position);
|
||||
@@ -477,7 +482,7 @@ export class SystemOrbitsRenderer {
|
||||
if (body.rotationalElements) {
|
||||
bodyOrientation(body.rotationalElements, epochJd, body.marker.quaternion);
|
||||
} else if (body.rotationPeriodHours) {
|
||||
body.marker.quaternion.copy(spinFor(current, body.frame, body.rotationPeriodHours, epochJd - body.elements.epochJd));
|
||||
body.marker.quaternion.copy(spinFor(current, body.frame, body.rotationPeriodHours, jdTdb - body.elements.epochJd));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,7 +492,7 @@ export class SystemOrbitsRenderer {
|
||||
continue;
|
||||
}
|
||||
moon.pivot.position.copy(parent.position);
|
||||
const current = meanElementsAt(moon.elements, moon.rates, epochJd);
|
||||
const current = meanElementsAt(moon.elements, moon.rates, jdTdb);
|
||||
const orbital = positionAtEpoch(current);
|
||||
moon.marker.position.set(orbital.x, orbital.y, orbital.z).applyQuaternion(moon.frame);
|
||||
orientOrbit(moon.orbitLine.quaternion, current, moon.frame);
|
||||
@@ -502,7 +507,7 @@ export class SystemOrbitsRenderer {
|
||||
if (moon.rotationalElements) {
|
||||
bodyOrientation(moon.rotationalElements, epochJd, moon.marker.quaternion);
|
||||
} else if (moon.rotationPeriodHours) {
|
||||
moon.marker.quaternion.copy(spinFor(current, moon.frame, moon.rotationPeriodHours, epochJd - moon.elements.epochJd));
|
||||
moon.marker.quaternion.copy(spinFor(current, moon.frame, moon.rotationPeriodHours, jdTdb - moon.elements.epochJd));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user