@essrt/physics 20.0.1 → 22.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 +4 -4
- package/package.json +4 -8
- package/src/cardinal/one-source-structure.cjs +3 -2
- package/src/chain-cycles/index.cjs +12 -30
- package/src/climate/l1-orbital.cjs +3 -1
- package/src/constants/coefficients.d.ts +17 -93
- package/src/constants/coefficients.js +7 -22
- package/src/constants/generated.d.ts +18 -156
- package/src/constants/generated.js +33 -168
- package/src/deltat/deep-time.cjs +36 -92
- package/src/deltat/recession-history.cjs +2 -1
- package/src/earth/deep-orbital-history.cjs +4 -4
- package/src/earth/frame-of-date.cjs +25 -1
- package/src/earth/precession-composed.cjs +3 -3
- package/src/earth/year-lengths.cjs +16 -1
- package/src/eclipse/sun-planetary-completion.cjs +11 -12
- package/src/index.js +28 -48
- package/src/layer0/derive-params.js +10 -5
- package/src/layer0/index.js +27 -73
- package/src/layer1/index.js +6 -37
- package/src/model.js +200 -225
- package/src/moon/arguments.cjs +57 -83
- package/src/moon/deep-modes-artifact.cjs +2 -2
- package/src/moon/month-chain.cjs +25 -59
- package/src/moon/series.cjs +14 -10
- package/src/phase/deep-calendar.cjs +35 -80
- package/src/planets/chain-artifact.js +2 -2
- package/src/planets/display-names.cjs +30 -0
- package/src/planets/geometry.cjs +79 -29
- package/src/planets/model.cjs +12 -11
- package/src/planets/spin-channel.cjs +46 -2
- package/types/chain-cycles/index.d.cts +9 -4
- package/types/climate/l1-orbital.d.cts +3 -1
- package/types/constants/coefficients.d.ts +17 -93
- package/types/constants/generated.d.ts +18 -156
- package/types/deltat/deep-time.d.cts +3 -16
- package/types/earth/frame-of-date.d.cts +9 -0
- package/types/earth/year-lengths.d.cts +12 -0
- package/types/eclipse/sun-planetary-completion.d.cts +0 -9
- package/types/index.d.ts +4 -13
- package/types/layer0/derive-params.d.ts +7 -4
- package/types/layer0/index.d.ts +6 -13
- package/types/layer1/index.d.ts +0 -2
- package/types/model.d.ts +8 -6
- package/types/moon/arguments.d.cts +23 -22
- package/types/moon/deep-modes-artifact.d.cts +3 -3
- package/types/moon/month-chain.d.cts +7 -16
- package/types/moon/series.d.cts +2 -4
- package/types/phase/deep-calendar.d.cts +14 -50
- package/types/planets/display-names.d.cts +2 -0
- package/types/planets/geometry.d.cts +36 -13
- package/types/planets/model.d.cts +7 -6
- package/types/planets/spin-channel.d.cts +4 -0
- package/src/cardinal/index.cjs +0 -324
- package/src/moon/ecc-channel.cjs +0 -141
- package/src/phase/index.cjs +0 -186
- package/src/planets/ecc-channel.cjs +0 -59
- package/src/sun/longitude-correction.cjs +0 -68
- package/types/cardinal/index.d.cts +0 -80
- package/types/moon/ecc-channel.d.cts +0 -30
- package/types/phase/index.d.cts +0 -38
- package/types/planets/ecc-channel.d.cts +0 -20
- package/types/sun/longitude-correction.d.cts +0 -41
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
export type PhaseMachinery = {
|
|
2
|
-
ensureTable: () => void;
|
|
3
|
-
cumulAtYear: (year: number) => (number | null);
|
|
4
|
-
yearAtCumul: (targetCumul: number) => (number | null);
|
|
5
|
-
grid: () => {
|
|
6
|
-
yearMin: number;
|
|
7
|
-
yearMax: number;
|
|
8
|
-
stepYears: number;
|
|
9
|
-
j2000Idx: number;
|
|
10
|
-
length: number;
|
|
11
|
-
};
|
|
12
|
-
};
|
|
13
1
|
export type DeepCalendar = {
|
|
14
2
|
/**
|
|
15
3
|
* SI-tropical-year label for a
|
|
@@ -23,10 +11,6 @@ export type DeepCalendar = {
|
|
|
23
11
|
* the exact inverse of jdToSIyear.
|
|
24
12
|
*/
|
|
25
13
|
siYearToJD: (ySI: number) => number;
|
|
26
|
-
/**
|
|
27
|
-
* inverse of the phase machinery's cumulAtYear — null outside the table.
|
|
28
|
-
*/
|
|
29
|
-
yearAtCumulIntegral: (targetCumul: number) => (number | null);
|
|
30
14
|
/**
|
|
31
15
|
* builds the ∫ daysPerYear dt table
|
|
32
16
|
* (idempotent; under the lattice α).
|
|
@@ -39,24 +23,12 @@ export type DeepCalendar = {
|
|
|
39
23
|
* year = startModelYear.
|
|
40
24
|
*/
|
|
41
25
|
yearToJD: (year: number) => (number | null);
|
|
42
|
-
/**
|
|
43
|
-
* calendar year of the k-th H-balanced event, k = 0 being `balancedYear`;
|
|
44
|
-
* negative k = past; null outside the table domain.
|
|
45
|
-
*/
|
|
46
|
-
balancedYearAtCycle: (cycleOffset: number) => (number | null);
|
|
47
26
|
/**
|
|
48
27
|
* diagnostic — the
|
|
49
28
|
* table length once built, null before.
|
|
50
29
|
*/
|
|
51
30
|
cumulDaysTableLength: () => (number | null);
|
|
52
31
|
};
|
|
53
|
-
/**
|
|
54
|
-
* @typedef {Object} PhaseMachinery
|
|
55
|
-
* @property {() => void} ensureTable
|
|
56
|
-
* @property {(year: number) => (number | null)} cumulAtYear
|
|
57
|
-
* @property {(targetCumul: number) => (number | null)} yearAtCumul
|
|
58
|
-
* @property {() => {yearMin: number, yearMax: number, stepYears: number, j2000Idx: number, length: number}} grid
|
|
59
|
-
*/
|
|
60
32
|
/**
|
|
61
33
|
* @typedef {Object} DeepCalendar
|
|
62
34
|
* @property {(jd: number) => number} jdToSIyear SI-tropical-year label for a
|
|
@@ -65,51 +37,43 @@ export type DeepCalendar = {
|
|
|
65
37
|
* with the runtime by construction; the round-trip bias Y_SI − Y is −11.0 yr
|
|
66
38
|
* at −302,635 and grows quadratically).
|
|
67
39
|
* @property {(ySI: number) => number} siYearToJD the exact inverse of jdToSIyear.
|
|
68
|
-
* @property {(targetCumul: number) => (number | null)} yearAtCumulIntegral
|
|
69
|
-
* inverse of the phase machinery's cumulAtYear — null outside the table.
|
|
70
40
|
* @property {() => void} ensureCumulDaysTable builds the ∫ daysPerYear dt table
|
|
71
41
|
* (idempotent; under the lattice α).
|
|
72
42
|
* @property {(year: number) => (number | null)} yearToJD calendar year → JD,
|
|
73
43
|
* integrating days-per-year from startModelYear; null outside the table
|
|
74
44
|
* domain or past the tidal-lock asymptote; startModelJD exactly at
|
|
75
45
|
* year = startModelYear.
|
|
76
|
-
* @property {(cycleOffset: number) => (number | null)} balancedYearAtCycle
|
|
77
|
-
* calendar year of the k-th H-balanced event, k = 0 being `balancedYear`;
|
|
78
|
-
* negative k = past; null outside the table domain.
|
|
79
46
|
* @property {() => (number | null)} cumulDaysTableLength diagnostic — the
|
|
80
47
|
* table length once built, null before.
|
|
81
48
|
*/
|
|
82
49
|
/**
|
|
83
50
|
* @param {{
|
|
84
|
-
* phase: () => PhaseMachinery,
|
|
85
51
|
* meanYearInDaysAtAgeMa: (tMa: number) => (number | null),
|
|
86
52
|
* withLatticeAlpha: (build: () => void) => void,
|
|
87
53
|
* startModelJD: number,
|
|
88
54
|
* startModelYear: number,
|
|
89
55
|
* startModelYearWithCorrection: number,
|
|
90
56
|
* siTropicalYearDays: number,
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* }} deps — `
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* `
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* anchor (≈ 2000.4977) — two different constants, do not unify.
|
|
57
|
+
* yearMin?: number,
|
|
58
|
+
* yearMax?: number,
|
|
59
|
+
* stepYears?: number,
|
|
60
|
+
* }} deps — `meanYearInDaysAtAgeMa` is the engine's days-per-year source
|
|
61
|
+
* (the LOD channel's `yearInDaysAtAge`); `withLatticeAlpha` the engine's
|
|
62
|
+
* R2 pin; `startModelYear` is the table zero (2000.5) and
|
|
63
|
+
* `startModelYearWithCorrection` the SI-axis anchor (≈ 2000.4977) — two
|
|
64
|
+
* different constants, do not unify; the grid (±500 Myr, 10-kyr cells)
|
|
65
|
+
* is the engines' deep-time grid — the table zero sits at the cell
|
|
66
|
+
* nearest `startModelYear`.
|
|
102
67
|
* @returns {DeepCalendar}
|
|
103
68
|
*/
|
|
104
|
-
export function createDeepCalendar({
|
|
105
|
-
phase: () => PhaseMachinery;
|
|
69
|
+
export function createDeepCalendar({ meanYearInDaysAtAgeMa, withLatticeAlpha, startModelJD, startModelYear, startModelYearWithCorrection, siTropicalYearDays, yearMin, yearMax, stepYears, }: {
|
|
106
70
|
meanYearInDaysAtAgeMa: (tMa: number) => (number | null);
|
|
107
71
|
withLatticeAlpha: (build: () => void) => void;
|
|
108
72
|
startModelJD: number;
|
|
109
73
|
startModelYear: number;
|
|
110
74
|
startModelYearWithCorrection: number;
|
|
111
75
|
siTropicalYearDays: number;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
76
|
+
yearMin?: number;
|
|
77
|
+
yearMax?: number;
|
|
78
|
+
stepYears?: number;
|
|
115
79
|
}): DeepCalendar;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type PlanetGeometryBody = {
|
|
2
2
|
key: string;
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
periodDays: number;
|
|
4
|
+
semiMajorAxisAu?: number;
|
|
5
5
|
orbitalEccentricityBase?: number;
|
|
6
6
|
longitudePerihelion?: number;
|
|
7
7
|
ascendingNode?: number;
|
|
@@ -11,8 +11,8 @@ export type PlanetGeometryBody = {
|
|
|
11
11
|
/**
|
|
12
12
|
* @typedef {{
|
|
13
13
|
* key: string,
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* periodDays: number,
|
|
15
|
+
* semiMajorAxisAu?: number,
|
|
16
16
|
* orbitalEccentricityBase?: number,
|
|
17
17
|
* longitudePerihelion?: number,
|
|
18
18
|
* ascendingNode?: number,
|
|
@@ -21,35 +21,35 @@ export type PlanetGeometryBody = {
|
|
|
21
21
|
* }} PlanetGeometryBody
|
|
22
22
|
*/
|
|
23
23
|
/**
|
|
24
|
-
* @param {PlanetGeometryBody} body
|
|
24
|
+
* @param {PlanetGeometryBody} body — `periodDays` the body's sidereal orbital
|
|
25
|
+
* period in SI days; `semiMajorAxisAu` the chain's J2000 a where a chain
|
|
26
|
+
* exists (else Kepler's a from the period, in the scene's solar-year units)
|
|
25
27
|
* @param {{
|
|
26
|
-
* holisticYears: number,
|
|
27
28
|
* meanSolarYearDays: number,
|
|
28
29
|
* currentAUDistanceKm: number,
|
|
29
30
|
* earthEccentricityJ2000: number,
|
|
30
31
|
* earthPerihelionLongitudeJ2000Deg: number,
|
|
31
|
-
* }} env — the engine's J2000 values
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* untouched by this layer).
|
|
32
|
+
* }} env — the engine's J2000 values; `meanSolarYearDays` is the scene's
|
|
33
|
+
* year unit (`pos` counts mean solar years), so `periodYears` is the period
|
|
34
|
+
* in that unit, the scaffold's `speed` = 2π/periodYears.
|
|
35
35
|
* @returns {{
|
|
36
|
-
*
|
|
36
|
+
* orbitDistance: number, periodYears: number, periodDays: number,
|
|
37
37
|
* realOrbitalEccentricity: (number | undefined),
|
|
38
38
|
* elipticOrbit: (number | undefined), perihelionDistance: (number | undefined),
|
|
39
39
|
* speedKmh: number, rotationPeriodHours: (number | undefined),
|
|
40
40
|
* eccentricityPerihelion: (number | undefined), lowestPoint: (number | undefined),
|
|
41
|
+
* ellipseFamily: (string | undefined),
|
|
41
42
|
* }}
|
|
42
43
|
*/
|
|
43
44
|
export function derivePlanetGeometry(body: PlanetGeometryBody, env: {
|
|
44
|
-
holisticYears: number;
|
|
45
45
|
meanSolarYearDays: number;
|
|
46
46
|
currentAUDistanceKm: number;
|
|
47
47
|
earthEccentricityJ2000: number;
|
|
48
48
|
earthPerihelionLongitudeJ2000Deg: number;
|
|
49
49
|
}): {
|
|
50
|
-
solarYearCount: number;
|
|
51
50
|
orbitDistance: number;
|
|
52
51
|
periodYears: number;
|
|
52
|
+
periodDays: number;
|
|
53
53
|
realOrbitalEccentricity: (number | undefined);
|
|
54
54
|
elipticOrbit: (number | undefined);
|
|
55
55
|
perihelionDistance: (number | undefined);
|
|
@@ -57,4 +57,27 @@ export function derivePlanetGeometry(body: PlanetGeometryBody, env: {
|
|
|
57
57
|
rotationPeriodHours: (number | undefined);
|
|
58
58
|
eccentricityPerihelion: (number | undefined);
|
|
59
59
|
lowestPoint: (number | undefined);
|
|
60
|
+
ellipseFamily: (string | undefined);
|
|
60
61
|
};
|
|
62
|
+
/**
|
|
63
|
+
* The scaffold's orbit inputs for one body — ONE home for the rule (plan 09
|
|
64
|
+
* step 7b): where the governed engine-D chain artifact has the body, the
|
|
65
|
+
* chain's J2000 semi-major axis and its fitted era-window mean motion (deg
|
|
66
|
+
* per Julian year → the period in SI days); otherwise the body's observed
|
|
67
|
+
* period input (the no-chain bodies Pluto, Halley, Eros, Ceres).
|
|
68
|
+
* @param {string} key
|
|
69
|
+
* @param {any} chainArtifact the CHAIN_ARTIFACT (or the governed JSON it embeds)
|
|
70
|
+
* @param {number} fallbackPeriodDays the observed period input, SI days
|
|
71
|
+
* @returns {{ periodDays: number, semiMajorAxisAu: (number | undefined) }}
|
|
72
|
+
*/
|
|
73
|
+
export function scaffoldOrbitInputs(key: string, chainArtifact: any, fallbackPeriodDays: number): {
|
|
74
|
+
periodDays: number;
|
|
75
|
+
semiMajorAxisAu: (number | undefined);
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Each body's scaffold ellipse family (the survey's measured taxonomy above).
|
|
79
|
+
* Pluto, Halley and Ceres take their body-name branches; a key absent here
|
|
80
|
+
* has no ellipse family (geometry-only).
|
|
81
|
+
* @type {Readonly<Record<string, string>>}
|
|
82
|
+
*/
|
|
83
|
+
export const SCAFFOLD_ELLIPSE_FAMILY: Readonly<Record<string, string>>;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
export type PlanetModelBody = {
|
|
2
2
|
/**
|
|
3
|
-
* -
|
|
3
|
+
* - the observed sidereal period input, SI days (the scaffold period of the no-chain bodies; the chain planets ride the artifact's elements)
|
|
4
4
|
*/
|
|
5
|
-
type?: string | undefined;
|
|
6
5
|
solarYearInput: number;
|
|
7
6
|
invPlaneInclinationJ2000?: number | undefined;
|
|
8
7
|
longitudePerihelion?: number | undefined;
|
|
@@ -26,7 +25,10 @@ export type PlanetModelBody = {
|
|
|
26
25
|
orbitDistanceOverride?: number | undefined;
|
|
27
26
|
};
|
|
28
27
|
export type PlanetModelEnv = {
|
|
29
|
-
|
|
28
|
+
/**
|
|
29
|
+
* - the governed engine-D chain artifact (CHAIN_ARTIFACT): the seven chain planets' scaffold a and period ride its J2000 elements (plan 09 step 7b)
|
|
30
|
+
*/
|
|
31
|
+
chainArtifact?: any;
|
|
30
32
|
meanSolarYearDays: number;
|
|
31
33
|
balancedYear: number;
|
|
32
34
|
systemResetN: number;
|
|
@@ -41,8 +43,7 @@ export type PlanetModelRecord = {
|
|
|
41
43
|
};
|
|
42
44
|
/**
|
|
43
45
|
* @typedef {Object} PlanetModelBody
|
|
44
|
-
* @property {
|
|
45
|
-
* @property {number} solarYearInput
|
|
46
|
+
* @property {number} solarYearInput - the observed sidereal period input, SI days (the scaffold period of the no-chain bodies; the chain planets ride the artifact's elements)
|
|
46
47
|
* @property {number} [invPlaneInclinationJ2000]
|
|
47
48
|
* @property {number} [longitudePerihelion]
|
|
48
49
|
* @property {number} [wobblePeriodYears] - carriers: the chain's g-mode beat (input since Phase 7 commit 2)
|
|
@@ -57,7 +58,7 @@ export type PlanetModelRecord = {
|
|
|
57
58
|
*/
|
|
58
59
|
/**
|
|
59
60
|
* @typedef {Object} PlanetModelEnv
|
|
60
|
-
* @property {
|
|
61
|
+
* @property {any} [chainArtifact] - the governed engine-D chain artifact (CHAIN_ARTIFACT): the seven chain planets' scaffold a and period ride its J2000 elements (plan 09 step 7b)
|
|
61
62
|
* @property {number} meanSolarYearDays
|
|
62
63
|
* @property {number} balancedYear
|
|
63
64
|
* @property {number} systemResetN
|
|
@@ -99,6 +99,8 @@ export function createPlanetSpinChannel(deps: {
|
|
|
99
99
|
meanDeg: number;
|
|
100
100
|
maxDeg: number;
|
|
101
101
|
} | null;
|
|
102
|
+
spinAxisEclipticJ2000AtYear: (year: number) => number[] | null;
|
|
103
|
+
orbitNormalEclipticJ2000AtYear: (year: number) => number[];
|
|
102
104
|
stepYr: number;
|
|
103
105
|
maxSpanYr: 10000000;
|
|
104
106
|
}>;
|
|
@@ -152,6 +154,8 @@ export function createPlanetSpinChannelFromArtifacts(deps: {
|
|
|
152
154
|
meanDeg: number;
|
|
153
155
|
maxDeg: number;
|
|
154
156
|
} | null;
|
|
157
|
+
spinAxisEclipticJ2000AtYear: (year: number) => number[] | null;
|
|
158
|
+
orbitNormalEclipticJ2000AtYear: (year: number) => number[];
|
|
155
159
|
stepYr: number;
|
|
156
160
|
maxSpanYr: 10000000;
|
|
157
161
|
}>;
|
package/src/cardinal/index.cjs
DELETED
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cardinal-point model — THE shared implementation (Phase 7.2).
|
|
3
|
-
*
|
|
4
|
-
* The §10 derived form and everything on it, extracted verbatim from
|
|
5
|
-
* tools/lib/orbital-engine.js (which the browser mirrored function-for-
|
|
6
|
-
* function — this file replaces both copies):
|
|
7
|
-
*
|
|
8
|
-
* JD_X(Y) = anchor_X + ΣT_trop(Y) + δ_X(Y) − δ_X(2000)
|
|
9
|
-
* ΣT_trop = lincoef·(Y−2000) + driftTerm(Y) + Ih(Y)
|
|
10
|
-
* δ_X = sinusoids + equation-of-centre orders (e(t)ⁿ·sin(nM), e(t)
|
|
11
|
-
* the LAW OF COSINES) + §10g quadrature-locked joint sidebands
|
|
12
|
-
* T_X(Y) = the EXACT term-by-term derivative of JD_X
|
|
13
|
-
*
|
|
14
|
-
* LOAD-BEARING, do not "improve":
|
|
15
|
-
* - lincoef/h0/h1 come from the fit and are used VERBATIM — recomputing
|
|
16
|
-
* lincoef from the 1-year anchor injects a −12,276 s ramp; holding H
|
|
17
|
-
* constant inside Ih costs up to 5.2 s (§10c/§10e-quinquies).
|
|
18
|
-
* - The drift term is Simpson on 2000-yr NODE SPACING (not a fixed node
|
|
19
|
-
* count: that was exact in-window and −33,758 s at −380 Ma) plus the
|
|
20
|
-
* Euler–Maclaurin endpoint term (omitting it costs 6–13 s).
|
|
21
|
-
* - The equation-of-centre terms are {order,sin,cos}, NOT sinusoids — read
|
|
22
|
-
* as H/16+H/32 harmonics they are wrong by the whole ~1.78 d braid.
|
|
23
|
-
* - §10g joint sidebands are SHARED across the four points with phase
|
|
24
|
-
* order·λ_X − 2π·div·c — COUNTER-rotating. The minus sign is load-bearing:
|
|
25
|
-
* the co-rotating sense captures NOTHING (measured — the sign experiment,
|
|
26
|
-
* doc 99 "the braid law").
|
|
27
|
-
* - The year-length derivative keeps ONE deliberate divergence from the
|
|
28
|
-
* exact derivative: its drift part uses the real-LOD convention
|
|
29
|
-
* (`meanYearRealLodDays`) — the tweakpane's epoch-local days ("~400 days
|
|
30
|
-
* at the Devonian") — where the JD form integrates the SI form. Equal at
|
|
31
|
-
* J2000 up to the known 118 ms fit-basis gap.
|
|
32
|
-
*
|
|
33
|
-
* The `cyclesBetween` the caller injects is the ENGINE'S OWN function, so
|
|
34
|
-
* each engine's deep-time/snapshot toggle semantics are preserved exactly;
|
|
35
|
-
* under deep time both engines now route it through @essrt/physics/phase.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
'use strict';
|
|
39
|
-
|
|
40
|
-
/** @type {Record<string, number>} */
|
|
41
|
-
const JOINT_LAMBDA = { SS: 0, AE: Math.PI / 2, WS: Math.PI, VE: 1.5 * Math.PI };
|
|
42
|
-
|
|
43
|
-
const DRIFT_NODE_SPACING_YEARS = 2000;
|
|
44
|
-
const DRIFT_SIMPSON_N_MIN = 64;
|
|
45
|
-
|
|
46
|
-
/** @typedef {{ lincoef: number, h0: number, h1: number }} DerivedCoefs */
|
|
47
|
-
/** @typedef {{ order: number, sin: number, cos: number }} EccTerm */
|
|
48
|
-
/** @typedef {{ order: number, div: number, sin: number, cos: number }} JointTerm */
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* @param {{
|
|
52
|
-
* isDeepTime: () => boolean,
|
|
53
|
-
* constants: {
|
|
54
|
-
* anchors: Record<string, number>,
|
|
55
|
-
* harmonics: Record<string, Array<[number, number, number]>>,
|
|
56
|
-
* eccTerms: (Record<string, EccTerm[]> | null),
|
|
57
|
-
* jointTerms: ({ terms: JointTerm[] } | null),
|
|
58
|
-
* derived: (DerivedCoefs | null),
|
|
59
|
-
* tropicalHarmonics: Array<[number, number, number]>,
|
|
60
|
-
* balancedYear: number,
|
|
61
|
-
* meanSolarYearDays: number,
|
|
62
|
-
* hJ2000: number,
|
|
63
|
-
* tiltMeanDeg: number,
|
|
64
|
-
* raAngleDeg: number,
|
|
65
|
-
* inclAmplitudeDeg: number,
|
|
66
|
-
* },
|
|
67
|
-
* fns: {
|
|
68
|
-
* cyclesBetween: (yearA: number, yearB: number, divisorN: number) => (number | null),
|
|
69
|
-
* analyticTropicalDays: (year: number) => (number | null),
|
|
70
|
-
* meanHAtAgeMa: (tMa: number) => (number | null),
|
|
71
|
-
* meanYearRealLodDays: (tMa: number) => (number | null),
|
|
72
|
-
* eccentricityAt: (year: number) => number,
|
|
73
|
-
* eccentricityRateAt: (year: number) => number,
|
|
74
|
-
* },
|
|
75
|
-
* }} deps
|
|
76
|
-
*/
|
|
77
|
-
function createCardinalModel({ isDeepTime, constants, fns }) {
|
|
78
|
-
const {
|
|
79
|
-
anchors, // { SS, WS, VE, AE } — J2000 event JDs
|
|
80
|
-
harmonics, // { type: [[div, sin, cos], …] } — δ sinusoids
|
|
81
|
-
eccTerms, // { type: [{order, sin, cos}, …] } | null
|
|
82
|
-
jointTerms, // { terms: [{order, div, sin, cos}, …] } | null
|
|
83
|
-
derived, // { lincoef, h0, h1 } — fit-calibrated, VERBATIM
|
|
84
|
-
tropicalHarmonics, // TROPICAL_YEAR_HARMONICS [[div, sin, cos], …]
|
|
85
|
-
balancedYear,
|
|
86
|
-
meanSolarYearDays,
|
|
87
|
-
hJ2000,
|
|
88
|
-
tiltMeanDeg, // for RA
|
|
89
|
-
raAngleDeg,
|
|
90
|
-
inclAmplitudeDeg,
|
|
91
|
-
} = constants;
|
|
92
|
-
const {
|
|
93
|
-
cyclesBetween, // (yearA, yearB, divisorN) => cycles | null
|
|
94
|
-
analyticTropicalDays, // (year) => SI days | null — the drift integrand base
|
|
95
|
-
meanHAtAgeMa, // (t_Ma) => H | null — the derivative's dc/dY
|
|
96
|
-
meanYearRealLodDays, // (t_Ma) => days | null — real-LOD drift convention
|
|
97
|
-
eccentricityAt, // (year) => e(t) — the model's ONE eccentricity law (H/3 vector sum, plan IP-eccentricity-unification)
|
|
98
|
-
eccentricityRateAt, // (year) => de/dyear of the same law — the braid's EoC derivative rides it
|
|
99
|
-
} = fns;
|
|
100
|
-
|
|
101
|
-
/** @param {number} year */
|
|
102
|
-
const cycleOf = (year) => {
|
|
103
|
-
const c = cyclesBetween(balancedYear, year, 1);
|
|
104
|
-
return c === null ? 0 : c;
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
/** @param {number} year */
|
|
108
|
-
const driftIntegrand = (year) => {
|
|
109
|
-
const a = analyticTropicalDays(year);
|
|
110
|
-
return a === null ? 0 : (a - meanSolarYearDays);
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
/** @param {number} year */
|
|
114
|
-
function driftTerm(year) {
|
|
115
|
-
const span = year - 2000;
|
|
116
|
-
if (span === 0) return 0;
|
|
117
|
-
let n = Math.ceil(Math.abs(span) / DRIFT_NODE_SPACING_YEARS);
|
|
118
|
-
if (n % 2) n++;
|
|
119
|
-
if (n < DRIFT_SIMPSON_N_MIN) n = DRIFT_SIMPSON_N_MIN;
|
|
120
|
-
const h = span / n;
|
|
121
|
-
const f0 = driftIntegrand(2000), fN = driftIntegrand(year);
|
|
122
|
-
let acc = f0 + fN;
|
|
123
|
-
for (let i = 1; i < n; i++) acc += driftIntegrand(2000 + i * h) * ((i % 2) ? 4 : 2);
|
|
124
|
-
return acc * h / 3 - (fN - f0) / 2;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/** Step 6d's self-corrected tropical harmonic series, as a length deviation.
|
|
128
|
-
* @param {number} year */
|
|
129
|
-
function tropHarmonicsAt(year) {
|
|
130
|
-
const c = cycleOf(year), c0 = cycleOf(2000);
|
|
131
|
-
let s = 0;
|
|
132
|
-
for (const [div, sinC, cosC] of tropicalHarmonics) {
|
|
133
|
-
const th = 2 * Math.PI * div * c, th0 = 2 * Math.PI * div * c0;
|
|
134
|
-
s += sinC * (Math.sin(th) - Math.sin(th0)) + cosC * (Math.cos(th) - Math.cos(th0));
|
|
135
|
-
}
|
|
136
|
-
return s;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Ih(Y) = Σ_{2000→Y} of the tropical harmonics, closed form. H stays INSIDE
|
|
140
|
-
// the integral (h0 + h1·c) — it moves 27.5 yr across the window and
|
|
141
|
-
// multiplies the amplitude. Deep-time only, so `derived` is present here.
|
|
142
|
-
/** @param {number} year */
|
|
143
|
-
function integratedTropHarmonics(year) {
|
|
144
|
-
const D = /** @type {DerivedCoefs} */ (derived);
|
|
145
|
-
const cY = cycleOf(year), c0 = cycleOf(2000);
|
|
146
|
-
let tot = 0, k0 = 0;
|
|
147
|
-
for (const [div, sinC, cosC] of tropicalHarmonics) {
|
|
148
|
-
const k = 2 * Math.PI * div;
|
|
149
|
-
/** @param {number} c */
|
|
150
|
-
const F = (c) => {
|
|
151
|
-
const sn = Math.sin(k * c), cs = Math.cos(k * c), Hc = D.h0 + D.h1 * c;
|
|
152
|
-
return [-Hc * cs / k + D.h1 * sn / (k * k), Hc * sn / k + D.h1 * cs / (k * k)];
|
|
153
|
-
};
|
|
154
|
-
const a = F(c0), b = F(cY);
|
|
155
|
-
tot += sinC * (b[0] - a[0]) + cosC * (b[1] - a[1]);
|
|
156
|
-
k0 += sinC * Math.sin(k * c0) + cosC * Math.cos(k * c0);
|
|
157
|
-
}
|
|
158
|
-
return tot - k0 * (year - 2000)
|
|
159
|
-
- (tropHarmonicsAt(year) - tropHarmonicsAt(2000)) / 2;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/** @param {number} year */
|
|
163
|
-
function sigmaTropical(year) {
|
|
164
|
-
const D = /** @type {DerivedCoefs} */ (derived);
|
|
165
|
-
return D.lincoef * (year - 2000) + driftTerm(year) + integratedTropHarmonics(year);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// δ_X(2000) — the self-correction that pins JD(2000) to the anchor exactly
|
|
169
|
-
// (sinusoids + ecc orders; the §10g joint terms self-correct inline).
|
|
170
|
-
// Computed lazily so the phase table builds on the caller's schedule.
|
|
171
|
-
/** @type {Record<string, number> | null} */
|
|
172
|
-
let _selfCorr = null;
|
|
173
|
-
function selfCorr() {
|
|
174
|
-
if (_selfCorr !== null) return _selfCorr;
|
|
175
|
-
_selfCorr = {};
|
|
176
|
-
const c2000 = cycleOf(2000);
|
|
177
|
-
for (const type of ['SS', 'WS', 'VE', 'AE']) {
|
|
178
|
-
let h2000 = 0;
|
|
179
|
-
for (const [div, sinC, cosC] of harmonics[type]) {
|
|
180
|
-
const th0 = 2 * Math.PI * div * c2000;
|
|
181
|
-
h2000 += sinC * Math.sin(th0) + cosC * Math.cos(th0);
|
|
182
|
-
}
|
|
183
|
-
const ecc = eccTerms && eccTerms[type];
|
|
184
|
-
if (ecc) {
|
|
185
|
-
const e0 = eccentricityAt(2000);
|
|
186
|
-
const th0 = 2 * Math.PI * 16 * c2000;
|
|
187
|
-
for (const t of ecc) {
|
|
188
|
-
const eN0 = Math.pow(e0, t.order);
|
|
189
|
-
h2000 += t.sin * eN0 * Math.sin(t.order * th0) + t.cos * eN0 * Math.cos(t.order * th0);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
_selfCorr[type] = h2000;
|
|
193
|
-
}
|
|
194
|
-
return _selfCorr;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/** @param {number} year @param {string} [type] */
|
|
198
|
-
function computeSolsticeJD(year, type) {
|
|
199
|
-
const cp = type || 'SS';
|
|
200
|
-
const anchor = anchors[cp];
|
|
201
|
-
const deep = isDeepTime();
|
|
202
|
-
|
|
203
|
-
let jd = deep
|
|
204
|
-
? anchor + sigmaTropical(year)
|
|
205
|
-
: anchor + meanSolarYearDays * (year - 2000);
|
|
206
|
-
|
|
207
|
-
const cY = cycleOf(year);
|
|
208
|
-
for (const [div, sinC, cosC] of harmonics[cp]) {
|
|
209
|
-
jd += sinC * Math.sin(2 * Math.PI * div * cY) + cosC * Math.cos(2 * Math.PI * div * cY);
|
|
210
|
-
}
|
|
211
|
-
const ecc = eccTerms && eccTerms[cp];
|
|
212
|
-
if (ecc) {
|
|
213
|
-
const e = eccentricityAt(year);
|
|
214
|
-
const th = 2 * Math.PI * 16 * cY;
|
|
215
|
-
for (const t of ecc) {
|
|
216
|
-
const eN = Math.pow(e, t.order);
|
|
217
|
-
jd += t.sin * eN * Math.sin(t.order * th) + t.cos * eN * Math.cos(t.order * th);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
if (jointTerms) {
|
|
221
|
-
const lam = JOINT_LAMBDA[cp];
|
|
222
|
-
const c2000 = cycleOf(2000);
|
|
223
|
-
for (const t of jointTerms.terms) {
|
|
224
|
-
const th = t.order * lam - 2 * Math.PI * t.div * cY;
|
|
225
|
-
const th0 = t.order * lam - 2 * Math.PI * t.div * c2000;
|
|
226
|
-
jd += t.sin * (Math.sin(th) - Math.sin(th0)) + t.cos * (Math.cos(th) - Math.cos(th0));
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
jd -= selfCorr()[cp];
|
|
230
|
-
return jd;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/** The EXACT term-by-term derivative of computeSolsticeJD — except the
|
|
234
|
-
* drift part, which deliberately stays in the real-LOD convention (see
|
|
235
|
-
* the file header). Neglected: the drift Euler–Maclaurin half-sample
|
|
236
|
-
* term's own derivative (f′/2, sub-µs).
|
|
237
|
-
* @param {number} year @param {string} [type] */
|
|
238
|
-
function computeSolsticeYearLength(year, type) {
|
|
239
|
-
const cp = type || 'SS';
|
|
240
|
-
const deep = isDeepTime();
|
|
241
|
-
const t_Ma = (2000 - year) / 1e6;
|
|
242
|
-
|
|
243
|
-
const H_at = deep ? (meanHAtAgeMa(t_Ma) ?? hJ2000) : hJ2000;
|
|
244
|
-
const dcdY = 1 / H_at;
|
|
245
|
-
const cY = cycleOf(year);
|
|
246
|
-
|
|
247
|
-
let length;
|
|
248
|
-
if (deep && derived) {
|
|
249
|
-
length = derived.lincoef;
|
|
250
|
-
const mSY_at = meanYearRealLodDays(t_Ma);
|
|
251
|
-
if (mSY_at !== null) length += (mSY_at - meanSolarYearDays);
|
|
252
|
-
length += tropHarmonicsAt(year);
|
|
253
|
-
let dTrop = 0;
|
|
254
|
-
for (const [div, sinC, cosC] of tropicalHarmonics) {
|
|
255
|
-
const k = 2 * Math.PI * div, th = k * cY;
|
|
256
|
-
dTrop += k * dcdY * (sinC * Math.cos(th) - cosC * Math.sin(th));
|
|
257
|
-
}
|
|
258
|
-
length -= dTrop / 2;
|
|
259
|
-
} else {
|
|
260
|
-
length = meanSolarYearDays;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
for (const [div, sinC, cosC] of harmonics[cp]) {
|
|
264
|
-
const k = 2 * Math.PI * div, th = k * cY;
|
|
265
|
-
length += k * dcdY * (sinC * Math.cos(th) - cosC * Math.sin(th));
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const ecc = eccTerms && eccTerms[cp];
|
|
269
|
-
if (ecc) {
|
|
270
|
-
const th16 = 2 * Math.PI * 16 * cY;
|
|
271
|
-
const thp = 2 * Math.PI * 16 * dcdY;
|
|
272
|
-
const e = eccentricityAt(year);
|
|
273
|
-
// de/dyear from the one law (formerly the H/16 law-of-cosines derivative).
|
|
274
|
-
// The braid's own phase θ16 stays: it is the perihelion-OF-DATE phase the
|
|
275
|
-
// equation of centre is seen through — an H/16 quantity by nature.
|
|
276
|
-
const de = eccentricityRateAt(year);
|
|
277
|
-
for (const t of ecc) {
|
|
278
|
-
const n = t.order, nth = n * th16;
|
|
279
|
-
const eN = Math.pow(e, n), eN1 = Math.pow(e, n - 1);
|
|
280
|
-
length += t.sin * (n * eN1 * de * Math.sin(nth) + eN * n * thp * Math.cos(nth))
|
|
281
|
-
+ t.cos * (n * eN1 * de * Math.cos(nth) - eN * n * thp * Math.sin(nth));
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (jointTerms) {
|
|
286
|
-
const lam = JOINT_LAMBDA[cp];
|
|
287
|
-
for (const t of jointTerms.terms) {
|
|
288
|
-
const k = 2 * Math.PI * t.div;
|
|
289
|
-
const th = t.order * lam - k * cY;
|
|
290
|
-
length += k * dcdY * (-t.sin * Math.cos(th) + t.cos * Math.sin(th));
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
return length;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/** RA where a cardinal point occurs — fully derived, zero fitted constants.
|
|
297
|
-
* INTEGRATED phase: the formula describes where the SCENE puts the point,
|
|
298
|
-
* and the scene's H/3 and H/8 objects rotate on integrated phase.
|
|
299
|
-
* @param {number} year @param {string} [type] */
|
|
300
|
-
function computeSolsticeRA(year, type) {
|
|
301
|
-
const sinE = Math.sin(tiltMeanDeg * Math.PI / 180);
|
|
302
|
-
const baseRA = /** @type {Record<string, number>} */ ({ SS: 90, WS: 270, VE: 0, AE: 180 })[type || 'SS'];
|
|
303
|
-
const raMean = baseRA - raAngleDeg / sinE;
|
|
304
|
-
const amp = inclAmplitudeDeg / sinE;
|
|
305
|
-
const cY = cycleOf(year);
|
|
306
|
-
const phase3 = 2 * Math.PI * 3 * cY;
|
|
307
|
-
const phase8 = 2 * Math.PI * 8 * cY;
|
|
308
|
-
return raMean + amp * (-Math.sin(phase3) + Math.sin(phase8));
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/** Tropical year as the mean of the four cardinal intervals — the
|
|
312
|
-
* physically correct definition (Σδ_X ≡ 0 makes the braid cancel).
|
|
313
|
-
* @param {number} year */
|
|
314
|
-
function computeTropicalYearLength(year) {
|
|
315
|
-
return (computeSolsticeYearLength(year, 'SS') +
|
|
316
|
-
computeSolsticeYearLength(year, 'WS') +
|
|
317
|
-
computeSolsticeYearLength(year, 'VE') +
|
|
318
|
-
computeSolsticeYearLength(year, 'AE')) / 4;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return { computeSolsticeJD, computeSolsticeYearLength, computeSolsticeRA, computeTropicalYearLength };
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
module.exports = { createCardinalModel, JOINT_LAMBDA };
|