@essrt/physics 7.0.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,27 +1,37 @@
1
1
  /**
2
- * Moon apparent position — THE shared implementation (Phase 8.2-7, the
3
- * final lunar layer). The D5 derived optics + the post-hoc RA/Dec override:
2
+ * Moon scene position — the ecliptic → equatorial conversion of the series
3
+ * output, THE shared implementation (Phase 8.2-7). Since plan 06 R3 item 1 it
4
+ * is ONLY that conversion: the rendered Moon is the GEOMETRIC series Moon
5
+ * (moon/series.cjs sceneEvalAt, the derived extension inside), on the same
6
+ * convention as the rendered Sun and planets.
4
7
  *
5
- * - sunGeoVecEqD5: framework-native geocentric Sun vector (equatorial km).
6
- * e(T) from the anchored observed eccentricity + drift; EoC coefficients
7
- * DERIVED from e via the Kepler series (2e − e³/4, (5/4)e², (13/12)e³ —
8
- * the identity the D1 laboratory proved at 2 ppm); mean-longitude rate =
9
- * framework tropical year; mean-anomaly rate = that minus the H/16
10
- * perihelion rate; R from the AU distance; ε from the framework
11
- * obliquity. One J2000 anchor: sunMeanLongitudeJ2000_deg.
12
- * - moonAberrationRaDec: central-difference v_E/c annual aberration,
13
- * SUBTRACTED (delta TO the aberration-removed direction u − v/c).
14
- * - overrideRaDec: ecl→eq (Meeus eq. 13.3/13.4) on the series output,
15
- * then framework-native analytic aberration + the small fitted residual
16
- * (MOON_CORRECTION_RESIDUAL), or the legacy 3-argument fitted
17
- * MOON_CORRECTION in pure-Meeus A/B mode.
18
- *
19
- * LOAD-BEARING: the correction patch's hard-coded linear arguments
20
- * (297.850 + 12.19074912·dJD, 134.963 + 13.06499295·dJD,
21
- * 357.529 + 0.98560028·dJD) are DELIBERATELY independent of the argument
22
- * skeleton — the fitted coefficients were produced against THIS form
23
- * (coefficients and runtime form are a matched pair). Do not "unify" them
24
- * with the args dispatcher.
8
+ * WHAT LEFT, and the measurement that retired it (record; the values stay in
9
+ * git history and docs/retired-record.md):
10
+ * - the "D5 derived optics" — the annual aberration v_E/c applied to the
11
+ * Moon's direction ("moonAberrationRaDec"). That operation turns the
12
+ * geometric Moon into its ASTROMETRIC place (the body at emission time,
13
+ * the observer's aberration not applied — Horizons quantity 1, a
14
+ * star-chart convention 20″·cos D from both the geometric and the
15
+ * apparent Moon). D5 had validated against exactly that reference and
16
+ * so applied the REFERENCE'S convention to the RENDERED Moon. The scene
17
+ * is an orrery of where the bodies are — geometric: Earth's shadow is
18
+ * cast by the geometric Sun and the Moon enters it at its geometric
19
+ * place — so the conversion belongs to the comparison instrument (the
20
+ * pipeline verifier bridges it, tools/lib/optimizer.js), not to the
21
+ * scene. Measured over 1970–2049: the bare series + extension against
22
+ * Horizons' APPARENT Moon (≡ geometric − 0.7″ for a co-moving body)
23
+ * −1.0″ ± 1.5″ flat in elongation; the D5-shifted rendered Moon +32.5″ +
24
+ * 20″·cos D against the same. (Plan 06 R3 scene conventions — the
25
+ * earlier wording "wrong physics" over-reached: astrometric is a valid
26
+ * convention, applied on the wrong side of the boundary.)
27
+ * - the fitted RA/Dec patches MOON_CORRECTION (pure-Meeus A/B) and
28
+ * MOON_CORRECTION_RESIDUAL (the post-D5 remainder, dominated by a −5.1″
29
+ * raCosMp term): fitted around the layer above at syzygies; the series +
30
+ * extension carry a ~1.3″ cos M′ structure against Horizons and no fitted
31
+ * value replaces it.
32
+ * - the fitted mean-longitude anchor moonMeeusLpCorrection (+32.75″, now 0,
33
+ * model-parameters.json): the eclipse tier's mean Sun compensated (κ +
34
+ * the I2 long inequality + the trend-ΔT offset), see eclipse/besselian.cjs.
25
35
  *
26
36
  * S8 (obliquity source) stays ENGINE-INJECTED per call: the browser passes
27
37
  * its live scene value (o.obliquityEarth, refreshed in updatePositions);
@@ -32,122 +42,31 @@
32
42
  'use strict';
33
43
 
34
44
  /**
35
- * @typedef {{raSinD: number, raCosD: number, raSinMp: number, raCosMp: number,
36
- * raSinMs: number, raCosMs: number, decSinD: number, decCosD: number,
37
- * decSinMp: number, decCosMp: number, decSinMs: number, decCosMs: number}} MoonCorrectionTable
38
- */
39
-
40
- /**
41
- * @param {{
42
- * constants: {
43
- * j2000JD: number,
44
- * julianCenturyDays: number,
45
- * sunMeanLongitudeJ2000Deg: number,
46
- * perihelionLongitudeJ2000Deg: number,
47
- * eccentricityJ2000: number,
48
- * eccentricityDotJ2000: number,
49
- * d5RateLDegPerDay: number,
50
- * d5RatePeriDegPerDay: number,
51
- * speedOfLight: number,
52
- * },
53
- * fns: {
54
- * computeObliquityEarth: (year: number) => number,
55
- * getAuDistanceKm: () => number,
56
- * isFrameworkNative: () => boolean,
57
- * getCorrectionResidual: () => (MoonCorrectionTable | null),
58
- * getCorrectionLegacy: () => (MoonCorrectionTable | null),
59
- * },
60
- * }} deps — the D5 rates are the engines' J2000-frozen values (the year
61
- * globals are deep-time-mutable, same pattern as FW_A2_RATE); the AU
62
- * distance is a GETTER (browser-mutable under deep time).
45
+ * No dependencies since R3 item 1 (the former D5 optics constants and the
46
+ * fitted-patch getters are gone); the factory shape is kept for the two
47
+ * scene twins' call sites.
48
+ * @param {object} [_deps] ignored
63
49
  */
64
- function createMoonApparent({ constants, fns }) {
65
- const {
66
- j2000JD, julianCenturyDays, sunMeanLongitudeJ2000Deg,
67
- perihelionLongitudeJ2000Deg, eccentricityJ2000, eccentricityDotJ2000,
68
- d5RateLDegPerDay, d5RatePeriDegPerDay, speedOfLight,
69
- } = constants;
70
- const {
71
- computeObliquityEarth, getAuDistanceKm, isFrameworkNative,
72
- getCorrectionResidual, getCorrectionLegacy,
73
- } = fns;
74
-
75
- /** Framework-native geocentric Sun vector, equatorial km.
76
- * @param {number} jd @returns {[number, number, number]} */
77
- function sunGeoVecEqD5(jd) {
78
- const T = (jd - j2000JD) / julianCenturyDays;
79
- const d = jd - j2000JD;
80
- const L0 = sunMeanLongitudeJ2000Deg + d5RateLDegPerDay * d;
81
- const M = ((L0 - (perihelionLongitudeJ2000Deg + d5RatePeriDegPerDay * d)) + 180) * Math.PI / 180; // geocentric-perigee convention (Sun perigee = Earth perihelion + 180°)
82
- const e = eccentricityJ2000 + eccentricityDotJ2000 * T;
83
- const Ceq = ((2 * e - Math.pow(e, 3) / 4) * Math.sin(M)
84
- + 1.25 * e * e * Math.sin(2 * M)
85
- + (13 / 12) * Math.pow(e, 3) * Math.sin(3 * M)) * 180 / Math.PI; // deg (Kepler EoC series)
86
- const lam = (L0 + Ceq) * Math.PI / 180;
87
- const v = M + Ceq * Math.PI / 180;
88
- const R = (1 - e * e) / (1 + e * Math.cos(v)) * getAuDistanceKm();
89
- const eps = computeObliquityEarth(2000 + d / 365.2425) * Math.PI / 180;
90
- return [R * Math.cos(lam), R * Math.sin(lam) * Math.cos(eps), R * Math.sin(lam) * Math.sin(eps)];
91
- }
92
-
93
- /** Annual-aberration delta (central difference, v_E/c SUBTRACTED).
94
- * @param {number} jd @param {number} ra @param {number} dec
95
- * @returns {{dRA: number, dDec: number}} */
96
- function moonAberrationRaDec(jd, ra, dec) {
97
- const h = 0.02; // days (central difference)
98
- const a = sunGeoVecEqD5(jd - h), b = sunGeoVecEqD5(jd + h);
99
- const s = 1 / (2 * h * 86400 * speedOfLight);
100
- const kx = -(b[0] - a[0]) * s, ky = -(b[1] - a[1]) * s, kz = -(b[2] - a[2]) * s; // v_E/c
101
- const cd = Math.cos(dec);
102
- const ux = cd * Math.cos(ra), uy = cd * Math.sin(ra), uz = Math.sin(dec);
103
- const wx = ux - kx, wy = uy - ky, wz = uz - kz; // SUBTRACT the aberration content
104
- const wr = Math.sqrt(wx * wx + wy * wy + wz * wz);
105
- let dRA = Math.atan2(wy, wx) - ra;
106
- dRA = Math.atan2(Math.sin(dRA), Math.cos(dRA));
107
- return { dRA, dDec: Math.asin(Math.max(-1, Math.min(1, wz / wr))) - dec };
108
- }
109
-
110
- /** The post-hoc RA/Dec override on the series output. Returns EQUATORIAL
111
- * radians — the engines own their scene storage conventions (the browser
112
- * stores dec as π/2 − dec, phi form).
113
- * @param {{lonDeg: number, betRad: number, meeusT: (number | undefined), obliquityDeg: number}} p
50
+ function createMoonApparent(_deps) {
51
+ /** The RA/Dec of the series output — ecliptic → equatorial (Meeus eq. 13.3,
52
+ * 13.4) on the injected obliquity. Returns EQUATORIAL radians — the engines
53
+ * own their scene storage conventions (the browser stores dec as π/2 − dec,
54
+ * phi form). `meeusT` is accepted for call-site stability and unused.
55
+ * @param {{lonDeg: number, betRad: number, meeusT?: (number | undefined), obliquityDeg: number}} p
114
56
  * @returns {{raRad: number, decRad: number}} */
115
- function overrideRaDec({ lonDeg, betRad, meeusT, obliquityDeg }) {
57
+ function overrideRaDec({ lonDeg, betRad, obliquityDeg }) {
116
58
  const eps = obliquityDeg * (Math.PI / 180);
117
59
  const cosE = Math.cos(eps), sinE = Math.sin(eps);
118
60
  const lamR = lonDeg * (Math.PI / 180);
119
61
  const sinLam = Math.sin(lamR), cosLam = Math.cos(lamR);
120
62
  const sinBet = Math.sin(betRad), cosBet = Math.cos(betRad);
121
-
122
- // Ecliptic → equatorial (Meeus eq. 13.3, 13.4)
123
63
  let newRA = Math.atan2(sinLam * cosE - Math.tan(betRad) * sinE, cosLam);
124
64
  if (newRA < 0) newRA += 2 * Math.PI;
125
- let newDec = Math.asin(sinBet * cosE + cosBet * sinE * sinLam);
126
-
127
- if (isFrameworkNative()) {
128
- const ab = moonAberrationRaDec(j2000JD + (meeusT || 0) * julianCenturyDays, newRA, newDec);
129
- newRA += ab.dRA;
130
- newDec += ab.dDec;
131
- }
132
- const mc = isFrameworkNative() ? getCorrectionResidual() : getCorrectionLegacy();
133
- if (mc) {
134
- const d2r = Math.PI / 180;
135
- const dJD = (meeusT || 0) * julianCenturyDays;
136
- // MATCHED-PAIR arguments — fitted against these exact linear forms.
137
- const Dc = (297.850 + 12.19074912 * dJD) * d2r;
138
- const Mpc = (134.963 + 13.06499295 * dJD) * d2r;
139
- const Msc = (357.529 + 0.98560028 * dJD) * d2r;
140
- newRA -= (mc.raSinD * Math.sin(Dc) + mc.raCosD * Math.cos(Dc)
141
- + mc.raSinMp * Math.sin(Mpc) + mc.raCosMp * Math.cos(Mpc)
142
- + mc.raSinMs * Math.sin(Msc) + mc.raCosMs * Math.cos(Msc)) * d2r;
143
- newDec -= (mc.decSinD * Math.sin(Dc) + mc.decCosD * Math.cos(Dc)
144
- + mc.decSinMp * Math.sin(Mpc) + mc.decCosMp * Math.cos(Mpc)
145
- + mc.decSinMs * Math.sin(Msc) + mc.decCosMs * Math.cos(Msc)) * d2r;
146
- }
65
+ const newDec = Math.asin(sinBet * cosE + cosBet * sinE * sinLam);
147
66
  return { raRad: newRA, decRad: newDec };
148
67
  }
149
68
 
150
- return { sunGeoVecEqD5, moonAberrationRaDec, overrideRaDec };
69
+ return { overrideRaDec };
151
70
  }
152
71
 
153
72
  module.exports = { createMoonApparent };
@@ -52,7 +52,8 @@
52
52
  * A1-class correlated-subsample effect (the tracked events sit −1.6″
53
53
  * below the +1.4″ fleet mean and sample the near-annual arguments at
54
54
  * correlated phases; the era-split refutes anchor double-counting and
55
- * the modern era improves most, 3.57 → 3.34). The ≤8″ api gate holds
55
+ * the modern era improves most, 3.57 → 3.34). The api gate (8″ then; 12″
56
+ * since plan 06 R3 item 1 — the physical ΔT-convention floor) holds
56
57
  * (max 6.2″). Coefficients are stored with the run→real argument
57
58
  * mapping already applied (the extraction's heliocentric-planet vs
58
59
  * geocentric-sun π-offset is folded into the signs).
@@ -26,6 +26,13 @@
26
26
 
27
27
  'use strict';
28
28
 
29
+ // Plan 06 R3 item 1: the DERIVED series-extension tail (the D2 planetary and
30
+ // node terms, moon/series-extension.cjs) is part of the ONE scene Moon —
31
+ // formerly added by the besselian tier alone, so the rendered Moon and the
32
+ // eclipse-location Moon were two different Moons. The truncated finder forms
33
+ // below stay bare by certified design.
34
+ const { moonSeriesExtensionDeg } = require('./series-extension.cjs');
35
+
29
36
  /**
30
37
  * @typedef {Array<[number, number, number, number, number]>} MeeusTermTable
31
38
  */
@@ -164,10 +171,17 @@ function createMoonSeries({ constants, fns }) {
164
171
  * owns the UT→TT conversion — engine timing conventions differ).
165
172
  * Returns everything the scene blocks write:
166
173
  * thetaAddRad — the hierarchy θ increment (EoC-half-subtracted Σl);
167
- * lonDeg — full ecliptic longitude incl. moonMeeusLpCorrection;
168
- * latRad/latDeg — Σb with all six corrections (BOTH computed from Σb
169
- * directly: the browser stores radians, the Node engine degrees, and
170
- * (x·D2R)/D2R is not bit-exactly x); distKm — two-term ellipse; T.
174
+ * lonDeg — full ecliptic longitude (GEOMETRIC, mean equinox of date)
175
+ * incl. the derived series-extension tail and moonMeeusLpCorrection
176
+ * (retired to 0 at plan 06 R3 item 1: measured against Horizons' apparent
177
+ * Moon over 1970–2049 the series needs no anchor, −1.0″ ± 1.5″ flat in
178
+ * elongation; the former +32.75″ was the eclipse tier's mean Sun
179
+ * compensated — its aberration κ, the I2 long inequality and the trend-ΔT
180
+ * offset — not a property of the Moon);
181
+ * latRad/latDeg — Σb with all six corrections plus the extension (BOTH
182
+ * computed from Σb directly: the browser stores radians, the Node engine
183
+ * degrees, and (x·D2R)/D2R is not bit-exactly x); distKm — two-term
184
+ * ellipse; T.
171
185
  * @param {number} dDaysTT
172
186
  * @returns {{thetaAddRad: number, lonDeg: number, latRad: number, latDeg: number, distKm: number, T: number}} */
173
187
  function sceneEvalAt(dDaysTT) {
@@ -180,9 +194,11 @@ function createMoonSeries({ constants, fns }) {
180
194
  const E = eFactorForD(d, T, T2);
181
195
  const E2 = E * E;
182
196
  const { A1, A2, A3 } = additionalArgs(T, d);
197
+ const ext = moonSeriesExtensionDeg(T); // derived tail, degrees → the tables' 1e-6° units
183
198
 
184
199
  let Sl = sumTable(moonL, Dr, Mr, Mpr, Fr, E, E2);
185
200
  Sl += 3958 * Math.sin(A1) + 1962 * Math.sin(Lp - Fr) + 318 * Math.sin(A2);
201
+ Sl += ext.dLonDeg * 1e6;
186
202
  // Subtract the EoC portion the off-centre orbit geometry already provides.
187
203
  const eocHalf = getEccentricityBase() / 2;
188
204
  Sl -= (2 * eocHalf / D2R * 1e6) * Math.sin(Mpr);
@@ -193,6 +209,7 @@ function createMoonSeries({ constants, fns }) {
193
209
  Sb += -2235 * Math.sin(Lp) + 382 * Math.sin(A3);
194
210
  Sb += 175 * Math.sin(A1 - Fr) + 175 * Math.sin(A1 + Fr);
195
211
  Sb += 127 * Math.sin(Lp - Mpr) - 115 * Math.sin(Lp + Mpr);
212
+ Sb += ext.dLatDeg * 1e6;
196
213
  const latRad = Sb * 1e-6 * D2R;
197
214
  const latDeg = Sb * 1e-6;
198
215