Turn each locked moon at its orbit's rate and Iapetus's pole round its orbit, so they face their planets at every date the clock reaches
The IAU gives a locked moon's W the mean motion of whichever orbit its authors had, and JPL's table has another. Near the present the difference is nothing; over the clock's AD 1 to 3000 it turned Proteus's far side to Neptune at AD 1 (146 degrees), Mimas 52 degrees from Saturn and Miranda 23. Iapetus was worse for another reason: its IAU pole is a straight line, 3.9 degrees a century in right ascension, through its orbit normal's 3 439-year circle round the Laplace pole, which by AD 1 has run past the celestial pole (Dec 97.9), 11 degrees off the orbit, with the face 87 degrees from Saturn. Mimas and Iapetus carry mission maps, so a wrong hemisphere was drawn facing Saturn. The ETL's lock check sampled only 1950-2100, so none of it failed. tools/etl/lib/locked-spin.ts, lockedToOrbit, called for every locked moon: - W's rate becomes the orbit's own mean motion, its constant moved so W is unchanged on 2025-01-01; the pole and every periodic term stay the IAU's. A W with a quadratic is left (Phobos's orbit already takes it; the Moon's is its tidal slowing, 0.75 degrees at AD 1). The kernel's rate must be within 1e-5 of the orbit's first (at most 3.4e-6, Iapetus). - Iapetus (poleFollowsOrbit): the pole follows its orbit normal, as a moon in a Cassini state does, in the IAU's own form: sines of the node's angle and four harmonics on right ascension, cosines on declination, fitted to the normal's circle and pinned to the IAU pole at the present; W takes sines of the same angles, fitted to hold the face where it is today. build.ts samples the lock over AD 1 to 3000 (8 114 dates, every 135 days) instead of 1950-2100, and subPlanetLongitudeDeg moved to the lib, shared by both. Measured on the real catalogue: at most 5.36 degrees (Titan) but the Moon 7.62 (its eccentricity, and W's quadratic at AD 1: a new named ceiling of 8), Mimas 8.94 (ceiling 11 -> 9.5) and Iapetus 15.95 (19 -> 16.5, 9.4 of it its row's lag); Proteus's own ceiling of 9 is gone, at 2.66. Iapetus's axis stays within 0.74 degrees of its orbit normal (11.06 before) and its pole is the IAU's at the present to 1e-4 degrees. Live on :4301, the longitude facing the planet at AD 1 / 1000 / 2025 / 2999: Proteus 2.6 / 2.6 / 2.6 / 2.6 (was -146.5 / -72.9 / 2.6 / 74.5), Iapetus -15.9 / -15.4 / -15.3 / -9.3 (-87.0 / -50.9 / -15.3 / 22.8), Mimas 4.1 / 6.8 / 5.7 / 8.4 (48.6 / 29.3 / 5.7 / -13.0), Miranda -0.1 / 2.2 / 0.0 / 1.4 (-23.4 / -9.6 / 0.0 / 12.6); the present is unchanged. The renderer spec now takes the rotational elements from bodies.json too, so no hand copy is left, and a new test turns Proteus, Miranda, Mimas and Iapetus to their planets at AD 1 and AD 3000. Guarded mutants, each run through the solar ETL and then the full suite on what it wrote: lockedToOrbit bypassed (validator: Mimas 52.30, ceiling 9.5; the new test fails), Iapetus on the IAU's straight pole (98.48), and its pole round the orbit without W's terms (73.13); each fails the new test and only it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ import { DEFAULT_EPOCH_JD, GM_SUN_AU3_PER_DAY2, ttMinusUtSeconds } from '../../s
|
||||
import { keplerRates } from '../../shared/astro/kepler';
|
||||
import { eclipticToEquatorial, laplacePlaneToEquatorial, OBLIQUITY_J2000_DEG } from '../../shared/astro/coordinates';
|
||||
import { orientationAt } from '../../shared/astro/rotational-elements';
|
||||
import { BodyRecord, RotationalElements } from '../../shared/models/body.model';
|
||||
import { BodyRecord } from '../../shared/models/body.model';
|
||||
import { ExoplanetRecord } from '../../shared/models/exoplanet.model';
|
||||
import { SystemOrbitsRenderer } from './system-orbits-renderer';
|
||||
import { bodyTexturePath, loadCachedTexture } from '../../shared/rendering/texture-catalog';
|
||||
@@ -593,29 +593,18 @@ describe('exoplanet size without a measured radius', () => {
|
||||
});
|
||||
|
||||
describe('solar-system bodies against Horizons', () => {
|
||||
// The records the app ships, read from bodies.json, and Horizons' own positions for them (ICRF,
|
||||
// AU; heliocentric for the planets, planet-centred for the moons) at dates across 1950-2100, so the
|
||||
// whole path — the ETL's reading of the mean elements, their rates, the Laplace planes and the
|
||||
// scene's frame — is checked against JPL's ephemeris rather than against itself. A hand copy of the
|
||||
// records stood here, and an ETL that dropped Standish's a, e and i rates or Io's and Europa's
|
||||
// backward periapses passed the whole suite on the data it wrote. Horizons' dates are TDB and the
|
||||
// renderer's are the clock's UT, so each is handed over TT - UT earlier: 69.184 s today, 29 in 1950.
|
||||
// The records the app ships, read from bodies.json with their IAU rotational elements, and
|
||||
// Horizons' own positions for them (ICRF, AU; heliocentric for the planets, planet-centred for the
|
||||
// moons) at dates across 1950-2100, so the whole path — the ETL's reading of the mean elements,
|
||||
// their rates, the Laplace planes and the scene's frame — is checked against JPL's ephemeris rather
|
||||
// than against itself. A hand copy of the records stood here, and an ETL that dropped Standish's a,
|
||||
// e and i rates or Io's and Europa's backward periapses passed the whole suite on the data it
|
||||
// wrote. Horizons' dates are TDB and the renderer's are the clock's UT, so each is handed over
|
||||
// TT - UT earlier: 69.184 s today, 29 in 1950.
|
||||
const SHIPPED: BodyRecord[] = JSON.parse(readFileSync(`${process.cwd()}/src/assets/data/bodies.json`, 'utf8'));
|
||||
// Mimas and Phobos among them for the terms of their IAU W that are motion along the orbit: the
|
||||
// Mimas-Tethys libration and Phobos's tidal acceleration (see `orbitalTermsOfPrimeMeridian`).
|
||||
const IDS = ['earth', 'jupiter', 'saturn', 'neptune', 'pluto', 'moon', 'io', 'europa', 'titan', 'triton', 'uranus', 'titania', 'charon', 'venus', 'mars', 'mimas', 'phobos'];
|
||||
// The IAU WGCCRE 2015 rotational elements bodies.json carries for them, from pck00011.tpc.
|
||||
const ROTATION: Record<string, RotationalElements> = {
|
||||
venus: {poleRaDeg: [272.76, 0, 0], poleDecDeg: [67.16, 0, 0], primeMeridianDeg: [160.2, -1.4813688, 0]},
|
||||
earth: {poleRaDeg: [0, -0.641, 0], poleDecDeg: [90, -0.557, 0], primeMeridianDeg: [190.147, 360.9856235, 0]},
|
||||
mars: {poleRaDeg: [317.269202, -0.10927547, 0], poleDecDeg: [54.432516, -0.05827105, 0], primeMeridianDeg: [176.049863, 350.891982443297, 0], terms: [{angleDeg: [79.398797, 0.5042615, 0], ra: 0.419057, dec: 0, pm: 0}, {angleDeg: [166.325722, 0.5042615, 0], ra: 0, dec: 1.591274, pm: 0}, {angleDeg: [95.391654, 0.5042615, 0], ra: 0, dec: 0, pm: 0.584542}]},
|
||||
jupiter: {poleRaDeg: [268.056595, -0.006499, 0], poleDecDeg: [64.495303, 0.002413, 0], primeMeridianDeg: [284.95, 870.536, 0]},
|
||||
io: {poleRaDeg: [268.05, -0.009, 0], poleDecDeg: [64.5, 0.003, 0], primeMeridianDeg: [200.39, 203.4889538, 0], terms: [{angleDeg: [283.9, 4850.7], ra: 0.094, dec: 0.04, pm: -0.085}, {angleDeg: [355.8, 1191.3], ra: 0.024, dec: 0.011, pm: -0.022}]},
|
||||
saturn: {poleRaDeg: [40.589, -0.036, 0], poleDecDeg: [83.537, -0.004, 0], primeMeridianDeg: [38.9, 810.7939024, 0]},
|
||||
uranus: {poleRaDeg: [257.311, 0, 0], poleDecDeg: [-15.175, 0, 0], primeMeridianDeg: [203.81, -501.1600928, 0]},
|
||||
pluto: {poleRaDeg: [132.993, 0, 0], poleDecDeg: [-6.163, 0, 0], primeMeridianDeg: [302.695, 56.3625225, 0]},
|
||||
moon: {poleRaDeg: [269.9949, 0.0031, 0], poleDecDeg: [66.5392, 0.013, 0], primeMeridianDeg: [38.3213, 13.17635815, -1.4e-12], terms: [{angleDeg: [125.045, -1935.5364525], ra: -3.8787, dec: 1.5419, pm: 3.561}, {angleDeg: [250.089, -3871.072905], ra: -0.1204, dec: 0.0239, pm: 0.1208}, {angleDeg: [260.008, 475263.3328725], ra: 0.07, dec: -0.0278, pm: -0.0642}, {angleDeg: [176.625, 487269.629985], ra: -0.0172, dec: 0.0068, pm: 0.0158}, {angleDeg: [357.529, 35999.0509575], ra: 0, dec: 0, pm: 0.0252}]},
|
||||
};
|
||||
// Each ceiling sits just above what these elements measure on that date: Earth 0.003 degrees,
|
||||
// Jupiter 0.063, Saturn 0.164, Pluto 0.054, the Moon 0.72 (no mean ellipse has its evection or
|
||||
// variation), Io 0.021, Europa 0.036, Titan 0.014, Triton 0.137, Titania 0.62 (against Uranus's
|
||||
@@ -644,8 +633,8 @@ describe('solar-system bodies against Horizons', () => {
|
||||
];
|
||||
|
||||
function record(id: string): BodyRecord {
|
||||
const { kind, orbit, rates, laplacePole, parentBodyId, massRatio } = SHIPPED.find((body) => body.id === id)!;
|
||||
return { id, systemStarId: 0, name: id, radiusKm: 1000, orbitSource: 'test', kind, orbit, rates, laplacePole, parentBodyId, massRatio, rotationalElements: ROTATION[id] };
|
||||
const { kind, orbit, rates, laplacePole, parentBodyId, massRatio, rotationalElements } = SHIPPED.find((body) => body.id === id)!;
|
||||
return { id, systemStarId: 0, name: id, radiusKm: 1000, orbitSource: 'test', kind, orbit, rates, laplacePole, parentBodyId, massRatio, rotationalElements };
|
||||
}
|
||||
|
||||
const renderer = new SystemOrbitsRenderer(IDS.map(record), []);
|
||||
@@ -796,7 +785,7 @@ describe('solar-system bodies against Horizons', () => {
|
||||
|
||||
/** Where the IAU puts a body's prime meridian at a TDB date, in the scene. */
|
||||
function iauPrimeMeridian(id: string, jdTdb: number): THREE.Vector3 {
|
||||
const { poleRaDeg, poleDecDeg, primeMeridianDeg } = orientationAt(ROTATION[id], jdTdb);
|
||||
const { poleRaDeg, poleDecDeg, primeMeridianDeg } = orientationAt(SHIPPED.find((body) => body.id === id)!.rotationalElements!, jdTdb);
|
||||
const w = (primeMeridianDeg * Math.PI) / 180;
|
||||
const meridian = laplacePlaneToEquatorial({ x: Math.cos(w), y: Math.sin(w), z: 0 }, { raDeg: poleRaDeg, decDeg: poleDecDeg });
|
||||
return new THREE.Vector3(meridian.x, meridian.y, meridian.z);
|
||||
@@ -907,3 +896,32 @@ describe('solar-system bodies against Horizons', () => {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('locked moons across the clock’s window', () => {
|
||||
// As shipped, pole, W and all: the IAU gives each a W fitted near the present, and its rate is
|
||||
// not quite its orbit's, nor Iapetus's pole a line for twenty centuries.
|
||||
const shipped: BodyRecord[] = JSON.parse(readFileSync(`${process.cwd()}/src/assets/data/bodies.json`, 'utf8'));
|
||||
const renderer = new SystemOrbitsRenderer(shipped.filter((body) => ['saturn', 'uranus', 'neptune', 'mimas', 'iapetus', 'miranda', 'proteus'].includes(body.id)), []);
|
||||
|
||||
/** East longitude, on its map, of the point on a moon's drawn sphere that faces its planet. */
|
||||
function facingPlanet(id: string): number {
|
||||
const moon = renderer.members.find((member) => member.id === id)!.marker;
|
||||
// Its position is from the planet, which is its pivot; SphereGeometry wraps u = atan2(z, -x) / 2 pi.
|
||||
const toPlanet = moon.position.clone().negate().applyQuaternion(moon.quaternion.clone().invert());
|
||||
const u = Math.atan2(toPlanet.z, -toPlanet.x) / (2 * Math.PI);
|
||||
return ((((u - 0.5) * 360) % 360) + 540) % 360 - 180;
|
||||
}
|
||||
|
||||
it('keeps Proteus, Miranda, Mimas and Iapetus facing their planets at AD 1 and AD 3000', () => {
|
||||
// Measured: Proteus 2.6 degrees at most over AD 1-3000, Miranda 2.8, Mimas 8.9, Iapetus 16 (9.4
|
||||
// of it the lag of the row its orbit is drawn from). On the IAU's own W and Iapetus's straight
|
||||
// pole they were 146, 23, 49 and 87 degrees at AD 1.
|
||||
for (const jd of [1721425.5, 2816787.4]) {
|
||||
renderer.update(jd);
|
||||
expect(Math.abs(facingPlanet('proteus'))).toBeLessThan(3);
|
||||
expect(Math.abs(facingPlanet('miranda'))).toBeLessThan(3);
|
||||
expect(Math.abs(facingPlanet('mimas'))).toBeLessThan(9.5);
|
||||
expect(Math.abs(facingPlanet('iapetus'))).toBeLessThan(16.5);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -95,10 +95,12 @@ export interface BodyRecord {
|
||||
obliquityDeg?: number;
|
||||
/**
|
||||
* Where the body's pole points and which way its prime meridian faces at any date, from the IAU
|
||||
* WGCCRE 2015 report (Archinal et al. 2018) as NAIF's `pck00011.tpc` carries it. Where present
|
||||
* it alone sets how the body is drawn, and the ETL checks the period and obliquity above against
|
||||
* it. Absent where the report gives none: Hyperion tumbles, and Nereid, Eris, Haumea and
|
||||
* Makemake have no model.
|
||||
* WGCCRE 2015 report (Archinal et al. 2018) as NAIF's `pck00011.tpc` carries it, but that a locked
|
||||
* moon's W turns at its drawn orbit's rate and Iapetus's pole goes round with its orbit's, so they
|
||||
* keep their faces to their planets over the clock's AD 1 to 3000 (see `lockedToOrbit` in the
|
||||
* ETL). Where present it alone sets how the body is drawn, and the ETL checks the period and
|
||||
* obliquity above against it. Absent where the report gives none: Hyperion tumbles, and Nereid,
|
||||
* Eris, Haumea and Makemake have no model.
|
||||
*/
|
||||
rotationalElements?: RotationalElements;
|
||||
}
|
||||
|
||||
+91
-44
@@ -809,8 +809,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
79.39932954,
|
||||
285.16188899,
|
||||
79.49055322521608,
|
||||
285.161879,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -906,8 +906,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
200.39,
|
||||
203.4889538,
|
||||
200.34890824984421,
|
||||
203.4889583,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -971,8 +971,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
36.022,
|
||||
101.3747235,
|
||||
36.015607949990184,
|
||||
101.3747242,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -1045,8 +1045,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
44.064,
|
||||
50.3176081,
|
||||
44.07221835003116,
|
||||
50.3176072,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -1119,8 +1119,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
259.51,
|
||||
21.5710715,
|
||||
259.498129049991,
|
||||
21.5710728,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -1199,8 +1199,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
333.46,
|
||||
381.994555,
|
||||
334.00971630006546,
|
||||
381.9944948,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -1264,8 +1264,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
6.32,
|
||||
262.7318996,
|
||||
6.336436700062315,
|
||||
262.7318978,
|
||||
0
|
||||
]
|
||||
}
|
||||
@@ -1315,8 +1315,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
8.95,
|
||||
190.6979085,
|
||||
8.928084400003424,
|
||||
190.6979109,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -1380,8 +1380,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
357.6,
|
||||
131.5349316,
|
||||
357.60821835003117,
|
||||
131.5349307,
|
||||
0
|
||||
]
|
||||
}
|
||||
@@ -1425,8 +1425,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
235.16,
|
||||
79.6900478,
|
||||
235.1773498500081,
|
||||
79.6900459,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -1481,8 +1481,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
186.5855,
|
||||
22.5769768,
|
||||
186.5964577999983,
|
||||
22.5769756,
|
||||
0
|
||||
]
|
||||
}
|
||||
@@ -1544,19 +1544,66 @@
|
||||
"rotationPeriodHours": 1903.9469348834284,
|
||||
"rotationalElements": {
|
||||
"poleRaDeg": [
|
||||
318.16,
|
||||
-3.949,
|
||||
283.4205056238858,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poleDecDeg": [
|
||||
75.03,
|
||||
-1.143,
|
||||
77.15116551386498,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
355.2,
|
||||
4.5379572,
|
||||
388.27970766227594,
|
||||
4.5379416,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
{
|
||||
"angleDeg": [
|
||||
261.10499999998126,
|
||||
-10.46898128087986
|
||||
],
|
||||
"ra": -42.60490881707297,
|
||||
"dec": 7.692796952777751,
|
||||
"pm": 41.78612687698857
|
||||
},
|
||||
{
|
||||
"angleDeg": [
|
||||
522.2099999999625,
|
||||
-20.93796256175972
|
||||
],
|
||||
"ra": -15.535995393995726,
|
||||
"dec": 1.2845933786338362,
|
||||
"pm": 15.53892376069157
|
||||
},
|
||||
{
|
||||
"angleDeg": [
|
||||
783.3149999999438,
|
||||
-31.406943842639578
|
||||
],
|
||||
"ra": -7.628387941010053,
|
||||
"dec": 0.45238994454766307,
|
||||
"pm": 7.62837456853055
|
||||
},
|
||||
{
|
||||
"angleDeg": [
|
||||
1044.419999999925,
|
||||
-41.87592512351944
|
||||
],
|
||||
"ra": -4.2134447101107595,
|
||||
"dec": 0.20197385694504744,
|
||||
"pm": 4.213441809239813
|
||||
},
|
||||
{
|
||||
"angleDeg": [
|
||||
1305.5249999999064,
|
||||
-52.344906404399296
|
||||
],
|
||||
"ra": -2.482395388436937,
|
||||
"dec": 0.10190190070186696,
|
||||
"pm": 2.4609277085117798
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -1644,8 +1691,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
30.7,
|
||||
-254.6906892,
|
||||
30.41144460000184,
|
||||
-254.6906576,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -1727,8 +1774,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
156.22,
|
||||
-142.8356681,
|
||||
156.12685870007942,
|
||||
-142.8356579,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -1792,8 +1839,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
108.05,
|
||||
-86.8688923,
|
||||
108.00982140004953,
|
||||
-86.8688879,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -1857,8 +1904,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
77.74,
|
||||
-41.3514316,
|
||||
77.67607950003162,
|
||||
-41.3514246,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -1913,8 +1960,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
6.77,
|
||||
-26.7394932,
|
||||
6.729821400017093,
|
||||
-26.7394888,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -1969,8 +2016,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
296.53,
|
||||
-61.2572637,
|
||||
296.5309131499458,
|
||||
-61.2572638,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -2124,8 +2171,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
93.38,
|
||||
320.7654228,
|
||||
91.53817645008237,
|
||||
320.7656245,
|
||||
0
|
||||
],
|
||||
"terms": [
|
||||
@@ -2190,8 +2237,8 @@
|
||||
0
|
||||
],
|
||||
"primeMeridianDeg": [
|
||||
122.695,
|
||||
56.3625225,
|
||||
122.70869724996541,
|
||||
56.362521,
|
||||
0
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user