@essrt/physics 1.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.
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Published reference curves — the external comparison formulas and
3
+ * datasets the model is plotted against (Phase 8.6). Single-copy
4
+ * extraction from the browser; every curve is EXACTLY its published
5
+ * form, with the source on each. These are comparison references, never
6
+ * model inputs (the §2d goalpost rule: they are hard-coded here, not
7
+ * injectable constants).
8
+ *
9
+ * Sources:
10
+ * - Laskar (2004) analytic tropical-year polynomial.
11
+ * - Peters: linear solar-day lengthening (1.7 ms/cy, +1.8 cy offset).
12
+ * - Chapront: sidereal-year polynomial.
13
+ * - Capitaine et al. (2009): general-precession rate polynomial.
14
+ * - Berger, A.L. (1978), J. Atmos. Sci. 35(12), 2362–2367 — epoch
15
+ * 1950.0; coefficients from the palinsol R package (Crucifix).
16
+ * Table 4 eccentricity (19 terms), Table 1 obliquity (47 terms).
17
+ * - Vondrák, Capitaine & Wallace (2011), A&A 534, A22 — Table 3
18
+ * long-term precession p_A (polynomial + 10 periodic terms).
19
+ * - Laskar et al. (2004), A&A 428, 261–285 — the La2004 N-body
20
+ * solution (IMCCE): [year, eccentricity, obliquity°, perihelion°]
21
+ * at 1000-yr intervals, −250,000..+100,000 from J2000, linearly
22
+ * interpolated.
23
+ * - Stephenson et al. (2016) ΔT cubic-spline segments (the poly is
24
+ * app-loaded JSON data; only the pure evaluator lives here).
25
+ * - Meeus (1998) eq. 22.2 first-order mean obliquity — DELIBERATELY
26
+ * the IAU 1976/1980 convention (23.4392911°, −46.8150″/cy) because
27
+ * the Meeus-comparison reports are checked against Meeus's own
28
+ * worked values; NOT the same quantity as the IAU 2006 obliquity.
29
+ *
30
+ * The four T-based analytic polynomials need the engine's approximate
31
+ * calendar→JD conversion — injected via createPublishedCurves.
32
+ */
33
+
34
+ 'use strict';
35
+
36
+ const _BER78_ARCSEC2RAD = Math.PI / (180 * 3600);
37
+ const _BER78_DEG2RAD = Math.PI / 180;
38
+
39
+ // Berger 1978 Table 4: Eccentricity (19 terms) — e·sin(ω̃), e·cos(ω̃) projections
40
+ const _BER78_ECC = [
41
+ [0.01860798, 4.207205, 28.620089], [0.01627522, 7.346091, 193.788772],
42
+ [-0.01300660, 17.857263, 308.307024], [0.00988829, 17.220546, 320.199637],
43
+ [-0.00336700, 16.846733, 279.376984], [0.00333077, 5.199079, 87.195000],
44
+ [-0.00235400, 18.231076, 349.129677], [0.00140015, 26.216758, 128.443387],
45
+ [0.00100700, 6.359169, 154.143880], [0.00085700, 16.210016, 291.269597],
46
+ [0.00064990, 3.065181, 114.860583], [0.00059900, 16.583829, 332.092251],
47
+ [0.00037800, 18.493980, 296.414411], [-0.00033700, 6.190953, 145.769910],
48
+ [0.00027600, 18.867793, 337.237063], [0.00018200, 17.425567, 152.092288],
49
+ [-0.00017400, 6.186001, 126.839891], [-0.00012400, 18.417441, 210.667199],
50
+ [0.00001250, 0.667863, 72.108838],
51
+ ];
52
+
53
+ /** Berger (1978) eccentricity. @param {number} year @returns {number} */
54
+ function eccBerger1978(year) {
55
+ const t = year - 1950; // years from 1950.0
56
+ let esinP = 0, ecosP = 0;
57
+ for (const [M, rate, phase] of _BER78_ECC) {
58
+ const arg = rate * _BER78_ARCSEC2RAD * t + phase * _BER78_DEG2RAD;
59
+ esinP += M * Math.sin(arg);
60
+ ecosP += M * Math.cos(arg);
61
+ }
62
+ return Math.sqrt(esinP * esinP + ecosP * ecosP);
63
+ }
64
+
65
+ // Berger 1978 Table 1: Obliquity (47 terms) — amplitudes in arcseconds
66
+ const _BER78_OBL_MEAN = 23.320556; // degrees
67
+ const _BER78_OBL = [
68
+ [-2462.2214466, 31.609974, 251.9025], [-857.3232075, 32.620504, 280.8325],
69
+ [-629.3231835, 24.172203, 128.3057], [-414.2804924, 31.983787, 292.7252],
70
+ [-311.7632587, 44.828336, 15.3747], [308.9408604, 30.973257, 263.7951],
71
+ [-162.5533601, 43.668246, 308.4258], [-116.1077911, 32.246691, 240.0099],
72
+ [101.1189923, 30.599444, 222.9725], [-67.6856209, 42.681324, 268.7809],
73
+ [24.9079067, 43.836462, 316.7998], [22.5811241, 47.439436, 319.6024],
74
+ [-21.1648355, 63.219948, 143.8050], [-15.6549876, 64.230478, 172.7351],
75
+ [15.3936813, 1.010530, 28.9300], [14.6660938, 7.437771, 123.5968],
76
+ [-11.7273029, 55.782177, 20.2082], [10.2742696, 0.373813, 40.8226],
77
+ [6.4914588, 13.218362, 123.4722], [5.8539148, 62.583231, 155.6977],
78
+ [-5.4872205, 63.593761, 184.6277], [-5.4290191, 76.438310, 267.2772],
79
+ [5.1609570, 45.815258, 55.0196], [5.0786314, 8.448301, 152.5268],
80
+ [-4.0735782, 56.792707, 49.1382], [3.7227167, 49.747842, 204.6609],
81
+ [3.3971932, 12.058272, 56.5233], [-2.8347004, 75.278220, 200.3284],
82
+ [-2.6550721, 65.241008, 201.6651], [-2.5717867, 64.604291, 213.5577],
83
+ [-2.4712188, 1.647247, 17.0374], [2.4625410, 7.811584, 164.4194],
84
+ [2.2464112, 12.207832, 94.5422], [-2.0755511, 63.856665, 131.9124],
85
+ [-1.9713669, 56.155990, 61.0309], [-1.8813061, 77.448840, 296.2073],
86
+ [-1.8468785, 6.801054, 135.4894], [1.8186742, 62.209418, 114.8750],
87
+ [1.7601888, 20.656133, 247.0691], [-1.5428851, 48.344406, 256.6114],
88
+ [1.4738838, 55.145460, 32.1008], [-1.4593669, 69.000539, 143.6804],
89
+ [1.4192259, 11.071350, 16.8784], [-1.1818980, 74.291298, 160.6835],
90
+ [1.1756474, 11.047742, 27.5932], [-1.1316126, 0.636717, 348.1074],
91
+ [1.0896928, 12.844549, 82.6496],
92
+ ];
93
+
94
+ /** Berger (1978) obliquity, degrees. @param {number} year @returns {number} */
95
+ function obliquityBerger1978(year) {
96
+ const t = year - 1950;
97
+ let eps = _BER78_OBL_MEAN;
98
+ for (const [A, rate, phase] of _BER78_OBL) {
99
+ eps += (A / 3600) * Math.cos(rate * _BER78_ARCSEC2RAD * t + phase * _BER78_DEG2RAD);
100
+ }
101
+ return eps;
102
+ }
103
+
104
+ // Vondrák et al. (2011) Table 3: general precession p_A periodic terms
105
+ const _VONDRAK_PA = [
106
+ // [period_cy, Cp, Sp]
107
+ [409.90, -6908.287473, -2845.175469],
108
+ [396.15, -3198.706291, 449.844989],
109
+ [537.22, 1453.674527, -1255.915323],
110
+ [402.90, -857.748557, 886.736783],
111
+ [417.15, 1173.231614, 418.887514],
112
+ [288.92, -156.981465, 997.912441],
113
+ [4043.00, 371.836550, -240.979710],
114
+ [306.00, -216.619040, 76.541307],
115
+ [277.00, 193.691479, -36.788069],
116
+ [203.00, 11.891524, -170.964086],
117
+ ];
118
+
119
+ /** Vondrák et al. (2011) axial-precession period in years (from dp_A/dT via
120
+ * central difference at h = 0.01 cy). @param {number} year @returns {number} */
121
+ function axialPrecessionVondrak2011(year) {
122
+ const T = (year - 2000) / 100; // Julian centuries from J2000
123
+ // Only the RATE dp_A/dT is used (central difference below); the browser
124
+ // original also accumulated p_A itself at T, dead code lint rejects here.
125
+ const h = 0.01;
126
+ const T1 = T + h, T2 = T - h;
127
+ let pA1 = 8134.017132 + 5043.0520035 * T1 - 0.00710733 * T1 * T1 + 271e-9 * Math.pow(T1, 3);
128
+ let pA2 = 8134.017132 + 5043.0520035 * T2 - 0.00710733 * T2 * T2 + 271e-9 * Math.pow(T2, 3);
129
+ for (const [P, Cp, Sp] of _VONDRAK_PA) {
130
+ pA1 += Cp * Math.cos(2 * Math.PI * T1 / P) + Sp * Math.sin(2 * Math.PI * T1 / P);
131
+ pA2 += Cp * Math.cos(2 * Math.PI * T2 / P) + Sp * Math.sin(2 * Math.PI * T2 / P);
132
+ }
133
+ const rateArcsecPerCy = (pA1 - pA2) / (2 * h);
134
+ if (Math.abs(rateArcsecPerCy) < 1) return NaN;
135
+ return 129600000 / rateArcsecPerCy; // period in years
136
+ }
137
+
138
+ // Laskar et al. (2004) La2004 N-body solution (IMCCE):
139
+ // [year, eccentricity, obliquity_deg, perihelionLong_deg] at 1000-yr
140
+ // intervals, −250,000..+100,000 from J2000.
141
+ const _LA2004 = [[-250000,0.0315165,24.3794,145.31],[-249000,0.0324735,24.3282,160.39],[-248000,0.0334116,24.2517,175.63],[-247000,0.0343575,24.1508,190.85],[-246000,0.0352765,24.0275,206.35],[-245000,0.0362297,23.884,221.93],[-244000,0.0370854,23.7231,237.64],[-243000,0.0379822,23.5486,253.47],[-242000,0.0388937,23.3647,269.5],[-241000,0.0396536,23.1757,285.63],[-240000,0.0405339,22.9871,301.88],[-239000,0.0413137,22.8039,318.29],[-238000,0.0420487,22.6311,334.86],[-237000,0.042808,22.4744,351.49],[-236000,0.043484,22.3385,8.2],[-235000,0.0441832,22.2273,25.14],[-234000,0.0447901,22.1451,41.98],[-233000,0.0453852,22.0942,58.96],[-232000,0.0459956,22.0762,75.98],[-231000,0.0464806,22.0923,92.96],[-230000,0.046954,22.1415,109.96],[-229000,0.0474756,22.2224,126.88],[-228000,0.0477953,22.3327,143.83],[-227000,0.0482131,22.4684,160.66],[-226000,0.0485252,22.6253,177.37],[-225000,0.0488191,22.799,194.11],[-224000,0.0490673,22.9837,210.67],[-223000,0.0492749,23.1744,227.11],[-222000,0.0494882,23.3659,243.57],[-221000,0.0496216,23.553,259.8],[-220000,0.0497211,23.7311,276.04],[-219000,0.0498639,23.8964,292.12],[-218000,0.0498886,24.0447,308.17],[-217000,0.0499196,24.1733,324.16],[-216000,0.049952,24.2798,340.02],[-215000,0.0499272,24.362,355.9],[-214000,0.0498877,24.4188,11.76],[-213000,0.0498077,24.4498,27.45],[-212000,0.0497544,24.4546,43.3],[-211000,0.0496173,24.4339,59.04],[-210000,0.0494747,24.389,74.79],[-209000,0.0493332,24.321,90.58],[-208000,0.0491811,24.2325,106.39],[-207000,0.04892,24.1258,122.23],[-206000,0.048776,24.0035,138.08],[-205000,0.0485283,23.8691,154.03],[-204000,0.0482621,23.7258,169.99],[-203000,0.0480422,23.5768,186.01],[-202000,0.0477501,23.426,202.06],[-201000,0.0474933,23.2765,218.23],[-200000,0.0471698,23.1317,234.36],[-199000,0.0468767,22.9949,250.58],[-198000,0.046592,22.8688,266.9],[-197000,0.0462137,22.7558,283.14],[-196000,0.0458929,22.6582,299.5],[-195000,0.0455855,22.5776,315.82],[-194000,0.0451592,22.5151,332.21],[-193000,0.0448202,22.4717,348.54],[-192000,0.0444461,22.4476,4.88],[-191000,0.0440074,22.4425,21.25],[-190000,0.0436324,22.4562,37.53],[-189000,0.0431482,22.4877,53.76],[-188000,0.0427588,22.5357,70.08],[-187000,0.0422439,22.5989,86.21],[-186000,0.041765,22.6756,102.37],[-185000,0.0413159,22.764,118.5],[-184000,0.0407789,22.8622,134.51],[-183000,0.0402464,22.9682,150.54],[-182000,0.0397952,23.0799,166.37],[-181000,0.0392173,23.1954,182.29],[-180000,0.0387022,23.3123,198.03],[-179000,0.0381743,23.4287,213.63],[-178000,0.0376354,23.5424,229.33],[-177000,0.0370789,23.6513,244.79],[-176000,0.0365263,23.7531,260.15],[-175000,0.0359868,23.846,275.59],[-174000,0.0354318,23.9279,290.72],[-173000,0.0348408,23.9972,305.92],[-172000,0.0343287,24.0524,320.92],[-171000,0.0337606,24.0923,335.92],[-170000,0.0331736,24.1157,350.77],[-169000,0.0326849,24.1222,5.52],[-168000,0.032105,24.1113,20.23],[-167000,0.0316027,24.0828,34.9],[-166000,0.0310757,24.0371,49.35],[-165000,0.0306075,23.9745,63.88],[-164000,0.0301362,23.8959,78.36],[-163000,0.0296773,23.8025,92.62],[-162000,0.0292961,23.696,107.04],[-161000,0.0289342,23.5782,121.36],[-160000,0.0285697,23.4517,135.63],[-159000,0.0283255,23.3192,149.96],[-158000,0.0281089,23.184,164.28],[-157000,0.0278894,23.0495,178.66],[-156000,0.0278304,22.9196,193.04],[-155000,0.0277609,22.7981,207.45],[-154000,0.0277789,22.689,222.05],[-153000,0.0278466,22.5955,236.56],[-152000,0.0280041,22.5214,251.16],[-151000,0.0281962,22.4693,266.04],[-150000,0.0284393,22.441,280.66],[-149000,0.0287575,22.4381,295.63],[-148000,0.0291433,22.4611,310.56],[-147000,0.0295163,22.5092,325.55],[-146000,0.0299744,22.5815,340.61],[-145000,0.0305242,22.676,355.72],[-144000,0.030976,22.7897,10.87],[-143000,0.0315819,22.9198,26.05],[-142000,0.0321592,23.0623,41.19],[-141000,0.0327355,23.2131,56.51],[-140000,0.0333691,23.3681,71.7],[-139000,0.0339709,23.5227,86.95],[-138000,0.0346175,23.6725,102.3],[-137000,0.0352474,23.8134,117.58],[-136000,0.0358264,23.9412,132.88],[-135000,0.036557,24.0522,148.29],[-134000,0.0370818,24.1435,163.64],[-133000,0.0377168,24.2122,179.06],[-132000,0.0383481,24.2565,194.51],[-131000,0.0388824,24.2753,210.02],[-130000,0.0394456,24.2675,225.61],[-129000,0.0399994,24.2336,241.14],[-128000,0.0404894,24.1743,256.94],[-127000,0.0409858,24.091,272.65],[-126000,0.0414099,23.9858,288.48],[-125000,0.0418623,23.8616,304.47],[-124000,0.0422427,23.7213,320.48],[-123000,0.0425465,23.5689,336.59],[-122000,0.042919,23.4086,352.84],[-121000,0.043149,23.2448,9.16],[-120000,0.0433711,23.0822,25.62],[-119000,0.0435785,22.9257,42.11],[-118000,0.0437217,22.7796,58.75],[-117000,0.043813,22.6485,75.51],[-116000,0.0438771,22.5361,92.2],[-115000,0.0439208,22.4458,109.12],[-114000,0.0438843,22.3803,126],[-113000,0.0438391,22.3412,142.88],[-112000,0.0437289,22.3295,159.85],[-111000,0.043647,22.3455,176.76],[-110000,0.0434158,22.388,193.69],[-109000,0.0432494,22.4555,210.53],[-108000,0.0430309,22.5459,227.36],[-107000,0.0427226,22.6559,244.14],[-106000,0.042452,22.7821,260.81],[-105000,0.0421227,22.9209,277.36],[-104000,0.0417688,23.0682,293.99],[-103000,0.0413776,23.2201,310.31],[-102000,0.0409648,23.3727,326.66],[-101000,0.0405626,23.522,342.95],[-100000,0.04006,23.6647,359.07],[-99000,0.0395846,23.7976,15.17],[-98000,0.0391425,23.9176,31.2],[-97000,0.0385531,24.0227,47.13],[-96000,0.0380871,24.1109,63.05],[-95000,0.0375395,24.1805,78.79],[-94000,0.0369975,24.2308,94.65],[-93000,0.0364478,24.2612,110.35],[-92000,0.0358754,24.2714,125.97],[-91000,0.0353196,24.2617,141.71],[-90000,0.0347434,24.2329,157.27],[-89000,0.0341063,24.1855,172.89],[-88000,0.0335975,24.1209,188.49],[-87000,0.0329379,24.0405,204.11],[-86000,0.0323467,23.9458,219.69],[-85000,0.0317999,23.839,235.28],[-84000,0.0311582,23.7219,250.92],[-83000,0.0305811,23.5968,266.59],[-82000,0.0299867,23.4662,282.11],[-81000,0.0294041,23.3325,297.89],[-80000,0.0288047,23.198,313.58],[-79000,0.0282095,23.0655,329.22],[-78000,0.027646,22.9373,345.08],[-77000,0.0270783,22.8158,0.82],[-76000,0.02647,22.7035,16.67],[-75000,0.0259517,22.6028,32.47],[-74000,0.0254081,22.5157,48.38],[-73000,0.0248079,22.4445,64.28],[-72000,0.0243451,22.3907,80.09],[-71000,0.0237902,22.356,96.01],[-70000,0.0232796,22.3415,111.94],[-69000,0.0227768,22.348,127.66],[-68000,0.0222938,22.3755,143.5],[-67000,0.0217932,22.4241,159.32],[-66000,0.0213111,22.4929,174.82],[-65000,0.0208223,22.5806,190.53],[-64000,0.0203886,22.6857,205.93],[-63000,0.0198634,22.8062,221.33],[-62000,0.0194167,22.9394,236.54],[-61000,0.0189825,23.0826,251.63],[-60000,0.0184692,23.2329,266.65],[-59000,0.0180438,23.3869,281.46],[-58000,0.0176055,23.5413,296.01],[-57000,0.017156,23.693,310.79],[-56000,0.0167394,23.8382,324.93],[-55000,0.0163304,23.9741,339.16],[-54000,0.0159614,24.0975,353.36],[-53000,0.0155713,24.2052,7.12],[-52000,0.0152168,24.295,20.85],[-51000,0.0149581,24.3644,34.65],[-50000,0.0146002,24.4113,47.95],[-49000,0.0143854,24.4346,61.51],[-48000,0.0141818,24.4332,74.72],[-47000,0.0139868,24.4064,88.08],[-46000,0.0138427,24.3549,101.34],[-45000,0.0137927,24.279,114.34],[-44000,0.0137057,24.1803,127.89],[-43000,0.0137194,24.0608,141.04],[-42000,0.0137352,23.9228,154.35],[-41000,0.0138241,23.7693,168.05],[-40000,0.0139293,23.6042,181.64],[-39000,0.0140394,23.4307,195.37],[-38000,0.0142706,23.2534,209.58],[-37000,0.0144241,23.0769,223.72],[-36000,0.0146517,22.9055,238.2],[-35000,0.0149423,22.7439,252.89],[-34000,0.015175,22.5969,267.79],[-33000,0.0154495,22.4684,283.02],[-32000,0.0157785,22.3624,298.13],[-31000,0.0160455,22.2822,313.83],[-30000,0.0163655,22.2299,329.52],[-29000,0.0166518,22.2072,345.26],[-28000,0.0169668,22.2148,1.3],[-27000,0.017279,22.2518,17.51],[-26000,0.0175068,22.3173,33.5],[-25000,0.0178681,22.4089,49.92],[-24000,0.0180946,22.523,66.2],[-23000,0.0183406,22.6566,82.54],[-22000,0.0186174,22.8053,98.89],[-21000,0.0188354,22.9641,115.23],[-20000,0.0190027,23.129,131.69],[-19000,0.0192231,23.2951,147.9],[-18000,0.0193471,23.4577,164.3],[-17000,0.019487,23.6129,180.71],[-16000,0.0195602,23.7568,196.91],[-15000,0.0196349,23.8859,213.3],[-14000,0.0196791,23.9975,229.67],[-13000,0.0196506,24.0892,245.93],[-12000,0.0196128,24.1592,262.38],[-11000,0.0195974,24.2067,278.66],[-10000,0.0194246,24.2307,295.27],[-9000,0.0193255,24.2314,311.59],[-8000,0.0191801,24.2096,328.15],[-7000,0.0189374,24.166,344.84],[-6000,0.0187484,24.1024,1.41],[-5000,0.0184535,24.0211,18.08],[-4000,0.0182109,23.9241,35.03],[-3000,0.0178458,23.8144,51.77],[-2000,0.0174968,23.6949,68.76],[-1000,0.0171614,23.5688,85.82],[0,0.0167024,23.4393,102.92],[1000,0.0162752,23.3099,120.24],[2000,0.0158501,23.1837,137.38],[3000,0.0153397,23.0639,155],[4000,0.0148355,22.9537,172.44],[5000,0.0143165,22.8553,189.84],[6000,0.0137745,22.7714,207.76],[7000,0.0132086,22.7037,225.38],[8000,0.0125777,22.6534,243.17],[9000,0.0120396,22.6214,261.14],[10000,0.0113842,22.6081,279.19],[11000,0.0107392,22.613,297.22],[12000,0.0101622,22.6358,315.51],[13000,0.0094766,22.6752,333.75],[14000,0.0088687,22.7299,352.45],[15000,0.0082117,22.7981,10.7],[16000,0.0076037,22.8779,29.64],[17000,0.0069711,22.9669,48.77],[18000,0.0063187,23.063,67.63],[19000,0.0057218,23.1637,87.47],[20000,0.0051508,23.2665,107.44],[21000,0.0044876,23.3691,128.61],[22000,0.0039799,23.4692,150.09],[23000,0.0034542,23.5645,173.52],[24000,0.0029897,23.6532,199.81],[25000,0.0026293,23.7335,227.07],[26000,0.0024025,23.8038,257.55],[27000,0.0024027,23.8628,290.12],[28000,0.0024327,23.9093,321.06],[29000,0.0027336,23.9423,350.12],[30000,0.0030507,23.9611,15.72],[31000,0.0034656,23.9654,40.22],[32000,0.0038979,23.9547,62.27],[33000,0.0043068,23.9292,82.51],[34000,0.0048111,23.8891,102.79],[35000,0.0052274,23.835,121.54],[36000,0.0056449,23.7678,140.49],[37000,0.0061018,23.6887,158.19],[38000,0.0065058,23.599,176.39],[39000,0.0068818,23.5005,194.27],[40000,0.0072957,23.3951,211.25],[41000,0.0076519,23.2851,229.03],[42000,0.0080371,23.1731,246.2],[43000,0.0083584,23.0615,263.2],[44000,0.0087128,22.953,280.34],[45000,0.0090746,22.8507,297.3],[46000,0.0093273,22.7572,314.23],[47000,0.0096936,22.6751,330.98],[48000,0.0099767,22.6072,347.7],[49000,0.0102492,22.5555,4.59],[50000,0.010534,22.522,21.08],[51000,0.0108041,22.5083,37.52],[52000,0.0110625,22.5153,54.49],[53000,0.0112886,22.5434,70.53],[54000,0.0115398,22.5926,87.08],[55000,0.0117922,22.6617,103.53],[56000,0.0119791,22.7496,119.72],[57000,0.0122013,22.8541,135.91],[58000,0.0124675,22.9727,152.1],[59000,0.0126032,23.1024,168.15],[60000,0.0128592,23.2402,184.18],[61000,0.0130467,23.3822,199.93],[62000,0.013238,23.5251,215.96],[63000,0.0134382,23.6653,231.64],[64000,0.0136253,23.7993,247.19],[65000,0.0138244,23.9236,263.02],[66000,0.014012,24.0353,278.45],[67000,0.0141457,24.1312,293.92],[68000,0.0144056,24.2091,309.41],[69000,0.0145104,24.2668,324.82],[70000,0.0146758,24.3024,340.18],[71000,0.0148766,24.3151,355.49],[72000,0.0149859,24.3043,10.86],[73000,0.015121,24.2695,26.31],[74000,0.015268,24.2118,41.4],[75000,0.0153626,24.1321,57.05],[76000,0.0154713,24.0317,72.35],[77000,0.0155409,23.9132,87.77],[78000,0.015628,23.7789,103.37],[79000,0.0157098,23.632,118.94],[80000,0.0156889,23.4759,134.57],[81000,0.0158005,23.3143,150.25],[82000,0.0157903,23.1512,166.06],[83000,0.0157705,22.991,181.91],[84000,0.0158109,22.8375,197.72],[85000,0.0157672,22.695,213.64],[86000,0.0157318,22.5675,229.75],[87000,0.0156819,22.4583,245.49],[88000,0.0156346,22.3704,261.59],[89000,0.0155539,22.3066,277.6],[90000,0.0154636,22.2682,293.34],[91000,0.0153624,22.2563,309.34],[92000,0.0153008,22.2713,325.02],[93000,0.0151278,22.3122,340.72],[94000,0.0150463,22.3777,356.19],[95000,0.0149407,22.4658,11.55],[96000,0.014781,22.5736,26.81],[97000,0.014683,22.6979,41.81],[98000,0.0145718,22.8354,56.46],[99000,0.0144578,22.982,71.43],[100000,0.0143543,23.1341,85.57]];
142
+
143
+ /** Interpolate La2004. col: 1=eccentricity, 2=obliquity, 3=perihelionLong.
144
+ * @param {number} year - years from J2000 @param {number} col @returns {number} */
145
+ function _la2004Interp(year, col) {
146
+ if (year < _LA2004[0][0] || year > _LA2004[_LA2004.length - 1][0]) return NaN;
147
+ const idx = (year - _LA2004[0][0]) / 1000;
148
+ const i = Math.floor(idx);
149
+ if (i >= _LA2004.length - 1) return _LA2004[_LA2004.length - 1][col];
150
+ const frac = idx - i;
151
+ return _LA2004[i][col] * (1 - frac) + _LA2004[i + 1][col] * frac;
152
+ }
153
+
154
+ /** La2004 eccentricity at calendar year. @param {number} year @returns {number} */
155
+ function eccLa2004(year) { return _la2004Interp(year - 2000, 1); }
156
+ /** La2004 obliquity (degrees) at calendar year. @param {number} year @returns {number} */
157
+ function obliquityLa2004(year) { return _la2004Interp(year - 2000, 2); }
158
+ /** La2004 perihelion longitude (degrees) at calendar year — WRAP-AWARE:
159
+ * the longitude wraps at 360°/0°, so adjacent samples are unwrapped before
160
+ * interpolating. @param {number} year @returns {number} */
161
+ function perihelionLa2004(year) {
162
+ const yearFromJ2000 = year - 2000;
163
+ if (yearFromJ2000 < _LA2004[0][0] || yearFromJ2000 > _LA2004[_LA2004.length - 1][0]) return NaN;
164
+ const idx = (yearFromJ2000 - _LA2004[0][0]) / 1000;
165
+ const i = Math.floor(idx);
166
+ if (i >= _LA2004.length - 1) return _LA2004[_LA2004.length - 1][3];
167
+ const frac = idx - i;
168
+ let v0 = _LA2004[i][3], v1 = _LA2004[i + 1][3];
169
+ // Unwrap: if values jump by more than 180°, adjust
170
+ if (v1 - v0 > 180) v1 -= 360;
171
+ else if (v0 - v1 > 180) v1 += 360;
172
+ return ((v0 * (1 - frac) + v1 * frac) % 360 + 360) % 360;
173
+ }
174
+
175
+ /**
176
+ * Stephenson et al. (2016) ΔT cubic-spline evaluator. The poly (segments
177
+ * with [y0, y1, a0..a3]) is app-loaded JSON data — only the pure evaluator
178
+ * lives here. Returns null outside the poly's window.
179
+ * @param {number} year
180
+ * @param {{ segments: Array<{y0: number, y1: number, a: number[]}> }} poly
181
+ * @returns {number|null}
182
+ */
183
+ function stephensonDeltaT(year, poly) {
184
+ for (const seg of poly.segments) {
185
+ if (seg.y0 <= year && year <= seg.y1) {
186
+ const t = (year - seg.y0) / (seg.y1 - seg.y0);
187
+ const [a0, a1, a2, a3] = seg.a;
188
+ return a0 + a1*t + a2*t*t + a3*t*t*t;
189
+ }
190
+ }
191
+ return null;
192
+ }
193
+
194
+ /** Meeus (1998) eq. 22.2 first-order mean obliquity, RADIANS — the IAU
195
+ * 1976/1980 convention, deliberately (see the module header).
196
+ * @param {number} T - Julian centuries TT from J2000 @returns {number} */
197
+ function meeusMeanObliquityRad(T) {
198
+ return (23.4392911 - 0.0130042 * T) * (Math.PI / 180);
199
+ }
200
+
201
+ /**
202
+ * The four T-based analytic polynomials, bound to the engine's approximate
203
+ * calendar→JD conversion.
204
+ * @param {{ yearToJDApprox: (year: number) => number, j2000JD: number,
205
+ * julianCenturyDays: number }} deps
206
+ */
207
+ function createPublishedCurves(deps) {
208
+ /** Laskar (2004) tropical year, days. @param {number} year @returns {number} */
209
+ function tropicalYearLaskar(year) {
210
+ const T = (deps.yearToJDApprox(year) - deps.j2000JD) / deps.julianCenturyDays;
211
+ return 365.2421896698 - 0.00000615359 * T
212
+ - 0.000000000729 * T * T + 0.000000000264 * Math.pow(T, 3);
213
+ }
214
+
215
+ /** Peters linear solar-day lengthening, SI seconds. @param {number} year @returns {number} */
216
+ function solarDayPeters(year) {
217
+ const T = (deps.yearToJDApprox(year) - deps.j2000JD) / deps.julianCenturyDays;
218
+ return 86400 + (T + 1.8) * 0.0017;
219
+ }
220
+
221
+ /** Chapront sidereal year, days. @param {number} year @returns {number} */
222
+ function siderealYearChapront(year) {
223
+ const T = (deps.yearToJDApprox(year) - deps.j2000JD) / deps.julianCenturyDays;
224
+ return 365.256362953 + 0.0000001139 * T
225
+ - 0.000000000076 * T * T - 0.00000000000169 * Math.pow(T, 3);
226
+ }
227
+
228
+ /** Capitaine et al. (2009) axial-precession period, years. @param {number} year @returns {number} */
229
+ function axialPrecessionCapitaine2009(year) {
230
+ const T = (deps.yearToJDApprox(year) - deps.j2000JD) / deps.julianCenturyDays;
231
+ const rateArcsecPerCy = 5028.796195 + 2.2108696 * T
232
+ + 0.00023892 * T * T - 0.000095428 * Math.pow(T, 3)
233
+ - 0.0000001915 * Math.pow(T, 4);
234
+ if (Math.abs(rateArcsecPerCy) < 1) return NaN;
235
+ // rate is arcsec/century; full circle = 1,296,000 arcsec
236
+ return 129600000 / rateArcsecPerCy;
237
+ }
238
+
239
+ return { tropicalYearLaskar, solarDayPeters, siderealYearChapront, axialPrecessionCapitaine2009 };
240
+ }
241
+
242
+ module.exports = {
243
+ createPublishedCurves,
244
+ eccBerger1978, obliquityBerger1978, axialPrecessionVondrak2011,
245
+ eccLa2004, obliquityLa2004, perihelionLa2004,
246
+ stephensonDeltaT, meeusMeanObliquityRad,
247
+ };
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Sun longitude correction — the fitted H-lattice harmonic stack
3
+ * (Phase 9, S-P8). THE shared evaluator behind what were THREE verbatim
4
+ * copies: the browser's sunLongitudeCorrection and two inline blocks in
5
+ * tools/lib/scene-graph (moveModel and the fast animator).
6
+ *
7
+ * The ~200″→~7″ RMS Sun-only correction (Phase Z-B): a fitted harmonic
8
+ * table on H-lattice divisors, with the H-LATTICE FILTER applied at
9
+ * evaluation — a divisor is structurally on-lattice iff EITHER
10
+ * (a) it is a year-multiple of H (integer year period), OR
11
+ * (b) it is a small precession divisor (1..20 — Earth's Fibonacci
12
+ * named cycles H/3, H/5, H/8, H/13, H/16, …), OR
13
+ * (c) it is one of the two lunar precession divisors (auto-tracked
14
+ * from the Meeus anchors).
15
+ * Divisors failing all three are design-rule violating and silently
16
+ * skipped. (Clause (d) "sharesFactorWithH" was removed 2026-07-15 — it
17
+ * admitted mid-range fit artifacts, divisors 84/92/115/122, that are
18
+ * not physically motivated. See tools/fit/sun-longitude-harmonics.js.)
19
+ *
20
+ * MATCHED PAIR: every dep is the J2000-FIXED value — the table was
21
+ * fitted against J2000 H and mSY (the Node engines' documented
22
+ * convention). The browser copy historically read the MUTABLE
23
+ * holisticyearLength/balancedYear globals; measured divergence under
24
+ * epoch shift was 8.2e-10 deg (~3 µas) — aligned onto the fitted
25
+ * convention at extraction (9-1), zero fixture drift.
26
+ *
27
+ * The engines own the application (θ −= corr·d2r on the Sun node only)
28
+ * and their enable flags.
29
+ */
30
+
31
+ 'use strict';
32
+
33
+ /**
34
+ * @typedef {Object} SunLongitudeCorrectionDeps
35
+ * @property {number} hYears - H, J2000-fixed (the fitted axis)
36
+ * @property {number} balancedYear - J2000-fixed
37
+ * @property {number} j2000JD
38
+ * @property {number} meanDeg - the fitted mean offset
39
+ * @property {Array<[number, number, number]>} harmonics - [divisor, sinC, cosC]
40
+ * @property {number} nNodalJ2000 - lunar nodal divisor (per 8H convention)
41
+ * @property {number} nApsidalJ2000 - lunar apsidal divisor
42
+ */
43
+
44
+ /** @param {SunLongitudeCorrectionDeps} deps */
45
+ function createSunLongitudeCorrection(deps) {
46
+ /** Correction in DEGREES at JD (subtract from the raw Sun longitude).
47
+ * @param {number} jd @returns {number} */
48
+ function correctionDegAt(jd) {
49
+ const year = 2000 + (jd - deps.j2000JD) / 365.25;
50
+ const t = year - deps.balancedYear;
51
+ let corr = deps.meanDeg;
52
+ const H_round = Math.round(deps.hYears);
53
+ for (const h of deps.harmonics) {
54
+ const divisor = h[0];
55
+ const isYearMultiple = divisor >= H_round && divisor % H_round === 0;
56
+ const isPrecessionDivisor = divisor > 0 && divisor <= 20;
57
+ const isLunarPrecession = divisor === deps.nNodalJ2000 || divisor === deps.nApsidalJ2000;
58
+ if (!isYearMultiple && !isPrecessionDivisor && !isLunarPrecession) continue;
59
+ const phase = 2 * Math.PI * t / (deps.hYears / divisor);
60
+ corr += h[1] * Math.sin(phase) + h[2] * Math.cos(phase);
61
+ }
62
+ return corr;
63
+ }
64
+
65
+ return { correctionDegAt };
66
+ }
67
+
68
+ module.exports = { createSunLongitudeCorrection };