@essrt/physics 6.0.1 → 8.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/src/model.js CHANGED
@@ -21,7 +21,6 @@ import { deriveEpochParams } from './layer0/derive-params.js';
21
21
  import * as FL from './planets/fibonacci-laws.cjs';
22
22
  import * as planetOrientation from './planets/orientation.cjs';
23
23
  import { createPhaseMachinery } from './phase/index.cjs';
24
- import { createCardinalModel } from './cardinal/index.cjs';
25
24
  import { createYearLengths, ONE_FAMILY_WINDOW_YEARS } from './earth/year-lengths.cjs';
26
25
  import { createDeepOrbitalHistory } from './earth/deep-orbital-history.cjs';
27
26
  import { CHAIN_ARTIFACT } from './planets/chain-artifact.js';
@@ -30,7 +29,6 @@ import { createDeltaTCycles } from './deltat/cycles.cjs';
30
29
  import { createDeepTimeLod } from './deltat/deep-time.cjs';
31
30
  import { createMoonRecessionHistory, createSolarChannelBudget } from './deltat/recession-history.cjs';
32
31
  import { evalClimateL1OrbitalPermil, createAlphaGiaChannel } from './climate/l1-orbital.cjs';
33
- import { createMoonEccChannel } from './moon/ecc-channel.cjs';
34
32
  import { createDeepEccChannel } from './moon/deep-ecc-channel.cjs';
35
33
  import { DEEP_MODES_ARTIFACT } from './moon/deep-modes-artifact.cjs';
36
34
  import { createMoonMonthChain } from './moon/month-chain.cjs';
@@ -38,7 +36,6 @@ import { createChainCycleIntegrator } from './chain-cycles/index.cjs';
38
36
  import { createMoonArguments, jdToDecimalYear } from './moon/arguments.cjs';
39
37
  import { createMoonSeries } from './moon/series.cjs';
40
38
  import { createSunPlanetaryCompletion } from './eclipse/sun-planetary-completion.cjs';
41
- import { moonSeriesExtensionDeg } from './moon/series-extension.cjs';
42
39
  import { createEclipseFinders } from './eclipse/finders.cjs';
43
40
  import { createBesselian } from './eclipse/besselian.cjs';
44
41
  import { driver2PeriodSecondsAtAge } from './planets/orbit-chain.cjs';
@@ -103,7 +100,6 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
103
100
  // wobble-marker distance). It is the K calibration input ONLY — Earth's
104
101
  // eccentricity law does not use it; see eccentricityAt (base' derived) below.
105
102
  const eccentricityAmplitude = C.earth.eccentricityAmplitude;
106
- const earthRAAngle = 2 * earthInclAmplitude - (earthInclAmplitude * earthInclAmplitude) / earthtiltMean;
107
103
  const earthInclMean = C.earthOrbital.earthInclinationJ2000_deg
108
104
  - earthInclAmplitude * Math.cos(((C.earthOrbital.earthPerihelionLongitudeJ2000
109
105
  - C.earthOrbital.earthInclinationCycleAnchor) * Math.PI) / 180);
@@ -232,26 +228,29 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
232
228
 
233
229
  const DT = F.DT_STACK;
234
230
  const RES = F.DT_RESONATOR;
231
+ // Periods STATED IN YEARS (the fit files' period_yr / T0_yr): plan 06 T5
232
+ // measured the former 8H/n labels chance-level; layer B item 3 removed the
233
+ // divisor from every runtime. Twins: tools/lib/deep-time.js, src/script.js,
234
+ // the website's essrt.ts.
235
235
  const dtCycles = createDeltaTCycles({
236
- eightHYears: 8 * H,
237
236
  taperFullHalfwidthYears: C.deepTime.dtStackTaperFullHalfwidthYr,
238
237
  taperTotalHalfwidthYears: C.deepTime.dtStackTaperTotalHalfwidthYr,
239
238
  tropicalYearSecondsJ2000: meanTropicalYearJ2000Seconds,
240
239
  cycles: {
241
- bond: { latticeN: DT.bond.lattice_n, cosCoeffSeconds: DT.bond.cos_coeff_s, sinCoeffSeconds: DT.bond.sin_coeff_s },
242
- hallstatt: { latticeN: DT.hallstatt.lattice_n, cosCoeffSeconds: DT.hallstatt.cos_coeff_s, sinCoeffSeconds: DT.hallstatt.sin_coeff_s },
243
- jose5: { latticeN: DT.jose5.lattice_n, cosCoeffSeconds: DT.jose5.cos_coeff_s, sinCoeffSeconds: DT.jose5.sin_coeff_s },
244
- jose4: { latticeN: DT.jose4.lattice_n, cosCoeffSeconds: DT.jose4.cos_coeff_s, sinCoeffSeconds: DT.jose4.sin_coeff_s },
240
+ bond: { periodYears: DT.bond.period_yr, cosCoeffSeconds: DT.bond.cos_coeff_s, sinCoeffSeconds: DT.bond.sin_coeff_s },
241
+ hallstatt: { periodYears: DT.hallstatt.period_yr, cosCoeffSeconds: DT.hallstatt.cos_coeff_s, sinCoeffSeconds: DT.hallstatt.sin_coeff_s },
242
+ jose5: { periodYears: DT.jose5.period_yr, cosCoeffSeconds: DT.jose5.cos_coeff_s, sinCoeffSeconds: DT.jose5.sin_coeff_s },
243
+ jose4: { periodYears: DT.jose4.period_yr, cosCoeffSeconds: DT.jose4.cos_coeff_s, sinCoeffSeconds: DT.jose4.sin_coeff_s },
245
244
  },
246
245
  resonator: {
247
- t0LatticeN: RES.T0_lattice_n,
246
+ t0Years: RES.T0_yr,
248
247
  q: RES.Q,
249
248
  kicks: RES.kick_epochs_year.map(/** @param {number} t @param {number} i */ (t, i) => ({
250
249
  tYear: t,
251
250
  cosSeconds: RES.kick_coefficients_s[i].cos,
252
251
  sinSeconds: RES.kick_coefficients_s[i].sin,
253
252
  })),
254
- tones: RES.drive_tones.map(/** @param {{dn: number, phi_locked_rad: number, amp_s: number}} t */ (t) => ({ dn: t.dn, phiLockedRad: t.phi_locked_rad, ampSeconds: t.amp_s })),
253
+ tones: RES.drive_tones.map(/** @param {{period_yr: number, phi_locked_rad: number, amp_s: number}} t */ (t) => ({ periodYears: t.period_yr, phiLockedRad: t.phi_locked_rad, ampSeconds: t.amp_s })),
255
254
  },
256
255
  });
257
256
 
@@ -346,14 +345,21 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
346
345
 
347
346
  // ── Earth scalars (integrated-phase display semantics) ────────────────────
348
347
  /** @param {number} year @returns {number} */
349
- const earthPerihelionDeg = laws.perihelionLongitudeDegAt ?? ((year) => {
350
- let longitude = 270.0 + 360.0 * cyclesBetween(balancedYear, year, 16);
351
- for (const [div, sinC, cosC] of F.PERI_HARMONICS_RAW) {
352
- const ph = phaseRadians(balancedYear, year, div);
353
- longitude += sinC * Math.sin(ph) + cosC * Math.cos(ph);
354
- }
355
- return (((longitude + F.PERI_OFFSET) % 360) + 360) % 360;
356
- });
348
+ // Plan 06 layer B — the eclipse Sun onto the series. The PUBLISHED Earth
349
+ // ϖ of date is the one-source series (the secular orbit; its J2000 element
350
+ // ≡ La2004's — the same field the D4c apsidal wheel renders). The SUN's
351
+ // equation of centre needs the MEAN elements of the era: the series plus the
352
+ // derived mean offset of the osculating channel (F.EARTH_OSCULATING_MEAN_
353
+ // OFFSET, tools/verify/earth-osculating-offset.js — the 1890–2110 mean of
354
+ // osculating − secular from the Horizons-seeded nine-body run). Measured:
355
+ // the fast osculating wobble is carried by the completion's direct terms and
356
+ // double-counts if added; the window mean is what the Sun residual holds
357
+ // (all-phase JPL sd 3.23″ → 1.59″; the former K law's Standish anchor
358
+ // 1.58″). The former K perihelion Fourier law (H divisors) is retired here.
359
+ const earthPerihelionDeg = (year) => oneSourceM.periOfDateDegAt(year);
360
+ const SUN_OFF = F.EARTH_OSCULATING_MEAN_OFFSET;
361
+ /** The eclipse Sun's ϖ of date (mean elements). @param {number} year @returns {number} */
362
+ const sunPerihelionDegAt = laws.perihelionLongitudeDegAt ?? ((year) => oneSourceM.periOfDateDegAt(year) + SUN_OFF.dPomArcsec / 3600);
357
363
  /** @param {number} year @returns {number} */
358
364
  const obliquityDeg = (year) => {
359
365
  let obliq = solsticeObliquityMean;
@@ -395,13 +401,10 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
395
401
  // (θ₀ = ϖ_ICRF(J2000) − 21.77° = 81.178°, the System-Reset anchor in
396
402
  // anchor form — doc 66 §1); base' is derived inside from the observed
397
403
  // J2000 eccentricity.
398
- const moonEcc = createMoonEccChannel({
399
- cyclesBetween,
400
- eccentricityBase: C.earth.eccentricityBase,
401
- perihelionLongitudeJ2000Deg: C.earthOrbital.earthPerihelionLongitudeJ2000,
402
- inclinationCycleAnchorDeg: C.earthOrbital.earthInclinationCycleAnchor,
403
- eccentricityJ2000: C.earthOrbital.earthEccentricityJ2000,
404
- });
404
+ // (The H/3 eccentricity-law channel — createMoonEccChannel — left this
405
+ // package with plan 06 layer B: the Sun's e is the one-source series plus
406
+ // the derived mean offset above; the browser's scene Sun still reads the
407
+ // law through its own _moonEcc until the cardinal points move.)
405
408
  // Engine-switch decision (ii) (plan 02 §8): the ONE deep e — the engine's
406
409
  // own ±10-Myr mode table, anchored form — feeds the ENTIRE lunar chain
407
410
  // (modulation, cycle counts, arguments eccAt/channelIntegral, E-factor).
@@ -411,10 +414,15 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
411
414
  // laws hook deliberately does NOT reach this channel.
412
415
  const deepEcc = createDeepEccChannel(DEEP_MODES_ARTIFACT);
413
416
  /** @param {number} year @returns {number} */
414
- const eccentricityAt = laws.eccentricityAt ?? ((year) => moonEcc.eccAt(year - 2000));
415
- /** de/dyear of the one law — the cardinal braid's equation-of-centre
416
- * derivative rides it. @param {number} year @returns {number} */
417
- const eccentricityRateAt = laws.eccentricityRateAt ?? ((year) => moonEcc.eccRateAt(year - 2000));
417
+ // Plan 06 layer B: the PUBLISHED Earth eccentricity is the one-source series
418
+ // (|z|, the secular orbit); the SUN's e adds the derived mean offset (see
419
+ // sunPerihelionDegAt). The former H/3 eccentricity law is retired here.
420
+ const eccentricityAt = (year) => oneSourceM.eAt(year);
421
+ /** The eclipse Sun's e (mean elements). @param {number} year @returns {number} */
422
+ const sunEccentricityAt = laws.eccentricityAt ?? ((year) => oneSourceM.eAt(year) + SUN_OFF.dE);
423
+ /** de/dyear of the Sun's e — the cardinal braid's equation-of-centre
424
+ * derivative rides it (±0.5-yr central difference; the constant offset
425
+ * cancels). @param {number} year @returns {number} */
418
426
  /** @param {number} year @returns {number} */
419
427
  const inclinationDeg = (year) => earthInclMean
420
428
  - earthInclAmplitude * Math.cos(phaseRadians(balancedYear, year, 3));
@@ -490,39 +498,13 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
490
498
  + RA_DAY_OFFSET_ECC_MS * Math.cos(phaseRadians(balancedYear, year, 16))
491
499
  + RA_DAY_OFFSET_OBLIQ_MS * Math.cos(phaseRadians(balancedYear, year, 8));
492
500
 
493
- // ── Cardinal-point model ──────────────────────────────────────────────────
494
- const cardinalM = createCardinalModel({
495
- isDeepTime: () => true,
496
- constants: {
497
- anchors: F.CARDINAL_POINT_ANCHORS_ADJUSTED,
498
- harmonics: F.CARDINAL_POINT_HARMONICS,
499
- eccTerms: F.CARDINAL_POINT_ECC_TERMS,
500
- jointTerms: F.CARDINAL_POINT_JOINT_TERMS,
501
- derived: F.CARDINAL_POINT_DERIVED,
502
- tropicalHarmonics: F.TROPICAL_YEAR_HARMONICS,
503
- balancedYear,
504
- meanSolarYearDays,
505
- hJ2000: H,
506
- tiltMeanDeg: earthtiltMean,
507
- raAngleDeg: earthRAAngle,
508
- inclAmplitudeDeg: earthInclAmplitude,
509
- },
510
- fns: {
511
- cyclesBetween: (a, b, n) => phase().cyclesBetween(a, b, n),
512
- // The frozen era clock's deps ride ITS convention H_era (plan 06 D8):
513
- // drift integrand, dc/dY and the real-LOD convention were fitted on it.
514
- analyticTropicalDays: (year) => {
515
- const tMa = (startmodelYear - year) / 1e6;
516
- const Ht = deepLod.eraClockHAtAge(tMa);
517
- if (Ht === null) return null;
518
- return (deepLod.siderealYearSecondsAtAge(tMa) / 86400) * (1 - 13 / Ht);
519
- },
520
- meanHAtAgeMa: (tMa) => deepLod.eraClockHAtAge(tMa),
521
- meanYearRealLodDays: (tMa) => deepLod.eraClockYearInDaysAtAge(tMa),
522
- eccentricityAt,
523
- eccentricityRateAt,
524
- },
525
- });
501
+ // ── Cardinal points (the fitted model retired, R1)──────────────────────────────────────────────────
502
+ // Plan 06 R1: the fitted cardinal-point model (CARDINAL_POINT_* — the §10
503
+ // derived form on the frozen era clock, fitted to the retired K scene's
504
+ // events; measured 60–140 min from Meeus ch. 27 at 0..−1000 and 6–25 min
505
+ // in 1500–2900) left this package. The instants are the CROSSINGS of the
506
+ // one Sun the scene renders and the eclipse chain certifies — see
507
+ // `cardinalCrossingJdUT` below the finders.
526
508
  // ── One-source cardinal structure (D4b) ───────────────────────────────────
527
509
  // The EoC layer (year lengths, crossing offsets, the e(t)-proportional
528
510
  // spread) on the one-source movement's own e(t)/ϖ(t) — the MODE tier (the
@@ -625,14 +607,28 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
625
607
  });
626
608
  return {
627
609
  yearLengths,
610
+ /** EXPERIMENT accessors: the series' e and equinox-referenced ϖ of date. @param {number} year @returns {number} */
611
+ eAt: (year) => sampleAt(year).e,
612
+ /** @param {number} year @returns {number} */
613
+ periOfDateDegAt: (year) => sampleAt(year).periOfDateDeg,
628
614
  /** The hybrid's obliquity at a decimal year, degrees — the banked series inside its span, the α(t)-coupled ζ-tail integration beyond (the deep sampler grows ~0.1 s/Myr). @param {number} year @returns {number} */
629
615
  epsAt: (year) => sampleAt(year).epsDeg,
630
616
  };
631
617
  })();
632
618
  const yearLengthsM = oneSourceM.yearLengths;
619
+ /** THE one-family tropical year of date in SI seconds: route B inside the
620
+ * one-family window, the tidal-chain year beyond (plan 06 R1 — the Sun's
621
+ * mean longitude, the cardinal instants and the published year lengths
622
+ * all ride this one function). @param {number} year @returns {number} */
623
+ const oneFamilyTropicalYearSeconds = (year) => (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS
624
+ ? yearLengthsM.tropicalYearSecondsAtYear(year)
625
+ : (deepLod.tropicalYearSecondsAtAge(yearToTMa(year)) ?? meanSolarYearDays * 86400));
633
626
 
634
- /** Tropical year: mean of the four cardinal intervals. @param {number} year @returns {number} */
635
- const tropicalYearDays = (year) => cardinalM.computeTropicalYearLength(year);
627
+ /** Tropical year of date in DAYS OF THE EPOCH (the kinematic day below) —
628
+ * the one-family SI year over the epoch's day length, so solarYearSeconds
629
+ * ≡ the one-family year. Plan 06 R1: formerly the fitted cardinal model's
630
+ * 4-mean. @param {number} year @returns {number} */
631
+ const tropicalYearDays = (year) => oneFamilyTropicalYearSeconds(year) / dayLengthSeconds(year);
636
632
  /** @param {number} year @returns {number} */
637
633
  const tropicalYearDirectDays = (year) => evalYearFourier(year, tropicalYearDaysBase(year), F.TROPICAL_YEAR_HARMONICS);
638
634
 
@@ -651,7 +647,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
651
647
  const syS = solarYearSeconds(year);
652
648
  const syD = tropicalYearDays(year);
653
649
  const sidDay = siderealDaySeconds(year);
654
- const raProjection = Math.cos((obliquityDeg(year) * Math.PI) / 180);
650
+ // Layer A (plan 06): the RA projection reads the published ε (the one-source
651
+ // hybrid), not the K comb — the browser's _sceneEpsTargetDeg twin; 1e-8 s.
652
+ const raProjection = Math.cos((oneSourceM.epsAt(year) * Math.PI) / 180);
655
653
  return (syS / (syD + 1) / Tp / (syD + 1)) * raProjection + sidDay;
656
654
  };
657
655
  /** @param {number} year @returns {number} */
@@ -926,20 +924,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
926
924
  return (dr === null || t === null) ? null : dr - t;
927
925
  };
928
926
 
929
- // Snapshot-phase obliquity — the engine's computeObliquityEarth convention
930
- // for the lunar chain (linear H-lattice phase; orbital-engine.js). NOT the
931
- // integrated-phase display obliquity above — the chain was certified
932
- // against this form.
933
- /** @param {number} year @returns {number} */
934
- const obliquitySnapshotDeg = (year) => {
935
- const t = year - balancedYear;
936
- let obliq = solsticeObliquityMean;
937
- for (const [div, sinC, cosC] of F.SOLSTICE_OBLIQUITY_HARMONICS) {
938
- const ph = (2 * Math.PI * t) / (H / div);
939
- obliq += sinC * Math.sin(ph) + cosC * Math.cos(ph);
940
- }
941
- return obliq;
942
- };
927
+ // (The K snapshot-phase obliquity the lunar chain was certified against left
928
+ // with layer A / item 3c: the arguments' carrier reads the published ε.)
943
929
 
944
930
  // The argument skeleton (the _FW_MOON bundle; Sun secular deviations on
945
931
  // the CALENDAR year coordinate — S3)
@@ -964,7 +950,10 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
964
950
  fns: {
965
951
  eccAt: /** @param {number} tYr */ (tYr) => deepEcc.eccAt(tYr),
966
952
  channelIntegral: /** @param {number} T @param {number} s */ (T, s) => deepEcc.channelIntegral(T, s),
967
- computeObliquityEarth: obliquitySnapshotDeg,
953
+ // Layer A / item 3c (plan 06): the arguments' obliquity carrier reads the
954
+ // PUBLISHED ε (the one-source hybrid), not the K snapshot comb — one ε
955
+ // everywhere. Twins: script.js (_sceneEpsTargetDeg), scene-graph.js.
956
+ computeObliquityEarth: /** @param {number} year @returns {number} */ (year) => oneSourceM.epsAt(year),
968
957
  jdToSIyear: yearFromJD,
969
958
  tropicalOrbitsBetween: mcTropical,
970
959
  apsidalOfDateCyclesBetween: mcApsidalOfDate,
@@ -1104,65 +1093,73 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1104
1093
  // same movement and is retired by the unification (measured identical on
1105
1094
  // every modern gate).
1106
1095
  const sunMeanLongitudeDegAt = (() => {
1107
- // SW PHASE B — the CLOSED FORM on the integrated lattice phase
1108
- // (supersedes the E5 ±3,000-yr trapezoid table; valid at EVERY epoch,
1109
- // no table, no domain window). drift = D_smooth + Σₖ D_k + D_torque:
1110
- // · D_smooth — the deep chain's smooth SI tropical-year physics
1111
- // (tropicalYearDaysBase × LOD), two-point trapezoid: the same
1112
- // structure the scene twin uses, so the scene δ stays bounded.
1113
- // · Each oscillatory term integrates ANALYTICALLY: the antiderivative
1114
- // of cos/sin on the integrated phase is (H/2πk)·[sin/−cos] — the H
1115
- // drift lives in the phase itself; the amplitude factor uses H_J2000
1116
- // (ppm-class difference, negligible).
1117
- // · THE REBASE (E5's corpus-preferred J2000 rate anchor) is applied
1118
- // PER HARMONIC as a SIN-SATURATED ramp, (H/2πk)·sin(Δφₖ): equal to
1119
- // the linear rebase for |Δφₖ| ≪ 1 (the corpus era) and bounded by
1120
- // the harmonic's own period beyond — extending a linearized local
1121
- // slope to deep time would be the fitted-linear-slope trap. Every
1122
- // quantity is derived; zero new constants; each component and its
1123
- // slope vanish at year 2000 by construction (the drift-only
1124
- // property is exact, not tabulated).
1125
- const RATE_LIN = sunTropicalRateDegPerCy / 100; // deg / SI yr
1126
- const precessionP0DegPerYr = 13 * 360 / H;
1127
- const tanEps = Math.tan(earthtiltMean * Math.PI / 180);
1128
- const A_RAD = earthInclAmplitude * Math.PI / 180;
1129
- /** @param {number} year @returns {number} */
1130
- const rateSmooth = (year) => 360 * (365.25 * 86400)
1131
- / (tropicalYearDaysBase(year)
1132
- * (deepLod.lodSecondsAtAge(yearToTMa(year)) ?? meanLengthOfDay));
1133
- /** @type {{rateSm0: number, ph0: Map<number, number>}|null} */
1134
- let anchor = null;
1135
- return /** @param {number} year @returns {number} */ (year) => {
1136
- if (anchor === null) {
1137
- const ph0 = new Map();
1138
- for (const [k] of F.TROPICAL_YEAR_HARMONICS) ph0.set(k, phaseRadians(balancedYear, 2000, k));
1139
- for (const k of [3, 8]) if (!ph0.has(k)) ph0.set(k, phaseRadians(balancedYear, 2000, k));
1140
- anchor = { rateSm0: rateSmooth(2000), ph0 };
1141
- }
1096
+ // Plan 06 R1 — the Sun's mean longitude of date is the INTEGRAL of the
1097
+ // one-source tropical year: L(t) = L0 + 360·∫_{2000}^{t} dt′/T_trop(t′),
1098
+ // T_trop the route-B tropical year of date in SI seconds (the ONE
1099
+ // year-length family; the frozen era clock's tropicalYearDaysBase × LOD
1100
+ // and its comb ripple + torque term left here). MEASURED before the
1101
+ // move (tools/explore r1 probes, plan 06 record): the route-B tropical
1102
+ // year equals the IAU/Laskar drift expression to 0.1 s over −1000..+3000
1103
+ // while the era-clock year carried NO secular drift (+1.09 s flat), so
1104
+ // the former mean longitude wandered +12 min around 500 AD and diverged
1105
+ // −3.3 min/cy after 2100 against both Meeus ch. 27 and the one-source
1106
+ // integral — outside every eclipse and JPL gate. MODERN-WINDOW COST,
1107
+ // stated (plan 06 I1 corrected an earlier misreading of the cache's
1108
+ // clock): against the JPL Sun cache (UT instants, verified; the registry
1109
+ // instrument's bridge) the one-source Sun read 1.29″ sd in 1970–2049
1110
+ // where the era-clock Sun read 0.80″ — both carried ~0.02–0.03″/yr
1111
+ // trends against JPL over 1900–2100; accepted because over millennia
1112
+ // the era-clock Sun is minutes off. Plan 06 I2 located the trend: the
1113
+ // Earth–Mars–Jupiter long inequality (1783 yr, 6.3″) the smooth year
1114
+ // cannot carry — derived on the model's own N-body and shipped in the
1115
+ // planetary completion, the instrument reads 1.03″ (Horizons ±3000 yr:
1116
+ // modern-window mean +8.4 → +0.8″, per-millennium sd roughly halved).
1117
+ // NUMERICS (rate vs point value): a cumulative trapezoid table of
1118
+ // cycles, yearly inside ±20,000 yr and per century beyond, grown on
1119
+ // demand from 2000 in both directions and interpolated inside a cell —
1120
+ // never a rate multiplied by a span. Beyond the one-family window the
1121
+ // tidal-chain year continues the integrand (the scene overlay ends at
1122
+ // 20,000 yr; the finders refuse out-of-domain epochs upstream).
1123
+ const JULIAN_YEAR_S = 365.25 * 86400;
1124
+ const tropSec = oneFamilyTropicalYearSeconds;
1125
+ const FINE_SPAN = 20000, COARSE_STEP = 100;
1126
+ /** cycles from 2000 to 2000 ± i·step (fwd/bwd tiers) */
1127
+ /** @type {number[]} */ const fwdFine = [0];
1128
+ /** @type {number[]} */ const bwdFine = [0];
1129
+ /** @type {number[]} */ const fwdCoarse = [];
1130
+ /** @type {number[]} */ const bwdCoarse = [];
1131
+ /** UNSIGNED trapezoid cycles over the span between two years (Julian
1132
+ * years → SI seconds over the one-family year); the caller applies the
1133
+ * direction sign. @param {number} ya @param {number} yb @returns {number} */
1134
+ const segAbs = (ya, yb) => 0.5 * (Math.abs(yb - ya) * JULIAN_YEAR_S) * (1 / tropSec(ya) + 1 / tropSec(yb));
1135
+ /** signed cycles from 2000 to `year` @param {number} year @returns {number} */
1136
+ const cyclesTo = (year) => {
1142
1137
  const dy = year - 2000;
1143
- // smooth deep physics (two-point trapezoid; exact 0 at 2000)
1144
- let drift = 0.5 * (rateSmooth(year) - anchor.rateSm0) * dy;
1145
- // year-harmonic ripple: rate ≈ −(RATE_LIN/T̄)·h(y); antiderivative +
1146
- // sin-saturated per-harmonic rebase
1147
- const HK = H / (2 * Math.PI);
1148
- for (const [k, sK, cK] of F.TROPICAL_YEAR_HARMONICS) {
1149
- const ph = phaseRadians(balancedYear, year, k);
1150
- const ph0 = /** @type {number} */ (anchor.ph0.get(k));
1151
- const scale = -(RATE_LIN / meanSolarYearDays) * (HK / k);
1152
- const anti = (-sK) * (Math.cos(ph) - Math.cos(ph0)) + cK * (Math.sin(ph) - Math.sin(ph0));
1153
- const h0 = sK * Math.sin(ph0) + cK * Math.cos(ph0);
1154
- drift += scale * (anti - h0 * Math.sin(ph - ph0));
1155
- }
1156
- // torque (E5): rate = −p₀·tanε·δε, δε = A(−cos φ₃ + cos φ₈); same
1157
- // antiderivative + per-component sin-saturated rebase
1158
- for (const [k, sgn] of [[3, -1], [8, 1]]) {
1159
- const ph = phaseRadians(balancedYear, year, k);
1160
- const ph0 = /** @type {number} */ (anchor.ph0.get(k));
1161
- const scale = -precessionP0DegPerYr * tanEps * A_RAD * sgn * (HK / k);
1162
- drift += scale * ((Math.sin(ph) - Math.sin(ph0)) - Math.cos(ph0) * Math.sin(ph - ph0));
1138
+ const s = dy >= 0 ? 1 : -1, a = Math.abs(dy);
1139
+ // WARM THE SAMPLER TO THE TARGET FIRST (plan 06 R4, measured): the
1140
+ // table below grows outward in 1-yr / 100-yr steps, and every step
1141
+ // beyond ~1.6 Myr enlarged the one-family sampler's >2-Myr tier just
1142
+ // past its span, rebuilding the whole deep integration about every 32
1143
+ // steps — ~125 rebuilds and 22 s for the first deep-epoch call (the
1144
+ // scene's first jump to −5 Myr), 0.4 ms once built. One read at the
1145
+ // clamped target builds the tier at its final span; the grid stores
1146
+ // are endpoint-independent, so every value is bit-identical.
1147
+ tropSec(2000 + s * Math.min(a, ONE_FAMILY_WINDOW_YEARS));
1148
+ const fine = s > 0 ? fwdFine : bwdFine;
1149
+ const growFine = (/** @type {number} */ upto) => { while (fine.length <= upto) { const k = fine.length; fine.push(fine[k - 1] + s * segAbs(2000 + s * (k - 1), 2000 + s * k)); } };
1150
+ if (a <= FINE_SPAN) {
1151
+ const i = Math.floor(a), f = a - i;
1152
+ growFine(i + 1);
1153
+ return fine[i] + f * (fine[i + 1] - fine[i]);
1163
1154
  }
1164
- return sunL0Deg + sunTropicalRateDegPerCy * dy / 100 + drift;
1155
+ growFine(FINE_SPAN);
1156
+ const coarse = s > 0 ? fwdCoarse : bwdCoarse;
1157
+ if (coarse.length === 0) coarse.push(fine[FINE_SPAN]);
1158
+ const c = (a - FINE_SPAN) / COARSE_STEP, ci = Math.floor(c), cf = c - ci;
1159
+ while (coarse.length <= ci + 1) { const k = coarse.length; const y0 = 2000 + s * (FINE_SPAN + (k - 1) * COARSE_STEP); coarse.push(coarse[k - 1] + s * segAbs(y0, y0 + s * COARSE_STEP)); }
1160
+ return coarse[ci] + cf * (coarse[ci + 1] - coarse[ci]);
1165
1161
  };
1162
+ return /** @param {number} year @returns {number} */ (year) => sunL0Deg + 360 * cyclesTo(year);
1166
1163
  })();
1167
1164
 
1168
1165
  // Eclipse finders — wired like the engine probe (tools/verify/
@@ -1180,8 +1177,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1180
1177
  frameworkSun: {
1181
1178
  sunMeanLongitudeJ2000Deg: sunL0Deg,
1182
1179
  tropicalRateDegPerCy: sunTropicalRateDegPerCy,
1183
- eccentricityAt,
1184
- perihelionLongitudeDegAt: earthPerihelionDeg,
1180
+ eccentricityAt: sunEccentricityAt,
1181
+ perihelionLongitudeDegAt: sunPerihelionDegAt,
1185
1182
  meanLongitudeDegAt: sunMeanLongitudeDegAt,
1186
1183
  },
1187
1184
  constants: {
@@ -1204,20 +1201,34 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1204
1201
  // μ = 1/(1+M_E/M_M) — 6.4399″ at current constants, tracks them live.
1205
1202
  const embWobbleArcsec = (moonDistanceKm / (MASS_RATIO_EARTH_MOON + 1) / currentAUDistance)
1206
1203
  * (648000 / Math.PI);
1207
- // FQ-5 N3 — the completion's carriers are FRAMEWORK-derived: one
1208
- // revolution per the model's own tropical period records (Earth from
1209
- // the framework mean solar year; the Moon-elongation rate from the
1210
- // sidereal month/year identity). The v3 table's amplitudes were
1211
- // re-extracted on exactly these rates (the carrier↔table matched pair).
1204
+ // FQ-5 N3 — the completion's carriers are FRAMEWORK-derived from the
1205
+ // model's own planet period records (the Moon-elongation rate from the
1206
+ // sidereal month/year identity). Plan 06 I2 — the carriers are SIDEREAL
1207
+ // (measured): the records' solarYearInput are OF-DATE periods (their
1208
+ // rates sit +1.39..1.62°/cy above the VSOP sidereal mean motions — the
1209
+ // framework precession plus input rounding), and a perturbation argument
1210
+ // is inertial (D'Alembert: only Σk = 0 arguments are frame-free), so on
1211
+ // of-date carriers every Σk ≠ 0 argument drifted by Σk·ψ(t) — 42..52° at
1212
+ // −3000 for the table's Σk = −1 terms (their ancient scatter against
1213
+ // Horizons 6.4 → 4.4″ on sidereal carriers), and 6 % of frequency for the
1214
+ // 1783-yr long inequality (1683 yr on the of-date carriers). Constant-free:
1215
+ // the record rate minus the model's own J2000 precession p₀ =
1216
+ // 360·36525·(1/T_trop − 1/T_sid) for the planets, the framework sidereal
1217
+ // year for Earth; identical to the N3 carriers at J2000 (same phase
1218
+ // anchors), ≤ 1.4°·|Σk| apart at the 200-yr extraction window's edges
1219
+ // (the composed table reproduces the N3 one there: JPL 1900–2100
1220
+ // all-phase sd unchanged to 0.01″). The parity gate's fingerprint mirrors
1221
+ // this arithmetic (test/create-model-parity.test.mjs).
1212
1222
  const degPerCyOf = /** @param {number} cyclesPerDay */ (cyclesPerDay) => 360 * 36525 * cyclesPerDay;
1223
+ const carrierPrecessionDegPerCy = degPerCyOf(1 / meanSolarYearDays) - degPerCyOf(1 / meanSiderealYearDays);
1213
1224
  const carrierRatesDegPerCy = {
1214
1225
  planets: [
1215
- degPerCyOf(1 / C.planetOrbitalElements.mercury.solarYearInput),
1216
- degPerCyOf(1 / C.planetOrbitalElements.venus.solarYearInput),
1217
- degPerCyOf(1 / meanSolarYearDays),
1218
- degPerCyOf(1 / C.planetOrbitalElements.mars.solarYearInput),
1219
- degPerCyOf(1 / C.planetOrbitalElements.jupiter.solarYearInput),
1220
- degPerCyOf(1 / C.planetOrbitalElements.saturn.solarYearInput),
1226
+ degPerCyOf(1 / C.planetOrbitalElements.mercury.solarYearInput) - carrierPrecessionDegPerCy,
1227
+ degPerCyOf(1 / C.planetOrbitalElements.venus.solarYearInput) - carrierPrecessionDegPerCy,
1228
+ degPerCyOf(1 / meanSiderealYearDays),
1229
+ degPerCyOf(1 / C.planetOrbitalElements.mars.solarYearInput) - carrierPrecessionDegPerCy,
1230
+ degPerCyOf(1 / C.planetOrbitalElements.jupiter.solarYearInput) - carrierPrecessionDegPerCy,
1231
+ degPerCyOf(1 / C.planetOrbitalElements.saturn.solarYearInput) - carrierPrecessionDegPerCy,
1221
1232
  ],
1222
1233
  moonElongation: degPerCyOf(1 / moonSiderealMonthInput - 1 / meanSiderealYearDays),
1223
1234
  };
@@ -1229,11 +1240,14 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1229
1240
  },
1230
1241
  sunLonDegAt: /** @param {number} jdUT */ (jdUT) => eclipseFinders.sunLonDegAt(jdUT),
1231
1242
  sunCompletionDeg: sunPlanetaryCompletionDeg,
1232
- moonExtensionAt: moonSeriesExtensionDeg,
1243
+ // plan 06 R3 item 1: the location tier on APPARENT places — the Sun's derived κ
1244
+ // (the same helper the cardinal instants use), the Moon's relative light-time
1245
+ // inside the besselian; the series-extension tail now lives in sceneEvalAt.
1246
+ sunAberrationDegAt: /** @param {number} year */ (year) => sunAberrationArcsecAt(year) / 3600,
1233
1247
  deltaTSecondsAt: /** @param {number} jd */ (jd) => (jdTTFromUT(jd) - jd) * 86400,
1234
1248
  obliquityDegAt: obliquityDeg,
1235
- eccentricityAt,
1236
- perihelionLongitudeDegAt: earthPerihelionDeg,
1249
+ eccentricityAt: sunEccentricityAt,
1250
+ perihelionLongitudeDegAt: sunPerihelionDegAt,
1237
1251
  yearFromJD,
1238
1252
  constants: {
1239
1253
  j2000JD,
@@ -1247,6 +1261,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1247
1261
  gmstMeanSiderealT0Deg: C.physicalConstants.gmstMeanSiderealT0Deg,
1248
1262
  gmstMeanSiderealRateDegPerDay: C.physicalConstants.gmstMeanSiderealRateDegPerDay,
1249
1263
  gmstMeanSiderealT2Deg: C.physicalConstants.gmstMeanSiderealT2Deg,
1264
+ speedOfLightKmS: C.physicalConstants.speedOfLight,
1250
1265
  },
1251
1266
  });
1252
1267
 
@@ -1268,6 +1283,93 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1268
1283
  if (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS) return yearLengthsM.tropicalYearSecondsAtYear(year);
1269
1284
  return deepLod.tropicalYearSecondsAtAge(yearToTMa(year));
1270
1285
  };
1286
+ // ── Cardinal instants (plan 06 R1) ─────────────────────────────────────
1287
+ /** The completed certified Sun (finder Sun − the derived planetary
1288
+ * completion) at a UT model-JD — the same longitude the scene renders
1289
+ * inside the overlay window; MEAN GEOMETRIC, mean equinox of date.
1290
+ * @param {number} jdUT @returns {number} */
1291
+ const sunLonCompletedDegAtJdUT = (jdUT) => eclipseFinders.sunLonDegAt(jdUT)
1292
+ - sunPlanetaryCompletionDeg((jdTTFromUT(jdUT) - j2000JD) / julianCenturyDays);
1293
+ /** The APPARENT Sun the cardinal instants are defined on (an equinox is
1294
+ * the apparent Sun crossing the equator — the USNO/Meeus instants are
1295
+ * apparent): the completed geometric Sun minus the aberration constant,
1296
+ * plus the nutation in longitude. Both derived, zero new constants —
1297
+ * κ = 2π·a/(c·T_sid·√(1−e²)) from the AU, c, the one-family sidereal year
1298
+ * and the Sun's e (20.50″ at J2000); Δψ from the four IAU 1980 leading
1299
+ * terms on the model's OWN lunar node, mean Sun and mean Moon (the
1300
+ * framework arguments), the same family the registry's JPL Sun
1301
+ * instrument bridges with. @param {number} jdUT @returns {number} */
1302
+ /** The Sun's derived aberration constant of date, arcsec — κ = 2π·a/(c·T_sid·√(1−e²))
1303
+ * from the AU, c, the one-family sidereal year and the Sun's e (20.50″ at
1304
+ * J2000); ONE home for the cardinal instants and the eclipse location tier.
1305
+ * @param {number} year @returns {number} */
1306
+ const sunAberrationArcsecAt = (year) => {
1307
+ const e = sunEccentricityAt(year);
1308
+ return (2 * Math.PI * currentAUDistance
1309
+ / (C.physicalConstants.speedOfLight * yearLengthsM.siderealYearSecondsAtYear(year) * Math.sqrt(1 - e * e)))
1310
+ * (648000 / Math.PI);
1311
+ };
1312
+ /** @param {number} jdUT @returns {number} */
1313
+ const sunApparentLonDegAtJdUT = (jdUT) => {
1314
+ const jdTT = jdTTFromUT(jdUT);
1315
+ const year = 2000 + (jdTT - j2000JD) / 365.25;
1316
+ const kappaArcsec = sunAberrationArcsecAt(year);
1317
+ const a = moonArgs.argsAt(jdTT);
1318
+ const d2r = Math.PI / 180;
1319
+ const Om = (a.Lp - a.F) * d2r, Ls = (a.Lp - a.D) * d2r, Lm = a.Lp * d2r;
1320
+ const NU = C.physicalConstants.nutationLeadingTermsArcsec;
1321
+ const dPsiArcsec = NU.psiOmega * Math.sin(Om) + NU.psi2Ls * Math.sin(2 * Ls)
1322
+ + NU.psi2Lm * Math.sin(2 * Lm) + NU.psi2Omega * Math.sin(2 * Om);
1323
+ return sunLonCompletedDegAtJdUT(jdUT) + (dPsiArcsec - kappaArcsec) / 3600;
1324
+ };
1325
+ /** @param {string} type @returns {number} */
1326
+ const cardinalTargetDeg = (type) => {
1327
+ const t = { VE: 0, SS: 90, AE: 180, WS: 270 }[type];
1328
+ if (t === undefined) throw new RangeError(`cardinal type must be VE|SS|AE|WS, got ${type}`);
1329
+ return t;
1330
+ };
1331
+ const CARDINAL_SI_YEAR_D = 365.2422;
1332
+ /** The crossing NEAREST a seed instant — the unique root within ±½ tropical
1333
+ * year of the seed (the wrapped longitude difference is the Newton residual).
1334
+ * Plan 06 R4c: the seed is a JD because the caller's year may live on a
1335
+ * different axis than the model year — the simulator's cardinal panel
1336
+ * labels its year with the displayed CALENDAR date (the Julian calendar
1337
+ * before 1582, 365.25-d years) while `year` below is the model year (the SI
1338
+ * axis, 365.2422-d count from J2000); the two part by 0.0078 d/yr, 114 yr at
1339
+ * −5.34 Myr, where the panel showed the events of −5341772 for a date in
1340
+ * −5341886 (owner). Seeding at the calendar year's midpoint returns that
1341
+ * year's own events at every epoch. @param {number} jdSeedUT @param {string} type @returns {number} */
1342
+ const cardinalCrossingNearJdUT = (jdSeedUT, type) => {
1343
+ const target = cardinalTargetDeg(type);
1344
+ const RATE = 360 / CARDINAL_SI_YEAR_D;
1345
+ let jd = jdSeedUT;
1346
+ for (let i = 0; i < 20; i++) {
1347
+ const d = ((((sunApparentLonDegAtJdUT(jd) - target) + 540) % 360 + 360) % 360) - 180;
1348
+ jd -= d / RATE;
1349
+ if (Math.abs(d) < 1e-10) break;
1350
+ }
1351
+ return jd - C.earthOrbital.deltaTStart / 86400;
1352
+ };
1353
+ /** The TRUE-UT JD at which the APPARENT Sun's longitude of date equals
1354
+ * the cardinal target in the given year — Newton on the crossing (the
1355
+ * Sun's rate 360°/tropical year; a handful of steps from the tropical-year
1356
+ * seed). Replaces the fitted cardinal model's instants (R1). Plan 06 R3
1357
+ * item 2: the Newton root lives on the FINDER axis (the curve-only ΔT the
1358
+ * eclipse finders certify), and the returned instant is that root minus the
1359
+ * deltaTStart bridge — true UT = TT − (deltaTStart + curve), the same
1360
+ * bridge the besselian and the registry instruments apply. Before this the
1361
+ * published instants were 54.55 s (0.9 min) late. Against Horizons over
1362
+ * ±3000 yr the remaining offset is the Sun's (plan 06 I2 measured).
1363
+ * `year` is the MODEL year (the SI axis) — see cardinalCrossingNearJdUT.
1364
+ * @param {number} year @param {string} type @returns {number} */
1365
+ const cardinalCrossingJdUT = (year, type) => {
1366
+ const target = cardinalTargetDeg(type);
1367
+ // seed: Jan 1.5 of the year (J2000 = 2000 Jan 1.5) + the mean date of the
1368
+ // March equinox (day 79.3) + the quarter-turns to the target
1369
+ const seed = j2000JD + (year - 2000) * CARDINAL_SI_YEAR_D + 79.3 + (target / 360) * CARDINAL_SI_YEAR_D;
1370
+ return cardinalCrossingNearJdUT(seed, type);
1371
+ };
1372
+
1271
1373
  return Object.freeze({
1272
1374
  time: Object.freeze({
1273
1375
  yearFromJD,
@@ -1390,13 +1492,18 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1390
1492
  measuredSolarDaySeconds,
1391
1493
  raDayOffsetMs,
1392
1494
  }),
1495
+ // Plan 06 R1: the instants are the crossings of the ONE Sun (the
1496
+ // certified completed Sun the scene renders), UT model-JD; the RA at a
1497
+ // crossing is the target longitude by construction (λ = 0/90/180/270 ⇒
1498
+ // RA = 0/90/180/270, any obliquity); the per-type year length is the
1499
+ // interval between successive crossings (SI days).
1393
1500
  cardinal: Object.freeze({
1394
- jd: /** @param {number} year @param {string} type @returns {number} */ (year, type) => cardinalM.computeSolsticeJD(year, type),
1395
- raDeg: /** @param {number} year @param {string} type @returns {number} */ (year, type) => {
1396
- const ra = cardinalM.computeSolsticeRA(year, type);
1397
- return ((ra % 360) + 360) % 360;
1398
- },
1399
- yearLengthDays: /** @param {number} year @param {string} type @returns {number} */ (year, type) => cardinalM.computeSolsticeYearLength(year, type),
1501
+ jd: /** @param {number} year @param {string} type @returns {number} */ (year, type) => cardinalCrossingJdUT(year, type),
1502
+ // R4c: the crossing nearest a UT instant (a JD seed — for callers whose
1503
+ // year coordinate is not the model year, e.g. a calendar year's midpoint).
1504
+ jdNearUT: /** @param {number} jdUT @param {string} type @returns {number} */ (jdUT, type) => cardinalCrossingNearJdUT(jdUT, type),
1505
+ raDeg: /** @param {number} year @param {string} type @returns {number} */ (year, type) => { void year; return cardinalTargetDeg(type); },
1506
+ yearLengthDays: /** @param {number} year @param {string} type @returns {number} */ (year, type) => cardinalCrossingJdUT(year + 1, type) - cardinalCrossingJdUT(year, type),
1400
1507
  }),
1401
1508
  // D4b: the one-source cardinal STRUCTURE — the EoC layer on the
1402
1509
  // movement's own e(t)/ϖ(t), valid at every epoch (mode tier). Absolute
@@ -1440,6 +1547,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1440
1547
  sunLonCompletedDegAtJD: /** @param {number} jd @returns {number} */ (jd) =>
1441
1548
  eclipseFinders.sunLonDegAt(jd)
1442
1549
  - sunPlanetaryCompletionDeg((jdTTFromUT(jd) - j2000JD) / julianCenturyDays),
1550
+ /** The APPARENT Sun (completed geometric − the derived aberration constant + the leading nutation terms on the model's own arguments) — the quantity Horizons' observer ecliptic longitude is; the cardinal instants are its crossings (plan 06 R1/I1). @param {number} jdUT @returns {number} */
1551
+ sunApparentLonDegAtJD: /** @param {number} jdUT @returns {number} */ (jdUT) => sunApparentLonDegAtJdUT(jdUT),
1443
1552
  findLunarInRange: /** @param {number} jdStart @param {number} jdEnd */ (jdStart, jdEnd) => eclipseFinders.findLunarEclipsesInRange(jdStart, jdEnd),
1444
1553
  findSolarInRange: /** @param {number} jdStart @param {number} jdEnd */ (jdStart, jdEnd) => eclipseFinders.findSolarEclipsesInRange(jdStart, jdEnd),
1445
1554
  deltaTSecondsAtJD: frameworkDeltaTSecondsAtJD,
@@ -1451,8 +1560,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1451
1560
  frameworkSunDeps: Object.freeze({
1452
1561
  sunMeanLongitudeJ2000Deg: sunL0Deg,
1453
1562
  tropicalRateDegPerCy: sunTropicalRateDegPerCy,
1454
- eccentricityAt,
1455
- perihelionLongitudeDegAt: earthPerihelionDeg,
1563
+ eccentricityAt: sunEccentricityAt,
1564
+ perihelionLongitudeDegAt: sunPerihelionDegAt,
1456
1565
  meanLongitudeDegAt: sunMeanLongitudeDegAt,
1457
1566
  }),
1458
1567
  // 20.3g location tier (see eclipse/besselian.cjs):