@essrt/physics 13.0.0 → 14.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/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@essrt/physics",
3
- "version": "13.0.0",
3
+ "version": "14.0.0",
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": "v17.0",
6
+ "modelVersion": "v18.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",
@@ -50,10 +50,7 @@
50
50
  "./moon/series-extension": { "types": "./types/moon/series-extension.d.cts", "default": "./src/moon/series-extension.cjs" },
51
51
  "./moon/apparent": { "types": "./types/moon/apparent.d.cts", "default": "./src/moon/apparent.cjs" },
52
52
  "./planets/geometry": { "types": "./types/planets/geometry.d.cts", "default": "./src/planets/geometry.cjs" },
53
- "./planets/fibonacci-laws": { "types": "./types/planets/fibonacci-laws.d.cts", "default": "./src/planets/fibonacci-laws.cjs" },
54
53
  "./planets/ecc-channel": { "types": "./types/planets/ecc-channel.d.cts", "default": "./src/planets/ecc-channel.cjs" },
55
- "./planets/orientation": { "types": "./types/planets/orientation.d.cts", "default": "./src/planets/orientation.cjs" },
56
- "./planets/asc-node-integrator": { "types": "./types/planets/asc-node-integrator.d.cts", "default": "./src/planets/asc-node-integrator.cjs" },
57
54
  "./planets/orbit-chain": { "types": "./types/planets/orbit-chain.d.cts", "default": "./src/planets/orbit-chain.cjs" },
58
55
  "./planets/keplerian-chain": { "types": "./types/planets/keplerian-chain.d.cts", "default": "./src/planets/keplerian-chain.cjs" },
59
56
  "./planets/spin-channel": { "types": "./types/planets/spin-channel.d.cts", "default": "./src/planets/spin-channel.cjs" },
@@ -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 = "v17.0";
39
+ export const MODEL_VERSION = "v18.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";
package/src/index.js CHANGED
@@ -122,12 +122,13 @@ export { createMoonApparent } from './moon/apparent.cjs';
122
122
  // sample) and the offset-circle wheel-angle solver: the scene twins' one placement.
123
123
  export { computeEarthFrameOfDate, solveWheelAngleForLongitude } from './earth/frame-of-date.cjs';
124
124
  // Phase 8.3 — the planet machinery, extracted by LAW over body records
125
- // (survey order: geometry → Fibonacci laws → channels → chains).
125
+ // (survey order: geometry → channels → chains). The ψ/K amplitude laws
126
+ // (planets/fibonacci-laws), the record-based orientation forms
127
+ // (planets/orientation) and the ascending-node integrator
128
+ // (planets/asc-node-integrator) are DELETED at plan 07 R5/R6: the planets'
129
+ // elements of date have one home, the N-body chain; docs/retired-record.md.
126
130
  export { derivePlanetGeometry } from './planets/geometry.cjs';
127
- export * as planetFibonacciLaws from './planets/fibonacci-laws.cjs';
128
131
  export { eccentricityFromCycles, computeEccentricityIntegrated } from './planets/ecc-channel.cjs';
129
- export * as planetOrientation from './planets/orientation.cjs';
130
- export { integrateAscendingNode } from './planets/asc-node-integrator.cjs';
131
132
  export * as planetOrbitChain from './planets/orbit-chain.cjs';
132
133
  // P5/K4.6c — the engine-D Keplerian chain (pure evaluator + the embedded
133
134
  // governed artifact; the browser flag path consumes exactly these).
package/src/model.js CHANGED
@@ -18,7 +18,6 @@
18
18
  * which itself mirrors tools/lib/constants.js §9.
19
19
  */
20
20
  import { deriveEpochParams } from './layer0/derive-params.js';
21
- import * as FL from './planets/fibonacci-laws.cjs';
22
21
  import { buildPlanetChainsFromArtifactData, computePlanetElementsAtYear } from './planets/keplerian-chain.cjs';
23
22
  import { createSecularSeriesOverride } from './planets/secular-series.cjs';
24
23
  import { computeEquatorNodeOriginSFrameDeg, convertNodeSFrameToEquatorOriginDeg } from './planets/inv-plane-frame.cjs';
@@ -97,14 +96,12 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
97
96
  const meanAnomalisticYearDays = (meanSolarYearDays * (H / 16)) / (H / 16 - 1);
98
97
  const meanTropicalYearJ2000Seconds = meanSolarYearDays * meanLengthOfDay;
99
98
 
100
- const earthInclAmplitude = C.earth.earthInvPlaneInclinationAmplitude;
101
- // Plan 07 R6: `earthtiltMean` and `eccentricityAmplitude` were read here
102
- // ONLY to invert the K constant from Earth's calibration. With the K law
103
- // retired nothing in this module reads either — they remain on C.earth for
104
- // the Earth-side devices (R9) and the registry.
105
- const earthInclMean = C.earthOrbital.earthInclinationJ2000_deg
106
- - earthInclAmplitude * Math.cos(((C.earthOrbital.earthPerihelionLongitudeJ2000
107
- - C.earthOrbital.earthInclinationCycleAnchor) * Math.PI) / 180);
99
+ // Plan 07 R5/R6/R9: `earthtiltMean`, `earthInvPlaneInclinationAmplitude`
100
+ // and `eccentricityAmplitude` were read here only to invert the K and ψ
101
+ // constants from Earth's calibration and to build the H/3 inclination
102
+ // device. With both laws retired nothing in this module reads them — they
103
+ // remain on C.earth as the K device's J2000 pose inputs (the scene's wheel
104
+ // geometry at the chain anchor epoch) and for the registry's record keys.
108
105
  const solsticeObliquityMean = F.SOLSTICE_OBLIQUITY_MEAN_FITTED;
109
106
 
110
107
  const G_CONSTANT = C.physicalConstants.G_CONSTANT;
@@ -429,14 +426,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
429
426
  /** de/dyear of the Sun's e — the cardinal braid's equation-of-centre
430
427
  * derivative rides it (±0.5-yr central difference; the constant offset
431
428
  * cancels). @param {number} year @returns {number} */
432
- /** @param {number} year @returns {number} */
433
- const inclinationDeg = (year) => earthInclMean
434
- - earthInclAmplitude * Math.cos(phaseRadians(balancedYear, year, 3));
435
- /** @param {number} year @returns {number} */
436
- const ascendingNodeDeg = (year) => {
437
- const period = -H / 5;
438
- return (((C.earthOrbital.earthAscendingNodeInvPlane + (360.0 * (year - 2000)) / period) % 360) + 360) % 360;
439
- };
429
+ // (Plan 07 R5/R9: Earth's H/3 inclination cosine and −H/5 linear node — the
430
+ // K device — stood here; `inclinationDeg`/`ascendingNodeDeg` are now the
431
+ // one-source construction defined with the planets' chain evaluators below.)
440
432
 
441
433
  // ── Year/day lengths ──────────────────────────────────────────────────────
442
434
  /** @param {number} year @param {number} base @param {Array<[number, number, number]>} harmonics @returns {number} */
@@ -617,6 +609,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
617
609
  });
618
610
  return {
619
611
  yearLengths,
612
+ /** The one-source sample at a decimal year (the engine year) — ε, e, ϖ of date, the Earth-orbit normal (the fields the frame of date and the invariable-plane elements read). @param {number} year */
613
+ sampleAt,
620
614
  /** EXPERIMENT accessors: the series' e and equinox-referenced ϖ of date. @param {number} year @returns {number} */
621
615
  eAt: (year) => sampleAt(year).e,
622
616
  /** @param {number} year @returns {number} */
@@ -716,14 +710,11 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
716
710
  for (const k of PLANET_KEYS) massFraction[k] = 1 / C.physicalConstants.massRatioDE440[k];
717
711
  massFraction.earth = massEarthAlone / M_SUN;
718
712
 
719
- const PSI = FL.computePsiConstant({
720
- earthInvPlaneInclinationAmplitude: earthInclAmplitude,
721
- massEarthAlone,
722
- massSun: M_SUN,
723
- });
724
713
  // (plan 07 R6: eccentricityAmplitudeK — the K constant inverted from
725
714
  // Earth's calibration — and the t2000 phase offset it fed went with the
726
- // eccentricity law. systemResetN is kept where other consumers read it.)
715
+ // eccentricity law; plan 07 R5: PSI — ψ = 3·A_earth·√(m_E/m_☉) — and the
716
+ // inclination law it fed went the same way. systemResetN is kept where
717
+ // other consumers read it.)
727
718
 
728
719
  /** @param {[number, number]|null} frac @returns {number|null} */
729
720
  const fractionToYears = (frac) => (frac === null ? null : (H * frac[0]) / frac[1]);
@@ -743,14 +734,6 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
743
734
  // obliquity of the spin channel (the IAU pole against the chain's J2000
744
735
  // plane, acute form) — both from the governed artifacts, no fractions.
745
736
  const wobble = computeSecularShape(/** @type {any} */ (CHAIN_ARTIFACT), k).beatYears;
746
- const il = FL.computeInclinationLaw({
747
- fibonacciD: mp.fibonacciD,
748
- massFrac: massFraction[k],
749
- invPlaneInclinationJ2000: ar.invPlaneInclinationJ2000,
750
- longitudePerihelion: ar.longitudePerihelion,
751
- inclinationCycleAnchor: mp.inclinationCycleAnchor,
752
- antiPhase: mp.antiPhase || false,
753
- }, PSI);
754
737
  const obliquityDerived = computeObliquityJ2000Deg({
755
738
  spin: C.planetSpinPhysical[k],
756
739
  anchorInclEclipticDeg: /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements[k].inclEclipticDeg,
@@ -770,8 +753,6 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
770
753
  ascendingNodeInvPlane: mp.ascendingNodeInvPlane,
771
754
  inclinationCycleAnchor: mp.inclinationCycleAnchor,
772
755
  invPlaneInclinationJ2000: ar.invPlaneInclinationJ2000,
773
- invPlaneInclinationAmplitude: il.amplitude,
774
- invPlaneInclinationMean: il.mean,
775
756
  obliquityMean,
776
757
  orbitalEccentricityJ2000: ar.orbitalEccentricityJ2000,
777
758
  solarYearInput: ar.solarYearInput,
@@ -779,8 +760,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
779
760
  });
780
761
  // R8 (plan 07): what `model.planets.record()` PUBLISHES is not the record
781
762
  // above. The internal record kept the ψ/K law outputs for the fitting
782
- // pipeline; at R6 the K half and both fitter steps that read it are gone,
783
- // so what remains above is the ψ half (until R5) and the structural inputs.
763
+ // pipeline; at R6 the K half and both fitter steps that read it went, at
764
+ // R5 the ψ half (invPlaneInclinationAmplitude/Mean) — what remains above
765
+ // is the structural inputs and the derived J2000 obliquity.
784
766
  // The separation stands for the same reason it was made —
785
767
  // but nothing outside needs the device, and serving it made /v1/bodies
786
768
  // contradict the model's own chain on the SAME quantities: Mercury's node
@@ -878,13 +860,15 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
878
860
  };
879
861
  /** Ecliptic longitude of perihelion, J2000 ecliptic and equinox (the chain's ϖ). @param {string} k @param {number} year @returns {number} */
880
862
  const planetPerihelionDeg = (k, year) => planetChainElementsAt(k, year).lonPeriEclipticDeg;
881
- /** 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} */
882
- const planetAscNodeDeg = (k, year) => {
863
+ /** The S&S longitude origin as an s-frame longitude (J2000-fixed, derived). @returns {number} */
864
+ const nodeOriginSSDeg = () => {
883
865
  if (planetNodeOriginSSDeg === null) {
884
866
  planetNodeOriginSSDeg = computeEquatorNodeOriginSFrameDeg(/** @type {any} */ (CHAIN_ARTIFACT).invariablePlane, C.earthOrbital.obliquityJ2000_deg);
885
867
  }
886
- return convertNodeSFrameToEquatorOriginDeg(planetChainElementsAt(k, year).ascNodeInvPlaneDeg, planetNodeOriginSSDeg);
868
+ return planetNodeOriginSSDeg;
887
869
  };
870
+ /** 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} */
871
+ const planetAscNodeDeg = (k, year) => convertNodeSFrameToEquatorOriginDeg(planetChainElementsAt(k, year).ascNodeInvPlaneDeg, nodeOriginSSDeg());
888
872
  /** Inclination to the model's invariable plane (exact orbit-normal rotation). @param {string} k @param {number} year @returns {number} */
889
873
  const planetInclinationDeg = (k, year) => planetChainElementsAt(k, year).inclInvPlaneDeg;
890
874
  /** Orbital eccentricity of date, from the same chain elements (R8): the
@@ -893,6 +877,52 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
893
877
  * e-vector does not have. @param {string} k @param {number} year @returns {number} */
894
878
  const planetEccentricity = (k, year) => planetChainElementsAt(k, year).e;
895
879
 
880
+ // ── Earth's invariable-plane elements of date (plan 07 R5/R9) ─────────────
881
+ // ONE published i_inv / Ω_inv everywhere — the mirror of the browser's
882
+ // inclInvPlaneModel / ascNodeInvPlaneModel (identical ops): the engine's
883
+ // own Earth-orbit normal of date (the one-source sampler — the banked
884
+ // secular series inside ±10 Myr, the mode tail beyond) against the
885
+ // artifact's invariable plane, the node expressed in the Souami & Souchay
886
+ // longitude origin through the same derived conversion the planets' node
887
+ // takes. The H/3 cosine on the fixed apsidal carrier and the −H/5 linear
888
+ // node that served here until R5 were the K device: 1.578677° against
889
+ // 1.578422° at J2000, 1.24° against 1.91° at −100 kyr, and 112° against
890
+ // 94° of node there. Doc 05 banks the one-source reading at rms 0.003°
891
+ // vs La2010 over −500 kyr. Argument: the engine year (dynamical time,
892
+ // Julian from J2000 TT — the sampler's own coordinate, as the chain's).
893
+ const invPlaneGeometry = (() => {
894
+ const ip = /** @type {{ inclEclipticDeg: number, ascNodeEclipticDeg: number }} */ (/** @type {any} */ (CHAIN_ARTIFACT).invariablePlane);
895
+ const D2R = Math.PI / 180;
896
+ const si = Math.sin(ip.inclEclipticDeg * D2R), ci = Math.cos(ip.inclEclipticDeg * D2R);
897
+ const nInv = [si * Math.sin(ip.ascNodeEclipticDeg * D2R), -si * Math.cos(ip.ascNodeEclipticDeg * D2R), ci];
898
+ const xIp = [Math.cos(ip.ascNodeEclipticDeg * D2R), Math.sin(ip.ascNodeEclipticDeg * D2R), 0];
899
+ const yIp = [nInv[1] * xIp[2] - nInv[2] * xIp[1], nInv[2] * xIp[0] - nInv[0] * xIp[2], nInv[0] * xIp[1] - nInv[1] * xIp[0]];
900
+ return { ascNodeEclipticDeg: ip.ascNodeEclipticDeg, nInv, xIp, yIp };
901
+ })();
902
+ /** The engine Earth-orbit normal of date, J2000 ecliptic frame. @param {number} year @returns {number[]} */
903
+ const earthOrbitNormalJ2000At = (year) => {
904
+ const s = oneSourceM.sampleAt(year);
905
+ return [s.orbitNormalX, s.orbitNormalY, Math.sqrt(Math.max(0, 1 - s.orbitNormalX * s.orbitNormalX - s.orbitNormalY * s.orbitNormalY))];
906
+ };
907
+ /** Earth's inclination of date to the model's invariable plane, degrees. @param {number} year @returns {number} */
908
+ const inclinationDeg = (year) => {
909
+ const nE = earthOrbitNormalJ2000At(year), nInv = invPlaneGeometry.nInv;
910
+ const d = nE[0] * nInv[0] + nE[1] * nInv[1] + nE[2] * nInv[2];
911
+ return Math.acos(Math.min(1, Math.max(-1, d))) / (Math.PI / 180);
912
+ };
913
+ /** Earth's ascending node of date on the invariable plane, S&S origin, degrees in [0, 360). @param {number} year @returns {number} */
914
+ const ascendingNodeDeg = (year) => {
915
+ const D2R = Math.PI / 180;
916
+ const { ascNodeEclipticDeg, nInv, xIp, yIp } = invPlaneGeometry;
917
+ const nE = earthOrbitNormalJ2000At(year);
918
+ let N = [nInv[1] * nE[2] - nInv[2] * nE[1], nInv[2] * nE[0] - nInv[0] * nE[2], nInv[0] * nE[1] - nInv[1] * nE[0]];
919
+ const L = Math.hypot(N[0], N[1], N[2]);
920
+ N = [N[0] / L, N[1] / L, N[2] / L];
921
+ const sFrameDeg = ((Math.atan2(N[0] * yIp[0] + N[1] * yIp[1] + N[2] * yIp[2],
922
+ N[0] * xIp[0] + N[1] * xIp[1] + N[2] * xIp[2]) / D2R + ascNodeEclipticDeg) % 360 + 360) % 360;
923
+ return convertNodeSFrameToEquatorOriginDeg(sFrameDeg, nodeOriginSSDeg());
924
+ };
925
+
896
926
  // ── Time axis: exact JD ↔ model-year conversion ───────────────────────────
897
927
  // The model's `year` inputs live on the SI axis (the axis the fits were
898
928
  // anchored on — tools/lib `_jdToSIyear`): linear in SI 86400-s days from the
@@ -2,31 +2,29 @@
2
2
  * createPlanetModel — the planet composition front door (Phase 8.3, L10).
3
3
  *
4
4
  * ONE law set, N body records: this factory binds an environment once and
5
- * runs the certified derivation chain over every body record, in the order
6
- * the chain requires (each step feeds the next):
5
+ * runs the certified derivation chain over every body record:
7
6
  *
8
- * ψ constant → inclination law (amplitude, mean)
9
- * → wobble period (beat of |axial| and |ICRF|)
10
- * K constant → obliquity mean (snapshot form) → eccentricity law
11
- * (amplitude, base, J2000 phase)
12
- * geometry (the type-branched ellipse family — the law-derived
13
- * eccentricity base feeds geometry for the seven carriers;
14
- * minor bodies use their record's base)
7
+ * inputs the chain's g-mode beat (wobble) and the spin channel's
8
+ * derived J2000 obliquity, carried on the record since plan 06
9
+ * Phase 7 commit 2
10
+ * geometry (the type-branched ellipse family on the OBSERVED J2000
11
+ * eccentricity; minor bodies use their record's base)
12
+ *
13
+ * The ψ and K amplitude laws that opened this chain (ψ constant →
14
+ * inclination amplitude/mean; K constant → eccentricity amplitude/base/
15
+ * phase) are DELETED — K at plan 07 R6, ψ at plan 07 R5. A planet's
16
+ * inclination and eccentricity of date have one home, the N-body chain
17
+ * (model.js planetChainElementsAt); docs/retired-record.md is the record.
15
18
  *
16
19
  * THIN BY DESIGN. This is the composition surface, not a rewiring: both
17
- * engines keep their existing direct call sites into the law modules, and
18
- * the runtime channels (eccentricity-at-year, orientation, the ascending-
19
- * node integrator, predictive precession) stay direct module calls because
20
- * they consume engine-owned state (scene JD, epoch machinery, fitted
21
- * tables). What this factory adds is the seam future bodies plug into:
22
- * a new body is a record + (at most) one new geometry branch — see the
23
- * minor-body placeholder note in geometry.cjs (Phase 18 perturbation
24
- * types land the same way).
20
+ * engines keep their existing direct call sites, and the runtime channels
21
+ * stay direct module calls because they consume engine-owned state (scene
22
+ * JD, epoch machinery, fitted tables). What this factory adds is the seam
23
+ * future bodies plug into: a new body is a record + (at most) one new
24
+ * geometry branch — see the minor-body placeholder note in geometry.cjs
25
+ * (Phase 18 perturbation types land the same way).
25
26
  *
26
- * Guard semantics mirror tools/lib/constants.js verbatim: the law steps
27
- * run only where the record carries the required fields (fibonacciD +
28
- * mass fraction for the ψ/K families, perihelion + axial periods for
29
- * wobble); geometry runs for every body. The identity gate
27
+ * Geometry runs for every body. The identity gate
30
28
  * (test/planet-model-identity.test.mjs) holds this factory bit-exact
31
29
  * against the shipped Node derivation.
32
30
  */
@@ -34,7 +32,6 @@
34
32
  'use strict';
35
33
 
36
34
  const { derivePlanetGeometry } = require('./geometry.cjs');
37
- const FL = require('./fibonacci-laws.cjs');
38
35
 
39
36
  /**
40
37
  * @typedef {Object} PlanetModelBody
@@ -66,15 +63,10 @@ const FL = require('./fibonacci-laws.cjs');
66
63
  * @property {number} currentAUDistanceKm
67
64
  * @property {number} earthEccentricityJ2000
68
65
  * @property {number} earthPerihelionLongitudeJ2000Deg
69
- * @property {{ earthInvPlaneInclinationAmplitude: number,
70
- * massEarthAlone: number, massSun: number }} calibration
71
- * @property {Record<string, number>} massFractions
72
66
  */
73
67
 
74
68
  /**
75
69
  * @typedef {Object} PlanetModelRecord
76
- * @property {number} [invPlaneInclinationAmplitude]
77
- * @property {number} [invPlaneInclinationMean]
78
70
  * @property {number} [wobblePeriodYears]
79
71
  * @property {number} [obliquityMeanDeg]
80
72
  * @property {ReturnType<typeof derivePlanetGeometry>} geometry
@@ -87,18 +79,13 @@ const FL = require('./fibonacci-laws.cjs');
87
79
  * @param {Record<string, PlanetModelBody>} bodies - keyed by body name
88
80
  * (the key selects the body-unique geometry branches: mercury, pluto,
89
81
  * halleys, ceres)
90
- * @returns {{ psiConstant: number,
91
- * bodies: Record<string, PlanetModelRecord> }}
82
+ * @returns {{ bodies: Record<string, PlanetModelRecord> }}
92
83
  */
93
84
  function createPlanetModel(env, bodies) {
94
- const psiConstant = FL.computePsiConstant({
95
- earthInvPlaneInclinationAmplitude: env.calibration.earthInvPlaneInclinationAmplitude,
96
- massEarthAlone: env.calibration.massEarthAlone,
97
- massSun: env.calibration.massSun,
98
- });
99
85
  // Plan 07 R6: kConstant, the System-Reset eccentricityAnchor
100
86
  // (balancedYear − systemResetN·H) and the t2000 phase it fed went with the
101
- // eccentricity law.
87
+ // eccentricity law; plan 07 R5: psiConstant (ψ = 3·A_earth·√(m_E/m_☉),
88
+ // from env.calibration) went with the inclination law.
102
89
 
103
90
  const geomEnv = {
104
91
  holisticYears: env.holisticYears,
@@ -111,22 +98,9 @@ function createPlanetModel(env, bodies) {
111
98
  /** @type {Record<string, PlanetModelRecord>} */
112
99
  const out = {};
113
100
  for (const [key, b] of Object.entries(bodies)) {
114
- const massFrac = env.massFractions[key];
115
101
  /** @type {PlanetModelRecord} */
116
102
  const rec = /** @type {PlanetModelRecord} */ ({});
117
103
 
118
- if (b.fibonacciD && massFrac && b.invPlaneInclinationJ2000 !== undefined) {
119
- const il = FL.computeInclinationLaw({
120
- fibonacciD: b.fibonacciD, massFrac,
121
- invPlaneInclinationJ2000: b.invPlaneInclinationJ2000,
122
- longitudePerihelion: /** @type {number} */ (b.longitudePerihelion),
123
- inclinationCycleAnchor: /** @type {number} */ (b.inclinationCycleAnchor),
124
- antiPhase: /** @type {boolean} */ (b.antiPhase),
125
- }, psiConstant);
126
- rec.invPlaneInclinationAmplitude = il.amplitude;
127
- rec.invPlaneInclinationMean = il.mean;
128
- }
129
-
130
104
  // Plan 06 Phase 7 commit 2: the K law's cycle period and obliquity input
131
105
  // are INPUTS of the record now — the chain's own g-mode beat
132
106
  // (keplerian-chain computeSecularShape) and the derived J2000 obliquity
@@ -156,7 +130,7 @@ function createPlanetModel(env, bodies) {
156
130
  out[key] = rec;
157
131
  }
158
132
 
159
- return { psiConstant, bodies: out };
133
+ return { bodies: out };
160
134
  }
161
135
 
162
136
  module.exports = { createPlanetModel };
package/types/index.d.ts CHANGED
@@ -12,9 +12,6 @@ export { createMoonArguments } from "./moon/arguments.cjs";
12
12
  export { createMoonSeries } from "./moon/series.cjs";
13
13
  export { createMoonApparent } from "./moon/apparent.cjs";
14
14
  export { derivePlanetGeometry } from "./planets/geometry.cjs";
15
- export * as planetFibonacciLaws from "./planets/fibonacci-laws.cjs";
16
- export * as planetOrientation from "./planets/orientation.cjs";
17
- export { integrateAscendingNode } from "./planets/asc-node-integrator.cjs";
18
15
  export * as planetOrbitChain from "./planets/orbit-chain.cjs";
19
16
  export { computeSecularShape } from "./planets/secular-shape.cjs";
20
17
  export { createSecularSeriesOverride } from "./planets/secular-series.cjs";
@@ -37,16 +37,8 @@ export type PlanetModelEnv = {
37
37
  currentAUDistanceKm: number;
38
38
  earthEccentricityJ2000: number;
39
39
  earthPerihelionLongitudeJ2000Deg: number;
40
- calibration: {
41
- earthInvPlaneInclinationAmplitude: number;
42
- massEarthAlone: number;
43
- massSun: number;
44
- };
45
- massFractions: Record<string, number>;
46
40
  };
47
41
  export type PlanetModelRecord = {
48
- invPlaneInclinationAmplitude?: number | undefined;
49
- invPlaneInclinationMean?: number | undefined;
50
42
  wobblePeriodYears?: number | undefined;
51
43
  obliquityMeanDeg?: number | undefined;
52
44
  geometry: ReturnType<typeof derivePlanetGeometry>;
@@ -80,14 +72,9 @@ export type PlanetModelRecord = {
80
72
  * @property {number} currentAUDistanceKm
81
73
  * @property {number} earthEccentricityJ2000
82
74
  * @property {number} earthPerihelionLongitudeJ2000Deg
83
- * @property {{ earthInvPlaneInclinationAmplitude: number,
84
- * massEarthAlone: number, massSun: number }} calibration
85
- * @property {Record<string, number>} massFractions
86
75
  */
87
76
  /**
88
77
  * @typedef {Object} PlanetModelRecord
89
- * @property {number} [invPlaneInclinationAmplitude]
90
- * @property {number} [invPlaneInclinationMean]
91
78
  * @property {number} [wobblePeriodYears]
92
79
  * @property {number} [obliquityMeanDeg]
93
80
  * @property {ReturnType<typeof derivePlanetGeometry>} geometry
@@ -99,11 +86,9 @@ export type PlanetModelRecord = {
99
86
  * @param {Record<string, PlanetModelBody>} bodies - keyed by body name
100
87
  * (the key selects the body-unique geometry branches: mercury, pluto,
101
88
  * halleys, ceres)
102
- * @returns {{ psiConstant: number,
103
- * bodies: Record<string, PlanetModelRecord> }}
89
+ * @returns {{ bodies: Record<string, PlanetModelRecord> }}
104
90
  */
105
91
  export function createPlanetModel(env: PlanetModelEnv, bodies: Record<string, PlanetModelBody>): {
106
- psiConstant: number;
107
92
  bodies: Record<string, PlanetModelRecord>;
108
93
  };
109
94
  import { derivePlanetGeometry } from "./geometry.cjs";
@@ -1,120 +0,0 @@
1
- /**
2
- * Dynamic ascending node — rate-based segment integration (Phase 8.3, L5).
3
- *
4
- * dΩ/dε = −sin(Ω)/tan(i), integrated over obliquity change with segment
5
- * handling at obliquity extrema and Earth-inclination crossovers (the
6
- * inclination DIRECTION flips the sign of the effect per segment).
7
- *
8
- * Extracted from tools/lib/orbital-engine.js
9
- * calculateDynamicAscendingNodeFromTilts (itself the mirror of the
10
- * browser's 6-arg variant — S-P5). The BROWSER variant remains engine-side
11
- * this slice: neither copy is fixture-pinned, so the signature unification
12
- * is deferred to the factory pass, WITH probes first (recorded follow-up).
13
- *
14
- * §2h BOUNDARY: this module takes {ascendingNodeDeg, inclinationDeg} — the
15
- * Tychosium `orbitTilta/orbitTiltb` scheme names never enter the package;
16
- * the engines own the atan2/hypot decomposition at their edge.
17
- *
18
- * All time-dependent inputs are INJECTED per call: the engine's obliquity
19
- * and Earth-inclination evaluators, the precomputed obliquity-extrema
20
- * lookup, the inclination-crossing finder, and (optionally) the per-planet
21
- * dynamic ecliptic inclination.
22
- */
23
-
24
- 'use strict';
25
-
26
- /**
27
- * @param {{ ascendingNodeDeg: number, inclinationDeg: number }} tilt —
28
- * static node and inclination (engine-decomposed from its scene scheme)
29
- * @param {number} currentYear
30
- * @param {{
31
- * obliquityAt: (year: number) => number,
32
- * earthInclinationAt: (year: number) => number,
33
- * obliquityExtremaInRange: (yearMin: number, yearMax: number) => number[],
34
- * inclinationCrossingsInRange: (inclinationDeg: number, yearMin: number, yearMax: number) => number[],
35
- * eclipticInclinationAt: ((year: number) => number) | null,
36
- * earthInclinationMeanDeg: number,
37
- * earthInclinationAmplitudeDeg: number,
38
- * epochYear?: number,
39
- * }} deps — eclipticInclinationAt non-null enables the dynamic-inclination
40
- * crossover search and per-segment rates (the planetName path).
41
- * @returns {number} dynamic ascending node longitude, degrees 0–360
42
- */
43
- function integrateAscendingNode(tilt, currentYear, deps) {
44
- const DEG2RAD = Math.PI / 180;
45
- const RAD2DEG = 180 / Math.PI;
46
-
47
- const staticOmega = ((tilt.ascendingNodeDeg % 360) + 360) % 360;
48
- const planetInclination = tilt.inclinationDeg;
49
-
50
- if (planetInclination < 1e-6) return staticOmega;
51
-
52
- const i = planetInclination * DEG2RAD;
53
- const OmegaRad = staticOmega * DEG2RAD;
54
- const tanI = Math.tan(i);
55
- if (Math.abs(tanI) < 1e-10) return staticOmega;
56
-
57
- const sinOmega = Math.sin(OmegaRad);
58
- const EPOCH_YEAR = deps.epochYear !== undefined ? deps.epochYear : 2000;
59
-
60
- /** @param {number} fromYear @param {number} toYear @returns {number} */
61
- const integrateEffect = (fromYear, toYear) => {
62
- if (Math.abs(toYear - fromYear) < 0.1) return 0;
63
-
64
- const yearMin = Math.min(fromYear, toYear);
65
- const yearMax = Math.max(fromYear, toYear);
66
- const dir = toYear >= fromYear ? 1 : -1; // integration direction — signs the total
67
-
68
- /** @type {number[]} */
69
- let criticalYears = [yearMin, yearMax];
70
-
71
- criticalYears.push(...deps.obliquityExtremaInRange(yearMin, yearMax));
72
-
73
- // Find ALL inclination crossings
74
- const minEarthIncl = deps.earthInclinationMeanDeg - deps.earthInclinationAmplitudeDeg;
75
- const maxEarthIncl = deps.earthInclinationMeanDeg + deps.earthInclinationAmplitudeDeg;
76
-
77
- // With a dynamic ecliptic inclination the planet can enter Earth's range
78
- // even if the static value is outside — always search for crossovers.
79
- if (deps.eclipticInclinationAt || (planetInclination >= minEarthIncl && planetInclination <= maxEarthIncl)) {
80
- const crossIncl = deps.eclipticInclinationAt
81
- ? deps.eclipticInclinationAt((yearMin + yearMax) / 2)
82
- : planetInclination;
83
- criticalYears.push(...deps.inclinationCrossingsInRange(crossIncl, yearMin, yearMax));
84
- }
85
-
86
- criticalYears = [...new Set(criticalYears)].sort((a, b) => a - b);
87
-
88
- // Integrate over segments
89
- let effect = 0;
90
- for (let idx = 0; idx < criticalYears.length - 1; idx++) {
91
- const segStart = criticalYears[idx];
92
- const segEnd = criticalYears[idx + 1];
93
-
94
- const oblStart = deps.obliquityAt(segStart);
95
- const oblEnd = deps.obliquityAt(segEnd);
96
- const deltaObl = (oblEnd - oblStart) * DEG2RAD;
97
-
98
- const midYear = (segStart + segEnd) / 2;
99
- const earthInclAtMid = deps.earthInclinationAt(midYear);
100
-
101
- const dynIncl = deps.eclipticInclinationAt
102
- ? deps.eclipticInclinationAt(midYear)
103
- : planetInclination;
104
- const inclDirection = earthInclAtMid > dynIncl ? 1 : -1;
105
- const dynTanI = Math.tan(dynIncl * DEG2RAD);
106
- if (Math.abs(dynTanI) < 1e-10) continue; // skip near-zero inclination
107
- const segRate = -sinOmega / dynTanI;
108
-
109
- effect += segRate * inclDirection * deltaObl * RAD2DEG;
110
- }
111
- return effect * dir;
112
- };
113
-
114
- const effectFromEpoch = integrateEffect(EPOCH_YEAR, currentYear);
115
-
116
- const newOmega = staticOmega + effectFromEpoch;
117
- return ((newOmega % 360) + 360) % 360;
118
- }
119
-
120
- module.exports = { integrateAscendingNode };
@@ -1,95 +0,0 @@
1
- /**
2
- * The ψ/K amplitude relations + the retired law-loop constructions —
3
- * THE shared implementation (Phase 8.3, layer L2). Status per relation
4
- * (doc 10 "The Six Fibonacci Relations"; the restatement: the N-body
5
- * chain is the planet path):
6
- * - ψ (computePsiConstant/computeInclinationLaw) and K (computeKConstant/
7
- * computeEccentricityLaw's amplitude) are OPEN PREDICTIONS — untested
8
- * empirical amplitude laws, derived from Earth alone, kept as stated.
9
- * - The wobble beat (computeWobblePeriodYears) is kinematic frame
10
- * arithmetic — the permanent class.
11
- * - The obliquity-mean snapshot and the eccentricity base/phase
12
- * (System-Reset construction) belong to the RETIRED law framework —
13
- * today serving the legacy scene scaffolding, the no-chain bodies
14
- * (Pluto/Halley/Eros) and the o.fib* diagnostics (doc 72 — the closed
15
- * loop; docs/37 is archived, see docs/retired-record.md).
16
- *
17
- * Extracted VERBATIM in the BROWSER's expression forms from src/script.js
18
- * (the ψ loop, calcWobblePeriod, the obliquity-cycle aliases,
19
- * calcObliquityMean's load-time snapshot branch, the K loop), which
20
- * tools/lib/constants.js + constants/utils.js hand-mirrored with last-ulp
21
- * operation-order variants (measured: 32/42 law outputs bit-exact, 10 at
22
- * the 1e-11..1e-15 relative class) — those variants dissolve here.
23
- *
24
- * LOAD-BEARING conventions:
25
- * - ψ = 3·A_earth·√(M_EARTH_ALONE/M_SUN): the ALONE/SYSTEM mass asymmetry
26
- * is calibration convention (doc 24) — switching Earth to SYSTEM shifts
27
- * ψ by 0.612% and would require re-calibrating A_earth.
28
- * - Wobble = beat of |axial| and |ICRF| RATES (sign-free — Venus's
29
- * prograde-axial/retrograde-ICRF case); |axial| > 8H ⇒ frozen ⇒
30
- * wobble = |ICRF| exactly.
31
- * - The obliquity-mean law here is the SNAPSHOT form (the browser's
32
- * module-load TDZ fallback — the value both engines actually ship at
33
- * load); the browser's runtime integrated path stays engine-side with
34
- * its Phase-8 anchors. Note it uses 1/(H/13), where the runtime
35
- * integrated form uses 13/H — historical operation orders, preserved.
36
- * - K-law phase offset: 90° in-phase, 270° anti-phase (Saturn) — the n=7
37
- * System Reset state (all planets at mean e, Saturn falling).
38
- * - The Node mirror carried an `else` branch for bodies without a wobble
39
- * period — DEAD code (the loop is fibonacciD-guarded and all seven
40
- * carriers have wobble periods); dropped here, recorded in the commit.
41
- */
42
-
43
- 'use strict';
44
-
45
- /**
46
- * ψ constant from Earth's calibration. @param {{
47
- * earthInvPlaneInclinationAmplitude: number,
48
- * massEarthAlone: number, massSun: number }} c @returns {number} */
49
- function computePsiConstant(c) {
50
- return 3 * c.earthInvPlaneInclinationAmplitude * Math.sqrt(c.massEarthAlone / c.massSun);
51
- }
52
-
53
- /**
54
- * ψ law: invariable-plane inclination amplitude and mean.
55
- * @param {{ fibonacciD: number, massFrac: number,
56
- * invPlaneInclinationJ2000: number, longitudePerihelion: number,
57
- * inclinationCycleAnchor: number, antiPhase: boolean }} b
58
- * @param {number} psiConstant
59
- * @returns {{ amplitude: number, mean: number }} */
60
- function computeInclinationLaw(b, psiConstant) {
61
- const amplitude = psiConstant / (b.fibonacciD * Math.sqrt(b.massFrac));
62
- const antiPhase = b.antiPhase ? -1 : 1;
63
- const mean = b.invPlaneInclinationJ2000
64
- - antiPhase * amplitude * Math.cos((b.longitudePerihelion - b.inclinationCycleAnchor) * Math.PI / 180);
65
- return { amplitude, mean };
66
- }
67
-
68
- // (The device's wobble beat of integer axial and obliquity fractions, the
69
- // Venus/Neptune |ICRF| obliquity-cycle fallback and the snapshot "mean
70
- // obliquity" left with plan 06 Phase 7 commit 2: the K law's cycle period is
71
- // the chain's own g-mode beat (keplerian-chain computeSecularShape) and its
72
- // obliquity input the derived J2000 obliquity (spin-channel
73
- // computeObliquityJ2000Deg). docs/retired-record.md carries the record.)
74
-
75
- // Plan 07 R6: computeKConstant and computeEccentricityLaw stood here — the
76
- // K relation (K = e_amp·√m·a^1.5 / (sin(tilt)·√d), inverted per planet) and
77
- // the System-Reset eccentricity construction it fed
78
- // (base = A·cosθ + √(e_J2000² − A²sin²θ) at the J2000 phase, θ measured on
79
- // the wobble beat with a 90°/270° anti-phase offset).
80
- //
81
- // They are DELETED, not reclassified. Measured first (plan 07 §9e): scaling
82
- // their outputs by 1.10 moved no scene position and no published record
83
- // field — both twins place planets from the N-body chain, which overwrites
84
- // the construction entirely — and the registry keys built on them had no
85
- // consumer in the docs, on the website or in the paper. The nine balance and
86
- // Fibonacci-law instruments that computed from them are retired with them;
87
- // docs/retired-record.md is the record.
88
- //
89
- // A planet's eccentricity of date now has one home: the chain
90
- // (`model.planets.eccentricity(k, year)`), and its long-term shape is the
91
- // chain's own g-mode structure.
92
-
93
- module.exports = {
94
- computePsiConstant, computeInclinationLaw,
95
- };
@@ -1,142 +0,0 @@
1
- /**
2
- * Planet orientation channel — THE shared implementation (Phase 8.3, L4):
3
- * invariable-plane inclination, ascending nodes, and the scene ecliptic
4
- * inclination (the balanced-year dot-product form canonicalized at S-P4).
5
- *
6
- * Time is EXPLICIT here (yearsSinceBalanced / calendar year). A finding
7
- * from extraction, preserved not fixed: the browser's
8
- * computePlanetInvPlaneInclinationDynamic(planet, currentYear) IGNORED its
9
- * currentYear parameter and read the live scene JD — the 8.3-0 fixtures
10
- * recorded identical values at every probed epoch. The browser wrapper
11
- * keeps that scene-coupling (visibly, at the wrapper); the Node engine
12
- * honors the year as before. Unifying them is a measured decision for the
13
- * factory layer, not a silent side effect.
14
- *
15
- * Two ascending-node conventions exist and BOTH are quantities (the S-P4
16
- * lesson): the year-2000-anchored linear node here
17
- * (ascendingNodeInvPlaneLinearAt — the node-integrator mirror)
18
- * and the balanced-year −8H/N node inside the scene tilt
19
- * (eclipticInclinationFromBalanced). Do not merge them.
20
- *
21
- * SCOPE SINCE v16.2 (owner-found audit): these record-based forms — the
22
- * planet record's linear lattice rates, the −8H/N node period, the
23
- * balanced-year inclination oscillation — are the RETIRED device. They serve
24
- * the structural record and the browser's no-chain bodies (Pluto, Halley,
25
- * Eros) only. The seven planets' elements of date on every published surface
26
- * (the simulator, the API's /v1/bodies, the MCP essrt_bodies) ride the N-body
27
- * Keplerian chain with the series handover (model.js planetChainElementsAt;
28
- * src/script.js _kcElementsOfDate).
29
- */
30
-
31
- 'use strict';
32
-
33
- /**
34
- * Invariable-plane inclination at explicit time (browser form): the ICRF
35
- * perihelion-linked oscillation i(t) = mean + s·A·cos(ϖ_ICRF(t) − anchor).
36
- * @param {{ key?: string, isEarth?: boolean,
37
- * invPlaneInclinationJ2000: number, invPlaneInclinationMean: number,
38
- * invPlaneInclinationAmplitude: number, inclinationCycleAnchor: number,
39
- * longitudePerihelion: number, perihelionEclipticYears: number,
40
- * antiPhase?: boolean }} body
41
- * @param {number} yearsSinceBalanced
42
- * @param {{ H: number, yearsFromBalancedToJ2000: number }} env
43
- * @returns {number} degrees */
44
- function invPlaneInclinationAt(body, yearsSinceBalanced, env) {
45
- const iJ2000 = body.invPlaneInclinationJ2000;
46
- const amplitude = body.invPlaneInclinationAmplitude;
47
- if (iJ2000 === undefined || amplitude === undefined) return iJ2000 || 0;
48
- if (amplitude === 0) return iJ2000;
49
-
50
- // ICRF period: Earth = H/3 directly; others = 1/(1/eclP − 1/(H/13)).
51
- const genPrecRate = 1 / (env.H / 13);
52
- const icrfPeriod = body.isEarth
53
- ? env.H / 3
54
- : 1 / (1 / body.perihelionEclipticYears - genPrecRate);
55
- const icrfRate = 360 / icrfPeriod;
56
-
57
- const periAtBalanced = body.longitudePerihelion - icrfRate * env.yearsFromBalancedToJ2000;
58
- const periCurrent = periAtBalanced + icrfRate * yearsSinceBalanced;
59
- const currentPhaseRad = (periCurrent - body.inclinationCycleAnchor) * Math.PI / 180;
60
- const antiPhaseSign = body.antiPhase ? -1 : 1;
61
- return body.invPlaneInclinationMean + antiPhaseSign * amplitude * Math.cos(currentPhaseRad);
62
- }
63
-
64
- /**
65
- * Ascending node on the invariable plane — the LINEAR year-2000-anchored
66
- * convention (node-integrator; see the header).
67
- * @param {{ ascendingNodeInvPlane?: number, ascendingNodePeriod?: number,
68
- * perihelionEclipticYears: number }} body
69
- * @param {number} year @returns {number} degrees 0–360 */
70
- function ascendingNodeInvPlaneLinearAt(body, year) {
71
- if (!body.ascendingNodeInvPlane) return 0;
72
- const period = body.ascendingNodePeriod || body.perihelionEclipticYears;
73
- const rate = 360 / period;
74
- return ((body.ascendingNodeInvPlane + rate * (year - 2000)) % 360 + 360) % 360;
75
- }
76
-
77
- /**
78
- * Scene ecliptic inclination — normal-vector dot product, balanced-year
79
- * anchoring, planet Ω on the −8H/N assignment (canonicalized at S-P4; the
80
- * mirror of src/script.js updateDynamicInclinations).
81
- * @param {{ perihelionEclipticYears: number, longitudePerihelion: number,
82
- * inclinationCycleAnchor: number, antiPhase?: boolean,
83
- * invPlaneInclinationMean: number, invPlaneInclinationAmplitude: number,
84
- * ascendingNodeInvPlane: number, ascendingNodeCyclesIn8H?: number }} body
85
- * @param {{ invPlanePrecessionYears: number, inclinationMean: number,
86
- * inclinationAmplitude: number, ascendingNodeInvPlane: number }} earth
87
- * @param {number} yearsSinceBalanced
88
- * @param {{ H: number, yearsFromBalancedToJ2000: number }} env
89
- * @returns {number} degrees */
90
- function eclipticInclinationFromBalanced(body, earth, yearsSinceBalanced, env) {
91
- const d2r = Math.PI / 180;
92
- const genPrecRate = 1 / (env.H / 13);
93
-
94
- // --- Earth's orbital plane ---
95
- const earthPhaseRad = (yearsSinceBalanced / earth.invPlanePrecessionYears) * 2 * Math.PI;
96
- const earthI = (earth.inclinationMean
97
- - earth.inclinationAmplitude * Math.cos(earthPhaseRad)) * d2r;
98
-
99
- // Earth Ω regresses at -H/5 (ecliptic precession rate), NOT at H/3.
100
- const earthAscNodePeriod = -env.H / 5;
101
- const earthOmegaRate = 360 / earthAscNodePeriod;
102
- const earthOmega = (earth.ascendingNodeInvPlane
103
- - earthOmegaRate * env.yearsFromBalancedToJ2000
104
- + earthOmegaRate * yearsSinceBalanced) * d2r;
105
-
106
- // --- Planet's orbital plane ---
107
- const eclRate = 1 / body.perihelionEclipticYears;
108
- const icrfRate = (eclRate - genPrecRate) * 360; // deg/yr
109
- const periICRFDeg = body.longitudePerihelion
110
- - icrfRate * env.yearsFromBalancedToJ2000
111
- + icrfRate * yearsSinceBalanced;
112
-
113
- const planetPhaseDeg = periICRFDeg - body.inclinationCycleAnchor;
114
- const antiPhaseSign = body.antiPhase ? -1 : 1;
115
- const planetI = (body.invPlaneInclinationMean
116
- + antiPhaseSign * body.invPlaneInclinationAmplitude * Math.cos(planetPhaseDeg * d2r)) * d2r;
117
-
118
- // Planet Ω advances at the asc-node period (-8H/N), NOT the ecliptic
119
- // perihelion period — they are different angles.
120
- const planetAscNodePeriod = body.ascendingNodeCyclesIn8H
121
- ? -(8 * env.H) / body.ascendingNodeCyclesIn8H
122
- : body.perihelionEclipticYears;
123
- const planetOmegaRate = 360 / planetAscNodePeriod;
124
- const planetOmegaDeg = body.ascendingNodeInvPlane
125
- - planetOmegaRate * env.yearsFromBalancedToJ2000
126
- + planetOmegaRate * yearsSinceBalanced;
127
- const planetOmega = planetOmegaDeg * d2r;
128
-
129
- // --- Dot product of normal vectors → angle between orbital planes ---
130
- const eNx = Math.sin(earthI) * Math.sin(earthOmega);
131
- const eNy = Math.sin(earthI) * Math.cos(earthOmega);
132
- const eNz = Math.cos(earthI);
133
-
134
- const pNx = Math.sin(planetI) * Math.sin(planetOmega);
135
- const pNy = Math.sin(planetI) * Math.cos(planetOmega);
136
- const pNz = Math.cos(planetI);
137
-
138
- const cosAngle = eNx * pNx + eNy * pNy + eNz * pNz;
139
- return Math.acos(Math.max(-1, Math.min(1, cosAngle))) * (180 / Math.PI);
140
- }
141
-
142
- module.exports = { invPlaneInclinationAt, ascendingNodeInvPlaneLinearAt, eclipticInclinationFromBalanced };
@@ -1,30 +0,0 @@
1
- /**
2
- * @param {{ ascendingNodeDeg: number, inclinationDeg: number }} tilt —
3
- * static node and inclination (engine-decomposed from its scene scheme)
4
- * @param {number} currentYear
5
- * @param {{
6
- * obliquityAt: (year: number) => number,
7
- * earthInclinationAt: (year: number) => number,
8
- * obliquityExtremaInRange: (yearMin: number, yearMax: number) => number[],
9
- * inclinationCrossingsInRange: (inclinationDeg: number, yearMin: number, yearMax: number) => number[],
10
- * eclipticInclinationAt: ((year: number) => number) | null,
11
- * earthInclinationMeanDeg: number,
12
- * earthInclinationAmplitudeDeg: number,
13
- * epochYear?: number,
14
- * }} deps — eclipticInclinationAt non-null enables the dynamic-inclination
15
- * crossover search and per-segment rates (the planetName path).
16
- * @returns {number} dynamic ascending node longitude, degrees 0–360
17
- */
18
- export function integrateAscendingNode(tilt: {
19
- ascendingNodeDeg: number;
20
- inclinationDeg: number;
21
- }, currentYear: number, deps: {
22
- obliquityAt: (year: number) => number;
23
- earthInclinationAt: (year: number) => number;
24
- obliquityExtremaInRange: (yearMin: number, yearMax: number) => number[];
25
- inclinationCrossingsInRange: (inclinationDeg: number, yearMin: number, yearMax: number) => number[];
26
- eclipticInclinationAt: ((year: number) => number) | null;
27
- earthInclinationMeanDeg: number;
28
- earthInclinationAmplitudeDeg: number;
29
- epochYear?: number;
30
- }): number;
@@ -1,27 +0,0 @@
1
- /**
2
- * ψ constant from Earth's calibration. @param {{
3
- * earthInvPlaneInclinationAmplitude: number,
4
- * massEarthAlone: number, massSun: number }} c @returns {number} */
5
- export function computePsiConstant(c: {
6
- earthInvPlaneInclinationAmplitude: number;
7
- massEarthAlone: number;
8
- massSun: number;
9
- }): number;
10
- /**
11
- * ψ law: invariable-plane inclination amplitude and mean.
12
- * @param {{ fibonacciD: number, massFrac: number,
13
- * invPlaneInclinationJ2000: number, longitudePerihelion: number,
14
- * inclinationCycleAnchor: number, antiPhase: boolean }} b
15
- * @param {number} psiConstant
16
- * @returns {{ amplitude: number, mean: number }} */
17
- export function computeInclinationLaw(b: {
18
- fibonacciD: number;
19
- massFrac: number;
20
- invPlaneInclinationJ2000: number;
21
- longitudePerihelion: number;
22
- inclinationCycleAnchor: number;
23
- antiPhase: boolean;
24
- }, psiConstant: number): {
25
- amplitude: number;
26
- mean: number;
27
- };
@@ -1,67 +0,0 @@
1
- /**
2
- * Invariable-plane inclination at explicit time (browser form): the ICRF
3
- * perihelion-linked oscillation i(t) = mean + s·A·cos(ϖ_ICRF(t) − anchor).
4
- * @param {{ key?: string, isEarth?: boolean,
5
- * invPlaneInclinationJ2000: number, invPlaneInclinationMean: number,
6
- * invPlaneInclinationAmplitude: number, inclinationCycleAnchor: number,
7
- * longitudePerihelion: number, perihelionEclipticYears: number,
8
- * antiPhase?: boolean }} body
9
- * @param {number} yearsSinceBalanced
10
- * @param {{ H: number, yearsFromBalancedToJ2000: number }} env
11
- * @returns {number} degrees */
12
- export function invPlaneInclinationAt(body: {
13
- key?: string;
14
- isEarth?: boolean;
15
- invPlaneInclinationJ2000: number;
16
- invPlaneInclinationMean: number;
17
- invPlaneInclinationAmplitude: number;
18
- inclinationCycleAnchor: number;
19
- longitudePerihelion: number;
20
- perihelionEclipticYears: number;
21
- antiPhase?: boolean;
22
- }, yearsSinceBalanced: number, env: {
23
- H: number;
24
- yearsFromBalancedToJ2000: number;
25
- }): number;
26
- /**
27
- * Ascending node on the invariable plane — the LINEAR year-2000-anchored
28
- * convention (node-integrator; see the header).
29
- * @param {{ ascendingNodeInvPlane?: number, ascendingNodePeriod?: number,
30
- * perihelionEclipticYears: number }} body
31
- * @param {number} year @returns {number} degrees 0–360 */
32
- export function ascendingNodeInvPlaneLinearAt(body: {
33
- ascendingNodeInvPlane?: number;
34
- ascendingNodePeriod?: number;
35
- perihelionEclipticYears: number;
36
- }, year: number): number;
37
- /**
38
- * Scene ecliptic inclination — normal-vector dot product, balanced-year
39
- * anchoring, planet Ω on the −8H/N assignment (canonicalized at S-P4; the
40
- * mirror of src/script.js updateDynamicInclinations).
41
- * @param {{ perihelionEclipticYears: number, longitudePerihelion: number,
42
- * inclinationCycleAnchor: number, antiPhase?: boolean,
43
- * invPlaneInclinationMean: number, invPlaneInclinationAmplitude: number,
44
- * ascendingNodeInvPlane: number, ascendingNodeCyclesIn8H?: number }} body
45
- * @param {{ invPlanePrecessionYears: number, inclinationMean: number,
46
- * inclinationAmplitude: number, ascendingNodeInvPlane: number }} earth
47
- * @param {number} yearsSinceBalanced
48
- * @param {{ H: number, yearsFromBalancedToJ2000: number }} env
49
- * @returns {number} degrees */
50
- export function eclipticInclinationFromBalanced(body: {
51
- perihelionEclipticYears: number;
52
- longitudePerihelion: number;
53
- inclinationCycleAnchor: number;
54
- antiPhase?: boolean;
55
- invPlaneInclinationMean: number;
56
- invPlaneInclinationAmplitude: number;
57
- ascendingNodeInvPlane: number;
58
- ascendingNodeCyclesIn8H?: number;
59
- }, earth: {
60
- invPlanePrecessionYears: number;
61
- inclinationMean: number;
62
- inclinationAmplitude: number;
63
- ascendingNodeInvPlane: number;
64
- }, yearsSinceBalanced: number, env: {
65
- H: number;
66
- yearsFromBalancedToJ2000: number;
67
- }): number;