@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,408 @@
1
+ # Azimuth and Altitude
2
+
3
+ **Azimuth** and **altitude** form the **horizontal coordinate system**, which describes where an object appears in the sky as seen from a specific location on Earth at a specific time. Unlike ecliptic or equatorial coordinates (which are fixed to the celestial sphere), horizontal coordinates change constantly as the Earth rotates.
4
+
5
+ - **Altitude** (also called elevation): The angle above the horizon, in degrees. 0 degrees = on the horizon, 90 degrees = directly overhead (the zenith), negative values = below the horizon.
6
+ - **Azimuth**: The compass direction along the horizon, measured in degrees.
7
+
8
+ This coordinate system is immediately practical: it tells you exactly where to point a telescope, where to look in the sky, or whether an object is above the horizon at all. It is used in telescope pointing systems, satellite tracking, architectural sun studies, and any situation where you need to know the physical direction of a celestial body from your location.
9
+
10
+ **IMPORTANT: Swiss Ephemeris azimuth convention** -- The Swiss Ephemeris measures azimuth starting from **South**, going clockwise through **West**:
11
+ - 0 degrees = **South**
12
+ - 90 degrees = **West**
13
+ - 180 degrees = **North**
14
+ - 270 degrees = **East**
15
+
16
+ This is **different from the common navigation/compass convention** where 0 degrees = North, 90 degrees = East, 180 degrees = South, 270 degrees = West. If you need compass bearings, add 180 degrees and normalize to [0, 360):
17
+
18
+ ```typescript
19
+ const compassAzimuth = (sweAzimuth + 180) % 360;
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Quick Example
25
+
26
+ ```typescript
27
+ import { SwissEph } from '../index';
28
+ import { SE_SUN } from '../../constants';
29
+
30
+ const swe = new SwissEph();
31
+
32
+ // Where is the Sun in the sky from New York at 3 PM UT on June 21, 2024?
33
+ const jd = SwissEph.julianDay(2024, 6, 21, 15);
34
+ const nyc = { longitude: -74.006, latitude: 40.713 };
35
+
36
+ // First get the Sun's ecliptic position
37
+ const sun = swe.calc(jd, SE_SUN);
38
+
39
+ // Convert to horizontal coordinates
40
+ const hor = swe.azalt(jd, nyc, sun.longitude, sun.latitude, sun.distance);
41
+
42
+ console.log(`Sun azimuth (SE convention): ${hor.azimuth.toFixed(2)} deg`);
43
+ console.log(`Sun true altitude: ${hor.trueAltitude.toFixed(2)} deg`);
44
+ console.log(`Sun apparent altitude: ${hor.apparentAltitude.toFixed(2)} deg`);
45
+
46
+ // Convert to compass bearing
47
+ const compass = (hor.azimuth + 180) % 360;
48
+ console.log(`Sun compass bearing: ${compass.toFixed(2)} deg`);
49
+
50
+ swe.close();
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Detailed Examples
56
+
57
+ ### Sun position throughout the day
58
+
59
+ ```typescript
60
+ import { SwissEph } from '../index';
61
+ import { SE_SUN } from '../../constants';
62
+
63
+ const swe = new SwissEph();
64
+
65
+ const london = { longitude: -0.128, latitude: 51.507 };
66
+
67
+ console.log('Sun position from London, June 21, 2024:');
68
+ console.log('Hour(UT) Altitude Azimuth(SE) Compass');
69
+
70
+ for (let hour = 4; hour <= 21; hour++) {
71
+ const jd = SwissEph.julianDay(2024, 6, 21, hour);
72
+ const sun = swe.calc(jd, SE_SUN);
73
+ const hor = swe.azalt(jd, london, sun.longitude, sun.latitude, sun.distance);
74
+
75
+ const compass = (hor.azimuth + 180) % 360;
76
+ const compassDir = compassDirection(compass);
77
+
78
+ if (hor.apparentAltitude > -2) { // Show when Sun is near or above horizon
79
+ console.log(
80
+ ` ${String(hour).padStart(2)}:00 ` +
81
+ `${hor.apparentAltitude.toFixed(1).padStart(6)} deg ` +
82
+ `${hor.azimuth.toFixed(1).padStart(7)} deg ` +
83
+ `${compass.toFixed(1).padStart(6)} deg ${compassDir}`
84
+ );
85
+ }
86
+ }
87
+
88
+ function compassDirection(deg: number): string {
89
+ const dirs = ['N','NNE','NE','ENE','E','ESE','SE','SSE','S','SSW','SW','WSW','W','WNW','NW','NNW'];
90
+ const index = Math.round(deg / 22.5) % 16;
91
+ return dirs[index];
92
+ }
93
+
94
+ swe.close();
95
+ ```
96
+
97
+ ### True altitude vs apparent altitude
98
+
99
+ The `azalt` function returns both the **true** (geometric) altitude and the **apparent** altitude (after atmospheric refraction). Refraction makes objects appear slightly higher than they actually are, especially near the horizon.
100
+
101
+ ```typescript
102
+ import { SwissEph } from '../index';
103
+ import { SE_SUN } from '../../constants';
104
+
105
+ const swe = new SwissEph();
106
+
107
+ const geo = { longitude: 0, latitude: 51.5 };
108
+
109
+ // Check the Sun near sunset when refraction is strongest
110
+ const jd = SwissEph.julianDay(2024, 3, 20, 18); // Near sunset at equinox
111
+ const sun = swe.calc(jd, SE_SUN);
112
+ const hor = swe.azalt(jd, geo, sun.longitude, sun.latitude, sun.distance);
113
+
114
+ console.log(`True (geometric) altitude: ${hor.trueAltitude.toFixed(4)} deg`);
115
+ console.log(`Apparent altitude: ${hor.apparentAltitude.toFixed(4)} deg`);
116
+ console.log(`Refraction effect: ${(hor.apparentAltitude - hor.trueAltitude).toFixed(4)} deg`);
117
+ // Near the horizon, refraction lifts the apparent position by about 0.5 degrees
118
+ // At higher altitudes the effect diminishes rapidly
119
+
120
+ swe.close();
121
+ ```
122
+
123
+ ### Custom atmospheric conditions
124
+
125
+ Refraction depends on pressure and temperature. You can specify these for your location.
126
+
127
+ ```typescript
128
+ import { SwissEph } from '../index';
129
+ import { SE_SUN } from '../../constants';
130
+
131
+ const swe = new SwissEph();
132
+
133
+ const geo = { longitude: 0, latitude: 51.5 };
134
+ const jd = SwissEph.julianDay(2024, 3, 20, 18);
135
+ const sun = swe.calc(jd, SE_SUN);
136
+
137
+ // Standard atmosphere (default)
138
+ const standard = swe.azalt(jd, geo, sun.longitude, sun.latitude, sun.distance);
139
+ console.log(`Standard (1013.25 mbar, 15C): apparent alt = ${standard.apparentAltitude.toFixed(4)} deg`);
140
+
141
+ // High altitude, cold conditions
142
+ const highAlt = swe.azalt(jd, geo, sun.longitude, sun.latitude, sun.distance,
143
+ 0, // calcFlag: SE_ECL2HOR (default)
144
+ 700, // pressure in mbar (at ~3000m elevation)
145
+ -10 // temperature in Celsius
146
+ );
147
+ console.log(`High altitude (700 mbar, -10C): apparent alt = ${highAlt.apparentAltitude.toFixed(4)} deg`);
148
+
149
+ // No refraction (pressure = 0)
150
+ const noRefr = swe.azalt(jd, geo, sun.longitude, sun.latitude, sun.distance,
151
+ 0, 0, 0);
152
+ console.log(`No refraction (0 mbar): apparent alt = ${noRefr.apparentAltitude.toFixed(4)} deg`);
153
+
154
+ swe.close();
155
+ ```
156
+
157
+ ### Using equatorial input (SE_EQU2HOR)
158
+
159
+ If you already have equatorial coordinates (Right Ascension and Declination) instead of ecliptic coordinates, use `SE_EQU2HOR` as the calculation flag.
160
+
161
+ ```typescript
162
+ import { SwissEph } from '../index';
163
+ import { SE_SUN, SEFLG_EQUATORIAL, SE_EQU2HOR } from '../../constants';
164
+
165
+ const swe = new SwissEph();
166
+
167
+ const jd = SwissEph.julianDay(2024, 6, 21, 15);
168
+ const geo = { longitude: -74.006, latitude: 40.713 };
169
+
170
+ // Get Sun in equatorial coordinates
171
+ const sun = swe.calc(jd, SE_SUN, SEFLG_EQUATORIAL);
172
+ const ra = sun.longitude; // Right Ascension in degrees
173
+ const dec = sun.latitude; // Declination in degrees
174
+
175
+ // Convert from equatorial to horizontal
176
+ // When using SE_EQU2HOR, pass RA as "lon" and Dec as "lat"
177
+ const hor = swe.azalt(jd, geo, ra, dec, 1, SE_EQU2HOR);
178
+
179
+ console.log(`From equatorial (RA=${ra.toFixed(2)}, Dec=${dec.toFixed(2)}):`);
180
+ console.log(` Azimuth: ${hor.azimuth.toFixed(2)} deg (SE convention)`);
181
+ console.log(` True altitude: ${hor.trueAltitude.toFixed(2)} deg`);
182
+ console.log(` Apparent altitude: ${hor.apparentAltitude.toFixed(2)} deg`);
183
+
184
+ swe.close();
185
+ ```
186
+
187
+ ### Reverse conversion: azimuth/altitude to ecliptic
188
+
189
+ Given a direction in the sky (azimuth and apparent altitude), you can find what ecliptic longitude and latitude that direction corresponds to.
190
+
191
+ ```typescript
192
+ import { SwissEph } from '../index';
193
+ import { SE_ECL2HOR, SE_EQU2HOR } from '../../constants';
194
+
195
+ const swe = new SwissEph();
196
+
197
+ const jd = SwissEph.julianDay(2024, 6, 21, 15);
198
+ const geo = { longitude: -74.006, latitude: 40.713 };
199
+
200
+ // I see something at azimuth 90 deg (West in SE convention) and altitude 45 deg
201
+ // What ecliptic coordinates does that correspond to?
202
+ const result = swe.azaltReverse(jd, geo, 90, 45, SE_ECL2HOR);
203
+ console.log(`Ecliptic longitude: ${result.azimuth.toFixed(2)} deg`);
204
+ console.log(`Ecliptic latitude: ${result.altitude.toFixed(2)} deg`);
205
+
206
+ // Or get equatorial coordinates back:
207
+ const resultEqu = swe.azaltReverse(jd, geo, 90, 45, SE_EQU2HOR);
208
+ console.log(`\nRight Ascension: ${resultEqu.azimuth.toFixed(2)} deg`);
209
+ console.log(`Declination: ${resultEqu.altitude.toFixed(2)} deg`);
210
+
211
+ swe.close();
212
+ ```
213
+
214
+ ### Checking if a planet is visible (above the horizon)
215
+
216
+ ```typescript
217
+ import { SwissEph } from '../index';
218
+ import {
219
+ SE_SUN, SE_MOON, SE_VENUS, SE_MARS, SE_JUPITER, SE_SATURN,
220
+ } from '../../constants';
221
+
222
+ const swe = new SwissEph();
223
+
224
+ const jd = SwissEph.julianDay(2024, 4, 15, 20); // 8 PM UT
225
+ const paris = { longitude: 2.352, latitude: 48.857 };
226
+
227
+ const bodies = [
228
+ { id: SE_SUN, name: 'Sun' },
229
+ { id: SE_MOON, name: 'Moon' },
230
+ { id: SE_VENUS, name: 'Venus' },
231
+ { id: SE_MARS, name: 'Mars' },
232
+ { id: SE_JUPITER, name: 'Jupiter' },
233
+ { id: SE_SATURN, name: 'Saturn' },
234
+ ];
235
+
236
+ console.log('Visibility from Paris at 20:00 UT, April 15, 2024:');
237
+ for (const b of bodies) {
238
+ const pos = swe.calc(jd, b.id);
239
+ const hor = swe.azalt(jd, paris, pos.longitude, pos.latitude, pos.distance);
240
+ const compass = (hor.azimuth + 180) % 360;
241
+ const visible = hor.apparentAltitude > 0 ? 'VISIBLE' : 'below horizon';
242
+ console.log(
243
+ ` ${b.name.padEnd(8)} alt: ${hor.apparentAltitude.toFixed(1).padStart(6)} deg` +
244
+ ` compass: ${compass.toFixed(0).padStart(3)} deg` +
245
+ ` ${visible}`
246
+ );
247
+ }
248
+
249
+ swe.close();
250
+ ```
251
+
252
+ ### Sky map: plotting planet positions in horizontal coordinates
253
+
254
+ ```typescript
255
+ import { SwissEph } from '../index';
256
+ import {
257
+ SE_MOON, SE_MERCURY, SE_VENUS, SE_MARS, SE_JUPITER, SE_SATURN,
258
+ } from '../../constants';
259
+
260
+ const swe = new SwissEph();
261
+
262
+ const jd = SwissEph.julianDay(2024, 8, 15, 22); // 10 PM UT
263
+ const geo = { longitude: 13.405, latitude: 52.520 }; // Berlin
264
+
265
+ const planets = [
266
+ { id: SE_MOON, name: 'Moon', symbol: 'D' },
267
+ { id: SE_MERCURY, name: 'Mercury', symbol: 'Me' },
268
+ { id: SE_VENUS, name: 'Venus', symbol: 'Ve' },
269
+ { id: SE_MARS, name: 'Mars', symbol: 'Ma' },
270
+ { id: SE_JUPITER, name: 'Jupiter', symbol: 'Ju' },
271
+ { id: SE_SATURN, name: 'Saturn', symbol: 'Sa' },
272
+ ];
273
+
274
+ console.log('Sky positions from Berlin at 22:00 UT, Aug 15, 2024:');
275
+ console.log('Planet Compass Altitude');
276
+
277
+ for (const p of planets) {
278
+ const pos = swe.calc(jd, p.id);
279
+ const hor = swe.azalt(jd, geo, pos.longitude, pos.latitude, pos.distance);
280
+
281
+ if (hor.apparentAltitude > 0) {
282
+ const compass = (hor.azimuth + 180) % 360;
283
+ console.log(
284
+ ` ${p.name.padEnd(9)} ${compass.toFixed(1).padStart(6)} deg ` +
285
+ `${hor.apparentAltitude.toFixed(1).padStart(5)} deg`
286
+ );
287
+ }
288
+ }
289
+
290
+ swe.close();
291
+ ```
292
+
293
+ ---
294
+
295
+ ## Deep Explanation
296
+
297
+ ### The azimuth convention in detail
298
+
299
+ **This is one of the most common sources of confusion when using the Swiss Ephemeris.**
300
+
301
+ The Swiss Ephemeris follows the astronomical convention where azimuth is measured from **South** and increases clockwise (through West):
302
+
303
+ | SE Azimuth | Direction |
304
+ |------------|-----------|
305
+ | 0 degrees | South |
306
+ | 90 degrees | West |
307
+ | 180 degrees | North |
308
+ | 270 degrees | East |
309
+
310
+ The common **navigation/compass convention** (used in GPS, maps, and daily life) measures from **North** and increases clockwise (through East):
311
+
312
+ | Compass Azimuth | Direction |
313
+ |-----------------|-----------|
314
+ | 0 degrees | North |
315
+ | 90 degrees | East |
316
+ | 180 degrees | South |
317
+ | 270 degrees | West |
318
+
319
+ To convert between them:
320
+
321
+ ```typescript
322
+ // Swiss Ephemeris to compass
323
+ const compassAzimuth = (sweAzimuth + 180) % 360;
324
+
325
+ // Compass to Swiss Ephemeris
326
+ const sweAzimuth = (compassAzimuth + 180) % 360;
327
+ ```
328
+
329
+ The formula is the same in both directions because 180 degrees is exactly half a circle.
330
+
331
+ The astronomical convention originated because in the Northern Hemisphere, celestial objects transit (cross the meridian) in the South, so it was natural for astronomers to make South the reference direction. Many classical astronomical instruments (transit telescopes, meridian circles) point South by default.
332
+
333
+ ### Altitude explained
334
+
335
+ | Altitude | Meaning |
336
+ |----------|---------|
337
+ | +90 degrees | Zenith (directly overhead) |
338
+ | +45 degrees | Halfway between horizon and zenith |
339
+ | 0 degrees | On the geometric horizon |
340
+ | -0.57 degrees | Approximate apparent altitude of a rising/setting star (refraction lifts it from ~-0.57 to 0) |
341
+ | -0.83 degrees | Approximate true altitude of the Sun's center at sunrise/sunset |
342
+ | -6 degrees | End of civil twilight |
343
+ | -12 degrees | End of nautical twilight |
344
+ | -18 degrees | End of astronomical twilight |
345
+ | -90 degrees | Nadir (directly below) |
346
+
347
+ ### Atmospheric refraction
348
+
349
+ Atmospheric refraction causes celestial objects to appear higher in the sky than they geometrically are. The effect is strongest at the horizon and negligible at the zenith:
350
+
351
+ | True altitude | Refraction amount |
352
+ |--------------|-------------------|
353
+ | 0 degrees (horizon) | ~34 arc-minutes (~0.57 degrees) |
354
+ | 5 degrees | ~10 arc-minutes |
355
+ | 10 degrees | ~5 arc-minutes |
356
+ | 20 degrees | ~2.5 arc-minutes |
357
+ | 45 degrees | ~1 arc-minute |
358
+ | 90 degrees (zenith) | 0 |
359
+
360
+ The `azalt` function returns both `trueAltitude` (geometric, no refraction) and `apparentAltitude` (with refraction). The difference between them is the refraction correction.
361
+
362
+ Refraction depends on atmospheric conditions:
363
+ - **Higher pressure** = more refraction (denser air bends light more)
364
+ - **Lower temperature** = more refraction (colder air is denser)
365
+ - Default values: 1013.25 mbar, 15 degrees C
366
+
367
+ Setting pressure to 0 effectively disables the refraction calculation (apparent altitude = true altitude).
368
+
369
+ ### SE_ECL2HOR vs SE_EQU2HOR
370
+
371
+ The `calcFlag` parameter determines what kind of input coordinates you are providing:
372
+
373
+ | Flag | Value | Input meaning |
374
+ |------|-------|--------------|
375
+ | `SE_ECL2HOR` | 0 | `lon` = ecliptic longitude, `lat` = ecliptic latitude. The function internally converts to equatorial then to horizontal. |
376
+ | `SE_EQU2HOR` | 1 | `lon` = Right Ascension (in degrees, not hours), `lat` = Declination. The function converts directly to horizontal. |
377
+
378
+ Both produce the same output (azimuth, true altitude, apparent altitude). Use whichever matches the coordinates you already have. If you have computed a planet position with `swe.calc()` (which returns ecliptic coordinates by default), use `SE_ECL2HOR`. If you used `SEFLG_EQUATORIAL` in the calc, use `SE_EQU2HOR`.
379
+
380
+ ### The `azaltReverse` function
381
+
382
+ This reverses the conversion: given an azimuth and apparent altitude (what you see in the sky), it returns the ecliptic (or equatorial) coordinates that correspond to that direction.
383
+
384
+ The return value uses the same `AzaltRevResult` type with fields named `azimuth` and `altitude`, but these actually contain the ecliptic longitude/latitude (or RA/Dec) depending on the `calcFlag`:
385
+
386
+ | calcFlag | `azimuth` field | `altitude` field |
387
+ |----------|----------------|-----------------|
388
+ | `SE_ECL2HOR` (0) | Ecliptic longitude | Ecliptic latitude |
389
+ | `SE_EQU2HOR` (1) | Right Ascension (degrees) | Declination |
390
+
391
+ ### The `dist` parameter
392
+
393
+ The `dist` parameter in `azalt()` is the distance of the object in AU. For most purposes, this has negligible effect and can be left at the default (1). It matters only for very close objects (like the Moon, which shows measurable parallax) -- but if you are getting the position from `swe.calc()`, you already have the correct distance in the result.
394
+
395
+ ### Practical uses
396
+
397
+ - **Telescope pointing**: Convert a planet's ephemeris position to azimuth/altitude for alt-azimuth telescope mounts. Remember to convert the SE azimuth to compass azimuth if your mount uses the navigation convention.
398
+ - **Visibility checking**: If a planet's apparent altitude is negative, it is below the horizon and not observable.
399
+ - **Shadow calculation**: The Sun's altitude determines shadow length. At altitude *h*, a vertical stick of height *H* casts a shadow of length *H / tan(h)*.
400
+ - **Solar energy**: The Sun's altitude and azimuth determine the angle of incidence on solar panels, which affects energy generation.
401
+ - **Photography**: The Sun's position determines lighting direction and quality. Low altitude produces warm, long-shadow light (golden hour); high altitude produces harsh, overhead light.
402
+
403
+ ### Tips
404
+
405
+ - Always remember the azimuth convention (0 = South in Swiss Ephemeris). Converting to compass bearing is simple: `(azimuth + 180) % 360`.
406
+ - The `apparentAltitude` is what you actually see; `trueAltitude` is the geometric reality. For observational purposes (is this object visible?), use `apparentAltitude`.
407
+ - At very low altitudes (near the horizon), refraction can be anomalous due to temperature inversions, mirages, and other atmospheric effects. The standard refraction formula is an average approximation.
408
+ - Setting `pressure` to 0 disables refraction, which is equivalent to what `SE_BIT_NO_REFRACTION` does for rise/set calculations.