@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 +1 -1
- package/src/constants/coefficients.js +2 -1
- package/src/constants/generated.d.ts +18 -2
- package/src/constants/generated.js +18 -2
- package/src/eclipse/besselian.cjs +278 -0
- package/src/model.js +40 -0
- package/src/moon/series.cjs +45 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@essrt/physics",
|
|
3
|
-
"version": "1.
|
|
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 = "
|
|
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: "
|
|
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: "
|
|
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 = "
|
|
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: "
|
|
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,
|
package/src/moon/series.cjs
CHANGED
|
@@ -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 =
|
|
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
|
-
/**
|
|
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
|
|
205
|
-
|
|
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 };
|