@essrt/physics 23.0.0 → 23.2.1

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 (36) hide show
  1. package/package.json +7 -2
  2. package/src/constants/coefficients.d.ts +1 -1
  3. package/src/constants/coefficients.js +3 -3
  4. package/src/constants/generated.d.ts +4 -3
  5. package/src/constants/generated.js +6 -5
  6. package/src/constants/units.cjs +20 -0
  7. package/src/deltat/deep-time-chain.js +457 -0
  8. package/src/deltat/deep-time.cjs +42 -13
  9. package/src/deltat/moon-distance-artifact.cjs +6 -3
  10. package/src/earth/deep-orbital-history.cjs +44 -15
  11. package/src/earth/mass-chain.cjs +95 -0
  12. package/src/earth/mean-obliquity-history.cjs +94 -0
  13. package/src/earth/precession-composed.cjs +49 -3
  14. package/src/index.js +17 -0
  15. package/src/layer0/index.js +20 -6
  16. package/src/math/angles.cjs +38 -0
  17. package/src/model.js +76 -227
  18. package/src/moon/arguments.cjs +5 -31
  19. package/src/planets/chain-artifact.js +2 -2
  20. package/src/time/calendar.cjs +165 -0
  21. package/types/constants/coefficients.d.ts +1 -1
  22. package/types/constants/generated.d.ts +4 -3
  23. package/types/constants/units.d.cts +8 -0
  24. package/types/deltat/deep-time-chain.d.ts +266 -0
  25. package/types/deltat/deep-time.d.cts +14 -0
  26. package/types/deltat/moon-distance-artifact.d.cts +3 -0
  27. package/types/earth/deep-orbital-history.d.cts +5 -3
  28. package/types/earth/mass-chain.d.cts +78 -0
  29. package/types/earth/mean-obliquity-history.d.cts +32 -0
  30. package/types/earth/precession-composed.d.cts +11 -1
  31. package/types/index.d.ts +6 -0
  32. package/types/layer0/index.d.ts +3 -2
  33. package/types/math/angles.d.cts +18 -0
  34. package/types/model.d.ts +21 -3
  35. package/types/moon/arguments.d.cts +2 -6
  36. package/types/time/calendar.d.cts +78 -0
@@ -57,9 +57,11 @@
57
57
  * measured at the 500-yr artifact cadence: 0.16″/0.18″ rms vs IAU-2006
58
58
  * over 1900–2100/1600–2400 vs the era tier's 0.31″/0.63″, and 0.0396°
59
59
  * vs La2004 over −200 kyr vs the deep tier's 0.069° — the C-1 verdict,
60
- * plan 02) and zetaModes serves only as the TAIL beyond the span (the
61
- * seam at the span edge is the extraction residual, far outside every
62
- * certified window). solarTorqueShareJ2000 and
60
+ * plan 02) and zetaModes serves only as the TAIL beyond the span, joined
61
+ * to the series at each edge so n̂ is continuous there (plan 12 §8.3 — the
62
+ * J2000-point anchor left a 1.6° step at −10 Myr that kicked the hybrid's
63
+ * mean obliquity +0.9° for the whole tail; far outside every certified window).
64
+ * solarTorqueShareJ2000 and
63
65
  * dynamicalEllipticityRatioAtYearFn: THE PRECESSION OF DATE — the two
64
66
  * factors on the precession constant (see alphaAtGeneral below); absent,
65
67
  * the constant is the secular law alone, bit-identical.
@@ -73,19 +75,22 @@ function createDeepOrbitalHistory({
73
75
  }) {
74
76
  const D2R = Math.PI / 180, R2D = 180 / Math.PI;
75
77
 
78
+ const mkRawSum = (
79
+ /** @type {ReadonlyArray<{omegaRadPerYr: number, re: number, im: number}>} */ modes,
80
+ ) => (/** @type {number} */ t) => {
81
+ let re = 0, im = 0;
82
+ for (const m of modes) {
83
+ const c = Math.cos(m.omegaRadPerYr * t), s = Math.sin(m.omegaRadPerYr * t);
84
+ re += m.re * c - m.im * s;
85
+ im += m.re * s + m.im * c;
86
+ }
87
+ return [re, im];
88
+ };
76
89
  const mkAnchored = (
77
90
  /** @type {ReadonlyArray<{omegaRadPerYr: number, re: number, im: number}>} */ modes,
78
91
  /** @type {[number, number]} */ anchor,
79
92
  ) => {
80
- const sum = (/** @type {number} */ t) => {
81
- let re = 0, im = 0;
82
- for (const m of modes) {
83
- const c = Math.cos(m.omegaRadPerYr * t), s = Math.sin(m.omegaRadPerYr * t);
84
- re += m.re * c - m.im * s;
85
- im += m.re * s + m.im * c;
86
- }
87
- return [re, im];
88
- };
93
+ const sum = mkRawSum(modes);
89
94
  const s0 = sum(0), R = [anchor[0] - s0[0], anchor[1] - s0[1]];
90
95
  return (/** @type {number} */ t) => { const [x, y] = sum(t); return [x + R[0], y + R[1]]; };
91
96
  };
@@ -161,9 +166,33 @@ function createDeepOrbitalHistory({
161
166
  const nS = sq.length, tEndYr = t0Yr + (nS - 1) * stepYr;
162
167
  const li = mkSeriesCubic(t0Yr, stepYr, nS);
163
168
  const R = [zetaAnchor[0] - li(sq, 0), zetaAnchor[1] - li(sp, 0)];
164
- zetaAt = (/** @type {number} */ t) => (t >= t0Yr && t <= tEndYr)
165
- ? [li(sq, t) + R[0], li(sp, t) + R[1]]
166
- : zetaModeSum(t);
169
+ // THE TAIL JOINS THE SERIES AT EACH SEAM (plan 12 §8.3, measured 2026-10).
170
+ // The mode-sum tail anchored to the J2000 POINT reached the span edges
171
+ // 1.61° (past) / 0.74° (future) away from the series — its ~70-kyr phases
172
+ // drift over 10 Myr (the extraction residual) and its J2000 anchor misses
173
+ // the series' mean plane by 0.506° — and the hybrid's mean obliquity
174
+ // STEPPED +0.90° / +0.30° there and stayed stepped (24.17° over ±1 Myr at
175
+ // −20 Myr against 23.3° inside the span; the API served it to ±500 Myr).
176
+ // Three anchors measured: J2000 point (above), the series' MEAN plane
177
+ // (n̂ step 1.12°/0.54°, ε step +0.74°/+0.57°), and the SEAM POINT per side
178
+ // — the tail's constant chosen so n̂ is continuous at the edge it continues
179
+ // (ε step +0.09°/+0.13°, the tail's mean 23.31–23.36° over ±100 Myr). The
180
+ // pole follows n̂ adiabatically (ds/dt = α(ŝ·n̂)(ŝ×n̂) conserves ŝ·n̂ for a
181
+ // slowly moving n̂), so a DISCONTINUITY in n̂ is what kicks ε, not the
182
+ // tail's mean plane: continuity wins. Inside the span nothing changes
183
+ // (bit-identical); beyond it the hybrid carries the Quaternary mean on the
184
+ // tail — NO tidal evolution of the mean (the cited mean obliquity of date
185
+ // is model.earth.meanObliquityCitedDeg).
186
+ const rawSum = mkRawSum(zetaModes);
187
+ const e0 = rawSum(t0Yr), e1 = rawSum(tEndYr);
188
+ const RPast = [li(sq, t0Yr) + R[0] - e0[0], li(sp, t0Yr) + R[1] - e0[1]];
189
+ const RFuture = [li(sq, tEndYr) + R[0] - e1[0], li(sp, tEndYr) + R[1] - e1[1]];
190
+ zetaAt = (/** @type {number} */ t) => {
191
+ if (t >= t0Yr && t <= tEndYr) return [li(sq, t) + R[0], li(sp, t) + R[1]];
192
+ const [x, y] = rawSum(t);
193
+ const Rt = t < t0Yr ? RPast : RFuture;
194
+ return [x + Rt[0], y + Rt[1]];
195
+ };
167
196
  }
168
197
  const zAnchor = /** @type {[number, number]} */ (
169
198
  [anchorE * Math.cos(anchorPeriEclipticDeg * D2R), anchorE * Math.sin(anchorPeriEclipticDeg * D2R)]);
@@ -0,0 +1,95 @@
1
+ /**
2
+ * The J2000 mass chain and the Moon's J2000 periods — ONE home (holisticuniverse
3
+ * plan 13 B3). Before B3 the mass chain (Moon Kepler → GM_EM → the Earth/Moon
4
+ * split → the Sun) was computed in three places (model.js, tools/lib/
5
+ * constants.js, src/script.js), and the Moon's derived periods in three more.
6
+ *
7
+ * Every expression here is the twins' expression in the twins' operation
8
+ * order (floating point is not associative; the goldens pin the values).
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ /**
14
+ * The mass chain in §9 order: Δa (the Sun's tidal coupling to the Earth–Moon
15
+ * barycentric wobble, leading order), the corrected Moon distance, Kepler on
16
+ * the Moon's orbit → G(M_E + M_M), the DE440 split → GM_E and GM_M, Kepler on
17
+ * Earth's orbit → G(M_☉ + M_E), the subtraction → GM_☉ and M_☉.
18
+ * @param {{
19
+ * moonDistanceKm: number, moonSiderealMonthDays: number, meanSiderealYearDays: number,
20
+ * meanLengthOfDaySeconds: number, massRatioEarthMoon: number, currentAUDistanceKm: number,
21
+ * gravitationalConstantKm3KgS2: number,
22
+ * }} c
23
+ * @returns {{
24
+ * meanSiderealYearSeconds: number, moonOrbitalShiftKm: number, moonDistanceCorrectedKm: number,
25
+ * gmEarthMoonSystemKm3S2: number, gmEarthAloneKm3S2: number, gmMoonAloneKm3S2: number,
26
+ * gmSunPlusEarthKm3S2: number, gmSunKm3S2: number, sunMassKg: number,
27
+ * }}
28
+ */
29
+ function computeMassChainJ2000(c) {
30
+ const meanSiderealYearSeconds = c.meanSiderealYearDays * 86400;
31
+ const moonOrbitalShiftKm = c.moonDistanceKm * (1 / (c.massRatioEarthMoon + 1)) * (c.moonSiderealMonthDays / c.meanSiderealYearDays);
32
+ const moonDistanceCorrectedKm = c.moonDistanceKm + moonOrbitalShiftKm;
33
+ const gmEarthMoonSystemKm3S2 = (4 * Math.PI * Math.PI * Math.pow(moonDistanceCorrectedKm, 3))
34
+ / Math.pow(c.moonSiderealMonthDays * c.meanLengthOfDaySeconds, 2);
35
+ const gmEarthAloneKm3S2 = gmEarthMoonSystemKm3S2 * (c.massRatioEarthMoon / (c.massRatioEarthMoon + 1));
36
+ const gmMoonAloneKm3S2 = gmEarthMoonSystemKm3S2 / (c.massRatioEarthMoon + 1);
37
+ const gmSunPlusEarthKm3S2 = (4 * Math.PI * Math.PI * Math.pow(c.currentAUDistanceKm, 3))
38
+ / Math.pow(meanSiderealYearSeconds, 2);
39
+ const gmSunKm3S2 = gmSunPlusEarthKm3S2 - gmEarthAloneKm3S2;
40
+ const sunMassKg = gmSunKm3S2 / c.gravitationalConstantKm3KgS2;
41
+ return { meanSiderealYearSeconds, moonOrbitalShiftKm, moonDistanceCorrectedKm, gmEarthMoonSystemKm3S2, gmEarthAloneKm3S2, gmMoonAloneKm3S2, gmSunPlusEarthKm3S2, gmSunKm3S2, sunMassKg };
42
+ }
43
+
44
+ /**
45
+ * The Moon's J2000 periods on the IAU inputs. The two precession inputs are
46
+ * equinox-OF-DATE observables (3231.50 d / 6798.38 d; the legacy "ICRF" name
47
+ * is kept); the star-referenced periods remove the equinox's own precession,
48
+ * 1/T_E = 1/T_I ∓ 1/T_p (the perigee advances, the node regresses), T_p the
49
+ * J2000 precession anchor in days. The months follow kinematically.
50
+ * @param {{
51
+ * moonSiderealMonthDays: number, moonApsidalPrecessionDaysInput: number, moonNodalPrecessionDaysInput: number,
52
+ * axialPrecessionJ2000Days: number, meanSiderealYearDays: number, meanSolarYearDays: number,
53
+ * }} c
54
+ * @returns {{
55
+ * moonApsidalPrecessionDaysEarth: number, moonNodalPrecessionDaysEarth: number,
56
+ * moonApsidalJ2000Seconds: number, moonNodalJ2000Seconds: number, moonSiderealMonthJ2000Seconds: number,
57
+ * moonAnomalisticMonthDays: number, moonNodalMonthDays: number, moonSynodicMonthDays: number, moonTropicalMonthDays: number,
58
+ * moonFullMoonCycleEarthDays: number, moonFullMoonCycleICRFDays: number,
59
+ * moonApsidalMeetsNodalDays: number, moonLunarLevelingCycleDays: number,
60
+ * moonDraconicYearICRFDays: number, moonDraconicYearEarthDays: number,
61
+ * }}
62
+ */
63
+ function computeMoonPeriodsJ2000(c) {
64
+ const T = c.axialPrecessionJ2000Days;
65
+ const M = c.moonSiderealMonthDays;
66
+ // the days forms (tools/lib/constants.js §7, src/script.js)
67
+ const moonApsidalPrecessionDaysEarth = 1 / (1 / c.moonApsidalPrecessionDaysInput - 1 / T);
68
+ const moonNodalPrecessionDaysEarth = 1 / (1 / c.moonNodalPrecessionDaysInput + 1 / T);
69
+ const moonAnomalisticMonthDays = 1 / (1 / M - 1 / moonApsidalPrecessionDaysEarth);
70
+ const moonNodalMonthDays = 1 / (1 / M + 1 / moonNodalPrecessionDaysEarth);
71
+ const moonSynodicMonthDays = M * c.meanSiderealYearDays / (c.meanSiderealYearDays - M);
72
+ const moonTropicalMonthDays = 1 / (1 / M + 1 / T);
73
+ const moonFullMoonCycleEarthDays = (moonSynodicMonthDays / (moonSynodicMonthDays - moonAnomalisticMonthDays)) * moonAnomalisticMonthDays;
74
+ const moonFullMoonCycleICRFDays = 1 / (1 / moonFullMoonCycleEarthDays + 1 / T);
75
+ const moonApsidalMeetsNodalDays = (moonNodalMonthDays / (moonAnomalisticMonthDays - moonNodalMonthDays)) * moonAnomalisticMonthDays;
76
+ const moonLunarLevelingCycleDays = (moonNodalPrecessionDaysEarth / (moonNodalPrecessionDaysEarth - moonApsidalPrecessionDaysEarth) * (moonApsidalPrecessionDaysEarth / c.meanSolarYearDays)) * c.meanSolarYearDays;
77
+ const moonDraconicYearICRFDays = 1 / ((1 / c.meanSolarYearDays) + (1 / moonNodalPrecessionDaysEarth));
78
+ const moonDraconicYearEarthDays = 1 / (1 / moonDraconicYearICRFDays - 1 / T);
79
+ // the seconds forms the deep-time chain anchors on (model.js / tools/lib/deep-time.js — the same
80
+ // relation written on the seconds, so the J2000 anchors of the month chain are bit-identical)
81
+ const moonApsidalOfDateJ2000Seconds = c.moonApsidalPrecessionDaysInput * 86400;
82
+ const moonNodalOfDateJ2000Seconds = c.moonNodalPrecessionDaysInput * 86400;
83
+ const moonApsidalJ2000Seconds = 1 / (1 / moonApsidalOfDateJ2000Seconds - 1 / (T * 86400));
84
+ const moonNodalJ2000Seconds = 1 / (1 / moonNodalOfDateJ2000Seconds + 1 / (T * 86400));
85
+ const moonSiderealMonthJ2000Seconds = M * 86400;
86
+ return {
87
+ moonApsidalPrecessionDaysEarth, moonNodalPrecessionDaysEarth,
88
+ moonApsidalJ2000Seconds, moonNodalJ2000Seconds, moonSiderealMonthJ2000Seconds,
89
+ moonAnomalisticMonthDays, moonNodalMonthDays, moonSynodicMonthDays, moonTropicalMonthDays,
90
+ moonFullMoonCycleEarthDays, moonFullMoonCycleICRFDays, moonApsidalMeetsNodalDays, moonLunarLevelingCycleDays,
91
+ moonDraconicYearICRFDays, moonDraconicYearEarthDays,
92
+ };
93
+ }
94
+
95
+ module.exports = { computeMassChainJ2000, computeMoonPeriodsJ2000 };
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The mean obliquity of date ε̄(t) — the SECOND column the model reads from the
3
+ * cited Earth–Moon history (plan 12 §8, option (a)): Farhat, Auclair-Desrotour,
4
+ * Boué & Laskar 2022's own mean obliquity, carried in
5
+ * data/astrogeo22-earth-moon.json beside the distance.
6
+ *
7
+ * WHY AN INPUT. The composed lunisolar law (earth/precession-composed.cjs)
8
+ * scales the precession CONSTANT of date — α(t)/α₀ on the spin history and the
9
+ * lunar torque's (a₀/a_M)³. The precession RATE is α(t)·cos ε̄(t), and the chain
10
+ * had held cos ε̄ at its J2000 value at every epoch — a purity freeze without
11
+ * its twin. Standard theory since Goldreich 1966 (Touma & Wisdom 1994; Farhat
12
+ * et al. 2022) has the mean obliquity lower in the deep past, evolved by the
13
+ * lunar tidal torque through the Laplace-plane transition — the same
14
+ * ocean-dissipation physics option A declined to model for the distance. The
15
+ * published solution that computes ε̄(t) alongside a(t) is the input, as the
16
+ * distance is. Measured (plan 12 §8.1): the factor cos ε̄(t)/cos ε̄(0) closes
17
+ * the composed rate to Farhat's own precession column from −2.0 % to +0.4 % at
18
+ * 2.46 Ga and moves every paleo precession referee toward its source.
19
+ *
20
+ * WHAT IS READ. The column's cosine RATIO cos ε̄(t)/cos ε̄(0) — the SHAPE, as
21
+ * the distance is read as a(t)/a(0). The column's own value is published as the
22
+ * cited mean: 23.2545° at age 0, a Myr-scale MEAN of a secular solution, not
23
+ * the J2000 instant 23.4393° the hybrid obliquity integration starts from.
24
+ *
25
+ * INTERPOLATION. Cubic Hermite with central-difference tangents between the
26
+ * 1-Myr samples (one-sided at the ends) — C1. The column is monotone (measured:
27
+ * no sign change of its first difference across the 4,429 samples), so no
28
+ * limiter is needed. Forward of J2000 (t < 0) the column's J2000 one-sided
29
+ * slope is held — a labelled extrapolation, as the distance holds the LLR rate
30
+ * (the input has no future). Beyond the table's last sample the history
31
+ * REFUSES: null, never an extrapolation.
32
+ *
33
+ * HISTORICAL ERA. The slope at J2000 is −0.00264°/Myr: the factor is
34
+ * 1 − 2.0e-7 at 10 kyr and 1 − 2.0e-6 at 100 kyr — the ΔT era is untouched at
35
+ * the 1e-7 class; the deep-time clock T_p(t) shortens 0.75 % at the Devonian
36
+ * and 2.5 % at 2.46 Ga.
37
+ */
38
+
39
+ 'use strict';
40
+
41
+ const D2R = Math.PI / 180;
42
+
43
+ /**
44
+ * @typedef {Object} MeanObliquityTable
45
+ * @property {ReadonlyArray<number>} ageMa - ascending, 0 first, uniform 1-Myr step
46
+ * @property {ReadonlyArray<number>} obliquityDeg - the cited mean obliquity column, degrees
47
+ */
48
+
49
+ /**
50
+ * @param {{ table: MeanObliquityTable }} deps
51
+ * @returns {{
52
+ * meanObliquityDegAtAge: (tMa: number) => number|null,
53
+ * cosRatioAtAge: (tMa: number) => number|null,
54
+ * meanObliquityJ2000Deg: number,
55
+ * spanMa: number,
56
+ * }}
57
+ */
58
+ function createMeanObliquityHistory(deps) {
59
+ const ages = deps.table.ageMa, eps = deps.table.obliquityDeg;
60
+ const n = ages ? ages.length : 0;
61
+ if (!(n >= 2) || !eps || eps.length !== n || ages[0] !== 0) {
62
+ throw new Error('mean-obliquity-history: the table must start at age 0 with ≥ 2 obliquity samples (a stale moon-distance embed lacks the obliquity column — regenerate the constants)');
63
+ }
64
+ const step = ages[1] - ages[0];
65
+ for (let i = 1; i < n; i++) {
66
+ if (ages[i] - ages[i - 1] !== step) throw new Error(`mean-obliquity-history: non-uniform age step at index ${i}`);
67
+ }
68
+ const spanMa = ages[n - 1];
69
+ // Central-difference tangents, deg per Ma (one-sided at the ends).
70
+ const m = new Float64Array(n);
71
+ m[0] = (eps[1] - eps[0]) / step;
72
+ for (let i = 1; i < n - 1; i++) m[i] = (eps[i + 1] - eps[i - 1]) / (2 * step);
73
+ m[n - 1] = (eps[n - 1] - eps[n - 2]) / step;
74
+ const cos0 = Math.cos(eps[0] * D2R);
75
+
76
+ /** @param {number} tMa @returns {number|null} the cited mean obliquity, degrees */
77
+ function meanObliquityDegAtAge(tMa) {
78
+ if (tMa <= 0) return eps[0] + m[0] * tMa; // forward: the J2000 slope held (labelled extrapolation)
79
+ if (tMa > spanMa) return null; // beyond the input: refused
80
+ const i = Math.min(Math.floor(tMa / step), n - 2);
81
+ const h = step, s = (tMa - ages[i]) / h, s2 = s * s, s3 = s2 * s;
82
+ return (2 * s3 - 3 * s2 + 1) * eps[i] + (s3 - 2 * s2 + s) * h * m[i]
83
+ + (-2 * s3 + 3 * s2) * eps[i + 1] + (s3 - s2) * h * m[i + 1];
84
+ }
85
+ /** cos ε̄(t)/cos ε̄(0) — the factor the precession RATE of date carries on the precession constant. ≡ 1 at J2000.
86
+ * @param {number} tMa @returns {number|null} */
87
+ function cosRatioAtAge(tMa) {
88
+ const e = meanObliquityDegAtAge(tMa);
89
+ return e === null ? null : Math.cos(e * D2R) / cos0;
90
+ }
91
+ return { meanObliquityDegAtAge, cosRatioAtAge, meanObliquityJ2000Deg: eps[0], spanMa };
92
+ }
93
+
94
+ module.exports = { createMeanObliquityHistory };
@@ -13,6 +13,20 @@
13
13
  * (1 − e²)^(−3/2) eccentricity factors and the lunar-inclination factor
14
14
  * 1 − 3/2·sin² i_M; ≈ 0.316, the textbook value). At J2000 every factor is 1.
15
15
  *
16
+ * THE RATE OF DATE vs THE CONSTANT (plan 12 §8, option (a)): the law above
17
+ * scales the precession CONSTANT α(t) ∝ ω(t)·[torque term] — the rate at the
18
+ * J2000 obliquity. The precession RATE is α(t)·cos ε̄(t), so the published
19
+ * rate carries one more factor, the cited mean obliquity's cos ε̄(t)/cos ε̄(0)
20
+ * (earth/mean-obliquity-history.cjs — Farhat 2022's own column, the second
21
+ * column read from the distance history; the chain had held cos ε̄ at J2000
22
+ * at every epoch). torqueRate… is the constant's scaling — what the hybrid
23
+ * obliquity integration takes, since its own ŝ·n̂ carries the cosine;
24
+ * composedRate… is the rate of date — T_p(t), the obliquity beat, the tropical
25
+ * year of date, the paleo referees' precession constant k. Both ≡ p₀ at J2000;
26
+ * the factor is 1 − 2e-7 at 10 kyr and 1.0245 at 2.46 Ga (the composed rate
27
+ * 106.6 → 109.2 ″/yr against Lantink 2022's 108.6 ± 8.5; Farhat's own
28
+ * precession column on the same table: 108.8).
29
+ *
16
30
  * THE ANCHOR p₀ (plan 06 S5 — one J2000 precession reading): p₀ is the
17
31
  * model's DERIVED J2000 rate, 1,296,000/axial0 with axial0 the certified
18
32
  * year-length laws' beat at 2000 (sid/(sid − trop) = 25,771.4 yr = 50.2883
@@ -64,14 +78,20 @@ function computeSolarTorqueShare(c) {
64
78
  * moonDistanceMetresAtAge: (tMa: number) => (number | null),
65
79
  * moonDistanceJ2000Metres: number,
66
80
  * yearToTMa: (year: number) => number,
81
+ * meanObliquityCosRatioAtAge?: (tMa: number) => (number | null),
67
82
  * }} deps - p0 = the model's derived J2000 rate, 1,296,000/axial0 (S5; a
68
83
  * number or a LAZY getter — the callers' certified year laws read the
69
84
  * deep-time factory this is built inside, so the anchor is resolved on
70
85
  * the first RATE use, never at construction; the torque term needs no
71
- * anchor); lodJ2000Seconds the SAME day basis lodSecondsAtAge(0) returns.
86
+ * anchor); lodJ2000Seconds the SAME day basis lodSecondsAtAge(0) returns;
87
+ * meanObliquityCosRatioAtAge the cited mean obliquity's cos ε̄(t)/cos ε̄(0)
88
+ * (OPTIONAL — absent, the rate of date is the constant alone, bit-identical).
72
89
  * @returns {{
73
90
  * composedRateArcsecPerYrAtAge: (tMa: number) => (number | null),
74
91
  * composedPeriodYearsAtAge: (tMa: number) => (number | null),
92
+ * torqueRateArcsecPerYrAtAge: (tMa: number) => (number | null),
93
+ * torquePeriodYearsAtAge: (tMa: number) => (number | null),
94
+ * obliquityFactorAtAge: (tMa: number) => (number | null),
75
95
  * composedRateArcsecPerYrAtYear: (year: number) => (number | null),
76
96
  * composedPeriodYearsAtYear: (year: number) => (number | null),
77
97
  * composedRateRatioAtAge: (tMa: number) => (number | null),
@@ -107,14 +127,37 @@ function createComposedPrecession(deps) {
107
127
  const lf = lunarTorqueFactorAtAge(tMa);
108
128
  return lf === null ? null : fS + (1 - fS) * lf;
109
129
  };
110
- /** @param {number} tMa */
111
- const composedRateArcsecPerYrAtAge = (tMa) => {
130
+ /** cos ε̄(t)/cos ε̄(0) from the cited mean obliquity — 1 when no history is
131
+ * injected (the constant alone, bit-identical). @param {number} tMa @returns {number|null} */
132
+ const cosRatio = deps.meanObliquityCosRatioAtAge;
133
+ /** @type {(tMa: number) => number|null} */
134
+ const obliquityFactorAtAge = cosRatio ? (tMa) => cosRatio(tMa) : () => 1;
135
+ /** THE PRECESSION CONSTANT OF DATE as a rate on the J2000 obliquity, ″/yr —
136
+ * [ω(t)/ω₀]·p₀·[f_S + (1 − f_S)(a₀/a_M)³] = α(t)·cos ε̄₀. The hybrid
137
+ * obliquity integration takes THIS: ds/dt = α(ŝ·n̂)(ŝ×n̂) carries its own cosine.
138
+ * @param {number} tMa */
139
+ const torqueRateArcsecPerYrAtAge = (tMa) => {
112
140
  const lod = deps.lodSecondsAtAge(tMa);
113
141
  const term = torqueTermAtAge(tMa);
114
142
  if (lod === null || !(lod > 0) || term === null) return null;
115
143
  return (deps.lodJ2000Seconds / lod) * p0() * term;
116
144
  };
117
145
  /** @param {number} tMa */
146
+ const torquePeriodYearsAtAge = (tMa) => {
147
+ const r = torqueRateArcsecPerYrAtAge(tMa);
148
+ return r === null ? null : 1296000 / r;
149
+ };
150
+ /** THE PRECESSION RATE OF DATE ψ̇(t), ″/yr — the constant × cos ε̄(t)/cos ε̄(0):
151
+ * the published rate (T_p(t) = 1,296,000/ψ̇, the obliquity beat, the tropical
152
+ * year of date, the paleo referees' precession constant k).
153
+ * @param {number} tMa */
154
+ const composedRateArcsecPerYrAtAge = (tMa) => {
155
+ const r = torqueRateArcsecPerYrAtAge(tMa);
156
+ if (r === null) return null;
157
+ const f = obliquityFactorAtAge(tMa);
158
+ return f === null ? null : r * f;
159
+ };
160
+ /** @param {number} tMa */
118
161
  const composedPeriodYearsAtAge = (tMa) => {
119
162
  const r = composedRateArcsecPerYrAtAge(tMa);
120
163
  return r === null ? null : 1296000 / r;
@@ -134,6 +177,9 @@ function createComposedPrecession(deps) {
134
177
  composedRateArcsecPerYrAtYear: (year) => composedRateArcsecPerYrAtAge(deps.yearToTMa(year)),
135
178
  composedPeriodYearsAtYear: (year) => composedPeriodYearsAtAge(deps.yearToTMa(year)),
136
179
  composedRateRatioAtAge,
180
+ torqueRateArcsecPerYrAtAge,
181
+ torquePeriodYearsAtAge,
182
+ obliquityFactorAtAge,
137
183
  torqueTermAtAge,
138
184
  lunarTorqueFactorAtAge,
139
185
  solarShare: fS,
package/src/index.js CHANGED
@@ -106,6 +106,20 @@ export { createDerivedViews } from './layer1/index.js';
106
106
  // file via the exports-map subpath; re-exported here so bundled ESM
107
107
  // consumers need only the package root.
108
108
  export { createDeepCalendar } from './phase/deep-calendar.cjs';
109
+ // Plan 13 B1: the civil calendar ↔ JD (Meeus ch. 7 with the 1582-10-15
110
+ // reform) and the angle constants/wraps — ONE home; the browser's eighteen
111
+ // calendar copies and 27 local D2R constants import these.
112
+ export { GREGORIAN_START, GREGORIAN_START_JD, calendarToJD, jdToCalendar, jdToDecimalYear, dayFractionToHMS, julianCalendarToJD, jdToJulianCalendar } from './time/calendar.cjs';
113
+ export { DEG2RAD, RAD2DEG, ARCSEC_PER_TURN, ARCSEC_PER_RAD, wrap360, wrap180, wrap180Closed, wrap2pi, wrapPi } from './math/angles.cjs';
114
+ // Plan 13 B2: THE deep-time chain assembly — the GIA α channel, the Layer-0
115
+ // bundle, the cited Moon-distance history, the ΔT cycle stack, |s₃|, the
116
+ // LOD/ΔT core, the lunar month chain and the mean*AtAge family, assembled
117
+ // ONCE from the generated constants; the Node twin and the browser destructure it.
118
+ export { createDeepTimeChain, MOON_ECC_SENSITIVITY_PERIGEE, MOON_ECC_SENSITIVITY_NODE } from './deltat/deep-time-chain.js';
119
+ // Plan 13 B3: the J2000 mass chain and the Moon's J2000 periods (one home for
120
+ // three copies each) and the unit constants.
121
+ export { computeMassChainJ2000, computeMoonPeriodsJ2000 } from './earth/mass-chain.cjs';
122
+ export { SECONDS_PER_DAY, DAYS_PER_JULIAN_YEAR, JULIAN_CENTURY_DAYS, SECONDS_PER_JULIAN_YEAR } from './constants/units.cjs';
109
123
  // D4b: the one-source cardinal structure — the EoC layer (year lengths,
110
124
  // crossing offsets, the e(t)-proportional spread) on the movement's own
111
125
  // e(t)/ϖ(t). Absolute dates deliberately excluded (per-renderer mean chain).
@@ -156,6 +170,9 @@ export { createYearLengths, computeMeanTropicalYearJ2000Days, ONE_FAMILY_WINDOW_
156
170
  // (spin ω(t) × [solar torque + lunar torque on the recession history]) —
157
171
  // leg-1's physical rate; the structural H(t)/13 clock is its named diagnostic.
158
172
  export { computeSolarTorqueShare, createComposedPrecession } from './earth/precession-composed.cjs';
173
+ // Plan 12 §8 option (a): the cited mean obliquity of date — the second column read from the
174
+ // Earth–Moon history; the precession RATE of date carries its cos ε̄(t)/cos ε̄(0) on the constant.
175
+ export { createMeanObliquityHistory } from './earth/mean-obliquity-history.cjs';
159
176
  // (planets/predict.cjs — the planet predictive-precession feature basis — was
160
177
  // retired at plan 06 R8 with PREDICT_COEFFS_PHYSICAL.)
161
178
  // L10 — the composition front door: one law set, N body records. Thin by
@@ -54,18 +54,21 @@
54
54
  * @property {(year: number) => number|null} moonDistanceMetres - null beyond the cited history's span
55
55
  * @property {(year: number) => number|null} lodSeconds
56
56
  * @property {(year: number) => number} siderealYearSeconds
57
- * @property {(year: number) => number} tropicalYearSeconds sidereal less one equinox turn per T_p(t) (the composed precession period)
57
+ * @property {(year: number) => number} tropicalYearSeconds sidereal less one equinox turn per T_p(t) (the composed precession period on the RATE of date)
58
58
  */
59
59
 
60
60
  /**
61
61
  * @param {{params: EpochParams, alphaAtAgeMa: (tMa: number) => number,
62
- * moonDistanceMetresAtAge: (tMa: number) => number|null}} deps —
62
+ * moonDistanceMetresAtAge: (tMa: number) => number|null,
63
+ * meanObliquityCosRatioAtAge?: (tMa: number) => number|null}} deps —
63
64
  * moonDistanceMetresAtAge is the engine's cited distance history
64
65
  * (deltat/moon-distance-history: Farhat et al. 2022 as a shape on the
65
- * J2000 anchor; plan 12 option A — the layer0 quartic twin is deleted).
66
+ * J2000 anchor; plan 12 option A — the layer0 quartic twin is deleted);
67
+ * meanObliquityCosRatioAtAge the cited mean obliquity's cos ε̄(t)/cos ε̄(0)
68
+ * (earth/mean-obliquity-history, plan 12 §8 option (a)) — OPTIONAL, absent → 1.
66
69
  * @returns {EpochPrimitives}
67
70
  */
68
- export const createEpochPrimitives = ({ params: p, alphaAtAgeMa, moonDistanceMetresAtAge }) => {
71
+ export const createEpochPrimitives = ({ params: p, alphaAtAgeMa, moonDistanceMetresAtAge, meanObliquityCosRatioAtAge }) => {
69
72
  /**
70
73
  * Years before the epoch, in Ma. The ONE place this conversion lives.
71
74
  * @param {number} year
@@ -80,6 +83,14 @@ export const createEpochPrimitives = ({ params: p, alphaAtAgeMa, moonDistanceMet
80
83
  */
81
84
  const moonDistanceMetresCore = (t) => moonDistanceMetresAtAge(t);
82
85
 
86
+ /**
87
+ * cos ε̄(t)/cos ε̄(0) of the cited mean obliquity — the precession RATE's
88
+ * factor on the constant (plan 12 §8); 1 when no history is injected.
89
+ * @param {number} t age in Ma
90
+ * @returns {number|null}
91
+ */
92
+ const meanObliquityCosRatioCore = (t) => (meanObliquityCosRatioAtAge ? meanObliquityCosRatioAtAge(t) : 1);
93
+
83
94
  /**
84
95
  * Length of day from angular-momentum conservation: as the Moon recedes it
85
96
  * takes orbital angular momentum and Earth's spin slows to compensate.
@@ -122,7 +133,9 @@ export const createEpochPrimitives = ({ params: p, alphaAtAgeMa, moonDistanceMet
122
133
  * Tropical year of date — the sidereal year less the equinox's own motion
123
134
  * over one year: one turn per the composed lunisolar precession period
124
135
  * T_p(t), i.e. 1/T_trop = 1/T_sid + ψ̇(t)/(1,296,000″ · Julian-year s) with
125
- * ψ̇(t) = [ω₀/ω(t)]⁻¹… spelled as (LOD₀/LOD(t))·p₀·[f_S + (1 − f_S)(a₀/a_M)³].
136
+ * ψ̇(t) the RATE of date, spelled as (LOD₀/LOD(t))·p₀·[f_S + (1 − f_S)(a₀/a_M)³]
137
+ * × cos ε̄(t)/cos ε̄(0) — the cited mean obliquity's factor (plan 12 §8 option (a);
138
+ * injected, 1 when absent).
126
139
  * Plan 09 step 5d: it was the unit's calendar convention sid·(1 − 13/H(t))
127
140
  * (plan 06 D2 kept it as a named device), 0.086 % slow on the equinox term
128
141
  * — 1.0 s of year at J2000, integrated by the deep JD↔year calendar. The
@@ -142,7 +155,8 @@ export const createEpochPrimitives = ({ params: p, alphaAtAgeMa, moonDistanceMet
142
155
  const lf = (a === null || !(a > 0)) ? null : Math.pow(p.moonDistanceNowM / a, 3);
143
156
  const term = lf === null ? null : p.precessionSolarShareJ2000 + (1 - p.precessionSolarShareJ2000) * lf;
144
157
  const p0 = p.precessionRateJ2000ArcsecPerYr;
145
- const rate = (lod === null || !(lod > 0) || term === null) ? p0 : (p.lodNowH13Seconds / lod) * p0 * term;
158
+ const f = meanObliquityCosRatioCore(t);
159
+ const rate = (lod === null || !(lod > 0) || term === null || f === null) ? p0 : (p.lodNowH13Seconds / lod) * p0 * term * f;
146
160
  return 1 / (1 / sid + rate / (1296000 * 365.25 * 86400));
147
161
  };
148
162
 
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Angles — ONE home for the degree ↔ radian constants and the wraps
3
+ * (holisticuniverse plan 13 B1). Before B1 the browser carried 27 local
4
+ * `D2R`-style constants and twelve local wrap helpers in three ±180 forms;
5
+ * the package itself a dozen private `D2R`s.
6
+ *
7
+ * The two ±180 forms in use differ only AT the boundary: `wrap180` maps
8
+ * +180 to −180 (the half-open [−180, 180) the longitude-difference sites
9
+ * use), `wrap180Closed` keeps +180 (the (−180, 180] of the residual
10
+ * printers). A site keeps the form it had — B1 moves the formula, never
11
+ * the convention.
12
+ *
13
+ * Pure, no model dependencies.
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ /** π/180 — degrees to radians. */
19
+ const DEG2RAD = Math.PI / 180;
20
+ /** 180/π — radians to degrees. */
21
+ const RAD2DEG = 180 / Math.PI;
22
+ /** Arcseconds in a full turn. */
23
+ const ARCSEC_PER_TURN = 1296000;
24
+ /** Arcseconds per radian, 206264.806… */
25
+ const ARCSEC_PER_RAD = (180 / Math.PI) * 3600;
26
+
27
+ /** @param {number} x degrees @returns {number} the same angle in [0, 360) */
28
+ const wrap360 = (x) => ((x % 360) + 360) % 360;
29
+ /** @param {number} x degrees @returns {number} the same angle in [−180, 180) */
30
+ const wrap180 = (x) => ((x + 540) % 360 + 360) % 360 - 180;
31
+ /** @param {number} x degrees @returns {number} the same angle in (−180, 180] */
32
+ const wrap180Closed = (x) => { const w = ((x % 360) + 360) % 360; return w > 180 ? w - 360 : w; };
33
+ /** @param {number} x radians @returns {number} the same angle in [0, 2π) */
34
+ const wrap2pi = (x) => { const tau = 2 * Math.PI; return ((x % tau) + tau) % tau; };
35
+ /** @param {number} x radians @returns {number} the same angle in [−π, π) */
36
+ const wrapPi = (x) => { const tau = 2 * Math.PI; return ((x + Math.PI) % tau + tau) % tau - Math.PI; };
37
+
38
+ module.exports = { DEG2RAD, RAD2DEG, ARCSEC_PER_TURN, ARCSEC_PER_RAD, wrap360, wrap180, wrap180Closed, wrap2pi, wrapPi };