@typescriptify/sweph 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.
Files changed (211) hide show
  1. package/README.md +422 -0
  2. package/ephe/semo_18.se1 +0 -0
  3. package/ephe/sepl_18.se1 +0 -0
  4. package/originalCode/.eslintrc.json +124 -0
  5. package/originalCode/.gitattributes +2 -0
  6. package/originalCode/.github/FUNDING.yml +5 -0
  7. package/originalCode/.github/workflows/test.yml +35 -0
  8. package/originalCode/LICENSE +840 -0
  9. package/originalCode/README.md +91 -0
  10. package/originalCode/binding.gyp +41 -0
  11. package/originalCode/constants.js +366 -0
  12. package/originalCode/docs.gif +0 -0
  13. package/originalCode/index.d.ts +5115 -0
  14. package/originalCode/index.js +7 -0
  15. package/originalCode/index.mjs +109 -0
  16. package/originalCode/package.json +55 -0
  17. package/originalCode/src/functions/azalt.cpp +39 -0
  18. package/originalCode/src/functions/azalt_rev.cpp +35 -0
  19. package/originalCode/src/functions/calc.cpp +29 -0
  20. package/originalCode/src/functions/calc_pctr.cpp +31 -0
  21. package/originalCode/src/functions/calc_ut.cpp +29 -0
  22. package/originalCode/src/functions/close.cpp +6 -0
  23. package/originalCode/src/functions/cotrans.cpp +26 -0
  24. package/originalCode/src/functions/cotrans_sp.cpp +26 -0
  25. package/originalCode/src/functions/cs2degstr.cpp +19 -0
  26. package/originalCode/src/functions/cs2lonlatstr.cpp +23 -0
  27. package/originalCode/src/functions/cs2timestr.cpp +23 -0
  28. package/originalCode/src/functions/csnorm.cpp +15 -0
  29. package/originalCode/src/functions/csroundsec.cpp +15 -0
  30. package/originalCode/src/functions/d2l.cpp +15 -0
  31. package/originalCode/src/functions/date_conversion.cpp +30 -0
  32. package/originalCode/src/functions/day_of_week.cpp +15 -0
  33. package/originalCode/src/functions/degnorm.cpp +15 -0
  34. package/originalCode/src/functions/deltat.cpp +15 -0
  35. package/originalCode/src/functions/deltat_ex.cpp +24 -0
  36. package/originalCode/src/functions/difcs2n.cpp +19 -0
  37. package/originalCode/src/functions/difcsn.cpp +19 -0
  38. package/originalCode/src/functions/difdeg2n.cpp +19 -0
  39. package/originalCode/src/functions/difdegn.cpp +19 -0
  40. package/originalCode/src/functions/fixstar.cpp +32 -0
  41. package/originalCode/src/functions/fixstar2.cpp +32 -0
  42. package/originalCode/src/functions/fixstar2_mag.cpp +28 -0
  43. package/originalCode/src/functions/fixstar2_ut.cpp +32 -0
  44. package/originalCode/src/functions/fixstar_mag.cpp +28 -0
  45. package/originalCode/src/functions/fixstar_ut.cpp +32 -0
  46. package/originalCode/src/functions/gauquelin_sector.cpp +44 -0
  47. package/originalCode/src/functions/get_ayanamsa.cpp +15 -0
  48. package/originalCode/src/functions/get_ayanamsa_ex.cpp +27 -0
  49. package/originalCode/src/functions/get_ayanamsa_ex_ut.cpp +27 -0
  50. package/originalCode/src/functions/get_ayanamsa_name.cpp +19 -0
  51. package/originalCode/src/functions/get_ayanamsa_ut.cpp +15 -0
  52. package/originalCode/src/functions/get_current_file_data.cpp +28 -0
  53. package/originalCode/src/functions/get_library_path.cpp +8 -0
  54. package/originalCode/src/functions/get_orbital_elements.cpp +29 -0
  55. package/originalCode/src/functions/get_planet_name.cpp +19 -0
  56. package/originalCode/src/functions/get_tid_acc.cpp +7 -0
  57. package/originalCode/src/functions/heliacal_pheno_ut.cpp +52 -0
  58. package/originalCode/src/functions/heliacal_ut.cpp +52 -0
  59. package/originalCode/src/functions/helio_cross.cpp +33 -0
  60. package/originalCode/src/functions/helio_cross_ut.cpp +33 -0
  61. package/originalCode/src/functions/house_name.cpp +20 -0
  62. package/originalCode/src/functions/house_pos.cpp +36 -0
  63. package/originalCode/src/functions/houses.cpp +35 -0
  64. package/originalCode/src/functions/houses_armc.cpp +38 -0
  65. package/originalCode/src/functions/houses_armc_ex2.cpp +47 -0
  66. package/originalCode/src/functions/houses_ex.cpp +37 -0
  67. package/originalCode/src/functions/houses_ex2.cpp +46 -0
  68. package/originalCode/src/functions/jdet_to_utc.cpp +38 -0
  69. package/originalCode/src/functions/jdut1_to_utc.cpp +38 -0
  70. package/originalCode/src/functions/julday.cpp +25 -0
  71. package/originalCode/src/functions/lat_to_lmt.cpp +27 -0
  72. package/originalCode/src/functions/lmt_to_lat.cpp +27 -0
  73. package/originalCode/src/functions/lun_eclipse_how.cpp +34 -0
  74. package/originalCode/src/functions/lun_eclipse_when.cpp +31 -0
  75. package/originalCode/src/functions/lun_eclipse_when_loc.cpp +39 -0
  76. package/originalCode/src/functions/lun_occult_when_glob.cpp +35 -0
  77. package/originalCode/src/functions/lun_occult_when_loc.cpp +43 -0
  78. package/originalCode/src/functions/lun_occult_where.cpp +34 -0
  79. package/originalCode/src/functions/mooncross.cpp +26 -0
  80. package/originalCode/src/functions/mooncross_node.cpp +30 -0
  81. package/originalCode/src/functions/mooncross_node_ut.cpp +30 -0
  82. package/originalCode/src/functions/mooncross_ut.cpp +26 -0
  83. package/originalCode/src/functions/nod_aps.cpp +42 -0
  84. package/originalCode/src/functions/nod_aps_ut.cpp +42 -0
  85. package/originalCode/src/functions/orbit_max_min_true_distance.cpp +37 -0
  86. package/originalCode/src/functions/pheno.cpp +29 -0
  87. package/originalCode/src/functions/pheno_ut.cpp +29 -0
  88. package/originalCode/src/functions/radnorm.cpp +15 -0
  89. package/originalCode/src/functions/refrac.cpp +23 -0
  90. package/originalCode/src/functions/refrac_extended.cpp +32 -0
  91. package/originalCode/src/functions/revjul.cpp +33 -0
  92. package/originalCode/src/functions/rise_trans.cpp +44 -0
  93. package/originalCode/src/functions/rise_trans_true_hor.cpp +46 -0
  94. package/originalCode/src/functions/set_delta_t_userdef.cpp +14 -0
  95. package/originalCode/src/functions/set_ephe_path.cpp +14 -0
  96. package/originalCode/src/functions/set_jpl_file.cpp +14 -0
  97. package/originalCode/src/functions/set_sid_mode.cpp +20 -0
  98. package/originalCode/src/functions/set_tid_acc.cpp +14 -0
  99. package/originalCode/src/functions/set_topo.cpp +20 -0
  100. package/originalCode/src/functions/sidtime.cpp +15 -0
  101. package/originalCode/src/functions/sidtime0.cpp +21 -0
  102. package/originalCode/src/functions/sol_eclipse_how.cpp +34 -0
  103. package/originalCode/src/functions/sol_eclipse_when_glob.cpp +31 -0
  104. package/originalCode/src/functions/sol_eclipse_when_loc.cpp +39 -0
  105. package/originalCode/src/functions/sol_eclipse_where.cpp +30 -0
  106. package/originalCode/src/functions/solcross.cpp +26 -0
  107. package/originalCode/src/functions/solcross_ut.cpp +26 -0
  108. package/originalCode/src/functions/split_deg.cpp +35 -0
  109. package/originalCode/src/functions/time_equ.cpp +25 -0
  110. package/originalCode/src/functions/utc_time_zone.cpp +48 -0
  111. package/originalCode/src/functions/utc_to_jd.cpp +37 -0
  112. package/originalCode/src/functions/version.cpp +8 -0
  113. package/originalCode/src/functions/vis_limit_mag.cpp +50 -0
  114. package/originalCode/src/sweph.cpp +150 -0
  115. package/originalCode/src/sweph.h +119 -0
  116. package/originalCode/swisseph/swecl.c +6428 -0
  117. package/originalCode/swisseph/swedate.c +588 -0
  118. package/originalCode/swisseph/swedate.h +81 -0
  119. package/originalCode/swisseph/swehel.c +3511 -0
  120. package/originalCode/swisseph/swehouse.c +3143 -0
  121. package/originalCode/swisseph/swehouse.h +98 -0
  122. package/originalCode/swisseph/swejpl.c +958 -0
  123. package/originalCode/swisseph/swejpl.h +103 -0
  124. package/originalCode/swisseph/swemmoon.c +1930 -0
  125. package/originalCode/swisseph/swemplan.c +967 -0
  126. package/originalCode/swisseph/swemptab.h +10640 -0
  127. package/originalCode/swisseph/swenut2000a.h +2819 -0
  128. package/originalCode/swisseph/sweodef.h +326 -0
  129. package/originalCode/swisseph/sweph.c +8614 -0
  130. package/originalCode/swisseph/sweph.h +849 -0
  131. package/originalCode/swisseph/swephexp.h +1020 -0
  132. package/originalCode/swisseph/swephlib.c +4634 -0
  133. package/originalCode/swisseph/swephlib.h +189 -0
  134. package/package.json +28 -0
  135. package/scripts/gen-swemptab.js +177 -0
  136. package/scripts/gen-swenut2000a.js +106 -0
  137. package/src/SwissEph/README.md +268 -0
  138. package/src/SwissEph/UseCases/Ayanamsa.md +363 -0
  139. package/src/SwissEph/UseCases/AzimuthAltitude.md +408 -0
  140. package/src/SwissEph/UseCases/CoordinateSystems.md +337 -0
  141. package/src/SwissEph/UseCases/DateAndTime.md +368 -0
  142. package/src/SwissEph/UseCases/DeltaT.md +258 -0
  143. package/src/SwissEph/UseCases/EphemerisFiles.md +338 -0
  144. package/src/SwissEph/UseCases/FixedStars.md +300 -0
  145. package/src/SwissEph/UseCases/GauquelinSectors.md +304 -0
  146. package/src/SwissEph/UseCases/HeliacalEvents.md +396 -0
  147. package/src/SwissEph/UseCases/HelioCrossings.md +325 -0
  148. package/src/SwissEph/UseCases/HousePosition.md +254 -0
  149. package/src/SwissEph/UseCases/HouseSystems.md +279 -0
  150. package/src/SwissEph/UseCases/LunarEclipse.md +326 -0
  151. package/src/SwissEph/UseCases/MeridianTransit.md +279 -0
  152. package/src/SwissEph/UseCases/MoonCrossings.md +373 -0
  153. package/src/SwissEph/UseCases/NodesAndApsides.md +307 -0
  154. package/src/SwissEph/UseCases/Occultation.md +352 -0
  155. package/src/SwissEph/UseCases/OrbitalElements.md +469 -0
  156. package/src/SwissEph/UseCases/Phenomena.md +328 -0
  157. package/src/SwissEph/UseCases/PlanetPositions.md +366 -0
  158. package/src/SwissEph/UseCases/Planetocentric.md +278 -0
  159. package/src/SwissEph/UseCases/Refraction.md +314 -0
  160. package/src/SwissEph/UseCases/RiseAndSet.md +433 -0
  161. package/src/SwissEph/UseCases/SiderealTime.md +302 -0
  162. package/src/SwissEph/UseCases/SolarEclipse.md +379 -0
  163. package/src/SwissEph/UseCases/SunCrossings.md +275 -0
  164. package/src/SwissEph/UseCases/TopocentricCorrection.md +335 -0
  165. package/src/SwissEph/errors.ts +10 -0
  166. package/src/SwissEph/index.ts +823 -0
  167. package/src/SwissEph/types.ts +291 -0
  168. package/src/constants.ts +762 -0
  169. package/src/file-reader.ts +147 -0
  170. package/src/index.ts +10 -0
  171. package/src/swecl.ts +4526 -0
  172. package/src/swedate.ts +376 -0
  173. package/src/swehel.ts +1939 -0
  174. package/src/swehouse.ts +2167 -0
  175. package/src/swejpl.ts +470 -0
  176. package/src/swemmoon.ts +1318 -0
  177. package/src/swemplan.ts +585 -0
  178. package/src/swemptab.ts +4448 -0
  179. package/src/swenut2000a.ts +2763 -0
  180. package/src/sweph.ts +3993 -0
  181. package/src/swephlib.ts +2720 -0
  182. package/src/types.ts +490 -0
  183. package/tests/c-style/ayanamsa.test.ts +63 -0
  184. package/tests/c-style/config.test.ts +96 -0
  185. package/tests/c-style/crossings.test.ts +81 -0
  186. package/tests/c-style/date-time.test.ts +114 -0
  187. package/tests/c-style/eclipses.test.ts +84 -0
  188. package/tests/c-style/fixed-stars.test.ts +66 -0
  189. package/tests/c-style/heliacal.test.ts +34 -0
  190. package/tests/c-style/houses.test.ts +135 -0
  191. package/tests/c-style/math-utils.test.ts +160 -0
  192. package/tests/c-style/orbital.test.ts +78 -0
  193. package/tests/c-style/phenomena.test.ts +42 -0
  194. package/tests/c-style/planetocentric.test.ts +26 -0
  195. package/tests/c-style/planets.test.ts +117 -0
  196. package/tests/c-style/rise-set.test.ts +71 -0
  197. package/tests/helpers.ts +21 -0
  198. package/tests/modern/ayanamsa.test.ts +47 -0
  199. package/tests/modern/calc.test.ts +113 -0
  200. package/tests/modern/config.test.ts +46 -0
  201. package/tests/modern/crossings.test.ts +45 -0
  202. package/tests/modern/eclipses.test.ts +81 -0
  203. package/tests/modern/errors.test.ts +71 -0
  204. package/tests/modern/heliacal.test.ts +30 -0
  205. package/tests/modern/houses.test.ts +87 -0
  206. package/tests/modern/orbital.test.ts +79 -0
  207. package/tests/modern/phenomena.test.ts +41 -0
  208. package/tests/modern/rise-set.test.ts +60 -0
  209. package/tests/modern/statics.test.ts +99 -0
  210. package/tests/modern/utilities.test.ts +70 -0
  211. package/tsconfig.json +20 -0
package/src/swecl.ts ADDED
@@ -0,0 +1,4526 @@
1
+ /*************************************************************
2
+ * Swiss Ephemeris eclipse / rise-set / azalt / Gauquelin
3
+ * Translated from swecl.c
4
+ *
5
+ * Copyright (C) 1997 - 2021 Astrodienst AG, Switzerland.
6
+ * All rights reserved. (AGPL)
7
+ *************************************************************/
8
+
9
+ import {
10
+ DEGTORAD, RADTODEG, PI, OK, ERR, AUNIT, EARTH_RADIUS, EARTH_OBLATENESS,
11
+ SE_ECL_NUT, SE_SUN, SE_MOON, SE_EARTH, SE_MERCURY, SE_VENUS, SE_MARS, SE_PLUTO, SE_NEPTUNE,
12
+ SE_MEAN_NODE, SE_TRUE_NODE, SE_MEAN_APOG, SE_OSCU_APOG, SE_INTP_APOG, SE_INTP_PERG,
13
+ SE_CHIRON, SE_CERES, SE_VESTA, SE_AST_OFFSET,
14
+ SE_NPLANETS, SE_FICT_OFFSET,
15
+ SE_CALC_RISE, SE_CALC_SET, SE_CALC_MTRANSIT, SE_CALC_ITRANSIT,
16
+ SE_BIT_DISC_CENTER, SE_BIT_DISC_BOTTOM, SE_BIT_NO_REFRACTION,
17
+ SE_BIT_GEOCTR_NO_ECL_LAT, SE_BIT_FIXED_DISC_SIZE,
18
+ SE_BIT_FORCE_SLOW_METHOD,
19
+ SE_BIT_CIVIL_TWILIGHT, SE_BIT_NAUTIC_TWILIGHT, SE_BIT_ASTRO_TWILIGHT,
20
+ SE_ECL2HOR, SE_EQU2HOR, SE_HOR2EQU,
21
+ SE_TRUE_TO_APP, SE_APP_TO_TRUE, SE_LAPSE_RATE,
22
+ SE_ECL_CENTRAL, SE_ECL_NONCENTRAL, SE_ECL_TOTAL, SE_ECL_ANNULAR,
23
+ SE_ECL_PARTIAL, SE_ECL_ANNULAR_TOTAL, SE_ECL_PENUMBRAL,
24
+ SE_ECL_ALLTYPES_LUNAR,
25
+ SE_ECL_VISIBLE, SE_ECL_MAX_VISIBLE,
26
+ SE_ECL_1ST_VISIBLE, SE_ECL_2ND_VISIBLE, SE_ECL_3RD_VISIBLE, SE_ECL_4TH_VISIBLE,
27
+ SE_ECL_PARTBEG_VISIBLE, SE_ECL_TOTBEG_VISIBLE, SE_ECL_TOTEND_VISIBLE, SE_ECL_PARTEND_VISIBLE,
28
+ SE_ECL_PENUMBBEG_VISIBLE, SE_ECL_PENUMBEND_VISIBLE,
29
+ SE_ECL_OCC_BEG_DAYLIGHT, SE_ECL_OCC_END_DAYLIGHT,
30
+ SE_ECL_ONE_TRY,
31
+ SE_NODBIT_MEAN, SE_NODBIT_OSCU_BAR, SE_NODBIT_FOPOINT,
32
+ SE_SIDBIT_ECL_T0, SE_SIDBIT_SSY_PLANE,
33
+ J2000, J_TO_J2000, J2000_TO_J, CLIGHT,
34
+ SEFLG_SPEED, SEFLG_EQUATORIAL, SEFLG_TOPOCTR, SEFLG_NONUT, SEFLG_TRUEPOS,
35
+ SEFLG_EPHMASK, SEFLG_XYZ, SEFLG_HELCTR, SEFLG_J2000, SEFLG_NOABERR,
36
+ SEFLG_NOGDEFL, SEFLG_SWIEPH, SEFLG_RADIANS,
37
+ SEFLG_BARYCTR, SEFLG_SIDEREAL, SEFLG_MOSEPH, SEFLG_ICRS, SEFLG_ORBEL_AA,
38
+ SEI_SUN, SEI_SUNBARY, SEI_EARTH,
39
+ SEI_ECL_GEOALT_MIN, SEI_ECL_GEOALT_MAX,
40
+ NDIAM, PLA_DIAM,
41
+ DSUN, DMOON, DEARTH_ECL, RSUN_ECL, RMOON, REARTH_ECL,
42
+ SAROS_CYCLE, NSAROS_SOLAR, NSAROS_LUNAR,
43
+ GEOGCONST, HELGRAVCONST, EARTH_MOON_MRAT,
44
+ MOON_MEAN_DIST, MOON_MEAN_INCL, MOON_MEAN_ECC,
45
+ NODE_CALC_INTV,
46
+ } from './constants';
47
+
48
+ import type { SweData, Epsilon, Nut } from './types';
49
+ import { createEpsilon, createNut } from './types';
50
+
51
+ import {
52
+ sweDegnorm, sweCotrans, sweDeltatEx, sweSidtime,
53
+ squareSum, swiPolcart, swiCartpol,
54
+ swiPolcartSp, swiCartpolSp, swiCrossProd, swiMod2PI,
55
+ swiCoortrf2, swiNutation, swiPrecess, swiBias,
56
+ } from './swephlib';
57
+
58
+ import {
59
+ sweCalc, sweCalcUt, sweFixstar, sweSetTopo, swiInitSwedIfStart,
60
+ swiForceAppPosEtc, swiGetObserver, swiGetDenum,
61
+ swiPrecessSpeed, swiNutate, swiDeflectLight, swiAberrLight,
62
+ calcEpsilon, nutMatrix,
63
+ swiTropRa2SidLon, swiTropRa2SidLonSosy,
64
+ sweGetAyanamsaEx,
65
+ } from './sweph';
66
+
67
+ import { swiMeanLunarElements } from './swemmoon';
68
+
69
+ import { sweHousePos } from './swehouse';
70
+
71
+ /* ---- Module state ---- */
72
+ let constLapseRate = SE_LAPSE_RATE;
73
+
74
+ /* ==================================================================
75
+ * Refraction helpers
76
+ * ================================================================== */
77
+
78
+ function calcDip(geoalt: number, atpress: number, attemp: number, lapseRate: number): number {
79
+ const krefr = (0.0342 + lapseRate) / (0.154 * 0.0238);
80
+ const d = 1 - 1.8480 * krefr * atpress / (273.15 + attemp) / (273.15 + attemp);
81
+ return -180.0 / PI * Math.acos(1 / (1 + geoalt / EARTH_RADIUS)) * Math.sqrt(d);
82
+ }
83
+
84
+ function calcAstronomicalRefr(inalt: number, atpress: number, attemp: number): number {
85
+ let r: number;
86
+ if (inalt > 17.904104638432) {
87
+ r = 0.97 / Math.tan(inalt * DEGTORAD);
88
+ } else {
89
+ r = (34.46 + 4.23 * inalt + 0.004 * inalt * inalt) / (1 + 0.505 * inalt + 0.0845 * inalt * inalt);
90
+ }
91
+ r = ((atpress - 80) / 930 / (1 + 0.00008 * (r + 39) * (attemp - 10)) * r) / 60.0;
92
+ return r;
93
+ }
94
+
95
+ export function sweRefrac(inalt: number, atpress: number, attemp: number, calcFlag: number): number {
96
+ const ptFactor = atpress / 1010.0 * 283.0 / (273.0 + attemp);
97
+ let a: number, refr: number;
98
+ if (calcFlag === SE_TRUE_TO_APP) {
99
+ const trualt = inalt;
100
+ if (trualt > 15) {
101
+ a = Math.tan((90 - trualt) * DEGTORAD);
102
+ refr = (58.276 * a - 0.0824 * a * a * a);
103
+ refr *= ptFactor / 3600.0;
104
+ } else if (trualt > -5) {
105
+ a = trualt + 10.3 / (trualt + 5.11);
106
+ if (a + 1e-10 >= 90) refr = 0;
107
+ else refr = 1.02 / Math.tan(a * DEGTORAD);
108
+ refr *= ptFactor / 60.0;
109
+ } else {
110
+ refr = 0;
111
+ }
112
+ let appalt = trualt;
113
+ if (appalt + refr > 0) appalt += refr;
114
+ return appalt;
115
+ } else {
116
+ const appalt = inalt;
117
+ a = appalt + 7.31 / (appalt + 4.4);
118
+ if (a + 1e-10 >= 90) refr = 0;
119
+ else {
120
+ refr = 1.00 / Math.tan(a * DEGTORAD);
121
+ refr -= 0.06 * Math.sin(14.7 * refr + 13);
122
+ }
123
+ refr *= ptFactor / 60.0;
124
+ let trualt = appalt;
125
+ if (appalt - refr > 0) trualt = appalt - refr;
126
+ return trualt;
127
+ }
128
+ }
129
+
130
+ export function sweSetLapseRate(lapseRate: number): void {
131
+ constLapseRate = lapseRate;
132
+ }
133
+
134
+ export function sweRefracExtended(
135
+ inalt: number, geoalt: number, atpress: number, attemp: number,
136
+ lapseRate: number, calcFlag: number, dret: number[] | null,
137
+ ): number {
138
+ let inaltAdj = inalt;
139
+ if (inaltAdj > 90) inaltAdj = 180 - inaltAdj;
140
+ const dip = calcDip(geoalt, atpress, attemp, lapseRate);
141
+ if (calcFlag === SE_TRUE_TO_APP) {
142
+ if (inaltAdj < -10) {
143
+ if (dret !== null) { dret[0] = inaltAdj; dret[1] = inaltAdj; dret[2] = 0; dret[3] = dip; }
144
+ return inaltAdj;
145
+ }
146
+ let y = inaltAdj;
147
+ let D = 0.0;
148
+ let yy0 = 0;
149
+ let D0 = D;
150
+ for (let i = 0; i < 5; i++) {
151
+ D = calcAstronomicalRefr(y, atpress, attemp);
152
+ const N2 = y - yy0;
153
+ yy0 = D - D0 - N2;
154
+ let N: number;
155
+ if (N2 !== 0.0 && yy0 !== 0.0)
156
+ N = y - N2 * (inaltAdj + D - y) / yy0;
157
+ else
158
+ N = inaltAdj + D;
159
+ yy0 = y;
160
+ D0 = D;
161
+ y = N;
162
+ }
163
+ const refr = D;
164
+ if (inaltAdj + refr < dip) {
165
+ if (dret !== null) { dret[0] = inaltAdj; dret[1] = inaltAdj; dret[2] = 0; dret[3] = dip; }
166
+ return inaltAdj;
167
+ }
168
+ if (dret !== null) { dret[0] = inaltAdj; dret[1] = inaltAdj + refr; dret[2] = refr; dret[3] = dip; }
169
+ return inaltAdj + refr;
170
+ } else {
171
+ const refr = calcAstronomicalRefr(inaltAdj, atpress, attemp);
172
+ const trualt = inaltAdj - refr;
173
+ if (dret !== null) {
174
+ if (inaltAdj > dip) {
175
+ dret[0] = trualt; dret[1] = inaltAdj; dret[2] = refr; dret[3] = dip;
176
+ } else {
177
+ dret[0] = inaltAdj; dret[1] = inaltAdj; dret[2] = 0; dret[3] = dip;
178
+ }
179
+ }
180
+ if (inaltAdj >= dip) return trualt;
181
+ else return inaltAdj;
182
+ }
183
+ }
184
+
185
+ /* ==================================================================
186
+ * Azimuth / Altitude conversions
187
+ * ================================================================== */
188
+
189
+ export function sweAzalt(
190
+ swed: SweData, tjdUt: number, calcFlag: number,
191
+ geopos: number[], atpress: number, attemp: number,
192
+ xin: number[], xaz: number[],
193
+ ): void {
194
+ const x = [0, 0, 0, 0, 0, 0];
195
+ const xra = [xin[0], xin[1], 1];
196
+ const armc = sweDegnorm(sweSidtime(swed, tjdUt) * 15 + geopos[0]);
197
+ if (calcFlag === SE_ECL2HOR) {
198
+ const res = sweCalc(swed, tjdUt + sweDeltatEx(tjdUt, -1, swed), SE_ECL_NUT, 0);
199
+ const epsTrue = res.xx[0];
200
+ const tmp = [0, 0, 0];
201
+ sweCotrans(xra, tmp, -epsTrue);
202
+ xra[0] = tmp[0]; xra[1] = tmp[1]; xra[2] = tmp[2];
203
+ }
204
+ const mdd = sweDegnorm(xra[0] - armc);
205
+ x[0] = sweDegnorm(mdd - 90);
206
+ x[1] = xra[1];
207
+ x[2] = 1;
208
+ const h = [0, 0, 0];
209
+ sweCotrans(x, h, 90 - geopos[1]);
210
+ x[0] = sweDegnorm(h[0] + 90);
211
+ xaz[0] = 360 - x[0];
212
+ xaz[1] = h[1]; // true height
213
+ let atpressAdj = atpress;
214
+ if (atpressAdj === 0) {
215
+ atpressAdj = 1013.25 * Math.pow(1 - 0.0065 * geopos[2] / 288, 5.255);
216
+ }
217
+ xaz[2] = sweRefracExtended(h[1], geopos[2], atpressAdj, attemp, constLapseRate, SE_TRUE_TO_APP, null);
218
+ }
219
+
220
+ export function sweAzaltRev(
221
+ swed: SweData, tjdUt: number, calcFlag: number,
222
+ geopos: number[], xin: number[], xout: number[],
223
+ ): void {
224
+ const x = [0, 0, 0, 0, 0, 0];
225
+ const xaz = [xin[0], xin[1], 1];
226
+ const armc = sweDegnorm(sweSidtime(swed, tjdUt) * 15 + geopos[0]);
227
+ // azimuth from south clockwise → from east counterclock
228
+ xaz[0] = 360 - xaz[0];
229
+ xaz[0] = sweDegnorm(xaz[0] - 90);
230
+ const dang = geopos[1] - 90;
231
+ const eq = [0, 0, 0];
232
+ sweCotrans(xaz, eq, dang);
233
+ xaz[0] = sweDegnorm(eq[0] + armc + 90);
234
+ xout[0] = xaz[0];
235
+ xout[1] = eq[1];
236
+ if (calcFlag === SE_HOR2EQU) return; // already equatorial
237
+ // ecliptic
238
+ const res = sweCalc(swed, tjdUt + sweDeltatEx(tjdUt, -1, swed), SE_ECL_NUT, 0);
239
+ const epsTrue = res.xx[0];
240
+ const ecl = [0, 0, 0];
241
+ sweCotrans([xaz[0], eq[1], 1], ecl, epsTrue);
242
+ xout[0] = ecl[0];
243
+ xout[1] = ecl[1];
244
+ }
245
+
246
+ /* ==================================================================
247
+ * Small helpers for rise/set
248
+ * ================================================================== */
249
+
250
+ function findMaximum(y00: number, y11: number, y2: number, dx: number): { dxret: number; yret: number } {
251
+ const c = y11;
252
+ const b = (y2 - y00) / 2.0;
253
+ const a = (y2 + y00) / 2.0 - c;
254
+ const x = -b / 2 / a;
255
+ const y = (4 * a * c - b * b) / 4 / a;
256
+ return { dxret: (x - 1) * dx, yret: y };
257
+ }
258
+
259
+ function rdiTwilight(rsmi: number): number {
260
+ if (rsmi & SE_BIT_ASTRO_TWILIGHT) return 18;
261
+ if (rsmi & SE_BIT_NAUTIC_TWILIGHT) return 12;
262
+ if (rsmi & SE_BIT_CIVIL_TWILIGHT) return 6;
263
+ return 0;
264
+ }
265
+
266
+ function getSunRadPlusRefr(ipl: number, dd: number, rsmi: number, refr: number): number {
267
+ let rdi = 0;
268
+ let ddAdj = dd;
269
+ if (rsmi & SE_BIT_FIXED_DISC_SIZE) {
270
+ if (ipl === SE_SUN) ddAdj = 1.0;
271
+ else if (ipl === SE_MOON) ddAdj = 0.00257;
272
+ }
273
+ if (!(rsmi & SE_BIT_DISC_CENTER))
274
+ rdi = Math.asin(PLA_DIAM[ipl] / 2.0 / AUNIT / ddAdj) * RADTODEG;
275
+ if (rsmi & SE_BIT_DISC_BOTTOM) rdi = -rdi;
276
+ if (!(rsmi & SE_BIT_NO_REFRACTION)) rdi += refr;
277
+ return rdi;
278
+ }
279
+
280
+ /* ==================================================================
281
+ * Meridian transit
282
+ * ================================================================== */
283
+
284
+ function calcMerTrans(
285
+ swed: SweData, tjdUt: number, ipl: number, epheflag: number, rsmi: number,
286
+ geopos: number[], starname: string | null,
287
+ serr: { value: string } | null,
288
+ ): { retval: number; tret: number } {
289
+ let tjdEt = tjdUt + sweDeltatEx(tjdUt, epheflag, swed);
290
+ let iflag = epheflag & SEFLG_EPHMASK;
291
+ iflag |= (SEFLG_EQUATORIAL | SEFLG_TOPOCTR);
292
+ const doFixstar = starname !== null && starname !== '';
293
+ let armc0 = sweSidtime(swed, tjdUt) + geopos[0] / 15;
294
+ if (armc0 >= 24) armc0 -= 24;
295
+ if (armc0 < 0) armc0 += 24;
296
+ armc0 *= 15;
297
+ let x0: Float64Array;
298
+ if (doFixstar) {
299
+ const res = sweFixstar(swed, starname!, tjdEt, iflag);
300
+ if (res.flags < 0) return { retval: ERR, tret: 0 };
301
+ x0 = res.xx;
302
+ } else {
303
+ const res = sweCalc(swed, tjdEt, ipl, iflag);
304
+ if (res.flags < 0) return { retval: ERR, tret: 0 };
305
+ x0 = res.xx;
306
+ }
307
+ const x = [x0[0], x0[1], 0, 0, 0, 0];
308
+ let t = tjdUt;
309
+ let arxc = armc0;
310
+ if (rsmi & SE_CALC_ITRANSIT) arxc = sweDegnorm(arxc + 180);
311
+ for (let i = 0; i < 4; i++) {
312
+ let mdd = sweDegnorm(x[0] - arxc);
313
+ if (i > 0 && mdd > 180) mdd -= 360;
314
+ t += mdd / 361;
315
+ let armc = sweSidtime(swed, t) + geopos[0] / 15;
316
+ if (armc >= 24) armc -= 24;
317
+ if (armc < 0) armc += 24;
318
+ armc *= 15;
319
+ arxc = armc;
320
+ if (rsmi & SE_CALC_ITRANSIT) arxc = sweDegnorm(arxc + 180);
321
+ if (!doFixstar) {
322
+ const te = t + sweDeltatEx(t, epheflag, swed);
323
+ const res = sweCalc(swed, te, ipl, iflag);
324
+ if (res.flags < 0) return { retval: ERR, tret: 0 };
325
+ x[0] = res.xx[0]; x[1] = res.xx[1];
326
+ }
327
+ }
328
+ return { retval: OK, tret: t };
329
+ }
330
+
331
+ /* ==================================================================
332
+ * rise_set_fast — simple fast algorithm for moderate latitudes
333
+ * ================================================================== */
334
+
335
+ function riseSetFast(
336
+ swed: SweData, tjdUt: number, ipl: number,
337
+ epheflag: number, rsmi: number,
338
+ dgeo: number[], atpress: number, attemp: number,
339
+ serr: { value: string } | null,
340
+ ): { retval: number; tret: number } {
341
+ const iflag = epheflag & SEFLG_EPHMASK;
342
+ let iflagtopo = iflag | SEFLG_EQUATORIAL;
343
+ let tohorFlag = SE_EQU2HOR;
344
+ const facrise = (rsmi & SE_CALC_SET) ? -1 : 1;
345
+ let nloop = (ipl === SE_MOON) ? 4 : 2;
346
+ let isSecondRun = false;
347
+ let tjdUtAdj = tjdUt;
348
+
349
+ if (!(rsmi & SE_BIT_GEOCTR_NO_ECL_LAT)) {
350
+ iflagtopo |= SEFLG_TOPOCTR;
351
+ sweSetTopo(swed, dgeo[0], dgeo[1], dgeo[2]);
352
+ }
353
+
354
+ // run_rise_again loop
355
+ for (;;) {
356
+ const res0 = sweCalcUt(swed, tjdUtAdj, ipl, iflagtopo);
357
+ if (res0.flags < 0) return { retval: ERR, tret: 0 };
358
+ const xx = res0.xx;
359
+
360
+ const decl = xx[1];
361
+ let sda = -Math.tan(dgeo[1] * DEGTORAD) * Math.tan(decl * DEGTORAD);
362
+ if (sda >= 1) sda = 10;
363
+ else if (sda <= -1) sda = 180;
364
+ else sda = Math.acos(sda) * RADTODEG;
365
+
366
+ const armc = sweDegnorm(sweSidtime(swed, tjdUtAdj) * 15 + dgeo[0]);
367
+ const md = sweDegnorm(xx[0] - armc);
368
+ const mdrise = sweDegnorm(sda * facrise);
369
+ let dmd = sweDegnorm(md - mdrise);
370
+ if (dmd > 358) dmd -= 360;
371
+
372
+ let tr = tjdUtAdj + dmd / 360;
373
+
374
+ let atpressAdj = atpress;
375
+ if (atpressAdj === 0) {
376
+ atpressAdj = 1013.25 * Math.pow(1 - 0.0065 * dgeo[2] / 288, 5.255);
377
+ }
378
+ const xxRef = [0, 0, 0, 0, 0, 0];
379
+ sweRefracExtended(0.000001, 0, atpressAdj, attemp, constLapseRate, SE_APP_TO_TRUE, xxRef);
380
+ const refr = xxRef[1] - xxRef[0];
381
+
382
+ if (rsmi & SE_BIT_GEOCTR_NO_ECL_LAT) {
383
+ tohorFlag = SE_ECL2HOR;
384
+ iflagtopo = iflag;
385
+ } else {
386
+ tohorFlag = SE_EQU2HOR;
387
+ iflagtopo = iflag | SEFLG_EQUATORIAL | SEFLG_TOPOCTR;
388
+ sweSetTopo(swed, dgeo[0], dgeo[1], dgeo[2]);
389
+ }
390
+
391
+ for (let i = 0; i < nloop; i++) {
392
+ const resI = sweCalcUt(swed, tr, ipl, iflagtopo);
393
+ if (resI.flags < 0) return { retval: ERR, tret: 0 };
394
+ const xxi = Array.from(resI.xx);
395
+ if (rsmi & SE_BIT_GEOCTR_NO_ECL_LAT) xxi[1] = 0;
396
+ const rdi = getSunRadPlusRefr(ipl, xxi[2], rsmi, refr);
397
+ const xaz = [0, 0, 0];
398
+ const xaz2 = [0, 0, 0];
399
+ sweAzalt(swed, tr, tohorFlag, dgeo, atpressAdj, attemp, xxi, xaz);
400
+ sweAzalt(swed, tr + 0.001, tohorFlag, dgeo, atpressAdj, attemp, xxi, xaz2);
401
+ const dd = xaz2[1] - xaz[1];
402
+ const dalt = xaz[1] + rdi;
403
+ let dt = dalt / dd / 1000.0;
404
+ if (dt > 0.1) dt = 0.1;
405
+ else if (dt < -0.1) dt = -0.1;
406
+ tr -= dt;
407
+ }
408
+
409
+ if (tr < tjdUt && !isSecondRun) {
410
+ tjdUtAdj += 0.5;
411
+ isSecondRun = true;
412
+ continue; // goto run_rise_again
413
+ }
414
+ return { retval: OK, tret: tr };
415
+ }
416
+ }
417
+
418
+ /* ==================================================================
419
+ * swe_rise_trans_true_hor — general rise/set with true horizon height
420
+ * ================================================================== */
421
+
422
+ function sweRiseTransTrueHor(
423
+ swed: SweData, tjdUt: number, ipl: number, starname: string | null,
424
+ epheflag: number, rsmi: number,
425
+ geopos: number[], atpress: number, attemp: number,
426
+ horhgt: number,
427
+ serr: { value: string } | null,
428
+ ): { retval: number; tret: number } {
429
+ const doFixstar = starname !== null && starname !== '';
430
+ if (geopos[2] < SEI_ECL_GEOALT_MIN || geopos[2] > SEI_ECL_GEOALT_MAX) {
431
+ if (serr) serr.value = `location for swe_rise_trans() must be between ${SEI_ECL_GEOALT_MIN} and ${SEI_ECL_GEOALT_MAX} m above sea`;
432
+ return { retval: ERR, tret: 0 };
433
+ }
434
+ let horhgtAdj = horhgt;
435
+ if (horhgtAdj === -100) {
436
+ horhgtAdj = 0.0001 + calcDip(geopos[2], atpress, attemp, constLapseRate);
437
+ }
438
+ let iplAdj = ipl;
439
+ if (iplAdj === SE_AST_OFFSET + 134340) iplAdj = SE_PLUTO;
440
+
441
+ let iflag = epheflag;
442
+ iflag &= (SEFLG_EPHMASK | SEFLG_NONUT | SEFLG_TRUEPOS);
443
+ let tohorFlag = SE_EQU2HOR;
444
+ if (rsmi & SE_BIT_GEOCTR_NO_ECL_LAT) {
445
+ tohorFlag = SE_ECL2HOR;
446
+ } else {
447
+ iflag |= SEFLG_EQUATORIAL;
448
+ iflag |= SEFLG_TOPOCTR;
449
+ sweSetTopo(swed, geopos[0], geopos[1], geopos[2]);
450
+ }
451
+
452
+ if (rsmi & (SE_CALC_MTRANSIT | SE_CALC_ITRANSIT)) {
453
+ return calcMerTrans(swed, tjdUt, iplAdj, epheflag, rsmi, geopos, starname, serr);
454
+ }
455
+ let rsmiAdj = rsmi;
456
+ if (!(rsmiAdj & (SE_CALC_RISE | SE_CALC_SET))) rsmiAdj |= SE_CALC_RISE;
457
+
458
+ // twilight
459
+ if (iplAdj === SE_SUN && (rsmiAdj & (SE_BIT_CIVIL_TWILIGHT | SE_BIT_NAUTIC_TWILIGHT | SE_BIT_ASTRO_TWILIGHT))) {
460
+ rsmiAdj |= (SE_BIT_NO_REFRACTION | SE_BIT_DISC_CENTER);
461
+ horhgtAdj = -rdiTwilight(rsmiAdj);
462
+ }
463
+
464
+ const jmax = 14;
465
+ const twohrs = 1.0 / 12.0;
466
+ let tjdEt = tjdUt + sweDeltatEx(tjdUt, epheflag, swed);
467
+
468
+ // Fixed star: compute once
469
+ let xc = [0, 0, 0, 0, 0, 0];
470
+ if (doFixstar) {
471
+ const res = sweFixstar(swed, starname!, tjdEt, iflag);
472
+ if (res.flags < 0) return { retval: ERR, tret: 0 };
473
+ xc = Array.from(res.xx);
474
+ }
475
+
476
+ // Compute heights at 15 sampling points spanning ~28 hours
477
+ const tc: number[] = new Array(24).fill(0);
478
+ const h: number[] = new Array(24).fill(0);
479
+ const xh: number[][] = [];
480
+ for (let ii = 0; ii <= jmax + 6; ii++) xh.push([0, 0, 0, 0, 0, 0]);
481
+ let dd = 0;
482
+ let nculm = -1;
483
+ const tculm: number[] = [0, 0, 0, 0];
484
+
485
+ for (let ii = 0, t = tjdUt - twohrs; ii <= jmax; ii++, t += twohrs) {
486
+ tc[ii] = t;
487
+ if (!doFixstar) {
488
+ const te = t + sweDeltatEx(t, epheflag, swed);
489
+ const res = sweCalc(swed, te, iplAdj, iflag);
490
+ if (res.flags < 0) return { retval: ERR, tret: 0 };
491
+ xc = Array.from(res.xx);
492
+ }
493
+ if (rsmiAdj & SE_BIT_GEOCTR_NO_ECL_LAT) xc[1] = 0;
494
+
495
+ if (ii === 0) {
496
+ if (doFixstar) dd = 0;
497
+ else if (rsmiAdj & SE_BIT_DISC_CENTER) dd = 0;
498
+ else if (iplAdj < NDIAM) dd = PLA_DIAM[iplAdj];
499
+ else if (iplAdj > SE_AST_OFFSET) dd = 0; // simplified: no ast_diam
500
+ else dd = 0;
501
+ }
502
+
503
+ let curdist = xc[2];
504
+ if (rsmiAdj & SE_BIT_FIXED_DISC_SIZE) {
505
+ if (iplAdj === SE_SUN) curdist = 1.0;
506
+ else if (iplAdj === SE_MOON) curdist = 0.00257;
507
+ }
508
+ const rdi = Math.asin(dd / 2 / AUNIT / curdist) * RADTODEG;
509
+
510
+ const ah = [0, 0, 0, 0, 0, 0];
511
+ sweAzalt(swed, t, tohorFlag, geopos, atpress, attemp, xc, ah);
512
+ xh[ii] = [...ah];
513
+
514
+ if (rsmiAdj & SE_BIT_DISC_BOTTOM) xh[ii][1] -= rdi;
515
+ else xh[ii][1] += rdi;
516
+
517
+ if (rsmiAdj & SE_BIT_NO_REFRACTION) {
518
+ xh[ii][1] -= horhgtAdj;
519
+ h[ii] = xh[ii][1];
520
+ } else {
521
+ const xcTmp = [0, 0, 0, 0, 0, 0];
522
+ sweAzaltRev(swed, t, SE_HOR2EQU, geopos, xh[ii], xcTmp);
523
+ const ahTmp = [0, 0, 0, 0, 0, 0];
524
+ sweAzalt(swed, t, SE_EQU2HOR, geopos, atpress, attemp, xcTmp, ahTmp);
525
+ ahTmp[1] -= horhgtAdj;
526
+ ahTmp[2] -= horhgtAdj;
527
+ xh[ii][1] = ahTmp[1];
528
+ xh[ii][2] = ahTmp[2];
529
+ h[ii] = ahTmp[2];
530
+ }
531
+
532
+ // Check for culmination
533
+ if (ii > 1) {
534
+ const dc0 = xh[ii - 2][1];
535
+ const dc1 = xh[ii - 1][1];
536
+ const dc2 = xh[ii][1];
537
+ let calcCulm = 0;
538
+ if (dc1 > dc0 && dc1 > dc2) calcCulm = 1;
539
+ if (dc1 < dc0 && dc1 < dc2) calcCulm = 2;
540
+ if (calcCulm) {
541
+ let dt = twohrs;
542
+ let tcu = t - dt;
543
+ const fm = findMaximum(dc0, dc1, dc2, dt);
544
+ tcu += fm.dxret + dt;
545
+ dt /= 3;
546
+ for (; dt > 0.0001; dt /= 3) {
547
+ const dc = [0, 0, 0];
548
+ for (let i = 0, tt = tcu - dt; i < 3; tt += dt, i++) {
549
+ const te = tt + sweDeltatEx(tt, epheflag, swed);
550
+ if (!doFixstar) {
551
+ const res = sweCalc(swed, te, iplAdj, iflag);
552
+ if (res.flags < 0) return { retval: ERR, tret: 0 };
553
+ xc = Array.from(res.xx);
554
+ }
555
+ if (rsmiAdj & SE_BIT_GEOCTR_NO_ECL_LAT) xc[1] = 0;
556
+ const ahC = [0, 0, 0, 0, 0, 0];
557
+ sweAzalt(swed, tt, tohorFlag, geopos, atpress, attemp, xc, ahC);
558
+ ahC[1] -= horhgtAdj;
559
+ dc[i] = ahC[1];
560
+ }
561
+ const fm2 = findMaximum(dc[0], dc[1], dc[2], dt);
562
+ tcu += fm2.dxret + dt;
563
+ }
564
+ nculm++;
565
+ tculm[nculm] = tcu;
566
+ }
567
+ }
568
+ }
569
+
570
+ // Insert culminations into height array
571
+ let jmaxAdj = jmax;
572
+ for (let i = 0; i <= nculm; i++) {
573
+ for (let j = 1; j <= jmaxAdj; j++) {
574
+ if (tculm[i] < tc[j]) {
575
+ for (let k = jmaxAdj; k >= j; k--) {
576
+ tc[k + 1] = tc[k];
577
+ h[k + 1] = h[k];
578
+ }
579
+ tc[j] = tculm[i];
580
+ if (!doFixstar) {
581
+ const te = tc[j] + sweDeltatEx(tc[j], epheflag, swed);
582
+ const res = sweCalc(swed, te, iplAdj, iflag);
583
+ if (res.flags < 0) return { retval: ERR, tret: 0 };
584
+ xc = Array.from(res.xx);
585
+ if (rsmiAdj & SE_BIT_GEOCTR_NO_ECL_LAT) xc[1] = 0;
586
+ }
587
+ let curdist = xc[2];
588
+ if (rsmiAdj & SE_BIT_FIXED_DISC_SIZE) {
589
+ if (iplAdj === SE_SUN) curdist = 1.0;
590
+ else if (iplAdj === SE_MOON) curdist = 0.00257;
591
+ }
592
+ const rdi = Math.asin(dd / 2 / AUNIT / curdist) * RADTODEG;
593
+ const ahIns = [0, 0, 0, 0, 0, 0];
594
+ sweAzalt(swed, tc[j], tohorFlag, geopos, atpress, attemp, xc, ahIns);
595
+ if (rsmiAdj & SE_BIT_DISC_BOTTOM) ahIns[1] -= rdi;
596
+ else ahIns[1] += rdi;
597
+ if (rsmiAdj & SE_BIT_NO_REFRACTION) {
598
+ ahIns[1] -= horhgtAdj;
599
+ h[j] = ahIns[1];
600
+ } else {
601
+ const xcRev = [0, 0, 0, 0, 0, 0];
602
+ sweAzaltRev(swed, tc[j], SE_HOR2EQU, geopos, ahIns, xcRev);
603
+ const ahRef = [0, 0, 0, 0, 0, 0];
604
+ sweAzalt(swed, tc[j], SE_EQU2HOR, geopos, atpress, attemp, xcRev, ahRef);
605
+ ahRef[1] -= horhgtAdj;
606
+ ahRef[2] -= horhgtAdj;
607
+ h[j] = ahRef[2];
608
+ }
609
+ jmaxAdj++;
610
+ break;
611
+ }
612
+ }
613
+ }
614
+
615
+ // Binary search for zero-crossings
616
+ for (let ii = 1; ii <= jmaxAdj; ii++) {
617
+ if (h[ii - 1] * h[ii] >= 0) continue;
618
+ if (h[ii - 1] < h[ii] && !(rsmiAdj & SE_CALC_RISE)) continue;
619
+ if (h[ii - 1] > h[ii] && !(rsmiAdj & SE_CALC_SET)) continue;
620
+ const dc = [h[ii - 1], h[ii]];
621
+ const t2 = [tc[ii - 1], tc[ii]];
622
+ for (let i = 0; i < 20; i++) {
623
+ const t = (t2[0] + t2[1]) / 2;
624
+ if (!doFixstar) {
625
+ const te = t + sweDeltatEx(t, epheflag, swed);
626
+ const res = sweCalc(swed, te, iplAdj, iflag);
627
+ if (res.flags < 0) return { retval: ERR, tret: 0 };
628
+ xc = Array.from(res.xx);
629
+ if (rsmiAdj & SE_BIT_GEOCTR_NO_ECL_LAT) xc[1] = 0;
630
+ }
631
+ let curdist = xc[2];
632
+ if (rsmiAdj & SE_BIT_FIXED_DISC_SIZE) {
633
+ if (iplAdj === SE_SUN) curdist = 1.0;
634
+ else if (iplAdj === SE_MOON) curdist = 0.00257;
635
+ }
636
+ const rdi = Math.asin(dd / 2 / AUNIT / curdist) * RADTODEG;
637
+ const ahBin = [0, 0, 0, 0, 0, 0];
638
+ sweAzalt(swed, t, tohorFlag, geopos, atpress, attemp, xc, ahBin);
639
+ if (rsmiAdj & SE_BIT_DISC_BOTTOM) ahBin[1] -= rdi;
640
+ else ahBin[1] += rdi;
641
+ let aha: number;
642
+ if (rsmiAdj & SE_BIT_NO_REFRACTION) {
643
+ ahBin[1] -= horhgtAdj;
644
+ aha = ahBin[1];
645
+ } else {
646
+ const xcRev = [0, 0, 0, 0, 0, 0];
647
+ sweAzaltRev(swed, t, SE_HOR2EQU, geopos, ahBin, xcRev);
648
+ const ahRef = [0, 0, 0, 0, 0, 0];
649
+ sweAzalt(swed, t, SE_EQU2HOR, geopos, atpress, attemp, xcRev, ahRef);
650
+ ahRef[1] -= horhgtAdj;
651
+ ahRef[2] -= horhgtAdj;
652
+ aha = ahRef[2];
653
+ }
654
+ if (aha * dc[0] <= 0) { dc[1] = aha; t2[1] = t; }
655
+ else { dc[0] = aha; t2[0] = t; }
656
+ }
657
+ const t = (t2[0] + t2[1]) / 2;
658
+ if (t > tjdUt) {
659
+ return { retval: OK, tret: t };
660
+ }
661
+ }
662
+ if (serr) serr.value = `rise or set not found for planet ${iplAdj}`;
663
+ return { retval: -2, tret: 0 };
664
+ }
665
+
666
+ /* ==================================================================
667
+ * swe_rise_trans — main entry point
668
+ * ================================================================== */
669
+
670
+ export function sweRiseTrans(
671
+ swed: SweData, tjdUt: number, ipl: number, starname: string | null,
672
+ epheflag: number, rsmi: number,
673
+ geopos: number[], atpress: number, attemp: number,
674
+ serr: { value: string } | null,
675
+ ): { retval: number; tret: number } {
676
+ swiInitSwedIfStart(swed);
677
+ const doFixstar = starname !== null && starname !== '';
678
+ if (!doFixstar
679
+ && (rsmi & (SE_CALC_RISE | SE_CALC_SET))
680
+ && !(rsmi & SE_BIT_FORCE_SLOW_METHOD)
681
+ && !(rsmi & (SE_BIT_CIVIL_TWILIGHT | SE_BIT_NAUTIC_TWILIGHT | SE_BIT_ASTRO_TWILIGHT))
682
+ && (ipl >= SE_SUN && ipl <= SE_TRUE_NODE)
683
+ && (Math.abs(geopos[1]) <= 60 || (ipl === SE_SUN && Math.abs(geopos[1]) <= 65))
684
+ ) {
685
+ return riseSetFast(swed, tjdUt, ipl, epheflag, rsmi, geopos, atpress, attemp, serr);
686
+ }
687
+ return sweRiseTransTrueHor(swed, tjdUt, ipl, starname, epheflag, rsmi, geopos, atpress, attemp, 0, serr);
688
+ }
689
+
690
+ export function sweRiseTransTrueHorExport(
691
+ swed: SweData, tjdUt: number, ipl: number, starname: string | null,
692
+ epheflag: number, rsmi: number,
693
+ geopos: number[], atpress: number, attemp: number,
694
+ horhgt: number,
695
+ serr: { value: string } | null,
696
+ ): { retval: number; tret: number } {
697
+ swiInitSwedIfStart(swed);
698
+ return sweRiseTransTrueHor(swed, tjdUt, ipl, starname, epheflag, rsmi, geopos, atpress, attemp, horhgt, serr);
699
+ }
700
+
701
+ /* ==================================================================
702
+ * swe_gauquelin_sector
703
+ * ================================================================== */
704
+
705
+ export function sweGauquelinSector(
706
+ swed: SweData,
707
+ tUt: number, ipl: number, starname: string | null,
708
+ iflag: number, imeth: number,
709
+ geopos: number[], atpress: number, attemp: number,
710
+ serr: { value: string } | null,
711
+ ): { retval: number; dgsect: number } {
712
+ swiInitSwedIfStart(swed);
713
+ if (imeth < 0 || imeth > 5) {
714
+ if (serr) serr.value = `invalid method: ${imeth}`;
715
+ return { retval: ERR, dgsect: 0 };
716
+ }
717
+ let iplAdj = ipl;
718
+ if (iplAdj === SE_AST_OFFSET + 134340) iplAdj = SE_PLUTO;
719
+ const doFixstar = starname !== null && starname !== '';
720
+
721
+ /*
722
+ * Methods 0 and 1: geometrically from ecliptic longitude and latitude
723
+ */
724
+ if (imeth === 0 || imeth === 1) {
725
+ const tEt = tUt + sweDeltatEx(tUt, iflag, swed);
726
+ const epsRad = swiEpsilnImport(tEt, iflag, swed);
727
+ const eps = epsRad * RADTODEG;
728
+ const nutlo = [0, 0];
729
+ swiNutationImport(tEt, iflag, nutlo, swed);
730
+ nutlo[0] *= RADTODEG;
731
+ nutlo[1] *= RADTODEG;
732
+ const armc = sweDegnorm(sweSidtime0Import(swed, tUt, eps + nutlo[1], nutlo[0]) * 15 + geopos[0]);
733
+ let x0: Float64Array;
734
+ if (doFixstar) {
735
+ const res = sweFixstar(swed, starname!, tEt, iflag);
736
+ if (res.flags < 0) return { retval: ERR, dgsect: 0 };
737
+ x0 = res.xx;
738
+ } else {
739
+ const res = sweCalc(swed, tEt, iplAdj, iflag);
740
+ if (res.flags < 0) return { retval: ERR, dgsect: 0 };
741
+ x0 = res.xx;
742
+ }
743
+ const xpin = [x0[0], imeth === 1 ? 0 : x0[1], x0[2], x0[3], x0[4], x0[5]];
744
+ const dgsect = sweHousePos(armc, geopos[1], eps + nutlo[1], 'G', xpin, null);
745
+ return { retval: OK, dgsect };
746
+ }
747
+
748
+ /*
749
+ * Methods 2-5: from rise and set times
750
+ */
751
+ const epheflag = iflag & SEFLG_EPHMASK;
752
+ let risemeth = 0;
753
+ if (imeth === 2 || imeth === 4) risemeth |= SE_BIT_NO_REFRACTION;
754
+ if (imeth === 2 || imeth === 3) risemeth |= SE_BIT_DISC_CENTER;
755
+
756
+ // find next rising
757
+ let riseRes = sweRiseTrans(swed, tUt, iplAdj, starname, epheflag, SE_CALC_RISE | risemeth, geopos, atpress, attemp, serr);
758
+ if (riseRes.retval === ERR) return { retval: ERR, dgsect: 0 };
759
+ let riseFound = riseRes.retval !== -2;
760
+ let trise = riseRes.tret;
761
+
762
+ // find next setting
763
+ let setRes = sweRiseTrans(swed, tUt, iplAdj, starname, epheflag, SE_CALC_SET | risemeth, geopos, atpress, attemp, serr);
764
+ if (setRes.retval === ERR) return { retval: ERR, dgsect: 0 };
765
+ let setFound = setRes.retval !== -2;
766
+ let tset = setRes.tret;
767
+
768
+ let aboveHorizon = false;
769
+
770
+ if (trise < tset && riseFound) {
771
+ aboveHorizon = false;
772
+ // find last set
773
+ let t = tUt - 1.2;
774
+ if (setFound) t = tset - 1.2;
775
+ setFound = true;
776
+ setRes = sweRiseTrans(swed, t, iplAdj, starname, epheflag, SE_CALC_SET | risemeth, geopos, atpress, attemp, serr);
777
+ if (setRes.retval === ERR) return { retval: ERR, dgsect: 0 };
778
+ if (setRes.retval === -2) setFound = false;
779
+ tset = setRes.tret;
780
+ } else if (trise >= tset && setFound) {
781
+ aboveHorizon = true;
782
+ // find last rise
783
+ let t = tUt - 1.2;
784
+ if (riseFound) t = trise - 1.2;
785
+ riseFound = true;
786
+ riseRes = sweRiseTrans(swed, t, iplAdj, starname, epheflag, SE_CALC_RISE | risemeth, geopos, atpress, attemp, serr);
787
+ if (riseRes.retval === ERR) return { retval: ERR, dgsect: 0 };
788
+ if (riseRes.retval === -2) riseFound = false;
789
+ trise = riseRes.tret;
790
+ }
791
+
792
+ if (riseFound && setFound) {
793
+ let dgsect: number;
794
+ if (aboveHorizon) {
795
+ dgsect = (tUt - trise) / (tset - trise) * 18 + 1;
796
+ } else {
797
+ dgsect = (tUt - tset) / (trise - tset) * 18 + 19;
798
+ }
799
+ return { retval: OK, dgsect };
800
+ } else {
801
+ if (serr) serr.value = `rise or set not found for planet ${iplAdj}`;
802
+ return { retval: ERR, dgsect: 0 };
803
+ }
804
+ }
805
+
806
+ /* ---- swe_pheno / swe_pheno_ut: planetary phenomena ---- */
807
+
808
+ const EULER = 2.718281828459;
809
+ const NMAG_ELEM = SE_VESTA + 1; // 21
810
+
811
+ /* Magnitude elements: [V(1,0), coeff1, coeff2, coeff3] */
812
+ const MAG_ELEM: number[][] = [
813
+ [-26.86, 0, 0, 0], // Sun
814
+ [-12.55, 0, 0, 0], // Moon
815
+ [-0.42, 3.80, -2.73, 2.00], // Mercury (obsolete placeholder)
816
+ [-4.40, 0.09, 2.39, -0.65], // Venus (obsolete placeholder)
817
+ [-1.52, 1.60, 0, 0], // Mars
818
+ [-9.40, 0.5, 0, 0], // Jupiter
819
+ [-8.88, -2.60, 1.25, 0.044], // Saturn
820
+ [-7.19, 0.0, 0, 0], // Uranus
821
+ [-6.87, 0.0, 0, 0], // Neptune
822
+ [-1.00, 0.0, 0, 0], // Pluto
823
+ [99, 0, 0, 0], // Mean Node
824
+ [99, 0, 0, 0], // True Node
825
+ [99, 0, 0, 0], // Mean Apog
826
+ [99, 0, 0, 0], // Oscu Apog
827
+ [99, 0, 0, 0], // Earth
828
+ [6.5, 0.15, 0, 0], // Chiron
829
+ [7.0, 0.15, 0, 0], // Pholus
830
+ [3.34, 0.12, 0, 0], // Ceres
831
+ [4.13, 0.11, 0, 0], // Pallas
832
+ [5.33, 0.32, 0, 0], // Juno
833
+ [3.20, 0.32, 0, 0], // Vesta
834
+ ];
835
+
836
+ function swiDotProdUnit(x: number[] | Float64Array, y: number[] | Float64Array): number {
837
+ let dop = x[0] * y[0] + x[1] * y[1] + x[2] * y[2];
838
+ const e1 = Math.sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
839
+ const e2 = Math.sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);
840
+ dop /= e1;
841
+ dop /= e2;
842
+ if (dop > 1) dop = 1;
843
+ if (dop < -1) dop = -1;
844
+ return dop;
845
+ }
846
+
847
+ /**
848
+ * swe_pheno: planetary phenomena (phase angle, phase, elongation, diameter, magnitude, horiz parallax)
849
+ *
850
+ * attr[0] = phase angle (degrees)
851
+ * attr[1] = phase (illuminated fraction, 0..1)
852
+ * attr[2] = elongation (degrees)
853
+ * attr[3] = apparent diameter (degrees)
854
+ * attr[4] = apparent magnitude
855
+ * attr[5] = horizontal parallax (Moon only, degrees)
856
+ */
857
+ export function swePheno(
858
+ swed: SweData, tjd: number, ipl: number, iflag: number,
859
+ serr: { value: string } | null,
860
+ ): { retval: number; attr: number[] } {
861
+ const attr = new Array(20).fill(0);
862
+ let serr2 = '';
863
+ iflag &= ~(0x40000 | 0x80000); // SEFLG_JPLHOR | SEFLG_JPLHOR_APPROX
864
+ /* Pluto with asteroid number 134340 treated as SE_PLUTO */
865
+ if (ipl === SE_AST_OFFSET + 134340) ipl = SE_PLUTO;
866
+ /* Ceres - Vesta must be SE_CERES etc., not 10001 etc. */
867
+ if (ipl > SE_AST_OFFSET && ipl <= SE_AST_OFFSET + 4)
868
+ ipl = ipl - SE_AST_OFFSET - 1 + SE_CERES;
869
+ iflag = iflag & (SEFLG_EPHMASK | SEFLG_TRUEPOS | SEFLG_J2000 |
870
+ SEFLG_NONUT | SEFLG_NOGDEFL | SEFLG_NOABERR | SEFLG_TOPOCTR);
871
+ let iflagp = iflag & (SEFLG_EPHMASK | SEFLG_TRUEPOS | SEFLG_J2000 |
872
+ SEFLG_NONUT | SEFLG_NOABERR);
873
+ iflagp |= SEFLG_HELCTR;
874
+ let epheflag = iflag & SEFLG_EPHMASK;
875
+ /*
876
+ * geocentric planet (XYZ)
877
+ */
878
+ let res = sweCalc(swed, tjd, ipl, iflag | SEFLG_XYZ);
879
+ if (res.flags === ERR) {
880
+ if (serr) serr.value = res.serr;
881
+ return { retval: ERR, attr };
882
+ }
883
+ const xx = Array.from(res.xx);
884
+ let retflag = res.flags;
885
+ // check epheflag and adjust
886
+ const epheflag2 = retflag & SEFLG_EPHMASK;
887
+ if (epheflag !== epheflag2) {
888
+ iflag = (iflag & ~epheflag) | epheflag2;
889
+ iflagp = (iflagp & ~epheflag) | epheflag2;
890
+ epheflag = epheflag2;
891
+ }
892
+ /* geocentric planet (lon/lat/dist) */
893
+ res = sweCalc(swed, tjd, ipl, iflag);
894
+ if (res.flags === ERR) {
895
+ if (serr) serr.value = res.serr;
896
+ return { retval: ERR, attr };
897
+ }
898
+ const lbr = Array.from(res.xx);
899
+ /* if moon, we need sun as well, for magnitude */
900
+ let xxs: number[] = [];
901
+ if (ipl === SE_MOON) {
902
+ res = sweCalc(swed, tjd, SE_SUN, iflag | SEFLG_XYZ);
903
+ if (res.flags === ERR) {
904
+ if (serr) serr.value = res.serr;
905
+ return { retval: ERR, attr };
906
+ }
907
+ xxs = Array.from(res.xx);
908
+ }
909
+ let lbr2: number[] = [0, 0, 0, 0, 0, 0];
910
+ let dt = 0;
911
+ if (ipl !== SE_SUN && ipl !== SE_EARTH &&
912
+ ipl !== SE_MEAN_NODE && ipl !== SE_TRUE_NODE &&
913
+ ipl !== SE_MEAN_APOG && ipl !== SE_OSCU_APOG) {
914
+ /*
915
+ * light time planet - earth
916
+ */
917
+ dt = lbr[2] * AUNIT / CLIGHT / 86400.0;
918
+ if (iflag & SEFLG_TRUEPOS) dt = 0;
919
+ /*
920
+ * heliocentric planet at tjd - dt (XYZ)
921
+ */
922
+ res = sweCalc(swed, tjd - dt, ipl, iflagp | SEFLG_XYZ);
923
+ if (res.flags === ERR) {
924
+ if (serr) serr.value = res.serr;
925
+ return { retval: ERR, attr };
926
+ }
927
+ const xx2 = Array.from(res.xx);
928
+ /* heliocentric planet (lon/lat/dist) */
929
+ res = sweCalc(swed, tjd - dt, ipl, iflagp);
930
+ if (res.flags === ERR) {
931
+ if (serr) serr.value = res.serr;
932
+ return { retval: ERR, attr };
933
+ }
934
+ lbr2 = Array.from(res.xx);
935
+ /*
936
+ * phase angle
937
+ */
938
+ attr[0] = Math.acos(swiDotProdUnit(xx, xx2)) * RADTODEG;
939
+ /*
940
+ * phase (illuminated fraction)
941
+ */
942
+ attr[1] = (1 + Math.cos(attr[0] * DEGTORAD)) / 2;
943
+ }
944
+ /*
945
+ * apparent diameter of disk
946
+ */
947
+ let dd: number;
948
+ if (ipl < NDIAM)
949
+ dd = PLA_DIAM[ipl];
950
+ else if (ipl > SE_AST_OFFSET)
951
+ dd = swed.astDiam * 1000; /* km -> m */
952
+ else
953
+ dd = 0;
954
+ if (lbr[2] < dd / 2 / AUNIT)
955
+ attr[3] = 180; /* assume position on surface of earth */
956
+ else
957
+ attr[3] = Math.asin(dd / 2 / AUNIT / lbr[2]) * 2 * RADTODEG;
958
+ /*
959
+ * apparent magnitude
960
+ */
961
+ if (ipl > SE_AST_OFFSET || (ipl < NMAG_ELEM && MAG_ELEM[ipl][0] < 99)) {
962
+ if (ipl === SE_SUN) {
963
+ /* ratio apparent diameter : average diameter */
964
+ let fac = attr[3] / (Math.asin(PLA_DIAM[SE_SUN] / 2.0 / AUNIT) * 2 * RADTODEG);
965
+ fac *= fac;
966
+ attr[4] = MAG_ELEM[ipl][0] - 2.5 * Math.log10(fac);
967
+ } else if (ipl === SE_MOON) {
968
+ /* MAG_MOON_VREIJS formula */
969
+ const a = attr[0];
970
+ if (a <= 147.1385465) {
971
+ /* Allen, C.W., 1976, Astrophysical Quantities */
972
+ attr[4] = -21.62 + 0.026 * Math.abs(a) + 0.000000004 * Math.pow(a, 4);
973
+ attr[4] += 5 * Math.log10(lbr[2] * lbr2[2] * AUNIT / EARTH_RADIUS);
974
+ } else {
975
+ /* Samaha cube phase angle */
976
+ attr[4] = -4.5444 - (2.5 * Math.log10(Math.pow(180 - a, 3)));
977
+ attr[4] += 5 * Math.log10(lbr[2] * lbr2[2] * AUNIT / EARTH_RADIUS);
978
+ }
979
+ /* MAG_MALLAMA_2018 formulas for Mercury through Neptune */
980
+ } else if (ipl === 2) { /* Mercury */
981
+ const a = attr[0];
982
+ const a2 = a * a; const a3 = a2 * a; const a4 = a3 * a; const a5 = a4 * a; const a6 = a5 * a;
983
+ attr[4] = -0.613 + a * 6.3280E-02 - a2 * 1.6336E-03 + a3 * 3.3644E-05 - a4 * 3.4265E-07 + a5 * 1.6893E-09 - a6 * 3.0334E-12;
984
+ attr[4] += 5 * Math.log10(lbr2[2] * lbr[2]);
985
+ } else if (ipl === 3) { /* Venus */
986
+ const a = attr[0];
987
+ const a2 = a * a; const a3 = a2 * a; const a4 = a3 * a;
988
+ if (a <= 163.7)
989
+ attr[4] = -4.384 - a * 1.044E-03 + a2 * 3.687E-04 - a3 * 2.814E-06 + a4 * 8.938E-09;
990
+ else
991
+ attr[4] = 236.05828 - a * 2.81914E+00 + a2 * 8.39034E-03;
992
+ attr[4] += 5 * Math.log10(lbr2[2] * lbr[2]);
993
+ if (attr[0] > 179.0)
994
+ serr2 = `magnitude value for Venus at phase angle i=${attr[0].toFixed(1)} is bad; formula is valid only for i < 179.0`;
995
+ } else if (ipl === 4) { /* Mars */
996
+ const a = attr[0];
997
+ const a2 = a * a;
998
+ if (a <= 50.0)
999
+ attr[4] = -1.601 + a * 0.02267 - a2 * 0.0001302;
1000
+ else
1001
+ attr[4] = -0.367 - a * 0.02573 + a2 * 0.0003445;
1002
+ attr[4] += 5 * Math.log10(lbr2[2] * lbr[2]);
1003
+ } else if (ipl === 5) { /* Jupiter */
1004
+ const a = attr[0];
1005
+ const a2 = a * a;
1006
+ attr[4] = -9.395 - a * 3.7E-04 + a2 * 6.16E-04;
1007
+ attr[4] += 5 * Math.log10(lbr2[2] * lbr[2]);
1008
+ } else if (ipl === 6) { /* Saturn */
1009
+ const a = attr[0];
1010
+ const T = (tjd - dt - J2000) / 36525.0;
1011
+ const inc = (28.075216 - 0.012998 * T + 0.000004 * T * T) * DEGTORAD;
1012
+ const om = (169.508470 + 1.394681 * T + 0.000412 * T * T) * DEGTORAD;
1013
+ let sinB = (Math.sin(inc) * Math.cos(lbr[1] * DEGTORAD)
1014
+ * Math.sin(lbr[0] * DEGTORAD - om)
1015
+ - Math.cos(inc) * Math.sin(lbr[1] * DEGTORAD));
1016
+ const sinB2 = (Math.sin(inc) * Math.cos(lbr2[1] * DEGTORAD)
1017
+ * Math.sin(lbr2[0] * DEGTORAD - om)
1018
+ - Math.cos(inc) * Math.sin(lbr2[1] * DEGTORAD));
1019
+ sinB = Math.abs(Math.sin((Math.asin(sinB) + Math.asin(sinB2)) / 2.0));
1020
+ attr[4] = -8.914 - 1.825 * sinB + 0.026 * a - 0.378 * sinB * Math.pow(2.7182818, -2.25 * a);
1021
+ attr[4] += 5 * Math.log10(lbr2[2] * lbr[2]);
1022
+ } else if (ipl === 7) { /* Uranus */
1023
+ const a = attr[0];
1024
+ const a2 = a * a;
1025
+ const fi_ = 0; // sub-Earth latitude in deg; ignored
1026
+ attr[4] = -7.110 - 8.4E-04 * fi_ + a * 6.587E-3 + a2 * 1.045E-4;
1027
+ attr[4] += 5 * Math.log10(lbr2[2] * lbr[2]);
1028
+ attr[4] -= 0.05;
1029
+ } else if (ipl === 8) { /* Neptune */
1030
+ if (tjd < 2444239.5) {
1031
+ attr[4] = -6.89;
1032
+ } else if (tjd <= 2451544.5) {
1033
+ attr[4] = -6.89 - 0.0055 * (tjd - 2444239.5) / 365.25;
1034
+ } else {
1035
+ attr[4] = -7.00;
1036
+ }
1037
+ attr[4] += 5 * Math.log10(lbr2[2] * lbr[2]);
1038
+ } else if (ipl < SE_CHIRON) {
1039
+ attr[4] = 5 * Math.log10(lbr2[2] * lbr[2])
1040
+ + MAG_ELEM[ipl][1] * attr[0] / 100.0
1041
+ + MAG_ELEM[ipl][2] * attr[0] * attr[0] / 10000.0
1042
+ + MAG_ELEM[ipl][3] * attr[0] * attr[0] * attr[0] / 1000000.0
1043
+ + MAG_ELEM[ipl][0];
1044
+ } else if (ipl < NMAG_ELEM || ipl > SE_AST_OFFSET) {
1045
+ /* other planets, asteroids */
1046
+ const ph1 = Math.pow(EULER, -3.33 * Math.pow(Math.tan(attr[0] * DEGTORAD / 2), 0.63));
1047
+ const ph2 = Math.pow(EULER, -1.87 * Math.pow(Math.tan(attr[0] * DEGTORAD / 2), 1.22));
1048
+ let me0: number, me1: number;
1049
+ if (ipl < NMAG_ELEM) {
1050
+ me0 = MAG_ELEM[ipl][0];
1051
+ me1 = MAG_ELEM[ipl][1];
1052
+ } else if (ipl === SE_AST_OFFSET + 1566) {
1053
+ /* Icarus */
1054
+ me0 = 16.9;
1055
+ me1 = 0.15;
1056
+ } else {
1057
+ me0 = swed.astH;
1058
+ me1 = swed.astG;
1059
+ }
1060
+ attr[4] = 5 * Math.log10(lbr2[2] * lbr[2])
1061
+ + me0
1062
+ - 2.5 * Math.log10((1 - me1) * ph1 + me1 * ph2);
1063
+ } else {
1064
+ /* fictitious bodies */
1065
+ attr[4] = 0;
1066
+ }
1067
+ }
1068
+ if (ipl !== SE_SUN && ipl !== SE_EARTH) {
1069
+ /*
1070
+ * elongation of planet
1071
+ */
1072
+ res = sweCalc(swed, tjd, SE_SUN, iflag | SEFLG_XYZ);
1073
+ if (res.flags === ERR) {
1074
+ if (serr) serr.value = res.serr;
1075
+ return { retval: ERR, attr };
1076
+ }
1077
+ const xx2Sun = Array.from(res.xx);
1078
+ res = sweCalc(swed, tjd, SE_SUN, iflag);
1079
+ if (res.flags === ERR) {
1080
+ if (serr) serr.value = res.serr;
1081
+ return { retval: ERR, attr };
1082
+ }
1083
+ attr[2] = Math.acos(swiDotProdUnit(xx, xx2Sun)) * RADTODEG;
1084
+ }
1085
+ /* horizontal parallax */
1086
+ if (ipl === SE_MOON) {
1087
+ /* geocentric horizontal parallax */
1088
+ res = sweCalc(swed, tjd, ipl, epheflag | SEFLG_TRUEPOS | SEFLG_EQUATORIAL | SEFLG_RADIANS);
1089
+ if (res.flags === ERR) {
1090
+ if (serr) serr.value = res.serr;
1091
+ return { retval: ERR, attr };
1092
+ }
1093
+ const xm = Array.from(res.xx);
1094
+ const sinhp = EARTH_RADIUS / xm[2] / AUNIT;
1095
+ attr[5] = Math.asin(sinhp) / DEGTORAD;
1096
+ /* topocentric horizontal parallax */
1097
+ if (iflag & SEFLG_TOPOCTR) {
1098
+ res = sweCalc(swed, tjd, ipl, epheflag | SEFLG_XYZ | SEFLG_TOPOCTR);
1099
+ if (res.flags === ERR) {
1100
+ if (serr) serr.value = res.serr;
1101
+ return { retval: ERR, attr };
1102
+ }
1103
+ const xmTopo = Array.from(res.xx);
1104
+ res = sweCalc(swed, tjd, ipl, epheflag | SEFLG_XYZ);
1105
+ if (res.flags === ERR) {
1106
+ if (serr) serr.value = res.serr;
1107
+ return { retval: ERR, attr };
1108
+ }
1109
+ const xxGeo = Array.from(res.xx);
1110
+ attr[5] = Math.acos(swiDotProdUnit(xmTopo, xxGeo)) / DEGTORAD;
1111
+ }
1112
+ }
1113
+ if (serr2 && serr) serr.value = serr2;
1114
+ return { retval: iflag, attr };
1115
+ }
1116
+
1117
+ /**
1118
+ * swe_pheno_ut: planetary phenomena for UT
1119
+ */
1120
+ export function swePhenoUt(
1121
+ swed: SweData, tjdUt: number, ipl: number, iflag: number,
1122
+ serr: { value: string } | null,
1123
+ ): { retval: number; attr: number[] } {
1124
+ let epheflag = iflag & SEFLG_EPHMASK;
1125
+ if (epheflag === 0) {
1126
+ epheflag = SEFLG_SWIEPH;
1127
+ iflag |= SEFLG_SWIEPH;
1128
+ }
1129
+ let deltat = sweDeltatEx(tjdUt, iflag, swed);
1130
+ let result = swePheno(swed, tjdUt + deltat, ipl, iflag, serr);
1131
+ /* if ephe required is not ephe returned, adjust delta t */
1132
+ if ((result.retval & SEFLG_EPHMASK) !== epheflag) {
1133
+ deltat = sweDeltatEx(tjdUt, result.retval, swed);
1134
+ result = swePheno(swed, tjdUt + deltat, ipl, iflag, serr);
1135
+ }
1136
+ return result;
1137
+ }
1138
+
1139
+ /* ---- Internal imports used by gauquelin method 0/1 ---- */
1140
+ import { swiEpsiln as swiEpsilnImport, swiNutation as swiNutationImport, sweSidtime0 as sweSidtime0Import } from './swephlib';
1141
+
1142
+ /* ==================================================================
1143
+ * Saros cycle data tables
1144
+ * ================================================================== */
1145
+
1146
+ interface SarosData { seriesNo: number; tstart: number }
1147
+
1148
+ const SAROS_DATA_SOLAR: SarosData[] = [
1149
+ {seriesNo: 0, tstart: 641886.5},
1150
+ {seriesNo: 1, tstart: 672214.5},
1151
+ {seriesNo: 2, tstart: 676200.5},
1152
+ {seriesNo: 3, tstart: 693357.5},
1153
+ {seriesNo: 4, tstart: 723685.5},
1154
+ {seriesNo: 5, tstart: 727671.5},
1155
+ {seriesNo: 6, tstart: 744829.5},
1156
+ {seriesNo: 7, tstart: 775157.5},
1157
+ {seriesNo: 8, tstart: 779143.5},
1158
+ {seriesNo: 9, tstart: 783131.5},
1159
+ {seriesNo: 10, tstart: 820044.5},
1160
+ {seriesNo: 11, tstart: 810859.5},
1161
+ {seriesNo: 12, tstart: 748993.5},
1162
+ {seriesNo: 13, tstart: 792492.5},
1163
+ {seriesNo: 14, tstart: 789892.5},
1164
+ {seriesNo: 15, tstart: 787294.5},
1165
+ {seriesNo: 16, tstart: 824207.5},
1166
+ {seriesNo: 17, tstart: 834779.5},
1167
+ {seriesNo: 18, tstart: 838766.5},
1168
+ {seriesNo: 19, tstart: 869094.5},
1169
+ {seriesNo: 20, tstart: 886251.5},
1170
+ {seriesNo: 21, tstart: 890238.5},
1171
+ {seriesNo: 22, tstart: 927151.5},
1172
+ {seriesNo: 23, tstart: 937722.5},
1173
+ {seriesNo: 24, tstart: 941709.5},
1174
+ {seriesNo: 25, tstart: 978623.5},
1175
+ {seriesNo: 26, tstart: 989194.5},
1176
+ {seriesNo: 27, tstart: 993181.5},
1177
+ {seriesNo: 28, tstart: 1023510.5},
1178
+ {seriesNo: 29, tstart: 1034081.5},
1179
+ {seriesNo: 30, tstart: 972214.5},
1180
+ {seriesNo: 31, tstart: 1061811.5},
1181
+ {seriesNo: 32, tstart: 1006529.5},
1182
+ {seriesNo: 33, tstart: 997345.5},
1183
+ {seriesNo: 34, tstart: 1021088.5},
1184
+ {seriesNo: 35, tstart: 1038245.5},
1185
+ {seriesNo: 36, tstart: 1042231.5},
1186
+ {seriesNo: 37, tstart: 1065974.5},
1187
+ {seriesNo: 38, tstart: 1089716.5},
1188
+ {seriesNo: 39, tstart: 1093703.5},
1189
+ {seriesNo: 40, tstart: 1117446.5},
1190
+ {seriesNo: 41, tstart: 1141188.5},
1191
+ {seriesNo: 42, tstart: 1145175.5},
1192
+ {seriesNo: 43, tstart: 1168918.5},
1193
+ {seriesNo: 44, tstart: 1192660.5},
1194
+ {seriesNo: 45, tstart: 1196647.5},
1195
+ {seriesNo: 46, tstart: 1220390.5},
1196
+ {seriesNo: 47, tstart: 1244132.5},
1197
+ {seriesNo: 48, tstart: 1234948.5},
1198
+ {seriesNo: 49, tstart: 1265277.5},
1199
+ {seriesNo: 50, tstart: 1282433.5},
1200
+ {seriesNo: 51, tstart: 1207395.5},
1201
+ {seriesNo: 52, tstart: 1217968.5},
1202
+ {seriesNo: 53, tstart: 1254881.5},
1203
+ {seriesNo: 54, tstart: 1252282.5},
1204
+ {seriesNo: 55, tstart: 1262855.5},
1205
+ {seriesNo: 56, tstart: 1293182.5},
1206
+ {seriesNo: 57, tstart: 1297169.5},
1207
+ {seriesNo: 58, tstart: 1314326.5},
1208
+ {seriesNo: 59, tstart: 1344654.5},
1209
+ {seriesNo: 60, tstart: 1348640.5},
1210
+ {seriesNo: 61, tstart: 1365798.5},
1211
+ {seriesNo: 62, tstart: 1396126.5},
1212
+ {seriesNo: 63, tstart: 1400112.5},
1213
+ {seriesNo: 64, tstart: 1417270.5},
1214
+ {seriesNo: 65, tstart: 1447598.5},
1215
+ {seriesNo: 66, tstart: 1444999.5},
1216
+ {seriesNo: 67, tstart: 1462157.5},
1217
+ {seriesNo: 68, tstart: 1492485.5},
1218
+ {seriesNo: 69, tstart: 1456959.5},
1219
+ {seriesNo: 70, tstart: 1421434.5},
1220
+ {seriesNo: 71, tstart: 1471518.5},
1221
+ {seriesNo: 72, tstart: 1455748.5},
1222
+ {seriesNo: 73, tstart: 1466320.5},
1223
+ {seriesNo: 74, tstart: 1496648.5},
1224
+ {seriesNo: 75, tstart: 1500634.5},
1225
+ {seriesNo: 76, tstart: 1511207.5},
1226
+ {seriesNo: 77, tstart: 1548120.5},
1227
+ {seriesNo: 78, tstart: 1552106.5},
1228
+ {seriesNo: 79, tstart: 1562679.5},
1229
+ {seriesNo: 80, tstart: 1599592.5},
1230
+ {seriesNo: 81, tstart: 1603578.5},
1231
+ {seriesNo: 82, tstart: 1614150.5},
1232
+ {seriesNo: 83, tstart: 1644479.5},
1233
+ {seriesNo: 84, tstart: 1655050.5},
1234
+ {seriesNo: 85, tstart: 1659037.5},
1235
+ {seriesNo: 86, tstart: 1695950.5},
1236
+ {seriesNo: 87, tstart: 1693351.5},
1237
+ {seriesNo: 88, tstart: 1631484.5},
1238
+ {seriesNo: 89, tstart: 1727666.5},
1239
+ {seriesNo: 90, tstart: 1672384.5},
1240
+ {seriesNo: 91, tstart: 1663200.5},
1241
+ {seriesNo: 92, tstart: 1693529.5},
1242
+ {seriesNo: 93, tstart: 1710685.5},
1243
+ {seriesNo: 94, tstart: 1714672.5},
1244
+ {seriesNo: 95, tstart: 1738415.5},
1245
+ {seriesNo: 96, tstart: 1755572.5},
1246
+ {seriesNo: 97, tstart: 1766144.5},
1247
+ {seriesNo: 98, tstart: 1789887.5},
1248
+ {seriesNo: 99, tstart: 1807044.5},
1249
+ {seriesNo: 100, tstart: 1817616.5},
1250
+ {seriesNo: 101, tstart: 1841359.5},
1251
+ {seriesNo: 102, tstart: 1858516.5},
1252
+ {seriesNo: 103, tstart: 1862502.5},
1253
+ {seriesNo: 104, tstart: 1892831.5},
1254
+ {seriesNo: 105, tstart: 1903402.5},
1255
+ {seriesNo: 106, tstart: 1887633.5},
1256
+ {seriesNo: 107, tstart: 1924547.5},
1257
+ {seriesNo: 108, tstart: 1921948.5},
1258
+ {seriesNo: 109, tstart: 1873251.5},
1259
+ {seriesNo: 110, tstart: 1890409.5},
1260
+ {seriesNo: 111, tstart: 1914151.5},
1261
+ {seriesNo: 112, tstart: 1918138.5},
1262
+ {seriesNo: 113, tstart: 1935296.5},
1263
+ {seriesNo: 114, tstart: 1959038.5},
1264
+ {seriesNo: 115, tstart: 1963024.5},
1265
+ {seriesNo: 116, tstart: 1986767.5},
1266
+ {seriesNo: 117, tstart: 2010510.5},
1267
+ {seriesNo: 118, tstart: 2014496.5},
1268
+ {seriesNo: 119, tstart: 2031654.5},
1269
+ {seriesNo: 120, tstart: 2061982.5},
1270
+ {seriesNo: 121, tstart: 2065968.5},
1271
+ {seriesNo: 122, tstart: 2083126.5},
1272
+ {seriesNo: 123, tstart: 2113454.5},
1273
+ {seriesNo: 124, tstart: 2104269.5},
1274
+ {seriesNo: 125, tstart: 2108256.5},
1275
+ {seriesNo: 126, tstart: 2151755.5},
1276
+ {seriesNo: 127, tstart: 2083302.5},
1277
+ {seriesNo: 128, tstart: 2080704.5},
1278
+ {seriesNo: 129, tstart: 2124203.5},
1279
+ {seriesNo: 130, tstart: 2121603.5},
1280
+ {seriesNo: 131, tstart: 2132176.5},
1281
+ {seriesNo: 132, tstart: 2162504.5},
1282
+ {seriesNo: 133, tstart: 2166490.5},
1283
+ {seriesNo: 134, tstart: 2177062.5},
1284
+ {seriesNo: 135, tstart: 2207390.5},
1285
+ {seriesNo: 136, tstart: 2217962.5},
1286
+ {seriesNo: 137, tstart: 2228534.5},
1287
+ {seriesNo: 138, tstart: 2258862.5},
1288
+ {seriesNo: 139, tstart: 2269434.5},
1289
+ {seriesNo: 140, tstart: 2273421.5},
1290
+ {seriesNo: 141, tstart: 2310334.5},
1291
+ {seriesNo: 142, tstart: 2314320.5},
1292
+ {seriesNo: 143, tstart: 2311722.5},
1293
+ {seriesNo: 144, tstart: 2355221.5},
1294
+ {seriesNo: 145, tstart: 2319695.5},
1295
+ {seriesNo: 146, tstart: 2284169.5},
1296
+ {seriesNo: 147, tstart: 2314498.5},
1297
+ {seriesNo: 148, tstart: 2325069.5},
1298
+ {seriesNo: 149, tstart: 2329056.5},
1299
+ {seriesNo: 150, tstart: 2352799.5},
1300
+ {seriesNo: 151, tstart: 2369956.5},
1301
+ {seriesNo: 152, tstart: 2380528.5},
1302
+ {seriesNo: 153, tstart: 2404271.5},
1303
+ {seriesNo: 154, tstart: 2421428.5},
1304
+ {seriesNo: 155, tstart: 2425414.5},
1305
+ {seriesNo: 156, tstart: 2455743.5},
1306
+ {seriesNo: 157, tstart: 2472900.5},
1307
+ {seriesNo: 158, tstart: 2476886.5},
1308
+ {seriesNo: 159, tstart: 2500629.5},
1309
+ {seriesNo: 160, tstart: 2517786.5},
1310
+ {seriesNo: 161, tstart: 2515187.5},
1311
+ {seriesNo: 162, tstart: 2545516.5},
1312
+ {seriesNo: 163, tstart: 2556087.5},
1313
+ {seriesNo: 164, tstart: 2487635.5},
1314
+ {seriesNo: 165, tstart: 2504793.5},
1315
+ {seriesNo: 166, tstart: 2535121.5},
1316
+ {seriesNo: 167, tstart: 2525936.5},
1317
+ {seriesNo: 168, tstart: 2543094.5},
1318
+ {seriesNo: 169, tstart: 2573422.5},
1319
+ {seriesNo: 170, tstart: 2577408.5},
1320
+ {seriesNo: 171, tstart: 2594566.5},
1321
+ {seriesNo: 172, tstart: 2624894.5},
1322
+ {seriesNo: 173, tstart: 2628880.5},
1323
+ {seriesNo: 174, tstart: 2646038.5},
1324
+ {seriesNo: 175, tstart: 2669780.5},
1325
+ {seriesNo: 176, tstart: 2673766.5},
1326
+ {seriesNo: 177, tstart: 2690924.5},
1327
+ {seriesNo: 178, tstart: 2721252.5},
1328
+ {seriesNo: 179, tstart: 2718653.5},
1329
+ {seriesNo: 180, tstart: 2729226.5},
1330
+ ];
1331
+
1332
+ const SAROS_DATA_LUNAR: SarosData[] = [
1333
+ {seriesNo: 1, tstart: 782437.5},
1334
+ {seriesNo: 2, tstart: 799593.5},
1335
+ {seriesNo: 3, tstart: 783824.5},
1336
+ {seriesNo: 4, tstart: 754884.5},
1337
+ {seriesNo: 5, tstart: 824724.5},
1338
+ {seriesNo: 6, tstart: 762857.5},
1339
+ {seriesNo: 7, tstart: 773430.5},
1340
+ {seriesNo: 8, tstart: 810343.5},
1341
+ {seriesNo: 9, tstart: 807743.5},
1342
+ {seriesNo: 10, tstart: 824901.5},
1343
+ {seriesNo: 11, tstart: 855229.5},
1344
+ {seriesNo: 12, tstart: 859215.5},
1345
+ {seriesNo: 13, tstart: 876373.5},
1346
+ {seriesNo: 14, tstart: 906701.5},
1347
+ {seriesNo: 15, tstart: 910687.5},
1348
+ {seriesNo: 16, tstart: 927845.5},
1349
+ {seriesNo: 17, tstart: 958173.5},
1350
+ {seriesNo: 18, tstart: 962159.5},
1351
+ {seriesNo: 19, tstart: 979317.5},
1352
+ {seriesNo: 20, tstart: 1009645.5},
1353
+ {seriesNo: 21, tstart: 1007046.5},
1354
+ {seriesNo: 22, tstart: 1017618.5},
1355
+ {seriesNo: 23, tstart: 1054531.5},
1356
+ {seriesNo: 24, tstart: 979493.5},
1357
+ {seriesNo: 25, tstart: 976895.5},
1358
+ {seriesNo: 26, tstart: 1020394.5},
1359
+ {seriesNo: 27, tstart: 1017794.5},
1360
+ {seriesNo: 28, tstart: 1028367.5},
1361
+ {seriesNo: 29, tstart: 1058695.5},
1362
+ {seriesNo: 30, tstart: 1062681.5},
1363
+ {seriesNo: 31, tstart: 1073253.5},
1364
+ {seriesNo: 32, tstart: 1110167.5},
1365
+ {seriesNo: 33, tstart: 1114153.5},
1366
+ {seriesNo: 34, tstart: 1131311.5},
1367
+ {seriesNo: 35, tstart: 1161639.5},
1368
+ {seriesNo: 36, tstart: 1165625.5},
1369
+ {seriesNo: 37, tstart: 1176197.5},
1370
+ {seriesNo: 38, tstart: 1213111.5},
1371
+ {seriesNo: 39, tstart: 1217097.5},
1372
+ {seriesNo: 40, tstart: 1221084.5},
1373
+ {seriesNo: 41, tstart: 1257997.5},
1374
+ {seriesNo: 42, tstart: 1255398.5},
1375
+ {seriesNo: 43, tstart: 1186946.5},
1376
+ {seriesNo: 44, tstart: 1283128.5},
1377
+ {seriesNo: 45, tstart: 1227845.5},
1378
+ {seriesNo: 46, tstart: 1225247.5},
1379
+ {seriesNo: 47, tstart: 1255575.5},
1380
+ {seriesNo: 48, tstart: 1272732.5},
1381
+ {seriesNo: 49, tstart: 1276719.5},
1382
+ {seriesNo: 50, tstart: 1307047.5},
1383
+ {seriesNo: 51, tstart: 1317619.5},
1384
+ {seriesNo: 52, tstart: 1328191.5},
1385
+ {seriesNo: 53, tstart: 1358519.5},
1386
+ {seriesNo: 54, tstart: 1375676.5},
1387
+ {seriesNo: 55, tstart: 1379663.5},
1388
+ {seriesNo: 56, tstart: 1409991.5},
1389
+ {seriesNo: 57, tstart: 1420562.5},
1390
+ {seriesNo: 58, tstart: 1424549.5},
1391
+ {seriesNo: 59, tstart: 1461463.5},
1392
+ {seriesNo: 60, tstart: 1465449.5},
1393
+ {seriesNo: 61, tstart: 1436509.5},
1394
+ {seriesNo: 62, tstart: 1493179.5},
1395
+ {seriesNo: 63, tstart: 1457653.5},
1396
+ {seriesNo: 64, tstart: 1435298.5},
1397
+ {seriesNo: 65, tstart: 1452456.5},
1398
+ {seriesNo: 66, tstart: 1476198.5},
1399
+ {seriesNo: 67, tstart: 1480184.5},
1400
+ {seriesNo: 68, tstart: 1503928.5},
1401
+ {seriesNo: 69, tstart: 1527670.5},
1402
+ {seriesNo: 70, tstart: 1531656.5},
1403
+ {seriesNo: 71, tstart: 1548814.5},
1404
+ {seriesNo: 72, tstart: 1579142.5},
1405
+ {seriesNo: 73, tstart: 1583128.5},
1406
+ {seriesNo: 74, tstart: 1600286.5},
1407
+ {seriesNo: 75, tstart: 1624028.5},
1408
+ {seriesNo: 76, tstart: 1628015.5},
1409
+ {seriesNo: 77, tstart: 1651758.5},
1410
+ {seriesNo: 78, tstart: 1675500.5},
1411
+ {seriesNo: 79, tstart: 1672901.5},
1412
+ {seriesNo: 80, tstart: 1683474.5},
1413
+ {seriesNo: 81, tstart: 1713801.5},
1414
+ {seriesNo: 82, tstart: 1645349.5},
1415
+ {seriesNo: 83, tstart: 1649336.5},
1416
+ {seriesNo: 84, tstart: 1686249.5},
1417
+ {seriesNo: 85, tstart: 1683650.5},
1418
+ {seriesNo: 86, tstart: 1694222.5},
1419
+ {seriesNo: 87, tstart: 1731136.5},
1420
+ {seriesNo: 88, tstart: 1735122.5},
1421
+ {seriesNo: 89, tstart: 1745694.5},
1422
+ {seriesNo: 90, tstart: 1776022.5},
1423
+ {seriesNo: 91, tstart: 1786594.5},
1424
+ {seriesNo: 92, tstart: 1797166.5},
1425
+ {seriesNo: 93, tstart: 1827494.5},
1426
+ {seriesNo: 94, tstart: 1838066.5},
1427
+ {seriesNo: 95, tstart: 1848638.5},
1428
+ {seriesNo: 96, tstart: 1878966.5},
1429
+ {seriesNo: 97, tstart: 1882952.5},
1430
+ {seriesNo: 98, tstart: 1880354.5},
1431
+ {seriesNo: 99, tstart: 1923853.5},
1432
+ {seriesNo: 100, tstart: 1881741.5},
1433
+ {seriesNo: 101, tstart: 1852801.5},
1434
+ {seriesNo: 102, tstart: 1889715.5},
1435
+ {seriesNo: 103, tstart: 1893701.5},
1436
+ {seriesNo: 104, tstart: 1897688.5},
1437
+ {seriesNo: 105, tstart: 1928016.5},
1438
+ {seriesNo: 106, tstart: 1938588.5},
1439
+ {seriesNo: 107, tstart: 1942575.5},
1440
+ {seriesNo: 108, tstart: 1972903.5},
1441
+ {seriesNo: 109, tstart: 1990059.5},
1442
+ {seriesNo: 110, tstart: 1994046.5},
1443
+ {seriesNo: 111, tstart: 2024375.5},
1444
+ {seriesNo: 112, tstart: 2034946.5},
1445
+ {seriesNo: 113, tstart: 2045518.5},
1446
+ {seriesNo: 114, tstart: 2075847.5},
1447
+ {seriesNo: 115, tstart: 2086418.5},
1448
+ {seriesNo: 116, tstart: 2083820.5},
1449
+ {seriesNo: 117, tstart: 2120733.5},
1450
+ {seriesNo: 118, tstart: 2124719.5},
1451
+ {seriesNo: 119, tstart: 2062852.5},
1452
+ {seriesNo: 120, tstart: 2086596.5},
1453
+ {seriesNo: 121, tstart: 2103752.5},
1454
+ {seriesNo: 122, tstart: 2094568.5},
1455
+ {seriesNo: 123, tstart: 2118311.5},
1456
+ {seriesNo: 124, tstart: 2142054.5},
1457
+ {seriesNo: 125, tstart: 2146040.5},
1458
+ {seriesNo: 126, tstart: 2169783.5},
1459
+ {seriesNo: 127, tstart: 2186940.5},
1460
+ {seriesNo: 128, tstart: 2197512.5},
1461
+ {seriesNo: 129, tstart: 2214670.5},
1462
+ {seriesNo: 130, tstart: 2238412.5},
1463
+ {seriesNo: 131, tstart: 2242398.5},
1464
+ {seriesNo: 132, tstart: 2266142.5},
1465
+ {seriesNo: 133, tstart: 2289884.5},
1466
+ {seriesNo: 134, tstart: 2287285.5},
1467
+ {seriesNo: 135, tstart: 2311028.5},
1468
+ {seriesNo: 136, tstart: 2334770.5},
1469
+ {seriesNo: 137, tstart: 2292659.5},
1470
+ {seriesNo: 138, tstart: 2276890.5},
1471
+ {seriesNo: 139, tstart: 2326974.5},
1472
+ {seriesNo: 140, tstart: 2304619.5},
1473
+ {seriesNo: 141, tstart: 2308606.5},
1474
+ {seriesNo: 142, tstart: 2345520.5},
1475
+ {seriesNo: 143, tstart: 2349506.5},
1476
+ {seriesNo: 144, tstart: 2360078.5},
1477
+ {seriesNo: 145, tstart: 2390406.5},
1478
+ {seriesNo: 146, tstart: 2394392.5},
1479
+ {seriesNo: 147, tstart: 2411550.5},
1480
+ {seriesNo: 148, tstart: 2441878.5},
1481
+ {seriesNo: 149, tstart: 2445864.5},
1482
+ {seriesNo: 150, tstart: 2456437.5},
1483
+ {seriesNo: 151, tstart: 2486765.5},
1484
+ {seriesNo: 152, tstart: 2490751.5},
1485
+ {seriesNo: 153, tstart: 2501323.5},
1486
+ {seriesNo: 154, tstart: 2538236.5},
1487
+ {seriesNo: 155, tstart: 2529052.5},
1488
+ {seriesNo: 156, tstart: 2473771.5},
1489
+ {seriesNo: 157, tstart: 2563367.5},
1490
+ {seriesNo: 158, tstart: 2508085.5},
1491
+ {seriesNo: 159, tstart: 2505486.5},
1492
+ {seriesNo: 160, tstart: 2542400.5},
1493
+ {seriesNo: 161, tstart: 2546386.5},
1494
+ {seriesNo: 162, tstart: 2556958.5},
1495
+ {seriesNo: 163, tstart: 2587287.5},
1496
+ {seriesNo: 164, tstart: 2597858.5},
1497
+ {seriesNo: 165, tstart: 2601845.5},
1498
+ {seriesNo: 166, tstart: 2632173.5},
1499
+ {seriesNo: 167, tstart: 2649330.5},
1500
+ {seriesNo: 168, tstart: 2653317.5},
1501
+ {seriesNo: 169, tstart: 2683645.5},
1502
+ {seriesNo: 170, tstart: 2694217.5},
1503
+ {seriesNo: 171, tstart: 2698203.5},
1504
+ {seriesNo: 172, tstart: 2728532.5},
1505
+ {seriesNo: 173, tstart: 2739103.5},
1506
+ {seriesNo: 174, tstart: 2683822.5},
1507
+ {seriesNo: 175, tstart: 2740492.5},
1508
+ {seriesNo: 176, tstart: 2724722.5},
1509
+ {seriesNo: 177, tstart: 2708952.5},
1510
+ {seriesNo: 178, tstart: 2732695.5},
1511
+ {seriesNo: 179, tstart: 2749852.5},
1512
+ {seriesNo: 180, tstart: 2753839.5},
1513
+ ];
1514
+
1515
+ /* ==================================================================
1516
+ * Small eclipse helpers
1517
+ * ================================================================== */
1518
+
1519
+ function findZero(y00: number, y11: number, y2: number, dx: number): { dxret: number; dxret2: number } | null {
1520
+ const c = y11;
1521
+ const b = (y2 - y00) / 2.0;
1522
+ const a = (y2 + y00) / 2.0 - c;
1523
+ if (b * b - 4 * a * c < 0) return null;
1524
+ const x1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
1525
+ const x2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
1526
+ return { dxret: (x1 - 1) * dx, dxret2: (x2 - 1) * dx };
1527
+ }
1528
+
1529
+ function calcPlanetStar(
1530
+ swed: SweData, tjdEt: number, ipl: number, starname: string | null,
1531
+ iflag: number,
1532
+ ): { retval: number; xx: Float64Array; serr: string } {
1533
+ if (starname !== null && starname !== '') {
1534
+ const res = sweFixstar(swed, starname, tjdEt, iflag);
1535
+ return { retval: res.flags, xx: res.xx, serr: res.serr || '' };
1536
+ } else {
1537
+ const res = sweCalc(swed, tjdEt, ipl, iflag);
1538
+ return { retval: res.flags, xx: res.xx, serr: res.serr };
1539
+ }
1540
+ }
1541
+
1542
+ function dotProd(x: number[] | Float64Array, y: number[] | Float64Array): number {
1543
+ return x[0] * y[0] + x[1] * y[1] + x[2] * y[2];
1544
+ }
1545
+
1546
+ /* ==================================================================
1547
+ * Phase 2: Core internal functions
1548
+ * ================================================================== */
1549
+
1550
+ function eclipseWhere(
1551
+ swed: SweData, tjdUt: number, ipl: number, starname: string | null,
1552
+ ifl: number,
1553
+ ): { retval: number; geopos: number[]; dcore: number[] } {
1554
+ const dcore = new Array(10).fill(0);
1555
+ const geopos = [0, 0];
1556
+ let retc = 0;
1557
+ const de = 6378140.0 / AUNIT;
1558
+ let earthobl = 1 - EARTH_OBLATENESS;
1559
+ const rmoon = RMOON;
1560
+ const dmoon = 2 * rmoon;
1561
+ let noEclipse = false;
1562
+ const iflag = SEFLG_SPEED | SEFLG_EQUATORIAL | ifl;
1563
+ const iflag2 = iflag | SEFLG_RADIANS;
1564
+ const iflagXyz = iflag | SEFLG_XYZ;
1565
+ const deltat = sweDeltatEx(tjdUt, ifl, swed);
1566
+ const tjd = tjdUt + deltat;
1567
+ /* moon in cartesian coordinates */
1568
+ let res = sweCalc(swed, tjd, SE_MOON, iflagXyz);
1569
+ if (res.flags === ERR) return { retval: ERR, geopos, dcore };
1570
+ const rm = Array.from(res.xx);
1571
+ /* moon in polar coordinates */
1572
+ res = sweCalc(swed, tjd, SE_MOON, iflag2);
1573
+ if (res.flags === ERR) return { retval: ERR, geopos, dcore };
1574
+ const lm = Array.from(res.xx);
1575
+ /* sun/star in cartesian coordinates */
1576
+ let res2 = calcPlanetStar(swed, tjd, ipl, starname, iflagXyz);
1577
+ if (res2.retval === ERR) return { retval: ERR, geopos, dcore };
1578
+ const rs = Array.from(res2.xx);
1579
+ /* sun/star in polar coordinates */
1580
+ res2 = calcPlanetStar(swed, tjd, ipl, starname, iflag2);
1581
+ if (res2.retval === ERR) return { retval: ERR, geopos, dcore };
1582
+ const ls = Array.from(res2.xx);
1583
+ /* save original positions */
1584
+ const rst = [rs[0], rs[1], rs[2]];
1585
+ const rmt = [rm[0], rm[1], rm[2]];
1586
+ const oe = swed.oec;
1587
+ let sidt: number;
1588
+ if (ifl & SEFLG_NONUT)
1589
+ sidt = sweSidtime0Import(swed, tjdUt, oe.eps * RADTODEG, 0) * 15 * DEGTORAD;
1590
+ else
1591
+ sidt = sweSidtime(swed, tjdUt) * 15 * DEGTORAD;
1592
+ /* radius of planet disk in AU */
1593
+ let drad: number;
1594
+ if (starname !== null && starname !== '')
1595
+ drad = 0;
1596
+ else if (ipl < NDIAM)
1597
+ drad = PLA_DIAM[ipl] / 2 / AUNIT;
1598
+ else if (ipl > SE_AST_OFFSET)
1599
+ drad = swed.astDiam / 2 * 1000 / AUNIT;
1600
+ else
1601
+ drad = 0;
1602
+ /* iter_where loop: 2 iterations for oblateness correction */
1603
+ for (let niter = 0; niter <= 1; niter++) {
1604
+ for (let i = 0; i <= 2; i++) { rs[i] = rst[i]; rm[i] = rmt[i]; }
1605
+ /* Account for oblateness: correct z coordinate */
1606
+ const lx = [lm[0], lm[1], lm[2]];
1607
+ swiPolcart(lx, rm);
1608
+ rm[2] /= earthobl;
1609
+ const dm = Math.sqrt(squareSum(rm));
1610
+ const lx2 = [ls[0], ls[1], ls[2]];
1611
+ swiPolcart(lx2, rs);
1612
+ rs[2] /= earthobl;
1613
+ /* sun - moon vector */
1614
+ const e = [0, 0, 0];
1615
+ const et = [0, 0, 0];
1616
+ for (let i = 0; i <= 2; i++) {
1617
+ e[i] = rm[i] - rs[i];
1618
+ et[i] = rmt[i] - rst[i];
1619
+ }
1620
+ const dsm = Math.sqrt(squareSum(e));
1621
+ const dsmt = Math.sqrt(squareSum(et));
1622
+ for (let i = 0; i <= 2; i++) {
1623
+ e[i] /= dsm;
1624
+ et[i] /= dsmt;
1625
+ }
1626
+ const sinf1 = (drad - rmoon) / dsm;
1627
+ const cosf1 = Math.sqrt(1 - sinf1 * sinf1);
1628
+ const sinf2 = (drad + rmoon) / dsm;
1629
+ const cosf2 = Math.sqrt(1 - sinf2 * sinf2);
1630
+ /* distance of moon from fundamental plane */
1631
+ const s0 = -dotProd(rm, e);
1632
+ /* distance of shadow axis from geocenter */
1633
+ const r0 = Math.sqrt(dm * dm - s0 * s0);
1634
+ /* diameter of core shadow on fundamental plane */
1635
+ const d0 = (s0 / dsm * (drad * 2 - dmoon) - dmoon) / cosf1;
1636
+ /* diameter of half-shadow on fundamental plane */
1637
+ const D0 = (s0 / dsm * (drad * 2 + dmoon) + dmoon) / cosf2;
1638
+ dcore[2] = r0;
1639
+ dcore[3] = d0;
1640
+ dcore[4] = D0;
1641
+ dcore[5] = cosf1;
1642
+ dcore[6] = cosf2;
1643
+ for (let i = 2; i < 5; i++) dcore[i] *= AUNIT / 1000.0;
1644
+ /* eclipse phase */
1645
+ retc = 0;
1646
+ if (de * cosf1 >= r0) {
1647
+ retc |= SE_ECL_CENTRAL;
1648
+ } else if (r0 <= de * cosf1 + Math.abs(d0) / 2) {
1649
+ retc |= SE_ECL_NONCENTRAL;
1650
+ } else if (r0 <= de * cosf2 + D0 / 2) {
1651
+ retc |= (SE_ECL_PARTIAL | SE_ECL_NONCENTRAL);
1652
+ } else {
1653
+ retc = 0;
1654
+ noEclipse = true;
1655
+ }
1656
+ /* distance of shadow point from fundamental plane */
1657
+ let d = s0 * s0 + de * de - dm * dm;
1658
+ d = d > 0 ? Math.sqrt(d) : 0;
1659
+ const s = s0 - d;
1660
+ /* geographic position of eclipse center */
1661
+ const xs = [0, 0, 0];
1662
+ for (let i = 0; i <= 2; i++) xs[i] = rm[i] + s * e[i];
1663
+ const xst = [xs[0], xs[1], xs[2]];
1664
+ xst[2] *= earthobl;
1665
+ swiCartpol(xst, xst);
1666
+ if (niter === 0) {
1667
+ const cosfi = Math.cos(xst[1]);
1668
+ const sinfi = Math.sin(xst[1]);
1669
+ const eobl = EARTH_OBLATENESS;
1670
+ const cc = 1 / Math.sqrt(cosfi * cosfi + (1 - eobl) * (1 - eobl) * sinfi * sinfi);
1671
+ const ss = (1 - eobl) * (1 - eobl) * cc;
1672
+ earthobl = ss;
1673
+ continue; /* next iteration */
1674
+ }
1675
+ swiPolcart(xst, xst);
1676
+ /* to longitude and latitude */
1677
+ swiCartpol(xs, xs);
1678
+ xs[0] -= sidt;
1679
+ xs[0] *= RADTODEG;
1680
+ xs[1] *= RADTODEG;
1681
+ xs[0] = sweDegnorm(xs[0]);
1682
+ if (xs[0] > 180) xs[0] -= 360;
1683
+ geopos[0] = xs[0];
1684
+ geopos[1] = xs[1];
1685
+ /* diameter of core shadow at place of maximum eclipse */
1686
+ const x = [0, 0, 0];
1687
+ for (let i = 0; i <= 2; i++) x[i] = rmt[i] - xst[i];
1688
+ const sDist = Math.sqrt(squareSum(x));
1689
+ dcore[0] = (sDist / dsmt * (drad * 2 - dmoon) - dmoon) * cosf1;
1690
+ dcore[0] *= AUNIT / 1000.0;
1691
+ dcore[1] = (sDist / dsmt * (drad * 2 + dmoon) + dmoon) * cosf2;
1692
+ dcore[1] *= AUNIT / 1000.0;
1693
+ if (!(retc & SE_ECL_PARTIAL) && !noEclipse) {
1694
+ if (dcore[0] > 0) retc |= SE_ECL_ANNULAR;
1695
+ else retc |= SE_ECL_TOTAL;
1696
+ }
1697
+ }
1698
+ return { retval: retc, geopos, dcore };
1699
+ }
1700
+
1701
+ function eclipseHow(
1702
+ swed: SweData, tjdUt: number, ipl: number, starname: string | null,
1703
+ ifl: number, geolon: number, geolat: number, geohgt: number,
1704
+ ): { retval: number; attr: number[] } {
1705
+ const attr = new Array(20).fill(0);
1706
+ let retc = 0;
1707
+ const iflag = SEFLG_EQUATORIAL | SEFLG_TOPOCTR | ifl;
1708
+ const iflagcart = iflag | SEFLG_XYZ;
1709
+ const geoposArr = [geolon, geolat, geohgt];
1710
+ const te = tjdUt + sweDeltatEx(tjdUt, ifl, swed);
1711
+ sweSetTopo(swed, geolon, geolat, geohgt);
1712
+ let res2 = calcPlanetStar(swed, te, ipl, starname, iflag);
1713
+ if (res2.retval === ERR) return { retval: ERR, attr };
1714
+ const ls = Array.from(res2.xx);
1715
+ let res = sweCalc(swed, te, SE_MOON, iflag);
1716
+ if (res.flags === ERR) return { retval: ERR, attr };
1717
+ const lm = Array.from(res.xx);
1718
+ res2 = calcPlanetStar(swed, te, ipl, starname, iflagcart);
1719
+ if (res2.retval === ERR) return { retval: ERR, attr };
1720
+ const xs = Array.from(res2.xx);
1721
+ res = sweCalc(swed, te, SE_MOON, iflagcart);
1722
+ if (res.flags === ERR) return { retval: ERR, attr };
1723
+ const xm = Array.from(res.xx);
1724
+ /* radius of planet disk in AU */
1725
+ let drad: number;
1726
+ if (starname !== null && starname !== '') drad = 0;
1727
+ else if (ipl < NDIAM) drad = PLA_DIAM[ipl] / 2 / AUNIT;
1728
+ else if (ipl > SE_AST_OFFSET) drad = swed.astDiam / 2 * 1000 / AUNIT;
1729
+ else drad = 0;
1730
+ /* azimuth and altitude */
1731
+ const xh = [0, 0, 0];
1732
+ sweAzalt(swed, tjdUt, SE_EQU2HOR, geoposArr, 0, 10, ls, xh);
1733
+ /* eclipse description */
1734
+ const rmoonDeg = Math.asin(RMOON / lm[2]) * RADTODEG;
1735
+ const rsunDeg = Math.asin(drad / ls[2]) * RADTODEG;
1736
+ const rsplusrm = rsunDeg + rmoonDeg;
1737
+ const rsminusrm = rsunDeg - rmoonDeg;
1738
+ const x1 = [0, 0, 0], x2 = [0, 0, 0];
1739
+ for (let i = 0; i < 3; i++) {
1740
+ x1[i] = xs[i] / ls[2];
1741
+ x2[i] = xm[i] / lm[2];
1742
+ }
1743
+ const dctr = Math.acos(swiDotProdUnit(x1, x2)) * RADTODEG;
1744
+ /* phase */
1745
+ if (dctr < rsminusrm) retc = SE_ECL_ANNULAR;
1746
+ else if (dctr < Math.abs(rsminusrm)) retc = SE_ECL_TOTAL;
1747
+ else if (dctr < rsplusrm) retc = SE_ECL_PARTIAL;
1748
+ else retc = 0;
1749
+ /* ratio of moon/sun diameters */
1750
+ attr[1] = rsunDeg > 0 ? rmoonDeg / rsunDeg : 0;
1751
+ /* magnitude: fraction of solar diameter covered */
1752
+ const lsunleft = -dctr + rsunDeg + rmoonDeg;
1753
+ attr[0] = rsunDeg > 0 ? lsunleft / rsunDeg / 2 : 1;
1754
+ /* obscuration: fraction of solar disc covered */
1755
+ const lsun = rsunDeg;
1756
+ const lmoon = rmoonDeg;
1757
+ const lctr = dctr;
1758
+ if (retc === 0 || lsun === 0) {
1759
+ attr[2] = 1;
1760
+ } else if (retc === SE_ECL_TOTAL || retc === SE_ECL_ANNULAR) {
1761
+ attr[2] = lmoon * lmoon / lsun / lsun;
1762
+ } else {
1763
+ let a = 2 * lctr * lmoon;
1764
+ let b = 2 * lctr * lsun;
1765
+ if (a < 1e-9) {
1766
+ attr[2] = lmoon * lmoon / lsun / lsun;
1767
+ } else {
1768
+ a = (lctr * lctr + lmoon * lmoon - lsun * lsun) / a;
1769
+ if (a > 1) a = 1; if (a < -1) a = -1;
1770
+ b = (lctr * lctr + lsun * lsun - lmoon * lmoon) / b;
1771
+ if (b > 1) b = 1; if (b < -1) b = -1;
1772
+ a = Math.acos(a);
1773
+ b = Math.acos(b);
1774
+ let sc1 = a * lmoon * lmoon / 2;
1775
+ let sc2 = b * lsun * lsun / 2;
1776
+ sc1 -= Math.cos(a) * Math.sin(a) * lmoon * lmoon / 2;
1777
+ sc2 -= Math.cos(b) * Math.sin(b) * lsun * lsun / 2;
1778
+ attr[2] = (sc1 + sc2) * 2 / PI / lsun / lsun;
1779
+ }
1780
+ }
1781
+ attr[7] = dctr;
1782
+ /* approximate minimum height for visibility */
1783
+ const hminAppr = -(34.4556 + (1.75 + 0.37) * Math.sqrt(geohgt)) / 60;
1784
+ if (xh[1] + rsunDeg + Math.abs(hminAppr) >= 0 && retc)
1785
+ retc |= SE_ECL_VISIBLE;
1786
+ attr[4] = xh[0]; /* azimuth */
1787
+ attr[5] = xh[1]; /* true altitude */
1788
+ attr[6] = xh[2]; /* apparent altitude */
1789
+ if (ipl === SE_SUN && (starname === null || starname === '')) {
1790
+ /* NASA magnitude */
1791
+ attr[8] = attr[0];
1792
+ if (retc & (SE_ECL_TOTAL | SE_ECL_ANNULAR))
1793
+ attr[8] = attr[1];
1794
+ /* saros series and member */
1795
+ let found = false;
1796
+ for (let i = 0; i < NSAROS_SOLAR; i++) {
1797
+ let d = (tjdUt - SAROS_DATA_SOLAR[i].tstart) / SAROS_CYCLE;
1798
+ if (d < 0 && d * SAROS_CYCLE > -2) d = 0.0000001;
1799
+ if (d < 0) continue;
1800
+ const j = Math.floor(d);
1801
+ if ((d - j) * SAROS_CYCLE < 2) {
1802
+ attr[9] = SAROS_DATA_SOLAR[i].seriesNo;
1803
+ attr[10] = j + 1;
1804
+ found = true;
1805
+ break;
1806
+ }
1807
+ const k = j + 1;
1808
+ if ((k - d) * SAROS_CYCLE < 2) {
1809
+ attr[9] = SAROS_DATA_SOLAR[i].seriesNo;
1810
+ attr[10] = k + 1;
1811
+ found = true;
1812
+ break;
1813
+ }
1814
+ }
1815
+ if (!found) {
1816
+ attr[9] = attr[10] = -99999999;
1817
+ }
1818
+ }
1819
+ return { retval: retc, attr };
1820
+ }
1821
+
1822
+ function lunEclipseHow(
1823
+ swed: SweData, tjdUt: number, ifl: number,
1824
+ ): { retval: number; attr: number[]; dcore: number[] } {
1825
+ const dcore = new Array(10).fill(0);
1826
+ const attr = new Array(20).fill(0);
1827
+ let retc = 0;
1828
+ const rmoon = RMOON;
1829
+ const dmoon = 2 * rmoon;
1830
+ const iflag = SEFLG_SPEED | SEFLG_EQUATORIAL | ifl | SEFLG_XYZ;
1831
+ const deltat = sweDeltatEx(tjdUt, ifl, swed);
1832
+ const tjd = tjdUt + deltat;
1833
+ /* moon in cartesian coordinates */
1834
+ let res = sweCalc(swed, tjd, SE_MOON, iflag);
1835
+ if (res.flags === ERR) return { retval: ERR, attr, dcore };
1836
+ const rm = Array.from(res.xx);
1837
+ const dm = Math.sqrt(squareSum(rm));
1838
+ /* sun in cartesian coordinates */
1839
+ res = sweCalc(swed, tjd, SE_SUN, iflag);
1840
+ if (res.flags === ERR) return { retval: ERR, attr, dcore };
1841
+ const rs = Array.from(res.xx);
1842
+ const ds = Math.sqrt(squareSum(rs));
1843
+ const x1 = [0, 0, 0], x2 = [0, 0, 0];
1844
+ for (let i = 0; i < 3; i++) {
1845
+ x1[i] = rs[i] / ds;
1846
+ x2[i] = rm[i] / dm;
1847
+ }
1848
+ const dctr = Math.acos(swiDotProdUnit(x1, x2)) * RADTODEG;
1849
+ /* selenocentric sun */
1850
+ for (let i = 0; i <= 2; i++) rs[i] -= rm[i];
1851
+ /* selenocentric earth */
1852
+ for (let i = 0; i <= 2; i++) rm[i] = -rm[i];
1853
+ /* sun - earth vector */
1854
+ const e = [0, 0, 0];
1855
+ for (let i = 0; i <= 2; i++) e[i] = rm[i] - rs[i];
1856
+ const dsm = Math.sqrt(squareSum(e));
1857
+ for (let i = 0; i <= 2; i++) e[i] /= dsm;
1858
+ const f1 = (RSUN_ECL - REARTH_ECL) / dsm;
1859
+ const cosf1 = Math.sqrt(1 - f1 * f1);
1860
+ const f2 = (RSUN_ECL + REARTH_ECL) / dsm;
1861
+ const cosf2 = Math.sqrt(1 - f2 * f2);
1862
+ /* distance of earth from fundamental plane */
1863
+ const s0 = -dotProd(rm, e);
1864
+ /* distance of shadow axis from selenocenter */
1865
+ const r0 = Math.sqrt(dm * dm - s0 * s0);
1866
+ /* diameter of core shadow (with atmosphere 1/50 factor) */
1867
+ let d0 = Math.abs(s0 / dsm * (DSUN - DEARTH_ECL) - DEARTH_ECL) * (1 + 1.0 / 50.0) / cosf1;
1868
+ let D0 = (s0 / dsm * (DSUN + DEARTH_ECL) + DEARTH_ECL) * (1 + 1.0 / 50.0) / cosf2;
1869
+ d0 /= cosf1;
1870
+ D0 /= cosf2;
1871
+ /* NASA agreement factors */
1872
+ d0 *= 0.99405;
1873
+ D0 *= 0.98813;
1874
+ dcore[0] = r0;
1875
+ dcore[1] = d0;
1876
+ dcore[2] = D0;
1877
+ dcore[3] = cosf1;
1878
+ dcore[4] = cosf2;
1879
+ /* phase and umbral magnitude */
1880
+ if (d0 / 2 >= r0 + rmoon / cosf1) {
1881
+ retc = SE_ECL_TOTAL;
1882
+ attr[0] = (d0 / 2 - r0 + rmoon) / dmoon;
1883
+ } else if (d0 / 2 >= r0 - rmoon / cosf1) {
1884
+ retc = SE_ECL_PARTIAL;
1885
+ attr[0] = (d0 / 2 - r0 + rmoon) / dmoon;
1886
+ } else if (D0 / 2 >= r0 - rmoon / cosf2) {
1887
+ retc = SE_ECL_PENUMBRAL;
1888
+ attr[0] = 0;
1889
+ }
1890
+ attr[8] = attr[0];
1891
+ /* penumbral magnitude */
1892
+ attr[1] = (D0 / 2 - r0 + rmoon) / dmoon;
1893
+ if (retc !== 0) attr[7] = 180 - Math.abs(dctr);
1894
+ /* saros series and member */
1895
+ let found = false;
1896
+ for (let i = 0; i < NSAROS_LUNAR; i++) {
1897
+ let d = (tjdUt - SAROS_DATA_LUNAR[i].tstart) / SAROS_CYCLE;
1898
+ if (d < 0 && d * SAROS_CYCLE > -2) d = 0.0000001;
1899
+ if (d < 0) continue;
1900
+ const j = Math.floor(d);
1901
+ if ((d - j) * SAROS_CYCLE < 2) {
1902
+ attr[9] = SAROS_DATA_LUNAR[i].seriesNo;
1903
+ attr[10] = j + 1;
1904
+ found = true; break;
1905
+ }
1906
+ const k = j + 1;
1907
+ if ((k - d) * SAROS_CYCLE < 2) {
1908
+ attr[9] = SAROS_DATA_LUNAR[i].seriesNo;
1909
+ attr[10] = k + 1;
1910
+ found = true; break;
1911
+ }
1912
+ }
1913
+ if (!found) attr[9] = attr[10] = -99999999;
1914
+ return { retval: retc, attr, dcore };
1915
+ }
1916
+
1917
+ /* ==================================================================
1918
+ * Phase 3: Contact time finders
1919
+ * ================================================================== */
1920
+
1921
+ function eclipseWhenLoc(
1922
+ swed: SweData, tjdStart: number, ifl: number, geopos: number[],
1923
+ backward: number,
1924
+ ): { retval: number; tret: number[]; attr: number[] } {
1925
+ const tret = new Array(10).fill(0);
1926
+ const attr = new Array(20).fill(0);
1927
+ let retflag = 0;
1928
+ let K = Math.floor((tjdStart - J2000) / 365.2425 * 12.3685);
1929
+ if (backward) K++; else K--;
1930
+ const iflag = SEFLG_EQUATORIAL | SEFLG_TOPOCTR | ifl;
1931
+ const iflagcart = iflag | SEFLG_XYZ;
1932
+ sweSetTopo(swed, geopos[0], geopos[1], geopos[2]);
1933
+ const twomin = 2.0 / 24.0 / 60.0;
1934
+ const tensec = 10.0 / 24.0 / 60.0 / 60.0;
1935
+ const twohr = 2.0 / 24.0;
1936
+ const tenmin = 10.0 / 24.0 / 60.0;
1937
+ for (;;) { /* next_try */
1938
+ retflag = 0;
1939
+ for (let i = 0; i <= 9; i++) tret[i] = 0;
1940
+ const T = K / 1236.85;
1941
+ const T2 = T * T, T3 = T2 * T, T4 = T3 * T;
1942
+ let Ff = sweDegnorm(160.7108 + 390.67050274 * K - 0.0016341 * T2 - 0.00000227 * T3 + 0.000000011 * T4);
1943
+ if (Ff > 180) Ff -= 180;
1944
+ if (Ff > 21 && Ff < 159) { if (backward) K--; else K++; continue; }
1945
+ let tjd = 2451550.09765 + 29.530588853 * K + 0.0001337 * T2 - 0.000000150 * T3 + 0.00000000073 * T4;
1946
+ let M = sweDegnorm(2.5534 + 29.10535669 * K - 0.0000218 * T2 - 0.00000011 * T3);
1947
+ let Mm = sweDegnorm(201.5643 + 385.81693528 * K + 0.1017438 * T2 + 0.00001239 * T3 + 0.000000058 * T4);
1948
+ const E = 1 - 0.002516 * T - 0.0000074 * T2;
1949
+ M *= DEGTORAD; Mm *= DEGTORAD;
1950
+ tjd = tjd - 0.4075 * Math.sin(Mm) + 0.1721 * E * Math.sin(M);
1951
+ sweSetTopo(swed, geopos[0], geopos[1], geopos[2]);
1952
+ let dtdiv = 2;
1953
+ let dtstart = 0.5;
1954
+ if (tjd < 1900000 || tjd > 2500000) dtstart = 2;
1955
+ for (let dt = dtstart; dt > 0.00001; dt /= dtdiv) {
1956
+ if (dt < 0.1) dtdiv = 3;
1957
+ const dc = [0, 0, 0];
1958
+ for (let i = 0, t = tjd - dt; i <= 2; i++, t += dt) {
1959
+ let r1 = sweCalc(swed, t, SE_SUN, iflagcart);
1960
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
1961
+ const xs = Array.from(r1.xx);
1962
+ r1 = sweCalc(swed, t, SE_SUN, iflag);
1963
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
1964
+ const ls = Array.from(r1.xx);
1965
+ r1 = sweCalc(swed, t, SE_MOON, iflagcart);
1966
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
1967
+ const xm = Array.from(r1.xx);
1968
+ r1 = sweCalc(swed, t, SE_MOON, iflag);
1969
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
1970
+ const lm = Array.from(r1.xx);
1971
+ const dm = Math.sqrt(squareSum(xm));
1972
+ const ds = Math.sqrt(squareSum(xs));
1973
+ const x1 = [xs[0] / ds, xs[1] / ds, xs[2] / ds];
1974
+ const x2 = [xm[0] / dm, xm[1] / dm, xm[2] / dm];
1975
+ dc[i] = Math.acos(swiDotProdUnit(x1, x2)) * RADTODEG;
1976
+ }
1977
+ const fm = findMaximum(dc[0], dc[1], dc[2], dt);
1978
+ tjd += fm.dxret + dt;
1979
+ }
1980
+ /* check if eclipse at this location */
1981
+ let r1 = sweCalc(swed, tjd, SE_SUN, iflagcart);
1982
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
1983
+ const xsFinal = Array.from(r1.xx);
1984
+ r1 = sweCalc(swed, tjd, SE_SUN, iflag);
1985
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
1986
+ const lsFinal = Array.from(r1.xx);
1987
+ r1 = sweCalc(swed, tjd, SE_MOON, iflagcart);
1988
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
1989
+ const xmFinal = Array.from(r1.xx);
1990
+ r1 = sweCalc(swed, tjd, SE_MOON, iflag);
1991
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
1992
+ const lmFinal = Array.from(r1.xx);
1993
+ let dctr = Math.acos(swiDotProdUnit(xsFinal, xmFinal)) * RADTODEG;
1994
+ let rmoon = Math.asin(RMOON / lmFinal[2]) * RADTODEG;
1995
+ let rsun = Math.asin(RSUN_ECL / lsFinal[2]) * RADTODEG;
1996
+ let rsplusrm = rsun + rmoon;
1997
+ let rsminusrm = rsun - rmoon;
1998
+ if (dctr > rsplusrm) { if (backward) K--; else K++; continue; }
1999
+ tret[0] = tjd - sweDeltatEx(tjd, ifl, swed);
2000
+ tret[0] = tjd - sweDeltatEx(tret[0], ifl, swed);
2001
+ if ((backward && tret[0] >= tjdStart - 0.0001) || (!backward && tret[0] <= tjdStart + 0.0001)) {
2002
+ if (backward) K--; else K++; continue;
2003
+ }
2004
+ if (dctr < rsminusrm) retflag = SE_ECL_ANNULAR;
2005
+ else if (dctr < Math.abs(rsminusrm)) retflag = SE_ECL_TOTAL;
2006
+ else if (dctr <= rsplusrm) retflag = SE_ECL_PARTIAL;
2007
+ const dctrmin = dctr;
2008
+ /* contacts 2 and 3 */
2009
+ if (dctr > Math.abs(rsminusrm)) {
2010
+ tret[2] = tret[3] = 0;
2011
+ } else {
2012
+ const dc = [0, 0, 0];
2013
+ dc[1] = Math.abs(rsminusrm) - dctrmin;
2014
+ for (let i = 0, t = tjd - twomin; i <= 2; i += 2, t = tjd + twomin) {
2015
+ r1 = sweCalc(swed, t, SE_SUN, iflagcart);
2016
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2017
+ const xs = Array.from(r1.xx);
2018
+ r1 = sweCalc(swed, t, SE_MOON, iflagcart);
2019
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2020
+ const xm = Array.from(r1.xx);
2021
+ const dm = Math.sqrt(squareSum(xm));
2022
+ const ds = Math.sqrt(squareSum(xs));
2023
+ rmoon = Math.asin(RMOON / dm) * RADTODEG * 0.99916;
2024
+ rsun = Math.asin(RSUN_ECL / ds) * RADTODEG;
2025
+ rsminusrm = rsun - rmoon;
2026
+ const x1 = [xs[0] / ds, xs[1] / ds, xs[2] / ds];
2027
+ const x2 = [xm[0] / dm, xm[1] / dm, xm[2] / dm];
2028
+ dctr = Math.acos(swiDotProdUnit(x1, x2)) * RADTODEG;
2029
+ dc[i] = Math.abs(rsminusrm) - dctr;
2030
+ }
2031
+ const fz = findZero(dc[0], dc[1], dc[2], twomin);
2032
+ if (fz) {
2033
+ tret[2] = tjd + fz.dxret + twomin;
2034
+ tret[3] = tjd + fz.dxret2 + twomin;
2035
+ }
2036
+ for (let m = 0, dt = tensec; m < 2; m++, dt /= 10) {
2037
+ for (let j = 2; j <= 3; j++) {
2038
+ r1 = sweCalc(swed, tret[j], SE_SUN, iflagcart | SEFLG_SPEED);
2039
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2040
+ const xs = Array.from(r1.xx);
2041
+ r1 = sweCalc(swed, tret[j], SE_MOON, iflagcart | SEFLG_SPEED);
2042
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2043
+ const xm = Array.from(r1.xx);
2044
+ const dc2 = [0, 0];
2045
+ for (let i = 0; i < 2; i++) {
2046
+ if (i === 1) { for (let k = 0; k < 3; k++) { xs[k] -= xs[k + 3] * dt; xm[k] -= xm[k + 3] * dt; } }
2047
+ const dm = Math.sqrt(squareSum(xm));
2048
+ const ds = Math.sqrt(squareSum(xs));
2049
+ rmoon = Math.asin(RMOON / dm) * RADTODEG * 0.99916;
2050
+ rsun = Math.asin(RSUN_ECL / ds) * RADTODEG;
2051
+ rsminusrm = rsun - rmoon;
2052
+ const x1 = [xs[0] / ds, xs[1] / ds, xs[2] / ds];
2053
+ const x2 = [xm[0] / dm, xm[1] / dm, xm[2] / dm];
2054
+ dctr = Math.acos(swiDotProdUnit(x1, x2)) * RADTODEG;
2055
+ dc2[i] = Math.abs(rsminusrm) - dctr;
2056
+ }
2057
+ const dt1 = -dc2[0] / ((dc2[0] - dc2[1]) / dt);
2058
+ tret[j] += dt1;
2059
+ }
2060
+ }
2061
+ tret[2] -= sweDeltatEx(tret[2], ifl, swed);
2062
+ tret[3] -= sweDeltatEx(tret[3], ifl, swed);
2063
+ }
2064
+ /* contacts 1 and 4 */
2065
+ {
2066
+ const dc = [0, 0, 0];
2067
+ dc[1] = rsplusrm - dctrmin;
2068
+ for (let i = 0, t = tjd - twohr; i <= 2; i += 2, t = tjd + twohr) {
2069
+ r1 = sweCalc(swed, t, SE_SUN, iflagcart);
2070
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2071
+ const xs = Array.from(r1.xx);
2072
+ r1 = sweCalc(swed, t, SE_MOON, iflagcart);
2073
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2074
+ const xm = Array.from(r1.xx);
2075
+ const dm = Math.sqrt(squareSum(xm));
2076
+ const ds = Math.sqrt(squareSum(xs));
2077
+ rmoon = Math.asin(RMOON / dm) * RADTODEG;
2078
+ rsun = Math.asin(RSUN_ECL / ds) * RADTODEG;
2079
+ rsplusrm = rsun + rmoon;
2080
+ const x1 = [xs[0] / ds, xs[1] / ds, xs[2] / ds];
2081
+ const x2 = [xm[0] / dm, xm[1] / dm, xm[2] / dm];
2082
+ dctr = Math.acos(swiDotProdUnit(x1, x2)) * RADTODEG;
2083
+ dc[i] = rsplusrm - dctr;
2084
+ }
2085
+ const fz = findZero(dc[0], dc[1], dc[2], twohr);
2086
+ if (fz) {
2087
+ tret[1] = tjd + fz.dxret + twohr;
2088
+ tret[4] = tjd + fz.dxret2 + twohr;
2089
+ }
2090
+ for (let m = 0, dt = tenmin; m < 3; m++, dt /= 10) {
2091
+ for (let j = 1; j <= 4; j += 3) {
2092
+ r1 = sweCalc(swed, tret[j], SE_SUN, iflagcart | SEFLG_SPEED);
2093
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2094
+ const xs = Array.from(r1.xx);
2095
+ r1 = sweCalc(swed, tret[j], SE_MOON, iflagcart | SEFLG_SPEED);
2096
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2097
+ const xm = Array.from(r1.xx);
2098
+ const dc2 = [0, 0];
2099
+ for (let i = 0; i < 2; i++) {
2100
+ if (i === 1) { for (let k = 0; k < 3; k++) { xs[k] -= xs[k + 3] * dt; xm[k] -= xm[k + 3] * dt; } }
2101
+ const dm = Math.sqrt(squareSum(xm));
2102
+ const ds = Math.sqrt(squareSum(xs));
2103
+ rmoon = Math.asin(RMOON / dm) * RADTODEG;
2104
+ rsun = Math.asin(RSUN_ECL / ds) * RADTODEG;
2105
+ rsplusrm = rsun + rmoon;
2106
+ const x1 = [xs[0] / ds, xs[1] / ds, xs[2] / ds];
2107
+ const x2 = [xm[0] / dm, xm[1] / dm, xm[2] / dm];
2108
+ dctr = Math.acos(swiDotProdUnit(x1, x2)) * RADTODEG;
2109
+ dc2[i] = Math.abs(rsplusrm) - dctr;
2110
+ }
2111
+ const dt1 = -dc2[0] / ((dc2[0] - dc2[1]) / dt);
2112
+ tret[j] += dt1;
2113
+ }
2114
+ }
2115
+ tret[1] -= sweDeltatEx(tret[1], ifl, swed);
2116
+ tret[4] -= sweDeltatEx(tret[4], ifl, swed);
2117
+ }
2118
+ /* visibility of eclipse phases */
2119
+ for (let i = 4; i >= 0; i--) {
2120
+ if (tret[i] === 0) continue;
2121
+ const how = eclipseHow(swed, tret[i], SE_SUN, null, ifl, geopos[0], geopos[1], geopos[2]);
2122
+ if (how.retval === ERR) return { retval: ERR, tret, attr };
2123
+ if (how.attr[6] > 0) {
2124
+ retflag |= SE_ECL_VISIBLE;
2125
+ if (i === 0) retflag |= SE_ECL_MAX_VISIBLE;
2126
+ else if (i === 1) retflag |= SE_ECL_1ST_VISIBLE;
2127
+ else if (i === 2) retflag |= SE_ECL_2ND_VISIBLE;
2128
+ else if (i === 3) retflag |= SE_ECL_3RD_VISIBLE;
2129
+ else if (i === 4) retflag |= SE_ECL_4TH_VISIBLE;
2130
+ }
2131
+ if (i === 0) { for (let j = 0; j < 20; j++) attr[j] = how.attr[j]; }
2132
+ }
2133
+ if (!(retflag & SE_ECL_VISIBLE)) { if (backward) K--; else K++; continue; }
2134
+ /* sunrise and sunset during eclipse */
2135
+ const riseRes = sweRiseTrans(swed, tret[1] - 0.001, SE_SUN, null, ifl, SE_CALC_RISE | SE_BIT_DISC_BOTTOM, geopos, 0, 0, null);
2136
+ if (riseRes.retval === ERR) return { retval: ERR, tret, attr };
2137
+ if (riseRes.retval === -2) return { retval: retflag, tret, attr };
2138
+ const tjdr = riseRes.tret;
2139
+ const setRes = sweRiseTrans(swed, tret[1] - 0.001, SE_SUN, null, ifl, SE_CALC_SET | SE_BIT_DISC_BOTTOM, geopos, 0, 0, null);
2140
+ if (setRes.retval === ERR) return { retval: ERR, tret, attr };
2141
+ if (setRes.retval === -2) return { retval: retflag, tret, attr };
2142
+ const tjds = setRes.tret;
2143
+ if (tjds < tret[1] || (tjds > tjdr && tjdr > tret[4])) {
2144
+ if (backward) K--; else K++; continue;
2145
+ }
2146
+ if (tjdr > tret[1] && tjdr < tret[4]) {
2147
+ tret[5] = tjdr;
2148
+ if (!(retflag & SE_ECL_MAX_VISIBLE)) {
2149
+ tret[0] = tjdr;
2150
+ const how = eclipseHow(swed, tret[5], SE_SUN, null, ifl, geopos[0], geopos[1], geopos[2]);
2151
+ if (how.retval === ERR) return { retval: ERR, tret, attr };
2152
+ retflag &= ~(SE_ECL_TOTAL | SE_ECL_ANNULAR | SE_ECL_PARTIAL);
2153
+ retflag |= (how.retval & (SE_ECL_TOTAL | SE_ECL_ANNULAR | SE_ECL_PARTIAL));
2154
+ }
2155
+ }
2156
+ if (tjds > tret[1] && tjds < tret[4]) {
2157
+ tret[6] = tjds;
2158
+ if (!(retflag & SE_ECL_MAX_VISIBLE)) {
2159
+ tret[0] = tjds;
2160
+ const how = eclipseHow(swed, tret[6], SE_SUN, null, ifl, geopos[0], geopos[1], geopos[2]);
2161
+ if (how.retval === ERR) return { retval: ERR, tret, attr };
2162
+ retflag &= ~(SE_ECL_TOTAL | SE_ECL_ANNULAR | SE_ECL_PARTIAL);
2163
+ retflag |= (how.retval & (SE_ECL_TOTAL | SE_ECL_ANNULAR | SE_ECL_PARTIAL));
2164
+ }
2165
+ }
2166
+ return { retval: retflag, tret, attr };
2167
+ }
2168
+ }
2169
+
2170
+ function occultWhenLoc(
2171
+ swed: SweData, tjdStart: number, ipl: number, starname: string | null,
2172
+ ifl: number, geopos: number[], backward: number,
2173
+ ): { retval: number; tret: number[]; attr: number[] } {
2174
+ const tret = new Array(10).fill(0);
2175
+ const attr = new Array(20).fill(0);
2176
+ let retflag = 0;
2177
+ const iflag = SEFLG_TOPOCTR | ifl;
2178
+ const iflaggeo = iflag & ~SEFLG_TOPOCTR;
2179
+ const iflagcart = iflag | SEFLG_XYZ;
2180
+ let direction = 1;
2181
+ const oneTry = backward & SE_ECL_ONE_TRY;
2182
+ let backwardBit = backward & 1;
2183
+ if (backwardBit) direction = -1;
2184
+ let stopAfterThis = false;
2185
+ sweSetTopo(swed, geopos[0], geopos[1], geopos[2]);
2186
+ const twomin = 2.0 / 24.0 / 60.0;
2187
+ const tensec = 10.0 / 24.0 / 60.0 / 60.0;
2188
+ const twohr = 2.0 / 24.0;
2189
+ const tenmin = 10.0 / 24.0 / 60.0;
2190
+ const dadd2 = 1;
2191
+ let t = tjdStart;
2192
+ let tjd = tjdStart;
2193
+ for (;;) { /* next_try */
2194
+ retflag = 0;
2195
+ for (let i = 0; i <= 9; i++) tret[i] = 0;
2196
+ let res2 = calcPlanetStar(swed, t, ipl, starname, iflaggeo);
2197
+ if (res2.retval === ERR) return { retval: ERR, tret, attr };
2198
+ let ls = Array.from(res2.xx);
2199
+ if (Math.abs(ls[1]) > 7 && starname !== null && starname !== '') {
2200
+ return { retval: ERR, tret, attr };
2201
+ }
2202
+ let res = sweCalc(swed, t, SE_MOON, iflaggeo);
2203
+ if (res.flags === ERR) return { retval: ERR, tret, attr };
2204
+ let lm = Array.from(res.xx);
2205
+ let dl = sweDegnorm(ls[0] - lm[0]);
2206
+ if (direction < 0) dl -= 360;
2207
+ while (Math.abs(dl) > 0.1) {
2208
+ t += dl / 13;
2209
+ res2 = calcPlanetStar(swed, t, ipl, starname, iflaggeo);
2210
+ if (res2.retval === ERR) return { retval: ERR, tret, attr };
2211
+ ls = Array.from(res2.xx);
2212
+ res = sweCalc(swed, t, SE_MOON, iflaggeo);
2213
+ if (res.flags === ERR) return { retval: ERR, tret, attr };
2214
+ lm = Array.from(res.xx);
2215
+ dl = sweDegnorm(ls[0] - lm[0]);
2216
+ if (dl > 180) dl -= 360;
2217
+ }
2218
+ tjd = t;
2219
+ let drad = Math.abs(ls[1] - lm[1]);
2220
+ if (drad > 2) {
2221
+ if (oneTry) { tret[0] = t + direction; return { retval: 0, tret, attr }; }
2222
+ t += direction * 20; tjd = t; continue;
2223
+ }
2224
+ /* radius of planet disk in AU */
2225
+ if (starname !== null && starname !== '') drad = 0;
2226
+ else if (ipl < NDIAM) drad = PLA_DIAM[ipl] / 2 / AUNIT;
2227
+ else if (ipl > SE_AST_OFFSET) drad = swed.astDiam / 2 * 1000 / AUNIT;
2228
+ else drad = 0;
2229
+ /* find time of minimum angular distance */
2230
+ let dtdiv = 2;
2231
+ let dtstart = dadd2;
2232
+ for (let dt = dtstart; dt > 0.00001; dt /= dtdiv) {
2233
+ if (dt < 0.01) dtdiv = 2;
2234
+ const dc = [0, 0, 0];
2235
+ for (let i = 0, tt = tjd - dt; i <= 2; i++, tt += dt) {
2236
+ res2 = calcPlanetStar(swed, tt, ipl, starname, iflagcart);
2237
+ if (res2.retval === ERR) return { retval: ERR, tret, attr };
2238
+ const xs = Array.from(res2.xx);
2239
+ res2 = calcPlanetStar(swed, tt, ipl, starname, iflag);
2240
+ if (res2.retval === ERR) return { retval: ERR, tret, attr };
2241
+ ls = Array.from(res2.xx);
2242
+ let r1 = sweCalc(swed, tt, SE_MOON, iflagcart);
2243
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2244
+ const xm = Array.from(r1.xx);
2245
+ r1 = sweCalc(swed, tt, SE_MOON, iflag);
2246
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2247
+ lm = Array.from(r1.xx);
2248
+ if (dt < 0.1 && Math.abs(ls[1] - lm[1]) > 2) {
2249
+ if (oneTry || stopAfterThis) { stopAfterThis = true; }
2250
+ else { t = tjd + direction * 20; tjd = t; break; }
2251
+ }
2252
+ dc[i] = Math.acos(swiDotProdUnit(xs, xm)) * RADTODEG;
2253
+ const rmoonTmp = Math.asin(RMOON / lm[2]) * RADTODEG;
2254
+ const rsunTmp = Math.asin(drad / ls[2]) * RADTODEG;
2255
+ dc[i] -= (rmoonTmp + rsunTmp);
2256
+ }
2257
+ if (dc[0] === 0 && dc[1] === 0 && dc[2] === 0) break; /* inner break triggered reloop */
2258
+ const fm = findMaximum(dc[0], dc[1], dc[2], dt);
2259
+ tjd += fm.dxret + dt;
2260
+ }
2261
+ if (stopAfterThis) { tret[0] = tjd + direction; return { retval: 0, tret, attr }; }
2262
+ res2 = calcPlanetStar(swed, tjd, ipl, starname, iflagcart);
2263
+ if (res2.retval === ERR) return { retval: ERR, tret, attr };
2264
+ const xsFinal = Array.from(res2.xx);
2265
+ res2 = calcPlanetStar(swed, tjd, ipl, starname, iflag);
2266
+ if (res2.retval === ERR) return { retval: ERR, tret, attr };
2267
+ ls = Array.from(res2.xx);
2268
+ let r1 = sweCalc(swed, tjd, SE_MOON, iflagcart);
2269
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2270
+ const xmFinal = Array.from(r1.xx);
2271
+ r1 = sweCalc(swed, tjd, SE_MOON, iflag);
2272
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2273
+ lm = Array.from(r1.xx);
2274
+ let dctr = Math.acos(swiDotProdUnit(xsFinal, xmFinal)) * RADTODEG;
2275
+ let rmoon = Math.asin(RMOON / lm[2]) * RADTODEG;
2276
+ let rsun = Math.asin(drad / ls[2]) * RADTODEG;
2277
+ let rsplusrm = rsun + rmoon;
2278
+ let rsminusrm = rsun - rmoon;
2279
+ if (dctr > rsplusrm) {
2280
+ if (oneTry) { tret[0] = tjd + direction; return { retval: 0, tret, attr }; }
2281
+ t = tjd + direction * 20; tjd = t; continue;
2282
+ }
2283
+ tret[0] = tjd - sweDeltatEx(tjd, ifl, swed);
2284
+ tret[0] = tjd - sweDeltatEx(tret[0], ifl, swed);
2285
+ if ((backwardBit && tret[0] >= tjdStart - 0.0001) || (!backwardBit && tret[0] <= tjdStart + 0.0001)) {
2286
+ if (oneTry) { tret[0] = tjd + direction; return { retval: 0, tret, attr }; }
2287
+ t = tjd + direction * 20; tjd = t; continue;
2288
+ }
2289
+ if (dctr < rsminusrm) retflag = SE_ECL_ANNULAR;
2290
+ else if (dctr < Math.abs(rsminusrm)) retflag = SE_ECL_TOTAL;
2291
+ else if (dctr <= rsplusrm) retflag = SE_ECL_PARTIAL;
2292
+ const dctrmin = dctr;
2293
+ /* contacts 2 and 3 */
2294
+ if (dctr > Math.abs(rsminusrm)) {
2295
+ tret[2] = tret[3] = 0;
2296
+ } else {
2297
+ const dc = [0, 0, 0];
2298
+ dc[1] = Math.abs(rsminusrm) - dctrmin;
2299
+ for (let i = 0, tt = tjd - twomin; i <= 2; i += 2, tt = tjd + twomin) {
2300
+ res2 = calcPlanetStar(swed, tt, ipl, starname, iflagcart);
2301
+ if (res2.retval === ERR) return { retval: ERR, tret, attr };
2302
+ const xs = Array.from(res2.xx);
2303
+ r1 = sweCalc(swed, tt, SE_MOON, iflagcart);
2304
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2305
+ const xm = Array.from(r1.xx);
2306
+ const dm = Math.sqrt(squareSum(xm));
2307
+ const ds = Math.sqrt(squareSum(xs));
2308
+ rmoon = Math.asin(RMOON / dm) * RADTODEG * 0.99916;
2309
+ rsun = Math.asin(drad / ds) * RADTODEG;
2310
+ rsminusrm = rsun - rmoon;
2311
+ const x1 = [xs[0] / ds, xs[1] / ds, xs[2] / ds];
2312
+ const x2 = [xm[0] / dm, xm[1] / dm, xm[2] / dm];
2313
+ dctr = Math.acos(swiDotProdUnit(x1, x2)) * RADTODEG;
2314
+ dc[i] = Math.abs(rsminusrm) - dctr;
2315
+ }
2316
+ const fz = findZero(dc[0], dc[1], dc[2], twomin);
2317
+ if (fz) { tret[2] = tjd + fz.dxret + twomin; tret[3] = tjd + fz.dxret2 + twomin; }
2318
+ for (let m = 0, dt = tensec; m < 2; m++, dt /= 10) {
2319
+ for (let j = 2; j <= 3; j++) {
2320
+ res2 = calcPlanetStar(swed, tret[j], ipl, starname, iflagcart | SEFLG_SPEED);
2321
+ if (res2.retval === ERR) return { retval: ERR, tret, attr };
2322
+ const xs = Array.from(res2.xx);
2323
+ r1 = sweCalc(swed, tret[j], SE_MOON, iflagcart | SEFLG_SPEED);
2324
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2325
+ const xm = Array.from(r1.xx);
2326
+ const dc2 = [0, 0];
2327
+ for (let i = 0; i < 2; i++) {
2328
+ if (i === 1) { for (let k = 0; k < 3; k++) { xs[k] -= xs[k + 3] * dt; xm[k] -= xm[k + 3] * dt; } }
2329
+ const dm = Math.sqrt(squareSum(xm));
2330
+ const ds = Math.sqrt(squareSum(xs));
2331
+ rmoon = Math.asin(RMOON / dm) * RADTODEG * 0.99916;
2332
+ rsun = Math.asin(drad / ds) * RADTODEG;
2333
+ rsminusrm = rsun - rmoon;
2334
+ const x1 = [xs[0] / ds, xs[1] / ds, xs[2] / ds];
2335
+ const x2 = [xm[0] / dm, xm[1] / dm, xm[2] / dm];
2336
+ dctr = Math.acos(swiDotProdUnit(x1, x2)) * RADTODEG;
2337
+ dc2[i] = Math.abs(rsminusrm) - dctr;
2338
+ }
2339
+ tret[j] += -dc2[0] / ((dc2[0] - dc2[1]) / dt);
2340
+ }
2341
+ }
2342
+ tret[2] -= sweDeltatEx(tret[2], ifl, swed);
2343
+ tret[3] -= sweDeltatEx(tret[3], ifl, swed);
2344
+ }
2345
+ /* contacts 1 and 4 */
2346
+ if (starname === null || starname === '') {
2347
+ const dc = [0, 0, 0];
2348
+ dc[1] = rsplusrm - dctrmin;
2349
+ for (let i = 0, tt = tjd - twohr; i <= 2; i += 2, tt = tjd + twohr) {
2350
+ res2 = calcPlanetStar(swed, tt, ipl, starname, iflagcart);
2351
+ if (res2.retval === ERR) return { retval: ERR, tret, attr };
2352
+ const xs = Array.from(res2.xx);
2353
+ r1 = sweCalc(swed, tt, SE_MOON, iflagcart);
2354
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2355
+ const xm = Array.from(r1.xx);
2356
+ const dm = Math.sqrt(squareSum(xm));
2357
+ const ds = Math.sqrt(squareSum(xs));
2358
+ rmoon = Math.asin(RMOON / dm) * RADTODEG;
2359
+ rsun = Math.asin(drad / ds) * RADTODEG;
2360
+ rsplusrm = rsun + rmoon;
2361
+ const x1 = [xs[0] / ds, xs[1] / ds, xs[2] / ds];
2362
+ const x2 = [xm[0] / dm, xm[1] / dm, xm[2] / dm];
2363
+ dctr = Math.acos(swiDotProdUnit(x1, x2)) * RADTODEG;
2364
+ dc[i] = rsplusrm - dctr;
2365
+ }
2366
+ const fz = findZero(dc[0], dc[1], dc[2], twohr);
2367
+ if (fz) { tret[1] = tjd + fz.dxret + twohr; tret[4] = tjd + fz.dxret2 + twohr; }
2368
+ for (let m = 0, dt = tenmin; m < 3; m++, dt /= 10) {
2369
+ for (let j = 1; j <= 4; j += 3) {
2370
+ res2 = calcPlanetStar(swed, tret[j], ipl, starname, iflagcart | SEFLG_SPEED);
2371
+ if (res2.retval === ERR) return { retval: ERR, tret, attr };
2372
+ const xs = Array.from(res2.xx);
2373
+ r1 = sweCalc(swed, tret[j], SE_MOON, iflagcart | SEFLG_SPEED);
2374
+ if (r1.flags === ERR) return { retval: ERR, tret, attr };
2375
+ const xm = Array.from(r1.xx);
2376
+ const dc2 = [0, 0];
2377
+ for (let i = 0; i < 2; i++) {
2378
+ if (i === 1) { for (let k = 0; k < 3; k++) { xs[k] -= xs[k + 3] * dt; xm[k] -= xm[k + 3] * dt; } }
2379
+ const dm = Math.sqrt(squareSum(xm));
2380
+ const ds = Math.sqrt(squareSum(xs));
2381
+ rmoon = Math.asin(RMOON / dm) * RADTODEG;
2382
+ rsun = Math.asin(drad / ds) * RADTODEG;
2383
+ rsplusrm = rsun + rmoon;
2384
+ const x1 = [xs[0] / ds, xs[1] / ds, xs[2] / ds];
2385
+ const x2 = [xm[0] / dm, xm[1] / dm, xm[2] / dm];
2386
+ dctr = Math.acos(swiDotProdUnit(x1, x2)) * RADTODEG;
2387
+ dc2[i] = Math.abs(rsplusrm) - dctr;
2388
+ }
2389
+ tret[j] += -dc2[0] / ((dc2[0] - dc2[1]) / dt);
2390
+ }
2391
+ }
2392
+ tret[1] -= sweDeltatEx(tret[1], ifl, swed);
2393
+ tret[4] -= sweDeltatEx(tret[4], ifl, swed);
2394
+ } else {
2395
+ /* fixed stars are point sources, contacts 1 and 4 = contacts 2 and 3 */
2396
+ tret[1] = tret[2];
2397
+ tret[4] = tret[3];
2398
+ }
2399
+ /* visibility of eclipse phases */
2400
+ for (let i = 4; i >= 0; i--) {
2401
+ if (tret[i] === 0) continue;
2402
+ const how = eclipseHow(swed, tret[i], ipl, starname, ifl, geopos[0], geopos[1], geopos[2]);
2403
+ if (how.retval === ERR) return { retval: ERR, tret, attr };
2404
+ if (how.attr[6] > 0) {
2405
+ retflag |= SE_ECL_VISIBLE;
2406
+ if (i === 0) retflag |= SE_ECL_MAX_VISIBLE;
2407
+ else if (i === 1) retflag |= SE_ECL_1ST_VISIBLE;
2408
+ else if (i === 2) retflag |= SE_ECL_2ND_VISIBLE;
2409
+ else if (i === 3) retflag |= SE_ECL_3RD_VISIBLE;
2410
+ else if (i === 4) retflag |= SE_ECL_4TH_VISIBLE;
2411
+ }
2412
+ if (i === 0) { for (let j = 0; j < 20; j++) attr[j] = how.attr[j]; }
2413
+ }
2414
+ if (!(retflag & SE_ECL_VISIBLE)) {
2415
+ if (oneTry) { tret[0] = tjd + direction; return { retval: 0, tret, attr }; }
2416
+ t = tjd + direction * 20; tjd = t; continue;
2417
+ }
2418
+ /* daylight checks */
2419
+ let riseRes = sweRiseTrans(swed, tret[1] - 0.1, ipl, starname, ifl, SE_CALC_RISE | SE_BIT_DISC_BOTTOM, geopos, 0, 0, null);
2420
+ if (riseRes.retval === ERR) return { retval: ERR, tret, attr };
2421
+ let setRes: { retval: number; tret: number } = { retval: 0, tret: 0 };
2422
+ if (riseRes.retval >= 0) {
2423
+ setRes = sweRiseTrans(swed, tret[1] - 0.1, ipl, starname, ifl, SE_CALC_SET | SE_BIT_DISC_BOTTOM, geopos, 0, 0, null);
2424
+ if (setRes.retval === ERR) return { retval: ERR, tret, attr };
2425
+ }
2426
+ if (riseRes.retval >= 0 && setRes.retval >= 0) {
2427
+ if (riseRes.tret > tret[1] && riseRes.tret < tret[4]) tret[5] = riseRes.tret;
2428
+ if (setRes.tret > tret[1] && setRes.tret < tret[4]) tret[6] = setRes.tret;
2429
+ }
2430
+ /* sun daylight check for beginning */
2431
+ riseRes = sweRiseTrans(swed, tret[1], SE_SUN, null, ifl, SE_CALC_RISE, geopos, 0, 0, null);
2432
+ if (riseRes.retval === ERR) return { retval: ERR, tret, attr };
2433
+ if (riseRes.retval >= 0) {
2434
+ setRes = sweRiseTrans(swed, tret[1], SE_SUN, null, ifl, SE_CALC_SET, geopos, 0, 0, null);
2435
+ if (setRes.retval === ERR) return { retval: ERR, tret, attr };
2436
+ if (setRes.retval >= 0 && setRes.tret < riseRes.tret)
2437
+ retflag |= SE_ECL_OCC_BEG_DAYLIGHT;
2438
+ }
2439
+ riseRes = sweRiseTrans(swed, tret[4], SE_SUN, null, ifl, SE_CALC_RISE, geopos, 0, 0, null);
2440
+ if (riseRes.retval === ERR) return { retval: ERR, tret, attr };
2441
+ if (riseRes.retval >= 0) {
2442
+ setRes = sweRiseTrans(swed, tret[4], SE_SUN, null, ifl, SE_CALC_SET, geopos, 0, 0, null);
2443
+ if (setRes.retval === ERR) return { retval: ERR, tret, attr };
2444
+ if (setRes.retval >= 0 && setRes.tret < riseRes.tret)
2445
+ retflag |= SE_ECL_OCC_END_DAYLIGHT;
2446
+ }
2447
+ return { retval: retflag, tret, attr };
2448
+ }
2449
+ }
2450
+
2451
+ /* ==================================================================
2452
+ * Phase 4: Global search functions
2453
+ * ================================================================== */
2454
+
2455
+ export function sweSolEclipseWhenGlob(
2456
+ swed: SweData, tjdStart: number, ifl: number, ifltype: number,
2457
+ backward: number,
2458
+ ): { retval: number; tret: number[] } {
2459
+ swiInitSwedIfStart(swed);
2460
+ const tret = new Array(10).fill(0);
2461
+ const de = 6378.140;
2462
+ const twohr = 2.0 / 24.0;
2463
+ const tenmin = 10.0 / 24.0 / 60.0;
2464
+ let retflag = 0;
2465
+ let direction = 1;
2466
+ let dontTimes = false;
2467
+ ifl &= SEFLG_EPHMASK;
2468
+ const iflag = SEFLG_EQUATORIAL | ifl;
2469
+ const iflagcart = iflag | SEFLG_XYZ;
2470
+ if (ifltype === (SE_ECL_PARTIAL | SE_ECL_CENTRAL)) return { retval: ERR, tret };
2471
+ if (ifltype === (SE_ECL_ANNULAR_TOTAL | SE_ECL_NONCENTRAL)) return { retval: ERR, tret };
2472
+ if (ifltype === 0)
2473
+ ifltype = SE_ECL_TOTAL | SE_ECL_ANNULAR | SE_ECL_PARTIAL | SE_ECL_ANNULAR_TOTAL | SE_ECL_NONCENTRAL | SE_ECL_CENTRAL;
2474
+ if (ifltype === SE_ECL_TOTAL || ifltype === SE_ECL_ANNULAR || ifltype === SE_ECL_ANNULAR_TOTAL)
2475
+ ifltype |= (SE_ECL_NONCENTRAL | SE_ECL_CENTRAL);
2476
+ if (ifltype === SE_ECL_PARTIAL) ifltype |= SE_ECL_NONCENTRAL;
2477
+ if (backward) direction = -1;
2478
+ let K = Math.floor((tjdStart - J2000) / 365.2425 * 12.3685);
2479
+ K -= direction;
2480
+ for (;;) { /* next_try */
2481
+ retflag = 0;
2482
+ dontTimes = false;
2483
+ for (let i = 0; i <= 9; i++) tret[i] = 0;
2484
+ const T = K / 1236.85;
2485
+ const T2 = T * T, T3 = T2 * T, T4 = T3 * T;
2486
+ let Ff = sweDegnorm(160.7108 + 390.67050274 * K - 0.0016341 * T2 - 0.00000227 * T3 + 0.000000011 * T4);
2487
+ if (Ff > 180) Ff -= 180;
2488
+ if (Ff > 21 && Ff < 159) { K += direction; continue; }
2489
+ let tjd = 2451550.09765 + 29.530588853 * K + 0.0001337 * T2 - 0.000000150 * T3 + 0.00000000073 * T4;
2490
+ let M = sweDegnorm(2.5534 + 29.10535669 * K - 0.0000218 * T2 - 0.00000011 * T3);
2491
+ let Mm = sweDegnorm(201.5643 + 385.81693528 * K + 0.1017438 * T2 + 0.00001239 * T3 + 0.000000058 * T4);
2492
+ const E = 1 - 0.002516 * T - 0.0000074 * T2;
2493
+ M *= DEGTORAD; Mm *= DEGTORAD;
2494
+ tjd = tjd - 0.4075 * Math.sin(Mm) + 0.1721 * E * Math.sin(M);
2495
+ let dtstart = 1;
2496
+ if (tjd < 2000000 || tjd > 2500000) dtstart = 5;
2497
+ let dtdiv = 4;
2498
+ for (let dt = dtstart; dt > 0.0001; dt /= dtdiv) {
2499
+ const dc = [0, 0, 0];
2500
+ for (let i = 0, t = tjd - dt; i <= 2; i++, t += dt) {
2501
+ let r1 = sweCalc(swed, t, SE_SUN, iflag);
2502
+ if (r1.flags === ERR) return { retval: ERR, tret };
2503
+ const ls = Array.from(r1.xx);
2504
+ r1 = sweCalc(swed, t, SE_MOON, iflag);
2505
+ if (r1.flags === ERR) return { retval: ERR, tret };
2506
+ const lm = Array.from(r1.xx);
2507
+ r1 = sweCalc(swed, t, SE_SUN, iflagcart);
2508
+ if (r1.flags === ERR) return { retval: ERR, tret };
2509
+ const xs = Array.from(r1.xx);
2510
+ r1 = sweCalc(swed, t, SE_MOON, iflagcart);
2511
+ if (r1.flags === ERR) return { retval: ERR, tret };
2512
+ const xm = Array.from(r1.xx);
2513
+ const xa = [xs[0] / ls[2], xs[1] / ls[2], xs[2] / ls[2]];
2514
+ const xb = [xm[0] / lm[2], xm[1] / lm[2], xm[2] / lm[2]];
2515
+ dc[i] = Math.acos(swiDotProdUnit(xa, xb)) * RADTODEG;
2516
+ const rmoon = Math.asin(RMOON / lm[2]) * RADTODEG;
2517
+ const rsun = Math.asin(RSUN_ECL / ls[2]) * RADTODEG;
2518
+ dc[i] -= (rmoon + rsun);
2519
+ }
2520
+ const fm = findMaximum(dc[0], dc[1], dc[2], dt);
2521
+ tjd += fm.dxret + dt;
2522
+ }
2523
+ let tjds = tjd - sweDeltatEx(tjd, ifl, swed);
2524
+ tjds = tjd - sweDeltatEx(tjds, ifl, swed);
2525
+ tjds = tjd - sweDeltatEx(tjds, ifl, swed);
2526
+ tjd = tjds;
2527
+ let ew = eclipseWhere(swed, tjd, SE_SUN, null, ifl);
2528
+ if (ew.retval === ERR) return { retval: ERR, tret };
2529
+ retflag = ew.retval;
2530
+ const geopos = ew.geopos;
2531
+ const attr = new Array(20).fill(0);
2532
+ let how = eclipseHow(swed, tjd, SE_SUN, null, ifl, geopos[0], geopos[1], 0);
2533
+ if (how.retval === ERR) return { retval: ERR, tret };
2534
+ if (how.retval === 0) { K += direction; continue; }
2535
+ tret[0] = tjd;
2536
+ if ((backward && tret[0] >= tjdStart - 0.0001) || (!backward && tret[0] <= tjdStart + 0.0001)) {
2537
+ K += direction; continue;
2538
+ }
2539
+ ew = eclipseWhere(swed, tjd, SE_SUN, null, ifl);
2540
+ if (ew.retval === ERR) return { retval: ERR, tret };
2541
+ retflag = ew.retval;
2542
+ let dcore = ew.dcore;
2543
+ if (retflag === 0) {
2544
+ retflag = SE_ECL_PARTIAL | SE_ECL_NONCENTRAL;
2545
+ tret[4] = tret[5] = tjd;
2546
+ dontTimes = true;
2547
+ }
2548
+ /* check eclipse type */
2549
+ if (!(ifltype & SE_ECL_NONCENTRAL) && (retflag & SE_ECL_NONCENTRAL)) { K += direction; continue; }
2550
+ if (!(ifltype & SE_ECL_CENTRAL) && (retflag & SE_ECL_CENTRAL)) { K += direction; continue; }
2551
+ if (!(ifltype & SE_ECL_ANNULAR) && (retflag & SE_ECL_ANNULAR)) { K += direction; continue; }
2552
+ if (!(ifltype & SE_ECL_PARTIAL) && (retflag & SE_ECL_PARTIAL)) { K += direction; continue; }
2553
+ if (!(ifltype & (SE_ECL_TOTAL | SE_ECL_ANNULAR_TOTAL)) && (retflag & SE_ECL_TOTAL)) { K += direction; continue; }
2554
+ if (!dontTimes) {
2555
+ /* contact times */
2556
+ let o: number;
2557
+ if (retflag & SE_ECL_PARTIAL) o = 0;
2558
+ else if (retflag & SE_ECL_NONCENTRAL) o = 1;
2559
+ else o = 2;
2560
+ const dta = twohr;
2561
+ const dtb = tenmin / 3.0;
2562
+ for (let n = 0; n <= o; n++) {
2563
+ let i1: number, i2: number;
2564
+ if (n === 0) { i1 = 2; i2 = 3; }
2565
+ else if (n === 1) { if (retflag & SE_ECL_PARTIAL) continue; i1 = 4; i2 = 5; }
2566
+ else { if (retflag & SE_ECL_NONCENTRAL) continue; i1 = 6; i2 = 7; }
2567
+ const dc = [0, 0, 0];
2568
+ for (let i = 0, t = tjd - dta; i <= 2; i++, t += dta) {
2569
+ ew = eclipseWhere(swed, t, SE_SUN, null, ifl);
2570
+ if (ew.retval === ERR) return { retval: ERR, tret };
2571
+ dcore = ew.dcore;
2572
+ if (n === 0) dc[i] = dcore[4] / 2 + de / dcore[5] - dcore[2];
2573
+ else if (n === 1) dc[i] = Math.abs(dcore[3]) / 2 + de / dcore[6] - dcore[2];
2574
+ else dc[i] = de / dcore[6] - dcore[2];
2575
+ }
2576
+ const fz = findZero(dc[0], dc[1], dc[2], dta);
2577
+ if (fz) {
2578
+ tret[i1!] = tjd + fz.dxret + dta;
2579
+ tret[i2!] = tjd + fz.dxret2 + dta;
2580
+ }
2581
+ for (let m = 0, dt = dtb; m < 3; m++, dt /= 3) {
2582
+ for (let j = i1!; j <= i2!; j += (i2! - i1!)) {
2583
+ const dc2 = [0, 0];
2584
+ for (let i = 0, t = tret[j] - dt; i < 2; i++, t += dt) {
2585
+ ew = eclipseWhere(swed, t, SE_SUN, null, ifl);
2586
+ if (ew.retval === ERR) return { retval: ERR, tret };
2587
+ dcore = ew.dcore;
2588
+ if (n === 0) dc2[i] = dcore[4] / 2 + de / dcore[5] - dcore[2];
2589
+ else if (n === 1) dc2[i] = Math.abs(dcore[3]) / 2 + de / dcore[6] - dcore[2];
2590
+ else dc2[i] = de / dcore[6] - dcore[2];
2591
+ }
2592
+ const dt1 = dc2[1] / ((dc2[1] - dc2[0]) / dt);
2593
+ tret[j] -= dt1;
2594
+ }
2595
+ }
2596
+ }
2597
+ /* annular-total eclipses */
2598
+ if (retflag & SE_ECL_TOTAL) {
2599
+ ew = eclipseWhere(swed, tret[0], SE_SUN, null, ifl);
2600
+ if (ew.retval === ERR) return { retval: ERR, tret };
2601
+ const dc0 = ew.dcore[0];
2602
+ ew = eclipseWhere(swed, tret[4], SE_SUN, null, ifl);
2603
+ if (ew.retval === ERR) return { retval: ERR, tret };
2604
+ const dc1 = ew.dcore[0];
2605
+ ew = eclipseWhere(swed, tret[5], SE_SUN, null, ifl);
2606
+ if (ew.retval === ERR) return { retval: ERR, tret };
2607
+ const dc2 = ew.dcore[0];
2608
+ if (dc0 * dc1 < 0 || dc0 * dc2 < 0) {
2609
+ retflag |= SE_ECL_ANNULAR_TOTAL;
2610
+ retflag &= ~SE_ECL_TOTAL;
2611
+ }
2612
+ }
2613
+ if (!(ifltype & SE_ECL_TOTAL) && (retflag & SE_ECL_TOTAL)) { K += direction; continue; }
2614
+ if (!(ifltype & SE_ECL_ANNULAR_TOTAL) && (retflag & SE_ECL_ANNULAR_TOTAL)) { K += direction; continue; }
2615
+ /* time of local apparent noon */
2616
+ const k2 = 2;
2617
+ const dc = [0, 0];
2618
+ for (let i = 0; i < 2; i++) {
2619
+ const j = i + k2;
2620
+ const tt = tret[j] + sweDeltatEx(tret[j], ifl, swed);
2621
+ let r1 = sweCalc(swed, tt, SE_SUN, iflag);
2622
+ if (r1.flags === ERR) return { retval: ERR, tret };
2623
+ const ls = Array.from(r1.xx);
2624
+ r1 = sweCalc(swed, tt, SE_MOON, iflag);
2625
+ if (r1.flags === ERR) return { retval: ERR, tret };
2626
+ const lm = Array.from(r1.xx);
2627
+ dc[i] = sweDegnorm(ls[0] - lm[0]);
2628
+ if (dc[i] > 180) dc[i] -= 360;
2629
+ }
2630
+ if (dc[0] * dc[1] >= 0) {
2631
+ tret[1] = 0;
2632
+ } else {
2633
+ let tjdNoon = tjds;
2634
+ let dt = 0.1;
2635
+ let dt1 = (tret[3] - tret[2]) / 2.0;
2636
+ if (dt1 < dt) dt = dt1 / 2.0;
2637
+ for (let j = 0; dt > 0.01; j++, dt /= 3) {
2638
+ const dc2 = [0, 0];
2639
+ for (let i = 0, t = tjdNoon; i <= 1; i++, t -= dt) {
2640
+ const tt = t + sweDeltatEx(t, ifl, swed);
2641
+ let r1 = sweCalc(swed, tt, SE_SUN, iflag);
2642
+ if (r1.flags === ERR) return { retval: ERR, tret };
2643
+ const ls = Array.from(r1.xx);
2644
+ r1 = sweCalc(swed, tt, SE_MOON, iflag);
2645
+ if (r1.flags === ERR) return { retval: ERR, tret };
2646
+ const lm = Array.from(r1.xx);
2647
+ dc2[i] = sweDegnorm(ls[0] - lm[0]);
2648
+ if (dc2[i] > 180) dc2[i] -= 360;
2649
+ if (dc2[i] > 180) dc2[i] -= 360;
2650
+ }
2651
+ const a = (dc2[1] - dc2[0]) / dt;
2652
+ if (a < 1e-10) break;
2653
+ dt1 = dc2[0] / a;
2654
+ tjdNoon += dt1;
2655
+ }
2656
+ tret[1] = tjdNoon;
2657
+ }
2658
+ }
2659
+ return { retval: retflag, tret };
2660
+ }
2661
+ }
2662
+
2663
+ export function sweLunOccultWhenGlob(
2664
+ swed: SweData, tjdStart: number, ipl: number, starname: string | null,
2665
+ ifl: number, ifltype: number, backward: number,
2666
+ ): { retval: number; tret: number[] } {
2667
+ swiInitSwedIfStart(swed);
2668
+ const tret = new Array(10).fill(0);
2669
+ const de = 6378.140;
2670
+ const twohr = 2.0 / 24.0;
2671
+ const tenmin = 10.0 / 24.0 / 60.0;
2672
+ let retflag = 0;
2673
+ let direction = 1;
2674
+ let dontTimes = false;
2675
+ const oneTry = backward & SE_ECL_ONE_TRY;
2676
+ if (ipl < 0) ipl = 0;
2677
+ if (ipl === SE_AST_OFFSET + 134340) ipl = SE_PLUTO;
2678
+ ifl &= SEFLG_EPHMASK;
2679
+ const iflag = SEFLG_EQUATORIAL | ifl;
2680
+ const iflagcart = iflag | SEFLG_XYZ;
2681
+ let backwardBit = backward & 1;
2682
+ if (ifltype === (SE_ECL_PARTIAL | SE_ECL_CENTRAL)) return { retval: ERR, tret };
2683
+ if (ipl !== SE_SUN) {
2684
+ const ifltype2 = ifltype & ~(SE_ECL_NONCENTRAL | SE_ECL_CENTRAL);
2685
+ if (ifltype2 === SE_ECL_ANNULAR || ifltype === SE_ECL_ANNULAR_TOTAL) return { retval: ERR, tret };
2686
+ }
2687
+ if (ipl !== SE_SUN && (ifltype & (SE_ECL_ANNULAR | SE_ECL_ANNULAR_TOTAL)))
2688
+ ifltype &= ~(SE_ECL_ANNULAR | SE_ECL_ANNULAR_TOTAL);
2689
+ if (ifltype === 0) {
2690
+ ifltype = SE_ECL_TOTAL | SE_ECL_PARTIAL | SE_ECL_NONCENTRAL | SE_ECL_CENTRAL;
2691
+ if (ipl === SE_SUN) ifltype |= (SE_ECL_ANNULAR | SE_ECL_ANNULAR_TOTAL);
2692
+ }
2693
+ if (ifltype & (SE_ECL_TOTAL | SE_ECL_ANNULAR | SE_ECL_ANNULAR_TOTAL))
2694
+ ifltype |= (SE_ECL_NONCENTRAL | SE_ECL_CENTRAL);
2695
+ if (ifltype & SE_ECL_PARTIAL) ifltype |= SE_ECL_NONCENTRAL;
2696
+ if (backwardBit) direction = -1;
2697
+ let t = tjdStart;
2698
+ let tjd = t;
2699
+ for (;;) { /* next_try */
2700
+ retflag = 0; dontTimes = false;
2701
+ for (let i = 0; i <= 9; i++) tret[i] = 0;
2702
+ let res2 = calcPlanetStar(swed, t, ipl, starname, ifl);
2703
+ if (res2.retval === ERR) return { retval: ERR, tret };
2704
+ let ls = Array.from(res2.xx);
2705
+ if (Math.abs(ls[1]) > 7 && starname !== null && starname !== '') return { retval: ERR, tret };
2706
+ let res = sweCalc(swed, t, SE_MOON, ifl);
2707
+ if (res.flags === ERR) return { retval: ERR, tret };
2708
+ let lm = Array.from(res.xx);
2709
+ let dl = sweDegnorm(ls[0] - lm[0]);
2710
+ if (direction < 0) dl -= 360;
2711
+ while (Math.abs(dl) > 0.1) {
2712
+ t += dl / 13;
2713
+ res2 = calcPlanetStar(swed, t, ipl, starname, ifl);
2714
+ if (res2.retval === ERR) return { retval: ERR, tret };
2715
+ ls = Array.from(res2.xx);
2716
+ res = sweCalc(swed, t, SE_MOON, ifl);
2717
+ if (res.flags === ERR) return { retval: ERR, tret };
2718
+ lm = Array.from(res.xx);
2719
+ dl = sweDegnorm(ls[0] - lm[0]);
2720
+ if (dl > 180) dl -= 360;
2721
+ }
2722
+ tjd = t;
2723
+ let drad = Math.abs(ls[1] - lm[1]);
2724
+ if (drad > 2) {
2725
+ if (oneTry) { tret[0] = t + direction; return { retval: 0, tret }; }
2726
+ t += direction * 20; tjd = t; continue;
2727
+ }
2728
+ if (starname !== null && starname !== '') drad = 0;
2729
+ else if (ipl < NDIAM) drad = PLA_DIAM[ipl] / 2 / AUNIT;
2730
+ else if (ipl > SE_AST_OFFSET) drad = swed.astDiam / 2 * 1000 / AUNIT;
2731
+ else drad = 0;
2732
+ const dadd2 = 1;
2733
+ let dtdiv = 3;
2734
+ for (let dt = dadd2; dt > 0.0001; dt /= dtdiv) {
2735
+ const dc = [0, 0, 0];
2736
+ for (let i = 0, tt = tjd - dt; i <= 2; i++, tt += dt) {
2737
+ res2 = calcPlanetStar(swed, tt, ipl, starname, iflag);
2738
+ if (res2.retval === ERR) return { retval: ERR, tret };
2739
+ const xsT = Array.from(res2.xx);
2740
+ res = sweCalc(swed, tt, SE_MOON, iflag);
2741
+ if (res.flags === ERR) return { retval: ERR, tret };
2742
+ const lmT = Array.from(res.xx);
2743
+ res2 = calcPlanetStar(swed, tt, ipl, starname, iflagcart);
2744
+ if (res2.retval === ERR) return { retval: ERR, tret };
2745
+ const xsC = Array.from(res2.xx);
2746
+ res = sweCalc(swed, tt, SE_MOON, iflagcart);
2747
+ if (res.flags === ERR) return { retval: ERR, tret };
2748
+ const xmC = Array.from(res.xx);
2749
+ dc[i] = Math.acos(swiDotProdUnit(xsC, xmC)) * RADTODEG;
2750
+ const rmoon = Math.asin(RMOON / lmT[2]) * RADTODEG;
2751
+ const rsun = Math.asin(drad / xsT[2]) * RADTODEG;
2752
+ dc[i] -= (rmoon + rsun);
2753
+ }
2754
+ const fm = findMaximum(dc[0], dc[1], dc[2], dt);
2755
+ tjd += fm.dxret + dt;
2756
+ }
2757
+ tjd -= sweDeltatEx(tjd, ifl, swed);
2758
+ const tjds = tjd;
2759
+ let ew = eclipseWhere(swed, tjd, ipl, starname, ifl);
2760
+ if (ew.retval === ERR) return { retval: ERR, tret };
2761
+ retflag = ew.retval;
2762
+ if (retflag === 0) {
2763
+ if (oneTry) { tret[0] = tjd; return { retval: 0, tret }; }
2764
+ t = tjd + direction * 20; tjd = t; continue;
2765
+ }
2766
+ tret[0] = tjd;
2767
+ if ((backwardBit && tret[0] >= tjdStart - 0.0001) || (!backwardBit && tret[0] <= tjdStart + 0.0001)) {
2768
+ t = tjd + direction * 20; tjd = t;
2769
+ if (oneTry) { tret[0] = tjd; return { retval: 0, tret }; }
2770
+ continue;
2771
+ }
2772
+ ew = eclipseWhere(swed, tjd, ipl, starname, ifl);
2773
+ if (ew.retval === ERR) return { retval: ERR, tret };
2774
+ retflag = ew.retval;
2775
+ let dcore = ew.dcore;
2776
+ if (retflag === 0) {
2777
+ retflag = SE_ECL_PARTIAL | SE_ECL_NONCENTRAL;
2778
+ tret[4] = tret[5] = tjd;
2779
+ dontTimes = true;
2780
+ }
2781
+ if (!(ifltype & SE_ECL_NONCENTRAL) && (retflag & SE_ECL_NONCENTRAL)) {
2782
+ t = tjd + direction * 20;
2783
+ if (oneTry) { tret[0] = tjd; return { retval: 0, tret }; }
2784
+ tjd = t; continue;
2785
+ }
2786
+ if (!(ifltype & SE_ECL_CENTRAL) && (retflag & SE_ECL_CENTRAL)) {
2787
+ t = tjd + direction * 20;
2788
+ if (oneTry) { tret[0] = tjd; return { retval: 0, tret }; }
2789
+ tjd = t; continue;
2790
+ }
2791
+ if (!(ifltype & SE_ECL_ANNULAR) && (retflag & SE_ECL_ANNULAR)) {
2792
+ t = tjd + direction * 20;
2793
+ if (oneTry) { tret[0] = tjd; return { retval: 0, tret }; }
2794
+ tjd = t; continue;
2795
+ }
2796
+ if (!(ifltype & SE_ECL_PARTIAL) && (retflag & SE_ECL_PARTIAL)) {
2797
+ t = tjd + direction * 20;
2798
+ if (oneTry) { tret[0] = tjd; return { retval: 0, tret }; }
2799
+ tjd = t; continue;
2800
+ }
2801
+ if (!(ifltype & (SE_ECL_TOTAL | SE_ECL_ANNULAR_TOTAL)) && (retflag & SE_ECL_TOTAL)) {
2802
+ t = tjd + direction * 20;
2803
+ if (oneTry) { tret[0] = tjd; return { retval: 0, tret }; }
2804
+ tjd = t; continue;
2805
+ }
2806
+ if (!dontTimes) {
2807
+ let o: number;
2808
+ if (retflag & SE_ECL_PARTIAL) o = 0;
2809
+ else if (retflag & SE_ECL_NONCENTRAL) o = 1;
2810
+ else o = 2;
2811
+ const dta = twohr;
2812
+ const dtb = tenmin;
2813
+ for (let n = 0; n <= o; n++) {
2814
+ let i1: number, i2: number;
2815
+ if (n === 0) { i1 = 2; i2 = 3; }
2816
+ else if (n === 1) { if (retflag & SE_ECL_PARTIAL) continue; i1 = 4; i2 = 5; }
2817
+ else { if (retflag & SE_ECL_NONCENTRAL) continue; i1 = 6; i2 = 7; }
2818
+ const dc = [0, 0, 0];
2819
+ for (let i = 0, tt = tjd - dta; i <= 2; i++, tt += dta) {
2820
+ ew = eclipseWhere(swed, tt, ipl, starname, ifl);
2821
+ if (ew.retval === ERR) return { retval: ERR, tret };
2822
+ dcore = ew.dcore;
2823
+ if (n === 0) dc[i] = dcore[4] / 2 + de / dcore[5] - dcore[2];
2824
+ else if (n === 1) dc[i] = Math.abs(dcore[3]) / 2 + de / dcore[6] - dcore[2];
2825
+ else dc[i] = de / dcore[6] - dcore[2];
2826
+ }
2827
+ const fz = findZero(dc[0], dc[1], dc[2], dta);
2828
+ if (fz) { tret[i1!] = tjd + fz.dxret + dta; tret[i2!] = tjd + fz.dxret2 + dta; }
2829
+ for (let m = 0, dt = dtb; m < 3; m++, dt /= 3) {
2830
+ for (let j = i1!; j <= i2!; j += (i2! - i1!)) {
2831
+ const dc2 = [0, 0];
2832
+ for (let i = 0, tt = tret[j] - dt; i < 2; i++, tt += dt) {
2833
+ ew = eclipseWhere(swed, tt, ipl, starname, ifl);
2834
+ if (ew.retval === ERR) return { retval: ERR, tret };
2835
+ dcore = ew.dcore;
2836
+ if (n === 0) dc2[i] = dcore[4] / 2 + de / dcore[5] - dcore[2];
2837
+ else if (n === 1) dc2[i] = Math.abs(dcore[3]) / 2 + de / dcore[6] - dcore[2];
2838
+ else dc2[i] = de / dcore[6] - dcore[2];
2839
+ }
2840
+ tret[j] -= dc2[1] / ((dc2[1] - dc2[0]) / dt);
2841
+ }
2842
+ }
2843
+ }
2844
+ /* annular-total check */
2845
+ if (retflag & SE_ECL_TOTAL) {
2846
+ ew = eclipseWhere(swed, tret[0], ipl, starname, ifl);
2847
+ if (ew.retval === ERR) return { retval: ERR, tret };
2848
+ const dc0 = ew.dcore[0];
2849
+ ew = eclipseWhere(swed, tret[4], ipl, starname, ifl);
2850
+ if (ew.retval === ERR) return { retval: ERR, tret };
2851
+ const dc1 = ew.dcore[0];
2852
+ ew = eclipseWhere(swed, tret[5], ipl, starname, ifl);
2853
+ if (ew.retval === ERR) return { retval: ERR, tret };
2854
+ const dc2 = ew.dcore[0];
2855
+ if (dc0 * dc1 < 0 || dc0 * dc2 < 0) {
2856
+ retflag |= SE_ECL_ANNULAR_TOTAL; retflag &= ~SE_ECL_TOTAL;
2857
+ }
2858
+ }
2859
+ if (!(ifltype & SE_ECL_TOTAL) && (retflag & SE_ECL_TOTAL)) {
2860
+ t = tjd + direction * 20;
2861
+ if (oneTry) { tret[0] = tjd; return { retval: 0, tret }; }
2862
+ tjd = t; continue;
2863
+ }
2864
+ if (!(ifltype & SE_ECL_ANNULAR_TOTAL) && (retflag & SE_ECL_ANNULAR_TOTAL)) {
2865
+ t = tjd + direction * 20;
2866
+ if (oneTry) { tret[0] = tjd; return { retval: 0, tret }; }
2867
+ tjd = t; continue;
2868
+ }
2869
+ /* time of local apparent noon */
2870
+ const k2 = 2;
2871
+ const dcN = [0, 0];
2872
+ for (let i = 0; i < 2; i++) {
2873
+ const j = i + k2;
2874
+ const tt = tret[j] + sweDeltatEx(tret[j], ifl, swed);
2875
+ res2 = calcPlanetStar(swed, tt, ipl, starname, iflag);
2876
+ if (res2.retval === ERR) return { retval: ERR, tret };
2877
+ ls = Array.from(res2.xx);
2878
+ res = sweCalc(swed, tt, SE_MOON, iflag);
2879
+ if (res.flags === ERR) return { retval: ERR, tret };
2880
+ lm = Array.from(res.xx);
2881
+ dcN[i] = sweDegnorm(ls[0] - lm[0]);
2882
+ if (dcN[i] > 180) dcN[i] -= 360;
2883
+ }
2884
+ if (dcN[0] * dcN[1] >= 0) {
2885
+ tret[1] = 0;
2886
+ } else {
2887
+ let tjdNoon = tjds;
2888
+ let dt = 0.1;
2889
+ let dt1 = (tret[3] - tret[2]) / 2.0;
2890
+ if (dt1 < dt) dt = dt1 / 2.0;
2891
+ for (let j = 0; dt > 0.01; j++, dt /= 3) {
2892
+ const dc2 = [0, 0];
2893
+ for (let i = 0, tt = tjdNoon; i <= 1; i++, tt -= dt) {
2894
+ const tte = tt + sweDeltatEx(tt, ifl, swed);
2895
+ res2 = calcPlanetStar(swed, tte, ipl, starname, iflag);
2896
+ if (res2.retval === ERR) return { retval: ERR, tret };
2897
+ ls = Array.from(res2.xx);
2898
+ res = sweCalc(swed, tte, SE_MOON, iflag);
2899
+ if (res.flags === ERR) return { retval: ERR, tret };
2900
+ lm = Array.from(res.xx);
2901
+ dc2[i] = sweDegnorm(ls[0] - lm[0]);
2902
+ if (dc2[i] > 180) dc2[i] -= 360;
2903
+ if (dc2[i] > 180) dc2[i] -= 360;
2904
+ }
2905
+ const a = (dc2[1] - dc2[0]) / dt;
2906
+ if (a < 1e-10) break;
2907
+ tjdNoon += dc2[0] / a;
2908
+ }
2909
+ tret[1] = tjdNoon;
2910
+ }
2911
+ }
2912
+ return { retval: retflag, tret };
2913
+ }
2914
+ }
2915
+
2916
+ export function sweLunEclipseWhen(
2917
+ swed: SweData, tjdStart: number, ifl: number, ifltype: number,
2918
+ backward: number,
2919
+ ): { retval: number; tret: number[] } {
2920
+ swiInitSwedIfStart(swed);
2921
+ const tret = new Array(10).fill(0);
2922
+ let retflag = 0;
2923
+ let direction = 1;
2924
+ const twohr = 2.0 / 24.0;
2925
+ const tenmin = 10.0 / 24.0 / 60.0;
2926
+ ifl &= SEFLG_EPHMASK;
2927
+ const iflag = SEFLG_EQUATORIAL | ifl;
2928
+ const iflagcart = iflag | SEFLG_XYZ;
2929
+ ifltype &= ~(SE_ECL_CENTRAL | SE_ECL_NONCENTRAL);
2930
+ if (ifltype & (SE_ECL_ANNULAR | SE_ECL_ANNULAR_TOTAL)) {
2931
+ ifltype &= ~(SE_ECL_ANNULAR | SE_ECL_ANNULAR_TOTAL);
2932
+ if (ifltype === 0) return { retval: ERR, tret };
2933
+ }
2934
+ if (ifltype === 0) ifltype = SE_ECL_TOTAL | SE_ECL_PENUMBRAL | SE_ECL_PARTIAL;
2935
+ if (backward) direction = -1;
2936
+ let K = Math.floor((tjdStart - J2000) / 365.2425 * 12.3685);
2937
+ K -= direction;
2938
+ for (;;) { /* next_try */
2939
+ retflag = 0;
2940
+ for (let i = 0; i <= 9; i++) tret[i] = 0;
2941
+ const kk = K + 0.5;
2942
+ const T = kk / 1236.85;
2943
+ const T2 = T * T, T3 = T2 * T, T4 = T3 * T;
2944
+ let F = sweDegnorm(160.7108 + 390.67050274 * kk - 0.0016341 * T2 - 0.00000227 * T3 + 0.000000011 * T4);
2945
+ let Ff = F;
2946
+ if (Ff > 180) Ff -= 180;
2947
+ if (Ff > 21 && Ff < 159) { K += direction; continue; }
2948
+ let tjd = 2451550.09765 + 29.530588853 * kk + 0.0001337 * T2 - 0.000000150 * T3 + 0.00000000073 * T4;
2949
+ let M = sweDegnorm(2.5534 + 29.10535669 * kk - 0.0000218 * T2 - 0.00000011 * T3);
2950
+ let Mm = sweDegnorm(201.5643 + 385.81693528 * kk + 0.1017438 * T2 + 0.00001239 * T3 + 0.000000058 * T4);
2951
+ let Om = sweDegnorm(124.7746 - 1.56375580 * kk + 0.0020691 * T2 + 0.00000215 * T3);
2952
+ const E = 1 - 0.002516 * T - 0.0000074 * T2;
2953
+ let A1 = sweDegnorm(299.77 + 0.107408 * kk - 0.009173 * T2);
2954
+ M *= DEGTORAD; Mm *= DEGTORAD; F *= DEGTORAD; Om *= DEGTORAD;
2955
+ const F1 = F - 0.02665 * Math.sin(Om) * DEGTORAD;
2956
+ A1 *= DEGTORAD;
2957
+ tjd = tjd - 0.4075 * Math.sin(Mm)
2958
+ + 0.1721 * E * Math.sin(M)
2959
+ + 0.0161 * Math.sin(2 * Mm)
2960
+ - 0.0097 * Math.sin(2 * F1)
2961
+ + 0.0073 * E * Math.sin(Mm - M)
2962
+ - 0.0050 * E * Math.sin(Mm + M)
2963
+ - 0.0023 * Math.sin(Mm - 2 * F1)
2964
+ + 0.0021 * E * Math.sin(2 * M)
2965
+ + 0.0012 * Math.sin(Mm + 2 * F1)
2966
+ + 0.0006 * E * Math.sin(2 * Mm + M)
2967
+ - 0.0004 * Math.sin(3 * Mm)
2968
+ - 0.0003 * E * Math.sin(M + 2 * F1)
2969
+ + 0.0003 * Math.sin(A1)
2970
+ - 0.0002 * E * Math.sin(M - 2 * F1)
2971
+ - 0.0002 * E * Math.sin(2 * Mm - M)
2972
+ - 0.0002 * Math.sin(Om);
2973
+ let dtstart = 0.1;
2974
+ if (tjd < 2100000 || tjd > 2500000) dtstart = 5;
2975
+ let dtdiv = 4;
2976
+ for (let dt = dtstart; dt > 0.001; dt /= dtdiv) {
2977
+ const dc = [0, 0, 0];
2978
+ for (let i = 0, t = tjd - dt; i <= 2; i++, t += dt) {
2979
+ let r1 = sweCalc(swed, t, SE_SUN, iflagcart);
2980
+ if (r1.flags === ERR) return { retval: ERR, tret };
2981
+ const xs = Array.from(r1.xx);
2982
+ r1 = sweCalc(swed, t, SE_MOON, iflagcart);
2983
+ if (r1.flags === ERR) return { retval: ERR, tret };
2984
+ const xm = Array.from(r1.xx);
2985
+ for (let m = 0; m < 3; m++) { xs[m] -= xm[m]; xm[m] = -xm[m]; }
2986
+ const ds = Math.sqrt(squareSum(xs));
2987
+ const dm = Math.sqrt(squareSum(xm));
2988
+ const xa = [xs[0] / ds, xs[1] / ds, xs[2] / ds];
2989
+ const xb = [xm[0] / dm, xm[1] / dm, xm[2] / dm];
2990
+ dc[i] = Math.acos(swiDotProdUnit(xa, xb)) * RADTODEG;
2991
+ const rearth = Math.asin(REARTH_ECL / dm) * RADTODEG;
2992
+ const rsun = Math.asin(RSUN_ECL / ds) * RADTODEG;
2993
+ dc[i] -= (rearth + rsun);
2994
+ }
2995
+ const fm = findMaximum(dc[0], dc[1], dc[2], dt);
2996
+ tjd += fm.dxret + dt;
2997
+ }
2998
+ let tjd2 = tjd - sweDeltatEx(tjd, ifl, swed);
2999
+ tjd2 = tjd - sweDeltatEx(tjd2, ifl, swed);
3000
+ tjd = tjd - sweDeltatEx(tjd2, ifl, swed);
3001
+ const leh = lunEclipseHow(swed, tjd, ifl);
3002
+ if (leh.retval === ERR) return { retval: ERR, tret };
3003
+ retflag = leh.retval;
3004
+ if (retflag === 0) { K += direction; continue; }
3005
+ tret[0] = tjd;
3006
+ if ((backward && tret[0] >= tjdStart - 0.0001) || (!backward && tret[0] <= tjdStart + 0.0001)) {
3007
+ K += direction; continue;
3008
+ }
3009
+ if (!(ifltype & SE_ECL_PENUMBRAL) && (retflag & SE_ECL_PENUMBRAL)) { K += direction; continue; }
3010
+ if (!(ifltype & SE_ECL_PARTIAL) && (retflag & SE_ECL_PARTIAL)) { K += direction; continue; }
3011
+ if (!(ifltype & SE_ECL_TOTAL) && (retflag & SE_ECL_TOTAL)) { K += direction; continue; }
3012
+ /* contact times */
3013
+ let o: number;
3014
+ if (retflag & SE_ECL_PENUMBRAL) o = 0;
3015
+ else if (retflag & SE_ECL_PARTIAL) o = 1;
3016
+ else o = 2;
3017
+ const dta = twohr;
3018
+ for (let n = 0; n <= o; n++) {
3019
+ let i1: number, i2: number;
3020
+ if (n === 0) { i1 = 6; i2 = 7; }
3021
+ else if (n === 1) { i1 = 2; i2 = 3; }
3022
+ else { i1 = 4; i2 = 5; }
3023
+ const dc = [0, 0, 0];
3024
+ for (let i = 0, t = tjd - dta; i <= 2; i++, t += dta) {
3025
+ const leh2 = lunEclipseHow(swed, t, ifl);
3026
+ if (leh2.retval === ERR) return { retval: ERR, tret };
3027
+ const dcore = leh2.dcore;
3028
+ if (n === 0) dc[i] = dcore[2] / 2 + RMOON / dcore[4] - dcore[0];
3029
+ else if (n === 1) dc[i] = dcore[1] / 2 + RMOON / dcore[3] - dcore[0];
3030
+ else dc[i] = dcore[1] / 2 - RMOON / dcore[3] - dcore[0];
3031
+ }
3032
+ const fz = findZero(dc[0], dc[1], dc[2], dta);
3033
+ let dtb = 0;
3034
+ if (fz) {
3035
+ dtb = (fz.dxret + dta) / 2;
3036
+ tret[i1] = tjd + fz.dxret + dta;
3037
+ tret[i2] = tjd + fz.dxret2 + dta;
3038
+ }
3039
+ for (let m = 0, dt = dtb / 2; m < 3; m++, dt /= 2) {
3040
+ for (let j = i1; j <= i2; j += (i2 - i1)) {
3041
+ const dc2 = [0, 0];
3042
+ for (let i = 0, t = tret[j] - dt; i < 2; i++, t += dt) {
3043
+ const leh2 = lunEclipseHow(swed, t, ifl);
3044
+ if (leh2.retval === ERR) return { retval: ERR, tret };
3045
+ const dcore = leh2.dcore;
3046
+ if (n === 0) dc2[i] = dcore[2] / 2 + RMOON / dcore[4] - dcore[0];
3047
+ else if (n === 1) dc2[i] = dcore[1] / 2 + RMOON / dcore[3] - dcore[0];
3048
+ else dc2[i] = dcore[1] / 2 - RMOON / dcore[3] - dcore[0];
3049
+ }
3050
+ tret[j] -= dc2[1] / ((dc2[1] - dc2[0]) / dt);
3051
+ }
3052
+ }
3053
+ }
3054
+ return { retval: retflag, tret };
3055
+ }
3056
+ }
3057
+
3058
+ /* ==================================================================
3059
+ * Phase 5: Public wrapper functions
3060
+ * ================================================================== */
3061
+
3062
+ export function sweSolEclipseWhere(
3063
+ swed: SweData, tjdUt: number, ifl: number,
3064
+ ): { retval: number; geopos: number[]; attr: number[] } {
3065
+ swiInitSwedIfStart(swed);
3066
+ ifl &= SEFLG_EPHMASK;
3067
+ const ew = eclipseWhere(swed, tjdUt, SE_SUN, null, ifl);
3068
+ if (ew.retval < 0) return { retval: ew.retval, geopos: ew.geopos, attr: new Array(20).fill(0) };
3069
+ const retflag = ew.retval;
3070
+ const how = eclipseHow(swed, tjdUt, SE_SUN, null, ifl, ew.geopos[0], ew.geopos[1], 0);
3071
+ if (how.retval === ERR) return { retval: ERR, geopos: ew.geopos, attr: how.attr };
3072
+ how.attr[3] = ew.dcore[0];
3073
+ return { retval: retflag, geopos: ew.geopos, attr: how.attr };
3074
+ }
3075
+
3076
+ export function sweSolEclipseHow(
3077
+ swed: SweData, tjdUt: number, ifl: number, geopos: number[],
3078
+ ): { retval: number; attr: number[] } {
3079
+ swiInitSwedIfStart(swed);
3080
+ if (geopos[2] < SEI_ECL_GEOALT_MIN || geopos[2] > SEI_ECL_GEOALT_MAX) return { retval: ERR, attr: new Array(20).fill(0) };
3081
+ ifl &= SEFLG_EPHMASK;
3082
+ const how = eclipseHow(swed, tjdUt, SE_SUN, null, ifl, geopos[0], geopos[1], geopos[2]);
3083
+ if (how.retval === ERR) return how;
3084
+ let retflag = how.retval;
3085
+ const ew = eclipseWhere(swed, tjdUt, SE_SUN, null, ifl);
3086
+ if (ew.retval === ERR) return { retval: ERR, attr: how.attr };
3087
+ if (retflag) retflag |= (ew.retval & (SE_ECL_CENTRAL | SE_ECL_NONCENTRAL));
3088
+ how.attr[3] = ew.dcore[0];
3089
+ /* sun azimuth/altitude at geopos */
3090
+ sweSetTopo(swed, geopos[0], geopos[1], geopos[2]);
3091
+ const res = sweCalcUt(swed, tjdUt, SE_SUN, ifl | SEFLG_TOPOCTR | SEFLG_EQUATORIAL);
3092
+ if (res.flags === ERR) return { retval: ERR, attr: how.attr };
3093
+ const xaz = [0, 0, 0];
3094
+ sweAzalt(swed, tjdUt, SE_EQU2HOR, geopos, 0, 10, Array.from(res.xx), xaz);
3095
+ how.attr[4] = xaz[0]; how.attr[5] = xaz[1]; how.attr[6] = xaz[2];
3096
+ if (xaz[2] <= 0) retflag = 0;
3097
+ if (retflag === 0) {
3098
+ for (let i = 0; i <= 3; i++) how.attr[i] = 0;
3099
+ for (let i = 8; i <= 10; i++) how.attr[i] = 0;
3100
+ }
3101
+ return { retval: retflag, attr: how.attr };
3102
+ }
3103
+
3104
+ export function sweSolEclipseWhenLoc(
3105
+ swed: SweData, tjdStart: number, ifl: number, geopos: number[],
3106
+ backward: number,
3107
+ ): { retval: number; tret: number[]; attr: number[] } {
3108
+ swiInitSwedIfStart(swed);
3109
+ if (geopos[2] < SEI_ECL_GEOALT_MIN || geopos[2] > SEI_ECL_GEOALT_MAX)
3110
+ return { retval: ERR, tret: new Array(10).fill(0), attr: new Array(20).fill(0) };
3111
+ ifl &= SEFLG_EPHMASK;
3112
+ const loc = eclipseWhenLoc(swed, tjdStart, ifl, geopos, backward);
3113
+ if (loc.retval <= 0) return loc;
3114
+ let retflag = loc.retval;
3115
+ const ew = eclipseWhere(swed, loc.tret[0], SE_SUN, null, ifl);
3116
+ if (ew.retval === ERR) return { retval: ERR, tret: loc.tret, attr: loc.attr };
3117
+ retflag |= (ew.retval & SE_ECL_NONCENTRAL);
3118
+ loc.attr[3] = ew.dcore[0];
3119
+ return { retval: retflag, tret: loc.tret, attr: loc.attr };
3120
+ }
3121
+
3122
+ export function sweLunOccultWhere(
3123
+ swed: SweData, tjdUt: number, ipl: number, starname: string | null, ifl: number,
3124
+ ): { retval: number; geopos: number[]; attr: number[] } {
3125
+ swiInitSwedIfStart(swed);
3126
+ if (ipl < 0) ipl = 0;
3127
+ ifl &= SEFLG_EPHMASK;
3128
+ if (ipl === SE_AST_OFFSET + 134340) ipl = SE_PLUTO;
3129
+ const ew = eclipseWhere(swed, tjdUt, ipl, starname, ifl);
3130
+ if (ew.retval < 0) return { retval: ew.retval, geopos: ew.geopos, attr: new Array(20).fill(0) };
3131
+ const retflag = ew.retval;
3132
+ const how = eclipseHow(swed, tjdUt, ipl, starname, ifl, ew.geopos[0], ew.geopos[1], 0);
3133
+ if (how.retval === ERR) return { retval: ERR, geopos: ew.geopos, attr: how.attr };
3134
+ how.attr[3] = ew.dcore[0];
3135
+ return { retval: retflag, geopos: ew.geopos, attr: how.attr };
3136
+ }
3137
+
3138
+ export function sweLunOccultWhenLoc(
3139
+ swed: SweData, tjdStart: number, ipl: number, starname: string | null,
3140
+ ifl: number, geopos: number[], backward: number,
3141
+ ): { retval: number; tret: number[]; attr: number[] } {
3142
+ swiInitSwedIfStart(swed);
3143
+ if (geopos[2] < SEI_ECL_GEOALT_MIN || geopos[2] > SEI_ECL_GEOALT_MAX)
3144
+ return { retval: ERR, tret: new Array(10).fill(0), attr: new Array(20).fill(0) };
3145
+ if (ipl < 0) ipl = 0;
3146
+ if (ipl === SE_AST_OFFSET + 134340) ipl = SE_PLUTO;
3147
+ ifl &= SEFLG_EPHMASK;
3148
+ const loc = occultWhenLoc(swed, tjdStart, ipl, starname, ifl, geopos, backward);
3149
+ if (loc.retval <= 0) return loc;
3150
+ let retflag = loc.retval;
3151
+ const ew = eclipseWhere(swed, loc.tret[0], ipl, starname, ifl);
3152
+ if (ew.retval === ERR) return { retval: ERR, tret: loc.tret, attr: loc.attr };
3153
+ retflag |= (ew.retval & SE_ECL_NONCENTRAL);
3154
+ loc.attr[3] = ew.dcore[0];
3155
+ return { retval: retflag, tret: loc.tret, attr: loc.attr };
3156
+ }
3157
+
3158
+ export function sweLunEclipseHow(
3159
+ swed: SweData, tjdUt: number, ifl: number, geopos: number[] | null,
3160
+ ): { retval: number; attr: number[] } {
3161
+ swiInitSwedIfStart(swed);
3162
+ if (geopos !== null && (geopos[2] < SEI_ECL_GEOALT_MIN || geopos[2] > SEI_ECL_GEOALT_MAX))
3163
+ return { retval: ERR, attr: new Array(20).fill(0) };
3164
+ ifl = ifl & ~SEFLG_TOPOCTR;
3165
+ ifl &= ~(0x40000 | 0x80000); /* SEFLG_JPLHOR | SEFLG_JPLHOR_APPROX */
3166
+ const leh = lunEclipseHow(swed, tjdUt, ifl);
3167
+ let retc = leh.retval;
3168
+ if (geopos === null) return { retval: retc, attr: leh.attr };
3169
+ /* azimuth and altitude of moon */
3170
+ sweSetTopo(swed, geopos[0], geopos[1], geopos[2]);
3171
+ const res = sweCalcUt(swed, tjdUt, SE_MOON, ifl | SEFLG_TOPOCTR | SEFLG_EQUATORIAL);
3172
+ if (res.flags === ERR) return { retval: ERR, attr: leh.attr };
3173
+ const xaz = [0, 0, 0];
3174
+ sweAzalt(swed, tjdUt, SE_EQU2HOR, geopos, 0, 10, Array.from(res.xx), xaz);
3175
+ leh.attr[4] = xaz[0]; leh.attr[5] = xaz[1]; leh.attr[6] = xaz[2];
3176
+ if (xaz[2] <= 0) retc = 0;
3177
+ return { retval: retc, attr: leh.attr };
3178
+ }
3179
+
3180
+ export function sweLunEclipseWhenLoc(
3181
+ swed: SweData, tjdStart: number, ifl: number, geopos: number[],
3182
+ backward: number,
3183
+ ): { retval: number; tret: number[]; attr: number[] } {
3184
+ swiInitSwedIfStart(swed);
3185
+ const tret = new Array(10).fill(0);
3186
+ const attr = new Array(20).fill(0);
3187
+ if (geopos[2] < SEI_ECL_GEOALT_MIN || geopos[2] > SEI_ECL_GEOALT_MAX)
3188
+ return { retval: ERR, tret, attr };
3189
+ ifl &= ~(0x40000 | 0x80000);
3190
+ let tjdStartAdj = tjdStart;
3191
+ for (;;) { /* next_lun_ecl */
3192
+ const lew = sweLunEclipseWhen(swed, tjdStartAdj, ifl, 0, backward);
3193
+ if (lew.retval === ERR) return { retval: ERR, tret: lew.tret, attr };
3194
+ for (let i = 0; i < 10; i++) tret[i] = lew.tret[i];
3195
+ /* visibility */
3196
+ let retflag = 0;
3197
+ for (let i = 7; i >= 0; i--) {
3198
+ if (i === 1) continue;
3199
+ if (tret[i] === 0) continue;
3200
+ const how = sweLunEclipseHow(swed, tret[i], ifl, geopos);
3201
+ if (how.retval === ERR) return { retval: ERR, tret, attr };
3202
+ if (how.attr[6] > 0) {
3203
+ retflag |= SE_ECL_VISIBLE;
3204
+ if (i === 0) retflag |= SE_ECL_MAX_VISIBLE;
3205
+ else if (i === 2) retflag |= SE_ECL_PARTBEG_VISIBLE;
3206
+ else if (i === 3) retflag |= SE_ECL_PARTEND_VISIBLE;
3207
+ else if (i === 4) retflag |= SE_ECL_TOTBEG_VISIBLE;
3208
+ else if (i === 5) retflag |= SE_ECL_TOTEND_VISIBLE;
3209
+ else if (i === 6) retflag |= SE_ECL_PENUMBBEG_VISIBLE;
3210
+ else if (i === 7) retflag |= SE_ECL_PENUMBEND_VISIBLE;
3211
+ }
3212
+ if (i === 0) { for (let j = 0; j < 20; j++) attr[j] = how.attr[j]; }
3213
+ }
3214
+ if (!(retflag & SE_ECL_VISIBLE)) {
3215
+ tjdStartAdj = backward ? tret[0] - 25 : tret[0] + 25;
3216
+ continue;
3217
+ }
3218
+ /* moon rise and set */
3219
+ let tjdMax = tret[0];
3220
+ const riseRes = sweRiseTrans(swed, tret[6] - 0.001, SE_MOON, null, ifl, SE_CALC_RISE | SE_BIT_DISC_BOTTOM, geopos, 0, 0, null);
3221
+ if (riseRes.retval === ERR) return { retval: ERR, tret, attr };
3222
+ let setRes: { retval: number; tret: number } = { retval: 0, tret: 0 };
3223
+ if (riseRes.retval >= 0) {
3224
+ setRes = sweRiseTrans(swed, tret[6] - 0.001, SE_MOON, null, ifl, SE_CALC_SET | SE_BIT_DISC_BOTTOM, geopos, 0, 0, null);
3225
+ if (setRes.retval === ERR) return { retval: ERR, tret, attr };
3226
+ }
3227
+ if (riseRes.retval >= 0 && setRes.retval >= 0) {
3228
+ const tjdr = riseRes.tret;
3229
+ const tjds = setRes.tret;
3230
+ if (tjds < tret[6] || (tjds > tjdr && tjdr > tret[7])) {
3231
+ tjdStartAdj = backward ? tret[0] - 25 : tret[0] + 25;
3232
+ continue;
3233
+ }
3234
+ if (tjdr > tret[6] && tjdr < tret[7]) {
3235
+ tret[6] = 0;
3236
+ for (let i = 2; i <= 5; i++) { if (tjdr > tret[i]) tret[i] = 0; }
3237
+ tret[8] = tjdr;
3238
+ if (tjdr > tret[0]) tjdMax = tjdr;
3239
+ }
3240
+ if (tjds > tret[6] && tjds < tret[7]) {
3241
+ tret[7] = 0;
3242
+ for (let i = 2; i <= 5; i++) { if (tjds < tret[i]) tret[i] = 0; }
3243
+ tret[9] = tjds;
3244
+ if (tjds < tret[0]) tjdMax = tjds;
3245
+ }
3246
+ }
3247
+ tret[0] = tjdMax;
3248
+ const howFinal = sweLunEclipseHow(swed, tjdMax, ifl, geopos);
3249
+ if (howFinal.retval === ERR) return { retval: ERR, tret, attr };
3250
+ if (howFinal.retval === 0) {
3251
+ tjdStartAdj = backward ? tret[0] - 25 : tret[0] + 25;
3252
+ continue;
3253
+ }
3254
+ for (let j = 0; j < 20; j++) attr[j] = howFinal.attr[j];
3255
+ retflag |= (howFinal.retval & SE_ECL_ALLTYPES_LUNAR);
3256
+ return { retval: retflag, tret, attr };
3257
+ }
3258
+ }
3259
+
3260
+ /* ==================================================================
3261
+ * Planetary nodes and apsides — swe_nod_aps / swe_nod_aps_ut
3262
+ * Translated from swecl.c lines 5001-5655
3263
+ * ================================================================== */
3264
+
3265
+ /* Mean orbital element tables (VSOP87 polynomials, Mercury–Neptune) */
3266
+ const el_node: number[][] = [
3267
+ [ 48.330893, 1.1861890, 0.00017587, 0.000000211], /* Mercury */
3268
+ [ 76.679920, 0.9011190, 0.00040665, -0.000000080], /* Venus */
3269
+ [ 0, 0, 0, 0 ], /* Earth */
3270
+ [ 49.558093, 0.7720923, 0.00001605, 0.000002325], /* Mars */
3271
+ [100.464441, 1.0209550, 0.00040117, 0.000000569], /* Jupiter */
3272
+ [113.665524, 0.8770970, -0.00012067, -0.000002380], /* Saturn */
3273
+ [ 74.005947, 0.5211258, 0.00133982, 0.000018516], /* Uranus */
3274
+ [131.784057, 1.1022057, 0.00026006, -0.000000636], /* Neptune */
3275
+ ];
3276
+ const el_peri: number[][] = [
3277
+ [ 77.456119, 1.5564775, 0.00029589, 0.000000056], /* Mercury */
3278
+ [131.563707, 1.4022188, -0.00107337, -0.000005315], /* Venus */
3279
+ [102.937348, 1.7195269, 0.00045962, 0.000000499], /* Earth */
3280
+ [336.060234, 1.8410331, 0.00013515, 0.000000318], /* Mars */
3281
+ [ 14.331309, 1.6126668, 0.00103127, -0.000004569], /* Jupiter */
3282
+ [ 93.056787, 1.9637694, 0.00083757, 0.000004899], /* Saturn */
3283
+ [173.005159, 1.4863784, 0.00021450, 0.000000433], /* Uranus */
3284
+ [ 48.123691, 1.4262677, 0.00037918, -0.000000003], /* Neptune */
3285
+ ];
3286
+ const el_incl: number[][] = [
3287
+ [ 7.004986, 0.0018215, -0.00001809, 0.000000053], /* Mercury */
3288
+ [ 3.394662, 0.0010037, -0.00000088, -0.000000007], /* Venus */
3289
+ [ 0, 0, 0, 0 ], /* Earth */
3290
+ [ 1.849726, -0.0006010, 0.00001276, -0.000000006], /* Mars */
3291
+ [ 1.303270, -0.0054966, 0.00000465, -0.000000004], /* Jupiter */
3292
+ [ 2.488878, -0.0037363, -0.00001516, 0.000000089], /* Saturn */
3293
+ [ 0.773196, 0.0007744, 0.00003749, -0.000000092], /* Uranus */
3294
+ [ 1.769952, -0.0093082, -0.00000708, 0.000000028], /* Neptune */
3295
+ ];
3296
+ const el_ecce: number[][] = [
3297
+ [ 0.20563175, 0.000020406, -0.0000000284, -0.00000000017], /* Mercury */
3298
+ [ 0.00677188, -0.000047766, 0.0000000975, 0.00000000044], /* Venus */
3299
+ [ 0.01670862, -0.000042037, -0.0000001236, 0.00000000004], /* Earth */
3300
+ [ 0.09340062, 0.000090483, -0.0000000806, -0.00000000035], /* Mars */
3301
+ [ 0.04849485, 0.000163244, -0.0000004719, -0.00000000197], /* Jupiter */
3302
+ [ 0.05550862, -0.000346818, -0.0000006456, 0.00000000338], /* Saturn */
3303
+ [ 0.04629590, -0.000027337, 0.0000000790, 0.00000000025], /* Uranus */
3304
+ [ 0.00898809, 0.000006408, -0.0000000008, -0.00000000005], /* Neptune */
3305
+ ];
3306
+ const el_sema: number[][] = [
3307
+ [ 0.387098310, 0.0, 0.0, 0.0], /* Mercury */
3308
+ [ 0.723329820, 0.0, 0.0, 0.0], /* Venus */
3309
+ [ 1.000001018, 0.0, 0.0, 0.0], /* Earth */
3310
+ [ 1.523679342, 0.0, 0.0, 0.0], /* Mars */
3311
+ [ 5.202603191, 0.0000001913, 0.0, 0.0], /* Jupiter */
3312
+ [ 9.554909596, 0.0000021389, 0.0, 0.0], /* Saturn */
3313
+ [ 19.218446062, -0.0000000372, 0.00000000098, 0.0], /* Uranus */
3314
+ [ 30.110386869, -0.0000001663, 0.00000000069, 0.0], /* Neptune */
3315
+ ];
3316
+ /* Ratios of mass of Sun to masses of the planets */
3317
+ const plmass: number[] = [
3318
+ 6023600, /* Mercury */
3319
+ 408523.719, /* Venus */
3320
+ 328900.5, /* Earth and Moon */
3321
+ 3098703.59, /* Mars */
3322
+ 1047.348644, /* Jupiter */
3323
+ 3497.9018, /* Saturn */
3324
+ 22902.98, /* Uranus */
3325
+ 19412.26, /* Neptune */
3326
+ 136566000, /* Pluto */
3327
+ ];
3328
+ const ipl_to_elem: number[] = [2, 0, 0, 1, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 2];
3329
+
3330
+ /**
3331
+ * Rotate J2000 equatorial XYZ → ecliptic of date.
3332
+ * C: swi_plan_for_osc_elem (sweph.c:5757)
3333
+ */
3334
+ function swiPlanForOscElemNodAps(iflag: number, tjd: number, xx: Float64Array | number[], swed: SweData): number {
3335
+ const x = new Float64Array(6);
3336
+ let oe: Epsilon = swed.oec;
3337
+ const oectmp = createEpsilon();
3338
+ /* ICRS to J2000 */
3339
+ if (!(iflag & SEFLG_ICRS) && swiGetDenum(SEI_SUN, iflag, swed) >= 403) {
3340
+ swiBias(xx, tjd, iflag, false, swed);
3341
+ }
3342
+ /* precession, equator 2000 -> equator of date
3343
+ * attention: speed vector has to be rotated,
3344
+ * but daily precession 0.137" may not be added! */
3345
+ swiPrecess(xx, tjd, iflag, J2000_TO_J, swed);
3346
+ /* precess speed vector separately (C: swi_precess(xx+3, ...)) */
3347
+ const tmpSpd = [xx[3], xx[4], xx[5]];
3348
+ swiPrecess(tmpSpd, tjd, iflag, J2000_TO_J, swed);
3349
+ xx[3] = tmpSpd[0]; xx[4] = tmpSpd[1]; xx[5] = tmpSpd[2];
3350
+ /* epsilon */
3351
+ if (tjd === swed.oec.teps) {
3352
+ oe = swed.oec;
3353
+ } else if (tjd === J2000) {
3354
+ oe = swed.oec2000;
3355
+ } else {
3356
+ calcEpsilon(tjd, iflag, oectmp, swed);
3357
+ oe = oectmp;
3358
+ }
3359
+ /* nutation */
3360
+ if (!(iflag & SEFLG_NONUT)) {
3361
+ let nutp: Nut;
3362
+ if (tjd === swed.nut.tnut) {
3363
+ nutp = swed.nut;
3364
+ } else if (tjd === J2000) {
3365
+ nutp = swed.nut2000;
3366
+ } else if (tjd === swed.nutv.tnut) {
3367
+ nutp = swed.nutv;
3368
+ } else {
3369
+ nutp = createNut();
3370
+ swiNutation(tjd, iflag, nutp.nutlo, swed);
3371
+ nutp.tnut = tjd;
3372
+ nutp.snut = Math.sin(nutp.nutlo[1]);
3373
+ nutp.cnut = Math.cos(nutp.nutlo[1]);
3374
+ nutMatrix(nutp, oe);
3375
+ }
3376
+ for (let i = 0; i <= 2; i++) {
3377
+ x[i] = xx[0] * nutp.matrix[0][i] +
3378
+ xx[1] * nutp.matrix[1][i] +
3379
+ xx[2] * nutp.matrix[2][i];
3380
+ }
3381
+ /* speed: rotation only */
3382
+ for (let i = 0; i <= 2; i++) {
3383
+ x[i + 3] = xx[3] * nutp.matrix[0][i] +
3384
+ xx[4] * nutp.matrix[1][i] +
3385
+ xx[5] * nutp.matrix[2][i];
3386
+ }
3387
+ for (let i = 0; i <= 5; i++) xx[i] = x[i];
3388
+ /* transformation to ecliptic */
3389
+ swiCoortrf2(xx, xx, oe.seps, oe.ceps);
3390
+ swiCoortrf2(xx, xx, oe.seps, oe.ceps, 3, 3);
3391
+ swiCoortrf2(xx, xx, nutp.snut, nutp.cnut);
3392
+ swiCoortrf2(xx, xx, nutp.snut, nutp.cnut, 3, 3);
3393
+ } else {
3394
+ /* no nutation: just ecliptic transformation */
3395
+ swiCoortrf2(xx, xx, oe.seps, oe.ceps);
3396
+ swiCoortrf2(xx, xx, oe.seps, oe.ceps, 3, 3);
3397
+ }
3398
+ return OK;
3399
+ }
3400
+
3401
+ /** Result from sweNodAps / sweNodApsUt */
3402
+ export interface NodApsResult {
3403
+ retval: number;
3404
+ xnasc: number[];
3405
+ xndsc: number[];
3406
+ xperi: number[];
3407
+ xaphe: number[];
3408
+ serr: string;
3409
+ }
3410
+
3411
+ /**
3412
+ * Compute planetary nodes and apsides.
3413
+ * C: swe_nod_aps (swecl.c:5064-5643)
3414
+ */
3415
+ export function sweNodAps(
3416
+ swed: SweData,
3417
+ tjdEt: number,
3418
+ ipl: number,
3419
+ iflag: number,
3420
+ method: number,
3421
+ ): NodApsResult {
3422
+ let i: number, j: number, ij: number;
3423
+ let iplx: number;
3424
+ let ipli: number;
3425
+ let istart: number, iend: number;
3426
+ let iflJ2000: number;
3427
+ let plm: number;
3428
+ let t = (tjdEt - J2000) / 36525;
3429
+ let dt: number;
3430
+ const x = new Float64Array(6);
3431
+ const xx = new Float64Array(24);
3432
+ const xobs = new Float64Array(6);
3433
+ const x2000 = new Float64Array(6);
3434
+ const xpos: Float64Array[] = [new Float64Array(6), new Float64Array(6), new Float64Array(6)];
3435
+ const xnorm = new Float64Array(6);
3436
+ const xposm = new Float64Array(6);
3437
+ const xn: Float64Array[] = [new Float64Array(6), new Float64Array(6), new Float64Array(6)];
3438
+ const xs: Float64Array[] = [new Float64Array(6), new Float64Array(6), new Float64Array(6)];
3439
+ const xq: Float64Array[] = [new Float64Array(6), new Float64Array(6), new Float64Array(6)];
3440
+ const xa: Float64Array[] = [new Float64Array(6), new Float64Array(6), new Float64Array(6)];
3441
+ const xobs2 = new Float64Array(6);
3442
+ const x2 = new Float64Array(6);
3443
+ /* xna, xnd, xpe, xap are views into xx */
3444
+ const xna = xx.subarray(0, 6);
3445
+ const xnd = xx.subarray(6, 12);
3446
+ const xpe = xx.subarray(12, 18);
3447
+ const xap = xx.subarray(18, 24);
3448
+ let incl: number, sema: number, ecce: number, parg: number, ea: number;
3449
+ let vincl: number, vsema: number, vecce: number, pargx: number, eax: number;
3450
+ const pedp = swed.pldat[SEI_EARTH];
3451
+ const psbdp = swed.pldat[SEI_SUNBARY];
3452
+ const xsun = psbdp.x;
3453
+ const xear = pedp.x;
3454
+ let ep: number[];
3455
+ let Gmsm: number, dzmin: number;
3456
+ let rxy: number, rxyz: number, fac: number, sgn: number;
3457
+ let sinnode: number, cosnode: number, sinincl: number, cosincl: number;
3458
+ let sinu: number, cosu: number, sinE: number, cosE: number, cosE2: number;
3459
+ let uu: number, ny: number, ny2: number, c2: number, v2: number;
3460
+ let pp: number, ro: number, ro2: number, rn: number, rn2: number;
3461
+ let oe: Epsilon;
3462
+ let isTrueNodaps = false;
3463
+ let doAberr = !(iflag & (SEFLG_TRUEPOS | SEFLG_NOABERR));
3464
+ let doDefl = !(iflag & SEFLG_TRUEPOS) && !(iflag & SEFLG_NOGDEFL);
3465
+ const doFocalPoint = !!(method & SE_NODBIT_FOPOINT);
3466
+ let ellipseIsBary = false;
3467
+ let iflg0: number;
3468
+ let serr = '';
3469
+ const pldat_xreturn = new Float64Array(24);
3470
+
3471
+ iflag &= ~(0x100000 | 0x200000); /* SEFLG_JPLHOR | SEFLG_JPLHOR_APPROX */
3472
+
3473
+ /* Pluto as asteroid 134340 → main body SE_PLUTO */
3474
+ if (ipl === SE_AST_OFFSET + 134340) ipl = SE_PLUTO;
3475
+
3476
+ /* to get control over the save area */
3477
+ swiForceAppPosEtc(swed);
3478
+
3479
+ method %= SE_NODBIT_FOPOINT;
3480
+ ipli = ipl;
3481
+ if (ipl === SE_SUN) ipli = SE_EARTH;
3482
+ if (ipl === SE_MOON) {
3483
+ doDefl = false;
3484
+ if (!(iflag & SEFLG_HELCTR)) doAberr = false;
3485
+ }
3486
+ iflg0 = (iflag & (SEFLG_EPHMASK | SEFLG_NONUT)) | SEFLG_SPEED | SEFLG_TRUEPOS;
3487
+ if (ipli !== SE_MOON) iflg0 |= SEFLG_HELCTR;
3488
+
3489
+ if (ipl === SE_MEAN_NODE || ipl === SE_TRUE_NODE ||
3490
+ ipl === SE_MEAN_APOG || ipl === SE_OSCU_APOG ||
3491
+ ipl < 0 ||
3492
+ (ipl >= SE_NPLANETS && ipl <= SE_AST_OFFSET)) {
3493
+ serr = `nodes/apsides for planet ${ipl} are not implemented`;
3494
+ return {
3495
+ retval: ERR, serr,
3496
+ xnasc: [0, 0, 0, 0, 0, 0], xndsc: [0, 0, 0, 0, 0, 0],
3497
+ xperi: [0, 0, 0, 0, 0, 0], xaphe: [0, 0, 0, 0, 0, 0],
3498
+ };
3499
+ }
3500
+
3501
+ for (i = 0; i < 24; i++) xx[i] = 0;
3502
+
3503
+ /***************************************
3504
+ * mean nodes and apsides
3505
+ ***************************************/
3506
+ if ((method === 0 || (method & SE_NODBIT_MEAN)) &&
3507
+ ((ipl >= SE_SUN && ipl <= SE_NEPTUNE) || ipl === SE_EARTH)) {
3508
+ if (ipl === SE_MOON) {
3509
+ const mle = swiMeanLunarElements(tjdEt);
3510
+ xna[0] = mle.node; xna[3] = mle.dnode;
3511
+ xpe[0] = mle.peri; xpe[3] = mle.dperi;
3512
+ incl = MOON_MEAN_INCL;
3513
+ vincl = 0;
3514
+ ecce = MOON_MEAN_ECC;
3515
+ vecce = 0;
3516
+ sema = MOON_MEAN_DIST / AUNIT;
3517
+ vsema = 0;
3518
+ } else {
3519
+ iplx = ipl_to_elem[ipl];
3520
+ ep = el_incl[iplx];
3521
+ incl = ep[0] + ep[1] * t + ep[2] * t * t + ep[3] * t * t * t;
3522
+ vincl = ep[1] / 36525;
3523
+ ep = el_sema[iplx];
3524
+ sema = ep[0] + ep[1] * t + ep[2] * t * t + ep[3] * t * t * t;
3525
+ vsema = ep[1] / 36525;
3526
+ ep = el_ecce[iplx];
3527
+ ecce = ep[0] + ep[1] * t + ep[2] * t * t + ep[3] * t * t * t;
3528
+ vecce = ep[1] / 36525;
3529
+ ep = el_node[iplx];
3530
+ /* ascending node */
3531
+ xna[0] = ep[0] + ep[1] * t + ep[2] * t * t + ep[3] * t * t * t;
3532
+ xna[3] = ep[1] / 36525;
3533
+ /* perihelion */
3534
+ ep = el_peri[iplx];
3535
+ xpe[0] = ep[0] + ep[1] * t + ep[2] * t * t + ep[3] * t * t * t;
3536
+ xpe[3] = ep[1] / 36525;
3537
+ }
3538
+ /* descending node */
3539
+ xnd[0] = sweDegnorm(xna[0] + 180);
3540
+ xnd[3] = xna[3];
3541
+ /* angular distance of perihelion from node */
3542
+ parg = xpe[0] = sweDegnorm(xpe[0] - xna[0]);
3543
+ pargx = xpe[3] = sweDegnorm(xpe[0] + xpe[3] - xna[3]);
3544
+ /* transform from orbital plane to mean ecliptic of date */
3545
+ {
3546
+ const cotBuf = [xpe[0], xpe[1], xpe[2]];
3547
+ sweCotrans(cotBuf, cotBuf, -incl);
3548
+ xpe[0] = cotBuf[0]; xpe[1] = cotBuf[1]; xpe[2] = cotBuf[2];
3549
+ }
3550
+ /* xpe+3 is aux. position, not speed!!! */
3551
+ {
3552
+ const cotBuf = [xpe[3], xpe[4], xpe[5]];
3553
+ sweCotrans(cotBuf, cotBuf, -incl - vincl);
3554
+ xpe[3] = cotBuf[0]; xpe[4] = cotBuf[1]; xpe[5] = cotBuf[2];
3555
+ }
3556
+ /* add node again */
3557
+ xpe[0] = sweDegnorm(xpe[0] + xna[0]);
3558
+ /* xpe+3 is aux. position, not speed!!! */
3559
+ xpe[3] = sweDegnorm(xpe[3] + xna[0] + xna[3]);
3560
+ /* speed */
3561
+ xpe[3] = sweDegnorm(xpe[3] - xpe[0]);
3562
+ /* heliocentric distance of perihelion and aphelion */
3563
+ xpe[2] = sema * (1 - ecce);
3564
+ xpe[5] = (sema + vsema) * (1 - ecce - vecce) - xpe[2];
3565
+ /* aphelion */
3566
+ xap[0] = sweDegnorm(xpe[0] + 180);
3567
+ xap[1] = -xpe[1];
3568
+ xap[3] = xpe[3];
3569
+ xap[4] = -xpe[4];
3570
+ if (doFocalPoint) {
3571
+ xap[2] = sema * ecce * 2;
3572
+ xap[5] = (sema + vsema) * (ecce + vecce) * 2 - xap[2];
3573
+ } else {
3574
+ xap[2] = sema * (1 + ecce);
3575
+ xap[5] = (sema + vsema) * (1 + ecce + vecce) - xap[2];
3576
+ }
3577
+ /* heliocentric distance of nodes */
3578
+ ea = Math.atan(Math.tan(-parg * DEGTORAD / 2) * Math.sqrt((1 - ecce) / (1 + ecce))) * 2;
3579
+ eax = Math.atan(Math.tan(-pargx * DEGTORAD / 2) * Math.sqrt((1 - ecce - vecce) / (1 + ecce + vecce))) * 2;
3580
+ xna[2] = sema * (Math.cos(ea) - ecce) / Math.cos(parg * DEGTORAD);
3581
+ xna[5] = (sema + vsema) * (Math.cos(eax) - ecce - vecce) / Math.cos(pargx * DEGTORAD);
3582
+ xna[5] -= xna[2];
3583
+ ea = Math.atan(Math.tan((180 - parg) * DEGTORAD / 2) * Math.sqrt((1 - ecce) / (1 + ecce))) * 2;
3584
+ eax = Math.atan(Math.tan((180 - pargx) * DEGTORAD / 2) * Math.sqrt((1 - ecce - vecce) / (1 + ecce + vecce))) * 2;
3585
+ xnd[2] = sema * (Math.cos(ea) - ecce) / Math.cos((180 - parg) * DEGTORAD);
3586
+ xnd[5] = (sema + vsema) * (Math.cos(eax) - ecce - vecce) / Math.cos((180 - pargx) * DEGTORAD);
3587
+ xnd[5] -= xnd[2];
3588
+ /* no light-time correction because speed is extremely small */
3589
+ for (i = 0; i < 4; i++) {
3590
+ const off = i * 6;
3591
+ xx[off + 0] *= DEGTORAD;
3592
+ xx[off + 1] *= DEGTORAD;
3593
+ xx[off + 3] *= DEGTORAD;
3594
+ xx[off + 4] *= DEGTORAD;
3595
+ /* swi_polcart_sp in-place */
3596
+ const tmpIn = [xx[off], xx[off + 1], xx[off + 2], xx[off + 3], xx[off + 4], xx[off + 5]];
3597
+ const tmpOut = [0, 0, 0, 0, 0, 0];
3598
+ swiPolcartSp(tmpIn, tmpOut);
3599
+ for (j = 0; j < 6; j++) xx[off + j] = tmpOut[j];
3600
+ }
3601
+
3602
+ /***************************************
3603
+ * "true" or osculating nodes and apsides
3604
+ ***************************************/
3605
+ } else {
3606
+ /* first, we need a heliocentric distance of the planet */
3607
+ const calcRes0 = sweCalc(swed, tjdEt, ipli, iflg0);
3608
+ if (calcRes0.flags === ERR) {
3609
+ serr = calcRes0.serr;
3610
+ return { retval: ERR, xnasc: [0,0,0,0,0,0], xndsc: [0,0,0,0,0,0], xperi: [0,0,0,0,0,0], xaphe: [0,0,0,0,0,0], serr };
3611
+ }
3612
+ for (i = 0; i < 6; i++) x[i] = calcRes0.xx[i];
3613
+ iflJ2000 = (iflag & SEFLG_EPHMASK) | SEFLG_J2000 | SEFLG_EQUATORIAL | SEFLG_XYZ | SEFLG_TRUEPOS | SEFLG_NONUT | SEFLG_SPEED;
3614
+ ellipseIsBary = false;
3615
+ if (ipli !== SE_MOON) {
3616
+ if ((method & SE_NODBIT_OSCU_BAR) && x[2] > 6) {
3617
+ iflJ2000 |= SEFLG_BARYCTR; /* only planets beyond Jupiter */
3618
+ ellipseIsBary = true;
3619
+ } else {
3620
+ iflJ2000 |= SEFLG_HELCTR;
3621
+ }
3622
+ }
3623
+ /* we need three positions and three speeds */
3624
+ if (ipli === SE_MOON) {
3625
+ dt = NODE_CALC_INTV;
3626
+ dzmin = 1e-15;
3627
+ Gmsm = GEOGCONST * (1 + 1 / EARTH_MOON_MRAT) / AUNIT / AUNIT / AUNIT * 86400.0 * 86400.0;
3628
+ } else {
3629
+ if ((ipli >= SE_MERCURY && ipli <= SE_PLUTO) || ipli === SE_EARTH)
3630
+ plm = 1 / plmass[ipl_to_elem[ipl]];
3631
+ else
3632
+ plm = 0;
3633
+ dt = NODE_CALC_INTV * 10 * x[2];
3634
+ dzmin = 1e-15 * dt / NODE_CALC_INTV;
3635
+ Gmsm = HELGRAVCONST * (1 + plm) / AUNIT / AUNIT / AUNIT * 86400.0 * 86400.0;
3636
+ }
3637
+ if (iflag & SEFLG_SPEED) {
3638
+ istart = 0; iend = 2;
3639
+ } else {
3640
+ istart = iend = 0; dt = 0;
3641
+ }
3642
+ for (i = istart, t = tjdEt - dt; i <= iend; i++, t += dt) {
3643
+ if (istart === iend) t = tjdEt;
3644
+ const cr = sweCalc(swed, t, ipli, iflJ2000);
3645
+ if (cr.flags === ERR) {
3646
+ serr = cr.serr;
3647
+ return { retval: ERR, xnasc: [0,0,0,0,0,0], xndsc: [0,0,0,0,0,0], xperi: [0,0,0,0,0,0], xaphe: [0,0,0,0,0,0], serr };
3648
+ }
3649
+ for (j = 0; j < 6; j++) xpos[i][j] = cr.xx[j];
3650
+ /* the EMB is used instead of the earth */
3651
+ if (ipli === SE_EARTH) {
3652
+ const crm = sweCalc(swed, t, SE_MOON, iflJ2000 & ~(SEFLG_BARYCTR | SEFLG_HELCTR));
3653
+ if (crm.flags === ERR) {
3654
+ serr = crm.serr;
3655
+ return { retval: ERR, xnasc: [0,0,0,0,0,0], xndsc: [0,0,0,0,0,0], xperi: [0,0,0,0,0,0], xaphe: [0,0,0,0,0,0], serr };
3656
+ }
3657
+ for (j = 0; j < 6; j++) xposm[j] = crm.xx[j];
3658
+ for (j = 0; j <= 5; j++)
3659
+ xpos[i][j] += xposm[j] / (EARTH_MOON_MRAT + 1.0);
3660
+ }
3661
+ swiPlanForOscElemNodAps(iflg0, t, xpos[i], swed);
3662
+ }
3663
+ for (i = istart; i <= iend; i++) {
3664
+ if (Math.abs(xpos[i][5]) < dzmin)
3665
+ xpos[i][5] = dzmin;
3666
+ fac = xpos[i][2] / xpos[i][5];
3667
+ sgn = xpos[i][5] / Math.abs(xpos[i][5]);
3668
+ for (j = 0; j <= 2; j++) {
3669
+ xn[i][j] = (xpos[i][j] - fac * xpos[i][j + 3]) * sgn;
3670
+ xs[i][j] = -xn[i][j];
3671
+ }
3672
+ }
3673
+ for (i = istart; i <= iend; i++) {
3674
+ /* node */
3675
+ rxy = Math.sqrt(xn[i][0] * xn[i][0] + xn[i][1] * xn[i][1]);
3676
+ cosnode = xn[i][0] / rxy;
3677
+ sinnode = xn[i][1] / rxy;
3678
+ /* inclination */
3679
+ swiCrossProd(xpos[i], xpos[i].subarray(3), xnorm);
3680
+ rxy = xnorm[0] * xnorm[0] + xnorm[1] * xnorm[1];
3681
+ c2 = rxy + xnorm[2] * xnorm[2];
3682
+ rxyz = Math.sqrt(c2);
3683
+ rxy = Math.sqrt(rxy);
3684
+ sinincl = rxy / rxyz;
3685
+ cosincl = Math.sqrt(1 - sinincl * sinincl);
3686
+ if (xnorm[2] < 0) cosincl = -cosincl; /* retrograde asteroid */
3687
+ /* argument of latitude */
3688
+ cosu = xpos[i][0] * cosnode + xpos[i][1] * sinnode;
3689
+ sinu = xpos[i][2] / sinincl;
3690
+ uu = Math.atan2(sinu, cosu);
3691
+ /* semi-axis */
3692
+ rxyz = Math.sqrt(squareSum(xpos[i]));
3693
+ v2 = squareSum(xpos[i], 3);
3694
+ sema = 1 / (2 / rxyz - v2 / Gmsm);
3695
+ /* eccentricity */
3696
+ pp = c2 / Gmsm;
3697
+ ecce = Math.sqrt(1 - pp / sema);
3698
+ /* eccentric anomaly */
3699
+ cosE = 1 / ecce * (1 - rxyz / sema);
3700
+ sinE = 1 / ecce / Math.sqrt(sema * Gmsm) * dotProd(xpos[i], xpos[i].subarray(3));
3701
+ /* true anomaly */
3702
+ ny = 2 * Math.atan(Math.sqrt((1 + ecce) / (1 - ecce)) * sinE / (1 + cosE));
3703
+ /* distance of perihelion from ascending node */
3704
+ xq[i][0] = swiMod2PI(uu - ny);
3705
+ xq[i][1] = 0; /* latitude */
3706
+ xq[i][2] = sema * (1 - ecce); /* distance of perihelion */
3707
+ /* transformation to ecliptic coordinates */
3708
+ swiPolcart(xq[i], xq[i]);
3709
+ swiCoortrf2(xq[i], xq[i], -sinincl, cosincl);
3710
+ swiCartpol(xq[i], xq[i]);
3711
+ /* adding node, we get perihelion in ecl. coord. */
3712
+ xq[i][0] += Math.atan2(sinnode, cosnode);
3713
+ xa[i][0] = swiMod2PI(xq[i][0] + PI);
3714
+ xa[i][1] = -xq[i][1];
3715
+ if (doFocalPoint) {
3716
+ xa[i][2] = sema * ecce * 2; /* distance of aphelion */
3717
+ } else {
3718
+ xa[i][2] = sema * (1 + ecce); /* distance of aphelion */
3719
+ }
3720
+ swiPolcart(xq[i], xq[i]);
3721
+ swiPolcart(xa[i], xa[i]);
3722
+ /* new distance of node from orbital ellipse */
3723
+ ny = swiMod2PI(ny - uu);
3724
+ ny2 = swiMod2PI(ny + PI);
3725
+ /* eccentric anomaly */
3726
+ cosE = Math.cos(2 * Math.atan(Math.tan(ny / 2) / Math.sqrt((1 + ecce) / (1 - ecce))));
3727
+ cosE2 = Math.cos(2 * Math.atan(Math.tan(ny2 / 2) / Math.sqrt((1 + ecce) / (1 - ecce))));
3728
+ /* new distance */
3729
+ rn = sema * (1 - ecce * cosE);
3730
+ rn2 = sema * (1 - ecce * cosE2);
3731
+ /* old node distance */
3732
+ ro = Math.sqrt(squareSum(xn[i]));
3733
+ ro2 = Math.sqrt(squareSum(xs[i]));
3734
+ /* correct length of position vector */
3735
+ for (j = 0; j <= 2; j++) {
3736
+ xn[i][j] *= rn / ro;
3737
+ xs[i][j] *= rn2 / ro2;
3738
+ }
3739
+ }
3740
+ for (i = 0; i <= 2; i++) {
3741
+ if (iflag & SEFLG_SPEED) {
3742
+ xpe[i] = xq[1][i];
3743
+ xpe[i + 3] = (xq[2][i] - xq[0][i]) / dt / 2;
3744
+ xap[i] = xa[1][i];
3745
+ xap[i + 3] = (xa[2][i] - xa[0][i]) / dt / 2;
3746
+ xna[i] = xn[1][i];
3747
+ xna[i + 3] = (xn[2][i] - xn[0][i]) / dt / 2;
3748
+ xnd[i] = xs[1][i];
3749
+ xnd[i + 3] = (xs[2][i] - xs[0][i]) / dt / 2;
3750
+ } else {
3751
+ xpe[i] = xq[0][i];
3752
+ xpe[i + 3] = 0;
3753
+ xap[i] = xa[0][i];
3754
+ xap[i + 3] = 0;
3755
+ xna[i] = xn[0][i];
3756
+ xna[i + 3] = 0;
3757
+ xnd[i] = xs[0][i];
3758
+ xnd[i + 3] = 0;
3759
+ }
3760
+ }
3761
+ isTrueNodaps = true;
3762
+ }
3763
+
3764
+ /* to set the variables required in the save area,
3765
+ * i.e. ecliptic, nutation, barycentric sun, earth
3766
+ * we compute the planet */
3767
+ if (ipli === SE_MOON && (iflag & (SEFLG_HELCTR | SEFLG_BARYCTR))) {
3768
+ swiForceAppPosEtc(swed);
3769
+ const cr2 = sweCalc(swed, tjdEt, SE_SUN, iflg0);
3770
+ if (cr2.flags === ERR) {
3771
+ serr = cr2.serr;
3772
+ return { retval: ERR, xnasc: [0,0,0,0,0,0], xndsc: [0,0,0,0,0,0], xperi: [0,0,0,0,0,0], xaphe: [0,0,0,0,0,0], serr };
3773
+ }
3774
+ } else {
3775
+ const cr2 = sweCalc(swed, tjdEt, ipli, iflg0 | (iflag & SEFLG_TOPOCTR));
3776
+ if (cr2.flags === ERR) {
3777
+ serr = cr2.serr;
3778
+ return { retval: ERR, xnasc: [0,0,0,0,0,0], xndsc: [0,0,0,0,0,0], xperi: [0,0,0,0,0,0], xaphe: [0,0,0,0,0,0], serr };
3779
+ }
3780
+ }
3781
+
3782
+ /***********************
3783
+ * position of observer
3784
+ ***********************/
3785
+ if (iflag & SEFLG_TOPOCTR) {
3786
+ swiGetObserver(tjdEt, iflag, false, xobs, swed);
3787
+ } else {
3788
+ for (i = 0; i <= 5; i++) xobs[i] = 0;
3789
+ }
3790
+ if (iflag & (SEFLG_HELCTR | SEFLG_BARYCTR)) {
3791
+ if ((iflag & SEFLG_HELCTR) && !(iflag & SEFLG_MOSEPH))
3792
+ for (i = 0; i <= 5; i++) xobs[i] = xsun[i];
3793
+ } else if (ipl === SE_SUN && !(iflag & SEFLG_MOSEPH)) {
3794
+ for (i = 0; i <= 5; i++) xobs[i] = xsun[i];
3795
+ } else {
3796
+ /* barycentric position of observer */
3797
+ for (i = 0; i <= 5; i++) xobs[i] += xear[i];
3798
+ }
3799
+
3800
+ /* ecliptic obliquity */
3801
+ if (iflag & SEFLG_J2000)
3802
+ oe = swed.oec2000;
3803
+ else
3804
+ oe = swed.oec;
3805
+
3806
+ /*************************************************
3807
+ * conversions shared by mean and osculating points
3808
+ *************************************************/
3809
+ for (ij = 0; ij < 4; ij++) {
3810
+ const off = ij * 6;
3811
+ /* no nodes for earth */
3812
+ if (ipli === SE_EARTH && ij <= 1) {
3813
+ for (i = 0; i <= 5; i++) xx[off + i] = 0;
3814
+ continue;
3815
+ }
3816
+ /*********************
3817
+ * to equator
3818
+ *********************/
3819
+ if (isTrueNodaps && !(iflag & SEFLG_NONUT)) {
3820
+ swiCoortrf2(xx, xx, -swed.nut.snut, swed.nut.cnut, off, off);
3821
+ if (iflag & SEFLG_SPEED)
3822
+ swiCoortrf2(xx, xx, -swed.nut.snut, swed.nut.cnut, off + 3, off + 3);
3823
+ }
3824
+ swiCoortrf2(xx, xx, -oe.seps, oe.ceps, off, off);
3825
+ swiCoortrf2(xx, xx, -oe.seps, oe.ceps, off + 3, off + 3);
3826
+ if (isTrueNodaps) {
3827
+ /****************************
3828
+ * to mean ecliptic of date
3829
+ ****************************/
3830
+ if (!(iflag & SEFLG_NONUT)) {
3831
+ /* swiNutate operates on first 6 elements, so we use a temp buffer */
3832
+ const tmp = new Float64Array(6);
3833
+ for (i = 0; i < 6; i++) tmp[i] = xx[off + i];
3834
+ swiNutate(tmp, iflag, true, swed);
3835
+ for (i = 0; i < 6; i++) xx[off + i] = tmp[i];
3836
+ }
3837
+ }
3838
+ /*********************
3839
+ * to J2000
3840
+ *********************/
3841
+ {
3842
+ const tmp = new Float64Array(6);
3843
+ for (i = 0; i < 6; i++) tmp[i] = xx[off + i];
3844
+ swiPrecess(tmp, tjdEt, iflag, J_TO_J2000, swed);
3845
+ if (iflag & SEFLG_SPEED)
3846
+ swiPrecessSpeed(tmp, tjdEt, iflag, J_TO_J2000, swed);
3847
+ for (i = 0; i < 6; i++) xx[off + i] = tmp[i];
3848
+ }
3849
+ /*********************
3850
+ * to barycenter
3851
+ *********************/
3852
+ if (ipli === SE_MOON) {
3853
+ for (i = 0; i <= 5; i++) xx[off + i] += xear[i];
3854
+ } else {
3855
+ if (!(iflag & SEFLG_MOSEPH) && !ellipseIsBary)
3856
+ for (j = 0; j <= 5; j++) xx[off + j] += xsun[j];
3857
+ }
3858
+ /*********************
3859
+ * to correct center
3860
+ *********************/
3861
+ for (j = 0; j <= 5; j++) xx[off + j] -= xobs[j];
3862
+ /* geocentric perigee/apogee of sun */
3863
+ if (ipl === SE_SUN && !(iflag & (SEFLG_HELCTR | SEFLG_BARYCTR)))
3864
+ for (j = 0; j <= 5; j++) xx[off + j] = -xx[off + j];
3865
+ /*********************
3866
+ * light deflection
3867
+ *********************/
3868
+ dt = Math.sqrt(squareSum(xx, off)) * AUNIT / CLIGHT / 86400.0;
3869
+ if (doDefl) {
3870
+ const tmp = new Float64Array(6);
3871
+ for (i = 0; i < 6; i++) tmp[i] = xx[off + i];
3872
+ swiDeflectLight(tmp, dt, iflag, swed);
3873
+ for (i = 0; i < 6; i++) xx[off + i] = tmp[i];
3874
+ }
3875
+ /*********************
3876
+ * aberration
3877
+ *********************/
3878
+ if (doAberr) {
3879
+ const tmp = new Float64Array(6);
3880
+ for (i = 0; i < 6; i++) tmp[i] = xx[off + i];
3881
+ swiAberrLight(tmp, xobs, iflag);
3882
+ for (i = 0; i < 6; i++) xx[off + i] = tmp[i];
3883
+ /*
3884
+ * Apparent speed is also influenced by
3885
+ * the difference of speed of the earth between t and t-dt.
3886
+ */
3887
+ if (iflag & SEFLG_SPEED) {
3888
+ /* get barycentric sun and earth for t-dt into save area */
3889
+ const cr3 = sweCalc(swed, tjdEt - dt, ipli, iflg0 | (iflag & SEFLG_TOPOCTR));
3890
+ if (cr3.flags === ERR) {
3891
+ serr = cr3.serr;
3892
+ return { retval: ERR, xnasc: [0,0,0,0,0,0], xndsc: [0,0,0,0,0,0], xperi: [0,0,0,0,0,0], xaphe: [0,0,0,0,0,0], serr };
3893
+ }
3894
+ if (iflag & SEFLG_TOPOCTR) {
3895
+ for (i = 0; i <= 5; i++) xobs2[i] = swed.topd.xobs[i];
3896
+ } else {
3897
+ for (i = 0; i <= 5; i++) xobs2[i] = 0;
3898
+ }
3899
+ if (iflag & (SEFLG_HELCTR | SEFLG_BARYCTR)) {
3900
+ if ((iflag & SEFLG_HELCTR) && !(iflag & SEFLG_MOSEPH))
3901
+ for (i = 0; i <= 5; i++) xobs2[i] = xsun[i];
3902
+ } else if (ipl === SE_SUN && !(iflag & SEFLG_MOSEPH)) {
3903
+ for (i = 0; i <= 5; i++) xobs2[i] = xsun[i];
3904
+ } else {
3905
+ /* barycentric position of observer */
3906
+ for (i = 0; i <= 5; i++) xobs2[i] += xear[i];
3907
+ }
3908
+ for (i = 3; i <= 5; i++)
3909
+ xx[off + i] += xobs[i] - xobs2[i];
3910
+ /* restore save area */
3911
+ const cr4 = sweCalc(swed, tjdEt, SE_SUN, iflg0 | (iflag & SEFLG_TOPOCTR));
3912
+ if (cr4.flags === ERR) {
3913
+ serr = cr4.serr;
3914
+ return { retval: ERR, xnasc: [0,0,0,0,0,0], xndsc: [0,0,0,0,0,0], xperi: [0,0,0,0,0,0], xaphe: [0,0,0,0,0,0], serr };
3915
+ }
3916
+ }
3917
+ }
3918
+ /*********************
3919
+ * precession
3920
+ *********************/
3921
+ /* save J2000 coordinates; required for sidereal positions */
3922
+ for (j = 0; j <= 5; j++) x2000[j] = xx[off + j];
3923
+ if (!(iflag & SEFLG_J2000)) {
3924
+ const tmp = new Float64Array(6);
3925
+ for (i = 0; i < 6; i++) tmp[i] = xx[off + i];
3926
+ swiPrecess(tmp, tjdEt, iflag, J2000_TO_J, swed);
3927
+ if (iflag & SEFLG_SPEED)
3928
+ swiPrecessSpeed(tmp, tjdEt, iflag, J2000_TO_J, swed);
3929
+ for (i = 0; i < 6; i++) xx[off + i] = tmp[i];
3930
+ }
3931
+ /*********************
3932
+ * nutation
3933
+ *********************/
3934
+ if (!(iflag & SEFLG_NONUT)) {
3935
+ const tmp = new Float64Array(6);
3936
+ for (i = 0; i < 6; i++) tmp[i] = xx[off + i];
3937
+ swiNutate(tmp, iflag, false, swed);
3938
+ for (i = 0; i < 6; i++) xx[off + i] = tmp[i];
3939
+ }
3940
+ /* now we have equatorial cartesian coordinates; keep them */
3941
+ for (j = 0; j <= 5; j++) pldat_xreturn[18 + j] = xx[off + j];
3942
+ /************************************************
3943
+ * transformation to ecliptic.
3944
+ ************************************************/
3945
+ swiCoortrf2(xx, xx, oe.seps, oe.ceps, off, off);
3946
+ if (iflag & SEFLG_SPEED)
3947
+ swiCoortrf2(xx, xx, oe.seps, oe.ceps, off + 3, off + 3);
3948
+ if (!(iflag & SEFLG_NONUT)) {
3949
+ swiCoortrf2(xx, xx, swed.nut.snut, swed.nut.cnut, off, off);
3950
+ if (iflag & SEFLG_SPEED)
3951
+ swiCoortrf2(xx, xx, swed.nut.snut, swed.nut.cnut, off + 3, off + 3);
3952
+ }
3953
+ /* now we have ecliptic cartesian coordinates */
3954
+ for (j = 0; j <= 5; j++) pldat_xreturn[6 + j] = xx[off + j];
3955
+ /************************************
3956
+ * sidereal positions
3957
+ ************************************/
3958
+ if (iflag & SEFLG_SIDEREAL) {
3959
+ /* project onto ecliptic t0 */
3960
+ if (swed.sidd.sidMode & SE_SIDBIT_ECL_T0) {
3961
+ swiTropRa2SidLon(x2000, pldat_xreturn.subarray(6, 12), pldat_xreturn.subarray(18, 24), iflag, swed);
3962
+ /* project onto solar system equator */
3963
+ } else if (swed.sidd.sidMode & SE_SIDBIT_SSY_PLANE) {
3964
+ swiTropRa2SidLonSosy(x2000, pldat_xreturn.subarray(6, 12), iflag, swed);
3965
+ } else {
3966
+ /* traditional algorithm */
3967
+ swiCartpolSp(pldat_xreturn.subarray(6, 12), pldat_xreturn);
3968
+ const ayaRes = sweGetAyanamsaEx(swed, tjdEt, iflag);
3969
+ if (ayaRes.retc === ERR) {
3970
+ serr = ayaRes.serr;
3971
+ return { retval: ERR, xnasc: [0,0,0,0,0,0], xndsc: [0,0,0,0,0,0], xperi: [0,0,0,0,0,0], xaphe: [0,0,0,0,0,0], serr };
3972
+ }
3973
+ pldat_xreturn[0] -= ayaRes.daya * DEGTORAD;
3974
+ swiPolcartSp(pldat_xreturn, pldat_xreturn.subarray(6, 12));
3975
+ }
3976
+ }
3977
+ if ((iflag & SEFLG_XYZ) && (iflag & SEFLG_EQUATORIAL)) {
3978
+ for (j = 0; j <= 5; j++) xx[off + j] = pldat_xreturn[18 + j];
3979
+ continue;
3980
+ }
3981
+ if (iflag & SEFLG_XYZ) {
3982
+ for (j = 0; j <= 5; j++) xx[off + j] = pldat_xreturn[6 + j];
3983
+ continue;
3984
+ }
3985
+ /************************************************
3986
+ * transformation to polar coordinates
3987
+ ************************************************/
3988
+ swiCartpolSp(pldat_xreturn.subarray(18, 24), pldat_xreturn.subarray(12, 18));
3989
+ swiCartpolSp(pldat_xreturn.subarray(6, 12), pldat_xreturn);
3990
+ /**********************
3991
+ * radians to degrees
3992
+ **********************/
3993
+ if (!(iflag & SEFLG_RADIANS)) {
3994
+ for (j = 0; j < 2; j++) {
3995
+ pldat_xreturn[j] *= RADTODEG; /* ecliptic */
3996
+ pldat_xreturn[j + 3] *= RADTODEG;
3997
+ pldat_xreturn[j + 12] *= RADTODEG; /* equator */
3998
+ pldat_xreturn[j + 15] *= RADTODEG;
3999
+ }
4000
+ }
4001
+ if (iflag & SEFLG_EQUATORIAL) {
4002
+ for (j = 0; j <= 5; j++) xx[off + j] = pldat_xreturn[12 + j];
4003
+ continue;
4004
+ } else {
4005
+ for (j = 0; j <= 5; j++) xx[off + j] = pldat_xreturn[j];
4006
+ continue;
4007
+ }
4008
+ }
4009
+
4010
+ /* copy to output */
4011
+ const xnasc = new Array<number>(6);
4012
+ const xndsc = new Array<number>(6);
4013
+ const xperi = new Array<number>(6);
4014
+ const xaphe = new Array<number>(6);
4015
+ for (i = 0; i <= 5; i++) {
4016
+ if (i > 2 && !(iflag & SEFLG_SPEED)) {
4017
+ xna[i] = xnd[i] = xpe[i] = xap[i] = 0;
4018
+ }
4019
+ xnasc[i] = xna[i];
4020
+ xndsc[i] = xnd[i];
4021
+ xperi[i] = xpe[i];
4022
+ xaphe[i] = xap[i];
4023
+ }
4024
+ return { retval: OK, xnasc, xndsc, xperi, xaphe, serr };
4025
+ }
4026
+
4027
+ /**
4028
+ * Compute planetary nodes and apsides (UT version).
4029
+ * C: swe_nod_aps_ut (swecl.c:5645-5654)
4030
+ */
4031
+ export function sweNodApsUt(
4032
+ swed: SweData,
4033
+ tjdUt: number,
4034
+ ipl: number,
4035
+ iflag: number,
4036
+ method: number,
4037
+ ): NodApsResult {
4038
+ const dt = sweDeltatEx(tjdUt, iflag, swed);
4039
+ return sweNodAps(swed, tjdUt + dt, ipl, iflag, method);
4040
+ }
4041
+
4042
+ /* ==================================================================
4043
+ * Orbital elements & max/min distance
4044
+ * C: swe_get_orbital_elements, swe_orbit_max_min_true_distance
4045
+ * ================================================================== */
4046
+
4047
+ export interface OrbitalElementsResult {
4048
+ retval: number;
4049
+ dret: number[];
4050
+ serr: string;
4051
+ }
4052
+
4053
+ export interface OrbitDistanceResult {
4054
+ retval: number;
4055
+ dmax: number;
4056
+ dmin: number;
4057
+ dtrue: number;
4058
+ serr: string;
4059
+ }
4060
+
4061
+ /**
4062
+ * Compute gravitational constant GM for orbital elements.
4063
+ * C: get_gmsm (swecl.c:5676-5731)
4064
+ */
4065
+ function getGmsm(swed: SweData, tjdEt: number, ipl: number, iflag: number, r: number): { retval: number; gmsm: number; serr: string } {
4066
+ let Gmsm = 0;
4067
+ let plm = 0;
4068
+ let serr = '';
4069
+ let iflJ2000p = (iflag & (SEFLG_EPHMASK | SEFLG_HELCTR | SEFLG_BARYCTR)) | SEFLG_J2000 | SEFLG_TRUEPOS | SEFLG_NONUT;
4070
+ if (!(iflJ2000p & (SEFLG_HELCTR | SEFLG_BARYCTR))) {
4071
+ iflJ2000p |= SEFLG_HELCTR;
4072
+ }
4073
+ if (ipl === SE_MOON) {
4074
+ Gmsm = GEOGCONST * (1 + 1 / EARTH_MOON_MRAT) / AUNIT / AUNIT / AUNIT * 86400.0 * 86400.0;
4075
+ } else {
4076
+ if ((ipl >= SE_MERCURY && ipl <= SE_PLUTO) || ipl === SE_EARTH) {
4077
+ plm = 0;
4078
+ if (iflag & SEFLG_ORBEL_AA) {
4079
+ if (ipl === SE_EARTH) {
4080
+ plm = 1.0 / plmass[ipl_to_elem[ipl]];
4081
+ plm += 1.0 / plmass[ipl_to_elem[SE_VENUS]];
4082
+ plm += 1.0 / plmass[ipl_to_elem[SE_MERCURY]];
4083
+ } else {
4084
+ for (let j = ipl; j >= SE_MERCURY; j--) {
4085
+ plm += 1.0 / plmass[ipl_to_elem[j]];
4086
+ }
4087
+ if (ipl >= SE_MARS)
4088
+ plm += 1.0 / plmass[ipl_to_elem[SE_EARTH]];
4089
+ }
4090
+ } else {
4091
+ plm = 1.0 / plmass[ipl_to_elem[ipl]];
4092
+ }
4093
+ Gmsm = HELGRAVCONST * (1 + plm) / AUNIT / AUNIT / AUNIT * 86400.0 * 86400.0;
4094
+ } else {
4095
+ // asteroid or fictitious object
4096
+ plm = 0;
4097
+ if (iflag & SEFLG_ORBEL_AA) {
4098
+ for (let j = SE_MERCURY; j <= SE_PLUTO; j++) {
4099
+ const res = sweCalc(swed, tjdEt, j, iflJ2000p);
4100
+ if (res.flags < 0) return { retval: ERR, gmsm: 0, serr: res.serr };
4101
+ serr = res.serr;
4102
+ if (r > res.xx[2])
4103
+ plm += 1.0 / plmass[ipl_to_elem[j]];
4104
+ }
4105
+ const resE = sweCalc(swed, tjdEt, SE_EARTH, iflJ2000p);
4106
+ if (resE.flags < 0) return { retval: ERR, gmsm: 0, serr: resE.serr };
4107
+ serr = resE.serr;
4108
+ if (r > resE.xx[2])
4109
+ plm += 1.0 / plmass[ipl_to_elem[SE_EARTH]];
4110
+ }
4111
+ Gmsm = HELGRAVCONST * (1 + plm) / AUNIT / AUNIT / AUNIT * 86400.0 * 86400.0;
4112
+ }
4113
+ }
4114
+ return { retval: OK, gmsm: Gmsm, serr };
4115
+ }
4116
+
4117
+
4118
+ /**
4119
+ * Compute osculating orbital elements (Kepler elements) of a planet or asteroid.
4120
+ * C: swe_get_orbital_elements (swecl.c:5772-5960)
4121
+ */
4122
+ export function sweGetOrbitalElements(
4123
+ swed: SweData, tjdEt: number, ipl: number, iflag: number,
4124
+ ): OrbitalElementsResult {
4125
+ const dret = new Array(50).fill(0);
4126
+ let serr = '';
4127
+ if (ipl <= 0 || ipl === SE_MEAN_NODE || ipl === SE_TRUE_NODE ||
4128
+ ipl === SE_MEAN_APOG || ipl === SE_OSCU_APOG ||
4129
+ ipl === SE_INTP_APOG || ipl === SE_INTP_PERG) {
4130
+ return { retval: ERR, dret, serr: `error in swe_get_orbital_elements(): object ${ipl} not valid\n` };
4131
+ }
4132
+ const iflJ2000 = (iflag & SEFLG_EPHMASK) | SEFLG_J2000 | SEFLG_XYZ | SEFLG_TRUEPOS | SEFLG_NONUT | SEFLG_SPEED;
4133
+ const iflJ2000p = (iflag & SEFLG_EPHMASK) | SEFLG_J2000 | SEFLG_TRUEPOS | SEFLG_NONUT | SEFLG_SPEED;
4134
+ /* heliocentric distance */
4135
+ let res = sweCalc(swed, tjdEt, ipl, iflJ2000p);
4136
+ if (res.flags < 0) return { retval: ERR, dret, serr: res.serr };
4137
+ serr = res.serr;
4138
+ const r = res.xx[2];
4139
+ let iflJ2000c = iflJ2000;
4140
+ if (ipl !== SE_MOON) {
4141
+ if ((iflag & SEFLG_BARYCTR) && r > 6) {
4142
+ iflJ2000c |= SEFLG_BARYCTR;
4143
+ } else {
4144
+ iflJ2000c |= SEFLG_HELCTR;
4145
+ }
4146
+ }
4147
+ /* GM */
4148
+ const gmRes = getGmsm(swed, tjdEt, ipl, iflag, r);
4149
+ if (gmRes.retval === ERR) return { retval: ERR, dret, serr: gmRes.serr };
4150
+ const Gmsm = gmRes.gmsm;
4151
+ if (gmRes.serr) serr = gmRes.serr;
4152
+ /* J2000 ecliptic XYZ + speed */
4153
+ res = sweCalc(swed, tjdEt, ipl, iflJ2000c);
4154
+ if (res.flags < 0) return { retval: ERR, dret, serr: res.serr };
4155
+ if (res.serr) serr = res.serr;
4156
+ const xpos = [res.xx[0], res.xx[1], res.xx[2], res.xx[3], res.xx[4], res.xx[5]];
4157
+ /* EMB for Earth */
4158
+ if (ipl === SE_EARTH) {
4159
+ const moonRes = sweCalc(swed, tjdEt, SE_MOON, iflJ2000c & ~(SEFLG_BARYCTR | SEFLG_HELCTR));
4160
+ if (moonRes.flags < 0) return { retval: ERR, dret, serr: moonRes.serr };
4161
+ for (let j = 0; j <= 5; j++)
4162
+ xpos[j] += moonRes.xx[j] / (EARTH_MOON_MRAT + 1.0);
4163
+ }
4164
+ const fac = xpos[2] / xpos[5];
4165
+ const sgn = xpos[5] / Math.abs(xpos[5]);
4166
+ const xn = [0, 0, 0];
4167
+ const xs = [0, 0, 0];
4168
+ for (let j = 0; j <= 2; j++) {
4169
+ xn[j] = (xpos[j] - fac * xpos[j + 3]) * sgn;
4170
+ xs[j] = -xn[j];
4171
+ }
4172
+ /* node */
4173
+ let rxy = Math.sqrt(xn[0] * xn[0] + xn[1] * xn[1]);
4174
+ const cosnode = xn[0] / rxy;
4175
+ const sinnode = xn[1] / rxy;
4176
+ /* inclination */
4177
+ const xnorm = new Float64Array(3);
4178
+ swiCrossProd(xpos, [xpos[3], xpos[4], xpos[5]], xnorm);
4179
+ rxy = xnorm[0] * xnorm[0] + xnorm[1] * xnorm[1];
4180
+ const c2 = rxy + xnorm[2] * xnorm[2];
4181
+ let rxyz = Math.sqrt(c2);
4182
+ rxy = Math.sqrt(rxy);
4183
+ const sinincl = rxy / rxyz;
4184
+ let cosincl = Math.sqrt(1 - sinincl * sinincl);
4185
+ if (xnorm[2] < 0) cosincl = -cosincl; /* retrograde */
4186
+ const incl = Math.acos(cosincl) * RADTODEG;
4187
+ /* argument of latitude */
4188
+ const cosu = xpos[0] * cosnode + xpos[1] * sinnode;
4189
+ const sinu = xpos[2] / sinincl;
4190
+ const uu = Math.atan2(sinu, cosu);
4191
+ /* semi-axis */
4192
+ rxyz = Math.sqrt(squareSum(xpos));
4193
+ const v2 = xpos[3] * xpos[3] + xpos[4] * xpos[4] + xpos[5] * xpos[5];
4194
+ const sema = 1.0 / (2.0 / rxyz - v2 / Gmsm);
4195
+ /* eccentricity */
4196
+ const pp = c2 / Gmsm;
4197
+ let ecceRatio = pp / sema;
4198
+ if (ecceRatio > 1) ecceRatio = 1;
4199
+ const ecce = Math.sqrt(1 - ecceRatio);
4200
+ /* eccentric anomaly */
4201
+ let ecce2 = ecce;
4202
+ if (ecce2 === 0) ecce2 = 0.0000000001;
4203
+ const cosE = (1 / ecce2) * (1 - rxyz / sema);
4204
+ const sinE = (1 / ecce2 / Math.sqrt(sema * Gmsm)) * dotProd(xpos, [xpos[3], xpos[4], xpos[5]]);
4205
+ const eanom = sweDegnorm(Math.atan2(sinE, cosE) * RADTODEG);
4206
+ /* true anomaly */
4207
+ let ny = 2 * Math.atan(Math.sqrt((1 + ecce) / (1 - ecce)) * sinE / (1 + cosE));
4208
+ let tanom = sweDegnorm(ny * RADTODEG);
4209
+ if (eanom > 180 && tanom < 180) tanom += 180;
4210
+ if (eanom < 180 && tanom > 180) tanom -= 180;
4211
+ /* mean anomaly */
4212
+ const manom = sweDegnorm(eanom - ecce * RADTODEG * Math.sin(eanom * DEGTORAD));
4213
+ /* distance of perihelion from ascending node */
4214
+ const xq = [0, 0, 0];
4215
+ xq[0] = swiMod2PI(uu - ny);
4216
+ const parg = xq[0] * RADTODEG;
4217
+ xq[1] = 0;
4218
+ xq[2] = sema * (1 - ecce);
4219
+ /* transformation to ecliptic coordinates */
4220
+ swiPolcart(xq, xq);
4221
+ swiCoortrf2(xq, xq, -sinincl, cosincl);
4222
+ swiCartpol(xq, xq);
4223
+ /* adding node → perihelion in ecl. coord. */
4224
+ xq[0] += Math.atan2(sinnode, cosnode);
4225
+ const xa = [0, 0, 0];
4226
+ xa[0] = swiMod2PI(xq[0] + PI);
4227
+ xa[1] = -xq[1];
4228
+ xa[2] = sema * (1 + ecce);
4229
+ swiPolcart(xq, xq);
4230
+ swiPolcart(xa, xa);
4231
+ /* new distance of node from orbital ellipse */
4232
+ ny = swiMod2PI(ny - uu);
4233
+ const ny2 = swiMod2PI(ny + PI);
4234
+ const cosE_n = Math.cos(2 * Math.atan(Math.tan(ny / 2) / Math.sqrt((1 + ecce) / (1 - ecce))));
4235
+ const cosE2 = Math.cos(2 * Math.atan(Math.tan(ny2 / 2) / Math.sqrt((1 + ecce) / (1 - ecce))));
4236
+ const rn = sema * (1 - ecce * cosE_n);
4237
+ const rn2 = sema * (1 - ecce * cosE2);
4238
+ const ro = Math.sqrt(squareSum(xn));
4239
+ const ro2 = Math.sqrt(squareSum(xs));
4240
+ for (let j = 0; j <= 2; j++) {
4241
+ xn[j] *= rn / ro;
4242
+ xs[j] *= rn2 / ro2;
4243
+ }
4244
+ swiCartpol(xn, xn);
4245
+ swiCartpol(xq, xq);
4246
+ const node = xn[0] * RADTODEG;
4247
+ const peri = sweDegnorm(node + parg);
4248
+ const mlon = sweDegnorm(manom + peri);
4249
+ let csid = sema * Math.sqrt(sema); // sidereal period in sidereal years
4250
+ if (ipl === SE_MOON) {
4251
+ const semam = sema * AUNIT / 383397772.5;
4252
+ csid = semam * Math.sqrt(semam);
4253
+ csid *= 27.32166 / 365.25636300;
4254
+ }
4255
+ const dmot = 0.9856076686 / csid; // daily motion
4256
+ csid *= 365.25636 / 365.242189; // sidereal period in tropical years J2000
4257
+ // daily motion due to precession (Simon et al. 1994)
4258
+ const T = (tjdEt - J2000) / 365250.0;
4259
+ const T2 = T * T; const T3 = T2 * T; const T4 = T3 * T; const T5 = T4 * T;
4260
+ const pa = (50288.200 + 222.4045 * T + 0.2095 * T2 - 0.9408 * T3 - 0.0090 * T4 + 0.0010 * T5) / 3600.0 / 365250.0;
4261
+ const ysid = 360.0 / ((1295977422.83429 - 2 * 2.0441 * T - 3 * 0.00523 * T * T) / 3600.0 / 365250.0);
4262
+ const ytrop = 360.0 / ((1296027711.03429 + 2 * 109.15809 * T + 3 * 0.07207 * T2 - 4 * 0.23530 * T3 - 5 * 0.00180 * T4 + 6 * 0.00020 * T5) / 3600.0 / 365250.0);
4263
+ let ctro = 360.0 / (dmot + pa) / 365.242189;
4264
+ ctro *= ysid / ytrop;
4265
+ let csyn: number;
4266
+ if (ipl === SE_EARTH) csyn = 0;
4267
+ else csyn = 360.0 / (0.9856076686 - dmot);
4268
+ dret[0] = sema;
4269
+ dret[1] = ecce;
4270
+ dret[2] = incl;
4271
+ dret[3] = node;
4272
+ dret[4] = parg;
4273
+ dret[5] = peri;
4274
+ dret[6] = manom;
4275
+ dret[7] = tanom;
4276
+ dret[8] = eanom;
4277
+ dret[9] = mlon;
4278
+ dret[10] = csid;
4279
+ dret[11] = dmot;
4280
+ dret[12] = ctro;
4281
+ dret[13] = csyn;
4282
+ dret[14] = tjdEt - dret[6] / dmot;
4283
+ dret[15] = sema * (1 - ecce);
4284
+ dret[16] = sema * (1 + ecce);
4285
+ return { retval: OK, dret, serr };
4286
+ }
4287
+
4288
+ /**
4289
+ * PQR transformation matrix + orbit constants from 5 Kepler elements.
4290
+ * C: osc_get_orbit_constants (swecl.c:5962-5988)
4291
+ */
4292
+ function oscGetOrbitConstants(dp: number[], pqr: number[]): void {
4293
+ const sema = dp[0];
4294
+ const ecce = dp[1];
4295
+ const incl = dp[2];
4296
+ const node = dp[3];
4297
+ const parg = dp[4];
4298
+ const cosnode = Math.cos(node * DEGTORAD);
4299
+ const sinnode = Math.sin(node * DEGTORAD);
4300
+ const cosincl = Math.cos(incl * DEGTORAD);
4301
+ const sinincl = Math.sin(incl * DEGTORAD);
4302
+ const cosparg = Math.cos(parg * DEGTORAD);
4303
+ const sinparg = Math.sin(parg * DEGTORAD);
4304
+ const fac = Math.sqrt((1 - ecce) * (1 + ecce));
4305
+ pqr[0] = cosparg * cosnode - sinparg * cosincl * sinnode;
4306
+ pqr[1] = -sinparg * cosnode - cosparg * cosincl * sinnode;
4307
+ pqr[2] = sinincl * sinnode;
4308
+ pqr[3] = cosparg * sinnode + sinparg * cosincl * cosnode;
4309
+ pqr[4] = -sinparg * sinnode + cosparg * cosincl * cosnode;
4310
+ pqr[5] = -sinincl * cosnode;
4311
+ pqr[6] = sinparg * sinincl;
4312
+ pqr[7] = cosparg * sinincl;
4313
+ pqr[8] = cosincl;
4314
+ pqr[9] = sema;
4315
+ pqr[10] = ecce;
4316
+ pqr[11] = fac;
4317
+ }
4318
+
4319
+ /**
4320
+ * Eccentric anomaly → ecliptic cartesian XYZ.
4321
+ * C: osc_get_ecl_pos (swecl.c:5990-6004)
4322
+ */
4323
+ function oscGetEclPos(ean: number, pqr: number[], xp: number[]): void {
4324
+ const cose = Math.cos(ean * DEGTORAD);
4325
+ const sine = Math.sin(ean * DEGTORAD);
4326
+ const sema = pqr[9];
4327
+ const ecce = pqr[10];
4328
+ const fac = pqr[11];
4329
+ const x0 = sema * (cose - ecce);
4330
+ const x1 = sema * fac * sine;
4331
+ xp[0] = pqr[0] * x0 + pqr[1] * x1;
4332
+ xp[1] = pqr[3] * x0 + pqr[4] * x1;
4333
+ xp[2] = pqr[6] * x0 + pqr[7] * x1;
4334
+ }
4335
+
4336
+ /**
4337
+ * Euclidean 3D distance between two vectors.
4338
+ * C: get_dist_from_2_vectors (swecl.c:6006-6013)
4339
+ */
4340
+ function getDistFrom2Vectors(x1: number[], x2: number[]): number {
4341
+ const r0 = x1[0] - x2[0];
4342
+ const r1 = x1[1] - x2[1];
4343
+ const r2 = x1[2] - x2[2];
4344
+ return Math.sqrt(r0 * r0 + r1 * r1 + r2 * r2);
4345
+ }
4346
+
4347
+ /**
4348
+ * Hill-climbing search for maximum distance on one ellipse relative to a fixed point.
4349
+ * C: osc_iterate_max_dist (swecl.c:6015-6049)
4350
+ */
4351
+ function oscIterateMaxDist(pqr: number[], xa: number[], xb: number[], highPrec: boolean): { deanopt: number; drmax: number } {
4352
+ let ean = 0;
4353
+ let eansv = 0;
4354
+ const dstepMin = highPrec ? 0.000001 : 1;
4355
+ oscGetEclPos(ean, pqr, xa);
4356
+ let r = getDistFrom2Vectors(xb, xa);
4357
+ let rmax = r;
4358
+ let dstep = 1;
4359
+ while (dstep >= dstepMin) {
4360
+ for (let i = 0; i < 2; i++) {
4361
+ while (r >= rmax) {
4362
+ eansv = ean;
4363
+ ean += (i === 0) ? dstep : -dstep;
4364
+ oscGetEclPos(ean, pqr, xa);
4365
+ r = getDistFrom2Vectors(xb, xa);
4366
+ if (r > rmax) rmax = r;
4367
+ }
4368
+ ean = eansv;
4369
+ r = rmax;
4370
+ }
4371
+ ean = eansv;
4372
+ r = rmax;
4373
+ dstep /= 10;
4374
+ }
4375
+ return { deanopt: eansv, drmax: rmax };
4376
+ }
4377
+
4378
+ /**
4379
+ * Hill-climbing search for minimum distance on one ellipse relative to a fixed point.
4380
+ * C: osc_iterate_min_dist (swecl.c:6051-6085)
4381
+ */
4382
+ function oscIterateMinDist(pqr: number[], xa: number[], xb: number[], highPrec: boolean): { deanopt: number; drmin: number } {
4383
+ let ean = 0;
4384
+ let eansv = 0;
4385
+ const dstepMin = highPrec ? 0.000001 : 1;
4386
+ oscGetEclPos(ean, pqr, xa);
4387
+ let r = getDistFrom2Vectors(xb, xa);
4388
+ let rmin = r;
4389
+ let dstep = 1;
4390
+ while (dstep >= dstepMin) {
4391
+ for (let i = 0; i < 2; i++) {
4392
+ while (r <= rmin) {
4393
+ eansv = ean;
4394
+ ean += (i === 0) ? dstep : -dstep;
4395
+ oscGetEclPos(ean, pqr, xa);
4396
+ r = getDistFrom2Vectors(xb, xa);
4397
+ if (r < rmin) rmin = r;
4398
+ }
4399
+ ean = eansv;
4400
+ r = rmin;
4401
+ }
4402
+ ean = eansv;
4403
+ r = rmin;
4404
+ dstep /= 10;
4405
+ }
4406
+ return { deanopt: eansv, drmin: rmin };
4407
+ }
4408
+
4409
+ /**
4410
+ * Max/min/true heliocentric distance from Kepler elements.
4411
+ * C: orbit_max_min_true_distance_helio (swecl.c:6090-6117)
4412
+ */
4413
+ function orbitMaxMinTrueDistanceHelio(
4414
+ swed: SweData, tjdEt: number, ipl: number, iflag: number,
4415
+ ): OrbitDistanceResult {
4416
+ let ipli = ipl;
4417
+ const iflagi = iflag & (SEFLG_EPHMASK | SEFLG_HELCTR | SEFLG_BARYCTR);
4418
+ if (ipl === SE_SUN) ipli = SE_EARTH;
4419
+ const elemRes = sweGetOrbitalElements(swed, tjdEt, ipli, iflagi);
4420
+ if (elemRes.retval === ERR) return { retval: ERR, dmax: 0, dmin: 0, dtrue: 0, serr: elemRes.serr };
4421
+ const de = elemRes.dret;
4422
+ const dmax = de[16];
4423
+ const dmin = de[15];
4424
+ const pqri = new Array(20).fill(0);
4425
+ oscGetOrbitConstants(de, pqri);
4426
+ const xinner = [0, 0, 0];
4427
+ oscGetEclPos(de[8], pqri, xinner);
4428
+ const dtrue = Math.sqrt(xinner[0] * xinner[0] + xinner[1] * xinner[1] + xinner[2] * xinner[2]);
4429
+ return { retval: elemRes.retval, dmax, dmin, dtrue, serr: elemRes.serr };
4430
+ }
4431
+
4432
+ /**
4433
+ * Compute maximum possible distance, minimum possible distance, and current true distance
4434
+ * of a planet geocentrically (or heliocentrically).
4435
+ * C: swe_orbit_max_min_true_distance (swecl.c:6159-6276)
4436
+ */
4437
+ export function sweOrbitMaxMinTrueDistance(
4438
+ swed: SweData, tjdEt: number, ipl: number, iflag: number,
4439
+ ): OrbitDistanceResult {
4440
+ const iflagi = iflag & (SEFLG_EPHMASK | SEFLG_HELCTR | SEFLG_BARYCTR);
4441
+ /* Sun, Moon, or heliocentric: delegate */
4442
+ if (ipl === SE_SUN || ipl === SE_MOON || (iflagi & (SEFLG_HELCTR | SEFLG_BARYCTR))) {
4443
+ return orbitMaxMinTrueDistanceHelio(swed, tjdEt, ipl, iflagi);
4444
+ }
4445
+ /* planet orbital elements */
4446
+ const dpRes = sweGetOrbitalElements(swed, tjdEt, ipl, iflagi);
4447
+ if (dpRes.retval === ERR) return { retval: ERR, dmax: 0, dmin: 0, dtrue: 0, serr: dpRes.serr };
4448
+ const dp = dpRes.dret;
4449
+ /* Earth orbital elements */
4450
+ const deRes = sweGetOrbitalElements(swed, tjdEt, SE_EARTH, iflagi);
4451
+ if (deRes.retval === ERR) return { retval: ERR, dmax: 0, dmin: 0, dtrue: 0, serr: deRes.serr };
4452
+ const de = deRes.dret;
4453
+ /* determine outer/inner */
4454
+ let douter: number[], dinner: number[];
4455
+ if (de[0] > dp[0]) { douter = de; dinner = dp; }
4456
+ else { douter = dp; dinner = de; }
4457
+ const pqro = new Array(20).fill(0);
4458
+ const pqri = new Array(20).fill(0);
4459
+ oscGetOrbitConstants(douter, pqro);
4460
+ oscGetOrbitConstants(dinner, pqri);
4461
+ let eano = douter[8];
4462
+ let eani = dinner[8];
4463
+ const xouter = [0, 0, 0];
4464
+ const xinner = [0, 0, 0];
4465
+ oscGetEclPos(eano, pqro, xouter);
4466
+ oscGetEclPos(eani, pqri, xinner);
4467
+ const rtrue = getDistFrom2Vectors(xouter, xinner);
4468
+ /* coarse search: 182×182 grid */
4469
+ const ncnt = 182;
4470
+ const dstep = 2;
4471
+ const maxXouter = [0, 0, 0];
4472
+ const maxXinner = [0, 0, 0];
4473
+ const minXouter = [0, 0, 0];
4474
+ const minXinner = [0, 0, 0];
4475
+ let rmax = 0, rmin = 100000000;
4476
+ let maxEanisv = 0, maxEanosv = 0, minEanisv = 0, minEanosv = 0;
4477
+ for (let j = 0; j < ncnt; j++) {
4478
+ eano = j * dstep;
4479
+ oscGetEclPos(eano, pqro, xouter);
4480
+ for (let i = 0; i < ncnt; i++) {
4481
+ eani = i;
4482
+ oscGetEclPos(eani, pqri, xinner);
4483
+ const rr = getDistFrom2Vectors(xouter, xinner);
4484
+ if (rr > rmax) {
4485
+ rmax = rr;
4486
+ maxEanisv = eani;
4487
+ maxEanosv = eano;
4488
+ for (let k = 0; k < 3; k++) { maxXouter[k] = xouter[k]; maxXinner[k] = xinner[k]; }
4489
+ }
4490
+ if (rr < rmin) {
4491
+ rmin = rr;
4492
+ minEanisv = eani;
4493
+ minEanosv = eano;
4494
+ for (let k = 0; k < 3; k++) { minXouter[k] = xouter[k]; minXinner[k] = xinner[k]; }
4495
+ }
4496
+ }
4497
+ }
4498
+ /* iterative refinement: maximum distance */
4499
+ eani = maxEanisv;
4500
+ eano = maxEanosv;
4501
+ for (let k = 0; k < 3; k++) { xouter[k] = maxXouter[k]; xinner[k] = maxXinner[k]; }
4502
+ let rmaxsv = 0;
4503
+ const nitermax = 300;
4504
+ for (let k = 0; k <= nitermax; k++) {
4505
+ let res = oscIterateMaxDist(pqri, xinner, xouter, true);
4506
+ eani = res.deanopt; rmax = res.drmax;
4507
+ res = oscIterateMaxDist(pqro, xouter, xinner, true);
4508
+ eano = res.deanopt; rmax = res.drmax;
4509
+ if (k > 0 && Math.abs(rmax - rmaxsv) < 0.00000001) break;
4510
+ rmaxsv = rmax;
4511
+ }
4512
+ /* iterative refinement: minimum distance */
4513
+ eani = minEanisv;
4514
+ eano = minEanosv;
4515
+ for (let k = 0; k < 3; k++) { xouter[k] = minXouter[k]; xinner[k] = minXinner[k]; }
4516
+ let rminsv = 0;
4517
+ for (let k = 0; k <= nitermax; k++) {
4518
+ let res = oscIterateMinDist(pqri, xinner, xouter, true);
4519
+ eani = res.deanopt; rmin = res.drmin;
4520
+ res = oscIterateMinDist(pqro, xouter, xinner, true);
4521
+ eano = res.deanopt; rmin = res.drmin;
4522
+ if (k > 0 && Math.abs(rmin - rminsv) < 0.00000001) break;
4523
+ rminsv = rmin;
4524
+ }
4525
+ return { retval: dpRes.retval, dmax: rmax, dmin: rmin, dtrue: rtrue, serr: dpRes.serr };
4526
+ }