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