1 /* SWISSEPH
2
3 Ephemeris computations
4 Author: Dieter Koch
5
6 ************************************************************/
7 /* Copyright (C) 1997 - 2021 Astrodienst AG, Switzerland. All rights reserved.
8
9 License conditions
10 ------------------
11
12 This file is part of Swiss Ephemeris.
13
14 Swiss Ephemeris is distributed with NO WARRANTY OF ANY KIND. No author
15 or distributor accepts any responsibility for the consequences of using it,
16 or for whether it serves any particular purpose or works at all, unless he
17 or she says so in writing.
18
19 Swiss Ephemeris is made available by its authors under a dual licensing
20 system. The software developer, who uses any part of Swiss Ephemeris
21 in his or her software, must choose between one of the two license models,
22 which are
23 a) GNU Affero General Public License (AGPL)
24 b) Swiss Ephemeris Professional License
25
26 The choice must be made before the software developer distributes software
27 containing parts of Swiss Ephemeris to others, and before any public
28 service using the developed software is activated.
29
30 If the developer choses the AGPL software license, he or she must fulfill
31 the conditions of that license, which includes the obligation to place his
32 or her whole software project under the AGPL or a compatible license.
33 See https://www.gnu.org/licenses/agpl-3.0.html
34
35 If the developer choses the Swiss Ephemeris Professional license,
36 he must follow the instructions as found in http://www.astro.com/swisseph/
37 and purchase the Swiss Ephemeris Professional Edition from Astrodienst
38 and sign the corresponding license contract.
39
40 The License grants you the right to use, copy, modify and redistribute
41 Swiss Ephemeris, but only under certain conditions described in the License.
42 Among other things, the License requires that the copyright notices and
43 this notice be preserved on all copies.
44
45 Authors of the Swiss Ephemeris: Dieter Koch and Alois Treindl
46
47 The authors of Swiss Ephemeris have no control or influence over any of
48 the derived works, i.e. over software or services created by other
49 programmers which use Swiss Ephemeris functions.
50
51 The names of the authors or of the copyright holder (Astrodienst) must not
52 be used for promoting any software, product or service which uses or contains
53 the Swiss Ephemeris. This copyright notice is the ONLY place where the
54 names of the authors can legally appear, except in cases where they have
55 given special permission in writing.
56
57 The trademarks 'Swiss Ephemeris' and 'Swiss Ephemeris inside' may be used
58 for promoting such software, products or services.
59 */
60
61 #include "swejpl.h"
62 #include "swephexp.h"
63 #include "sweph.h"
64 #include "swephlib.h"
65 #include <time.h>
66
67 #define SEFLG_EPHMASK (SEFLG_JPLEPH|SEFLG_SWIEPH|SEFLG_MOSEPH)
68 static int find_maximum(double y00, double y11, double y2, double dx,
69 double *dxret, double *yret);
70 static int find_zero(double y00, double y11, double y2, double dx,
71 double *dxret, double *dxret2);
72 static double calc_dip(double geoalt, double atpress, double attemp, double lapse_rate);
73 static double calc_astronomical_refr(double geoalt,double atpress, double attemp);
74 static TLS double const_lapse_rate = SE_LAPSE_RATE; /* for refraction */
75
76 #if 0
77 #define DSUN (1391978489.9 / AUNIT) /* this value is consistent with
78 * 959.63 arcsec at AU distance (Astr. Alm.) */
79 #else
80 #define DSUN (1392000000.0 / AUNIT)
81 #endif
82 #define DMOON (3476300.0 / AUNIT)
83 #define DEARTH (6378140.0 * 2 / AUNIT)
84 #define RSUN (DSUN / 2)
85 #define RMOON (DMOON / 2)
86 #define REARTH (DEARTH / 2)
87 /*#define SEI_OCC_FAST (16 * 1024L)*/
88 static int32 eclipse_where( double tjd_ut, int32 ipl, char *starname, int32 ifl, double *geopos,
89 double *dcore, char *serr);
90 static int32 eclipse_how( double tjd_ut, int32 ipl, char *starname, int32 ifl,
91 double geolon, double geolat, double geohgt,
92 double *attr, char *serr);
93 static int32 eclipse_when_loc(double tjd_start, int32 ifl, double *geopos,
94 double *tret, double *attr, AS_BOOL backward, char *serr);
95 static int32 occult_when_loc(double tjd_start, int32 ipl, char *starname, int32 ifl,
96 double *geopos, double *tret, double *attr, AS_BOOL backward, char *serr);
97 static int32 lun_eclipse_how(double tjd_ut, int32 ifl, double *attr,
98 double *dcore, char *serr);
99 static int32 calc_mer_trans(
100 double tjd_ut, int32 ipl, int32 epheflag, int32 rsmi,
101 double *geopos,
102 char *starname,
103 double *tret,
104 char *serr);
105 static int32 calc_planet_star(double tjd_et, int32 ipl, char *starname, int32 iflag, double *x, char *serr);
106
107 struct saros_data {int series_no; double tstart;};
108
109 // Saros cycle numbers of solar eclipses with date of initial
110 // eclipse. Table was derived from the table of the Nasa Eclipse Web Site:
111 // https://eclipse.gsfc.nasa.gov/SEsaros/SEsaros0-180.html
112 // Note, for eclipse dates =< 15 Feb -1604 and eclipse dates
113 // >= 2 Sep 2666, Saros cycle numbers cannot always be given.
114 #define SAROS_CYCLE 6585.3213
115 #define NSAROS_SOLAR 181
116 struct saros_data saros_data_solar[NSAROS_SOLAR] = {
117 {0, 641886.5}, /* 23 May -2955 */
118 {1, 672214.5}, /* 04 Jun -2872 */
119 {2, 676200.5}, /* 04 May -2861 */
120 {3, 693357.5}, /* 24 Apr -2814 */
121 {4, 723685.5}, /* 06 May -2731 */
122 {5, 727671.5}, /* 04 Apr -2720 */
123 {6, 744829.5}, /* 27 Mar -2673 */
124 {7, 775157.5}, /* 08 Apr -2590 */
125 {8, 779143.5}, /* 07 Mar -2579 */
126 {9, 783131.5}, /* 06 Feb -2568 */
127 {10, 820044.5}, /* 28 Feb -2467 */
128 {11, 810859.5}, /* 06 Jan -2492 */
129 {12, 748993.5}, /* 20 Aug -2662 */
130 {13, 792492.5}, /* 23 Sep -2543 */
131 {14, 789892.5}, /* 11 Aug -2550 */
132 {15, 787294.5}, /* 01 Jul -2557 */
133 {16, 824207.5}, /* 23 Jul -2456 */
134 {17, 834779.5}, /* 03 Jul -2427 */
135 {18, 838766.5}, /* 02 Jun -2416 */
136 {19, 869094.5}, /* 15 Jun -2333 */
137 {20, 886251.5}, /* 05 Jun -2286 */
138 {21, 890238.5}, /* 05 May -2275 */
139 {22, 927151.5}, /* 28 May -2174 */
140 {23, 937722.5}, /* 07 May -2145 */
141 {24, 941709.5}, /* 06 Apr -2134 */
142 {25, 978623.5}, /* 30 Apr -2033 */
143 {26, 989194.5}, /* 08 Apr -2004 */
144 {27, 993181.5}, /* 09 Mar -1993 */
145 {28, 1023510.5}, /* 22 Mar -1910 */
146 {29, 1034081.5}, /* 01 Mar -1881 */
147 {30, 972214.5}, /* 12 Oct -2051 */
148 {31, 1061811.5}, /* 31 Jan -1805 */
149 {32, 1006529.5}, /* 24 Sep -1957 */
150 {33, 997345.5}, /* 02 Aug -1982 */
151 {34, 1021088.5}, /* 04 Aug -1917 */
152 {35, 1038245.5}, /* 25 Jul -1870 */
153 {36, 1042231.5}, /* 23 Jun -1859 */
154 {37, 1065974.5}, /* 25 Jun -1794 */
155 {38, 1089716.5}, /* 26 Jun -1729 */
156 {39, 1093703.5}, /* 26 May -1718 */
157 {40, 1117446.5}, /* 28 May -1653 */
158 {41, 1141188.5}, /* 28 May -1588 */
159 {42, 1145175.5}, /* 28 Apr -1577 */
160 {43, 1168918.5}, /* 29 Apr -1512 */
161 {44, 1192660.5}, /* 30 Apr -1447 */
162 {45, 1196647.5}, /* 30 Mar -1436 */
163 {46, 1220390.5}, /* 01 Apr -1371 */
164 {47, 1244132.5}, /* 02 Apr -1306 */
165 {48, 1234948.5}, /* 08 Feb -1331 */
166 {49, 1265277.5}, /* 22 Feb -1248 */
167 {50, 1282433.5}, /* 11 Feb -1201 */
168 {51, 1207395.5}, /* 02 Sep -1407 */
169 {52, 1217968.5}, /* 14 Aug -1378 */
170 {53, 1254881.5}, /* 06 Sep -1277 */
171 {54, 1252282.5}, /* 25 Jul -1284 */
172 {55, 1262855.5}, /* 06 Jul -1255 */
173 {56, 1293182.5}, /* 17 Jul -1172 */
174 {57, 1297169.5}, /* 17 Jun -1161 */
175 {58, 1314326.5}, /* 07 Jun -1114 */
176 {59, 1344654.5}, /* 19 Jun -1031 */
177 {60, 1348640.5}, /* 18 May -1020 */
178 {61, 1365798.5}, /* 10 May -0973 */
179 {62, 1396126.5}, /* 22 May -0890 */
180 {63, 1400112.5}, /* 20 Apr -0879 */
181 {64, 1417270.5}, /* 11 Apr -0832 */
182 {65, 1447598.5}, /* 24 Apr -0749 */
183 {66, 1444999.5}, /* 12 Mar -0756 */
184 {67, 1462157.5}, /* 04 Mar -0709 */
185 {68, 1492485.5}, /* 16 Mar -0626 */
186 {69, 1456959.5}, /* 09 Dec -0724 */
187 {70, 1421434.5}, /* 05 Sep -0821 */
188 {71, 1471518.5}, /* 19 Oct -0684 */
189 {72, 1455748.5}, /* 16 Aug -0727 */
190 {73, 1466320.5}, /* 27 Jul -0698 */
191 {74, 1496648.5}, /* 08 Aug -0615 */
192 {75, 1500634.5}, /* 07 Jul -0604 */
193 {76, 1511207.5}, /* 18 Jun -0575 */
194 {77, 1548120.5}, /* 11 Jul -0474 */
195 {78, 1552106.5}, /* 09 Jun -0463 */
196 {79, 1562679.5}, /* 21 May -0434 */
197 {80, 1599592.5}, /* 13 Jun -0333 */
198 {81, 1603578.5}, /* 12 May -0322 */
199 {82, 1614150.5}, /* 22 Apr -0293 */
200 {83, 1644479.5}, /* 05 May -0210 */
201 {84, 1655050.5}, /* 14 Apr -0181 */
202 {85, 1659037.5}, /* 14 Mar -0170 */
203 {86, 1695950.5}, /* 06 Apr -0069 */
204 {87, 1693351.5}, /* 23 Feb -0076 */
205 {88, 1631484.5}, /* 06 Oct -0246 */
206 {89, 1727666.5}, /* 04 Feb 0018 */
207 {90, 1672384.5}, /* 28 Sep -0134 */
208 {91, 1663200.5}, /* 06 Aug -0159 */
209 {92, 1693529.5}, /* 19 Aug -0076 */
210 {93, 1710685.5}, /* 09 Aug -0029 */
211 {94, 1714672.5}, /* 09 Jul -0018 */
212 {95, 1738415.5}, /* 11 Jul 0047 */
213 {96, 1755572.5}, /* 01 Jul 0094 */
214 {97, 1766144.5}, /* 11 Jun 0123 */
215 {98, 1789887.5}, /* 12 Jun 0188 */
216 {99, 1807044.5}, /* 03 Jun 0235 */
217 {100, 1817616.5}, /* 13 May 0264 */
218 {101, 1841359.5}, /* 15 May 0329 */
219 {102, 1858516.5}, /* 05 May 0376 */
220 {103, 1862502.5}, /* 04 Apr 0387 */
221 {104, 1892831.5}, /* 17 Apr 0470 */
222 {105, 1903402.5}, /* 27 Mar 0499 */
223 {106, 1887633.5}, /* 23 Jan 0456 */
224 {107, 1924547.5}, /* 15 Feb 0557 */
225 {108, 1921948.5}, /* 04 Jan 0550 */
226 {109, 1873251.5}, /* 07 Sep 0416 */
227 {110, 1890409.5}, /* 30 Aug 0463 */
228 {111, 1914151.5}, /* 30 Aug 0528 */
229 {112, 1918138.5}, /* 31 Jul 0539 */
230 {113, 1935296.5}, /* 22 Jul 0586 */
231 {114, 1959038.5}, /* 23 Jul 0651 */
232 {115, 1963024.5}, /* 21 Jun 0662 */
233 {116, 1986767.5}, /* 23 Jun 0727 */
234 {117, 2010510.5}, /* 24 Jun 0792 */
235 {118, 2014496.5}, /* 24 May 0803 */
236 {119, 2031654.5}, /* 15 May 0850 */
237 {120, 2061982.5}, /* 27 May 0933 */
238 {121, 2065968.5}, /* 25 Apr 0944 */
239 {122, 2083126.5}, /* 17 Apr 0991 */
240 {123, 2113454.5}, /* 29 Apr 1074 */
241 {124, 2104269.5}, /* 06 Mar 1049 */
242 {125, 2108256.5}, /* 04 Feb 1060 */
243 {126, 2151755.5}, /* 10 Mar 1179 */
244 {127, 2083302.5}, /* 10 Oct 0991 */
245 {128, 2080704.5}, /* 29 Aug 0984 */
246 {129, 2124203.5}, /* 03 Oct 1103 */
247 {130, 2121603.5}, /* 20 Aug 1096 */
248 {131, 2132176.5}, /* 01 Aug 1125 */
249 {132, 2162504.5}, /* 13 Aug 1208 */
250 {133, 2166490.5}, /* 13 Jul 1219 */
251 {134, 2177062.5}, /* 22 Jun 1248 */
252 {135, 2207390.5}, /* 05 Jul 1331 */
253 {136, 2217962.5}, /* 14 Jun 1360 */
254 {137, 2228534.5}, /* 25 May 1389 */
255 {138, 2258862.5}, /* 06 Jun 1472 */
256 {139, 2269434.5}, /* 17 May 1501 */
257 {140, 2273421.5}, /* 16 Apr 1512 */
258 {141, 2310334.5}, /* 19 May 1613 */
259 {142, 2314320.5}, /* 17 Apr 1624 */
260 {143, 2311722.5}, /* 07 Mar 1617 */
261 {144, 2355221.5}, /* 11 Apr 1736 */
262 {145, 2319695.5}, /* 04 Jan 1639 */
263 {146, 2284169.5}, /* 19 Sep 1541 */
264 {147, 2314498.5}, /* 12 Oct 1624 */
265 {148, 2325069.5}, /* 21 Sep 1653 */
266 {149, 2329056.5}, /* 21 Aug 1664 */
267 {150, 2352799.5}, /* 24 Aug 1729 */
268 {151, 2369956.5}, /* 14 Aug 1776 */
269 {152, 2380528.5}, /* 26 Jul 1805 */
270 {153, 2404271.5}, /* 28 Jul 1870 */
271 {154, 2421428.5}, /* 19 Jul 1917 */
272 {155, 2425414.5}, /* 17 Jun 1928 */
273 {156, 2455743.5}, /* 01 Jul 2011 */
274 {157, 2472900.5}, /* 21 Jun 2058 */
275 {158, 2476886.5}, /* 20 May 2069 */
276 {159, 2500629.5}, /* 23 May 2134 */
277 {160, 2517786.5}, /* 13 May 2181 */
278 {161, 2515187.5}, /* 01 Apr 2174 */
279 {162, 2545516.5}, /* 15 Apr 2257 */
280 {163, 2556087.5}, /* 25 Mar 2286 */
281 {164, 2487635.5}, /* 24 Oct 2098 */
282 {165, 2504793.5}, /* 16 Oct 2145 */
283 {166, 2535121.5}, /* 29 Oct 2228 */
284 {167, 2525936.5}, /* 06 Sep 2203 */
285 {168, 2543094.5}, /* 28 Aug 2250 */
286 {169, 2573422.5}, /* 10 Sep 2333 */
287 {170, 2577408.5}, /* 09 Aug 2344 */
288 {171, 2594566.5}, /* 01 Aug 2391 */
289 {172, 2624894.5}, /* 13 Aug 2474 */
290 {173, 2628880.5}, /* 12 Jul 2485 */
291 {174, 2646038.5}, /* 04 Jul 2532 */
292 {175, 2669780.5}, /* 05 Jul 2597 */
293 {176, 2673766.5}, /* 04 Jun 2608 */
294 {177, 2690924.5}, /* 27 May 2655 */
295 {178, 2721252.5}, /* 09 Jun 2738 */
296 {179, 2718653.5}, /* 28 Apr 2731 */
297 {180, 2729226.5}, /* 08 Apr 2760 */
298 };
299
300 // Saros cycle numbers of lunar eclipses with date of initial
301 // eclipse. Table was derived from the table of the Nasa Eclipse Web Site:
302 // https://eclipse.gsfc.nasa.gov/LEsaros/LEsaroscat.html
303 // Note, for eclipse dates =< 29 April -1337 and eclipse dates
304 // >= 10 Aug 2892, Saros cycle numbers cannot always be given.
305 #define NSAROS_LUNAR 180
306 struct saros_data saros_data_lunar[NSAROS_LUNAR] = {
307 {1, 782437.5}, /* 14 Mar -2570 */
308 {2, 799593.5}, /* 03 Mar -2523 */
309 {3, 783824.5}, /* 30 Dec -2567 */
310 {4, 754884.5}, /* 06 Oct -2646 */
311 {5, 824724.5}, /* 22 Dec -2455 */
312 {6, 762857.5}, /* 04 Aug -2624 */
313 {7, 773430.5}, /* 16 Jul -2595 */
314 {8, 810343.5}, /* 08 Aug -2494 */
315 {9, 807743.5}, /* 26 Jun -2501 */
316 {10, 824901.5}, /* 17 Jun -2454 */
317 {11, 855229.5}, /* 29 Jun -2371 */
318 {12, 859215.5}, /* 28 May -2360 */
319 {13, 876373.5}, /* 20 May -2313 */
320 {14, 906701.5}, /* 01 Jun -2230 */
321 {15, 910687.5}, /* 30 Apr -2219 */
322 {16, 927845.5}, /* 21 Apr -2172 */
323 {17, 958173.5}, /* 04 May -2089 */
324 {18, 962159.5}, /* 02 Apr -2078 */
325 {19, 979317.5}, /* 24 Mar -2031 */
326 {20, 1009645.5}, /* 05 Apr -1948 */
327 {21, 1007046.5}, /* 22 Feb -1955 */
328 {22, 1017618.5}, /* 02 Feb -1926 */
329 {23, 1054531.5}, /* 25 Feb -1825 */
330 {24, 979493.5}, /* 16 Sep -2031 */
331 {25, 976895.5}, /* 06 Aug -2038 */
332 {26, 1020394.5}, /* 09 Sep -1919 */
333 {27, 1017794.5}, /* 28 Jul -1926 */
334 {28, 1028367.5}, /* 09 Jul -1897 */
335 {29, 1058695.5}, /* 21 Jul -1814 */
336 {30, 1062681.5}, /* 19 Jun -1803 */
337 {31, 1073253.5}, /* 30 May -1774 */
338 {32, 1110167.5}, /* 23 Jun -1673 */
339 {33, 1114153.5}, /* 22 May -1662 */
340 {34, 1131311.5}, /* 13 May -1615 */
341 {35, 1161639.5}, /* 25 May -1532 */
342 {36, 1165625.5}, /* 24 Apr -1521 */
343 {37, 1176197.5}, /* 03 Apr -1492 */
344 {38, 1213111.5}, /* 27 Apr -1391 */
345 {39, 1217097.5}, /* 26 Mar -1380 */
346 {40, 1221084.5}, /* 24 Feb -1369 */
347 {41, 1257997.5}, /* 18 Mar -1268 */
348 {42, 1255398.5}, /* 04 Feb -1275 */
349 {43, 1186946.5}, /* 07 Sep -1463 */
350 {44, 1283128.5}, /* 06 Jan -1199 */
351 {45, 1227845.5}, /* 29 Aug -1351 */
352 {46, 1225247.5}, /* 19 Jul -1358 */
353 {47, 1255575.5}, /* 31 Jul -1275 */
354 {48, 1272732.5}, /* 21 Jul -1228 */
355 {49, 1276719.5}, /* 21 Jun -1217 */
356 {50, 1307047.5}, /* 03 Jul -1134 */
357 {51, 1317619.5}, /* 13 Jun -1105 */
358 {52, 1328191.5}, /* 23 May -1076 */
359 {53, 1358519.5}, /* 05 Jun -0993 */
360 {54, 1375676.5}, /* 26 May -0946 */
361 {55, 1379663.5}, /* 25 Apr -0935 */
362 {56, 1409991.5}, /* 07 May -0852 */
363 {57, 1420562.5}, /* 16 Apr -0823 */
364 {58, 1424549.5}, /* 16 Mar -0812 */
365 {59, 1461463.5}, /* 09 Apr -0711 */
366 {60, 1465449.5}, /* 08 Mar -0700 */
367 {61, 1436509.5}, /* 13 Dec -0780 */
368 {62, 1493179.5}, /* 08 Feb -0624 */
369 {63, 1457653.5}, /* 03 Nov -0722 */
370 {64, 1435298.5}, /* 20 Aug -0783 */
371 {65, 1452456.5}, /* 11 Aug -0736 */
372 {66, 1476198.5}, /* 12 Aug -0671 */
373 {67, 1480184.5}, /* 11 Jul -0660 */
374 {68, 1503928.5}, /* 14 Jul -0595 */
375 {69, 1527670.5}, /* 15 Jul -0530 */
376 {70, 1531656.5}, /* 13 Jun -0519 */
377 {71, 1548814.5}, /* 04 Jun -0472 */
378 {72, 1579142.5}, /* 17 Jun -0389 */
379 {73, 1583128.5}, /* 16 May -0378 */
380 {74, 1600286.5}, /* 07 May -0331 */
381 {75, 1624028.5}, /* 08 May -0266 */
382 {76, 1628015.5}, /* 07 Apr -0255 */
383 {77, 1651758.5}, /* 09 Apr -0190 */
384 {78, 1675500.5}, /* 10 Apr -0125 */
385 {79, 1672901.5}, /* 27 Feb -0132 */
386 {80, 1683474.5}, /* 07 Feb -0103 */
387 {81, 1713801.5}, /* 19 Feb -0020 */
388 {82, 1645349.5}, /* 21 Sep -0208 */
389 {83, 1649336.5}, /* 22 Aug -0197 */
390 {84, 1686249.5}, /* 13 Sep -0096 */
391 {85, 1683650.5}, /* 02 Aug -0103 */
392 {86, 1694222.5}, /* 13 Jul -0074 */
393 {87, 1731136.5}, /* 06 Aug 0027 */
394 {88, 1735122.5}, /* 05 Jul 0038 */
395 {89, 1745694.5}, /* 15 Jun 0067 */
396 {90, 1776022.5}, /* 27 Jun 0150 */
397 {91, 1786594.5}, /* 07 Jun 0179 */
398 {92, 1797166.5}, /* 17 May 0208 */
399 {93, 1827494.5}, /* 30 May 0291 */
400 {94, 1838066.5}, /* 09 May 0320 */
401 {95, 1848638.5}, /* 19 Apr 0349 */
402 {96, 1878966.5}, /* 01 May 0432 */
403 {97, 1882952.5}, /* 31 Mar 0443 */
404 {98, 1880354.5}, /* 18 Feb 0436 */
405 {99, 1923853.5}, /* 24 Mar 0555 */
406 {100, 1881741.5}, /* 06 Dec 0439 */
407 {101, 1852801.5}, /* 11 Sep 0360 */
408 {102, 1889715.5}, /* 05 Oct 0461 */
409 {103, 1893701.5}, /* 03 Sep 0472 */
410 {104, 1897688.5}, /* 04 Aug 0483 */
411 {105, 1928016.5}, /* 16 Aug 0566 */
412 {106, 1938588.5}, /* 27 Jul 0595 */
413 {107, 1942575.5}, /* 26 Jun 0606 */
414 {108, 1972903.5}, /* 08 Jul 0689 */
415 {109, 1990059.5}, /* 27 Jun 0736 */
416 {110, 1994046.5}, /* 28 May 0747 */
417 {111, 2024375.5}, /* 10 Jun 0830 */
418 {112, 2034946.5}, /* 20 May 0859 */
419 {113, 2045518.5}, /* 29 Apr 0888 */
420 {114, 2075847.5}, /* 13 May 0971 */
421 {115, 2086418.5}, /* 21 Apr 1000 */
422 {116, 2083820.5}, /* 11 Mar 0993 */
423 {117, 2120733.5}, /* 03 Apr 1094 */
424 {118, 2124719.5}, /* 02 Mar 1105 */
425 {119, 2062852.5}, /* 14 Oct 0935 */
426 {120, 2086596.5}, /* 16 Oct 1000 */
427 {121, 2103752.5}, /* 06 Oct 1047 */
428 {122, 2094568.5}, /* 14 Aug 1022 */
429 {123, 2118311.5}, /* 16 Aug 1087 */
430 {124, 2142054.5}, /* 17 Aug 1152 */
431 {125, 2146040.5}, /* 17 Jul 1163 */
432 {126, 2169783.5}, /* 18 Jul 1228 */
433 {127, 2186940.5}, /* 09 Jul 1275 */
434 {128, 2197512.5}, /* 18 Jun 1304 */
435 {129, 2214670.5}, /* 10 Jun 1351 */
436 {130, 2238412.5}, /* 10 Jun 1416 */
437 {131, 2242398.5}, /* 10 May 1427 */
438 {132, 2266142.5}, /* 12 May 1492 */
439 {133, 2289884.5}, /* 13 May 1557 */
440 {134, 2287285.5}, /* 01 Apr 1550 */
441 {135, 2311028.5}, /* 13 Apr 1615 */
442 {136, 2334770.5}, /* 13 Apr 1680 */
443 {137, 2292659.5}, /* 17 Dec 1564 */
444 {138, 2276890.5}, /* 15 Oct 1521 */
445 {139, 2326974.5}, /* 09 Dec 1658 */
446 {140, 2304619.5}, /* 25 Sep 1597 */
447 {141, 2308606.5}, /* 25 Aug 1608 */
448 {142, 2345520.5}, /* 19 Sep 1709 */
449 {143, 2349506.5}, /* 18 Aug 1720 */
450 {144, 2360078.5}, /* 29 Jul 1749 */
451 {145, 2390406.5}, /* 11 Aug 1832 */
452 {146, 2394392.5}, /* 11 Jul 1843 */
453 {147, 2411550.5}, /* 02 Jul 1890 */
454 {148, 2441878.5}, /* 15 Jul 1973 */
455 {149, 2445864.5}, /* 13 Jun 1984 */
456 {150, 2456437.5}, /* 25 May 2013 */
457 {151, 2486765.5}, /* 06 Jun 2096 */
458 {152, 2490751.5}, /* 07 May 2107 */
459 {153, 2501323.5}, /* 16 Apr 2136 */
460 {154, 2538236.5}, /* 10 May 2237 */
461 {155, 2529052.5}, /* 18 Mar 2212 */
462 {156, 2473771.5}, /* 08 Nov 2060 */
463 {157, 2563367.5}, /* 01 Mar 2306 */
464 {158, 2508085.5}, /* 21 Oct 2154 */
465 {159, 2505486.5}, /* 09 Sep 2147 */
466 {160, 2542400.5}, /* 03 Oct 2248 */
467 {161, 2546386.5}, /* 02 Sep 2259 */
468 {162, 2556958.5}, /* 12 Aug 2288 */
469 {163, 2587287.5}, /* 27 Aug 2371 */
470 {164, 2597858.5}, /* 05 Aug 2400 */
471 {165, 2601845.5}, /* 06 Jul 2411 */
472 {166, 2632173.5}, /* 18 Jul 2494 */
473 {167, 2649330.5}, /* 09 Jul 2541 */
474 {168, 2653317.5}, /* 08 Jun 2552 */
475 {169, 2683645.5}, /* 22 Jun 2635 */
476 {170, 2694217.5}, /* 01 Jun 2664 */
477 {171, 2698203.5}, /* 01 May 2675 */
478 {172, 2728532.5}, /* 15 May 2758 */
479 {173, 2739103.5}, /* 24 Apr 2787 */
480 {174, 2683822.5}, /* 16 Dec 2635 */
481 {175, 2740492.5}, /* 11 Feb 2791 */
482 {176, 2724722.5}, /* 09 Dec 2747 */
483 {177, 2708952.5}, /* 05 Oct 2704 */
484 {178, 2732695.5}, /* 07 Oct 2769 */
485 {179, 2749852.5}, /* 27 Sep 2816 */
486 {180, 2753839.5}, /* 28 Aug 2827 */
487 };
488
489 /* Computes geographic location and type of solar eclipse
490 * for a given tjd
491 * iflag: to indicate ephemeris to be used
492 * (SEFLG_JPLEPH, SEFLG_SWIEPH, SEFLG_MOSEPH)
493 *
494 * Algorithms for the central line is taken from Montenbruck, pp. 179ff.,
495 * with the exception, that we consider refraction for the maxima of
496 * partial and noncentral eclipses.
497 * Geographical positions are referred to sea level / the mean ellipsoid.
498 *
499 * Errors:
500 * - from uncertainty of JPL-ephemerides (0.01 arcsec):
501 * about 40 meters
502 * - from displacement of shadow points by atmospheric refraction:
503 * a few meters
504 * - from deviation of the geoid from the ellipsoid
505 * a few meters
506 * - from polar motion
507 * a few meters
508 * For geographical locations that are interesting for observation,
509 * the error is always < 100 m.
510 * However, if the sun is close to the horizon,
511 * all of these errors can grow up to a km or more.
512 *
513 * Function returns:
514 * -1 (ERR) on error (e.g. if swe_calc() for sun or moon fails)
515 * 0 if there is no solar eclipse at tjd
516 * SE_ECL_TOTAL
517 * SE_ECL_ANNULAR
518 * SE_ECL_TOTAL | SE_ECL_CENTRAL
519 * SE_ECL_TOTAL | SE_ECL_NONCENTRAL
520 * SE_ECL_ANNULAR | SE_ECL_CENTRAL
521 * SE_ECL_ANNULAR | SE_ECL_NONCENTRAL
522 * SE_ECL_PARTIAL
523 *
524 * geopos[0]: geographic longitude of central line
525 * geopos[1]: geographic latitude of central line
526 *
527 * not implemented so far:
528 *
529 * geopos[2]: geographic longitude of northern limit of umbra
530 * geopos[3]: geographic latitude of northern limit of umbra
531 * geopos[4]: geographic longitude of southern limit of umbra
532 * geopos[5]: geographic latitude of southern limit of umbra
533 * geopos[6]: geographic longitude of northern limit of penumbra
534 * geopos[7]: geographic latitude of northern limit of penumbra
535 * geopos[8]: geographic longitude of southern limit of penumbra
536 * geopos[9]: geographic latitude of southern limit of penumbra
537 *
538 * Attention: "northern" and "southern" limits of umbra do not
539 * necessarily correspond to the northernmost or southernmost
540 * geographic position, where the total, annular, or partial
541 * phase is visible at a given time.
542 * Imagine a situation in northern summer, when the sun illuminates
543 * the northern polar circle. The southernmost point of the core
544 * shadow may then touch the north pole, and therefore the
545 * northernmost point will be more in the south.
546 * Note also that with annular eclipses, the northern edge is
547 * usually geographically the southern one. With annular-total
548 * ones, the two lines cross, usually twice. The maximum is always
549 * total in such cases.
550 *
551 * attr[0] fraction of solar diameter covered by moon (magnitude)
552 * attr[1] ratio of lunar diameter to solar one
553 * attr[2] fraction of solar disc covered by moon (obscuration)
554 * attr[3] diameter of core shadow in km
555 * attr[4] azimuth of sun at tjd
556 * attr[5] true altitude of sun above horizon at tjd
557 * attr[6] apparent altitude of sun above horizon at tjd
558 * attr[7] angular distance of moon from sun in degrees
559 * attr[8] magnitude acc. to NASA;
560 * = attr[0] for partial and attr[1] for annular and total eclipses
561 * attr[9] saros series number
562 * attr[10] saros series member number
563 * declare as attr[20] at least !
564 */
swe_sol_eclipse_where(double tjd_ut,int32 ifl,double * geopos,double * attr,char * serr)565 int32 CALL_CONV swe_sol_eclipse_where(
566 double tjd_ut,
567 int32 ifl,
568 double *geopos,
569 double *attr,
570 char *serr)
571 {
572 int32 retflag, retflag2;
573 double dcore[10];
574 ifl &= SEFLG_EPHMASK;
575 swi_set_tid_acc(tjd_ut, ifl, 0, serr);
576 if ((retflag = eclipse_where(tjd_ut, SE_SUN, NULL, ifl, geopos, dcore, serr)) < 0)
577 return retflag;
578 if ((retflag2 = eclipse_how(tjd_ut, SE_SUN, NULL, ifl, geopos[0], geopos[1], 0, attr, serr)) == ERR)
579 return retflag2;
580 attr[3] = dcore[0];
581 return retflag;
582 }
583
584 /*
585 double tjd_ut, time, Jul. day UT
586 int32 ipl, planet number
587 char* starname, star name, must be NULL or ”” if not a star
588 int32 ifl, ephemeris flag
589 double *geopos, return array, 2 doubles, geo. long. and lat.
590 eastern longitude is positive,
591 western longitude is negative,
592 northern latitude is positive,
593 double *attr, return array, 20 doubles, see below
594 char *serr return error string
595
596 array attr:
597 attr[0] fraction of object's diameter covered by moon (magnitude)
598 attr[1] ratio of lunar diameter to object's diameter
599 attr[2] fraction of object's disc covered by moon (obscuration)
600 attr[3] diameter of core shadow in km
601 attr[4] azimuth of object at tjd
602 attr[5] true altitude of object above horizon at tjd
603 attr[6] apparent altitude of object above horizon at tjd
604 attr[7] angular distance of moon from object in degrees
605 */
swe_lun_occult_where(double tjd_ut,int32 ipl,char * starname,int32 ifl,double * geopos,double * attr,char * serr)606 int32 CALL_CONV swe_lun_occult_where(
607 double tjd_ut,
608 int32 ipl,
609 char *starname,
610 int32 ifl,
611 double *geopos,
612 double *attr,
613 char *serr)
614 {
615 int32 retflag, retflag2;
616 double dcore[10];
617 if (ipl < 0) ipl = 0;
618 ifl &= SEFLG_EPHMASK;
619 swi_set_tid_acc(tjd_ut, ifl, 0, serr);
620 /* function calls for Pluto with asteroid number 134340
621 * are treated as calls for Pluto as main body SE_PLUTO */
622 if (ipl == SE_AST_OFFSET + 134340)
623 ipl = SE_PLUTO;
624 if ((retflag = eclipse_where(tjd_ut, ipl, starname, ifl, geopos, dcore, serr)) < 0)
625 return retflag;
626 if ((retflag2 = eclipse_how(tjd_ut, ipl, starname, ifl, geopos[0], geopos[1], 0, attr, serr)) == ERR)
627 return retflag2;
628 attr[3] = dcore[0];
629 return retflag;
630 }
631
632 /* Used by several swe_sol_eclipse_ functions.
633 * Like swe_sol_eclipse_where(), but instead of attr[0], it returns:
634 *
635 * dcore[0]: core shadow width in km
636 * dcore[2]: distance of shadow axis from geocenter r0
637 * dcore[3]: diameter of core shadow on fundamental plane d0
638 * dcore[4]: diameter of half-shadow on fundamental plane D0
639 */
eclipse_where(double tjd_ut,int32 ipl,char * starname,int32 ifl,double * geopos,double * dcore,char * serr)640 static int32 eclipse_where( double tjd_ut, int32 ipl, char *starname, int32 ifl, double *geopos, double *dcore,
641 char *serr)
642 {
643 int i;
644 int32 retc = 0, niter = 0;
645 double e[6], et[6], rm[6], rs[6], rmt[6], rst[6], xs[6], xst[6];
646 #if 0
647 double erm[6];
648 #endif
649 double x[6];
650 double lm[6], ls[6], lx[6];
651 double dsm, dsmt, d0, D0, s0, r0, d, s, dm;
652 double de = 6378140.0 / AUNIT;
653 double earthobl = 1 - EARTH_OBLATENESS;
654 double deltat, tjd, sidt;
655 double drad;
656 double sinf1, sinf2, cosf1, cosf2;
657 double rmoon = RMOON;
658 double dmoon = 2 * rmoon;
659 int32 iflag, iflag2;
660 /* double ecce = sqrt(2 * EARTH_OBLATENESS - EARTH_OBLATENESS * EARTH_OBLATENESS); */
661 AS_BOOL no_eclipse = FALSE;
662 struct epsilon *oe = &swed.oec;
663 for (i = 0; i < 10; i++)
664 dcore[i] = 0;
665 /* nutation need not be in lunar and solar positions,
666 * if mean sidereal time will be used */
667 iflag = SEFLG_SPEED | SEFLG_EQUATORIAL | ifl;
668 iflag2 = iflag | SEFLG_RADIANS;
669 iflag = iflag | SEFLG_XYZ;
670 deltat = swe_deltat_ex(tjd_ut, ifl, serr);
671 tjd = tjd_ut + deltat;
672 /* moon in cartesian coordinates */
673 if ((retc = swe_calc(tjd, SE_MOON, iflag, rm, serr)) == ERR)
674 return retc;
675 /* moon in polar coordinates */
676 if ((retc = swe_calc(tjd, SE_MOON, iflag2, lm, serr)) == ERR)
677 return retc;
678 /* sun in cartesian coordinates */
679 if ((retc = calc_planet_star(tjd, ipl, starname, iflag, rs, serr)) == ERR)
680 return retc;
681 /* sun in polar coordinates */
682 if ((retc = calc_planet_star(tjd, ipl, starname, iflag2, ls, serr)) == ERR)
683 return retc;
684 /* save sun position */
685 for (i = 0; i <= 2; i++)
686 rst[i] = rs[i];
687 /* save moon position */
688 for (i = 0; i <= 2; i++)
689 rmt[i] = rm[i];
690 if (iflag & SEFLG_NONUT)
691 sidt = swe_sidtime0(tjd_ut, oe->eps * RADTODEG, 0) * 15 * DEGTORAD;
692 else
693 sidt = swe_sidtime(tjd_ut) * 15 * DEGTORAD;
694 /*
695 * radius of planet disk in AU
696 */
697 if (starname != NULL && *starname != '\0')
698 drad = 0;
699 else if (ipl < NDIAM)
700 drad = pla_diam[ipl] / 2 / AUNIT;
701 else if (ipl > SE_AST_OFFSET)
702 drad = swed.ast_diam / 2 * 1000 / AUNIT; /* km -> m -> AU */
703 else
704 drad = 0;
705 iter_where:
706 for (i = 0; i <= 2; i++) {
707 rs[i] = rst[i];
708 rm[i] = rmt[i];
709 }
710 /* Account for oblateness of earth:
711 * Instead of flattening the earth, we apply the
712 * correction to the z coordinate of the moon and
713 * the sun. This makes the calculation easier.
714 */
715 for (i = 0; i <= 2; i++)
716 lx[i] = lm[i];
717 swi_polcart(lx, rm);
718 rm[2] /= earthobl;
719 /* distance of moon from geocenter */
720 dm = sqrt(square_sum(rm));
721 /* Account for oblateness of earth */
722 for (i = 0; i <= 2; i++)
723 lx[i] = ls[i];
724 swi_polcart(lx, rs);
725 rs[2] /= earthobl;
726 /* sun - moon vector */
727 for (i = 0; i <= 2; i++) {
728 e[i] = (rm[i] - rs[i]);
729 et[i] = (rmt[i] - rst[i]);
730 }
731 /* distance sun - moon */
732 dsm = sqrt(square_sum(e));
733 dsmt = sqrt(square_sum(et));
734 /* sun - moon unit vector */
735 for (i = 0; i <= 2; i++) {
736 e[i] /= dsm;
737 et[i] /= dsmt;
738 #if 0
739 erm[i] = rm[i] / dm;
740 #endif
741 }
742 sinf1 = ((drad - rmoon) / dsm);
743 cosf1 = sqrt(1 - sinf1 * sinf1);
744 sinf2 = ((drad + rmoon) / dsm);
745 cosf2 = sqrt(1 - sinf2 * sinf2);
746 /* distance of moon from fundamental plane */
747 s0 = -dot_prod(rm, e);
748 /* distance of shadow axis from geocenter */
749 r0 = sqrt(dm * dm - s0 * s0);
750 /* diameter of core shadow on fundamental plane */
751 d0 = (s0 / dsm * (drad * 2 - dmoon) - dmoon) / cosf1;
752 /* diameter of half-shadow on fundamental plane */
753 D0 = (s0 / dsm * (drad * 2 + dmoon) + dmoon) / cosf2;
754 dcore[2] = r0;
755 dcore[3] = d0;
756 dcore[4] = D0;
757 dcore[5] = cosf1;
758 dcore[6] = cosf2;
759 for (i = 2; i < 5; i++)
760 dcore[i] *= AUNIT / 1000.0;
761 /**************************
762 * central (total or annular) phase
763 **************************/
764 retc = 0;
765 if (de * cosf1 >= r0) {
766 retc |= SE_ECL_CENTRAL;
767 } else if (r0 <= de * cosf1 + fabs(d0) / 2) {
768 retc |= SE_ECL_NONCENTRAL;
769 } else if (r0 <= de * cosf2 + D0 / 2) {
770 retc |= (SE_ECL_PARTIAL | SE_ECL_NONCENTRAL);
771 } else {
772 if (serr != NULL)
773 sprintf(serr, "no solar eclipse at tjd = %f", tjd);
774 for (i = 0; i < 10; i++)
775 geopos[i] = 0;
776 *dcore = 0;
777 retc = 0;
778 d = 0;
779 no_eclipse = TRUE;
780 /*return retc;*/
781 }
782 /* distance of shadow point from fundamental plane */
783 d = s0 * s0 + de * de - dm * dm;
784 if (d > 0)
785 d = sqrt(d);
786 else
787 d = 0;
788 /* distance of moon from shadow point on earth */
789 s = s0 - d;
790 /* next: geographic position of eclipse center.
791 * if shadow axis does not touch the earth,
792 * place on earth with maximum occultation is computed.
793 */
794 #if 0 /* the following stuff is meaningless for observations */
795 /*
796 * account for refraction at horizon
797 */
798 if (d == 0) {
799 double ds, a, b;
800 /* distance of sun from geocenter */
801 ds = sqrt(square_sum(rs));
802 a = PI - acos(swi_dot_prod_unit(e, erm));
803 /* refraction at horizon + sun radius = about 0.83 degrees */
804 b = 34.4556 / 60.0 * DEGTORAD + asin(drad / ds);
805 # if 0
806 /* at edge of umbra and penumbra
807 * light rays are not parallel to shadow axis.
808 * for a short time close to contact of umbra and
809 * penumbra, an angle < 0.27 degrees would have
810 * to be subtracted from b;
811 */
812 if (retc & SE_ECL_PARTIAL) {
813 d = d0;
814 sinf = sinf1;
815 } else {
816 d = D0;
817 sinf = sinf2;
818 }
819 c = (r0 - de) / d * 2 * sinf;
820 if (c > sinf1) {
821 b -= .....;
822 }
823 printf("%f %f %f", a * RADTODEG, b * RADTODEG, s);
824 printf(" %f\n", s);
825 # else
826 if (retc & SE_ECL_PARTIAL)
827 b -= asin(sinf2); /* maximum! */
828 else
829 b -= asin(sinf1);
830 # endif
831 s += tan(b) * cos(PI / 2 - a) * dm;
832 }
833 #endif
834 /* geographic position of eclipse center (maximum) */
835 for (i = 0; i <= 2; i++)
836 xs[i] = rm[i] + s * e[i];
837 /* we need geographic position with correct z, as well */
838 for (i = 0; i <= 2; i++)
839 xst[i] = xs[i];
840 xst[2] *= earthobl;
841 swi_cartpol(xst, xst);
842 if (niter <= 0) {
843 double cosfi = cos(xst[1]);
844 double sinfi = sin(xst[1]);
845 double eobl = EARTH_OBLATENESS;
846 double cc= 1 / sqrt(cosfi * cosfi + (1-eobl) * (1-eobl) * sinfi * sinfi);
847 double ss= (1-eobl) * (1-eobl) * cc;
848 earthobl = ss;
849 niter++;
850 goto iter_where;
851 }
852 swi_polcart(xst, xst);
853 /* to longitude and latitude */
854 swi_cartpol(xs, xs);
855 /* measure from sidereal time at greenwich */
856 xs[0] -= sidt;
857 xs[0] *= RADTODEG;
858 xs[1] *= RADTODEG;
859 xs[0] = swe_degnorm(xs[0]);
860 /* west is negative */
861 if (xs[0] > 180)
862 xs[0] -= 360;
863 geopos[0] = xs[0];
864 geopos[1] = xs[1];
865 /* diameter of core shadow:
866 * first, distance moon - place of eclipse on earth */
867 for (i = 0; i <= 2; i++)
868 x[i] = rmt[i] - xst[i];
869 s = sqrt(square_sum(x));
870 /* diameter of core shadow at place of maximum eclipse */
871 *dcore = (s / dsmt * ( drad * 2 - dmoon) - dmoon) * cosf1;
872 *dcore *= AUNIT / 1000.0;
873 /* diameter of penumbra at place of maximum eclipse */
874 dcore[1] = (s / dsmt * ( drad * 2 + dmoon) + dmoon) * cosf2;
875 dcore[1] *= AUNIT / 1000.0;
876 if (!(retc & SE_ECL_PARTIAL) && !no_eclipse) {
877 if (*dcore > 0) {
878 /*printf("annular\n");*/
879 retc |= SE_ECL_ANNULAR;
880 } else {
881 /*printf("total\n");*/
882 retc |= SE_ECL_TOTAL;
883 }
884 }
885 return retc;
886 }
887
calc_planet_star(double tjd_et,int32 ipl,char * starname,int32 iflag,double * x,char * serr)888 static int32 calc_planet_star(double tjd_et, int32 ipl, char *starname, int32 iflag, double *x, char *serr)
889 {
890 int retc = OK;
891 if (starname == NULL || *starname == '\0') {
892 retc = swe_calc(tjd_et, ipl, iflag, x, serr);
893 } else {
894 retc = swe_fixstar(starname, tjd_et, iflag, x, serr);
895 }
896 return retc;
897 }
898
899 /* Computes attributes of a solar eclipse for given tjd, geo. longitude,
900 * geo. latitude, and geo. height.
901 *
902 * retflag SE_ECL_TOTAL or SE_ECL_ANNULAR or SE_ECL_PARTIAL
903 * SE_ECL_NONCENTRAL
904 * if 0, no eclipse is visible at geogr. position.
905 *
906 * attr[0] fraction of solar diameter covered by moon;
907 * with total/annular eclipses, it results in magnitude acc. to IMCCE.
908 * attr[1] ratio of lunar diameter to solar one
909 * attr[2] fraction of solar disc covered by moon (obscuration)
910 * attr[3] diameter of core shadow in km
911 * attr[4] azimuth of sun at tjd
912 * attr[5] true altitude of sun above horizon at tjd
913 * attr[6] apparent altitude of sun above horizon at tjd
914 * attr[7] elongation of moon in degrees
915 * attr[8] magnitude acc. to NASA;
916 * = attr[0] for partial and attr[1] for annular and total eclipses
917 * attr[9] saros series number
918 * attr[10] saros series member number
919 * declare as attr[20] at least !
920 *
921 */
swe_sol_eclipse_how(double tjd_ut,int32 ifl,double * geopos,double * attr,char * serr)922 int32 CALL_CONV swe_sol_eclipse_how(
923 double tjd_ut,
924 int32 ifl,
925 double *geopos,
926 double *attr,
927 char *serr)
928 {
929 int32 retflag, retflag2, i;
930 double dcore[10], ls[6], xaz[6];
931 double geopos2[20];
932 for (i = 0; i <= 10; i++)
933 attr[i] = 0;
934 if (geopos[2] < SEI_ECL_GEOALT_MIN || geopos[2] > SEI_ECL_GEOALT_MAX) {
935 if (serr != NULL)
936 sprintf(serr, "location for eclipses must be between %.0f and %.0f m above sea", SEI_ECL_GEOALT_MIN, SEI_ECL_GEOALT_MAX);
937 return ERR;
938 }
939 ifl &= SEFLG_EPHMASK;
940 swi_set_tid_acc(tjd_ut, ifl, 0, serr);
941 if ((retflag = eclipse_how(tjd_ut, SE_SUN, NULL, ifl, geopos[0], geopos[1], geopos[2], attr, serr)) == ERR)
942 return retflag;
943 if ((retflag2 = eclipse_where(tjd_ut, SE_SUN, NULL, ifl, geopos2, dcore, serr)) == ERR)
944 return retflag2;
945 if (retflag)
946 retflag |= (retflag2 & (SE_ECL_CENTRAL | SE_ECL_NONCENTRAL));
947 attr[3] = dcore[0];
948 swe_set_topo(geopos[0], geopos[1], geopos[2]);
949 if (swe_calc_ut(tjd_ut, SE_SUN, ifl | SEFLG_TOPOCTR | SEFLG_EQUATORIAL, ls, serr) == ERR)
950 return ERR;
951 swe_azalt(tjd_ut, SE_EQU2HOR, geopos, 0, 10, ls, xaz);
952 attr[4] = xaz[0];
953 attr[5] = xaz[1];
954 attr[6] = xaz[2];
955 if (xaz[2] <= 0)
956 retflag = 0;
957 if (retflag == 0) {
958 for (i = 0; i <= 3; i++)
959 attr[i] = 0;
960 for (i = 8; i <= 10; i++)
961 attr[i] = 0;
962 }
963 return retflag;
964 }
965
966 #define USE_AZ_NAV 0
eclipse_how(double tjd_ut,int32 ipl,char * starname,int32 ifl,double geolon,double geolat,double geohgt,double * attr,char * serr)967 static int32 eclipse_how( double tjd_ut, int32 ipl, char *starname, int32 ifl,
968 double geolon, double geolat, double geohgt,
969 double *attr, char *serr)
970 {
971 int i, j, k;
972 int32 retc = 0;
973 double te, d;
974 double xs[6], xm[6], ls[6], lm[6], x1[6], x2[6];
975 double rmoon, rsun, rsplusrm, rsminusrm;
976 double dctr;
977 double drad;
978 int32 iflag = SEFLG_EQUATORIAL | SEFLG_TOPOCTR | ifl;
979 int32 iflagcart = iflag | SEFLG_XYZ;
980 #if USE_AZ_NAV
981 double mdd, eps, sidt, armc;
982 #endif
983 double xh[6], hmin_appr;
984 double lsun, lmoon, lctr, lsunleft, a, b, sc1, sc2;
985 double geopos[3];
986 for (i = 0; i < 10; i++)
987 attr[i] = 0;
988 geopos[0] = geolon;
989 geopos[1] = geolat;
990 geopos[2] = geohgt;
991 te = tjd_ut + swe_deltat_ex(tjd_ut, ifl, serr);
992 swe_set_topo(geolon, geolat, geohgt);
993 if (calc_planet_star(te, ipl, starname, iflag, ls, serr) == ERR)
994 return ERR;
995 if (swe_calc(te, SE_MOON, iflag, lm, serr) == ERR)
996 return ERR;
997 if (calc_planet_star(te, ipl, starname, iflagcart, xs, serr) == ERR)
998 return ERR;
999 if (swe_calc(te, SE_MOON, iflagcart, xm, serr) == ERR)
1000 return ERR;
1001 /*
1002 * radius of planet disk in AU
1003 */
1004 if (starname != NULL && *starname != '\0')
1005 drad = 0;
1006 else if (ipl < NDIAM)
1007 drad = pla_diam[ipl] / 2 / AUNIT;
1008 else if (ipl > SE_AST_OFFSET)
1009 drad = swed.ast_diam / 2 * 1000 / AUNIT; /* km -> m -> AU */
1010 else
1011 drad = 0;
1012 /*
1013 * azimuth and altitude of sun or planet
1014 */
1015 #if USE_AZ_NAV /* old */
1016 eps = swi_epsiln(te, iflag);
1017 if (iflag & SEFLG_NONUT)
1018 sidt = swe_sidtime0(tjd_ut, eps * RADTODEG, 0) * 15;
1019 else
1020 sidt = swe_sidtime(tjd_ut) * 15;
1021 armc = sidt + geolon;
1022 mdd = swe_degnorm(ls[0] - armc);
1023 xh[0] = swe_degnorm(mdd - 90);
1024 xh[1] = ls[1];
1025 xh[2] = ls[2];
1026 swe_cotrans(xh, xh, 90 - geolat); /* azimuth from east, counterclock, via north */
1027 #else
1028 swe_azalt(tjd_ut, SE_EQU2HOR, geopos, 0, 10, ls, xh); /* azimuth from south, clockwise, via west */
1029 #endif
1030 /* eclipse description */
1031 rmoon = asin(RMOON / lm[2]) * RADTODEG;
1032 rsun = asin(drad / ls[2]) * RADTODEG;
1033 rsplusrm = rsun + rmoon;
1034 rsminusrm = rsun - rmoon;
1035 for (i = 0; i < 3; i++) {
1036 x1[i] = xs[i] / ls[2];
1037 x2[i] = xm[i] / lm[2];
1038 }
1039 dctr = acos(swi_dot_prod_unit(x1, x2)) * RADTODEG;
1040 /*
1041 * phase
1042 */
1043 if (dctr < rsminusrm)
1044 retc = SE_ECL_ANNULAR;
1045 else if (dctr < fabs(rsminusrm))
1046 retc = SE_ECL_TOTAL;
1047 else if (dctr < rsplusrm)
1048 retc = SE_ECL_PARTIAL;
1049 else {
1050 retc = 0;
1051 if (serr != NULL)
1052 sprintf(serr, "no solar eclipse at tjd = %f", tjd_ut);
1053 }
1054 /*
1055 * ratio of diameter of moon to that of sun
1056 */
1057 if (rsun > 0)
1058 attr[1] = rmoon / rsun;
1059 else
1060 attr[1] = 0;
1061 /*
1062 * eclipse magnitude:
1063 * fraction of solar diameter covered by moon
1064 */
1065 lsun = asin(rsun / 2 * DEGTORAD) * 2;
1066 lsunleft = (-dctr + rsun + rmoon);
1067 if (lsun > 0) {
1068 attr[0] = lsunleft / rsun / 2;
1069 } else {
1070 //attr[0] = 100;
1071 attr[0] = 1;
1072 }
1073 /*if (retc == SE_ECL_ANNULAR || retc == SE_ECL_TOTAL)
1074 attr[0] = attr[1];*/
1075 /*
1076 * obscuration:
1077 * fraction of solar disc obscured by moon
1078 */
1079 lsun = rsun;
1080 lmoon = rmoon;
1081 lctr = dctr;
1082 if (retc == 0 || lsun == 0) {
1083 //attr[2] = 100;
1084 attr[2] = 1;
1085 } else if (retc == SE_ECL_TOTAL || retc == SE_ECL_ANNULAR) {
1086 attr[2] = lmoon * lmoon / lsun / lsun;
1087 } else {
1088 a = 2 * lctr * lmoon;
1089 b = 2 * lctr * lsun;
1090 if (a < 1e-9) {
1091 attr[2] = lmoon * lmoon / lsun / lsun;
1092 } else {
1093 a = (lctr * lctr + lmoon * lmoon - lsun * lsun) / a;
1094 if (a > 1) a = 1;
1095 if (a < -1) a = -1;
1096 b = (lctr * lctr + lsun * lsun - lmoon * lmoon) / b;
1097 if (b > 1) b = 1;
1098 if (b < -1) b = -1;
1099 a = acos(a);
1100 b = acos(b);
1101 sc1 = a * lmoon * lmoon / 2;
1102 sc2 = b * lsun * lsun / 2;
1103 sc1 -= (cos(a) * sin(a)) * lmoon * lmoon / 2;
1104 sc2 -= (cos(b) * sin(b)) * lsun * lsun / 2;
1105 attr[2] = (sc1 + sc2) * 2 / PI / lsun / lsun;
1106 }
1107 }
1108 attr[7] = dctr;
1109 /* approximate minimum height for visibility, considering
1110 * refraction and dip
1111 * 34.4556': refraction at horizon, from Bennets formulae
1112 * 1.75' / sqrt(geohgt): dip of horizon
1113 * 0.37' / sqrt(geohgt): refraction between horizon and observer */
1114 hmin_appr = -(34.4556 + (1.75 + 0.37) * sqrt(geohgt)) / 60;
1115 if (xh[1] + rsun + fabs(hmin_appr) >= 0 && retc)
1116 retc |= SE_ECL_VISIBLE; /* eclipse visible */
1117 #if USE_AZ_NAV /* old */
1118 attr[4] = swe_degnorm(90 - xh[0]); /* azimuth, from north, clockwise, via east */
1119 #else
1120 attr[4] = xh[0]; /* azimuth, from south, clockwise, via west */
1121 #endif
1122 attr[5] = xh[1]; /* height */
1123 attr[6] = xh[2]; /* height */
1124 if (ipl == SE_SUN && (starname == NULL || *starname == '\0')) {
1125 /* magnitude of solar eclipse according to NASA */
1126 attr[8] = attr[0]; /* fraction of diameter occulted */
1127 if (retc & (SE_ECL_TOTAL | SE_ECL_ANNULAR))
1128 attr[8] = attr[1]; /* ratio between diameters of sun and moon */
1129 /* saros series and member */
1130 for (i = 0; i < NSAROS_SOLAR; i++) {
1131 d = (tjd_ut - saros_data_solar[i].tstart) / SAROS_CYCLE;
1132 if (d < 0 && d * SAROS_CYCLE > -2) d = 0.0000001;
1133 if (d < 0) continue;
1134 j = (int) d;
1135 if ((d - j) * SAROS_CYCLE < 2) {
1136 attr[9] = (double) saros_data_solar[i].series_no;
1137 attr[10] = (double) j + 1;
1138 break;
1139 }
1140 k = j + 1;
1141 if ((k - d) * SAROS_CYCLE < 2) {
1142 attr[9] = (double) saros_data_solar[i].series_no;
1143 attr[10] = (double) k + 1;
1144 break;
1145 }
1146 }
1147 if (i == NSAROS_SOLAR) {
1148 attr[9] = attr[10] = -99999999;
1149 }
1150 }
1151 return retc;
1152 }
1153
1154 /* When is the next solar eclipse anywhere on earth?
1155 *
1156 * input parameters:
1157 *
1158 * tjd_start start time for search (UT)
1159 * ifl ephemeris to be used (SEFLG_SWIEPH, etc.)
1160 * ifltype eclipse type to be searched (SE_ECL_TOTAL, etc.)
1161 * 0, if any type of eclipse is wanted
1162 *
1163 * return values:
1164 *
1165 * retflag SE_ECL_TOTAL or SE_ECL_ANNULAR or SE_ECL_PARTIAL
1166 * or SE_ECL_ANNULAR_TOTAL
1167 * SE_ECL_CENTRAL
1168 * SE_ECL_NONCENTRAL
1169 *
1170 * tret[0] time of maximum eclipse
1171 * tret[1] time, when eclipse takes place at local apparent noon
1172 * tret[2] time of eclipse begin
1173 * tret[3] time of eclipse end
1174 * tret[4] time of totality begin
1175 * tret[5] time of totality end
1176 * tret[6] time of center line begin
1177 * tret[7] time of center line end
1178 * tret[8] time when annular-total eclipse becomes total
1179 * not implemented so far
1180 * tret[9] time when annular-total eclipse becomes annular again
1181 * not implemented so far
1182 * declare as tret[10] at least!
1183 *
1184 */
swe_sol_eclipse_when_glob(double tjd_start,int32 ifl,int32 ifltype,double * tret,int32 backward,char * serr)1185 int32 CALL_CONV swe_sol_eclipse_when_glob(double tjd_start, int32 ifl, int32 ifltype,
1186 double *tret, int32 backward, char *serr)
1187 {
1188 int i, j, k, m, n, o, i1 = 0, i2 = 0;
1189 int32 retflag = 0, retflag2 = 0;
1190 double de = 6378.140, a;
1191 double t, tt, tjd, tjds, dt, dtint, dta, dtb;
1192 double T, T2, T3, T4, K, M, Mm;
1193 double E, Ff;
1194 double xs[6], xm[6], ls[6], lm[6];
1195 double rmoon, rsun, dcore[10];
1196 double dc[3], dctr;
1197 double twohr = 2.0 / 24.0;
1198 double tenmin = 10.0 / 24.0 / 60.0;
1199 double dt1 = 0, dt2 = 0;
1200 double geopos[20], attr[20];
1201 double dtstart, dtdiv;
1202 double xa[6], xb[6];
1203 int direction = 1;
1204 AS_BOOL dont_times = FALSE;
1205 int32 iflag, iflagcart;
1206 ifl &= SEFLG_EPHMASK;
1207 swi_set_tid_acc(tjd_start, ifl, 0, serr);
1208 iflag = SEFLG_EQUATORIAL | ifl;
1209 iflagcart = iflag | SEFLG_XYZ;
1210 if (ifltype == (SE_ECL_PARTIAL | SE_ECL_CENTRAL)) {
1211 if (serr != NULL)
1212 strcpy(serr, "central partial eclipses do not exist");
1213 return ERR;
1214 }
1215 if (ifltype == (SE_ECL_ANNULAR_TOTAL | SE_ECL_NONCENTRAL)) {
1216 if (serr != NULL)
1217 strcpy(serr, "non-central hybrid (annular-total) eclipses do not exist");
1218 return ERR;
1219 }
1220 if (ifltype == 0)
1221 ifltype = SE_ECL_TOTAL | SE_ECL_ANNULAR | SE_ECL_PARTIAL
1222 | SE_ECL_ANNULAR_TOTAL | SE_ECL_NONCENTRAL | SE_ECL_CENTRAL;
1223 if (ifltype == SE_ECL_TOTAL || ifltype == SE_ECL_ANNULAR || ifltype == SE_ECL_ANNULAR_TOTAL)
1224 ifltype |= (SE_ECL_NONCENTRAL | SE_ECL_CENTRAL);
1225 if (ifltype == SE_ECL_PARTIAL)
1226 ifltype |= SE_ECL_NONCENTRAL;
1227 if (backward)
1228 direction = -1;
1229 K = (int) ((tjd_start - J2000) / 365.2425 * 12.3685);
1230 K -= direction;
1231 next_try:
1232 retflag = 0;
1233 dont_times = FALSE;
1234 for (i = 0; i <= 9; i++)
1235 tret[i] = 0;
1236 T = K / 1236.85;
1237 T2 = T * T; T3 = T2 * T; T4 = T3 * T;
1238 Ff = swe_degnorm(160.7108 + 390.67050274 * K
1239 - 0.0016341 * T2
1240 - 0.00000227 * T3
1241 + 0.000000011 * T4);
1242 if (Ff > 180)
1243 Ff -= 180;
1244 if (Ff > 21 && Ff < 159) { /* no eclipse possible */
1245 K += direction;
1246 goto next_try;
1247 }
1248 /* approximate time of geocentric maximum eclipse
1249 * formula from Meeus, German, p. 381 */
1250 tjd = 2451550.09765 + 29.530588853 * K
1251 + 0.0001337 * T2
1252 - 0.000000150 * T3
1253 + 0.00000000073 * T4;
1254 M = swe_degnorm(2.5534 + 29.10535669 * K
1255 - 0.0000218 * T2
1256 - 0.00000011 * T3);
1257 Mm = swe_degnorm(201.5643 + 385.81693528 * K
1258 + 0.1017438 * T2
1259 + 0.00001239 * T3
1260 + 0.000000058 * T4);
1261 E = 1 - 0.002516 * T - 0.0000074 * T2;
1262 M *= DEGTORAD;
1263 Mm *= DEGTORAD;
1264 tjd = tjd - 0.4075 * sin(Mm)
1265 + 0.1721 * E * sin(M);
1266 /*
1267 * time of maximum eclipse (if eclipse) =
1268 * minimum geocentric angle between sun and moon edges.
1269 * After this time has been determined, check
1270 * whether or not an eclipse is taking place with
1271 * the functions eclipse_where() and _how().
1272 */
1273 dtstart = 1;
1274 if (tjd < 2000000 || tjd > 2500000)
1275 dtstart = 5;
1276 dtdiv = 4;
1277 for (dt = dtstart;
1278 dt > 0.0001;
1279 dt /= dtdiv) {
1280 for (i = 0, t = tjd - dt; i <= 2; i++, t += dt) {
1281 if (swe_calc(t, SE_SUN, iflag, ls, serr) == ERR)
1282 return ERR;
1283 if (swe_calc(t, SE_MOON, iflag, lm, serr) == ERR)
1284 return ERR;
1285 if (swe_calc(t, SE_SUN, iflagcart, xs, serr) == ERR)
1286 return ERR;
1287 if (swe_calc(t, SE_MOON, iflagcart, xm, serr) == ERR)
1288 return ERR;
1289 for (m = 0; m < 3; m++) {
1290 xa[m] = xs[m] / ls[2];
1291 xb[m] = xm[m] / lm[2];
1292 }
1293 dc[i] = acos(swi_dot_prod_unit(xa, xb)) * RADTODEG;
1294 rmoon = asin(RMOON / lm[2]) * RADTODEG;
1295 rsun = asin(RSUN / ls[2]) * RADTODEG;
1296 dc[i] -= (rmoon + rsun);
1297 }
1298 find_maximum(dc[0], dc[1], dc[2], dt, &dtint, &dctr);
1299 tjd += dtint + dt;
1300 }
1301 tjds = tjd - swe_deltat_ex(tjd, ifl, serr);
1302 tjds = tjd - swe_deltat_ex(tjds, ifl, serr);
1303 tjds = tjd = tjd - swe_deltat_ex(tjds, ifl, serr);
1304 if ((retflag = eclipse_where(tjd, SE_SUN, NULL, ifl, geopos, dcore, serr)) == ERR)
1305 return retflag;
1306 retflag2 = retflag;
1307 /* in extreme cases _where() returns no eclipse, where there is
1308 * actually a very small one, therefore call _how() with the
1309 * coordinates returned by _where(): */
1310 if ((retflag2 = eclipse_how(tjd, SE_SUN, NULL, ifl, geopos[0], geopos[1], 0, attr, serr)) == ERR)
1311 return retflag2;
1312 if (retflag2 == 0) {
1313 K += direction;
1314 goto next_try;
1315 }
1316 tret[0] = tjd;
1317 if ((backward && tret[0] >= tjd_start - 0.0001)
1318 || (!backward && tret[0] <= tjd_start + 0.0001)) {
1319 K += direction;
1320 goto next_try;
1321 }
1322 /*
1323 * eclipse type, SE_ECL_TOTAL, _ANNULAR, etc.
1324 * SE_ECL_ANNULAR_TOTAL will be discovered later
1325 */
1326 if ((retflag = eclipse_where(tjd, SE_SUN, NULL, ifl, geopos, dcore, serr)) == ERR)
1327 return retflag;
1328 if (retflag == 0) { /* can happen with extremely small percentage */
1329 retflag = SE_ECL_PARTIAL | SE_ECL_NONCENTRAL;
1330 tret[4] = tret[5] = tjd; /* fix this ???? */
1331 dont_times = TRUE;
1332 }
1333 /*
1334 * check whether or not eclipse type found is wanted
1335 */
1336 /* non central eclipse is wanted: */
1337 if (!(ifltype & SE_ECL_NONCENTRAL) && (retflag & SE_ECL_NONCENTRAL)) {
1338 K += direction;
1339 goto next_try;
1340 }
1341 /* central eclipse is wanted: */
1342 if (!(ifltype & SE_ECL_CENTRAL) && (retflag & SE_ECL_CENTRAL)) {
1343 K += direction;
1344 goto next_try;
1345 }
1346 /* non annular eclipse is wanted: */
1347 if (!(ifltype & SE_ECL_ANNULAR) && (retflag & SE_ECL_ANNULAR)) {
1348 K += direction;
1349 goto next_try;
1350 }
1351 /* non partial eclipse is wanted: */
1352 if (!(ifltype & SE_ECL_PARTIAL) && (retflag & SE_ECL_PARTIAL)) {
1353 K += direction;
1354 goto next_try;
1355 }
1356 /* annular-total eclipse will be discovered later */
1357 if (!(ifltype & (SE_ECL_TOTAL | SE_ECL_ANNULAR_TOTAL)) && (retflag & SE_ECL_TOTAL)) {
1358 K += direction;
1359 goto next_try;
1360 }
1361 if (dont_times)
1362 goto end_search_global;
1363 /*
1364 * n = 0: times of eclipse begin and end
1365 * n = 1: times of totality begin and end
1366 * n = 2: times of center line begin and end
1367 */
1368 if (retflag & SE_ECL_PARTIAL)
1369 o = 0;
1370 else if (retflag & SE_ECL_NONCENTRAL)
1371 o = 1;
1372 else
1373 o = 2;
1374 dta = twohr;
1375 dtb = tenmin / 3.0;
1376 for (n = 0; n <= o; n++) {
1377 if (n == 0) {
1378 /*dc[1] = dcore[3] / 2 + de - dcore[1];*/
1379 i1 = 2; i2 = 3;
1380 } else if (n == 1) {
1381 if (retflag & SE_ECL_PARTIAL)
1382 continue;
1383 i1 = 4; i2 = 5;
1384 } else if (n == 2) {
1385 if (retflag & SE_ECL_NONCENTRAL)
1386 continue;
1387 i1 = 6; i2 = 7;
1388 }
1389 for (i = 0, t = tjd - dta; i <= 2; i += 1, t += dta) {
1390 if ((retflag2 = eclipse_where(t, SE_SUN, NULL, ifl, geopos, dcore, serr)) == ERR)
1391 return retflag2;
1392 if (n == 0)
1393 dc[i] = dcore[4] / 2 + de / dcore[5] - dcore[2];
1394 else if (n == 1)
1395 dc[i] = fabs(dcore[3]) / 2 + de / dcore[6] - dcore[2];
1396 else if (n == 2)
1397 dc[i] = de / dcore[6] - dcore[2];
1398 }
1399 find_zero(dc[0], dc[1], dc[2], dta, &dt1, &dt2);
1400 tret[i1] = tjd + dt1 + dta;
1401 tret[i2] = tjd + dt2 + dta;
1402 for (m = 0, dt = dtb; m < 3; m++, dt /= 3) {
1403 for (j = i1; j <= i2; j += (i2 - i1)) {
1404 for (i = 0, t = tret[j] - dt; i < 2; i++, t += dt) {
1405 if ((retflag2 = eclipse_where(t, SE_SUN, NULL, ifl, geopos, dcore, serr)) == ERR)
1406 return retflag2;
1407 if (n == 0)
1408 dc[i] = dcore[4] / 2 + de / dcore[5] - dcore[2];
1409 else if (n == 1)
1410 dc[i] = fabs(dcore[3]) / 2 + de / dcore[6] - dcore[2];
1411 else if (n == 2)
1412 dc[i] = de / dcore[6] - dcore[2];
1413 }
1414 dt1 = dc[1] / ((dc[1] - dc[0]) / dt);
1415 tret[j] -= dt1;
1416 }
1417 }
1418 }
1419 /*
1420 * annular-total eclipses
1421 */
1422 if (retflag & SE_ECL_TOTAL) {
1423 if ((retflag2 = eclipse_where(tret[0], SE_SUN, NULL, ifl, geopos, dcore, serr)) == ERR)
1424 return retflag2;
1425 dc[0] = *dcore;
1426 if ((retflag2 = eclipse_where(tret[4], SE_SUN, NULL, ifl, geopos, dcore, serr)) == ERR)
1427 return retflag2;
1428 dc[1] = *dcore;
1429 if ((retflag2 = eclipse_where(tret[5], SE_SUN, NULL, ifl, geopos, dcore, serr)) == ERR)
1430 return retflag2;
1431 dc[2] = *dcore;
1432 /* the maximum is always total, and there is either one or
1433 * to times before and after, when the core shadow becomes
1434 * zero and totality changes into annularity or vice versa.
1435 */
1436 if (dc[0] * dc[1] < 0 || dc[0] * dc[2] < 0) {
1437 retflag |= SE_ECL_ANNULAR_TOTAL;
1438 retflag &= ~SE_ECL_TOTAL;
1439 }
1440 }
1441 /* if eclipse is given but not wanted: */
1442 if (!(ifltype & SE_ECL_TOTAL) && (retflag & SE_ECL_TOTAL)) {
1443 K += direction;
1444 goto next_try;
1445 }
1446 /* if annular_total eclipse is given but not wanted: */
1447 if (!(ifltype & SE_ECL_ANNULAR_TOTAL) && (retflag & SE_ECL_ANNULAR_TOTAL)) {
1448 K += direction;
1449 goto next_try;
1450 }
1451 /*
1452 * time of maximum eclipse at local apparent noon
1453 */
1454 /* first, find out, if there is a solar transit
1455 * between begin and end of eclipse */
1456 k = 2;
1457 for (i = 0; i < 2; i++) {
1458 j = i + k;
1459 tt = tret[j] + swe_deltat_ex(tret[j], ifl, serr);
1460 if (swe_calc(tt, SE_SUN, iflag, ls, serr) == ERR)
1461 return ERR;
1462 if (swe_calc(tt, SE_MOON, iflag, lm, serr) == ERR)
1463 return ERR;
1464 dc[i] = swe_degnorm(ls[0] - lm[0]);
1465 if (dc[i] > 180)
1466 dc[i] -= 360;
1467 }
1468 if (dc[0] * dc[1] >= 0) /* no transit */
1469 tret[1] = 0;
1470 else {
1471 tjd = tjds;
1472 dt = 0.1;
1473 dt1 = (tret[3] - tret[2]) / 2.0;
1474 if (dt1 < dt)
1475 dt = dt1 / 2.0;
1476 for (j = 0;
1477 dt > 0.01;
1478 j++, dt /= 3) {
1479 for (i = 0, t = tjd; i <= 1; i++, t -= dt) {
1480 tt = t + swe_deltat_ex(t, ifl, serr);
1481 if (swe_calc(tt, SE_SUN, iflag, ls, serr) == ERR)
1482 return ERR;
1483 if (swe_calc(tt, SE_MOON, iflag, lm, serr) == ERR)
1484 return ERR;
1485 dc[i] = swe_degnorm(ls[0] - lm[0]);
1486 if (dc[i] > 180)
1487 dc[i] -= 360;
1488 if (dc[i] > 180)
1489 dc[i] -= 360;
1490 }
1491 a = (dc[1] - dc[0]) / dt;
1492 if (a < 1e-10)
1493 break;
1494 dt1 = dc[0] / a;
1495 tjd += dt1;
1496 }
1497 tret[1] = tjd;
1498 }
1499 end_search_global:
1500 return retflag;
1501 /*
1502 * the time of maximum occultation is practically identical
1503 * with the time of maximum core shadow diameter.
1504 *
1505 * the time, when duration of totality is maximal,
1506 * is not an interesting computation either. Near the maximum
1507 * occulation, the time of totality can be the same by
1508 * a second for hundreds of kilometers (for 10 minutes
1509 * or more).
1510 *
1511 * for annular eclipses the maximum duration is close to the
1512 * beginning and the end of the center lines, where is also
1513 * the minimum of core shadow diameter.
1514 */
1515 }
1516
1517 /* When is the next lunar occultation anywhere on earth?
1518 * This function also finds solar eclipses, but is less efficient
1519 * than swe_sol_eclipse_when_glob().
1520 *
1521 * input parameters:
1522 *
1523 * tjd_start start time for search (UT)
1524 * ipl planet number of occulted body
1525 * starname name of occulted star. Must be NULL or "", if a planetary
1526 * occultation is to be calculated. For the use of this
1527 * field, also see swe_fixstar().
1528 * ifl ephemeris to be used (SEFLG_SWIEPH, etc.)
1529 * ephemeris flag.
1530 *
1531 * ifltype eclipse type to be searched (SE_ECL_TOTAL, etc.)
1532 * 0, if any type of eclipse is wanted
1533 * this functionality also works with occultations
1534 *
1535 * backward if 1, causes search backward in time
1536 *
1537 * If you want to have only one conjunction
1538 * of the moon with the body tested, add the following flag:
1539 * backward |= SE_ECL_ONE_TRY. If this flag is not set,
1540 * the function will search for an occultation until it
1541 * finds one. For bodies with ecliptical latitudes > 5,
1542 * the function may search successlessly until it reaches
1543 * the end of the ephemeris.
1544 * (Note: we do not add SE_ECL_ONE_TRY to ifl, because
1545 * ifl may contain SEFLG_TOPOCTR (=SE_ECL_ONE_TRY) from
1546 * the parameter iflag of swe_calc() etc. Although the
1547 * topocentric flag is irrelevant here, it might cause
1548 * confusion.)
1549 *
1550 * return values:
1551 *
1552 * retflag SE_ECL_TOTAL or SE_ECL_ANNULAR or SE_ECL_PARTIAL
1553 * or SE_ECL_ANNULAR_TOTAL
1554 * SE_ECL_CENTRAL
1555 * SE_ECL_NONCENTRAL
1556 *
1557 * tret[0] time of maximum eclipse
1558 * tret[1] time, when eclipse takes place at local apparent noon
1559 * tret[2] time of eclipse begin
1560 * tret[3] time of eclipse end
1561 * tret[4] time of totality begin
1562 * tret[5] time of totality end
1563 * tret[6] time of center line begin
1564 * tret[7] time of center line end
1565 * tret[8] time when annular-total eclipse becomes total
1566 * not implemented so far
1567 * tret[9] time when annular-total eclipse becomes annular again
1568 * not implemented so far
1569 * declare as tret[10] at least!
1570 *
1571 */
swe_lun_occult_when_glob(double tjd_start,int32 ipl,char * starname,int32 ifl,int32 ifltype,double * tret,int32 backward,char * serr)1572 int32 CALL_CONV swe_lun_occult_when_glob(
1573 double tjd_start, int32 ipl, char *starname, int32 ifl, int32 ifltype,
1574 double *tret, int32 backward, char *serr)
1575 {
1576 int i, j, k, m, n, o, i1, i2;
1577 int32 retflag = 0, retflag2 = 0;
1578 double de = 6378.140, a;
1579 double t, tt, tjd = 0, tjds, dt, dtint, dta, dtb;
1580 double drad, dl;
1581 double xs[6], xm[6], ls[6], lm[6];
1582 double rmoon, rsun, dcore[10];
1583 double dc[20], dctr;
1584 double twohr = 2.0 / 24.0;
1585 double tenmin = 10.0 / 24.0 / 60.0;
1586 double dt1 = 0, dt2 = 0, dadd2 = 1;
1587 double geopos[20];
1588 double dtstart, dtdiv;
1589 int direction = 1;
1590 int32 ifltype2;
1591 int32 iflag, iflagcart;
1592 AS_BOOL dont_times = FALSE;
1593 int32 one_try = backward & SE_ECL_ONE_TRY;
1594 if (ipl < 0) ipl = 0;
1595 /*if (backward & SEI_OCC_FAST)
1596 dont_times = TRUE; */
1597 /* function calls for Pluto with asteroid number 134340
1598 * are treated as calls for Pluto as main body SE_PLUTO */
1599 if (ipl == SE_AST_OFFSET + 134340)
1600 ipl = SE_PLUTO;
1601 ifl &= SEFLG_EPHMASK;
1602 swi_set_tid_acc(tjd_start, ifl, 0, serr);
1603 iflag = SEFLG_EQUATORIAL | ifl;
1604 iflagcart = iflag | SEFLG_XYZ;
1605 backward &= 1L;
1606 /*
1607 * initializations
1608 */
1609 if (ifltype == (SE_ECL_PARTIAL | SE_ECL_CENTRAL)) {
1610 if (serr != NULL)
1611 strcpy(serr, "central partial eclipses do not exist");
1612 return ERR;
1613 }
1614 if (ipl != SE_SUN) {
1615 ifltype2 = (ifltype & ~(SE_ECL_NONCENTRAL | SE_ECL_CENTRAL));
1616 if (ifltype2 == SE_ECL_ANNULAR || ifltype == SE_ECL_ANNULAR_TOTAL) {
1617 if (serr != NULL)
1618 sprintf(serr, "annular occulation do not exist for object %d %s\n", ipl, starname);
1619 return ERR;
1620 }
1621 }
1622 if (ipl != SE_SUN && (ifltype & (SE_ECL_ANNULAR | SE_ECL_ANNULAR_TOTAL)))
1623 ifltype &= ~(SE_ECL_ANNULAR|SE_ECL_ANNULAR_TOTAL);
1624 if (ifltype == 0) {
1625 ifltype = SE_ECL_TOTAL | SE_ECL_PARTIAL | SE_ECL_NONCENTRAL | SE_ECL_CENTRAL;
1626 if (ipl == SE_SUN)
1627 ifltype |= (SE_ECL_ANNULAR | SE_ECL_ANNULAR_TOTAL);
1628 }
1629 if (ifltype & (SE_ECL_TOTAL | SE_ECL_ANNULAR | SE_ECL_ANNULAR_TOTAL))
1630 ifltype |= (SE_ECL_NONCENTRAL | SE_ECL_CENTRAL);
1631 if (ifltype & SE_ECL_PARTIAL)
1632 ifltype |= SE_ECL_NONCENTRAL;
1633 retflag = 0;
1634 for (i = 0; i <= 9; i++)
1635 tret[i] = 0;
1636 if (backward)
1637 direction = -1;
1638 t = tjd_start;
1639 tjd = t;
1640 next_try:
1641 if (calc_planet_star(t, ipl, starname, ifl, ls, serr) == ERR)
1642 return ERR;
1643 /* fixed stars with an ecliptic latitude > 7 or < -7 cannot have
1644 * an occultation. Even lunar parallax andd proper motion of star
1645 * will never allow it. */
1646 if (fabs(ls[1]) > 7 && starname != NULL && *starname != '\0') {
1647 if (serr != NULL)
1648 sprintf(serr, "occultation never occurs: star %s has ecl. lat. %.1f", starname, ls[1]);
1649 return ERR;
1650 }
1651 if (swe_calc(t, SE_MOON, ifl, lm, serr) == ERR)
1652 return ERR;
1653 dl = swe_degnorm(ls[0] - lm[0]);
1654 if (direction < 0)
1655 dl -= 360;
1656 /* get rough conjunction in ecliptic longitude */
1657 while (fabs(dl) > 0.1) {
1658 t += dl / 13;
1659 if (calc_planet_star(t, ipl, starname, ifl, ls, serr) == ERR)
1660 return ERR;
1661 if (swe_calc(t, SE_MOON, ifl, lm, serr) == ERR)
1662 return ERR;
1663 dl = swe_degnorm(ls[0] - lm[0]);
1664 if (dl > 180) dl -= 360;
1665 }
1666 tjd = t;
1667 /* difference in latitude too big for an occultation */
1668 drad = fabs(ls[1] - lm[1]);
1669 if (drad > 2) {
1670 if (one_try) {
1671 tret[0] = t + direction; /* return a date suitable for next try */
1672 return 0;
1673 }
1674 t += direction * 20;
1675 tjd = t;
1676 goto next_try;
1677 }
1678 /*
1679 * radius of planet disk in AU
1680 */
1681 if (starname != NULL && *starname != '\0')
1682 drad = 0;
1683 else if (ipl < NDIAM)
1684 drad = pla_diam[ipl] / 2 / AUNIT;
1685 else if (ipl > SE_AST_OFFSET)
1686 drad = swed.ast_diam / 2 * 1000 / AUNIT; /* km -> m -> AU */
1687 else
1688 drad = 0;
1689 /*
1690 * time of maximum eclipse (if eclipse) =
1691 * minimum geocentric angle between sun and moon edges.
1692 * After this time has been determined, check
1693 * whether or not an eclipse is taking place with
1694 * the functions eclipse_where() and _how().
1695 */
1696 dtstart = dadd2; /* originally 1 */
1697 dtdiv = 3;
1698 for (dt = dtstart;
1699 dt > 0.0001;
1700 dt /= dtdiv) {
1701 for (i = 0, t = tjd - dt; i <= 2; i++, t += dt) {
1702 if (calc_planet_star(t, ipl, starname, iflag, ls, serr) == ERR)
1703 return ERR;
1704 if (swe_calc(t, SE_MOON, iflag, lm, serr) == ERR)
1705 return ERR;
1706 if (calc_planet_star(t, ipl, starname, iflagcart, xs, serr) == ERR)
1707 return ERR;
1708 if (swe_calc(t, SE_MOON, iflagcart, xm, serr) == ERR)
1709 return ERR;
1710 dc[i] = acos(swi_dot_prod_unit(xs, xm)) * RADTODEG;
1711 rmoon = asin(RMOON / lm[2]) * RADTODEG;
1712 rsun = asin(drad / ls[2]) * RADTODEG;
1713 dc[i] -= (rmoon + rsun);
1714 }
1715 find_maximum(dc[0], dc[1], dc[2], dt, &dtint, &dctr);
1716 tjd += dtint + dt;
1717 }
1718 tjd -= swe_deltat_ex(tjd, ifl, serr);
1719 tjds = tjd;
1720 if ((retflag = eclipse_where(tjd, ipl, starname, ifl, geopos, dcore, serr)) == ERR)
1721 return retflag;
1722 retflag2 = retflag;
1723 /* in extreme cases _where() returns no eclipse, where there is
1724 * actually a very small one, therefore call _how() with the
1725 * coordinates returned by _where(): */
1726 /* if ((retflag2 = eclipse_how(tjd, ipl, starname, ifl, geopos[0], geopos[1], 0, attr, serr)) == ERR)
1727 return retflag2; */
1728 if (retflag2 == 0) {
1729 /* only one try! */
1730 /* if (one_try && ((direction == 1 && tjd > tjd_start) || (direction == -1 && tjd < tjd_start))) {*/
1731 if (one_try) {
1732 tret[0] = tjd;
1733 return 0;
1734 }
1735 /*t= tjd + direction * dadd;*/
1736 t = tjd + direction * 20;
1737 tjd = t;
1738 goto next_try;
1739 }
1740 tret[0] = tjd;
1741 /* should not happen anymore Version 2.01 */
1742 if ((backward && tret[0] >= tjd_start - 0.0001)
1743 || (!backward && tret[0] <= tjd_start + 0.0001)) {
1744 /*t= tjd + direction * dadd;*/
1745 t = tjd + direction * 20;
1746 tjd = t;
1747 goto next_try;
1748 }
1749 /*
1750 * eclipse type, SE_ECL_TOTAL, _ANNULAR, etc.
1751 * SE_ECL_ANNULAR_TOTAL will be discovered later
1752 */
1753 if ((retflag = eclipse_where(tjd, ipl, starname, ifl, geopos, dcore, serr)) == ERR)
1754 return retflag;
1755 if (retflag == 0) { /* can happen with extremely small percentage */
1756 retflag = SE_ECL_PARTIAL | SE_ECL_NONCENTRAL;
1757 tret[4] = tret[5] = tjd; /* fix this ???? */
1758 dont_times = TRUE;
1759 }
1760 /*
1761 * check whether or not eclipse type found is wanted
1762 */
1763 /* non central eclipse is wanted: */
1764 if (!(ifltype & SE_ECL_NONCENTRAL) && (retflag & SE_ECL_NONCENTRAL)) {
1765 /*t= tjd + direction * dadd;*/
1766 t = tjd + direction * 20;
1767 if (one_try) {
1768 tret[0] = tjd;
1769 return 0;
1770 }
1771 tjd = t;
1772 goto next_try;
1773 }
1774 /* central eclipse is wanted: */
1775 if (!(ifltype & SE_ECL_CENTRAL) && (retflag & SE_ECL_CENTRAL)) {
1776 /*t= tjd + direction * dadd;*/
1777 t = tjd + direction * 20;
1778 if (one_try) {
1779 tret[0] = tjd;
1780 return 0;
1781 }
1782 tjd = t;
1783 goto next_try;
1784 }
1785 /* non annular eclipse is wanted: */
1786 if (!(ifltype & SE_ECL_ANNULAR) && (retflag & SE_ECL_ANNULAR)) {
1787 /*t= tjd + direction * dadd;*/
1788 t = tjd + direction * 20;
1789 if (one_try) {
1790 tret[0] = tjd;
1791 return 0;
1792 }
1793 tjd = t;
1794 goto next_try;
1795 }
1796 /* non partial eclipse is wanted: */
1797 if (!(ifltype & SE_ECL_PARTIAL) && (retflag & SE_ECL_PARTIAL)) {
1798 /*t= tjd + direction * dadd;*/
1799 t = tjd + direction * 20;
1800 if (one_try) {
1801 tret[0] = tjd;
1802 return 0;
1803 }
1804 tjd = t;
1805 goto next_try;
1806 }
1807 /* annular-total eclipse will be discovered later */
1808 if (!(ifltype & (SE_ECL_TOTAL | SE_ECL_ANNULAR_TOTAL)) && (retflag & SE_ECL_TOTAL)) {
1809 /*t= tjd + direction * dadd;*/
1810 t = tjd + direction * 20;
1811 if (one_try) {
1812 tret[0] = tjd;
1813 return 0;
1814 }
1815 tjd = t;
1816 goto next_try;
1817 }
1818 if (dont_times)
1819 goto end_search_global;
1820 /*
1821 * n = 0: times of eclipse begin and end
1822 * n = 1: times of totality begin and end
1823 * n = 2: times of center line begin and end
1824 */
1825 if (retflag & SE_ECL_PARTIAL)
1826 o = 0;
1827 else if (retflag & SE_ECL_NONCENTRAL)
1828 o = 1;
1829 else
1830 o = 2;
1831 dta = twohr;
1832 dtb = tenmin;
1833 for (n = 0; n <= o; n++) {
1834 if (n == 0) {
1835 /*dc[1] = dcore[3] / 2 + de - dcore[1];*/
1836 i1 = 2; i2 = 3;
1837 } else if (n == 1) {
1838 if (retflag & SE_ECL_PARTIAL)
1839 continue;
1840 i1 = 4; i2 = 5;
1841 } else if (n == 2) {
1842 if (retflag & SE_ECL_NONCENTRAL)
1843 continue;
1844 i1 = 6; i2 = 7;
1845 }
1846 for (i = 0, t = tjd - dta; i <= 2; i += 1, t += dta) {
1847 if ((retflag2 = eclipse_where(t, ipl, starname, ifl, geopos, dcore, serr)) == ERR)
1848 return retflag2;
1849 if (n == 0)
1850 dc[i] = dcore[4] / 2 + de / dcore[5] - dcore[2];
1851 else if (n == 1)
1852 dc[i] = fabs(dcore[3]) / 2 + de / dcore[6] - dcore[2];
1853 else if (n == 2)
1854 dc[i] = de / dcore[6] - dcore[2];
1855 }
1856 find_zero(dc[0], dc[1], dc[2], dta, &dt1, &dt2);
1857 tret[i1] = tjd + dt1 + dta;
1858 tret[i2] = tjd + dt2 + dta;
1859 for (m = 0, dt = dtb; m < 3; m++, dt /= 3) {
1860 for (j = i1; j <= i2; j += (i2 - i1)) {
1861 for (i = 0, t = tret[j] - dt; i < 2; i++, t += dt) {
1862 if ((retflag2 = eclipse_where(t, ipl, starname, ifl, geopos, dcore, serr)) == ERR)
1863 return retflag2;
1864 if (n == 0)
1865 dc[i] = dcore[4] / 2 + de / dcore[5] - dcore[2];
1866 else if (n == 1)
1867 dc[i] = fabs(dcore[3]) / 2 + de / dcore[6] - dcore[2];
1868 else if (n == 2)
1869 dc[i] = de / dcore[6] - dcore[2];
1870 }
1871 dt1 = dc[1] / ((dc[1] - dc[0]) / dt);
1872 tret[j] -= dt1;
1873 }
1874 }
1875 }
1876 /*
1877 * annular-total eclipses
1878 */
1879 if (retflag & SE_ECL_TOTAL) {
1880 if ((retflag2 = eclipse_where(tret[0], ipl, starname, ifl, geopos, dcore, serr)) == ERR)
1881 return retflag2;
1882 dc[0] = *dcore;
1883 if ((retflag2 = eclipse_where(tret[4], ipl, starname, ifl, geopos, dcore, serr)) == ERR)
1884 return retflag2;
1885 dc[1] = *dcore;
1886 if ((retflag2 = eclipse_where(tret[5], ipl, starname, ifl, geopos, dcore, serr)) == ERR)
1887 return retflag2;
1888 dc[2] = *dcore;
1889 /* the maximum is always total, and there is either one or
1890 * to times before and after, when the core shadow becomes
1891 * zero and totality changes into annularity or vice versa.
1892 */
1893 if (dc[0] * dc[1] < 0 || dc[0] * dc[2] < 0) {
1894 retflag |= SE_ECL_ANNULAR_TOTAL;
1895 retflag &= ~SE_ECL_TOTAL;
1896 }
1897 }
1898 /* if eclipse is given but not wanted: */
1899 if (!(ifltype & SE_ECL_TOTAL) && (retflag & SE_ECL_TOTAL)) {
1900 /*t= tjd + direction * dadd;*/
1901 t = tjd + direction * 20;
1902 if (one_try) {
1903 tret[0] = tjd;
1904 return 0;
1905 }
1906 tjd = t;
1907 goto next_try;
1908 }
1909 /* if annular_total eclipse is given but not wanted: */
1910 if (!(ifltype & SE_ECL_ANNULAR_TOTAL) && (retflag & SE_ECL_ANNULAR_TOTAL)) {
1911 /*t= tjd + direction * dadd;*/
1912 t = tjd + direction * 20;
1913 if (one_try) {
1914 tret[0] = tjd;
1915 return 0;
1916 }
1917 tjd = t;
1918 goto next_try;
1919 }
1920 /*
1921 * time of maximum eclipse at local apparent noon
1922 */
1923 /* first, find out, if there is a solar transit
1924 * between begin and end of eclipse */
1925 k = 2;
1926 for (i = 0; i < 2; i++) {
1927 j = i + k;
1928 tt = tret[j] + swe_deltat_ex(tret[j], ifl, serr);
1929 if (calc_planet_star(tt, ipl, starname, iflag, ls, serr) == ERR)
1930 return ERR;
1931 if (swe_calc(tt, SE_MOON, iflag, lm, serr) == ERR)
1932 return ERR;
1933 dc[i] = swe_degnorm(ls[0] - lm[0]);
1934 if (dc[i] > 180)
1935 dc[i] -= 360;
1936 }
1937 if (dc[0] * dc[1] >= 0) /* no transit */
1938 tret[1] = 0;
1939 else {
1940 tjd = tjds;
1941 dt = 0.1;
1942 dt1 = (tret[3] - tret[2]) / 2.0;
1943 if (dt1 < dt)
1944 dt = dt1 / 2.0;
1945 for (j = 0;
1946 dt > 0.01;
1947 j++, dt /= 3) {
1948 for (i = 0, t = tjd; i <= 1; i++, t -= dt) {
1949 tt = t + swe_deltat_ex(t, ifl, serr);
1950 if (calc_planet_star(tt, ipl, starname, iflag, ls, serr) == ERR)
1951 return ERR;
1952 if (swe_calc(tt, SE_MOON, iflag, lm, serr) == ERR)
1953 return ERR;
1954 dc[i] = swe_degnorm(ls[0] - lm[0]);
1955 if (dc[i] > 180)
1956 dc[i] -= 360;
1957 if (dc[i] > 180)
1958 dc[i] -= 360;
1959 }
1960 a = (dc[1] - dc[0]) / dt;
1961 if (a < 1e-10)
1962 break;
1963 dt1 = dc[0] / a;
1964 tjd += dt1;
1965 }
1966 tret[1] = tjd;
1967 }
1968 end_search_global:
1969 return retflag;
1970 /*
1971 * the time of maximum occultation is practically identical
1972 * with the time of maximum core shadow diameter.
1973 *
1974 * the time, when duration of totality is maximal,
1975 * is not an interesting computation either. Near the maximum
1976 * occulation, the time of totality can be the same by
1977 * a second for hundreds of kilometers (for 10 minutes
1978 * or more).
1979 *
1980 * for annular eclipses the maximum duration is close to the
1981 * beginning and the end of the center lines, where is also
1982 * the minimum of core shadow diameter.
1983 */
1984 }
1985
1986 /* When is the next solar eclipse at a given geographical position?
1987 * Note the uncertainty of Delta T for the remote past and for
1988 * the future.
1989 *
1990 * retflag SE_ECL_TOTAL or SE_ECL_ANNULAR or SE_ECL_PARTIAL
1991 * SE_ECL_VISIBLE,
1992 * SE_ECL_MAX_VISIBLE,
1993 * SE_ECL_1ST_VISIBLE, SE_ECL_2ND_VISIBLE
1994 * SE_ECL_3ST_VISIBLE, SE_ECL_4ND_VISIBLE
1995 *
1996 * tret[0] time of maximum eclipse
1997 * tret[1] time of first contact
1998 * tret[2] time of second contact
1999 * tret[3] time of third contact
2000 * tret[4] time of forth contact
2001 * tret[5] time of sun rise between first and forth contact
2002 * tret[6] time of sun set beween first and forth contact
2003 *
2004 * attr[0] fraction of solar diameter covered by moon;
2005 * with total/annular eclipses, it results in magnitude acc. to IMCCE.
2006 * attr[1] ratio of lunar diameter to solar one
2007 * attr[2] fraction of solar disc covered by moon (obscuration)
2008 * attr[3] diameter of core shadow in km
2009 * attr[4] azimuth of sun at tjd
2010 * attr[5] true altitude of sun above horizon at tjd
2011 * attr[6] apparent altitude of sun above horizon at tjd
2012 * attr[7] elongation of moon in degrees
2013 * attr[8] magnitude acc. to NASA;
2014 * = attr[0] for partial and attr[1] for annular and total eclipses
2015 * attr[9] saros series number
2016 * attr[10] saros series member number
2017 * declare as attr[20] at least !
2018 */
swe_sol_eclipse_when_loc(double tjd_start,int32 ifl,double * geopos,double * tret,double * attr,int32 backward,char * serr)2019 int32 CALL_CONV swe_sol_eclipse_when_loc(double tjd_start, int32 ifl,
2020 double *geopos, double *tret, double *attr, int32 backward, char *serr)
2021 {
2022 int32 retflag = 0, retflag2 = 0;
2023 double geopos2[20], dcore[10];
2024 if (geopos[2] < SEI_ECL_GEOALT_MIN || geopos[2] > SEI_ECL_GEOALT_MAX) {
2025 if (serr != NULL)
2026 sprintf(serr, "location for eclipses must be between %.0f and %.0f m above sea", SEI_ECL_GEOALT_MIN, SEI_ECL_GEOALT_MAX);
2027 return ERR;
2028 }
2029 ifl &= SEFLG_EPHMASK;
2030 swi_set_tid_acc(tjd_start, ifl, 0, serr);
2031 if ((retflag = eclipse_when_loc(tjd_start, ifl, geopos, tret, attr, backward, serr)) <= 0)
2032 return retflag;
2033 /*
2034 * diameter of core shadow
2035 */
2036 if ((retflag2 = eclipse_where(tret[0], SE_SUN, NULL, ifl, geopos2, dcore, serr)) == ERR)
2037 return retflag2;
2038 retflag |= (retflag2 & SE_ECL_NONCENTRAL);
2039 attr[3] = dcore[0];
2040 return retflag;
2041 }
2042
2043 /* When is the next solar eclipse at a given geographical position?
2044 * Note the uncertainty of Delta T for the remote past and for
2045 * the future.
2046 *
2047 * retflag SE_ECL_TOTAL or SE_ECL_ANNULAR or SE_ECL_PARTIAL
2048 * SE_ECL_VISIBLE,
2049 * SE_ECL_MAX_VISIBLE,
2050 * SE_ECL_1ST_VISIBLE, SE_ECL_2ND_VISIBLE
2051 * SE_ECL_3ST_VISIBLE, SE_ECL_4ND_VISIBLE
2052 * SE_ECL_OCC_BEG_DAYLIGHT, SE_ECL_OCC_END_DAYLIGHT
2053 * The latter two indicate that the beginning or end of the occultation takes
2054 * place during the day. If Venus is occulted, it may be observable with the
2055 * naked eye; if other objects, it may be observable with telescopes.
2056 *
2057 * int32 ipl planet number of occulted body
2058 * char* starname name of occulted star. Must be NULL or "", if a planetary
2059 * occultation is to be calculated. For the use of this
2060 * field, also see swe_fixstar().
2061 * int32 ifl ephemeris flag. If you want to have only one conjunction
2062 * of the moon with the body tested, add the following flag:
2063 * backward |= SE_ECL_ONE_TRY. If this flag is not set,
2064 * the function will search for an occultation until it
2065 * finds one. For bodies with ecliptical latitudes > 5,
2066 * the function may search unsuccessfully until it reaches
2067 * the end of the ephemeris.
2068 *
2069 * for all other parameters, see function swe_sol_eclipse_when_loc().
2070 */
swe_lun_occult_when_loc(double tjd_start,int32 ipl,char * starname,int32 ifl,double * geopos,double * tret,double * attr,int32 backward,char * serr)2071 int32 CALL_CONV swe_lun_occult_when_loc(double tjd_start, int32 ipl, char *starname, int32 ifl,
2072 double *geopos, double *tret, double *attr, int32 backward, char *serr)
2073 {
2074 int32 retflag = 0, retflag2 = 0;
2075 double geopos2[20], dcore[10];
2076 /* function calls for Pluto with asteroid number 134340
2077 * are treated as calls for Pluto as main body SE_PLUTO */
2078 if (geopos[2] < SEI_ECL_GEOALT_MIN || geopos[2] > SEI_ECL_GEOALT_MAX) {
2079 if (serr != NULL)
2080 sprintf(serr, "location for occultations must be between %.0f and %.0f m above sea", SEI_ECL_GEOALT_MIN, SEI_ECL_GEOALT_MAX);
2081 return ERR;
2082 }
2083 if (ipl < 0) ipl = 0;
2084 if (ipl == SE_AST_OFFSET + 134340)
2085 ipl = SE_PLUTO;
2086 ifl &= SEFLG_EPHMASK;
2087 swi_set_tid_acc(tjd_start, ifl, 0, serr);
2088 if ((retflag = occult_when_loc(tjd_start, ipl, starname, ifl, geopos, tret, attr, backward, serr)) <= 0)
2089 return retflag;
2090 /*
2091 * diameter of core shadow
2092 */
2093 if ((retflag2 = eclipse_where(tret[0], ipl, starname, ifl, geopos2, dcore, serr)) == ERR)
2094 return retflag2;
2095 retflag |= (retflag2 & SE_ECL_NONCENTRAL);
2096 attr[3] = dcore[0];
2097 return retflag;
2098 }
2099
eclipse_when_loc(double tjd_start,int32 ifl,double * geopos,double * tret,double * attr,int32 backward,char * serr)2100 static int32 eclipse_when_loc(double tjd_start, int32 ifl, double *geopos, double *tret, double *attr, int32 backward, char *serr)
2101 {
2102 int i, j, k, m;
2103 int32 retflag = 0, retc;
2104 double t, tjd, dt, dtint, K, T, T2, T3, T4, F, M, Mm;
2105 double tjdr, tjds;
2106 double E, Ff, A1, Om;
2107 double xs[6], xm[6], ls[6], lm[6], x1[6], x2[6], dm, ds;
2108 double rmoon, rsun, rsplusrm, rsminusrm;
2109 double dc[3], dctr, dctrmin;
2110 double twomin = 2.0 / 24.0 / 60.0;
2111 double tensec = 10.0 / 24.0 / 60.0 / 60.0;
2112 double twohr = 2.0 / 24.0;
2113 double tenmin = 10.0 / 24.0 / 60.0;
2114 double dt1 = 0, dt2 = 0, dtdiv, dtstart;
2115 int32 iflag = SEFLG_EQUATORIAL | SEFLG_TOPOCTR | ifl;
2116 int32 iflagcart = iflag | SEFLG_XYZ;
2117 swe_set_topo(geopos[0], geopos[1], geopos[2]);
2118 K = (int) ((tjd_start - J2000) / 365.2425 * 12.3685);
2119 if (backward)
2120 K++;
2121 else
2122 K--;
2123 next_try:
2124 T = K / 1236.85;
2125 T2 = T * T; T3 = T2 * T; T4 = T3 * T;
2126 Ff = F = swe_degnorm(160.7108 + 390.67050274 * K
2127 - 0.0016341 * T2
2128 - 0.00000227 * T3
2129 + 0.000000011 * T4);
2130 if (Ff > 180)
2131 Ff -= 180;
2132 if (Ff > 21 && Ff < 159) { /* no eclipse possible */
2133 if (backward)
2134 K--;
2135 else
2136 K++;
2137 goto next_try;
2138 }
2139 /* approximate time of geocentric maximum eclipse.
2140 * formula from Meeus, German, p. 381 */
2141 tjd = 2451550.09765 + 29.530588853 * K
2142 + 0.0001337 * T2
2143 - 0.000000150 * T3
2144 + 0.00000000073 * T4;
2145 M = swe_degnorm(2.5534 + 29.10535669 * K
2146 - 0.0000218 * T2
2147 - 0.00000011 * T3);
2148 Mm = swe_degnorm(201.5643 + 385.81693528 * K
2149 + 0.1017438 * T2
2150 + 0.00001239 * T3
2151 + 0.000000058 * T4);
2152 Om = swe_degnorm(124.7746 - 1.56375580 * K
2153 + 0.0020691 * T2
2154 + 0.00000215 * T3);
2155 E = 1 - 0.002516 * T - 0.0000074 * T2;
2156 A1 = swe_degnorm(299.77 + 0.107408 * K - 0.009173 * T2);
2157 M *= DEGTORAD;
2158 Mm *= DEGTORAD;
2159 F *= DEGTORAD;
2160 Om *= DEGTORAD;
2161 A1 *= DEGTORAD;
2162 tjd = tjd - 0.4075 * sin(Mm)
2163 + 0.1721 * E * sin(M);
2164 swe_set_topo(geopos[0], geopos[1], geopos[2]);
2165 dtdiv = 2;
2166 dtstart = 0.5;
2167 if (tjd < 1900000 || tjd > 2500000) /* because above formula is not good (delta t?) */
2168 dtstart = 2;
2169 for (dt = dtstart;
2170 dt > 0.00001;
2171 dt /= dtdiv) {
2172 if (dt < 0.1)
2173 dtdiv = 3;
2174 for (i = 0, t = tjd - dt; i <= 2; i++, t += dt) {
2175 /* this takes some time, but is necessary to avoid
2176 * missing an eclipse */
2177 if (swe_calc(t, SE_SUN, iflagcart, xs, serr) == ERR)
2178 return ERR;
2179 if (swe_calc(t, SE_SUN, iflag, ls, serr) == ERR)
2180 return ERR;
2181 if (swe_calc(t, SE_MOON, iflagcart, xm, serr) == ERR)
2182 return ERR;
2183 if (swe_calc(t, SE_MOON, iflag, lm, serr) == ERR)
2184 return ERR;
2185 dm = sqrt(square_sum(xm));
2186 ds = sqrt(square_sum(xs));
2187 for (k = 0; k < 3; k++) {
2188 x1[k] = xs[k] / ds /*ls[2]*/;
2189 x2[k] = xm[k] / dm /*lm[2]*/;
2190 }
2191 dc[i] = acos(swi_dot_prod_unit(x1, x2)) * RADTODEG;
2192 }
2193 find_maximum(dc[0], dc[1], dc[2], dt, &dtint, &dctr);
2194 tjd += dtint + dt;
2195 }
2196 if (swe_calc(tjd, SE_SUN, iflagcart, xs, serr) == ERR)
2197 return ERR;
2198 if (swe_calc(tjd, SE_SUN, iflag, ls, serr) == ERR)
2199 return ERR;
2200 if (swe_calc(tjd, SE_MOON, iflagcart, xm, serr) == ERR)
2201 return ERR;
2202 if (swe_calc(tjd, SE_MOON, iflag, lm, serr) == ERR)
2203 return ERR;
2204 dctr = acos(swi_dot_prod_unit(xs, xm)) * RADTODEG;
2205 rmoon = asin(RMOON / lm[2]) * RADTODEG;
2206 rsun = asin(RSUN / ls[2]) * RADTODEG;
2207 rsplusrm = rsun + rmoon;
2208 rsminusrm = rsun - rmoon;
2209 if (dctr > rsplusrm) {
2210 if (backward)
2211 K--;
2212 else
2213 K++;
2214 goto next_try;
2215 }
2216 tret[0] = tjd - swe_deltat_ex(tjd, ifl, serr);
2217 tret[0] = tjd - swe_deltat_ex(tret[0], ifl, serr); /* these two lines are an iteration! */
2218 if ((backward && tret[0] >= tjd_start - 0.0001)
2219 || (!backward && tret[0] <= tjd_start + 0.0001)) {
2220 if (backward)
2221 K--;
2222 else
2223 K++;
2224 goto next_try;
2225 }
2226 if (dctr < rsminusrm)
2227 retflag = SE_ECL_ANNULAR;
2228 else if (dctr < fabs(rsminusrm))
2229 retflag = SE_ECL_TOTAL;
2230 else if (dctr <= rsplusrm)
2231 retflag = SE_ECL_PARTIAL;
2232 dctrmin = dctr;
2233 /* contacts 2 and 3 */
2234 if (dctr > fabs(rsminusrm)) /* partial, no 2nd and 3rd contact */
2235 tret[2] = tret[3] = 0;
2236 else {
2237 dc[1] = fabs(rsminusrm) - dctrmin;
2238 for (i = 0, t = tjd - twomin; i <= 2; i += 2, t = tjd + twomin) {
2239 if (swe_calc(t, SE_SUN, iflagcart, xs, serr) == ERR)
2240 return ERR;
2241 if (swe_calc(t, SE_MOON, iflagcart, xm, serr) == ERR)
2242 return ERR;
2243 dm = sqrt(square_sum(xm));
2244 ds = sqrt(square_sum(xs));
2245 rmoon = asin(RMOON / dm) * RADTODEG;
2246 rmoon *= 0.99916; /* gives better accuracy for 2nd/3rd contacts */
2247 rsun = asin(RSUN / ds) * RADTODEG;
2248 rsminusrm = rsun - rmoon;
2249 for (k = 0; k < 3; k++) {
2250 x1[k] = xs[k] / ds /*ls[2]*/;
2251 x2[k] = xm[k] / dm /*lm[2]*/;
2252 }
2253 dctr = acos(swi_dot_prod_unit(x1, x2)) * RADTODEG;
2254 dc[i] = fabs(rsminusrm) - dctr;
2255 }
2256 find_zero(dc[0], dc[1], dc[2], twomin, &dt1, &dt2);
2257 tret[2] = tjd + dt1 + twomin;
2258 tret[3] = tjd + dt2 + twomin;
2259 for (m = 0, dt = tensec; m < 2; m++, dt /= 10) {
2260 for (j = 2; j <= 3; j++) {
2261 if (swe_calc(tret[j], SE_SUN, iflagcart | SEFLG_SPEED, xs, serr) == ERR)
2262 return ERR;
2263 if (swe_calc(tret[j], SE_MOON, iflagcart | SEFLG_SPEED, xm, serr) == ERR)
2264 return ERR;
2265 for (i = 0; i < 2; i++) {
2266 if (i == 1) {
2267 for(k = 0; k < 3; k++) {
2268 xs[k] -= xs[k+3] * dt;
2269 xm[k] -= xm[k+3] * dt;
2270 }
2271 }
2272 dm = sqrt(square_sum(xm));
2273 ds = sqrt(square_sum(xs));
2274 rmoon = asin(RMOON / dm) * RADTODEG;
2275 rmoon *= 0.99916; /* gives better accuracy for 2nd/3rd contacts */
2276 rsun = asin(RSUN / ds) * RADTODEG;
2277 rsminusrm = rsun - rmoon;
2278 for (k = 0; k < 3; k++) {
2279 x1[k] = xs[k] / ds /*ls[2]*/;
2280 x2[k] = xm[k] / dm /*lm[2]*/;
2281 }
2282 dctr = acos(swi_dot_prod_unit(x1, x2)) * RADTODEG;
2283 dc[i] = fabs(rsminusrm) - dctr;
2284 }
2285 dt1 = -dc[0] / ((dc[0] - dc[1]) / dt);
2286 tret[j] += dt1;
2287 }
2288 }
2289 tret[2] -= swe_deltat_ex(tret[2], ifl, serr);
2290 tret[3] -= swe_deltat_ex(tret[3], ifl, serr);
2291 }
2292 /* contacts 1 and 4 */
2293 dc[1] = rsplusrm - dctrmin;
2294 for (i = 0, t = tjd - twohr; i <= 2; i += 2, t = tjd + twohr) {
2295 if (swe_calc(t, SE_SUN, iflagcart, xs, serr) == ERR)
2296 return ERR;
2297 if (swe_calc(t, SE_MOON, iflagcart, xm, serr) == ERR)
2298 return ERR;
2299 dm = sqrt(square_sum(xm));
2300 ds = sqrt(square_sum(xs));
2301 rmoon = asin(RMOON / dm) * RADTODEG;
2302 rsun = asin(RSUN / ds) * RADTODEG;
2303 rsplusrm = rsun + rmoon;
2304 for (k = 0; k < 3; k++) {
2305 x1[k] = xs[k] / ds /*ls[2]*/;
2306 x2[k] = xm[k] / dm /*lm[2]*/;
2307 }
2308 dctr = acos(swi_dot_prod_unit(x1, x2)) * RADTODEG;
2309 dc[i] = rsplusrm - dctr;
2310 }
2311 find_zero(dc[0], dc[1], dc[2], twohr, &dt1, &dt2);
2312 tret[1] = tjd + dt1 + twohr;
2313 tret[4] = tjd + dt2 + twohr;
2314 for (m = 0, dt = tenmin; m < 3; m++, dt /= 10) {
2315 for (j = 1; j <= 4; j += 3) {
2316 if (swe_calc(tret[j], SE_SUN, iflagcart | SEFLG_SPEED, xs, serr) == ERR)
2317 return ERR;
2318 if (swe_calc(tret[j], SE_MOON, iflagcart | SEFLG_SPEED, xm, serr) == ERR)
2319 return ERR;
2320 for (i = 0; i < 2; i++) {
2321 if (i == 1) {
2322 for(k = 0; k < 3; k++) {
2323 xs[k] -= xs[k+3] * dt;
2324 xm[k] -= xm[k+3] * dt;
2325 }
2326 }
2327 dm = sqrt(square_sum(xm));
2328 ds = sqrt(square_sum(xs));
2329 rmoon = asin(RMOON / dm) * RADTODEG;
2330 rsun = asin(RSUN / ds) * RADTODEG;
2331 rsplusrm = rsun + rmoon;
2332 for (k = 0; k < 3; k++) {
2333 x1[k] = xs[k] / ds /*ls[2]*/;
2334 x2[k] = xm[k] / dm /*lm[2]*/;
2335 }
2336 dctr = acos(swi_dot_prod_unit(x1, x2)) * RADTODEG;
2337 dc[i] = fabs(rsplusrm) - dctr;
2338 }
2339 dt1 = -dc[0] / ((dc[0] - dc[1]) / dt);
2340 tret[j] += dt1;
2341 }
2342 }
2343 tret[1] -= swe_deltat_ex(tret[1], ifl, serr);
2344 tret[4] -= swe_deltat_ex(tret[4], ifl, serr);
2345 /*
2346 * visibility of eclipse phases
2347 */
2348 for (i = 4; i >= 0; i--) { /* attr for i = 0 must be kept !!! */
2349 if (tret[i] == 0)
2350 continue;
2351 if (eclipse_how(tret[i], SE_SUN, NULL, ifl, geopos[0], geopos[1], geopos[2],
2352 attr, serr) == ERR)
2353 return ERR;
2354 /*if (retflag2 & SE_ECL_VISIBLE) { could be wrong for 1st/4th contact */
2355 if (attr[6] > 0) { /* this is safe, sun above horizon, using app. alt. */
2356 retflag |= SE_ECL_VISIBLE;
2357 switch(i) {
2358 case 0: retflag |= SE_ECL_MAX_VISIBLE; break;
2359 case 1: retflag |= SE_ECL_1ST_VISIBLE; break;
2360 case 2: retflag |= SE_ECL_2ND_VISIBLE; break;
2361 case 3: retflag |= SE_ECL_3RD_VISIBLE; break;
2362 case 4: retflag |= SE_ECL_4TH_VISIBLE; break;
2363 default: break;
2364 }
2365 }
2366 }
2367 #if 1
2368 if (!(retflag & SE_ECL_VISIBLE)) {
2369 if (backward)
2370 K--;
2371 else
2372 K++;
2373 goto next_try;
2374 }
2375 #endif
2376 if ((retc = swe_rise_trans(tret[1] - 0.001, SE_SUN, NULL, iflag, SE_CALC_RISE|SE_BIT_DISC_BOTTOM, geopos, 0, 0, &tjdr, serr)) == ERR)
2377 return ERR;
2378 if (retc == -2) /* circumpolar sun */
2379 return retflag;
2380 if ((retc = swe_rise_trans(tret[1] - 0.001, SE_SUN, NULL, iflag, SE_CALC_SET|SE_BIT_DISC_BOTTOM, geopos, 0, 0, &tjds, serr)) == ERR)
2381 return ERR;
2382 if (retc == -2) /* circumpolar sun */
2383 return retflag;
2384 if (tjds < tret[1] || (tjds > tjdr && tjdr > tret[4])) {
2385 if (backward)
2386 K--;
2387 else
2388 K++;
2389 goto next_try;
2390 }
2391 if (tjdr > tret[1] && tjdr < tret[4]) {
2392 tret[5] = tjdr;
2393 if (!(retflag & SE_ECL_MAX_VISIBLE)) {
2394 tret[0] = tjdr;
2395 if ((retc = eclipse_how(tret[5], SE_SUN, NULL, ifl, geopos[0], geopos[1], geopos[2], attr, serr)) == ERR)
2396 return ERR;
2397 retflag &= ~(SE_ECL_TOTAL|SE_ECL_ANNULAR|SE_ECL_PARTIAL);
2398 retflag |= (retc & (SE_ECL_TOTAL|SE_ECL_ANNULAR|SE_ECL_PARTIAL));
2399 }
2400 }
2401 if (tjds > tret[1] && tjds < tret[4]) {
2402 tret[6] = tjds;
2403 if (!(retflag & SE_ECL_MAX_VISIBLE)) {
2404 tret[0] = tjds;
2405 if ((retc = eclipse_how(tret[6], SE_SUN, NULL, ifl, geopos[0], geopos[1], geopos[2], attr, serr)) == ERR)
2406 return ERR;
2407 retflag &= ~(SE_ECL_TOTAL|SE_ECL_ANNULAR|SE_ECL_PARTIAL);
2408 retflag |= (retc & (SE_ECL_TOTAL|SE_ECL_ANNULAR|SE_ECL_PARTIAL));
2409 }
2410 }
2411 return retflag;
2412 }
2413
occult_when_loc(double tjd_start,int32 ipl,char * starname,int32 ifl,double * geopos,double * tret,double * attr,int32 backward,char * serr)2414 static int32 occult_when_loc(
2415 double tjd_start, int32 ipl, char *starname,
2416 int32 ifl, double *geopos, double *tret, double *attr,
2417 int32 backward, char *serr)
2418 {
2419 int i, j, k, m;
2420 int32 retflag = 0, retc;
2421 double t, tjd, dt, dtint;
2422 double tjdr, tjds;
2423 double xs[6], xm[6], ls[6], lm[6], x1[6], x2[6], dm, ds;
2424 double rmoon, rsun, rsplusrm, rsminusrm;
2425 double dc[20], dctr, dctrmin;
2426 double twomin = 2.0 / 24.0 / 60.0;
2427 double tensec = 10.0 / 24.0 / 60.0 / 60.0;
2428 double twohr = 2.0 / 24.0;
2429 double tenmin = 10.0 / 24.0 / 60.0;
2430 double dt1 = 0, dt2 = 0, dtdiv, dtstart;
2431 double dadd2 = 1;
2432 double drad, dl;
2433 //AS_BOOL is_partial = FALSE;
2434 int32 iflag = SEFLG_TOPOCTR | ifl;
2435 int32 iflaggeo = iflag & ~SEFLG_TOPOCTR;
2436 int32 iflagcart = iflag | SEFLG_XYZ;
2437 int direction = 1;
2438 int32 one_try = backward & SE_ECL_ONE_TRY;
2439 AS_BOOL stop_after_this = FALSE;
2440 backward &= 1L;
2441 retflag = 0;
2442 swe_set_topo(geopos[0], geopos[1], geopos[2]);
2443 for (i = 0; i <= 9; i++)
2444 tret[i] = 0;
2445 if (backward)
2446 direction = -1;
2447 t = tjd_start;
2448 tjd = tjd_start;
2449 next_try:
2450 //is_partial = FALSE;
2451 if (calc_planet_star(t, ipl, starname, iflaggeo, ls, serr) == ERR)
2452 return ERR;
2453 /* fixed stars with an ecliptic latitude > 7 or < -7 cannot have
2454 * an occultation. Even lunar parallax andd proper motion of star
2455 * will never allow it. */
2456 if (fabs(ls[1]) > 7 && starname != NULL && *starname != '\0') {
2457 if (serr != NULL)
2458 sprintf(serr, "occultation never occurs: star %s has ecl. lat. %.1f", starname, ls[1]);
2459 return ERR;
2460 }
2461 if (swe_calc(t, SE_MOON, iflaggeo, lm, serr) == ERR)
2462 return ERR;
2463 dl = swe_degnorm(ls[0] - lm[0]);
2464 if (direction < 0)
2465 dl -= 360;
2466 /* get rough conjunction in ecliptic longitude */
2467 while (fabs(dl) > 0.1) {
2468 t += dl / 13;
2469 if (calc_planet_star(t, ipl, starname, iflaggeo, ls, serr) == ERR)
2470 return ERR;
2471 if (swe_calc(t, SE_MOON, iflaggeo, lm, serr) == ERR)
2472 return ERR;
2473 dl = swe_degnorm(ls[0] - lm[0]);
2474 if (dl > 180) dl -= 360;
2475 }
2476 tjd = t;
2477 /* difference in latitude too big for an occultation */
2478 drad = fabs(ls[1] - lm[1]);
2479 if (drad > 2) {
2480 if (one_try) {
2481 tret[0] = t + direction; /* return a date suitable for next try */
2482 return 0;
2483 }
2484 t += direction * 20;
2485 tjd = t;
2486 goto next_try;
2487 }
2488 /*
2489 * radius of planet disk in AU
2490 */
2491 if (starname != NULL && *starname != '\0')
2492 drad = 0;
2493 else if (ipl < NDIAM)
2494 drad = pla_diam[ipl] / 2 / AUNIT;
2495 else if (ipl > SE_AST_OFFSET)
2496 drad = swed.ast_diam / 2 * 1000 / AUNIT; /* km -> m -> AU */
2497 else
2498 drad = 0;
2499 /* now find out, if there is an occultation at our geogr. location */
2500 dtdiv = 2;
2501 dtstart = dadd2; /* formerly 0.2 */
2502 for (dt = dtstart;
2503 dt > 0.00001;
2504 dt /= dtdiv) {
2505 if (dt < 0.01)
2506 dtdiv = 2;
2507 for (i = 0, t = tjd - dt; i <= 2; i++, t += dt) {
2508 /* this takes some time, but is necessary to avoid
2509 * missing an eclipse */
2510 if (calc_planet_star(t, ipl, starname, iflagcart, xs, serr) == ERR)
2511 return ERR;
2512 if (calc_planet_star(t, ipl, starname, iflag, ls, serr) == ERR)
2513 return ERR;
2514 if (swe_calc(t, SE_MOON, iflagcart, xm, serr) == ERR)
2515 return ERR;
2516 if (swe_calc(t, SE_MOON, iflag, lm, serr) == ERR)
2517 return ERR;
2518 if (dt < 0.1 && fabs(ls[1] - lm[1]) > 2) {
2519 if (one_try || stop_after_this) {
2520 stop_after_this = TRUE;
2521 } else {
2522 /*t = tjd + direction * 2;*/
2523 t = tjd + direction * 20;
2524 tjd = t;
2525 goto next_try;
2526 }
2527 }
2528 dc[i] = acos(swi_dot_prod_unit(xs, xm)) * RADTODEG;
2529 rmoon = asin(RMOON / lm[2]) * RADTODEG;
2530 rsun = asin(drad / ls[2]) * RADTODEG;
2531 dc[i] -= (rmoon + rsun);
2532 }
2533 find_maximum(dc[0], dc[1], dc[2], dt, &dtint, &dctr);
2534 tjd += dtint + dt;
2535 }
2536 if (stop_after_this) { /* has one_try = TRUE */
2537 tret[0] = tjd + direction; /* return a date suitable for next try */
2538 return 0;
2539 }
2540 if (calc_planet_star(tjd, ipl, starname, iflagcart, xs, serr) == ERR)
2541 return ERR;
2542 if (calc_planet_star(tjd, ipl, starname, iflag, ls, serr) == ERR)
2543 return ERR;
2544 if (swe_calc(tjd, SE_MOON, iflagcart, xm, serr) == ERR)
2545 return ERR;
2546 if (swe_calc(tjd, SE_MOON, iflag, lm, serr) == ERR)
2547 return ERR;
2548 dctr = acos(swi_dot_prod_unit(xs, xm)) * RADTODEG;
2549 rmoon = asin(RMOON / lm[2]) * RADTODEG;
2550 rsun = asin(drad / ls[2]) * RADTODEG;
2551 rsplusrm = rsun + rmoon;
2552 rsminusrm = rsun - rmoon;
2553 if (dctr > rsplusrm) {
2554 if (one_try) {
2555 tret[0] = tjd + direction; /* return a date suitable for next try */
2556 return 0;
2557 }
2558 /*t = tjd + direction;*/
2559 t = tjd + direction * 20;
2560 tjd = t;
2561 goto next_try;
2562 }
2563 tret[0] = tjd - swe_deltat_ex(tjd, ifl, serr);
2564 tret[0] = tjd - swe_deltat_ex(tret[0], ifl, serr);
2565 if ((backward && tret[0] >= tjd_start - 0.0001)
2566 || (!backward && tret[0] <= tjd_start + 0.0001)) {
2567 /* t = tjd + direction;*/
2568 if (one_try) {
2569 tret[0] = tjd + direction; /* return a date suitable for next try */
2570 return 0;
2571 }
2572 t = tjd + direction * 20;
2573 tjd = t;
2574 goto next_try;
2575 }
2576 if (dctr < rsminusrm)
2577 retflag = SE_ECL_ANNULAR;
2578 else if (dctr < fabs(rsminusrm))
2579 retflag = SE_ECL_TOTAL;
2580 else if (dctr <= rsplusrm)
2581 retflag = SE_ECL_PARTIAL;
2582 dctrmin = dctr;
2583 /* contacts 2 and 3 */
2584 if (dctr > fabs(rsminusrm)) { /* partial, no 2nd and 3rd contact */
2585 tret[2] = tret[3] = 0;
2586 //is_partial = TRUE;
2587 } else {
2588 dc[1] = fabs(rsminusrm) - dctrmin;
2589 for (i = 0, t = tjd - twomin; i <= 2; i += 2, t = tjd + twomin) {
2590 if (calc_planet_star(t, ipl, starname, iflagcart, xs, serr) == ERR)
2591 return ERR;
2592 if (swe_calc(t, SE_MOON, iflagcart, xm, serr) == ERR)
2593 return ERR;
2594 dm = sqrt(square_sum(xm));
2595 ds = sqrt(square_sum(xs));
2596 rmoon = asin(RMOON / dm) * RADTODEG;
2597 rmoon *= 0.99916; /* gives better accuracy for 2nd/3rd contacts */
2598 rsun = asin(drad / ds) * RADTODEG;
2599 rsminusrm = rsun - rmoon;
2600 for (k = 0; k < 3; k++) {
2601 x1[k] = xs[k] / ds /*ls[2]*/;
2602 x2[k] = xm[k] / dm /*lm[2]*/;
2603 }
2604 dctr = acos(swi_dot_prod_unit(x1, x2)) * RADTODEG;
2605 dc[i] = fabs(rsminusrm) - dctr;
2606 }
2607 find_zero(dc[0], dc[1], dc[2], twomin, &dt1, &dt2);
2608 tret[2] = tjd + dt1 + twomin;
2609 tret[3] = tjd + dt2 + twomin;
2610 for (m = 0, dt = tensec; m < 2; m++, dt /= 10) {
2611 for (j = 2; j <= 3; j++) {
2612 if (calc_planet_star(tret[j], ipl, starname, iflagcart | SEFLG_SPEED, xs, serr) == ERR)
2613 return ERR;
2614 if (swe_calc(tret[j], SE_MOON, iflagcart | SEFLG_SPEED, xm, serr) == ERR)
2615 return ERR;
2616 for (i = 0; i < 2; i++) {
2617 if (i == 1) {
2618 for(k = 0; k < 3; k++) {
2619 xs[k] -= xs[k+3] * dt;
2620 xm[k] -= xm[k+3] * dt;
2621 }
2622 }
2623 dm = sqrt(square_sum(xm));
2624 ds = sqrt(square_sum(xs));
2625 rmoon = asin(RMOON / dm) * RADTODEG;
2626 rmoon *= 0.99916; /* gives better accuracy for 2nd/3rd contacts */
2627 rsun = asin(drad / ds) * RADTODEG;
2628 rsminusrm = rsun - rmoon;
2629 for (k = 0; k < 3; k++) {
2630 x1[k] = xs[k] / ds /*ls[2]*/;
2631 x2[k] = xm[k] / dm /*lm[2]*/;
2632 }
2633 dctr = acos(swi_dot_prod_unit(x1, x2)) * RADTODEG;
2634 dc[i] = fabs(rsminusrm) - dctr;
2635 }
2636 dt1 = -dc[0] / ((dc[0] - dc[1]) / dt);
2637 tret[j] += dt1;
2638 }
2639 }
2640 tret[2] -= swe_deltat_ex(tret[2], ifl, serr);
2641 tret[3] -= swe_deltat_ex(tret[3], ifl, serr);
2642 //is_partial = FALSE;
2643 }
2644 /* contacts 1 and 4 */
2645 dc[1] = rsplusrm - dctrmin;
2646 if (starname == NULL || *starname == '\0') {
2647 for (i = 0, t = tjd - twohr; i <= 2; i += 2, t = tjd + twohr) {
2648 if (calc_planet_star(t, ipl, starname, iflagcart, xs, serr) == ERR)
2649 return ERR;
2650 if (swe_calc(t, SE_MOON, iflagcart, xm, serr) == ERR)
2651 return ERR;
2652 dm = sqrt(square_sum(xm));
2653 ds = sqrt(square_sum(xs));
2654 rmoon = asin(RMOON / dm) * RADTODEG;
2655 rsun = asin(drad / ds) * RADTODEG;
2656 rsplusrm = rsun + rmoon;
2657 for (k = 0; k < 3; k++) {
2658 x1[k] = xs[k] / ds /*ls[2]*/;
2659 x2[k] = xm[k] / dm /*lm[2]*/;
2660 }
2661 dctr = acos(swi_dot_prod_unit(x1, x2)) * RADTODEG;
2662 dc[i] = rsplusrm - dctr;
2663 }
2664 find_zero(dc[0], dc[1], dc[2], twohr, &dt1, &dt2);
2665 tret[1] = tjd + dt1 + twohr;
2666 tret[4] = tjd + dt2 + twohr;
2667 for (m = 0, dt = tenmin; m < 3; m++, dt /= 10) {
2668 for (j = 1; j <= 4; j += 3) {
2669 if (calc_planet_star(tret[j], ipl, starname, iflagcart | SEFLG_SPEED, xs, serr) == ERR)
2670 return ERR;
2671 if (swe_calc(tret[j], SE_MOON, iflagcart | SEFLG_SPEED, xm, serr) == ERR)
2672 return ERR;
2673 for (i = 0; i < 2; i++) {
2674 if (i == 1) {
2675 for(k = 0; k < 3; k++) {
2676 xs[k] -= xs[k+3] * dt;
2677 xm[k] -= xm[k+3] * dt;
2678 }
2679 }
2680 dm = sqrt(square_sum(xm));
2681 ds = sqrt(square_sum(xs));
2682 rmoon = asin(RMOON / dm) * RADTODEG;
2683 rsun = asin(drad / ds) * RADTODEG;
2684 rsplusrm = rsun + rmoon;
2685 for (k = 0; k < 3; k++) {
2686 x1[k] = xs[k] / ds /*ls[2]*/;
2687 x2[k] = xm[k] / dm /*lm[2]*/;
2688 }
2689 dctr = acos(swi_dot_prod_unit(x1, x2)) * RADTODEG;
2690 dc[i] = fabs(rsplusrm) - dctr;
2691 }
2692 dt1 = -dc[0] / ((dc[0] - dc[1]) / dt);
2693 tret[j] += dt1;
2694 }
2695 }
2696 tret[1] -= swe_deltat_ex(tret[1], ifl, serr);
2697 tret[4] -= swe_deltat_ex(tret[4], ifl, serr);
2698 } else { /* fixed stars are point sources, contacts 1 and 4 = contacts 2 and 3 */
2699 tret[1] = tret[2];
2700 tret[4] = tret[3];
2701 }
2702 /*
2703 * visibility of eclipse phases
2704 */
2705 for (i = 4; i >= 0; i--) { /* attr for i = 0 must be kept !!! */
2706 if (tret[i] == 0)
2707 continue;
2708 if (eclipse_how(tret[i], ipl, starname, ifl, geopos[0], geopos[1], geopos[2],
2709 attr, serr) == ERR)
2710 return ERR;
2711 /*if (retflag2 & SE_ECL_VISIBLE) { could be wrong for 1st/4th contact */
2712 if (attr[6] > 0) { /* this is save, sun above horizon (using app. alt.) */
2713 retflag |= SE_ECL_VISIBLE;
2714 switch(i) {
2715 case 0: retflag |= SE_ECL_MAX_VISIBLE; break;
2716 case 1: retflag |= SE_ECL_1ST_VISIBLE; break;
2717 case 2: retflag |= SE_ECL_2ND_VISIBLE; break;
2718 case 3: retflag |= SE_ECL_3RD_VISIBLE; break;
2719 case 4: retflag |= SE_ECL_4TH_VISIBLE; break;
2720 default: break;
2721 }
2722 }
2723 }
2724 #if 1
2725 if (!(retflag & SE_ECL_VISIBLE)) {
2726 /* t = tjd + direction;*/
2727 if (one_try) {
2728 tret[0] = tjd + direction; /* return a date suitable for next try */
2729 return 0;
2730 }
2731 t = tjd + direction * 20;
2732 tjd = t;
2733 goto next_try;
2734 }
2735 #endif
2736 if ((retc = swe_rise_trans(tret[1] - 0.1, ipl, starname, iflag, SE_CALC_RISE|SE_BIT_DISC_BOTTOM, geopos, 0, 0, &tjdr, serr)) == ERR)
2737 return ERR;
2738 if (retc >= 0 && (retc = swe_rise_trans(tret[1] - 0.1, ipl, starname, iflag, SE_CALC_SET|SE_BIT_DISC_BOTTOM, geopos, 0, 0, &tjds, serr)) == ERR)
2739 return ERR;
2740 if (retc >= 0) {
2741 if (tjdr > tret[1] && tjdr < tret[4])
2742 tret[5] = tjdr;
2743 if (tjds > tret[1] && tjds < tret[4])
2744 tret[6] = tjds;
2745 }
2746 /* note, circumpolar sun above horizon is not tested */
2747 //if (!is_partial) {
2748 if ((retc = swe_rise_trans(tret[1], SE_SUN, NULL, iflag, SE_CALC_RISE, geopos, 0, 0, &tjdr, serr)) == ERR)
2749 return ERR;
2750 if (retc >= 0 && (retc = swe_rise_trans(tret[1], SE_SUN, NULL, iflag, SE_CALC_SET, geopos, 0, 0, &tjds, serr)) == ERR)
2751 return ERR;
2752 if (retc >= 0) {
2753 if (tjds < tjdr)
2754 retflag |= SE_ECL_OCC_BEG_DAYLIGHT;
2755 }
2756 if ((retc = swe_rise_trans(tret[4], SE_SUN, NULL, iflag, SE_CALC_RISE, geopos, 0, 0, &tjdr, serr)) == ERR)
2757 return ERR;
2758 if (retc >= 0 && (retc = swe_rise_trans(tret[4], SE_SUN, NULL, iflag, SE_CALC_SET, geopos, 0, 0, &tjds, serr)) == ERR)
2759 return ERR;
2760 if (retc >= 0) {
2761 if (tjds < tjdr)
2762 retflag |= SE_ECL_OCC_END_DAYLIGHT;
2763 }
2764 //}
2765 return retflag;
2766 }
2767
2768 /*
2769 * swe_azalt()
2770 * Computes azimut and height, from either ecliptic or
2771 * equatorial coordinates
2772 *
2773 * input:
2774 * tjd_ut
2775 * iflag either SE_ECL2HOR or SE_EQU2HOR
2776 * geopos[3] geograph. longitude, latitude, height above sea
2777 * atpress atmospheric pressure at geopos in millibars (hPa)
2778 * attemp atmospheric temperature in degrees C
2779 * xin[2] input coordinates polar, in degrees
2780 *
2781 * Horizontal coordinates are returned in
2782 * xaz[3] xaz[0] = azimuth
2783 * xaz[1] = true altitude
2784 * xaz[2] = apparent altitude
2785 *
2786 * If atpress is not given (= 0), the programm assumes 1013.25 mbar;
2787 * if a non-zero height above sea is given, atpress is estimated.
2788 * geohgt height of observer above sea (optional)
2789 */
swe_azalt(double tjd_ut,int32 calc_flag,double * geopos,double atpress,double attemp,double * xin,double * xaz)2790 void CALL_CONV swe_azalt(
2791 double tjd_ut,
2792 int32 calc_flag,
2793 double *geopos,
2794 double atpress,
2795 double attemp,
2796 double *xin,
2797 double *xaz)
2798 {
2799 int i;
2800 double x[6], xra[3];
2801 double armc = swe_degnorm(swe_sidtime(tjd_ut) * 15 + geopos[0]);
2802 double mdd, eps_true;
2803 for (i = 0; i < 2; i++)
2804 xra[i] = xin[i];
2805 xra[2] = 1;
2806 if (calc_flag == SE_ECL2HOR) {
2807 swe_calc(tjd_ut + swe_deltat_ex(tjd_ut, -1, NULL), SE_ECL_NUT, 0, x, NULL);
2808 eps_true = x[0];
2809 swe_cotrans(xra, xra, -eps_true);
2810 }
2811 mdd = swe_degnorm(xra[0] - armc);
2812 x[0] = swe_degnorm(mdd - 90);
2813 x[1] = xra[1];
2814 x[2] = 1;
2815 /* azimuth from east, counterclock */
2816 swe_cotrans(x, x, 90 - geopos[1]);
2817 /* azimuth from south to west */
2818 x[0] = swe_degnorm(x[0] + 90);
2819 xaz[0] = 360 - x[0];
2820 xaz[1] = x[1]; /* true height */
2821 if (atpress == 0) {
2822 /* estimate atmospheric pressure */
2823 atpress = 1013.25 * pow(1 - 0.0065 * geopos[2] / 288, 5.255);
2824 }
2825 xaz[2] = swe_refrac_extended(x[1], geopos[2], atpress, attemp, const_lapse_rate, SE_TRUE_TO_APP, NULL);
2826 /* xaz[2] = swe_refrac_extended(xaz[2], geopos[2], atpress, attemp, const_lapse_rate, SE_APP_TO_TRUE, NULL);*/
2827 }
2828
2829 /*
2830 * swe_azalt_rev()
2831 * computes either ecliptical or equatorial coordinates from
2832 * azimuth and true altitude in degrees.
2833 * For conversion between true and apparent altitude, there is
2834 * the function swe_refrac().
2835 *
2836 * input:
2837 * tjd_ut
2838 * iflag either SE_HOR2ECL or SE_HOR2EQU
2839 * xin[2] azimut and true altitude, in degrees
2840 */
swe_azalt_rev(double tjd_ut,int32 calc_flag,double * geopos,double * xin,double * xout)2841 void CALL_CONV swe_azalt_rev(
2842 double tjd_ut,
2843 int32 calc_flag,
2844 double *geopos,
2845 double *xin,
2846 double *xout)
2847 {
2848 int i;
2849 double x[6], xaz[3];
2850 double geolon = geopos[0];
2851 double geolat = geopos[1];
2852 double armc = swe_degnorm(swe_sidtime(tjd_ut) * 15 + geolon);
2853 double eps_true, dang;
2854 for (i = 0; i < 2; i++)
2855 xaz[i] = xin[i];
2856 xaz[2] = 1;
2857 /* azimuth is from south, clockwise.
2858 * we need it from east, counterclock */
2859 xaz[0] = 360 - xaz[0];
2860 xaz[0] = swe_degnorm(xaz[0] - 90);
2861 /* equatorial positions */
2862 dang = geolat - 90;
2863 swe_cotrans(xaz, xaz, dang);
2864 xaz[0] = swe_degnorm(xaz[0] + armc + 90);
2865 xout[0] = xaz[0];
2866 xout[1] = xaz[1];
2867 /* ecliptic positions */
2868 if (calc_flag == SE_HOR2ECL) {
2869 swe_calc(tjd_ut + swe_deltat_ex(tjd_ut, -1, NULL), SE_ECL_NUT, 0, x, NULL);
2870 eps_true = x[0];
2871 swe_cotrans(xaz, x, eps_true);
2872 xout[0] = x[0];
2873 xout[1] = x[1];
2874 }
2875 }
2876
2877 /* swe_refrac()
2878 * Transforms apparent to true altitude and vice-versa.
2879 * These formulae do not handle the case when the
2880 * sun is visible below the geometrical horizon
2881 * (from a mountain top or an air plane)
2882 * input:
2883 * double inalt; * altitude of object in degrees *
2884 * double atpress; * millibars (hectopascal) *
2885 * double attemp; * degrees C *
2886 * int32 calc_flag; * either SE_APP_TO_TRUE or
2887 * * SE_TRUE_TO_APP
2888 */
swe_refrac(double inalt,double atpress,double attemp,int32 calc_flag)2889 double CALL_CONV swe_refrac(double inalt, double atpress, double attemp, int32 calc_flag)
2890 {
2891 double a, refr;
2892 double pt_factor = atpress / 1010.0 * 283.0 / (273.0 + attemp);
2893 double trualt, appalt;
2894 #if 0
2895 /*
2896 * -- S. L. Moshier */
2897 double y, yy0, D0, N, D, P, Q;
2898 int i;
2899 if (calc_flag == SE_TRUE_TO_APP) {
2900 trualt = inalt;
2901 if( (trualt < -2.0) || (trualt >= 90.0) )
2902 return(trualt);
2903 /* For high altitude angle, AA page B61
2904 * Accuracy "usually about 0.1' ".
2905 */
2906 if( trualt > 15.0 ) {
2907 D = 0.00452*atpress/((273.0+attemp)*tan( DEGTORAD*trualt ));
2908 return(trualt + D);
2909 }
2910 /* Formula for low altitude is from the Almanac for Computers.
2911 * It gives the correction for observed altitude, so has
2912 * to be inverted numerically to get the observed from the true.
2913 * Accuracy about 0.2' for -20C < T < +40C and 970mb < P < 1050mb.
2914 */
2915 /* Start iteration assuming correction = 0
2916 */
2917 y = trualt;
2918 D = 0.0;
2919 /* Invert Almanac for Computers formula numerically
2920 */
2921 P = (atpress - 80.0)/930.0;
2922 Q = 4.8e-3 * (attemp - 10.0);
2923 yy0 = y;
2924 D0 = D;
2925 for( i=0; i<4; i++ ) {
2926 N = y + (7.31/(y+4.4));
2927 N = 1.0/tan(DEGTORAD*N);
2928 D = N*P/(60.0 + Q * (N + 39.0));
2929 N = y - yy0;
2930 yy0 = D - D0 - N; /* denominator of derivative */
2931 if( (N != 0.0) && (yy0 != 0.0) )
2932 /* Newton iteration with numerically estimated derivative */
2933 N = y - N*(trualt + D - y)/yy0;
2934 else
2935 /* Can't do it on first pass */
2936 N = trualt + D;
2937 yy0 = y;
2938 D0 = D;
2939 y = N;
2940 }
2941 return( trualt + D );
2942 } else {
2943 #else
2944 /* another algorithm, from Meeus, German, p. 114ff.
2945 */
2946 if (calc_flag == SE_TRUE_TO_APP) {
2947 trualt = inalt;
2948 if (trualt > 15) {
2949 a = tan((90 - trualt) * DEGTORAD);
2950 refr = (58.276 * a - 0.0824 * a * a * a);
2951 refr *= pt_factor / 3600.0;
2952 } else if (trualt > -5) {
2953 /* the following tan is not defined for a value
2954 * of trualt near -5.00158 and 89.89158 */
2955 a = trualt + 10.3 / (trualt + 5.11);
2956 if (a + 1e-10 >= 90)
2957 refr = 0;
2958 else
2959 refr = 1.02 / tan(a * DEGTORAD);
2960 refr *= pt_factor / 60.0;
2961 } else
2962 refr = 0;
2963 appalt = trualt;
2964 if (appalt + refr > 0)
2965 appalt += refr;
2966 return appalt;
2967 } else {
2968 #endif
2969 /* apparent to true */
2970 appalt = inalt;
2971 /* the following tan is not defined for a value
2972 * of inalt near -4.3285 and 89.9225 */
2973 a = appalt + 7.31 / (appalt + 4.4);
2974 if (a + 1e-10 >= 90)
2975 refr = 0;
2976 else {
2977 refr = 1.00 / tan(a * DEGTORAD);
2978 refr -= 0.06 * sin(14.7 * refr + 13);
2979 }
2980 refr *= pt_factor / 60.0;
2981 trualt = appalt;
2982 if (appalt - refr > 0)
2983 trualt = appalt - refr;
2984 return trualt;
2985 }
2986 }
2987
2988 void CALL_CONV swe_set_lapse_rate(double lapse_rate)
2989 {
2990 const_lapse_rate = lapse_rate;
2991 }
2992
2993 /* swe_refrac_extended()
2994 *
2995 * This function was created thanks to and with the help of the
2996 * archaeoastronomer Victor Reijs.
2997 * It is more correct and more skilled than the old function swe_refrac():
2998 * - it allows correct calculation of refraction for altitudes above sea > 0,
2999 * where the ideal horizon and planets that are visible may have a
3000 * negative height. (for swe_refrac(), negative apparent heights do not
3001 * exist!)
3002 * - it allows to manipulate the refraction constant
3003 *
3004 * Transforms apparent to true altitude and vice-versa.
3005 * input:
3006 * double inalt; * altitude of object above geometric horizon in degrees*
3007 * * geometric horizon = plane perpendicular to gravity *
3008 * double geoalt; * altitude of observer above sea level in meters *
3009 * double atpress; * millibars (hectopascal) *
3010 * double lapse_rate; * (dT/dh) [deg K/m]
3011 * double attemp; * degrees C *
3012 * int32 calc_flag; * either SE_APP_TO_TRUE or
3013 * * SE_TRUE_TO_APP
3014 *
3015 * function returns:
3016 * double *dret; * array of 4 doubles; declare 20 doubles !
3017 * - dret[0] true altitude, if possible; otherwise input value
3018 * - dret[1] apparent altitude, if possible; otherwise input value
3019 * - dret[2] refraction
3020 * - dret[3] dip of the horizon
3021 *
3022 * The body is above the horizon if the dret[0] != dret[1]
3023 *
3024 * case 1, conversion from true altitude to apparent altitude
3025 * - apparent altitude, if body is observable above ideal horizon
3026 * - true altitude (the input value), otherwise
3027 * "ideal horizon" is the horizon as seen above an ideal sphere (as seen
3028 * from a plane over the ocean with a clear sky)
3029 * case 2, conversion from apparent altitude to true altitude
3030 * - the true altitude resulting from the input apparent altitude, if this value
3031 * is a plausible apparent altitude, i.e. if it is a position above the ideal
3032 * horizon, taking into account the dip of the horizon
3033 * - the input altitude otherwise
3034 *
3035 * The body is above the horizon if the dret[0] != dret[1]
3036 */
3037 double CALL_CONV swe_refrac_extended(double inalt, double geoalt, double atpress, double attemp, double lapse_rate, int32 calc_flag, double *dret)
3038 {
3039 double refr;
3040 double trualt;
3041 double dip = calc_dip(geoalt, atpress, attemp, lapse_rate);
3042 double D, D0, N, y, yy0;
3043 int i;
3044 /* make sure that inalt <=90 */
3045 if( (inalt>90) )
3046 inalt=180-inalt;
3047 if (calc_flag == SE_TRUE_TO_APP) {
3048 if (inalt < -10) {
3049 if (dret != NULL) {
3050 dret[0]=inalt;
3051 dret[1]=inalt;
3052 dret[2]=0;
3053 dret[3]=dip;
3054 }
3055 return inalt;
3056 }
3057 /* by iteration */
3058 y = inalt;
3059 D = 0.0;
3060 yy0 = 0;
3061 D0 = D;
3062 for(i=0; i<5; i++) {
3063 D = calc_astronomical_refr(y,atpress,attemp);
3064 N = y - yy0;
3065 yy0 = D - D0 - N; /* denominator of derivative */
3066 if (N != 0.0 && yy0 != 0.0) /* sic !!! code by Moshier */
3067 N = y - N*(inalt + D - y)/yy0; /* Newton iteration with numerically estimated derivative */
3068 else /* Can't do it on first pass */
3069 N = inalt + D;
3070 yy0 = y;
3071 D0 = D;
3072 y = N;
3073 }
3074 refr = D;
3075 if( (inalt + refr < dip) ) {
3076 if (dret != NULL) {
3077 dret[0]=inalt;
3078 dret[1]=inalt;
3079 dret[2]=0;
3080 dret[3]=dip;
3081 }
3082 return inalt;
3083 }
3084 if (dret != NULL) {
3085 dret[0]=inalt;
3086 dret[1]=inalt+refr;
3087 dret[2]=refr;
3088 dret[3]=dip;
3089 }
3090 return inalt+refr;
3091 } else {
3092 refr = calc_astronomical_refr(inalt,atpress,attemp);
3093 trualt=inalt-refr;
3094 //printf("inalt=%f, dip=%f\n", inalt, dip);
3095 if (dret != NULL) {
3096 if (inalt > dip) {
3097 dret[0]=trualt;
3098 dret[1]=inalt;
3099 dret[2]=refr;
3100 dret[3]=dip;
3101 } else {
3102 dret[0]=inalt;
3103 dret[1]=inalt;
3104 dret[2]=0;
3105 dret[3]=dip;
3106 }
3107 }
3108 // Apparent altitude cannot be below dip.
3109 // True altitude is only returned if apparent altitude is hgher than dip.
3110 // Othwise the apparent altitude is returned.
3111 //if (trualt > dip)
3112 if (inalt >= dip) // bug fix dieter, 4 feb 20
3113 return trualt;
3114 else
3115 return inalt;
3116 }
3117 }
3118
3119 /* calculate the astronomical refraction
3120 * input parameters:
3121 * double inalt * apparent altitude of object
3122 * double atpress * atmospheric pressure millibars (hectopascal) *
3123 * double attemp * atmospheric temperature degrees C *
3124 * returns double r in degrees
3125 */
3126 static double calc_astronomical_refr(double inalt,double atpress, double attemp)
3127 {
3128 #if 0
3129 /* formula based on G.G. Bennett, The calculation of astronomical refraction in marine navigation,
3130 * Journal of Inst. Navigation, No. 35, page 255-259, 1982,
3131 * page 257 for refraction formula: formula H
3132 * and page 259 for atmospheric compensation
3133 */
3134 double refractaccent = 1/tan(DEGTORAD*(inalt + 7.31/(inalt+4.4)));
3135 double r = (refractaccent - 0.06 * sin(DEGTORAD*(14.7*refractaccent +13)));
3136 r = ((atpress - 80) / 930 / (1 + 0.00008 * (r + 39) * (attemp - 10)) * r)/60;
3137 return r;
3138 #else
3139 /* Formula by Sinclair, see article mentioned above, p. 256. Better for
3140 * apparent altitudes < 0; */
3141 double r;
3142 if (inalt > 17.904104638432) { /* for continuous function, instead of '>15' */
3143 r = 0.97 / tan(inalt * DEGTORAD);
3144 } else {
3145 r = (34.46 + 4.23 * inalt + 0.004 * inalt * inalt) / (1 + 0.505 * inalt + 0.0845 * inalt * inalt);
3146 }
3147 r = ((atpress - 80) / 930 / (1 + 0.00008 * (r + 39) * (attemp - 10)) * r) / 60.0;
3148 return r;
3149 #endif
3150 }
3151
3152 /* calculate dip of the horizon
3153 * input parameters:
3154 * double geoalt * altitude of observer above sea level in meters *
3155 * double atpress * atmospheric pressure millibars (hectopascal) *
3156 * double attemp * atmospheric temperature degrees C *
3157 * double lapse_rate * (dT/dh) [deg K/m]
3158 * returns dip in degrees
3159 */
3160 static double calc_dip(double geoalt, double atpress, double attemp, double lapse_rate)
3161 {
3162 /* below formula is based on A. Thom, Megalithic lunar observations, 1973 (page 32).
3163 * conversion to metric has been done by
3164 * V. Reijs, 2000, http://www.iol.ie/~geniet/eng/refract.htm
3165 */
3166 double krefr = (0.0342 + lapse_rate) / (0.154 * 0.0238);
3167 double d = 1-1.8480*krefr*atpress/(273.16+attemp)/(273.16+attemp);
3168 /* return -0.03203*sqrt(geoalt)*sqrt(d); */
3169 /* double a = acos(1/(1+geoalt/EARTH_RADIUS));*/
3170 return -180.0/PI * acos(1 / (1 + geoalt / EARTH_RADIUS)) * sqrt(d);
3171 }
3172
3173
3174 /* Computes attributes of a lunar eclipse for given tjd and geopos
3175 *
3176 * retflag SE_ECL_TOTAL or SE_ECL_PARTIAL
3177 * SE_ECL_PENUMBRAL
3178 * if 0, there is no eclipse
3179 *
3180 * attr[0] umbral magnitude at tjd
3181 * attr[1] penumbral magnitude
3182 * attr[4] azimuth of moon at tjd
3183 * attr[5] true altitude of moon above horizon at tjd
3184 * attr[6] apparent altitude of moon above horizon at tjd
3185 * attr[7] distance of moon from opposition in degrees
3186 * attr[8] umbral magnitude at tjd (= attr[0])
3187 * attr[9] saros series number
3188 * attr[10] saros series member number
3189 * declare as attr[20] at least !
3190 *
3191 */
3192 int32 CALL_CONV swe_lun_eclipse_how(
3193 double tjd_ut,
3194 int32 ifl,
3195 double *geopos,
3196 double *attr,
3197 char *serr)
3198 {
3199 double dcore[10];
3200 double lm[6], xaz[6];
3201 int32 retc;
3202 /* attention: geopos[] is not used so far; may be NULL */
3203 if (geopos != NULL)
3204 geopos[0] = geopos[0]; /* to shut up mint */
3205 if (geopos != NULL && (geopos[2] < SEI_ECL_GEOALT_MIN || geopos[2] > SEI_ECL_GEOALT_MAX)) {
3206 if (serr != NULL)
3207 sprintf(serr, "location for eclipses must be between %.0f and %.0f m above sea", SEI_ECL_GEOALT_MIN, SEI_ECL_GEOALT_MAX);
3208 return ERR;
3209 }
3210 ifl = ifl & ~SEFLG_TOPOCTR;
3211 ifl &= ~(SEFLG_JPLHOR | SEFLG_JPLHOR_APPROX);
3212 swi_set_tid_acc(tjd_ut, ifl, 0, serr);
3213 retc = lun_eclipse_how(tjd_ut, ifl, attr, dcore, serr);
3214 if (geopos == NULL) {
3215 return retc;
3216 }
3217 /*
3218 * azimuth and altitude of moon
3219 */
3220 swe_set_topo(geopos[0], geopos[1], geopos[2]);
3221 if (swe_calc_ut(tjd_ut, SE_MOON, ifl | SEFLG_TOPOCTR | SEFLG_EQUATORIAL, lm, serr) == ERR)
3222 return ERR;
3223 swe_azalt(tjd_ut, SE_EQU2HOR, geopos, 0, 10, lm, xaz);
3224 attr[4] = xaz[0];
3225 attr[5] = xaz[1];
3226 attr[6] = xaz[2];
3227 if (xaz[2] <= 0)
3228 retc = 0;
3229 return retc;
3230 }
3231
3232 /*
3233 * attr[]: see swe_lun_eclipse_how()
3234 *
3235 * dcore[0]: distance of shadow axis from geocenter r0
3236 * dcore[1]: diameter of core shadow on fundamental plane d0
3237 * dcore[2]: diameter of half-shadow on fundamental plane D0
3238 */
3239 static int32 lun_eclipse_how(
3240 double tjd_ut,
3241 int32 ifl,
3242 double *attr,
3243 double *dcore,
3244 char *serr)
3245 {
3246 int i, j, k;
3247 int32 retc = 0;
3248 double e[6], rm[6], rs[6];
3249 double dsm, d0, D0, s0, r0, ds, dm;
3250 double dctr, x1[6], x2[6];
3251 double f1, f2;
3252 double deltat, tjd, d;
3253 double cosf1, cosf2;
3254 double rmoon = RMOON;
3255 double dmoon = 2 * rmoon;
3256 int32 iflag;
3257 for (i = 0; i < 10; i++)
3258 dcore[i] = 0;
3259 for (i = 0; i < 20; i++)
3260 attr[i] = 0;
3261 /* nutation need not be in lunar and solar positions,
3262 * if mean sidereal time will be used */
3263 iflag = SEFLG_SPEED | SEFLG_EQUATORIAL | ifl;
3264 iflag = iflag | SEFLG_XYZ;
3265 deltat = swe_deltat_ex(tjd_ut, ifl, serr);
3266 tjd = tjd_ut + deltat;
3267 /* moon in cartesian coordinates */
3268 if (swe_calc(tjd, SE_MOON, iflag, rm, serr) == ERR)
3269 return ERR;
3270 /* distance of moon from geocenter */
3271 dm = sqrt(square_sum(rm));
3272 /* sun in cartesian coordinates */
3273 if (swe_calc(tjd, SE_SUN, iflag, rs, serr) == ERR)
3274 return ERR;
3275 /* distance of sun from geocenter */
3276 ds = sqrt(square_sum(rs));
3277 for (i = 0; i < 3; i++) {
3278 x1[i] = rs[i] / ds;
3279 x2[i] = rm[i] / dm;
3280 }
3281 dctr = acos(swi_dot_prod_unit(x1, x2)) * RADTODEG;
3282 /* selenocentric sun */
3283 for (i = 0; i <= 2; i++)
3284 rs[i] -= rm[i];
3285 /* selenocentric earth */
3286 for (i = 0; i <= 2; i++)
3287 rm[i] = -rm[i];
3288 /* sun - earth vector */
3289 for (i = 0; i <= 2; i++)
3290 e[i] = (rm[i] - rs[i]);
3291 /* distance sun - earth */
3292 dsm = sqrt(square_sum(e));
3293 /* sun - earth unit vector */
3294 for (i = 0; i <= 2; i++)
3295 e[i] /= dsm;
3296 f1 = ((RSUN - REARTH) / dsm);
3297 cosf1 = sqrt(1 - f1 * f1);
3298 f2 = ((RSUN + REARTH) / dsm);
3299 cosf2 = sqrt(1 - f2 * f2);
3300 /* distance of earth from fundamental plane */
3301 s0 = -dot_prod(rm, e);
3302 /* distance of shadow axis from selenocenter */
3303 r0 = sqrt(dm * dm - s0 * s0);
3304 /* diameter of core shadow on fundamental plane */
3305 /* one 50th is added for effect of atmosphere, AA98, L4 */
3306 d0 = fabs(s0 / dsm * (DSUN - DEARTH) - DEARTH) * (1 + 1.0 / 50.0) / cosf1;
3307 /* diameter of half-shadow on fundamental plane */
3308 D0 = (s0 / dsm * (DSUN + DEARTH) + DEARTH) * (1 + 1.0 / 50.0) / cosf2;
3309 d0 /= cosf1;
3310 D0 /= cosf2;
3311 /* for better agreement with NASA: */
3312 d0 *= 0.99405;
3313 D0 *= 0.98813;
3314 dcore[0] = r0;
3315 dcore[1] = d0;
3316 dcore[2] = D0;
3317 dcore[3] = cosf1;
3318 dcore[4] = cosf2;
3319 /**************************
3320 * phase and umbral magnitude
3321 **************************/
3322 retc = 0;
3323 if (d0 / 2 >= r0 + rmoon / cosf1) {
3324 retc = SE_ECL_TOTAL;
3325 attr[0] = (d0 / 2 - r0 + rmoon) / dmoon;
3326 } else if (d0 / 2 >= r0 - rmoon / cosf1) {
3327 retc = SE_ECL_PARTIAL;
3328 attr[0] = (d0 / 2 - r0 + rmoon) / dmoon;
3329 } else if (D0 / 2 >= r0 - rmoon / cosf2) {
3330 retc = SE_ECL_PENUMBRAL;
3331 attr[0] = 0;
3332 } else {
3333 if (serr != NULL)
3334 sprintf(serr, "no lunar eclipse at tjd = %f", tjd);
3335 }
3336 attr[8] = attr[0];
3337 /**************************
3338 * penumbral magnitude
3339 **************************/
3340 attr[1] = (D0 / 2 - r0 + rmoon) / dmoon;
3341 if (retc != 0)
3342 attr[7] = 180 - fabs(dctr);
3343 /* saros series and member */
3344 for (i = 0; i < NSAROS_LUNAR; i++) {
3345 d = (tjd_ut - saros_data_lunar[i].tstart) / SAROS_CYCLE;
3346 if (d < 0 && d * SAROS_CYCLE > -2) d = 0.0000001;
3347 if (d < 0) continue;
3348 j = (int) d;
3349 if ((d - j) * SAROS_CYCLE < 2) {
3350 attr[9] = (double) saros_data_lunar[i].series_no;
3351 attr[10] = (double) j + 1;
3352 break;
3353 }
3354 k = j + 1;
3355 if ((k - d) * SAROS_CYCLE < 2) {
3356 attr[9] = (double) saros_data_lunar[i].series_no;
3357 attr[10] = (double) k + 1;
3358 break;
3359 }
3360 }
3361 if (i == NSAROS_LUNAR) {
3362 attr[9] = attr[10] = -99999999;
3363 }
3364 return retc;
3365 }
3366
3367 /* When is the next lunar eclipse?
3368 *
3369 * retflag SE_ECL_TOTAL or SE_ECL_PENUMBRAL or SE_ECL_PARTIAL
3370 *
3371 * tret[0] time of maximum eclipse
3372 * tret[1]
3373 * tret[2] time of partial phase begin (indices consistent with solar eclipses)
3374 * tret[3] time of partial phase end
3375 * tret[4] time of totality begin
3376 * tret[5] time of totality end
3377 * tret[6] time of penumbral phase begin
3378 * tret[7] time of penumbral phase end
3379 */
3380 int32 CALL_CONV swe_lun_eclipse_when(double tjd_start, int32 ifl, int32 ifltype,
3381 double *tret, int32 backward, char *serr)
3382 {
3383 int i, j, m, n, o, i1 = 0, i2 = 0;
3384 int32 retflag = 0, retflag2 = 0;
3385 double t, tjd, tjd2, dt, dtint, dta, dtb;
3386 double T, T2, T3, T4, K, F, M, Mm;
3387 double E, Ff, F1, A1, Om;
3388 double xs[6], xm[6], dm, ds;
3389 double rsun, rearth, dcore[10];
3390 double dc[3], dctr;
3391 double twohr = 2.0 / 24.0;
3392 double tenmin = 10.0 / 24.0 / 60.0;
3393 double dt1 = 0, dt2 = 0;
3394 double kk;
3395 double attr[20];
3396 double dtstart, dtdiv;
3397 double xa[6], xb[6];
3398 int direction = 1;
3399 int32 iflag;
3400 int32 iflagcart;
3401 ifl &= SEFLG_EPHMASK;
3402 swi_set_tid_acc(tjd_start, ifl, 0, serr);
3403 iflag = SEFLG_EQUATORIAL | ifl;
3404 iflagcart = iflag | SEFLG_XYZ;
3405 ifltype &= ~(SE_ECL_CENTRAL|SE_ECL_NONCENTRAL);
3406 if (ifltype & (SE_ECL_ANNULAR|SE_ECL_ANNULAR_TOTAL)) {
3407 ifltype &= ~(SE_ECL_ANNULAR|SE_ECL_ANNULAR_TOTAL);
3408 if (ifltype == 0) {
3409 if (serr != NULL) {
3410 strcpy(serr, "annular lunar eclipses don't exist");
3411 }
3412 return ERR; /* avoids infinite loop */
3413 }
3414 }
3415 if (ifltype == 0)
3416 ifltype = SE_ECL_TOTAL | SE_ECL_PENUMBRAL | SE_ECL_PARTIAL;
3417 if (backward)
3418 direction = -1;
3419 K = (int) ((tjd_start - J2000) / 365.2425 * 12.3685);
3420 K -= direction;
3421 next_try:
3422 retflag = 0;
3423 for (i = 0; i <= 9; i++)
3424 tret[i] = 0;
3425 kk = K + 0.5;
3426 T = kk / 1236.85;
3427 T2 = T * T; T3 = T2 * T; T4 = T3 * T;
3428 Ff = F = swe_degnorm(160.7108 + 390.67050274 * kk
3429 - 0.0016341 * T2
3430 - 0.00000227 * T3
3431 + 0.000000011 * T4);
3432 if (Ff > 180)
3433 Ff -= 180;
3434 if (Ff > 21 && Ff < 159) { /* no eclipse possible */
3435 K += direction;
3436 goto next_try;
3437 }
3438 /* approximate time of geocentric maximum eclipse
3439 * formula from Meeus, German, p. 381 */
3440 tjd = 2451550.09765 + 29.530588853 * kk
3441 + 0.0001337 * T2
3442 - 0.000000150 * T3
3443 + 0.00000000073 * T4;
3444 M = swe_degnorm(2.5534 + 29.10535669 * kk
3445 - 0.0000218 * T2
3446 - 0.00000011 * T3);
3447 Mm = swe_degnorm(201.5643 + 385.81693528 * kk
3448 + 0.1017438 * T2
3449 + 0.00001239 * T3
3450 + 0.000000058 * T4);
3451 Om = swe_degnorm(124.7746 - 1.56375580 * kk
3452 + 0.0020691 * T2
3453 + 0.00000215 * T3);
3454 E = 1 - 0.002516 * T - 0.0000074 * T2;
3455 A1 = swe_degnorm(299.77 + 0.107408 * kk - 0.009173 * T2);
3456 M *= DEGTORAD;
3457 Mm *= DEGTORAD;
3458 F *= DEGTORAD;
3459 Om *= DEGTORAD;
3460 F1 = F - 0.02665 * sin(Om) * DEGTORAD;
3461 A1 *= DEGTORAD;
3462 tjd = tjd - 0.4075 * sin(Mm)
3463 + 0.1721 * E * sin(M)
3464 + 0.0161 * sin(2 * Mm)
3465 - 0.0097 * sin(2 * F1)
3466 + 0.0073 * E * sin(Mm - M)
3467 - 0.0050 * E * sin(Mm + M)
3468 - 0.0023 * sin(Mm - 2 * F1)
3469 + 0.0021 * E * sin(2 * M)
3470 + 0.0012 * sin(Mm + 2 * F1)
3471 + 0.0006 * E * sin(2 * Mm + M)
3472 - 0.0004 * sin(3 * Mm)
3473 - 0.0003 * E * sin(M + 2 * F1)
3474 + 0.0003 * sin(A1)
3475 - 0.0002 * E * sin(M - 2 * F1)
3476 - 0.0002 * E * sin(2 * Mm - M)
3477 - 0.0002 * sin(Om);
3478 /*
3479 * precise computation:
3480 * time of maximum eclipse (if eclipse) =
3481 * minimum selenocentric angle between sun and earth edges.
3482 * After this time has been determined, check
3483 * whether or not an eclipse is taking place with
3484 * the function lun_eclipse_how().
3485 */
3486 dtstart = 0.1;
3487 if (tjd < 2000000 || tjd > 2500000)
3488 dtstart = 5;
3489 dtdiv = 4;
3490 for (j = 0, dt = dtstart;
3491 dt > 0.001;
3492 j++, dt /= dtdiv) {
3493 for (i = 0, t = tjd - dt; i <= 2; i++, t += dt) {
3494 if (swe_calc(t, SE_SUN, iflagcart, xs, serr) == ERR)
3495 return ERR;
3496 if (swe_calc(t, SE_MOON, iflagcart, xm, serr) == ERR)
3497 return ERR;
3498 for (m = 0; m < 3; m++) {
3499 xs[m] -= xm[m]; /* selenocentric sun */
3500 xm[m] = -xm[m]; /* selenocentric earth */
3501 }
3502 ds = sqrt(square_sum(xs));
3503 dm = sqrt(square_sum(xm));
3504 for (m = 0; m < 3; m++) {
3505 xa[m] = xs[m] / ds;
3506 xb[m] = xm[m] / dm;
3507 }
3508 dc[i] = acos(swi_dot_prod_unit(xa, xb)) * RADTODEG;
3509 rearth = asin(REARTH / dm) * RADTODEG;
3510 rsun = asin(RSUN / ds) * RADTODEG;
3511 dc[i] -= (rearth + rsun);
3512 }
3513 find_maximum(dc[0], dc[1], dc[2], dt, &dtint, &dctr);
3514 tjd += dtint + dt;
3515 }
3516 tjd2 = tjd - swe_deltat_ex(tjd, ifl, serr);
3517 tjd2 = tjd - swe_deltat_ex(tjd2, ifl, serr);
3518 tjd = tjd - swe_deltat_ex(tjd2, ifl, serr);
3519 if ((retflag = swe_lun_eclipse_how(tjd, ifl, NULL, attr, serr)) == ERR)
3520 return retflag;
3521 if (retflag == 0) {
3522 K += direction;
3523 goto next_try;
3524 }
3525 tret[0] = tjd;
3526 if ((backward && tret[0] >= tjd_start - 0.0001)
3527 || (!backward && tret[0] <= tjd_start + 0.0001)) {
3528 K += direction;
3529 goto next_try;
3530 }
3531 /*
3532 * check whether or not eclipse type found is wanted
3533 */
3534 /* non penumbral eclipse is wanted: */
3535 if (!(ifltype & SE_ECL_PENUMBRAL) && (retflag & SE_ECL_PENUMBRAL)) {
3536 K += direction;
3537 goto next_try;
3538 }
3539 /* non partial eclipse is wanted: */
3540 if (!(ifltype & SE_ECL_PARTIAL) && (retflag & SE_ECL_PARTIAL)) {
3541 K += direction;
3542 goto next_try;
3543 }
3544 /* annular-total eclipse will be discovered later */
3545 if (!(ifltype & (SE_ECL_TOTAL)) && (retflag & SE_ECL_TOTAL)) {
3546 K += direction;
3547 goto next_try;
3548 }
3549 /*
3550 * n = 0: times of eclipse begin and end
3551 * n = 1: times of totality begin and end
3552 * n = 2: times of center line begin and end
3553 */
3554 if (retflag & SE_ECL_PENUMBRAL)
3555 o = 0;
3556 else if (retflag & SE_ECL_PARTIAL)
3557 o = 1;
3558 else
3559 o = 2;
3560 dta = twohr;
3561 dtb = tenmin;
3562 for (n = 0; n <= o; n++) {
3563 if (n == 0) {
3564 i1 = 6; i2 = 7;
3565 } else if (n == 1) {
3566 i1 = 2; i2 = 3;
3567 } else if (n == 2) {
3568 i1 = 4; i2 = 5;
3569 }
3570 #if 1
3571 for (i = 0, t = tjd - dta; i <= 2; i += 1, t += dta) {
3572 if ((retflag2 = lun_eclipse_how(t, ifl, attr, dcore, serr)) == ERR)
3573 return retflag2;
3574 if (n == 0)
3575 dc[i] = dcore[2] / 2 + RMOON / dcore[4] - dcore[0];
3576 else if (n == 1)
3577 dc[i] = dcore[1] / 2 + RMOON / dcore[3] - dcore[0];
3578 else if (n == 2)
3579 dc[i] = dcore[1] / 2 - RMOON / dcore[3] - dcore[0];
3580 }
3581 find_zero(dc[0], dc[1], dc[2], dta, &dt1, &dt2);
3582 dtb = (dt1 + dta) / 2;
3583 tret[i1] = tjd + dt1 + dta;
3584 tret[i2] = tjd + dt2 + dta;
3585 #else
3586 tret[i1] = tjd - dtb;
3587 tret[i2] = tjd + dtb;
3588 #endif
3589 for (m = 0, dt = dtb / 2; m < 3; m++, dt /= 2) {
3590 for (j = i1; j <= i2; j += (i2 - i1)) {
3591 for (i = 0, t = tret[j] - dt; i < 2; i++, t += dt) {
3592 if ((retflag2 = lun_eclipse_how(t, ifl, attr, dcore, serr)) == ERR)
3593 return retflag2;
3594 if (n == 0)
3595 dc[i] = dcore[2] / 2 + RMOON / dcore[4] - dcore[0];
3596 else if (n == 1)
3597 dc[i] = dcore[1] / 2 + RMOON / dcore[3] - dcore[0];
3598 else if (n == 2)
3599 dc[i] = dcore[1] / 2 - RMOON / dcore[3] - dcore[0];
3600 }
3601 dt1 = dc[1] / ((dc[1] - dc[0]) / dt);
3602 tret[j] -= dt1;
3603 }
3604 }
3605 }
3606 return retflag;
3607 }
3608
3609 /* When is the next lunar eclipse, observable at a geographic position?
3610 *
3611 * retflag SE_ECL_TOTAL or SE_ECL_PENUMBRAL or SE_ECL_PARTIAL
3612 *
3613 * tret[0] time of maximum eclipse
3614 * tret[1]
3615 * tret[2] time of partial phase begin (indices consistent with solar eclipses)
3616 * tret[3] time of partial phase end
3617 * tret[4] time of totality begin
3618 * tret[5] time of totality end
3619 * tret[6] time of penumbral phase begin
3620 * tret[7] time of penumbral phase end
3621 * tret[8] time of moonrise, if it occurs during the eclipse
3622 * tret[9] time of moonset, if it occurs during the eclipse
3623 *
3624 * attr[0] umbral magnitude at tjd
3625 * attr[1] penumbral magnitude
3626 * attr[4] azimuth of moon at tjd
3627 * attr[5] true altitude of moon above horizon at tjd
3628 * attr[6] apparent altitude of moon above horizon at tjd
3629 * attr[7] distance of moon from opposition in degrees
3630 * attr[8] umbral magnitude at tjd (= attr[0])
3631 * attr[9] saros series number
3632 * attr[10] saros series member number
3633 * declare as attr[20] at least !
3634 */
3635 int32 CALL_CONV swe_lun_eclipse_when_loc(double tjd_start, int32 ifl,
3636 double *geopos, double *tret, double *attr, int32 backward, char *serr)
3637 {
3638 int32 retflag = 0, retflag2 = 0, retc;
3639 double tjdr, tjds, tjd_max = 0;
3640 int i;
3641 if (geopos != NULL && (geopos[2] < SEI_ECL_GEOALT_MIN || geopos[2] > SEI_ECL_GEOALT_MAX)) {
3642 if (serr != NULL)
3643 sprintf(serr, "location for eclipses must be between %.0f and %.0f m above sea", SEI_ECL_GEOALT_MIN, SEI_ECL_GEOALT_MAX);
3644 return ERR;
3645 }
3646 ifl &= ~(SEFLG_JPLHOR | SEFLG_JPLHOR_APPROX);
3647 next_lun_ecl:
3648 if ((retflag = swe_lun_eclipse_when(tjd_start, ifl, 0, tret, backward, serr)) == ERR) {
3649 return ERR;
3650 }
3651 /*
3652 * visibility of eclipse phases
3653 */
3654 retflag = 0;
3655 for (i = 7; i >= 0; i--) {
3656 if (i == 1) continue;
3657 if (tret[i] == 0) continue;
3658 if ((retflag2 = swe_lun_eclipse_how(tret[i], ifl, geopos, attr, serr)) == ERR)
3659 return ERR;
3660 if (attr[6] > 0) { /* moon above horizon, using app. alt. */
3661 retflag |= SE_ECL_VISIBLE;
3662 switch(i) {
3663 case 0: retflag |= SE_ECL_MAX_VISIBLE; break;
3664 case 2: retflag |= SE_ECL_PARTBEG_VISIBLE; break;
3665 case 3: retflag |= SE_ECL_PARTEND_VISIBLE; break;
3666 case 4: retflag |= SE_ECL_TOTBEG_VISIBLE; break;
3667 case 5: retflag |= SE_ECL_TOTEND_VISIBLE; break;
3668 case 6: retflag |= SE_ECL_PENUMBBEG_VISIBLE; break;
3669 case 7: retflag |= SE_ECL_PENUMBEND_VISIBLE; break;
3670 default: break;
3671 }
3672 }
3673 }
3674 if (!(retflag & SE_ECL_VISIBLE)) {
3675 if (backward)
3676 tjd_start = tret[0] - 25;
3677 else
3678 tjd_start = tret[0] + 25;
3679 goto next_lun_ecl;
3680 }
3681 /* moon rise and moon set */
3682 tjd_max = tret[0];
3683 if ((retc = swe_rise_trans(tret[6] - 0.001, SE_MOON, NULL, ifl, SE_CALC_RISE|SE_BIT_DISC_BOTTOM, geopos, 0, 0, &tjdr, serr)) == ERR)
3684 return ERR;
3685 if (retc >= 0 && (retc = swe_rise_trans(tret[6] - 0.001, SE_MOON, NULL, ifl, SE_CALC_SET|SE_BIT_DISC_BOTTOM, geopos, 0, 0, &tjds, serr)) == ERR)
3686 return ERR;
3687 if (retc >= 0) {
3688 if (tjds < tret[6] || (tjds > tjdr && tjdr > tret[7])) {
3689 if (backward)
3690 tjd_start = tret[0] - 25;
3691 else
3692 tjd_start = tret[0] + 25;
3693 goto next_lun_ecl;
3694 }
3695 if (tjdr > tret[6] && tjdr < tret[7]) {
3696 tret[6] = 0;
3697 for (i = 2; i <= 5; i++) {
3698 if (tjdr > tret[i])
3699 tret[i] = 0;
3700 }
3701 tret[8] = tjdr;
3702 if (tjdr > tret[0]) {
3703 tjd_max = tjdr;
3704 }
3705 }
3706 if (tjds > tret[6] && tjds < tret[7]) {
3707 tret[7] = 0;
3708 for (i = 2; i <= 5; i++) {
3709 if (tjds < tret[i])
3710 tret[i] = 0;
3711 }
3712 tret[9] = tjds;
3713 if (tjds < tret[0]) {
3714 tjd_max = tjds;
3715 }
3716 }
3717 }
3718 tret[0] = tjd_max;
3719 if ((retflag2 = swe_lun_eclipse_how(tjd_max, ifl, geopos, attr, serr)) == ERR)
3720 return ERR;
3721 if (retflag2 == 0) {
3722 if (backward)
3723 tjd_start = tret[0] - 25;
3724 else
3725 tjd_start = tret[0] + 25;
3726 goto next_lun_ecl;
3727 }
3728 retflag |= (retflag2 & SE_ECL_ALLTYPES_LUNAR);
3729 return retflag;
3730 }
3731
3732 /*
3733 * function calculates planetary phenomena
3734 *
3735 * attr[0] = phase angle (earth-planet-sun)
3736 * attr[1] = phase (illumined fraction of disc)
3737 * attr[2] = elongation of planet
3738 * attr[3] = apparent diameter of disc
3739 * attr[4] = apparent magnitude
3740 * attr[5] = geocentric horizontal parallax (Moon)
3741 * declare as attr[20] at least !
3742 *
3743 * Note: the lunar magnitude is quite a complicated thing,
3744 * but our algorithm is very simple.
3745 * The phase of the moon, its distance from the earth and
3746 * the sun is considered, but no other factors.
3747 *
3748 */
3749 #define EULER 2.718281828459
3750 #define NMAG_ELEM (SE_VESTA + 1)
3751 #define MAG_HILTON_2005 0
3752 #define MAG_MALLAMA_2018 1
3753 /* Magnitudes according to:
3754 * - "Explanatory Supplement to the Astronomical Almanac" 1986.
3755 * Magnitudes for Mercury and Venus:
3756 * - James L. Hilton, "Improving the Visual Magnitudes of the Planets in
3757 * the Astronomical Almanac, I. Mercury and Venus.", The Astronomical
3758 * Journal, 129:2902-2906, 2005 June
3759 * - James L. Hilton, "Erratum...", The Astronomical Journal,
3760 * 130:2928, 2005 December
3761 * For the ring system of Saturn:
3762 * - Jean Meeus, Astronomical Algorithms, p. 301ff. (German edition 1999(2), p. 329ff.)
3763 * */
3764 static const double mag_elem[NMAG_ELEM][4] = {
3765 /* DTV-Atlas Astronomie, p. 32 */
3766 {-26.86, 0, 0, 0}, /* Sun */
3767 {-12.55, 0, 0, 0}, /* Moon */
3768 /* IAU 1986 */
3769 /* Venus and Mercury values are obsolete, are only kept
3770 * as place holders. Correct handling according to Hilton
3771 * 2005 is seen further below in the code. */
3772 {-0.42, 3.80, -2.73, 2.00}, /* Mercury (obsolete, but don't delete this line!) */
3773 {-4.40, 0.09, 2.39, -0.65}, /* Venus (obsolete, but don't delete this line!) */
3774 {- 1.52, 1.60, 0, 0}, /* Mars */
3775 {- 9.40, 0.5, 0, 0}, /* Jupiter */
3776 {- 8.88, -2.60, 1.25, 0.044}, /* Saturn */
3777 {- 7.19, 0.0, 0, 0}, /* Uranus */
3778 {- 6.87, 0.0, 0, 0}, /* Neptune */
3779 {- 1.00, 0.0, 0, 0}, /* Pluto */
3780 {99, 0, 0, 0}, /* nodes and apogees */
3781 {99, 0, 0, 0},
3782 {99, 0, 0, 0},
3783 {99, 0, 0, 0},
3784 {99, 0, 0, 0}, /* Earth */
3785 /* from Bowell data base */
3786 {6.5, 0.15, 0, 0}, /* Chiron */
3787 {7.0, 0.15, 0, 0}, /* Pholus */
3788 {3.34, 0.12, 0, 0}, /* Ceres */
3789 {4.13, 0.11, 0, 0}, /* Pallas */
3790 {5.33, 0.32, 0, 0}, /* Juno */
3791 {3.20, 0.32, 0, 0}, /* Vesta */
3792 };
3793 int32 CALL_CONV swe_pheno(double tjd, int32 ipl, int32 iflag, double *attr, char *serr)
3794 {
3795 int i;
3796 double xx[6], xx2[6], xxs[6], lbr[6], lbr2[6], dt = 0, dd;
3797 double fac;
3798 double T, in, om, sinB;
3799 double ph1, ph2, me[2];
3800 int32 iflagp, epheflag, retflag, epheflag2;
3801 char serr2[AS_MAXCH];
3802 *serr2 = '\0';
3803 iflag &= ~(SEFLG_JPLHOR | SEFLG_JPLHOR_APPROX);
3804 /* function calls for Pluto with asteroid number 134340
3805 * are treated as calls for Pluto as main body SE_PLUTO */
3806 if (ipl == SE_AST_OFFSET + 134340)
3807 ipl = SE_PLUTO;
3808 for (i = 0; i < 20; i++)
3809 attr[i] = 0;
3810 /* Ceres - Vesta must be SE_CERES etc., not 10001 etc. */
3811 if (ipl > SE_AST_OFFSET && ipl <= SE_AST_OFFSET + 4)
3812 ipl = ipl - SE_AST_OFFSET - 1 + SE_CERES;
3813 iflag = iflag & (SEFLG_EPHMASK |
3814 SEFLG_TRUEPOS |
3815 SEFLG_J2000 |
3816 SEFLG_NONUT |
3817 SEFLG_NOGDEFL |
3818 SEFLG_NOABERR |
3819 SEFLG_TOPOCTR);
3820 iflagp = iflag & (SEFLG_EPHMASK |
3821 SEFLG_TRUEPOS |
3822 SEFLG_J2000 |
3823 SEFLG_NONUT |
3824 SEFLG_NOABERR);
3825 iflagp |= SEFLG_HELCTR;
3826 epheflag = iflag & SEFLG_EPHMASK;
3827 /*
3828 * geocentric planet
3829 */
3830 if ((retflag = swe_calc(tjd, (int) ipl, iflag | SEFLG_XYZ, xx, serr)) == ERR)
3831 /* int cast can be removed when swe_calc() gets int32 ipl definition */
3832 return ERR;
3833 // check epheflag and adjust iflag
3834 epheflag2 = retflag & SEFLG_EPHMASK;
3835 if (epheflag != epheflag2) {
3836 iflag &= ~epheflag;
3837 iflagp &= ~epheflag;
3838 iflag |= epheflag2;
3839 iflagp |= epheflag2;
3840 epheflag = epheflag2;
3841 }
3842 if (swe_calc(tjd, (int) ipl, iflag, lbr, serr) == ERR)
3843 /* int cast can be removed when swe_calc() gets int32 ipl definition */
3844 return ERR;
3845 /* if moon, we need sun as well, for magnitude */
3846 if (ipl == SE_MOON) {
3847 if (swe_calc(tjd, SE_SUN, iflag | SEFLG_XYZ, xxs, serr) == ERR)
3848 return ERR;
3849 }
3850 if (ipl != SE_SUN && ipl != SE_EARTH &&
3851 ipl != SE_MEAN_NODE && ipl != SE_TRUE_NODE &&
3852 ipl != SE_MEAN_APOG && ipl != SE_OSCU_APOG) {
3853 /*
3854 * light time planet - earth
3855 */
3856 dt = lbr[2] * AUNIT / CLIGHT / 86400.0;
3857 if (iflag & SEFLG_TRUEPOS)
3858 dt = 0;
3859 /*
3860 * heliocentric planet at tjd - dt
3861 */
3862 if (swe_calc(tjd - dt, (int) ipl, iflagp | SEFLG_XYZ, xx2, serr) == ERR)
3863 /* int cast can be removed when swe_calc() gets int32 ipl definition */
3864 return ERR;
3865 if (swe_calc(tjd - dt, (int) ipl, iflagp, lbr2, serr) == ERR)
3866 /* int cast can be removed when swe_calc() gets int32 ipl definition */
3867 return ERR;
3868 /*
3869 * phase angle
3870 */
3871 attr[0] = acos(swi_dot_prod_unit(xx, xx2)) * RADTODEG;
3872 /*
3873 * phase
3874 */
3875 attr[1] = (1 + cos(attr[0] * DEGTORAD)) / 2;
3876 }
3877 /*
3878 * apparent diameter of disk
3879 */
3880 if (ipl < NDIAM)
3881 dd = pla_diam[ipl];
3882 else if (ipl > SE_AST_OFFSET)
3883 dd = swed.ast_diam * 1000; /* km -> m */
3884 else
3885 dd = 0;
3886 if (lbr[2] < dd / 2 / AUNIT)
3887 attr[3] = 180; /* assume position on surface of earth */
3888 else
3889 attr[3] = asin(dd / 2 / AUNIT / lbr[2]) * 2 * RADTODEG;
3890 /*
3891 * apparent magnitude
3892 */
3893 if (ipl > SE_AST_OFFSET || (ipl < NMAG_ELEM && mag_elem[ipl][0] < 99)) {
3894 if (ipl == SE_SUN) {
3895 /* ratio apparent diameter : average diameter */
3896 fac = attr[3] / (asin(pla_diam[SE_SUN] / 2.0 / AUNIT) * 2 * RADTODEG);
3897 fac *= fac;
3898 attr[4] = mag_elem[ipl][0] - 2.5 * log10(fac);
3899 } else if (ipl == SE_MOON) {
3900 /* formula according to Allen, C.W., 1976, Astrophysical Quantities */
3901 /*attr[4] = -21.62 + 5 * log10(384410497.8 / EARTH_RADIUS) / log10(10) + 0.026 * fabs(attr[0]) + 0.000000004 * pow(attr[0], 4);*/
3902 //attr[4] = -21.62 + 5 * log10(lbr[2] * AUNIT / EARTH_RADIUS) / log10(10) + 0.026 * fabs(attr[0]) + 0.000000004 * pow(attr[0], 4);
3903 attr[4] = -21.62 + 5 * log10(lbr[2] * AUNIT / EARTH_RADIUS) + 0.026 * fabs(attr[0]) + 0.000000004 * pow(attr[0], 4);
3904 #if 0
3905 /* ratio apparent diameter : average diameter */
3906 fac = attr[3] / (asin(pla_diam[SE_MOON] / 2.0 / 384400000.0) * 2 * RADTODEG);
3907 /* distance sun - moon */
3908 for (i = 0; i < 3; i++)
3909 xxs[i] -= xx[i];
3910 dsm = sqrt(square_sum(xxs));
3911 /* account for phase and distance of moon: */
3912 fac *= fac * attr[1];
3913 /* account for distance of sun from moon: */
3914 fac *= dsm * dsm;
3915 attr[4] = mag_elem[ipl][0] - 2.5 * log10(fac);
3916 #endif
3917 /*printf("1 = %f, 2 = %f\n", mag, mag2);*/
3918 #if MAG_HILTON_2005
3919 } else if (ipl == SE_MERCURY) {
3920 /* valid range is actually 2.1° < i < 169.5° */
3921 double i100 = attr[0] / 100.0;
3922 attr[4] = -0.60 + 4.98 * i100 - 4.88 * i100 * i100 + 3.02 * i100 * i100 * i100;
3923 attr[4] += 5 * log10(lbr2[2] * lbr[2]);
3924 if (attr[0] < 2.1 || attr[0] > 169.5)
3925 sprintf(serr2, "magnitude value for Mercury at phase angle i=%.1f is bad; formula is valid only for 2.1 < i < 169.5", attr[0]);
3926 } else if (ipl == SE_VENUS) {
3927 double i100 = attr[0] / 100.0;
3928 if (attr[0] < 163.6) /* actual valid range is 2.2° < i < 163.6° */
3929 attr[4] = -4.47 + 1.03 * i100 + 0.57 * i100 * i100 + 0.13 * i100 * i100 * i100;
3930 else /* actual valid range is 163.6° < i < 170.2° */
3931 attr[4] = 0.98 - 1.02 * i100;
3932 attr[4] += 5 * log10(lbr2[2] * lbr[2]);
3933 if (attr[0] < 2.2 || attr[0] > 170.2)
3934 sprintf(serr2, "magnitude value for Venus at phase angle i=%.1f is bad; formula is valid only for 2.2 < i < 170.2", attr[0]);
3935 #endif
3936 #if MAG_MALLAMA_2018
3937 // see: A. Mallama, J.Hilton,
3938 // "ComputingApparentPlanetaryMagnitudesforTheAstronomicalAlmanac" (2018)
3939 // https://arxiv.org/ftp/arxiv/papers/1808/1808.01973.pdf
3940 } else if (ipl == SE_MERCURY) {
3941 double a = attr[0];
3942 double a2 = a * a; double a3 = a2 * a; double a4 = a3 * a; double a5 = a4 * a; double a6 = a5 * a;
3943 attr[4] = -0.613 + a * 6.3280E-02 - a2 * 1.6336E-03 + a3 * 3.3644E-05 - a4 * 3.4265E-07 + a5 * 1.6893E-09 - a6 * 3.0334E-12;
3944 attr[4] += 5 * log10(lbr2[2] * lbr[2]);
3945 } else if (ipl == SE_VENUS) {
3946 double a = attr[0];
3947 double a2 = a * a; double a3 = a2 * a; double a4 = a3 * a;
3948 if (a <= 163.7)
3949 attr[4] = -4.384 - a * 1.044E-03 + a2 * 3.687E-04 - a3 * 2.814E-06 + a4 * 8.938E-09;
3950 else
3951 attr[4] = 236.05828 - a * 2.81914E+00 + a2 * 8.39034E-03;
3952 attr[4] += 5 * log10(lbr2[2] * lbr[2]);
3953 if (attr[0] > 179.0)
3954 sprintf(serr2, "magnitude value for Venus at phase angle i=%.1f is bad; formula is valid only for i < 179.0", attr[0]);
3955 } else if (ipl == SE_MARS) {
3956 double a = attr[0];
3957 double a2 = a * a;
3958 /* With the following formulae, the terms +L(λe)+L(LS) have been omitted.
3959 * They are "the magnitude corrections for the longitude of the sub-Earth
3960 * meridian of the illuminated disk and the longitude of the vernal
3961 * equinox,respectively".
3962 * The apparent magnitude of Mars changes considerably within hours,
3963 * depending on the surface that is seen.
3964 * Note that the maximum phase angle of mars as seen from earth is
3965 * about 45°.
3966 * The deviation of this simplified solution from Horizons is
3967 * smaller than 0.1m.
3968 */
3969 if (a <= 50.0)
3970 attr[4] = -1.601 + a * 0.02267 - a2 * 0.0001302;
3971 else // irrelevant to earth-centered observation
3972 attr[4] = -0.367 - a * 0.02573 + a2 * 0.0003445;
3973 attr[4] += 5 * log10(lbr2[2] * lbr[2]);
3974 } else if (ipl == SE_JUPITER) {
3975 /* the phase angle of Jupiter never exceeds 12°. */
3976 double a = attr[0];
3977 double a2 = a * a;
3978 attr[4] = -9.395 - a * 3.7E-04 + a2 * 6.16E-04;
3979 attr[4] += 5 * log10(lbr2[2] * lbr[2]);
3980 } else if (ipl == SE_SATURN) {
3981 double a = attr[0];
3982 double sinB2;
3983 T = (tjd - dt - J2000) / 36525.0;
3984 in = (28.075216 - 0.012998 * T + 0.000004 * T * T) * DEGTORAD;
3985 om = (169.508470 + 1.394681 * T + 0.000412 * T * T) * DEGTORAD;
3986 // B is "mean tilt of the ring plane to the Earth and Sun (If the Earth
3987 // and Sun are on opposite sides of the ring plane B = 0)" according to Hilton:
3988 // https://syrte.obspm.fr/astro/journees2019/journees_pdf/SessionV_1/HiltonStewart_final.pdf
3989 // Mallama does not provide B. We derive it from
3990 // Meeus, p. 301ff. (German version 329ff.)
3991 // There are small differences from Horizons < 0.02m.
3992 sinB = (sin(in) * cos(lbr[1] * DEGTORAD)
3993 * sin(lbr[0] * DEGTORAD - om)
3994 - cos(in) * sin(lbr[1] * DEGTORAD));
3995 sinB2 = (sin(in) * cos(lbr2[1] * DEGTORAD)
3996 * sin(lbr2[0] * DEGTORAD - om)
3997 - cos(in) * sin(lbr2[1] * DEGTORAD));
3998 sinB = fabs(sin((asin(sinB) + asin(sinB2)) / 2.0));/**/
3999 attr[4] = -8.914 - 1.825 * sinB + 0.026 * a - 0.378 * sinB * pow(2.7182818,-2.25 * a);
4000 attr[4] += 5 * log10(lbr2[2] * lbr[2]);
4001 } else if (ipl == SE_URANUS) {
4002 // This is a simplified solution ignoring the term depending on
4003 // sub-Earth latitude. The difference from Horizons is +-0.03m.
4004 double a = attr[0];
4005 double a2 = a * a;
4006 double fi_ = 0; // sub-Earth latitude in deg; ignored here
4007 attr[4] = -7.110 - 8.4E-04 * fi_ + a * 6.587E-3 + a2 * 1.045E-4;
4008 attr[4] += 5 * log10(lbr2[2] * lbr[2]);
4009 // instead of the term with fi_, we do subtract the 0.05m.
4010 // the remaining error is +-0.03m
4011 attr[4] -= 0.05;
4012 } else if (ipl == SE_NEPTUNE) {
4013 if (tjd < 2444239.5) {
4014 attr[4] = -6.89;
4015 } else if (tjd <= 2451544.5) {
4016 attr[4] = -6.89 - 0.0055 * (tjd - 2444239.5) / 365.25;
4017 // Mallama has 0.0054, but that would make the curve discontinuos
4018 // Nevertheless, JPL Horizons has 0.0054 and the discontinuity
4019 } else {
4020 attr[4] = -7.00;
4021 }
4022 attr[4] += 5 * log10(lbr2[2] * lbr[2]);
4023 #else
4024 } else if (ipl == SE_SATURN) {
4025 double u1, u2, du;
4026 /* rings are considered according to Meeus, p. 301ff. (German version 329ff.) */
4027 T = (tjd - dt - J2000) / 36525.0;
4028 in = (28.075216 - 0.012998 * T + 0.000004 * T * T) * DEGTORAD;
4029 om = (169.508470 + 1.394681 * T + 0.000412 * T * T) * DEGTORAD;
4030 sinB = fabs(sin(in) * cos(lbr[1] * DEGTORAD)
4031 * sin(lbr[0] * DEGTORAD - om)
4032 - cos(in) * sin(lbr[1] * DEGTORAD));
4033 u1 = atan2(sin(in) * tan(lbr2[1] * DEGTORAD)
4034 + cos(in) * sin(lbr2[0] * DEGTORAD - om),
4035 cos(lbr2[0] * DEGTORAD - om)) * RADTODEG;
4036 u2 = atan2(sin(in) * tan(lbr[1] * DEGTORAD)
4037 + cos(in) * sin(lbr[0] * DEGTORAD - om),
4038 cos(lbr[0] * DEGTORAD - om)) * RADTODEG;
4039 du = swe_degnorm(u1 - u2);
4040 if (du > 10)
4041 du = 360 - du;
4042 attr[4] = 5 * log10(lbr2[2] * lbr[2])
4043 + mag_elem[ipl][1] * sinB
4044 + mag_elem[ipl][2] * sinB * sinB
4045 + mag_elem[ipl][3] * du
4046 + mag_elem[ipl][0];
4047 #endif
4048 } else if (ipl < SE_CHIRON) {
4049 attr[4] = 5 * log10(lbr2[2] * lbr[2])
4050 + mag_elem[ipl][1] * attr[0] /100.0
4051 + mag_elem[ipl][2] * attr[0] * attr[0] / 10000.0
4052 + mag_elem[ipl][3] * attr[0] * attr[0] * attr[0] / 1000000.0
4053 + mag_elem[ipl][0];
4054 } else if (ipl < NMAG_ELEM || ipl > SE_AST_OFFSET) { /* other planets, asteroids */
4055 ph1 = pow(EULER, -3.33 * pow(tan(attr[0] * DEGTORAD / 2), 0.63));
4056 ph2 = pow(EULER, -1.87 * pow(tan(attr[0] * DEGTORAD / 2), 1.22));
4057 if (ipl < NMAG_ELEM) { /* other planets, main asteroids */
4058 me[0] = mag_elem[ipl][0];
4059 me[1] = mag_elem[ipl][1];
4060 } else if (ipl == SE_AST_OFFSET + 1566) {
4061 /* Icarus has elements from JPL database */
4062 me[0] = 16.9;
4063 me[1] = 0.15;
4064 } else { /* other asteroids */
4065 me[0] = swed.ast_H;
4066 me[1] = swed.ast_G;
4067 }
4068 attr[4] = 5 * log10(lbr2[2] * lbr[2])
4069 + me[0]
4070 - 2.5 * log10((1 - me[1]) * ph1 + me[1] * ph2);
4071 } else { /* ficticious bodies */
4072 attr[4] = 0;
4073 }
4074 }
4075 if (ipl != SE_SUN && ipl != SE_EARTH) {
4076 /*
4077 * elongation of planet
4078 */
4079 if (swe_calc(tjd, SE_SUN, iflag | SEFLG_XYZ, xx2, serr) == ERR)
4080 return ERR;
4081 if (swe_calc(tjd, SE_SUN, iflag, lbr2, serr) == ERR)
4082 return ERR;
4083 attr[2] = acos(swi_dot_prod_unit(xx, xx2)) * RADTODEG;
4084 }
4085 /* horizontal parallax */
4086 if (ipl == SE_MOON) {
4087 double sinhp, xm[6];
4088 /* geocentric horizontal parallax */
4089 /* Expl.Suppl. to the AA 1984, p.400 */
4090 if (swe_calc(tjd, (int) ipl, epheflag|SEFLG_TRUEPOS|SEFLG_EQUATORIAL|SEFLG_RADIANS, xm, serr) == ERR)
4091 /* int cast can be removed when swe_calc() gets int32 ipl definition */
4092 return ERR;
4093 sinhp = EARTH_RADIUS / xm[2] / AUNIT;
4094 attr[5] = asin(sinhp) / DEGTORAD;
4095 /* topocentric horizontal parallax */
4096 if (iflag & SEFLG_TOPOCTR) {
4097 if (swe_calc(tjd, (int) ipl, epheflag|SEFLG_XYZ|SEFLG_TOPOCTR, xm, serr) == ERR)
4098 return ERR;
4099 if (swe_calc(tjd, (int) ipl, epheflag|SEFLG_XYZ, xx, serr) == ERR)
4100 return ERR;
4101 attr[5] = acos(swi_dot_prod_unit(xm, xx)) / DEGTORAD;
4102 #if 0
4103 {
4104 /* Expl. Suppl. to the Astronomical Almanac 1984, p. 400;
4105 * Does not take into account
4106 * - the topocentric distance of the moon
4107 * - the distance of the observer from the geocenter
4108 */
4109 double tsid, h, e, f = EARTH_OBLATENESS;
4110 double cosz, sinz, phi;
4111 /* local apparent sidereal time */
4112 tsid = swe_sidtime(tjd - swe_deltat_ex(tjd, iflag, serr)) * 15 + swed.topd.geolon;
4113 /* local hour angle of the moon */
4114 h = swe_degnorm(tsid - xm[0] / DEGTORAD);
4115 /* geocentric latitude of the observer */
4116 e = sqrt(f * (2 - f));
4117 phi = atan((1 - e * e) * tan(swed.topd.geolat * DEGTORAD));
4118 /* sine of geocentric zenith angle of moon */
4119 cosz = sin(xm[1]) * sin(phi) + cos(xm[1]) * cos(phi) * cos(h * DEGTORAD);
4120 sinz = sqrt(1 - cosz * cosz);
4121 attr[5] = asin(sinz * sinhp / (1 - sinz * sinhp)) / DEGTORAD;
4122 }
4123 #endif
4124 }
4125 }
4126 if (*serr2 != '\0' && serr != NULL)
4127 strcpy(serr, serr2);
4128 return iflag;
4129 }
4130
4131 int32 CALL_CONV swe_pheno_ut(double tjd_ut, int32 ipl, int32 iflag, double *attr, char *serr)
4132 {
4133 double deltat;
4134 int32 retflag = OK;
4135 int32 epheflag = iflag & SEFLG_EPHMASK;
4136 if (epheflag == 0) {
4137 epheflag = SEFLG_SWIEPH;
4138 iflag |= SEFLG_SWIEPH;
4139 }
4140 deltat = swe_deltat_ex(tjd_ut, iflag, serr);
4141 retflag = swe_pheno(tjd_ut + deltat, ipl, iflag, attr, serr);
4142 /* if ephe required is not ephe returned, adjust delta t: */
4143 if ((retflag & SEFLG_EPHMASK) != epheflag) {
4144 deltat = swe_deltat_ex(tjd_ut, retflag, serr);
4145 retflag = swe_pheno(tjd_ut + deltat, ipl, iflag, attr, serr);
4146 }
4147 return retflag;
4148 }
4149
4150 static int find_maximum(double y00, double y11, double y2, double dx,
4151 double *dxret, double *yret)
4152 {
4153 double a, b, c, x, y;
4154 c = y11;
4155 b = (y2 - y00) / 2.0;
4156 a = (y2 + y00) / 2.0 - c;
4157 x = -b / 2 / a;
4158 y = (4 * a * c - b * b) / 4 / a;
4159 *dxret = (x - 1) * dx;
4160 if (yret != NULL)
4161 *yret = y;
4162 return OK;
4163 }
4164
4165 static int find_zero(double y00, double y11, double y2, double dx,
4166 double *dxret, double *dxret2)
4167 {
4168 double a, b, c, x1, x2;
4169 c = y11;
4170 b = (y2 - y00) / 2.0;
4171 a = (y2 + y00) / 2.0 - c;
4172 if (b * b - 4 * a * c < 0)
4173 return ERR;
4174 x1 = (-b + sqrt(b * b - 4 * a * c)) / 2 / a;
4175 x2 = (-b - sqrt(b * b - 4 * a * c)) / 2 / a;
4176 *dxret = (x1 - 1) * dx;
4177 *dxret2 = (x2 - 1) * dx;
4178 return OK;
4179 }
4180
4181 double rdi_twilight(int32 rsmi)
4182 {
4183 double rdi = 0;
4184 if (rsmi & SE_BIT_CIVIL_TWILIGHT)
4185 rdi = 6;
4186 if (rsmi & SE_BIT_NAUTIC_TWILIGHT)
4187 rdi = 12;
4188 if (rsmi & SE_BIT_ASTRO_TWILIGHT)
4189 rdi = 18;
4190 return rdi;
4191 }
4192
4193 static double get_sun_rad_plus_refr(int32 ipl, double dd, int32 rsmi, double refr)
4194 {
4195 double rdi = 0;
4196 if (rsmi & SE_BIT_FIXED_DISC_SIZE) {
4197 if (ipl == SE_SUN)
4198 dd = 1.0;
4199 else if (ipl == SE_MOON)
4200 dd = 0.00257;
4201 }
4202 /* apparent radius of disc */
4203 if (!(rsmi & SE_BIT_DISC_CENTER))
4204 rdi = asin( pla_diam[ipl] / 2.0 / AUNIT / dd) * RADTODEG;
4205 if (rsmi & SE_BIT_DISC_BOTTOM)
4206 rdi = -rdi;
4207 if (!(rsmi & SE_BIT_NO_REFRACTION)) {
4208 rdi += refr; // (34.5 / 60.0);
4209 }
4210 return rdi;
4211 }
4212
4213 /* Simple fast algorithm for risings and settings of
4214 * - planets Sun, Moon, Mercury - Pluto + Lunar Nodes and Fixed stars
4215 * Does not work well for geographic latitudes
4216 * > 65 N/S for the Sun
4217 * > 60 N/S for the Moon and the planets
4218 * and is called only for latitudes smaller than this.
4219 */
4220 static int32 rise_set_fast(
4221 double tjd_ut, int32 ipl,
4222 int32 epheflag, int32 rsmi,
4223 double *dgeo,
4224 double atpress, double attemp,
4225 double *tret,
4226 char *serr)
4227 {
4228 int i;
4229 double xx[6], xaz[6], xaz2[6];
4230 double dd, dt, refr;
4231 double dtsum = 0;
4232 int32 iflag = epheflag & (SEFLG_JPLEPH|SEFLG_SWIEPH|SEFLG_MOSEPH);
4233 int32 iflagtopo = iflag | SEFLG_EQUATORIAL;
4234 double sda, armc, md, dmd, mdrise, rdi, tr, dalt;
4235 double decl;
4236 double tjd_ut0 = tjd_ut;
4237 int32 facrise = 1;
4238 int32 tohor_flag = SE_EQU2HOR;
4239 AS_BOOL is_second_run = FALSE;
4240 int nloop = 2;
4241 *tret = 0;
4242 if (ipl == SE_MOON)
4243 nloop = 4;
4244 if (rsmi & SE_CALC_SET)
4245 facrise = -1;
4246 if (!(rsmi & SE_BIT_GEOCTR_NO_ECL_LAT)) {
4247 iflagtopo |= SEFLG_TOPOCTR;
4248 swe_set_topo(dgeo[0], dgeo[1], dgeo[2]);
4249 }
4250 run_rise_again:
4251 if (swe_calc_ut(tjd_ut, ipl, iflagtopo, xx, serr) == ERR)
4252 return ERR;
4253 /* the diurnal arc is a bit fuzzy,
4254 * - because the object changes declination during the day
4255 * - because there is refraction of light
4256 * nevertheless this works well as soon as the object is not
4257 * circumpolar or near-circumpolar
4258 */
4259 decl = xx[1];
4260 // semi-diurnal arcs
4261 sda = -tan(dgeo[1] * DEGTORAD) * tan(decl * DEGTORAD);
4262 if (sda >= 1) {
4263 sda = 10; // actually sda = 0°, but we give it a value of 10°
4264 // to account for refraction. value 0 would cause
4265 // problems
4266 } else if (sda <= -1) {
4267 sda = 180;
4268 } else {
4269 sda = acos(sda) * RADTODEG;
4270 }
4271 // sidereal time at tjd_start
4272 armc = swe_degnorm(swe_sidtime(tjd_ut) * 15 + dgeo[0]);
4273 // meridian distance of object
4274 md = swe_degnorm(xx[0] - armc);
4275 mdrise = swe_degnorm(sda * facrise);
4276 //dmd = swe_degnorm(md - mdrise - 1);
4277 dmd = swe_degnorm(md - mdrise);
4278 // Avoid the risk of getting the event of next day:
4279 #if 0
4280 if (dmd > 358) {
4281 tjd_ut -= 0.1;
4282 goto run_rise_again;
4283 }
4284 #else
4285 if (dmd > 358) {
4286 dmd -= 360;
4287 }
4288 #endif
4289 // rough subsequent rising/setting time
4290 tr = tjd_ut + dmd / 360;
4291 /* if object is sun or moon and rising of upper limb is required,
4292 * calculate apparent radius of disk (ignoring refraction);
4293 * with other objects disk diameter is ignored. */
4294 rdi = 0;
4295 /* true altitude of sun, when it appears at the horizon;
4296 * refraction for a body visible at the horizon at 0m above sea,
4297 */
4298 if (atpress == 0) {
4299 /* estimate atmospheric pressure */
4300 atpress = 1013.25 * pow(1 - 0.0065 * dgeo[2] / 288, 5.255);
4301 }
4302 swe_refrac_extended(0.000001, 0, atpress, attemp, const_lapse_rate, SE_APP_TO_TRUE, xx);
4303 refr = xx[1] - xx[0];
4304 //fprintf(stderr, "refr=%f, %f, %f\n", refr, xx[0], xx[1]);
4305 if (rsmi & SE_BIT_GEOCTR_NO_ECL_LAT) {
4306 tohor_flag = SE_ECL2HOR;
4307 iflagtopo = iflag;
4308 } else {
4309 tohor_flag = SE_EQU2HOR; // this is more efficient
4310 iflagtopo = iflag | SEFLG_EQUATORIAL;
4311 iflagtopo |= SEFLG_TOPOCTR;
4312 swe_set_topo(dgeo[0], dgeo[1], dgeo[2]);
4313 }
4314 for (i = 0; i < nloop; i++) {
4315 if (swe_calc_ut(tr, ipl, iflagtopo, xx, serr) == ERR)
4316 return ERR;
4317 if (rsmi & SE_BIT_GEOCTR_NO_ECL_LAT)
4318 xx[1] = 0;
4319 rdi = get_sun_rad_plus_refr(ipl, xx[2], rsmi, refr);
4320 swe_azalt(tr, tohor_flag, dgeo, atpress, attemp, xx, xaz);
4321 swe_azalt(tr + 0.001, tohor_flag, dgeo, atpress, attemp, xx, xaz2);
4322 dd = (xaz2[1] - xaz[1]);
4323 dalt = xaz[1] + rdi;
4324 dt = dalt / dd / 1000.0;
4325 if (dt > 0.1) dt = 0.1;
4326 else if (dt < -0.1) dt = -0.1;
4327 dtsum += dt;
4328 if ((0) && fabs(dt) > 5.0 / 86400.0 && nloop < 20)
4329 nloop++;
4330 tr -= dt;
4331 }
4332 //fprintf(stderr, "tr-tjd=%f tin=%f tout=%f\n", tr - tjd_ut0, tjd_ut0, tr);
4333 // if the event found is before input time, we search next event.
4334 if (tr < tjd_ut0 && !is_second_run) {
4335 tjd_ut += 0.5;
4336 is_second_run = TRUE;
4337 goto run_rise_again;
4338 }
4339 *tret = tr;
4340 return OK;
4341 }
4342
4343 /* rise, set, and meridian transits of sun, moon, planets, and stars
4344 *
4345 * tjd_ut universal time from when on search ought to start
4346 * ipl planet number, neglected, if starname is given
4347 * starname pointer to string. if a planet, not a star, is
4348 * wanted, starname must be NULL or ""
4349 * epheflag used for ephemeris only
4350 * rsmi SE_CALC_RISE, SE_CALC_SET, SE_CALC_MTRANSIT, SE_CALC_ITRANSIT
4351 * | SE_BIT_DISC_CENTER for rises of disc center of body
4352 * | SE_BIT_DISC_BOTTOM for rises of disc bottom of body
4353 * | SE_BIT_GEOCTR_NO_ECL_LAT use geocentric position of object
4354 * and ignore ecliptic latitude
4355 * | SE_BIT_NO_REFRACTION to neglect refraction
4356 * | SE_BIT_CIVIL_TWILIGHT calculate civil twilight
4357 * | SE_BIT_NAUTIC_TWILIGHT calculate nautical twilight
4358 * | SE_BIT_ASTRO_TWILIGHT calculate astronomical twilight
4359 * | SE_BIT_FIXED_DISC_SIZE neglect the effect of distance on disc size
4360 * | SE_BIT_HINDU_RISING risings according to Hindu astrology
4361 * = (SE_BIT_DISC_CENTER|SE_BIT_NO_REFRACTION|SE_BIT_GEOCTR_NO_ECL_LAT)
4362 * geopos array of doubles for geogr. long., lat. and height above sea
4363 * atpress atmospheric pressure
4364 * attemp atmospheric temperature
4365 *
4366 * return variables:
4367 * tret time of rise, set, meridian transits
4368 * serr[256] error string
4369 * function return value -2 means that the body does not rise or set */
4370 #define SEFLG_EPHMASK (SEFLG_JPLEPH|SEFLG_SWIEPH|SEFLG_MOSEPH)
4371 int32 CALL_CONV swe_rise_trans(
4372 double tjd_ut, int32 ipl, char *starname,
4373 int32 epheflag, int32 rsmi,
4374 double *geopos,
4375 double atpress, double attemp,
4376 double *tret,
4377 char *serr)
4378 {
4379 int32 retval = 0;
4380 /* Simple fast algorithm for risings and settings of
4381 * - planets Sun, Moon, Mercury - Pluto + Lunar Nodes
4382 * Does not work well for geographic latitudes
4383 * > 65 N/S for the Sun
4384 * > 60 N/S for the Moon and the planets
4385 * Beyond these limits, some risings or settings may be missed.
4386 */
4387 AS_BOOL do_fixstar = (starname != NULL && *starname != '\0');
4388 if (!do_fixstar
4389 && (rsmi & (SE_CALC_RISE|SE_CALC_SET))
4390 && !(rsmi & SE_BIT_FORCE_SLOW_METHOD)
4391 && !(rsmi & (SE_BIT_CIVIL_TWILIGHT|SE_BIT_NAUTIC_TWILIGHT|SE_BIT_ASTRO_TWILIGHT))
4392 && (ipl >= SE_SUN && ipl <= SE_TRUE_NODE)
4393 && (fabs(geopos[1]) <= 60 || (ipl == SE_SUN && fabs(geopos[1]) <= 65))
4394 ) {
4395 retval = rise_set_fast(tjd_ut, ipl, epheflag, rsmi, geopos, atpress, attemp, tret, serr);
4396 return retval;
4397 }
4398 return swe_rise_trans_true_hor(tjd_ut, ipl, starname, epheflag, rsmi, geopos, atpress, attemp, 0, tret, serr);
4399 }
4400
4401 /* same as swe_rise_trans(), but allows to define the height of the horizon
4402 * at the point of the rising or setting (horhgt) */
4403 int32 CALL_CONV swe_rise_trans_true_hor(
4404 double tjd_ut, int32 ipl, char *starname,
4405 int32 epheflag, int32 rsmi,
4406 double *geopos,
4407 double atpress, double attemp,
4408 double horhgt,
4409 double *tret,
4410 char *serr)
4411 {
4412 int i, j, k, ii, calc_culm, nculm = -1;
4413 double tjd_et = tjd_ut + swe_deltat_ex(tjd_ut, epheflag, serr);
4414 double xc[6], xh[20][6], ah[6], aha;
4415 double tculm[4], tcu, tc[20], h[20], t2[6], dc[6], dtint, dx, rdi, dd = 0;
4416 int32 iflag = epheflag;
4417 int jmax = 14;
4418 double t, te, tt, dt, twohrs = 1.0 / 12.0;
4419 double curdist;
4420 int32 tohor_flag = SE_EQU2HOR;
4421 int nazalt = 0;
4422 int ncalc = 0;
4423 AS_BOOL do_fixstar = (starname != NULL && *starname != '\0');
4424 if (geopos[2] < SEI_ECL_GEOALT_MIN || geopos[2] > SEI_ECL_GEOALT_MAX) {
4425 if (serr != NULL)
4426 sprintf(serr, "location for swe_rise_trans() must be between %.0f and %.0f m above sea", SEI_ECL_GEOALT_MIN, SEI_ECL_GEOALT_MAX);
4427 return ERR;
4428 }
4429 /*swi_set_tid_acc(tjd_ut, epheflag, 0, serr);*/
4430 /* function calls for Pluto with asteroid number 134340
4431 * are treated as calls for Pluto as main body SE_PLUTO */
4432 if (ipl == SE_AST_OFFSET + 134340)
4433 ipl = SE_PLUTO;
4434 xh[0][0] = 0; /* to shut up mint */
4435 /* allowing SEFLG_NONUT and SEFLG_TRUEPOS speeds it up */
4436 iflag &= (SEFLG_EPHMASK | SEFLG_NONUT | SEFLG_TRUEPOS);
4437 *tret = 0;
4438 if (rsmi & SE_BIT_GEOCTR_NO_ECL_LAT) {
4439 tohor_flag = SE_ECL2HOR;
4440 } else {
4441 tohor_flag = SE_EQU2HOR;
4442 iflag |= SEFLG_EQUATORIAL;
4443 iflag |= SEFLG_TOPOCTR;
4444 swe_set_topo(geopos[0], geopos[1], geopos[2]);
4445 }
4446 if (rsmi & (SE_CALC_MTRANSIT | SE_CALC_ITRANSIT))
4447 return calc_mer_trans(tjd_ut, ipl, epheflag, rsmi,
4448 geopos, starname, tret, serr);
4449 if (!(rsmi & (SE_CALC_RISE | SE_CALC_SET)))
4450 rsmi |= SE_CALC_RISE;
4451 /* twilight calculation */
4452 if (ipl == SE_SUN && (rsmi & (SE_BIT_CIVIL_TWILIGHT|SE_BIT_NAUTIC_TWILIGHT|SE_BIT_ASTRO_TWILIGHT))) {
4453 rsmi |= (SE_BIT_NO_REFRACTION | SE_BIT_DISC_CENTER);
4454 horhgt = -rdi_twilight(rsmi);
4455 /* note: twilight is not dependent on height of horizon, so we can
4456 * use this parameter and define a fictitious height of horizon */
4457 }
4458 /* find culmination points within 28 hours from t0 - twohrs.
4459 * culminations are required in case there are maxima or minima
4460 * in height slightly above or below the horizon.
4461 * we do not use meridian transits, because in polar regions
4462 * the culmination points may considerably deviate from
4463 * transits. also, there are cases where the moon rises in the
4464 * western half of the sky for a short time.
4465 */
4466 if (do_fixstar) {
4467 if (swe_fixstar(starname, tjd_et, iflag, xc, serr) == ERR)
4468 return ERR;
4469 }
4470 for (ii = 0, t = tjd_ut - twohrs; ii <= jmax; ii++, t += twohrs) {
4471 tc[ii] = t;
4472 if (!do_fixstar) {
4473 te = t + swe_deltat_ex(t, epheflag, serr);
4474 if (swe_calc(te, ipl, iflag, xc, serr) == ERR)
4475 return ERR;
4476 ncalc++;
4477 }
4478 if (rsmi & SE_BIT_GEOCTR_NO_ECL_LAT)
4479 xc[1] = 0;
4480 /* diameter of object in km */
4481 if (ii == 0) {
4482 if (do_fixstar)
4483 dd = 0;
4484 else if (rsmi & SE_BIT_DISC_CENTER)
4485 dd = 0;
4486 else if (ipl < NDIAM)
4487 dd = pla_diam[ipl];
4488 else if (ipl > SE_AST_OFFSET)
4489 dd = swed.ast_diam * 1000; /* km -> m */
4490 else
4491 dd = 0;
4492 }
4493 curdist = xc[2];
4494 if (rsmi & SE_BIT_FIXED_DISC_SIZE) {
4495 if (ipl == SE_SUN) {
4496 curdist = 1.0;
4497 } else if (ipl == SE_MOON) {
4498 curdist = 0.00257;
4499 }
4500 }
4501 /* apparent radius of disc */
4502 rdi = asin( dd / 2 / AUNIT / curdist ) * RADTODEG;
4503 /* true height of center of body */
4504 swe_azalt(t, tohor_flag, geopos, atpress, attemp, xc, xh[ii]);
4505 nazalt++;
4506 if (rsmi & SE_BIT_DISC_BOTTOM) {
4507 /* true height of bottom point of body */
4508 xh[ii][1] -= rdi;
4509 } else {
4510 /* true height of uppermost point of body */
4511 xh[ii][1] += rdi;
4512 }
4513 /* apparent height of uppermost point of body */
4514 if (rsmi & SE_BIT_NO_REFRACTION) {
4515 xh[ii][1] -= horhgt;
4516 h[ii] = xh[ii][1];
4517 } else {
4518 swe_azalt_rev(t, SE_HOR2EQU, geopos, xh[ii], xc);
4519 nazalt++;
4520 swe_azalt(t, SE_EQU2HOR, geopos, atpress, attemp, xc, xh[ii]);
4521 nazalt++;
4522 xh[ii][1] -= horhgt;
4523 xh[ii][2] -= horhgt;
4524 h[ii] = xh[ii][2];
4525 }
4526 calc_culm = 0;
4527 if (ii > 1) {
4528 dc[0] = xh[ii-2][1];
4529 dc[1] = xh[ii-1][1];
4530 dc[2] = xh[ii][1];
4531 if (dc[1] > dc[0] && dc[1] > dc[2])
4532 calc_culm = 1;
4533 if (dc[1] < dc[0] && dc[1] < dc[2])
4534 calc_culm = 2;
4535 }
4536 if (calc_culm) {
4537 dt = twohrs;
4538 tcu = t - dt;
4539 find_maximum(dc[0], dc[1], dc[2], dt, &dtint, &dx);
4540 tcu += dtint + dt;
4541 dt /= 3;
4542 for (; dt > 0.0001; dt /= 3) {
4543 for (i = 0, tt = tcu - dt; i < 3; tt += dt, i++) {
4544 te = tt + swe_deltat_ex(tt, epheflag, serr);
4545 if (!do_fixstar)
4546 if (swe_calc(te, ipl, iflag, xc, serr) == ERR)
4547 return ERR;
4548 if (rsmi & SE_BIT_GEOCTR_NO_ECL_LAT)
4549 xc[1] = 0;
4550 ncalc++;
4551 swe_azalt(tt, tohor_flag, geopos, atpress, attemp, xc, ah);
4552 nazalt++;
4553 ah[1] -= horhgt;
4554 dc[i] = ah[1];
4555 }
4556 find_maximum(dc[0], dc[1], dc[2], dt, &dtint, &dx);
4557 tcu += dtint + dt;
4558 }
4559 nculm++;
4560 tculm[nculm] = tcu;
4561 }
4562 }
4563 /* note: there can be a rise or set on the poles, even if
4564 * there is no culmination. So, we must not leave here
4565 * in any case. */
4566 /* insert culminations into array of heights */
4567 for (i = 0; i <= nculm; i++) {
4568 for (j = 1; j <= jmax; j++) {
4569 if (tculm[i] < tc[j]) {
4570 for (k = jmax; k >= j; k--) {
4571 tc[k+1] = tc[k];
4572 h[k+1] = h[k];
4573 }
4574 tc[j] = tculm[i];
4575 if (!do_fixstar) {
4576 te = tc[j] + swe_deltat_ex(tc[j], epheflag, serr);
4577 if (swe_calc(te, ipl, iflag, xc, serr) == ERR)
4578 return ERR;
4579 if (rsmi & SE_BIT_GEOCTR_NO_ECL_LAT)
4580 xc[1] = 0;
4581 ncalc++;
4582 }
4583 curdist = xc[2];
4584 if (rsmi & SE_BIT_FIXED_DISC_SIZE) {
4585 if ( ipl == SE_SUN ) {
4586 curdist = 1.0;
4587 } else if (ipl == SE_MOON) {
4588 curdist = 0.00257;
4589 }
4590 }
4591 /* apparent radius of disc */
4592 rdi = asin( dd / 2 / AUNIT / curdist ) * RADTODEG;
4593 /* true height of center of body */
4594 swe_azalt(tc[j], tohor_flag, geopos, atpress, attemp, xc, ah);
4595 nazalt++;
4596 if (rsmi & SE_BIT_DISC_BOTTOM) {
4597 /* true height of bottom point of body */
4598 ah[1] -= rdi;
4599 } else {
4600 /* true height of uppermost point of body */
4601 ah[1] += rdi;
4602 }
4603 /* apparent height of uppermost point of body */
4604 if (rsmi & SE_BIT_NO_REFRACTION) {
4605 ah[1] -= horhgt;
4606 h[j] = ah[1];
4607 } else {
4608 swe_azalt_rev(tc[j], SE_HOR2EQU, geopos, ah, xc);
4609 nazalt++;
4610 swe_azalt(tc[j], SE_EQU2HOR, geopos, atpress, attemp, xc, ah);
4611 nazalt++;
4612 ah[1] -= horhgt;
4613 ah[2] -= horhgt;
4614 h[j] = ah[2];
4615 }
4616 jmax++;
4617 break;
4618 }
4619 }
4620 }
4621 *tret = 0;
4622 /* find points with zero height.
4623 * binary search */
4624 for (ii = 1; ii <= jmax; ii++) {
4625 if (h[ii-1] * h[ii] >= 0)
4626 continue;
4627 if (h[ii-1] < h[ii] && !(rsmi & SE_CALC_RISE))
4628 continue;
4629 if (h[ii-1] > h[ii] && !(rsmi & SE_CALC_SET))
4630 continue;
4631 dc[0] = h[ii-1];
4632 dc[1] = h[ii];
4633 t2[0] = tc[ii-1];
4634 t2[1] = tc[ii];
4635 for (i = 0; i < 20; i++) {
4636 t = (t2[0] + t2[1]) / 2;
4637 if (!do_fixstar) {
4638 te = t + swe_deltat_ex(t, epheflag, serr);
4639 if (swe_calc(te, ipl, iflag, xc, serr) == ERR)
4640 return ERR;
4641 if (rsmi & SE_BIT_GEOCTR_NO_ECL_LAT)
4642 xc[1] = 0;
4643 ncalc++;
4644 }
4645 curdist = xc[2];
4646 if (rsmi & SE_BIT_FIXED_DISC_SIZE) {
4647 if (ipl == SE_SUN) {
4648 curdist = 1.0;
4649 } else if (ipl == SE_MOON) {
4650 curdist = 0.00257;
4651 }
4652 }
4653 /* apparent radius of disc */
4654 rdi = asin( dd / 2 / AUNIT / curdist ) * RADTODEG;
4655 /* true height of center of body */
4656 swe_azalt(t, tohor_flag, geopos, atpress, attemp, xc, ah);
4657 nazalt++;
4658 if (rsmi & SE_BIT_DISC_BOTTOM) {
4659 /* true height of bottom point of body */
4660 ah[1] -= rdi;
4661 } else {
4662 /* true height of uppermost point of body */
4663 ah[1] += rdi;
4664 }
4665 /* apparent height of uppermost point of body */
4666 if (rsmi & SE_BIT_NO_REFRACTION) {
4667 ah[1] -= horhgt;
4668 aha = ah[1];
4669 } else {
4670 swe_azalt_rev(t, SE_HOR2EQU, geopos, ah, xc);
4671 nazalt++;
4672 swe_azalt(t, SE_EQU2HOR, geopos, atpress, attemp, xc, ah);
4673 nazalt++;
4674 ah[1] -= horhgt;
4675 ah[2] -= horhgt;
4676 aha = ah[2];
4677 }
4678 if (aha * dc[0] <= 0) {
4679 dc[1] = aha;
4680 t2[1] = t;
4681 } else {
4682 dc[0] = aha;
4683 t2[0] = t;
4684 }
4685 }
4686 if (t > tjd_ut) {
4687 *tret = t;
4688 // fprintf(stderr, "nazalt=%d\n", nazalt);
4689 // fprintf(stderr, "ncalc=%d\n", ncalc);
4690 return OK;
4691 }
4692 }
4693 if (serr)
4694 sprintf(serr, "rise or set not found for planet %d", ipl);
4695 return -2; /* no t of rise or set found */
4696 }
4697
4698 static int32 calc_mer_trans(
4699 double tjd_ut, int32 ipl, int32 epheflag, int32 rsmi,
4700 double *geopos,
4701 char *starname,
4702 double *tret,
4703 char *serr)
4704 {
4705 int i;
4706 double tjd_et = tjd_ut + swe_deltat_ex(tjd_ut, epheflag, serr);
4707 double armc, armc0, arxc, x0[6], x[6], t, te;
4708 double mdd;
4709 int32 iflag = epheflag;
4710 AS_BOOL do_fixstar = (starname != NULL && *starname != '\0');
4711 iflag &= SEFLG_EPHMASK;
4712 *tret = 0;
4713 iflag |= (SEFLG_EQUATORIAL | SEFLG_TOPOCTR);
4714 armc0 = swe_sidtime(tjd_ut) + geopos[0] / 15;
4715 if (armc0 >= 24)
4716 armc0 -= 24;
4717 if (armc0 < 0)
4718 armc0 += 24;
4719 armc0 *= 15;
4720 if (do_fixstar) {
4721 if (swe_fixstar(starname, tjd_et, iflag, x0, serr) == ERR)
4722 return ERR;
4723 } else {
4724 if (swe_calc(tjd_et, ipl, iflag, x0, serr) == ERR)
4725 return ERR;
4726 }
4727 /*
4728 * meridian transits
4729 */
4730 x[0] = x0[0];
4731 x[1] = x0[1];
4732 t = tjd_ut;
4733 arxc = armc0;
4734 if (rsmi & SE_CALC_ITRANSIT)
4735 arxc = swe_degnorm(arxc + 180);
4736 for (i = 0; i < 4; i++) {
4737 mdd = swe_degnorm(x[0] - arxc);
4738 if (i > 0 && mdd > 180)
4739 mdd -= 360;
4740 t += mdd / 361;
4741 armc = swe_sidtime(t) + geopos[0] / 15;
4742 if (armc >= 24)
4743 armc -= 24;
4744 if (armc < 0)
4745 armc += 24;
4746 armc *= 15;
4747 arxc = armc;
4748 if (rsmi & SE_CALC_ITRANSIT)
4749 arxc = swe_degnorm(arxc + 180);
4750 if (!do_fixstar) {
4751 te = t + swe_deltat_ex(t, epheflag, serr);
4752 if (swe_calc(te, ipl, iflag, x, serr) == ERR)
4753 return ERR;
4754 }
4755 }
4756 *tret = t;
4757 return OK;
4758 }
4759
4760 /*
4761 Nodes and apsides of planets and moon
4762
4763 Planetary nodes can be defined in three different ways:
4764 a) They can be understood as a direction or as an axis
4765 defined by the intersection line of two orbital planes.
4766 E.g., the nodes of Mars are defined by the intersection
4767 line of Mars' orbital plane with the ecliptic (= the
4768 Earths orbit heliocentrically or the solar orbit
4769 geocentrically). However, as Michael Erlewine points
4770 out in his elaborate web page on this topic
4771 (http://thenewage.com/resources/articles/interface.html),
4772 planetary nodes can be defined for any couple of
4773 planets. E.g. there is also an intersection line for the
4774 two orbital planes of Mars and Saturn.
4775 Because such lines are, in principle, infinite, the
4776 heliocentric and the geocentric positions of the
4777 planetary nodes will be the same. There are astrologers
4778 that use such heliocentric planetary nodes in geocentric
4779 charts.
4780 The ascending and the descending node will, in this
4781 case, be in precise opposition.
4782
4783 b) The planetary nodes can also be understood in a
4784 different way, not as an axis, but as the two points on a
4785 planetary orbit that are located precisely on the
4786 intersection line of the two planes.
4787 This second definition makes no difference for the moon or
4788 for heliocentric positions of planets, but it does so for
4789 geocentric positions. There are two possibilities for
4790 geocentric planetary nodes based on this definition.
4791 1) The common solution is that the points on the
4792 planets orbit are transformed to the geocenter. The
4793 two points will not be in opposition anymore, or
4794 they will only roughly be so with the outer planets. The
4795 advantage of these nodes is that when a planet is in
4796 conjunction with its node, then its ecliptic latitude
4797 will be zero. This is not true when a planet is in
4798 geocentric conjunction with its heliocentric node.
4799 (And neither is it always true for the inner planets,
4800 i.e. Mercury and Venus.)
4801 2) The second possibility that nobody seems to have
4802 thought of so far: One may compute the points of
4803 the earth's orbit that lie exactly on another planet's
4804 orbital plane and transform it to the geocenter. The two
4805 points will always be in an approximate square.
4806
4807 c) Third, the planetary nodes could be defined as the
4808 intersection points of the plane defined by their
4809 momentary geocentric position and motion with the
4810 plane of the ecliptic. Such points would move very fast
4811 around the planetary stations. Here again, as in b)1),
4812 the planet would cross the ecliptic and its ecliptic
4813 latitude would be 0 exactly when it were in
4814 conjunction with one of its nodes.
4815
4816 The Swiss Ephemeris supports the solutions a) and b) 1).
4817
4818 Possible definitions for apsides
4819
4820 a) The planetary apsides can be defined as the perihelion and
4821 aphelion points on a planetary orbit. For a
4822 geocentric chart, these points could be transformed
4823 from the heliocenter to the geocenter.
4824 b) However, one might consider these points as
4825 astrologically relevant axes rather than as points on a
4826 planetary orbit. Again, this would allow heliocentric
4827 positions in a geocentric chart.
4828
4829 Note: For the "Dark Moon" or "Lilith", which I usually
4830 define as the lunar apogee, some astrologers give a
4831 different definition. They understand it as the second focal
4832 point of the moon's orbital ellipse. This definition does not
4833 make a difference for geocentric positions, because the
4834 apogee and the second focus are in exactly the same geocentric
4835 direction. However, it makes a difference with topocentric
4836 positions, because the two points do not have same distance.
4837 Analogous "black planets" have been proposed: they would be the
4838 second focal points of the planets' orbital ellipses. The
4839 heliocentric positions of these "black planets" are identical
4840 with the heliocentric positions of the aphelia, but geocentric
4841 positions are not identical, because the focal points are
4842 much closer to the sun than the aphelia.
4843
4844 The Swiss Ephemeris allows to compute the "black planets" as well.
4845
4846 Mean positions
4847
4848 Mean nodes and apsides can be computed for the Moon, the
4849 Earth and the planets Mercury - Neptune. They are taken
4850 from the planetary theory VSOP87. Mean points can not be
4851 calculated for Pluto and the asteroids, because there is no
4852 planetary theory for them.
4853
4854 Osculating nodes and apsides
4855
4856 Nodes and apsides can also be derived from the osculating
4857 orbital elements of a body, the paramaters that define an
4858 ideal unperturbed elliptic (two-body) orbit.
4859 For astrology, note that this is a simplification and
4860 idealization.
4861 Problem with Neptune: Neptune's orbit around the sun does not
4862 have much in common with an ellipse. There are often two
4863 perihelia and two aphelia within one revolution. As a result,
4864 there is a wild oscillation of the osculating perihelion (and
4865 aphelion).
4866 In actuality, Neptune's orbit is not heliocentric orbit at all.
4867 The twofold perihelia and aphelia are an effect of the motion of
4868 the sun about the solar system barycenter. This motion is
4869 much faster than the motion of Neptune, and Neptune
4870 cannot react on such fast displacements of the Sun. As a
4871 result, Neptune seems to move around the barycenter (or a
4872 mean sun) rather than around the true sun. In fact,
4873 Neptune's orbit around the barycenter is therefore closer to
4874 an ellipse than the his orbit around the sun. The same
4875 statement is also true for Saturn, Uranus and Pluto, but not
4876 for Jupiter and the inner planets.
4877
4878 This fundamental problem about osculating ellipses of
4879 planetary orbits does of course not only affect the apsides
4880 but also the nodes.
4881
4882 Two solutions can be thought of for this problem:
4883 1) The one would be to interpolate between actual
4884 passages of the planets through their nodes and
4885 apsides. However, this works only well with Mercury.
4886 With all other planets, the supporting points are too far
4887 apart as to make an accurate interpolation possible.
4888 This solution is not implemented, here.
4889 2) The other solution is to compute the apsides of the
4890 orbit around the barycenter rather than around the sun.
4891 This procedure makes sense for planets beyond Jupiter,
4892 it comes closer to the mean apsides and nodes for
4893 planets that have such points defined. For all other
4894 transsaturnian planets and asteroids, this solution yields
4895 a kind of "mean" nodes and apsides. On the other hand,
4896 the barycentric ellipse does not make any sense for
4897 inner planets and Jupiter.
4898
4899 The Swiss Ephemeris supports solution 2) for planets and
4900 asteroids beyond Jupiter.
4901
4902 Anyway, neither the heliocentric nor the barycentric ellipse
4903 is a perfect representation of the nature of a planetary orbit,
4904 and it will not yield the degree of precision that today's
4905 astrology is used to.
4906 The best choice of method will probably be:
4907 - For Mercury - Neptune: mean nodes and apsides
4908 - For asteroids that belong to the inner asteroid belt:
4909 osculating nodes/apsides from a heliocentric ellipse
4910 - For Pluto and outer asteroids: osculating nodes/apsides
4911 from a barycentric ellipse
4912
4913 The Moon is a special case: A "lunar true node" makes
4914 more sense, because it can be defined without the idea of an
4915 ellipse, e.g. as the intersection axis of the momentary lunar
4916 orbital plane with the ecliptic. Or it can be said that the
4917 momentary motion of the moon points to one of the two
4918 ecliptic points that are called the "true nodes". So, these
4919 points make sense. With planetary nodes, the situation is
4920 somewhat different, at least if we make a difference
4921 between heliocentric and geocentric positions. If so, the
4922 planetary nodes are points on a heliocentric orbital ellipse,
4923 which are transformed to the geocenter. An ellipse is
4924 required here, because a solar distance is required. In
4925 contrast to the planetary nodes, the lunar node does not
4926 require a distance, therefore manages without the idea of an
4927 ellipse and does not share its weaknesses.
4928 On the other hand, the lunar apsides DO require the idea of
4929 an ellipse. And because the lunar ellipse is actually
4930 extremely distorted, even more than any other celestial
4931 ellipse, the "true Lilith" (apogee), for which printed
4932 ephemerides are available, does not make any sense at all.
4933 (See the chapter on the lunar node and apogee.)
4934
4935 Special case: the Earth
4936
4937 The Earth is another special case. Instead of the motion of
4938 the Earth herself, the heliocentric motion of the Earth-
4939 Moon-Barycenter (EMB) is used to determine the
4940 osculating perihelion.
4941 There is no node of the earth orbit itself. However, there is
4942 an axis around which the earth's orbital plane slowly rotates
4943 due to planetary precession. The position points of this axis
4944 are not calculated by the Swiss Ephemeris.
4945
4946 Special case: the Sun
4947
4948 In addition to the Earth (EMB) apsides, the function
4949 computes so-to-say "apsides" of the sun, i.e. points on the
4950 orbit of the Sun where it is closest to and where it is farthest
4951 from the Earth. These points form an opposition and are
4952 used by some astrologers, e.g. by the Dutch astrologer
4953 George Bode or the Swiss astrologer Liduina Schmed. The
4954 perigee, located at about 13 Capricorn, is called the
4955 "Black Sun", the other one, in Cancer, the "Diamond".
4956 So, for a complete set of apsides, one ought to calculate
4957 them for the Sun and the Earth and all other planets.
4958
4959 The modes of the Swiss Ephemeris function
4960 swe_nod_aps()
4961
4962 The function swe_nod_aps() can be run in the following
4963 modes:
4964 1) Mean positions are given for nodes and apsides of Sun,
4965 Moon, Earth, and the up to Neptune. Osculating
4966 positions are given with Pluto and all asteroids. This is
4967 the default mode.
4968 2) Osculating positions are returned for nodes and apsides
4969 of all planets.
4970 3) Same as 2), but for planets and asteroids beyond
4971 Jupiter, a barycentric ellipse is used.
4972 4) Same as 1), but for Pluto and asteroids beyond Jupiter,
4973 a barycentric ellipse is used.
4974
4975 In all of these modes, the second focal point of the ellipse
4976 can be computed instead of the aphelion.
4977 Like the planetary function swe_calc(), swe_nod_aps() is
4978 able to return geocentric, topocentric, heliocentric, or
4979 barycentric position.
4980 *
4981 * tjd_ut julian day, ephemeris time
4982 * ipl planet number
4983 * iflag as usual, SEFLG_HELCTR, etc.
4984 * xnasc an array of 6 doubles: ascending node
4985 * xndsc an array of 6 doubles: ascending node
4986 * xperi an array of 6 doubles: perihelion
4987 * xaphe an array of 6 doubles: aphelion
4988 * method see below
4989 * serr error message
4990 *
4991 * method can have the following values:
4992 * - 0 or SE_NODBIT_MEAN. MEAN positions are given for
4993 * nodes and apsides of Sun, Moon, Earth, and the
4994 * planets up to Neptune. Osculating positions are
4995 * given with Pluto and all asteroids.
4996 * - SE_NODBIT_OSCU. Osculating positions are given
4997 * for all nodes and apsides.
4998 * - SE_NODBIT_OSCU_BAR. Osculating nodes and apsides
4999 * are computed from barycentric ellipses, for planets
5000 * beyond Jupiter, but from heliocentric ones for
5001 * ones for Jupiter and inner planets.
5002 * - SE_NODBIT_MEAN and SE_NODBIT_OSCU_BAR can be combined.
5003 * The program behaves the same way as with simple
5004 * SE_NODBIT_MEAN, but uses barycentric ellipses for
5005 * planets beyond Neptune and asteroids beyond Jupiter.
5006 * - SE_NODBIT_FOCAL can be combined with any of the other
5007 * bits. The second focal points of the ellipses will
5008 * be returned instead of the aphelia.
5009 */
5010 /* mean elements for Mercury - Neptune from VSOP87 (mean equinox of date) */
5011 static const double el_node[8][4] =
5012 {{ 48.330893, 1.1861890, 0.00017587, 0.000000211,}, /* Mercury */
5013 { 76.679920, 0.9011190, 0.00040665, -0.000000080,}, /* Venus */
5014 { 0 , 0 , 0 , 0 ,}, /* Earth */
5015 { 49.558093, 0.7720923, 0.00001605, 0.000002325,}, /* Mars */
5016 {100.464441, 1.0209550, 0.00040117, 0.000000569,}, /* Jupiter */
5017 {113.665524, 0.8770970, -0.00012067, -0.000002380,}, /* Saturn */
5018 { 74.005947, 0.5211258, 0.00133982, 0.000018516,}, /* Uranus */
5019 {131.784057, 1.1022057, 0.00026006, -0.000000636,}, /* Neptune */
5020 };
5021 static const double el_peri[8][4] =
5022 {{ 77.456119, 1.5564775, 0.00029589, 0.000000056,}, /* Mercury */
5023 {131.563707, 1.4022188, -0.00107337, -0.000005315,}, /* Venus */
5024 {102.937348, 1.7195269, 0.00045962, 0.000000499,}, /* Earth */
5025 {336.060234, 1.8410331, 0.00013515, 0.000000318,}, /* Mars */
5026 { 14.331309, 1.6126668, 0.00103127, -0.000004569,}, /* Jupiter */
5027 { 93.056787, 1.9637694, 0.00083757, 0.000004899,}, /* Saturn */
5028 {173.005159, 1.4863784, 0.00021450, 0.000000433,}, /* Uranus */
5029 { 48.123691, 1.4262677, 0.00037918, -0.000000003,}, /* Neptune */
5030 };
5031 static const double el_incl[8][4] =
5032 {{ 7.004986, 0.0018215, -0.00001809, 0.000000053,}, /* Mercury */
5033 { 3.394662, 0.0010037, -0.00000088, -0.000000007,}, /* Venus */
5034 { 0, 0, 0, 0 ,}, /* Earth */
5035 { 1.849726, -0.0006010, 0.00001276, -0.000000006,}, /* Mars */
5036 { 1.303270, -0.0054966, 0.00000465, -0.000000004,}, /* Jupiter */
5037 { 2.488878, -0.0037363, -0.00001516, 0.000000089,}, /* Saturn */
5038 { 0.773196, 0.0007744, 0.00003749, -0.000000092,}, /* Uranus */
5039 { 1.769952, -0.0093082, -0.00000708, 0.000000028,}, /* Neptune */
5040 };
5041 static const double el_ecce[8][4] =
5042 {{ 0.20563175, 0.000020406, -0.0000000284, -0.00000000017,}, /* Mercury */
5043 { 0.00677188, -0.000047766, 0.0000000975, 0.00000000044,}, /* Venus */
5044 { 0.01670862, -0.000042037, -0.0000001236, 0.00000000004,}, /* Earth */
5045 { 0.09340062, 0.000090483, -0.0000000806, -0.00000000035,}, /* Mars */
5046 { 0.04849485, 0.000163244, -0.0000004719, -0.00000000197,}, /* Jupiter */
5047 { 0.05550862, -0.000346818, -0.0000006456, 0.00000000338,}, /* Saturn */
5048 { 0.04629590, -0.000027337, 0.0000000790, 0.00000000025,}, /* Uranus */
5049 { 0.00898809, 0.000006408, -0.0000000008, -0.00000000005,}, /* Neptune */
5050 };
5051 static const double el_sema[8][4] =
5052 {{ 0.387098310, 0.0, 0.0, 0.0,}, /* Mercury */
5053 { 0.723329820, 0.0, 0.0, 0.0,}, /* Venus */
5054 { 1.000001018, 0.0, 0.0, 0.0,}, /* Earth */
5055 { 1.523679342, 0.0, 0.0, 0.0,}, /* Mars */
5056 { 5.202603191, 0.0000001913, 0.0, 0.0,}, /* Jupiter */
5057 { 9.554909596, 0.0000021389, 0.0, 0.0,}, /* Saturn */
5058 { 19.218446062, -0.0000000372, 0.00000000098, 0.0,}, /* Uranus */
5059 { 30.110386869, -0.0000001663, 0.00000000069, 0.0,}, /* Neptune */
5060 };
5061 /* Ratios of mass of Sun to masses of the planets */
5062 static const double plmass[9] = {
5063 6023600, /* Mercury */
5064 408523.719, /* Venus */
5065 328900.5, /* Earth and Moon */
5066 3098703.59, /* Mars */
5067 1047.348644, /* Jupiter */
5068 3497.9018, /* Saturn */
5069 22902.98, /* Uranus */
5070 19412.26, /* Neptune */
5071 136566000, /* Pluto */
5072 };
5073 static const int ipl_to_elem[15] = {2, 0, 0, 1, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 2,};
5074 int32 CALL_CONV swe_nod_aps(double tjd_et, int32 ipl, int32 iflag,
5075 int32 method,
5076 double *xnasc, double *xndsc,
5077 double *xperi, double *xaphe,
5078 char *serr)
5079 {
5080 int ij, i, j;
5081 int32 iplx;
5082 int32 ipli;
5083 int istart, iend;
5084 int32 iflJ2000;
5085 double daya, plm;
5086 double t = (tjd_et - J2000) / 36525, dt;
5087 double x[6], xx[24], *xp, xobs[6], x2000[6];
5088 double xpos[3][6], xnorm[6];
5089 double xposm[6];
5090 double xn[3][6], xs[3][6];
5091 double xq[3][6], xa[3][6];
5092 double xobs2[6], x2[6];
5093 double *xna, *xnd, *xpe, *xap;
5094 double incl, sema, ecce, parg, ea, vincl, vsema, vecce, pargx, eax;
5095 struct plan_data *pedp = &swed.pldat[SEI_EARTH];
5096 struct plan_data *psbdp = &swed.pldat[SEI_SUNBARY];
5097 struct plan_data pldat;
5098 double *xsun = psbdp->x;
5099 double *xear = pedp->x;
5100 const double *ep;
5101 double Gmsm, dzmin;
5102 double rxy, rxyz, fac, sgn;
5103 double sinnode, cosnode, sinincl, cosincl, sinu, cosu, sinE, cosE, cosE2;
5104 double uu, ny, ny2, c2, v2, pp, ro, ro2, rn, rn2;
5105 struct epsilon *oe;
5106 AS_BOOL is_true_nodaps = FALSE;
5107 AS_BOOL do_aberr = !(iflag & (SEFLG_TRUEPOS | SEFLG_NOABERR));
5108 AS_BOOL do_defl = !(iflag & SEFLG_TRUEPOS) && !(iflag & SEFLG_NOGDEFL);
5109 AS_BOOL do_focal_point = method & SE_NODBIT_FOPOINT;
5110 AS_BOOL ellipse_is_bary = FALSE;
5111 int32 iflg0;
5112 iflag &= ~(SEFLG_JPLHOR | SEFLG_JPLHOR_APPROX);
5113 /* function calls for Pluto with asteroid number 134340
5114 * are treated as calls for Pluto as main body SE_PLUTO */
5115 if (ipl == SE_AST_OFFSET + 134340)
5116 ipl = SE_PLUTO;
5117 xna = xx;
5118 xnd = xx+6;
5119 xpe = xx+12;
5120 xap = xx+18;
5121 xpos[0][0] = 0; /* to shut up mint */
5122 /* to get control over the save area: */
5123 swi_force_app_pos_etc();
5124 method %= SE_NODBIT_FOPOINT;
5125 ipli = ipl;
5126 if (ipl == SE_SUN)
5127 ipli = SE_EARTH;
5128 if (ipl == SE_MOON) {
5129 do_defl = FALSE;
5130 if (!(iflag & SEFLG_HELCTR))
5131 do_aberr = FALSE;
5132 }
5133 iflg0 = (iflag & (SEFLG_EPHMASK|SEFLG_NONUT)) | SEFLG_SPEED | SEFLG_TRUEPOS;
5134 if (ipli != SE_MOON)
5135 iflg0 |= SEFLG_HELCTR;
5136 if (ipl == SE_MEAN_NODE || ipl == SE_TRUE_NODE ||
5137 ipl == SE_MEAN_APOG || ipl == SE_OSCU_APOG ||
5138 ipl < 0 ||
5139 (ipl >= SE_NPLANETS && ipl <= SE_AST_OFFSET))
5140 // (ipl >= SE_FICT_OFFSET && ipl - SE_FICT_OFFSET < SE_NFICT_ELEM))
5141 {
5142 if (serr != NULL)
5143 sprintf(serr, "nodes/apsides for planet %5.0f are not implemented", (double) ipl);
5144 if (xnasc != NULL)
5145 for (i = 0; i <= 5; i++)
5146 xnasc[i] = 0;
5147 if (xndsc != NULL)
5148 for (i = 0; i <= 5; i++)
5149 xndsc[i] = 0;
5150 if (xaphe != NULL)
5151 for (i = 0; i <= 5; i++)
5152 xaphe[i] = 0;
5153 if (xperi != NULL)
5154 for (i = 0; i <= 5; i++)
5155 xperi[i] = 0;
5156 return ERR;
5157 }
5158 for (i = 0; i < 24; i++)
5159 xx[i] = 0;
5160 /***************************************
5161 * mean nodes and apsides
5162 ***************************************/
5163 /* mean points only for Sun - Neptune */
5164 if ((method == 0 || (method & SE_NODBIT_MEAN)) &&
5165 ((ipl >= SE_SUN && ipl <= SE_NEPTUNE) || ipl == SE_EARTH)) {
5166 if (ipl == SE_MOON) {
5167 swi_mean_lunar_elements(tjd_et, &xna[0], &xna[3], &xpe[0], &xpe[3]);
5168 incl = MOON_MEAN_INCL;
5169 vincl = 0;
5170 ecce = MOON_MEAN_ECC;
5171 vecce = 0;
5172 sema = MOON_MEAN_DIST / AUNIT;
5173 vsema = 0;
5174 } else {
5175 iplx = ipl_to_elem[ipl];
5176 ep = el_incl[iplx];
5177 incl = ep[0] + ep[1] * t + ep[2] * t * t + ep[3] * t * t * t;
5178 vincl = ep[1] / 36525;
5179 ep = el_sema[iplx];
5180 sema = ep[0] + ep[1] * t + ep[2] * t * t + ep[3] * t * t * t;
5181 vsema = ep[1] / 36525;
5182 ep = el_ecce[iplx];
5183 ecce = ep[0] + ep[1] * t + ep[2] * t * t + ep[3] * t * t * t;
5184 vecce = ep[1] / 36525;
5185 ep = el_node[iplx];
5186 /* ascending node */
5187 xna[0] = ep[0] + ep[1] * t + ep[2] * t * t + ep[3] * t * t * t;
5188 xna[3] = ep[1] / 36525;
5189 /* perihelion */
5190 ep = el_peri[iplx];
5191 xpe[0] = ep[0] + ep[1] * t + ep[2] * t * t + ep[3] * t * t * t;
5192 xpe[3] = ep[1] / 36525;
5193 }
5194 /* descending node */
5195 xnd[0] = swe_degnorm(xna[0] + 180);
5196 xnd[3] = xna[3];
5197 /* angular distance of perihelion from node */
5198 parg = xpe[0] = swe_degnorm(xpe[0] - xna[0]);
5199 pargx = xpe[3] = swe_degnorm(xpe[0] + xpe[3] - xna[3]);
5200 /* transform from orbital plane to mean ecliptic of date */
5201 swe_cotrans(xpe, xpe, -incl);
5202 /* xpe+3 is aux. position, not speed!!! */
5203 swe_cotrans(xpe+3, xpe+3, -incl-vincl);
5204 /* add node again */
5205 xpe[0] = swe_degnorm(xpe[0] + xna[0]);
5206 /* xpe+3 is aux. position, not speed!!! */
5207 xpe[3] = swe_degnorm(xpe[3] + xna[0] + xna[3]);
5208 /* speed */
5209 xpe[3] = swe_degnorm(xpe[3] - xpe[0]);
5210 /* heliocentric distance of perihelion and aphelion */
5211 xpe[2] = sema * (1 - ecce);
5212 xpe[5] = (sema + vsema) * (1 - ecce - vecce) - xpe[2];
5213 /* aphelion */
5214 xap[0] = swe_degnorm(xpe[0] + 180);
5215 xap[1] = -xpe[1];
5216 xap[3] = xpe[3];
5217 xap[4] = -xpe[4];
5218 if (do_focal_point) {
5219 xap[2] = sema * ecce * 2;
5220 xap[5] = (sema + vsema) * (ecce + vecce) * 2 - xap[2];
5221 } else {
5222 xap[2] = sema * (1 + ecce);
5223 xap[5] = (sema + vsema) * (1 + ecce + vecce) - xap[2];
5224 }
5225 /* heliocentric distance of nodes */
5226 ea = atan(tan(-parg * DEGTORAD / 2) * sqrt((1-ecce)/(1+ecce))) * 2;
5227 eax = atan(tan(-pargx * DEGTORAD / 2) * sqrt((1-ecce-vecce)/(1+ecce+vecce))) * 2;
5228 xna[2] = sema * (cos(ea) - ecce) / cos(parg * DEGTORAD);
5229 xna[5] = (sema+vsema) * (cos(eax) - ecce - vecce) / cos(pargx * DEGTORAD);
5230 xna[5] -= xna[2];
5231 ea = atan(tan((180 - parg) * DEGTORAD / 2) * sqrt((1-ecce)/(1+ecce))) * 2;
5232 eax = atan(tan((180 - pargx) * DEGTORAD / 2) * sqrt((1-ecce-vecce)/(1+ecce+vecce))) * 2;
5233 xnd[2] = sema * (cos(ea) - ecce) / cos((180 - parg) * DEGTORAD);
5234 xnd[5] = (sema+vsema) * (cos(eax) - ecce - vecce) / cos((180 - pargx) * DEGTORAD);
5235 xnd[5] -= xnd[2];
5236 /* no light-time correction because speed is extremely small */
5237 for (i = 0, xp = xx; i < 4; i++, xp += 6) {
5238 /* to cartesian coordinates */
5239 xp[0] *= DEGTORAD;
5240 xp[1] *= DEGTORAD;
5241 xp[3] *= DEGTORAD;
5242 xp[4] *= DEGTORAD;
5243 swi_polcart_sp(xp, xp);
5244 }
5245 /***************************************
5246 * "true" or osculating nodes and apsides
5247 ***************************************/
5248 } else {
5249 /* first, we need a heliocentric distance of the planet */
5250 if (swe_calc(tjd_et, ipli, iflg0, x, serr) == ERR)
5251 return ERR;
5252 iflJ2000 = (iflag & SEFLG_EPHMASK)|SEFLG_J2000|SEFLG_EQUATORIAL|SEFLG_XYZ|SEFLG_TRUEPOS|SEFLG_NONUT|SEFLG_SPEED;
5253 ellipse_is_bary = FALSE;
5254 if (ipli != SE_MOON) {
5255 if ((method & SE_NODBIT_OSCU_BAR) && x[2] > 6) {
5256 iflJ2000 |= SEFLG_BARYCTR; /* only planets beyond Jupiter */
5257 ellipse_is_bary = TRUE;
5258 } else {
5259 iflJ2000 |= SEFLG_HELCTR;
5260 }
5261 }
5262 /* we need three positions and three speeds
5263 * for three nodes/apsides. from the three node positions,
5264 * the speed of the node will be computed. */
5265 if (ipli == SE_MOON) {
5266 dt = NODE_CALC_INTV;
5267 dzmin = 1e-15;
5268 Gmsm = GEOGCONST * (1 + 1 / EARTH_MOON_MRAT) /AUNIT/AUNIT/AUNIT*86400.0*86400.0;
5269 } else {
5270 if ((ipli >= SE_MERCURY && ipli <= SE_PLUTO) || ipli == SE_EARTH)
5271 plm = 1 / plmass[ipl_to_elem[ipl]];
5272 else
5273 plm = 0;
5274 dt = NODE_CALC_INTV * 10 * x[2];
5275 dzmin = 1e-15 * dt / NODE_CALC_INTV;
5276 Gmsm = HELGRAVCONST * (1 + plm) /AUNIT/AUNIT/AUNIT*86400.0*86400.0;
5277 }
5278 if (iflag & SEFLG_SPEED) {
5279 istart = 0;
5280 iend = 2;
5281 } else {
5282 istart = iend = 0;
5283 dt = 0;
5284 }
5285 for (i = istart, t = tjd_et - dt; i <= iend; i++, t += dt) {
5286 if (istart == iend)
5287 t = tjd_et;
5288 if (swe_calc(t, ipli, iflJ2000, xpos[i], serr) == ERR)
5289 return ERR;
5290 /* the EMB is used instead of the earth */
5291 if (ipli == SE_EARTH) {
5292 if (swe_calc(t, SE_MOON, iflJ2000 & ~(SEFLG_BARYCTR|SEFLG_HELCTR), xposm, serr) == ERR)
5293 return ERR;
5294 for (j = 0; j <= 5; j++)
5295 xpos[i][j] += xposm[j] / (EARTH_MOON_MRAT + 1.0);
5296 }
5297 swi_plan_for_osc_elem(iflg0, t, xpos[i]);
5298 }
5299 for (i = istart; i <= iend; i++) {
5300 if (fabs(xpos[i][5]) < dzmin)
5301 xpos[i][5] = dzmin;
5302 fac = xpos[i][2] / xpos[i][5];
5303 sgn = xpos[i][5] / fabs(xpos[i][5]);
5304 for (j = 0; j <= 2; j++) {
5305 xn[i][j] = (xpos[i][j] - fac * xpos[i][j+3]) * sgn;
5306 xs[i][j] = -xn[i][j];
5307 }
5308 }
5309 for (i = istart; i <= iend; i++) {
5310 /* node */
5311 rxy = sqrt(xn[i][0] * xn[i][0] + xn[i][1] * xn[i][1]);
5312 cosnode = xn[i][0] / rxy;
5313 sinnode = xn[i][1] / rxy;
5314 /* inclination */
5315 swi_cross_prod(xpos[i], xpos[i]+3, xnorm);
5316 rxy = xnorm[0] * xnorm[0] + xnorm[1] * xnorm[1];
5317 c2 = (rxy + xnorm[2] * xnorm[2]);
5318 rxyz = sqrt(c2);
5319 rxy = sqrt(rxy);
5320 sinincl = rxy / rxyz;
5321 cosincl = sqrt(1 - sinincl * sinincl);
5322 if (xnorm[2] < 0) cosincl = -cosincl; /* retrograde asteroid, e.g. 20461 Dioretsa */
5323 /* argument of latitude */
5324 cosu = xpos[i][0] * cosnode + xpos[i][1] * sinnode;
5325 sinu = xpos[i][2] / sinincl;
5326 uu = atan2(sinu, cosu);
5327 /* semi-axis */
5328 rxyz = sqrt(square_sum(xpos[i]));
5329 v2 = square_sum((xpos[i]+3));
5330 sema = 1 / (2 / rxyz - v2 / Gmsm);
5331 /* eccentricity */
5332 pp = c2 / Gmsm;
5333 ecce = sqrt(1 - pp / sema);
5334 /* eccentric anomaly */
5335 cosE = 1 / ecce * (1 - rxyz / sema);
5336 sinE = 1 / ecce / sqrt(sema * Gmsm) * dot_prod(xpos[i], (xpos[i]+3));
5337 /* true anomaly */
5338 ny = 2 * atan(sqrt((1+ecce)/(1-ecce)) * sinE / (1 + cosE));
5339 /* distance of perihelion from ascending node */
5340 xq[i][0] = swi_mod2PI(uu - ny);
5341 xq[i][1] = 0; /* latitude */
5342 xq[i][2] = sema * (1 - ecce); /* distance of perihelion */
5343 /* transformation to ecliptic coordinates */
5344 swi_polcart(xq[i], xq[i]);
5345 swi_coortrf2(xq[i], xq[i], -sinincl, cosincl);
5346 swi_cartpol(xq[i], xq[i]);
5347 /* adding node, we get perihelion in ecl. coord. */
5348 xq[i][0] += atan2(sinnode, cosnode);
5349 xa[i][0] = swi_mod2PI(xq[i][0] + PI);
5350 xa[i][1] = -xq[i][1];
5351 if (do_focal_point) {
5352 xa[i][2] = sema * ecce * 2; /* distance of aphelion */
5353 } else {
5354 xa[i][2] = sema * (1 + ecce); /* distance of aphelion */
5355 }
5356 swi_polcart(xq[i], xq[i]);
5357 swi_polcart(xa[i], xa[i]);
5358 /* new distance of node from orbital ellipse:
5359 * true anomaly of node: */
5360 ny = swi_mod2PI(ny - uu);
5361 ny2 = swi_mod2PI(ny + PI);
5362 /* eccentric anomaly */
5363 cosE = cos(2 * atan(tan(ny / 2) / sqrt((1+ecce) / (1-ecce))));
5364 cosE2 = cos(2 * atan(tan(ny2 / 2) / sqrt((1+ecce) / (1-ecce))));
5365 /* new distance */
5366 rn = sema * (1 - ecce * cosE);
5367 rn2 = sema * (1 - ecce * cosE2);
5368 /* old node distance */
5369 ro = sqrt(square_sum(xn[i]));
5370 ro2 = sqrt(square_sum(xs[i]));
5371 /* correct length of position vector */
5372 for (j = 0; j <= 2; j++) {
5373 xn[i][j] *= rn / ro;
5374 xs[i][j] *= rn2 / ro2;
5375 }
5376 }
5377 for (i = 0; i <= 2; i++) {
5378 if (iflag & SEFLG_SPEED) {
5379 xpe[i] = xq[1][i];
5380 xpe[i+3] = (xq[2][i] - xq[0][i]) / dt / 2;
5381 xap[i] = xa[1][i];
5382 xap[i+3] = (xa[2][i] - xa[0][i]) / dt / 2;
5383 xna[i] = xn[1][i];
5384 xna[i+3] = (xn[2][i] - xn[0][i]) / dt / 2;
5385 xnd[i] = xs[1][i];
5386 xnd[i+3] = (xs[2][i] - xs[0][i]) / dt / 2;
5387 } else {
5388 xpe[i] = xq[0][i];
5389 xpe[i+3] = 0;
5390 xap[i] = xa[0][i];
5391 xap[i+3] = 0;
5392 xna[i] = xn[0][i];
5393 xna[i+3] = 0;
5394 xnd[i] = xs[0][i];
5395 xnd[i+3] = 0;
5396 }
5397 }
5398 is_true_nodaps = TRUE;
5399 }
5400 /* to set the variables required in the save area,
5401 * i.e. ecliptic, nutation, barycentric sun, earth
5402 * we compute the planet */
5403 if (ipli == SE_MOON && (iflag & (SEFLG_HELCTR | SEFLG_BARYCTR))) {
5404 swi_force_app_pos_etc();
5405 if (swe_calc(tjd_et, SE_SUN, iflg0, x, serr) == ERR)
5406 return ERR;
5407 } else {
5408 if (swe_calc(tjd_et, ipli, iflg0 | (iflag & SEFLG_TOPOCTR), x, serr) == ERR)
5409 return ERR;
5410 }
5411 /***********************
5412 * position of observer
5413 ***********************/
5414 if (iflag & SEFLG_TOPOCTR) {
5415 /* geocentric position of observer */
5416 if (swi_get_observer(tjd_et, iflag, FALSE, xobs, serr) != OK)
5417 return ERR;
5418 /*for (i = 0; i <= 5; i++)
5419 xobs[i] = swed.topd.xobs[i];*/
5420 } else {
5421 for (i = 0; i <= 5; i++)
5422 xobs[i] = 0;
5423 }
5424 if (iflag & (SEFLG_HELCTR | SEFLG_BARYCTR)) {
5425 if ((iflag & SEFLG_HELCTR) && !(iflag & SEFLG_MOSEPH))
5426 for (i = 0; i <= 5; i++)
5427 xobs[i] = xsun[i];
5428 } else if (ipl == SE_SUN && !(iflag & SEFLG_MOSEPH)) {
5429 for (i = 0; i <= 5; i++)
5430 xobs[i] = xsun[i];
5431 } else {
5432 /* barycentric position of observer */
5433 for (i = 0; i <= 5; i++)
5434 xobs[i] += xear[i];
5435 }
5436 /* ecliptic obliqity */
5437 if (iflag & SEFLG_J2000)
5438 oe = &swed.oec2000;
5439 else
5440 oe = &swed.oec;
5441 /*************************************************
5442 * conversions shared by mean and osculating points
5443 *************************************************/
5444 for (ij = 0, xp = xx; ij < 4; ij++, xp += 6) {
5445 /* no nodes for earth */
5446 if (ipli == SE_EARTH && ij <= 1) {
5447 for (i = 0; i <= 5; i++)
5448 xp[i] = 0;
5449 continue;
5450 }
5451 /*********************
5452 * to equator
5453 *********************/
5454 if (is_true_nodaps && !(iflag & SEFLG_NONUT)) {
5455 swi_coortrf2(xp, xp, -swed.nut.snut, swed.nut.cnut);
5456 if (iflag & SEFLG_SPEED)
5457 swi_coortrf2(xp+3, xp+3, -swed.nut.snut, swed.nut.cnut);
5458 }
5459 swi_coortrf2(xp, xp, -oe->seps, oe->ceps);
5460 swi_coortrf2(xp+3, xp+3, -oe->seps, oe->ceps);
5461 if (is_true_nodaps) {
5462 /****************************
5463 * to mean ecliptic of date
5464 ****************************/
5465 if (!(iflag & SEFLG_NONUT))
5466 swi_nutate(xp, iflag, TRUE);
5467 }
5468 /*********************
5469 * to J2000
5470 *********************/
5471 swi_precess(xp, tjd_et, iflag, J_TO_J2000);
5472 if (iflag & SEFLG_SPEED)
5473 swi_precess_speed(xp, tjd_et, iflag, J_TO_J2000);
5474 // fprintf(stderr, "%.17f, %.17f, %.17f, %.17f, %.17f, %.17f\n", xp[0], xp[1], xp[2], xp[3], xp[4], xp[5]);
5475 /*********************
5476 * to barycenter
5477 *********************/
5478 if (ipli == SE_MOON) {
5479 for (i = 0; i <= 5; i++)
5480 xp[i] += xear[i];
5481 } else {
5482 if (!(iflag & SEFLG_MOSEPH) && !ellipse_is_bary)
5483 for (j = 0; j <= 5; j++)
5484 xp[j] += xsun[j];
5485 }
5486 /*********************
5487 * to correct center
5488 *********************/
5489 for (j = 0; j <= 5; j++)
5490 xp[j] -= xobs[j];
5491 /* geocentric perigee/apogee of sun */
5492 if (ipl == SE_SUN && !(iflag & (SEFLG_HELCTR | SEFLG_BARYCTR)))
5493 for (j = 0; j <= 5; j++)
5494 xp[j] = -xp[j];
5495 /*********************
5496 * light deflection
5497 *********************/
5498 dt = sqrt(square_sum(xp)) * AUNIT / CLIGHT / 86400.0;
5499 if (do_defl)
5500 swi_deflect_light(xp, dt, iflag);
5501 /*********************
5502 * aberration
5503 *********************/
5504 if (do_aberr) {
5505 swi_aberr_light(xp, xobs, iflag);
5506 /*
5507 * Apparent speed is also influenced by
5508 * the difference of speed of the earth between t and t-dt.
5509 * Neglecting this would result in an error of several 0.1"
5510 */
5511 if (iflag & SEFLG_SPEED) {
5512 /* get barycentric sun and earth for t-dt into save area */
5513 if (swe_calc(tjd_et - dt, ipli, iflg0 | (iflag & SEFLG_TOPOCTR), x2, serr) == ERR)
5514 return ERR;
5515 if (iflag & SEFLG_TOPOCTR) {
5516 /* geocentric position of observer */
5517 /* if (swi_get_observer(tjd_et - dt, iflag, FALSE, xobs, serr) != OK)
5518 return ERR;*/
5519 for (i = 0; i <= 5; i++)
5520 xobs2[i] = swed.topd.xobs[i];
5521 } else {
5522 for (i = 0; i <= 5; i++)
5523 xobs2[i] = 0;
5524 }
5525 if (iflag & (SEFLG_HELCTR | SEFLG_BARYCTR)) {
5526 if ((iflag & SEFLG_HELCTR) && !(iflag & SEFLG_MOSEPH))
5527 for (i = 0; i <= 5; i++)
5528 xobs2[i] = xsun[i];
5529 } else if (ipl == SE_SUN && !(iflag & SEFLG_MOSEPH)) {
5530 for (i = 0; i <= 5; i++)
5531 xobs2[i] = xsun[i];
5532 } else {
5533 /* barycentric position of observer */
5534 for (i = 0; i <= 5; i++)
5535 xobs2[i] += xear[i];
5536 }
5537 for (i = 3; i <= 5; i++)
5538 xp[i] += xobs[i] - xobs2[i];
5539 /* The above call of swe_calc() has destroyed the
5540 * parts of the save area
5541 * (i.e. bary sun, earth nutation matrix!).
5542 * to restore it:
5543 */
5544 if (swe_calc(tjd_et, SE_SUN, iflg0 | (iflag & SEFLG_TOPOCTR), x2, serr) == ERR)
5545 return ERR;
5546 }
5547 }
5548 /*********************
5549 * precession
5550 *********************/
5551 /* save J2000 coordinates; required for sidereal positions */
5552 for (j = 0; j <= 5; j++)
5553 x2000[j] = xp[j];
5554 if (!(iflag & SEFLG_J2000)) {
5555 swi_precess(xp, tjd_et, iflag, J2000_TO_J);
5556 if (iflag & SEFLG_SPEED)
5557 swi_precess_speed(xp, tjd_et, iflag, J2000_TO_J);
5558 }
5559 /*********************
5560 * nutation
5561 *********************/
5562 if (!(iflag & SEFLG_NONUT))
5563 swi_nutate(xp, iflag, FALSE);
5564 /* now we have equatorial cartesian coordinates; keep them */
5565 for (j = 0; j <= 5; j++)
5566 pldat.xreturn[18+j] = xp[j];
5567 /************************************************
5568 * transformation to ecliptic. *
5569 * with sidereal calc. this will be overwritten *
5570 * afterwards. *
5571 ************************************************/
5572 swi_coortrf2(xp, xp, oe->seps, oe->ceps);
5573 if (iflag & SEFLG_SPEED)
5574 swi_coortrf2(xp+3, xp+3, oe->seps, oe->ceps);
5575 if (!(iflag & SEFLG_NONUT)) {
5576 swi_coortrf2(xp, xp, swed.nut.snut, swed.nut.cnut);
5577 if (iflag & SEFLG_SPEED)
5578 swi_coortrf2(xp+3, xp+3, swed.nut.snut, swed.nut.cnut);
5579 }
5580 /* now we have ecliptic cartesian coordinates */
5581 for (j = 0; j <= 5; j++)
5582 pldat.xreturn[6+j] = xp[j];
5583 /************************************
5584 * sidereal positions *
5585 ************************************/
5586 if (iflag & SEFLG_SIDEREAL) {
5587 /* project onto ecliptic t0 */
5588 if (swed.sidd.sid_mode & SE_SIDBIT_ECL_T0) {
5589 if (swi_trop_ra2sid_lon(x2000, pldat.xreturn+6, pldat.xreturn+18, iflag) != OK)
5590 return ERR;
5591 /* project onto solar system equator */
5592 } else if (swed.sidd.sid_mode & SE_SIDBIT_SSY_PLANE) {
5593 if (swi_trop_ra2sid_lon_sosy(x2000, pldat.xreturn+6, iflag) != OK)
5594 return ERR;
5595 } else {
5596 /* traditional algorithm */
5597 swi_cartpol_sp(pldat.xreturn+6, pldat.xreturn);
5598 if (swi_get_ayanamsa_ex(tjd_et, iflag, &daya, serr) == ERR)
5599 return ERR;
5600 pldat.xreturn[0] -= daya * DEGTORAD;
5601 swi_polcart_sp(pldat.xreturn, pldat.xreturn+6);
5602 }
5603 }
5604 if ((iflag & SEFLG_XYZ) && (iflag & SEFLG_EQUATORIAL)) {
5605 for (j = 0; j <= 5; j++)
5606 xp[j] = pldat.xreturn[18+j];
5607 continue;
5608 }
5609 if (iflag & SEFLG_XYZ) {
5610 for (j = 0; j <= 5; j++)
5611 xp[j] = pldat.xreturn[6+j];
5612 continue;
5613 }
5614 /************************************************
5615 * transformation to polar coordinates *
5616 ************************************************/
5617 swi_cartpol_sp(pldat.xreturn+18, pldat.xreturn+12);
5618 swi_cartpol_sp(pldat.xreturn+6, pldat.xreturn);
5619 /**********************
5620 * radians to degrees *
5621 **********************/
5622 if (!(iflag & SEFLG_RADIANS)) {
5623 for (j = 0; j < 2; j++) {
5624 pldat.xreturn[j] *= RADTODEG; /* ecliptic */
5625 pldat.xreturn[j+3] *= RADTODEG;
5626 pldat.xreturn[j+12] *= RADTODEG; /* equator */
5627 pldat.xreturn[j+15] *= RADTODEG;
5628 }
5629 }
5630 if (iflag & SEFLG_EQUATORIAL) {
5631 for (j = 0; j <= 5; j++)
5632 xp[j] = pldat.xreturn[12+j];
5633 continue;
5634 } else {
5635 for (j = 0; j <= 5; j++)
5636 xp[j] = pldat.xreturn[j];
5637 continue;
5638 }
5639 }
5640 for (i = 0; i <= 5; i++) {
5641 if (i > 2 && !(iflag & SEFLG_SPEED))
5642 xna[i] = xnd[i] = xpe[i] = xap[i] = 0;
5643 if (xnasc != NULL)
5644 xnasc[i] = xna[i];
5645 if (xndsc != NULL)
5646 xndsc[i] = xnd[i];
5647 if (xperi != NULL)
5648 xperi[i] = xpe[i];
5649 if (xaphe != NULL)
5650 xaphe[i] = xap[i];
5651 }
5652 return OK;
5653 }
5654
5655 int32 CALL_CONV swe_nod_aps_ut(double tjd_ut, int32 ipl, int32 iflag,
5656 int32 method,
5657 double *xnasc, double *xndsc,
5658 double *xperi, double *xaphe,
5659 char *serr) {
5660 /*swi_set_tid_acc(tjd_ut, iflag, 0, serr);*/
5661 return swe_nod_aps(tjd_ut + swe_deltat_ex(tjd_ut, iflag, serr),
5662 ipl, iflag, method, xnasc, xndsc, xperi, xaphe,
5663 serr);
5664 }
5665
5666 #ifdef TEST_ORBEL_AA
5667 /* Corrections to Gmsm to make orbital elements agree with AA 2011-2013
5668 * using DE406, or AA 2016 using DE431. Example:
5669 * https://books.google.ch/books?id=Mc_VuQN_gVsC&pg=PR5&dq=%22astronomical+almanac%22+%22osculating+%22+planets&hl=en&sa=X&ved=0ahUKEwib1-rR_rjLAhUEWSwKHZRQAjYQ6AEIOzAG#v=onepage&q=%22astronomical%20almanac%22%20%22osculating%20%22%20planets&f=false
5670 * swetest -bj2455600.5 -p8 -orbel -hel -ejplde406.eph -j2000
5671 * This is only a test feature. Orbital elements in agreement with AA should
5672 * be calculated using iflag | SEFLG_ORBEL_AA.
5673 */
5674 static const double Gmsm_factor_AA[] = {
5675 1, /* Mercury */
5676 0.9999999, /* Venus */
5677 0.9999941, /* EMB */
5678 0.9999941, /* Mars */
5679 0.9999941, /* Jupiter */
5680 0.9990404, /* Saturn */
5681 0.9987549, /* Uranus */
5682 0.998711, /* Neptune */
5683 0.99866025, /* Pluto */
5684 };
5685 #endif
5686 static int32 get_gmsm(double tjd_et, int32 ipl, int32 iflag, double r, double *gmsm, char *serr)
5687 {
5688 int j;
5689 double Gmsm = 0, plm = 0, x[6];
5690 int32 iflJ2000p = (iflag & (SEFLG_EPHMASK |SEFLG_HELCTR|SEFLG_BARYCTR))|SEFLG_J2000|SEFLG_TRUEPOS|SEFLG_NONUT;
5691 if (!(iflJ2000p & (SEFLG_HELCTR|SEFLG_BARYCTR)))
5692 iflJ2000p |= SEFLG_HELCTR;
5693 if (ipl == SE_MOON) {
5694 Gmsm = GEOGCONST * (1 + 1 / EARTH_MOON_MRAT) /AUNIT/AUNIT/AUNIT*86400.0*86400.0;
5695 } else {
5696 if ((ipl >= SE_MERCURY && ipl <= SE_PLUTO) || ipl == SE_EARTH) {
5697 plm = 0;
5698 /* to reproduce AA orbital elements, sum up masses of all planets
5699 * that are inside the orbit to be computed */
5700 if (iflag & SEFLG_ORBEL_AA) {
5701 if (ipl == SE_EARTH) {
5702 plm = 1.0 / plmass[ipl_to_elem[ipl]];
5703 plm += 1.0 / plmass[ipl_to_elem[SE_VENUS]];
5704 plm += 1.0 / plmass[ipl_to_elem[SE_MERCURY]];
5705 } else {
5706 for (j = ipl; j >= SE_MERCURY; j--) {
5707 plm += 1.0 / plmass[ipl_to_elem[j]];
5708 }
5709 if (ipl >= SE_MARS)
5710 plm += 1.0 / plmass[ipl_to_elem[SE_EARTH]];
5711 }
5712 /* ... treat it as a pure two-body problem */
5713 } else {
5714 plm = 1.0 / plmass[ipl_to_elem[ipl]];
5715 }
5716 Gmsm = HELGRAVCONST * (1 + plm) /AUNIT/AUNIT/AUNIT*86400.0*86400.0;
5717 #ifdef TEST_ORBEL_AA
5718 if (!(iflag & SEFLG_ORBEL_AA))
5719 Gmsm /= Gmsm_factor_AA[ipl_to_elem[ipl]];
5720 #endif
5721 // asteroid or fictitious object
5722 } else {
5723 plm = 0;
5724 if (iflag & SEFLG_ORBEL_AA) {
5725 for (j = SE_MERCURY; j <= SE_PLUTO; j++) {
5726 if (swe_calc(tjd_et, j, iflJ2000p, x, serr) == ERR)
5727 return ERR;
5728 if (r > x[2])
5729 plm += 1.0 / plmass[ipl_to_elem[j]];
5730 }
5731 if (swe_calc(tjd_et, SE_EARTH, iflJ2000p, x, serr) == ERR)
5732 return ERR;
5733 if (r > x[2])
5734 plm += 1.0 / plmass[ipl_to_elem[SE_EARTH]];
5735 }
5736 Gmsm = HELGRAVCONST * (1 + plm) /AUNIT/AUNIT/AUNIT*86400.0*86400.0;
5737 }
5738 }
5739 *gmsm = Gmsm;
5740 return OK;
5741 }
5742
5743 /* Function calculates osculating orbital elements (Kepler elements) of a planet
5744 * or asteroid or the Earth-Moon barycentre.
5745 * The function returns error if called for the Sun, the lunar nodes, or the apsides.
5746 * Input parameters:
5747 * tjd_et Julian day number, in TT (ET)
5748 * ipl object number
5749 * iflag can contain
5750 * - ephemeris flag: SEFLG_JPLEPH, SEFLG_SWIEPH, SEFLG_MOSEPH
5751 * - center:
5752 * Sun: SEFLG_HELCTR (assumed as default) or
5753 * SS Barycentre: SEFLG_BARYCTR (rel. to solar system barycentre)
5754 * (only possible for planets beyond Jupiter)
5755 * For elements of the Moon, the calculation is geocentric.
5756 * - sum all masses inside the orbit to be computed (method
5757 * of Astronomical Almanac):
5758 * SEFLG_ORBEL_AA
5759 * - reference ecliptic: SEFLG_J2000;
5760 * if missing, mean ecliptic of date is chosen (still not implemented)
5761 * output parameters:
5762 * dret[] array of return values, declare as dret[50]
5763 * dret[0] semimajor axis (a)
5764 * dret[1] eccentricity (e)
5765 * dret[2] inclination (in)
5766 * dret[3] longitude of ascending node (upper case omega OM)
5767 * dret[4] argument of periapsis (lower case omega om)
5768 * dret[5] longitude of periapsis (peri)
5769 * dret[6] mean anomaly at epoch (M0)
5770 * dret[7] true anomaly at epoch (N0)
5771 * dret[8] eccentric anomaly at epoch (E0)
5772 * dret[9] mean longitude at epoch (LM)
5773 * dret[10] sidereal orbital period in tropical years
5774 * dret[11] mean daily motion
5775 * dret[12] tropical period in years
5776 * dret[13] synodic period in days,
5777 * negative, if inner planet (Venus, Mercury, Aten asteroids) or Moon
5778 * dret[14] time of perihelion passage
5779 * dret[15] perihelion distance
5780 * dret[16] aphelion distance
5781 */
5782 int32 CALL_CONV swe_get_orbital_elements(
5783 double tjd_et,
5784 int32 ipl, int32 iflag,
5785 double *dret,
5786 char *serr)
5787 {
5788 int j;
5789 double x[6], xpos[6], xposm[6], xn[6], xs[6], xnorm[6], xq[6], xa[6] ;
5790 //int32 iflJ2000 = (iflag & SEFLG_EPHMASK)|SEFLG_J2000|SEFLG_EQUATORIAL|SEFLG_XYZ|SEFLG_TRUEPOS|SEFLG_NONUT|SEFLG_SPEED;
5791 int32 iflJ2000 = (iflag & SEFLG_EPHMASK)|SEFLG_J2000|SEFLG_XYZ|SEFLG_TRUEPOS|SEFLG_NONUT|SEFLG_SPEED;
5792 int32 iflJ2000p = (iflag & SEFLG_EPHMASK)|SEFLG_J2000|SEFLG_TRUEPOS|SEFLG_NONUT|SEFLG_SPEED;
5793 double Gmsm;
5794 int32 iflg0 = 0;
5795 double fac, sgn, rxy, rxyz, c2, cosnode, sinnode;
5796 double incl, node, parg, peri, mlon;
5797 double csid, ctro, csyn, dmot, pa;
5798 double ytrop, ysid, T, T2, T3, T4, T5;
5799 double sinincl, cosincl, cosu, sinu, uu, eanom, tanom, manom;
5800 double v2, sema, pp, ecce, cosE, sinE, ny, ny2, rn, rn2, ro, ro2, cosE2;
5801 double r, ecce2;
5802 if (ipl <= 0 || ipl == SE_MEAN_NODE || ipl == SE_TRUE_NODE || ipl == SE_MEAN_APOG || ipl == SE_OSCU_APOG || ipl == SE_INTP_APOG || ipl == SE_INTP_PERG) {
5803 if (serr != NULL)
5804 sprintf(serr, "error in swe_get_orbital_elements(): object %d not valid\n", ipl);
5805 return ERR;
5806 }
5807 if (ipl != SE_MOON)
5808 iflg0 |= SEFLG_HELCTR;
5809 /* first, we need a heliocentric distance of the planet */
5810 if (swe_calc(tjd_et, ipl, iflJ2000p, x, serr) == ERR)
5811 return ERR;
5812 r = x[2];
5813 if (ipl != SE_MOON) {
5814 if ((iflag & SEFLG_BARYCTR) && r > 6) {
5815 iflJ2000 |= SEFLG_BARYCTR; /* only planets beyond Jupiter */
5816 } else {
5817 iflJ2000 |= SEFLG_HELCTR;
5818 }
5819 }
5820 if (get_gmsm(tjd_et, ipl, iflag, r, &Gmsm, serr))
5821 return ERR;
5822 if (swe_calc(tjd_et, ipl, iflJ2000, xpos, serr) == ERR)
5823 return ERR;
5824 /* the EMB is used instead of the earth */
5825 if (ipl == SE_EARTH) {
5826 if (swe_calc(tjd_et, SE_MOON, iflJ2000 & ~(SEFLG_BARYCTR|SEFLG_HELCTR), xposm, serr) == ERR)
5827 return ERR;
5828 for (j = 0; j <= 5; j++)
5829 xpos[j] += xposm[j] / (EARTH_MOON_MRAT + 1.0);
5830 }
5831 fac = xpos[2] / xpos[5];
5832 sgn = xpos[5] / fabs(xpos[5]);
5833 for (j = 0; j <= 2; j++) {
5834 xn[j] = (xpos[j] - fac * xpos[j+3]) * sgn;
5835 xs[j] = -xn[j];
5836 }
5837 /* node */
5838 rxy = sqrt(xn[0] * xn[0] + xn[1] * xn[1]);
5839 cosnode = xn[0] / rxy;
5840 sinnode = xn[1] / rxy;
5841 /* inclination */
5842 swi_cross_prod(xpos, xpos+3, xnorm);
5843 rxy = xnorm[0] * xnorm[0] + xnorm[1] * xnorm[1];
5844 c2 = (rxy + xnorm[2] * xnorm[2]);
5845 rxyz = sqrt(c2);
5846 rxy = sqrt(rxy);
5847 sinincl = rxy / rxyz;
5848 cosincl = sqrt(1 - sinincl * sinincl);
5849 if (xnorm[2] < 0) cosincl = -cosincl; /* retrograde asteroid, e.g. 20461 Dioretsa */
5850 incl = acos(cosincl) * RADTODEG; // inclination
5851 /* argument of latitude */
5852 cosu = xpos[0] * cosnode + xpos[1] * sinnode;
5853 sinu = xpos[2] / sinincl;
5854 uu = atan2(sinu, cosu);
5855 /* semi-axis */
5856 rxyz = sqrt(square_sum(xpos));
5857 v2 = square_sum((xpos+3));
5858 sema = 1.0 / (2.0 / rxyz - v2 / Gmsm);
5859 /* eccentricity */
5860 pp = c2 / Gmsm;
5861 ecce = pp / sema;
5862 if (ecce > 1)
5863 ecce = 1;
5864 ecce = sqrt(1 - ecce);
5865 /* eccentric anomaly */
5866 ecce2 = ecce;
5867 if (ecce2 == 0)
5868 ecce2 = 0.0000000001;
5869 cosE = 1 / ecce2 * (1 - rxyz / sema);
5870 sinE = 1 / ecce2 / sqrt(sema * Gmsm) * dot_prod(xpos, (xpos+3));
5871 eanom = swe_degnorm(atan2(sinE, cosE) * RADTODEG);
5872 // eanom = acos((1 - rxyz / sema) / ecce2);
5873 /* true anomaly */
5874 ny = 2 * atan(sqrt((1+ecce)/(1-ecce)) * sinE / (1 + cosE));
5875 tanom = swe_degnorm(ny * RADTODEG);
5876 if (eanom > 180 && tanom < 180)
5877 tanom += 180;
5878 if (eanom < 180 && tanom > 180)
5879 tanom -= 180;
5880 // tanom = acos((sema * (1 - ecce * ecce) / rxyz - 1.0) / ecce2);
5881 /* mean anomaly */
5882 manom = swe_degnorm(eanom - ecce * RADTODEG * sin(eanom * DEGTORAD)); // mean anomaly
5883 /* distance of perihelion from ascending node */
5884 xq[0] = swi_mod2PI(uu - ny);
5885 parg = xq[0] * RADTODEG;
5886 xq[1] = 0; /* latitude */
5887 xq[2] = sema * (1 - ecce); /* distance of perihelion */
5888 /* transformation to ecliptic coordinates */
5889 swi_polcart(xq, xq);
5890 swi_coortrf2(xq, xq, -sinincl, cosincl);
5891 swi_cartpol(xq, xq);
5892 /* adding node, we get perihelion in ecl. coord. */
5893 xq[0] += atan2(sinnode, cosnode);
5894 xa[0] = swi_mod2PI(xq[0] + PI);
5895 xa[1] = -xq[1];
5896 // if (do_focal_point) {
5897 // xa[2] = sema * ecce * 2; /* distance of aphelion */
5898 // } else {
5899 xa[2] = sema * (1 + ecce); /* distance of aphelion */
5900 // }
5901 swi_polcart(xq, xq);
5902 swi_polcart(xa, xa);
5903 /* new distance of node from orbital ellipse:
5904 * true anomaly of node: */
5905 ny = swi_mod2PI(ny - uu);
5906 ny2 = swi_mod2PI(ny + PI);
5907 /* eccentric anomaly */
5908 cosE = cos(2 * atan(tan(ny / 2) / sqrt((1+ecce) / (1-ecce))));
5909 cosE2 = cos(2 * atan(tan(ny2 / 2) / sqrt((1+ecce) / (1-ecce))));
5910 /* new distance */
5911 rn = sema * (1 - ecce * cosE);
5912 rn2 = sema * (1 - ecce * cosE2);
5913 /* old node distance */
5914 ro = sqrt(square_sum(xn));
5915 ro2 = sqrt(square_sum(xs));
5916 /* correct length of position vector */
5917 for (j = 0; j <= 2; j++) {
5918 xn[j] *= rn / ro;
5919 xs[j] *= rn2 / ro2;
5920 }
5921 swi_cartpol(xn, xn);
5922 swi_cartpol(xq, xq);
5923 node = xn[0] * RADTODEG;
5924 peri = swe_degnorm(node + parg);
5925 mlon = swe_degnorm(manom + peri);
5926 csid = sema * sqrt(sema); // sidereal period in sidereal years
5927 if (ipl == SE_MOON) {
5928 double semam = sema * AUNIT / 383397772.5;
5929 csid = semam * sqrt(semam); // sidereal period in sidereal months
5930 csid *= 27.32166 / 365.25636300;
5931 }
5932 dmot = 0.9856076686 / csid; // daily motion
5933 csid *= 365.25636 / 365.242189; // sidereal period in tropical years J2000
5934 // daily motion due to precession (Simon et alii 1994)
5935 T = (tjd_et - J2000) / 365250.0;
5936 T2 = T * T; T3 = T2 * T; T4 = T3 * T; T5 = T4 * T;
5937 pa = (50288.200 + 222.4045 * T + 0.2095 * T2 - 0.9408 * T3 - 0.0090 * T4 + 0.0010 * T5) / 3600.0 / 365250.0;
5938 // sidereal and tropical year length (Simon et alii 1994)
5939 ysid = (1295977422.83429 - 2 * 2.0441 * T - 3 * 0.00523 * T * T) / 3600.0 / 365250.0;
5940 ysid = 360.0 / ysid;
5941 ytrop = (1296027711.03429 + 2 * 109.15809 * T + 3 * 0.07207 * T2 - 4 * 0.23530 * T3 - 5 * 0.00180 * T4 + 6 * 0.00020 * T5) / 3600.0 / 365250.0;
5942 ytrop = 360.0 / ytrop;
5943 ctro = 360.0 / (dmot + pa) / 365.242189; // tropical period in years
5944 ctro *= ysid / ytrop; // tropical period in tropical years J2000
5945 if (ipl == SE_EARTH)
5946 csyn = 0;
5947 else
5948 csyn = 360.0 / (0.9856076686 - dmot); // synodic period in days
5949 dret[0] = sema; // semimajor axis
5950 dret[1] = ecce; // eccentricity
5951 dret[2] = incl; // inclination
5952 dret[3] = node; // node
5953 dret[4] = parg; // argument of perihelion
5954 dret[5] = peri; // longitude of perihelion
5955 dret[6] = manom; // mean anomaly
5956 dret[7] = tanom; // true anomaly
5957 dret[8] = eanom; // eccentric anomaly
5958 dret[9] = mlon; // mean longitude
5959 dret[10] = csid; // sidereal orbital period in sidereal years
5960 dret[11] = dmot; // daily motion
5961 dret[12] = ctro; // tropical period in years
5962 dret[13] = csyn; /* synodic period in days */
5963 dret[14] = tjd_et - dret[6] / dmot; /* tjd_et of perihelion passage */
5964 dret[15] = sema * (1 - ecce); /* perihelion distance */
5965 dret[16] = sema * (1 + ecce); /* aphelion distance */
5966 // printf("%d: a=%f, e=%f, in=%f, node=%f, parg=%f, peri=%f\n", ipl, sema, ecce, dret[2], dret[3], dret[4], dret[5]);
5967 // printf("tan=%f, ean=%f, man=%f, mlon=%f\n", dret[7], dret[8], dret[6], dret[9]);
5968 // printf("cyc=%f, dmot=%f, cyct=%f, cycs=%f\n", dret[10], dret[11], dret[12], dret[13]);
5969 // printf("tperi=%f, rperi=%f, raph=%f\n", dret[14], dret[15], dret[16]);
5970 return OK;
5971 }
5972
5973 static void osc_get_orbit_constants(double *dp, double *pqr)
5974 {
5975 double sema = dp[0];
5976 double ecce = dp[1];
5977 double incl = dp[2];
5978 double node = dp[3];
5979 double parg = dp[4];
5980 double cosnode = cos(node * DEGTORAD);
5981 double sinnode = sin(node * DEGTORAD);
5982 double cosincl = cos(incl * DEGTORAD);
5983 double sinincl = sin(incl * DEGTORAD);
5984 double cosparg = cos(parg * DEGTORAD);
5985 double sinparg = sin(parg * DEGTORAD);
5986 double fac = sqrt((1 - ecce) * (1 + ecce));
5987 pqr[0] = cosparg * cosnode - sinparg * cosincl * sinnode;
5988 pqr[1] = -sinparg * cosnode - cosparg * cosincl * sinnode;
5989 pqr[2] = sinincl * sinnode;
5990 pqr[3] = cosparg * sinnode + sinparg * cosincl * cosnode;
5991 pqr[4] = -sinparg * sinnode + cosparg * cosincl * cosnode;
5992 pqr[5] = -sinincl * cosnode;
5993 pqr[6] = sinparg * sinincl;
5994 pqr[7] = cosparg * sinincl;
5995 pqr[8] = cosincl;
5996 pqr[9] = sema;
5997 pqr[10] = ecce;
5998 pqr[11] = fac;
5999 }
6000
6001 static void osc_get_ecl_pos(double ean, double *pqr, double *xp)
6002 {
6003 double x[2];
6004 double cose = cos(ean * DEGTORAD);
6005 double sine = sin(ean * DEGTORAD);
6006 double sema = pqr[9];
6007 double ecce = pqr[10];
6008 double fac = pqr[11];
6009 x[0] = sema * (cose - ecce);
6010 x[1] = sema * fac * sine;
6011 /* transformation to ecliptic */
6012 xp[0] = pqr[0] * x[0] + pqr[1] * x[1];
6013 xp[1] = pqr[3] * x[0] + pqr[4] * x[1];
6014 xp[2] = pqr[6] * x[0] + pqr[7] * x[1];
6015 }
6016
6017 static double get_dist_from_2_vectors(double *x1, double *x2)
6018 {
6019 double r0, r1, r2;
6020 r0 = x1[0] - x2[0];
6021 r1 = x1[1] - x2[1];
6022 r2 = x1[2] - x2[2];
6023 return sqrt(r0 * r0 + r1 * r1 + r2 * r2);
6024 }
6025
6026 static void osc_iterate_max_dist(double ean, double *pqr, double *xa, double *xb, double *deanopt, double *drmax, AS_BOOL high_prec)
6027 {
6028 int i;
6029 double r, rmax, eansv = 0, dstep, dstep_min = 1;
6030 if (high_prec)
6031 dstep_min = 0.000001;
6032 ean = 0;
6033 osc_get_ecl_pos(ean, pqr, xa);
6034 r = get_dist_from_2_vectors(xb, xa);
6035 rmax = r;
6036 dstep = 1;
6037 while (dstep >= dstep_min) {
6038 //printf("r=%.17f, dstep=%f\n", r, dstep);
6039 for (i = 0; i < 2; i++) {
6040 while(r >= rmax) {
6041 eansv = ean;
6042 if (i == 0)
6043 ean += dstep;
6044 else
6045 ean -= dstep;
6046 osc_get_ecl_pos(ean, pqr, xa);
6047 r = get_dist_from_2_vectors(xb, xa);
6048 if (r > rmax)
6049 rmax = r;
6050 }
6051 ean = eansv;
6052 r = rmax;
6053 }
6054 ean = eansv;
6055 r = rmax;
6056 dstep /= 10;
6057 }
6058 *drmax = rmax;
6059 *deanopt = eansv;
6060 }
6061
6062 static void osc_iterate_min_dist(double ean, double *pqr, double *xa, double *xb, double *deanopt, double *drmin, AS_BOOL high_prec)
6063 {
6064 int i;
6065 double r, rmin, eansv = 0, dstep, dstep_min = 1;
6066 if (high_prec)
6067 dstep_min = 0.000001;
6068 ean = 0;
6069 osc_get_ecl_pos(ean, pqr, xa);
6070 r = get_dist_from_2_vectors(xb, xa);
6071 rmin = r;
6072 dstep = 1;
6073 while (dstep >= dstep_min) {
6074 //printf("r=%.17f, dstep=%f\n", r, dstep);
6075 for (i = 0; i < 2; i++) {
6076 while(r <= rmin) {
6077 eansv = ean;
6078 if (i == 0)
6079 ean += dstep;
6080 else
6081 ean -= dstep;
6082 osc_get_ecl_pos(ean, pqr, xa);
6083 r = get_dist_from_2_vectors(xb, xa);
6084 if (r < rmin)
6085 rmin = r;
6086 }
6087 ean = eansv;
6088 r = rmin;
6089 }
6090 ean = eansv;
6091 r = rmin;
6092 dstep /= 10;
6093 }
6094 *drmin = rmin;
6095 *deanopt = eansv;
6096 }
6097
6098 /* function calculates maximum distance, minimum distance and true distance between
6099 * the Sun and the Earth-Moon barycentre. Maximum and minimum distance are derived
6100 * from Kepler elements. */
6101 static int32 orbit_max_min_true_distance_helio(double tjd_et, int ipl, int32 iflag, double *dmax, double *dmin, double *dtrue, char *serr)
6102 {
6103 double xinner[3], pqri[20];
6104 double eani;
6105 double de[50];
6106 int32 retval;
6107 int32 ipli = ipl;
6108 int32 iflagi = (iflag & (SEFLG_EPHMASK | SEFLG_HELCTR | SEFLG_BARYCTR));
6109 if (ipl == SE_SUN) {
6110 ipli = SE_EARTH;
6111 }
6112 /* Kepler elements */
6113 if ((retval = swe_get_orbital_elements(tjd_et, ipli, iflagi, de, serr)) == ERR)
6114 return ERR;
6115 *dmax = de[16];
6116 *dmin = de[15];
6117 osc_get_orbit_constants(de, pqri);
6118 /* true distance */
6119 eani = de[8];
6120 /* heliocentric ecliptic position of EMB, cartesian coordinates J2000 */
6121 osc_get_ecl_pos(eani, pqri, xinner);
6122 /* its distance */
6123 *dtrue = sqrt(xinner[0] * xinner[0] + xinner[1] * xinner[1] + xinner[2] * xinner[2]);
6124 #ifdef DEBUG_REL_DIST
6125 printf("rtrue=%.17f (%.17f, %.17f\n", *dtrue, *dmin, *dmax);
6126 #endif
6127 return retval;
6128 }
6129
6130 /* This function calculates calculates the maximum possible distance, the
6131 * minimum possible distance, and the current true distance of planet, the EMB,
6132 * or an asteroid. The calculation can be done either heliocentrically or
6133 * geocentrically. With heliocentric calculations, it is based on the momentary
6134 * Kepler ellipse of the planet. With geocentric calculations, it is based on
6135 * the Kepler ellipses of the planet and the EMB. The geocentric calculation is
6136 * rather expensive.
6137 *
6138 * The problem is a bit tricky. The maximum and minimum possible distance of
6139 * an object from the earth can only be calculated over a limited time range,
6140 * not over the whole time the solar system exists. Since a scan of the whole
6141 * available time range is very costly, we should not do that. Alternatively,
6142 * one could create a database that provides the minimal and maximal distances
6143 * for each object. However, the creation and maintenance of such a database
6144 * would be expensive, too. In addition, since planetary orbits change over
6145 * time, a limited period won't provide a meaningful value.
6146 *
6147 * Instead, we determine the maximal and minimal distance from the osculating
6148 * ellipses of the planet and the Earth-Moon barycentre, assuming that both
6149 * the planet and the EMB could have any position on its respective ellipse.
6150 *
6151 * Note that instead of the position of the Earth, the position of the EMB
6152 * is used. Using the true position of the Earth would make the problem
6153 * considerably more complicated. Even if this were done, the Swiss Ephemeris
6154 * still is not able provide the true planets, but ony their barycentres. E.g.
6155 * it cannot provide the true position of Jupiter, but only the position of
6156 * the barycentre of the Jupiter system. The geocentric difference between
6157 * the two is below 0.2 arcsec for all planets.
6158 *
6159 * Input:
6160 * tjd_et epoch
6161 * ipl planet number
6162 * iflag ephemeris flag and optional heliocentrif flag (SEFLG_HELCTR)
6163 *
6164 * output:
6165 * dmax maximum distance (pointer to double)
6166 * dmin minimum distance (pointer to double)
6167 * dtrue true distance (pointer to double)
6168 * serr error string
6169 */
6170 int32 CALL_CONV swe_orbit_max_min_true_distance(double tjd_et, int32 ipl, int32 iflag, double *dmax, double *dmin, double *dtrue, char *serr)
6171 {
6172 int i, j, k, retval;
6173 int32 iflagi = (iflag & (SEFLG_EPHMASK | SEFLG_HELCTR | SEFLG_BARYCTR));
6174 double dp[50], de[50];
6175 double xouter[3], xinner[3], max_xouter[3], max_xinner[3], min_xouter[3], min_xinner[3], pqro[20], pqri[20];
6176 double eano, eani;
6177 double *douter, *dinner;
6178 double r, rtrue, rmax = 0, rmin = 100000000, rminsv = 0, rmaxsv = 0;
6179 double min_eanisv = 0, min_eanosv = 0, max_eanisv = 0, max_eanosv = 0;
6180 int ncnt;
6181 double dstep;
6182 double nitermax = 300;
6183 /* separate handling for the Sun, Moon and heliocentric calculation */
6184 if (ipl == SE_SUN || ipl == SE_MOON || (iflagi & (SEFLG_HELCTR | SEFLG_BARYCTR))) {
6185 retval = orbit_max_min_true_distance_helio(tjd_et, ipl, iflagi, dmax, dmin, dtrue, serr);
6186 return retval;
6187 }
6188 if ((retval = swe_get_orbital_elements(tjd_et, ipl, iflagi, dp, serr)) == ERR)
6189 return ERR;
6190 if ((retval = swe_get_orbital_elements(tjd_et, SE_EARTH, iflagi, de, serr)) == ERR)
6191 return ERR;
6192 if (de[0] > dp[0]) {
6193 douter = de;
6194 dinner = dp;
6195 } else {
6196 douter = dp;
6197 dinner = de;
6198 }
6199 osc_get_orbit_constants(douter, pqro);
6200 osc_get_orbit_constants(dinner, pqri);
6201 eano = douter[8]; // ecc. anomaly outer planet
6202 eani = dinner[8]; // ecc. anomaly inner planet
6203 osc_get_ecl_pos(eano, pqro, xouter); // coordinates outer planet J2000
6204 osc_get_ecl_pos(eani, pqri, xinner); // coordinates inner planet J2000
6205 rtrue = get_dist_from_2_vectors(xouter, xinner); // true distance between them
6206 // printf("rtrue=%.17f\n", rtrue);
6207 /* search rough maximum and minimum distance for objects on the two ellipses.
6208 * Attention, there may be two minima or maxima, and we need the smaller
6209 * minimum and the greate maximum.
6210 * To find minima and maxima, we start with a rough calculation: We move the
6211 * one planet through the whole orbit in two-degree steps. For each step,
6212 * we move the other planet through its own whole orbit at two-degree steps.
6213 * In vary rare cases we may get the wrong minimum. To avoid that, we would
6214 * have to make smaller steps, but that would considerably reduce
6215 * performance. A faster algorithm without this problem would require
6216 * considerably higher sophistication.
6217 * */
6218 ncnt = 182;
6219 dstep = 2;
6220 for (i = 0; i < 3; i++) { /* initialisation */
6221 max_xouter[i] = 0;
6222 max_xinner[i] = 0;
6223 min_xouter[i] = 0;
6224 min_xinner[i] = 0;
6225 }
6226 for (j = 0; j < ncnt; j++) {
6227 eano = (double) j * dstep;
6228 osc_get_ecl_pos(eano, pqro, xouter);
6229 for (i = 0; i < ncnt; i++) {
6230 eani = (double) i;
6231 osc_get_ecl_pos(eani, pqri, xinner);
6232 r = get_dist_from_2_vectors(xouter, xinner);
6233 /* maximum/minimum found; save positions and ecc. anomalies */
6234 if (r > rmax) {
6235 rmax = r;
6236 max_eanisv = eani;
6237 max_eanosv = eano;
6238 for (k = 0; k < 3; k++) {
6239 max_xouter[k] = xouter[k];
6240 max_xinner[k] = xinner[k];
6241 }
6242 }
6243 if (r < rmin) {
6244 rmin = r;
6245 min_eanisv = eani;
6246 min_eanosv = eano;
6247 for (k = 0; k < 3; k++) {
6248 min_xouter[k] = xouter[k];
6249 min_xinner[k] = xinner[k];
6250 }
6251 }
6252 }
6253 }
6254 /* find accurate values, starting iterations from above-calculated rough values;
6255 * maximum distance: */
6256 eani = max_eanisv;
6257 eano = max_eanosv;
6258 for (k = 0; k < 3; k++) {
6259 xouter[k] = max_xouter[k];
6260 xinner[k] = max_xinner[k];
6261 }
6262 for (k = 0; k <= nitermax; k++) {
6263 osc_iterate_max_dist(eani, pqri, xinner, xouter, &eani, &rmax, TRUE);
6264 osc_iterate_max_dist(eano, pqro, xouter, xinner, &eano, &rmax, TRUE);
6265 if (k > 0 && fabs(rmax - rmaxsv) < 0.00000001)
6266 break;
6267 rmaxsv = rmax;
6268 }
6269 /* minimum distance: */
6270 eani = min_eanisv;
6271 eano = min_eanosv;
6272 for (k = 0; k < 3; k++) {
6273 xouter[k] = min_xouter[k];
6274 xinner[k] = min_xinner[k];
6275 }
6276 for (k = 0; k <= nitermax; k++) {
6277 osc_iterate_min_dist(eani, pqri, xinner, xouter, &eani, &rmin, TRUE);
6278 osc_iterate_min_dist(eano, pqro, xouter, xinner, &eano, &rmin, TRUE);
6279 if (k > 0 && fabs(rmin - rminsv) < 0.00000001)
6280 break;
6281 rminsv = rmin;
6282 }
6283 *dmax = rmax;
6284 *dmin = rmin;
6285 *dtrue = rtrue;
6286 return retval;
6287 }
6288
6289 /* function finds the gauquelin sector position of a planet or fixed star
6290 *
6291 * if starname != NULL then a star is computed.
6292 * iflag: use the flags SE_SWIEPH, SE_JPLEPH, SE_MOSEPH, SEFLG_TOPOCTR.
6293 *
6294 * imeth defines method:
6295 * imeth = 0 use Placidus house position
6296 * imeth = 1 use Placidus house posiition (with planetary lat = 0)
6297 * imeth = 2 use rise and set of body's disc center
6298 * imeth = 3 use rise and set of body's disc center with refraction
6299 * rise and set are defined as appearance and disappearance of disc center
6300 *
6301 * geopos is an array of 3 doubles for geo. longitude, geo. latitude, elevation.
6302 * atpress and attemp are only needed for imeth = 3. If imeth = 3,
6303 * If imeth=3 and atpress not given (= 0), the programm assumes 1013.25 mbar;
6304 * if a non-zero height above sea is given in geopos, atpress is estimated.
6305 * dgsect is return area (pointer to a double)
6306 * serr is pointer to error string, may be NULL
6307 *
6308 */
6309 int32 CALL_CONV swe_gauquelin_sector(
6310 double t_ut, /* input time (UT) */
6311 int32 ipl, /* planet number, if planet, or moon;
6312 * ipl is ignored if the following parameter (starname) is set*/
6313 char *starname, /* star name, if star; otherwise NULL or empty */
6314 int32 iflag, /* flag for ephemeris and SEFLG_TOPOCTR */
6315 int32 imeth, /* method: 0 = with lat., 1 = without lat.,
6316 * 2 = from rise/set, 3 = from rise/set with refraction */
6317 double *geopos, /* array of three doubles containing
6318 * geograph. long., lat., height of observer */
6319 double atpress, /* atmospheric pressure, only useful with imeth=3;
6320 * if 0, default = 1013.25 mbar is used */
6321 double attemp, /* atmospheric temperature in degrees Celsius,
6322 *only useful with imeth=3 */
6323 double *dgsect, /* return address for gauquelin sector position */
6324 char *serr) /* return address for error message */
6325 {
6326 AS_BOOL rise_found = TRUE;
6327 AS_BOOL set_found = TRUE;
6328 int32 retval;
6329 double tret[3];
6330 double t_et, t;
6331 double x0[6];
6332 double eps, nutlo[2], armc;
6333 int32 epheflag = iflag & SEFLG_EPHMASK;
6334 AS_BOOL do_fixstar = (starname != NULL && *starname != '\0');
6335 int32 risemeth = 0;
6336 AS_BOOL above_horizon = FALSE;
6337 if (imeth < 0 || imeth > 5) {
6338 if (serr)
6339 sprintf(serr, "invalid method: %d", imeth);
6340 return ERR;
6341 }
6342 /* function calls for Pluto with asteroid number 134340
6343 * are treated as calls for Pluto as main body SE_PLUTO */
6344 if (ipl == SE_AST_OFFSET + 134340)
6345 ipl = SE_PLUTO;
6346 /*
6347 * geometrically from ecl. longitude and latitude
6348 */
6349 if (imeth == 0 || imeth == 1) {
6350 t_et = t_ut + swe_deltat_ex(t_ut, iflag, serr);
6351 eps = swi_epsiln(t_et, iflag) * RADTODEG;
6352 swi_nutation(t_et, iflag, nutlo);
6353 nutlo[0] *= RADTODEG;
6354 nutlo[1] *= RADTODEG;
6355 armc = swe_degnorm(swe_sidtime0(t_ut, eps + nutlo[1], nutlo[0]) * 15 + geopos[0]);
6356 if (do_fixstar) {
6357 if (swe_fixstar(starname, t_et, iflag, x0, serr) == ERR)
6358 return ERR;
6359 } else {
6360 if (swe_calc(t_et, ipl, iflag, x0, serr) == ERR)
6361 return ERR;
6362 }
6363 if (imeth == 1)
6364 x0[1] = 0;
6365 *dgsect = swe_house_pos(armc, geopos[1], eps + nutlo[1], 'G', x0, NULL);
6366 return OK;
6367 }
6368 /*
6369 * from rise and set times
6370 */
6371 if (imeth == 2 || imeth == 4)
6372 risemeth |= SE_BIT_NO_REFRACTION;
6373 if (imeth == 2 || imeth == 3)
6374 risemeth |= SE_BIT_DISC_CENTER;
6375 /* find the next rising time of the planet or star */
6376 retval = swe_rise_trans(t_ut, ipl, starname, epheflag, SE_CALC_RISE|risemeth, geopos, atpress, attemp, &(tret[0]), serr);
6377 if (retval == ERR) {
6378 return ERR;
6379 } else if (retval == -2) {
6380 /* actually, we could return ERR here. However, we
6381 * keep this variable, in case we implement an algorithm
6382 * for Gauquelin sector positions of circumpolar bodies.
6383 * As with the Ludwig Otto procedure with Placidus, one
6384 * could replace missing rises or sets by meridian transits,
6385 * although there are cases where even this is not possible.
6386 * Sometimes a body both appears and disappears on the western
6387 * part of the horizon. Using true culminations rather than meridan
6388 * transits would not help in any case either, because there are
6389 * cases where a body does not have a culmination within days,
6390 * e.g. the sun near the poles.
6391 */
6392 rise_found = FALSE;
6393 }
6394 /* find the next setting time of the planet or star */
6395 retval = swe_rise_trans(t_ut, ipl, starname, epheflag, SE_CALC_SET|risemeth, geopos, atpress, attemp, &(tret[1]), serr);
6396 if (retval == ERR) {
6397 return ERR;
6398 } else if (retval == -2) {
6399 set_found = FALSE;
6400 }
6401 if (tret[0] < tret[1] && rise_found == TRUE) {
6402 above_horizon = FALSE;
6403 /* find last set */
6404 t = t_ut - 1.2;
6405 if (set_found) t = tret[1] - 1.2;
6406 set_found = TRUE;
6407 retval = swe_rise_trans(t, ipl, starname, epheflag, SE_CALC_SET|risemeth, geopos, atpress, attemp, &(tret[1]), serr);
6408 if (retval == ERR) {
6409 return ERR;
6410 } else if (retval == -2) {
6411 set_found = FALSE;
6412 }
6413 } else if (tret[0] >= tret[1] && set_found == TRUE) {
6414 above_horizon = TRUE;
6415 /* find last rise */
6416 t = t_ut - 1.2;
6417 if (rise_found) t = tret[0] - 1.2;
6418 rise_found = TRUE;
6419 retval = swe_rise_trans(t, ipl, starname, epheflag, SE_CALC_RISE|risemeth, geopos, atpress, attemp, &(tret[0]), serr);
6420 if (retval == ERR) {
6421 return ERR;
6422 } else if (retval == -2) {
6423 rise_found = FALSE;
6424 }
6425 }
6426 if (rise_found && set_found) {
6427 if (above_horizon) {
6428 *dgsect = (t_ut - tret[0]) / (tret[1] - tret[0]) * 18 + 1;
6429 } else {
6430 *dgsect = (t_ut - tret[1]) / (tret[0] - tret[1]) * 18 + 19;
6431 }
6432 return OK;
6433 } else {
6434 *dgsect = 0;
6435 if (serr)
6436 sprintf(serr, "rise or set not found for planet %d", ipl);
6437 return ERR;
6438 }
6439 }
6440