diff --git a/src/app/features/galaxy-system/system-orbits-renderer.spec.ts b/src/app/features/galaxy-system/system-orbits-renderer.spec.ts index 0c01ec2..333be1e 100644 --- a/src/app/features/galaxy-system/system-orbits-renderer.spec.ts +++ b/src/app/features/galaxy-system/system-orbits-renderer.spec.ts @@ -442,8 +442,18 @@ describe('rotation without IAU elements', () => { expect(spinSense(spinning({ rotationPeriodHours: 23.934 }))).toBeGreaterThan(0.99); }); + it('turns Nereid, as shipped, once in the 11.594 hours Kepler measured: a sixth of a turn in 1.93 hours', () => { + const shipped: BodyRecord[] = JSON.parse(readFileSync(`${process.cwd()}/src/assets/data/bodies.json`, 'utf8')); + const renderer = new SystemOrbitsRenderer(shipped.filter((body) => body.id === 'neptune' || body.id === 'nereid'), []); + const nereid = renderer.members.find((member) => member.id === 'nereid')!.marker; + renderer.update(DEFAULT_EPOCH_JD); + const start = nereid.quaternion.clone(); + renderer.update(DEFAULT_EPOCH_JD + 11.594 / 6 / 24); + expect((nereid.quaternion.angleTo(start) * 180) / Math.PI).toBeCloseTo(60, 1); + }); + it('leaves a body with no published rotation still', () => { - // Titan: Horizons states no period for it, and an invented one would be a claim. + // Hyperion, which tumbles: an invented period would be a claim. const renderer = new SystemOrbitsRenderer([spinning({ rotationPeriodHours: undefined })], [], undefined, 1); renderer.update(DEFAULT_EPOCH_JD); const start = renderer.members[0].marker.quaternion.clone(); diff --git a/src/app/features/galaxy-system/system-orbits-renderer.ts b/src/app/features/galaxy-system/system-orbits-renderer.ts index 4d4084e..aafff2f 100644 --- a/src/app/features/galaxy-system/system-orbits-renderer.ts +++ b/src/app/features/galaxy-system/system-orbits-renderer.ts @@ -297,9 +297,9 @@ const SPIN_AXIS = new THREE.Vector3(0, 1, 0); const HOURS_PER_DAY = 24; /** - * How a body the IAU gives no rotational elements for is turned at a given date — Eris, Haumea - * and Makemake, whose periods are measured (Makemake's only to a factor of two, see its spec in - * `fetchSolarSystem.ts`) and whose poles are not: at its own sidereal rate, about + * How a body the IAU gives no rotational elements for is turned at a given date — Eris, Haumea, + * Makemake and Nereid, whose periods are measured (Makemake's only to a factor of two, see its + * spec in `fetchSolarSystem.ts`) and whose poles are not: at its own sidereal rate, about * its orbit's normal, backwards for a negative period. None of them has an obliquity, so none is * applied. The phase is arbitrary: each body starts at its elements' epoch in the shortest * rotation of +Y onto its axis, and turns from there. Exoplanets have no published rotation at diff --git a/src/app/shared/models/body.model.ts b/src/app/shared/models/body.model.ts index 25fe0f8..8fd579d 100644 --- a/src/app/shared/models/body.model.ts +++ b/src/app/shared/models/body.model.ts @@ -88,8 +88,10 @@ export interface BodyRecord { * Horizons gives a negative rate (Venus, Uranus), and the tilt of that axis from its orbital * plane — which past 90 degrees already says the turn is retrograde. * - * Absent where Horizons publishes neither — the view then leaves the body still rather than - * spinning it at an invented rate. + * For a locked moon the period is its orbit's, from the mean motion that carries it round; for + * a body whose source states none, the measured one its ETL spec carries (Eris, Nereid). Absent + * only for Hyperion, which tumbles — the view leaves it still rather than spinning it at an + * invented rate. */ rotationPeriodHours?: number; obliquityDeg?: number; diff --git a/src/assets/data/bodies.json b/src/assets/data/bodies.json index 2fde121..fe24818 100644 --- a/src/assets/data/bodies.json +++ b/src/assets/data/bodies.json @@ -2130,7 +2130,8 @@ "decDeg": 69.117 }, "orbitSource": "JPL SSD satellite mean elements, epoch 2000 Jan 1, within 11.2 degrees of Horizons from 1950 to 2100", - "parentBodyId": "neptune" + "parentBodyId": "neptune", + "rotationPeriodHours": 11.594 }, { "id": "proteus", diff --git a/tools/etl/build.ts b/tools/etl/build.ts index e0f8990..b5ceba1 100644 --- a/tools/etl/build.ts +++ b/tools/etl/build.ts @@ -204,6 +204,13 @@ const TRACK_OFFSET_CEILINGS_DEG: Record = { hyperion: 23, nereid */ const WITHOUT_ROTATIONAL_ELEMENTS = new Set(['hyperion', 'nereid', 'eris', 'haumea', 'makemake']); +/** + * The one moon drawn still: Hyperion, whose page says "Rotational period = Chaotic". Every other + * moon without a lock has a measured day; Nereid's page states none, and it was drawn still until + * its K2 light curve's 11.594 hours was taken (see its spec). + */ +const TUMBLING = new Set(['hyperion']); + /** * How far the IAU's day, 360 degrees over W's rate, may be from the one Horizons states, as a * fraction of it. Measured on this catalogue: at most 1.8e-5 (Jupiter's System III, 9.92492 hours @@ -357,8 +364,12 @@ function validateBodies(bodies: BodyRecord[], horizonsOrbits: Map orbitHours * 0.1, `Moon ${body.id} does not keep one face to its planet, yet turns once in ${body.rotationPeriodHours} hours against an orbit of ${orbitHours}.` diff --git a/tools/etl/fetchSolarSystem.ts b/tools/etl/fetchSolarSystem.ts index cfe4239..984fb79 100644 --- a/tools/etl/fetchSolarSystem.ts +++ b/tools/etl/fetchSolarSystem.ts @@ -147,8 +147,10 @@ const BODY_SPECS: BodySpec[] = [ { id: 'titania', name: 'Titania', horizonsCommand: '703', ...URANUS_MOON }, { id: 'oberon', name: 'Oberon', horizonsCommand: '704', ...URANUS_MOON }, { id: 'triton', name: 'Triton', kind: 'moon', horizonsCommand: '801', center: '500@899', parentBodyId: 'neptune' }, - // Nereid's eccentric orbit, 0.75, cannot hold a face to Neptune; its page states no spin. - { id: 'nereid', name: 'Nereid', kind: 'moon', horizonsCommand: '802', center: '500@899', parentBodyId: 'neptune', spinsFreely: true, trackStepDays: 1 }, + // Nereid's eccentric orbit, 0.75, cannot hold a face to Neptune. Its page states no spin, but + // Kepler's K2 light curve gives 11.594 +/- 0.017 hours, confirming the short periods measured + // from the ground (Kiss et al. 2016, MNRAS 457, 2908; arXiv:1601.02395). No pole is known. + { id: 'nereid', name: 'Nereid', kind: 'moon', horizonsCommand: '802', center: '500@899', parentBodyId: 'neptune', spinsFreely: true, rotationPeriodHours: 11.594, trackStepDays: 1 }, { id: 'proteus', name: 'Proteus', kind: 'moon', horizonsCommand: '808', center: '500@899', parentBodyId: 'neptune' }, // Pluto's section prints its epoch as 2000 Jan 1.0; JPL's current table gives Charon's as // 2000-01-01.5, and read at 1.0 Charon sat 27.8 to 28.2 degrees — half a day of its motion is