@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,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The constants context (§2d) — stable import point.
|
|
3
|
+
*
|
|
4
|
+
* `generated.js` is written by `tools/constants/generate.mjs` from the JSON
|
|
5
|
+
* source of truth and is never hand-edited. Consumers import from here so they
|
|
6
|
+
* do not depend on the generator's output path.
|
|
7
|
+
*
|
|
8
|
+
* Injected via `createModel`, never imported by a layer: importing constants
|
|
9
|
+
* inside a layer is what makes counterfactuals impossible, and that capability
|
|
10
|
+
* is the one thing DE440 and Laskar cannot offer.
|
|
11
|
+
*
|
|
12
|
+
* Validation targets are deliberately absent from DEFAULT_CONSTANTS —
|
|
13
|
+
* `laplaceLagrangeBounds`, `knownValues`, `ascendingNodesSouamiSouchay`,
|
|
14
|
+
* `jplEclipticInclinationTrends`, `eigenmodePhasesLaplaceLagrange` — plus
|
|
15
|
+
* `galaxyMotion`, which is presentation rather than physics.
|
|
16
|
+
*
|
|
17
|
+
* They ship as REFERENCE_DATA instead: single-sourced, so nothing duplicates
|
|
18
|
+
* them as literals, but never injectable. `createModel` refuses these keys
|
|
19
|
+
* outright, because a counterfactual must not be able to move the goalposts it
|
|
20
|
+
* is judged by. See CLASSIFICATION in the generator.
|
|
21
|
+
*/
|
|
22
|
+
export { DEFAULT_CONSTANTS, CONSTANTS_HASH, REFERENCE_DATA } from './generated.js';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Fitting-pipeline output, at full double precision (§2j). Kept apart from
|
|
26
|
+
* DEFAULT_CONSTANTS because the lifecycle differs: these regenerate wholesale
|
|
27
|
+
* when a fit step runs, and are never hand-edited. COEFFICIENTS_HASH identifies
|
|
28
|
+
* the fit, CONSTANTS_HASH the parameters — a result is reproducible from both.
|
|
29
|
+
*/
|
|
30
|
+
export { FITTED_COEFFICIENTS, COEFFICIENTS_HASH } from './coefficients.js';
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ΔT cycle-correction stack — THE shared implementation (Phase 8.4, slice 2).
|
|
3
|
+
*
|
|
4
|
+
* The four anchored lattice-cycle ΔT corrections (Bond 8H/1830, Hallstatt
|
|
5
|
+
* 8H/1104, Jose5 8H/2989, Jose4 8H/3749 — Jose5/Jose4 are a COUPLED PAIR,
|
|
6
|
+
* fitted jointly), their implied δLOD twins (the d/dy of each correction:
|
|
7
|
+
* physical consistency between the corrected ΔT curve and the LOD curve),
|
|
8
|
+
* and the Core-mantle swing episode (Resonator driver: 2-kick damped
|
|
9
|
+
* eigenmode T₀ = 8H/685, Q = 1.8, plus one switch-on-compensated drive
|
|
10
|
+
* tone at the bond−hallstatt difference frequency — the §10g-adjacent
|
|
11
|
+
* phase-locked construction; docs/104 §6/§8).
|
|
12
|
+
*
|
|
13
|
+
* One ±300/400-kyr taper serves the whole family (the browser carried four
|
|
14
|
+
* per-cycle copies of the same two JSON half-widths plus a fifth
|
|
15
|
+
* function-local pair in its taper derivative — all value-identical,
|
|
16
|
+
* dissolved here; the historical "Holocene taper" name survives only in
|
|
17
|
+
* engine comments).
|
|
18
|
+
*
|
|
19
|
+
* MATCHED PAIR: the coefficients were fitted JOINTLY (dt-corrections-fit.js
|
|
20
|
+
* --joint) with the USNO 86400.0014 / deltaTStart 56.05 anchors — the
|
|
21
|
+
* 4-cycle+swing δLOD sum at J2000 closes Layer-4 LOD_real onto the USNO
|
|
22
|
+
* anchor by construction. Coefficients and this evaluation form ship
|
|
23
|
+
* together; do not simplify the anchored (raw − raw@2000) construction or
|
|
24
|
+
* the impulse-consistent tone compensation.
|
|
25
|
+
*
|
|
26
|
+
* ENGINE-SIDE, by design: the enable flags (browser mutable research
|
|
27
|
+
* toggles, Node env vars), the per-cycle gated wrappers, the stack sum,
|
|
28
|
+
* and the coefficient PROVENANCE (browser FIT.DT_STACK/FIT.DT_RESONATOR,
|
|
29
|
+
* Node the fit-output data JSONs) — all injected.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
'use strict';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {{ latticeN: number, cosCoeffSeconds: number, sinCoeffSeconds: number }} CycleSpec
|
|
36
|
+
* @typedef {{ tYear: number, cosSeconds: number, sinSeconds: number }} ResonatorKick
|
|
37
|
+
* @typedef {{ dn: number, phiLockedRad: number, ampSeconds: number }} ResonatorTone
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @typedef {Object} DeltaTCyclesDeps
|
|
42
|
+
* @property {number} eightHYears - 8 × the holistic year (J2000)
|
|
43
|
+
* @property {number} taperFullHalfwidthYears - full strength within |y−2000| ≤ this
|
|
44
|
+
* @property {number} taperTotalHalfwidthYears - zero beyond
|
|
45
|
+
* @property {number} tropicalYearSecondsJ2000 - δLOD denominator (variation ≤1e-8 in-window)
|
|
46
|
+
* @property {Record<string, CycleSpec>} cycles - keyed bond/hallstatt/jose5/jose4
|
|
47
|
+
* @property {{ t0LatticeN: number, q: number, kicks: ResonatorKick[],
|
|
48
|
+
* tones: ResonatorTone[] }} resonator
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Build the cycle-correction evaluators over one engine's constant set.
|
|
53
|
+
* All evaluators are UNGATED — the engines own their enable flags.
|
|
54
|
+
* @param {DeltaTCyclesDeps} deps
|
|
55
|
+
*/
|
|
56
|
+
function createDeltaTCycles(deps) {
|
|
57
|
+
const HW_FULL = deps.taperFullHalfwidthYears;
|
|
58
|
+
const HW_TOTAL = deps.taperTotalHalfwidthYears;
|
|
59
|
+
const YEAR_S = deps.tropicalYearSecondsJ2000;
|
|
60
|
+
|
|
61
|
+
/** @param {number} year @returns {number} 1 inside, cosine roll-off, 0 beyond */
|
|
62
|
+
function taperAt(year) {
|
|
63
|
+
const dy = Math.abs(year - 2000);
|
|
64
|
+
if (dy <= HW_FULL) return 1.0;
|
|
65
|
+
if (dy >= HW_TOTAL) return 0.0;
|
|
66
|
+
const u = (dy - HW_FULL) / (HW_TOTAL - HW_FULL);
|
|
67
|
+
return 0.5 * (1.0 + Math.cos(Math.PI * u));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** @param {number} year @returns {number} d(taper)/dy */
|
|
71
|
+
function taperDerivativeAt(year) {
|
|
72
|
+
const dy = Math.abs(year - 2000);
|
|
73
|
+
if (dy <= HW_FULL) return 0;
|
|
74
|
+
if (dy >= HW_TOTAL) return 0;
|
|
75
|
+
const width = HW_TOTAL - HW_FULL;
|
|
76
|
+
const u = (dy - HW_FULL) / width;
|
|
77
|
+
// taper(y) = 0.5·(1 + cos(π·u)); d/dy = −0.5·π·sin(π·u) · (du/dy)
|
|
78
|
+
// du/dy = (1/width) · sign(y − 2000)
|
|
79
|
+
const sign = year >= 2000 ? 1 : -1;
|
|
80
|
+
return -0.5 * Math.PI * Math.sin(Math.PI * u) / width * sign;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Per-cycle derived constants — period = 8H/N, ω = 2π/period, and the
|
|
84
|
+
// J2000 anchor value (the correction is exactly 0 at year 2000 by
|
|
85
|
+
// construction: taper·(raw − raw@2000)).
|
|
86
|
+
/** @type {Record<string, {omega: number, cosC: number, sinC: number, rawAtJ2000: number}>} */
|
|
87
|
+
const cyc = {};
|
|
88
|
+
for (const [key, c] of Object.entries(deps.cycles)) {
|
|
89
|
+
const periodYears = deps.eightHYears / c.latticeN;
|
|
90
|
+
const omega = 2 * Math.PI / periodYears;
|
|
91
|
+
cyc[key] = {
|
|
92
|
+
omega, cosC: c.cosCoeffSeconds, sinC: c.sinCoeffSeconds,
|
|
93
|
+
rawAtJ2000: c.cosCoeffSeconds * Math.cos(omega * 2000) + c.sinCoeffSeconds * Math.sin(omega * 2000),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Anchored cycle ΔT correction, seconds. 0 at 2000 and outside the taper.
|
|
98
|
+
* @param {string} cycleKey @param {number} year @returns {number} */
|
|
99
|
+
function cycleDeltaTSecondsAt(cycleKey, year) {
|
|
100
|
+
const c = cyc[cycleKey];
|
|
101
|
+
const taper = taperAt(year);
|
|
102
|
+
if (taper <= 0) return 0;
|
|
103
|
+
const raw = c.cosC * Math.cos(c.omega * year) + c.sinC * Math.sin(c.omega * year);
|
|
104
|
+
return taper * (raw - c.rawAtJ2000);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Implied δLOD of one cycle correction (s to add to LOD_mean):
|
|
108
|
+
* δLOD = 86400 · d/dy[correction] / yearS, product rule over taper·(raw−raw₀).
|
|
109
|
+
* @param {string} cycleKey @param {number} year @returns {number} */
|
|
110
|
+
function cycleLodSecondsAt(cycleKey, year) {
|
|
111
|
+
const c = cyc[cycleKey];
|
|
112
|
+
const taper = taperAt(year);
|
|
113
|
+
if (taper <= 0) return 0;
|
|
114
|
+
const raw = c.cosC * Math.cos(c.omega * year) + c.sinC * Math.sin(c.omega * year);
|
|
115
|
+
const raw_prime = c.omega * (c.sinC * Math.cos(c.omega * year) - c.cosC * Math.sin(c.omega * year));
|
|
116
|
+
const taper_prime = taperDerivativeAt(year);
|
|
117
|
+
const dCdy = taper_prime * (raw - c.rawAtJ2000) + taper * raw_prime;
|
|
118
|
+
return 86400 * dCdy / YEAR_S;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Core-mantle swing (Resonator driver) ─────────────────────────────────
|
|
122
|
+
const R = deps.resonator;
|
|
123
|
+
const RES_T0_YR = deps.eightHYears / R.t0LatticeN;
|
|
124
|
+
const RES_W0 = 2 * Math.PI / RES_T0_YR;
|
|
125
|
+
const RES_LAMBDA = RES_W0 / (2 * R.q);
|
|
126
|
+
const RES_WD = RES_W0 * Math.sqrt(1 - 1 / (4 * R.q * R.q));
|
|
127
|
+
const RES_KICKS = R.kicks.map((k) => ({ t: k.tYear, cos_s: k.cosSeconds, sin_s: k.sinSeconds }));
|
|
128
|
+
const RES_TONES = R.tones.map((t) => ({
|
|
129
|
+
omega: 2 * Math.PI * t.dn / deps.eightHYears, phi_locked: t.phiLockedRad, amp_s: t.ampSeconds,
|
|
130
|
+
}));
|
|
131
|
+
|
|
132
|
+
// IMPULSE-CONSISTENT episode: kicks are sin-only (displacement-continuous —
|
|
133
|
+
// ΔT is accumulated angle and must not step); the drive tone's displacement
|
|
134
|
+
// at the excitation epoch is cancelled by an eigenmode transient (switched-on
|
|
135
|
+
// drive; slope discontinuities at the kicks ARE the impulses).
|
|
136
|
+
/** @param {{omega: number, phi_locked: number}} t @returns {number} */
|
|
137
|
+
function resonatorToneC0(t) {
|
|
138
|
+
return Math.cos(t.omega * RES_KICKS[0].t - t.phi_locked);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** @param {number} year @returns {number} raw episode displacement, seconds */
|
|
142
|
+
function resonatorRawSecondsAt(year) {
|
|
143
|
+
let v = 0;
|
|
144
|
+
for (const k of RES_KICKS) {
|
|
145
|
+
const dt = year - k.t;
|
|
146
|
+
if (dt < 0) continue;
|
|
147
|
+
const e = Math.exp(-RES_LAMBDA * dt);
|
|
148
|
+
v += e * (k.cos_s * Math.cos(RES_WD * dt) + k.sin_s * Math.sin(RES_WD * dt));
|
|
149
|
+
}
|
|
150
|
+
const dt1 = year - RES_KICKS[0].t;
|
|
151
|
+
if (dt1 >= 0) {
|
|
152
|
+
const e1 = Math.exp(-RES_LAMBDA * dt1);
|
|
153
|
+
for (const t of RES_TONES) {
|
|
154
|
+
v += e1 * t.amp_s * (Math.cos(t.omega * year - t.phi_locked)
|
|
155
|
+
- resonatorToneC0(t) * Math.cos(RES_WD * dt1));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return v;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** @param {number} year @returns {number} d(raw)/dy */
|
|
162
|
+
function resonatorRawPrimeAt(year) {
|
|
163
|
+
let v = 0;
|
|
164
|
+
for (const k of RES_KICKS) {
|
|
165
|
+
const dt = year - k.t;
|
|
166
|
+
if (dt < 0) continue;
|
|
167
|
+
const e = Math.exp(-RES_LAMBDA * dt);
|
|
168
|
+
v += e * ((-RES_LAMBDA * k.cos_s + RES_WD * k.sin_s) * Math.cos(RES_WD * dt)
|
|
169
|
+
+ (-RES_LAMBDA * k.sin_s - RES_WD * k.cos_s) * Math.sin(RES_WD * dt));
|
|
170
|
+
}
|
|
171
|
+
const dt1 = year - RES_KICKS[0].t;
|
|
172
|
+
if (dt1 >= 0) {
|
|
173
|
+
const e1 = Math.exp(-RES_LAMBDA * dt1);
|
|
174
|
+
for (const t of RES_TONES) {
|
|
175
|
+
const c0 = resonatorToneC0(t);
|
|
176
|
+
// d/dy of e1·[cos(ωy−φ) − c0·cos(w_d·dt1)] — product rule
|
|
177
|
+
v += e1 * t.amp_s * (-RES_LAMBDA * (Math.cos(t.omega * year - t.phi_locked)
|
|
178
|
+
- c0 * Math.cos(RES_WD * dt1))
|
|
179
|
+
- t.omega * Math.sin(t.omega * year - t.phi_locked)
|
|
180
|
+
+ c0 * RES_WD * Math.sin(RES_WD * dt1));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return v;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Analytic SECOND derivative — the dLOD/dt decomposition needs it: the
|
|
187
|
+
// resonator's δLOD has a genuine STEP at each kick, and a finite-difference
|
|
188
|
+
// rate smears it into a window-wide rectangle. Coefficient map applied
|
|
189
|
+
// twice for the kicks; product rule expansion for the compensated tone.
|
|
190
|
+
/** @param {number} year @returns {number} d²(raw)/dy² */
|
|
191
|
+
function resonatorRawSecondAt(year) {
|
|
192
|
+
let v = 0;
|
|
193
|
+
for (const k of RES_KICKS) {
|
|
194
|
+
const dt = year - k.t;
|
|
195
|
+
if (dt < 0) continue;
|
|
196
|
+
const e = Math.exp(-RES_LAMBDA * dt);
|
|
197
|
+
const a1 = -RES_LAMBDA * k.cos_s + RES_WD * k.sin_s;
|
|
198
|
+
const b1 = -RES_LAMBDA * k.sin_s - RES_WD * k.cos_s;
|
|
199
|
+
const a2 = -RES_LAMBDA * a1 + RES_WD * b1;
|
|
200
|
+
const b2 = -RES_LAMBDA * b1 - RES_WD * a1;
|
|
201
|
+
v += e * (a2 * Math.cos(RES_WD * dt) + b2 * Math.sin(RES_WD * dt));
|
|
202
|
+
}
|
|
203
|
+
const dt1 = year - RES_KICKS[0].t;
|
|
204
|
+
if (dt1 >= 0) {
|
|
205
|
+
const e1 = Math.exp(-RES_LAMBDA * dt1);
|
|
206
|
+
for (const t of RES_TONES) {
|
|
207
|
+
const c0 = resonatorToneC0(t);
|
|
208
|
+
const A = Math.cos(t.omega * year - t.phi_locked) - c0 * Math.cos(RES_WD * dt1);
|
|
209
|
+
const A1 = -t.omega * Math.sin(t.omega * year - t.phi_locked)
|
|
210
|
+
+ c0 * RES_WD * Math.sin(RES_WD * dt1);
|
|
211
|
+
const A2 = -t.omega * t.omega * Math.cos(t.omega * year - t.phi_locked)
|
|
212
|
+
+ c0 * RES_WD * RES_WD * Math.cos(RES_WD * dt1);
|
|
213
|
+
v += t.amp_s * e1 * (RES_LAMBDA * RES_LAMBDA * A - 2 * RES_LAMBDA * A1 + A2);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return v;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const RES_DT_RAW_AT_J2000 = resonatorRawSecondsAt(2000);
|
|
220
|
+
|
|
221
|
+
/** @param {number} year @returns {number} anchored swing ΔT correction, s */
|
|
222
|
+
function swingDeltaTSecondsAt(year) {
|
|
223
|
+
const taper = taperAt(year);
|
|
224
|
+
if (taper <= 0) return 0;
|
|
225
|
+
return taper * (resonatorRawSecondsAt(year) - RES_DT_RAW_AT_J2000);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** @param {number} year @returns {number} implied swing δLOD, s */
|
|
229
|
+
function swingLodSecondsAt(year) {
|
|
230
|
+
const taper = taperAt(year);
|
|
231
|
+
if (taper <= 0) return 0;
|
|
232
|
+
const dCdy = taperDerivativeAt(year) * (resonatorRawSecondsAt(year) - RES_DT_RAW_AT_J2000)
|
|
233
|
+
+ taper * resonatorRawPrimeAt(year);
|
|
234
|
+
return 86400 * dCdy / YEAR_S;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Analytic d(δLOD)/dy — exact in the flat-taper region, central
|
|
238
|
+
* difference in the transition (values there are microscopic).
|
|
239
|
+
* @param {number} year @returns {number} */
|
|
240
|
+
function swingLodRateAt(year) {
|
|
241
|
+
const taper = taperAt(year);
|
|
242
|
+
if (taper <= 0) return 0;
|
|
243
|
+
if (taper >= 1) {
|
|
244
|
+
return 86400 * resonatorRawSecondAt(year) / YEAR_S;
|
|
245
|
+
}
|
|
246
|
+
return (swingLodSecondsAt(year + 1) - swingLodSecondsAt(year - 1)) / 2;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
taperAt, taperDerivativeAt,
|
|
251
|
+
cycleDeltaTSecondsAt, cycleLodSecondsAt,
|
|
252
|
+
swingDeltaTSecondsAt, swingLodSecondsAt, swingLodRateAt,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
module.exports = { createDeltaTCycles };
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep-time LOD / ΔT core — THE shared implementation (Phase 8.4, slice 3).
|
|
3
|
+
*
|
|
4
|
+
* The angular-momentum-conservation LOD (Layer 1/2), the H(t) identity, the
|
|
5
|
+
* mass-loss year chain, the Simpson ΔT integrator with the H/5 ecliptic
|
|
6
|
+
* "missing motion" integrand, the corrected-LOD composite, the Actual-LOD
|
|
7
|
+
* Fourier ripple, and the dLOD/dt driver decomposition (tidal / GIA /
|
|
8
|
+
* stack / resonator channels).
|
|
9
|
+
*
|
|
10
|
+
* INJECTED, by design:
|
|
11
|
+
* - moonDistanceMetresAtAge — the 8.2 moon month-chain (each engine's own
|
|
12
|
+
* factory wiring).
|
|
13
|
+
* - moiFactorAtAge — the L1-orbital-coupled GIA α(t). The lattice-α pin
|
|
14
|
+
* machinery (_withLatticeAlpha) is ENGINE STATE and stays there.
|
|
15
|
+
* - siderealYearDaysFourierAt — the IAU-base integrated-phase Fourier
|
|
16
|
+
* evaluator (Phase D matched pair). 8.4-1 S-D2: the browser's old Actual
|
|
17
|
+
* divided by its epoch-aware f(Y) evaluator instead, double-counting the
|
|
18
|
+
* deep-time drift already in mean(t) — 12% wrong at −400 Ma. Both engines
|
|
19
|
+
* now inject the ripple-form evaluator.
|
|
20
|
+
* - cycleLodSumAt / swingLodAt / swingLodRateAt — the engine-GATED
|
|
21
|
+
* cycle-stack sums (flags are engine dispatch).
|
|
22
|
+
*
|
|
23
|
+
* The ΔT CACHE and the post-integration correction adds stay engine-side:
|
|
24
|
+
* both engines key the cache on their flag state (the browser's flags are
|
|
25
|
+
* runtime-mutable UI toggles), and the corrections are added SEQUENTIALLY
|
|
26
|
+
* onto the integral — pre-summing them here would change the FP
|
|
27
|
+
* association, so deltaTRawSecondsAtAge returns the raw integral and each
|
|
28
|
+
* engine keeps its exact add order.
|
|
29
|
+
*
|
|
30
|
+
* Year anchor: every year-of-age conversion here is 2000 − t_Ma·1e6 — the
|
|
31
|
+
* convention of the ΔT integrand and the Node engine throughout. 8.4-1
|
|
32
|
+
* S-D3/S-D5: the browser's WithCorrections and decomposition historically
|
|
33
|
+
* used startmodelYear (2000.5), a half-year phase offset — aligned with
|
|
34
|
+
* measured fixture re-records.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
'use strict';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {Object} DeepTimeLodConstants
|
|
41
|
+
* @property {number} lTotalEmKgm2S - Earth–Moon total angular momentum
|
|
42
|
+
* @property {number} mMoonAloneKg
|
|
43
|
+
* @property {number} mEarthAloneKg
|
|
44
|
+
* @property {number} rEarthMetres
|
|
45
|
+
* @property {number} gmEmM3PerS2 - GM of the Earth–Moon system
|
|
46
|
+
* @property {number} eFactorMoon - √(1−e²) factor of the lunar orbit
|
|
47
|
+
* @property {number} aLockMetres - tidal-lock distance (LOD denominator → 0)
|
|
48
|
+
* @property {number} aMoonNowMetres - Farhat polynomial anchor
|
|
49
|
+
* @property {number} alpha1PerMa @property {number} alpha3PerMa3
|
|
50
|
+
* @property {number} alpha4PerMa4
|
|
51
|
+
* @property {number} holisticYearJ2000
|
|
52
|
+
* @property {number} lodNowH13Seconds - the H/13-identity mean LOD at J2000
|
|
53
|
+
* @property {number} meanSiderealYearJ2000Seconds
|
|
54
|
+
* @property {number} solarMassLossFracPerYear
|
|
55
|
+
* @property {number} siderealYearDaysKinematicJ2000 - Actual-LOD numerator
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @typedef {Object} DeepTimeLodDeps
|
|
60
|
+
* @property {DeepTimeLodConstants} constants
|
|
61
|
+
* @property {(tMa: number) => number} moonDistanceMetresAtAge
|
|
62
|
+
* @property {(tMa: number) => number} moiFactorAtAge - GIA α(t)
|
|
63
|
+
* @property {(year: number) => number} siderealYearDaysFourierAt
|
|
64
|
+
* @property {(year: number) => number} cycleLodSumAt - gated δLOD sum (incl. swing)
|
|
65
|
+
* @property {(year: number) => number} swingLodAt - gated swing δLOD alone
|
|
66
|
+
* @property {(year: number) => number} swingLodRateAt - gated analytic swing rate
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
/** @param {DeepTimeLodDeps} deps */
|
|
70
|
+
function createDeepTimeLod(deps) {
|
|
71
|
+
const K = deps.constants;
|
|
72
|
+
|
|
73
|
+
/** Layer 1/2 mean LOD: LOD = 2π·I(t) / (L_total − L_moon(t)).
|
|
74
|
+
* @param {number} t_Ma @returns {number|null} seconds, null past tidal lock */
|
|
75
|
+
function lodSecondsAtAge(t_Ma) {
|
|
76
|
+
const a = deps.moonDistanceMetresAtAge(t_Ma);
|
|
77
|
+
if (a <= 0 || a >= K.aLockMetres) return null;
|
|
78
|
+
return (2 * Math.PI * (deps.moiFactorAtAge(t_Ma) * K.mEarthAloneKg * K.rEarthMetres * K.rEarthMetres)) /
|
|
79
|
+
(K.lTotalEmKgm2S - K.mMoonAloneKg * Math.sqrt(K.gmEmM3PerS2 * a) * K.eFactorMoon);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Same LOD with an EXPLICIT α (the browser's "α at climate mean" curve).
|
|
83
|
+
* @param {number} t_Ma @param {number} alpha @returns {number|null} */
|
|
84
|
+
function lodSecondsAtAgeWithAlpha(t_Ma, alpha) {
|
|
85
|
+
const a = deps.moonDistanceMetresAtAge(t_Ma);
|
|
86
|
+
if (a <= 0 || a >= K.aLockMetres) return null;
|
|
87
|
+
const iEarth = alpha * K.mEarthAloneKg * K.rEarthMetres * K.rEarthMetres;
|
|
88
|
+
return (2 * Math.PI * iEarth) /
|
|
89
|
+
(K.lTotalEmKgm2S - K.mMoonAloneKg * Math.sqrt(K.gmEmM3PerS2 * a) * K.eFactorMoon);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** @param {number} t_Ma @returns {number|null} */
|
|
93
|
+
function lodHoursAtAge(t_Ma) {
|
|
94
|
+
const s = lodSecondsAtAge(t_Ma);
|
|
95
|
+
return (s === null) ? null : s / 3600;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** H(t) via the H/13 identity: H(t) = H_J2000 · LOD(t)/LOD_J2000.
|
|
99
|
+
* @param {number} t_Ma @returns {number|null} */
|
|
100
|
+
function hAtAge(t_Ma) {
|
|
101
|
+
const LOD_s = lodSecondsAtAge(t_Ma);
|
|
102
|
+
if (LOD_s === null) return null;
|
|
103
|
+
return K.holisticYearJ2000 * LOD_s / K.lodNowH13Seconds;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Sidereal year seconds (Kepler under linear mass loss, dT/T = −2 dM/M).
|
|
107
|
+
* @param {number} t_Ma @returns {number} */
|
|
108
|
+
function siderealYearSecondsAtAge(t_Ma) {
|
|
109
|
+
if (t_Ma === 0) return K.meanSiderealYearJ2000Seconds;
|
|
110
|
+
const mass_loss_fraction = K.solarMassLossFracPerYear * t_Ma * 1e6;
|
|
111
|
+
return K.meanSiderealYearJ2000Seconds * (1 - 2 * mass_loss_fraction);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** @param {number} t_Ma @returns {number} tropical = sidereal · (1 − 13/H(t)) */
|
|
115
|
+
function tropicalYearSecondsAtAge(t_Ma) {
|
|
116
|
+
const sidSec = siderealYearSecondsAtAge(t_Ma);
|
|
117
|
+
const Ht = hAtAge(t_Ma);
|
|
118
|
+
if (Ht === null) return sidSec * (1 - 13 / K.holisticYearJ2000);
|
|
119
|
+
return sidSec * (1 - 13 / Ht);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** @param {number} t_Ma @returns {number|null} */
|
|
123
|
+
function tropicalYearDaysAtAge(t_Ma) {
|
|
124
|
+
const seconds = tropicalYearSecondsAtAge(t_Ma);
|
|
125
|
+
return seconds === null ? null : seconds / 86400;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** @param {number} t_Ma @returns {number|null} tropical seconds / LOD(t) */
|
|
129
|
+
function yearInDaysAtAge(t_Ma) {
|
|
130
|
+
const LOD_s = lodSecondsAtAge(t_Ma);
|
|
131
|
+
if (LOD_s === null) return null;
|
|
132
|
+
return tropicalYearSecondsAtAge(t_Ma) / LOD_s;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* RAW ΔT integral relative to J2000 (0 at t=0 by convention) — Simpson
|
|
137
|
+
* over the H/5-raw kinematic LOD, WITHOUT the cycle corrections and
|
|
138
|
+
* WITHOUT a cache. The engines wrap this: flag-keyed cache + their exact
|
|
139
|
+
* sequential post-integration adds (see the module header).
|
|
140
|
+
* @param {number} t_Ma @returns {number} seconds (NaN past tidal lock)
|
|
141
|
+
*/
|
|
142
|
+
function deltaTRawSecondsAtAge(t_Ma) {
|
|
143
|
+
if (t_Ma === 0) return 0;
|
|
144
|
+
const absSpan = Math.abs(t_Ma);
|
|
145
|
+
let n = Math.max(32, Math.ceil(absSpan * 10));
|
|
146
|
+
if (n > 1024) n = 1024;
|
|
147
|
+
if (n % 2 === 1) n++;
|
|
148
|
+
const h = t_Ma / n;
|
|
149
|
+
|
|
150
|
+
let sum = 0;
|
|
151
|
+
for (let i = 0; i <= n; i++) {
|
|
152
|
+
const tau = i * h;
|
|
153
|
+
const lodMean = lodSecondsAtAge(tau);
|
|
154
|
+
if (lodMean === null) return NaN;
|
|
155
|
+
const yearS = tropicalYearSecondsAtAge(tau);
|
|
156
|
+
// H/5 ecliptic "missing motion" — the solar day is measured against
|
|
157
|
+
// the Sun on the ECLIPTIC (precesses at H/5), not the inclination
|
|
158
|
+
// frame (H/3). Adds ~3.5 ms at J2000 (raw kinematic 86400.003 s); the
|
|
159
|
+
// fitted cycle stack closes Layer-4 LOD_real onto the USNO anchor.
|
|
160
|
+
// Non-null: both can only be null when lodSecondsAtAge(tau) is null,
|
|
161
|
+
// which already returned NaN above — the checker can't see the chain.
|
|
162
|
+
const H_local = /** @type {number} */ (hAtAge(tau));
|
|
163
|
+
const mSY_days = /** @type {number} */ (tropicalYearDaysAtAge(tau));
|
|
164
|
+
const lodH5Raw = lodMean + lodMean / ((H_local / 5) * mSY_days);
|
|
165
|
+
const integrand = (86400 - lodH5Raw) * yearS * 1e6 / 86400;
|
|
166
|
+
const w = (i === 0 || i === n) ? 1 : (i % 2 === 1 ? 4 : 2);
|
|
167
|
+
sum += w * integrand;
|
|
168
|
+
}
|
|
169
|
+
return (sum * h) / 3;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Layer 3/4 LOD: tidal mean + the gated cycle δLOD sum.
|
|
173
|
+
* @param {number} t_Ma @returns {number|null} */
|
|
174
|
+
function lodSecondsWithCorrectionsAtAge(t_Ma) {
|
|
175
|
+
const tidal = lodSecondsAtAge(t_Ma);
|
|
176
|
+
if (tidal === null) return null;
|
|
177
|
+
const year = 2000 - t_Ma * 1e6;
|
|
178
|
+
return tidal + deps.cycleLodSumAt(year);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** ACTUAL LOD: tidal mean × Fourier ripple —
|
|
182
|
+
* actual(t) = mean(t) × Y_days_kinematic_J2000 / Y_days_fourier(year).
|
|
183
|
+
* Fixed bases (the ripple isolates year-to-year variation; the deep-time
|
|
184
|
+
* drift is already in mean(t)). @param {number} t_Ma @returns {number|null} */
|
|
185
|
+
function lodSecondsActualAtAge(t_Ma) {
|
|
186
|
+
const mean_t = lodSecondsAtAge(t_Ma);
|
|
187
|
+
if (mean_t === null) return null;
|
|
188
|
+
const year_at_t = 2000 - t_Ma * 1e6;
|
|
189
|
+
const Y_days_fourier = deps.siderealYearDaysFourierAt(year_at_t);
|
|
190
|
+
return mean_t * K.siderealYearDaysKinematicJ2000 / Y_days_fourier;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* dLOD/dt driver decomposition, ms/century per channel.
|
|
195
|
+
* tidal: Moon-recession (Farhat da/dt, LLR-anchored) · gia: α(t) numeric
|
|
196
|
+
* derivative · stack: 50-yr central difference on the FLAGS-ONLY δLOD sum
|
|
197
|
+
* (smooth; shortest harmonic 716 yr) · resonator: the ANALYTIC rate (the
|
|
198
|
+
* episode's δLOD steps at the kicks — a central difference smears them).
|
|
199
|
+
* @param {number} t_Ma
|
|
200
|
+
* @returns {{ tidal: number|null, gia: number|null, stack: number|null,
|
|
201
|
+
* resonator: number|null, net_L2: number|null, net_L3: number|null,
|
|
202
|
+
* net_L4: number|null }}
|
|
203
|
+
*/
|
|
204
|
+
function dLodDtDecompositionAtAge(t_Ma) {
|
|
205
|
+
const nullResult = { tidal: null, gia: null, stack: null, resonator: null,
|
|
206
|
+
net_L2: null, net_L3: null, net_L4: null };
|
|
207
|
+
const a = deps.moonDistanceMetresAtAge(t_Ma);
|
|
208
|
+
if (a === null || a <= 0 || a >= K.aLockMetres) return nullResult;
|
|
209
|
+
const lod_s = lodSecondsAtAge(t_Ma);
|
|
210
|
+
if (lod_s === null) return nullResult;
|
|
211
|
+
const alpha = deps.moiFactorAtAge(t_Ma);
|
|
212
|
+
const I_E = alpha * K.mEarthAloneKg * K.rEarthMetres * K.rEarthMetres;
|
|
213
|
+
const year = 2000 - t_Ma * 1e6;
|
|
214
|
+
|
|
215
|
+
// Farhat polynomial derivative: da/dyear = −A_now·(α₁ + 3α₃t² + 4α₄t³)/1e6
|
|
216
|
+
// (t_Ma > 0 = past; forward-time sign flip). At J2000: +3.82 cm/yr (LLR) ✓
|
|
217
|
+
const da_dt_yr = -K.aMoonNowMetres * (K.alpha1PerMa + 3 * K.alpha3PerMa3 * t_Ma * t_Ma
|
|
218
|
+
+ 4 * K.alpha4PerMa4 * t_Ma * t_Ma * t_Ma) / 1e6;
|
|
219
|
+
const SEC_PER_YR = 365.25 * 86400;
|
|
220
|
+
const da_dt_s = da_dt_yr / SEC_PER_YR;
|
|
221
|
+
|
|
222
|
+
// Tidal channel: dL_M/dt = m_M · ½ · √(GM/a) · da/dt · √(1−e²)
|
|
223
|
+
const dLm_dt = K.mMoonAloneKg * 0.5 * Math.sqrt(K.gmEmM3PerS2 / a) * da_dt_s * K.eFactorMoon;
|
|
224
|
+
const domega_dt_tidal = -dLm_dt / I_E;
|
|
225
|
+
const dLod_dt_tidal_s_per_s = -(lod_s * lod_s) / (2 * Math.PI) * domega_dt_tidal;
|
|
226
|
+
const dLod_dt_tidal_ms_per_cy = dLod_dt_tidal_s_per_s * SEC_PER_YR * 100 * 1000;
|
|
227
|
+
|
|
228
|
+
// GIA channel: dLOD/dt = LOD·(dα/dt)/α, α differentiated numerically
|
|
229
|
+
// (100-yr step — well below any L1 harmonic period).
|
|
230
|
+
const EPS_MA = 1e-4;
|
|
231
|
+
const alpha_plus = deps.moiFactorAtAge(t_Ma - EPS_MA);
|
|
232
|
+
const alpha_minus = deps.moiFactorAtAge(t_Ma + EPS_MA);
|
|
233
|
+
const dalpha_dyr = (alpha_plus - alpha_minus) / (2 * EPS_MA * 1e6);
|
|
234
|
+
const dLod_dt_gia_s_per_yr = lod_s * dalpha_dyr / alpha;
|
|
235
|
+
const dLod_dt_gia_ms_per_cy = dLod_dt_gia_s_per_yr * 100 * 1000;
|
|
236
|
+
|
|
237
|
+
// Flags-only stack rate (sum minus the resonator term), 50-yr window.
|
|
238
|
+
const DYR = 50;
|
|
239
|
+
const dFlags_dyr = ((deps.cycleLodSumAt(year + DYR) - deps.swingLodAt(year + DYR))
|
|
240
|
+
- (deps.cycleLodSumAt(year - DYR) - deps.swingLodAt(year - DYR)))
|
|
241
|
+
/ (2 * DYR);
|
|
242
|
+
const dLod_dt_stack_only_ms_per_cy = dFlags_dyr * 100 * 1000;
|
|
243
|
+
const dLod_dt_resonator_ms_per_cy = deps.swingLodRateAt(year) * 100 * 1000;
|
|
244
|
+
|
|
245
|
+
const net_L2 = dLod_dt_tidal_ms_per_cy + dLod_dt_gia_ms_per_cy;
|
|
246
|
+
const net_L3 = net_L2 + dLod_dt_stack_only_ms_per_cy;
|
|
247
|
+
const net_L4 = net_L3 + dLod_dt_resonator_ms_per_cy;
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
tidal: dLod_dt_tidal_ms_per_cy,
|
|
251
|
+
gia: dLod_dt_gia_ms_per_cy,
|
|
252
|
+
stack: dLod_dt_stack_only_ms_per_cy,
|
|
253
|
+
resonator: dLod_dt_resonator_ms_per_cy,
|
|
254
|
+
net_L2, net_L3, net_L4,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
lodSecondsAtAge, lodSecondsAtAgeWithAlpha, lodHoursAtAge, hAtAge,
|
|
260
|
+
siderealYearSecondsAtAge, tropicalYearSecondsAtAge, tropicalYearDaysAtAge,
|
|
261
|
+
yearInDaysAtAge, deltaTRawSecondsAtAge, lodSecondsWithCorrectionsAtAge,
|
|
262
|
+
lodSecondsActualAtAge, dLodDtDecompositionAtAge,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module.exports = { createDeepTimeLod };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Historical ΔT — the Espenak/Meeus piecewise polynomial (Phase 8.4,
|
|
3
|
+
* slice 4; canon-only since 8.4-4c). EXACTLY the published NASA canon
|
|
4
|
+
* (eclipse.gsfc.nasa.gov/SEcat5/deltatpoly.html): every era segment
|
|
5
|
+
* including the separate 1986–2005 one, and the −20 + 32u² parabola
|
|
6
|
+
* (with the −0.5628·(2150−y) correction above 2050) outside the
|
|
7
|
+
* tabulated range — Espenak's own out-of-range guidance, not NaN.
|
|
8
|
+
*
|
|
9
|
+
* HISTORY: the browser shipped a simplification until 8.4-4c (1986–2005
|
|
10
|
+
* merged into the 62.92-branch; NaN outside −1999..3000) with a matching
|
|
11
|
+
* 62.92 display anchor. Both were replaced by the canon + its 63.86
|
|
12
|
+
* anchor (astro-reference externalCurveAnchors.deltaTEspenakJ2000Seconds)
|
|
13
|
+
* in one measured commit — curve and anchor are a matched pair.
|
|
14
|
+
*
|
|
15
|
+
* ONE VARIANT REMAINS OUTSIDE (recorded in plan §12g for Phase 9):
|
|
16
|
+
* tools/fit/sun-longitude-harmonics.js — its ΔT timed the UT→TT
|
|
17
|
+
* conversion when the SHIPPED sun-harmonic coefficients were fitted, so
|
|
18
|
+
* it is half of a matched pair and swaps only with a deliberate refit.
|
|
19
|
+
*
|
|
20
|
+
* The engines own their anchoring conventions (the browser subtracts its
|
|
21
|
+
* DELTA_T_ESPENAK_J2000_S display anchor separately).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
'use strict';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Espenak/Meeus ΔT in seconds — the FULL published canon: separate
|
|
28
|
+
* 1986–2005 segment, and the −20 + 32u² parabola (with the −0.5628
|
|
29
|
+
* correction above 2050) outside the tabulated range instead of NaN.
|
|
30
|
+
* @param {number} year - calendar year
|
|
31
|
+
* @returns {number}
|
|
32
|
+
*/
|
|
33
|
+
function deltaTEspenakMeeusCanonSeconds(year) {
|
|
34
|
+
let u, t;
|
|
35
|
+
if (year < -500) {
|
|
36
|
+
u = (year - 1820) / 100;
|
|
37
|
+
return -20 + 32 * u * u;
|
|
38
|
+
} else if (year < 500) {
|
|
39
|
+
u = year / 100;
|
|
40
|
+
return 10583.6 - 1014.41 * u + 33.78311 * u ** 2 - 5.952053 * u ** 3 - 0.1798452 * u ** 4
|
|
41
|
+
+ 0.022174192 * u ** 5 + 0.0090316521 * u ** 6;
|
|
42
|
+
} else if (year < 1600) {
|
|
43
|
+
u = (year - 1000) / 100;
|
|
44
|
+
return 1574.2 - 556.01 * u + 71.23472 * u ** 2 + 0.319781 * u ** 3 - 0.8503463 * u ** 4
|
|
45
|
+
- 0.005050998 * u ** 5 + 0.0083572073 * u ** 6;
|
|
46
|
+
} else if (year < 1700) {
|
|
47
|
+
t = year - 1600;
|
|
48
|
+
return 120 - 0.9808 * t - 0.01532 * t ** 2 + t ** 3 / 7129;
|
|
49
|
+
} else if (year < 1800) {
|
|
50
|
+
t = year - 1700;
|
|
51
|
+
return 8.83 + 0.1603 * t - 0.0059285 * t ** 2 + 0.00013336 * t ** 3 - t ** 4 / 1174000;
|
|
52
|
+
} else if (year < 1860) {
|
|
53
|
+
t = year - 1800;
|
|
54
|
+
return 13.72 - 0.332447 * t + 0.0068612 * t ** 2 + 0.0041116 * t ** 3 - 0.00037436 * t ** 4
|
|
55
|
+
+ 0.0000121272 * t ** 5 - 0.0000001699 * t ** 6 + 0.000000000875 * t ** 7;
|
|
56
|
+
} else if (year < 1900) {
|
|
57
|
+
t = year - 1860;
|
|
58
|
+
return 7.62 + 0.5737 * t - 0.251754 * t ** 2 + 0.01680668 * t ** 3 - 0.0004473624 * t ** 4 + t ** 5 / 233174;
|
|
59
|
+
} else if (year < 1920) {
|
|
60
|
+
t = year - 1900;
|
|
61
|
+
return -2.79 + 1.494119 * t - 0.0598939 * t ** 2 + 0.0061966 * t ** 3 - 0.000197 * t ** 4;
|
|
62
|
+
} else if (year < 1941) {
|
|
63
|
+
t = year - 1920;
|
|
64
|
+
return 21.20 + 0.84493 * t - 0.076100 * t ** 2 + 0.0020936 * t ** 3;
|
|
65
|
+
} else if (year < 1961) {
|
|
66
|
+
t = year - 1950;
|
|
67
|
+
return 29.07 + 0.407 * t - t ** 2 / 233 + t ** 3 / 2547;
|
|
68
|
+
} else if (year < 1986) {
|
|
69
|
+
t = year - 1975;
|
|
70
|
+
return 45.45 + 1.067 * t - t ** 2 / 260 - t ** 3 / 718;
|
|
71
|
+
} else if (year < 2005) {
|
|
72
|
+
t = year - 2000;
|
|
73
|
+
return 63.86 + 0.3345 * t - 0.060374 * t ** 2 + 0.0017275 * t ** 3 + 0.000651814 * t ** 4
|
|
74
|
+
+ 0.00002373599 * t ** 5;
|
|
75
|
+
} else if (year < 2050) {
|
|
76
|
+
t = year - 2000;
|
|
77
|
+
return 62.92 + 0.32217 * t + 0.005589 * t ** 2;
|
|
78
|
+
} else {
|
|
79
|
+
u = (year - 1820) / 100;
|
|
80
|
+
return -20 + 32 * u * u - 0.5628 * (2150 - year);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = { deltaTEspenakMeeusCanonSeconds };
|