@essrt/physics 23.3.3 → 23.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +4 -1
  2. package/package.json +2 -2
  3. package/src/constants/coefficients.d.ts +5 -1
  4. package/src/constants/coefficients.js +3 -2
  5. package/src/constants/generated.d.ts +2 -3
  6. package/src/constants/generated.js +3 -4
  7. package/src/constants/units.cjs +4 -1
  8. package/src/deltat/deep-time.cjs +37 -72
  9. package/src/index.js +30 -48
  10. package/src/model/cardinal-instants.js +101 -0
  11. package/src/model/context.js +54 -0
  12. package/src/model/deep-time-chain.js +79 -0
  13. package/src/model/delta-t.js +38 -0
  14. package/src/model/derived-constants.js +58 -0
  15. package/src/model/earth-scalars.js +47 -0
  16. package/src/model/inv-plane-elements.js +56 -0
  17. package/src/model/lunar-theory.js +216 -0
  18. package/src/model/moon-epoch.js +28 -0
  19. package/src/model/one-source-movement.js +192 -0
  20. package/src/model/planet-elements-of-date.js +61 -0
  21. package/src/model/planet-records.js +63 -0
  22. package/src/model/planet-spin-channel.js +41 -0
  23. package/src/model/published-periods.js +38 -0
  24. package/src/model/sun-and-eclipses.js +199 -0
  25. package/src/model/surface.js +330 -0
  26. package/src/model/time-axis.js +24 -0
  27. package/src/model/year-day-lengths.js +42 -0
  28. package/src/model.js +62 -1612
  29. package/src/moon/arguments.cjs +27 -43
  30. package/src/planets/chain-artifact.js +2 -2
  31. package/src/planets/keplerian-chain.cjs +1 -1
  32. package/types/constants/coefficients.d.ts +5 -1
  33. package/types/constants/generated.d.ts +2 -3
  34. package/types/constants/units.d.cts +3 -0
  35. package/types/deltat/deep-time.d.cts +18 -24
  36. package/types/index.d.ts +1 -1
  37. package/types/model/cardinal-instants.d.ts +14 -0
  38. package/types/model/context.d.ts +87 -0
  39. package/types/model/deep-time-chain.d.ts +288 -0
  40. package/types/model/delta-t.d.ts +11 -0
  41. package/types/model/derived-constants.d.ts +25 -0
  42. package/types/model/earth-scalars.d.ts +14 -0
  43. package/types/model/inv-plane-elements.d.ts +6 -0
  44. package/types/model/lunar-theory.d.ts +72 -0
  45. package/types/model/moon-epoch.d.ts +10 -0
  46. package/types/model/one-source-movement.d.ts +74 -0
  47. package/types/model/planet-elements-of-date.d.ts +9 -0
  48. package/types/model/planet-records.d.ts +7 -0
  49. package/types/model/planet-spin-channel.d.ts +29 -0
  50. package/types/model/published-periods.d.ts +7 -0
  51. package/types/model/sun-and-eclipses.d.ts +52 -0
  52. package/types/model/surface.d.ts +279 -0
  53. package/types/model/time-axis.d.ts +12 -0
  54. package/types/model/year-day-lengths.d.ts +8 -0
  55. package/types/model.d.ts +9 -81
  56. package/types/moon/arguments.d.cts +5 -11
  57. package/types/planets/keplerian-chain.d.cts +3 -1
@@ -0,0 +1,330 @@
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
+ solarLocalCircumstances: /** @param {number} jdGreatest @param {number} latDeg @param {number} lonDeg */ (jdGreatest, latDeg, lonDeg) => besselian.localCircumstances(jdGreatest, latDeg, lonDeg),
316
+ }),
317
+ climate: Object.freeze({
318
+ l1OrbitalPermil: evalClimateL1,
319
+ }),
320
+ planets: Object.freeze({
321
+ keys: Object.freeze([...PLANET_KEYS]),
322
+ record: /** @param {string} k @returns {Record<string, any>|undefined} */ (k) => PUBLISHED_PLANET_RECORDS[k],
323
+ perihelionLongitudeDeg: planetPerihelionDeg,
324
+ ascendingNodeInvPlaneDeg: planetAscNodeDeg,
325
+ invPlaneInclinationDeg: planetInclinationDeg,
326
+ eccentricity: planetEccentricity,
327
+ spin: planetSpin,
328
+ }),
329
+ });
330
+ }
@@ -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 { C, 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 1296000 / C.earthOrbital.generalPrecessionJ2000_arcsecPerYr;
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 */