@essrt/physics 12.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": "12.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": "v16.2",
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 = "v16.2";
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,15 +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 earthtiltMean = C.earth.earthtiltMean;
101
- const earthInclAmplitude = C.earth.earthInvPlaneInclinationAmplitude;
102
- // eccentricityAmplitude: the Law-4 input A (the 1246 triangle closure; the
103
- // wobble-marker distance). It is the K calibration input ONLY — Earth's
104
- // eccentricity law does not use it; see eccentricityAt (base' derived) below.
105
- const eccentricityAmplitude = C.earth.eccentricityAmplitude;
106
- const earthInclMean = C.earthOrbital.earthInclinationJ2000_deg
107
- - earthInclAmplitude * Math.cos(((C.earthOrbital.earthPerihelionLongitudeJ2000
108
- - 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.
109
105
  const solsticeObliquityMean = F.SOLSTICE_OBLIQUITY_MEAN_FITTED;
110
106
 
111
107
  const G_CONSTANT = C.physicalConstants.G_CONSTANT;
@@ -430,14 +426,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
430
426
  /** de/dyear of the Sun's e — the cardinal braid's equation-of-centre
431
427
  * derivative rides it (±0.5-yr central difference; the constant offset
432
428
  * cancels). @param {number} year @returns {number} */
433
- /** @param {number} year @returns {number} */
434
- const inclinationDeg = (year) => earthInclMean
435
- - earthInclAmplitude * Math.cos(phaseRadians(balancedYear, year, 3));
436
- /** @param {number} year @returns {number} */
437
- const ascendingNodeDeg = (year) => {
438
- const period = -H / 5;
439
- return (((C.earthOrbital.earthAscendingNodeInvPlane + (360.0 * (year - 2000)) / period) % 360) + 360) % 360;
440
- };
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.)
441
432
 
442
433
  // ── Year/day lengths ──────────────────────────────────────────────────────
443
434
  /** @param {number} year @param {number} base @param {Array<[number, number, number]>} harmonics @returns {number} */
@@ -618,6 +609,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
618
609
  });
619
610
  return {
620
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,
621
614
  /** EXPERIMENT accessors: the series' e and equinox-referenced ϖ of date. @param {number} year @returns {number} */
622
615
  eAt: (year) => sampleAt(year).e,
623
616
  /** @param {number} year @returns {number} */
@@ -717,19 +710,11 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
717
710
  for (const k of PLANET_KEYS) massFraction[k] = 1 / C.physicalConstants.massRatioDE440[k];
718
711
  massFraction.earth = massEarthAlone / M_SUN;
719
712
 
720
- const PSI = FL.computePsiConstant({
721
- earthInvPlaneInclinationAmplitude: earthInclAmplitude,
722
- massEarthAlone,
723
- massSun: M_SUN,
724
- });
725
- const eccentricityAmplitudeK = FL.computeKConstant({
726
- eccentricityAmplitude,
727
- massEarthAlone,
728
- massSun: M_SUN,
729
- earthTiltMeanDeg: earthtiltMean,
730
- });
731
- const systemResetN = C.foundational.systemResetN;
732
- const t2000 = 2000 - (balancedYear - systemResetN * H);
713
+ // (plan 07 R6: eccentricityAmplitudeK — the K constant inverted from
714
+ // Earth's calibration — and the t2000 phase offset it fed went with the
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.)
733
718
 
734
719
  /** @param {[number, number]|null} frac @returns {number|null} */
735
720
  const fractionToYears = (frac) => (frac === null ? null : (H * frac[0]) / frac[1]);
@@ -749,34 +734,13 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
749
734
  // obliquity of the spin channel (the IAU pole against the chain's J2000
750
735
  // plane, acute form) — both from the governed artifacts, no fractions.
751
736
  const wobble = computeSecularShape(/** @type {any} */ (CHAIN_ARTIFACT), k).beatYears;
752
- const il = FL.computeInclinationLaw({
753
- fibonacciD: mp.fibonacciD,
754
- massFrac: massFraction[k],
755
- invPlaneInclinationJ2000: ar.invPlaneInclinationJ2000,
756
- longitudePerihelion: ar.longitudePerihelion,
757
- inclinationCycleAnchor: mp.inclinationCycleAnchor,
758
- antiPhase: mp.antiPhase || false,
759
- }, PSI);
760
737
  const obliquityDerived = computeObliquityJ2000Deg({
761
738
  spin: C.planetSpinPhysical[k],
762
739
  anchorInclEclipticDeg: /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements[k].inclEclipticDeg,
763
740
  anchorAscNodeEclipticDeg: /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements[k].ascNodeEclipticDeg,
764
741
  obliquityJ2000Deg: C.earthOrbital.obliquityJ2000_deg,
765
742
  });
766
- const obliquityMean = Math.min(obliquityDerived, 180 - obliquityDerived); // acute: the K law reads sin|ε|
767
- const el = FL.computeEccentricityLaw({
768
- fibonacciD: mp.fibonacciD,
769
- massFrac: massFraction[k],
770
- solarYearInput: ar.solarYearInput,
771
- orbitalEccentricityJ2000: ar.orbitalEccentricityJ2000,
772
- antiPhase: mp.antiPhase || false,
773
- }, {
774
- kConstant: eccentricityAmplitudeK,
775
- obliquityMeanDeg: obliquityMean,
776
- wobblePeriodYears: wobble,
777
- t2000,
778
- meanSolarYearDays,
779
- });
743
+ const obliquityMean = Math.min(obliquityDerived, 180 - obliquityDerived); // acute form of the derived J2000 obliquity
780
744
  PLANET_RECORDS[k] = Object.freeze({
781
745
  name: mp.name,
782
746
  perihelionEclipticYears: ecl,
@@ -789,19 +753,17 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
789
753
  ascendingNodeInvPlane: mp.ascendingNodeInvPlane,
790
754
  inclinationCycleAnchor: mp.inclinationCycleAnchor,
791
755
  invPlaneInclinationJ2000: ar.invPlaneInclinationJ2000,
792
- invPlaneInclinationAmplitude: il.amplitude,
793
- invPlaneInclinationMean: il.mean,
794
756
  obliquityMean,
795
757
  orbitalEccentricityJ2000: ar.orbitalEccentricityJ2000,
796
- orbitalEccentricityAmplitude: el.amplitude,
797
- orbitalEccentricityBase: el.base,
798
- eccentricityPhaseJ2000: el.phaseJ2000,
799
758
  solarYearInput: ar.solarYearInput,
800
759
  axialTiltJ2000: ar.axialTiltJ2000,
801
760
  });
802
761
  // R8 (plan 07): what `model.planets.record()` PUBLISHES is not the record
803
- // above. The record keeps the ψ/K law outputs because the fitting pipeline
804
- // reads them (step 7a derive-eccentricity-amplitudes, the EoC fractions) —
762
+ // above. The internal record kept the ψ/K law outputs for the fitting
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.
766
+ // The separation stands for the same reason it was made —
805
767
  // but nothing outside needs the device, and serving it made /v1/bodies
806
768
  // contradict the model's own chain on the SAME quantities: Mercury's node
807
769
  // cycle 298,060 yr against the chain's 232,001, Jupiter's perihelion period
@@ -898,13 +860,15 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
898
860
  };
899
861
  /** Ecliptic longitude of perihelion, J2000 ecliptic and equinox (the chain's ϖ). @param {string} k @param {number} year @returns {number} */
900
862
  const planetPerihelionDeg = (k, year) => planetChainElementsAt(k, year).lonPeriEclipticDeg;
901
- /** 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} */
902
- const planetAscNodeDeg = (k, year) => {
863
+ /** The S&S longitude origin as an s-frame longitude (J2000-fixed, derived). @returns {number} */
864
+ const nodeOriginSSDeg = () => {
903
865
  if (planetNodeOriginSSDeg === null) {
904
866
  planetNodeOriginSSDeg = computeEquatorNodeOriginSFrameDeg(/** @type {any} */ (CHAIN_ARTIFACT).invariablePlane, C.earthOrbital.obliquityJ2000_deg);
905
867
  }
906
- return convertNodeSFrameToEquatorOriginDeg(planetChainElementsAt(k, year).ascNodeInvPlaneDeg, planetNodeOriginSSDeg);
868
+ return planetNodeOriginSSDeg;
907
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());
908
872
  /** Inclination to the model's invariable plane (exact orbit-normal rotation). @param {string} k @param {number} year @returns {number} */
909
873
  const planetInclinationDeg = (k, year) => planetChainElementsAt(k, year).inclInvPlaneDeg;
910
874
  /** Orbital eccentricity of date, from the same chain elements (R8): the
@@ -913,6 +877,52 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
913
877
  * e-vector does not have. @param {string} k @param {number} year @returns {number} */
914
878
  const planetEccentricity = (k, year) => planetChainElementsAt(k, year).e;
915
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
+
916
926
  // ── Time axis: exact JD ↔ model-year conversion ───────────────────────────
917
927
  // The model's `year` inputs live on the SI axis (the axis the fits were
918
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,21 +63,12 @@ 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,
71
- * eccentricityAmplitude: number, earthTiltMeanDeg: number }} calibration
72
- * @property {Record<string, number>} massFractions
73
66
  */
74
67
 
75
68
  /**
76
69
  * @typedef {Object} PlanetModelRecord
77
- * @property {number} [invPlaneInclinationAmplitude]
78
- * @property {number} [invPlaneInclinationMean]
79
70
  * @property {number} [wobblePeriodYears]
80
71
  * @property {number} [obliquityMeanDeg]
81
- * @property {number} [eccentricityAmplitude]
82
- * @property {number} [eccentricityBase]
83
- * @property {number} [eccentricityPhaseJ2000Deg]
84
72
  * @property {ReturnType<typeof derivePlanetGeometry>} geometry
85
73
  */
86
74
 
@@ -91,25 +79,13 @@ const FL = require('./fibonacci-laws.cjs');
91
79
  * @param {Record<string, PlanetModelBody>} bodies - keyed by body name
92
80
  * (the key selects the body-unique geometry branches: mercury, pluto,
93
81
  * halleys, ceres)
94
- * @returns {{ psiConstant: number, kConstant: number,
95
- * eccentricityAnchor: number, t2000: number,
96
- * bodies: Record<string, PlanetModelRecord> }}
82
+ * @returns {{ bodies: Record<string, PlanetModelRecord> }}
97
83
  */
98
84
  function createPlanetModel(env, bodies) {
99
- const psiConstant = FL.computePsiConstant({
100
- earthInvPlaneInclinationAmplitude: env.calibration.earthInvPlaneInclinationAmplitude,
101
- massEarthAlone: env.calibration.massEarthAlone,
102
- massSun: env.calibration.massSun,
103
- });
104
- const kConstant = FL.computeKConstant({
105
- eccentricityAmplitude: env.calibration.eccentricityAmplitude,
106
- massEarthAlone: env.calibration.massEarthAlone,
107
- massSun: env.calibration.massSun,
108
- earthTiltMeanDeg: env.calibration.earthTiltMeanDeg,
109
- });
110
- // Anchor = balancedYear − systemResetN·H (n=7: the System Reset state).
111
- const eccentricityAnchor = env.balancedYear - env.systemResetN * env.holisticYears;
112
- const t2000 = 2000 - eccentricityAnchor;
85
+ // Plan 07 R6: kConstant, the System-Reset eccentricityAnchor
86
+ // (balancedYear − systemResetN·H) and the t2000 phase it fed went with the
87
+ // eccentricity law; plan 07 R5: psiConstant (ψ = 3·A_earth·√(m_E/m_☉),
88
+ // from env.calibration) went with the inclination law.
113
89
 
114
90
  const geomEnv = {
115
91
  holisticYears: env.holisticYears,
@@ -122,22 +98,9 @@ function createPlanetModel(env, bodies) {
122
98
  /** @type {Record<string, PlanetModelRecord>} */
123
99
  const out = {};
124
100
  for (const [key, b] of Object.entries(bodies)) {
125
- const massFrac = env.massFractions[key];
126
101
  /** @type {PlanetModelRecord} */
127
102
  const rec = /** @type {PlanetModelRecord} */ ({});
128
103
 
129
- if (b.fibonacciD && massFrac && b.invPlaneInclinationJ2000 !== undefined) {
130
- const il = FL.computeInclinationLaw({
131
- fibonacciD: b.fibonacciD, massFrac,
132
- invPlaneInclinationJ2000: b.invPlaneInclinationJ2000,
133
- longitudePerihelion: /** @type {number} */ (b.longitudePerihelion),
134
- inclinationCycleAnchor: /** @type {number} */ (b.inclinationCycleAnchor),
135
- antiPhase: /** @type {boolean} */ (b.antiPhase),
136
- }, psiConstant);
137
- rec.invPlaneInclinationAmplitude = il.amplitude;
138
- rec.invPlaneInclinationMean = il.mean;
139
- }
140
-
141
104
  // Plan 06 Phase 7 commit 2: the K law's cycle period and obliquity input
142
105
  // are INPUTS of the record now — the chain's own g-mode beat
143
106
  // (keplerian-chain computeSecularShape) and the derived J2000 obliquity
@@ -146,27 +109,18 @@ function createPlanetModel(env, bodies) {
146
109
  if (b.wobblePeriodYears !== undefined) rec.wobblePeriodYears = b.wobblePeriodYears;
147
110
  if (b.obliquityMeanDeg !== undefined) rec.obliquityMeanDeg = b.obliquityMeanDeg;
148
111
 
149
- if (b.fibonacciD && massFrac) {
150
- const el = FL.computeEccentricityLaw({
151
- fibonacciD: b.fibonacciD, massFrac,
152
- solarYearInput: b.solarYearInput,
153
- orbitalEccentricityJ2000: /** @type {number} */ (b.orbitalEccentricityJ2000),
154
- antiPhase: /** @type {boolean} */ (b.antiPhase),
155
- }, {
156
- kConstant, obliquityMeanDeg: /** @type {number} */ (rec.obliquityMeanDeg),
157
- wobblePeriodYears: /** @type {number} */ (rec.wobblePeriodYears), t2000,
158
- meanSolarYearDays: env.meanSolarYearDays,
159
- });
160
- rec.eccentricityAmplitude = el.amplitude;
161
- rec.eccentricityBase = el.base;
162
- rec.eccentricityPhaseJ2000Deg = el.phaseJ2000;
163
- }
112
+ // Plan 07 R6: the K law's eccentricityAmplitude / eccentricityBase /
113
+ // eccentricityPhaseJ2000Deg stood here. With the law gone, the ellipse
114
+ // geometry below reads the OBSERVED J2000 eccentricity where it used to
115
+ // read the law's System-Reset base — the observation the chain is
116
+ // anchored on, not a construction. The stored base survives only for the
117
+ // additional bodies, which carry theirs in model-parameters.json.
164
118
 
165
119
  rec.geometry = derivePlanetGeometry({
166
120
  key, type: b.type,
167
121
  solarYearInput: b.solarYearInput,
168
- orbitalEccentricityBase: rec.eccentricityBase !== undefined
169
- ? rec.eccentricityBase : b.orbitalEccentricityBase,
122
+ orbitalEccentricityBase: b.orbitalEccentricityBase !== undefined
123
+ ? b.orbitalEccentricityBase : b.orbitalEccentricityJ2000,
170
124
  longitudePerihelion: b.longitudePerihelion,
171
125
  ascendingNode: b.ascendingNode,
172
126
  rotationPeriodDays: b.rotationPeriodDays,
@@ -176,7 +130,7 @@ function createPlanetModel(env, bodies) {
176
130
  out[key] = rec;
177
131
  }
178
132
 
179
- return { psiConstant, kConstant, eccentricityAnchor, t2000, bodies: out };
133
+ return { bodies: out };
180
134
  }
181
135
 
182
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,23 +37,10 @@ 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
- eccentricityAmplitude: number;
45
- earthTiltMeanDeg: number;
46
- };
47
- massFractions: Record<string, number>;
48
40
  };
49
41
  export type PlanetModelRecord = {
50
- invPlaneInclinationAmplitude?: number | undefined;
51
- invPlaneInclinationMean?: number | undefined;
52
42
  wobblePeriodYears?: number | undefined;
53
43
  obliquityMeanDeg?: number | undefined;
54
- eccentricityAmplitude?: number | undefined;
55
- eccentricityBase?: number | undefined;
56
- eccentricityPhaseJ2000Deg?: number | undefined;
57
44
  geometry: ReturnType<typeof derivePlanetGeometry>;
58
45
  };
59
46
  /**
@@ -85,20 +72,11 @@ export type PlanetModelRecord = {
85
72
  * @property {number} currentAUDistanceKm
86
73
  * @property {number} earthEccentricityJ2000
87
74
  * @property {number} earthPerihelionLongitudeJ2000Deg
88
- * @property {{ earthInvPlaneInclinationAmplitude: number,
89
- * massEarthAlone: number, massSun: number,
90
- * eccentricityAmplitude: number, earthTiltMeanDeg: number }} calibration
91
- * @property {Record<string, number>} massFractions
92
75
  */
93
76
  /**
94
77
  * @typedef {Object} PlanetModelRecord
95
- * @property {number} [invPlaneInclinationAmplitude]
96
- * @property {number} [invPlaneInclinationMean]
97
78
  * @property {number} [wobblePeriodYears]
98
79
  * @property {number} [obliquityMeanDeg]
99
- * @property {number} [eccentricityAmplitude]
100
- * @property {number} [eccentricityBase]
101
- * @property {number} [eccentricityPhaseJ2000Deg]
102
80
  * @property {ReturnType<typeof derivePlanetGeometry>} geometry
103
81
  */
104
82
  /**
@@ -108,15 +86,9 @@ export type PlanetModelRecord = {
108
86
  * @param {Record<string, PlanetModelBody>} bodies - keyed by body name
109
87
  * (the key selects the body-unique geometry branches: mercury, pluto,
110
88
  * halleys, ceres)
111
- * @returns {{ psiConstant: number, kConstant: number,
112
- * eccentricityAnchor: number, t2000: number,
113
- * bodies: Record<string, PlanetModelRecord> }}
89
+ * @returns {{ bodies: Record<string, PlanetModelRecord> }}
114
90
  */
115
91
  export function createPlanetModel(env: PlanetModelEnv, bodies: Record<string, PlanetModelBody>): {
116
- psiConstant: number;
117
- kConstant: number;
118
- eccentricityAnchor: number;
119
- t2000: number;
120
92
  bodies: Record<string, PlanetModelRecord>;
121
93
  };
122
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,108 +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
- /**
76
- * K constant from Earth's calibration. @param {{
77
- * eccentricityAmplitude: number, massEarthAlone: number, massSun: number,
78
- * earthTiltMeanDeg: number }} c @returns {number} */
79
- function computeKConstant(c) {
80
- return c.eccentricityAmplitude * Math.sqrt(c.massEarthAlone / c.massSun)
81
- / (Math.sin(c.earthTiltMeanDeg * Math.PI / 180) * Math.sqrt(3));
82
- }
83
-
84
- /**
85
- * K law: eccentricity amplitude, base and J2000 phase.
86
- * @param {{ fibonacciD: number, massFrac: number, solarYearInput: number,
87
- * orbitalEccentricityJ2000: number, antiPhase: boolean }} b
88
- * @param {{ kConstant: number, obliquityMeanDeg: number,
89
- * wobblePeriodYears: number, t2000: number, meanSolarYearDays: number }} env
90
- * @returns {{ amplitude: number, base: number, phaseJ2000: number }} */
91
- function computeEccentricityLaw(b, env) {
92
- const a = Math.pow(b.solarYearInput / env.meanSolarYearDays, 2 / 3);
93
- const amplitude = env.kConstant * Math.sin(Math.abs(env.obliquityMeanDeg) * Math.PI / 180) * Math.sqrt(b.fibonacciD)
94
- / (Math.sqrt(b.massFrac) * Math.pow(a, 1.5));
95
- const eJ2000 = b.orbitalEccentricityJ2000;
96
- const phaseOffset = b.antiPhase ? 270 : 90;
97
- const phaseDeg = (env.t2000 / env.wobblePeriodYears) * 360 + phaseOffset;
98
- const cosTheta = Math.cos(phaseDeg * Math.PI / 180);
99
- const sinTheta = Math.sin(phaseDeg * Math.PI / 180);
100
- const disc = eJ2000 * eJ2000 - amplitude * amplitude * sinTheta * sinTheta;
101
- const base = amplitude * cosTheta + Math.sqrt(Math.max(0, disc));
102
- return { amplitude, base, phaseJ2000: ((phaseDeg % 360) + 360) % 360 };
103
- }
104
-
105
- module.exports = {
106
- computePsiConstant, computeInclinationLaw,
107
- computeKConstant, computeEccentricityLaw,
108
- };
@@ -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,61 +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
- };
28
- /**
29
- * K constant from Earth's calibration. @param {{
30
- * eccentricityAmplitude: number, massEarthAlone: number, massSun: number,
31
- * earthTiltMeanDeg: number }} c @returns {number} */
32
- export function computeKConstant(c: {
33
- eccentricityAmplitude: number;
34
- massEarthAlone: number;
35
- massSun: number;
36
- earthTiltMeanDeg: number;
37
- }): number;
38
- /**
39
- * K law: eccentricity amplitude, base and J2000 phase.
40
- * @param {{ fibonacciD: number, massFrac: number, solarYearInput: number,
41
- * orbitalEccentricityJ2000: number, antiPhase: boolean }} b
42
- * @param {{ kConstant: number, obliquityMeanDeg: number,
43
- * wobblePeriodYears: number, t2000: number, meanSolarYearDays: number }} env
44
- * @returns {{ amplitude: number, base: number, phaseJ2000: number }} */
45
- export function computeEccentricityLaw(b: {
46
- fibonacciD: number;
47
- massFrac: number;
48
- solarYearInput: number;
49
- orbitalEccentricityJ2000: number;
50
- antiPhase: boolean;
51
- }, env: {
52
- kConstant: number;
53
- obliquityMeanDeg: number;
54
- wobblePeriodYears: number;
55
- t2000: number;
56
- meanSolarYearDays: number;
57
- }): {
58
- amplitude: number;
59
- base: number;
60
- phaseJ2000: number;
61
- };
@@ -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;