@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.
Files changed (65) hide show
  1. package/README.md +4 -1
  2. package/package.json +2 -2
  3. package/src/cardinal/one-source-structure.cjs +3 -1
  4. package/src/constants/coefficients.d.ts +5 -1
  5. package/src/constants/coefficients.js +3 -2
  6. package/src/constants/generated.d.ts +13 -14
  7. package/src/constants/generated.js +14 -15
  8. package/src/constants/units.cjs +4 -1
  9. package/src/deltat/deep-time.cjs +37 -72
  10. package/src/earth/deep-orbital-history.cjs +48 -34
  11. package/src/earth/year-lengths.cjs +2 -1
  12. package/src/eclipse/besselian.cjs +24 -11
  13. package/src/index.js +30 -48
  14. package/src/model/cardinal-instants.js +101 -0
  15. package/src/model/context.js +54 -0
  16. package/src/model/deep-time-chain.js +79 -0
  17. package/src/model/delta-t.js +38 -0
  18. package/src/model/derived-constants.js +58 -0
  19. package/src/model/earth-scalars.js +47 -0
  20. package/src/model/inv-plane-elements.js +56 -0
  21. package/src/model/lunar-theory.js +216 -0
  22. package/src/model/moon-epoch.js +28 -0
  23. package/src/model/one-source-movement.js +192 -0
  24. package/src/model/planet-elements-of-date.js +61 -0
  25. package/src/model/planet-records.js +63 -0
  26. package/src/model/planet-spin-channel.js +41 -0
  27. package/src/model/published-periods.js +38 -0
  28. package/src/model/sun-and-eclipses.js +199 -0
  29. package/src/model/surface.js +332 -0
  30. package/src/model/time-axis.js +24 -0
  31. package/src/model/year-day-lengths.js +42 -0
  32. package/src/model.js +62 -1612
  33. package/src/moon/arguments.cjs +27 -43
  34. package/src/planets/chain-artifact.js +2 -2
  35. package/src/planets/keplerian-chain.cjs +3 -3
  36. package/src/planets/model.cjs +2 -2
  37. package/types/constants/coefficients.d.ts +5 -1
  38. package/types/constants/generated.d.ts +13 -14
  39. package/types/constants/units.d.cts +3 -0
  40. package/types/deltat/deep-time.d.cts +18 -24
  41. package/types/earth/deep-orbital-history.d.cts +23 -0
  42. package/types/eclipse/besselian.d.cts +37 -18
  43. package/types/index.d.ts +1 -1
  44. package/types/model/cardinal-instants.d.ts +14 -0
  45. package/types/model/context.d.ts +87 -0
  46. package/types/model/deep-time-chain.d.ts +288 -0
  47. package/types/model/delta-t.d.ts +11 -0
  48. package/types/model/derived-constants.d.ts +25 -0
  49. package/types/model/earth-scalars.d.ts +14 -0
  50. package/types/model/inv-plane-elements.d.ts +6 -0
  51. package/types/model/lunar-theory.d.ts +72 -0
  52. package/types/model/moon-epoch.d.ts +10 -0
  53. package/types/model/one-source-movement.d.ts +74 -0
  54. package/types/model/planet-elements-of-date.d.ts +9 -0
  55. package/types/model/planet-records.d.ts +7 -0
  56. package/types/model/planet-spin-channel.d.ts +29 -0
  57. package/types/model/published-periods.d.ts +7 -0
  58. package/types/model/sun-and-eclipses.d.ts +53 -0
  59. package/types/model/surface.d.ts +281 -0
  60. package/types/model/time-axis.d.ts +12 -0
  61. package/types/model/year-day-lengths.d.ts +8 -0
  62. package/types/model.d.ts +10 -81
  63. package/types/moon/arguments.d.cts +5 -11
  64. package/types/planets/keplerian-chain.d.cts +4 -2
  65. package/types/planets/model.d.cts +3 -3
@@ -0,0 +1,199 @@
1
+ /**
2
+ * The certified Sun and the eclipse tier — the mean longitude of date, the finders, the planetary completion, the besselian location tier.
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
+ import { createBesselian } from '../eclipse/besselian.cjs';
8
+ import { createEclipseFinders } from '../eclipse/finders.cjs';
9
+ import { computeCarrierRatesDegPerCy, createSunPlanetaryCompletion } from '../eclipse/sun-planetary-completion.cjs';
10
+
11
+ /** @param {import('./lunar-theory.js').Ctx} ctx @returns the bindings the later slices read */
12
+ export function assembleSunAndEclipses(ctx) {
13
+ const {
14
+ C, F, late, MASS_RATIO_EARTH_MOON, currentAUDistance, frameworkDeltaTSecondsAtJD, j2000JD, jdTTFromUT,
15
+ julianCenturyDays, meanSiderealYearDays, meanSolarYearDays, moonArgs, moonDistanceKm, moonSeries,
16
+ moonSiderealMonthInput, moonSynodicMonthDays, obliquityDeg, oneFamilyTropicalYearSeconds, sunEccentricityAt,
17
+ sunPerihelionDegAt, yearFromJD,
18
+ } = ctx;
19
+ // The certified Sun — assembled from the model's own laws, ZERO fitted Sun
20
+ // constants: e(t) and ϖ(t) are the eclipse Sun's mean elements of date
21
+ // (earth-scalars.js: the one-source series plus the derived mean offset);
22
+ // L(t) is the integral of the one-family tropical year (below). The
23
+ // finders, the derived planetary completion and the besselian location
24
+ // tier are wired on it; the sun-vs-horizons gate banks its modern-window
25
+ // residual, the eclipse audit the finders' canon statistics.
26
+ const sunL0Deg = C.earthOrbital.sunMeanLongitudeJ2000_deg;
27
+ const sunTropicalRateDegPerCy = 360 * julianCenturyDays / meanSolarYearDays;
28
+ const sunMeanLongitudeDegAt = (() => {
29
+ // The Sun's mean longitude of date is the INTEGRAL of the one-source
30
+ // tropical year: L(t) = L0 + 360·∫_{2000}^{t} dt′/T_trop(t′), T_trop the
31
+ // one-family tropical year of date in SI seconds (one-source-movement.js).
32
+ // MEASURED: it equals the IAU/Laskar drift expression to 0.1 s over
33
+ // −1000..+3000. The smooth year cannot carry the Earth–Mars–Jupiter long
34
+ // inequality (1783 yr, 6.3″); derived on the model's own N-body and
35
+ // shipped in the planetary completion, the JPL instrument reads 1.03″
36
+ // all-phase sd (Horizons ±3000 yr: modern-window mean +0.8″).
37
+ // NUMERICS (rate vs point value): a cumulative trapezoid table of
38
+ // cycles, yearly inside ±20,000 yr and per century beyond, grown on
39
+ // demand from 2000 in both directions and interpolated inside a cell —
40
+ // never a rate multiplied by a span. Beyond the one-family window the
41
+ // tidal-chain year continues the integrand (the scene overlay ends at
42
+ // 20,000 yr; the finders refuse out-of-domain epochs upstream).
43
+ const JULIAN_YEAR_S = 365.25 * 86400;
44
+ const tropSec = oneFamilyTropicalYearSeconds;
45
+ const FINE_SPAN = 20000, COARSE_STEP = 100;
46
+ /** cycles from 2000 to 2000 ± i·step (fwd/bwd tiers) */
47
+ /** @type {number[]} */ const fwdFine = [0];
48
+ /** @type {number[]} */ const bwdFine = [0];
49
+ /** @type {number[]} */ const fwdCoarse = [];
50
+ /** @type {number[]} */ const bwdCoarse = [];
51
+ /** UNSIGNED trapezoid cycles over the span between two years (Julian
52
+ * years → SI seconds over the one-family year); the caller applies the
53
+ * direction sign. @param {number} ya @param {number} yb @returns {number} */
54
+ const segAbs = (ya, yb) => 0.5 * (Math.abs(yb - ya) * JULIAN_YEAR_S) * (1 / tropSec(ya) + 1 / tropSec(yb));
55
+ /** signed cycles from 2000 to `year` @param {number} year @returns {number} */
56
+ const cyclesTo = (year) => {
57
+ const dy = year - 2000;
58
+ const s = dy >= 0 ? 1 : -1, a = Math.abs(dy);
59
+ // WARM THE SAMPLER TO THE TARGET FIRST (plan 06 R4, measured): the
60
+ // table below grows outward in 1-yr / 100-yr steps, and every step
61
+ // beyond ~1.6 Myr enlarged the one-family sampler's >2-Myr tier just
62
+ // past its span, rebuilding the whole deep integration about every 32
63
+ // steps — ~125 rebuilds and 22 s for the first deep-epoch call (the
64
+ // scene's first jump to −5 Myr), 0.4 ms once built. One read at the
65
+ // clamped target builds the tier at its final span; the grid stores
66
+ // are endpoint-independent, so every value is bit-identical.
67
+ tropSec(2000 + s * Math.min(a, ONE_FAMILY_WINDOW_YEARS));
68
+ const fine = s > 0 ? fwdFine : bwdFine;
69
+ const growFine = (/** @type {number} */ upto) => { while (fine.length <= upto) { const k = fine.length; fine.push(fine[k - 1] + s * segAbs(2000 + s * (k - 1), 2000 + s * k)); } };
70
+ if (a <= FINE_SPAN) {
71
+ const i = Math.floor(a), f = a - i;
72
+ growFine(i + 1);
73
+ return fine[i] + f * (fine[i + 1] - fine[i]);
74
+ }
75
+ growFine(FINE_SPAN);
76
+ const coarse = s > 0 ? fwdCoarse : bwdCoarse;
77
+ if (coarse.length === 0) coarse.push(fine[FINE_SPAN]);
78
+ const c = (a - FINE_SPAN) / COARSE_STEP, ci = Math.floor(c), cf = c - ci;
79
+ while (coarse.length <= ci + 1) { const k = coarse.length; const y0 = 2000 + s * (FINE_SPAN + (k - 1) * COARSE_STEP); coarse.push(coarse[k - 1] + s * segAbs(y0, y0 + s * COARSE_STEP)); }
80
+ return coarse[ci] + cf * (coarse[ci + 1] - coarse[ci]);
81
+ };
82
+ return /** @param {number} year @returns {number} */ (year) => sunL0Deg + 360 * cyclesTo(year);
83
+ })();
84
+ // The lunar arguments' Sun perihelion of date is the eclipse Sun's mean
85
+ // perihelion (`sunPerihelionDegAt`) read DIRECTLY, wrapped.
86
+ // Every consumer wraps the argument it enters (M = Lsun − ϖ − 180) or
87
+ // differences it over ±25 days (the rate anchors, wrapped to ±180°), so
88
+ // no continuous angle is needed. MEASURED: a 100-yr unwrapping ladder
89
+ // (two series reads per rung, 53,400 rungs at −5.34 Myr) cost the cold
90
+ // deep jump 1,062 s — 343× the in-table jump against the perf gate's 8× —
91
+ // for a value the consumers reduced mod 360 anyway.
92
+
93
+ // Eclipse finders — wired like the engine probe (tools/verify/
94
+ // eclipse-audit.js). The finder axis is JD(UT): the series wrapper applies
95
+ // UT→TT internally. Ground-track/umbra paths deliberately absent: the
96
+ // scene-umbra projection navigates the Tychosium-derived scaffold, which
97
+ // never enters this package (§2h).
98
+ const eclipseFinders = createEclipseFinders({
99
+ moonLonDegAt: /** @param {number} jd */ (jd) => moonSeries.truncatedLonDeg(jd),
100
+ moonBetaDegAt: /** @param {number} jd */ (jd) => moonSeries.truncatedBetaDeg(jd),
101
+ moonDistanceKmAt: /** @param {number} jd */ (jd) => moonSeries.truncatedDistanceKm(jd),
102
+ deltaTSecondsAt: frameworkDeltaTSecondsAtJD,
103
+ getSynodicMonthDays: () => moonSynodicMonthDays,
104
+ getSunDistanceKm: () => currentAUDistance,
105
+ frameworkSun: {
106
+ sunMeanLongitudeJ2000Deg: sunL0Deg,
107
+ tropicalRateDegPerCy: sunTropicalRateDegPerCy,
108
+ eccentricityAt: sunEccentricityAt,
109
+ perihelionLongitudeDegAt: sunPerihelionDegAt,
110
+ meanLongitudeDegAt: sunMeanLongitudeDegAt,
111
+ },
112
+ constants: {
113
+ rEarthMetres: (C.bodyDiametersKm.earth / 2) * 1000,
114
+ moonDiameterKm: C.bodyDiametersKm.moon,
115
+ sunDiameterKm: C.bodyDiametersKm.sun,
116
+ j2000JD,
117
+ julianCenturyDays,
118
+ },
119
+ });
120
+
121
+ // 20.3g — the solar-eclipse LOCATION tier: shadow geometry on the FULL
122
+ // series (sceneEvalAt — the truncated finder forms omit the fitted Lp
123
+ // anchor and the −2235·sin(Lp) β family, deliberate for finder
124
+ // statistics, required here) at the model's ABSOLUTE TT (deltaTStart +
125
+ // curve, the same convention every other ΔT consumer reads). Both axis
126
+ // conventions are measured against the NASA path-table centerlines —
127
+ // see eclipse/besselian.cjs.
128
+ // Derived Earth-around-EMB wobble for the Sun completion: a_M·μ/AU,
129
+ // μ = 1/(1+M_E/M_M) — 6.4399″ at current constants, tracks them live.
130
+ const embWobbleArcsec = (moonDistanceKm / (MASS_RATIO_EARTH_MOON + 1) / currentAUDistance)
131
+ * (648000 / Math.PI);
132
+ // FQ-5 N3 — the completion's carriers are FRAMEWORK-derived from the
133
+ // model's own planet period records (the Moon-elongation rate from the
134
+ // sidereal month/year identity). Plan 06 I2 — the carriers are SIDEREAL
135
+ // (measured): the records' orbitalPeriodInputDays are OF-DATE periods (their
136
+ // rates sit +1.39..1.62°/cy above the VSOP sidereal mean motions — the
137
+ // framework precession plus input rounding), and a perturbation argument
138
+ // is inertial (D'Alembert: only Σk = 0 arguments are frame-free), so on
139
+ // of-date carriers every Σk ≠ 0 argument drifted by Σk·ψ(t) — 42..52° at
140
+ // −3000 for the table's Σk = −1 terms (their ancient scatter against
141
+ // Horizons 6.4 → 4.4″ on sidereal carriers), and 6 % of frequency for the
142
+ // 1783-yr long inequality (1683 yr on the of-date carriers). Constant-free:
143
+ // the record rate minus the model's own J2000 precession p₀ =
144
+ // 360·36525·(1/T_trop − 1/T_sid) for the planets, the framework sidereal
145
+ // year for Earth; identical to the N3 carriers at J2000 (same phase
146
+ // anchors), ≤ 1.4°·|Σk| apart at the 200-yr extraction window's edges
147
+ // (the composed table reproduces the N3 one there: JPL 1900–2100
148
+ // all-phase sd unchanged to 0.01″). The parity gate's fingerprint mirrors
149
+ // this arithmetic (test/create-model-parity.test.mjs).
150
+ // PLAN 06 I3 — the carriers are the model's own banked J2000 sidereal mean
151
+ // motions (the embedded sun-completion artifact; ONE home,
152
+ // computeCarrierRatesDegPerCy — the former record-based arithmetic sat
153
+ // 4.1″/yr off for Venus, 1.7″/yr for Jupiter: 83° of the Venus–Earth
154
+ // argument at ±9000 yr), Earth the framework sidereal year, the Moon
155
+ // elongation from the sidereal month/year identity. The long-period rows
156
+ // ride the embedded e-vectors of date, and the lunar equation rides the
157
+ // framework's own Moon elongation of date (ṅ included) — the constant-rate
158
+ // carrier ran ~29° off it at ±9000 yr.
159
+ const carrierRatesDegPerCy = computeCarrierRatesDegPerCy({ meanSiderealYearDays, moonSiderealMonthDays: moonSiderealMonthInput });
160
+ const { sunPlanetaryCompletionDeg } = createSunPlanetaryCompletion({
161
+ embWobbleArcsec,
162
+ carrierRatesDegPerCy,
163
+ moonElongationDegAtT: /** @param {number} T */ (T) => moonArgs.argsAt(j2000JD + T * julianCenturyDays).D,
164
+ });
165
+ const besselian = createBesselian({
166
+ moonFullAtDaysTT: /** @param {number} dDaysTT */ (dDaysTT) => {
167
+ const ev = moonSeries.sceneEvalAt(dDaysTT);
168
+ return { lonDeg: ev.lonDeg, latDeg: ev.latDeg, distKm: ev.distKm };
169
+ },
170
+ sunLonDegAt: /** @param {number} jdUT */ (jdUT) => eclipseFinders.sunLonDegAt(jdUT),
171
+ sunCompletionDeg: sunPlanetaryCompletionDeg,
172
+ // plan 06 R3 item 1: the location tier on APPARENT places — the Sun's derived κ
173
+ // (the same helper the cardinal instants use), the Moon's relative light-time
174
+ // inside the besselian; the series-extension tail now lives in sceneEvalAt.
175
+ sunAberrationDegAt: /** @param {number} year */ (year) => late.sunAberrationArcsecAt(year) / 3600,
176
+ deltaTSecondsAt: /** @param {number} jd */ (jd) => (jdTTFromUT(jd) - jd) * 86400,
177
+ obliquityDegAt: obliquityDeg,
178
+ eccentricityAt: sunEccentricityAt,
179
+ perihelionLongitudeDegAt: sunPerihelionDegAt,
180
+ yearFromJD,
181
+ constants: {
182
+ j2000JD,
183
+ julianCenturyDays,
184
+ earthDiameterKm: C.bodyDiametersKm.earth,
185
+ moonDiameterKm: C.bodyDiametersKm.moon,
186
+ sunDiameterKm: C.bodyDiametersKm.sun,
187
+ sunDistanceKm: currentAUDistance,
188
+ earthFlatteningInverse: C.physicalConstants.earthFlatteningInverseWGS84,
189
+ ttBridgeSeconds: F.DT_ANCHOR.deltaTStartSeconds,
190
+ gmstMeanSiderealT0Deg: C.physicalConstants.gmstMeanSiderealT0Deg,
191
+ gmstMeanSiderealRateDegPerDay: C.physicalConstants.gmstMeanSiderealRateDegPerDay,
192
+ gmstMeanSiderealT2Deg: C.physicalConstants.gmstMeanSiderealT2Deg,
193
+ speedOfLightKmS: C.physicalConstants.speedOfLight,
194
+ },
195
+ });
196
+
197
+ return { besselian, eclipseFinders, sunL0Deg, sunMeanLongitudeDegAt, sunPlanetaryCompletionDeg, sunTropicalRateDegPerCy };
198
+ }
199
+ /** @typedef {import('./lunar-theory.js').Ctx & ReturnType<typeof assembleSunAndEclipses>} Ctx */
@@ -0,0 +1,332 @@
1
+ /**
2
+ * The assembled surface — the frozen object createModel() returns.
3
+ *
4
+ * The last slice of `assembleModel` (../model.js): reads the bindings every earlier slice built and returns the published surface.
5
+ */
6
+ import { ONE_FAMILY_WINDOW_YEARS } from '../earth/year-lengths.cjs';
7
+
8
+ /** @param {import('./cardinal-instants.js').Ctx} ctx @returns the published surface (frozen) */
9
+ export function assembleSurface(ctx) {
10
+ const {
11
+ C, F, EPOCH_PARAMS, GM_EARTH_ALONE, GM_EARTH_MOON_SYSTEM, GM_MOON_ALONE, GM_SUN, GM_SUN_PLUS_EARTH, G_CONSTANT,
12
+ MASS_RATIO_EARTH_MOON, M_SUN, PLANET_KEYS, PUBLISHED_PLANET_RECORDS, anomalisticYearDays, ascendingNodeDeg,
13
+ besselian, cardinalCrossingJdUT, cardinalCrossingNearJdUT, cardinalTargetDeg, certifiedAxialPrecessionJ2000Years,
14
+ chain, dayLengthSeconds, deepLod, deltaTSeconds, earthMoiFactorAtAge, earthPerihelionDeg, eccentricityAt,
15
+ eclipseFinders, evalClimateL1, frameworkDeltaTSecondsAtJD, inclinationDeg, j2000JD, jdFromYear, jdTTFromUT,
16
+ julianCenturyDays, lodRealSeconds, meanLengthOfDay, meanSiderealYearDays, moonDistanceCorrected, moonDistanceKm,
17
+ moonDistanceKmAtYearOrNaN, moonOrbitalShift, moonSeries, moonSiderealMonth, moonSiderealMonthDaysAt,
18
+ moonSynodicMonthDays, oneSourceM, planetAscNodeDeg, planetEccentricity, planetInclinationDeg, planetPerihelionDeg,
19
+ planetSpin, publishedAxialPrecessionYearsAtYear, publishedTropicalYearSecondsAtYear, s3ArcsecPerYr,
20
+ siTropicalYearDays, siderealDaySeconds, siderealYearDays, spinAxisInertialRateMeanArcsecPerYr, stellarDaySeconds,
21
+ sunApparentLonDegAtJdUT, sunEccentricityAt, sunL0Deg, sunMeanLongitudeDegAt, sunPerihelionDegAt,
22
+ sunPlanetaryCompletionDeg, sunTropicalRateDegPerCy, tropicalYearDays, yearFromJD, yearLengthsM, yearToTMa,
23
+ } = ctx;
24
+ return Object.freeze({
25
+ time: Object.freeze({
26
+ yearFromJD,
27
+ jdFromYear,
28
+ siTropicalYearDays,
29
+ }),
30
+ // The Moon-Kepler mass chain — the J2000 derivation docs 24 / the website's
31
+ // "mass from the Moon" page narrate, published from the one place that
32
+ // computes it (physics 22.1.0; the pages used to carry its numbers as
33
+ // hand-typed literals): Kepler III on the Moon's orbit with the
34
+ // solar-tidal shift Δa = a·μ·m (μ the Moon's mass fraction, m the lunar
35
+ // small parameter T_M/T_sidereal-year), the Earth/Moon split on the
36
+ // DE440 mass ratio, and GM_Sun from Earth's heliocentric orbit minus
37
+ // GM_Earth. Every value is the J2000 snapshot the rest of the model runs
38
+ // on; the of-date Moon distance is the recession history (epoch.*).
39
+ massChain: Object.freeze({
40
+ moonDistanceKm,
41
+ moonSiderealMonthDays: moonSiderealMonth,
42
+ siderealYearDays: meanSiderealYearDays,
43
+ dayLengthSeconds: meanLengthOfDay,
44
+ massRatioEarthMoon: MASS_RATIO_EARTH_MOON,
45
+ lunarSmallParameter: moonSiderealMonth / meanSiderealYearDays,
46
+ moonMassFraction: 1 / (MASS_RATIO_EARTH_MOON + 1),
47
+ solarTidalShiftKm: moonOrbitalShift,
48
+ moonDistanceCorrectedKm: moonDistanceCorrected,
49
+ gmEarthMoonSystemKm3S2: GM_EARTH_MOON_SYSTEM,
50
+ gmEarthKm3S2: GM_EARTH_ALONE,
51
+ gmMoonKm3S2: GM_MOON_ALONE,
52
+ gmSunPlusEarthKm3S2: GM_SUN_PLUS_EARTH,
53
+ gmSunKm3S2: GM_SUN,
54
+ sunMassKg: M_SUN,
55
+ earthMassKg: GM_EARTH_ALONE / G_CONSTANT,
56
+ gravitationalConstantKm3KgS2: G_CONSTANT,
57
+ }),
58
+ // The ONE certified Sun's mean elements of date — the lunar arguments of
59
+ // every runtime build their Sun from these two (the Node scene twin and
60
+ // the browser read them here).
61
+ sun: Object.freeze({
62
+ /** Mean longitude of date, degrees, unwrapped — L0 + 360·∫dt/T_trop (the one-family tropical year). @param {number} year @returns {number} */
63
+ meanLongitudeDegAt: sunMeanLongitudeDegAt,
64
+ /** The Sun's mean perihelion longitude of date (Earth's ϖ, mean elements), degrees, wrapped to [0, 360). @param {number} year @returns {number} */
65
+ perihelionLongitudeDegAt: sunPerihelionDegAt,
66
+ }),
67
+ epoch: Object.freeze({
68
+ yearToTMa,
69
+ // The Layer-2 mean day of date — the tidal + GIA base the deep-time law
70
+ // gives at any age (86,399.9997 s at J2000): the day the paleo anchors
71
+ // and the ΔT integrand ride. NOT the observable solar day of date —
72
+ // that is lodRealSecondsAtYear below (plan 08 storyline sweep).
73
+ lodSecondsAtYear: /** @param {number} year @returns {number|null} */ (year) => deepLod.lodSecondsAtAge(yearToTMa(year)),
74
+ // LOD_real of date — the Layer-4 observable (the simulator's Solar Day,
75
+ // the registry's lodRealPhysical): the kinematic day + the ecliptic term
76
+ // + the calibrated ΔT cycle stack. 86,400.001780 s at J2000.
77
+ lodRealSecondsAtYear: /** @param {number} year @returns {number} */ (year) => lodRealSeconds(year),
78
+ alphaAtYear: /** @param {number} year @returns {number} */ (year) => earthMoiFactorAtAge(yearToTMa(year)),
79
+ moonDistanceKmAtYear: /** @param {number} year @returns {number} */ (year) => moonDistanceKmAtYearOrNaN(year),
80
+ // D6: OF-DATE — mass-loss law / the banked planetary λ̇ ratio
81
+ // (identical at J2000 where the ratio ≡ 1 by construction)
82
+ siderealYearSecondsAtYear: /** @param {number} year @returns {number} */ (year) => yearLengthsM.siderealYearSecondsAtYear(year),
83
+ deltaTSecondsAtYear: deltaTSeconds,
84
+ // THE published of-date precession period is the one-family route: the
85
+ // hybrid's own equinox regression against the λ̇-corrected sidereal year
86
+ // (~25,771 yr at J2000 ≈ IAU), inside the banked tiers (|Δyear| ≤
87
+ // ONE_FAMILY_WINDOW_YEARS); beyond, the composed lunisolar period on
88
+ // the same J2000 anchor (the of-date wobble is unresolved there). The
89
+ // lunar chain's (d′) rate completion (pDynDegPerYearAt) rides THIS
90
+ // published period — a MATCHED TRIPLE with the eclipse gates (a
91
+ // 3–15 min lunar-timing shift was measured when it moved).
92
+ axialPrecessionYearsAtYear: publishedAxialPrecessionYearsAtYear,
93
+ // The same publication rule for the mean tropical year of date (SI s):
94
+ // one-family inside the window, the tidal-chain mean beyond.
95
+ tropicalYearSecondsAtYear: publishedTropicalYearSecondsAtYear,
96
+ }),
97
+ // Plan 06 Phase 3 S3 → S5 — the lunisolar precession clock as a
98
+ // first-class surface: Earth's spin clock, ONE home for its published
99
+ // faces, spoken in PERIODS and their RATIOS — no unit, no integer.
100
+ // ONE J2000 reading: every face reads 25,771.6 yr at J2000 — the
101
+ // of-date period (the published family, seamed at ±2 Myr) and the
102
+ // composed mean T_p(t) (the deep-time clock, anchored on the same
103
+ // derived value). The composition's terms; the apsidal period from the
104
+ // one family's own apsidal rate; the two ratios the owner found
105
+ // wandering by hand — T_aps/T_p (4.33 at J2000, 0.84 … 9.9 across
106
+ // ±26 kyr) and T_peri/T_p with T_peri = 1/(1/T_p + 1/T_aps) the
107
+ // perihelion-of-date period (0.812 at J2000) — on the of-date T_p.
108
+ lunisolar: Object.freeze({
109
+ /** The mean lunisolar precession period T_p(t), years — 1,296,000/the precession RATE of date (25,771.6 at J2000): THE published deep-time clock. @param {number} year @returns {number} */
110
+ meanPeriodYearsAtYear: (year) => { const p = deepLod.lunisolarPrecessionPeriodYearsAtAge(yearToTMa(year)); return p === null ? certifiedAxialPrecessionJ2000Years() : p; },
111
+ /** THE PRECESSION RATE OF DATE ψ̇(t) = [ω/ω₀]·p₀·[f_S + (1 − f_S)(a₀/a_M)³] × cos ε̄(t)/cos ε̄(0), ″/yr — the constant's scaling on the spin and the lunar torque times the cited mean obliquity's cosine (plan 12 §8 option (a); p₀ = 50.2880, the IAU 2006 input). @param {number} year @returns {number} */
112
+ meanRateArcsecPerYrAtYear: (year) => { const r = deepLod.lunisolarPrecessionRateArcsecPerYrAtAge(yearToTMa(year)); return r === null ? 1296000 / certifiedAxialPrecessionJ2000Years() : r; },
113
+ /** THE PRECESSION CONSTANT OF DATE as a rate on the J2000 obliquity, ″/yr — the spin and torque scaling alone (the hybrid obliquity integration's integrand: its own ŝ·n̂ carries the cosine). @param {number} year @returns {number} */
114
+ torqueRateArcsecPerYrAtYear: (year) => { const r = deepLod.lunisolarTorqueRateArcsecPerYrAtAge(yearToTMa(year)); return r === null ? 1296000 / certifiedAxialPrecessionJ2000Years() : r; },
115
+ /** 1,296,000/the torque rate, years. @param {number} year @returns {number} */
116
+ torquePeriodYearsAtYear: (year) => { const p = deepLod.lunisolarTorquePeriodYearsAtAge(yearToTMa(year)); return p === null ? certifiedAxialPrecessionJ2000Years() : p; },
117
+ /** cos ε̄(t)/cos ε̄(0) — the rate of date's obliquity factor on the constant (1 at J2000; 1.0075 at the Devonian, 1.0245 at 2.46 Ga). @param {number} year @returns {number} */
118
+ precessionObliquityFactorAtYear: (year) => { const f = deepLod.precessionObliquityFactorAtAge(yearToTMa(year)); return f === null ? 1 : f; },
119
+ /** THE CITED MEAN OBLIQUITY OF DATE ε̄(t), degrees — Farhat et al. 2022's own column of the Earth–Moon history (a Myr-scale MEAN: 23.25° at age 0 against the J2000 instant 23.44°; 22.23° Devonian, 19.73° at 2.46 Ga); read as the shape cos ε̄(t)/cos ε̄(0) on the precession rate. NaN beyond the input's span (4,429 Ma); forward of J2000 the J2000 slope is held. @param {number} year @returns {number} */
120
+ meanObliquityCitedDegAtYear: (year) => { const e = chain.meanObliquityCitedDegAtAge(yearToTMa(year)); return e === null ? NaN : e; },
121
+ /** The of-date period — the one-family beat inside ±2 Myr, the composed mean beyond. @param {number} year @returns {number} */
122
+ ofDatePeriodYearsAtYear: publishedAxialPrecessionYearsAtYear,
123
+ /** f_S, the solar fraction of the J2000 precession torque. */
124
+ solarShareJ2000: EPOCH_PARAMS.precessionSolarShareJ2000,
125
+ /** THE PRECESSION OF DATE — the solar-torque factor 1 + f_S·([(1 − e²)/(1 − e₀²)]^(−3/2) − 1) at the eccentricity of date (1 at J2000; +3.2·10⁻⁴ on the million-year mean). The spin integration multiplies it into the secular rate. @param {number} year @returns {number} */
126
+ solarTorqueFactorOfDateAtYear: (year) => oneSourceM.precessionOfDateFactorsAt(year).solarTorque,
127
+ /** THE PRECESSION OF DATE — J₂(t)/J₂₀, the oblateness of date from the GIA channel (ψ̇ ∝ J₂; 1 at J2000, bounded ±5·10⁻⁴; dJ₂/dt at J2000 = the observed satellite rate). @param {number} year @returns {number} */
128
+ ellipticityRatioOfDateAtYear: (year) => oneSourceM.precessionOfDateFactorsAt(year).ellipticity,
129
+ /** (a₀/a_M(t))³ — the lunar torque's growth on the recession history. @param {number} year @returns {number} */
130
+ lunarTorqueFactorAtYear: (year) => { const f = deepLod.lunarTorqueFactorAtAge(yearToTMa(year)); return f === null ? 1 : f; },
131
+ /** f_S + (1 − f_S)(a₀/a_M)³ — the torque term of the composed precession rate. @param {number} year @returns {number} */
132
+ torqueTermAtYear: (year) => { const t = deepLod.precessionTorqueTermAtAge(yearToTMa(year)); return t === null ? 1 : t; },
133
+ /** The hybrid's precession constant α = p₀ / cos ε₀, ″/yr (p₀ the derived J2000 rate, ε₀ the J2000 obliquity input; 54.81). */
134
+ torqueConstantJ2000ArcsecPerYr: (1296000 / certifiedAxialPrecessionJ2000Years()) / Math.cos((C.earthOrbital.obliquityJ2000_deg * Math.PI) / 180),
135
+ /** |s₃|, the dominant nodal mode of Earth's orbit — the largest-amplitude ζ mode of the banked deep secular modes, ″/yr (18.85; the obliquity beat's partner). ONE home for the registry, the browser and the API. */
136
+ nodalModeS3ArcsecPerYr: s3ArcsecPerYr,
137
+ /** The ecliptic (nodal) precession period 1,296,000/|s₃|, years (68,751) — an orbital quantity, fixed at every epoch at the two-body level. */
138
+ nodalPeriodYears: 1296000 / s3ArcsecPerYr,
139
+ /** THE SPIN AXIS'S INERTIAL PRECESSION RATE OF DATE, ″/yr — the composed rate scaled by the
140
+ * hybrid's self-anchor factor (the inertial-to-general ratio at J2000, the multiplicative form
141
+ * the obliquity integration itself applies): 50.385 at J2000 (series-driven). The obliquity beat and the
142
+ * obliquity lines ride THIS rate; the general precession (meanRateArcsecPerYrAtYear) is the
143
+ * equinox along the ecliptic of date. @param {number} year @returns {number} */
144
+ spinAxisInertialRateArcsecPerYrAtYear: (year) => {
145
+ const r = deepLod.lunisolarPrecessionRateArcsecPerYrAtAge(yearToTMa(year));
146
+ const psiDot = r === null ? 1296000 / certifiedAxialPrecessionJ2000Years() : r;
147
+ return psiDot * (certifiedAxialPrecessionJ2000Years() / oneSourceM.axialPrecessionYearsLunisolarJ2000);
148
+ },
149
+ /** The obliquity beat 2π/(ψ̇ᵢₙ(t) − |s₃|), years — the SHIPPED deep-time obliquity period at an INSTANT
150
+ * (falsification leg 1; 41,098 at J2000). ψ̇ᵢₙ is the spin axis's INERTIAL rate (above): the equator
151
+ * against the ecliptic's nodal mode in the inertial frame, the rate the hybrid obliquity integration
152
+ * self-anchors on — not the general precession, which reads the beat 0.3 % long (41,224). v23.0: a
153
+ * dated record sees the beat of the pole rate's mean over its own window, with the pole's of-date
154
+ * factors (obliquityPeriodRealizedYears below: 40,927 over the LR04 record, the climate formula's p+s₀
155
+ * line; La2004's own series reads 40,900 on the same window — compare realized with realized, never
156
+ * this instant with Laskar's long-window precession frequency 50.476). @param {number} year @returns {number} */
157
+ obliquityBeatYearsAtYear: (year) => {
158
+ const r = deepLod.lunisolarPrecessionRateArcsecPerYrAtAge(yearToTMa(year));
159
+ const psiDot = r === null ? 1296000 / certifiedAxialPrecessionJ2000Years() : r;
160
+ const psiDotInertial = psiDot * (certifiedAxialPrecessionJ2000Years() / oneSourceM.axialPrecessionYearsLunisolarJ2000);
161
+ return 1296000 / (psiDotInertial - s3ArcsecPerYr);
162
+ },
163
+ /** THE REALIZED POLE RATE OVER A WINDOW, ″/yr — see the one home above (the mean of the inertial rate of
164
+ * date with its three of-date factors); the rate the obliquity lines of a record spanning the window carry. */
165
+ spinAxisInertialRateMeanArcsecPerYr,
166
+ /** THE REALIZED OBLIQUITY PERIOD OVER A WINDOW, years — 1,296,000/(⟨ψ̇ᵢₙ⟩_window − |s₃|): the period a record
167
+ * spanning [yearFrom, yearTo] sees (40,930 over the LR04 record 0–5.32 Ma, 41,018 over the last Myr); the
168
+ * J2000 beat (obliquityBeatYearsAtYear) is its instantaneous limit.
169
+ * @param {number} yearFrom @param {number} yearTo @returns {number} */
170
+ obliquityPeriodRealizedYears: (yearFrom, yearTo) => 1296000 / (spinAxisInertialRateMeanArcsecPerYr(yearFrom, yearTo) - s3ArcsecPerYr),
171
+ /** The apsidal (perihelion vs the stars) period of date, JULIAN years — the ONE movement's own apsidal rate (the scene's perihelion; anom/(anom − sid) of the one family, ≡ 360°/apsidalRateFixedArcsecPerJulianYr), inside the published window only; null beyond. Before 2026-09 this rode the planet chain's secular tangent, which parted from the scene's perihelion passages by seconds of anomalistic year within a few millennia (year-lengths.cjs). @param {number} year @returns {number|null} */
172
+ apsidalPeriodYearsAtYear: (year) => (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS ? yearLengthsM.inclinationPrecessionYearsAtYear(year) : null),
173
+ /** T_aps(t) / T_p(t) — the apsidal period in of-date precession periods (4.33 at J2000, a reading; 0.84 … 9.9 across ±26 kyr, measured); null beyond the published window. @param {number} year @returns {number|null} */
174
+ apsidalPerPrecessionAtYear: (year) => {
175
+ if (Math.abs(year - 2000) > ONE_FAMILY_WINDOW_YEARS) return null;
176
+ return yearLengthsM.inclinationPrecessionYearsAtYear(year) / publishedAxialPrecessionYearsAtYear(year);
177
+ },
178
+ /** T_peri(t) = 1/(1/T_p + 1/T_aps) — the perihelion-of-date period (equinox precession + inertial perihelion motion, frame arithmetic at every epoch), JULIAN years, on the of-date T_p — ≡ the one family's anom/(anom − trop); null beyond the published window. @param {number} year @returns {number|null} */
179
+ periOfDatePeriodYearsAtYear: (year) => {
180
+ if (Math.abs(year - 2000) > ONE_FAMILY_WINDOW_YEARS) return null;
181
+ const tp = publishedAxialPrecessionYearsAtYear(year), taps = yearLengthsM.inclinationPrecessionYearsAtYear(year);
182
+ return 1 / (1 / tp + 1 / taps);
183
+ },
184
+ /** T_peri(t) / T_p(t) (0.812 at J2000 — the J2000 reading); null beyond the published window. @param {number} year @returns {number|null} */
185
+ periOfDatePerPrecessionAtYear: (year) => {
186
+ if (Math.abs(year - 2000) > ONE_FAMILY_WINDOW_YEARS) return null;
187
+ const tp = publishedAxialPrecessionYearsAtYear(year), taps = yearLengthsM.inclinationPrecessionYearsAtYear(year);
188
+ return (1 / (1 / tp + 1 / taps)) / tp;
189
+ },
190
+ /** The published of-date window, years from 2000. */
191
+ publishedWindowYears: ONE_FAMILY_WINDOW_YEARS,
192
+ /** THE SPIN AXIS'S INERTIAL PRECESSION PERIOD at J2000, years (25,722 in the shipped,
193
+ * series-driven configuration) — the rate the hybrid obliquity integration self-anchors
194
+ * ŝ on (earth/deep-orbital-history, D4e): the general-precession anchor p₀ PLUS the
195
+ * planetary mean the N-body ecliptic's own motion carries (+0.097 ″/yr ≡ IAU's planetary
196
+ * precession ψ_A − p_A; the bare 18-mode table anchors 0.18 — the g₂-gap trap: pass the
197
+ * secular series). Two rates, two uses — the obliquity line frequencies p + s_i (the
198
+ * spin axis against the ecliptic's nodal modes, in the inertial frame) carry THIS
199
+ * rate; the climatic-precession lines p + g_i (the perihelion from the equinox along
200
+ * the ecliptic of date) carry p₀. */
201
+ spinAxisInertialPeriodYearsJ2000: oneSourceM.axialPrecessionYearsLunisolarJ2000,
202
+ /** The same as a rate, ″/yr (50.385 at J2000 with the series; p₀ = 50.288 is the general precession). */
203
+ spinAxisInertialRateArcsecPerYrJ2000: 1296000 / oneSourceM.axialPrecessionYearsLunisolarJ2000,
204
+ }),
205
+ earth: Object.freeze({
206
+ perihelionLongitudeDeg: earthPerihelionDeg,
207
+ // THE published obliquity is the hybrid — one torque law integrated on
208
+ // the dynamical orbit plane of the N-body chain, zero fitted constants
209
+ // (≡ La2004 to ≤ 4″ over ±50 kyr).
210
+ // Beyond the ζ series' span (±10 Myr) the hybrid carries NO tidal evolution
211
+ // of the mean obliquity (nothing in ds/dt = α(ŝ·n̂)(ŝ×n̂) moves ŝ·n̂'s mean):
212
+ // its deep mean is the Quaternary mean on the tail's mean plane (plan 12
213
+ // §8.3). The cited mean obliquity of date is `meanObliquityCitedDeg`.
214
+ obliquityDeg: /** @param {number} year @returns {number} */ (year) => oneSourceM.epsAt(year),
215
+ /** The hybrid's own domain, [yearFrom, yearTo] — the banked ζ series' span, ±10 Myr about J2000 (plan 12 §8.5: the published surfaces serve `obliquityDeg` inside it only; null without a series, i.e. a bare createModel()). */
216
+ obliquityHybridDomainYears: oneSourceM.obliquityHybridDomainYears,
217
+ /** The cited mean obliquity of date ε̄(t) (Farhat et al. 2022's column; plan 12 §8 option (a)), degrees — a Myr-scale mean; NaN beyond 4,429 Ma. @param {number} year @returns {number} */
218
+ meanObliquityCitedDeg: /** @param {number} year @returns {number} */ (year) => { const e = chain.meanObliquityCitedDegAtAge(yearToTMa(year)); return e === null ? NaN : e; },
219
+ eccentricity: eccentricityAt,
220
+ inclinationDeg,
221
+ ascendingNodeDeg,
222
+ }),
223
+ // The year lengths of date in DAYS OF THE EPOCH (the published SI years
224
+ // over the kinematic day of date — year-day-lengths.js) and the day
225
+ // lengths of date in SI seconds (one-source-movement.js). The published
226
+ // SI year lengths themselves are `yearLengths` (the one-family route) and
227
+ // the seamed `epoch.*AtYear` surfaces above.
228
+ lengths: Object.freeze({
229
+ tropicalYearDays,
230
+ siderealYearDays,
231
+ anomalisticYearDays,
232
+ dayLengthSeconds,
233
+ lodRealSeconds,
234
+ siderealDaySeconds,
235
+ stellarDaySeconds,
236
+ }),
237
+ // Plan 06 R1: the instants are the crossings of the ONE Sun (the
238
+ // certified completed Sun the scene renders), UT model-JD; the RA at a
239
+ // crossing is the target longitude by construction (λ = 0/90/180/270 ⇒
240
+ // RA = 0/90/180/270, any obliquity); the per-type year length is the
241
+ // interval between successive crossings (SI days).
242
+ cardinal: Object.freeze({
243
+ jd: /** @param {number} year @param {string} type @returns {number} */ (year, type) => cardinalCrossingJdUT(year, type),
244
+ // R4c: the crossing nearest a UT instant (a JD seed — for callers whose
245
+ // year coordinate is not the model year, e.g. a calendar year's midpoint).
246
+ jdNearUT: /** @param {number} jdUT @param {string} type @returns {number} */ (jdUT, type) => cardinalCrossingNearJdUT(jdUT, type),
247
+ raDeg: /** @param {number} year @param {string} type @returns {number} */ (year, type) => { void year; return cardinalTargetDeg(type); },
248
+ yearLengthDays: /** @param {number} year @param {string} type @returns {number} */ (year, type) => cardinalCrossingJdUT(year + 1, type) - cardinalCrossingJdUT(year, type),
249
+ }),
250
+ // The one-source cardinal STRUCTURE — the EoC layer on the movement's own
251
+ // e(t)/ϖ(t), valid at every epoch (mode tier). Absolute dates deliberately
252
+ // absent; `cardinal` above serves the instants.
253
+ cardinalStructure: Object.freeze({
254
+ // D6: year LENGTHS gain the λ̇ drift coherently (rate-form
255
+ // correction — beats stay invariant); offsets/spreads keep the raw
256
+ // form (μs-class / second-order there).
257
+ yearLengthSeconds: /** @param {number} year @param {'VE'|'SS'|'AE'|'WS'} type @returns {number} */ (year, type) => yearLengthsM.cardinal.yearLengthSeconds(year, type),
258
+ eocOffsetSeconds: /** @param {number} year @param {'VE'|'SS'|'AE'|'WS'} type @returns {number} */ (year, type) => yearLengthsM.cardinal.eocOffsetSeconds(year, type),
259
+ spreadSeconds: /** @param {number} year */ (year) => yearLengthsM.cardinal.spreadSeconds(year),
260
+ anomalisticYearSeconds: /** @param {number} year @returns {number} */ (year) => yearLengthsM.anomalisticYearSecondsAtYear(year),
261
+ }),
262
+ // THE ONE of-date year-length family + its beats (S2; SI seconds) —
263
+ // the single surface the panel, charts, API and website consume.
264
+ yearLengths: yearLengthsM,
265
+ moon: Object.freeze({
266
+ distanceKmAtYear: /** @param {number} year @returns {number} */ (year) => moonDistanceKmAtYearOrNaN(year),
267
+ siderealMonthDaysAtYear: moonSiderealMonthDaysAt,
268
+ synodicMonthDays: moonSynodicMonthDays,
269
+ // The apparent-position chain (truncated Meeus Ch. 47 series on
270
+ // framework-native arguments). JD(UT) axis — UT→TT applied internally.
271
+ lonDegAtJD: /** @param {number} jd @returns {number} */ (jd) => moonSeries.truncatedLonDeg(jd),
272
+ betaDegAtJD: /** @param {number} jd @returns {number} */ (jd) => moonSeries.truncatedBetaDeg(jd),
273
+ distanceKmAtJD: /** @param {number} jd @returns {number} */ (jd) => moonSeries.truncatedDistanceKm(jd),
274
+ }),
275
+ eclipse: Object.freeze({
276
+ sunLonDegAtJD: /** @param {number} jd @returns {number} */ (jd) => eclipseFinders.sunLonDegAt(jd),
277
+ // The COMPLETED tier Sun (K8b follow-up, owner-approved injection):
278
+ // the bare finder Sun minus the DERIVED planetary-completion table —
279
+ // 70 framework-carrier terms plus the 6.44″ Earth-around-EMB wobble
280
+ // (the "lunar equation"; eclipse/sun-planetary-completion.cjs, zero
281
+ // fitted constants). The finders themselves deliberately stay bare
282
+ // (their certified canon statistics were produced on the bare form;
283
+ // besselian.cjs applies the same subtraction internally). This
284
+ // surface exists for the SCENE tier's E5 wheel-Sun bridge, so the
285
+ // rendered Sun carries the model's own derived apparent-class terms.
286
+ // Clock: the completion argument rides TT centuries from the same
287
+ // framework ΔT the besselian composition uses.
288
+ sunLonCompletedDegAtJD: /** @param {number} jd @returns {number} */ (jd) =>
289
+ eclipseFinders.sunLonDegAt(jd)
290
+ - sunPlanetaryCompletionDeg((jdTTFromUT(jd) - j2000JD) / julianCenturyDays),
291
+ /** The APPARENT Sun (completed geometric − the derived aberration constant + the leading nutation terms on the model's own arguments) — the quantity Horizons' observer ecliptic longitude is; the cardinal instants are its crossings (plan 06 R1/I1). @param {number} jdUT @returns {number} */
292
+ sunApparentLonDegAtJD: /** @param {number} jdUT @returns {number} */ (jdUT) => sunApparentLonDegAtJdUT(jdUT),
293
+ findLunarInRange: /** @param {number} jdStart @param {number} jdEnd */ (jdStart, jdEnd) => eclipseFinders.findLunarEclipsesInRange(jdStart, jdEnd),
294
+ findSolarInRange: /** @param {number} jdStart @param {number} jdEnd */ (jdStart, jdEnd) => eclipseFinders.findSolarEclipsesInRange(jdStart, jdEnd),
295
+ deltaTSecondsAtJD: frameworkDeltaTSecondsAtJD,
296
+ /** The ΔT trend anchor at J2000 (seconds): the TT − UT bridge every finder axis adds to a
297
+ * true-UT JD. The joint ΔT fit's output, shipped in its own artifact (FIT.DT_ANCHOR —
298
+ * plan 14: never a constants-file input), exposed here so no consumer reaches into the
299
+ * constants for it. */
300
+ ttBridgeSeconds: F.DT_ANCHOR.deltaTStartSeconds,
301
+ // E4 — the framework-native Sun deps, exported so the OTHER finder
302
+ // construction sites (tools/verify/eclipse-audit.js, the browser
303
+ // _eclipse twins) spread the SAME assembly into their own
304
+ // createEclipseFinders call instead of triplicating it (the
305
+ // three-runtimes rule — cf. moon-distance-history):
306
+ frameworkSunDeps: Object.freeze({
307
+ sunMeanLongitudeJ2000Deg: sunL0Deg,
308
+ tropicalRateDegPerCy: sunTropicalRateDegPerCy,
309
+ eccentricityAt: sunEccentricityAt,
310
+ perihelionLongitudeDegAt: sunPerihelionDegAt,
311
+ meanLongitudeDegAt: sunMeanLongitudeDegAt,
312
+ }),
313
+ // 20.3g location tier (see eclipse/besselian.cjs):
314
+ umbraGroundAtJD: /** @param {number} jd @returns {{latDeg: number, lonDeg: number} | null} */ (jd) => besselian.umbraGroundAt(jd),
315
+ /** THE ONE GMST — mean sidereal time at Greenwich, degrees, at a UT JD (eclipse/besselian.cjs computeGmstDeg; the observer tier's hour angle). @param {number} jdUT @returns {number} */
316
+ gmstDegAtJD: (jdUT) => besselian.gmstDeg(jdUT),
317
+ solarLocalCircumstances: /** @param {number} jdGreatest @param {number} latDeg @param {number} lonDeg */ (jdGreatest, latDeg, lonDeg) => besselian.localCircumstances(jdGreatest, latDeg, lonDeg),
318
+ }),
319
+ climate: Object.freeze({
320
+ l1OrbitalPermil: evalClimateL1,
321
+ }),
322
+ planets: Object.freeze({
323
+ keys: Object.freeze([...PLANET_KEYS]),
324
+ record: /** @param {string} k @returns {Record<string, any>|undefined} */ (k) => PUBLISHED_PLANET_RECORDS[k],
325
+ perihelionLongitudeDeg: planetPerihelionDeg,
326
+ ascendingNodeInvPlaneDeg: planetAscNodeDeg,
327
+ invPlaneInclinationDeg: planetInclinationDeg,
328
+ eccentricity: planetEccentricity,
329
+ spin: planetSpin,
330
+ }),
331
+ });
332
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The time axis — exact JD ↔ model-year conversion on the SI axis.
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('./inv-plane-elements.js').Ctx} ctx @returns the bindings the later slices read */
8
+ export function assembleTimeAxis(ctx) {
9
+ const { meanTropicalYearJ2000Seconds, startmodelJD, startmodelYear } = ctx;
10
+ // The model's `year` inputs live on the SI axis (the axis the fits were
11
+ // anchored on — tools/lib `_jdToSIyear`): linear in SI 86400-s days from the
12
+ // model start, so the JD↔year map is closed-form and exact to double
13
+ // precision. Callers holding an exact JD (e.g. 2058768.5385006 TT) convert
14
+ // here and NEVER roll their own — a caller-side linear-vs-calendar mix once
15
+ // put the eclipse umbra twin 8 km off (§12h; ≤1 m once unified).
16
+ const siTropicalYearDays = meanTropicalYearJ2000Seconds / 86400;
17
+ /** Model year (SI axis) at a JD(TT). @param {number} jd @returns {number} */
18
+ const yearFromJD = (jd) => startmodelYear + (jd - startmodelJD) / siTropicalYearDays;
19
+ /** JD(TT) at a model year (SI axis). @param {number} year @returns {number} */
20
+ const jdFromYear = (year) => startmodelJD + (year - startmodelYear) * siTropicalYearDays;
21
+
22
+ return { jdFromYear, siTropicalYearDays, yearFromJD };
23
+ }
24
+ /** @typedef {import('./inv-plane-elements.js').Ctx & ReturnType<typeof assembleTimeAxis>} Ctx */
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The J2000 precession anchor p₀; the kinematic day of date and the year lengths in days of the epoch.
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('./earth-scalars.js').Ctx} ctx @returns the bindings the later slices read */
9
+ export function assembleYearDayLengths(ctx) {
10
+ const { chain, late, deepLod, meanLengthOfDay, yearToTMa } = ctx;
11
+ // The year lengths of date in DAYS are the published SI year lengths over
12
+ // the kinematic day of date; the kinematic day of date is the Layer-0 tidal
13
+ // mean LOD, exactly ω₀ (the SI day) at J2000.
14
+ /** THE J2000 precession anchor p₀: the IAU 2006 general precession in
15
+ * longitude at J2000 (astro-reference.json
16
+ * earthOrbital.generalPrecessionJ2000_arcsecPerYr = 50.28796195 ″/yr),
17
+ * stated as the period 1,296,000/p_A = 25,771.575 Julian years. ONE home:
18
+ * the composed clock's p₀, the hybrid's self-anchor and the lunisolar
19
+ * surface all read it (the identifier predates the anchor's move onto the
20
+ * IAU input). @returns {number} */
21
+ function certifiedAxialPrecessionJ2000Years() {
22
+ return chain.certifiedAxialPrecessionJ2000Years(); // the ONE home: the deep-time chain's export (1,296,000 / p_A on the same constants)
23
+ }
24
+ /** Kinematic day of date (Layer 0 tidal mean LOD; ω₀ at J2000). Past the
25
+ * tidal-lock asymptote the chain is null — the J2000 day stands in.
26
+ * @param {number} year @returns {number} */
27
+ const dayLengthSeconds = (year) => deepLod.lodSecondsAtAge(yearToTMa(year)) ?? meanLengthOfDay;
28
+ /** Sidereal year of date in days of the epoch — the published SI sidereal
29
+ * year (the λ̇ channel inside the one-family window, the mass-loss law
30
+ * beyond) over the kinematic day of date. @param {number} year @returns {number} */
31
+ const siderealYearDays = (year) => late.publishedSiderealYearSecondsAtYear(year) / dayLengthSeconds(year);
32
+ /** Anomalistic year of date in days of the epoch — the published SI
33
+ * anomalistic year (one-family inside its window; NaN beyond, where the
34
+ * movement's apsidal line is not resolved). @param {number} year @returns {number} */
35
+ const anomalisticYearDays = (year) => {
36
+ const s = Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS ? late.yearLengthsM.anomalisticYearSecondsAtYear(year) : NaN;
37
+ return s / dayLengthSeconds(year);
38
+ };
39
+
40
+ return { anomalisticYearDays, certifiedAxialPrecessionJ2000Years, dayLengthSeconds, siderealYearDays };
41
+ }
42
+ /** @typedef {import('./earth-scalars.js').Ctx & ReturnType<typeof assembleYearDayLengths>} Ctx */