@essrt/physics 1.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.
- package/README.md +52 -0
- package/package.json +58 -0
- package/src/cardinal/index.cjs +323 -0
- package/src/chain-cycles/index.cjs +189 -0
- package/src/climate/l1-orbital.cjs +46 -0
- package/src/constants/coefficients.js +52 -0
- package/src/constants/generated.d.ts +710 -0
- package/src/constants/generated.js +868 -0
- package/src/constants/index.js +30 -0
- package/src/deltat/cycles.cjs +256 -0
- package/src/deltat/deep-time.cjs +266 -0
- package/src/deltat/historical.cjs +84 -0
- package/src/eclipse/finders.cjs +311 -0
- package/src/index.js +252 -0
- package/src/layer0/derive-params.js +85 -0
- package/src/layer0/index.js +166 -0
- package/src/layer1/index.js +86 -0
- package/src/layer2/index.js +6 -0
- package/src/moon/apparent.cjs +153 -0
- package/src/moon/arguments.cjs +320 -0
- package/src/moon/ecc-channel.cjs +108 -0
- package/src/moon/month-chain.cjs +251 -0
- package/src/moon/series.cjs +211 -0
- package/src/phase/index.cjs +186 -0
- package/src/planets/asc-node-integrator.cjs +120 -0
- package/src/planets/corrections.cjs +167 -0
- package/src/planets/ecc-channel.cjs +59 -0
- package/src/planets/fibonacci-laws.cjs +136 -0
- package/src/planets/geometry.cjs +145 -0
- package/src/planets/model.cjs +185 -0
- package/src/planets/orbit-chain.cjs +46 -0
- package/src/planets/orientation.cjs +133 -0
- package/src/planets/predict.cjs +324 -0
- package/src/reference/published-curves.cjs +247 -0
- package/src/sun/longitude-correction.cjs +68 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-native lunar fundamental arguments — THE shared implementation
|
|
3
|
+
* (Phase 8.2-5). The measured argument-decomposition recipe of doc 66 §1:
|
|
4
|
+
* {Lp, D, M, Mp, F} in degrees, of-date, Meeus Ch. 47 convention.
|
|
5
|
+
*
|
|
6
|
+
* Extracted VERBATIM from src/script.js (_FW_MOON, _FW_SUN_SEC,
|
|
7
|
+
* _fwSunSecularDeviations, the two bounded Lp carriers, _fwMoonArgsDeep,
|
|
8
|
+
* _fwMoonArgs, and the pure-Meeus reference polynomials), which
|
|
9
|
+
* tools/lib/scene-graph.js mirrored. Mirror gaps closed here:
|
|
10
|
+
*
|
|
11
|
+
* - S3: the Sun secular deviations' year coordinate. The browser used the
|
|
12
|
+
* full Julian/Gregorian calendar conversion; the tools mirror used a
|
|
13
|
+
* linear approximation. The calendar conversion is EMBEDDED here as a
|
|
14
|
+
* private helper, so both engines now evaluate the same coordinate.
|
|
15
|
+
* - S9: ϖ/Ω composition order in the deep branch (wrapper-composed vs
|
|
16
|
+
* use-site subtraction) — one form now, the browser's.
|
|
17
|
+
* - S10: P_DEGCY was derived from the browser's MUTABLE holisticyearLength
|
|
18
|
+
* global (a latent epoch dependency in a J2000-frozen bundle) — it is
|
|
19
|
+
* built from the injected holisticYearJ2000 and has no consumers; kept
|
|
20
|
+
* as documentation of the frame decomposition.
|
|
21
|
+
*
|
|
22
|
+
* Construction (docs/66 §1, abridged): linear rates are observational J2000
|
|
23
|
+
* anchors; perigee/node ride the PHASE-AWARE e_E channel integral (s_ϖ
|
|
24
|
+
* 2.407 / s_Ω 1.018, Meeus-effective); Lp's planetary T² remainder rides
|
|
25
|
+
* the bounded e_E² carrier + the obliquity-line carrier (K_PL derived
|
|
26
|
+
* lazily from the closed v4 budget — zero new constants); D and M are
|
|
27
|
+
* identity-composed (D ≡ Lp − L_sun, M ≡ L_sun − ϖ_sun) with secular
|
|
28
|
+
* content from the closed-form year-length-harmonic integrals. Deep time:
|
|
29
|
+
* the SAME factored-law chains that phase the scene layers supply the
|
|
30
|
+
* skeleton (always-chains, Stage B); snapshot keeps the certified
|
|
31
|
+
* polynomial skeleton, clamped at |T| ≤ 100 cy (unclamped, the fitted T⁴
|
|
32
|
+
* tail reverses the lunar mean motion at year ≈ 1.99e6).
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
'use strict';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Astronomical JD → decimal year: Julian calendar before 1582-10-15,
|
|
39
|
+
* Gregorian after (Meeus Ch. 7 inverse). The S3-aligned coordinate for the
|
|
40
|
+
* Sun secular deviations. @param {number} jd @returns {number}
|
|
41
|
+
*/
|
|
42
|
+
function jdToDecimalYear(jd) {
|
|
43
|
+
const J = jd + 0.5;
|
|
44
|
+
const Z = Math.floor(J);
|
|
45
|
+
const F = J - Z;
|
|
46
|
+
let A = Z;
|
|
47
|
+
if (Z >= 2299161) {
|
|
48
|
+
const alpha = Math.floor((Z - 1867216.25) / 36524.25);
|
|
49
|
+
A = Z + 1 + alpha - Math.floor(alpha / 4);
|
|
50
|
+
}
|
|
51
|
+
const B = A + 1524;
|
|
52
|
+
const C = Math.floor((B - 122.1) / 365.25);
|
|
53
|
+
const D = Math.floor(365.25 * C);
|
|
54
|
+
const E = Math.floor((B - D) / 30.6001);
|
|
55
|
+
const day = B - D - Math.floor(30.6001 * E) + F;
|
|
56
|
+
const month = (E < 14) ? E - 1 : E - 13;
|
|
57
|
+
const year = (month > 2) ? C - 4716 : C - 4715;
|
|
58
|
+
const isGregorian = (Z >= 2299161);
|
|
59
|
+
const isLeap = isGregorian
|
|
60
|
+
? (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0))
|
|
61
|
+
: (year % 4 === 0);
|
|
62
|
+
const monthLengths = [31, isLeap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
63
|
+
let dayOfYear = day;
|
|
64
|
+
for (let m = 0; m < month - 1; m++) dayOfYear += monthLengths[m];
|
|
65
|
+
const daysInYear = isLeap ? 366 : 365;
|
|
66
|
+
return year + (dayOfYear - 1) / daysInYear;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @typedef {{Lp: number, D: number, M: number, Mp: number, F: number}} MoonArgsDeg
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {{
|
|
75
|
+
* constants: {
|
|
76
|
+
* j2000JD: number,
|
|
77
|
+
* julianCenturyDays: number,
|
|
78
|
+
* holisticYearJ2000: number,
|
|
79
|
+
* balancedYearJ2000: number,
|
|
80
|
+
* meanSolarYearDays: number,
|
|
81
|
+
* meanAnomalisticYearDays: number,
|
|
82
|
+
* tropicalYearHarmonics: Array<[number, number, number]>,
|
|
83
|
+
* anomalisticYearHarmonics: Array<[number, number, number]>,
|
|
84
|
+
* eccentricityJ2000: number,
|
|
85
|
+
* eccentricityDotJ2000: number,
|
|
86
|
+
* eccentricityDotDotJ2000: number,
|
|
87
|
+
* elpEarthFigureJ2ArcsecPerCy2: number,
|
|
88
|
+
* elpGeneralPrecessionPA_T2ArcsecPerCy2: number,
|
|
89
|
+
* eccE0: number,
|
|
90
|
+
* },
|
|
91
|
+
* fns: {
|
|
92
|
+
* eccAt: (tYr: number) => number,
|
|
93
|
+
* channelIntegral: (T: number, s: number) => number,
|
|
94
|
+
* computeObliquityEarth: (year: number) => number,
|
|
95
|
+
* jdToSIyear: (jd: number) => number,
|
|
96
|
+
* tropicalOrbitsBetween: (yearA: number, yearB: number) => (number | null),
|
|
97
|
+
* apsidalOfDateCyclesBetween: (yearA: number, yearB: number) => (number | null),
|
|
98
|
+
* nodalOfDateCyclesBetween: (yearA: number, yearB: number) => (number | null),
|
|
99
|
+
* cyclesBetween: (yearA: number, yearB: number, divisorN: number) => (number | null),
|
|
100
|
+
* isDeepTime: () => boolean,
|
|
101
|
+
* isFrameworkNative: () => boolean,
|
|
102
|
+
* },
|
|
103
|
+
* }} deps — fns are the ENGINE'S OWN chain wrappers (each engine's toggle
|
|
104
|
+
* semantics ride along); eccAt/channelIntegral are the shared moon ecc
|
|
105
|
+
* channel; constants are J2000-frozen injections.
|
|
106
|
+
*/
|
|
107
|
+
function createMoonArguments({ constants, fns }) {
|
|
108
|
+
const {
|
|
109
|
+
j2000JD, julianCenturyDays, holisticYearJ2000, balancedYearJ2000,
|
|
110
|
+
meanSolarYearDays, meanAnomalisticYearDays,
|
|
111
|
+
tropicalYearHarmonics, anomalisticYearHarmonics,
|
|
112
|
+
eccentricityJ2000, eccentricityDotJ2000, eccentricityDotDotJ2000,
|
|
113
|
+
elpEarthFigureJ2ArcsecPerCy2, elpGeneralPrecessionPA_T2ArcsecPerCy2,
|
|
114
|
+
eccE0,
|
|
115
|
+
} = constants;
|
|
116
|
+
const {
|
|
117
|
+
eccAt, channelIntegral, computeObliquityEarth, jdToSIyear,
|
|
118
|
+
tropicalOrbitsBetween, apsidalOfDateCyclesBetween, nodalOfDateCyclesBetween,
|
|
119
|
+
cyclesBetween, isDeepTime, isFrameworkNative,
|
|
120
|
+
} = fns;
|
|
121
|
+
|
|
122
|
+
// ── The _FW_MOON bundle: Meeus Ch. 47 J2000 anchors + derived checks ─────
|
|
123
|
+
const bundle = (() => {
|
|
124
|
+
const LP0 = 218.3164477, D0 = 297.8501921, M0 = 357.5291092,
|
|
125
|
+
MP0 = 134.9633964, F0 = 93.2720950;
|
|
126
|
+
const LPR = 481267.88123421, DR = 445267.1114034, MR = 35999.0502909,
|
|
127
|
+
MPR = 477198.8675055, FR = 483202.0175233;
|
|
128
|
+
const P_DEGCY = 360 * 13 / holisticYearJ2000 * 100; // framework general precession, deg/Julian cy (S10: J2000-frozen by injection; no consumers — documentation of the frame decomposition)
|
|
129
|
+
const WDOT = LPR - MPR; // perigee ϖ̇ of-date (+4069.0137) = ϖ̇_ICRF + p
|
|
130
|
+
const NDOT = LPR - FR; // node Ω̇ of-date (−1934.1363) = Ω̇_ICRF + p
|
|
131
|
+
// e_E channel Taylor CHECKS (documented J2000 reference values — the live
|
|
132
|
+
// path is the phase-aware channelIntegral; T2_*/T3_* are NOT consumed):
|
|
133
|
+
const E0 = eccentricityJ2000, EDOT0 = eccentricityDotJ2000;
|
|
134
|
+
const KAPPA = 3 * E0 * EDOT0 / (1 - E0 * E0);
|
|
135
|
+
// v4 frame attribution: FRAME-EFFECTIVE exponents (physical pair after
|
|
136
|
+
// removing the IAU ṗ_A frame term: s_ϖ 2.479 / s_Ω 0.867).
|
|
137
|
+
const S_W = 2.407, S_N = 1.018;
|
|
138
|
+
const T2_W = S_W * WDOT * KAPPA / 2; // −0.010318 °/cy² (Meeus ϖ −0.010320)
|
|
139
|
+
const T2_N = S_N * NDOT * KAPPA / 2; // +0.0020752 °/cy² (Meeus Ω +0.0020753)
|
|
140
|
+
const EDDOT0 = eccentricityDotDotJ2000;
|
|
141
|
+
const KAPPA_DOT = 3 * (EDOT0 * EDOT0 + E0 * EDDOT0) / (1 - E0 * E0)
|
|
142
|
+
+ 6 * E0 * E0 * EDOT0 * EDOT0 / Math.pow(1 - E0 * E0, 2);
|
|
143
|
+
const T3_W = WDOT * (S_W * S_W * KAPPA * KAPPA + S_W * KAPPA_DOT) / 6;
|
|
144
|
+
const T3_N = NDOT * (S_N * S_N * KAPPA * KAPPA + S_N * KAPPA_DOT) / 6;
|
|
145
|
+
// Lp carrier T²: framework tidal n̈/2 (LLR) + planetary secular remainder
|
|
146
|
+
const T2_LP_TIDAL = (-25.86 / 3600) / 2;
|
|
147
|
+
const T2_LP_PLANETARY = -0.0015786 - T2_LP_TIDAL; // +7.247″/cy²
|
|
148
|
+
const T2_LP = T2_LP_TIDAL + T2_LP_PLANETARY;
|
|
149
|
+
return { LP0, D0, M0, MP0, F0, LPR, DR, MR, P_DEGCY, WDOT, NDOT, T2_W, T2_N, T3_W, T3_N, T2_LP, T2_LP_TIDAL, S_W, S_N };
|
|
150
|
+
})();
|
|
151
|
+
|
|
152
|
+
// ── Sun secular deviations: closed-form year-length-harmonic integrals ───
|
|
153
|
+
// Normalized to value 0 AND slope 0 at J2000. KL/KA/KS capture the J2000
|
|
154
|
+
// mean-year values at construction by design; phases use the fixed J2000
|
|
155
|
+
// lattice (the documented snapshot approximation).
|
|
156
|
+
const sunSec = (() => {
|
|
157
|
+
const trop = tropicalYearHarmonics, anom = anomalisticYearHarmonics;
|
|
158
|
+
const PBAR = meanSolarYearDays, ABAR = meanAnomalisticYearDays, DBAR = ABAR - PBAR;
|
|
159
|
+
const KL = -(360 / PBAR);
|
|
160
|
+
const wbar = 360 * DBAR / ABAR;
|
|
161
|
+
const KA = wbar * PBAR / (ABAR * DBAR);
|
|
162
|
+
const KS = -wbar / DBAR;
|
|
163
|
+
/** @param {number} y @param {Array<[number, number, number]>} set */
|
|
164
|
+
const dP = (y, set) => {
|
|
165
|
+
const t = y - balancedYearJ2000; let s = 0;
|
|
166
|
+
for (const [d, sc, cc] of set) {
|
|
167
|
+
const w = 2 * Math.PI / (holisticYearJ2000 / d);
|
|
168
|
+
s += sc * Math.sin(w * t) + cc * Math.cos(w * t);
|
|
169
|
+
} return s;
|
|
170
|
+
};
|
|
171
|
+
/** @param {number} y @param {Array<[number, number, number]>} set */
|
|
172
|
+
const intdP = (y, set) => {
|
|
173
|
+
const t = y - balancedYearJ2000; let s = 0;
|
|
174
|
+
for (const [d, sc, cc] of set) {
|
|
175
|
+
const w = 2 * Math.PI / (holisticYearJ2000 / d);
|
|
176
|
+
s += (-sc * Math.cos(w * t) + cc * Math.sin(w * t)) / w;
|
|
177
|
+
} return s;
|
|
178
|
+
};
|
|
179
|
+
return { int0T: intdP(2000, trop), int0A: intdP(2000, anom),
|
|
180
|
+
slope0L: KL * dP(2000, trop),
|
|
181
|
+
slope0P: KA * dP(2000, anom) + KS * dP(2000, trop),
|
|
182
|
+
KL, KA, KS, intdP, trop, anom };
|
|
183
|
+
})();
|
|
184
|
+
|
|
185
|
+
/** @param {number} jdTT @returns {{dLs: number, dPeri: number}} */
|
|
186
|
+
function sunSecularDeviations(jdTT) {
|
|
187
|
+
const y = jdToDecimalYear(jdTT); // S3: the calendar coordinate, both engines
|
|
188
|
+
const S = sunSec;
|
|
189
|
+
const iT = S.intdP(y, S.trop) - S.int0T, iA = S.intdP(y, S.anom) - S.int0A;
|
|
190
|
+
return {
|
|
191
|
+
dLs: S.KL * iT - S.slope0L * (y - 2000),
|
|
192
|
+
dPeri: S.KA * iA + S.KS * iT - S.slope0P * (y - 2000),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ── Bounded Lp carriers (K_PL / C_OBL derived lazily — zero new constants)
|
|
197
|
+
/** @type {number | null} */
|
|
198
|
+
let kPl = null;
|
|
199
|
+
/** @param {number} T @returns {number} */
|
|
200
|
+
function planetaryCarrier(T) {
|
|
201
|
+
if (T === 0) return 0;
|
|
202
|
+
if (kPl === null) {
|
|
203
|
+
const de2dT = Math.pow(eccAt(50), 2) - Math.pow(eccAt(-50), 2); // Δ(e²) per cy at J2000
|
|
204
|
+
const t2Obl = (elpEarthFigureJ2ArcsecPerCy2 + elpGeneralPrecessionPA_T2ArcsecPerCy2) / 3600;
|
|
205
|
+
kPl = 2 * (bundle.T2_LP - bundle.T2_LP_TIDAL - t2Obl) / de2dT;
|
|
206
|
+
}
|
|
207
|
+
// The channel's e0 anchor CONST — not eccAt(0), which under integrated
|
|
208
|
+
// phase carries the R3 drift correction (the 8.2-2 convention).
|
|
209
|
+
const e0sq = eccE0 * eccE0;
|
|
210
|
+
/** @param {number} t */
|
|
211
|
+
const f = (t) => { const e = eccAt(t * 100); return e * e - e0sq; };
|
|
212
|
+
const N = Math.max(2, 2 * Math.ceil(Math.abs(T) * 100 / 8000));
|
|
213
|
+
const h = T / N;
|
|
214
|
+
let sum = f(0) + f(T);
|
|
215
|
+
for (let i = 1; i < N; i++) sum += f(i * h) * (i % 2 ? 4 : 2);
|
|
216
|
+
return kPl * sum * h / 3;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** @type {{eps0: number, C: number} | null} */
|
|
220
|
+
let obl = null;
|
|
221
|
+
/** @param {number} T @returns {number} */
|
|
222
|
+
function obliquityCarrier(T) {
|
|
223
|
+
if (T === 0) return 0;
|
|
224
|
+
if (obl === null) {
|
|
225
|
+
const T2_OBL = (elpEarthFigureJ2ArcsecPerCy2 + elpGeneralPrecessionPA_T2ArcsecPerCy2) / 3600;
|
|
226
|
+
const eps0 = computeObliquityEarth(2000);
|
|
227
|
+
const epsDot = computeObliquityEarth(2050) - computeObliquityEarth(1950);
|
|
228
|
+
obl = { eps0, C: 2 * T2_OBL / epsDot };
|
|
229
|
+
}
|
|
230
|
+
/** @param {number} t */
|
|
231
|
+
const f = (t) => computeObliquityEarth(2000 + t * 100) - /** @type {{eps0: number, C: number}} */ (obl).eps0;
|
|
232
|
+
const N = Math.max(2, 2 * Math.ceil(Math.abs(T) * 100 / 2000));
|
|
233
|
+
const h = T / N;
|
|
234
|
+
let sum = f(0) + f(T);
|
|
235
|
+
for (let i = 1; i < N; i++) sum += f(i * h) * (i % 2 ? 4 : 2);
|
|
236
|
+
return /** @type {{eps0: number, C: number}} */ (obl).C * sum * h / 3;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ── Deep-time branch: always-chains (Stage B) ────────────────────────────
|
|
240
|
+
/** @type {number | null} */
|
|
241
|
+
let argsY0 = null;
|
|
242
|
+
/** @param {number} jd @returns {MoonArgsDeg | null} */
|
|
243
|
+
function fwArgsDeep(jd) {
|
|
244
|
+
const A = bundle;
|
|
245
|
+
if (argsY0 === null) argsY0 = jdToSIyear(j2000JD);
|
|
246
|
+
const y = jdToSIyear(jd);
|
|
247
|
+
const Ntrop = tropicalOrbitsBetween(argsY0, y);
|
|
248
|
+
const Naps = apsidalOfDateCyclesBetween(argsY0, y);
|
|
249
|
+
const Nnod = nodalOfDateCyclesBetween(argsY0, y);
|
|
250
|
+
const Nperi = cyclesBetween(argsY0, y, 16);
|
|
251
|
+
if (Ntrop === null || Naps === null || Nnod === null || Nperi === null) return null; // tidal-lock guard
|
|
252
|
+
/** @param {number} x */
|
|
253
|
+
const wrap = (x) => ((x % 360) + 360) % 360;
|
|
254
|
+
const dev = sunSecularDeviations(jd);
|
|
255
|
+
const Tj = (jd - j2000JD) / julianCenturyDays;
|
|
256
|
+
const Lp = A.LP0 + 360 * Ntrop + planetaryCarrier(Tj) + obliquityCarrier(Tj);
|
|
257
|
+
const w = (A.LP0 - A.MP0) + 360 * Naps; // perigee ϖ (of-date, advance)
|
|
258
|
+
const om = (A.LP0 - A.F0) - 360 * Nnod; // node Ω (of-date, regression)
|
|
259
|
+
const Lsun = (A.LP0 - A.D0) + 360 * (y - argsY0) + dev.dLs; // mean Sun (model timeline)
|
|
260
|
+
const ws = (A.LP0 - A.D0 - A.M0) + 360 * Nperi + dev.dPeri; // Sun perihelion (H/16 chain)
|
|
261
|
+
return {
|
|
262
|
+
Lp: wrap(Lp), D: wrap(Lp - Lsun), M: wrap(Lsun - ws),
|
|
263
|
+
Mp: wrap(Lp - w), F: wrap(Lp - om),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Framework-native args {Lp, D, M, Mp, F} (degrees, of-date) at JD_TT.
|
|
268
|
+
* Deep time: the chains; else the certified polynomial skeleton with the
|
|
269
|
+
* phase-aware channel perigee/node and identity-composed D/M.
|
|
270
|
+
* @param {number} jdTT @returns {MoonArgsDeg} */
|
|
271
|
+
function fwArgs(jdTT) {
|
|
272
|
+
if (isDeepTime()) {
|
|
273
|
+
const dt = fwArgsDeep(jdTT);
|
|
274
|
+
if (dt !== null) return dt;
|
|
275
|
+
}
|
|
276
|
+
const A = bundle;
|
|
277
|
+
const T = (jdTT - j2000JD) / julianCenturyDays;
|
|
278
|
+
/** @param {number} x */
|
|
279
|
+
const wrap = (x) => ((x % 360) + 360) % 360;
|
|
280
|
+
// Polynomial tails clamped at |T| ≤ 100 cy — see the header.
|
|
281
|
+
const Tc = Math.max(-100, Math.min(100, T));
|
|
282
|
+
const Tc2 = Tc * Tc, Tc3 = Tc2 * Tc, Tc4 = Tc3 * Tc;
|
|
283
|
+
const Lp = A.LP0 + A.LPR * T + A.T2_LP * Tc2 + Tc3 / 538841 - Tc4 / 65194000;
|
|
284
|
+
const w = (A.LP0 - A.MP0) + A.WDOT * (T + channelIntegral(T, A.S_W)); // perigee ϖ (of-date)
|
|
285
|
+
const om = (A.LP0 - A.F0) + A.NDOT * (T + channelIntegral(T, A.S_N)); // node Ω (of-date)
|
|
286
|
+
const dev = sunSecularDeviations(jdTT);
|
|
287
|
+
const Lsun = (A.LP0 - A.D0) + (A.LPR - A.DR) * T + dev.dLs; // of-date mean Sun
|
|
288
|
+
const ws = (A.LP0 - A.D0 - A.M0) + (A.LPR - A.DR - A.MR) * T + dev.dPeri; // of-date Sun perihelion
|
|
289
|
+
return {
|
|
290
|
+
Lp: wrap(Lp), D: wrap(Lp - Lsun), M: wrap(Lsun - ws),
|
|
291
|
+
Mp: wrap(Lp - w), F: wrap(Lp - om),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** Pure Meeus Ch. 47 polynomial reference (A/B mode) — exact fractions.
|
|
296
|
+
* @param {number} jdTT @returns {MoonArgsDeg} */
|
|
297
|
+
function pureMeeusArgs(jdTT) {
|
|
298
|
+
const T = (jdTT - j2000JD) / julianCenturyDays;
|
|
299
|
+
const T2 = T * T, T3 = T2 * T, T4 = T3 * T;
|
|
300
|
+
/** @param {number} x */
|
|
301
|
+
const wrap = (x) => ((x % 360) + 360) % 360;
|
|
302
|
+
return {
|
|
303
|
+
Lp: wrap(218.3164477 + 481267.88123421 * T - 0.0015786 * T2 + T3 / 538841 - T4 / 65194000),
|
|
304
|
+
D: wrap(297.8501921 + 445267.1114034 * T - 0.0018819 * T2 + T3 / 545868 - T4 / 113065000),
|
|
305
|
+
M: wrap(357.5291092 + 35999.0502909 * T - 0.0001536 * T2 + T3 / 24490000),
|
|
306
|
+
Mp: wrap(134.9633964 + 477198.8675055 * T + 0.0087414 * T2 + T3 / 69699 - T4 / 14712000),
|
|
307
|
+
F: wrap( 93.2720950 + 483202.0175233 * T - 0.0036539 * T2 - T3 / 3526000 + T4 / 863310000),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** Mode-dispatched arguments (probe hooks stay engine-side).
|
|
312
|
+
* @param {number} jdTT @returns {MoonArgsDeg} */
|
|
313
|
+
function argsAt(jdTT) {
|
|
314
|
+
return isFrameworkNative() ? fwArgs(jdTT) : pureMeeusArgs(jdTT);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return { argsAt, fwArgs, fwArgsDeep, pureMeeusArgs, sunSecularDeviations, planetaryCarrier, obliquityCarrier, bundle };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
module.exports = { createMoonArguments, jdToDecimalYear };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Moon-channel eccentricity line — THE shared implementation (Phase 8.2-2).
|
|
3
|
+
*
|
|
4
|
+
* ONE movement, FULLY DERIVED, zero solved values (doc 66 §"Framework-native
|
|
5
|
+
* e_E"): e(t) = eccentricityBase · (1 + cos θ(t) / 2), where θ rides the H/3
|
|
6
|
+
* wobble cycle that drives Earth's inclination. Mean = base (Law 5),
|
|
7
|
+
* amplitude = base/2, phase anchor θ₀ = ϖ_ICRF(J2000) − 21.77° = 81.18° past
|
|
8
|
+
* max. The observed J2000 e (−0.86%), ė (+1.7%) and the sign of ë are
|
|
9
|
+
* PREDICTIONS of this line, not inputs.
|
|
10
|
+
*
|
|
11
|
+
* NOT the same quantity as the Sun machinery's eccentricity: Earth's own
|
|
12
|
+
* orbital e for the cardinal-point/EoC path keeps the H/16 perihelion law
|
|
13
|
+
* (`cardinal`'s injected eccentricityAt). This line is the MOON channel's
|
|
14
|
+
* view of the H/3 movement — two different laws, deliberately.
|
|
15
|
+
*
|
|
16
|
+
* Extracted VERBATIM from src/script.js (_FW_ECC, _fwEarthEcc,
|
|
17
|
+
* _eCompModulation, _fwChannelIntegral, the framework branch of _fwEFactor),
|
|
18
|
+
* which tools/lib mirrored; this file replaces both copies. The 8.2-1
|
|
19
|
+
* alignment made the two engines bit-exact first, so this move is provably
|
|
20
|
+
* behaviour-preserving against the lunar golden masters.
|
|
21
|
+
*
|
|
22
|
+
* LOAD-BEARING conventions:
|
|
23
|
+
* - The injected `cyclesBetween` is the ENGINE'S OWN integrated-phase
|
|
24
|
+
* function (divisor 3 on the H lattice). Each engine's deep-time/snapshot
|
|
25
|
+
* toggle semantics ride along with it.
|
|
26
|
+
* - `e0`/`g0` are ANCHOR CONSTS by construction, never eccAt(0): under
|
|
27
|
+
* integrated phase, cycles(2000→2000) carries the R3 drift correction and
|
|
28
|
+
* is not exactly zero. The tools mirror once recomputed g0 from
|
|
29
|
+
* _fwEarthEcc(0) — unified here to the const.
|
|
30
|
+
* - channelIntegral uses composite Simpson with step ≤ ~4,000 yr (N ≥ 2,
|
|
31
|
+
* even) — the discretization the certified numbers were produced with.
|
|
32
|
+
* - Past the tidal-lock asymptote (cyclesBetween → null) eccAt returns the
|
|
33
|
+
* mean — the bounded continuation, not an error.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
'use strict';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {{
|
|
40
|
+
* cyclesBetween: (yearA: number, yearB: number, divisorN: number) => (number | null),
|
|
41
|
+
* eccentricityBase: number,
|
|
42
|
+
* perihelionLongitudeJ2000Deg: number,
|
|
43
|
+
* inclinationCycleAnchorDeg: number,
|
|
44
|
+
* }} deps — cyclesBetween MUST be the engine's own H-lattice phase counter
|
|
45
|
+
* (integrated under deep time); perihelionLongitudeJ2000Deg is ϖ_ICRF at
|
|
46
|
+
* J2000 (102.94719°, the FULL value — a truncated copy once cost 0.684″);
|
|
47
|
+
* inclinationCycleAnchorDeg is the H/3 inclination anchor (21.77°).
|
|
48
|
+
*/
|
|
49
|
+
function createMoonEccChannel({
|
|
50
|
+
cyclesBetween,
|
|
51
|
+
eccentricityBase,
|
|
52
|
+
perihelionLongitudeJ2000Deg,
|
|
53
|
+
inclinationCycleAnchorDeg,
|
|
54
|
+
}) {
|
|
55
|
+
const th0 = (perihelionLongitudeJ2000Deg - inclinationCycleAnchorDeg) * Math.PI / 180;
|
|
56
|
+
const amplitude = eccentricityBase / 2;
|
|
57
|
+
const mean = eccentricityBase;
|
|
58
|
+
const e0 = mean + amplitude * Math.cos(th0); // J2000 anchor, exact by construction
|
|
59
|
+
const g0 = Math.pow(1 - e0 * e0, -1.5); // (1−e²)^(−3/2) at the anchor
|
|
60
|
+
|
|
61
|
+
/** e_E on the H/3 line at tYr years from J2000 (negative = past).
|
|
62
|
+
* Returns the mean past the tidal-lock asymptote.
|
|
63
|
+
* @param {number} tYr @returns {number} */
|
|
64
|
+
function eccAt(tYr) {
|
|
65
|
+
const cycles = cyclesBetween(2000, 2000 + tYr, 3);
|
|
66
|
+
if (cycles === null) return mean;
|
|
67
|
+
return mean + amplitude * Math.cos(th0 + 2 * Math.PI * cycles);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** [g(e(t))/g₀]^s — the perigee/node rate modulation, ≡ 1 at J2000.
|
|
71
|
+
* tMa is age in Myr (positive = past, the deep-time chain convention).
|
|
72
|
+
* @param {number} tMa @param {number} s @returns {number} */
|
|
73
|
+
function modulation(tMa, s) {
|
|
74
|
+
if (tMa === 0) return 1;
|
|
75
|
+
const e = eccAt(-tMa * 1e6);
|
|
76
|
+
return Math.pow(Math.pow(1 - e * e, -1.5) / g0, s);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** ∫₀ᵀ [(g(e(t′))/g₀)^s − 1] dt′ in Julian centuries — the phase-aware
|
|
80
|
+
* channel-rate integral (the old frozen-κ T²/T³ Taylor coefficients were
|
|
81
|
+
* this integral's J2000 truncation). Composite Simpson, step ≤ ~4 kyr.
|
|
82
|
+
* @param {number} T @param {number} s @returns {number} */
|
|
83
|
+
function channelIntegral(T, s) {
|
|
84
|
+
if (T === 0) return 0;
|
|
85
|
+
/** @param {number} t */
|
|
86
|
+
const f = (t) => {
|
|
87
|
+
const e = eccAt(t * 100); // t in cy → years
|
|
88
|
+
return Math.pow(Math.pow(1 - e * e, -1.5) / g0, s) - 1;
|
|
89
|
+
};
|
|
90
|
+
const N = Math.max(2, 2 * Math.ceil(Math.abs(T) * 100 / 8000));
|
|
91
|
+
const h = T / N;
|
|
92
|
+
let sum = f(0) + f(T);
|
|
93
|
+
for (let i = 1; i < N; i++) sum += f(i * h) * (i % 2 ? 4 : 2);
|
|
94
|
+
return sum * h / 3;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Bounded Meeus E-factor: E ≡ e_E(t)/e_E(J2000), value 1 at J2000 by
|
|
98
|
+
* construction. The caller supplies tYr = days-from-J2000 / mean solar
|
|
99
|
+
* year; the pure-Meeus A/B polynomial branch stays ENGINE-LOCAL with its
|
|
100
|
+
* mode toggle. @param {number} tYr @returns {number} */
|
|
101
|
+
function eFactorAt(tYr) {
|
|
102
|
+
return eccAt(tYr) / e0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { eccAt, modulation, channelIntegral, eFactorAt, th0, amplitude, mean, e0, g0 };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = { createMoonEccChannel };
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The deep-time lunar month/precession chain — THE shared implementation
|
|
3
|
+
* (Phase 8.2-3). Extracted VERBATIM from src/script.js, which
|
|
4
|
+
* tools/lib/deep-time.js mirrored; the 8.2-1 alignment proved the two
|
|
5
|
+
* bit-exact (56/56 fixture probes) before this move.
|
|
6
|
+
*
|
|
7
|
+
* The chain, by dependency (doc 99 / doc 66):
|
|
8
|
+
*
|
|
9
|
+
* Farhat distance a(t) ──► solar Δa (doc 24) ──► corrected a
|
|
10
|
+
* │ │
|
|
11
|
+
* ▼ ▼
|
|
12
|
+
* (engine LOD layer, injected) Kepler sidereal month
|
|
13
|
+
* ├─► synodic month (beat with sidereal year)
|
|
14
|
+
* ├─► tropical month (H/13 identity)
|
|
15
|
+
* └─► perigee/node precession
|
|
16
|
+
* (Brouwer–Clemence m² scaling ×
|
|
17
|
+
* e_E-channel modulation — the
|
|
18
|
+
* FACTORED law: period =
|
|
19
|
+
* invariant mean / rate modulation)
|
|
20
|
+
* ├─► anomalistic month
|
|
21
|
+
* ├─► nodal (draconic) month
|
|
22
|
+
* ├─► apsidal-meets-nodal beat
|
|
23
|
+
* └─► lunar leveling beat
|
|
24
|
+
*
|
|
25
|
+
* LOAD-BEARING conventions:
|
|
26
|
+
* - t_Ma is AGE in Myr: positive = past. The chain returns null past the
|
|
27
|
+
* tidal-lock asymptote (via the injected LOD) — bounded physics, not error.
|
|
28
|
+
* - The J2000 anchors (moonApsidalJ2000Seconds etc.) are the engines' own
|
|
29
|
+
* derived constants, injected — the t_Ma === 0 fast paths return them
|
|
30
|
+
* EXACTLY (anchor identity, pinned by the 0 Ma fixture probes).
|
|
31
|
+
* - `modulation` is the shared moon eccentricity channel's [g/g₀]^s with the
|
|
32
|
+
* Meeus-EFFECTIVE sensitivities s_ϖ = 2.407 / s_Ω = 1.018 (the physical
|
|
33
|
+
* pair is 2.479/0.867 after removing the IAU frame term — doc 66 §1).
|
|
34
|
+
* - The 'OfDate' cycle/precession family carries the legacy-'ICRF' input
|
|
35
|
+
* names but holds OF-DATE observational anchors (Option C+); the
|
|
36
|
+
* star-referenced values differ by ∓13 counts per H.
|
|
37
|
+
* - apsidalMeetsNodal / lunarLeveling existed ONLY in the browser before
|
|
38
|
+
* this move (the Node scene aliased apsidal-of-date on a cancellation
|
|
39
|
+
* argument valid for the paired scene nodes only) — the Node engine gains
|
|
40
|
+
* the real forms here.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
'use strict';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param {{
|
|
47
|
+
* constants: {
|
|
48
|
+
* aMoonNowMetres: number,
|
|
49
|
+
* alpha1PerMa: number,
|
|
50
|
+
* alpha3PerMa3: number,
|
|
51
|
+
* alpha4PerMa4: number,
|
|
52
|
+
* gmEarthMoonM3PerS2: number,
|
|
53
|
+
* massRatioEarthMoon: number,
|
|
54
|
+
* moonSiderealMonthInputDays: number,
|
|
55
|
+
* holisticYearJ2000: number,
|
|
56
|
+
* meanSiderealYearJ2000Seconds: number,
|
|
57
|
+
* nApsidalOfDateJ2000: number,
|
|
58
|
+
* nNodalOfDateJ2000: number,
|
|
59
|
+
* moonApsidalJ2000Seconds: number,
|
|
60
|
+
* moonNodalJ2000Seconds: number,
|
|
61
|
+
* moonSiderealMonthJ2000Seconds: number,
|
|
62
|
+
* sPerigee: number,
|
|
63
|
+
* sNode: number,
|
|
64
|
+
* },
|
|
65
|
+
* fns: {
|
|
66
|
+
* meanLodSecondsAtAge: (tMa: number) => (number | null),
|
|
67
|
+
* meanSiderealYearSecondsAtAge: (tMa: number) => number,
|
|
68
|
+
* meanHAtAge: (tMa: number) => (number | null),
|
|
69
|
+
* modulation: (tMa: number, s: number) => number,
|
|
70
|
+
* },
|
|
71
|
+
* }} deps — fns are the ENGINE'S OWN layer-0/1 evaluators and the shared
|
|
72
|
+
* eccentricity channel's modulation; constants are the engine's derived
|
|
73
|
+
* J2000 anchors (both engines derive them from the same JSON).
|
|
74
|
+
*/
|
|
75
|
+
function createMoonMonthChain({ constants, fns }) {
|
|
76
|
+
const {
|
|
77
|
+
aMoonNowMetres, alpha1PerMa, alpha3PerMa3, alpha4PerMa4,
|
|
78
|
+
gmEarthMoonM3PerS2, massRatioEarthMoon, moonSiderealMonthInputDays,
|
|
79
|
+
holisticYearJ2000, meanSiderealYearJ2000Seconds,
|
|
80
|
+
nApsidalOfDateJ2000, nNodalOfDateJ2000,
|
|
81
|
+
moonApsidalJ2000Seconds, moonNodalJ2000Seconds, moonSiderealMonthJ2000Seconds,
|
|
82
|
+
sPerigee, sNode,
|
|
83
|
+
} = constants;
|
|
84
|
+
const { meanLodSecondsAtAge, meanSiderealYearSecondsAtAge, meanHAtAge, modulation } = fns;
|
|
85
|
+
|
|
86
|
+
/** Farhat α₁/α₃/α₄ polynomial — Earth–Moon distance in metres at age t_Ma.
|
|
87
|
+
* @param {number} tMa @returns {number} */
|
|
88
|
+
function distanceMetresAtAge(tMa) {
|
|
89
|
+
const t = tMa;
|
|
90
|
+
return aMoonNowMetres * (1 + alpha1PerMa * t + alpha3PerMa3 * t * t * t + alpha4PerMa4 * t * t * t * t);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** @param {number} tMa @returns {number} */
|
|
94
|
+
function distanceKmAtAge(tMa) {
|
|
95
|
+
return distanceMetresAtAge(tMa) / 1000;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Solar Δa correction (km) for the Moon's apparent semi-major axis
|
|
99
|
+
* (doc 24). @param {number} tMa @param {number} aApparentKm
|
|
100
|
+
* @returns {number | null} */
|
|
101
|
+
function solarDeltaAKmAtAge(tMa, aApparentKm) {
|
|
102
|
+
const lodS = meanLodSecondsAtAge(tMa);
|
|
103
|
+
if (lodS === null) return null;
|
|
104
|
+
const tSidDaysAtEpoch = meanSiderealYearSecondsAtAge(tMa) / lodS;
|
|
105
|
+
return aApparentKm * (1 / (massRatioEarthMoon + 1)) *
|
|
106
|
+
(moonSiderealMonthInputDays / tSidDaysAtEpoch);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Kepler-effective distance in km (a_apparent + solar Δa).
|
|
110
|
+
* @param {number} tMa @returns {number | null} */
|
|
111
|
+
function distanceCorrectedKmAtAge(tMa) {
|
|
112
|
+
const aApp = distanceKmAtAge(tMa);
|
|
113
|
+
const dA = solarDeltaAKmAtAge(tMa, aApp);
|
|
114
|
+
return (dA === null) ? null : aApp + dA;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Sidereal month in seconds (Kepler on the corrected a).
|
|
118
|
+
* @param {number} tMa @returns {number | null} */
|
|
119
|
+
function siderealMonthSecondsAtAge(tMa) {
|
|
120
|
+
const aCorrKm = distanceCorrectedKmAtAge(tMa);
|
|
121
|
+
if (aCorrKm === null) return null;
|
|
122
|
+
return 2 * Math.PI * Math.sqrt(Math.pow(aCorrKm * 1000, 3) / gmEarthMoonM3PerS2);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Synodic month in seconds (Moon–Sun alignment beat).
|
|
126
|
+
* @param {number} tMa @returns {number | null} */
|
|
127
|
+
function synodicMonthSecondsAtAge(tMa) {
|
|
128
|
+
const tSm = siderealMonthSecondsAtAge(tMa);
|
|
129
|
+
if (tSm === null) return null;
|
|
130
|
+
const tYr = meanSiderealYearSecondsAtAge(tMa);
|
|
131
|
+
return tSm * tYr / (tYr - tSm);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Tropical month in seconds (equinox-referenced, the H/13 identity).
|
|
135
|
+
* @param {number} tMa @returns {number | null} */
|
|
136
|
+
function tropicalMonthSecondsAtAge(tMa) {
|
|
137
|
+
const tSm = siderealMonthSecondsAtAge(tMa);
|
|
138
|
+
if (tSm === null) return null;
|
|
139
|
+
const tYr = meanSiderealYearSecondsAtAge(tMa);
|
|
140
|
+
const hT = meanHAtAge(tMa);
|
|
141
|
+
return tSm * (1 - 13 * tSm / (/** @type {number} */ (hT) * tYr));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Apsidal cycles per H (of-date convention; legacy-'ICRF' input name):
|
|
145
|
+
* N × (H/H₀)², real-valued. @param {number} tMa @returns {number | null} */
|
|
146
|
+
function apsidalCyclesOfDateAtAge(tMa) {
|
|
147
|
+
const hT = meanHAtAge(tMa);
|
|
148
|
+
if (hT === null) return null;
|
|
149
|
+
return nApsidalOfDateJ2000 * Math.pow(hT / holisticYearJ2000, 2);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** @param {number} tMa @returns {number | null} */
|
|
153
|
+
function nodalCyclesOfDateAtAge(tMa) {
|
|
154
|
+
const hT = meanHAtAge(tMa);
|
|
155
|
+
if (hT === null) return null;
|
|
156
|
+
return nNodalOfDateJ2000 * Math.pow(hT / holisticYearJ2000, 2);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** @param {number} tMa @returns {number | null} */
|
|
160
|
+
function apsidalPrecessionSecondsOfDateAtAge(tMa) {
|
|
161
|
+
const n = apsidalCyclesOfDateAtAge(tMa);
|
|
162
|
+
const hT = meanHAtAge(tMa);
|
|
163
|
+
const tYrS = meanSiderealYearSecondsAtAge(tMa);
|
|
164
|
+
if (n === null || hT === null) return null;
|
|
165
|
+
return hT * tYrS / n; // H in years × seconds/year / N
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** @param {number} tMa @returns {number | null} */
|
|
169
|
+
function nodalPrecessionSecondsOfDateAtAge(tMa) {
|
|
170
|
+
const n = nodalCyclesOfDateAtAge(tMa);
|
|
171
|
+
const hT = meanHAtAge(tMa);
|
|
172
|
+
const tYrS = meanSiderealYearSecondsAtAge(tMa);
|
|
173
|
+
if (n === null || hT === null) return null;
|
|
174
|
+
return hT * tYrS / n;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Perigee precession period in seconds — Brouwer–Clemence m² scaling ×
|
|
178
|
+
* e_E-channel modulation (the factored law: period = invariant mean /
|
|
179
|
+
* rate modulation). @param {number} tMa @returns {number | null} */
|
|
180
|
+
function perigeePrecessionSecondsAtAge(tMa) {
|
|
181
|
+
if (tMa === 0) return moonApsidalJ2000Seconds;
|
|
182
|
+
const tSmT = siderealMonthSecondsAtAge(tMa);
|
|
183
|
+
const tYrT = meanSiderealYearSecondsAtAge(tMa);
|
|
184
|
+
if (tSmT === null) return null;
|
|
185
|
+
return moonApsidalJ2000Seconds
|
|
186
|
+
* Math.pow(tYrT / meanSiderealYearJ2000Seconds, 2)
|
|
187
|
+
* (moonSiderealMonthJ2000Seconds / tSmT)
|
|
188
|
+
/ modulation(tMa, sPerigee);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** @param {number} tMa @returns {number | null} */
|
|
192
|
+
function nodePrecessionSecondsAtAge(tMa) {
|
|
193
|
+
if (tMa === 0) return moonNodalJ2000Seconds;
|
|
194
|
+
const tSmT = siderealMonthSecondsAtAge(tMa);
|
|
195
|
+
const tYrT = meanSiderealYearSecondsAtAge(tMa);
|
|
196
|
+
if (tSmT === null) return null;
|
|
197
|
+
return moonNodalJ2000Seconds
|
|
198
|
+
* Math.pow(tYrT / meanSiderealYearJ2000Seconds, 2)
|
|
199
|
+
* (moonSiderealMonthJ2000Seconds / tSmT)
|
|
200
|
+
/ modulation(tMa, sNode);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Anomalistic month in seconds (perigee-to-perigee).
|
|
204
|
+
* @param {number} tMa @returns {number | null} */
|
|
205
|
+
function anomalisticMonthSecondsAtAge(tMa) {
|
|
206
|
+
const tSm = siderealMonthSecondsAtAge(tMa);
|
|
207
|
+
const tPer = perigeePrecessionSecondsAtAge(tMa);
|
|
208
|
+
if (tSm === null || tPer === null) return null;
|
|
209
|
+
return tSm * tPer / (tPer - tSm);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Nodal (draconic) month in seconds.
|
|
213
|
+
* @param {number} tMa @returns {number | null} */
|
|
214
|
+
function nodalMonthSecondsAtAge(tMa) {
|
|
215
|
+
const tSm = siderealMonthSecondsAtAge(tMa);
|
|
216
|
+
const tNode = nodePrecessionSecondsAtAge(tMa);
|
|
217
|
+
if (tSm === null || tNode === null) return null;
|
|
218
|
+
return tSm * tNode / (tNode + tSm);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Beat of the anomalistic and nodal months.
|
|
222
|
+
* @param {number} tMa @returns {number | null} */
|
|
223
|
+
function apsidalMeetsNodalSecondsAtAge(tMa) {
|
|
224
|
+
const tAnom = anomalisticMonthSecondsAtAge(tMa);
|
|
225
|
+
const tNod = nodalMonthSecondsAtAge(tMa);
|
|
226
|
+
if (tAnom === null || tNod === null) return null;
|
|
227
|
+
return tNod * tAnom / (tAnom - tNod);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Beat of the nodal and apsidal precessions.
|
|
231
|
+
* @param {number} tMa @returns {number | null} */
|
|
232
|
+
function lunarLevelingSecondsAtAge(tMa) {
|
|
233
|
+
const tApsi = perigeePrecessionSecondsAtAge(tMa);
|
|
234
|
+
const tNode = nodePrecessionSecondsAtAge(tMa);
|
|
235
|
+
if (tApsi === null || tNode === null) return null;
|
|
236
|
+
return tNode * tApsi / (tNode - tApsi);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
distanceMetresAtAge, distanceKmAtAge, solarDeltaAKmAtAge,
|
|
241
|
+
distanceCorrectedKmAtAge, siderealMonthSecondsAtAge,
|
|
242
|
+
synodicMonthSecondsAtAge, tropicalMonthSecondsAtAge,
|
|
243
|
+
apsidalCyclesOfDateAtAge, nodalCyclesOfDateAtAge,
|
|
244
|
+
apsidalPrecessionSecondsOfDateAtAge, nodalPrecessionSecondsOfDateAtAge,
|
|
245
|
+
perigeePrecessionSecondsAtAge, nodePrecessionSecondsAtAge,
|
|
246
|
+
anomalisticMonthSecondsAtAge, nodalMonthSecondsAtAge,
|
|
247
|
+
apsidalMeetsNodalSecondsAtAge, lunarLevelingSecondsAtAge,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
module.exports = { createMoonMonthChain };
|