@essrt/physics 4.8.0 → 5.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.
Files changed (62) hide show
  1. package/README.md +4 -0
  2. package/package.json +42 -39
  3. package/src/climate/l1-orbital.cjs +76 -5
  4. package/src/constants/coefficients.d.ts +678 -0
  5. package/src/constants/coefficients.js +4 -4
  6. package/src/constants/generated.d.ts +42 -41
  7. package/src/constants/generated.js +6 -5
  8. package/src/deltat/deep-time.cjs +69 -2
  9. package/src/earth/deep-orbital-history.cjs +6 -5
  10. package/src/earth/precession-composed.cjs +120 -0
  11. package/src/earth/year-lengths.cjs +12 -1
  12. package/src/index.js +6 -2
  13. package/src/layer0/derive-params.js +21 -4
  14. package/src/layer0/index.js +35 -4
  15. package/src/model.js +176 -33
  16. package/src/planets/chain-artifact.js +2 -2
  17. package/types/cardinal/index.d.cts +80 -0
  18. package/types/cardinal/one-source-structure.d.cts +35 -0
  19. package/types/chain-cycles/index.d.cts +33 -0
  20. package/types/climate/l1-orbital.d.cts +103 -0
  21. package/types/constants/coefficients.d.ts +678 -0
  22. package/types/constants/generated.d.ts +754 -0
  23. package/types/constants/index.d.ts +2 -0
  24. package/types/deltat/cycles.d.cts +72 -0
  25. package/types/deltat/deep-time.d.cts +135 -0
  26. package/types/deltat/historical.d.cts +8 -0
  27. package/types/deltat/recession-history.d.cts +80 -0
  28. package/types/earth/deep-orbital-history.d.cts +77 -0
  29. package/types/earth/precession-composed.d.cts +59 -0
  30. package/types/earth/sidereal-channel-artifact.d.cts +12 -0
  31. package/types/earth/sidereal-year-channel.d.cts +38 -0
  32. package/types/earth/year-lengths.d.cts +67 -0
  33. package/types/eclipse/besselian.d.cts +101 -0
  34. package/types/eclipse/finders.d.cts +116 -0
  35. package/types/eclipse/sun-planetary-completion.d.cts +39 -0
  36. package/types/index.d.ts +78 -0
  37. package/types/layer0/derive-params.d.ts +46 -0
  38. package/types/layer0/index.d.ts +64 -0
  39. package/types/layer1/index.d.ts +10 -0
  40. package/types/model.d.ts +184 -0
  41. package/types/moon/apparent.d.cts +78 -0
  42. package/types/moon/arguments.d.cts +138 -0
  43. package/types/moon/deep-ecc-channel.d.cts +25 -0
  44. package/types/moon/deep-modes-artifact.d.cts +140 -0
  45. package/types/moon/ecc-channel.d.cts +30 -0
  46. package/types/moon/month-chain.d.cts +79 -0
  47. package/types/moon/series-extension.d.cts +11 -0
  48. package/types/moon/series.d.cts +91 -0
  49. package/types/phase/index.d.cts +38 -0
  50. package/types/planets/asc-node-integrator.d.cts +30 -0
  51. package/types/planets/chain-artifact.d.ts +15 -0
  52. package/types/planets/ecc-channel.d.cts +20 -0
  53. package/types/planets/fibonacci-laws.d.cts +90 -0
  54. package/types/planets/geometry.d.cts +60 -0
  55. package/types/planets/inv-plane-frame.d.cts +36 -0
  56. package/types/planets/keplerian-chain.d.cts +173 -0
  57. package/types/planets/model.d.cts +116 -0
  58. package/types/planets/orbit-chain.d.cts +12 -0
  59. package/types/planets/orientation.d.cts +67 -0
  60. package/types/planets/predict.d.cts +88 -0
  61. package/types/planets/secular-series.d.cts +73 -0
  62. package/types/sun/longitude-correction.d.cts +41 -0
package/src/model.js CHANGED
@@ -22,14 +22,14 @@ 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
24
  import { createCardinalModel } from './cardinal/index.cjs';
25
- import { createYearLengths } from './earth/year-lengths.cjs';
25
+ import { createYearLengths, ONE_FAMILY_WINDOW_YEARS } from './earth/year-lengths.cjs';
26
26
  import { createDeepOrbitalHistory } from './earth/deep-orbital-history.cjs';
27
27
  import { CHAIN_ARTIFACT } from './planets/chain-artifact.js';
28
28
  import { buildPlanetChainsFromArtifactData, computeApsidalSecularDegPerYr } from './planets/keplerian-chain.cjs';
29
29
  import { createDeltaTCycles } from './deltat/cycles.cjs';
30
30
  import { createDeepTimeLod } from './deltat/deep-time.cjs';
31
31
  import { createMoonRecessionHistory, createSolarChannelBudget } from './deltat/recession-history.cjs';
32
- import { evalClimateL1OrbitalPermil } from './climate/l1-orbital.cjs';
32
+ import { evalClimateL1OrbitalPermil, createAlphaGiaChannel } from './climate/l1-orbital.cjs';
33
33
  import { createMoonEccChannel } from './moon/ecc-channel.cjs';
34
34
  import { createDeepEccChannel } from './moon/deep-ecc-channel.cjs';
35
35
  import { DEEP_MODES_ARTIFACT } from './moon/deep-modes-artifact.cjs';
@@ -132,27 +132,42 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
132
132
  const GM_SUN = GM_SUN_PLUS_EARTH - GM_EARTH_ALONE;
133
133
  const M_SUN = GM_SUN / G_CONSTANT;
134
134
 
135
- // ── α(t): climate-driven GIA channel with the R2 lattice pin ──────────────
135
+ // ── α(t): the GIA channel — the LAGGED response to the L1 ice history, with the R2 lattice pin ──
136
136
  const CLIMATE = F.CLIMATE_FORMULA_COEFFS;
137
137
  const CLIMATE_REGIME = CLIMATE.regimes['lr04-post-mpt'];
138
- const alphaClimateScale = C.deepTime.alphaClimateScalePerMille;
139
138
 
140
- /** @param {number} year @returns {number} */
139
+ /** The climate formula's L1 (unlagged) — the orbital δ¹⁸O layer itself. @param {number} year @returns {number} */
141
140
  const evalClimateL1 = (year) => evalClimateL1OrbitalPermil(year, {
142
141
  l1Terms: CLIMATE_REGIME.L1,
143
142
  yStdDenormalization: CLIMATE_REGIME.denormalization.y_std,
144
- eightHKyr: CLIMATE.config.eight_H_kyr,
143
+ });
144
+ // ONE home (climate/l1-orbital.cjs): k derived from the Cox–Chao rate, τ the
145
+ // record-measured relaxation time (plan 06 D7). Same construction in
146
+ // src/script.js, tools/lib/deep-time.js and the website's essrt.ts.
147
+ const alphaGia = createAlphaGiaChannel({
148
+ l1Terms: CLIMATE_REGIME.L1,
149
+ yStdDenormalization: CLIMATE_REGIME.denormalization.y_std,
150
+ relaxationKyr: C.deepTime.alphaGiaRelaxationKyr,
151
+ alphaGiaRateJ2000PerYr: C.deepTime.alphaGiaRateJ2000PerYr,
152
+ alphaJ2000: earthMoiFactorJ2000,
145
153
  });
146
154
 
147
155
  let latticeAlphaPin = false;
148
- /** @type {number|null} */
149
- let alphaL1J2000 = null;
156
+ // Exact-argument memo (mirrors src/script.js + tools/lib/deep-time.js —
157
+ // the round-5 perf campaign): same tMa → bit-identical value; the pin
158
+ // branch bypasses the memo; bounded by wholesale clear.
159
+ /** @type {Map<number, number>} */
160
+ const alphaMemo = new Map();
150
161
  /** @param {number} tMa @returns {number} */
151
162
  const earthMoiFactorAtAge = (tMa) => {
152
163
  if (latticeAlphaPin) return earthMoiFactorJ2000;
153
- if (alphaL1J2000 === null) alphaL1J2000 = evalClimateL1(2000);
154
- const L1at = evalClimateL1(2000 - tMa * 1e6);
155
- return earthMoiFactorJ2000 - alphaClimateScale * (L1at - alphaL1J2000);
164
+ let v = alphaMemo.get(tMa);
165
+ if (v === undefined) {
166
+ v = alphaGia.alphaAt(2000 - tMa * 1e6);
167
+ if (alphaMemo.size >= 8192) alphaMemo.clear();
168
+ alphaMemo.set(tMa, v);
169
+ }
170
+ return v;
156
171
  };
157
172
 
158
173
  // ── Layer 0 + deep-time core ──────────────────────────────────────────────
@@ -175,6 +190,11 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
175
190
  moonDistanceKm,
176
191
  moonOrbitalEccentricity: C.moonReference.moonOrbitalEccentricityBase,
177
192
  gmEarthMoonSystemKm3S2: GM_EARTH_MOON_SYSTEM,
193
+ // the solar precession torque's inputs (plan 06 Phase 3: the unit H(t) = 13·T_p,composed)
194
+ gmSunKm3S2: GM_SUN,
195
+ astronomicalUnitKm: currentAUDistance,
196
+ earthOrbitalEccentricityJ2000: C.earthOrbital.earthEccentricityJ2000,
197
+ moonEclipticInclinationJ2000Deg: C.moonReference.moonEclipticInclinationJ2000,
178
198
  });
179
199
 
180
200
  // ── Driver 1½: regime-aware recession history + the solar channels ────────
@@ -261,6 +281,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
261
281
  meanSiderealYearJ2000Seconds: meanSiderealYearSeconds,
262
282
  solarMassLossFracPerYear: EPOCH_PARAMS.solarMassLossFracPerYear,
263
283
  siderealYearDaysKinematicJ2000: meanSiderealYearDaysKinematic,
284
+ precessionSolarShareJ2000: EPOCH_PARAMS.precessionSolarShareJ2000,
264
285
  },
265
286
  moonDistanceMetresAtAge,
266
287
  moiFactorAtAge: earthMoiFactorAtAge,
@@ -272,8 +293,12 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
272
293
  });
273
294
 
274
295
  // ── Integrated ∫1/H(t)dt phase (R2 α-pin honoured while building) ─────────
296
+ // The phase counter every FROZEN comb reads rides the frozen era clock's
297
+ // own convention H_era = H₀·LOD/LOD₀ (plan 06 D8: two named counters — the
298
+ // coefficients were fitted against this counter and ship with it), NOT the
299
+ // unit H(t) = 13·T_p,composed that the physics publishes.
275
300
  const phaseM = createPhaseMachinery({
276
- holisticHAtAgeMa: (tMa) => deepLod.hAtAge(tMa),
301
+ holisticHAtAgeMa: (tMa) => deepLod.eraClockHAtAge(tMa),
277
302
  tableAnchorYear: startmodelYear,
278
303
  driftRefYear: startModelYearWithCorrection,
279
304
  hJ2000: H,
@@ -402,16 +427,19 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
402
427
  if (lod === null) return meanSiderealYearDays;
403
428
  return deepLod.siderealYearSecondsAtAge(tMa) / lod;
404
429
  };
430
+ // The comb family's bases are the FROZEN era clock's forms (H_era; plan 06
431
+ // D8) — the combs were fitted on them. The physical year lengths ride the
432
+ // unit through yearLengths (family B) and the tidal-chain mean.
405
433
  /** @param {number} year @returns {number} */
406
434
  const tropicalYearDaysBase = (year) => {
407
- const days = deepLod.yearInDaysAtAge(yearToTMa(year));
435
+ const days = deepLod.eraClockYearInDaysAtAge(yearToTMa(year));
408
436
  return days === null ? meanSolarYearDays : days;
409
437
  };
410
438
  /** @param {number} year @returns {number} */
411
439
  const anomalisticYearDaysBase = (year) => {
412
440
  const tMa = yearToTMa(year);
413
- const Ht = deepLod.hAtAge(tMa);
414
- const tropD = deepLod.yearInDaysAtAge(tMa);
441
+ const Ht = deepLod.eraClockHAtAge(tMa);
442
+ const tropD = deepLod.eraClockYearInDaysAtAge(tMa);
415
443
  if (Ht === null || tropD === null) return meanAnomalisticYearDays;
416
444
  return (tropD * (Ht / 16)) / (Ht / 16 - 1);
417
445
  };
@@ -445,14 +473,16 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
445
473
  },
446
474
  fns: {
447
475
  cyclesBetween: (a, b, n) => phase().cyclesBetween(a, b, n),
476
+ // The frozen era clock's deps ride ITS convention H_era (plan 06 D8):
477
+ // drift integrand, dc/dY and the real-LOD convention were fitted on it.
448
478
  analyticTropicalDays: (year) => {
449
479
  const tMa = (startmodelYear - year) / 1e6;
450
- const Ht = deepLod.hAtAge(tMa);
480
+ const Ht = deepLod.eraClockHAtAge(tMa);
451
481
  if (Ht === null) return null;
452
482
  return (deepLod.siderealYearSecondsAtAge(tMa) / 86400) * (1 - 13 / Ht);
453
483
  },
454
- meanHAtAgeMa: (tMa) => deepLod.hAtAge(tMa),
455
- meanYearRealLodDays: (tMa) => deepLod.yearInDaysAtAge(tMa),
484
+ meanHAtAgeMa: (tMa) => deepLod.eraClockHAtAge(tMa),
485
+ meanYearRealLodDays: (tMa) => deepLod.eraClockYearInDaysAtAge(tMa),
456
486
  eccentricityAt,
457
487
  eccentricityRateAt,
458
488
  },
@@ -463,9 +493,10 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
463
493
  // package's embedded deep-modes artifact; the banked-series tier is the
464
494
  // repo-data-bound instrument). Absolute dates deliberately absent — see
465
495
  // cardinal/one-source-structure.cjs. The mean year is SI SECONDS: the
466
- // sidereal year of date reduced by the SECULAR α(H(t)) equinox precession
467
- // (axial0·H(t)/H0 — the movement's leg-1 convention, NOT the H/13
468
- // kinematic identity; the two are a recorded 0.09% relation tension).
496
+ // sidereal year of date reduced by the SECULAR equinox precession
497
+ // (axial0·H(t)/H₀ with H(t) the UNIT = 13·T_p,composed — plan 06 D6/Phase
498
+ // 3, the movement's leg-1 convention, NOT the H/13 kinematic identity at
499
+ // J2000; the two are a recorded 0.09% relation tension there).
469
500
  // THE ONE of-date year-length family (owner: "move to 1 implementation")
470
501
  // — createYearLengths owns tropical (equinox-rate mean, wobble included),
471
502
  // sidereal (the D6 λ̇ channel), anomalistic (the cardinal structure on
@@ -478,7 +509,13 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
478
509
  // path; the npm package alone has no 8 MB series), the movement runs the
479
510
  // SERIES tier exactly like the browser — killing the mode-vs-series
480
511
  // value split (measured: 6 s on the anomalistic year at J2000).
481
- const yearLengthsM = (() => {
512
+ // The engine-D planet chains (ONE build; the one-family route's apsidal
513
+ // tangent and the lunisolar surface's n_aps read the same instance).
514
+ const kcChainsM = buildPlanetChainsFromArtifactData(CHAIN_ARTIFACT);
515
+ // The ONE-SOURCE movement: the hybrid's ε(t) (plan 06 Phase 3 S3b — THE
516
+ // published obliquity) and the one-family year lengths, on the same
517
+ // per-tier sampler.
518
+ const oneSourceM = (() => {
482
519
  const AEarth = /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements.earth;
483
520
  // The ψ̇ anchor: the CERTIFIED of-date laws at 2000 — identical to the
484
521
  // engine/browser (computeSiderealYearDaysDirect / computeSolarYearDays
@@ -490,6 +527,12 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
490
527
  const sidLaw2000 = evalYearFourier(2000, siderealYearDaysBase(2000), F.SIDEREAL_YEAR_HARMONICS);
491
528
  const solLaw2000 = evalYearFourier(2000, tropicalYearDaysBase(2000), F.TROPICAL_YEAR_HARMONICS);
492
529
  const axial0 = sidLaw2000 / (sidLaw2000 - solLaw2000);
530
+ // D6 → Phase 3 (plan 06): the deep-time ψ̇(t) the hybrid precesses on is
531
+ // the COMPOSED lunisolar rate, and since Phase 3 the unit H(t) IS
532
+ // 13 × that period (deltat/deep-time.cjs hAtAge), so the injection is
533
+ // the plain period₀·H(t)/H₀ again — on the unit, not on the frozen
534
+ // era clock's H_era (which misses the lunar 1/a³ growth: 70.9 vs
535
+ // Lantink 2022's 108.6 ± 8.5 ″/yr at 2.46 Ga; the unit reads 104.5).
493
536
  const H0 = /** @type {number} */ (deepLod.hAtAge(0));
494
537
  const seriesArt = /** @type {any} */ (secularSeriesArtifact);
495
538
  const sb = seriesArt && seriesArt.bodies && seriesArt.bodies.earth;
@@ -505,7 +548,13 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
505
548
  anchorInclEclipticDeg: AEarth.inclEclipticDeg,
506
549
  anchorAscNodeEclipticDeg: AEarth.ascNodeEclipticDeg,
507
550
  axialPrecessionYearsJ2000: axial0,
508
- obliquityJ2000Deg: obliquityDeg(2000),
551
+ // Phase 3 S3b: the hybrid's initial condition is the OBSERVED J2000
552
+ // mean obliquity — the IAU 2006 input constant (84381.406″), the same
553
+ // anchor the browser's _deepHistSeries and the Node one-source movement
554
+ // use. It was the K comb's own J2000 value, 0.233″ above IAU: a
555
+ // rendering-device residual imported as the initial condition of the
556
+ // physics (found when the package began publishing the hybrid).
557
+ obliquityJ2000Deg: C.earthOrbital.obliquityJ2000_deg,
509
558
  axialPrecessionYearsAtYearFn: (yr) => {
510
559
  const h = deepLod.hAtAge((startmodelYear - yr) / 1e6);
511
560
  return axial0 * (h === null ? 1 : h / H0);
@@ -534,13 +583,19 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
534
583
  };
535
584
  // The anomalistic rides the chain's SECULAR apsidal tangent (the same
536
585
  // rate family the panel's Prec. cell shows) — ONE helper, keplerian-chain.
537
- const kcChains = buildPlanetChainsFromArtifactData(CHAIN_ARTIFACT);
538
- return createYearLengths({
586
+ const kcChains = kcChainsM;
587
+ const yearLengths = createYearLengths({
539
588
  sampleAt,
540
589
  massLossSiderealSecondsAtYearFn: (year) => deepLod.siderealYearSecondsAtAge(yearToTMa(year)),
541
590
  apsidalSecularDegPerYrFn: (year) => computeApsidalSecularDegPerYr(year, kcChains.earth, kcChains),
542
591
  });
592
+ return {
593
+ yearLengths,
594
+ /** 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} */
595
+ epsAt: (year) => sampleAt(year).epsDeg,
596
+ };
543
597
  })();
598
+ const yearLengthsM = oneSourceM.yearLengths;
544
599
 
545
600
  /** Tropical year: mean of the four cardinal intervals. @param {number} year @returns {number} */
546
601
  const tropicalYearDays = (year) => cardinalM.computeTropicalYearLength(year);
@@ -1159,6 +1214,22 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1159
1214
  });
1160
1215
 
1161
1216
  // ── The assembled surface ─────────────────────────────────────────────────
1217
+ // Plan 06 Phase 3 S2 — THE published of-date precession period and mean
1218
+ // tropical year: the one-family route (B) inside the banked tiers
1219
+ // (|Δyear| ≤ ONE_FAMILY_WINDOW_YEARS), the unit's secular mean beyond
1220
+ // (H(t)/13; the tidal-chain year). Shared by `epoch` and `lunisolar`.
1221
+ /** @param {number} year @returns {number} */
1222
+ const publishedAxialPrecessionYearsAtYear = (year) => {
1223
+ if (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS) return yearLengthsM.axialPrecessionYearsAtYear(year);
1224
+ const h = deepLod.hAtAge(yearToTMa(year));
1225
+ return (h === null ? H : h) / 13;
1226
+ };
1227
+ /** @param {number} year @returns {number} */
1228
+ const publishedTropicalYearSecondsAtYear = (year) => {
1229
+ if (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS) return yearLengthsM.tropicalYearSecondsAtYear(year);
1230
+ return deepLod.tropicalYearSecondsAtAge(yearToTMa(year));
1231
+ };
1232
+
1162
1233
  return Object.freeze({
1163
1234
  time: Object.freeze({
1164
1235
  yearFromJD,
@@ -1176,22 +1247,94 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1176
1247
  siderealYearSecondsAtYear: /** @param {number} year @returns {number} */ (year) => yearLengthsM.siderealYearSecondsAtYear(year),
1177
1248
  deltaTSecondsAtYear: deltaTSeconds,
1178
1249
  cyclesBetween,
1179
- // The DYNAMICAL axial precession period (the tweakpane identity):
1180
- // day-form beat of the sidereal and solar year evaluators — real at
1181
- // J2000 (~25,771 yr ≈ IAU), epoch-valid at any age. The same value
1182
- // the (d′) of-date rate completion integrates.
1183
- axialPrecessionYearsAtYear: /** @param {number} year @returns {number} */ (year) => {
1184
- const sid = siderealYearDays(year);
1185
- return sid / (sid - tropicalYearDirectDays(year));
1250
+ // Plan 06 Phase 3 S2 — THE published of-date precession period is the
1251
+ // one-family route (B): the hybrid's own equinox regression against
1252
+ // the λ̇-corrected sidereal year (~25,771 yr at J2000 ≈ IAU), inside
1253
+ // the banked tiers (|Δyear| ≤ ONE_FAMILY_WINDOW_YEARS); beyond, the
1254
+ // unit's SECULAR mean H(t)/13 = the composed lunisolar period (the
1255
+ // of-date wobble is unresolved there). The comb family's beat
1256
+ // sid/(sid − tropicalYearDirectDays) — the frozen era clock's
1257
+ // kinematic-day device — is no longer published here; it still
1258
+ // anchors the lunar chain's (d′) rate completion (pDynDegPerYearAt,
1259
+ // a MATCHED TRIPLE with the eclipse gates: M0(a) measured a 3–15 min
1260
+ // lunar-timing shift if it moved).
1261
+ axialPrecessionYearsAtYear: publishedAxialPrecessionYearsAtYear,
1262
+ // The same publication rule for the mean tropical year of date (SI s):
1263
+ // one-family inside the window, the tidal-chain mean beyond.
1264
+ tropicalYearSecondsAtYear: publishedTropicalYearSecondsAtYear,
1265
+ }),
1266
+ // Plan 06 Phase 3 S3 — the lunisolar precession clock as a first-class
1267
+ // surface: Earth's spin clock, ONE home for its published faces, spoken
1268
+ // in PERIODS and their RATIOS — no unit, no integer. T_p(t) is the
1269
+ // composed lunisolar period (calculation map 2.1 step 6; the internal
1270
+ // identifier H is 13·T_p by definition and is not a face here); the
1271
+ // composition's terms; the of-date beat (the published family, seamed
1272
+ // at ±2 Myr); the apsidal period from the engine-D chain's secular
1273
+ // tangent and the two ratios the owner found wandering by hand —
1274
+ // T_aps/T_p (4.33 at J2000, 0.84 … 9.5 across ±26 kyr) and T_peri/T_p
1275
+ // with T_peri = 1/(1/T_p + 1/T_aps) the perihelion-of-date period
1276
+ // (0.812 at J2000). That these read close to 13/3 and 13/16 today is
1277
+ // the J2000 reading, stated nowhere as structure.
1278
+ lunisolar: Object.freeze({
1279
+ /** The mean lunisolar precession period T_p(t), years — the composed torque rate's period. @param {number} year @returns {number} */
1280
+ meanPeriodYearsAtYear: (year) => { const h = deepLod.hAtAge(yearToTMa(year)); return (h === null ? H : h) / 13; },
1281
+ /** The composed rate ψ̇(t) = [ω/ω₀]·p₀·[f_S + (1 − f_S)(a₀/a_M)³], ″/yr. @param {number} year @returns {number} */
1282
+ meanRateArcsecPerYrAtYear: (year) => { const r = deepLod.lunisolarPrecessionRateArcsecPerYrAtAge(yearToTMa(year)); return r === null ? 1296000 / (H / 13) : r; },
1283
+ /** The of-date period — the one-family beat inside ±2 Myr, the mean beyond. @param {number} year @returns {number} */
1284
+ ofDatePeriodYearsAtYear: publishedAxialPrecessionYearsAtYear,
1285
+ /** f_S, the solar fraction of the J2000 precession torque. */
1286
+ solarShareJ2000: EPOCH_PARAMS.precessionSolarShareJ2000,
1287
+ /** (a₀/a_M(t))³ — the lunar torque's growth on the recession history. @param {number} year @returns {number} */
1288
+ lunarTorqueFactorAtYear: (year) => { const f = deepLod.lunarTorqueFactorAtAge(yearToTMa(year)); return f === null ? 1 : f; },
1289
+ /** f_S + (1 − f_S)(a₀/a_M)³ — the torque term the unit divides H_era by. @param {number} year @returns {number} */
1290
+ torqueTermAtYear: (year) => { const t = deepLod.precessionTorqueTermAtAge(yearToTMa(year)); return t === null ? 1 : t; },
1291
+ /** The hybrid's precession constant α = p₀ / cos ε₀, ″/yr (ε₀ the J2000 obliquity input). */
1292
+ torqueConstantJ2000ArcsecPerYr: (1296000 / (H / 13)) / Math.cos((C.earthOrbital.obliquityJ2000_deg * Math.PI) / 180),
1293
+ /** The apsidal (perihelion vs the stars) period from the engine-D chain's secular tangent, years — inside the published window only (the tangent is an extrapolation beyond the banked series: it turns negative at −5 Myr); null beyond. @param {number} year @returns {number|null} */
1294
+ apsidalPeriodYearsAtYear: (year) => (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS ? 360 / computeApsidalSecularDegPerYr(year, kcChainsM.earth, kcChainsM) : null),
1295
+ /** T_aps(t) / T_p(t) — the apsidal period in mean precession periods (4.33 at J2000, a fitted reading; 0.84 … 9.5 across ±26 kyr, measured); null beyond the published window. @param {number} year @returns {number|null} */
1296
+ apsidalPerPrecessionAtYear: (year) => {
1297
+ if (Math.abs(year - 2000) > ONE_FAMILY_WINDOW_YEARS) return null;
1298
+ const h = deepLod.hAtAge(yearToTMa(year));
1299
+ return (360 / computeApsidalSecularDegPerYr(year, kcChainsM.earth, kcChainsM)) / ((h === null ? H : h) / 13);
1300
+ },
1301
+ /** T_peri(t) = 1/(1/T_p + 1/T_aps) — the perihelion-of-date period (equinox precession + inertial perihelion motion, frame arithmetic at every epoch), years; null beyond the published window. @param {number} year @returns {number|null} */
1302
+ periOfDatePeriodYearsAtYear: (year) => {
1303
+ if (Math.abs(year - 2000) > ONE_FAMILY_WINDOW_YEARS) return null;
1304
+ const h = deepLod.hAtAge(yearToTMa(year));
1305
+ const tp = (h === null ? H : h) / 13, taps = 360 / computeApsidalSecularDegPerYr(year, kcChainsM.earth, kcChainsM);
1306
+ return 1 / (1 / tp + 1 / taps);
1307
+ },
1308
+ /** T_peri(t) / T_p(t) (0.812 at J2000 — the J2000 reading); null beyond the published window. @param {number} year @returns {number|null} */
1309
+ periOfDatePerPrecessionAtYear: (year) => {
1310
+ if (Math.abs(year - 2000) > ONE_FAMILY_WINDOW_YEARS) return null;
1311
+ const h = deepLod.hAtAge(yearToTMa(year));
1312
+ const tp = (h === null ? H : h) / 13, taps = 360 / computeApsidalSecularDegPerYr(year, kcChainsM.earth, kcChainsM);
1313
+ return (1 / (1 / tp + 1 / taps)) / tp;
1186
1314
  },
1315
+ /** The published of-date window, years from 2000. */
1316
+ publishedWindowYears: ONE_FAMILY_WINDOW_YEARS,
1187
1317
  }),
1188
1318
  earth: Object.freeze({
1189
1319
  perihelionLongitudeDeg: earthPerihelionDeg,
1190
- obliquityDeg,
1320
+ // Plan 06 Phase 3 S3b: THE published obliquity is the hybrid — one
1321
+ // torque law integrated on the dynamical orbit plane of the N-body
1322
+ // chain, zero fitted constants (≡ La2004 to ≤ 4″ over ±50 kyr). The
1323
+ // fitted 16-harmonic K comb, a device fitted to the scene's own
1324
+ // wheel geometry (0.005″ to it, 1.27° off La2004 at −20 kyr), keeps
1325
+ // its name below as the frozen era clock's device: it anchors the
1326
+ // hybrid at J2000, the kinematic-day stack and the lunar arguments.
1327
+ obliquityDeg: /** @param {number} year @returns {number} */ (year) => oneSourceM.epsAt(year),
1328
+ obliquityCombDeg: obliquityDeg,
1191
1329
  eccentricity: eccentricityAt,
1192
1330
  inclinationDeg,
1193
1331
  ascendingNodeDeg,
1194
1332
  }),
1333
+ // The DEVICE family (plan 06 Phase 3 S2): the frozen era clock's comb
1334
+ // year lengths (A), its cardinal 4-mean (A′) and the kinematic-day stack
1335
+ // built on them (the ΔT stack's Layer 3/4 basis, USNO-anchored). NOT the
1336
+ // published year lengths — those are `yearLengths` (the one-family
1337
+ // route) and the seamed `epoch.*AtYear` surfaces above.
1195
1338
  lengths: Object.freeze({
1196
1339
  tropicalYearDays,
1197
1340
  tropicalYearDirectDays,