@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
@@ -0,0 +1,302 @@
1
+ # Sidereal Time
2
+
3
+ **Sidereal time** is "star time" -- it measures the rotation of the Earth relative to the distant stars, rather than relative to the Sun. While a **solar day** (noon to noon) is 24 hours, a **sidereal day** (the time for the stars to return to the same position in the sky) is about **23 hours, 56 minutes, and 4 seconds**. The difference arises because the Earth is simultaneously orbiting the Sun: after one full rotation relative to the stars, it has moved about 1 degree along its orbit, so it needs to rotate a little extra to bring the Sun back to the same position.
4
+
5
+ **Greenwich Sidereal Time (GST)** is the sidereal time at the Prime Meridian (longitude 0 degrees). It tells you which Right Ascension is currently crossing the meridian at Greenwich. To get **Local Sidereal Time (LST)** for any other location, simply add the observer's longitude (in hours):
6
+
7
+ ```
8
+ LST = GST + (longitude_degrees / 15)
9
+ ```
10
+
11
+ Sidereal time is fundamental to observational astronomy (it tells you which stars and constellations are visible right now), and in astrology it is the basis for house calculations: the **ARMC** (Ascensional Right Ascension of the Midheaven) is simply sidereal time multiplied by 15 (converting hours to degrees).
12
+
13
+ ---
14
+
15
+ ## Quick Example
16
+
17
+ ```typescript
18
+ import { SwissEph } from '../index';
19
+
20
+ const swe = new SwissEph();
21
+
22
+ // Greenwich Sidereal Time at J2000.0
23
+ const jd = SwissEph.julianDay(2000, 1, 1, 12);
24
+ const gst = swe.siderealTime(jd);
25
+
26
+ console.log(`Greenwich Sidereal Time: ${gst.toFixed(6)} hours`);
27
+ // ~18.697374 hours
28
+
29
+ // Convert to hh:mm:ss
30
+ const h = Math.floor(gst);
31
+ const m = Math.floor((gst - h) * 60);
32
+ const s = ((gst - h) * 60 - m) * 60;
33
+ console.log(`GST: ${h}h ${m}m ${s.toFixed(2)}s`);
34
+
35
+ swe.close();
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Detailed Examples
41
+
42
+ ### Local Sidereal Time for any location
43
+
44
+ To get the local sidereal time, add the observer's longitude (converted to hours by dividing by 15). East longitudes are positive, west longitudes are negative.
45
+
46
+ ```typescript
47
+ import { SwissEph } from '../index';
48
+
49
+ const swe = new SwissEph();
50
+
51
+ const jd = SwissEph.julianDay(2024, 6, 21, 22, ); // June 21, 2024 at 22:00 UT
52
+
53
+ const gst = swe.siderealTime(jd);
54
+ console.log(`Greenwich Sidereal Time: ${formatHMS(gst)}`);
55
+
56
+ // Local Sidereal Time for different cities
57
+ const cities = [
58
+ { name: 'London', lon: -0.128 },
59
+ { name: 'New York', lon: -74.006 },
60
+ { name: 'Tokyo', lon: 139.692 },
61
+ { name: 'Sydney', lon: 151.209 },
62
+ { name: 'Mumbai', lon: 72.878 },
63
+ ];
64
+
65
+ for (const city of cities) {
66
+ // LST = GST + longitude/15, then normalize to [0, 24)
67
+ let lst = gst + city.lon / 15;
68
+ while (lst < 0) lst += 24;
69
+ while (lst >= 24) lst -= 24;
70
+
71
+ console.log(`${city.name.padEnd(12)} LST: ${formatHMS(lst)}`);
72
+ }
73
+
74
+ function formatHMS(hours: number): string {
75
+ const h = Math.floor(hours);
76
+ const m = Math.floor((hours - h) * 60);
77
+ const s = ((hours - h) * 60 - m) * 60;
78
+ return `${String(h).padStart(2, '0')}h ${String(m).padStart(2, '0')}m ${s.toFixed(2).padStart(5, '0')}s`;
79
+ }
80
+
81
+ swe.close();
82
+ ```
83
+
84
+ ### What is on the meridian right now?
85
+
86
+ The Local Sidereal Time directly tells you the Right Ascension that is currently crossing your local meridian (due south in the Northern Hemisphere). Any object with that RA is at its highest point in the sky (its culmination or upper transit).
87
+
88
+ ```typescript
89
+ import { SwissEph } from '../index';
90
+ import { SE_SUN, SE_MOON, SE_MARS, SE_JUPITER, SEFLG_EQUATORIAL } from '../../constants';
91
+
92
+ const swe = new SwissEph();
93
+
94
+ // Current observer: Los Angeles, some evening
95
+ const lon = -118.243;
96
+ const jd = SwissEph.julianDay(2024, 8, 15, 4); // 4:00 UT = ~9 PM PDT
97
+
98
+ const gst = swe.siderealTime(jd);
99
+ let lst = gst + lon / 15;
100
+ while (lst < 0) lst += 24;
101
+ while (lst >= 24) lst -= 24;
102
+
103
+ console.log(`Local Sidereal Time: ${lst.toFixed(4)} hours`);
104
+ console.log(`RA on the meridian: ${lst.toFixed(4)} hours = ${(lst * 15).toFixed(2)} deg`);
105
+ console.log();
106
+
107
+ // Check how far each planet is from the meridian
108
+ const bodies = [
109
+ { id: SE_SUN, name: 'Sun' },
110
+ { id: SE_MOON, name: 'Moon' },
111
+ { id: SE_MARS, name: 'Mars' },
112
+ { id: SE_JUPITER, name: 'Jupiter' },
113
+ ];
114
+
115
+ for (const b of bodies) {
116
+ const pos = swe.calc(jd, b.id, SEFLG_EQUATORIAL);
117
+ const raHours = pos.longitude / 15; // Convert RA from degrees to hours
118
+
119
+ // Hour angle = LST - RA (how far west of the meridian)
120
+ let ha = lst - raHours;
121
+ while (ha < -12) ha += 24;
122
+ while (ha > 12) ha -= 24;
123
+
124
+ const side = ha > 0 ? 'west of meridian' : 'east of meridian';
125
+ console.log(
126
+ `${b.name.padEnd(9)} RA: ${raHours.toFixed(2)}h ` +
127
+ `HA: ${Math.abs(ha).toFixed(2)}h ${side}` +
128
+ `${Math.abs(ha) < 1 ? ' <-- NEAR TRANSIT' : ''}`
129
+ );
130
+ }
131
+
132
+ swe.close();
133
+ ```
134
+
135
+ ### ARMC: Sidereal time as the basis for house cusps
136
+
137
+ In house calculations, the starting point is the **ARMC** (Ascensional Right Ascension of the MC, also known simply as the MC in Right Ascension). It equals the Local Sidereal Time expressed in degrees:
138
+
139
+ ```typescript
140
+ import { SwissEph } from '../index';
141
+
142
+ const swe = new SwissEph();
143
+
144
+ // London, J2000.0
145
+ const jd = SwissEph.julianDay(2000, 1, 1, 12);
146
+ const londonLon = -0.128;
147
+ const londonLat = 51.507;
148
+
149
+ // Get sidereal time and compute ARMC
150
+ const gst = swe.siderealTime(jd);
151
+ let lst = gst + londonLon / 15;
152
+ while (lst < 0) lst += 24;
153
+ while (lst >= 24) lst -= 24;
154
+
155
+ const armc = lst * 15; // Convert hours to degrees
156
+ console.log(`ARMC: ${armc.toFixed(4)} deg`);
157
+
158
+ // Verify: the houses() function returns ARMC as well
159
+ const houses = swe.houses(jd, { longitude: londonLon, latitude: londonLat });
160
+ console.log(`ARMC from houses(): ${houses.armc.toFixed(4)} deg`);
161
+ console.log(`MC: ${houses.mc.toFixed(4)} deg`);
162
+
163
+ // Note: ARMC and MC are NOT the same thing!
164
+ // ARMC is in Right Ascension (equatorial).
165
+ // MC is the ecliptic longitude where the meridian crosses the ecliptic.
166
+
167
+ swe.close();
168
+ ```
169
+
170
+ ### Sidereal time throughout a day
171
+
172
+ Sidereal time advances about 24h 03m 56s in one solar day (because a sidereal day is shorter, the sidereal clock gains about 3 minutes and 56 seconds per solar day relative to a normal clock):
173
+
174
+ ```typescript
175
+ import { SwissEph } from '../index';
176
+
177
+ const swe = new SwissEph();
178
+
179
+ console.log('Sidereal time at Greenwich, March 20, 2024:');
180
+ console.log('UT Time GST');
181
+
182
+ for (let hour = 0; hour <= 24; hour += 3) {
183
+ const jd = SwissEph.julianDay(2024, 3, 20, hour);
184
+ const gst = swe.siderealTime(jd);
185
+
186
+ const h = Math.floor(gst);
187
+ const m = Math.floor((gst - h) * 60);
188
+ const s = ((gst - h) * 60 - m) * 60;
189
+ console.log(
190
+ `${String(hour).padStart(2, '0')}:00 UT ` +
191
+ `${String(h).padStart(2, '0')}h ${String(m).padStart(2, '0')}m ${s.toFixed(1).padStart(4)}s`
192
+ );
193
+ }
194
+
195
+ // Show the sidereal day length
196
+ const jd1 = SwissEph.julianDay(2024, 3, 20, 0);
197
+ const jd2 = SwissEph.julianDay(2024, 3, 21, 0);
198
+ const gst1 = swe.siderealTime(jd1);
199
+ let gst2 = swe.siderealTime(jd2);
200
+ if (gst2 < gst1) gst2 += 24;
201
+ const gain = (gst2 - gst1) - 24;
202
+ console.log(`\nSidereal time gained in 24 solar hours: ${(gain * 60).toFixed(2)} minutes`);
203
+ // ~3.94 minutes
204
+
205
+ swe.close();
206
+ ```
207
+
208
+ ### At what UT does a specific RA transit?
209
+
210
+ If you want to know when a specific Right Ascension crosses the local meridian:
211
+
212
+ ```typescript
213
+ import { SwissEph } from '../index';
214
+
215
+ const swe = new SwissEph();
216
+
217
+ // When does RA = 6h 45m (Sirius, approximately) transit at Greenwich?
218
+ const targetRA = 6 + 45 / 60; // 6.75 hours
219
+
220
+ // Start from midnight on a given date
221
+ const jdStart = SwissEph.julianDay(2024, 1, 15, 0);
222
+ const gstStart = swe.siderealTime(jdStart);
223
+
224
+ // How many sidereal hours until the target RA reaches the meridian?
225
+ let wait = targetRA - gstStart;
226
+ while (wait < 0) wait += 24;
227
+
228
+ // Convert sidereal hours to solar hours
229
+ // 1 sidereal hour = 0.99727 solar hours
230
+ const solarHours = wait * 0.99727;
231
+
232
+ const transitJd = jdStart + solarHours / 24;
233
+ const date = SwissEph.fromJulianDay(transitJd);
234
+
235
+ console.log(`Sirius (RA ~6h 45m) transits Greenwich at approximately:`);
236
+ console.log(` ${date.year}-${String(date.month).padStart(2, '0')}-${String(date.day).padStart(2, '0')} ` +
237
+ `${Math.floor(date.hour)}:${String(Math.floor((date.hour % 1) * 60)).padStart(2, '0')} UT`);
238
+
239
+ swe.close();
240
+ ```
241
+
242
+ ---
243
+
244
+ ## Deep Explanation
245
+
246
+ ### Sidereal day vs solar day
247
+
248
+ A **sidereal day** is one complete rotation of Earth relative to the distant stars: 23h 56m 04.0905s of mean solar time. A **solar day** (noon to noon) is longer because while the Earth rotates, it also advances about 1 degree along its orbit. The Sun appears to drift about 1 degree eastward per day against the background stars, so after one sidereal day the Earth must rotate about 4 more minutes to "catch up" to the Sun.
249
+
250
+ This means there are approximately 366.25 sidereal days in one year but only 365.25 solar days. The extra sidereal day comes from the Earth's orbital motion itself -- one full orbit adds one extra rotation relative to the stars.
251
+
252
+ ### Mean vs apparent sidereal time
253
+
254
+ The Swiss Ephemeris `siderealTime()` returns **mean sidereal time** corrected for nutation (effectively giving apparent sidereal time). The distinction:
255
+
256
+ - **Mean sidereal time** uses the mean equinox, ignoring the short-period nutation oscillation
257
+ - **Apparent sidereal time** accounts for nutation, which causes the equinox to wobble by up to about 1.2 seconds of time (17" of arc)
258
+
259
+ The nutation correction is small but important for precise house-cusp calculations.
260
+
261
+ ### The relationship to Right Ascension
262
+
263
+ Right Ascension is measured along the celestial equator, starting from the vernal equinox (the point where the Sun crosses the equator heading north), and increasing eastward. It is measured in hours (0h to 24h), where 1h = 15 degrees.
264
+
265
+ The Local Sidereal Time is numerically equal to the Right Ascension currently on the observer's meridian. So if LST = 12h 30m, any star with RA = 12h 30m is at its highest point in your sky right now. Stars with smaller RA are west of the meridian (already past their highest), and stars with larger RA are east (still rising).
266
+
267
+ ### Hour Angle
268
+
269
+ The **Hour Angle (HA)** of an object is the difference between the Local Sidereal Time and the object's Right Ascension:
270
+
271
+ ```
272
+ HA = LST - RA
273
+ ```
274
+
275
+ - HA = 0h: the object is on the meridian (transiting)
276
+ - HA > 0: the object is west of the meridian (past transit, heading toward setting)
277
+ - HA < 0: the object is east of the meridian (still rising, before transit)
278
+ - HA = +6h: the object is on the western horizon (approximately)
279
+ - HA = -6h: the object is on the eastern horizon (approximately)
280
+
281
+ ### ARMC and house calculations
282
+
283
+ The ARMC (Ascensional Right Ascension of the MC) is the fundamental input for house-cusp calculations. It equals the Local Sidereal Time expressed in degrees (multiply hours by 15). Given the ARMC, the geographic latitude, and the obliquity of the ecliptic, all house systems can compute their cusps.
284
+
285
+ The MC (Medium Coeli, Midheaven) is the ecliptic longitude where the meridian intersects the ecliptic. It is derived from the ARMC by converting from equatorial to ecliptic coordinates. The MC is NOT simply the ARMC in different units -- the conversion depends on the obliquity.
286
+
287
+ ### Practical uses of sidereal time
288
+
289
+ - **Telescope alignment**: When setting up a Go-To telescope or an equatorial mount, you need to know the current sidereal time to sync the mount's RA axis
290
+ - **Observing planning**: Knowing the current LST tells you which part of the sky is best placed for observation (objects near the meridian are highest and least affected by atmospheric refraction)
291
+ - **House cusps**: In astrology, sidereal time is the foundation of house calculations via the ARMC
292
+ - **Satellite tracking**: Communication satellites and ground stations use sidereal time to predict satellite positions relative to Earth's rotation
293
+
294
+ ### Sidereal time at the vernal equinox
295
+
296
+ At the moment of the vernal equinox (when the Sun crosses 0h RA), the Greenwich Sidereal Time at solar noon equals approximately 0h. This is the annual "reset point." From there, sidereal time gains about 3 minutes 56 seconds per solar day, completing a full 24-hour cycle in one year.
297
+
298
+ Specifically, at the March equinox, the GST at 12:00 UT is close to 0h (it varies slightly because the equinox does not fall at exactly noon). At the June solstice, the GST at noon is close to 6h. At the September equinox, it is close to 12h. At the December solstice, it is close to 18h.
299
+
300
+ ### Input time scale
301
+
302
+ The `siderealTime()` method expects the input Julian Day to be in **Universal Time (UT)**, regardless of the `timeMode` setting of the SwissEph instance. This is because sidereal time is a measure of Earth's rotation, which is defined in UT. The function internally applies Delta-T to compute the nutation correction.
@@ -0,0 +1,379 @@
1
+ # Solar Eclipses
2
+
3
+ A **solar eclipse** occurs when the Moon passes between the Earth and the Sun, blocking some or all of the Sun's light. This happens because the Moon's orbit occasionally brings it directly in line between the Earth and Sun. Despite the Sun being about 400 times larger than the Moon, it is also about 400 times farther away, so they appear nearly the same size in our sky -- making total solar eclipses possible.
4
+
5
+ Solar eclipses are among the most dramatic astronomical events. They can only happen during a New Moon, but they do not happen at every New Moon because the Moon's orbit is tilted about 5 degrees relative to the ecliptic. Only when the Moon is near one of its orbital nodes (where its orbit crosses the ecliptic plane) can an eclipse occur.
6
+
7
+ The Swiss Ephemeris can predict solar eclipses with high precision: finding when they happen, what type they are, where the central path falls on Earth, and how the eclipse appears from any given location.
8
+
9
+ ---
10
+
11
+ ## Quick Example
12
+
13
+ ```typescript
14
+ import { SwissEph } from '../index';
15
+
16
+ const swe = new SwissEph();
17
+
18
+ // Find the next solar eclipse after January 1, 2024
19
+ const jd = SwissEph.julianDay(2024, 1, 1, 0);
20
+ const eclipse = swe.solarEclipseGlobal(jd);
21
+
22
+ // Convert the time of maximum eclipse to a readable date
23
+ const date = SwissEph.fromJulianDay(eclipse.maximum);
24
+ console.log(`Next solar eclipse: ${date.year}-${date.month}-${date.day}`);
25
+ console.log(`Maximum at JD: ${eclipse.maximum.toFixed(6)}`);
26
+
27
+ swe.close();
28
+ ```
29
+
30
+ ---
31
+
32
+ ## Detailed Examples
33
+
34
+ ### Finding the 2024 April 8 total solar eclipse
35
+
36
+ ```typescript
37
+ import { SwissEph } from '../index';
38
+ import { SE_ECL_TOTAL } from '../../constants';
39
+
40
+ const swe = new SwissEph();
41
+
42
+ // Search for the next total solar eclipse after 2024-01-01
43
+ const jd = SwissEph.julianDay(2024, 1, 1, 0);
44
+ const eclipse = swe.solarEclipseGlobal(jd, SE_ECL_TOTAL);
45
+
46
+ const date = SwissEph.fromJulianDay(eclipse.maximum);
47
+ const hours = (date.hour);
48
+ const h = Math.floor(hours);
49
+ const m = Math.floor((hours - h) * 60);
50
+ const s = Math.round(((hours - h) * 60 - m) * 60);
51
+ console.log(`Total solar eclipse: ${date.year}-${String(date.month).padStart(2,'0')}-${String(date.day).padStart(2,'0')}`);
52
+ console.log(`Maximum at: ${h}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')} UT`);
53
+ // Total solar eclipse: 2024-04-08
54
+ // Maximum at: 18:17:... UT
55
+
56
+ swe.close();
57
+ ```
58
+
59
+ ### Where is the central path?
60
+
61
+ Use `solarEclipseWhere` to find the geographic coordinates of the central line at the moment of maximum eclipse.
62
+
63
+ ```typescript
64
+ import { SwissEph } from '../index';
65
+
66
+ const swe = new SwissEph();
67
+
68
+ // First, find the eclipse
69
+ const jd = SwissEph.julianDay(2024, 1, 1, 0);
70
+ const eclipse = swe.solarEclipseGlobal(jd);
71
+
72
+ // Now find where the central line falls at maximum
73
+ const where = swe.solarEclipseWhere(eclipse.maximum);
74
+ console.log(`Central path at maximum:`);
75
+ console.log(` Longitude: ${where.geopos.longitude.toFixed(2)} deg`);
76
+ console.log(` Latitude: ${where.geopos.latitude.toFixed(2)} deg`);
77
+ // For the 2024 Apr 8 eclipse: approximately lon=-104, lat=25 (northern Mexico)
78
+
79
+ console.log(`Eclipse type flags: ${where.type}`);
80
+ console.log(`Obscuration: ${(where.attributes.fraction * 100).toFixed(1)}%`);
81
+ console.log(`Magnitude: ${where.attributes.magnitude.toFixed(4)}`);
82
+
83
+ swe.close();
84
+ ```
85
+
86
+ ### Tracking the central path across the Earth
87
+
88
+ You can call `solarEclipseWhere` at different times during the eclipse to trace the entire path of the shadow across the Earth's surface.
89
+
90
+ ```typescript
91
+ import { SwissEph } from '../index';
92
+
93
+ const swe = new SwissEph();
94
+
95
+ const jd = SwissEph.julianDay(2024, 1, 1, 0);
96
+ const eclipse = swe.solarEclipseGlobal(jd);
97
+
98
+ // Trace from first central contact to last central contact
99
+ // second = start of central phase, third = end of central phase
100
+ const start = eclipse.second;
101
+ const end = eclipse.third;
102
+ const steps = 20;
103
+ const dt = (end - start) / steps;
104
+
105
+ console.log('Central path of the solar eclipse:');
106
+ for (let i = 0; i <= steps; i++) {
107
+ const t = start + i * dt;
108
+ const where = swe.solarEclipseWhere(t);
109
+ const date = SwissEph.fromJulianDay(t);
110
+ const h = Math.floor(date.hour);
111
+ const m = Math.floor((date.hour - h) * 60);
112
+ console.log(
113
+ ` ${h}:${String(m).padStart(2,'0')} UT ` +
114
+ `lon=${where.geopos.longitude.toFixed(1)}, lat=${where.geopos.latitude.toFixed(1)}`
115
+ );
116
+ }
117
+
118
+ swe.close();
119
+ ```
120
+
121
+ ### How does the eclipse look from a specific city?
122
+
123
+ Use `solarEclipseHow` to get eclipse attributes at a specific location and time, or use `solarEclipseLocal` to find the next eclipse visible from that location.
124
+
125
+ ```typescript
126
+ import { SwissEph } from '../index';
127
+
128
+ const swe = new SwissEph();
129
+
130
+ // Dallas, Texas (in the path of the 2024 April 8 total eclipse)
131
+ const dallas = { longitude: -96.797, latitude: 32.7767 };
132
+
133
+ // Find the next solar eclipse visible from Dallas after 2024-01-01
134
+ const jd = SwissEph.julianDay(2024, 1, 1, 0);
135
+ const local = swe.solarEclipseLocal(jd, dallas);
136
+
137
+ const date = SwissEph.fromJulianDay(local.maximum);
138
+ console.log(`Eclipse visible from Dallas on: ${date.year}-${date.month}-${date.day}`);
139
+
140
+ // Four contact times
141
+ const fmt = (jd: number) => {
142
+ const d = SwissEph.fromJulianDay(jd);
143
+ const h = Math.floor(d.hour);
144
+ const m = Math.floor((d.hour - h) * 60);
145
+ const s = Math.round(((d.hour - h) * 60 - m) * 60);
146
+ return `${h}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')} UT`;
147
+ };
148
+
149
+ console.log(`First contact (partial begins): ${fmt(local.firstContact)}`);
150
+ console.log(`Second contact (total begins): ${fmt(local.secondContact)}`);
151
+ console.log(`Maximum eclipse: ${fmt(local.maximum)}`);
152
+ console.log(`Third contact (total ends): ${fmt(local.thirdContact)}`);
153
+ console.log(`Fourth contact (partial ends): ${fmt(local.fourthContact)}`);
154
+
155
+ // Attributes at maximum
156
+ console.log(`\nEclipse attributes:`);
157
+ console.log(` Obscuration fraction: ${(local.attributes.fraction * 100).toFixed(1)}%`);
158
+ console.log(` Magnitude: ${local.attributes.magnitude.toFixed(4)}`);
159
+ console.log(` Sun/Moon diameter ratio: ${local.attributes.ratio.toFixed(4)}`);
160
+ console.log(` Saros cycle: ${local.attributes.sarosCycle}`);
161
+ console.log(` Saros member: ${local.attributes.sarosMember}`);
162
+
163
+ swe.close();
164
+ ```
165
+
166
+ ### Eclipse attributes at a location using `solarEclipseHow`
167
+
168
+ If you already know the JD of an eclipse (from `solarEclipseGlobal`) and just want to check how it looks from a specific place, `solarEclipseHow` is more direct than `solarEclipseLocal`.
169
+
170
+ ```typescript
171
+ import { SwissEph } from '../index';
172
+
173
+ const swe = new SwissEph();
174
+
175
+ // Find the eclipse first
176
+ const jd = SwissEph.julianDay(2024, 1, 1, 0);
177
+ const eclipse = swe.solarEclipseGlobal(jd);
178
+
179
+ // Check from multiple cities
180
+ const cities = [
181
+ { name: 'Dallas', geo: { longitude: -96.797, latitude: 32.777 } },
182
+ { name: 'New York', geo: { longitude: -74.006, latitude: 40.713 } },
183
+ { name: 'London', geo: { longitude: -0.128, latitude: 51.507 } },
184
+ ];
185
+
186
+ for (const city of cities) {
187
+ const how = swe.solarEclipseHow(eclipse.maximum, city.geo);
188
+ const fraction = how.attributes.fraction;
189
+ if (fraction > 0) {
190
+ console.log(`${city.name}: ${(fraction * 100).toFixed(1)}% obscured, magnitude ${how.attributes.magnitude.toFixed(3)}`);
191
+ } else {
192
+ console.log(`${city.name}: eclipse not visible`);
193
+ }
194
+ }
195
+
196
+ swe.close();
197
+ ```
198
+
199
+ ### Filtering by eclipse type
200
+
201
+ You can search specifically for total, annular, partial, or hybrid eclipses.
202
+
203
+ ```typescript
204
+ import { SwissEph } from '../index';
205
+ import {
206
+ SE_ECL_TOTAL, SE_ECL_ANNULAR, SE_ECL_PARTIAL, SE_ECL_ANNULAR_TOTAL,
207
+ } from '../../constants';
208
+
209
+ const swe = new SwissEph();
210
+ let jd = SwissEph.julianDay(2024, 1, 1, 0);
211
+
212
+ // Find the next 5 total solar eclipses
213
+ console.log('Next 5 total solar eclipses:');
214
+ for (let i = 0; i < 5; i++) {
215
+ const ecl = swe.solarEclipseGlobal(jd, SE_ECL_TOTAL);
216
+ const date = SwissEph.fromJulianDay(ecl.maximum);
217
+ console.log(` ${date.year}-${String(date.month).padStart(2,'0')}-${String(date.day).padStart(2,'0')}`);
218
+ jd = ecl.maximum + 1; // Move past this eclipse to find the next one
219
+ }
220
+
221
+ // Find the next annular eclipse
222
+ const annular = swe.solarEclipseGlobal(SwissEph.julianDay(2024, 1, 1, 0), SE_ECL_ANNULAR);
223
+ const d = SwissEph.fromJulianDay(annular.maximum);
224
+ console.log(`\nNext annular eclipse: ${d.year}-${d.month}-${d.day}`);
225
+
226
+ // SE_ECL_ANNULAR_TOTAL (= SE_ECL_HYBRID) finds hybrid eclipses
227
+ // A hybrid eclipse is annular in some parts of the path and total in others
228
+
229
+ swe.close();
230
+ ```
231
+
232
+ ### Searching backward in time
233
+
234
+ Set `backward` to `true` to search for the most recent past eclipse.
235
+
236
+ ```typescript
237
+ import { SwissEph } from '../index';
238
+
239
+ const swe = new SwissEph();
240
+
241
+ // Find the most recent solar eclipse before January 1, 2024
242
+ const jd = SwissEph.julianDay(2024, 1, 1, 0);
243
+ const eclipse = swe.solarEclipseGlobal(jd, 0, true); // backward = true
244
+
245
+ const date = SwissEph.fromJulianDay(eclipse.maximum);
246
+ console.log(`Most recent eclipse before 2024: ${date.year}-${date.month}-${date.day}`);
247
+ // 2023-10-14 (annular eclipse across the Americas)
248
+
249
+ swe.close();
250
+ ```
251
+
252
+ ### Listing eclipses over a range of years
253
+
254
+ ```typescript
255
+ import { SwissEph } from '../index';
256
+ import { SE_ECL_TOTAL, SE_ECL_ANNULAR, SE_ECL_PARTIAL, SE_ECL_ANNULAR_TOTAL } from '../../constants';
257
+
258
+ const swe = new SwissEph();
259
+
260
+ let jd = SwissEph.julianDay(2024, 1, 1, 0);
261
+ const endJd = SwissEph.julianDay(2030, 1, 1, 0);
262
+
263
+ const typeNames = (flags: number): string => {
264
+ if (flags & SE_ECL_TOTAL) return 'Total';
265
+ if (flags & SE_ECL_ANNULAR_TOTAL) return 'Hybrid';
266
+ if (flags & SE_ECL_ANNULAR) return 'Annular';
267
+ if (flags & SE_ECL_PARTIAL) return 'Partial';
268
+ return 'Unknown';
269
+ };
270
+
271
+ console.log('Solar eclipses 2024-2029:');
272
+ while (jd < endJd) {
273
+ const ecl = swe.solarEclipseGlobal(jd);
274
+ if (ecl.maximum > endJd) break;
275
+ const date = SwissEph.fromJulianDay(ecl.maximum);
276
+ console.log(` ${date.year}-${String(date.month).padStart(2,'0')}-${String(date.day).padStart(2,'0')} ${typeNames(ecl.type)}`);
277
+ jd = ecl.maximum + 1;
278
+ }
279
+
280
+ swe.close();
281
+ ```
282
+
283
+ ---
284
+
285
+ ## Deep Explanation
286
+
287
+ ### Eclipse types
288
+
289
+ | Type | Constant | Description |
290
+ |------|----------|-------------|
291
+ | **Total** | `SE_ECL_TOTAL` (4) | The Moon completely covers the Sun's disc. The sky goes dark, the corona becomes visible. Only possible when the Moon is near perigee (closer to Earth). |
292
+ | **Annular** | `SE_ECL_ANNULAR` (8) | The Moon is centered on the Sun but too far away (near apogee) to cover it completely, leaving a bright ring ("annulus") of sunlight around the Moon. |
293
+ | **Partial** | `SE_ECL_PARTIAL` (16) | The Moon covers only part of the Sun's disc. This happens at the edges of a total/annular path, or when the alignment is not precise enough for a central eclipse. |
294
+ | **Hybrid** | `SE_ECL_ANNULAR_TOTAL` (32) | Also called `SE_ECL_HYBRID`. The eclipse is annular at some points on the path and total at others. This rare type occurs when the Moon's shadow tip just barely reaches Earth's surface. |
295
+
296
+ The `type` field in the result is a bitmask. You can also see `SE_ECL_CENTRAL` (1) if the eclipse has a central path, or `SE_ECL_NONCENTRAL` (2) if it does not. Use bitwise AND to check:
297
+
298
+ ```typescript
299
+ import { SE_ECL_TOTAL, SE_ECL_CENTRAL } from '../../constants';
300
+
301
+ if (eclipse.type & SE_ECL_TOTAL) {
302
+ console.log('This is a total eclipse');
303
+ }
304
+ if (eclipse.type & SE_ECL_CENTRAL) {
305
+ console.log('It has a central path across Earth');
306
+ }
307
+ ```
308
+
309
+ ### The four contacts
310
+
311
+ A solar eclipse has up to four "contact" points, which define the key moments of the eclipse for a specific location:
312
+
313
+ 1. **First contact** (`firstContact` / `first`): The Moon's disc first touches the Sun's disc. Partial phase begins.
314
+ 2. **Second contact** (`secondContact` / `second`): The Moon completely covers (or maximally overlaps) the Sun. Totality or annularity begins. Only occurs for total/annular eclipses.
315
+ 3. **Third contact** (`thirdContact` / `third`): Totality or annularity ends. The Moon begins to move off the Sun's disc.
316
+ 4. **Fourth contact** (`fourthContact` / `fourth`): The Moon's disc completely separates from the Sun. Eclipse ends.
317
+
318
+ For a partial eclipse, only first and fourth contacts occur (second and third will be 0).
319
+
320
+ In the **global** result (`solarEclipseGlobal`), the contact times refer to the eclipse overall on Earth's surface. In the **local** result (`solarEclipseLocal`), they refer to the specific observer's location.
321
+
322
+ ### Eclipse magnitude vs obscuration
323
+
324
+ These are two different measurements that are often confused:
325
+
326
+ - **Magnitude** (`attributes.magnitude`): The fraction of the Sun's *diameter* covered by the Moon at maximum eclipse. A magnitude of 1.0 means the Moon's diameter exactly matches the Sun's; greater than 1.0 means the Moon is larger (deep total eclipse). For an annular eclipse, magnitude is less than 1.0.
327
+
328
+ - **Obscuration fraction** (`attributes.fraction`): The fraction of the Sun's *area* that is covered. This is what matters for how much light is blocked. Because area scales with the square of the diameter, a magnitude of 0.5 (half the diameter) covers only about 40% of the area, not 50%.
329
+
330
+ For total eclipses, the obscuration is 1.0 (100%). For annular eclipses, even though the Moon is centered on the Sun, the obscuration is less than 1.0 because the ring of Sun around the Moon still shines.
331
+
332
+ ### The Saros cycle
333
+
334
+ The **Saros cycle** is a period of approximately 6,585.3 days (18 years, 11 days, and 8 hours) after which the Sun, Moon, and nodes return to nearly the same relative geometry. This means eclipses tend to repeat in a predictable pattern.
335
+
336
+ Each eclipse belongs to a **Saros series** (identified by `attributes.sarosCycle`), and its position within that series is given by `attributes.sarosMember`. A Saros series typically produces about 70-80 eclipses over 1,200-1,400 years, starting with small partial eclipses near one pole, progressing to central (total or annular) eclipses, and ending with small partials near the opposite pole.
337
+
338
+ Because the Saros period is not a whole number of days (the extra 8 hours), each successive eclipse in a series occurs about 120 degrees further west in longitude. After three Saros cycles (54 years 34 days, called an "Exeligmos"), the eclipse returns to approximately the same longitude.
339
+
340
+ ### The `solarEclipseGlobal` result
341
+
342
+ | Field | Description |
343
+ |-------|-------------|
344
+ | `type` | Bitmask of eclipse type flags |
345
+ | `maximum` | JD of maximum eclipse (greatest magnitude) |
346
+ | `first` | JD when the eclipse first begins anywhere on Earth |
347
+ | `second` | JD when central eclipse begins (or totality/annularity begins) |
348
+ | `third` | JD when central eclipse ends |
349
+ | `fourth` | JD when the eclipse ends everywhere on Earth |
350
+ | `sunrise` | JD of sunrise for the location of maximum (0 if not applicable) |
351
+ | `sunset` | JD of sunset for the location of maximum (0 if not applicable) |
352
+
353
+ ### The `solarEclipseLocal` attributes
354
+
355
+ | Field | Description |
356
+ |-------|-------------|
357
+ | `fraction` | Obscuration: fraction of Sun's area covered (0.0 to 1.0) |
358
+ | `ratio` | Ratio of apparent lunar diameter to solar diameter |
359
+ | `magnitude` | Eclipse magnitude: fraction of Sun's diameter covered |
360
+ | `sarosCycle` | Saros series number |
361
+ | `sarosMember` | Member number within the Saros series |
362
+ | `solarDiameter` | Apparent diameter of the Sun (arc seconds) |
363
+ | `lunarDiameter` | Apparent diameter of the Moon (arc seconds) |
364
+ | `sarosRepetition` | How many eclipses in this Saros series so far |
365
+ | `eclipseLongitude` | Ecliptic longitude where greatest eclipse occurs |
366
+ | `eclipseLatitude` | Ecliptic latitude where greatest eclipse occurs |
367
+ | `eclipseMagnitude` | Magnitude at the point of greatest eclipse |
368
+ | `sunAltitude` | Altitude of the Sun at the location during maximum |
369
+
370
+ ### Safety note
371
+
372
+ Never look directly at the Sun during a partial or annular eclipse without proper solar filters. Only during the brief totality phase of a total eclipse (when the Sun is completely covered) is it safe to view without protection. The Swiss Ephemeris helps you calculate exactly when totality begins and ends at your location (second and third contacts), but always exercise caution and use certified eclipse glasses or solar filters.
373
+
374
+ ### Tips for working with eclipse calculations
375
+
376
+ - When iterating through eclipses, advance `jd` by at least 1 day past the previous eclipse's maximum to avoid finding the same eclipse again.
377
+ - The `type` filter parameter in `solarEclipseGlobal` skips eclipses that do not match. If you pass `SE_ECL_TOTAL`, it will skip annular, partial, and hybrid eclipses and return the next total one.
378
+ - For the most precise local timing, use `solarEclipseLocal` rather than `solarEclipseGlobal` followed by `solarEclipseHow`. The local function finds the eclipse that is actually visible from that location.
379
+ - Contact times that are 0 indicate that contact did not occur (e.g., second and third contacts are 0 for a partial eclipse, since totality/annularity never happens).