@essrt/physics 10.0.0 → 11.0.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 +2 -2
- package/src/constants/generated.js +1 -1
- package/src/earth/deep-orbital-history.cjs +94 -37
- package/src/earth/sidereal-channel-artifact.cjs +4 -3
- package/src/earth/sidereal-year-channel.cjs +14 -4
- package/src/earth/year-lengths.cjs +88 -53
- package/src/model.js +9 -14
- package/types/earth/deep-orbital-history.d.cts +3 -0
- package/types/earth/sidereal-channel-artifact.d.cts +2 -2
- package/types/earth/year-lengths.d.cts +14 -16
- package/types/model.d.ts +4 -2
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@essrt/physics",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.0.1",
|
|
4
4
|
"description": "ESSRT (Expanding Solar System Resonance Theory) physics core — 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
|
-
"modelVersion": "
|
|
6
|
+
"modelVersion": "v16.0",
|
|
7
7
|
"_note": "The model identity this package version ships (IP-unified-architecture §10: package semver tracks the API axis; the model version + CONSTANTS_HASH/COEFFICIENTS_HASH in src/constants/ carry the scientific identity). The version-pinning gate enforces this pairing against public/input/model-version.json."
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
export const CONSTANTS_HASH = "726b10e23d8ace09";
|
|
37
37
|
|
|
38
38
|
/** Model version label — single source: public/input/model-version.json (§10 two-axis scheme). */
|
|
39
|
-
export const MODEL_VERSION = "
|
|
39
|
+
export const MODEL_VERSION = "v16.0";
|
|
40
40
|
|
|
41
41
|
/** Canonical preprint DOI — single source: public/input/model-version.json. */
|
|
42
42
|
export const PREPRINT_DOI = "10.21203/rs.3.rs-8758810/v4";
|
|
@@ -342,12 +342,44 @@ function createDeepOrbitalHistory({
|
|
|
342
342
|
return [o[0] / r, o[1] / r, o[2] / r];
|
|
343
343
|
};
|
|
344
344
|
|
|
345
|
-
/**
|
|
346
|
-
*
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
345
|
+
/** The equinox's GENERAL-PRECESSION longitude (deg, J2000 ecliptic frame):
|
|
346
|
+
* the broken angle through the ascending node of the ecliptic of date on
|
|
347
|
+
* the J2000 ecliptic — Π (the node's longitude) plus the arc along the
|
|
348
|
+
* ecliptic of date from that node to the equinox. This is the p_A of the
|
|
349
|
+
* precession literature (Lieske 1977; Simon et al. 1994: p_A = Λ_A − Π_A):
|
|
350
|
+
* the quantity the mean longitude OF DATE subtracts from the fixed-frame
|
|
351
|
+
* mean longitude — the N-body λ = Ω + ω + M is itself this broken angle
|
|
352
|
+
* (lattice-long-window-test.mjs), so the tropical year of date must be
|
|
353
|
+
* built on THIS rate. NOT the projected longitude atan2(g_y, g_x)
|
|
354
|
+
* (equinoxLonJ2000Deg, which the frame reconstruction needs): the two
|
|
355
|
+
* agree at J2000, where the ecliptic of date coincides with the J2000
|
|
356
|
+
* ecliptic, and part with the SQUARE of the ecliptic's tilt to its J2000
|
|
357
|
+
* position — (i²/4)·sin 2u class: 0.9″ at 0 AD, 5.9″ at −3000,
|
|
358
|
+
* arcminutes at deep time; as a rate 0.05 s of tropical year at ±4 kyr,
|
|
359
|
+
* 0.1–0.2 s at ±10 kyr, ~1 s beyond 100 kyr (measured 2026-09: the scene's
|
|
360
|
+
* own sidereal angle read exactly this against the projected-rate law at
|
|
361
|
+
* 16 epochs, and the re-booked Sun is flat against DE441 in the inertial
|
|
362
|
+
* frame over −2500…+2000 where the projected booking ramped to +5.6″).
|
|
363
|
+
* Degenerate node as i → 0: the dogleg's two parts compensate each other's
|
|
364
|
+
* error, and below 1e-9 the projected form is the exact limit.
|
|
365
|
+
* @param {number[]} n the ecliptic pole of date @param {number[]} gu the unit equinox */
|
|
366
|
+
function generalPrecessionLonDeg(n, gu) {
|
|
367
|
+
const Nx = -n[1], Ny = n[0]; // ẑ × n̂ — the ascending node of the ecliptic of date
|
|
368
|
+
const sN = Math.hypot(Nx, Ny);
|
|
369
|
+
if (sN < 1e-9) return Math.atan2(gu[1], gu[0]) * R2D;
|
|
370
|
+
const N = [Nx / sN, Ny / sN, 0];
|
|
371
|
+
const Pi = Math.atan2(N[1], N[0]);
|
|
372
|
+
const u = Math.atan2(dot(n, cross(N, gu)), dot(N, gu));
|
|
373
|
+
return (Pi + u) * R2D;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** The three of-date longitudes whose node RATES the grid stores (the
|
|
377
|
+
* projected and general-precession equinox longitudes, the perihelion
|
|
378
|
+
* longitude of date) — read from a full sample at the probe state, so the
|
|
379
|
+
* rates are finite differences of exactly the stored quantities. */
|
|
380
|
+
function eqLonsOnlyDeg(/** @type {number[]} */ s, /** @type {number} */ t) {
|
|
381
|
+
const smp = sampleAt(s, t);
|
|
382
|
+
return { proj: smp.equinoxLonJ2000Deg, general: smp.generalPrecessionLonDeg, peri: smp.periOfDateDeg };
|
|
351
383
|
}
|
|
352
384
|
|
|
353
385
|
/** One quantity bundle at time t (years from J2000) from spin axis s. */
|
|
@@ -383,26 +415,39 @@ function createDeepOrbitalHistory({
|
|
|
383
415
|
// interpolation on the grid is exact to the ζ series' own resolution.
|
|
384
416
|
orbitNormalX: n[0],
|
|
385
417
|
orbitNormalY: n[1],
|
|
386
|
-
// The equinox node (ŝ×n̂) longitude in the J2000 ecliptic
|
|
387
|
-
//
|
|
388
|
-
//
|
|
389
|
-
//
|
|
390
|
-
// tropical
|
|
391
|
-
//
|
|
418
|
+
// The equinox node (ŝ×n̂) PROJECTED longitude in the J2000 ecliptic
|
|
419
|
+
// frame — the frame reconstruction's input (frame-of-date.cjs rebuilds
|
|
420
|
+
// the unit equinox from it). Its rate is the general precession only
|
|
421
|
+
// to first order in the ecliptic's tilt to its J2000 position: the
|
|
422
|
+
// tropical-year consumers read generalPrecessionLonDeg below (measured
|
|
423
|
+
// 2026-09 — the projected rate booked the Sun's mean longitude of date
|
|
424
|
+
// 5.9″ off at −3000 and the sidereal year of date 1 s off at depth).
|
|
425
|
+
// ⚠ RATE-CONSUMER CONTRACT (measured): these fields' rates are correct
|
|
392
426
|
// AS IS — α is self-anchored to the LUNISOLAR rate at construction
|
|
393
427
|
// (K_LUNI, the one-RK4-step probe above), so the realized general
|
|
394
|
-
// precession p_geom(J2000) equals 360/axialPrecessionYearsJ2000
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
// sid/(sid−sol) rate and
|
|
398
|
-
// mean — a double-count of
|
|
399
|
-
// J2000 — and consumers had to
|
|
428
|
+
// precession p_geom(J2000) equals 360/axialPrecessionYearsJ2000 per
|
|
429
|
+
// JULIAN year (the sampler's time unit) by construction. No
|
|
430
|
+
// correction, no δ subtraction. History (why this contract exists):
|
|
431
|
+
// before the self-anchor, α carried the GENERAL sid/(sid−sol) rate and
|
|
432
|
+
// the n̂(t) geometry re-added the planetary mean — a double-count of
|
|
433
|
+
// +0.097″/yr ≈ +2.4 s of tropical year at J2000 — and consumers had to
|
|
434
|
+
// subtract the runtime anchor
|
|
400
435
|
// δ = p_geom(J2000) − 360/axialPrecessionYearsJ2000.
|
|
401
436
|
// A consumer may still measure δ at runtime as a SELF-CHECK; it
|
|
402
437
|
// must read ≈0 (sub-1e-4 ″/yr class). The LONGITUDE itself is raw
|
|
403
438
|
// geometry and has always needed no correction.
|
|
404
439
|
equinoxLonJ2000Deg: ((Math.atan2(gu[1], gu[0]) * R2D) % 360 + 360) % 360,
|
|
405
440
|
equinoxLonRateDegPerYr: 0, // filled at store time (build's ±2.5-yr central difference)
|
|
441
|
+
// The equinox's GENERAL-PRECESSION longitude (the broken angle through
|
|
442
|
+
// the node of the ecliptic of date — the literature's p_A; see
|
|
443
|
+
// generalPrecessionLonDeg above): its year-over-year retrograde advance
|
|
444
|
+
// IS the general precession of date, wobble included (the n̂(t)
|
|
445
|
+
// geometry generates the equinox wobble; ψ̇ itself is the secular
|
|
446
|
+
// α(H(t)) times the two factors of date). The tropical year of date is
|
|
447
|
+
// built on this rate (earth/year-lengths.cjs).
|
|
448
|
+
generalPrecessionLonDeg: ((generalPrecessionLonDeg(n, gu) % 360) + 360) % 360,
|
|
449
|
+
generalPrecessionLonRateDegPerYr: 0, // filled at store time (build's ±2.5-yr central difference)
|
|
450
|
+
periOfDateRateDegPerYr: 0, // filled at store time — the C1 input for periOfDateDeg (the apsidal rate of date)
|
|
406
451
|
};
|
|
407
452
|
}
|
|
408
453
|
|
|
@@ -442,12 +487,20 @@ function createDeepOrbitalHistory({
|
|
|
442
487
|
// centuries with steps at the 1700/1800/1900/2000 cell edges
|
|
443
488
|
// (measured; the levels were the real wobble at 100-yr resolution,
|
|
444
489
|
// the staircase was this interpolation order).
|
|
490
|
+
// The perihelion longitude of date gets the same C1 treatment (2026-09,
|
|
491
|
+
// owner-found): linear interpolation of periOfDateDeg made the
|
|
492
|
+
// movement's year-over-year apsidal rate — the anomalistic year, the
|
|
493
|
+
// Prec. cell — a staircase of ±0.1 s across the 100-yr cells, reading
|
|
494
|
+
// 365.2596375 d at 2000.0 and 365.2596390 d at 2000.5, and the panel's
|
|
495
|
+
// apsidal beat 111,494 where the registry read 111,548.
|
|
445
496
|
const storeWithRate = (/** @type {number} */ key, /** @type {number[]} */ sv, /** @type {number} */ tv) => {
|
|
446
497
|
const smp = sampleAt(sv, tv);
|
|
447
498
|
const hR = 2.5;
|
|
448
|
-
const lp =
|
|
449
|
-
const lm =
|
|
450
|
-
smp.equinoxLonRateDegPerYr = (((lp - lm + 540) % 360) - 180) / (2 * hR);
|
|
499
|
+
const lp = eqLonsOnlyDeg(rk4(sv, tv, +hR), tv + hR);
|
|
500
|
+
const lm = eqLonsOnlyDeg(rk4(sv, tv, -hR), tv - hR);
|
|
501
|
+
smp.equinoxLonRateDegPerYr = (((lp.proj - lm.proj + 540) % 360) - 180) / (2 * hR);
|
|
502
|
+
smp.generalPrecessionLonRateDegPerYr = (((lp.general - lm.general + 540) % 360) - 180) / (2 * hR);
|
|
503
|
+
smp.periOfDateRateDegPerYr = (((lp.peri - lm.peri + 540) % 360) - 180) / (2 * hR);
|
|
451
504
|
grid.set(key, smp);
|
|
452
505
|
};
|
|
453
506
|
storeWithRate(0, s, 0);
|
|
@@ -475,31 +528,35 @@ function createDeepOrbitalHistory({
|
|
|
475
528
|
if (!a) throw new Error(`deep-orbital-history: ${tYr} outside the built range`);
|
|
476
529
|
const f = (tYr - k0) / stepYr;
|
|
477
530
|
const lerp = (/** @type {number} */ x, /** @type {number} */ y) => x + (y - x) * f;
|
|
478
|
-
|
|
531
|
+
// C1 (cubic Hermite) on a wrapped angle with node rates (deg/yr) —
|
|
532
|
+
// linear interpolation here made the realized equinox RATE
|
|
533
|
+
// piecewise-constant per grid cell (the measured per-year
|
|
534
|
+
// precession staircase). Node rates come from the build's own
|
|
535
|
+
// ±2.5-yr central differences.
|
|
536
|
+
const hermiteAngle = (/** @type {number} */ l0, /** @type {number} */ l1, /** @type {number} */ r0, /** @type {number} */ r1) => {
|
|
537
|
+
const dl = ((l1 - l0 + 540) % 360) - 180; // unwrapped segment
|
|
538
|
+
const m0 = r0 * stepYr, m1 = r1 * stepYr;
|
|
539
|
+
const f2 = f * f, f3 = f2 * f;
|
|
540
|
+
const v = (2 * f3 - 3 * f2 + 1) * l0 + (f3 - 2 * f2 + f) * m0
|
|
541
|
+
+ (-2 * f3 + 3 * f2) * (l0 + dl) + (f3 - f2) * m1;
|
|
542
|
+
return ((v % 360) + 360) % 360;
|
|
543
|
+
};
|
|
479
544
|
return {
|
|
480
545
|
epsDeg: lerp(a.epsDeg, b.epsDeg),
|
|
481
546
|
e: lerp(a.e, b.e),
|
|
482
|
-
periOfDateDeg: (
|
|
547
|
+
periOfDateDeg: hermiteAngle(a.periOfDateDeg, b.periOfDateDeg, a.periOfDateRateDegPerYr, b.periOfDateRateDegPerYr ?? a.periOfDateRateDegPerYr),
|
|
548
|
+
periOfDateRateDegPerYr: lerp(a.periOfDateRateDegPerYr, b.periOfDateRateDegPerYr ?? a.periOfDateRateDegPerYr),
|
|
483
549
|
eSinPeri: lerp(a.eSinPeri, b.eSinPeri),
|
|
484
550
|
eCosPeri: lerp(a.eCosPeri, b.eCosPeri),
|
|
485
551
|
inclEclDeg: lerp(a.inclEclDeg, b.inclEclDeg),
|
|
486
552
|
orbitNormalX: lerp(a.orbitNormalX, b.orbitNormalX),
|
|
487
553
|
orbitNormalY: lerp(a.orbitNormalY, b.orbitNormalY),
|
|
488
|
-
|
|
489
|
-
// realized equinox RATE piecewise-constant per grid cell (the
|
|
490
|
-
// measured per-year precession staircase). Node rates come from
|
|
491
|
-
// the build's own ±2.5-yr central differences.
|
|
492
|
-
equinoxLonJ2000Deg: (() => {
|
|
493
|
-
const l0 = a.equinoxLonJ2000Deg;
|
|
494
|
-
const dl = ((b.equinoxLonJ2000Deg - l0 + 540) % 360) - 180; // unwrapped segment
|
|
495
|
-
const m0 = a.equinoxLonRateDegPerYr * stepYr;
|
|
496
|
-
const m1 = (b.equinoxLonRateDegPerYr ?? a.equinoxLonRateDegPerYr) * stepYr;
|
|
497
|
-
const f2 = f * f, f3 = f2 * f;
|
|
498
|
-
const v = (2 * f3 - 3 * f2 + 1) * l0 + (f3 - 2 * f2 + f) * m0
|
|
499
|
-
+ (-2 * f3 + 3 * f2) * (l0 + dl) + (f3 - f2) * m1;
|
|
500
|
-
return ((v % 360) + 360) % 360;
|
|
501
|
-
})(),
|
|
554
|
+
equinoxLonJ2000Deg: hermiteAngle(a.equinoxLonJ2000Deg, b.equinoxLonJ2000Deg, a.equinoxLonRateDegPerYr, b.equinoxLonRateDegPerYr ?? a.equinoxLonRateDegPerYr),
|
|
502
555
|
equinoxLonRateDegPerYr: lerp(a.equinoxLonRateDegPerYr, b.equinoxLonRateDegPerYr ?? a.equinoxLonRateDegPerYr),
|
|
556
|
+
// the same C1 form for the general-precession longitude (its
|
|
557
|
+
// year-over-year difference is the tropical-year law's p).
|
|
558
|
+
generalPrecessionLonDeg: hermiteAngle(a.generalPrecessionLonDeg, b.generalPrecessionLonDeg, a.generalPrecessionLonRateDegPerYr, b.generalPrecessionLonRateDegPerYr ?? a.generalPrecessionLonRateDegPerYr),
|
|
559
|
+
generalPrecessionLonRateDegPerYr: lerp(a.generalPrecessionLonRateDegPerYr, b.generalPrecessionLonRateDegPerYr ?? a.generalPrecessionLonRateDegPerYr),
|
|
503
560
|
};
|
|
504
561
|
},
|
|
505
562
|
};
|