@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,268 @@
1
+ # SwissEph — Modern TypeScript Wrapper
2
+
3
+ A class-based wrapper around the raw Swiss Ephemeris TypeScript translation. It replaces C-style patterns (threaded `SweData`, `Float64Array` positional indexing, mutable output arrays, error codes) with named return types, automatic UT/ET routing, and thrown errors.
4
+
5
+ ## Quick Start
6
+
7
+ ```ts
8
+ import { SwissEph } from './src/SwissEph';
9
+ import { SE_SUN, SE_MOON, SE_MARS } from './src/constants';
10
+
11
+ const swe = new SwissEph();
12
+ const jd = SwissEph.julianDay(2024, 4, 8, 12);
13
+
14
+ const sun = swe.calc(jd, SE_SUN);
15
+ console.log(`Sun: ${sun.longitude.toFixed(2)}°`);
16
+
17
+ const moon = swe.calc(jd, SE_MOON);
18
+ console.log(`Moon: ${moon.longitude.toFixed(2)}°`);
19
+
20
+ swe.close();
21
+ ```
22
+
23
+ ## Constructor Options
24
+
25
+ ```ts
26
+ const swe = new SwissEph({
27
+ timeMode: 'ut', // 'ut' (default) or 'et'
28
+ ephemeris: 'moshier', // 'moshier' (default), 'swisseph', or 'jpl'
29
+ siderealMode: SE_SIDM_LAHIRI, // optional: enable sidereal mode
30
+ topo: { longitude: -0.1278, latitude: 51.5074, altitude: 11 }, // optional
31
+ });
32
+ ```
33
+
34
+ - **`timeMode`** — determines whether Julian day arguments are treated as UT or ET. Most users want `'ut'`.
35
+ - **`ephemeris`** — which ephemeris engine to use. `'moshier'` needs no data files. `'swisseph'` requires SE1 files loaded via `loadEphemerisFile()`. `'jpl'` requires a JPL file loaded via `loadJplFile()`.
36
+ - **`siderealMode`** — activates sidereal zodiac. Use `SE_SIDM_*` constants.
37
+ - **`topo`** — activates topocentric corrections. Pass geographic coordinates.
38
+
39
+ ## Planet Positions
40
+
41
+ ```ts
42
+ import { SE_MARS, SEFLG_EQUATORIAL } from './src/constants';
43
+
44
+ const mars = swe.calc(jd, SE_MARS);
45
+ // { longitude, latitude, distance, longitudeSpeed, latitudeSpeed, distanceSpeed, flags }
46
+
47
+ // Equatorial coordinates:
48
+ const marsEq = swe.calc(jd, SE_MARS, SEFLG_EQUATORIAL);
49
+ // longitude = right ascension, latitude = declination
50
+ ```
51
+
52
+ ## Fixed Stars
53
+
54
+ ```ts
55
+ const spica = swe.fixedStar('Spica', jd);
56
+ // { longitude, latitude, distance, ..., starName: 'Spica,alVir' }
57
+
58
+ const mag = swe.fixedStarMagnitude('Spica');
59
+ // 0.98
60
+ ```
61
+
62
+ ## Houses
63
+
64
+ ```ts
65
+ const geo = { longitude: -0.1278, latitude: 51.5074 };
66
+ const h = swe.houses(jd, geo, 'P'); // Placidus
67
+ // h.cusps[1]..h.cusps[12], h.ascendant, h.mc, h.vertex, ...
68
+
69
+ // House position of a planet:
70
+ const pos = swe.housePosition(h.armc, geo.latitude, 23.44, 'P', sun.longitude, sun.latitude);
71
+ // e.g. 10.05
72
+ ```
73
+
74
+ ## Ayanamsa
75
+
76
+ ```ts
77
+ import { SE_SIDM_LAHIRI } from './src/constants';
78
+
79
+ swe.setSiderealMode(SE_SIDM_LAHIRI);
80
+ const aya = swe.getAyanamsa(jd);
81
+ // e.g. 24.17
82
+
83
+ const name = swe.getAyanamsaName(SE_SIDM_LAHIRI);
84
+ // 'Lahiri'
85
+ ```
86
+
87
+ ## Eclipses
88
+
89
+ ### Solar Eclipse
90
+
91
+ ```ts
92
+ // Next global solar eclipse after a date:
93
+ const ecl = swe.solarEclipseGlobal(jd);
94
+ // { type, maximum, first, second, third, fourth, sunrise, sunset }
95
+
96
+ // Where is the central line at a given time?
97
+ const where = swe.solarEclipseWhere(ecl.maximum);
98
+ // { type, geopos: { longitude, latitude }, attributes: { fraction, magnitude, ... } }
99
+
100
+ // How does it look from a specific location?
101
+ const how = swe.solarEclipseHow(ecl.maximum, geo);
102
+ // { type, attributes: { fraction, ratio, magnitude, ... } }
103
+ ```
104
+
105
+ ### Lunar Eclipse
106
+
107
+ ```ts
108
+ const lun = swe.lunarEclipseGlobal(jd);
109
+ // { type, maximum, partialBegin, partialEnd, totalBegin, totalEnd, penumbralBegin, penumbralEnd }
110
+
111
+ const lunHow = swe.lunarEclipseHow(lun.maximum);
112
+ // { type, umbraMagnitude, penumbraMagnitude, moonDiameter, ... }
113
+ ```
114
+
115
+ ## Rise / Set / Transit
116
+
117
+ ```ts
118
+ const geo = { longitude: -0.1278, latitude: 51.5074 };
119
+
120
+ const sunrise = swe.rise(jd, SE_SUN, geo);
121
+ const sunset = swe.set(jd, SE_SUN, geo);
122
+ const transit = swe.transit(jd, SE_SUN, geo); // upper meridian
123
+
124
+ console.log(`Sunrise JD: ${sunrise.jd}`);
125
+ ```
126
+
127
+ With atmospheric options:
128
+
129
+ ```ts
130
+ const r = swe.rise(jd, SE_SUN, geo, { pressure: 1013, temperature: 20 });
131
+ ```
132
+
133
+ ## Azimuth & Altitude
134
+
135
+ ```ts
136
+ const az = swe.azalt(jd, geo, sun.longitude, sun.latitude);
137
+ // { azimuth, trueAltitude, apparentAltitude }
138
+ ```
139
+
140
+ ## Phenomena
141
+
142
+ ```ts
143
+ const pheno = swe.phenomena(jd, SE_MARS);
144
+ // { phaseAngle, phase, elongation, apparentDiameter, apparentMagnitude }
145
+ ```
146
+
147
+ ## Crossings
148
+
149
+ ```ts
150
+ // Vernal equinox (Sun crosses 0° longitude):
151
+ const equinox = swe.sunCrossing(0, jd);
152
+ // { jd: 2460388.815... }
153
+
154
+ // Moon crosses 90°:
155
+ const moonCross = swe.moonCrossing(90, jd);
156
+
157
+ // Moon node crossing:
158
+ const node = swe.moonNodeCrossing(jd);
159
+ // { jd, longitude, latitude }
160
+ ```
161
+
162
+ ## Orbital Elements
163
+
164
+ ```ts
165
+ const orb = swe.orbitalElements(jd, SE_MARS);
166
+ // { semiAxis, eccentricity, inclination, ascNode, argPerihelion, tropicalPeriod, ... }
167
+
168
+ const dist = swe.orbitDistances(jd, SE_MARS);
169
+ // { max, min, true }
170
+ ```
171
+
172
+ ## Nodes & Apsides
173
+
174
+ ```ts
175
+ const na = swe.nodesApsides(jd, SE_MARS);
176
+ // { ascendingNode: PlanetPosition, descendingNode, perihelion, aphelion }
177
+ ```
178
+
179
+ ## Heliacal Events
180
+
181
+ ```ts
182
+ import { SE_HELIACAL_RISING } from './src/constants';
183
+
184
+ const geo = { longitude: 8.55, latitude: 47.37, altitude: 400 };
185
+ const hel = swe.heliacalEvent(jd, geo, 'Venus', SE_HELIACAL_RISING);
186
+ // { startVisible, bestVisible, endVisible }
187
+ ```
188
+
189
+ ## Planetocentric
190
+
191
+ ```ts
192
+ import { SE_MARS, SE_EARTH } from './src/constants';
193
+
194
+ // Mars as seen from Jupiter:
195
+ const marsFromJupiter = swe.calcPlanetocentric(jd, SE_MARS, 5);
196
+ ```
197
+
198
+ ## Date/Time Utilities (static)
199
+
200
+ ```ts
201
+ const jd = SwissEph.julianDay(2024, 4, 8, 12);
202
+ const date = SwissEph.fromJulianDay(jd);
203
+ // { year: 2024, month: 4, day: 8, hour: 12 }
204
+
205
+ const utc = SwissEph.utcToJd(2024, 4, 8, 12, 0, 0);
206
+ // { tjdEt, tjdUt }
207
+
208
+ const dow = SwissEph.dayOfWeek(jd); // 0=Mon .. 6=Sun
209
+ ```
210
+
211
+ ## Math Utilities (static)
212
+
213
+ ```ts
214
+ SwissEph.normalizeDegrees(370); // 10
215
+ SwissEph.degreeMidpoint(350, 10); // 0
216
+ SwissEph.version(); // '2.10.03'
217
+ ```
218
+
219
+ ## Error Handling
220
+
221
+ All methods throw `SwissEphError` when the underlying engine returns an error:
222
+
223
+ ```ts
224
+ import { SwissEphError } from './src/SwissEph';
225
+
226
+ try {
227
+ const pos = swe.calc(jd, 999);
228
+ } catch (e) {
229
+ if (e instanceof SwissEphError) {
230
+ console.error('Swiss Ephemeris error:', e.message);
231
+ }
232
+ }
233
+ ```
234
+
235
+ ## Loading Ephemeris Files
236
+
237
+ For higher precision than Moshier, load SE1 or JPL files:
238
+
239
+ ```ts
240
+ // Swiss Ephemeris SE1 files:
241
+ const swe = new SwissEph({ ephemeris: 'swisseph' });
242
+ const buf = fs.readFileSync('ephe/sepl_18.se1').buffer;
243
+ swe.loadEphemerisFile(buf, 'sepl_18.se1');
244
+
245
+ // JPL files:
246
+ const sweJpl = new SwissEph({ ephemeris: 'jpl' });
247
+ const jplBuf = fs.readFileSync('de441.eph').buffer;
248
+ sweJpl.loadJplFile(jplBuf, 'de441.eph');
249
+ ```
250
+
251
+ ## Type Reference
252
+
253
+ All return types are exported from `./src/SwissEph/types`:
254
+
255
+ - `PlanetPosition` — core position result
256
+ - `StarPosition` — extends PlanetPosition with `starName`
257
+ - `HouseResult` — house cusps and angles
258
+ - `NodesApsides` — nodes and apsides (4 PlanetPositions)
259
+ - `OrbitalElements` — Keplerian orbital elements
260
+ - `OrbitDistances` — max/min/true distance
261
+ - `SolarEclipseGlobal` / `Local` / `Where` / `How`
262
+ - `LunarEclipseGlobal` / `Local` / `How`
263
+ - `OccultationGlobal` / `Local` / `Where`
264
+ - `RiseSetResult`, `PhenoResult`, `AzaltResult`, `AzaltRevResult`
265
+ - `CrossingResult`, `MoonNodeCrossingResult`
266
+ - `HeliacalResult`, `HeliacalPhenoResult`, `VisualLimitResult`
267
+ - `GauquelinResult`, `GeoPosition`, `SwissEphOptions`
268
+ - `DateResult`, `UtcToJdResult`, `JdToUtcResult`, `SplitDegResult`
@@ -0,0 +1,363 @@
1
+ # Ayanamsa and the Sidereal Zodiac
2
+
3
+ The **ayanamsa** is the angular difference between the tropical zodiac and the sidereal zodiac, measured in degrees. Understanding it requires knowing about two different ways to define the zodiac:
4
+
5
+ - **Tropical zodiac**: Defined by the seasons. 0 degrees Aries is fixed to the **vernal equinox** (the point where the Sun crosses the celestial equator heading north, around March 20). This is the system used by most Western astrologers.
6
+
7
+ - **Sidereal zodiac**: Defined by the fixed stars. 0 degrees Aries is fixed to a specific star or stellar reference point. This is the system used by Vedic/Indian astrology (Jyotish) and some Western sidereal astrologers.
8
+
9
+ Due to the **precession of the equinoxes**, Earth's rotational axis slowly wobbles like a spinning top, completing a full circle in about 25,800 years. This causes the vernal equinox point to drift westward against the background stars at a rate of roughly 50.3 arcseconds per year. As a result, the tropical and sidereal zodiacs slowly drift apart.
10
+
11
+ The **ayanamsa** is this accumulated drift. In 2024, the Lahiri ayanamsa is approximately 24.18 degrees. This means that a planet at 0 degrees Aries in the tropical zodiac is at approximately 5.82 degrees Pisces in the Lahiri sidereal zodiac (0 - 24.18 = -24.18, or equivalently 360 - 24.18 = 335.82 degrees = 5.82 Pisces).
12
+
13
+ Different traditions and scholars disagree on the exact epoch when the two zodiacs coincided (i.e., when the ayanamsa was zero), leading to many different ayanamsa systems.
14
+
15
+ ---
16
+
17
+ ## Quick Example
18
+
19
+ ```typescript
20
+ import { SwissEph } from '../index';
21
+ import { SE_SIDM_LAHIRI } from '../../constants';
22
+
23
+ const swe = new SwissEph({ siderealMode: SE_SIDM_LAHIRI });
24
+ const jd = SwissEph.julianDay(2024, 1, 15, 12);
25
+
26
+ // Get the Lahiri ayanamsa value
27
+ const ayanamsa = swe.getAyanamsa(jd);
28
+ console.log(`Lahiri ayanamsa: ${ayanamsa.toFixed(4)} degrees`);
29
+ // Lahiri ayanamsa: ~24.1678 degrees
30
+
31
+ swe.close();
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Detailed Examples
37
+
38
+ ### Getting the ayanamsa value for different systems
39
+
40
+ ```typescript
41
+ import { SwissEph } from '../index';
42
+ import {
43
+ SE_SIDM_FAGAN_BRADLEY, SE_SIDM_LAHIRI, SE_SIDM_RAMAN,
44
+ SE_SIDM_KRISHNAMURTI, SE_SIDM_TRUE_CITRA, SE_SIDM_TRUE_REVATI,
45
+ SE_SIDM_GALCENT_0SAG,
46
+ } from '../../constants';
47
+
48
+ const jd = SwissEph.julianDay(2024, 1, 1, 12);
49
+
50
+ const systems = [
51
+ { id: SE_SIDM_FAGAN_BRADLEY, name: 'Fagan-Bradley' },
52
+ { id: SE_SIDM_LAHIRI, name: 'Lahiri' },
53
+ { id: SE_SIDM_RAMAN, name: 'Raman' },
54
+ { id: SE_SIDM_KRISHNAMURTI, name: 'Krishnamurti' },
55
+ { id: SE_SIDM_TRUE_CITRA, name: 'True Citra (Spica=180)' },
56
+ { id: SE_SIDM_TRUE_REVATI, name: 'True Revati' },
57
+ { id: SE_SIDM_GALCENT_0SAG, name: 'Galactic Center = 0 Sag' },
58
+ ];
59
+
60
+ for (const sys of systems) {
61
+ const swe = new SwissEph({ siderealMode: sys.id });
62
+ const aya = swe.getAyanamsa(jd);
63
+ console.log(`${sys.name.padEnd(30)} ${aya.toFixed(4)} deg`);
64
+ swe.close();
65
+ }
66
+ ```
67
+
68
+ ### Computing sidereal planet positions
69
+
70
+ To get planet positions in the sidereal zodiac, you must:
71
+ 1. Set the sidereal mode (which ayanamsa to use)
72
+ 2. Pass `SEFLG_SIDEREAL` in the flags to `calc()`
73
+
74
+ **Important**: Setting `siderealMode` in the constructor (or calling `setSiderealMode()`) only configures which ayanamsa system to use. It does **not** automatically make `calc()` return sidereal positions. You must explicitly pass `SEFLG_SIDEREAL` in the flags.
75
+
76
+ ```typescript
77
+ import { SwissEph } from '../index';
78
+ import {
79
+ SE_SUN, SE_MOON, SE_MARS, SE_JUPITER,
80
+ SE_SIDM_LAHIRI, SEFLG_SIDEREAL,
81
+ } from '../../constants';
82
+
83
+ const swe = new SwissEph({ siderealMode: SE_SIDM_LAHIRI });
84
+ const jd = SwissEph.julianDay(2024, 1, 15, 12);
85
+
86
+ const planets = [
87
+ { id: SE_SUN, name: 'Sun' },
88
+ { id: SE_MOON, name: 'Moon' },
89
+ { id: SE_MARS, name: 'Mars' },
90
+ { id: SE_JUPITER, name: 'Jupiter' },
91
+ ];
92
+
93
+ console.log('Sidereal positions (Lahiri):');
94
+ for (const p of planets) {
95
+ const pos = swe.calc(jd, p.id, SEFLG_SIDEREAL);
96
+
97
+ // Convert to Vedic sign
98
+ const signs = ['Mesha','Vrishabha','Mithuna','Karka','Simha','Kanya',
99
+ 'Tula','Vrischika','Dhanu','Makara','Kumbha','Meena'];
100
+ const signIndex = Math.floor(pos.longitude / 30);
101
+ const degInSign = pos.longitude - signIndex * 30;
102
+
103
+ console.log(
104
+ `${p.name.padEnd(9)} ${pos.longitude.toFixed(4).padStart(9)} deg = ` +
105
+ `${degInSign.toFixed(2)} deg ${signs[signIndex]}`
106
+ );
107
+ }
108
+
109
+ swe.close();
110
+ ```
111
+
112
+ ### Comparing tropical and sidereal positions
113
+
114
+ ```typescript
115
+ import { SwissEph } from '../index';
116
+ import { SE_SUN, SE_SIDM_LAHIRI, SEFLG_SIDEREAL } from '../../constants';
117
+
118
+ const swe = new SwissEph({ siderealMode: SE_SIDM_LAHIRI });
119
+ const jd = SwissEph.julianDay(2024, 1, 15, 12);
120
+
121
+ // Tropical position (no SEFLG_SIDEREAL)
122
+ const tropical = swe.calc(jd, SE_SUN);
123
+
124
+ // Sidereal position (with SEFLG_SIDEREAL)
125
+ const sidereal = swe.calc(jd, SE_SUN, SEFLG_SIDEREAL);
126
+
127
+ // The ayanamsa
128
+ const ayanamsa = swe.getAyanamsa(jd);
129
+
130
+ console.log(`Tropical Sun: ${tropical.longitude.toFixed(4)} deg`);
131
+ console.log(`Sidereal Sun: ${sidereal.longitude.toFixed(4)} deg`);
132
+ console.log(`Ayanamsa: ${ayanamsa.toFixed(4)} deg`);
133
+ console.log(`Difference: ${(tropical.longitude - sidereal.longitude).toFixed(4)} deg`);
134
+ // The difference should match the ayanamsa value (approximately)
135
+
136
+ swe.close();
137
+ ```
138
+
139
+ ### Using setSiderealMode at runtime
140
+
141
+ You can change the sidereal mode after construction:
142
+
143
+ ```typescript
144
+ import { SwissEph } from '../index';
145
+ import { SE_SUN, SE_SIDM_LAHIRI, SE_SIDM_RAMAN, SEFLG_SIDEREAL } from '../../constants';
146
+
147
+ const swe = new SwissEph();
148
+ const jd = SwissEph.julianDay(2024, 1, 15, 12);
149
+
150
+ // Set Lahiri mode
151
+ swe.setSiderealMode(SE_SIDM_LAHIRI);
152
+ const lahiri = swe.calc(jd, SE_SUN, SEFLG_SIDEREAL);
153
+ console.log(`Sun (Lahiri): ${lahiri.longitude.toFixed(4)} deg`);
154
+
155
+ // Switch to Raman mode
156
+ swe.setSiderealMode(SE_SIDM_RAMAN);
157
+ const raman = swe.calc(jd, SE_SUN, SEFLG_SIDEREAL);
158
+ console.log(`Sun (Raman): ${raman.longitude.toFixed(4)} deg`);
159
+
160
+ console.log(`Difference: ${(lahiri.longitude - raman.longitude).toFixed(4)} deg`);
161
+
162
+ swe.close();
163
+ ```
164
+
165
+ ### Custom ayanamsa with SE_SIDM_USER
166
+
167
+ If none of the built-in systems match your needs, you can define your own ayanamsa by specifying the epoch (t0) when the ayanamsa was zero and optionally the ayanamsa value at a reference epoch:
168
+
169
+ ```typescript
170
+ import { SwissEph } from '../index';
171
+ import { SE_SUN, SE_SIDM_USER, SEFLG_SIDEREAL } from '../../constants';
172
+
173
+ // Define a custom ayanamsa:
174
+ // t0 = the Julian Day when the ayanamsa is zero (both zodiacs align)
175
+ // ayanT0 = the ayanamsa value at t0 (usually 0 for the alignment epoch)
176
+ //
177
+ // Example: suppose you believe the zodiacs aligned on March 21, 285 AD
178
+ const t0 = SwissEph.julianDay(285, 3, 21, 0);
179
+ const ayanT0 = 0; // ayanamsa = 0 at this epoch
180
+
181
+ const swe = new SwissEph({
182
+ siderealMode: SE_SIDM_USER,
183
+ siderealT0: t0,
184
+ siderealAyanT0: ayanT0,
185
+ });
186
+
187
+ const jd = SwissEph.julianDay(2024, 1, 15, 12);
188
+
189
+ const aya = swe.getAyanamsa(jd);
190
+ console.log(`Custom ayanamsa: ${aya.toFixed(4)} deg`);
191
+
192
+ const sun = swe.calc(jd, SE_SUN, SEFLG_SIDEREAL);
193
+ console.log(`Sun (custom sidereal): ${sun.longitude.toFixed(4)} deg`);
194
+
195
+ swe.close();
196
+ ```
197
+
198
+ You can also use `SE_SIDM_USER` to define a fixed ayanamsa value at a known date:
199
+
200
+ ```typescript
201
+ import { SwissEph } from '../index';
202
+ import { SE_SIDM_USER, SEFLG_SIDEREAL } from '../../constants';
203
+
204
+ // Suppose you want the ayanamsa to be exactly 23.85 degrees at J2000.0
205
+ const t0 = SwissEph.julianDay(2000, 1, 1, 12); // J2000.0
206
+ const ayanT0 = 23.85; // degrees
207
+
208
+ const swe = new SwissEph({
209
+ siderealMode: SE_SIDM_USER,
210
+ siderealT0: t0,
211
+ siderealAyanT0: ayanT0,
212
+ });
213
+
214
+ const jd = SwissEph.julianDay(2024, 1, 15, 12);
215
+ const aya = swe.getAyanamsa(jd);
216
+ console.log(`Custom ayanamsa at 2024: ${aya.toFixed(4)} deg`);
217
+ // Will be ~23.85 + ~24 years of precession (~0.34 deg) = ~24.19
218
+
219
+ swe.close();
220
+ ```
221
+
222
+ ### Getting the name of an ayanamsa system
223
+
224
+ ```typescript
225
+ import { SwissEph } from '../index';
226
+ import {
227
+ SE_SIDM_FAGAN_BRADLEY, SE_SIDM_LAHIRI, SE_SIDM_TRUE_CITRA,
228
+ SE_SIDM_TRUE_REVATI, SE_SIDM_GALCENT_0SAG,
229
+ } from '../../constants';
230
+
231
+ const swe = new SwissEph();
232
+
233
+ const modes = [
234
+ SE_SIDM_FAGAN_BRADLEY,
235
+ SE_SIDM_LAHIRI,
236
+ SE_SIDM_TRUE_CITRA,
237
+ SE_SIDM_TRUE_REVATI,
238
+ SE_SIDM_GALCENT_0SAG,
239
+ ];
240
+
241
+ for (const mode of modes) {
242
+ console.log(`Mode ${mode}: ${swe.getAyanamsaName(mode)}`);
243
+ }
244
+
245
+ swe.close();
246
+ ```
247
+
248
+ ### Ayanamsa over time
249
+
250
+ The ayanamsa changes slowly due to precession. Here is how to track it across centuries:
251
+
252
+ ```typescript
253
+ import { SwissEph } from '../index';
254
+ import { SE_SIDM_LAHIRI } from '../../constants';
255
+
256
+ const swe = new SwissEph({ siderealMode: SE_SIDM_LAHIRI });
257
+
258
+ // Ayanamsa at different epochs
259
+ const years = [0, 285, 500, 1000, 1500, 1900, 1950, 2000, 2024, 2100];
260
+
261
+ for (const y of years) {
262
+ const jd = SwissEph.julianDay(y, 1, 1, 12);
263
+ const aya = swe.getAyanamsa(jd);
264
+ console.log(`Year ${String(y).padStart(5)}: ayanamsa = ${aya.toFixed(4)} deg`);
265
+ }
266
+ // Around year 285, the Lahiri ayanamsa was near 0 (the two zodiacs were aligned)
267
+ // The ayanamsa grows by ~50.3 arcseconds per year = ~1 degree every 71.6 years
268
+
269
+ swe.close();
270
+ ```
271
+
272
+ ---
273
+
274
+ ## Deep Explanation
275
+
276
+ ### Key ayanamsa systems
277
+
278
+ | Constant | Value | Name / Description |
279
+ |---------------------------|-------|-----------------------------------------------------------------------------------------------------------|
280
+ | `SE_SIDM_FAGAN_BRADLEY` | 0 | **Fagan-Bradley**: The standard Western sidereal ayanamsa. Based on Cyril Fagan's research (1950s). Ayanamsa ~24.04 deg in 2000. Used by Western sidereal astrologers. |
281
+ | `SE_SIDM_LAHIRI` | 1 | **Lahiri**: The official ayanamsa of the Indian government (adopted 1955 by the Calendar Reform Committee). The most widely used ayanamsa in Vedic astrology. ~23.86 deg at J2000. |
282
+ | `SE_SIDM_DELUCE` | 2 | **De Luce**: Based on Robert DeLuce's calculations. |
283
+ | `SE_SIDM_RAMAN` | 3 | **Raman**: B.V. Raman's ayanamsa, popular in South Indian astrology. ~22.38 deg at J2000. |
284
+ | `SE_SIDM_USHASHASHI` | 4 | **Usha-Shashi**: ~20.08 deg at J2000. |
285
+ | `SE_SIDM_KRISHNAMURTI` | 5 | **Krishnamurti**: Used in the KP (Krishnamurti Paddhati) system. ~23.73 deg at J2000. |
286
+ | `SE_SIDM_GALCENT_0SAG` | 17 | **Galactic Center at 0 Sagittarius**: Defines the sidereal zodiac such that the Galactic Center is at exactly 0 degrees Sagittarius (240 deg). |
287
+ | `SE_SIDM_TRUE_CITRA` | 27 | **True Citra (Chitrapaksha)**: Defines the zodiac so that the star Spica (Citra) is always at exactly 180 deg (0 Libra). The ayanamsa adjusts dynamically based on Spica's true position. |
288
+ | `SE_SIDM_TRUE_REVATI` | 28 | **True Revati**: Defines the zodiac so that the star Revati (zeta Piscium) is always at 359 deg 50' (29d50' Pisces). |
289
+ | `SE_SIDM_TRUE_PUSHYA` | 29 | **True Pushya**: Defines the zodiac so that the star Pushya (delta Cancri) is always at 106 deg (16 deg Cancer). |
290
+ | `SE_SIDM_USER` | 255 | **User-defined**: Supply your own epoch and ayanamsa value. |
291
+
292
+ There are over 40 built-in ayanamsa systems in total. The ones listed above are the most commonly used. See the constants file for the complete list (SE_SIDM_0 through SE_SIDM_46, plus SE_SIDM_USER = 255).
293
+
294
+ ### How ayanamsa works mathematically
295
+
296
+ For a given Julian Day, the ayanamsa A is computed as:
297
+
298
+ ```
299
+ sidereal_longitude = tropical_longitude - A
300
+ ```
301
+
302
+ Or equivalently:
303
+ ```
304
+ A = tropical_longitude - sidereal_longitude
305
+ ```
306
+
307
+ The ayanamsa is a single value that applies uniformly to all longitudes. To convert any tropical position to sidereal, subtract the ayanamsa. To convert sidereal to tropical, add the ayanamsa.
308
+
309
+ ### True star-based vs. epoch-based ayanamsas
310
+
311
+ There are two categories of ayanamsa:
312
+
313
+ 1. **Epoch-based** (Lahiri, Fagan-Bradley, Raman, etc.): The ayanamsa is defined by choosing an epoch when the two zodiacs were aligned (or an epoch with a known ayanamsa value). The value changes at the constant rate of precession (~50.3"/year). These ayanamsas change smoothly and predictably.
314
+
315
+ 2. **True star-based** (True Citra, True Revati, True Pushya): The ayanamsa is defined by the current actual position of a reference star. Since the star has its own proper motion (independent movement through space), the ayanamsa subtly fluctuates in ways that cannot be predicted from precession alone. These are slightly more accurate in terms of the zodiac's relationship to the actual stars but are more complex to compute.
316
+
317
+ ### The precession cycle
318
+
319
+ The precession of the equinoxes is caused by the gravitational pull of the Sun and Moon on Earth's equatorial bulge. Key facts:
320
+
321
+ - **Rate**: ~50.29 arcseconds per year (varies slightly over millennia)
322
+ - **Full cycle**: ~25,772 years
323
+ - **Direction**: The vernal equinox moves westward (backward through the zodiac)
324
+ - **Current direction**: The vernal equinox is currently in the early degrees of Pisces (sidereal) and slowly moving toward Aquarius -- this is the astronomical basis for the "Age of Aquarius"
325
+
326
+ ### Vedic astrology context
327
+
328
+ In Jyotish (Vedic/Indian astrology), the sidereal zodiac is fundamental. The 12 rashis (signs) and 27 nakshatras (lunar mansions) are all defined in sidereal terms. Most Vedic astrologers use the Lahiri ayanamsa, though Krishnamurti (KP) and Raman are also popular.
329
+
330
+ The nakshatras divide the sidereal zodiac into 27 equal portions of 13 degrees 20 minutes each:
331
+ - Ashwini: 0d00' to 13d20' Aries (sidereal)
332
+ - Bharani: 13d20' to 26d40' Aries (sidereal)
333
+ - Krittika: 26d40' Aries to 10d00' Taurus (sidereal)
334
+ - ...and so on
335
+
336
+ To place a planet in its nakshatra, first compute its sidereal longitude (using `SEFLG_SIDEREAL`), then divide by 13.333:
337
+
338
+ ```typescript
339
+ const nakshatraIndex = Math.floor(siderealLongitude / (360 / 27));
340
+ // 0 = Ashwini, 1 = Bharani, ..., 26 = Revati
341
+ ```
342
+
343
+ ### Important: SEFLG_SIDEREAL must be passed explicitly
344
+
345
+ Setting `siderealMode` in the constructor or calling `setSiderealMode()` only tells the engine **which** ayanamsa system to use. It does **not** make `calc()` automatically return sidereal positions. You must pass `SEFLG_SIDEREAL` as a flag to `calc()` each time you want sidereal results.
346
+
347
+ This design allows you to easily get both tropical and sidereal positions from the same `SwissEph` instance:
348
+
349
+ ```typescript
350
+ const swe = new SwissEph({ siderealMode: SE_SIDM_LAHIRI });
351
+
352
+ // Tropical (default -- no SEFLG_SIDEREAL)
353
+ const tropical = swe.calc(jd, SE_SUN);
354
+
355
+ // Sidereal (must pass the flag)
356
+ const sidereal = swe.calc(jd, SE_SUN, SEFLG_SIDEREAL);
357
+ ```
358
+
359
+ ### Edge cases
360
+
361
+ - **Negative ayanamsa**: For dates before approximately 285 AD (depending on the system), the Lahiri ayanamsa is negative, meaning the sidereal longitude is greater than the tropical longitude.
362
+ - **Ayanamsa near 30 degrees**: At some point in the far future (~year 2300 for Lahiri), the ayanamsa will be 30 degrees, meaning the tropical and sidereal zodiacs will be off by exactly one sign. We are currently about 6 degrees short of this.
363
+ - **Combining with other flags**: `SEFLG_SIDEREAL` can be combined with `SEFLG_EQUATORIAL`, `SEFLG_TOPOCTR`, and other flags using bitwise OR.