@essrt/physics 23.3.3 → 24.0.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/cardinal/one-source-structure.cjs +3 -1
- package/src/constants/coefficients.d.ts +5 -1
- package/src/constants/coefficients.js +3 -2
- package/src/constants/generated.d.ts +13 -14
- package/src/constants/generated.js +14 -15
- package/src/constants/units.cjs +4 -1
- package/src/deltat/deep-time.cjs +37 -72
- package/src/earth/deep-orbital-history.cjs +48 -34
- package/src/earth/year-lengths.cjs +2 -1
- package/src/eclipse/besselian.cjs +24 -11
- 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 +332 -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 +3 -3
- package/src/planets/model.cjs +2 -2
- package/types/constants/coefficients.d.ts +5 -1
- package/types/constants/generated.d.ts +13 -14
- package/types/constants/units.d.cts +3 -0
- package/types/deltat/deep-time.d.cts +18 -24
- package/types/earth/deep-orbital-history.d.cts +23 -0
- package/types/eclipse/besselian.d.cts +37 -18
- 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 +53 -0
- package/types/model/surface.d.ts +281 -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 +10 -81
- package/types/moon/arguments.d.cts +5 -11
- package/types/planets/keplerian-chain.d.cts +4 -2
- package/types/planets/model.d.cts +3 -3
|
@@ -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.orbitalPeriodInputDays * 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.orbitalPeriodInputDays);
|
|
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 */
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The planets' elements of date — the chain with the series handover: ϖ, Ω (S&S origin), i, e.
|
|
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 { DEEP_MODES_ARTIFACT } from '../moon/deep-modes-artifact.cjs';
|
|
7
|
+
import { CHAIN_ARTIFACT } from '../planets/chain-artifact.js';
|
|
8
|
+
import { computeEquatorNodeOriginSFrameDeg, convertNodeSFrameToEquatorOriginDeg } from '../planets/inv-plane-frame.cjs';
|
|
9
|
+
import { buildPlanetChainsFromArtifactData, computePlanetElementsAtYear } from '../planets/keplerian-chain.cjs';
|
|
10
|
+
import { createSecularSeriesOverride } from '../planets/secular-series.cjs';
|
|
11
|
+
|
|
12
|
+
/** @param {import('./planet-spin-channel.js').Ctx} ctx @returns the bindings the later slices read */
|
|
13
|
+
export function assemblePlanetElementsOfDate(ctx) {
|
|
14
|
+
const { C, secularSeriesArtifact } = ctx;
|
|
15
|
+
// The planets' elements of date ride THE CHAIN — the only planet path the
|
|
16
|
+
// simulator renders — with the series handover beyond each planet's
|
|
17
|
+
// measured boundary when the series artifact is supplied: the mirror of the
|
|
18
|
+
// browser's _chainElementsOfDate → _chainSeriesSecularEl. The chain's
|
|
19
|
+
// argument is dynamical time; the API's decimal year is taken as that
|
|
20
|
+
// argument (ΔT on a secular element is far below its stated accuracy).
|
|
21
|
+
let planetChains = /** @type {any} */ (null);
|
|
22
|
+
let planetSeriesOverride = /** @type {any} */ (null);
|
|
23
|
+
let planetNodeOriginSSDeg = /** @type {number|null} */ (null);
|
|
24
|
+
/** @param {string} k @param {number} year */
|
|
25
|
+
const planetChainElementsAt = (k, year) => {
|
|
26
|
+
if (!planetChains) planetChains = buildPlanetChainsFromArtifactData(/** @type {any} */ (CHAIN_ARTIFACT));
|
|
27
|
+
const el = computePlanetElementsAtYear(year, planetChains[k], planetChains);
|
|
28
|
+
const seriesArt = /** @type {any} */ (secularSeriesArtifact);
|
|
29
|
+
if (!seriesArt) return el;
|
|
30
|
+
if (!planetSeriesOverride) {
|
|
31
|
+
planetSeriesOverride = createSecularSeriesOverride({
|
|
32
|
+
series: seriesArt,
|
|
33
|
+
anchorElements: /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements,
|
|
34
|
+
invariablePlane: /** @type {any} */ (CHAIN_ARTIFACT).invariablePlane,
|
|
35
|
+
planetZModes: /** @type {any} */ (DEEP_MODES_ARTIFACT).planetZ,
|
|
36
|
+
planetZetaModes: /** @type {any} */ (DEEP_MODES_ARTIFACT).planetZeta,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return planetSeriesOverride.applyToElements(k, year, el);
|
|
40
|
+
};
|
|
41
|
+
/** Ecliptic longitude of perihelion, J2000 ecliptic and equinox (the chain's ϖ). @param {string} k @param {number} year @returns {number} */
|
|
42
|
+
const planetPerihelionDeg = (k, year) => planetChainElementsAt(k, year).lonPeriEclipticDeg;
|
|
43
|
+
/** The S&S longitude origin as an s-frame longitude (J2000-fixed, derived). @returns {number} */
|
|
44
|
+
const nodeOriginSSDeg = () => {
|
|
45
|
+
if (planetNodeOriginSSDeg === null) {
|
|
46
|
+
planetNodeOriginSSDeg = computeEquatorNodeOriginSFrameDeg(/** @type {any} */ (CHAIN_ARTIFACT).invariablePlane, C.earthOrbital.obliquityJ2000_deg);
|
|
47
|
+
}
|
|
48
|
+
return planetNodeOriginSSDeg;
|
|
49
|
+
};
|
|
50
|
+
/** Ascending node on the model's invariable plane, from the plane's ascending node on the ICRF equator (the Souami & Souchay 2012 origin; derived conversion, inv-plane-frame). @param {string} k @param {number} year @returns {number} */
|
|
51
|
+
const planetAscNodeDeg = (k, year) => convertNodeSFrameToEquatorOriginDeg(planetChainElementsAt(k, year).ascNodeInvPlaneDeg, nodeOriginSSDeg());
|
|
52
|
+
/** Inclination to the model's invariable plane (exact orbit-normal rotation). @param {string} k @param {number} year @returns {number} */
|
|
53
|
+
const planetInclinationDeg = (k, year) => planetChainElementsAt(k, year).inclInvPlaneDeg;
|
|
54
|
+
/** Orbital eccentricity of date, from the same chain elements — the chain's
|
|
55
|
+
* multi-mode e-vector has no single base + amplitude + phase triple, so the
|
|
56
|
+
* value of date is the published form. @param {string} k @param {number} year @returns {number} */
|
|
57
|
+
const planetEccentricity = (k, year) => planetChainElementsAt(k, year).e;
|
|
58
|
+
|
|
59
|
+
return { nodeOriginSSDeg, planetAscNodeDeg, planetEccentricity, planetInclinationDeg, planetPerihelionDeg };
|
|
60
|
+
}
|
|
61
|
+
/** @typedef {import('./planet-spin-channel.js').Ctx & ReturnType<typeof assemblePlanetElementsOfDate>} Ctx */
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The planets' structural records — keys, mass fractions, the published J2000 record.
|
|
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 { PLANET_DISPLAY_NAMES } from '../planets/display-names.cjs';
|
|
8
|
+
import { computeObliquityJ2000Deg } from '../planets/spin-channel.cjs';
|
|
9
|
+
|
|
10
|
+
/** @param {import('./delta-t.js').Ctx} ctx @returns the bindings the later slices read */
|
|
11
|
+
export function assemblePlanetRecords(ctx) {
|
|
12
|
+
const { C, GM_EARTH_ALONE, G_CONSTANT, M_SUN } = ctx;
|
|
13
|
+
const PLANET_KEYS = ['mercury', 'venus', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune'];
|
|
14
|
+
const massEarthAlone = GM_EARTH_ALONE / G_CONSTANT;
|
|
15
|
+
/** @type {Record<string, number>} */
|
|
16
|
+
const massFraction = {};
|
|
17
|
+
for (const k of PLANET_KEYS) massFraction[k] = 1 / C.physicalConstants.massRatioDE440[k];
|
|
18
|
+
massFraction.earth = massEarthAlone / M_SUN;
|
|
19
|
+
|
|
20
|
+
/** @type {Record<string, Record<string, any>>} */
|
|
21
|
+
const PUBLISHED_PLANET_RECORDS = {}; // what model.planets.record() serves
|
|
22
|
+
for (const k of PLANET_KEYS) {
|
|
23
|
+
const ar = C.planetOrbitalElements[k];
|
|
24
|
+
// The obliquity is the DERIVED J2000 obliquity of the spin channel (the
|
|
25
|
+
// IAU pole against the chain's J2000 plane, acute form) — from the
|
|
26
|
+
// governed artifacts.
|
|
27
|
+
const obliquityDerived = computeObliquityJ2000Deg({
|
|
28
|
+
spin: C.planetSpinPhysical[k],
|
|
29
|
+
anchorInclEclipticDeg: /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements[k].inclEclipticDeg,
|
|
30
|
+
anchorAscNodeEclipticDeg: /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements[k].ascNodeEclipticDeg,
|
|
31
|
+
obliquityJ2000Deg: C.earthOrbital.obliquityJ2000_deg,
|
|
32
|
+
});
|
|
33
|
+
const obliquityMean = Math.min(obliquityDerived, 180 - obliquityDerived); // acute form of the derived J2000 obliquity
|
|
34
|
+
// What `model.planets.record()` PUBLISHES is the structural record: the
|
|
35
|
+
// REFERENCE inputs (IAU J2000 ϖ, the Souami & Souchay node and
|
|
36
|
+
// inclination, DE440 e, the period input, the axial tilt), the DERIVED
|
|
37
|
+
// `obliquityMean` (the spin channel against the chain's J2000 plane), and
|
|
38
|
+
// the two PERIODS that are chain eigenmodes — 1,296,000/g and 1,296,000/s
|
|
39
|
+
// (apsidal prograde positive, nodal regression negative). Quantities that
|
|
40
|
+
// vary with the epoch (e, i, Ω, ϖ of date) are served AT AN EPOCH by
|
|
41
|
+
// planet-elements-of-date.js and `/v1/bodies`' `at[]` rows, never as a
|
|
42
|
+
// single oscillation's mean + amplitude + phase — the chain's multi-mode
|
|
43
|
+
// vectors have none.
|
|
44
|
+
const chainModes = /** @type {any} */ (CHAIN_ARTIFACT);
|
|
45
|
+
const gArc = chainModes.g[k] && chainModes.g[k].arcsecPerYr;
|
|
46
|
+
const sArc = chainModes.s[k] && chainModes.s[k].arcsecPerYr;
|
|
47
|
+
PUBLISHED_PLANET_RECORDS[k] = Object.freeze({
|
|
48
|
+
name: PLANET_DISPLAY_NAMES[k], // presentation — planets/display-names, the deleted JSON's last leaf (plan 11 step 6)
|
|
49
|
+
orbitalPeriodInputDays: ar.orbitalPeriodInputDays,
|
|
50
|
+
longitudePerihelion: ar.longitudePerihelion,
|
|
51
|
+
ascendingNodeInvPlane: ar.ascendingNodeInvPlaneClosedForm, // the closed-form verified J2000 node — astro-reference since plan 11 step 2
|
|
52
|
+
invPlaneInclinationJ2000: ar.invPlaneInclinationJ2000,
|
|
53
|
+
orbitalEccentricityJ2000: ar.orbitalEccentricityJ2000,
|
|
54
|
+
axialTiltJ2000: ar.axialTiltJ2000,
|
|
55
|
+
obliquityMean,
|
|
56
|
+
perihelionEclipticYears: gArc ? 1296000 / gArc : undefined,
|
|
57
|
+
ascendingNodePeriod: sArc ? 1296000 / sArc : undefined,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { PLANET_KEYS, PUBLISHED_PLANET_RECORDS, massFraction };
|
|
62
|
+
}
|
|
63
|
+
/** @typedef {import('./delta-t.js').Ctx & ReturnType<typeof assemblePlanetRecords>} Ctx */
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The planets' spin channel — each planet's precession constant from its own torques on the chain's orbit.
|
|
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 { DEEP_MODES_ARTIFACT } from '../moon/deep-modes-artifact.cjs';
|
|
7
|
+
import { CHAIN_ARTIFACT } from '../planets/chain-artifact.js';
|
|
8
|
+
import { createPlanetSpinChannelFromArtifacts } from '../planets/spin-channel.cjs';
|
|
9
|
+
|
|
10
|
+
/** @param {import('./planet-records.js').Ctx} ctx @returns the bindings the later slices read */
|
|
11
|
+
export function assemblePlanetSpinChannel(ctx) {
|
|
12
|
+
const { C, GM_SUN, PLANET_KEYS, massFraction } = ctx;
|
|
13
|
+
// The planets' spin channel: the precession constant
|
|
14
|
+
// from each planet's OWN torques (astro-reference planetSpinPhysical) on the
|
|
15
|
+
// model's OWN orbit — the chain's J2000 a/e/plane and the deep ζ table —
|
|
16
|
+
// integrated as dŝ/dt = α(ŝ·n̂)(ŝ×n̂) from the IAU J2000 pole. ONE home:
|
|
17
|
+
// planets/spin-channel.cjs; built lazily per planet, pure in `year`.
|
|
18
|
+
/** @type {Map<string, ReturnType<typeof createPlanetSpinChannelFromArtifacts>>} */
|
|
19
|
+
const spinChannels = new Map();
|
|
20
|
+
/** @param {string} k */
|
|
21
|
+
const planetSpin = (k) => {
|
|
22
|
+
if (!PLANET_KEYS.includes(k)) throw new Error(`planets.spin: unknown planet '${k}'`);
|
|
23
|
+
let ch = spinChannels.get(k);
|
|
24
|
+
if (!ch) {
|
|
25
|
+
ch = createPlanetSpinChannelFromArtifacts({
|
|
26
|
+
key: k,
|
|
27
|
+
planetSpinPhysical: C.planetSpinPhysical,
|
|
28
|
+
chainAnchorElements: /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements,
|
|
29
|
+
planetZeta: /** @type {any} */ (DEEP_MODES_ARTIFACT).planetZeta,
|
|
30
|
+
massFractionOfSun: massFraction[k],
|
|
31
|
+
gmSunKm3S2: GM_SUN,
|
|
32
|
+
obliquityJ2000Deg: C.earthOrbital.obliquityJ2000_deg,
|
|
33
|
+
});
|
|
34
|
+
spinChannels.set(k, ch);
|
|
35
|
+
}
|
|
36
|
+
return ch;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return { planetSpin };
|
|
40
|
+
}
|
|
41
|
+
/** @typedef {import('./planet-records.js').Ctx & ReturnType<typeof assemblePlanetSpinChannel>} Ctx */
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The published periods of date — the seamed precession period, tropical year and sidereal year (one-family inside the window, the deep chain beyond).
|
|
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 { ONE_FAMILY_WINDOW_YEARS } from '../earth/year-lengths.cjs';
|
|
7
|
+
|
|
8
|
+
/** @param {import('./sun-and-eclipses.js').Ctx} ctx @returns the bindings the later slices read */
|
|
9
|
+
export function assemblePublishedPeriods(ctx) {
|
|
10
|
+
const { certifiedAxialPrecessionJ2000Years, deepLod, yearLengthsM, yearToTMa } = ctx;
|
|
11
|
+
// Plan 06 Phase 3 S2 — THE published of-date precession period and mean
|
|
12
|
+
// tropical year: the one-family route (B) inside the banked tiers
|
|
13
|
+
// (|Δyear| ≤ ONE_FAMILY_WINDOW_YEARS), the composed lunisolar period
|
|
14
|
+
// beyond (S5: on the derived J2000 anchor, so the two faces agree at
|
|
15
|
+
// 25,771.4 at J2000 and the deep tail is the same clock scaled; the
|
|
16
|
+
// tidal-chain year). Shared by `epoch` and `lunisolar`.
|
|
17
|
+
/** @param {number} year @returns {number} */
|
|
18
|
+
const publishedAxialPrecessionYearsAtYear = (year) => {
|
|
19
|
+
if (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS) return yearLengthsM.axialPrecessionYearsAtYear(year);
|
|
20
|
+
const p = deepLod.lunisolarPrecessionPeriodYearsAtAge(yearToTMa(year));
|
|
21
|
+
return p === null ? certifiedAxialPrecessionJ2000Years() : p;
|
|
22
|
+
};
|
|
23
|
+
/** @param {number} year @returns {number} */
|
|
24
|
+
const publishedTropicalYearSecondsAtYear = (year) => {
|
|
25
|
+
if (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS) return yearLengthsM.tropicalYearSecondsAtYear(year);
|
|
26
|
+
return deepLod.tropicalYearSecondsAtAge(yearToTMa(year));
|
|
27
|
+
};
|
|
28
|
+
/** The published sidereal year of date, SI seconds — the one-family λ̇
|
|
29
|
+
* channel inside the window, the mass-loss law beyond (plan 09: the
|
|
30
|
+
* `lengths` surface's days ride it). @param {number} year @returns {number} */
|
|
31
|
+
const publishedSiderealYearSecondsAtYear = (year) => {
|
|
32
|
+
if (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS) return yearLengthsM.siderealYearSecondsAtYear(year);
|
|
33
|
+
return deepLod.siderealYearSecondsAtAge(yearToTMa(year));
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return { publishedAxialPrecessionYearsAtYear, publishedSiderealYearSecondsAtYear, publishedTropicalYearSecondsAtYear };
|
|
37
|
+
}
|
|
38
|
+
/** @typedef {import('./sun-and-eclipses.js').Ctx & ReturnType<typeof assemblePublishedPeriods>} Ctx */
|