Draw four moons' nodes at JPL's current rates, which Horizons and the IAU's poles agree with
The archived satellite table the moons are read from gives older node
periods than JPL's current one for Miranda (17.727 years against URA182's
17.787), Ganymede (132.654 against 137.812), Callisto (338.82 against
577.264) and Titan (704.60 against 687.370), and 74ea1d6 turned the IAU's
poles after those older rates, taking them for the right ones. They were
not: fitted to Horizons' osculating elements on Uranus's equator over
1601-2399, Miranda's node turns 2023.97 degrees a century (rms 0.04),
against the current table's 2023.95, the IAU's U11 2024.22 and the row's
2030.80. That commit moved Miranda's axis from 0.38 to 2.36 degrees of
Horizons' orbit normal. Mimas's node, 0.986 years in both tables, now takes
the IAU's S3, 36505.5 degrees a century, 1.2 from a Horizons fit where
the row's is 4.5.
The ETL spec carries the node period (nodePeriodYears), and the periapsis
keeps the row's longitude rate: the row gives the longitude's rate (Callisto
68.7 degrees a century, the current table 67.2), so the argument takes up
the change. On the row's argument Callisto's periapsis moved 44 degrees by
2100 and it strayed 0.71 degrees from Horizons over 1950-2100.
Worst angle from Horizons' osculating orbit normal (ICRF), HEAD then now:
- Miranda, 1601-2399: drawn orbit 2.11 -> 0.12, axis 2.36 -> 0.34.
- Mimas, 1750-2249: orbit 0.33 -> 0.11, axis 0.65 -> 0.40.
- Ganymede, 1600-2199: orbit 0.17 -> 0.11, axis 0.13 -> 0.01.
- Callisto, 1600-2199: orbit 0.53 -> 0.20, axis 0.02 -> 0.03.
- Titan, 1750-2249: orbit 0.048 -> 0.052.
Against the 1950-2100 Horizons tracks the ETL checks: Callisto 0.19 -> 0.08,
Miranda 1.73 -> 1.62, Ganymede 0.30 -> 0.29, Mimas 7.43 -> 7.42, Titan 0.06.
lockedToOrbit now barely moves the IAU's node terms: Miranda's U11 to
-2023.95, Ganymede's J5 to 261.23 (262.1), Mimas's S3 not at all, and
Callisto's J6, 3.1 per cent from its new node rate, to 62.36 (64.3), which
takes Callisto's axis from 0.56 to 0.22 degrees of its drawn orbit.
With the drawn rates right, leaving every node term at the IAU's rate no
longer failed the ETL (Mimas used to), nor did a 1 per cent tolerance, the
node's angle without its harmonics, or the older node periods. The axis
ceiling is now 0.25 degrees for Europa (0.13 measured), Ganymede (0.16),
Callisto (0.22), Rhea (0.17), Miranda (0.23) and Triton (0.15), and
Callisto's track ceiling 0.15 (0.08). Guarded mutants, each through the
solar ETL on the real catalogue and then the suite on the bodies.json it
wrote:
- node terms left at the IAU's rates: ETL "europa's spin axis leans up to
0.33"; suite fails only 'turns the poles of Europa, Ganymede, Callisto,
Rhea, Miranda and Triton round with their drawn nodes'.
- tolerance 1 per cent: ETL, Callisto 0.33; the same test, alone.
- harmonics dropped: ETL, Triton 0.29 (the suite's three dates miss it).
- the archived node periods: ETL, Callisto's track 0.19; the suite fails
that test and 'draws Miranda's orbit, and turns its axis, where Horizons
has its orbit in 1601 and 2390' (orbit 2.02, axis 2.36 at 1601).
- the row's argument kept: ETL, Callisto's track 0.71.
The docs that called the IAU's rates the wrong ones are corrected
(lockedToOrbit, the axis ceiling in build.ts, whose Titan node now turns
in 687 years), and the README and BodyRecord doc no longer say every
locked moon's node terms are re-rated: only those within 5 per cent of a
multiple of the node's rate, never the Moon's or Phobos's, and not the
circles Ariel's, Umbriel's, Titania's and Oberon's poles go round on
(0.36 to 0.50 degrees off their orbits). The README also names the five
bodies the IAU's elements do not turn. Unit suite 866 passed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -55,10 +55,14 @@ in it is measured and what is not.
|
||||
**System view** — selecting a star flies the camera continuously into its system rather than
|
||||
cutting to a new scene. The Sun gets the real solar-system bodies, moving on JPL's mean orbital
|
||||
elements — Standish's for the planets, JPL SSD's satellite table for the moons, the Small-Body
|
||||
Database for Ceres, Eris, Haumea and Makemake — and turned by the IAU's rotational elements, Earth
|
||||
by the IERS Earth Rotation Angle; a tidally locked moon's prime meridian (but the Moon's and
|
||||
Phobos's) turns at its JPL mean motion and its pole's terms on its node at its JPL node rate, both
|
||||
re-phased to the IAU's values on 2025-01-01, and Iapetus's pole follows its orbit normal
|
||||
Database for Ceres, Eris, Haumea and Makemake — and turned by the IAU's rotational elements
|
||||
(Eris, Haumea, Makemake and Nereid, which have none, at their measured days about their orbit
|
||||
normals, and Hyperion, which tumbles, not at all), Earth by the IERS Earth Rotation Angle; a
|
||||
tidally locked moon's prime meridian turns at its JPL mean motion, and its pole's terms that turn
|
||||
within 5 per cent of a multiple of its node's rate at that multiple of its JPL node rate, both
|
||||
re-phased to the IAU's values on 2025-01-01 (the Moon's and Phobos's are left as the IAU has them,
|
||||
and so are the circles Ariel's, Umbriel's, Titania's and Oberon's poles go round on, at rates none
|
||||
of their nodes has), and Iapetus's pole follows its orbit normal
|
||||
(`lockedToOrbit`), so each keeps its face to its planet from AD 1 to 3000; other
|
||||
stars get their confirmed exoplanets. Orbits are drawn as ellipses and bodies are propagated
|
||||
along them by a Kepler solver to the date on the map's clock. Under them, a dashed grid marks out
|
||||
@@ -333,7 +337,7 @@ plugin's own files are kept so it can be listed from a marketplace of its own la
|
||||
## Data credits
|
||||
|
||||
Star catalogue: [HYG database](https://github.com/astronexus/HYG-Database) (Hipparcos, Yale
|
||||
Bright Star, Gliese) — 68 388 stars within 250 pc. Solar-system orbits: JPL approximate planetary mean elements (Standish), JPL SSD satellite mean elements and the JPL Small-Body Database; rotation: the IAU WGCCRE 2015 report via NAIF's pck00011, with a locked moon's W and node terms re-rated to its JPL mean elements and Iapetus's pole carried round its orbit normal, and for Earth the IERS Conventions 2010; physical data, and the positions the orbits are checked against: NASA/JPL Horizons. Exoplanets: NASA Exoplanet
|
||||
Bright Star, Gliese) — 68 388 stars within 250 pc. Solar-system orbits: JPL approximate planetary mean elements (Standish), JPL SSD satellite mean elements and the JPL Small-Body Database; rotation: the IAU WGCCRE 2015 report via NAIF's pck00011, with a locked moon's W and its pole's terms within 5 per cent of its node's rate re-rated to its JPL mean elements (but the Moon's and Phobos's) and Iapetus's pole carried round its orbit normal, and for Earth the IERS Conventions 2010; physical data, and the positions the orbits are checked against: NASA/JPL Horizons. Exoplanets: NASA Exoplanet
|
||||
Archive. Deep-sky objects: [OpenNGC](https://github.com/mattiaverga/OpenNGC). Body and skybox
|
||||
imagery: NASA/JPL/USGS public domain and Solar System Scope (CC BY 4.0) — per-file provenance
|
||||
is recorded in `src/assets/textures/README.md`.
|
||||
|
||||
@@ -920,7 +920,22 @@ 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)), []);
|
||||
const renderer = new SystemOrbitsRenderer(
|
||||
shipped.filter((body) => ['jupiter', 'saturn', 'uranus', 'neptune', 'europa', 'ganymede', 'callisto', 'mimas', 'rhea', 'iapetus', 'miranda', 'triton', 'proteus'].includes(body.id)),
|
||||
[]
|
||||
);
|
||||
|
||||
/** Degrees between two lines, the way a spin axis and an orbit normal are compared: Miranda turns backwards against the IAU's pole. */
|
||||
function linesApartDeg(a: THREE.Vector3, b: THREE.Vector3): number {
|
||||
return (Math.acos(Math.min(1, Math.abs(a.clone().normalize().dot(b.clone().normalize())))) * 180) / Math.PI;
|
||||
}
|
||||
|
||||
/** A moon's drawn spin axis and the normal of its drawn orbit line, in the scene's ICRF frame. */
|
||||
function axisAndOrbitNormal(id: string): { axis: THREE.Vector3; normal: THREE.Vector3 } {
|
||||
const moon = renderer.members.find((member) => member.id === id)!.marker;
|
||||
const line = moon.parent!.children.find((child) => child.name === 'orbit-line')!;
|
||||
return { axis: new THREE.Vector3(0, 1, 0).applyQuaternion(moon.quaternion), normal: new THREE.Vector3(0, 0, 1).applyQuaternion(line.quaternion) };
|
||||
}
|
||||
|
||||
/** East longitude, on its map, of the point on a moon's drawn sphere that faces its planet. */
|
||||
function facingPlanet(id: string): number {
|
||||
@@ -944,20 +959,49 @@ describe('locked moons across the clock’s window', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('keeps the axes of Miranda, Mimas and Iapetus on their drawn orbits’ normals, as a Cassini state holds them, at AD 1, today and AD 3000', () => {
|
||||
// Measured over AD 1-3000: Miranda 0.42 degrees at most, Mimas 0.47, Iapetus 0.74. With their
|
||||
// poles going round at the IAU's node rates, Miranda was 7.6 off at AD 1 and Mimas 2.6; with
|
||||
// Iapetus's pole on its Laplace pole, 8.3 off at every date.
|
||||
it('keeps the axes of Mimas and Iapetus on their drawn orbits’ normals, as a Cassini state holds them, at AD 1, today and AD 3000', () => {
|
||||
// Measured over AD 1-3000: Mimas 0.44 degrees at most, Iapetus 0.74. With Iapetus's pole on
|
||||
// its Laplace pole, 8.3 off at every date.
|
||||
for (const jd of [1721425.5, 2460676.5, 2816787.4]) {
|
||||
renderer.update(jd);
|
||||
for (const id of ['miranda', 'mimas', 'iapetus']) {
|
||||
const moon = renderer.members.find((member) => member.id === id)!.marker;
|
||||
const line = moon.parent!.children.find((child) => child.name === 'orbit-line')!;
|
||||
const axis = new THREE.Vector3(0, 1, 0).applyQuaternion(moon.quaternion);
|
||||
const normal = new THREE.Vector3(0, 0, 1).applyQuaternion(line.quaternion);
|
||||
// A line, not a direction: Miranda turns backwards against the IAU's pole.
|
||||
expect((Math.acos(Math.min(1, Math.abs(axis.dot(normal)))) * 180) / Math.PI).toBeLessThan(1);
|
||||
for (const id of ['mimas', 'iapetus']) {
|
||||
const { axis, normal } = axisAndOrbitNormal(id);
|
||||
expect(linesApartDeg(axis, normal)).toBeLessThan(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('turns the poles of Europa, Ganymede, Callisto, Rhea, Miranda and Triton round with their drawn nodes, at AD 1, today and AD 3000', () => {
|
||||
// Each pole goes round on a term of its node's angle, re-rated to the node's drawn rate (see
|
||||
// `lockedToOrbit`), each node at JPL's current rate. Measured at these dates: at most 0.23
|
||||
// degrees (Miranda). On the IAU's rates Rhea is 0.73, Miranda 0.51 and Triton 0.42, and on the
|
||||
// archived table's node periods Callisto 0.48 and Miranda 0.42.
|
||||
for (const jd of [1721425.5, 2460676.5, 2816787.4]) {
|
||||
renderer.update(jd);
|
||||
for (const id of ['europa', 'ganymede', 'callisto', 'rhea', 'miranda', 'triton']) {
|
||||
const { axis, normal } = axisAndOrbitNormal(id);
|
||||
expect(linesApartDeg(axis, normal), id).toBeLessThan(0.25);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('draws Miranda’s orbit, and turns its axis, where Horizons has its orbit in 1601 and 2390', () => {
|
||||
// Horizons' osculating orbit normal (ura184, ICRF), averaged over three of Miranda's orbits about
|
||||
// each date; it wobbles 0.01 degrees about that. The drawn node turns at JPL's current 17.787-year
|
||||
// period (see `nodePeriodYears` in the ETL); on the archived table's 17.727, which the IAU's pole
|
||||
// was once turned after too, the drawn orbit was 2.1 degrees from Horizons' at both dates and the
|
||||
// axis 2.4 at 1601. A date this far back is TDB less some two minutes; the node moves 0.004 degrees in that.
|
||||
const HORIZONS_NORMALS: Array<[jd: number, raDeg: number, decDeg: number]> = [
|
||||
[2305813.5, 72.83137, 16.17526],
|
||||
[2594102.5, 81.27691, 17.43782]
|
||||
];
|
||||
for (const [jd, raDeg, decDeg] of HORIZONS_NORMALS) {
|
||||
renderer.update(jd);
|
||||
const [ra, dec] = [(raDeg * Math.PI) / 180, (decDeg * Math.PI) / 180];
|
||||
const horizons = new THREE.Vector3(Math.cos(dec) * Math.cos(ra), Math.cos(dec) * Math.sin(ra), Math.sin(dec));
|
||||
const { axis, normal } = axisAndOrbitNormal('miranda');
|
||||
expect(linesApartDeg(normal, horizons)).toBeLessThan(0.5);
|
||||
expect(linesApartDeg(axis, horizons)).toBeLessThan(0.5);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -105,11 +105,13 @@ export interface BodyRecord {
|
||||
/**
|
||||
* 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, but that a locked
|
||||
* moon's W, and its pole's terms on its node, turn at its drawn orbit's rates and Iapetus's pole
|
||||
* goes round with its orbit's, so they keep their faces to their planets, and their poles round
|
||||
* their orbits', 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,
|
||||
* moon's W, and its pole's terms that turn within 5 per cent of a multiple of its node's rate, turn
|
||||
* at its drawn orbit's rates and Iapetus's pole goes round with its orbit's, so they keep their
|
||||
* faces to their planets, and their poles round their orbits', over the clock's AD 1 to 3000 (see
|
||||
* `lockedToOrbit` in the ETL). The Moon's and Phobos's, whose W has a quadratic, are the IAU's
|
||||
* whole, and so are the terms Ariel's, Umbriel's, Titania's and Oberon's poles go round on, which
|
||||
* turn at none of their nodes' multiples. 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;
|
||||
|
||||
+22
-22
@@ -1025,8 +1025,8 @@
|
||||
},
|
||||
"rates": {
|
||||
"meanMotionDegPerDay": 50.3176072,
|
||||
"longitudeOfAscendingNodeDegPerDay": -0.007430053246547835,
|
||||
"argumentOfPeriapsisDegPerDay": 0.015509705634511267
|
||||
"longitudeOfAscendingNodeDegPerDay": -0.00715196269822335,
|
||||
"argumentOfPeriapsisDegPerDay": 0.015231615086186782
|
||||
},
|
||||
"laplacePole": {
|
||||
"raDeg": 268.168,
|
||||
@@ -1063,8 +1063,8 @@
|
||||
},
|
||||
{
|
||||
"angleDeg": [
|
||||
117.57926275587673,
|
||||
271.38269483015966
|
||||
120.11864659790176,
|
||||
261.22543755260784
|
||||
],
|
||||
"ra": 0.431,
|
||||
"dec": 0.186,
|
||||
@@ -1099,14 +1099,14 @@
|
||||
},
|
||||
"rates": {
|
||||
"meanMotionDegPerDay": 21.5710728,
|
||||
"longitudeOfAscendingNodeDegPerDay": -0.002908996763377476,
|
||||
"argumentOfPeriapsisDegPerDay": 0.004790407209562851
|
||||
"longitudeOfAscendingNodeDegPerDay": -0.0017074099257316522,
|
||||
"argumentOfPeriapsisDegPerDay": 0.003588820371917027
|
||||
},
|
||||
"laplacePole": {
|
||||
"raDeg": 268.639,
|
||||
"decDeg": 64.749
|
||||
},
|
||||
"orbitSource": "JPL SSD satellite mean elements, epoch 1997 Jan 16, within 0.2 degrees of Horizons from 1950 to 2100",
|
||||
"orbitSource": "JPL SSD satellite mean elements, epoch 1997 Jan 16, within 0.1 degrees of Horizons from 1950 to 2100",
|
||||
"parentBodyId": "jupiter",
|
||||
"rotationPeriodHours": 400.5364072574082,
|
||||
"rotationalElements": {
|
||||
@@ -1137,8 +1137,8 @@
|
||||
},
|
||||
{
|
||||
"angleDeg": [
|
||||
229.8,
|
||||
64.3
|
||||
230.28422637269546,
|
||||
62.363147537348596
|
||||
],
|
||||
"ra": 0.59,
|
||||
"dec": 0.254,
|
||||
@@ -1173,8 +1173,8 @@
|
||||
},
|
||||
"rates": {
|
||||
"meanMotionDegPerDay": 381.9944948,
|
||||
"longitudeOfAscendingNodeDegPerDay": -0.9996209770462032,
|
||||
"argumentOfPeriapsisDegPerDay": 1.9992419540924065,
|
||||
"longitudeOfAscendingNodeDegPerDay": -0.9994661190965093,
|
||||
"argumentOfPeriapsisDegPerDay": 1.9990870961427125,
|
||||
"meanAnomalyTerms": {
|
||||
"b": 0,
|
||||
"c": 30.901081394463684,
|
||||
@@ -1208,8 +1208,8 @@
|
||||
"terms": [
|
||||
{
|
||||
"angleDeg": [
|
||||
178.81408536763,
|
||||
-36511.15618661257
|
||||
177.4,
|
||||
-36505.5
|
||||
],
|
||||
"ra": 13.56,
|
||||
"dec": -1.53,
|
||||
@@ -1461,8 +1461,8 @@
|
||||
},
|
||||
"rates": {
|
||||
"meanMotionDegPerDay": 22.5769756,
|
||||
"longitudeOfAscendingNodeDegPerDay": -0.001398845136769169,
|
||||
"argumentOfPeriapsisDegPerDay": 0.002799120423059061
|
||||
"longitudeOfAscendingNodeDegPerDay": -0.0014339093695790572,
|
||||
"argumentOfPeriapsisDegPerDay": 0.002834184655868949
|
||||
},
|
||||
"laplacePole": {
|
||||
"raDeg": 36.214,
|
||||
@@ -1671,14 +1671,14 @@
|
||||
},
|
||||
"rates": {
|
||||
"meanMotionDegPerDay": 254.6906576,
|
||||
"longitudeOfAscendingNodeDegPerDay": -0.05560028675847895,
|
||||
"argumentOfPeriapsisDegPerDay": 0.11058300049002093
|
||||
"longitudeOfAscendingNodeDegPerDay": -0.05541273308413766,
|
||||
"argumentOfPeriapsisDegPerDay": 0.11039544681567964
|
||||
},
|
||||
"laplacePole": {
|
||||
"raDeg": 77.311,
|
||||
"decDeg": 15.175
|
||||
},
|
||||
"orbitSource": "JPL SSD satellite mean elements, epoch 1980 Jan 1, within 1.8 degrees of Horizons from 1950 to 2100",
|
||||
"orbitSource": "JPL SSD satellite mean elements, epoch 1980 Jan 1, within 1.7 degrees of Horizons from 1950 to 2100",
|
||||
"parentBodyId": "uranus",
|
||||
"rotationPeriodHours": 33.9235057988244,
|
||||
"rotationalElements": {
|
||||
@@ -1700,8 +1700,8 @@
|
||||
"terms": [
|
||||
{
|
||||
"angleDeg": [
|
||||
103.87516350424971,
|
||||
-2030.8004738534437
|
||||
102.16251712700222,
|
||||
-2023.950075898128
|
||||
],
|
||||
"ra": 4.41,
|
||||
"dec": 4.25,
|
||||
@@ -1718,8 +1718,8 @@
|
||||
},
|
||||
{
|
||||
"angleDeg": [
|
||||
207.75032700849943,
|
||||
-4061.6009477068874
|
||||
204.32503425400444,
|
||||
-4047.900151796256
|
||||
],
|
||||
"ra": -0.04,
|
||||
"dec": -0.02,
|
||||
|
||||
+29
-8
@@ -180,12 +180,12 @@ const MOON_OFFSET_CEILINGS_DEG: Record<string, number> = { hyperion: 10, iapetus
|
||||
* - Iapetus, 10.34: the row sits 9.4 degrees behind Horizons at its own epoch, 2000 Jan 1.5, and
|
||||
* keeps that offset; its plane agrees with Horizons' to 0.07 degrees and its period to 0.001 per
|
||||
* cent, so the fault is in the row's longitude, which this has no second source to correct.
|
||||
* - Mimas, 7.43: its orbit carries the 44-degree libration of its resonance with Tethys (see
|
||||
* - Mimas, 7.42: its orbit carries the 44-degree libration of its resonance with Tethys (see
|
||||
* `orbitFromW` in `fetchSolarSystem.ts`), but not the rest of what Horizons integrates.
|
||||
* - Ceres, 7.12 (1953): the SBDB's elements are osculating, exact at 2026 Jun 9 and drifting
|
||||
* either side; 1.9 by 2050, 5.3 by 2100, and 39 at 1600 on Horizons' own figures.
|
||||
*
|
||||
* And three are held tighter than the rest, each where one reading of its row is all that keeps it
|
||||
* And four are held tighter than the rest, each where one reading of its row is all that keeps it
|
||||
* close, and without it the card would quietly restate itself under the general ceiling:
|
||||
*
|
||||
* - Tethys, 0.28, which takes the other half of that libration, 2.23 degrees, from its W. Without
|
||||
@@ -193,9 +193,12 @@ const MOON_OFFSET_CEILINGS_DEG: Record<string, number> = { hyperion: 10, iapetus
|
||||
* - Io, 0.07, and Europa, 0.23, whose periapses turn backwards, held by the Laplace resonance at
|
||||
* 2n(Europa) - n(Io), -0.7395 degrees a day (`apsidesRegress`). Read as advancing, Io strays 0.96
|
||||
* and Europa 2.24, and their cards said "within 1.0" and "within 2.3".
|
||||
* - Callisto, 0.08, whose node turns at JPL's current rate and its periapsis's longitude at the
|
||||
* row's (`nodePeriodYears`). On the row's argument its periapsis moves 44 degrees by 2100 and it
|
||||
* strays 0.71; on the row's node, 0.19.
|
||||
*/
|
||||
const MAX_TRACK_OFFSET_DEG = 3;
|
||||
const TRACK_OFFSET_CEILINGS_DEG: Record<string, number> = { hyperion: 23, nereid: 12, iapetus: 11, mimas: 8, ceres: 8, tethys: 0.5, io: 0.2, europa: 0.5 };
|
||||
const TRACK_OFFSET_CEILINGS_DEG: Record<string, number> = { hyperion: 23, nereid: 12, iapetus: 11, mimas: 8, ceres: 8, tethys: 0.5, io: 0.2, europa: 0.5, callisto: 0.15 };
|
||||
|
||||
/**
|
||||
* The bodies the IAU WGCCRE 2015 report gives no rotational elements for: Hyperion tumbles, and
|
||||
@@ -273,18 +276,36 @@ const SUB_PLANET_CEILINGS_DEG: Record<string, number> = { moon: 8, mimas: 9.5, i
|
||||
* How far a locked moon's spin axis may lean from the normal of the orbit it is drawn going round,
|
||||
* over the same dates. A locked moon sits in a Cassini state, its axis on its orbit normal as the
|
||||
* node carries both round the Laplace pole, and the IAU's pole goes round on a term of the node's
|
||||
* angle; at the rate the IAU's source had for it and not the drawn orbit's, Miranda's axis was 7.89
|
||||
* degrees off at AD 9 and Mimas's 2.63, and an Iapetus pole left on the Laplace pole is 8.30 off at
|
||||
* every date (see `lockedToOrbit`).
|
||||
* angle; at the rate the IAU's source had for it and not the drawn orbit's, Rhea's axis is 0.77
|
||||
* degrees off by AD 1 and Triton's 0.51, and an Iapetus pole left on the Laplace pole is 8.30 off
|
||||
* at every date (see `lockedToOrbit`).
|
||||
*
|
||||
* Measured on this catalogue: at most 0.97 degrees (Tethys, whose IAU pole sits 0.69 from its orbit
|
||||
* normal today; Titan 0.94, whose pole the IAU holds still while its node turns in 705 years) but
|
||||
* normal today; Titan 0.94, whose pole the IAU holds still while its node turns in 687 years) but
|
||||
* for four. The Moon 6.98, its real 6.7-degree tilt to its orbit. Phobos 1.81 and Deimos 1.74, and
|
||||
* Proteus 1.09: their IAU poles nod with Mars's and Neptune's precessing poles, the Laplace poles
|
||||
* their orbits are drawn round are fixed.
|
||||
*
|
||||
* And six are held tighter, each where its node terms turned at the node's rate, or its node at
|
||||
* JPL's current rate, are what keep it close: Europa 0.13, Ganymede 0.16, Callisto 0.22, Rhea 0.17,
|
||||
* Miranda 0.23 and Triton 0.15. On the IAU's rates they are 0.33, 0.21, 0.33, 0.77, 0.59 and 0.51,
|
||||
* on a tolerance of 1 per cent Callisto and Rhea are left there, on the node's angle alone and not
|
||||
* its harmonics Triton is 0.29, and on the archived table's node periods Callisto is 0.56 and
|
||||
* Miranda 0.42: all under the general ceiling.
|
||||
*/
|
||||
const MAX_AXIS_FROM_ORBIT_DEG = 1;
|
||||
const AXIS_FROM_ORBIT_CEILINGS_DEG: Record<string, number> = { moon: 7.1, phobos: 2, deimos: 2, proteus: 1.2 };
|
||||
const AXIS_FROM_ORBIT_CEILINGS_DEG: Record<string, number> = {
|
||||
moon: 7.1,
|
||||
phobos: 2,
|
||||
deimos: 2,
|
||||
proteus: 1.2,
|
||||
europa: 0.25,
|
||||
ganymede: 0.25,
|
||||
callisto: 0.25,
|
||||
rhea: 0.25,
|
||||
miranda: 0.25,
|
||||
triton: 0.25
|
||||
};
|
||||
/** The clock's window, AD 1 to 3000 (`CLOCK_WINDOW` in `time.store.ts`), as Julian dates. */
|
||||
const CLOCK_START_JD = Date.parse('0001-01-01T00:00Z') / 86400000 + 2440587.5;
|
||||
const CLOCK_END_JD = Date.parse('3000-01-01T00:00Z') / 86400000 + 2440587.5;
|
||||
|
||||
@@ -13,6 +13,7 @@ import { lockedToOrbit } from './lib/locked-spin';
|
||||
import { dataPath, ensureDataDir } from './lib/paths';
|
||||
|
||||
const HOURS_PER_DAY = 24;
|
||||
const DAYS_PER_JULIAN_YEAR = 365.25;
|
||||
|
||||
interface BodySpec {
|
||||
id: string;
|
||||
@@ -51,6 +52,25 @@ interface BodySpec {
|
||||
nodeOffsetDeg?: number;
|
||||
epochJd?: number;
|
||||
periodDays?: number;
|
||||
/**
|
||||
* The node's period, in Julian years, where the row's is out of date. The archived table the rows
|
||||
* are read from (see `fetchSatelliteMeanElementsHtml`) has older node periods for four moons than
|
||||
* JPL's current one (ssd.jpl.nasa.gov/sats/elem), and Horizons and the IAU agree with the current
|
||||
* ones: Miranda 17.787 years there (URA182) against the row's 17.727, Ganymede 137.812 (JUP365)
|
||||
* against 132.654, Callisto 577.264 against 338.82, Titan 687.370 (SAT441) against 704.60. Fitted
|
||||
* to Horizons' osculating elements on Uranus's equator over 1601-2399, Miranda's node turns
|
||||
* 2023.97 degrees a century (rms 0.04): the current table's 2023.95, the IAU's U11 2024.22, the
|
||||
* row's 2030.80. On the row's rate Miranda's drawn orbit was 2.0 degrees from Horizons' at 1601
|
||||
* and 2.1 at 2399, and the axis `lockedToOrbit` turned after it 2.4 at 1601; on this one, at most 0.12 and 0.34 over 1601-2399.
|
||||
*
|
||||
* The row's periapsis turns at its argument's rate from that node, and it is the longitude, node
|
||||
* plus argument, the row gives the rate of: Callisto's turns 68.7 degrees a century in the row and
|
||||
* 67.2 in the current table, where their arguments turn at 175.0 and 129.5. So the argument takes
|
||||
* up what the node's rate gives: on the new node and the row's argument, Callisto's periapsis
|
||||
* moved 44 degrees by 2100 and Callisto strayed 0.71 degrees from Horizons over 1950-2100 (0.19
|
||||
* before); keeping the row's longitude, 0.08.
|
||||
*/
|
||||
nodePeriodYears?: number;
|
||||
/**
|
||||
* The terms of the IAU's W that are this locked moon's motion along its orbit, which its row has
|
||||
* no column for: W's quadratic, and the term whose angle turns at `angleRateDegPerCentury`, if
|
||||
@@ -72,6 +92,14 @@ interface BodySpec {
|
||||
/** S5 in pck00011.tpc, 316.45 + 506.2 T: the libration of Mimas and Tethys in their 4:2 resonance. */
|
||||
const MIMAS_TETHYS_LIBRATION = { angleRateDegPerCentury: 506.2 };
|
||||
|
||||
/**
|
||||
* Mimas's node period, 0.986 years in both JPL's tables, is given to three figures: anywhere from
|
||||
* 36 493 to 36 530 degrees a century. It takes the IAU's S3, 36 505.5, which a fit to Horizons'
|
||||
* osculating elements on Saturn's equator over 1750-2249, 36 506.7, is 1.2 from; the row's figure,
|
||||
* 36 511.2, is 4.5.
|
||||
*/
|
||||
const MIMAS_NODE_PERIOD_YEARS = 36000 / 36505.5;
|
||||
|
||||
/**
|
||||
* The poles of the equators JPL refers Uranus's and Pluto's moons to, from the IAU WGCCRE 2015
|
||||
* report, each taken at the end the table's inclinations are measured from (Titania 0.079
|
||||
@@ -125,14 +153,14 @@ const BODY_SPECS: BodySpec[] = [
|
||||
{ id: 'deimos', name: 'Deimos', kind: 'moon', horizonsCommand: '402', center: '500@499', parentBodyId: 'mars' },
|
||||
{ id: 'io', name: 'Io', kind: 'moon', horizonsCommand: '501', center: '500@599', parentBodyId: 'jupiter', apsidesRegress: true },
|
||||
{ id: 'europa', name: 'Europa', kind: 'moon', horizonsCommand: '502', center: '500@599', parentBodyId: 'jupiter', apsidesRegress: true },
|
||||
{ id: 'ganymede', name: 'Ganymede', kind: 'moon', horizonsCommand: '503', center: '500@599', parentBodyId: 'jupiter' },
|
||||
{ id: 'callisto', name: 'Callisto', kind: 'moon', horizonsCommand: '504', center: '500@599', parentBodyId: 'jupiter' },
|
||||
{ id: 'mimas', name: 'Mimas', kind: 'moon', horizonsCommand: '601', center: '500@699', parentBodyId: 'saturn', orbitFromW: MIMAS_TETHYS_LIBRATION },
|
||||
{ id: 'ganymede', name: 'Ganymede', kind: 'moon', horizonsCommand: '503', center: '500@599', parentBodyId: 'jupiter', nodePeriodYears: 137.812 },
|
||||
{ id: 'callisto', name: 'Callisto', kind: 'moon', horizonsCommand: '504', center: '500@599', parentBodyId: 'jupiter', nodePeriodYears: 577.264 },
|
||||
{ id: 'mimas', name: 'Mimas', kind: 'moon', horizonsCommand: '601', center: '500@699', parentBodyId: 'saturn', orbitFromW: MIMAS_TETHYS_LIBRATION, nodePeriodYears: MIMAS_NODE_PERIOD_YEARS },
|
||||
{ id: 'enceladus', name: 'Enceladus', kind: 'moon', horizonsCommand: '602', center: '500@699', parentBodyId: 'saturn' },
|
||||
{ id: 'tethys', name: 'Tethys', kind: 'moon', horizonsCommand: '603', center: '500@699', parentBodyId: 'saturn', orbitFromW: MIMAS_TETHYS_LIBRATION },
|
||||
{ id: 'dione', name: 'Dione', kind: 'moon', horizonsCommand: '604', center: '500@699', parentBodyId: 'saturn' },
|
||||
{ id: 'rhea', name: 'Rhea', kind: 'moon', horizonsCommand: '605', center: '500@699', parentBodyId: 'saturn' },
|
||||
{ id: 'titan', name: 'Titan', kind: 'moon', horizonsCommand: '606', center: '500@699', parentBodyId: 'saturn' },
|
||||
{ id: 'titan', name: 'Titan', kind: 'moon', horizonsCommand: '606', center: '500@699', parentBodyId: 'saturn', nodePeriodYears: 687.37 },
|
||||
// Hyperion tumbles ("Rotational period = Chaotic") and Phoebe, captured, turns in 9.27 hours.
|
||||
// Hyperion's eccentricity is 0.105 in JPL's current table (ssd.jpl.nasa.gov/sats/elem, SAT441).
|
||||
{ id: 'hyperion', name: 'Hyperion', kind: 'moon', horizonsCommand: '607', center: '500@699', parentBodyId: 'saturn', spinsFreely: true, measuredEccentricity: 0.105, trackStepDays: 1 },
|
||||
@@ -147,7 +175,7 @@ const BODY_SPECS: BodySpec[] = [
|
||||
// (2.58 in 1969). The table's note on misstated retrograde mean motions is about another source,
|
||||
// Jacobson 2000 on Jupiter's outer moons, and says the table carries the corrected values.
|
||||
{ id: 'phoebe', name: 'Phoebe', kind: 'moon', horizonsCommand: '609', center: '500@699', parentBodyId: 'saturn', spinsFreely: true, periodDays: 550.30391 },
|
||||
{ id: 'miranda', name: 'Miranda', horizonsCommand: '705', ...URANUS_MOON },
|
||||
{ id: 'miranda', name: 'Miranda', horizonsCommand: '705', ...URANUS_MOON, nodePeriodYears: 17.787 },
|
||||
{ id: 'ariel', name: 'Ariel', horizonsCommand: '701', ...URANUS_MOON },
|
||||
{ id: 'umbriel', name: 'Umbriel', horizonsCommand: '702', ...URANUS_MOON },
|
||||
{ id: 'titania', name: 'Titania', horizonsCommand: '703', ...URANUS_MOON },
|
||||
@@ -215,6 +243,9 @@ export async function fetchSolarSystem(): Promise<{ bodies: BodyRecord[]; horizo
|
||||
(parentName
|
||||
? parseSatelliteMeanElements(satelliteElements, parentName, spec.name, spec.apsidesRegress ?? false, spec.equatorPole)
|
||||
: parsePlanetMeanElements(planetElements, spec.id));
|
||||
const nodeRate = spec.nodePeriodYears
|
||||
? Math.sign(read.rates.longitudeOfAscendingNodeDegPerDay) * (360 / (spec.nodePeriodYears * DAYS_PER_JULIAN_YEAR))
|
||||
: read.rates.longitudeOfAscendingNodeDegPerDay;
|
||||
const corrected: MeanOrbit = {
|
||||
...read,
|
||||
orbit: {
|
||||
@@ -222,7 +253,13 @@ export async function fetchSolarSystem(): Promise<{ bodies: BodyRecord[]; horizo
|
||||
longitudeOfAscendingNodeDeg: read.orbit.longitudeOfAscendingNodeDeg + (spec.nodeOffsetDeg ?? 0),
|
||||
epochJd: spec.epochJd ?? read.orbit.epochJd
|
||||
},
|
||||
rates: spec.periodDays ? { ...read.rates, meanMotionDegPerDay: 360 / spec.periodDays } : read.rates
|
||||
rates: {
|
||||
...read.rates,
|
||||
...(spec.periodDays ? { meanMotionDegPerDay: 360 / spec.periodDays } : {}),
|
||||
longitudeOfAscendingNodeDegPerDay: nodeRate,
|
||||
// The periapsis's longitude keeps the row's rate; see `nodePeriodYears`.
|
||||
argumentOfPeriapsisDegPerDay: read.rates.argumentOfPeriapsisDegPerDay + (read.rates.longitudeOfAscendingNodeDegPerDay - nodeRate)
|
||||
}
|
||||
};
|
||||
if (spec.orbitFromW && !rotation) {
|
||||
throw new Error(`${spec.name}'s orbit takes terms from a W the kernel does not give.`);
|
||||
|
||||
@@ -23,8 +23,8 @@ const POLE_HARMONICS = 5;
|
||||
|
||||
/**
|
||||
* How far a periodic term's angle may turn from a multiple of the node's rate, as a fraction of it,
|
||||
* and still be taken for the node's angle as the IAU's source had it. Measured: at most 3.4e-2
|
||||
* (Ganymede's J5), then Rhea's R4 1.2e-2 and Miranda's U11 3.2e-3; the nearest that is not a node is
|
||||
* and still be taken for the node's angle as the IAU's source had it. Measured: at most 3.1e-2
|
||||
* (Callisto's J6), then Rhea's R4 1.2e-2 and Ganymede's J5 3.3e-3; the nearest that is not a node is
|
||||
* a term of Miranda's W alone, 6.0e-2 from three times it. Multiples go up to the ninth, the most the
|
||||
* report takes (Triton's N7); past that, Umbriel's W has a term 1.0e-2 from ten times its node's.
|
||||
*/
|
||||
@@ -58,14 +58,20 @@ export function subPlanetLongitudeDeg(body: Pick<BodyRecord, 'orbit' | 'rates' |
|
||||
*
|
||||
* The node's angle goes the same way. A moon in a Cassini state keeps its axis on its orbit normal,
|
||||
* which goes round the Laplace pole with the node, and the IAU's pole goes round with it on a term
|
||||
* of the node's angle, at the node's rate as its source had it: Miranda's U11 at -2024.22 degrees a
|
||||
* century, where the table the orbit is drawn from has -2030.80. On a 4.3-degree circle that parted
|
||||
* the axis from the drawn orbit by 7.9 degrees at AD 1, and Mimas's by 2.6. Every term whose angle
|
||||
* turns within {@link MAX_NODE_RATE_OFFSET} of a multiple of the node's rate is set to that multiple,
|
||||
* its constant moved so the angle is unchanged at the present, and the pole with it: over AD 1-3000
|
||||
* Miranda's axis stays within 0.42 degrees of its orbit normal and Mimas's within 0.47, and the
|
||||
* Io's, Europa's, Ganymede's, Rhea's and Triton's within 0.17. The rest of the pole and its terms
|
||||
* are the IAU's: Callisto's J6 turns 40 per cent slower than its node, and is left.
|
||||
* of the node's angle, at the node's rate as its source had it, not quite JPL's current one the
|
||||
* orbit is drawn at (see `nodePeriodYears` in `fetchSolarSystem.ts`): Rhea's R4 turns 1.2 per cent
|
||||
* faster than its node, Callisto's J6 3.1 per cent, and Miranda's U11 0.013 per cent, which on a
|
||||
* 4.4-degree circle over twenty centuries still adds up. On the IAU's rates the axes part from the
|
||||
* drawn orbits by AD 1 or 3000: Rhea's by 0.77 degrees, Miranda's 0.59, Triton's 0.51, Europa's and
|
||||
* Callisto's 0.33. Every term whose angle turns within {@link MAX_NODE_RATE_OFFSET} of a multiple of
|
||||
* the node's rate is set to that multiple, its constant moved so the angle is unchanged at the
|
||||
* present, and the pole with it: over AD 1-3000 the axes of Io, Europa, Ganymede, Callisto, Rhea,
|
||||
* Miranda and Triton stay within 0.23 degrees of their orbit normals, and Mimas's, whose drawn node
|
||||
* takes the IAU's S3 itself, within 0.44. The rest of the pole and its terms are the IAU's, and so
|
||||
* are all of the Moon's and Phobos's, left whole with their W: Ariel's, Umbriel's, Titania's and
|
||||
* Oberon's poles go round on angles of their own at none of their nodes' multiples (Oberon's, the
|
||||
* nearest, 8.7 per cent from three times its node's rate), and their axes stay within 0.50 degrees
|
||||
* of their orbit normals without.
|
||||
*
|
||||
* `poleFollowsOrbit` is for Iapetus, whose IAU pole moves 3.9 degrees a century in right ascension
|
||||
* and 1.1 in declination: a straight line through its orbit normal's 3 439-year circle round the
|
||||
|
||||
Reference in New Issue
Block a user