@essrt/physics 4.4.0 → 4.4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@essrt/physics",
3
- "version": "4.4.0",
3
+ "version": "4.4.1",
4
4
  "description": "ESSRT (Expanding Solar System Resonance Theory) physics core \u2014 pure, injectable-constants, no I/O, no globals, no DOM. The complete model incl. fitted coefficients; every published version immutably ships one recorded model identity.",
5
5
  "essrt": {
6
6
  "modelVersion": "v13.0",
@@ -83,6 +83,46 @@ function createDeepOrbitalHistory({
83
83
  const s0 = sum(0), R = [anchor[0] - s0[0], anchor[1] - s0[1]];
84
84
  return (/** @type {number} */ t) => { const [x, y] = sum(t); return [x + R[0], y + R[1]]; };
85
85
  };
86
+ // CO-ROTATING anchor (the ϖ̇ fix, measured): the DC residual above is a
87
+ // spurious zero-frequency mode — it preserves the J2000 VALUE but dilutes
88
+ // the local argument rate by |sum(0)|/|anchor| (z: 11.47 → 7.93″/yr, a
89
+ // 92 s-class anomalistic-year bias in the mode tier). Here the residual
90
+ // ROTATES at the raw sum's own local arg-rate g* = Im(ż·z̄)/|z|²
91
+ // (analytic), so at t=0 both the value AND the rate are exact, and at
92
+ // depth the residual is one |R|-amplitude term at a physical frequency
93
+ // instead of a DC offset (deep envelope measured unchanged:
94
+ // [0.0001, 0.0672] → [0.0003, 0.0678] over 10–50 Myr).
95
+ // z ONLY: ζ keeps the DC anchor DELIBERATELY — its anchor is ~0 by
96
+ // definition (Earth's inclination to the J2000 ecliptic at J2000), i.e.
97
+ // the coordinate origin; a rotating residual there would inject a fake
98
+ // wobble into n̂(t).
99
+ const mkAnchoredCoRot = (
100
+ /** @type {ReadonlyArray<{omegaRadPerYr: number, re: number, im: number}>} */ modes,
101
+ /** @type {[number, number]} */ anchor,
102
+ ) => {
103
+ const sum = (/** @type {number} */ t) => {
104
+ let re = 0, im = 0;
105
+ for (const m of modes) {
106
+ const c = Math.cos(m.omegaRadPerYr * t), s = Math.sin(m.omegaRadPerYr * t);
107
+ re += m.re * c - m.im * s;
108
+ im += m.re * s + m.im * c;
109
+ }
110
+ return [re, im];
111
+ };
112
+ let q0 = 0, p0 = 0, dq0 = 0, dp0 = 0;
113
+ for (const m of modes) {
114
+ q0 += m.re; p0 += m.im;
115
+ dq0 += -m.omegaRadPerYr * m.im;
116
+ dp0 += m.omegaRadPerYr * m.re;
117
+ }
118
+ const gStar = (dp0 * q0 - dq0 * p0) / (q0 * q0 + p0 * p0);
119
+ const R = [anchor[0] - q0, anchor[1] - p0];
120
+ return (/** @type {number} */ t) => {
121
+ const [x, y] = sum(t);
122
+ const c = Math.cos(gStar * t), s = Math.sin(gStar * t);
123
+ return [x + R[0] * c - R[1] * s, y + R[0] * s + R[1] * c];
124
+ };
125
+ };
86
126
  const s2h = Math.sin(anchorInclEclipticDeg / 2 * D2R);
87
127
  const zetaAnchor = /** @type {[number, number]} */ (
88
128
  [s2h * Math.cos(anchorAscNodeEclipticDeg * D2R), s2h * Math.sin(anchorAscNodeEclipticDeg * D2R)]);
@@ -108,7 +148,7 @@ function createDeepOrbitalHistory({
108
148
  }
109
149
  const zAnchor = /** @type {[number, number]} */ (
110
150
  [anchorE * Math.cos(anchorPeriEclipticDeg * D2R), anchorE * Math.sin(anchorPeriEclipticDeg * D2R)]);
111
- const zModeSum = mkAnchored(zModes, zAnchor);
151
+ const zModeSum = mkAnchoredCoRot(zModes, zAnchor); // co-rotating residual — the ϖ̇ fix (see mkAnchoredCoRot)
112
152
  // C-4a: the z-side one-source path — same construction as zetaSeries
113
153
  // (anchored engine series inside the span, anchored mode sum as the tail).
114
154
  let zAt = zModeSum;