@essrt/physics 1.3.2 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@essrt/physics",
3
- "version": "1.3.2",
3
+ "version": "1.5.0",
4
4
  "description": "ESSRT (Expanding Solar System Resonance Theory) physics core \u2014 pure, injectable-constants, no I/O, no globals, no DOM. The complete model incl. fitted coefficients; every published version immutably ships one recorded model identity.",
5
5
  "essrt": {
6
6
  "modelVersion": "v11.0",
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  /** @type {string} */
20
- export const COEFFICIENTS_HASH = "1f1692da72c0e10a";
20
+ export const COEFFICIENTS_HASH = "bf011174cdd73175";
21
21
 
22
22
  /** @type {Readonly<Record<string, unknown>>} */
23
23
  export const FITTED_COEFFICIENTS = Object.freeze({
@@ -34,6 +34,7 @@ export const FITTED_COEFFICIENTS = Object.freeze({
34
34
  DT_STACK: {"bond":{"lattice_n":1830,"period_yr":1465.8666666666666,"cos_coeff_s":145.55092942423886,"sin_coeff_s":329.34746510072887,"raw_at_j2000_s":152.06408184557264,"amplitude_s":360.0761389269436,"phase_deg":66.15757672735135,"source":"Stage D anchored fit (USNO LOD constraint active)","joint_mode":true},"hallstatt":{"lattice_n":1104,"period_yr":2429.8333333333335,"cos_coeff_s":-73.00792820024905,"sin_coeff_s":32.70844569690342,"raw_at_j2000_s":-61.685166343361075,"amplitude_s":80,"phase_deg":155.86703617290817,"target_amp_source":"CAPPED from anchored quad-fit free amp 100.73 s → 80 s prior","joint_mode":true},"jose5":{"lattice_n":2989,"period_yr":897.469387755102,"cos_coeff_s":-34.5239711109052,"sin_coeff_s":36.16760178299611,"raw_at_j2000_s":31.185593588239136,"amplitude_s":50.00000000000001,"phase_deg":133.66807127984734,"target_amp_source":"CAPPED from anchored quad-fit free amp 86.44 s → 50 s prior","joint_mode":true},"jose4":{"lattice_n":3749,"period_yr":715.5337423312883,"cos_coeff_s":38.542513112148065,"sin_coeff_s":-31.850819188835548,"raw_at_j2000_s":41.35969402774114,"amplitude_s":50,"phase_deg":-39.56969682845315,"target_amp_source":"unconstrained: quad-fit free amp 47.06 s (below 50 s prior — no cap applied)","joint_mode":true}},
35
35
  ELONGATION_CORRECTION: {"venus":{"cosVwE_sinEl_ra":-0.00013078499578943705,"cosVwE_sinEl_dec":0.00023700025209189867,"sinEl_d_ra":0.0007755608738916972,"sinEl_d_dec":-0.0003797976373342036,"sinVwE_sinEl_ra":-0.0006776328282247138,"sinVwE_sinEl_dec":-0.00043481049672109887,"sin2VwE_sinEl_ra":0.0018944674227536075,"sin2VwE_sinEl_dec":0.007226452365794387,"cos2VwE_sinEl_ra":0.0022333742453015813,"cos2VwE_sinEl_dec":0.00643045263681664,"cos4VwE_sinEl_ra":-0.005168878088645803,"cos4VwE_sinEl_dec":-0.001292412216853322,"sin4VwE_sinEl_ra":0.002473729458674539,"sin4VwE_sinEl_dec":0.000937054827431583,"sinVwE_sinEl_d2_ra":0.0002912977464846237,"sinVwE_sinEl_d2_dec":0.00009508378159137789,"cos3VwE_sinEl_ra":-0.0004372175697726782,"cos3VwE_sinEl_dec":0.002048597993702653,"sin3VwE_sinEl_ra":-0.011369485008173839,"sin3VwE_sinEl_dec":-0.007442688270196107,"sin2syn_ra":-0.0010979280775001478,"sin2syn_dec":-0.00036294328226461474,"cos1syn_ra":0.0006738224187823681,"cos1syn_dec":-0.0006544590346891286,"sin3VwE_sinEl_d2_ra":0.002112353789427301,"sin3VwE_sinEl_d2_dec":0.0024447308843828846,"sin2VwE_sinEl_d2_ra":-0.000381924061656027,"sin2VwE_sinEl_d2_dec":-0.001112791195900534,"cos2VwE_sinEl_d2_ra":-0.0009611408402006777,"cos2VwE_sinEl_d2_dec":-0.0016972655933243698,"cosEl_d_ra":0.00019899967515409905,"cosEl_d_dec":-0.00034331917768957944,"cosVwE_cosEl_d_ra":-0.005960071418246849,"cosVwE_cosEl_d_dec":0.0031678195308038003,"sinVwE_cosEl_d_ra":-0.007190793725682356,"sinVwE_cosEl_d_dec":0.0013596615637549907,"cosEl_d2_ra":-0.00020663256097824926,"cosEl_d2_dec":-0.00004027080936022818,"cosVwE_cosEl_d2_ra":0.0005814547197492051,"cosVwE_cosEl_d2_dec":-0.000935906915634443,"sinVwE_cosEl_d2_ra":0.0007813571841350501,"sinVwE_cosEl_d2_dec":-0.00047223215820587456},"mars":{"cosVwE_sinEl_ra":-0.0011140487798611339,"cosVwE_sinEl_dec":-0.0036743796624175667,"sinEl_d_ra":0.0033627186215710245,"sinEl_d_dec":-0.012701815547956385,"sinVwE_sinEl_ra":-0.006356141564681869,"sinVwE_sinEl_dec":-0.00013369961659808483,"sin2VwE_sinEl_ra":0.00031724439612794344,"sin2VwE_sinEl_dec":0.01739168539941939,"cos2VwE_sinEl_ra":0.004143516153439421,"cos2VwE_sinEl_dec":0.00022778444218044325,"cos4VwE_sinEl_ra":0.005428306044246736,"cos4VwE_sinEl_dec":-0.01080111524328561,"sin4VwE_sinEl_ra":-0.0222174954712206,"sin4VwE_sinEl_dec":0.004286393953615477,"sinVwE_sinEl_d2_ra":0.005283420045811403,"sinVwE_sinEl_d2_dec":-0.0019096834272178508,"cos3VwE_sinEl_ra":-0.01713337342795123,"cos3VwE_sinEl_dec":0.002581862227614283,"sin3VwE_sinEl_ra":0.01566231796548501,"sin3VwE_sinEl_dec":-0.0046477676601650916,"sin2syn_ra":0.00256480149974197,"sin2syn_dec":-0.012209611899495584,"cos1syn_ra":-0.001006874061925775,"cos1syn_dec":0.009872831735740904,"sin3VwE_sinEl_d2_ra":-0.037392015803510585,"sin3VwE_sinEl_d2_dec":0.002473538068428684,"sin2VwE_sinEl_d2_ra":0.0038455948792325678,"sin2VwE_sinEl_d2_dec":-0.014778577154897714,"cos2VwE_sinEl_d2_ra":-0.011807978628793191,"cos2VwE_sinEl_d2_dec":0.0012557544306986937,"cosEl_d_ra":0.000284882868911627,"cosEl_d_dec":-0.0021820884562501544,"cosVwE_cosEl_d_ra":-0.0003505964419227473,"cosVwE_cosEl_d_dec":0.0055104246995418575,"sinVwE_cosEl_d_ra":0.0002518986669389568,"sinVwE_cosEl_d_dec":-0.0020101942296837116,"cosEl_d2_ra":0.00006092485835005758,"cosEl_d2_dec":-0.001141320906493464,"cosVwE_cosEl_d2_ra":0.00023028197939182156,"cosVwE_cosEl_d2_dec":-0.002735862075021542,"sinVwE_cosEl_d2_ra":0.00011442353028413846,"sinVwE_cosEl_d2_dec":-0.0005857378114159205}},
36
36
  GRAVITATION_CORRECTION: {"mercury":[{"period":47.426470068243695,"raSin":0.00020636689960606448,"raCos":-0.000463323074166234,"decSin":-0.0012812669809594151,"decCos":-0.000661073144287821},{"period":23.713235034121848,"raSin":0.0024571786204407695,"raCos":-0.00007829078384963884,"decSin":-0.00040252237651284623,"decCos":0.006123565966605076}],"mars":[{"period":2.2353274492027086,"raSin":0.003981258082191666,"raCos":-0.004279949161181997,"decSin":-0.0007025871136428711,"decCos":0.00013803329632746126},{"period":2.742729776153993,"raSin":0.0009224827474374854,"raCos":0.0035243396145755255,"decSin":-0.000041052377051558215,"decCos":0.0003882097655946518}],"jupiter":[{"period":12.782258986772387,"raSin":0.00010028515727242995,"raCos":-0.0013936371417833254,"decSin":0.0018857367234133855,"decCos":-0.0008391359414103676},{"period":19.858868818477937,"raSin":0.022964869787559093,"raCos":-0.005600637142447164,"decSin":-0.0004034780418806047,"decCos":0.00008820719281509448}],"saturn":[{"period":19.858868818477937,"raSin":-0.0053846507635660415,"raCos":-0.005118860493020084,"decSin":0.016664189906193345,"decCos":-0.012277491565905576},{"period":136.08644480519482,"raSin":0.023864298650611435,"raCos":0.003917202339383959,"decSin":-0.0009902470204102892,"decCos":-0.002105843197823579},{"period":71.74090714591357,"raSin":0.005228577374514756,"raCos":-0.02062423940765225,"decSin":0.0010240283184854112,"decCos":0.005623456135787617},{"period":45.36214826839827,"raSin":0.013297492020279023,"raCos":-0.002641113806354099,"decSin":-0.008482436148476174,"decCos":0.00024099809887146969}],"uranus":[{"period":171.42995910020446,"raSin":0.011161170123927776,"raCos":0.006577304787331431,"decSin":0.0003585949163466174,"decCos":0.0006814216700589923},{"period":13.812126704288008,"raSin":-0.0021822974433193016,"raCos":-0.014428543965216015,"decSin":0.000022821047430643845,"decCos":-0.0011047767505970472}],"neptune":[{"period":12.782258986772387,"raSin":-0.00020371209839160938,"raCos":-0.009366315007525577,"decSin":4.249640867513261e-7,"decCos":-0.00027671876932212577},{"period":35.870453572956784,"raSin":0.0013395890197134145,"raCos":-0.0049071499875754645,"decSin":0.00005320422039121083,"decCos":-0.0000805247365884665}]},
37
+ MEEUS_DISTANCE_TERMS: {"meanKm":385000.56,"terms":[[0,0,1,0,-20905355],[2,0,-1,0,-3699111],[2,0,0,0,-2955968],[0,0,2,0,-569925],[0,1,0,0,48888],[0,0,0,2,-3149],[2,0,-2,0,246158],[2,-1,-1,0,-152138],[2,0,1,0,-170733],[2,-1,0,0,-204586],[0,1,-1,0,-129620],[1,0,0,0,108743],[0,1,1,0,104755],[2,0,0,-2,10321],[0,0,1,2,0],[0,0,1,-2,79661],[4,0,-1,0,-34782],[0,0,3,0,-23210],[4,0,-2,0,-21636],[2,1,-1,0,24208],[2,1,0,0,30824],[1,0,-1,0,-8379],[1,1,0,0,-16675],[2,-1,1,0,-12831],[2,0,2,0,-10445],[4,0,0,0,-11650],[2,0,-3,0,14403],[0,1,-2,0,-7003],[2,0,-1,2,0],[2,-1,-2,0,10056],[1,0,1,0,6322],[2,-2,0,0,-9884],[0,1,2,0,5751],[0,2,0,0,0],[2,-2,-1,0,-4950],[2,0,1,-2,4130],[2,0,0,2,0],[4,-1,-1,0,-3958],[0,0,2,2,0],[3,0,-1,0,3258],[2,1,1,0,2616],[4,-1,-2,0,-1897],[0,2,-1,0,-2117],[2,2,-1,0,2354],[2,1,-2,0,0],[2,-1,0,-2,0],[4,0,1,0,-1423],[0,0,4,0,-1117],[4,-1,0,0,-1571],[1,0,-2,0,-1739],[2,1,0,-2,0],[0,0,2,-2,-4421],[1,1,1,0,0],[3,0,-2,0,0],[4,0,-3,0,0],[2,-1,2,0,0],[0,2,1,0,1165],[1,1,-1,0,0],[2,0,3,0,0],[2,0,-1,-2,8752]]},
37
38
  MEEUS_LATITUDE_TERMS: [[0,0,0,1,5128122],[0,0,1,1,280602],[0,0,1,-1,277693],[2,0,0,-1,173237],[2,0,-1,1,55413],[2,0,-1,-1,46271],[2,0,0,1,32573],[0,0,2,1,17198],[2,0,1,-1,9266],[0,0,2,-1,8822],[2,-1,0,-1,8216],[2,0,-2,-1,4324],[2,0,1,1,4200],[2,1,0,-1,-3359],[2,-1,-1,1,2463],[2,-1,0,1,2211],[2,-1,-1,-1,2065],[0,1,-1,-1,-1870],[4,0,-1,-1,1828],[0,1,0,1,-1794],[0,0,0,3,-1749],[0,1,-1,1,-1565],[1,0,0,1,-1491],[0,1,1,1,-1475],[0,1,1,-1,-1410],[0,1,0,-1,-1344],[1,0,0,-1,-1335],[0,0,3,1,1107],[4,0,0,-1,1021],[4,0,-1,1,833],[0,0,1,-3,777],[4,0,-2,1,671],[2,0,0,-3,607],[2,0,2,-1,596],[2,-1,1,-1,491],[2,0,-2,1,-451],[0,0,3,-1,439],[2,0,2,1,422],[2,0,-3,-1,421],[2,1,-1,1,-366],[2,1,0,1,-351],[4,0,0,1,331],[2,-1,1,1,315],[2,-2,0,-1,302],[0,0,1,3,-283],[2,1,1,-1,-229],[1,1,0,-1,223],[1,1,0,1,223],[0,1,-2,-1,-220],[2,1,-1,-1,-220],[1,0,1,1,-185],[2,-1,-2,-1,181],[0,1,2,1,-177],[4,0,-2,-1,176],[4,-1,-1,-1,166],[1,0,1,-1,-164],[4,0,1,-1,132],[1,0,-1,-1,-119],[4,-1,0,-1,115],[2,-2,0,1,107]],
38
39
  MEEUS_LONGITUDE_TERMS: [[0,0,1,0,6288774],[2,0,-1,0,1274027],[2,0,0,0,658314],[0,0,2,0,213618],[0,1,0,0,-185116],[0,0,0,2,-114332],[2,0,-2,0,58793],[2,-1,-1,0,57066],[2,0,1,0,53322],[2,-1,0,0,45758],[0,1,-1,0,-40923],[1,0,0,0,-34720],[0,1,1,0,-30383],[2,0,0,-2,15327],[0,0,1,2,-12528],[0,0,1,-2,10980],[4,0,-1,0,10675],[0,0,3,0,10034],[4,0,-2,0,8548],[2,1,-1,0,-7888],[2,1,0,0,-6766],[1,0,-1,0,-5163],[1,1,0,0,4987],[2,-1,1,0,4036],[2,0,2,0,3994],[4,0,0,0,3861],[2,0,-3,0,3665],[0,1,-2,0,-2689],[2,0,-1,2,-2602],[2,-1,-2,0,2390],[1,0,1,0,-2348],[2,-2,0,0,2236],[0,1,2,0,-2120],[0,2,0,0,-2069],[2,-2,-1,0,2048],[2,0,1,-2,-1773],[2,0,0,2,-1595],[4,-1,-1,0,1215],[0,0,2,2,-1110],[3,0,-1,0,-892],[2,1,1,0,-810],[4,-1,-2,0,759],[0,2,-1,0,-713],[2,2,-1,0,-700],[2,1,-2,0,691],[2,-1,0,-2,596],[4,0,1,0,549],[0,0,4,0,537],[4,-1,0,0,520],[1,0,-2,0,-487],[2,1,0,-2,-399],[0,0,2,-2,-381],[1,1,1,0,351],[3,0,-2,0,-340],[4,0,-3,0,330],[2,-1,2,0,327],[0,2,1,0,-323],[1,1,-1,0,299],[2,0,3,0,294]],
39
40
  MOON_CORRECTION: {"raSinD":7.184453869061568e-7,"raCosD":0.000007773645658437624,"raSinMp":1.4279023255816577e-7,"raCosMp":-0.000016143853244117095,"raSinMs":0.00004290517368813111,"raCosMs":-0.000028078099681085273,"decSinD":-0.00001414309929430708,"decCosD":0.000007487034647903192,"decSinMp":0.000008568789356865513,"decCosMp":0.000010874252100847628,"decSinMs":-0.00002315228957457375,"decCosMs":0.0000057588397049875064},
@@ -2,14 +2,14 @@
2
2
  // Gives the TypeScript website full type safety at the boundary (§2g) while
3
3
  // packages/physics stays JavaScript.
4
4
 
5
- export declare const CONSTANTS_HASH: "c27d98c2f75e949b";
5
+ export declare const CONSTANTS_HASH: "fa3230753655d190";
6
6
 
7
7
  export declare const MODEL_VERSION: string;
8
8
 
9
9
  export declare const PREPRINT_DOI: string;
10
10
 
11
11
  export declare const DEFAULT_CONSTANTS: {
12
- readonly hash: "c27d98c2f75e949b";
12
+ readonly hash: "fa3230753655d190";
13
13
  readonly additionalBodies: {
14
14
  "pluto": {
15
15
  "name": string;
@@ -239,6 +239,22 @@ export declare const DEFAULT_CONSTANTS: {
239
239
  "MASS_RATIO_EARTH_MOON": number;
240
240
  "speedOfLight": number;
241
241
  "earthMoiFactorJ2000": number;
242
+ "earthFlatteningInverseWGS84": number;
243
+ "aberrationConstantArcsec": number;
244
+ "gmstMeanSiderealT0Deg": number;
245
+ "gmstMeanSiderealRateDegPerDay": number;
246
+ "gmstMeanSiderealT2Deg": number;
247
+ "nutationLeadingTermsArcsec": {
248
+ "psiOmega": number;
249
+ "psi2Ls": number;
250
+ "psi2Lm": number;
251
+ "psi2Omega": number;
252
+ "epsOmega": number;
253
+ "eps2Ls": number;
254
+ "eps2Lm": number;
255
+ "eps2Omega": number;
256
+ "omegaNodeJ2000Deg": number;
257
+ };
242
258
  "solarLuminosityW": number;
243
259
  "solarWindMassLossKgPerS": number;
244
260
  "massRatioDE440": {
@@ -32,7 +32,7 @@
32
32
  * carry it so a counterfactual is reproducible (§2d).
33
33
  * @type {string}
34
34
  */
35
- export const CONSTANTS_HASH = "c27d98c2f75e949b";
35
+ export const CONSTANTS_HASH = "fa3230753655d190";
36
36
 
37
37
  /** Model version label — single source: public/input/model-version.json (§10 two-axis scheme). */
38
38
  export const MODEL_VERSION = "v11.0";
@@ -42,7 +42,7 @@ export const PREPRINT_DOI = "10.21203/rs.3.rs-8758810/v4";
42
42
 
43
43
  /** @type {Readonly<Record<string, unknown>>} */
44
44
  export const DEFAULT_CONSTANTS = Object.freeze({
45
- hash: "c27d98c2f75e949b",
45
+ hash: "fa3230753655d190",
46
46
  additionalBodies: {
47
47
  "pluto": {
48
48
  "name": "Pluto",
@@ -296,6 +296,22 @@ export const DEFAULT_CONSTANTS = Object.freeze({
296
296
  "MASS_RATIO_EARTH_MOON": 81.30056816,
297
297
  "speedOfLight": 299792.458,
298
298
  "earthMoiFactorJ2000": 0.3306947,
299
+ "earthFlatteningInverseWGS84": 298.257223563,
300
+ "aberrationConstantArcsec": 20.4955,
301
+ "gmstMeanSiderealT0Deg": 280.46061837,
302
+ "gmstMeanSiderealRateDegPerDay": 360.98564736629,
303
+ "gmstMeanSiderealT2Deg": 0.000387933,
304
+ "nutationLeadingTermsArcsec": {
305
+ "psiOmega": -17.2,
306
+ "psi2Ls": -1.32,
307
+ "psi2Lm": -0.23,
308
+ "psi2Omega": 0.21,
309
+ "epsOmega": 9.2,
310
+ "eps2Ls": 0.57,
311
+ "eps2Lm": 0.1,
312
+ "eps2Omega": -0.09,
313
+ "omegaNodeJ2000Deg": 125.04452
314
+ },
299
315
  "solarLuminosityW": 3.828e+26,
300
316
  "solarWindMassLossKgPerS": 1600000000,
301
317
  "massRatioDE440": {
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Solar-eclipse shadow geometry — the location tier (20.3g).
3
+ *
4
+ * Instantaneous shadow-axis evaluation on the FULL shared series (no
5
+ * precomputed polynomial elements): at any JD(UT) the geocentric Sun and
6
+ * Moon are placed in the equatorial-of-date frame, the umbra axis is
7
+ * intersected with the ellipsoid, and observer local circumstances follow
8
+ * from the observer's perpendicular offset to the axis versus the
9
+ * penumbra/umbra cone radii at the observer's axial plane.
10
+ *
11
+ * THE TWO AXIS CONVENTIONS (both measured against the 15 NASA path-table
12
+ * centerline points, tools/verify/eclipse-audit.js `centerlines`; the raw
13
+ * finder chain read ~61″ shadow-plane, this chain 6.3″ mean / 9.1″ max —
14
+ * within every NASA path half-width):
15
+ *
16
+ * 1. TT bridge. The framework ΔT curve is ZEROED AT J2000 by construction
17
+ * (it integrates LOD excess from the epoch anchor); the model's
18
+ * absolute ΔT is deltaTStart + curve — exactly how every other
19
+ * consumer reads it (the browser accumulator, Meeus geometry, the ΔT
20
+ * charts; deltaTStart is the joint-world trend anchor, deliberately
21
+ * ~8 s below the IERS instantaneous 63.6 s — the industrial-era
22
+ * acceleration the cyclic model does not capture). The eclipse
23
+ * FINDERS deliberately stay on the zeroed convention — their fitted
24
+ * anchors (SUN_HARMONICS, canon statistics) were produced through it,
25
+ * and eclipse TIMING is elongation-class, where the convention
26
+ * cancels against the fitted phases. GROUND LOCATION is absolute: the
27
+ * Earth-fixed frame rides true UT rotation, so the bodies are
28
+ * evaluated at the model's absolute TT. ttBridgeSeconds = deltaTStart
29
+ * (no new constant; measured BETTER on the NASA reference points than
30
+ * the observed 63.8/64.2 s — mean 6.3″ vs 7.3″). ~34″ of the raw
31
+ * offset.
32
+ *
33
+ * 2. Full series, not the truncated finder forms. truncatedLonDeg omits
34
+ * the fitted moonMeeusLpCorrection anchor (+33.8″) and truncatedBetaDeg
35
+ * omits the −2235·sin(Lp) latitude family (~8″) — both deliberate for
36
+ * the certified finder statistics (knife-edge canon events), both
37
+ * required here. The injected moonFullAtDaysTT is the series'
38
+ * sceneEvalAt — the same evaluation the browser scene ships. Measured:
39
+ * ~27″ of the raw offset.
40
+ *
41
+ * Annual aberration is deliberately ABSENT: for ground location a
42
+ * common-mode rotation of both bodies moves the ground point by the raw
43
+ * angle (~0.6 km), and the elongation content cancels at syzygy (the
44
+ * round-3 result). Both bodies here are geometric mean-of-date, matching
45
+ * the MEAN sidereal time used for the Earth-fixed mapping.
46
+ *
47
+ * The scene-umbra conventions (THREE scaffold navigation) never enter this
48
+ * package (§2h) — this is an independent construction on the shared
49
+ * series, cross-checked against the scene twins through the same NASA
50
+ * reference points.
51
+ */
52
+
53
+ 'use strict';
54
+
55
+ /**
56
+ * @typedef {Object} BesselianDeps
57
+ * @property {(dDaysTT: number) => {lonDeg: number, latDeg: number, distKm: number}} moonFullAtDaysTT
58
+ * full-series Moon: ecliptic-of-date longitude/latitude (deg) + distance (km), TT axis (days since J2000)
59
+ * @property {(jdUT: number) => number} sunLonDegAt - geometric mean sun longitude (deg), JD(UT) axis with the finder's internal ΔT
60
+ * @property {(jd: number) => number} deltaTSecondsAt - framework ΔT (J2000-zeroed convention)
61
+ * @property {(year: number) => number} obliquityDegAt - framework obliquity (deg) at calendar year
62
+ * @property {(year: number) => number} eccentricityAt - framework Earth-orbit eccentricity at calendar year
63
+ * @property {(year: number) => number} perihelionLongitudeDegAt - framework perihelion longitude (deg) at calendar year
64
+ * @property {(jd: number) => number} yearFromJD - the model's own JD → calendar-year conversion
65
+ * @property {{ j2000JD: number, julianCenturyDays: number, earthDiameterKm: number,
66
+ * moonDiameterKm: number, sunDiameterKm: number, sunDistanceKm: number,
67
+ * earthFlatteningInverse: number, ttBridgeSeconds: number,
68
+ * gmstMeanSiderealT0Deg: number, gmstMeanSiderealRateDegPerDay: number,
69
+ * gmstMeanSiderealT2Deg: number }} constants
70
+ */
71
+
72
+ /** @param {BesselianDeps} deps */
73
+ function createBesselian(deps) {
74
+ const K = deps.constants;
75
+ const D2R = Math.PI / 180;
76
+ const R_E_KM = K.earthDiameterKm / 2;
77
+ const R_SUN_KM = K.sunDiameterKm / 2;
78
+ const R_MOON_KM = K.moonDiameterKm / 2;
79
+ const F = 1 / K.earthFlatteningInverse;
80
+
81
+ /** Mean sidereal time at Greenwich, degrees (mean equinox — pairs with the
82
+ * mean-of-date body longitudes; coefficients single-sourced from
83
+ * astro-reference.json physicalConstants). @param {number} jdUT @returns {number} */
84
+ function gmstDeg(jdUT) {
85
+ const T = (jdUT - K.j2000JD) / K.julianCenturyDays;
86
+ return ((K.gmstMeanSiderealT0Deg + K.gmstMeanSiderealRateDegPerDay * (jdUT - K.j2000JD)
87
+ + K.gmstMeanSiderealT2Deg * T * T) % 360 + 360) % 360;
88
+ }
89
+
90
+ /** Ecliptic-of-date → equatorial-of-date vector (km).
91
+ * @param {number} lonDeg @param {number} betDeg @param {number} distKm
92
+ * @param {number} epsRad @returns {[number, number, number]} */
93
+ function eclToEq(lonDeg, betDeg, distKm, epsRad) {
94
+ const lam = lonDeg * D2R, bet = betDeg * D2R;
95
+ const x = Math.cos(bet) * Math.cos(lam);
96
+ const y = Math.cos(bet) * Math.sin(lam) * Math.cos(epsRad) - Math.sin(bet) * Math.sin(epsRad);
97
+ const z = Math.cos(bet) * Math.sin(lam) * Math.sin(epsRad) + Math.sin(bet) * Math.cos(epsRad);
98
+ return [distKm * x, distKm * y, distKm * z];
99
+ }
100
+
101
+ /** Elliptical Sun distance — FRAMEWORK-NATIVE: the model's own
102
+ * eccentricity (Law-5-locked base + drift) and perihelion longitude
103
+ * (H/16 precession) close the ellipse, with the true anomaly from the
104
+ * already-computed sun longitude (v = λ − (ϖ + 180°), geocentric-perigee
105
+ * convention). Agrees with the Meeus Ch. 25 polynomial to ±0.001% over a
106
+ * year, with no fitted external coefficients, and stays honest at deep
107
+ * time (e and ϖ evolve with the model). The distance is LOAD-BEARING for
108
+ * the umbral cone radius — a small difference of large terms: the
109
+ * mean-AU approximation's ±1.7% seasonal error became tens of percent of
110
+ * umbra radius (measured: Carbondale 2017 central duration 95 s vs the
111
+ * real 158 s; elliptical gives 157 s).
112
+ * @param {number} year @param {number} sunLonDeg @returns {number} km */
113
+ function sunDistanceKm(year, sunLonDeg) {
114
+ const e = deps.eccentricityAt(year);
115
+ const v = (sunLonDeg - (deps.perihelionLongitudeDegAt(year) + 180)) * D2R;
116
+ return K.sunDistanceKm * (1 - e * e) / (1 + e * Math.cos(v));
117
+ }
118
+
119
+ /** Sun and Moon geocentric equatorial-of-date vectors at true TT.
120
+ * @param {number} jdUT @returns {{S: [number,number,number], M: [number,number,number]}} */
121
+ function bodiesAt(jdUT) {
122
+ const jb = jdUT + K.ttBridgeSeconds / 86400; // finder-axis input → its internal ΔT lands on true TT
123
+ const year = deps.yearFromJD(jb);
124
+ const eps = deps.obliquityDegAt(year) * D2R;
125
+ const sunLon = deps.sunLonDegAt(jb);
126
+ const dTT = (jb - K.j2000JD) + deps.deltaTSecondsAt(jb) / 86400;
127
+ const moon = deps.moonFullAtDaysTT(dTT);
128
+ return {
129
+ S: eclToEq(sunLon, 0, sunDistanceKm(year, sunLon), eps),
130
+ M: eclToEq(moon.lonDeg, moon.latDeg, moon.distKm, eps),
131
+ };
132
+ }
133
+
134
+ /** Umbra-axis ground intersection at JD(UT): geodetic latitude, east
135
+ * longitude — or null when the axis misses the ellipsoid.
136
+ * @param {number} jdUT @returns {{latDeg: number, lonDeg: number} | null} */
137
+ function umbraGroundAt(jdUT) {
138
+ const { S, M } = bodiesAt(jdUT);
139
+ const dv = [M[0] - S[0], M[1] - S[1], M[2] - S[2]];
140
+ const dl = Math.hypot(dv[0], dv[1], dv[2]);
141
+ const d = [dv[0] / dl, dv[1] / dl, dv[2] / dl];
142
+ const MdotD = M[0] * d[0] + M[1] * d[1] + M[2] * d[2];
143
+ const MdotM = M[0] * M[0] + M[1] * M[1] + M[2] * M[2];
144
+ const disc = MdotD * MdotD - (MdotM - R_E_KM * R_E_KM);
145
+ if (disc < 0) return null;
146
+ const s = -MdotD - Math.sqrt(disc);
147
+ const hit = [M[0] + s * d[0], M[1] + s * d[1], M[2] + s * d[2]];
148
+ const r = Math.hypot(hit[0], hit[1], hit[2]);
149
+ const latGc = Math.asin(Math.max(-1, Math.min(1, hit[2] / r)));
150
+ const latDeg = Math.atan(Math.tan(latGc) / ((1 - F) * (1 - F))) / D2R;
151
+ let lonDeg = Math.atan2(hit[1], hit[0]) / D2R - gmstDeg(jdUT);
152
+ lonDeg = ((lonDeg + 540) % 360) - 180;
153
+ return { latDeg, lonDeg };
154
+ }
155
+
156
+ /** Observer geocentric position in the equatorial-of-date frame (km).
157
+ * Geodetic latitude on the ellipsoid (sea level).
158
+ * @param {number} jdUT @param {number} latDeg @param {number} lonDeg
159
+ * @returns {[number, number, number]} */
160
+ function observerVec(jdUT, latDeg, lonDeg) {
161
+ const u = Math.atan((1 - F) * Math.tan(latDeg * D2R)); // reduced latitude
162
+ const rho = [Math.cos(u), (1 - F) * Math.sin(u)]; // [equatorial, polar] components / R_E
163
+ const lst = (gmstDeg(jdUT) + lonDeg) * D2R; // local mean sidereal angle
164
+ return [
165
+ R_E_KM * rho[0] * Math.cos(lst),
166
+ R_E_KM * rho[0] * Math.sin(lst),
167
+ R_E_KM * rho[1],
168
+ ];
169
+ }
170
+
171
+ /**
172
+ * Instantaneous shadow state at the observer: perpendicular offset from
173
+ * the axis vs cone radii at the observer's axial plane.
174
+ * @param {number} jdUT @param {number} latDeg @param {number} lonDeg
175
+ * @returns {{offsetKm: number, penumbraKm: number, umbraKm: number, magnitude: number}}
176
+ * umbraKm is SIGNED: positive = annular (beyond the umbral vertex),
177
+ * negative = total; |umbraKm| is the central-zone radius either way.
178
+ */
179
+ function shadowStateAt(jdUT, latDeg, lonDeg) {
180
+ const { S, M } = bodiesAt(jdUT);
181
+ const O = observerVec(jdUT, latDeg, lonDeg);
182
+ const dv = [M[0] - S[0], M[1] - S[1], M[2] - S[2]];
183
+ const dl = Math.hypot(dv[0], dv[1], dv[2]);
184
+ const d = [dv[0] / dl, dv[1] / dl, dv[2] / dl]; // Sun→Moon (toward Earth)
185
+ const OM = [O[0] - M[0], O[1] - M[1], O[2] - M[2]];
186
+ const along = OM[0] * d[0] + OM[1] * d[1] + OM[2] * d[2]; // axial distance Moon→observer plane
187
+ const w = [OM[0] - along * d[0], OM[1] - along * d[1], OM[2] - along * d[2]];
188
+ const offsetKm = Math.hypot(w[0], w[1], w[2]);
189
+ const tanF1 = (R_SUN_KM + R_MOON_KM) / dl; // penumbral half-angle
190
+ const tanF2 = (R_SUN_KM - R_MOON_KM) / dl; // umbral half-angle
191
+ const penumbraKm = R_MOON_KM + along * tanF1;
192
+ const umbraKm = along * tanF2 - R_MOON_KM; // signed: >0 annular, <0 total
193
+ // Besselian magnitude convention: SIGNED umbral radius in the
194
+ // denominator — total events read >1 at the centre, annular <1.
195
+ const magnitude = (penumbraKm - offsetKm) / (penumbraKm + umbraKm);
196
+ return { offsetKm, penumbraKm, umbraKm, magnitude };
197
+ }
198
+
199
+ /**
200
+ * Local circumstances for an observer around one solar eclipse.
201
+ * Scans ±4 h around jdGreatest at 30 s, bisects contacts to ~1 s.
202
+ * @param {number} jdGreatest - the finder's greatest-eclipse JD(UT)
203
+ * @param {number} latDeg @param {number} lonDeg
204
+ * @returns {{kind: 'none'|'partial'|'annular'|'total',
205
+ * magnitude: number, maxJd: number,
206
+ * contacts: {c1: number|null, c2: number|null, c3: number|null, c4: number|null},
207
+ * centralDurationSeconds: number|null}}
208
+ */
209
+ function localCircumstances(jdGreatest, latDeg, lonDeg) {
210
+ const HALF_WIN = 4 / 24, STEP = 30 / 86400;
211
+ // maximum: minimize offset − nothing fancier than a fine scan + refine
212
+ let best = { t: jdGreatest, magnitude: -Infinity };
213
+ for (let t = jdGreatest - HALF_WIN; t <= jdGreatest + HALF_WIN; t += STEP) {
214
+ const st = shadowStateAt(t, latDeg, lonDeg);
215
+ if (st.magnitude > best.magnitude) best = { t, magnitude: st.magnitude };
216
+ }
217
+ // golden-section-ish refine on magnitude around the best sample
218
+ let lo = best.t - STEP, hi = best.t + STEP;
219
+ for (let i = 0; i < 30; i++) {
220
+ const m1 = lo + (hi - lo) / 3, m2 = hi - (hi - lo) / 3;
221
+ if (shadowStateAt(m1, latDeg, lonDeg).magnitude
222
+ < shadowStateAt(m2, latDeg, lonDeg).magnitude) lo = m1; else hi = m2;
223
+ }
224
+ const maxJd = (lo + hi) / 2;
225
+ const atMax = shadowStateAt(maxJd, latDeg, lonDeg);
226
+
227
+ /** Bisect a sign change of fn between a (negative) and b (positive).
228
+ * @param {(t: number) => number} fn @param {number} a @param {number} b
229
+ * @returns {number} */
230
+ const bisect = (fn, a, b) => {
231
+ for (let i = 0; i < 40; i++) {
232
+ const m = (a + b) / 2;
233
+ if (fn(m) < 0) a = m; else b = m;
234
+ }
235
+ return (a + b) / 2;
236
+ };
237
+ /** @param {number} t @returns {number} >0 inside the penumbra */
238
+ const penEdge = (t) => {
239
+ const st = shadowStateAt(t, latDeg, lonDeg);
240
+ return st.penumbraKm - st.offsetKm;
241
+ };
242
+ /** @param {number} t @returns {number} >0 inside the central zone */
243
+ const umbEdge = (t) => {
244
+ const st = shadowStateAt(t, latDeg, lonDeg);
245
+ return Math.abs(st.umbraKm) - st.offsetKm;
246
+ };
247
+
248
+ /** @type {{c1: number|null, c2: number|null, c3: number|null, c4: number|null}} */
249
+ const contacts = { c1: null, c2: null, c3: null, c4: null };
250
+ let kind = /** @type {'none'|'partial'|'annular'|'total'} */ ('none');
251
+ if (penEdge(maxJd) > 0) {
252
+ kind = 'partial';
253
+ // outer contacts: walk out from max until outside, then bisect
254
+ for (let t = maxJd; t >= maxJd - HALF_WIN; t -= STEP) {
255
+ if (penEdge(t) < 0) { contacts.c1 = bisect(penEdge, t, t + STEP); break; }
256
+ }
257
+ for (let t = maxJd; t <= maxJd + HALF_WIN; t += STEP) {
258
+ if (penEdge(t) < 0) { contacts.c4 = bisect(/** @param {number} x */ (x) => -penEdge(x), t - STEP, t); break; }
259
+ }
260
+ if (umbEdge(maxJd) > 0) {
261
+ kind = atMax.umbraKm < 0 ? 'total' : 'annular';
262
+ for (let t = maxJd; t >= maxJd - HALF_WIN; t -= STEP) {
263
+ if (umbEdge(t) < 0) { contacts.c2 = bisect(umbEdge, t, t + STEP); break; }
264
+ }
265
+ for (let t = maxJd; t <= maxJd + HALF_WIN; t += STEP) {
266
+ if (umbEdge(t) < 0) { contacts.c3 = bisect(/** @param {number} x */ (x) => -umbEdge(x), t - STEP, t); break; }
267
+ }
268
+ }
269
+ }
270
+ const centralDurationSeconds = (contacts.c2 !== null && contacts.c3 !== null)
271
+ ? (contacts.c3 - contacts.c2) * 86400 : null;
272
+ return { kind, magnitude: Math.max(0, atMax.magnitude), maxJd, contacts, centralDurationSeconds };
273
+ }
274
+
275
+ return { umbraGroundAt, shadowStateAt, localCircumstances };
276
+ }
277
+
278
+ module.exports = { createBesselian };
package/src/model.js CHANGED
@@ -32,6 +32,7 @@ import { createChainCycleIntegrator } from './chain-cycles/index.cjs';
32
32
  import { createMoonArguments, jdToDecimalYear } from './moon/arguments.cjs';
33
33
  import { createMoonSeries } from './moon/series.cjs';
34
34
  import { createEclipseFinders } from './eclipse/finders.cjs';
35
+ import { createBesselian } from './eclipse/besselian.cjs';
35
36
  import { driver2PeriodSecondsAtAge } from './planets/orbit-chain.cjs';
36
37
 
37
38
  /**
@@ -769,6 +770,9 @@ export function assembleModel(C, F) {
769
770
  constants: {
770
771
  moonL: F.MEEUS_LONGITUDE_TERMS,
771
772
  moonB: F.MEEUS_LATITUDE_TERMS,
773
+ moonR: F.MEEUS_DISTANCE_TERMS.terms,
774
+ moonRMeanKm: F.MEEUS_DISTANCE_TERMS.meanKm,
775
+ moonDistanceJ2000Km: C.moonReference.moonDistance,
772
776
  j2000JD,
773
777
  julianCenturyDays,
774
778
  moonMeeusLpCorrectionDeg: C.moon.moonMeeusLpCorrection,
@@ -813,6 +817,39 @@ export function assembleModel(C, F) {
813
817
  },
814
818
  });
815
819
 
820
+ // 20.3g — the solar-eclipse LOCATION tier: shadow geometry on the FULL
821
+ // series (sceneEvalAt — the truncated finder forms omit the fitted Lp
822
+ // anchor and the −2235·sin(Lp) β family, deliberate for finder
823
+ // statistics, required here) at the model's ABSOLUTE TT (deltaTStart +
824
+ // curve, the same convention every other ΔT consumer reads). Both axis
825
+ // conventions are measured against the NASA path-table centerlines —
826
+ // see eclipse/besselian.cjs.
827
+ const besselian = createBesselian({
828
+ moonFullAtDaysTT: /** @param {number} dDaysTT */ (dDaysTT) => {
829
+ const ev = moonSeries.sceneEvalAt(dDaysTT);
830
+ return { lonDeg: ev.lonDeg, latDeg: ev.latDeg, distKm: ev.distKm };
831
+ },
832
+ sunLonDegAt: /** @param {number} jdUT */ (jdUT) => eclipseFinders.sunLonDegAt(jdUT),
833
+ deltaTSecondsAt: /** @param {number} jd */ (jd) => (jdTTFromUT(jd) - jd) * 86400,
834
+ obliquityDegAt: obliquityDeg,
835
+ eccentricityAt,
836
+ perihelionLongitudeDegAt: earthPerihelionDeg,
837
+ yearFromJD,
838
+ constants: {
839
+ j2000JD,
840
+ julianCenturyDays,
841
+ earthDiameterKm: C.bodyDiametersKm.earth,
842
+ moonDiameterKm: C.bodyDiametersKm.moon,
843
+ sunDiameterKm: C.bodyDiametersKm.sun,
844
+ sunDistanceKm: currentAUDistance,
845
+ earthFlatteningInverse: C.physicalConstants.earthFlatteningInverseWGS84,
846
+ ttBridgeSeconds: C.earthOrbital.deltaTStart,
847
+ gmstMeanSiderealT0Deg: C.physicalConstants.gmstMeanSiderealT0Deg,
848
+ gmstMeanSiderealRateDegPerDay: C.physicalConstants.gmstMeanSiderealRateDegPerDay,
849
+ gmstMeanSiderealT2Deg: C.physicalConstants.gmstMeanSiderealT2Deg,
850
+ },
851
+ });
852
+
816
853
  // ── The assembled surface ─────────────────────────────────────────────────
817
854
  return Object.freeze({
818
855
  time: Object.freeze({
@@ -871,6 +908,9 @@ export function assembleModel(C, F) {
871
908
  findLunarInRange: /** @param {number} jdStart @param {number} jdEnd */ (jdStart, jdEnd) => eclipseFinders.findLunarEclipsesInRange(jdStart, jdEnd),
872
909
  findSolarInRange: /** @param {number} jdStart @param {number} jdEnd */ (jdStart, jdEnd) => eclipseFinders.findSolarEclipsesInRange(jdStart, jdEnd),
873
910
  deltaTSecondsAtJD: frameworkDeltaTSecondsAtJD,
911
+ // 20.3g location tier (see eclipse/besselian.cjs):
912
+ umbraGroundAtJD: /** @param {number} jd @returns {{latDeg: number, lonDeg: number} | null} */ (jd) => besselian.umbraGroundAt(jd),
913
+ solarLocalCircumstances: /** @param {number} jdGreatest @param {number} latDeg @param {number} lonDeg */ (jdGreatest, latDeg, lonDeg) => besselian.localCircumstances(jdGreatest, latDeg, lonDeg),
874
914
  }),
875
915
  climate: Object.freeze({
876
916
  l1OrbitalPermil: evalClimateL1,
@@ -35,6 +35,9 @@
35
35
  * constants: {
36
36
  * moonL: MeeusTermTable,
37
37
  * moonB: MeeusTermTable,
38
+ * moonR: MeeusTermTable,
39
+ * moonRMeanKm: number,
40
+ * moonDistanceJ2000Km: number,
38
41
  * j2000JD: number,
39
42
  * julianCenturyDays: number,
40
43
  * moonMeeusLpCorrectionDeg: number,
@@ -64,7 +67,7 @@
64
67
  */
65
68
  function createMoonSeries({ constants, fns }) {
66
69
  const {
67
- moonL, moonB, j2000JD, julianCenturyDays,
70
+ moonL, moonB, moonR, moonRMeanKm, moonDistanceJ2000Km, j2000JD, julianCenturyDays,
68
71
  moonMeeusLpCorrectionDeg, fwA2RateDegPerCy, fwA3RateDegPerCy,
69
72
  } = constants;
70
73
  const {
@@ -115,6 +118,40 @@ function createMoonSeries({ constants, fns }) {
115
118
  return s;
116
119
  }
117
120
 
121
+ /** Σr table pass — COSINE of the argument (Meeus 47.A distance column),
122
+ * same E/E² convention. Units 0.001 km.
123
+ * @param {MeeusTermTable} table @param {number} Dr @param {number} Mr
124
+ * @param {number} Mpr @param {number} Fr @param {number} E
125
+ * @param {number} E2 @returns {number} */
126
+ function sumTableCos(table, Dr, Mr, Mpr, Fr, E, E2) {
127
+ let s = 0;
128
+ for (let i = 0; i < table.length; i++) {
129
+ const r = table[i];
130
+ if (r[4] === 0) continue;
131
+ const arg = r[0] * Dr + r[1] * Mr + r[2] * Mpr + r[3] * Fr;
132
+ let term = r[4] * Math.cos(arg);
133
+ const absM = r[1] < 0 ? -r[1] : r[1];
134
+ if (absM === 1) term *= E;
135
+ else if (absM === 2) term *= E2;
136
+ s += term;
137
+ }
138
+ return s;
139
+ }
140
+
141
+ /** Full Meeus Ch. 47 distance: r = meanKm + Σr·10⁻³ km, scaled by the
142
+ * framework's Driver-1 distance ratio (≡ 1 at J2000; the epoch-mutable
143
+ * getter over the J2000 constant, preserving the deep-time scaling the
144
+ * previous two-term ellipse form carried). ~2 km vs JPL at syzygy where
145
+ * the two-term form was ~5,700 km too far (the 2D-family terms all peak
146
+ * at eclipses).
147
+ * @param {number} Dr @param {number} Mr @param {number} Mpr
148
+ * @param {number} Fr @param {number} E @param {number} E2
149
+ * @returns {number} km */
150
+ function fullDistanceKm(Dr, Mr, Mpr, Fr, E, E2) {
151
+ const Sr = sumTableCos(moonR, Dr, Mr, Mpr, Fr, E, E2);
152
+ return (moonRMeanKm + Sr * 1e-3) * (getMoonDistanceKm() / moonDistanceJ2000Km);
153
+ }
154
+
118
155
  /** The PRODUCTION scene evaluation at d days TT from J2000 (the caller
119
156
  * owns the UT→TT conversion — engine timing conventions differ).
120
157
  * Returns everything the scene blocks write:
@@ -155,7 +192,7 @@ function createMoonSeries({ constants, fns }) {
155
192
  const fullSl = Sl + (2 * eocHalf / D2R * 1e6) * Math.sin(Mpr)
156
193
  + (1.25 * eocHalf * eocHalf / D2R * 1e6) * Math.sin(2 * Mpr);
157
194
  const lonDeg = Lp / D2R + fullSl * 1e-6 + moonMeeusLpCorrectionDeg;
158
- const distKm = getMoonDistanceKm() * (1 - getEccentricityBase() * Math.cos(Mpr));
195
+ const distKm = fullDistanceKm(Dr, Mr, Mpr, Fr, E, E2);
159
196
  return { thetaAddRad, lonDeg, latRad, latDeg, distKm, T };
160
197
  }
161
198
 
@@ -197,12 +234,15 @@ function createMoonSeries({ constants, fns }) {
197
234
  return sumTable(moonB, Dr, Mr, Mpr, Fr, E, E2) * 1e-6;
198
235
  }
199
236
 
200
- /** Two-term ellipse distance at JD_UT (shared with the scene form).
237
+ /** Full Meeus Ch. 47 distance at JD_UT (shared with the scene form).
201
238
  * @param {number} jdUT @returns {number} km */
202
239
  function truncatedDistanceKm(jdUT) {
203
240
  const jdTT = jdUT + deltaTSeconds(jdUT) / 86400;
204
- const Mpr = argsAt(jdTT).Mp * D2R;
205
- return getMoonDistanceKm() * (1 - getEccentricityBase() * Math.cos(Mpr));
241
+ const T = (jdTT - j2000JD) / julianCenturyDays;
242
+ const args = argsAt(jdTT);
243
+ const Dr = args.D * D2R, Mr = args.M * D2R, Mpr = args.Mp * D2R, Fr = args.F * D2R;
244
+ const E = eFactorAtJdTT(jdTT, T, T * T);
245
+ return fullDistanceKm(Dr, Mr, Mpr, Fr, E, E * E);
206
246
  }
207
247
 
208
248
  return { sceneEvalAt, truncatedLonDeg, truncatedBetaDeg, truncatedDistanceKm, additionalArgs };