@essrt/physics 22.1.0 → 23.2.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 (45) hide show
  1. package/README.md +2 -1
  2. package/package.json +9 -3
  3. package/src/constants/coefficients.d.ts +11 -11
  4. package/src/constants/coefficients.js +4 -4
  5. package/src/constants/generated.d.ts +4 -16
  6. package/src/constants/generated.js +8 -32
  7. package/src/constants/units.cjs +20 -0
  8. package/src/deltat/deep-time-chain.js +457 -0
  9. package/src/deltat/deep-time.cjs +68 -25
  10. package/src/deltat/moon-distance-artifact.cjs +23 -0
  11. package/src/deltat/moon-distance-history.cjs +115 -0
  12. package/src/earth/deep-orbital-history.cjs +69 -19
  13. package/src/earth/mass-chain.cjs +95 -0
  14. package/src/earth/mean-obliquity-history.cjs +94 -0
  15. package/src/earth/precession-composed.cjs +49 -3
  16. package/src/index.js +23 -1
  17. package/src/layer0/derive-params.js +1 -5
  18. package/src/layer0/index.js +30 -17
  19. package/src/math/angles.cjs +38 -0
  20. package/src/model.js +100 -266
  21. package/src/moon/arguments.cjs +5 -31
  22. package/src/moon/month-chain.cjs +12 -19
  23. package/src/planets/chain-artifact.js +2 -2
  24. package/src/time/calendar.cjs +165 -0
  25. package/types/constants/coefficients.d.ts +11 -11
  26. package/types/constants/generated.d.ts +4 -16
  27. package/types/constants/units.d.cts +8 -0
  28. package/types/deltat/deep-time-chain.d.ts +266 -0
  29. package/types/deltat/deep-time.d.cts +42 -12
  30. package/types/deltat/moon-distance-artifact.d.cts +22 -0
  31. package/types/deltat/moon-distance-history.d.cts +36 -0
  32. package/types/earth/deep-orbital-history.d.cts +5 -3
  33. package/types/earth/mass-chain.d.cts +78 -0
  34. package/types/earth/mean-obliquity-history.d.cts +32 -0
  35. package/types/earth/precession-composed.d.cts +11 -1
  36. package/types/index.d.ts +8 -1
  37. package/types/layer0/derive-params.d.ts +3 -2
  38. package/types/layer0/index.d.ts +9 -6
  39. package/types/math/angles.d.cts +18 -0
  40. package/types/model.d.ts +21 -3
  41. package/types/moon/arguments.d.cts +2 -6
  42. package/types/moon/month-chain.d.cts +7 -13
  43. package/types/time/calendar.d.cts +78 -0
  44. package/src/deltat/recession-history.cjs +0 -169
  45. package/types/deltat/recession-history.d.cts +0 -80
package/src/model.js CHANGED
@@ -17,7 +17,6 @@
17
17
  * src/lib/essrt.ts) operation-for-operation where FP association matters,
18
18
  * which itself mirrors tools/lib/constants.js §9.
19
19
  */
20
- import { deriveEpochParams } from './layer0/derive-params.js';
21
20
  import { buildPlanetChainsFromArtifactData, computePlanetElementsAtYear } from './planets/keplerian-chain.cjs';
22
21
  import { createSecularSeriesOverride } from './planets/secular-series.cjs';
23
22
  import { computeEquatorNodeOriginSFrameDeg, convertNodeSFrameToEquatorOriginDeg } from './planets/inv-plane-frame.cjs';
@@ -26,13 +25,11 @@ import { createDeepOrbitalHistory } from './earth/deep-orbital-history.cjs';
26
25
  import { CHAIN_ARTIFACT } from './planets/chain-artifact.js';
27
26
  import { createPlanetSpinChannelFromArtifacts, computeObliquityJ2000Deg } from './planets/spin-channel.cjs';
28
27
  import { PLANET_DISPLAY_NAMES } from './planets/display-names.cjs';
29
- import { createDeltaTCycles } from './deltat/cycles.cjs';
30
- import { createDeepTimeLod } from './deltat/deep-time.cjs';
31
- import { createMoonRecessionHistory, createSolarChannelBudget } from './deltat/recession-history.cjs';
32
- import { evalClimateL1OrbitalPermil, createAlphaGiaChannel } from './climate/l1-orbital.cjs';
33
- import { createDeepEccChannel } from './moon/deep-ecc-channel.cjs';
28
+ import { createDeepTimeChain } from './deltat/deep-time-chain.js';
29
+ import { computeMassChainJ2000 } from './earth/mass-chain.cjs';
30
+ import { MOON_DISTANCE_ARTIFACT } from './deltat/moon-distance-artifact.cjs';
31
+ import { evalClimateL1OrbitalPermil } from './climate/l1-orbital.cjs';
34
32
  import { DEEP_MODES_ARTIFACT } from './moon/deep-modes-artifact.cjs';
35
- import { createMoonMonthChain } from './moon/month-chain.cjs';
36
33
  import { createChainCycleIntegrator } from './chain-cycles/index.cjs';
37
34
  import { createMoonArguments, jdToDecimalYear } from './moon/arguments.cjs';
38
35
  import { createMoonSeries } from './moon/series.cjs';
@@ -51,16 +48,9 @@ import { driver2PeriodSecondsAtAge } from './planets/orbit-chain.cjs';
51
48
  // here into F.RA_DAY_OFFSET_MS; plan 09 step 5b deleted that family — it was
52
49
  // the equation of time's window term of an instrument, not a motion.)
53
50
 
54
- /**
55
- * Moon-channel eccentricity sensitivities (perigee/node), the [g/g₀]^s
56
- * exponents of the factored deep-time law. Same known-exception class as the
57
- * RA offsets above: single source src/script.js _FW_MOON, mirrored as
58
- * literals in tools/lib/deep-time.js (_ECOMP_S_W/_ECOMP_S_N). Both are
59
- * Meeus-effective — S_N moved 1.0 → 1.018 with the v4 frame-attribution
60
- * batch.
61
- */
62
- const MOON_ECC_SENSITIVITY_PERIGEE = 2.407;
63
- const MOON_ECC_SENSITIVITY_NODE = 1.018;
51
+ // (The Moon-channel eccentricity sensitivities s_ϖ/s_Ω — the [g/g₀]^s exponents of
52
+ // the factored deep-time law — have ONE home since plan 13 B2:
53
+ // deltat/deep-time-chain.js MOON_ECC_SENSITIVITY_PERIGEE / _NODE.)
64
54
 
65
55
  /**
66
56
  * Assemble the model surfaces from a resolved constants context + fitted
@@ -108,7 +98,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
108
98
  // comb) left with that device; no comb phase is evaluated here any more.
109
99
 
110
100
  const meanSiderealYearDays = C.yearLengthRef.siderealYear;
111
- const meanSiderealYearSeconds = meanSiderealYearDays * 86400;
101
+ // (the sidereal year in SI seconds is the mass chain's own, below)
112
102
  // Plan 09: the J2000 day length ω₀ is the SI day (an input); the device's
113
103
  // H/13 kinematic-sidereal-days construction (86,399.99968 s) is deleted.
114
104
  const meanLengthOfDay = C.yearLengthRef.solarDay;
@@ -130,7 +120,6 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
130
120
  const G_CONSTANT = C.physicalConstants.G_CONSTANT;
131
121
  const MASS_RATIO_EARTH_MOON = C.physicalConstants.MASS_RATIO_EARTH_MOON;
132
122
  const currentAUDistance = C.physicalConstants.currentAUDistance;
133
- const earthMoiFactorJ2000 = C.physicalConstants.earthMoiFactorJ2000;
134
123
  const moonDistanceKm = C.moonReference.moonDistance;
135
124
  const moonSiderealMonthInput = C.moonReference.moonSiderealMonthInput;
136
125
 
@@ -138,171 +127,54 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
138
127
  // eighth-integer-per-8H count read it back 7.8 ms long; deleted).
139
128
  const moonSiderealMonth = moonSiderealMonthInput;
140
129
 
141
- // Mass chain: Moon Kepler → GM_EM → Earth/Moon split → Sun (§9 order)
142
- const moonOrbitalShift = moonDistanceKm * (1 / (MASS_RATIO_EARTH_MOON + 1)) * (moonSiderealMonth / meanSiderealYearDays);
143
- const moonDistanceCorrected = moonDistanceKm + moonOrbitalShift;
144
- const GM_EARTH_MOON_SYSTEM = (4 * Math.PI * Math.PI * Math.pow(moonDistanceCorrected, 3))
145
- / Math.pow(moonSiderealMonth * meanLengthOfDay, 2);
146
- const GM_EARTH_ALONE = GM_EARTH_MOON_SYSTEM * (MASS_RATIO_EARTH_MOON / (MASS_RATIO_EARTH_MOON + 1));
147
- const GM_MOON_ALONE = GM_EARTH_MOON_SYSTEM / (MASS_RATIO_EARTH_MOON + 1);
148
- const GM_SUN_PLUS_EARTH = (4 * Math.PI * Math.PI * Math.pow(currentAUDistance, 3))
149
- / Math.pow(meanSiderealYearSeconds, 2);
150
- const GM_SUN = GM_SUN_PLUS_EARTH - GM_EARTH_ALONE;
151
- const M_SUN = GM_SUN / G_CONSTANT;
152
-
153
- // ── α(t): the GIA channel — the LAGGED response to the L1 ice history, with the R2 lattice pin ──
154
- const CLIMATE = F.CLIMATE_FORMULA_COEFFS;
155
- const CLIMATE_REGIME = CLIMATE.regimes['lr04-post-mpt'];
130
+ // The mass chain (Moon Kepler → GM_EM → the Earth/Moon split → the Sun, §9
131
+ // order) — ONE home since plan 13 B3: earth/mass-chain.cjs (the deep-time
132
+ // chain below computes the same scalars from the same context).
133
+ const mass = computeMassChainJ2000({
134
+ moonDistanceKm, moonSiderealMonthDays: moonSiderealMonth, meanSiderealYearDays,
135
+ meanLengthOfDaySeconds: meanLengthOfDay, massRatioEarthMoon: MASS_RATIO_EARTH_MOON,
136
+ currentAUDistanceKm: currentAUDistance, gravitationalConstantKm3KgS2: G_CONSTANT,
137
+ });
138
+ const moonOrbitalShift = mass.moonOrbitalShiftKm;
139
+ const moonDistanceCorrected = mass.moonDistanceCorrectedKm;
140
+ const GM_EARTH_MOON_SYSTEM = mass.gmEarthMoonSystemKm3S2;
141
+ const GM_EARTH_ALONE = mass.gmEarthAloneKm3S2;
142
+ const GM_MOON_ALONE = mass.gmMoonAloneKm3S2;
143
+ const GM_SUN_PLUS_EARTH = mass.gmSunPlusEarthKm3S2;
144
+ const GM_SUN = mass.gmSunKm3S2;
145
+ const M_SUN = mass.sunMassKg;
156
146
 
147
+ // ── THE DEEP-TIME CHAIN — ONE assembly (plan 13 B2) ──────────────────────
148
+ // The GIA α channel (the lagged response to the L1 ice history, with the R2
149
+ // lattice pin and the exact-argument memo), the Layer-0 bundle, the cited
150
+ // Earth–Moon distance history (plan 12 option A), the ΔT cycle stack (all
151
+ // channels ON here), |s₃|, the LOD/ΔT core and the lunar month chain are
152
+ // assembled once in deltat/deep-time-chain.js from this context's constants
153
+ // and fitted coefficients (so a counterfactual context flows through them);
154
+ // the Node twin and the browser destructure the same module.
155
+ const chain = createDeepTimeChain({
156
+ constants: C, fitted: F, deepModes: DEEP_MODES_ARTIFACT, moonDistanceArtifact: MOON_DISTANCE_ARTIFACT,
157
+ ageAnchorYear: startmodelYear,
158
+ });
159
+ const { alphaGia, earthMoiFactorAtAge, EPOCH_PARAMS, recession, dtCycles, dtCycleLodCorrectionSum, s3ArcsecPerYr, deepLod, deepEcc, moonChain } = chain;
160
+ const CLIMATE_REGIME = F.CLIMATE_FORMULA_COEFFS.regimes['lr04-post-mpt'];
157
161
  /** The climate formula's L1 (unlagged) — the orbital δ¹⁸O layer itself. @param {number} year @returns {number} */
158
162
  const evalClimateL1 = (year) => evalClimateL1OrbitalPermil(year, {
159
163
  l1Terms: CLIMATE_REGIME.L1,
160
164
  yStdDenormalization: CLIMATE_REGIME.denormalization.y_std,
161
165
  });
162
- // ONE home (climate/l1-orbital.cjs): k derived from the Cox–Chao rate, τ the
163
- // record-measured relaxation time (plan 06 D7). Same construction in
164
- // src/script.js, tools/lib/deep-time.js and the website's essrt.ts.
165
- const alphaGia = createAlphaGiaChannel({
166
- l1Terms: CLIMATE_REGIME.L1,
167
- yStdDenormalization: CLIMATE_REGIME.denormalization.y_std,
168
- relaxationKyr: C.deepTime.alphaGiaRelaxationKyr,
169
- alphaGiaRateJ2000PerYr: C.deepTime.alphaGiaRateJ2000PerYr,
170
- alphaJ2000: earthMoiFactorJ2000,
171
- // the channel's second observable, J₂(t)/J₂₀ — scaled on the OBSERVED
172
- // dJ₂/dt; consumed by the spin integration below (the precession of date)
173
- j2RateJ2000PerYr: C.deepTime.j2RateJ2000PerYr,
174
- j2J2000: C.physicalConstants.earthJ2,
175
- });
176
-
177
- // Exact-argument memo (mirrors src/script.js + tools/lib/deep-time.js —
178
- // the round-5 perf campaign): same tMa → bit-identical value; bounded by
179
- // wholesale clear. (Plan 09 step 7a: the lattice-α pin branch left with
180
- // the ∫dt/H_era phase table, the one table this model built under it.)
181
- /** @type {Map<number, number>} */
182
- const alphaMemo = new Map();
183
- /** @param {number} tMa @returns {number} */
184
- const earthMoiFactorAtAge = (tMa) => {
185
- let v = alphaMemo.get(tMa);
186
- if (v === undefined) {
187
- v = alphaGia.alphaAt(2000 - tMa * 1e6);
188
- if (alphaMemo.size >= 8192) alphaMemo.clear();
189
- alphaMemo.set(tMa, v);
190
- }
191
- return v;
192
- };
193
-
194
- // ── Layer 0 + deep-time core ──────────────────────────────────────────────
195
- const EPOCH_PARAMS = deriveEpochParams({
196
- solarLuminosityW: C.physicalConstants.solarLuminosityW,
197
- solarWindKgPerS: C.physicalConstants.solarWindMassLossKgPerS,
198
- speedOfLightKmPerS: C.physicalConstants.speedOfLight,
199
- alpha1PerMa: C.deepTime.alpha1PerMa,
200
- alpha3PerMa3: C.deepTime.alpha3PerMa3,
201
- alpha4PerMa4: C.deepTime.alpha4PerMa4,
202
- meanSiderealYearSeconds,
203
- lodJ2000Seconds: meanLengthOfDay, // plan 09: ω₀ = the SI day (was the H/13 kinematic day)
204
- sunMassKg: M_SUN,
205
- gmEarthAloneKm3S2: GM_EARTH_ALONE,
206
- gmMoonAloneKm3S2: GM_MOON_ALONE,
207
- gravitationalConstantKm3KgS2: G_CONSTANT,
208
- earthMoiFactorJ2000,
209
- earthDiameterKm: C.bodyDiametersKm.earth,
210
- moonDistanceKm,
211
- moonOrbitalEccentricity: C.moonReference.moonOrbitalEccentricityBase,
212
- gmEarthMoonSystemKm3S2: GM_EARTH_MOON_SYSTEM,
213
- // the solar precession torque's inputs (plan 06 Phase 3: the unit H(t) = 13·T_p,composed)
214
- gmSunKm3S2: GM_SUN,
215
- astronomicalUnitKm: currentAUDistance,
216
- earthOrbitalEccentricityJ2000: C.earthOrbital.earthEccentricityJ2000,
217
- moonEclipticInclinationJ2000Deg: C.moonReference.moonEclipticInclinationJ2000,
218
- // p₀'s period — the tropical year of date's equinox term (plan 09 step 5d)
219
- axialPrecessionJ2000Years: certifiedAxialPrecessionJ2000Years(),
220
- });
221
-
222
- // ── Driver 1½: regime-aware recession history + the solar channels ────────
223
- // The quartic stays bit-identical ≤ jointMa (the Wells/Wu-gated era);
224
- // beyond, the fitted staircase spline to the Roche crossing, and the
225
- // ocean-leak/thermal-pump channels make L_EM time-dependent.
226
- const REGIME = C.deepTime.recessionRegime;
227
- const recession = createMoonRecessionHistory({
228
- aMoonNowMetres: EPOCH_PARAMS.moonDistanceNowM,
229
- alpha1PerMa: EPOCH_PARAMS.alpha1PerMa,
230
- alpha3PerMa3: EPOCH_PARAMS.alpha3PerMa3,
231
- alpha4PerMa4: EPOCH_PARAMS.alpha4PerMa4,
232
- regime: {
233
- jointMa: REGIME.jointMa,
234
- knotAgesMa: REGIME.knotAgesMa,
235
- knotDistancesKm: REGIME.knotDistancesKm,
236
- genesisMa: REGIME.genesisMa,
237
- rocheLimitKm: REGIME.rocheLimitKm,
238
- },
239
- });
240
166
  const moonDistanceMetresAtAge = recession.distanceMetresAtAge;
241
- const solarBudget = createSolarChannelBudget({
242
- lTotalJ2000KgM2S: EPOCH_PARAMS.totalAngularMomentumKgM2S,
243
- mMoonAloneKg: EPOCH_PARAMS.moonMassKg,
244
- gmEmM3PerS2: EPOCH_PARAMS.gmEarthMoonM3S2,
245
- eFactorMoon: EPOCH_PARAMS.moonEccentricityFactor,
246
- beta0: REGIME.solarOceanLeakBeta0,
247
- pumpStartMa: REGIME.thermalPumpStartMa,
248
- pumpEndMa: REGIME.thermalPumpEndMa,
249
- pumpFactor: REGIME.thermalPumpFactor,
250
- jointMa: REGIME.jointMa,
251
- genesisMa: REGIME.genesisMa,
252
- distanceMetresAtAge: moonDistanceMetresAtAge,
253
- });
254
-
255
- const DT = F.DT_STACK;
256
- const RES = F.DT_RESONATOR;
257
- // Periods STATED IN YEARS (the fit files' period_yr / T0_yr): plan 06 T5
258
- // measured the former 8H/n labels chance-level; layer B item 3 removed the
259
- // divisor from every runtime. Twins: tools/lib/deep-time.js, src/script.js,
260
- // the website's essrt.ts.
261
- const dtCycles = createDeltaTCycles({
262
- taperFullHalfwidthYears: C.deepTime.dtStackTaperFullHalfwidthYr,
263
- taperTotalHalfwidthYears: C.deepTime.dtStackTaperTotalHalfwidthYr,
264
- tropicalYearSecondsJ2000: meanTropicalYearJ2000Seconds,
265
- cycles: {
266
- bond: { periodYears: DT.bond.period_yr, cosCoeffSeconds: DT.bond.cos_coeff_s, sinCoeffSeconds: DT.bond.sin_coeff_s },
267
- hallstatt: { periodYears: DT.hallstatt.period_yr, cosCoeffSeconds: DT.hallstatt.cos_coeff_s, sinCoeffSeconds: DT.hallstatt.sin_coeff_s },
268
- jose5: { periodYears: DT.jose5.period_yr, cosCoeffSeconds: DT.jose5.cos_coeff_s, sinCoeffSeconds: DT.jose5.sin_coeff_s },
269
- jose4: { periodYears: DT.jose4.period_yr, cosCoeffSeconds: DT.jose4.cos_coeff_s, sinCoeffSeconds: DT.jose4.sin_coeff_s },
270
- },
271
- resonator: {
272
- t0Years: RES.T0_yr,
273
- q: RES.Q,
274
- kicks: RES.kick_epochs_year.map(/** @param {number} t @param {number} i */ (t, i) => ({
275
- tYear: t,
276
- cosSeconds: RES.kick_coefficients_s[i].cos,
277
- sinSeconds: RES.kick_coefficients_s[i].sin,
278
- })),
279
- tones: RES.drive_tones.map(/** @param {{period_yr: number, phi_locked_rad: number, amp_s: number}} t */ (t) => ({ periodYears: t.period_yr, phiLockedRad: t.phi_locked_rad, ampSeconds: t.amp_s })),
280
- },
281
- });
282
-
283
- /** Layer-3/4 cyclic δLOD sum (all shipped channels ON). @param {number} year @returns {number} */
284
- const dtCycleLodCorrectionSum = (year) =>
285
- dtCycles.cycleLodSecondsAt('bond', year)
286
- + dtCycles.cycleLodSecondsAt('hallstatt', year)
287
- + dtCycles.cycleLodSecondsAt('jose5', year)
288
- + dtCycles.cycleLodSecondsAt('jose4', year)
289
- + dtCycles.swingLodSecondsAt(year);
290
-
291
- // Plan 06 S6 — |s₃|, the dominant nodal mode of Earth's orbit: the
292
- // largest-amplitude ζ mode of the banked deep secular modes (the recipe
293
- // the registry's `eclPrecYears`/`obliqCycleYears` and the browser's
294
- // obliquity-beat helper use). ONE home here; the deep-time factory's
295
- // ecliptic missing-motion term, the lunisolar surface and the API read it.
296
- const s3ArcsecPerYr = (() => {
297
- const z = DEEP_MODES_ARTIFACT.earthZeta
298
- .filter((m) => Math.abs(m.omegaRadPerYr) > 1e-9)
299
- .sort((a, b) => Math.hypot(b.re, b.im) - Math.hypot(a.re, a.im))[0];
300
- return Math.abs((z.omegaRadPerYr * 180) / Math.PI) * 3600;
301
- })();
167
+ /** The published surfaces' km reading — NaN beyond the cited history's span (the API's
168
+ * validity span lies inside it; the deep charts stop where the input stops).
169
+ * @param {number} year @returns {number} */
170
+ const moonDistanceKmAtYearOrNaN = (year) => { const m = moonDistanceMetresAtAge(yearToTMa(year)); return m === null ? NaN : m / 1000; };
302
171
 
303
172
  /** THE REALIZED POLE RATE OVER A WINDOW, ″/yr — the mean over [yearFrom, yearTo] of the rate the spin
304
173
  * integration actually turns the pole at: the inertial secular rate of date × the solar-torque factor at
305
174
  * e(t) × J₂(t)/J₂₀ × cos ε(t)/cos ε₀ (ds/dt = α (ŝ·n̂)(ŝ×n̂): the azimuthal rate about n̂ is α cos ε).
175
+ * The secular rate here is the CONSTANT's scaling (lunisolarTorqueRate…), not the published rate of
176
+ * date: the hybrid's own ε(t) carries the ONE cosine, so the cited mean obliquity's factor (plan 12 §8)
177
+ * does not enter twice — inside the ζ-series span the hybrid IS the model's obliquity of date.
306
178
  * Measured (2026-10): the dominant obliquity line of the model's own ε(t) over the last 5 Myr sits at
307
179
  * this mean minus |s₃| (40,938 fitted vs 40,936 predicted), NOT at the J2000 beat (41,098) — the pole
308
180
  * turned faster in the past (spin +0.06 ″/yr over 5 Myr, lower mean obliquity +0.05, higher mean
@@ -323,7 +195,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
323
195
  const kInertial = certifiedAxialPrecessionJ2000Years() / oneSourceM.axialPrecessionYearsLunisolarJ2000;
324
196
  let sum = 0, n = 0;
325
197
  for (let y = lo; y <= hi; y += stepYears) {
326
- const r = deepLod.lunisolarPrecessionRateArcsecPerYrAtAge(yearToTMa(y));
198
+ const r = deepLod.lunisolarTorqueRateArcsecPerYrAtAge(yearToTMa(y));
327
199
  const psiDot = (r === null ? 1296000 / certifiedAxialPrecessionJ2000Years() : r) * kInertial;
328
200
  const f = oneSourceM.precessionOfDateFactorsAt(y);
329
201
  sum += psiDot * f.solarTorque * (useEllipticity ? f.ellipticity : 1) * Math.cos(oneSourceM.epsAt(y) * Math.PI / 180) / cosEps0;
@@ -332,38 +204,6 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
332
204
  return sum / n;
333
205
  };
334
206
 
335
- const deepLod = createDeepTimeLod({
336
- constants: {
337
- lTotalEmKgm2S: EPOCH_PARAMS.totalAngularMomentumKgM2S,
338
- mMoonAloneKg: EPOCH_PARAMS.moonMassKg,
339
- mEarthAloneKg: EPOCH_PARAMS.earthMassKg,
340
- rEarthMetres: EPOCH_PARAMS.earthRadiusM,
341
- gmEmM3PerS2: EPOCH_PARAMS.gmEarthMoonM3S2,
342
- eFactorMoon: EPOCH_PARAMS.moonEccentricityFactor,
343
- aLockMetres: EPOCH_PARAMS.moonLockDistanceM,
344
- aMoonNowMetres: EPOCH_PARAMS.moonDistanceNowM,
345
- alpha1PerMa: EPOCH_PARAMS.alpha1PerMa,
346
- alpha3PerMa3: EPOCH_PARAMS.alpha3PerMa3,
347
- alpha4PerMa4: EPOCH_PARAMS.alpha4PerMa4,
348
- lodNowH13Seconds: EPOCH_PARAMS.lodNowH13Seconds,
349
- meanSiderealYearJ2000Seconds: meanSiderealYearSeconds,
350
- solarMassLossFracPerYear: EPOCH_PARAMS.solarMassLossFracPerYear,
351
- precessionSolarShareJ2000: EPOCH_PARAMS.precessionSolarShareJ2000,
352
- },
353
- moonDistanceMetresAtAge,
354
- moiFactorAtAge: earthMoiFactorAtAge,
355
- // S5: the composed clock's J2000 anchor — the certified year laws' beat
356
- // at 2000 (ONE home below, shared with the hybrid's self-anchor); lazy.
357
- precessionPeriodJ2000YearsFn: () => certifiedAxialPrecessionJ2000Years(),
358
- // Plan 06 T2 item: the solar day's ecliptic missing-motion term rides the
359
- // nodal period 1,296,000/|s₃| (orbital), not the spin unit's H/5.
360
- nodalPeriodYearsFn: () => 1296000 / s3ArcsecPerYr,
361
- cycleLodSumAt: dtCycleLodCorrectionSum,
362
- swingLodAt: (year) => dtCycles.swingLodSecondsAt(year),
363
- swingLodRateAt: (year) => dtCycles.swingLodRateAt(year),
364
- lEmAtAgeKgm2S: solarBudget.lEmAtAgeKgm2S,
365
- });
366
-
367
207
  // (Plan 09 step 7a: the integrated ∫dt/H_era phase table and its counter
368
208
  // `cyclesBetween` — the frozen era clock's own convention H_era =
369
209
  // H₀·LOD/LOD₀, plan 06 D8's second named counter, which every FROZEN comb
@@ -438,7 +278,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
438
278
  // cardinal braid) reads the one-source series below (plan 06 layer B —
439
279
  // the two agree within 4.2e-5 in-era). The laws hook deliberately does
440
280
  // NOT reach this channel.
441
- const deepEcc = createDeepEccChannel(DEEP_MODES_ARTIFACT);
281
+ // (`deepEcc` is the deep-time chain's, destructured above.)
442
282
  /** @param {number} year @returns {number} */
443
283
  // Plan 06 layer B: the PUBLISHED Earth eccentricity is the one-source series
444
284
  // (|z|, the secular orbit); the SUN's e adds the derived mean offset (see
@@ -551,6 +391,12 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
551
391
  // 108.6 ± 8.5 ″/yr at 2.46 Ga; the composed rate reads 104.5).
552
392
  const seriesArt = /** @type {any} */ (secularSeriesArtifact);
553
393
  const sb = seriesArt && seriesArt.bodies && seriesArt.bodies.earth;
394
+ // Plan 12 §8.5 (owner): the hybrid's own domain is the banked ζ series' span (years; null
395
+ // without a series). Beyond it the ζ-mode tail carries the Quaternary mean with no tidal
396
+ // evolution — the API refuses the obliquity there and serves the cited mean beside it.
397
+ const obliquityHybridDomainYears = sb
398
+ ? /** @type {[number, number]} */ ([2000 + seriesArt.t0Yr, 2000 + seriesArt.t0Yr + (sb.zetaQ.length - 1) * sb.stepYr])
399
+ : null;
554
400
  const hist = createDeepOrbitalHistory({
555
401
  zModes: DEEP_MODES_ARTIFACT.earthZ,
556
402
  zetaModes: DEEP_MODES_ARTIFACT.earthZeta,
@@ -570,7 +416,10 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
570
416
  // rendering-device residual imported as the initial condition of the
571
417
  // physics (found when the package began publishing the hybrid).
572
418
  obliquityJ2000Deg: C.earthOrbital.obliquityJ2000_deg,
573
- axialPrecessionYearsAtYearFn: (yr) => deepLod.lunisolarPrecessionPeriodYearsAtAge((startmodelYear - yr) / 1e6) ?? axial0,
419
+ // The CONSTANT's scaling (the torque period), not the published rate of date: the
420
+ // integration's own ŝ·n̂ carries the obliquity's cosine, so the cited mean obliquity's
421
+ // factor (plan 12 §8 option (a)) must not enter twice. Bit-identical to the pre-§8 form.
422
+ axialPrecessionYearsAtYearFn: (yr) => deepLod.lunisolarTorquePeriodYearsAtAge((startmodelYear - yr) / 1e6) ?? axial0,
574
423
  // THE PRECESSION OF DATE (plan 06 §9 item 10): on top of the secular
575
424
  // law above, the solar torque at the eccentricity of date and the
576
425
  // oblateness of date from the GIA channel — both exactly 1 at J2000,
@@ -588,19 +437,23 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
588
437
  // and the year-length rates (±0.5-yr central differences through the
589
438
  // grid) then returned grid-segment averages instead of local rates
590
439
  // (anomalistic of date +2.63 s / +19 s, visit-order dependent).
591
- const samplers = /** @type {Map<number|'deep', any>} */ (new Map());
592
- let deepRangeYr = 0;
440
+ const samplers = /** @type {Map<number, any>} */ (new Map());
593
441
  const gridStep = (/** @type {number} */ n) => (n <= 50000 ? 100 : n <= 2000000 ? 1000 : 5000);
594
- const tierSpan = (/** @type {number} */ n) => (n <= 50000 ? 50000 : n <= 2000000 ? 2000000 : Math.ceil(n * 1.25 / 5000) * 5000);
442
+ const tierSpan = (/** @type {number} */ n) => (n <= 50000 ? 50000 : 2000000);
595
443
  const sampleAt = (/** @type {number} */ year) => {
596
444
  const t = year - 2000, need = Math.max(20000, Math.abs(t) * 1.25);
597
- const span = tierSpan(need);
598
- const key = span > 2000000 ? /** @type {'deep'} */ ('deep') : span;
599
- if (!samplers.has(key) || (key === 'deep' && span > deepRangeYr)) {
600
- if (key === 'deep') deepRangeYr = span;
601
- samplers.set(key, hist.build(span, -span, gridStep(span)));
445
+ if (need > 2000000) {
446
+ // The deep tier (5000-yr grid): walked in the queried DIRECTION only,
447
+ // to two nodes past the query, and EXTENDED by the factory's shared
448
+ // walk when a deeper epoch arrives (plan 13 A1). It was built to
449
+ // ±1.25·|t| in both directions and rebuilt from J2000 on every deeper
450
+ // probe — bit-identical values, 4× the RK4 steps of one walk.
451
+ const span = Math.ceil((Math.abs(t) + 2 * 5000) / 5000) * 5000;
452
+ return hist.build(t < 0 ? -span : span, 0, 5000).at(t);
602
453
  }
603
- return samplers.get(key).at(t);
454
+ const span = tierSpan(need);
455
+ if (!samplers.has(span)) samplers.set(span, hist.build(span, -span, gridStep(span)));
456
+ return samplers.get(span).at(t);
604
457
  };
605
458
  // The anomalistic year rides THIS movement's own apsidal line (the
606
459
  // scene's perihelion) — the factory owns the construction; the planet
@@ -619,6 +472,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
619
472
  periOfDateDegAt: (year) => sampleAt(year).periOfDateDeg,
620
473
  /** 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} */
621
474
  epsAt: (year) => sampleAt(year).epsDeg,
475
+ /** The hybrid's own domain, [yearFrom, yearTo] — the banked ζ series' span (plan 12 §8.5); null without a series. */
476
+ obliquityHybridDomainYears,
622
477
  /** 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}} */
623
478
  precessionOfDateFactorsAt: (year) => hist.precessionOfDateFactorsAt(year - 2000),
624
479
  /** The spin axis's INERTIAL precession period at J2000, years — the self-anchored
@@ -686,8 +541,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
686
541
  const moonSiderealMonthDaysAt = (year) => {
687
542
  const tMa = yearToTMa(year);
688
543
  const lod = deepLod.lodSecondsAtAge(tMa);
689
- if (lod === null) return NaN;
690
- const aAppKm = moonDistanceMetresAtAge(tMa) / 1000;
544
+ const aAppM = moonDistanceMetresAtAge(tMa);
545
+ if (lod === null || aAppM === null) return NaN;
546
+ const aAppKm = aAppM / 1000;
691
547
  const sidYrDays = deepLod.siderealYearSecondsAtAge(tMa) / lod;
692
548
  const deltaA = aAppKm * (1 / (MASS_RATIO_EARTH_MOON + 1)) * (moonSiderealMonthInput / sidYrDays);
693
549
  const aCorrM = (aAppKm + deltaA) * 1000;
@@ -950,11 +806,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
950
806
  // (perigee advances, node regresses), T_p the J2000 precession anchor.
951
807
  // The retired era device counted these as eighth-integers per 8H and read
952
808
  // the periods back as totalDaysInH/N (the ∓13 was H/13 cycles per H).
953
- const moonApsidalOfDateJ2000Seconds = C.moonReference.moonApsidalPrecessionDaysInputICRF * 86400;
954
- const moonNodalOfDateJ2000Seconds = C.moonReference.moonNodalPrecessionDaysInputICRF * 86400;
955
- const moonApsidalJ2000Seconds = 1 / (1 / moonApsidalOfDateJ2000Seconds - 1 / (axialPrecessionJ2000Days * 86400));
956
- const moonNodalJ2000Seconds = 1 / (1 / moonNodalOfDateJ2000Seconds + 1 / (axialPrecessionJ2000Days * 86400));
957
- const moonSiderealMonthJ2000Seconds = moonSiderealMonth * 86400;
809
+ const { moonApsidalJ2000Seconds } = chain;
958
810
  // (Plan 09 step 5c: the per-H of-date cycle counts and the month chain's H²
959
811
  // scaling of the of-date periods are deleted; the of-date perigee/node are
960
812
  // the chain's count compositions.)
@@ -964,43 +816,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
964
816
  const moonAnomalisticMonthDays = 1 / (1 / moonSiderealMonth - 86400 / moonApsidalJ2000Seconds);
965
817
  const moonSynodicMonthDays = moonSiderealMonth * meanSiderealYearDays / (meanSiderealYearDays - moonSiderealMonth);
966
818
 
967
- // moonEcc — the model's ONE eccentricity law — is created above (with
968
- // eccentricityAt); the Moon channel's E-factor e(t)/e(J2000) and its
969
- // perigee/node T² channel ride it, e(J2000) being the observed value exactly.
970
-
971
- // Layer-2 month/precession chain (Brouwer-Clemence m² scaling × the
972
- // e_E-line modulation)
973
- const moonChain = createMoonMonthChain({
974
- constants: {
975
- aMoonNowMetres: EPOCH_PARAMS.moonDistanceNowM,
976
- alpha1PerMa: EPOCH_PARAMS.alpha1PerMa,
977
- alpha3PerMa3: EPOCH_PARAMS.alpha3PerMa3,
978
- alpha4PerMa4: EPOCH_PARAMS.alpha4PerMa4,
979
- gmEarthMoonM3PerS2: EPOCH_PARAMS.gmEarthMoonM3S2,
980
- massRatioEarthMoon: MASS_RATIO_EARTH_MOON,
981
- moonSiderealMonthInputDays: moonSiderealMonthInput,
982
- meanSiderealYearJ2000Seconds: meanSiderealYearSeconds,
983
- moonApsidalJ2000Seconds,
984
- moonNodalJ2000Seconds,
985
- moonSiderealMonthJ2000Seconds,
986
- sPerigee: MOON_ECC_SENSITIVITY_PERIGEE,
987
- sNode: MOON_ECC_SENSITIVITY_NODE,
988
- },
989
- fns: {
990
- meanLodSecondsAtAge: /** @param {number} tMa */ (tMa) => deepLod.lodSecondsAtAge(tMa),
991
- meanSiderealYearSecondsAtAge: /** @param {number} tMa */ (tMa) => deepLod.siderealYearSecondsAtAge(tMa),
992
- // plan 09 step 5c: the tropical month's equinox turn is one per T_p(t)
993
- precessionPeriodYearsAtAge: /** @param {number} tMa */ (tMa) => deepLod.lunisolarPrecessionPeriodYearsAtAge(tMa),
994
- // Decision (ii) (owner-approved MODEL CHANGE, plan 02 §8): the whole
995
- // lunar chain — this modulation, the integrated cycle counts it
996
- // drives, the arguments' eccAt/channelIntegral and the E-factor —
997
- // reads the ONE deep e. The era shift in the ancient eclipse stack is
998
- // accepted and rebaselined with its explanation (deep e is
999
- // La2004-corroborated in-era).
1000
- modulation: /** @param {number} tMa @param {number} s */ (tMa, s) => deepEcc.modulation(tMa, s),
1001
- distanceMetresAtAge: moonDistanceMetresAtAge,
1002
- },
1003
- });
819
+ // The Layer-2 month/precession chain (Brouwer-Clemence m² scaling × the
820
+ // e_E-line modulation on the ONE deep e — decision (ii), plan 02 §8) is
821
+ // the deep-time chain's `moonChain` above.
1004
822
 
1005
823
  // Chain-cycle integrator. S5/S12 conventions: age anchor = startmodelYear
1006
824
  // (the scene's t_Ma convention), grid anchor C(2000) = 0 — grid anchor ≠
@@ -1568,7 +1386,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1568
1386
  // + the calibrated ΔT cycle stack. 86,400.001780 s at J2000.
1569
1387
  lodRealSecondsAtYear: /** @param {number} year @returns {number} */ (year) => lodRealSeconds(year),
1570
1388
  alphaAtYear: /** @param {number} year @returns {number} */ (year) => earthMoiFactorAtAge(yearToTMa(year)),
1571
- moonDistanceKmAtYear: /** @param {number} year @returns {number} */ (year) => moonDistanceMetresAtAge(yearToTMa(year)) / 1000,
1389
+ moonDistanceKmAtYear: /** @param {number} year @returns {number} */ (year) => moonDistanceKmAtYearOrNaN(year),
1572
1390
  // D6: OF-DATE — mass-loss law / the banked planetary λ̇ ratio
1573
1391
  // (identical at J2000 where the ratio ≡ 1 by construction)
1574
1392
  siderealYearSecondsAtYear: /** @param {number} year @returns {number} */ (year) => yearLengthsM.siderealYearSecondsAtYear(year),
@@ -1601,10 +1419,18 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1601
1419
  // ±26 kyr) and T_peri/T_p with T_peri = 1/(1/T_p + 1/T_aps) the
1602
1420
  // perihelion-of-date period (0.812 at J2000) — on the of-date T_p.
1603
1421
  lunisolar: Object.freeze({
1604
- /** The mean lunisolar precession period T_p(t), years — the composed torque rate's period on the derived J2000 anchor (25,771.4 at J2000). @param {number} year @returns {number} */
1422
+ /** 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} */
1605
1423
  meanPeriodYearsAtYear: (year) => { const p = deepLod.lunisolarPrecessionPeriodYearsAtAge(yearToTMa(year)); return p === null ? certifiedAxialPrecessionJ2000Years() : p; },
1606
- /** The composed rate ψ̇(t) = [ω/ω₀]·p₀·[f_S + (1 − f_S)(a₀/a_M)³], ″/yr (p₀ = 50.2883, derived). @param {number} year @returns {number} */
1424
+ /** 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} */
1607
1425
  meanRateArcsecPerYrAtYear: (year) => { const r = deepLod.lunisolarPrecessionRateArcsecPerYrAtAge(yearToTMa(year)); return r === null ? 1296000 / certifiedAxialPrecessionJ2000Years() : r; },
1426
+ /** 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} */
1427
+ torqueRateArcsecPerYrAtYear: (year) => { const r = deepLod.lunisolarTorqueRateArcsecPerYrAtAge(yearToTMa(year)); return r === null ? 1296000 / certifiedAxialPrecessionJ2000Years() : r; },
1428
+ /** 1,296,000/the torque rate, years. @param {number} year @returns {number} */
1429
+ torquePeriodYearsAtYear: (year) => { const p = deepLod.lunisolarTorquePeriodYearsAtAge(yearToTMa(year)); return p === null ? certifiedAxialPrecessionJ2000Years() : p; },
1430
+ /** 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} */
1431
+ precessionObliquityFactorAtYear: (year) => { const f = deepLod.precessionObliquityFactorAtAge(yearToTMa(year)); return f === null ? 1 : f; },
1432
+ /** 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} */
1433
+ meanObliquityCitedDegAtYear: (year) => { const e = chain.meanObliquityCitedDegAtAge(yearToTMa(year)); return e === null ? NaN : e; },
1608
1434
  /** The of-date period — the one-family beat inside ±2 Myr, the composed mean beyond. @param {number} year @returns {number} */
1609
1435
  ofDatePeriodYearsAtYear: publishedAxialPrecessionYearsAtYear,
1610
1436
  /** f_S, the solar fraction of the J2000 precession torque. */
@@ -1697,7 +1523,15 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1697
1523
  // fitted 16-harmonic K comb that sat beside it as `obliquityCombDeg`
1698
1524
  // (a device fitted to the scene's own wheel geometry, 1.27° off
1699
1525
  // La2004 at −20 kyr) is deleted with the frozen era device (plan 09).
1526
+ // Beyond the ζ series' span (±10 Myr) the hybrid carries NO tidal evolution
1527
+ // of the mean obliquity (nothing in ds/dt = α(ŝ·n̂)(ŝ×n̂) moves ŝ·n̂'s mean):
1528
+ // its deep mean is the Quaternary mean on the tail's mean plane (plan 12
1529
+ // §8.3). The cited mean obliquity of date is `meanObliquityCitedDeg`.
1700
1530
  obliquityDeg: /** @param {number} year @returns {number} */ (year) => oneSourceM.epsAt(year),
1531
+ /** 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()). */
1532
+ obliquityHybridDomainYears: oneSourceM.obliquityHybridDomainYears,
1533
+ /** 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} */
1534
+ meanObliquityCitedDeg: /** @param {number} year @returns {number} */ (year) => { const e = chain.meanObliquityCitedDegAtAge(yearToTMa(year)); return e === null ? NaN : e; },
1701
1535
  eccentricity: eccentricityAt,
1702
1536
  inclinationDeg,
1703
1537
  ascendingNodeDeg,
@@ -1746,7 +1580,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1746
1580
  // the single surface the panel, charts, API and website consume.
1747
1581
  yearLengths: yearLengthsM,
1748
1582
  moon: Object.freeze({
1749
- distanceKmAtYear: /** @param {number} year @returns {number} */ (year) => moonDistanceMetresAtAge(yearToTMa(year)) / 1000,
1583
+ distanceKmAtYear: /** @param {number} year @returns {number} */ (year) => moonDistanceKmAtYearOrNaN(year),
1750
1584
  siderealMonthDaysAtYear: moonSiderealMonthDaysAt,
1751
1585
  synodicMonthDays: moonSynodicMonthDays,
1752
1586
  // The apparent-position chain (truncated Meeus Ch. 47 series on
@@ -1780,7 +1614,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1780
1614
  // construction sites (tools/verify/eclipse-audit.js, the browser
1781
1615
  // _eclipse twins) spread the SAME assembly into their own
1782
1616
  // createEclipseFinders call instead of triplicating it (the
1783
- // three-runtimes rule — cf. recession-history):
1617
+ // three-runtimes rule — cf. moon-distance-history):
1784
1618
  frameworkSunDeps: Object.freeze({
1785
1619
  sunMeanLongitudeJ2000Deg: sunL0Deg,
1786
1620
  tropicalRateDegPerCy: sunTropicalRateDegPerCy,
@@ -38,37 +38,11 @@
38
38
 
39
39
  'use strict';
40
40
 
41
- /**
42
- * Astronomical JD → decimal year: Julian calendar before 1582-10-15,
43
- * Gregorian after (Meeus Ch. 7 inverse). The S3-aligned coordinate for the
44
- * Sun secular deviations. @param {number} jd @returns {number}
45
- */
46
- function jdToDecimalYear(jd) {
47
- const J = jd + 0.5;
48
- const Z = Math.floor(J);
49
- const F = J - Z;
50
- let A = Z;
51
- if (Z >= 2299161) {
52
- const alpha = Math.floor((Z - 1867216.25) / 36524.25);
53
- A = Z + 1 + alpha - Math.floor(alpha / 4);
54
- }
55
- const B = A + 1524;
56
- const C = Math.floor((B - 122.1) / 365.25);
57
- const D = Math.floor(365.25 * C);
58
- const E = Math.floor((B - D) / 30.6001);
59
- const day = B - D - Math.floor(30.6001 * E) + F;
60
- const month = (E < 14) ? E - 1 : E - 13;
61
- const year = (month > 2) ? C - 4716 : C - 4715;
62
- const isGregorian = (Z >= 2299161);
63
- const isLeap = isGregorian
64
- ? (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0))
65
- : (year % 4 === 0);
66
- const monthLengths = [31, isLeap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
67
- let dayOfYear = day;
68
- for (let m = 0; m < month - 1; m++) dayOfYear += monthLengths[m];
69
- const daysInYear = isLeap ? 366 : 365;
70
- return year + (dayOfYear - 1) / daysInYear;
71
- }
41
+ // Astronomical JD → decimal year (Meeus Ch. 7 inverse, the 1582-10-15
42
+ // reform): the S3-aligned coordinate for the Sun secular deviations. ONE
43
+ // home since plan 13 B1 — time/calendar.cjs; re-exported here for the
44
+ // readers that import it from the arguments module.
45
+ const { jdToDecimalYear } = require('../time/calendar.cjs');
72
46
 
73
47
  /**
74
48
  * @typedef {{Lp: number, D: number, M: number, Mp: number, F: number}} MoonArgsDeg