@essrt/physics 23.3.2 → 23.6.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 +6 -1
- package/package.json +2 -2
- package/src/constants/coefficients.d.ts +5 -1
- package/src/constants/coefficients.js +3 -2
- package/src/constants/generated.d.ts +2 -3
- package/src/constants/generated.js +3 -4
- package/src/constants/units.cjs +4 -1
- package/src/deltat/deep-time.cjs +37 -72
- package/src/index.js +35 -48
- package/src/model/cardinal-instants.js +101 -0
- package/src/model/context.js +54 -0
- package/src/model/deep-time-chain.js +79 -0
- package/src/model/delta-t.js +38 -0
- package/src/model/derived-constants.js +58 -0
- package/src/model/earth-scalars.js +47 -0
- package/src/model/inv-plane-elements.js +56 -0
- package/src/model/lunar-theory.js +216 -0
- package/src/model/moon-epoch.js +28 -0
- package/src/model/one-source-movement.js +192 -0
- package/src/model/planet-elements-of-date.js +61 -0
- package/src/model/planet-records.js +63 -0
- package/src/model/planet-spin-channel.js +41 -0
- package/src/model/published-periods.js +38 -0
- package/src/model/sun-and-eclipses.js +199 -0
- package/src/model/surface.js +330 -0
- package/src/model/time-axis.js +24 -0
- package/src/model/year-day-lengths.js +42 -0
- package/src/model.js +62 -1612
- package/src/moon/arguments.cjs +27 -43
- package/src/planets/chain-artifact.js +2 -2
- package/src/planets/keplerian-chain.cjs +1 -1
- package/types/constants/coefficients.d.ts +5 -1
- package/types/constants/generated.d.ts +2 -3
- package/types/constants/units.d.cts +3 -0
- package/types/deltat/deep-time.d.cts +18 -24
- package/types/index.d.ts +1 -1
- package/types/model/cardinal-instants.d.ts +14 -0
- package/types/model/context.d.ts +87 -0
- package/types/model/deep-time-chain.d.ts +288 -0
- package/types/model/delta-t.d.ts +11 -0
- package/types/model/derived-constants.d.ts +25 -0
- package/types/model/earth-scalars.d.ts +14 -0
- package/types/model/inv-plane-elements.d.ts +6 -0
- package/types/model/lunar-theory.d.ts +72 -0
- package/types/model/moon-epoch.d.ts +10 -0
- package/types/model/one-source-movement.d.ts +74 -0
- package/types/model/planet-elements-of-date.d.ts +9 -0
- package/types/model/planet-records.d.ts +7 -0
- package/types/model/planet-spin-channel.d.ts +29 -0
- package/types/model/published-periods.d.ts +7 -0
- package/types/model/sun-and-eclipses.d.ts +52 -0
- package/types/model/surface.d.ts +279 -0
- package/types/model/time-axis.d.ts +12 -0
- package/types/model/year-day-lengths.d.ts +8 -0
- package/types/model.d.ts +9 -81
- package/types/moon/arguments.d.cts +5 -11
- package/types/planets/keplerian-chain.d.cts +3 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The planets' elements of date — the chain with the series handover: ϖ, Ω (S&S origin), i, e.
|
|
3
|
+
*
|
|
4
|
+
* One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
|
|
5
|
+
*/
|
|
6
|
+
import { DEEP_MODES_ARTIFACT } from '../moon/deep-modes-artifact.cjs';
|
|
7
|
+
import { CHAIN_ARTIFACT } from '../planets/chain-artifact.js';
|
|
8
|
+
import { computeEquatorNodeOriginSFrameDeg, convertNodeSFrameToEquatorOriginDeg } from '../planets/inv-plane-frame.cjs';
|
|
9
|
+
import { buildPlanetChainsFromArtifactData, computePlanetElementsAtYear } from '../planets/keplerian-chain.cjs';
|
|
10
|
+
import { createSecularSeriesOverride } from '../planets/secular-series.cjs';
|
|
11
|
+
|
|
12
|
+
/** @param {import('./planet-spin-channel.js').Ctx} ctx @returns the bindings the later slices read */
|
|
13
|
+
export function assemblePlanetElementsOfDate(ctx) {
|
|
14
|
+
const { C, secularSeriesArtifact } = ctx;
|
|
15
|
+
// The planets' elements of date ride THE CHAIN — the only planet path the
|
|
16
|
+
// simulator renders — with the series handover beyond each planet's
|
|
17
|
+
// measured boundary when the series artifact is supplied: the mirror of the
|
|
18
|
+
// browser's _chainElementsOfDate → _chainSeriesSecularEl. The chain's
|
|
19
|
+
// argument is dynamical time; the API's decimal year is taken as that
|
|
20
|
+
// argument (ΔT on a secular element is far below its stated accuracy).
|
|
21
|
+
let planetChains = /** @type {any} */ (null);
|
|
22
|
+
let planetSeriesOverride = /** @type {any} */ (null);
|
|
23
|
+
let planetNodeOriginSSDeg = /** @type {number|null} */ (null);
|
|
24
|
+
/** @param {string} k @param {number} year */
|
|
25
|
+
const planetChainElementsAt = (k, year) => {
|
|
26
|
+
if (!planetChains) planetChains = buildPlanetChainsFromArtifactData(/** @type {any} */ (CHAIN_ARTIFACT));
|
|
27
|
+
const el = computePlanetElementsAtYear(year, planetChains[k], planetChains);
|
|
28
|
+
const seriesArt = /** @type {any} */ (secularSeriesArtifact);
|
|
29
|
+
if (!seriesArt) return el;
|
|
30
|
+
if (!planetSeriesOverride) {
|
|
31
|
+
planetSeriesOverride = createSecularSeriesOverride({
|
|
32
|
+
series: seriesArt,
|
|
33
|
+
anchorElements: /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements,
|
|
34
|
+
invariablePlane: /** @type {any} */ (CHAIN_ARTIFACT).invariablePlane,
|
|
35
|
+
planetZModes: /** @type {any} */ (DEEP_MODES_ARTIFACT).planetZ,
|
|
36
|
+
planetZetaModes: /** @type {any} */ (DEEP_MODES_ARTIFACT).planetZeta,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return planetSeriesOverride.applyToElements(k, year, el);
|
|
40
|
+
};
|
|
41
|
+
/** Ecliptic longitude of perihelion, J2000 ecliptic and equinox (the chain's ϖ). @param {string} k @param {number} year @returns {number} */
|
|
42
|
+
const planetPerihelionDeg = (k, year) => planetChainElementsAt(k, year).lonPeriEclipticDeg;
|
|
43
|
+
/** The S&S longitude origin as an s-frame longitude (J2000-fixed, derived). @returns {number} */
|
|
44
|
+
const nodeOriginSSDeg = () => {
|
|
45
|
+
if (planetNodeOriginSSDeg === null) {
|
|
46
|
+
planetNodeOriginSSDeg = computeEquatorNodeOriginSFrameDeg(/** @type {any} */ (CHAIN_ARTIFACT).invariablePlane, C.earthOrbital.obliquityJ2000_deg);
|
|
47
|
+
}
|
|
48
|
+
return planetNodeOriginSSDeg;
|
|
49
|
+
};
|
|
50
|
+
/** Ascending node on the model's invariable plane, from the plane's ascending node on the ICRF equator (the Souami & Souchay 2012 origin; derived conversion, inv-plane-frame). @param {string} k @param {number} year @returns {number} */
|
|
51
|
+
const planetAscNodeDeg = (k, year) => convertNodeSFrameToEquatorOriginDeg(planetChainElementsAt(k, year).ascNodeInvPlaneDeg, nodeOriginSSDeg());
|
|
52
|
+
/** Inclination to the model's invariable plane (exact orbit-normal rotation). @param {string} k @param {number} year @returns {number} */
|
|
53
|
+
const planetInclinationDeg = (k, year) => planetChainElementsAt(k, year).inclInvPlaneDeg;
|
|
54
|
+
/** Orbital eccentricity of date, from the same chain elements — the chain's
|
|
55
|
+
* multi-mode e-vector has no single base + amplitude + phase triple, so the
|
|
56
|
+
* value of date is the published form. @param {string} k @param {number} year @returns {number} */
|
|
57
|
+
const planetEccentricity = (k, year) => planetChainElementsAt(k, year).e;
|
|
58
|
+
|
|
59
|
+
return { nodeOriginSSDeg, planetAscNodeDeg, planetEccentricity, planetInclinationDeg, planetPerihelionDeg };
|
|
60
|
+
}
|
|
61
|
+
/** @typedef {import('./planet-spin-channel.js').Ctx & ReturnType<typeof assemblePlanetElementsOfDate>} Ctx */
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The planets' structural records — keys, mass fractions, the published J2000 record.
|
|
3
|
+
*
|
|
4
|
+
* One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
|
|
5
|
+
*/
|
|
6
|
+
import { CHAIN_ARTIFACT } from '../planets/chain-artifact.js';
|
|
7
|
+
import { PLANET_DISPLAY_NAMES } from '../planets/display-names.cjs';
|
|
8
|
+
import { computeObliquityJ2000Deg } from '../planets/spin-channel.cjs';
|
|
9
|
+
|
|
10
|
+
/** @param {import('./delta-t.js').Ctx} ctx @returns the bindings the later slices read */
|
|
11
|
+
export function assemblePlanetRecords(ctx) {
|
|
12
|
+
const { C, GM_EARTH_ALONE, G_CONSTANT, M_SUN } = ctx;
|
|
13
|
+
const PLANET_KEYS = ['mercury', 'venus', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune'];
|
|
14
|
+
const massEarthAlone = GM_EARTH_ALONE / G_CONSTANT;
|
|
15
|
+
/** @type {Record<string, number>} */
|
|
16
|
+
const massFraction = {};
|
|
17
|
+
for (const k of PLANET_KEYS) massFraction[k] = 1 / C.physicalConstants.massRatioDE440[k];
|
|
18
|
+
massFraction.earth = massEarthAlone / M_SUN;
|
|
19
|
+
|
|
20
|
+
/** @type {Record<string, Record<string, any>>} */
|
|
21
|
+
const PUBLISHED_PLANET_RECORDS = {}; // what model.planets.record() serves
|
|
22
|
+
for (const k of PLANET_KEYS) {
|
|
23
|
+
const ar = C.planetOrbitalElements[k];
|
|
24
|
+
// The obliquity is the DERIVED J2000 obliquity of the spin channel (the
|
|
25
|
+
// IAU pole against the chain's J2000 plane, acute form) — from the
|
|
26
|
+
// governed artifacts.
|
|
27
|
+
const obliquityDerived = computeObliquityJ2000Deg({
|
|
28
|
+
spin: C.planetSpinPhysical[k],
|
|
29
|
+
anchorInclEclipticDeg: /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements[k].inclEclipticDeg,
|
|
30
|
+
anchorAscNodeEclipticDeg: /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements[k].ascNodeEclipticDeg,
|
|
31
|
+
obliquityJ2000Deg: C.earthOrbital.obliquityJ2000_deg,
|
|
32
|
+
});
|
|
33
|
+
const obliquityMean = Math.min(obliquityDerived, 180 - obliquityDerived); // acute form of the derived J2000 obliquity
|
|
34
|
+
// What `model.planets.record()` PUBLISHES is the structural record: the
|
|
35
|
+
// REFERENCE inputs (IAU J2000 ϖ, the Souami & Souchay node and
|
|
36
|
+
// inclination, DE440 e, the period input, the axial tilt), the DERIVED
|
|
37
|
+
// `obliquityMean` (the spin channel against the chain's J2000 plane), and
|
|
38
|
+
// the two PERIODS that are chain eigenmodes — 1,296,000/g and 1,296,000/s
|
|
39
|
+
// (apsidal prograde positive, nodal regression negative). Quantities that
|
|
40
|
+
// vary with the epoch (e, i, Ω, ϖ of date) are served AT AN EPOCH by
|
|
41
|
+
// planet-elements-of-date.js and `/v1/bodies`' `at[]` rows, never as a
|
|
42
|
+
// single oscillation's mean + amplitude + phase — the chain's multi-mode
|
|
43
|
+
// vectors have none.
|
|
44
|
+
const chainModes = /** @type {any} */ (CHAIN_ARTIFACT);
|
|
45
|
+
const gArc = chainModes.g[k] && chainModes.g[k].arcsecPerYr;
|
|
46
|
+
const sArc = chainModes.s[k] && chainModes.s[k].arcsecPerYr;
|
|
47
|
+
PUBLISHED_PLANET_RECORDS[k] = Object.freeze({
|
|
48
|
+
name: PLANET_DISPLAY_NAMES[k], // presentation — planets/display-names, the deleted JSON's last leaf (plan 11 step 6)
|
|
49
|
+
solarYearInput: ar.solarYearInput,
|
|
50
|
+
longitudePerihelion: ar.longitudePerihelion,
|
|
51
|
+
ascendingNodeInvPlane: ar.ascendingNodeInvPlaneClosedForm, // the closed-form verified J2000 node — astro-reference since plan 11 step 2
|
|
52
|
+
invPlaneInclinationJ2000: ar.invPlaneInclinationJ2000,
|
|
53
|
+
orbitalEccentricityJ2000: ar.orbitalEccentricityJ2000,
|
|
54
|
+
axialTiltJ2000: ar.axialTiltJ2000,
|
|
55
|
+
obliquityMean,
|
|
56
|
+
perihelionEclipticYears: gArc ? 1296000 / gArc : undefined,
|
|
57
|
+
ascendingNodePeriod: sArc ? 1296000 / sArc : undefined,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { PLANET_KEYS, PUBLISHED_PLANET_RECORDS, massFraction };
|
|
62
|
+
}
|
|
63
|
+
/** @typedef {import('./delta-t.js').Ctx & ReturnType<typeof assemblePlanetRecords>} Ctx */
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The planets' spin channel — each planet's precession constant from its own torques on the chain's orbit.
|
|
3
|
+
*
|
|
4
|
+
* One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
|
|
5
|
+
*/
|
|
6
|
+
import { DEEP_MODES_ARTIFACT } from '../moon/deep-modes-artifact.cjs';
|
|
7
|
+
import { CHAIN_ARTIFACT } from '../planets/chain-artifact.js';
|
|
8
|
+
import { createPlanetSpinChannelFromArtifacts } from '../planets/spin-channel.cjs';
|
|
9
|
+
|
|
10
|
+
/** @param {import('./planet-records.js').Ctx} ctx @returns the bindings the later slices read */
|
|
11
|
+
export function assemblePlanetSpinChannel(ctx) {
|
|
12
|
+
const { C, GM_SUN, PLANET_KEYS, massFraction } = ctx;
|
|
13
|
+
// The planets' spin channel: the precession constant
|
|
14
|
+
// from each planet's OWN torques (astro-reference planetSpinPhysical) on the
|
|
15
|
+
// model's OWN orbit — the chain's J2000 a/e/plane and the deep ζ table —
|
|
16
|
+
// integrated as dŝ/dt = α(ŝ·n̂)(ŝ×n̂) from the IAU J2000 pole. ONE home:
|
|
17
|
+
// planets/spin-channel.cjs; built lazily per planet, pure in `year`.
|
|
18
|
+
/** @type {Map<string, ReturnType<typeof createPlanetSpinChannelFromArtifacts>>} */
|
|
19
|
+
const spinChannels = new Map();
|
|
20
|
+
/** @param {string} k */
|
|
21
|
+
const planetSpin = (k) => {
|
|
22
|
+
if (!PLANET_KEYS.includes(k)) throw new Error(`planets.spin: unknown planet '${k}'`);
|
|
23
|
+
let ch = spinChannels.get(k);
|
|
24
|
+
if (!ch) {
|
|
25
|
+
ch = createPlanetSpinChannelFromArtifacts({
|
|
26
|
+
key: k,
|
|
27
|
+
planetSpinPhysical: C.planetSpinPhysical,
|
|
28
|
+
chainAnchorElements: /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements,
|
|
29
|
+
planetZeta: /** @type {any} */ (DEEP_MODES_ARTIFACT).planetZeta,
|
|
30
|
+
massFractionOfSun: massFraction[k],
|
|
31
|
+
gmSunKm3S2: GM_SUN,
|
|
32
|
+
obliquityJ2000Deg: C.earthOrbital.obliquityJ2000_deg,
|
|
33
|
+
});
|
|
34
|
+
spinChannels.set(k, ch);
|
|
35
|
+
}
|
|
36
|
+
return ch;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return { planetSpin };
|
|
40
|
+
}
|
|
41
|
+
/** @typedef {import('./planet-records.js').Ctx & ReturnType<typeof assemblePlanetSpinChannel>} Ctx */
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The published periods of date — the seamed precession period, tropical year and sidereal year (one-family inside the window, the deep chain beyond).
|
|
3
|
+
*
|
|
4
|
+
* One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
|
|
5
|
+
*/
|
|
6
|
+
import { ONE_FAMILY_WINDOW_YEARS } from '../earth/year-lengths.cjs';
|
|
7
|
+
|
|
8
|
+
/** @param {import('./sun-and-eclipses.js').Ctx} ctx @returns the bindings the later slices read */
|
|
9
|
+
export function assemblePublishedPeriods(ctx) {
|
|
10
|
+
const { certifiedAxialPrecessionJ2000Years, deepLod, yearLengthsM, yearToTMa } = ctx;
|
|
11
|
+
// Plan 06 Phase 3 S2 — THE published of-date precession period and mean
|
|
12
|
+
// tropical year: the one-family route (B) inside the banked tiers
|
|
13
|
+
// (|Δyear| ≤ ONE_FAMILY_WINDOW_YEARS), the composed lunisolar period
|
|
14
|
+
// beyond (S5: on the derived J2000 anchor, so the two faces agree at
|
|
15
|
+
// 25,771.4 at J2000 and the deep tail is the same clock scaled; the
|
|
16
|
+
// tidal-chain year). Shared by `epoch` and `lunisolar`.
|
|
17
|
+
/** @param {number} year @returns {number} */
|
|
18
|
+
const publishedAxialPrecessionYearsAtYear = (year) => {
|
|
19
|
+
if (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS) return yearLengthsM.axialPrecessionYearsAtYear(year);
|
|
20
|
+
const p = deepLod.lunisolarPrecessionPeriodYearsAtAge(yearToTMa(year));
|
|
21
|
+
return p === null ? certifiedAxialPrecessionJ2000Years() : p;
|
|
22
|
+
};
|
|
23
|
+
/** @param {number} year @returns {number} */
|
|
24
|
+
const publishedTropicalYearSecondsAtYear = (year) => {
|
|
25
|
+
if (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS) return yearLengthsM.tropicalYearSecondsAtYear(year);
|
|
26
|
+
return deepLod.tropicalYearSecondsAtAge(yearToTMa(year));
|
|
27
|
+
};
|
|
28
|
+
/** The published sidereal year of date, SI seconds — the one-family λ̇
|
|
29
|
+
* channel inside the window, the mass-loss law beyond (plan 09: the
|
|
30
|
+
* `lengths` surface's days ride it). @param {number} year @returns {number} */
|
|
31
|
+
const publishedSiderealYearSecondsAtYear = (year) => {
|
|
32
|
+
if (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS) return yearLengthsM.siderealYearSecondsAtYear(year);
|
|
33
|
+
return deepLod.siderealYearSecondsAtAge(yearToTMa(year));
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return { publishedAxialPrecessionYearsAtYear, publishedSiderealYearSecondsAtYear, publishedTropicalYearSecondsAtYear };
|
|
37
|
+
}
|
|
38
|
+
/** @typedef {import('./sun-and-eclipses.js').Ctx & ReturnType<typeof assemblePublishedPeriods>} Ctx */
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The certified Sun and the eclipse tier — the mean longitude of date, the finders, the planetary completion, the besselian location tier.
|
|
3
|
+
*
|
|
4
|
+
* One slice of `assembleModel` (../model.js): receives the context the earlier slices built, returns the bindings the later ones read.
|
|
5
|
+
*/
|
|
6
|
+
import { ONE_FAMILY_WINDOW_YEARS } from '../earth/year-lengths.cjs';
|
|
7
|
+
import { createBesselian } from '../eclipse/besselian.cjs';
|
|
8
|
+
import { createEclipseFinders } from '../eclipse/finders.cjs';
|
|
9
|
+
import { computeCarrierRatesDegPerCy, createSunPlanetaryCompletion } from '../eclipse/sun-planetary-completion.cjs';
|
|
10
|
+
|
|
11
|
+
/** @param {import('./lunar-theory.js').Ctx} ctx @returns the bindings the later slices read */
|
|
12
|
+
export function assembleSunAndEclipses(ctx) {
|
|
13
|
+
const {
|
|
14
|
+
C, F, late, MASS_RATIO_EARTH_MOON, currentAUDistance, frameworkDeltaTSecondsAtJD, j2000JD, jdTTFromUT,
|
|
15
|
+
julianCenturyDays, meanSiderealYearDays, meanSolarYearDays, moonArgs, moonDistanceKm, moonSeries,
|
|
16
|
+
moonSiderealMonthInput, moonSynodicMonthDays, obliquityDeg, oneFamilyTropicalYearSeconds, sunEccentricityAt,
|
|
17
|
+
sunPerihelionDegAt, yearFromJD,
|
|
18
|
+
} = ctx;
|
|
19
|
+
// The certified Sun — assembled from the model's own laws, ZERO fitted Sun
|
|
20
|
+
// constants: e(t) and ϖ(t) are the eclipse Sun's mean elements of date
|
|
21
|
+
// (earth-scalars.js: the one-source series plus the derived mean offset);
|
|
22
|
+
// L(t) is the integral of the one-family tropical year (below). The
|
|
23
|
+
// finders, the derived planetary completion and the besselian location
|
|
24
|
+
// tier are wired on it; the sun-vs-horizons gate banks its modern-window
|
|
25
|
+
// residual, the eclipse audit the finders' canon statistics.
|
|
26
|
+
const sunL0Deg = C.earthOrbital.sunMeanLongitudeJ2000_deg;
|
|
27
|
+
const sunTropicalRateDegPerCy = 360 * julianCenturyDays / meanSolarYearDays;
|
|
28
|
+
const sunMeanLongitudeDegAt = (() => {
|
|
29
|
+
// The Sun's mean longitude of date is the INTEGRAL of the one-source
|
|
30
|
+
// tropical year: L(t) = L0 + 360·∫_{2000}^{t} dt′/T_trop(t′), T_trop the
|
|
31
|
+
// one-family tropical year of date in SI seconds (one-source-movement.js).
|
|
32
|
+
// MEASURED: it equals the IAU/Laskar drift expression to 0.1 s over
|
|
33
|
+
// −1000..+3000. The smooth year cannot carry the Earth–Mars–Jupiter long
|
|
34
|
+
// inequality (1783 yr, 6.3″); derived on the model's own N-body and
|
|
35
|
+
// shipped in the planetary completion, the JPL instrument reads 1.03″
|
|
36
|
+
// all-phase sd (Horizons ±3000 yr: modern-window mean +0.8″).
|
|
37
|
+
// NUMERICS (rate vs point value): a cumulative trapezoid table of
|
|
38
|
+
// cycles, yearly inside ±20,000 yr and per century beyond, grown on
|
|
39
|
+
// demand from 2000 in both directions and interpolated inside a cell —
|
|
40
|
+
// never a rate multiplied by a span. Beyond the one-family window the
|
|
41
|
+
// tidal-chain year continues the integrand (the scene overlay ends at
|
|
42
|
+
// 20,000 yr; the finders refuse out-of-domain epochs upstream).
|
|
43
|
+
const JULIAN_YEAR_S = 365.25 * 86400;
|
|
44
|
+
const tropSec = oneFamilyTropicalYearSeconds;
|
|
45
|
+
const FINE_SPAN = 20000, COARSE_STEP = 100;
|
|
46
|
+
/** cycles from 2000 to 2000 ± i·step (fwd/bwd tiers) */
|
|
47
|
+
/** @type {number[]} */ const fwdFine = [0];
|
|
48
|
+
/** @type {number[]} */ const bwdFine = [0];
|
|
49
|
+
/** @type {number[]} */ const fwdCoarse = [];
|
|
50
|
+
/** @type {number[]} */ const bwdCoarse = [];
|
|
51
|
+
/** UNSIGNED trapezoid cycles over the span between two years (Julian
|
|
52
|
+
* years → SI seconds over the one-family year); the caller applies the
|
|
53
|
+
* direction sign. @param {number} ya @param {number} yb @returns {number} */
|
|
54
|
+
const segAbs = (ya, yb) => 0.5 * (Math.abs(yb - ya) * JULIAN_YEAR_S) * (1 / tropSec(ya) + 1 / tropSec(yb));
|
|
55
|
+
/** signed cycles from 2000 to `year` @param {number} year @returns {number} */
|
|
56
|
+
const cyclesTo = (year) => {
|
|
57
|
+
const dy = year - 2000;
|
|
58
|
+
const s = dy >= 0 ? 1 : -1, a = Math.abs(dy);
|
|
59
|
+
// WARM THE SAMPLER TO THE TARGET FIRST (plan 06 R4, measured): the
|
|
60
|
+
// table below grows outward in 1-yr / 100-yr steps, and every step
|
|
61
|
+
// beyond ~1.6 Myr enlarged the one-family sampler's >2-Myr tier just
|
|
62
|
+
// past its span, rebuilding the whole deep integration about every 32
|
|
63
|
+
// steps — ~125 rebuilds and 22 s for the first deep-epoch call (the
|
|
64
|
+
// scene's first jump to −5 Myr), 0.4 ms once built. One read at the
|
|
65
|
+
// clamped target builds the tier at its final span; the grid stores
|
|
66
|
+
// are endpoint-independent, so every value is bit-identical.
|
|
67
|
+
tropSec(2000 + s * Math.min(a, ONE_FAMILY_WINDOW_YEARS));
|
|
68
|
+
const fine = s > 0 ? fwdFine : bwdFine;
|
|
69
|
+
const growFine = (/** @type {number} */ upto) => { while (fine.length <= upto) { const k = fine.length; fine.push(fine[k - 1] + s * segAbs(2000 + s * (k - 1), 2000 + s * k)); } };
|
|
70
|
+
if (a <= FINE_SPAN) {
|
|
71
|
+
const i = Math.floor(a), f = a - i;
|
|
72
|
+
growFine(i + 1);
|
|
73
|
+
return fine[i] + f * (fine[i + 1] - fine[i]);
|
|
74
|
+
}
|
|
75
|
+
growFine(FINE_SPAN);
|
|
76
|
+
const coarse = s > 0 ? fwdCoarse : bwdCoarse;
|
|
77
|
+
if (coarse.length === 0) coarse.push(fine[FINE_SPAN]);
|
|
78
|
+
const c = (a - FINE_SPAN) / COARSE_STEP, ci = Math.floor(c), cf = c - ci;
|
|
79
|
+
while (coarse.length <= ci + 1) { const k = coarse.length; const y0 = 2000 + s * (FINE_SPAN + (k - 1) * COARSE_STEP); coarse.push(coarse[k - 1] + s * segAbs(y0, y0 + s * COARSE_STEP)); }
|
|
80
|
+
return coarse[ci] + cf * (coarse[ci + 1] - coarse[ci]);
|
|
81
|
+
};
|
|
82
|
+
return /** @param {number} year @returns {number} */ (year) => sunL0Deg + 360 * cyclesTo(year);
|
|
83
|
+
})();
|
|
84
|
+
// The lunar arguments' Sun perihelion of date is the eclipse Sun's mean
|
|
85
|
+
// perihelion (`sunPerihelionDegAt`) read DIRECTLY, wrapped.
|
|
86
|
+
// Every consumer wraps the argument it enters (M = Lsun − ϖ − 180) or
|
|
87
|
+
// differences it over ±25 days (the rate anchors, wrapped to ±180°), so
|
|
88
|
+
// no continuous angle is needed. MEASURED: a 100-yr unwrapping ladder
|
|
89
|
+
// (two series reads per rung, 53,400 rungs at −5.34 Myr) cost the cold
|
|
90
|
+
// deep jump 1,062 s — 343× the in-table jump against the perf gate's 8× —
|
|
91
|
+
// for a value the consumers reduced mod 360 anyway.
|
|
92
|
+
|
|
93
|
+
// Eclipse finders — wired like the engine probe (tools/verify/
|
|
94
|
+
// eclipse-audit.js). The finder axis is JD(UT): the series wrapper applies
|
|
95
|
+
// UT→TT internally. Ground-track/umbra paths deliberately absent: the
|
|
96
|
+
// scene-umbra projection navigates the Tychosium-derived scaffold, which
|
|
97
|
+
// never enters this package (§2h).
|
|
98
|
+
const eclipseFinders = createEclipseFinders({
|
|
99
|
+
moonLonDegAt: /** @param {number} jd */ (jd) => moonSeries.truncatedLonDeg(jd),
|
|
100
|
+
moonBetaDegAt: /** @param {number} jd */ (jd) => moonSeries.truncatedBetaDeg(jd),
|
|
101
|
+
moonDistanceKmAt: /** @param {number} jd */ (jd) => moonSeries.truncatedDistanceKm(jd),
|
|
102
|
+
deltaTSecondsAt: frameworkDeltaTSecondsAtJD,
|
|
103
|
+
getSynodicMonthDays: () => moonSynodicMonthDays,
|
|
104
|
+
getSunDistanceKm: () => currentAUDistance,
|
|
105
|
+
frameworkSun: {
|
|
106
|
+
sunMeanLongitudeJ2000Deg: sunL0Deg,
|
|
107
|
+
tropicalRateDegPerCy: sunTropicalRateDegPerCy,
|
|
108
|
+
eccentricityAt: sunEccentricityAt,
|
|
109
|
+
perihelionLongitudeDegAt: sunPerihelionDegAt,
|
|
110
|
+
meanLongitudeDegAt: sunMeanLongitudeDegAt,
|
|
111
|
+
},
|
|
112
|
+
constants: {
|
|
113
|
+
rEarthMetres: (C.bodyDiametersKm.earth / 2) * 1000,
|
|
114
|
+
moonDiameterKm: C.bodyDiametersKm.moon,
|
|
115
|
+
sunDiameterKm: C.bodyDiametersKm.sun,
|
|
116
|
+
j2000JD,
|
|
117
|
+
julianCenturyDays,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// 20.3g — the solar-eclipse LOCATION tier: shadow geometry on the FULL
|
|
122
|
+
// series (sceneEvalAt — the truncated finder forms omit the fitted Lp
|
|
123
|
+
// anchor and the −2235·sin(Lp) β family, deliberate for finder
|
|
124
|
+
// statistics, required here) at the model's ABSOLUTE TT (deltaTStart +
|
|
125
|
+
// curve, the same convention every other ΔT consumer reads). Both axis
|
|
126
|
+
// conventions are measured against the NASA path-table centerlines —
|
|
127
|
+
// see eclipse/besselian.cjs.
|
|
128
|
+
// Derived Earth-around-EMB wobble for the Sun completion: a_M·μ/AU,
|
|
129
|
+
// μ = 1/(1+M_E/M_M) — 6.4399″ at current constants, tracks them live.
|
|
130
|
+
const embWobbleArcsec = (moonDistanceKm / (MASS_RATIO_EARTH_MOON + 1) / currentAUDistance)
|
|
131
|
+
* (648000 / Math.PI);
|
|
132
|
+
// FQ-5 N3 — the completion's carriers are FRAMEWORK-derived from the
|
|
133
|
+
// model's own planet period records (the Moon-elongation rate from the
|
|
134
|
+
// sidereal month/year identity). Plan 06 I2 — the carriers are SIDEREAL
|
|
135
|
+
// (measured): the records' solarYearInput are OF-DATE periods (their
|
|
136
|
+
// rates sit +1.39..1.62°/cy above the VSOP sidereal mean motions — the
|
|
137
|
+
// framework precession plus input rounding), and a perturbation argument
|
|
138
|
+
// is inertial (D'Alembert: only Σk = 0 arguments are frame-free), so on
|
|
139
|
+
// of-date carriers every Σk ≠ 0 argument drifted by Σk·ψ(t) — 42..52° at
|
|
140
|
+
// −3000 for the table's Σk = −1 terms (their ancient scatter against
|
|
141
|
+
// Horizons 6.4 → 4.4″ on sidereal carriers), and 6 % of frequency for the
|
|
142
|
+
// 1783-yr long inequality (1683 yr on the of-date carriers). Constant-free:
|
|
143
|
+
// the record rate minus the model's own J2000 precession p₀ =
|
|
144
|
+
// 360·36525·(1/T_trop − 1/T_sid) for the planets, the framework sidereal
|
|
145
|
+
// year for Earth; identical to the N3 carriers at J2000 (same phase
|
|
146
|
+
// anchors), ≤ 1.4°·|Σk| apart at the 200-yr extraction window's edges
|
|
147
|
+
// (the composed table reproduces the N3 one there: JPL 1900–2100
|
|
148
|
+
// all-phase sd unchanged to 0.01″). The parity gate's fingerprint mirrors
|
|
149
|
+
// this arithmetic (test/create-model-parity.test.mjs).
|
|
150
|
+
// PLAN 06 I3 — the carriers are the model's own banked J2000 sidereal mean
|
|
151
|
+
// motions (the embedded sun-completion artifact; ONE home,
|
|
152
|
+
// computeCarrierRatesDegPerCy — the former record-based arithmetic sat
|
|
153
|
+
// 4.1″/yr off for Venus, 1.7″/yr for Jupiter: 83° of the Venus–Earth
|
|
154
|
+
// argument at ±9000 yr), Earth the framework sidereal year, the Moon
|
|
155
|
+
// elongation from the sidereal month/year identity. The long-period rows
|
|
156
|
+
// ride the embedded e-vectors of date, and the lunar equation rides the
|
|
157
|
+
// framework's own Moon elongation of date (ṅ included) — the constant-rate
|
|
158
|
+
// carrier ran ~29° off it at ±9000 yr.
|
|
159
|
+
const carrierRatesDegPerCy = computeCarrierRatesDegPerCy({ meanSiderealYearDays, moonSiderealMonthDays: moonSiderealMonthInput });
|
|
160
|
+
const { sunPlanetaryCompletionDeg } = createSunPlanetaryCompletion({
|
|
161
|
+
embWobbleArcsec,
|
|
162
|
+
carrierRatesDegPerCy,
|
|
163
|
+
moonElongationDegAtT: /** @param {number} T */ (T) => moonArgs.argsAt(j2000JD + T * julianCenturyDays).D,
|
|
164
|
+
});
|
|
165
|
+
const besselian = createBesselian({
|
|
166
|
+
moonFullAtDaysTT: /** @param {number} dDaysTT */ (dDaysTT) => {
|
|
167
|
+
const ev = moonSeries.sceneEvalAt(dDaysTT);
|
|
168
|
+
return { lonDeg: ev.lonDeg, latDeg: ev.latDeg, distKm: ev.distKm };
|
|
169
|
+
},
|
|
170
|
+
sunLonDegAt: /** @param {number} jdUT */ (jdUT) => eclipseFinders.sunLonDegAt(jdUT),
|
|
171
|
+
sunCompletionDeg: sunPlanetaryCompletionDeg,
|
|
172
|
+
// plan 06 R3 item 1: the location tier on APPARENT places — the Sun's derived κ
|
|
173
|
+
// (the same helper the cardinal instants use), the Moon's relative light-time
|
|
174
|
+
// inside the besselian; the series-extension tail now lives in sceneEvalAt.
|
|
175
|
+
sunAberrationDegAt: /** @param {number} year */ (year) => late.sunAberrationArcsecAt(year) / 3600,
|
|
176
|
+
deltaTSecondsAt: /** @param {number} jd */ (jd) => (jdTTFromUT(jd) - jd) * 86400,
|
|
177
|
+
obliquityDegAt: obliquityDeg,
|
|
178
|
+
eccentricityAt: sunEccentricityAt,
|
|
179
|
+
perihelionLongitudeDegAt: sunPerihelionDegAt,
|
|
180
|
+
yearFromJD,
|
|
181
|
+
constants: {
|
|
182
|
+
j2000JD,
|
|
183
|
+
julianCenturyDays,
|
|
184
|
+
earthDiameterKm: C.bodyDiametersKm.earth,
|
|
185
|
+
moonDiameterKm: C.bodyDiametersKm.moon,
|
|
186
|
+
sunDiameterKm: C.bodyDiametersKm.sun,
|
|
187
|
+
sunDistanceKm: currentAUDistance,
|
|
188
|
+
earthFlatteningInverse: C.physicalConstants.earthFlatteningInverseWGS84,
|
|
189
|
+
ttBridgeSeconds: F.DT_ANCHOR.deltaTStartSeconds,
|
|
190
|
+
gmstMeanSiderealT0Deg: C.physicalConstants.gmstMeanSiderealT0Deg,
|
|
191
|
+
gmstMeanSiderealRateDegPerDay: C.physicalConstants.gmstMeanSiderealRateDegPerDay,
|
|
192
|
+
gmstMeanSiderealT2Deg: C.physicalConstants.gmstMeanSiderealT2Deg,
|
|
193
|
+
speedOfLightKmS: C.physicalConstants.speedOfLight,
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
return { besselian, eclipseFinders, sunL0Deg, sunMeanLongitudeDegAt, sunPlanetaryCompletionDeg, sunTropicalRateDegPerCy };
|
|
198
|
+
}
|
|
199
|
+
/** @typedef {import('./lunar-theory.js').Ctx & ReturnType<typeof assembleSunAndEclipses>} Ctx */
|