Turn each body at its own rate, from Horizons' own figures
The view had one rotation in it — the planet on the detail page, at 0.08 rad/s, a number with no source. Nothing in the system view turned at all. The data was already on disk: every cached Horizons page carries how its body spins, in one of five forms. The rate in radians per second is preferred where it appears, because it is signed — that is how Venus and Uranus are known to turn backwards — then a period in hours or days, then the `9h 55m 29.711 s` the giant planets use, and finally the word every major moon here carries instead of a number: Synchronous. A tidally locked moon's day is its orbit, so Kepler supplies it from the elements already parsed and the parent it goes round. Seventeen of the eighteen bodies come out within 1% of their published period — Earth 23.934 h, Jupiter 9.925 h, Venus -5832.5 h, Io 42.5 h, Callisto 400.5 h. Titan is the exception: its page states no period at all, so it is left still rather than turned at an invented rate. The axis is the orbit normal tilted by the obliquity about the orbit's ascending node, which is where an obliquity is measured from and the only line in the orbit the elements name. The phase at the epoch is published for none of these bodies, so the face turned toward the camera is not a claim; the rate and the direction are. At true rates nothing is visible moving — Earth turns 15 degrees an hour. A clock the reader can run faster is the next piece, and the audit asks for it anyway. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi
This commit is contained in:
@@ -372,3 +372,58 @@ describe('SystemOrbitsRenderer exoplanet propagation', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('rotation', () => {
|
||||
/** Earth, near enough: a day of 23.934 h, tipped 23.44 degrees off its orbit. */
|
||||
function spinning(overrides: Partial<BodyRecord> = {}): BodyRecord {
|
||||
return {
|
||||
id: 'earth',
|
||||
systemStarId: 0,
|
||||
name: 'Earth',
|
||||
kind: 'planet',
|
||||
radiusKm: 6371,
|
||||
orbit: { semiMajorAxisAu: 1, eccentricity: 0.0167, inclinationDeg: 0, longitudeOfAscendingNodeDeg: 0, argumentOfPeriapsisDeg: 0, meanAnomalyAtEpochDeg: 0, epochJd: DEFAULT_EPOCH_JD },
|
||||
rotationPeriodHours: 23.934,
|
||||
obliquityDeg: 23.4392911,
|
||||
...overrides
|
||||
};
|
||||
}
|
||||
|
||||
/** How far the marker has turned about its own axis between two dates, in degrees. */
|
||||
function turnedDegrees(body: BodyRecord, afterDays: number): number {
|
||||
const renderer = new SystemOrbitsRenderer([body], [], undefined, 1);
|
||||
renderer.update(DEFAULT_EPOCH_JD);
|
||||
const start = renderer.members[0].marker.quaternion.clone();
|
||||
renderer.update(DEFAULT_EPOCH_JD + afterDays);
|
||||
const turn = start.invert().multiply(renderer.members[0].marker.quaternion);
|
||||
const axis = new THREE.Vector3();
|
||||
const angle = 2 * Math.acos(Math.min(1, Math.abs(turn.w)));
|
||||
turn.normalize();
|
||||
axis.set(turn.x, turn.y, turn.z);
|
||||
const signed = axis.y >= 0 ? angle : -angle;
|
||||
return (signed * 180) / Math.PI;
|
||||
}
|
||||
|
||||
it('turns a body once per its own sidereal day', () => {
|
||||
// A full turn in 23.934 h, so a quarter of that is a quarter turn.
|
||||
expect(Math.abs(turnedDegrees(spinning(), 23.934 / 96))).toBeCloseTo(90, 1);
|
||||
});
|
||||
|
||||
it('turns a retrograde body the other way', () => {
|
||||
// Venus: its day runs backwards, which the catalogue carries as a negative period.
|
||||
const forward = turnedDegrees(spinning(), 23.934 / 96);
|
||||
const backward = turnedDegrees(spinning({ rotationPeriodHours: -23.934 }), 23.934 / 96);
|
||||
|
||||
expect(Math.sign(backward)).toBe(-Math.sign(forward));
|
||||
});
|
||||
|
||||
it('leaves a body with no published rotation still', () => {
|
||||
// Titan: Horizons states no period for it, and an invented one 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();
|
||||
renderer.update(DEFAULT_EPOCH_JD + 40);
|
||||
|
||||
expect(renderer.members[0].marker.quaternion.angleTo(start)).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user