@essrt/physics 20.0.1 → 21.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 (55) hide show
  1. package/README.md +4 -4
  2. package/package.json +3 -8
  3. package/src/cardinal/one-source-structure.cjs +3 -2
  4. package/src/chain-cycles/index.cjs +12 -30
  5. package/src/constants/coefficients.d.ts +16 -92
  6. package/src/constants/coefficients.js +5 -21
  7. package/src/constants/generated.d.ts +12 -16
  8. package/src/constants/generated.js +21 -25
  9. package/src/deltat/deep-time.cjs +36 -92
  10. package/src/deltat/recession-history.cjs +2 -1
  11. package/src/earth/deep-orbital-history.cjs +4 -4
  12. package/src/earth/precession-composed.cjs +3 -3
  13. package/src/earth/year-lengths.cjs +16 -1
  14. package/src/eclipse/sun-planetary-completion.cjs +11 -12
  15. package/src/index.js +22 -46
  16. package/src/layer0/derive-params.js +10 -5
  17. package/src/layer0/index.js +27 -73
  18. package/src/layer1/index.js +6 -37
  19. package/src/model.js +182 -210
  20. package/src/moon/arguments.cjs +57 -83
  21. package/src/moon/deep-modes-artifact.cjs +2 -2
  22. package/src/moon/month-chain.cjs +25 -59
  23. package/src/moon/series.cjs +9 -5
  24. package/src/phase/deep-calendar.cjs +35 -80
  25. package/src/planets/chain-artifact.js +2 -2
  26. package/src/planets/geometry.cjs +52 -23
  27. package/src/planets/model.cjs +5 -5
  28. package/types/chain-cycles/index.d.cts +9 -4
  29. package/types/constants/coefficients.d.ts +16 -92
  30. package/types/constants/generated.d.ts +12 -16
  31. package/types/deltat/deep-time.d.cts +3 -16
  32. package/types/earth/year-lengths.d.cts +12 -0
  33. package/types/eclipse/sun-planetary-completion.d.cts +0 -9
  34. package/types/index.d.ts +2 -12
  35. package/types/layer0/derive-params.d.ts +7 -4
  36. package/types/layer0/index.d.ts +6 -13
  37. package/types/layer1/index.d.ts +0 -2
  38. package/types/model.d.ts +6 -6
  39. package/types/moon/arguments.d.cts +23 -22
  40. package/types/moon/deep-modes-artifact.d.cts +3 -3
  41. package/types/moon/month-chain.d.cts +7 -16
  42. package/types/moon/series.d.cts +2 -2
  43. package/types/phase/deep-calendar.d.cts +14 -50
  44. package/types/planets/geometry.d.cts +27 -11
  45. package/types/planets/model.d.cts +9 -3
  46. package/src/cardinal/index.cjs +0 -324
  47. package/src/moon/ecc-channel.cjs +0 -141
  48. package/src/phase/index.cjs +0 -186
  49. package/src/planets/ecc-channel.cjs +0 -59
  50. package/src/sun/longitude-correction.cjs +0 -68
  51. package/types/cardinal/index.d.cts +0 -80
  52. package/types/moon/ecc-channel.d.cts +0 -30
  53. package/types/phase/index.d.cts +0 -38
  54. package/types/planets/ecc-channel.d.cts +0 -20
  55. package/types/sun/longitude-correction.d.cts +0 -41
package/src/model.js CHANGED
@@ -21,8 +21,7 @@ import { deriveEpochParams } from './layer0/derive-params.js';
21
21
  import { buildPlanetChainsFromArtifactData, computePlanetElementsAtYear } from './planets/keplerian-chain.cjs';
22
22
  import { createSecularSeriesOverride } from './planets/secular-series.cjs';
23
23
  import { computeEquatorNodeOriginSFrameDeg, convertNodeSFrameToEquatorOriginDeg } from './planets/inv-plane-frame.cjs';
24
- import { createPhaseMachinery } from './phase/index.cjs';
25
- import { createYearLengths, ONE_FAMILY_WINDOW_YEARS } from './earth/year-lengths.cjs';
24
+ import { createYearLengths, computeMeanTropicalYearJ2000Days, 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';
28
27
  import { createPlanetSpinChannelFromArtifacts, computeObliquityJ2000Deg } from './planets/spin-channel.cjs';
@@ -47,9 +46,9 @@ import { driver2PeriodSecondsAtAge } from './planets/orbit-chain.cjs';
47
46
  * engine too — packaging them into FITTED_COEFFICIENTS is the remaining
48
47
  * §7a-step-1b move; until then this is their single packaged home.
49
48
  */
50
- // (Plan 07 R10: the RA day offset's two cosine amplitudes left here as
51
- // literals; they ride F.RA_DAY_OFFSET_MS now — [div, sin, cos] on the J2000
52
- // phase, ONE home for the three runtimes and the website.)
49
+ // (Plan 07 R10 moved the RA day offset's two cosine amplitudes from literals
50
+ // here into F.RA_DAY_OFFSET_MS; plan 09 step 5b deleted that family — it was
51
+ // the equation of time's window term of an instrument, not a motion.)
53
52
 
54
53
  /**
55
54
  * Moon-channel eccentricity sensitivities (perigee/node), the [g/g₀]^s
@@ -86,8 +85,13 @@ const MOON_ECC_SENSITIVITY_NODE = 1.018;
86
85
  */
87
86
  export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type {any} */ (null), options = {}) {
88
87
  // ── Derived constants (constants.js §9 order) ─────────────────────────────
89
- const H = C.foundational.holisticyearLength;
90
- const meanSolarYearDays = Math.round(C.foundational.inputmeanlengthsolaryearindays * (H / 8)) / (H / 8);
88
+ // (plan 09 step 7b: `foundational.holisticyearLength` — the unit H — is no
89
+ // longer read here; the unit function left the physics with it)
90
+ // Plan 09: the mean solar year in days (the calendar's year axis) is the
91
+ // J2000 mean tropical year DERIVED in one home from the IAU sidereal year
92
+ // and p_A per Julian year (year-lengths.cjs); the Meeus & Savoie 1992 input
93
+ // 365.2421897 left the registry, the device's rounding onto 1/(H/8) before it.
94
+ const meanSolarYearDays = computeMeanTropicalYearJ2000Days(C.yearLengthRef.siderealYear, C.earthOrbital.generalPrecessionJ2000_arcsecPerYr);
91
95
  const startmodelYear = C.foundational.startmodelYear;
92
96
  const startmodelJD = C.foundational.startmodelJD;
93
97
  const startModelYearWithCorrection = startmodelYear + C.foundational.correctionDays / meanSolarYearDays;
@@ -96,14 +100,18 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
96
100
  // H/16 = −302,635, the "balanced year" — was rotated out of every fitted
97
101
  // (sin, cos) pair exactly (tools/fit/reorigin-combs-j2000.mjs; measured
98
102
  // bit-identical on every surface over ±300 kyr); it is no longer an input.
99
- const balancedYear = 2000;
103
+ // Plan 09 step 5b: the J2000 comb origin's last reader (the RA day offset
104
+ // comb) left with that device; no comb phase is evaluated here any more.
100
105
 
101
106
  const meanSiderealYearDays = C.yearLengthRef.siderealYear;
102
107
  const meanSiderealYearSeconds = meanSiderealYearDays * 86400;
103
- const meanSiderealYearDaysKinematic = (meanSolarYearDays * H) / (H - 13);
104
- const meanLengthOfDay = meanSiderealYearSeconds / meanSiderealYearDaysKinematic;
105
- const meanAnomalisticYearDays = (meanSolarYearDays * (H / 16)) / (H / 16 - 1);
108
+ // Plan 09: the J2000 day length ω₀ is the SI day (an input); the device's
109
+ // H/13 kinematic-sidereal-days construction (86,399.99968 s) is deleted.
110
+ const meanLengthOfDay = C.yearLengthRef.solarDay;
106
111
  const meanTropicalYearJ2000Seconds = meanSolarYearDays * meanLengthOfDay;
112
+ // Plan 09: THE J2000 precession anchor p₀ in days (Julian years) — the IAU
113
+ // 2006 general precession; the Moon's star-referenced periods read it.
114
+ const axialPrecessionJ2000Days = (1296000 / C.earthOrbital.generalPrecessionJ2000_arcsecPerYr) * 365.25;
107
115
 
108
116
  // Plan 07 R5/R6/R9: `earthtiltMean`, `earthInvPlaneInclinationAmplitude`
109
117
  // and `eccentricityAmplitude` were read here only to invert the K and ψ
@@ -111,7 +119,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
111
119
  // device. With both laws retired nothing in this module reads them — they
112
120
  // remain on C.earth as the K device's J2000 pose inputs (the scene's wheel
113
121
  // geometry at the chain anchor epoch) and for the registry's record keys.
114
- const solsticeObliquityMean = F.SOLSTICE_OBLIQUITY_MEAN_FITTED;
122
+ // (Plan 09: the 16-harmonic solstice-obliquity comb — SOLSTICE_OBLIQUITY_
123
+ // MEAN_FITTED / _HARMONICS, the frozen era record — is deleted; the model's
124
+ // ONE obliquity of date is the one-source hybrid, `oneSourceM.epsAt`.)
115
125
 
116
126
  const G_CONSTANT = C.physicalConstants.G_CONSTANT;
117
127
  const MASS_RATIO_EARTH_MOON = C.physicalConstants.MASS_RATIO_EARTH_MOON;
@@ -120,9 +130,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
120
130
  const moonDistanceKm = C.moonReference.moonDistance;
121
131
  const moonSiderealMonthInput = C.moonReference.moonSiderealMonthInput;
122
132
 
123
- // 8H-lattice moon sidereal month (constants.js §"Moon derived months")
124
- const totalDaysInH = H * meanSolarYearDays;
125
- const moonSiderealMonth = totalDaysInH / (Math.round((8 * totalDaysInH) / moonSiderealMonthInput) / 8);
133
+ // Plan 09: the Moon's sidereal month is the IAU input itself (the device's
134
+ // eighth-integer-per-8H count read it back 7.8 ms long; deleted).
135
+ const moonSiderealMonth = moonSiderealMonthInput;
126
136
 
127
137
  // Mass chain: Moon Kepler → GM_EM → Earth/Moon split → Sun (§9 order)
128
138
  const moonOrbitalShift = moonDistanceKm * (1 / (MASS_RATIO_EARTH_MOON + 1)) * (moonSiderealMonth / meanSiderealYearDays);
@@ -160,15 +170,14 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
160
170
  j2J2000: C.physicalConstants.earthJ2,
161
171
  });
162
172
 
163
- let latticeAlphaPin = false;
164
173
  // Exact-argument memo (mirrors src/script.js + tools/lib/deep-time.js —
165
- // the round-5 perf campaign): same tMa → bit-identical value; the pin
166
- // branch bypasses the memo; bounded by wholesale clear.
174
+ // the round-5 perf campaign): same tMa → bit-identical value; bounded by
175
+ // wholesale clear. (Plan 09 step 7a: the lattice-α pin branch left with
176
+ // the ∫dt/H_era phase table, the one table this model built under it.)
167
177
  /** @type {Map<number, number>} */
168
178
  const alphaMemo = new Map();
169
179
  /** @param {number} tMa @returns {number} */
170
180
  const earthMoiFactorAtAge = (tMa) => {
171
- if (latticeAlphaPin) return earthMoiFactorJ2000;
172
181
  let v = alphaMemo.get(tMa);
173
182
  if (v === undefined) {
174
183
  v = alphaGia.alphaAt(2000 - tMa * 1e6);
@@ -186,9 +195,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
186
195
  alpha1PerMa: C.deepTime.alpha1PerMa,
187
196
  alpha3PerMa3: C.deepTime.alpha3PerMa3,
188
197
  alpha4PerMa4: C.deepTime.alpha4PerMa4,
189
- holisticYearJ2000: H,
190
198
  meanSiderealYearSeconds,
191
- meanSiderealYearDaysKinematic,
199
+ lodJ2000Seconds: meanLengthOfDay, // plan 09: ω₀ = the SI day (was the H/13 kinematic day)
192
200
  sunMassKg: M_SUN,
193
201
  gmEarthAloneKm3S2: GM_EARTH_ALONE,
194
202
  gmMoonAloneKm3S2: GM_MOON_ALONE,
@@ -203,6 +211,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
203
211
  astronomicalUnitKm: currentAUDistance,
204
212
  earthOrbitalEccentricityJ2000: C.earthOrbital.earthEccentricityJ2000,
205
213
  moonEclipticInclinationJ2000Deg: C.moonReference.moonEclipticInclinationJ2000,
214
+ // p₀'s period — the tropical year of date's equinox term (plan 09 step 5d)
215
+ axialPrecessionJ2000Years: certifiedAxialPrecessionJ2000Years(),
206
216
  });
207
217
 
208
218
  // ── Driver 1½: regime-aware recession history + the solar channels ────────
@@ -331,11 +341,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
331
341
  alpha1PerMa: EPOCH_PARAMS.alpha1PerMa,
332
342
  alpha3PerMa3: EPOCH_PARAMS.alpha3PerMa3,
333
343
  alpha4PerMa4: EPOCH_PARAMS.alpha4PerMa4,
334
- holisticYearJ2000: H,
335
344
  lodNowH13Seconds: EPOCH_PARAMS.lodNowH13Seconds,
336
345
  meanSiderealYearJ2000Seconds: meanSiderealYearSeconds,
337
346
  solarMassLossFracPerYear: EPOCH_PARAMS.solarMassLossFracPerYear,
338
- siderealYearDaysKinematicJ2000: meanSiderealYearDaysKinematic,
339
347
  precessionSolarShareJ2000: EPOCH_PARAMS.precessionSolarShareJ2000,
340
348
  },
341
349
  moonDistanceMetresAtAge,
@@ -346,43 +354,18 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
346
354
  // Plan 06 T2 item: the solar day's ecliptic missing-motion term rides the
347
355
  // nodal period 1,296,000/|s₃| (orbital), not the spin unit's H/5.
348
356
  nodalPeriodYearsFn: () => 1296000 / s3ArcsecPerYr,
349
- siderealYearDaysFourierAt: (year) => evalSiderealYearFourierIAU(year),
350
357
  cycleLodSumAt: dtCycleLodCorrectionSum,
351
358
  swingLodAt: (year) => dtCycles.swingLodSecondsAt(year),
352
359
  swingLodRateAt: (year) => dtCycles.swingLodRateAt(year),
353
360
  lEmAtAgeKgm2S: solarBudget.lEmAtAgeKgm2S,
354
361
  });
355
362
 
356
- // ── Integrated ∫1/H(t)dt phase (R2 α-pin honoured while building) ─────────
357
- // The phase counter every FROZEN comb reads rides the frozen era clock's
358
- // own convention H_era = H₀·LOD/LOD₀ (plan 06 D8: two named counters — the
359
- // coefficients were fitted against this counter and ship with it), NOT the
360
- // unit H(t) = 13·T_p,composed that the physics publishes.
361
- const phaseM = createPhaseMachinery({
362
- holisticHAtAgeMa: (tMa) => deepLod.eraClockHAtAge(tMa),
363
- tableAnchorYear: startmodelYear,
364
- driftRefYear: startModelYearWithCorrection,
365
- hJ2000: H,
366
- yearMin: -500e6,
367
- yearMax: 500e6,
368
- stepYears: 10000,
369
- });
370
- let phaseTableBuilt = false;
371
- const phase = () => {
372
- if (!phaseTableBuilt) {
373
- latticeAlphaPin = true;
374
- try { phaseM.ensureTable(); } finally { latticeAlphaPin = false; }
375
- phaseTableBuilt = true;
376
- }
377
- return phaseM;
378
- };
379
- /** @param {number} yearA @param {number} yearB @param {number} divisorN @returns {number} */
380
- const cyclesBetween = (yearA, yearB, divisorN) => {
381
- const cyc = phase().cyclesBetween(yearA, yearB, divisorN);
382
- return cyc === null ? (divisorN * (yearB - yearA)) / H : cyc;
383
- };
384
- /** @param {number} anchorYear @param {number} year @param {number} divisorN @returns {number} */
385
- const phaseRadians = (anchorYear, year, divisorN) => 2 * Math.PI * cyclesBetween(anchorYear, year, divisorN);
363
+ // (Plan 09 step 7a: the integrated ∫dt/H_era phase table and its counter
364
+ // `cyclesBetween` — the frozen era clock's own convention H_era =
365
+ // H₀·LOD/LOD₀, plan 06 D8's second named counter, which every FROZEN comb
366
+ // read — are DELETED with the last comb; `epoch.cyclesBetween` left the
367
+ // surface with them, no reader remained. Step 5b: `phaseRadians` left with
368
+ // the RA day offset comb.)
386
369
 
387
370
  /** @param {number} year @returns {number} */
388
371
  const yearToTMa = (year) => (2000 - year) / 1e6;
@@ -404,15 +387,10 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
404
387
  const SUN_OFF = F.EARTH_OSCULATING_MEAN_OFFSET;
405
388
  /** The eclipse Sun's ϖ of date (mean elements). @param {number} year @returns {number} */
406
389
  const sunPerihelionDegAt = laws.perihelionLongitudeDegAt ?? ((year) => oneSourceM.periOfDateDegAt(year) + SUN_OFF.dPomArcsec / 3600);
390
+ // Plan 09: the obliquity of date is the one-source hybrid everywhere — the
391
+ // eclipse tier (besselian deps) read the retired 16-harmonic comb here.
407
392
  /** @param {number} year @returns {number} */
408
- const obliquityDeg = (year) => {
409
- let obliq = solsticeObliquityMean;
410
- for (const [div, sinC, cosC] of F.SOLSTICE_OBLIQUITY_HARMONICS) {
411
- const ph = phaseRadians(balancedYear, year, div);
412
- obliq += sinC * Math.sin(ph) + cosC * Math.cos(ph);
413
- }
414
- return obliq;
415
- };
393
+ const obliquityDeg = (year) => oneSourceM.epsAt(year);
416
394
  // ECCENTRICITY UNIFICATION — ONE law for the whole model (plan
417
395
  // IP-eccentricity-unification, decision D1 = form (e)). Earth's orbital
418
396
  // eccentricity is the magnitude of a vector sum, e = |v₁ + v₂|: v₁ of
@@ -430,9 +408,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
430
408
  // lattice periods; H/16 is the OF-DATE perihelion period (13 + 3 = 16 —
431
409
  // the H/3 rotation seen from the H/13 equinox) and belongs to ϖ_of-date;
432
410
  // measured, the H/16 law's present ė (−0.84e-5/cy) is 5× below the
433
- // observed −4.20e-5 while this law reads −4.31e-5 (Phase-0 record:
434
- // tools/explore/fq7s-h3-law-candidate.mjs; JPL Sun 1.49″, registry 0.80″,
435
- // syzygy 3.72″). Consumers (engine-switch decision (ii) restatement,
411
+ // observed −4.20e-5 while this law reads −4.31e-5 (Phase-0 record: the
412
+ // H/3-law candidate instrument, deleted at plan 09 step 7e — docs/109 §22;
413
+ // JPL Sun 1.49″, registry 0.80″, syzygy 3.72″). Consumers (engine-switch decision (ii) restatement,
436
414
  // plan 02 §8): the eclipse Sun (equation of centre), the besselian Sun
437
415
  // distance and the cardinal-point braid — the CLOCK-side machinery. The
438
416
  // LUNAR chain (E-factor, perigee/node modulation/T², argument Δe² and
@@ -472,75 +450,51 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
472
450
  // one-source construction defined with the planets' chain evaluators below.)
473
451
 
474
452
  // ── Year/day lengths ──────────────────────────────────────────────────────
475
- /** @param {number} year @param {number} base @param {Array<[number, number, number]>} harmonics @returns {number} */
476
- const evalYearFourier = (year, base, harmonics) => {
477
- let result = base;
478
- const c1 = phase().cyclesBetween(balancedYear, year, 1);
479
- if (c1 === null) return result;
480
- for (const [div, sinC, cosC] of harmonics) {
481
- const ph = div * c1 * 2 * Math.PI;
482
- result += sinC * Math.sin(ph) + cosC * Math.cos(ph);
483
- }
484
- return result;
485
- };
486
- /** @param {number} year @returns {number} */
487
- const evalSiderealYearFourierIAU = (year) => evalYearFourier(year, meanSiderealYearDays, F.SIDEREAL_YEAR_HARMONICS);
488
- /** @param {number} year @returns {number} */
489
- const siderealYearDaysBase = (year) => {
490
- const tMa = yearToTMa(year);
491
- const lod = deepLod.lodSecondsAtAge(tMa);
492
- if (lod === null) return meanSiderealYearDays;
493
- return deepLod.siderealYearSecondsAtAge(tMa) / lod;
494
- };
495
- // The comb family's bases are the FROZEN era clock's forms (H_era; plan 06
496
- // D8) — the combs were fitted on them. The physical year lengths ride the
497
- // unit through yearLengths (family B) and the tidal-chain mean.
498
- /** @param {number} year @returns {number} */
499
- const tropicalYearDaysBase = (year) => {
500
- const days = deepLod.eraClockYearInDaysAtAge(yearToTMa(year));
501
- return days === null ? meanSolarYearDays : days;
502
- };
503
- /** THE J2000 precession anchor (plan 06 S5 — one J2000 precession
504
- * reading): the certified of-date year laws at 2000, sid/(sid − trop) =
505
- * 25,771.4 yr = 50.2883 ″/yr (IAU 50.2879 to 8×10⁻⁶) — identical to the
506
- * engine/browser Direct twins. ONE home: the composed clock's p₀, the
507
- * hybrid's self-anchor and the lunisolar surface all read it. The former
508
- * H/13 reading (25,793.6) is the fit anchor's, not a period. Memoized;
509
- * hoisted so the deep-time factory's lazy dep can name it.
510
- * @returns {number} */
453
+ // Plan 09: the three year-length combs (TROPICAL/SIDEREAL/ANOMALISTIC_
454
+ // YEAR_HARMONICS on the frozen era clock's bases) and their evaluators
455
+ // are deleted. The year lengths of date in DAYS are the published SI year
456
+ // lengths over the kinematic day of date; the kinematic day of date is the
457
+ // Layer-0 tidal mean LOD, exactly ω₀ (the SI day) at J2000 — the former
458
+ // "sidereal seconds over comb days" carried the λ̇ ripple of the sidereal
459
+ // year as a day-length ripple (≤ 0.1 ms in the era, 0.66 ms at ±26 kyr).
460
+ /** THE J2000 precession anchor p₀ (plan 09): the IAU 2006 general
461
+ * precession in longitude at J2000 (astro-reference.json
462
+ * earthOrbital.generalPrecessionJ2000_arcsecPerYr = 50.28796195 ″/yr),
463
+ * stated as the period 1,296,000/p_A = 25,771.575 Julian years. ONE home:
464
+ * the composed clock's p₀, the hybrid's self-anchor and the lunisolar
465
+ * surface all read it. Before plan 09 it was the retired year combs' beat
466
+ * at 2000 (plan 06 S5: 25,771.395 yr, 8 ppm faster); the H/13 reading
467
+ * (25,793.6) was the fit anchor's, not a period. The identifier keeps its
468
+ * name. @returns {number} */
511
469
  function certifiedAxialPrecessionJ2000Years() {
512
- if (certifiedAxial0Memo === undefined) {
513
- const sidLaw2000 = evalYearFourier(2000, siderealYearDaysBase(2000), F.SIDEREAL_YEAR_HARMONICS);
514
- const solLaw2000 = evalYearFourier(2000, tropicalYearDaysBase(2000), F.TROPICAL_YEAR_HARMONICS);
515
- certifiedAxial0Memo = sidLaw2000 / (sidLaw2000 - solLaw2000);
516
- }
517
- return certifiedAxial0Memo;
470
+ return 1296000 / C.earthOrbital.generalPrecessionJ2000_arcsecPerYr;
518
471
  }
519
- /** @type {number|undefined} */
520
- let certifiedAxial0Memo;
521
- /** @param {number} year @returns {number} */
522
- const anomalisticYearDaysBase = (year) => {
523
- const tMa = yearToTMa(year);
524
- const Ht = deepLod.eraClockHAtAge(tMa);
525
- const tropD = deepLod.eraClockYearInDaysAtAge(tMa);
526
- if (Ht === null || tropD === null) return meanAnomalisticYearDays;
527
- return (tropD * (Ht / 16)) / (Ht / 16 - 1);
528
- };
529
- /** @param {number} year @returns {number} */
530
- const siderealYearDays = (year) => evalYearFourier(year, siderealYearDaysBase(year), F.SIDEREAL_YEAR_HARMONICS);
531
- /** @param {number} year @returns {number} */
532
- const anomalisticYearDays = (year) => evalYearFourier(year, anomalisticYearDaysBase(year), F.ANOMALISTIC_YEAR_HARMONICS);
533
- /** Kinematic LOD (Layer 0). @param {number} year @returns {number} */
534
- const dayLengthSeconds = (year) => deepLod.siderealYearSecondsAtAge(yearToTMa(year)) / siderealYearDays(year);
535
- /** @param {number} year @returns {number} */
536
- const raDayOffsetMs = (year) => {
537
- let ms = F.RA_DAY_OFFSET_MS.mean;
538
- for (const [div, sinC, cosC] of F.RA_DAY_OFFSET_MS.terms) {
539
- const ph = phaseRadians(balancedYear, year, div);
540
- ms += sinC * Math.sin(ph) + cosC * Math.cos(ph);
541
- }
542
- return ms;
472
+ /** Kinematic day of date (Layer 0 tidal mean LOD; ω₀ at J2000). Past the
473
+ * tidal-lock asymptote the chain is null — the J2000 day stands in, as
474
+ * the retired comb base did. @param {number} year @returns {number} */
475
+ const dayLengthSeconds = (year) => deepLod.lodSecondsAtAge(yearToTMa(year)) ?? meanLengthOfDay;
476
+ /** Sidereal year of date in days of the epoch — the published SI sidereal
477
+ * year (the λ̇ channel inside the one-family window, the mass-loss law
478
+ * beyond) over the kinematic day of date. @param {number} year @returns {number} */
479
+ const siderealYearDays = (year) => publishedSiderealYearSecondsAtYear(year) / dayLengthSeconds(year);
480
+ /** Anomalistic year of date in days of the epoch — the published SI
481
+ * anomalistic year (one-family inside its window; NaN beyond, where the
482
+ * movement's apsidal line is not resolved). @param {number} year @returns {number} */
483
+ const anomalisticYearDays = (year) => {
484
+ const s = Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS ? yearLengthsM.anomalisticYearSecondsAtYear(year) : NaN;
485
+ return s / dayLengthSeconds(year);
543
486
  };
487
+ // (Plan 09 step 5b: raDayOffsetMs — the fitted RA_DAY_OFFSET_MS comb, a −14.194 ms
488
+ // mean + lines on H/16 and H/8 — and measuredSolarDaySeconds = dayLength + that
489
+ // offset are deleted. The quantity was the equation of time's window term of the
490
+ // Framework-Verification instrument: 365 solar noons counted from the June
491
+ // solstice end 0.2422 d short of the tropical year, so the mean noon-to-noon
492
+ // interval differs from the mean solar day by −(0.2422/365)·dEoT/dt at the
493
+ // solstice — the e·cos(90° − ϖ) term on the perihelion-of-date cycle (the comb's
494
+ // "H/16" line, 5.6 ms vs 5.2 first-order) and the tan²(ε/2) term on the obliquity
495
+ // cycle (the "H/8" line, 1.7 vs 1.4). A property of the window, not of the sky:
496
+ // the physical day of date is dayLengthSeconds; the equation of time is the
497
+ // certified Sun's. docs/11 § "The retired RA day offset".)
544
498
 
545
499
  // ── Cardinal points (the fitted model retired, R1)──────────────────────────────────────────────────
546
500
  // Plan 06 R1: the fitted cardinal-point model (CARDINAL_POINT_* — the §10
@@ -576,21 +530,21 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
576
530
  // per-tier sampler.
577
531
  const oneSourceM = (() => {
578
532
  const AEarth = /** @type {any} */ (CHAIN_ARTIFACT).j2000AnchorElements.earth;
579
- // The ψ̇ anchor: the CERTIFIED of-date laws at 2000 — identical to the
580
- // engine/browser (computeSiderealYearDaysDirect / computeSolarYearDays
581
- // Direct twins → 25,771.4). The FOUNDATIONAL mean pair used before
582
- // (meanSiderealYearDays/meanSolarYearDays → 25,796) anchored this
583
- // movement's realized precession 25 yr off the certified value — the
584
- // hidden split behind the API's 365.242204 tropical mean (found in the
585
- // owner's one-implementation drive).
533
+ // The ψ̇ anchor: the IAU 2006 general precession p_A (plan 09; 25,771.575
534
+ // yr — the ONE home certifiedAxialPrecessionJ2000Years, twins in
535
+ // tools-lib and the browser). History: plan 06 S5 anchored it on the
536
+ // certified year combs' beat at 2000 (25,771.4); before that the device's
537
+ // mean pair (meanSiderealYearDays/meanSolarYearDays → 25,796) anchored
538
+ // this movement's realized precession 25 yr off — the hidden split behind
539
+ // the API's 365.242204 tropical mean (found in the owner's
540
+ // one-implementation drive).
586
541
  const axial0 = certifiedAxialPrecessionJ2000Years(); // S5: the ONE home
587
542
  // D6 → Phase 3 (plan 06): the deep-time ψ̇(t) the hybrid precesses on is
588
- // the COMPOSED lunisolar rate, and since Phase 3 the unit H(t) IS
589
- // 13 × that period (deltat/deep-time.cjs hAtAge), so the injection is
590
- // the plain period₀·H(t)/H₀ again — on the unit, not on the frozen
591
- // era clock's H_era (which misses the lunar 1/a³ growth: 70.9 vs
592
- // Lantink 2022's 108.6 ± 8.5 ″/yr at 2.46 Ga; the unit reads 104.5).
593
- const H0 = /** @type {number} */ (deepLod.hAtAge(0));
543
+ // the COMPOSED lunisolar rate — its period of date T_p(t) read directly
544
+ // (plan 09 step 5c; it was period₀·H(t)/H₀ on the unit, the same number
545
+ // to 2e-16 since the unit scales with T_p). Not the frozen era clock's
546
+ // H_era (which misses the lunar 1/a³ growth: 70.9 vs Lantink 2022's
547
+ // 108.6 ± 8.5 ″/yr at 2.46 Ga; the composed rate reads 104.5).
594
548
  const seriesArt = /** @type {any} */ (secularSeriesArtifact);
595
549
  const sb = seriesArt && seriesArt.bodies && seriesArt.bodies.earth;
596
550
  const hist = createDeepOrbitalHistory({
@@ -612,10 +566,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
612
566
  // rendering-device residual imported as the initial condition of the
613
567
  // physics (found when the package began publishing the hybrid).
614
568
  obliquityJ2000Deg: C.earthOrbital.obliquityJ2000_deg,
615
- axialPrecessionYearsAtYearFn: (yr) => {
616
- const h = deepLod.hAtAge((startmodelYear - yr) / 1e6);
617
- return axial0 * (h === null ? 1 : h / H0);
618
- },
569
+ axialPrecessionYearsAtYearFn: (yr) => deepLod.lunisolarPrecessionPeriodYearsAtAge((startmodelYear - yr) / 1e6) ?? axial0,
619
570
  // THE PRECESSION OF DATE (plan 06 §9 item 10): on top of the secular
620
571
  // law above, the solar torque at the eccentricity of date and the
621
572
  // oblateness of date from the GIA channel — both exactly 1 at J2000,
@@ -686,8 +637,6 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
686
637
  * ≡ the one-family year. Plan 06 R1: formerly the fitted cardinal model's
687
638
  * 4-mean. @param {number} year @returns {number} */
688
639
  const tropicalYearDays = (year) => oneFamilyTropicalYearSeconds(year) / dayLengthSeconds(year);
689
- /** @param {number} year @returns {number} */
690
- const tropicalYearDirectDays = (year) => evalYearFourier(year, tropicalYearDaysBase(year), F.TROPICAL_YEAR_HARMONICS);
691
640
 
692
641
  /** Layer-4 solar day of date in SI seconds — LOD_real, the shipped
693
642
  * observable (the tweakpane Solar Day row, the registry's lodRealPhysical,
@@ -727,8 +676,6 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
727
676
  const raProjection = Math.cos((oneSourceM.epsAt(year) * Math.PI) / 180);
728
677
  return (sidDay / Tp / (syD + 1)) * raProjection + sidDay;
729
678
  };
730
- /** @param {number} year @returns {number} */
731
- const measuredSolarDaySeconds = (year) => dayLengthSeconds(year) + raDayOffsetMs(year) / 1000;
732
679
 
733
680
  // ── Moon at epoch ─────────────────────────────────────────────────────────
734
681
  /** Solar-Δa-corrected Kepler month. @param {number} year @returns {number} */
@@ -993,25 +940,26 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
993
940
  const j2000JD = 2451545.0;
994
941
  const julianCenturyDays = 36525;
995
942
 
996
- // J2000 Moon precession anchors (Option C+ — of-date observational anchors
997
- // in the legacy-'ICRF'-named inputs; the E values are star-referenced ∓13)
998
- const nApsidalIJ2000 = Math.round((8 * totalDaysInH) / C.moonReference.moonApsidalPrecessionDaysInputICRF) / 8;
999
- const nNodalIJ2000 = Math.round((8 * totalDaysInH) / C.moonReference.moonNodalPrecessionDaysInputICRF) / 8;
1000
- const nApsidalEJ2000 = nApsidalIJ2000 - 13;
1001
- const nNodalEJ2000 = nNodalIJ2000 + 13;
1002
- const moonApsidalJ2000Seconds = (totalDaysInH / nApsidalEJ2000) * meanLengthOfDay;
1003
- const moonNodalJ2000Seconds = (totalDaysInH / nNodalEJ2000) * meanLengthOfDay;
1004
- // the OF-DATE anchors (the H² counter's J2000 period — the Meeus/IERS
1005
- // observables 3231.49 d / 6798.38 d)
1006
- const moonApsidalOfDateJ2000Seconds = (totalDaysInH / nApsidalIJ2000) * meanLengthOfDay;
1007
- const moonNodalOfDateJ2000Seconds = (totalDaysInH / nNodalIJ2000) * meanLengthOfDay;
1008
- const moonSiderealMonthJ2000Seconds = moonSiderealMonth * meanLengthOfDay;
943
+ // J2000 Moon precession anchors (plan 09: the IAU inputs themselves). The
944
+ // legacy-'ICRF'-named inputs hold the EQUINOX-OF-DATE observables (Meeus/
945
+ // IERS 3231.50 d / 6798.38 d); the star-referenced (inertial) periods
946
+ // follow by removing the equinox's own precession, 1/T_E = 1/T_I ∓ 1/T_p
947
+ // (perigee advances, node regresses), T_p the J2000 precession anchor.
948
+ // The retired era device counted these as eighth-integers per 8H and read
949
+ // the periods back as totalDaysInH/N (the ∓13 was H/13 cycles per H).
950
+ const moonApsidalOfDateJ2000Seconds = C.moonReference.moonApsidalPrecessionDaysInputICRF * 86400;
951
+ const moonNodalOfDateJ2000Seconds = C.moonReference.moonNodalPrecessionDaysInputICRF * 86400;
952
+ const moonApsidalJ2000Seconds = 1 / (1 / moonApsidalOfDateJ2000Seconds - 1 / (axialPrecessionJ2000Days * 86400));
953
+ const moonNodalJ2000Seconds = 1 / (1 / moonNodalOfDateJ2000Seconds + 1 / (axialPrecessionJ2000Days * 86400));
954
+ const moonSiderealMonthJ2000Seconds = moonSiderealMonth * 86400;
955
+ // (Plan 09 step 5c: the per-H of-date cycle counts and the month chain's H²
956
+ // scaling of the of-date periods are deleted; the of-date perigee/node are
957
+ // the chain's count compositions.)
1009
958
 
1010
- // 8H-lattice derived months (constants.js §Moon derived months)
1011
- const nSid = Math.round((8 * totalDaysInH) / moonSiderealMonthInput) / 8;
1012
- const moonTropicalMonthDays = totalDaysInH / (nSid + 13);
1013
- const moonAnomalisticMonthDays = totalDaysInH / (nSid - nApsidalEJ2000);
1014
- const moonSynodicMonthDays = totalDaysInH / (nSid + 13 - H);
959
+ // Derived months on the IAU inputs (constants.js §Moon derived months)
960
+ const moonTropicalMonthDays = 1 / (1 / moonSiderealMonth + 1 / axialPrecessionJ2000Days);
961
+ const moonAnomalisticMonthDays = 1 / (1 / moonSiderealMonth - 86400 / moonApsidalJ2000Seconds);
962
+ const moonSynodicMonthDays = moonSiderealMonth * meanSiderealYearDays / (meanSiderealYearDays - moonSiderealMonth);
1015
963
 
1016
964
  // moonEcc — the model's ONE eccentricity law — is created above (with
1017
965
  // eccentricityAt); the Moon channel's E-factor e(t)/e(J2000) and its
@@ -1028,14 +976,9 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1028
976
  gmEarthMoonM3PerS2: EPOCH_PARAMS.gmEarthMoonM3S2,
1029
977
  massRatioEarthMoon: MASS_RATIO_EARTH_MOON,
1030
978
  moonSiderealMonthInputDays: moonSiderealMonthInput,
1031
- holisticYearJ2000: H,
1032
979
  meanSiderealYearJ2000Seconds: meanSiderealYearSeconds,
1033
- nApsidalOfDateJ2000: nApsidalIJ2000,
1034
- nNodalOfDateJ2000: nNodalIJ2000,
1035
980
  moonApsidalJ2000Seconds,
1036
981
  moonNodalJ2000Seconds,
1037
- moonApsidalOfDateJ2000Seconds,
1038
- moonNodalOfDateJ2000Seconds,
1039
982
  moonSiderealMonthJ2000Seconds,
1040
983
  sPerigee: MOON_ECC_SENSITIVITY_PERIGEE,
1041
984
  sNode: MOON_ECC_SENSITIVITY_NODE,
@@ -1043,7 +986,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1043
986
  fns: {
1044
987
  meanLodSecondsAtAge: /** @param {number} tMa */ (tMa) => deepLod.lodSecondsAtAge(tMa),
1045
988
  meanSiderealYearSecondsAtAge: /** @param {number} tMa */ (tMa) => deepLod.siderealYearSecondsAtAge(tMa),
1046
- meanHAtAge: /** @param {number} tMa */ (tMa) => deepLod.hAtAge(tMa),
989
+ // plan 09 step 5c: the tropical month's equinox turn is one per T_p(t)
990
+ precessionPeriodYearsAtAge: /** @param {number} tMa */ (tMa) => deepLod.lunisolarPrecessionPeriodYearsAtAge(tMa),
1047
991
  // Decision (ii) (owner-approved MODEL CHANGE, plan 02 §8): the whole
1048
992
  // lunar chain — this modulation, the integrated cycle counts it
1049
993
  // drives, the arguments' eccAt/channelIntegral and the E-factor —
@@ -1061,8 +1005,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1061
1005
  // Float64Array tables key correctly and persist.
1062
1006
  const chainCycles = createChainCycleIntegrator({
1063
1007
  ageAnchorYear: startmodelYear,
1064
- tropicalYearSecondsAtAge: /** @param {number} tMa */ (tMa) => deepLod.tropicalYearSecondsAtAge(tMa),
1065
- tropicalYearJ2000Seconds: meanTropicalYearJ2000Seconds,
1008
+ tropicalYearJ2000Seconds: meanTropicalYearJ2000Seconds, // the SI tick (plan 09 step 7c′)
1066
1009
  isDeepTime: () => true,
1067
1010
  });
1068
1011
  /** @param {number} tMa @returns {number|null} */
@@ -1071,6 +1014,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1071
1014
  const tropicalMonthPeriodFn = (tMa) => moonChain.tropicalMonthSecondsAtAge(tMa);
1072
1015
  /** @param {number} tMa @returns {number|null} */
1073
1016
  const anomalisticMonthPeriodFn = (tMa) => moonChain.anomalisticMonthSecondsAtAge(tMa);
1017
+ /** @param {number} tMa @returns {number|null} */
1018
+ const siderealMonthPeriodFn = (tMa) => moonChain.siderealMonthSecondsAtAge(tMa);
1074
1019
  const jupiterT0Seconds = C.planetOrbitalElements.jupiter.solarYearInput * 86400;
1075
1020
  /** @param {number} tMa @returns {number} */
1076
1021
  const jupiterPeriodFn = (tMa) => driver2PeriodSecondsAtAge(tMa, jupiterT0Seconds, EPOCH_PARAMS.solarMassLossFracPerYear);
@@ -1082,6 +1027,8 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1082
1027
  const mcAnomalistic = (a, b) => chainCycles.cyclesBetween(anomalisticMonthPeriodFn, a, b);
1083
1028
  /** @param {number} a @param {number} b @returns {number|null} */
1084
1029
  const mcJupiter = (a, b) => chainCycles.cyclesBetween(jupiterPeriodFn, a, b);
1030
+ /** The Moon's sidereal (star-referenced) orbits — the series' A3 argument (plan 09 step 5c). @param {number} a @param {number} b @returns {number|null} */
1031
+ const mcSidereal = (a, b) => chainCycles.cyclesBetween(siderealMonthPeriodFn, a, b);
1085
1032
  /** @param {number} a @param {number} b @returns {number|null} */
1086
1033
  const mcApsidalOfDate = (a, b) => {
1087
1034
  const t = mcTropical(a, b), n = mcAnomalistic(a, b);
@@ -1102,12 +1049,6 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1102
1049
  constants: {
1103
1050
  j2000JD,
1104
1051
  julianCenturyDays,
1105
- holisticYearJ2000: H,
1106
- balancedYearJ2000: balancedYear,
1107
- meanSolarYearDays,
1108
- meanAnomalisticYearDays,
1109
- tropicalYearHarmonics: F.TROPICAL_YEAR_HARMONICS,
1110
- anomalisticYearHarmonics: F.ANOMALISTIC_YEAR_HARMONICS,
1111
1052
  eccentricityJ2000: C.earthOrbital.earthEccentricityJ2000,
1112
1053
  eccentricityDotJ2000: C.earthOrbital.earthEccentricityDotJ2000,
1113
1054
  eccentricityDotDotJ2000: C.earthOrbital.earthEccentricityDotDotJ2000,
@@ -1123,22 +1064,31 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1123
1064
  // PUBLISHED ε (the one-source hybrid), not the K snapshot comb — one ε
1124
1065
  // everywhere. Twins: script.js (_sceneEpsTargetDeg), scene-graph.js.
1125
1066
  computeObliquityEarth: /** @param {number} year @returns {number} */ (year) => oneSourceM.epsAt(year),
1067
+ // Plan 09: the arguments' Sun IS the one certified Sun — the mean
1068
+ // longitude that integrates the one-family tropical year (the eclipse
1069
+ // finder's Sun) and the series' mean perihelion of date (unwrapped),
1070
+ // both on the TT Julian-year axis. The former "model timeline" Sun
1071
+ // (360° per mean solar year plus the integral of the retired year
1072
+ // combs) sat 0.2° behind the IAU mean Sun at −2000 — the combs could
1073
+ // not carry the tropical year's secular drift. Twins: scene-graph.js,
1074
+ // script.js.
1075
+ sunMeanLonDegAtJdTT: /** @param {number} jdTT @returns {number} */ (jdTT) => sunMeanLongitudeDegAt(2000 + (jdTT - j2000JD) / 365.25),
1076
+ sunPerihelionLonDegAtJdTT: /** @param {number} jdTT @returns {number} */ (jdTT) => sunPerihelionDegAt(2000 + (jdTT - j2000JD) / 365.25),
1126
1077
  jdToSIyear: yearFromJD,
1127
1078
  tropicalOrbitsBetween: mcTropical,
1128
1079
  apsidalOfDateCyclesBetween: mcApsidalOfDate,
1129
1080
  nodalOfDateCyclesBetween: mcNodalOfDate,
1130
- cyclesBetween,
1131
1081
  isDeepTime: () => true,
1132
1082
  isFrameworkNative: () => true,
1133
1083
  // (d′) of-date rate completion: the DYNAMICAL axial precession (the
1134
1084
  // tweakpane day-form identity — real at J2000, epoch-valid) and the
1135
1085
  // KINEMATIC pair's beat the chains embed. MATCHED TRIPLE with the
1136
1086
  // tools-lib and browser wirings.
1137
- pDynDegPerYearAt: /** @param {number} year */ (year) => {
1138
- const sid = siderealYearDays(year);
1139
- const sol = tropicalYearDirectDays(year);
1140
- return 360 * (sid - sol) / sid;
1141
- },
1087
+ // Plan 09: the dynamical rate of date is the published precession
1088
+ // period's (the one-family beat inside its window, the composed clock
1089
+ // beyond) — the retired year combs' day-form beat read 60 % of the
1090
+ // secular drift. Degrees per Julian year.
1091
+ pDynDegPerYearAt: /** @param {number} year */ (year) => 360 / publishedAxialPrecessionYearsAtYear(year),
1142
1092
  pKinDegPerYearAt: /** @param {number} year */ (year) => {
1143
1093
  // MODEL-START-anchored age, matching the tools-lib/browser wirings
1144
1094
  // bit-exactly — NOT yearToTMa's 2000.0 convention (the recorded
@@ -1172,10 +1122,12 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1172
1122
  // Bounded Meeus E-factor from the ONE deep e (decision (ii); the
1173
1123
  // pure-Meeus polynomial A/B branch stays engine-local)
1174
1124
  /** @param {number} dDays @returns {number} */
1175
- const fwEFactor = (dDays) => deepEcc.eFactorAt(dDays / C.foundational.inputmeanlengthsolaryearindays);
1125
+ // (plan 09 step 7c: on the channel's Julian-year axis — the N-body series'
1126
+ // time; the device's rounded tropical-year input read the axis 2e-5 short)
1127
+ const fwEFactor = (dDays) => deepEcc.eFactorAt(dDays / 365.25);
1176
1128
 
1177
- // D2 derived additional-argument rates (deg/cy, J2000 8H-lattice months;
1178
- // record: tools/explore/derive-a1a2a3.js)
1129
+ // D2 derived additional-argument rates (deg/cy, from the J2000 month inputs;
1130
+ // the derivation record derive-a1a2a3.js was deleted at plan 09 step 7e — docs/109 §22)
1179
1131
  const fwA2RateDegPerCy = 2 * ((360 * 36525) / moonTropicalMonthDays)
1180
1132
  - (360 * 36525) / moonAnomalisticMonthDays
1181
1133
  - 2 * ((360 * 36525) / C.planetOrbitalElements.jupiter.solarYearInput);
@@ -1218,7 +1170,7 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1218
1170
  jdToSIyear: yearFromJD,
1219
1171
  tropicalOrbitsBetween: mcTropical,
1220
1172
  apsidalOfDateCyclesBetween: mcApsidalOfDate,
1221
- cyclesBetween,
1173
+ siderealOrbitsBetween: mcSidereal,
1222
1174
  jupiterOrbitsBetween: mcJupiter,
1223
1175
  isDeepTime: () => true,
1224
1176
  isFrameworkNative: () => true,
@@ -1330,6 +1282,14 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1330
1282
  };
1331
1283
  return /** @param {number} year @returns {number} */ (year) => sunL0Deg + 360 * cyclesTo(year);
1332
1284
  })();
1285
+ // Plan 09: the lunar arguments' Sun perihelion of date is the eclipse
1286
+ // Sun's mean perihelion (`sunPerihelionDegAt`) read DIRECTLY, wrapped.
1287
+ // Every consumer wraps the argument it enters (M = Lsun − ϖ − 180) or
1288
+ // differences it over ±25 days (the rate anchors, wrapped to ±180°), so
1289
+ // no continuous angle is needed. MEASURED: a 100-yr unwrapping ladder
1290
+ // (two series reads per rung, 53,400 rungs at −5.34 Myr) cost the cold
1291
+ // deep jump 1,062 s — 343× the in-table jump against the perf gate's 8× —
1292
+ // for a value the consumers reduced mod 360 anyway.
1333
1293
 
1334
1294
  // Eclipse finders — wired like the engine probe (tools/verify/
1335
1295
  // eclipse-audit.js). The finder axis is JD(UT): the series wrapper applies
@@ -1453,6 +1413,13 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1453
1413
  if (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS) return yearLengthsM.tropicalYearSecondsAtYear(year);
1454
1414
  return deepLod.tropicalYearSecondsAtAge(yearToTMa(year));
1455
1415
  };
1416
+ /** The published sidereal year of date, SI seconds — the one-family λ̇
1417
+ * channel inside the window, the mass-loss law beyond (plan 09: the
1418
+ * `lengths` surface's days ride it). @param {number} year @returns {number} */
1419
+ const publishedSiderealYearSecondsAtYear = (year) => {
1420
+ if (Math.abs(year - 2000) <= ONE_FAMILY_WINDOW_YEARS) return yearLengthsM.siderealYearSecondsAtYear(year);
1421
+ return deepLod.siderealYearSecondsAtAge(yearToTMa(year));
1422
+ };
1456
1423
  // ── Cardinal instants (plan 06 R1) ─────────────────────────────────────
1457
1424
  /** The completed certified Sun (finder Sun − the derived planetary
1458
1425
  * completion) at a UT model-JD — the same longitude the scene renders
@@ -1546,9 +1513,20 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1546
1513
  jdFromYear,
1547
1514
  siTropicalYearDays,
1548
1515
  }),
1516
+ // Plan 09: the ONE certified Sun's mean elements of date — the lunar
1517
+ // arguments of every runtime build their Sun from these two (the Node
1518
+ // scene twin and the browser read them here; the "model timeline" Sun on
1519
+ // the retired year combs is gone).
1520
+ sun: Object.freeze({
1521
+ /** Mean longitude of date, degrees, unwrapped — L0 + 360·∫dt/T_trop (the one-family tropical year). @param {number} year @returns {number} */
1522
+ meanLongitudeDegAt: sunMeanLongitudeDegAt,
1523
+ /** The Sun's mean perihelion longitude of date (Earth's ϖ, mean elements), degrees, wrapped to [0, 360). @param {number} year @returns {number} */
1524
+ perihelionLongitudeDegAt: sunPerihelionDegAt,
1525
+ }),
1549
1526
  epoch: Object.freeze({
1550
1527
  yearToTMa,
1551
- hAtYear: /** @param {number} year @returns {number|null} */ (year) => deepLod.hAtAge(yearToTMa(year)),
1528
+ // (plan 09 step 7b: `hAtYear` — the unit H(t) on a published surface, the
1529
+ // API's `epoch.h` — is deleted; the deep-time clock is axialPrecessionYearsAtYear)
1552
1530
  // The Layer-2 mean day of date — the tidal + GIA base the deep-time law
1553
1531
  // gives at any age (86,399.9997 s at J2000): the day the paleo anchors
1554
1532
  // and the ΔT integrand ride. NOT the observable solar day of date —
@@ -1564,18 +1542,17 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1564
1542
  // (identical at J2000 where the ratio ≡ 1 by construction)
1565
1543
  siderealYearSecondsAtYear: /** @param {number} year @returns {number} */ (year) => yearLengthsM.siderealYearSecondsAtYear(year),
1566
1544
  deltaTSecondsAtYear: deltaTSeconds,
1567
- cyclesBetween,
1568
1545
  // Plan 06 Phase 3 S2 — THE published of-date precession period is the
1569
1546
  // one-family route (B): the hybrid's own equinox regression against
1570
1547
  // the λ̇-corrected sidereal year (~25,771 yr at J2000 ≈ IAU), inside
1571
1548
  // the banked tiers (|Δyear| ≤ ONE_FAMILY_WINDOW_YEARS); beyond, the
1572
1549
  // composed lunisolar period on the same J2000 anchor (S5; the
1573
- // of-date wobble is unresolved there). The comb family's beat
1574
- // sid/(sid − tropicalYearDirectDays) — the frozen era clock's
1575
- // kinematic-day device — is no longer published here; it still
1576
- // anchors the lunar chain's (d′) rate completion (pDynDegPerYearAt,
1577
- // a MATCHED TRIPLE with the eclipse gates: M0(a) measured a 3–15 min
1578
- // lunar-timing shift if it moved).
1550
+ // of-date wobble is unresolved there). The comb family's beat (the
1551
+ // frozen era clock's kinematic-day device) is deleted at plan 09; the
1552
+ // lunar chain's (d′) rate completion (pDynDegPerYearAt) rides THIS
1553
+ // published period — a MATCHED TRIPLE with the eclipse gates (M0(a)
1554
+ // measured a 3–15 min lunar-timing shift when it moved; the records
1555
+ // were re-baselined on the plan 09 move).
1579
1556
  axialPrecessionYearsAtYear: publishedAxialPrecessionYearsAtYear,
1580
1557
  // The same publication rule for the mean tropical year of date (SI s):
1581
1558
  // one-family inside the window, the tidal-chain mean beyond.
@@ -1686,12 +1663,10 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1686
1663
  // Plan 06 Phase 3 S3b: THE published obliquity is the hybrid — one
1687
1664
  // torque law integrated on the dynamical orbit plane of the N-body
1688
1665
  // chain, zero fitted constants (≡ La2004 to ≤ 4″ over ±50 kyr). The
1689
- // fitted 16-harmonic K comb, a device fitted to the scene's own
1690
- // wheel geometry (0.005″ to it, 1.27° off La2004 at −20 kyr), keeps
1691
- // its name below as the frozen era clock's device: it anchors the
1692
- // hybrid at J2000, the kinematic-day stack and the lunar arguments.
1666
+ // fitted 16-harmonic K comb that sat beside it as `obliquityCombDeg`
1667
+ // (a device fitted to the scene's own wheel geometry, 1.27° off
1668
+ // La2004 at −20 kyr) is deleted with the frozen era device (plan 09).
1693
1669
  obliquityDeg: /** @param {number} year @returns {number} */ (year) => oneSourceM.epsAt(year),
1694
- obliquityCombDeg: obliquityDeg,
1695
1670
  eccentricity: eccentricityAt,
1696
1671
  inclinationDeg,
1697
1672
  ascendingNodeDeg,
@@ -1703,15 +1678,12 @@ export function assembleModel(C, F, laws = {}, secularSeriesArtifact = /** @type
1703
1678
  // route) and the seamed `epoch.*AtYear` surfaces above.
1704
1679
  lengths: Object.freeze({
1705
1680
  tropicalYearDays,
1706
- tropicalYearDirectDays,
1707
1681
  siderealYearDays,
1708
1682
  anomalisticYearDays,
1709
1683
  dayLengthSeconds,
1710
1684
  lodRealSeconds,
1711
1685
  siderealDaySeconds,
1712
1686
  stellarDaySeconds,
1713
- measuredSolarDaySeconds,
1714
- raDayOffsetMs,
1715
1687
  }),
1716
1688
  // Plan 06 R1: the instants are the crossings of the ONE Sun (the
1717
1689
  // certified completed Sun the scene renders), UT model-JD; the RA at a