Files
star-map/src/app/features/body-detail/body-view-model.spec.ts
T
SenrokaiandClaude Opus 5.5 86d97b903e Print Earth's inclination as 0.00 degrees, not -0.00
Standish's Table 2a fits the Earth-Moon barycentre's inclination as -0.00054346 degrees, and the
card printed toFixed(2) of it: "Inclination -0.00°", where the branch's base read 0.00. A negative
inclination is the same orbit as its size with the node turned half round, so the card prints the
size. The elements the map propagates are left as Standish gives them. The live Earth page now
reads "INCLINATION 0.00°".

Control: printing the fitted sign again fails "prints the size of an inclination fitted below
zero".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 19:11:56 +02:00

132 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from 'vitest';
import { BodyRecord, OrbitalElements } from '../../shared/models/body.model';
import { ExoplanetRecord } from '../../shared/models/exoplanet.model';
import { StarRecord, SUN_STAR_ID } from '../../shared/models/star.model';
import { bodyReadouts } from './body-readouts';
import { buildBodyViewModel } from './body-view-model';
const orbit = (overrides: Partial<OrbitalElements> = {}): OrbitalElements => ({
semiMajorAxisAu: 1,
eccentricity: 0.0167,
inclinationDeg: 0,
longitudeOfAscendingNodeDeg: 0,
argumentOfPeriapsisDeg: 0,
meanAnomalyAtEpochDeg: 0,
epochJd: 2451545,
...overrides,
});
const sun: StarRecord = {
id: SUN_STAR_ID,
name: 'Sol',
x: 0,
y: 0,
z: 0,
magnitude: -26.7,
spectralType: 'G2V',
colorIndex: 0.65,
};
const earth: BodyRecord = {
id: 'earth',
systemStarId: SUN_STAR_ID,
name: 'Earth',
kind: 'planet',
radiusKm: 6371,
orbit: orbit(),
// Standish's mean longitude rate, 35 999.373 degrees a century.
rates: { meanMotionDegPerDay: 35999.37306329 / 36525, longitudeOfAscendingNodeDegPerDay: 0, argumentOfPeriapsisDegPerDay: 0 },
orbitSource: 'JPL approximate mean elements (Standish), fit for 3000 BC to AD 3000',
};
const luna: BodyRecord = {
id: 'luna',
systemStarId: SUN_STAR_ID,
name: 'Moon',
kind: 'moon',
radiusKm: 1737,
parentBodyId: 'earth',
orbit: orbit({ semiMajorAxisAu: 0.00257 }),
// JPL SSD's sidereal mean motion for the Moon.
rates: { meanMotionDegPerDay: 13.176358, longitudeOfAscendingNodeDegPerDay: -0.05299, argumentOfPeriapsisDegPerDay: 0.16435 },
orbitSource: 'JPL SSD satellite mean elements, epoch 2000 Jan 1',
};
describe('buildBodyViewModel', () => {
const catalogues = { bodies: [earth, luna], exoplanets: [] as ExoplanetRecord[], stars: [sun] };
it('gives a planet the sidereal year its published mean motion goes round in', () => {
const model = buildBodyViewModel('earth', catalogues);
expect(model?.orbitalPeriodSource).toBe('measured');
expect(model?.orbitalPeriodDays).toBeCloseTo(365.2564, 4);
});
it('gives a moon its period too, from the same mean motion that carries it round', () => {
// The card used to refuse, while the scene turned the Moon round the Earth all the same.
const model = buildBodyViewModel('luna', catalogues);
expect(model?.orbitalPeriodSource).toBe('measured');
expect(model?.orbitalPeriodDays).toBeCloseTo(27.32166, 5);
});
it('prints the size of an inclination fitted below zero, as the same orbit with its node turned half round', () => {
const tilted: BodyRecord = { ...earth, orbit: orbit({ inclinationDeg: -0.00054346 }) };
const model = buildBodyViewModel('earth', { ...catalogues, bodies: [tilted] })!;
expect(bodyReadouts(model).measured.find((row) => row.label === 'Inclination')?.value).toBe('0.00°');
});
it('says where the orbit comes from, in the card’s provenance', () => {
expect(bodyReadouts(buildBodyViewModel('luna', catalogues)!).provenance).toContain('Orbit: JPL SSD satellite mean elements, epoch 2000 Jan 1.');
});
it('says a moon without a map is illustrated, without saying it was never imaged', () => {
// luna has no map under that id. Voyager and Cassini photographed every moon drawn this way.
const provenance = bodyReadouts(buildBodyViewModel('luna', catalogues)!).provenance;
expect(provenance).toContain('Not an observation — no global map of this world is used here.');
expect(provenance).not.toContain('no image of this world exists');
});
it('says an exoplanet has never been imaged', () => {
const exoplanet: ExoplanetRecord = { id: 'x', hostStarId: SUN_STAR_ID, hostStarName: 'Sol', name: 'X b', orbit: { semiMajorAxisAu: 0.05 } };
const model = buildBodyViewModel('x', { bodies: [], exoplanets: [exoplanet], stars: [sun] })!;
expect(bodyReadouts(model).provenance).toContain('Not an observation — no image of this world exists.');
});
it('marks a published exoplanet period as measured, not derived', () => {
const exoplanet: ExoplanetRecord = {
id: 'kepler-22-b',
hostStarId: null,
hostStarName: 'Kepler-22',
name: 'Kepler-22 b',
periodDays: 289.9,
orbit: { semiMajorAxisAu: 0.849 },
};
const model = buildBodyViewModel('kepler-22-b', {
bodies: [],
exoplanets: [exoplanet],
stars: [],
});
expect(model?.orbitalPeriodSource).toBe('measured');
expect(model?.orbitalPeriodDays).toBe(289.9);
});
it('leaves an exoplanet with no published period undefined rather than assuming a solar-mass host', () => {
const exoplanet: ExoplanetRecord = {
id: 'x',
hostStarId: null,
hostStarName: 'X',
name: 'X b',
orbit: { semiMajorAxisAu: 0.05 },
};
const model = buildBodyViewModel('x', { bodies: [], exoplanets: [exoplanet], stars: [] });
expect(model?.orbitalPeriodDays).toBeUndefined();
});
it('returns undefined for an id in neither catalogue', () => {
expect(buildBodyViewModel('nowhere', catalogues)).toBeUndefined();
});
it('carries the host star id, so callers need not rescan the catalogues for it', () => {
expect(buildBodyViewModel('earth', catalogues)?.hostStarId).toBe(SUN_STAR_ID);
});
});