@essrt/physics 23.3.3 → 23.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/package.json +2 -2
- package/src/constants/coefficients.d.ts +5 -1
- package/src/constants/coefficients.js +3 -2
- package/src/constants/generated.d.ts +2 -3
- package/src/constants/generated.js +3 -4
- package/src/constants/units.cjs +4 -1
- package/src/deltat/deep-time.cjs +37 -72
- package/src/index.js +30 -48
- package/src/model/cardinal-instants.js +101 -0
- package/src/model/context.js +54 -0
- package/src/model/deep-time-chain.js +79 -0
- package/src/model/delta-t.js +38 -0
- package/src/model/derived-constants.js +58 -0
- package/src/model/earth-scalars.js +47 -0
- package/src/model/inv-plane-elements.js +56 -0
- package/src/model/lunar-theory.js +216 -0
- package/src/model/moon-epoch.js +28 -0
- package/src/model/one-source-movement.js +192 -0
- package/src/model/planet-elements-of-date.js +61 -0
- package/src/model/planet-records.js +63 -0
- package/src/model/planet-spin-channel.js +41 -0
- package/src/model/published-periods.js +38 -0
- package/src/model/sun-and-eclipses.js +199 -0
- package/src/model/surface.js +330 -0
- package/src/model/time-axis.js +24 -0
- package/src/model/year-day-lengths.js +42 -0
- package/src/model.js +62 -1612
- package/src/moon/arguments.cjs +27 -43
- package/src/planets/chain-artifact.js +2 -2
- package/src/planets/keplerian-chain.cjs +1 -1
- package/types/constants/coefficients.d.ts +5 -1
- package/types/constants/generated.d.ts +2 -3
- package/types/constants/units.d.cts +3 -0
- package/types/deltat/deep-time.d.cts +18 -24
- package/types/index.d.ts +1 -1
- package/types/model/cardinal-instants.d.ts +14 -0
- package/types/model/context.d.ts +87 -0
- package/types/model/deep-time-chain.d.ts +288 -0
- package/types/model/delta-t.d.ts +11 -0
- package/types/model/derived-constants.d.ts +25 -0
- package/types/model/earth-scalars.d.ts +14 -0
- package/types/model/inv-plane-elements.d.ts +6 -0
- package/types/model/lunar-theory.d.ts +72 -0
- package/types/model/moon-epoch.d.ts +10 -0
- package/types/model/one-source-movement.d.ts +74 -0
- package/types/model/planet-elements-of-date.d.ts +9 -0
- package/types/model/planet-records.d.ts +7 -0
- package/types/model/planet-spin-channel.d.ts +29 -0
- package/types/model/published-periods.d.ts +7 -0
- package/types/model/sun-and-eclipses.d.ts +52 -0
- package/types/model/surface.d.ts +279 -0
- package/types/model/time-axis.d.ts +12 -0
- package/types/model/year-day-lengths.d.ts +8 -0
- package/types/model.d.ts +9 -81
- package/types/moon/arguments.d.cts +5 -11
- package/types/planets/keplerian-chain.d.cts +3 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ΔT (TT − UT1) — the raw integral plus the cycle stack, memoized on the exact argument.
|
|
3
|
+
*
|
|
4
|
+
* One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** @param {import('./moon-epoch.js').Ctx} ctx @returns the bindings the later slices read */
|
|
8
|
+
export function assembleDeltaT(ctx) {
|
|
9
|
+
const { F, deepLod, dtCycles, yearToTMa } = ctx;
|
|
10
|
+
/** @param {number} tMa @returns {number} */
|
|
11
|
+
// Exact-argument memo (the alphaMemo pattern of deltat/deep-time-chain.js): the cardinal Newton
|
|
12
|
+
// solve asks for ΔT at the SAME jd twice per step (jdTTFromUT in the
|
|
13
|
+
// apparent Sun and again under the planetary completion) — measured 8.8 →
|
|
14
|
+
// 5.6 ms per panel year (8 solves) at modern epochs, values bit-identical.
|
|
15
|
+
/** @type {Map<number, number>} */
|
|
16
|
+
const dtMemo = new Map();
|
|
17
|
+
/** @param {number} tMa @returns {number} */
|
|
18
|
+
const meanDeltaTSecondsAtAge = (tMa) => {
|
|
19
|
+
if (tMa === 0) return 0;
|
|
20
|
+
const hit = dtMemo.get(tMa);
|
|
21
|
+
if (hit !== undefined) return hit;
|
|
22
|
+
let result = deepLod.deltaTRawSecondsAtAge(tMa);
|
|
23
|
+
const yearY = 2000 - tMa * 1e6;
|
|
24
|
+
result += dtCycles.cycleDeltaTSecondsAt('bond', yearY);
|
|
25
|
+
result += dtCycles.cycleDeltaTSecondsAt('hallstatt', yearY);
|
|
26
|
+
result += dtCycles.cycleDeltaTSecondsAt('jose5', yearY);
|
|
27
|
+
result += dtCycles.cycleDeltaTSecondsAt('jose4', yearY);
|
|
28
|
+
result += dtCycles.swingDeltaTSecondsAt(yearY);
|
|
29
|
+
if (dtMemo.size >= 8192) dtMemo.clear();
|
|
30
|
+
dtMemo.set(tMa, result);
|
|
31
|
+
return result;
|
|
32
|
+
};
|
|
33
|
+
/** @param {number} year @returns {number} */
|
|
34
|
+
const deltaTSeconds = (year) => F.DT_ANCHOR.deltaTStartSeconds + meanDeltaTSecondsAtAge(yearToTMa(year)); // the joint fit's anchor (plan 14: its own artifact)
|
|
35
|
+
|
|
36
|
+
return { deltaTSeconds, meanDeltaTSecondsAtAge };
|
|
37
|
+
}
|
|
38
|
+
/** @typedef {import('./moon-epoch.js').Ctx & ReturnType<typeof assembleDeltaT>} Ctx */
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derived constants — the §9-order scalars from the constants context and the J2000 mass chain.
|
|
3
|
+
*
|
|
4
|
+
* One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
|
|
5
|
+
*/
|
|
6
|
+
import { computeMassChainJ2000 } from '../earth/mass-chain.cjs';
|
|
7
|
+
import { computeMeanTropicalYearJ2000Days } from '../earth/year-lengths.cjs';
|
|
8
|
+
|
|
9
|
+
/** @param {import('./context.js').ModelInputs} ctx @returns the bindings the later slices read */
|
|
10
|
+
export function assembleDerivedConstants(ctx) {
|
|
11
|
+
const { C } = ctx;
|
|
12
|
+
// The mean solar year in days (the calendar's year axis) is the J2000 mean
|
|
13
|
+
// tropical year DERIVED in one home from the IAU sidereal year and p_A per
|
|
14
|
+
// Julian year (year-lengths.cjs).
|
|
15
|
+
const meanSolarYearDays = computeMeanTropicalYearJ2000Days(C.yearLengthRef.siderealYear, C.earthOrbital.generalPrecessionJ2000_arcsecPerYr);
|
|
16
|
+
// The scene epoch's ONE home — astro-reference timeReference.sceneEpochJD /
|
|
17
|
+
// sceneEpochYear; the SI-year axis (time-axis.js) anchors on the epoch itself.
|
|
18
|
+
const startmodelYear = C.timeReference.sceneEpochYear;
|
|
19
|
+
const startmodelJD = C.timeReference.sceneEpochJD;
|
|
20
|
+
|
|
21
|
+
const meanSiderealYearDays = C.yearLengthRef.siderealYear;
|
|
22
|
+
// (the sidereal year in SI seconds is the mass chain's own, below)
|
|
23
|
+
// The J2000 day length ω₀ is the SI day (an input).
|
|
24
|
+
const meanLengthOfDay = C.yearLengthRef.solarDay;
|
|
25
|
+
const meanTropicalYearJ2000Seconds = meanSolarYearDays * meanLengthOfDay;
|
|
26
|
+
// THE J2000 precession anchor p₀ in days (Julian years) — the IAU 2006
|
|
27
|
+
// general precession; the Moon's star-referenced periods read it.
|
|
28
|
+
const axialPrecessionJ2000Days = (1296000 / C.earthOrbital.generalPrecessionJ2000_arcsecPerYr) * 365.25;
|
|
29
|
+
|
|
30
|
+
const G_CONSTANT = C.physicalConstants.G_CONSTANT;
|
|
31
|
+
const MASS_RATIO_EARTH_MOON = C.physicalConstants.MASS_RATIO_EARTH_MOON;
|
|
32
|
+
const currentAUDistance = C.physicalConstants.currentAUDistance;
|
|
33
|
+
const moonDistanceKm = C.moonReference.moonDistance;
|
|
34
|
+
const moonSiderealMonthInput = C.moonReference.moonSiderealMonthInput;
|
|
35
|
+
|
|
36
|
+
// The Moon's sidereal month is the IAU input itself.
|
|
37
|
+
const moonSiderealMonth = moonSiderealMonthInput;
|
|
38
|
+
|
|
39
|
+
// The mass chain (Moon Kepler → GM_EM → the Earth/Moon split → the Sun, §9
|
|
40
|
+
// order) — ONE home: earth/mass-chain.cjs (the deep-time chain,
|
|
41
|
+
// deep-time-chain.js, computes the same scalars from the same context).
|
|
42
|
+
const mass = computeMassChainJ2000({
|
|
43
|
+
moonDistanceKm, moonSiderealMonthDays: moonSiderealMonth, meanSiderealYearDays,
|
|
44
|
+
meanLengthOfDaySeconds: meanLengthOfDay, massRatioEarthMoon: MASS_RATIO_EARTH_MOON,
|
|
45
|
+
currentAUDistanceKm: currentAUDistance, gravitationalConstantKm3KgS2: G_CONSTANT,
|
|
46
|
+
});
|
|
47
|
+
const moonOrbitalShift = mass.moonOrbitalShiftKm;
|
|
48
|
+
const moonDistanceCorrected = mass.moonDistanceCorrectedKm;
|
|
49
|
+
const GM_EARTH_MOON_SYSTEM = mass.gmEarthMoonSystemKm3S2;
|
|
50
|
+
const GM_EARTH_ALONE = mass.gmEarthAloneKm3S2;
|
|
51
|
+
const GM_MOON_ALONE = mass.gmMoonAloneKm3S2;
|
|
52
|
+
const GM_SUN_PLUS_EARTH = mass.gmSunPlusEarthKm3S2;
|
|
53
|
+
const GM_SUN = mass.gmSunKm3S2;
|
|
54
|
+
const M_SUN = mass.sunMassKg;
|
|
55
|
+
|
|
56
|
+
return { GM_EARTH_ALONE, GM_EARTH_MOON_SYSTEM, GM_MOON_ALONE, GM_SUN, GM_SUN_PLUS_EARTH, G_CONSTANT, MASS_RATIO_EARTH_MOON, M_SUN, axialPrecessionJ2000Days, currentAUDistance, meanLengthOfDay, meanSiderealYearDays, meanSolarYearDays, meanTropicalYearJ2000Seconds, moonDistanceCorrected, moonDistanceKm, moonOrbitalShift, moonSiderealMonth, moonSiderealMonthInput, startmodelJD, startmodelYear };
|
|
57
|
+
}
|
|
58
|
+
/** @typedef {import('./context.js').ModelInputs & ReturnType<typeof assembleDerivedConstants>} Ctx */
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Earth scalars of date — e, ϖ, ε read from the one-source movement; the eclipse Sun's mean elements (the research `laws` hook).
|
|
3
|
+
*
|
|
4
|
+
* One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** @param {import('./deep-time-chain.js').Ctx} ctx @returns the bindings the later slices read */
|
|
8
|
+
export function assembleEarthScalars(ctx) {
|
|
9
|
+
const { F, laws, late } = ctx;
|
|
10
|
+
/** @param {number} year @returns {number} */
|
|
11
|
+
// Plan 06 layer B — the eclipse Sun onto the series. The PUBLISHED Earth
|
|
12
|
+
// ϖ of date is the one-source series (the secular orbit; its J2000 element
|
|
13
|
+
// ≡ La2004's — the same field the D4c apsidal wheel renders). The SUN's
|
|
14
|
+
// equation of centre needs the MEAN elements of the era: the series plus the
|
|
15
|
+
// derived mean offset of the osculating channel (F.EARTH_OSCULATING_MEAN_
|
|
16
|
+
// OFFSET, tools/verify/earth-osculating-offset.js — the 1890–2110 mean of
|
|
17
|
+
// osculating − secular from the Horizons-seeded nine-body run). Measured:
|
|
18
|
+
// the fast osculating wobble is carried by the completion's direct terms and
|
|
19
|
+
// double-counts if added; the window mean is what the Sun residual holds
|
|
20
|
+
// (all-phase JPL sd 3.23″ → 1.59″).
|
|
21
|
+
const earthPerihelionDeg = (year) => late.oneSourceM.periOfDateDegAt(year);
|
|
22
|
+
const SUN_OFF = F.EARTH_OSCULATING_MEAN_OFFSET;
|
|
23
|
+
/** The eclipse Sun's ϖ of date (mean elements). @param {number} year @returns {number} */
|
|
24
|
+
const sunPerihelionDegAt = laws.perihelionLongitudeDegAt ?? ((year) => late.oneSourceM.periOfDateDegAt(year) + SUN_OFF.dPomArcsec / 3600);
|
|
25
|
+
// The obliquity of date is the one-source hybrid everywhere (the eclipse
|
|
26
|
+
// tier's besselian reads it here).
|
|
27
|
+
/** @param {number} year @returns {number} */
|
|
28
|
+
const obliquityDeg = (year) => late.oneSourceM.epsAt(year);
|
|
29
|
+
// ONE eccentricity for the whole model, two readers. The PUBLISHED Earth
|
|
30
|
+
// eccentricity is the one-source series (|z|, the secular orbit) and the
|
|
31
|
+
// SUN's e adds the derived mean offset (see sunPerihelionDegAt) — the
|
|
32
|
+
// clock-side machinery reads these: the eclipse Sun's equation of centre,
|
|
33
|
+
// the besselian Sun distance, the cardinal braid. The LUNAR chain
|
|
34
|
+
// (E-factor, perigee/node modulation, argument Δe² and the of-date rates)
|
|
35
|
+
// rides the ONE deep e — the engine's own ±10-Myr z-vector, `deepEcc` of
|
|
36
|
+
// the deep-time chain (deep-time-chain.js); the two agree within 4.2e-5
|
|
37
|
+
// in-era. The laws hook deliberately does NOT reach the deep e channel.
|
|
38
|
+
/** @param {number} year @returns {number} */
|
|
39
|
+
const eccentricityAt = (year) => late.oneSourceM.eAt(year);
|
|
40
|
+
/** The eclipse Sun's e (mean elements). @param {number} year @returns {number} */
|
|
41
|
+
const sunEccentricityAt = laws.eccentricityAt ?? ((year) => late.oneSourceM.eAt(year) + SUN_OFF.dE);
|
|
42
|
+
// (Earth's inclination and node of date — `inclinationDeg` /
|
|
43
|
+
// `ascendingNodeDeg` — are the one-source construction in inv-plane-elements.js.)
|
|
44
|
+
|
|
45
|
+
return { earthPerihelionDeg, eccentricityAt, obliquityDeg, sunEccentricityAt, sunPerihelionDegAt };
|
|
46
|
+
}
|
|
47
|
+
/** @typedef {import('./deep-time-chain.js').Ctx & ReturnType<typeof assembleEarthScalars>} Ctx */
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Earth's invariable-plane elements of date — the ONE published i_inv / Ω_inv.
|
|
3
|
+
*
|
|
4
|
+
* One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
|
|
5
|
+
*/
|
|
6
|
+
import { CHAIN_ARTIFACT } from '../planets/chain-artifact.js';
|
|
7
|
+
import { convertNodeSFrameToEquatorOriginDeg } from '../planets/inv-plane-frame.cjs';
|
|
8
|
+
|
|
9
|
+
/** @param {import('./planet-elements-of-date.js').Ctx} ctx @returns the bindings the later slices read */
|
|
10
|
+
export function assembleInvPlaneElements(ctx) {
|
|
11
|
+
const { nodeOriginSSDeg, oneSourceM } = ctx;
|
|
12
|
+
// ONE published i_inv / Ω_inv everywhere — the mirror of the browser's
|
|
13
|
+
// inclInvPlaneModel / ascNodeInvPlaneModel (identical ops): the engine's
|
|
14
|
+
// own Earth-orbit normal of date (the one-source sampler — the banked
|
|
15
|
+
// secular series inside ±10 Myr, the mode tail beyond) against the
|
|
16
|
+
// artifact's invariable plane, the node expressed in the Souami & Souchay
|
|
17
|
+
// longitude origin through the same derived conversion the planets' node
|
|
18
|
+
// takes. Doc 05 banks the one-source reading at rms 0.003° vs La2010 over
|
|
19
|
+
// −500 kyr. Argument: the engine year (dynamical time,
|
|
20
|
+
// Julian from J2000 TT — the sampler's own coordinate, as the chain's).
|
|
21
|
+
const invPlaneGeometry = (() => {
|
|
22
|
+
const ip = /** @type {{ inclEclipticDeg: number, ascNodeEclipticDeg: number }} */ (/** @type {any} */ (CHAIN_ARTIFACT).invariablePlane);
|
|
23
|
+
const D2R = Math.PI / 180;
|
|
24
|
+
const si = Math.sin(ip.inclEclipticDeg * D2R), ci = Math.cos(ip.inclEclipticDeg * D2R);
|
|
25
|
+
const nInv = [si * Math.sin(ip.ascNodeEclipticDeg * D2R), -si * Math.cos(ip.ascNodeEclipticDeg * D2R), ci];
|
|
26
|
+
const xIp = [Math.cos(ip.ascNodeEclipticDeg * D2R), Math.sin(ip.ascNodeEclipticDeg * D2R), 0];
|
|
27
|
+
const yIp = [nInv[1] * xIp[2] - nInv[2] * xIp[1], nInv[2] * xIp[0] - nInv[0] * xIp[2], nInv[0] * xIp[1] - nInv[1] * xIp[0]];
|
|
28
|
+
return { ascNodeEclipticDeg: ip.ascNodeEclipticDeg, nInv, xIp, yIp };
|
|
29
|
+
})();
|
|
30
|
+
/** The engine Earth-orbit normal of date, J2000 ecliptic frame. @param {number} year @returns {number[]} */
|
|
31
|
+
const earthOrbitNormalJ2000At = (year) => {
|
|
32
|
+
const s = oneSourceM.sampleAt(year);
|
|
33
|
+
return [s.orbitNormalX, s.orbitNormalY, Math.sqrt(Math.max(0, 1 - s.orbitNormalX * s.orbitNormalX - s.orbitNormalY * s.orbitNormalY))];
|
|
34
|
+
};
|
|
35
|
+
/** Earth's inclination of date to the model's invariable plane, degrees. @param {number} year @returns {number} */
|
|
36
|
+
const inclinationDeg = (year) => {
|
|
37
|
+
const nE = earthOrbitNormalJ2000At(year), nInv = invPlaneGeometry.nInv;
|
|
38
|
+
const d = nE[0] * nInv[0] + nE[1] * nInv[1] + nE[2] * nInv[2];
|
|
39
|
+
return Math.acos(Math.min(1, Math.max(-1, d))) / (Math.PI / 180);
|
|
40
|
+
};
|
|
41
|
+
/** Earth's ascending node of date on the invariable plane, S&S origin, degrees in [0, 360). @param {number} year @returns {number} */
|
|
42
|
+
const ascendingNodeDeg = (year) => {
|
|
43
|
+
const D2R = Math.PI / 180;
|
|
44
|
+
const { ascNodeEclipticDeg, nInv, xIp, yIp } = invPlaneGeometry;
|
|
45
|
+
const nE = earthOrbitNormalJ2000At(year);
|
|
46
|
+
let N = [nInv[1] * nE[2] - nInv[2] * nE[1], nInv[2] * nE[0] - nInv[0] * nE[2], nInv[0] * nE[1] - nInv[1] * nE[0]];
|
|
47
|
+
const L = Math.hypot(N[0], N[1], N[2]);
|
|
48
|
+
N = [N[0] / L, N[1] / L, N[2] / L];
|
|
49
|
+
const sFrameDeg = ((Math.atan2(N[0] * yIp[0] + N[1] * yIp[1] + N[2] * yIp[2],
|
|
50
|
+
N[0] * xIp[0] + N[1] * xIp[1] + N[2] * xIp[2]) / D2R + ascNodeEclipticDeg) % 360 + 360) % 360;
|
|
51
|
+
return convertNodeSFrameToEquatorOriginDeg(sFrameDeg, nodeOriginSSDeg());
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return { ascendingNodeDeg, inclinationDeg };
|
|
55
|
+
}
|
|
56
|
+
/** @typedef {import('./planet-elements-of-date.js').Ctx & ReturnType<typeof assembleInvPlaneElements>} Ctx */
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lunar theory — the shared-chain assembly: the derived months, the chain-cycle counters, the arguments, UT→TT, the Meeus series.
|
|
3
|
+
*
|
|
4
|
+
* One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
|
|
5
|
+
*/
|
|
6
|
+
import { createChainCycleIntegrator } from '../chain-cycles/index.cjs';
|
|
7
|
+
import { J2000_JD, JULIAN_CENTURY_DAYS } from '../constants/units.cjs';
|
|
8
|
+
import { createMoonArguments, jdToDecimalYear } from '../moon/arguments.cjs';
|
|
9
|
+
import { createMoonSeries } from '../moon/series.cjs';
|
|
10
|
+
import { driver2PeriodSecondsAtAge } from '../planets/orbit-chain.cjs';
|
|
11
|
+
|
|
12
|
+
/** @param {import('./time-axis.js').Ctx} ctx @returns the bindings the later slices read */
|
|
13
|
+
export function assembleLunarTheory(ctx) {
|
|
14
|
+
const {
|
|
15
|
+
C, F, late, EPOCH_PARAMS, axialPrecessionJ2000Days, chain, deepEcc, deepLod, meanDeltaTSecondsAtAge,
|
|
16
|
+
meanSiderealYearDays, meanSolarYearDays, meanTropicalYearJ2000Seconds, moonChain, moonDistanceKm,
|
|
17
|
+
moonDistanceMetresAtAge, moonSiderealMonth, oneSourceM, startmodelJD, startmodelYear, sunPerihelionDegAt,
|
|
18
|
+
yearFromJD,
|
|
19
|
+
} = ctx;
|
|
20
|
+
// Wiring mirrors the engine call sites exactly — tools/lib/deep-time.js
|
|
21
|
+
// (ecc channel, month chain, J2000 precession anchors),
|
|
22
|
+
// tools/lib/scene-graph.js (chain-cycles, arguments, series) and
|
|
23
|
+
// tools/verify/eclipse-audit.js (finders). Deep-time and framework-native
|
|
24
|
+
// are hardwired ON here: they are the shipped defaults; the A/B env
|
|
25
|
+
// toggles (SG_DEEP_TIME/MOON_ARGS_PURE_MEEUS) stay an engine concern.
|
|
26
|
+
const j2000JD = J2000_JD; // ONE home: constants/units (plan 14 step 2)
|
|
27
|
+
const julianCenturyDays = JULIAN_CENTURY_DAYS;
|
|
28
|
+
|
|
29
|
+
// J2000 Moon precession anchors (the IAU inputs themselves). The
|
|
30
|
+
// legacy-'ICRF'-named inputs hold the EQUINOX-OF-DATE observables (Meeus/
|
|
31
|
+
// IERS 3231.50 d / 6798.38 d); the star-referenced (inertial) periods
|
|
32
|
+
// follow by removing the equinox's own precession, 1/T_E = 1/T_I ∓ 1/T_p
|
|
33
|
+
// (perigee advances, node regresses), T_p the J2000 precession anchor.
|
|
34
|
+
const { moonApsidalJ2000Seconds } = chain;
|
|
35
|
+
// (The of-date perigee/node are the chain's count compositions —
|
|
36
|
+
// mcApsidalOfDate / mcNodalOfDate below.)
|
|
37
|
+
|
|
38
|
+
// Derived months on the IAU inputs (constants.js §Moon derived months)
|
|
39
|
+
const moonTropicalMonthDays = 1 / (1 / moonSiderealMonth + 1 / axialPrecessionJ2000Days);
|
|
40
|
+
const moonAnomalisticMonthDays = 1 / (1 / moonSiderealMonth - 86400 / moonApsidalJ2000Seconds);
|
|
41
|
+
const moonSynodicMonthDays = moonSiderealMonth * meanSiderealYearDays / (meanSiderealYearDays - moonSiderealMonth);
|
|
42
|
+
|
|
43
|
+
// The Layer-2 month/precession chain (Brouwer-Clemence m² scaling × the
|
|
44
|
+
// e_E-line modulation on the ONE deep e — decision (ii), plan 02 §8) is
|
|
45
|
+
// the deep-time chain's `moonChain` (deep-time-chain.js).
|
|
46
|
+
|
|
47
|
+
// Chain-cycle integrator. S5/S12 conventions: age anchor = startmodelYear
|
|
48
|
+
// (the scene's t_Ma convention), grid anchor C(2000) = 0 — grid anchor ≠
|
|
49
|
+
// age anchor, deliberately. One stable period fn per chain so the shared
|
|
50
|
+
// Float64Array tables key correctly and persist.
|
|
51
|
+
const chainCycles = createChainCycleIntegrator({
|
|
52
|
+
ageAnchorYear: startmodelYear,
|
|
53
|
+
tropicalYearJ2000Seconds: meanTropicalYearJ2000Seconds, // the SI tick (plan 09 step 7c′)
|
|
54
|
+
isDeepTime: () => true,
|
|
55
|
+
});
|
|
56
|
+
/** @param {number} tMa @returns {number|null} */
|
|
57
|
+
const nodalMonthPeriodFn = (tMa) => moonChain.nodalMonthSecondsAtAge(tMa);
|
|
58
|
+
/** @param {number} tMa @returns {number|null} */
|
|
59
|
+
const tropicalMonthPeriodFn = (tMa) => moonChain.tropicalMonthSecondsAtAge(tMa);
|
|
60
|
+
/** @param {number} tMa @returns {number|null} */
|
|
61
|
+
const anomalisticMonthPeriodFn = (tMa) => moonChain.anomalisticMonthSecondsAtAge(tMa);
|
|
62
|
+
/** @param {number} tMa @returns {number|null} */
|
|
63
|
+
const siderealMonthPeriodFn = (tMa) => moonChain.siderealMonthSecondsAtAge(tMa);
|
|
64
|
+
const jupiterT0Seconds = C.planetOrbitalElements.jupiter.solarYearInput * 86400;
|
|
65
|
+
/** @param {number} tMa @returns {number} */
|
|
66
|
+
const jupiterPeriodFn = (tMa) => driver2PeriodSecondsAtAge(tMa, jupiterT0Seconds, EPOCH_PARAMS.solarMassLossFracPerYear);
|
|
67
|
+
/** @param {number} a @param {number} b @returns {number|null} */
|
|
68
|
+
const mcDraconic = (a, b) => chainCycles.cyclesBetween(nodalMonthPeriodFn, a, b);
|
|
69
|
+
/** @param {number} a @param {number} b @returns {number|null} */
|
|
70
|
+
const mcTropical = (a, b) => chainCycles.cyclesBetween(tropicalMonthPeriodFn, a, b);
|
|
71
|
+
/** @param {number} a @param {number} b @returns {number|null} */
|
|
72
|
+
const mcAnomalistic = (a, b) => chainCycles.cyclesBetween(anomalisticMonthPeriodFn, a, b);
|
|
73
|
+
/** @param {number} a @param {number} b @returns {number|null} */
|
|
74
|
+
const mcJupiter = (a, b) => chainCycles.cyclesBetween(jupiterPeriodFn, a, b);
|
|
75
|
+
/** The Moon's sidereal (star-referenced) orbits — the series' A3 argument (plan 09 step 5c). @param {number} a @param {number} b @returns {number|null} */
|
|
76
|
+
const mcSidereal = (a, b) => chainCycles.cyclesBetween(siderealMonthPeriodFn, a, b);
|
|
77
|
+
/** @param {number} a @param {number} b @returns {number|null} */
|
|
78
|
+
const mcApsidalOfDate = (a, b) => {
|
|
79
|
+
const t = mcTropical(a, b), n = mcAnomalistic(a, b);
|
|
80
|
+
return (t === null || n === null) ? null : t - n;
|
|
81
|
+
};
|
|
82
|
+
/** @param {number} a @param {number} b @returns {number|null} */
|
|
83
|
+
const mcNodalOfDate = (a, b) => {
|
|
84
|
+
const dr = mcDraconic(a, b), t = mcTropical(a, b);
|
|
85
|
+
return (dr === null || t === null) ? null : dr - t;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// The argument skeleton (the _FW_MOON bundle; Sun secular deviations on
|
|
89
|
+
// the CALENDAR year coordinate — S3)
|
|
90
|
+
const moonArgs = createMoonArguments({
|
|
91
|
+
constants: {
|
|
92
|
+
j2000JD,
|
|
93
|
+
julianCenturyDays,
|
|
94
|
+
eccentricityJ2000: C.earthOrbital.earthEccentricityJ2000,
|
|
95
|
+
eccentricityDotJ2000: C.earthOrbital.earthEccentricityDotJ2000,
|
|
96
|
+
eccentricityDotDotJ2000: C.earthOrbital.earthEccentricityDotDotJ2000,
|
|
97
|
+
elpEarthFigureJ2ArcsecPerCy2: C.moonMeeus.elpW1T2Decomposition_arcsecPerCy2.earthFigureJ2,
|
|
98
|
+
elpGeneralPrecessionPA_T2ArcsecPerCy2: C.moonMeeus.elpW1T2Decomposition_arcsecPerCy2.generalPrecessionPA_T2_Lieske1976,
|
|
99
|
+
// Decision (ii): the lunar chain reads ONE deep e end to end
|
|
100
|
+
eccE0: deepEcc.e0,
|
|
101
|
+
},
|
|
102
|
+
fns: {
|
|
103
|
+
eccAt: /** @param {number} tYr */ (tYr) => deepEcc.eccAt(tYr),
|
|
104
|
+
channelIntegral: /** @param {number} T @param {number} s */ (T, s) => deepEcc.channelIntegral(T, s),
|
|
105
|
+
// The arguments' obliquity carrier reads the PUBLISHED ε (the one-source
|
|
106
|
+
// hybrid) — one ε everywhere. Twins: script.js (_sceneEpsTargetDeg), scene-graph.js.
|
|
107
|
+
computeObliquityEarth: /** @param {number} year @returns {number} */ (year) => oneSourceM.epsAt(year),
|
|
108
|
+
// The arguments' Sun IS the one certified Sun — the mean longitude that
|
|
109
|
+
// integrates the one-family tropical year (the eclipse finder's Sun,
|
|
110
|
+
// sun-and-eclipses.js) and the series' mean perihelion of date
|
|
111
|
+
// (unwrapped), both on the TT Julian-year axis. Twins: scene-graph.js,
|
|
112
|
+
// script.js.
|
|
113
|
+
sunMeanLonDegAtJdTT: /** @param {number} jdTT @returns {number} */ (jdTT) => late.sunMeanLongitudeDegAt(2000 + (jdTT - j2000JD) / 365.25),
|
|
114
|
+
sunPerihelionLonDegAtJdTT: /** @param {number} jdTT @returns {number} */ (jdTT) => sunPerihelionDegAt(2000 + (jdTT - j2000JD) / 365.25),
|
|
115
|
+
jdToSIyear: yearFromJD,
|
|
116
|
+
tropicalOrbitsBetween: mcTropical,
|
|
117
|
+
apsidalOfDateCyclesBetween: mcApsidalOfDate,
|
|
118
|
+
nodalOfDateCyclesBetween: mcNodalOfDate,
|
|
119
|
+
isDeepTime: () => true,
|
|
120
|
+
isFrameworkNative: () => true,
|
|
121
|
+
// (d′) of-date rate completion: the DYNAMICAL axial precession and the
|
|
122
|
+
// KINEMATIC pair's beat the chains embed. MATCHED TRIPLE with the
|
|
123
|
+
// tools-lib and browser wirings. The dynamical rate of date is the
|
|
124
|
+
// published precession period's (the one-family beat inside its window,
|
|
125
|
+
// the composed clock beyond — published-periods.js). Degrees per Julian year.
|
|
126
|
+
pDynDegPerYearAt: /** @param {number} year */ (year) => 360 / late.publishedAxialPrecessionYearsAtYear(year),
|
|
127
|
+
pKinDegPerYearAt: /** @param {number} year */ (year) => {
|
|
128
|
+
// MODEL-START-anchored age, matching the tools-lib/browser wirings
|
|
129
|
+
// bit-exactly — NOT yearToTMa's 2000.0 convention (the recorded
|
|
130
|
+
// 0.4977-yr parity trap, same class as getMoonDistanceKm in the series wiring below).
|
|
131
|
+
const t = (startmodelYear - year) / 1e6;
|
|
132
|
+
const sid = deepLod.siderealYearSecondsAtAge(t);
|
|
133
|
+
const trop = deepLod.tropicalYearSecondsAtAge(t);
|
|
134
|
+
return (sid === null || trop === null) ? 0 : 360 * (sid - trop) / sid;
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// UT→TT on the CALENDAR decimal-year coordinate (script.js Phase 9.16 —
|
|
140
|
+
// a linear-year approximation here once cost ~5–6 s of ΔT and ~1e-3° of
|
|
141
|
+
// Moon longitude at the Babylonian epochs)
|
|
142
|
+
/** @param {number} jd @returns {number} */
|
|
143
|
+
const jdTTFromUT = (jd) => {
|
|
144
|
+
const tMa = (startmodelYear - jdToDecimalYear(jd)) / 1e6;
|
|
145
|
+
const dT = meanDeltaTSecondsAtAge(tMa);
|
|
146
|
+
return Number.isFinite(dT) ? jd + dT / 86400 : jd;
|
|
147
|
+
};
|
|
148
|
+
// ΔT at a JD on the CALENDAR-year axis (mirrors deep-time frameworkDeltaT;
|
|
149
|
+
// NOT deltaTSeconds of delta-t.js, which adds the deltaTStart anchor)
|
|
150
|
+
/** @param {number} jd @returns {number} */
|
|
151
|
+
const frameworkDeltaTSecondsAtJD = (jd) => {
|
|
152
|
+
const decYear = startmodelYear + (jd - startmodelJD) / meanSolarYearDays;
|
|
153
|
+
const dT = meanDeltaTSecondsAtAge((startmodelYear - decYear) / 1e6);
|
|
154
|
+
return Number.isFinite(dT) ? dT : 0;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Bounded Meeus E-factor from the ONE deep e (decision (ii); the
|
|
158
|
+
// pure-Meeus polynomial A/B branch stays engine-local)
|
|
159
|
+
/** @param {number} dDays @returns {number} */
|
|
160
|
+
// (on the channel's Julian-year axis — the N-body series' time)
|
|
161
|
+
const fwEFactor = (dDays) => deepEcc.eFactorAt(dDays / 365.25);
|
|
162
|
+
|
|
163
|
+
// D2 derived additional-argument rates (deg/cy, from the J2000 month inputs —
|
|
164
|
+
// docs/109 §22 for the derivation)
|
|
165
|
+
const fwA2RateDegPerCy = 2 * ((360 * 36525) / moonTropicalMonthDays)
|
|
166
|
+
- (360 * 36525) / moonAnomalisticMonthDays
|
|
167
|
+
- 2 * ((360 * 36525) / C.planetOrbitalElements.jupiter.solarYearInput);
|
|
168
|
+
const fwA3RateDegPerCy = (360 * 36525) / moonSiderealMonth;
|
|
169
|
+
|
|
170
|
+
// Meeus Ch. 47 truncated series (framework-native arguments + E-factor)
|
|
171
|
+
const moonSeries = createMoonSeries({
|
|
172
|
+
constants: {
|
|
173
|
+
moonL: F.MEEUS_LONGITUDE_TERMS,
|
|
174
|
+
moonB: F.MEEUS_LATITUDE_TERMS,
|
|
175
|
+
moonR: F.MEEUS_DISTANCE_TERMS.terms,
|
|
176
|
+
moonRMeanKm: F.MEEUS_DISTANCE_TERMS.meanKm,
|
|
177
|
+
moonDistanceJ2000Km: C.moonReference.moonDistance,
|
|
178
|
+
j2000JD,
|
|
179
|
+
julianCenturyDays,
|
|
180
|
+
fwA2RateDegPerCy,
|
|
181
|
+
fwA3RateDegPerCy,
|
|
182
|
+
},
|
|
183
|
+
fns: {
|
|
184
|
+
argsAt: /** @param {number} jdTT */ (jdTT) => moonArgs.argsAt(jdTT),
|
|
185
|
+
eFactorForD: fwEFactor,
|
|
186
|
+
eFactorAtJdTT: /** @param {number} jdTT */ (jdTT) => fwEFactor(jdTT - j2000JD),
|
|
187
|
+
// 20.3d(i): the Driver-1 ratio at the EVALUATED epoch (per-jd pure
|
|
188
|
+
// evaluator). MATCHED TRIPLE with the tools-lib and browser getters:
|
|
189
|
+
// identical age arithmetic — the SI-linear year (yearFromJD, never
|
|
190
|
+
// the calendar year: the recorded linear-vs-calendar mirror trap)
|
|
191
|
+
// against the MODEL-START anchor (startmodelYear, the browser's
|
|
192
|
+
// J2000_CALENDAR_YEAR — NOT this model's 2000.0 yearToTMa convention:
|
|
193
|
+
// the 0.5-yr difference is 4.95e-11 of distance and failed the
|
|
194
|
+
// bit-exact parity gate; plan 11 step 5 dropped the −0.83-d correction
|
|
195
|
+
// from the anchor, both twins alike). Falls back to the J2000 constant
|
|
196
|
+
// without a jd (legacy call shape) or past the domain.
|
|
197
|
+
getMoonDistanceKm: /** @param {number} [jdTT] */ (jdTT) => {
|
|
198
|
+
if (jdTT === undefined) return moonDistanceKm;
|
|
199
|
+
const d = moonDistanceMetresAtAge((startmodelYear - yearFromJD(jdTT)) / 1e6);
|
|
200
|
+
return d === null ? moonDistanceKm : d / 1000;
|
|
201
|
+
},
|
|
202
|
+
getEccentricityBase: () => C.moonReference.moonOrbitalEccentricityBase,
|
|
203
|
+
deltaTSeconds: /** @param {number} jd */ (jd) => (jdTTFromUT(jd) - jd) * 86400,
|
|
204
|
+
jdToSIyear: yearFromJD,
|
|
205
|
+
tropicalOrbitsBetween: mcTropical,
|
|
206
|
+
apsidalOfDateCyclesBetween: mcApsidalOfDate,
|
|
207
|
+
siderealOrbitsBetween: mcSidereal,
|
|
208
|
+
jupiterOrbitsBetween: mcJupiter,
|
|
209
|
+
isDeepTime: () => true,
|
|
210
|
+
isFrameworkNative: () => true,
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return { frameworkDeltaTSecondsAtJD, j2000JD, jdTTFromUT, julianCenturyDays, moonArgs, moonSeries, moonSynodicMonthDays };
|
|
215
|
+
}
|
|
216
|
+
/** @typedef {import('./time-axis.js').Ctx & ReturnType<typeof assembleLunarTheory>} Ctx */
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Moon at epoch — the solar-Δa-corrected Kepler month.
|
|
3
|
+
*
|
|
4
|
+
* One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** @param {import('./one-source-movement.js').Ctx} ctx @returns the bindings the later slices read */
|
|
8
|
+
export function assembleMoonEpoch(ctx) {
|
|
9
|
+
const {
|
|
10
|
+
EPOCH_PARAMS, MASS_RATIO_EARTH_MOON, deepLod, moonDistanceMetresAtAge, moonSiderealMonthInput, yearToTMa,
|
|
11
|
+
} = ctx;
|
|
12
|
+
/** Solar-Δa-corrected Kepler month. @param {number} year @returns {number} */
|
|
13
|
+
const moonSiderealMonthDaysAt = (year) => {
|
|
14
|
+
const tMa = yearToTMa(year);
|
|
15
|
+
const lod = deepLod.lodSecondsAtAge(tMa);
|
|
16
|
+
const aAppM = moonDistanceMetresAtAge(tMa);
|
|
17
|
+
if (lod === null || aAppM === null) return NaN;
|
|
18
|
+
const aAppKm = aAppM / 1000;
|
|
19
|
+
const sidYrDays = deepLod.siderealYearSecondsAtAge(tMa) / lod;
|
|
20
|
+
const deltaA = aAppKm * (1 / (MASS_RATIO_EARTH_MOON + 1)) * (moonSiderealMonthInput / sidYrDays);
|
|
21
|
+
const aCorrM = (aAppKm + deltaA) * 1000;
|
|
22
|
+
const monthSeconds = 2 * Math.PI * Math.sqrt(Math.pow(aCorrM, 3) / EPOCH_PARAMS.gmEarthMoonM3S2);
|
|
23
|
+
return monthSeconds / lod;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
return { moonSiderealMonthDaysAt };
|
|
27
|
+
}
|
|
28
|
+
/** @typedef {import('./one-source-movement.js').Ctx & ReturnType<typeof assembleMoonEpoch>} Ctx */
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one-source movement — the hybrid's ε(t), e(t), ϖ(t) on the per-tier sampler, the ONE year-length family, and the days of date on them.
|
|
3
|
+
*
|
|
4
|
+
* One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
|
|
5
|
+
*/
|
|
6
|
+
import { createDeepOrbitalHistory } from '../earth/deep-orbital-history.cjs';
|
|
7
|
+
import { ONE_FAMILY_WINDOW_YEARS, createYearLengths } from '../earth/year-lengths.cjs';
|
|
8
|
+
import { DEEP_MODES_ARTIFACT } from '../moon/deep-modes-artifact.cjs';
|
|
9
|
+
import { CHAIN_ARTIFACT } from '../planets/chain-artifact.js';
|
|
10
|
+
|
|
11
|
+
/** @param {import('./year-day-lengths.js').Ctx} ctx @returns the bindings the later slices read */
|
|
12
|
+
export function assembleOneSourceMovement(ctx) {
|
|
13
|
+
const {
|
|
14
|
+
C, options, secularSeriesArtifact, EPOCH_PARAMS, alphaGia, certifiedAxialPrecessionJ2000Years, dayLengthSeconds,
|
|
15
|
+
deepLod, dtCycleLodCorrectionSum, meanSolarYearDays, startmodelYear, yearToTMa,
|
|
16
|
+
} = ctx;
|
|
17
|
+
// The cardinal INSTANTS are the crossings of the one Sun the scene renders
|
|
18
|
+
// and the eclipse chain certifies (cardinal-instants.js); the cardinal
|
|
19
|
+
// STRUCTURE here is the EoC layer (year lengths, crossing offsets, the
|
|
20
|
+
// e(t)-proportional spread) on the one-source movement's own e(t)/ϖ(t) —
|
|
21
|
+
// the MODE tier (the package's embedded deep-modes artifact; the series
|
|
22
|
+
// tier when the governed artifact is injected). Absolute dates deliberately
|
|
23
|
+
// absent — see cardinal/one-source-structure.cjs. The mean year is SI
|
|
24
|
+
// SECONDS: the sidereal year of date reduced by the SECULAR equinox
|
|
25
|
+
// precession on the composed T_p(t).
|
|
26
|
+
// THE ONE of-date year-length family — createYearLengths owns tropical
|
|
27
|
+
// (equinox-rate mean, wobble included), sidereal (the λ̇ channel),
|
|
28
|
+
// anomalistic (the cardinal structure on the SAME mean), all λ̇-corrected
|
|
29
|
+
// coherently, SI seconds, and the beats of that pair.
|
|
30
|
+
// S3 tier unification: when the caller injects the governed secular-
|
|
31
|
+
// series artifact (createModel opts.secularSeriesArtifact — the API/Node
|
|
32
|
+
// path; the npm package alone has no 8 MB series), the movement runs the
|
|
33
|
+
// SERIES tier exactly like the browser — killing the mode-vs-series
|
|
34
|
+
// value split (measured: 6 s on the anomalistic year at J2000).
|
|
35
|
+
// The ONE-SOURCE movement: the hybrid's ε(t) (plan 06 Phase 3 S3b — THE
|
|
36
|
+
// published obliquity) and the one-family year lengths, on the same
|
|
37
|
+
// per-tier sampler.
|
|
38
|
+
const oneSourceM = (() => {
|
|
39
|
+
const AEarth = /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements.earth;
|
|
40
|
+
// The ψ̇ anchor: the IAU 2006 general precession p_A (25,771.575 yr — the
|
|
41
|
+
// ONE home certifiedAxialPrecessionJ2000Years, twins in tools-lib and the
|
|
42
|
+
// browser).
|
|
43
|
+
const axial0 = certifiedAxialPrecessionJ2000Years(); // the ONE home
|
|
44
|
+
// The deep-time ψ̇(t) the hybrid precesses on is the COMPOSED lunisolar
|
|
45
|
+
// rate — its period of date T_p(t) read directly; the composition carries
|
|
46
|
+
// the lunar 1/a³ growth (104.5 ″/yr at 2.46 Ga against Lantink 2022's
|
|
47
|
+
// 108.6 ± 8.5).
|
|
48
|
+
const seriesArt = /** @type {any} */ (secularSeriesArtifact);
|
|
49
|
+
const sb = seriesArt && seriesArt.bodies && seriesArt.bodies.earth;
|
|
50
|
+
// Plan 12 §8.5 (owner): the hybrid's own domain is the banked ζ series' span (years; null
|
|
51
|
+
// without a series). Beyond it the ζ-mode tail carries the Quaternary mean with no tidal
|
|
52
|
+
// evolution — the API refuses the obliquity there and serves the cited mean beside it.
|
|
53
|
+
const obliquityHybridDomainYears = sb
|
|
54
|
+
? /** @type {[number, number]} */ ([2000 + seriesArt.t0Yr, 2000 + seriesArt.t0Yr + (sb.zetaQ.length - 1) * sb.stepYr])
|
|
55
|
+
: null;
|
|
56
|
+
const hist = createDeepOrbitalHistory({
|
|
57
|
+
zModes: DEEP_MODES_ARTIFACT.earthZ,
|
|
58
|
+
zetaModes: DEEP_MODES_ARTIFACT.earthZeta,
|
|
59
|
+
...(sb ? {
|
|
60
|
+
zetaSeries: { t0Yr: seriesArt.t0Yr, stepYr: sb.stepYr, q: sb.zetaQ, p: sb.zetaP },
|
|
61
|
+
zSeries: { t0Yr: seriesArt.t0Yr, stepYr: sb.stepYr, q: sb.zQ, p: sb.zP },
|
|
62
|
+
} : {}),
|
|
63
|
+
anchorE: AEarth.e,
|
|
64
|
+
anchorPeriEclipticDeg: AEarth.lonPeriEclipticDeg,
|
|
65
|
+
anchorInclEclipticDeg: AEarth.inclEclipticDeg,
|
|
66
|
+
anchorAscNodeEclipticDeg: AEarth.ascNodeEclipticDeg,
|
|
67
|
+
axialPrecessionYearsJ2000: axial0,
|
|
68
|
+
// The hybrid's initial condition is the OBSERVED J2000 mean obliquity —
|
|
69
|
+
// the IAU 2006 input constant (84381.406″), the same anchor the
|
|
70
|
+
// browser's _deepHistSeries and the Node one-source movement use.
|
|
71
|
+
obliquityJ2000Deg: C.earthOrbital.obliquityJ2000_deg,
|
|
72
|
+
// The CONSTANT's scaling (the torque period), not the published rate of date: the
|
|
73
|
+
// integration's own ŝ·n̂ carries the obliquity's cosine, so the cited mean obliquity's
|
|
74
|
+
// factor (plan 12 §8 option (a)) must not enter twice. Bit-identical to the pre-§8 form.
|
|
75
|
+
axialPrecessionYearsAtYearFn: (yr) => deepLod.lunisolarTorquePeriodYearsAtAge((startmodelYear - yr) / 1e6) ?? axial0,
|
|
76
|
+
// THE PRECESSION OF DATE: on top of the secular law above, the solar
|
|
77
|
+
// torque at the eccentricity of date and the oblateness of date from
|
|
78
|
+
// the GIA channel — both exactly 1 at J2000, so the J2000 anchor is
|
|
79
|
+
// untouched. The channel is
|
|
80
|
+
// read PURE (alphaGia, not the lattice-pin-aware earthMoiFactorAtAge):
|
|
81
|
+
// the samplers are cached, their values must be pure in year.
|
|
82
|
+
solarTorqueShareJ2000: EPOCH_PARAMS.precessionSolarShareJ2000,
|
|
83
|
+
// options.giaEllipticityOfDate === false: the orbital forcing alone (the T1 line derivation).
|
|
84
|
+
dynamicalEllipticityRatioAtYearFn: options.giaEllipticityOfDate === false ? undefined : alphaGia.j2RatioAt,
|
|
85
|
+
});
|
|
86
|
+
// PER-TIER ROUTING (the anomalistic-contamination fix): one sampler PER
|
|
87
|
+
// grid tier, built on first entry and KEPT — a query always reads the
|
|
88
|
+
// tier its own span selects. The former single grown sampler replaced
|
|
89
|
+
// the 100-yr grid with the 1000/5000-yr one after any deep-time probe,
|
|
90
|
+
// and the year-length rates (±0.5-yr central differences through the
|
|
91
|
+
// grid) then returned grid-segment averages instead of local rates
|
|
92
|
+
// (anomalistic of date +2.63 s / +19 s, visit-order dependent).
|
|
93
|
+
const samplers = /** @type {Map<number, any>} */ (new Map());
|
|
94
|
+
const gridStep = (/** @type {number} */ n) => (n <= 50000 ? 100 : n <= 2000000 ? 1000 : 5000);
|
|
95
|
+
const tierSpan = (/** @type {number} */ n) => (n <= 50000 ? 50000 : 2000000);
|
|
96
|
+
const sampleAt = (/** @type {number} */ year) => {
|
|
97
|
+
const t = year - 2000, need = Math.max(20000, Math.abs(t) * 1.25);
|
|
98
|
+
if (need > 2000000) {
|
|
99
|
+
// The deep tier (5000-yr grid): walked in the queried DIRECTION only,
|
|
100
|
+
// to two nodes past the query, and EXTENDED by the factory's shared
|
|
101
|
+
// walk when a deeper epoch arrives (plan 13 A1). It was built to
|
|
102
|
+
// ±1.25·|t| in both directions and rebuilt from J2000 on every deeper
|
|
103
|
+
// probe — bit-identical values, 4× the RK4 steps of one walk.
|
|
104
|
+
const span = Math.ceil((Math.abs(t) + 2 * 5000) / 5000) * 5000;
|
|
105
|
+
return hist.build(t < 0 ? -span : span, 0, 5000).at(t);
|
|
106
|
+
}
|
|
107
|
+
const span = tierSpan(need);
|
|
108
|
+
if (!samplers.has(span)) samplers.set(span, hist.build(span, -span, gridStep(span)));
|
|
109
|
+
return samplers.get(span).at(t);
|
|
110
|
+
};
|
|
111
|
+
// The anomalistic year rides THIS movement's own apsidal line (the
|
|
112
|
+
// scene's perihelion) — the factory owns the construction; the planet
|
|
113
|
+
// chain's secular tangent is no longer a member (year-lengths.cjs).
|
|
114
|
+
const yearLengths = createYearLengths({
|
|
115
|
+
sampleAt,
|
|
116
|
+
massLossSiderealSecondsAtYearFn: (year) => deepLod.siderealYearSecondsAtAge(yearToTMa(year)),
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
yearLengths,
|
|
120
|
+
/** The one-source sample at a decimal year (the engine year) — ε, e, ϖ of date, the Earth-orbit normal (the fields the frame of date and the invariable-plane elements read). @param {number} year */
|
|
121
|
+
sampleAt,
|
|
122
|
+
/** EXPERIMENT accessors: the series' e and equinox-referenced ϖ of date. @param {number} year @returns {number} */
|
|
123
|
+
eAt: (year) => sampleAt(year).e,
|
|
124
|
+
/** @param {number} year @returns {number} */
|
|
125
|
+
periOfDateDegAt: (year) => sampleAt(year).periOfDateDeg,
|
|
126
|
+
/** The hybrid's obliquity at a decimal year, degrees — the banked series inside its span, the α(t)-coupled ζ-tail integration beyond (the deep sampler grows ~0.1 s/Myr). @param {number} year @returns {number} */
|
|
127
|
+
epsAt: (year) => sampleAt(year).epsDeg,
|
|
128
|
+
/** The hybrid's own domain, [yearFrom, yearTo] — the banked ζ series' span (plan 12 §8.5); null without a series. */
|
|
129
|
+
obliquityHybridDomainYears,
|
|
130
|
+
/** The precession of date's two factors at a decimal year (the factory's own — ONE home). @param {number} year @returns {{solarTorque: number, ellipticity: number}} */
|
|
131
|
+
precessionOfDateFactorsAt: (year) => hist.precessionOfDateFactorsAt(year - 2000),
|
|
132
|
+
/** The spin axis's INERTIAL precession period at J2000, years — the self-anchored
|
|
133
|
+
* lunisolar rate the hybrid integrates ŝ on (deep-orbital-history D4e): the general
|
|
134
|
+
* precession anchor plus the planetary mean the ecliptic's own motion carries. */
|
|
135
|
+
axialPrecessionYearsLunisolarJ2000: hist.axialPrecessionYearsLunisolarJ2000,
|
|
136
|
+
};
|
|
137
|
+
})();
|
|
138
|
+
const yearLengthsM = oneSourceM.yearLengths;
|
|
139
|
+
/** THE one-family tropical year of date in SI seconds: route B inside the
|
|
140
|
+
* one-family window, the tidal-chain year beyond (plan 06 R1 — the Sun's
|
|
141
|
+
* mean longitude, the cardinal instants and the published year lengths
|
|
142
|
+
* all ride this one function). @param {number} year @returns {number} */
|
|
143
|
+
const oneFamilyTropicalYearSeconds = (year) => (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS
|
|
144
|
+
? yearLengthsM.tropicalYearSecondsAtYear(year)
|
|
145
|
+
: (deepLod.tropicalYearSecondsAtAge(yearToTMa(year)) ?? meanSolarYearDays * 86400));
|
|
146
|
+
|
|
147
|
+
/** Tropical year of date in DAYS OF THE EPOCH (the kinematic day,
|
|
148
|
+
* year-day-lengths.js) — the one-family SI year over the epoch's day
|
|
149
|
+
* length, so solarYearSeconds ≡ the one-family year. @param {number} year @returns {number} */
|
|
150
|
+
const tropicalYearDays = (year) => oneFamilyTropicalYearSeconds(year) / dayLengthSeconds(year);
|
|
151
|
+
|
|
152
|
+
/** Layer-4 solar day of date in SI seconds — LOD_real, the shipped
|
|
153
|
+
* observable (the tweakpane Solar Day row, the registry's lodRealPhysical,
|
|
154
|
+
* the Framework Verification panel's day): the kinematic day of date + the
|
|
155
|
+
* ecliptic missing-motion term on the nodal period + the calibrated ΔT
|
|
156
|
+
* cycle stack incl. the core-mantle swing. Twin of tools/lib
|
|
157
|
+
* computeLodRealSecondsAtEpoch and the browser's _vfpSolarDaySecondsOfDate
|
|
158
|
+
* (86,400.001780 s at 2000). @param {number} year @returns {number} */
|
|
159
|
+
const lodRealSeconds = (year) =>
|
|
160
|
+
dayLengthSeconds(year)
|
|
161
|
+
+ (deepLod.eclipticLodCorrectionSecondsAtAge(yearToTMa(year)) ?? 0)
|
|
162
|
+
+ dtCycleLodCorrectionSum(year);
|
|
163
|
+
/** Mean sidereal day of date in SI seconds — on LOD_real (plan 08 storyline
|
|
164
|
+
* sweep, 2026-10-09: the published day bases are the SI day of exactly
|
|
165
|
+
* 86,400 s, the IAU constants, and LOD_real; this rode the KINEMATIC day
|
|
166
|
+
* before, 1.7 ms below the simulator's readout at J2000). One rotation
|
|
167
|
+
* more against the equinox than there are solar days in a year.
|
|
168
|
+
* @param {number} year @returns {number} */
|
|
169
|
+
const siderealDaySeconds = (year) => {
|
|
170
|
+
const Y = tropicalYearDays(year);
|
|
171
|
+
return (lodRealSeconds(year) * Y) / (Y + 1);
|
|
172
|
+
};
|
|
173
|
+
/** Mean stellar day of date in SI seconds — the sidereal day plus the
|
|
174
|
+
* equinox's daily regression projected onto the equator (on LOD_real, like
|
|
175
|
+
* the sidereal day). @param {number} year @returns {number} */
|
|
176
|
+
const stellarDaySeconds = (year) => {
|
|
177
|
+
const tMa = yearToTMa(year);
|
|
178
|
+
// One equinox turn per T_p(t) — the composed lunisolar period (its
|
|
179
|
+
// certified J2000 anchor past the chain's domain).
|
|
180
|
+
const TpRaw = deepLod.lunisolarPrecessionPeriodYearsAtAge(tMa);
|
|
181
|
+
const Tp = TpRaw === null ? certifiedAxialPrecessionJ2000Years() : TpRaw;
|
|
182
|
+
const syD = tropicalYearDays(year);
|
|
183
|
+
const sidDay = siderealDaySeconds(year);
|
|
184
|
+
// The RA projection reads the published ε (the one-source hybrid) — the
|
|
185
|
+
// browser's _sceneEpsTargetDeg twin; 1e-8 s.
|
|
186
|
+
const raProjection = Math.cos((oneSourceM.epsAt(year) * Math.PI) / 180);
|
|
187
|
+
return (sidDay / Tp / (syD + 1)) * raProjection + sidDay;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
return { lodRealSeconds, oneFamilyTropicalYearSeconds, oneSourceM, siderealDaySeconds, stellarDaySeconds, tropicalYearDays, yearLengthsM };
|
|
191
|
+
}
|
|
192
|
+
/** @typedef {import('./year-day-lengths.js').Ctx & ReturnType<typeof assembleOneSourceMovement>} Ctx */
|