Turn the Sun about its IAU pole, once in 25.38 days, as every planet already is
Every body with IAU elements was turned by its pole and W, but the Sun, which is the system's star marker and no BodyRecord, was built with an identity rotation and never touched: its pole pointed at RA 90, Dec 0, 115.03 degrees from the WGCCRE 2015 solar pole (RA 286.13, Dec 63.87), and it stood still where its W turns 14.1844 degrees a day. SUN_ROTATIONAL_ELEMENTS carries NAIF body 10 from pck00011.tpc, and the ETL fails if they are not the kernel's. The scene turns the star marker by bodyOrientation each tick when the star is the Sun, as the renderer turns the planets. In the running app the Sun's drawn pole lies on the IAU's (0.00 degrees) and its map turns 14.1844 degrees between 2026-01-01 and 01-02. The map's longitudes are Solar System Scope's, not Carrington's, so the phase of W is not the Sun's own; the pole and the rate are. Controls: leaving the marker unturned fails "turns the Sun about its IAU pole, once in 25.38 days"; the app's elements off the kernel's (W rate 14.18) fails the ETL. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -845,6 +845,28 @@ describe('GalaxySystemSceneComponent camera-flight transitions', () => {
|
|||||||
expect(note()).toMatch(/^Orbits propagated from published elements to now, \d{4}-\d\d-\d\d \d\d:\d\d UTC\.$/);
|
expect(note()).toMatch(/^Orbits propagated from published elements to now, \d{4}-\d\d-\d\d \d\d:\d\d UTC\.$/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('turns the Sun about its IAU pole, once in 25.38 days', async () => {
|
||||||
|
const time = TestBed.inject(TimeStore);
|
||||||
|
time.setRate(0);
|
||||||
|
time.setDate(new Date('2026-01-01T00:00Z'));
|
||||||
|
navigationStore.selectStar(SUN.id);
|
||||||
|
await flushAsync();
|
||||||
|
await advanceFrames(engine, 2.5);
|
||||||
|
const sun = (): THREE.Object3D => (fixture.componentInstance as unknown as { starMarker: THREE.Object3D }).starMarker;
|
||||||
|
const turned = (local: THREE.Vector3): THREE.Vector3 => local.applyQuaternion(sun().getWorldQuaternion(new THREE.Quaternion()));
|
||||||
|
|
||||||
|
// The sphere's +Y, which MAP_TO_BODY carries onto the body's pole, at RA 286.13, Dec 63.87.
|
||||||
|
const ra = (286.13 * Math.PI) / 180;
|
||||||
|
const dec = (63.87 * Math.PI) / 180;
|
||||||
|
const pole = new THREE.Vector3(Math.cos(dec) * Math.cos(ra), Math.cos(dec) * Math.sin(ra), Math.sin(dec));
|
||||||
|
expect(turned(new THREE.Vector3(0, 1, 0)).angleTo(pole)).toBeLessThan(1e-6);
|
||||||
|
|
||||||
|
const before = turned(new THREE.Vector3(1, 0, 0));
|
||||||
|
time.setDate(new Date('2026-01-02T00:00Z'));
|
||||||
|
await advanceFrames(engine, 0.1);
|
||||||
|
expect((turned(new THREE.Vector3(1, 0, 0)).angleTo(before) * 180) / Math.PI).toBeCloseTo(14.1844, 3);
|
||||||
|
});
|
||||||
|
|
||||||
it('names the date the system is drawn for once the clock is set to one', async () => {
|
it('names the date the system is drawn for once the clock is set to one', async () => {
|
||||||
const note = (): string => (fixture.componentInstance as unknown as { hudNote: () => string }).hudNote();
|
const note = (): string => (fixture.componentInstance as unknown as { hudNote: () => string }).hudNote();
|
||||||
TestBed.inject(TimeStore).setDate(new Date('2020-12-21T18:00Z'));
|
TestBed.inject(TimeStore).setDate(new Date('2020-12-21T18:00Z'));
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ import {
|
|||||||
} from '../../shared/astro/galaxy';
|
} from '../../shared/astro/galaxy';
|
||||||
import { DataLoaderService } from '../../core/data/data-loader.service';
|
import { DataLoaderService } from '../../core/data/data-loader.service';
|
||||||
import { EngineService, SceneCamera } from '../../core/engine/engine.service';
|
import { EngineService, SceneCamera } from '../../core/engine/engine.service';
|
||||||
import { BodyRecord } from '../../shared/models/body.model';
|
import { BodyRecord, RotationalElements } from '../../shared/models/body.model';
|
||||||
|
import { SUN_ROTATIONAL_ELEMENTS } from '../../shared/astro/rotational-elements';
|
||||||
|
import { bodyOrientation } from '../../shared/rendering/body-orientation';
|
||||||
import { DeepSkyRecord } from '../../shared/models/deepsky.model';
|
import { DeepSkyRecord } from '../../shared/models/deepsky.model';
|
||||||
import { ExoplanetRecord } from '../../shared/models/exoplanet.model';
|
import { ExoplanetRecord } from '../../shared/models/exoplanet.model';
|
||||||
import { applyMilkyWaySkybox } from '../../shared/rendering/skybox';
|
import { applyMilkyWaySkybox } from '../../shared/rendering/skybox';
|
||||||
@@ -548,6 +550,8 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
|||||||
private currentStarId: number | null = null;
|
private currentStarId: number | null = null;
|
||||||
private systemRenderer?: SystemOrbitsRenderer;
|
private systemRenderer?: SystemOrbitsRenderer;
|
||||||
private starMarker?: THREE.Mesh;
|
private starMarker?: THREE.Mesh;
|
||||||
|
/** How the star marker is turned: the Sun's IAU elements for the Sun, nothing for any other star. */
|
||||||
|
private starRotation?: RotationalElements;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly engine: EngineService,
|
private readonly engine: EngineService,
|
||||||
@@ -820,6 +824,9 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
if (this.systemGroup.visible) {
|
if (this.systemGroup.visible) {
|
||||||
this.systemRenderer?.update(this.time.julianDate());
|
this.systemRenderer?.update(this.time.julianDate());
|
||||||
|
if (this.starMarker && this.starRotation) {
|
||||||
|
bodyOrientation(this.starRotation, this.time.julianDate(), this.starMarker.quaternion);
|
||||||
|
}
|
||||||
this.keepMarkersLegible(camera);
|
this.keepMarkersLegible(camera);
|
||||||
}
|
}
|
||||||
this.updateSelectionMark(camera);
|
this.updateSelectionMark(camera);
|
||||||
@@ -2249,6 +2256,10 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy {
|
|||||||
// stayed put as the camera closed in and ended up filling the screen with the flat gradient
|
// stayed put as the camera closed in and ended up filling the screen with the flat gradient
|
||||||
// that was meant to dress the star, over the photograph underneath it.
|
// that was meant to dress the star, over the photograph underneath it.
|
||||||
this.starMarker = new THREE.Mesh(this.starMarkerGeometry, starMarkerMaterial);
|
this.starMarker = new THREE.Mesh(this.starMarkerGeometry, starMarkerMaterial);
|
||||||
|
// Its pole 115 degrees from the one the IAU gives, and still, until it was turned like a planet.
|
||||||
|
// The map's longitudes are Solar System Scope's, not Carrington's, so only the pole and the
|
||||||
|
// 25.38-day turn are the Sun's own.
|
||||||
|
this.starRotation = star.id === SOL_STAR_ID ? SUN_ROTATIONAL_ELEMENTS : undefined;
|
||||||
this.systemGroup.add(this.starMarker);
|
this.systemGroup.add(this.starMarker);
|
||||||
|
|
||||||
this.galaxyGroup.visible = false;
|
this.galaxyGroup.visible = false;
|
||||||
|
|||||||
@@ -102,6 +102,13 @@ export function parsePckRotationalElements(text: string, naifId: number): { elem
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Sun's, which is no `BodyRecord` but the system's star marker: NAIF body 10 in pck00011.tpc,
|
||||||
|
* the WGCCRE 2015 pole at RA 286.13, Dec 63.87 and W = 84.176 + 14.1844 d, a sidereal day of 25.38
|
||||||
|
* days at the Carrington latitude. The ETL checks them against the kernel.
|
||||||
|
*/
|
||||||
|
export const SUN_ROTATIONAL_ELEMENTS: RotationalElements = { poleRaDeg: [286.13, 0, 0], poleDecDeg: [63.87, 0, 0], primeMeridianDeg: [84.176, 14.1844, 0] };
|
||||||
|
|
||||||
function polynomial(coefficients: readonly number[], x: number): number {
|
function polynomial(coefficients: readonly number[], x: number): number {
|
||||||
return (coefficients[0] ?? 0) + (coefficients[1] ?? 0) * x + (coefficients[2] ?? 0) * x * x;
|
return (coefficients[0] ?? 0) + (coefficients[1] ?? 0) * x + (coefficients[2] ?? 0) * x * x;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { SUN_STAR_ID } from '../../src/app/shared/models/star.model';
|
|||||||
import { fetchHorizonsBody } from './lib/horizons';
|
import { fetchHorizonsBody } from './lib/horizons';
|
||||||
import { MeanOrbit, parsePlanetMeanElements, parseSatelliteMeanElements, parseSmallBodyElements } from '../../src/app/shared/astro/mean-elements';
|
import { MeanOrbit, parsePlanetMeanElements, parseSatelliteMeanElements, parseSmallBodyElements } from '../../src/app/shared/astro/mean-elements';
|
||||||
import { fetchPlanetMeanElementsText, fetchSatelliteMeanElementsHtml, fetchSmallBodyAnswer } from './lib/mean-elements';
|
import { fetchPlanetMeanElementsText, fetchSatelliteMeanElementsHtml, fetchSmallBodyAnswer } from './lib/mean-elements';
|
||||||
import { MIN_PERIODIC_TERM_DEG, orbitalTermsOfPrimeMeridian, parsePckRotationalElements } from '../../src/app/shared/astro/rotational-elements';
|
import { MIN_PERIODIC_TERM_DEG, orbitalTermsOfPrimeMeridian, parsePckRotationalElements, SUN_ROTATIONAL_ELEMENTS } from '../../src/app/shared/astro/rotational-elements';
|
||||||
import { fetchPckText } from './lib/pck';
|
import { fetchPckText } from './lib/pck';
|
||||||
import { dataPath, ensureDataDir } from './lib/paths';
|
import { dataPath, ensureDataDir } from './lib/paths';
|
||||||
|
|
||||||
@@ -155,6 +155,10 @@ export async function fetchSolarSystem(): Promise<{ bodies: BodyRecord[]; horizo
|
|||||||
const planetElements = await fetchPlanetMeanElementsText();
|
const planetElements = await fetchPlanetMeanElementsText();
|
||||||
const satelliteElements = await fetchSatelliteMeanElementsHtml();
|
const satelliteElements = await fetchSatelliteMeanElementsHtml();
|
||||||
const pck = await fetchPckText();
|
const pck = await fetchPckText();
|
||||||
|
// The app turns the Sun by elements it carries itself; they must be the kernel's.
|
||||||
|
if (JSON.stringify(parsePckRotationalElements(pck, 10)?.elements) !== JSON.stringify(SUN_ROTATIONAL_ELEMENTS)) {
|
||||||
|
throw new Error(`The Sun's rotational elements in the app, ${JSON.stringify(SUN_ROTATIONAL_ELEMENTS)}, are not the kernel's.`);
|
||||||
|
}
|
||||||
const gmById = new Map<string, number | undefined>();
|
const gmById = new Map<string, number | undefined>();
|
||||||
|
|
||||||
for (const spec of BODY_SPECS) {
|
for (const spec of BODY_SPECS) {
|
||||||
|
|||||||
Reference in New Issue
Block a user