1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2012 Gerhard HOLTKAMP
4 //
5 
6 /***************************************************************************
7 * Calculate Solar Eclipses                                                 *
8 *                                                                          *
9 *                                                                          *
10 * The algorithm for the phases of the Moon and of the dates for the        *
11 * equinoxes and solstices of the Sun as well as the dates of eclipses      *
12 * are based on Jean Meeus "Astronomical Formulae for Calculators",         *
13 * Willman-Bell, Inc., Richmond, Virginia, 1988                             *
14 *                                                                          *
15 * The algorithm for the Modified Julian Date and the calendar as well as   *
16 * the detailed eclipse calculations are based on                           *
17 * O.Montebruck and T.Pfleger, "Astronomy with a PC",                       *
18 * Springer Verlag, Berlin, Heidelberg, New York, 1989                      *
19 * and on the                                                               *
20 * "Explanatory Supplement to the Astronomical Almanac"                     *
21 * University Science Books, Mill Valley, CA, 1992                          *
22 *                                                                          *
23 * Open Source Code. License: GNU LGPL Version 2+                          *
24 *                                                                          *
25 * Author: Gerhard HOLTKAMP,        28-JAN-2013                              *
26 ***************************************************************************/
27 
28 /*------------ include files and definitions -----------------------------*/
29 #include "eclsolar.h"
30 #include <cstdio>
31 #include <cstdlib>
32 #include <cstring>
33 #include <cmath>
34 #include <ctime>
35 using namespace std;
36 
37 #include "astrolib.h"
38 
39 /* const double PI = 3.14159265359; */
40 const double degrad = M_PI / 180.0;
41 
42 // ################ Solar Eclipse Class ####################
43 
EclSolar()44 EclSolar::EclSolar()
45 {
46  esinit();
47 }
48 
~EclSolar()49 EclSolar::~EclSolar()
50 {
51 
52 }
53 
atan23(double y,double x)54 double EclSolar::atan23 (double y, double x)
55  {
56   /* redefine atan2 so that it doesn't crash when both x and y are 0 */
57   double result;
58 
59   if ((x == 0) && (y == 0)) result = 0;
60   else result = atan2 (y, x);
61 
62   return result;
63  }
64 
esinit()65 void EclSolar::esinit()
66 {
67  // initialize eclipse data
68   eb_start_called = false;
69   eb_moonph_called = false;
70   eb_lunecl = true;
71   eb_lunactive = false;
72   eb_local_called = false;
73 
74   eb_day = 1;
75   eb_month = 1;
76   eb_year = 2012;
77   eb_hour = 0;
78   eb_minute = 0;
79   eb_second = 0;
80   eb_tzone = 0;
81   eb_del_auto = 1;
82   DefTime();
83   eb_time = 0;
84   eb_del_tdut = DefTdUt(eb_year);
85   eb_geolat = 0;
86   eb_geolong = 0;
87   eb_geoheight = 0;
88   eb_lstcall = 0;
89   eb_locecl = 0;
90   eb_lastyear = -9999;
91   eb_numecl = 0;
92   eb_lasttz = eb_tzone;
93   eb_lastdlt = eb_del_tdut;
94   eb_cstep = 1.0;
95   eb_cmxlat = 0;
96   eb_cmxlng = 0;
97   eb_penangle = 1.0;
98   eb_penamode = 0;
99 }
100 
DefTime()101 void EclSolar::DefTime ()  // Get System Time and Date
102  {
103   time_t tt;
104   int hh, mm, ss;
105   double jd, hr;
106 
107   tt = time(NULL);
108   jd = 40587.0 + tt/86400.0; // seconds since 1-JAN-1970
109 
110   caldat(jd, hh, mm, ss, hr);
111   eb_year = ss;
112   eb_month = mm;
113   eb_day = hh;
114 
115   dms(hr, hh, mm, jd);
116   eb_hour = hh;
117   eb_minute = mm;
118   eb_second = int(jd);
119   if (eb_del_auto) eb_del_tdut = DefTdUt(eb_year);
120   };
121 
getYear() const122 int EclSolar::getYear() const
123 {
124   return eb_year;
125 }
126 
putYear(int yr)127 void EclSolar::putYear(int yr)
128 {
129   eb_start_called = false;
130   eb_moonph_called = false;
131   eb_local_called = false;
132   eb_lunactive = false;
133 
134   eb_year = yr;
135   if (eb_del_auto)  eb_del_tdut = DefTdUt(eb_year);
136   moonph();
137 
138 }
139 
getNumberEclYear()140 int EclSolar::getNumberEclYear()
141 {
142  // RETURN the number of eclipses of the currently selected year
143 
144     int j, k;
145 
146     if (!eb_moonph_called) moonph();  // calculate the eclipses of the year
147 
148     if (eb_lunecl) return eb_numecl;
149 
150     k = 0;
151     for (j=0; j<eb_numecl; j++)
152     {
153         if (eb_phase[j] > 0) k++;
154     }
155 
156     return k;
157 }
setLunarEcl(bool lecl)158 void EclSolar::setLunarEcl(bool lecl)
159 {
160   // if lecl = true include lunar eclipses in addition to solar ones
161   if (lecl) eb_lunecl = true;
162   else eb_lunecl = false;
163   eb_start_called = false;
164   eb_local_called = false;
165 }
166 
setStepWidth(double s)167 void EclSolar::setStepWidth(double s)
168 {
169   // set the step width (in minutes) used for calculations
170   if (s < 0.01) eb_cstep = 0.01;
171   else eb_cstep = s;
172 }
173 
setTimezone(double d)174 void EclSolar::setTimezone(double d)
175 {
176   // set the timezone to d (hours difference from UTC) for I/O
177   eb_tzone = d;
178 }
179 
setDeltaTAI_UTC(double d)180 void EclSolar::setDeltaTAI_UTC(double d)
181 {
182   // c is the difference between TAI and UTC according to the IERS
183   // we have to add 32.184 sec to get to the difference TT - UT
184   // which is used in the calculations here
185 
186   eb_del_tdut = d + 32.184;
187   eb_del_auto = 0;
188 }
189 
setAutoTAI_UTC()190 void EclSolar::setAutoTAI_UTC()
191 {
192   // set the difference between TAI and UTC according to the IERS
193   eb_del_auto = true;
194   eb_del_tdut = DefTdUt(eb_year);
195 }
196 
setLocalPos(double lat,double lng,double hgt)197 void EclSolar::setLocalPos(double lat, double lng, double hgt)
198 {
199   // set the geographic coordinates for the position of the local info
200   // latitude lat, longitude lng in decimal degrees
201   // height hgt in meters
202 
203   eb_geolat = lat;
204   eb_geolong = lng;
205   eb_geoheight = hgt;
206 
207   eb_local_called = false;
208 }
209 
getLocalVisibility(double & mjd_start,double & mjd_stop)210 int EclSolar::getLocalVisibility(double &mjd_start, double &mjd_stop)
211 {
212     // local start and stop times (MJD) for eclipse
213     // RETURN 0 if not locally visible
214 
215     char wbuf[700];
216 
217     if (!eb_local_called) getLocalDetails(wbuf);
218 
219     mjd_start = eb_lcb1;
220     mjd_stop = eb_lce1;
221 
222     return eb_lccnt;
223 }
224 
getLocalTotal(double & mjd_start,double & mjd_stop)225 int EclSolar::getLocalTotal(double &mjd_start, double &mjd_stop)
226 {
227     // local start and stop times (MJD) for totality/annularity
228     // RETURN 0 if not locally visible
229 
230     int k, j;
231     char wbuf[700];
232 
233     if (!eb_local_called) getLocalDetails(wbuf);
234 
235     mjd_start = 0;
236     mjd_stop = 0;
237 
238     if(eb_lccnt == 0) return 0;
239 
240     k = 0;
241     if(eb_lunactive)
242     {
243         for (j=0; j<eb_nphase; j++)
244         {
245            if ((k==0) && (eb_spp[j] > 3))
246            {
247                mjd_start = eb_spt[j];
248                mjd_stop  = eb_ept[j];
249                k = 1;
250            }
251         }
252 
253         if(mjd_start < eb_lcb1) mjd_start = eb_lcb1;
254         if(mjd_start > eb_lce1) k = 0;
255         if(mjd_stop > eb_lce1) mjd_stop = eb_lce1;
256         if(mjd_stop < eb_lcb1) k = 0;
257 
258         eb_ltotb = mjd_start;
259         eb_ltote = mjd_stop;
260 
261     }
262     else k = eb_lccnt;
263 
264     mjd_start = eb_ltotb;
265     mjd_stop = eb_ltote;
266 
267     return k;
268 }
269 
getLocalMax(double & mjdmax,double & magmax,double & elmax)270 int EclSolar::getLocalMax(double &mjdmax, double &magmax, double &elmax)
271 {
272 // get local (solar) eclipse maximum
273 // mjdmax : Modified Julian Date of maximum eclipse
274 // magmax : maximum (local) magnitude of eclipse
275 // elmax : local Sun elevation at maximum
276 
277 // RETURN : 0 if eclipse not visible
278 
279 int k;
280 
281     mjdmax = 0;
282     magmax = 0;
283     elmax = 0;
284 
285     if (eb_lunactive) return 0;
286 
287     k = getLocalVisibility(mjdmax, magmax);
288 
289     if (k != 0)
290     {
291      mjdmax = eb_jdmaxps;
292      magmax = eb_maxps;
293      elmax = eb_maxelv;
294     }
295 
296     return k;
297 }
298 
getPenumbra(double & mjd_start,double & mjd_stop)299 int EclSolar::getPenumbra(double &mjd_start, double &mjd_stop)
300 {
301     // start and stop times (MJD) for penumbral eclipse of Moon
302     // RETURN 0 if no lunar eclipse
303 
304     int j, k;
305 
306     if (!eb_start_called) eclStart();
307 
308     mjd_start = 0;
309     mjd_stop = 0;
310 
311     if (!eb_lunactive) return 0;
312 
313     if (eb_nphase <= 0) return 0;
314 
315     k = 0 ;
316     for (j=0; j<eb_nphase; j++)
317     {
318         if (eb_spp[j] == 1)
319         {
320             mjd_start = eb_spt[j];
321             mjd_stop  =eb_ept[j];
322             k = 1;
323         }
324     }
325 
326     return k;
327 }
328 
getPartial(double & mjd_start,double & mjd_stop)329 int EclSolar::getPartial(double &mjd_start, double &mjd_stop)
330 {
331     // (global) start and stop times (MJD) for partial phase
332     // RETURN 0 if no lunar eclipse
333 
334     int j, k;
335 
336     if (!eb_start_called) eclStart();
337 
338     mjd_start = 0;
339     mjd_stop = 0;
340 
341     if (eb_nphase <= 0) return 0;
342 
343     k = 0;
344 
345     if(eb_lunactive)
346     {
347         for (j=0; j<eb_nphase; j++)
348         {
349            if ((k==0) && (eb_spp[j] == 3))
350            {
351                mjd_start = eb_spt[j];
352                mjd_stop  =eb_ept[j];
353                k = 1;
354            }
355         }
356     }
357     else
358     {
359      for (j=0; j<eb_nphase; j++)
360      {
361         if ((k==0) && (eb_spp[j] == 1))
362         {
363             mjd_start = eb_spt[j];
364             mjd_stop  =eb_ept[j];
365             k = 1;
366         }
367      }
368     }
369 
370     return k;
371 
372 }
373 
getTotal(double & mjd_start,double & mjd_stop)374 int EclSolar::getTotal(double &mjd_start, double &mjd_stop)
375 {
376     // (global) start and stop times (MJD) for totality/annularity
377     // RETURN 0 if no lunar eclipse
378 
379     int j, k;
380 
381     if (!eb_start_called) eclStart();
382 
383     mjd_start = 0;
384     mjd_stop = 0;
385 
386     if (eb_nphase <= 0) return 0;
387 
388     k = 0;
389 
390     if(eb_lunactive)
391     {
392         for (j=0; j<eb_nphase; j++)
393         {
394            if ((k==0) && (eb_spp[j] > 3))
395            {
396                mjd_start = eb_spt[j];
397                mjd_stop  =eb_ept[j];
398                k = 1;
399            }
400         }
401     }
402     else
403     {
404      for (j=0; j<eb_nphase; j++)
405      {
406         if ((k==0) && (eb_spp[j] > 1))
407         {
408             mjd_start = eb_spt[j];
409             mjd_stop  =eb_ept[j];
410             k = 1;
411         }
412      }
413     }
414 
415     return k;
416 
417 }
418 
setCurrentMJD(int year,int month,int day,int hour,int min,double sec)419 void EclSolar::setCurrentMJD(int year, int month, int day, int hour, int min, double sec)
420 {
421     // set the (MJD-) time currently used for calculations to year, month, day, hour, min, sec
422     // corrected for timezone
423 
424     double jd;
425 
426     jd = ddd(hour, min, sec) - eb_tzone;
427     jd = mjd(day, month, year, jd);
428 
429     eb_lastjd = jd;
430 
431 }
432 
getDatefromMJD(double mjd,int & year,int & month,int & day,int & hour,int & min,double & sec) const433 void EclSolar::getDatefromMJD(double mjd, int &year, int &month, int &day, int &hour, int &min, double &sec) const
434 {
435     // convert times given in Modified Julian Date (MJD) into conventional date and time
436     // correct for timezone
437 
438     double magn;
439 
440     caldat((mjd + eb_tzone/24.0), day, month, year, magn);
441     dms (magn,hour,min,sec);
442     if (sec>30.0) min++;
443     if (min>59)
444      {
445       hour++;
446       min=0;
447      };
448 
449 }
450 
451 //---------------------- getEclYearInfo--------------------------------
452 
getEclYearInfo(char * wbuf)453 void EclSolar::getEclYearInfo(char* wbuf)
454  {
455   // output the eclipse dates in buffer wbuf accurate to the minute.
456 
457   char dts[13];
458   char outbuf[127];
459   char magbuf[30];
460   int j, p, kecl;
461 
462   if (!eb_moonph_called) moonph();  // calculate the eclipses of the year
463 
464   sprintf(wbuf,"Solar Eclipses for %4i  UTC +%4.1f", eb_year, eb_tzone);
465 
466   kecl = 1;
467   for (j=0; j<eb_numecl; j++)
468    {
469     sprintf(dts,"%1i : ", kecl);
470     strcpy (outbuf,dts);
471     dtmstr((eb_eclmjd[j] + eb_tzone/24.0),dts);
472     dts[12] = '\0';
473     strcat (outbuf,dts);
474     p = eb_phase[j];
475 
476     switch (p)
477      {
478       case 1: strcat(outbuf,"\t Partial Sun");
479               sprintf(magbuf,"  (magnitude:%5.2f)",eb_magnitude[j]);
480               strcat(outbuf,magbuf);
481               break;
482 
483       case 2: strcat(outbuf,"\t non-central Annular Sun");
484               break;
485       case 4: strcat(outbuf,"\t Annular Sun");
486               break;
487 
488       case 3: strcat(outbuf, "\t non-central Total Sun");
489               break;
490       case 5: strcat(outbuf, "\t Total Sun");
491               break;
492 
493       case 6: strcat(outbuf, "\t Annular/Total Sun");
494               break;
495 
496       case -1:
497       case -2: strcat(outbuf, "\t Penumbral Moon");
498                sprintf(magbuf,"  (magnitude:%5.2f)",eb_magnitude[j]);
499                strcat(outbuf,magbuf);
500                break;
501 
502       case -3: strcat(outbuf, "\t Partial Moon");
503                sprintf(magbuf,"  (magnitude:%5.2f)",eb_magnitude[j]);
504                strcat(outbuf,magbuf);
505                break;
506 
507       case -4: strcat(outbuf, "\t Total Moon");
508                sprintf(magbuf,"  (magnitude:%5.2f)",eb_magnitude[j]);
509                strcat(outbuf,magbuf);
510                break;
511      };
512 
513     if ((p > 0) || eb_lunecl)  // solar eclipse only or also lunar eclipses
514      {
515       strcat (wbuf, "\n");
516       strcat (wbuf, outbuf);
517       kecl++;
518      };
519    }
520  }
521 
getEclYearInfo(int k,int & yr,int & month,int & day,int & hour,int & min,double & sec,double & tzone,double & magn)522 int EclSolar::getEclYearInfo(int k, int &yr, int &month, int &day,
523                              int &hour, int &min, double &sec, double &tzone, double &magn)
524 {
525   /* output the eclipse info for k-th eclipse
526 
527      year, month, day, hour, minutes, seconds and timezone
528      magn = magnitude of eclipse
529 
530    RETURN: phase of eclipse. 0 if no k-th eclipse
531 
532       1: Partial Sun
533       2: non-central Annular Sun
534       3: non-central Total Sun
535       4: Annular Sun
536       5: Total Sun
537       6: Annular/Total Sun
538 
539      -1, -2: Penumbral Moon
540      -3: Partial Moon
541      -4: Total Moon
542 
543  */
544   bool nls;
545   int j, p, kecl;
546 
547   nls = true;
548 
549   if (!eb_moonph_called) moonph();  // calculate the eclipses of the year
550 
551   if (k < 1)
552   {
553       k = eb_eclselect;  // select current eclipse as default
554       nls = false;
555   };
556   if ((k < 1) && (k > eb_numecl)) return 0;
557 
558   p = k - 1;
559   if (nls && (!eb_lunecl))
560   {
561    kecl = 0;
562    p = -1;
563    for(j=0; j<eb_numecl; j++)
564    {
565        if (eb_phase[j] > 0)
566        {
567            kecl++;
568            if (kecl == k) p = j;
569        };
570    };
571   };
572 
573   if (p < 0) return 0;
574 
575   j = p;
576 
577   // convert MJD into corresponding date and time
578   caldat((eb_eclmjd[j] + eb_tzone/24.0), day, month, yr, magn);
579   dms (magn,hour,min,sec);
580   if (sec>30.0) min++;
581   if (min>59)
582    {
583     hour++;
584     min=0;
585    };
586 
587   magn = eb_magnitude[j];
588   tzone = eb_tzone;
589 
590   return eb_phase[j];
591 }
592 
getEclTxt(int k,char * jtxt)593 int EclSolar::getEclTxt (int k, char* jtxt)
594  {
595   // get the data / kind of eclipse info for the j-th eclipse
596   // and place it into jtxt
597 
598     // RETURN : the phase of the eclipse. 0 if no j-th eclipse
599 
600   int p, j;
601   char dts[13];
602 
603   if (!eb_moonph_called) moonph();  // calculate the eclipses of the year
604 
605   if (k < 1) k = eb_eclselect;  // select current eclipse as default
606   if ((k < 1) && (k > eb_numecl)) return 0;
607 
608   j = k-1;
609 
610   sprintf(jtxt, "%2i :", (j+1));
611   sprintf(dts, "%5i ", eb_year);
612   strcat (jtxt, dts);
613   dtmstr((eb_eclmjd[j]+eb_tzone/24.0),dts);
614   dts[6] = '\0';
615   strcat (jtxt,dts);
616 
617   p = eb_phase[j];
618   switch (p)
619    {
620     case 1: strcat(jtxt," Par.Sun");
621             break;
622     case 2: strcat(jtxt, " non-centr.Ann.Sun");
623             break;
624     case 4: strcat(jtxt," Ann.Sun");
625             break;
626     case 3: strcat(jtxt," non-centr.Tot.Sun");
627             break;
628     case 5: strcat(jtxt," Tot.Sun");
629             break;
630     case 6: strcat(jtxt," Ann/Tot.");
631             break;
632 
633     case -1:
634     case -2: strcat(jtxt," Pen.Moon");
635              break;
636     case -3: strcat(jtxt," Par.Moon");
637              break;
638     case -4: strcat(jtxt," Tot.Moon");
639              break;
640    };
641 
642   return p;
643  }
644 
645 // ----------------------------- Select Eclipse -------------------------
646 
putEclSelect(int es)647 void EclSolar::putEclSelect(int es)
648 {
649  // store the number of the eclipse selected for detailed calculations
650  int k, j;
651 
652  if (!eb_moonph_called) moonph();  // calculate the eclipses of the year
653 
654  k = 0;
655 
656  eb_lunactive = false;
657  eb_eclselect = 1;
658  for (j=0; j<eb_numecl; j++)
659   {
660    if ((eb_phase[j] > 0) || eb_lunecl)  // only solar eclipses if set
661     {
662      k++;
663      if (k == es)
664      {
665          eb_eclselect = j + 1;
666          if (eb_phase[j] < 0) eb_lunactive = true;
667      };
668     };
669   };
670  eb_start_called = false;
671 }
672 
nextEcl()673 void EclSolar::nextEcl()
674 {
675  // select the next eclipse for detailed calculations
676  int k, j, es;
677 
678  if (!eb_moonph_called) moonph();  // calculate the eclipses of the year
679  eb_start_called = false;
680 
681  k = eb_eclselect + 1;
682  if (k > eb_numecl)
683  {
684      k = eb_year + 1;
685      putYear (k);
686      k = 1;
687      putEclSelect(k);
688 
689      return;
690  };
691 
692  if (eb_lunecl)  // easier when lunar eclipses are included
693  {
694      putEclSelect(k);
695 
696      return;
697  };
698 
699  eb_lunactive = false;
700 
701  es = eb_eclselect;
702  k = 0;
703  for (j=es; j<eb_numecl; j++)
704  {
705     if ((k == 0) && (eb_phase[j] > 0)) k = j + 1;
706  };
707 
708  if(k > 0)
709  {
710     eb_eclselect = k;
711     return;
712  }
713 
714  k = eb_year + 1;
715  putYear (k);
716  es = 1;
717  putEclSelect(es);
718 
719 }
720 
previousEcl()721 void EclSolar::previousEcl()
722 {
723  // select the previous eclipse for detailed calculations
724  int k, j, es;
725 
726  if (!eb_moonph_called) moonph();  // calculate the eclipses of the year
727  eb_start_called = false;
728 
729  k = eb_eclselect - 1;
730 
731  if (k <= 0)
732  {
733      k = eb_year - 1;
734      putYear (k);
735      k = eb_numecl;
736  };
737 
738  if (eb_lunecl)  // easier when lunar eclipses are included
739  {
740      putEclSelect(k);
741 
742      return;
743  };
744 
745  eb_lunactive = false;
746 
747  es = 0;
748  k--;
749  for (j=k; j>=0; j--)
750  {
751      if ((es == 0) && (eb_phase[j] > 0)) es = j + 1;
752  };
753 
754  if(es > 0) eb_eclselect = es;
755  else putEclSelect(0);  // this case should never happen!
756 
757 }
758 
getLastMJD() const759 double EclSolar::getLastMJD() const
760 {
761  // RETURN the MJD last used in calculations
762 
763     return eb_lastjd;
764 }
765 
setPenumbraAngle(double pa,int mode)766 void EclSolar::setPenumbraAngle(double pa, int mode)
767 {
768     /* set the Penumbra Angle
769        if mode == 0 the angle will be set to pa-times the maximum angle
770        (pa < 1.0). Set pa = 1 for the normal penumbra boundaries
771 
772        if mode == 1 the angle will be set such that the penumbra line
773        markes magnitude pa. pa == 0 will mark normal penumbra boundaries
774 
775        if mode == 2 the angle will be set such that the penumbra line
776        markes the obscuration pa. pa == 0.5 will mean that 50% of the Sun's
777        disk is covered by the Moon etc.
778     */
779 
780     double mjd, dpn1, dpn2, pan1, pan2;
781     double mag, m1, m2, s, ps;
782     int j;
783     Vec3 ubm, ube;
784     Eclipse eclp;
785 
786     if (mode == 0)
787     {
788         eb_penangle = pa;
789         eb_penamode = 0;
790         if (pa > 1.0) eb_penangle = 1.0;
791         if (pa < 0) eb_penangle = 1.0;
792 
793         return;
794     }
795 
796     if (!eb_start_called) eclStart();
797 
798     if (mode == 1)
799     {
800         eb_penamode = 1;
801         mjd = eb_eclmjd[eb_eclselect-1];
802         eclp.umbra (mjd, eb_del_tdut, ubm, ube, dpn1, pan1);
803         eclp.penumd (mjd, eb_del_tdut, ubm, ube, dpn2, pan2);
804 
805         if(dpn2 > 0)
806         {
807             eb_penangle = fabs(pa);
808             if(eb_penangle > 1.0) eb_penangle = 1.0;
809             eb_penangle = 1.0 - eb_penangle * (1.0 + dpn1 / dpn2);
810         }
811         else eb_penangle = 1.0;
812 
813         return;
814     }
815 
816     if (mode == 2)
817     {
818         eb_penamode = 2;
819         mjd = eb_eclmjd[eb_eclselect-1];
820         eclp.umbra (mjd, eb_del_tdut, ubm, ube, dpn1, pan1);
821         eclp.penumd (mjd, eb_del_tdut, ubm, ube, dpn2, pan2);
822 
823         ps = pa;  // find the magnitude that corresponds to the obscuration
824         if (ps > 1.0) ps = 1.0;
825         if (ps < 0) ps = 0;
826         for (j=1; j<11; j++)
827         {
828             mag = double(j) * 0.1;
829             s = sunObscure(dpn2, dpn1, mag);
830             if (s >= ps) break;
831         }
832 
833         m1 = mag - 0.1;
834         m2 = mag;
835         for (j=0; j<8; j++)  // 8 iterations should be plenty
836         {
837            mag = (m2 + m1) * 0.5;
838            s = sunObscure(dpn2, dpn1, mag);
839            if (s > ps) m2 = mag;
840            else m1 = mag;
841         }
842 
843         if(dpn2 > 0)
844         {
845             eb_penangle = fabs(mag);
846             if(eb_penangle > 1.0) eb_penangle = 1.0;
847             eb_penangle = 1.0 - eb_penangle * (1.0 + dpn1 / dpn2);
848         }
849         else eb_penangle = 1.0;
850 
851         return;
852     }
853 
854     eb_penamode = 0;
855     eb_penangle = 1.0;
856 
857 }
858 
859 // -------- auxiliary functions --------------------------------
GetMonth(int mm,char * mchr)860 void EclSolar::GetMonth (int mm, char* mchr)
861  {
862   // get three letter designation of month
863   // mm is the number of the month
864   // mchr is a char[4] array to receive the three letters of the month
865 
866   switch (mm)
867    {
868     case 1 : strcpy(mchr,"JAN"); break;
869     case 2 : strcpy(mchr,"FEB"); break;
870     case 3 : strcpy(mchr,"MAR"); break;
871     case 4 : strcpy(mchr,"APR"); break;
872     case 5 : strcpy(mchr,"MAY"); break;
873     case 6 : strcpy(mchr,"JUN"); break;
874     case 7 : strcpy(mchr,"JUL"); break;
875     case 8 : strcpy(mchr,"AUG"); break;
876     case 9 : strcpy(mchr,"SEP"); break;
877     case 10 : strcpy(mchr,"OCT"); break;
878     case 11 : strcpy(mchr,"NOV"); break;
879     case 12 : strcpy(mchr,"DEC"); break;
880     default: strcpy(mchr,"ERR");
881    };
882 }
883 
phmjd(double yearf,double phase,double tdut,int & eph,double & ejd,double & emag)884 double EclSolar::phmjd (double yearf, double phase, double tdut,
885 				  int& eph, double& ejd, double& emag)
886   /*
887     Calculate the Modified Julian Date of the occurrence of the specified
888     phase of the Moon and check for possible eclipses.
889     yearf : year.fraction of date close to the Moon phase
890     phase : 0    for New Moon,
891             0.25 for First Quarter,
892             0.5  for Full Moon,
893             0.75 for Last Quarter.
894     tdut = TDT - UT in sec
895 
896     RETURN: the MJD of the lunar phase event
897 
898     OUTPUT:
899     eph : phase of the eclipse on that date
900           0 = no eclipse, 1 = partial solar eclipse,
901           2 = non-central annular, 3 = non-central total
902           4 = annular solar, 5 = total solar,
903           6 = annular/total solar, -1 =partial penumbral lunar,
904           -2 = total penumbral lunar, -3 = partial umbral lunar,
905           -4 = total umbral lunar
906     ejd : Modified Julian Date of the maximum of the eclipse (if any)
907     emag : magnitude of the eclipse (if any)
908   */
909  {
910   double tt, jd, k, m, p, f;
911   double s, c, gam, u;
912   int tst;
913   Eclipse eclp;
914 
915   // preliminary (modified) Julian Date
916   k = floor ((yearf - 1900.0) * 12.3685) + phase;
917   tt = k / 1236.85;
918   jd = 166.56 + (132.87 - 0.009173*tt)*tt;
919   jd = 15020.25933 + 29.53058868*k
920                    + (0.0001178 - 0.000000155*tt)*tt*tt
921                    + 0.00033 * sin (degrad*jd);
922 
923   eph = 0;   // assume no eclipse
924 
925   // Sun's mean anomaly
926   m = degrad * (359.2242 + 29.10535608*k
927                - (0.0000333 - 0.00000347*tt)*tt*tt);
928   // Moon's mean anomaly
929   p = degrad * (306.0253 + 385.81691808*k
930               + (0.0107306 + 0.00001236*tt)*tt*tt);
931   // 2* Moon's argument of latitude
932   f = 2.0 * degrad * (21.2964 + 390.67050646*k
933                    - (0.0016528 - 0.00000239*tt)*tt*tt);
934 
935   if ((phase == 0) || (phase == 0.5))  // for Full and New Moon
936    {
937     k = (0.1734 - 0.000393*tt) * sin (m)
938         + 0.0021 * sin (2.0 * m)
939         - 0.4068 * sin (p)
940         + 0.0161 * sin (2.0 * p)
941         - 0.0004 * sin (3.0 * p)
942         + 0.0104 * sin (f)
943         - 0.0051 * sin (m + p)
944         - 0.0074 * sin (m - p)
945         + 0.0004 * sin (f + m)
946         - 0.0004 * sin (f - m)
947         - 0.0006 * sin (f + p)
948         + 0.0010 * sin (f - p)
949         + 0.0005 * sin (m + 2.0*p);
950 
951     // check for possible eclipses.
952     f = f / 2.0;
953     if (fabs(sin(f)) <= 0.36)   // eclipses are possible
954      {
955       ejd = (0.1734 - 0.000393*tt) * sin(m)
956             + 0.0021 * sin(2.0*m)
957             - 0.4068 * sin(p)
958             + 0.0161 * sin(2.0*p)
959             - 0.0051 * sin(m+p)
960             - 0.0074 * sin(m-p)
961             - 0.0104 * sin(2.0*f);
962       ejd += jd;
963 
964       s = 5.19595 - 0.0048*cos(m) + 0.0020*cos(2.0*m) - 0.3283*cos(p)
965                   - 0.0060*cos(m+p) + 0.0041*cos(m-p);
966 
967       c = 0.2070*sin(m) + 0.0024*sin(2.0*m) - 0.0390*sin(p)
968                         + 0.0115*sin(2.0*p) - 0.0073*sin(m+p)
969                         - 0.0067*sin(m-p) + 0.0117*sin(2.0*f);
970 
971       gam = s*sin(f) + c*cos(f);
972       u = 0.0059 + 0.0046*cos(m) - 0.0182*cos(p)
973                  + 0.0004*cos(2.0*p) - 0.0005*cos(m+p);
974 
975       if (phase == 0)          // check for solar eclipse
976        {
977         if (fabs(gam) <= (1.5432+u))
978          {
979           if (fabs(gam) < 0.9972)    // central eclipse
980            {
981             emag = 1.0;
982             if (u < 0) eph = 5;  // total eclipse
983             else
984              {
985               if (u > 0.0047) eph = 4;  // annular eclipse
986               else
987                {
988                 if (u < (0.00464*cos(asin(gam)))) eph = 6; // annular/total
989                 else eph = 4;  // annular
990                }; // end if u>0.0047
991              }; // end if u<0
992            }  // end if fabs(gam) if
993           else      // non-central eclipse
994            {
995             eph = 1;   // assume partial eclipse
996             if (fabs(gam) < (0.9972+fabs(u))) // non-central annular or total
997              {
998               eph = eclp.solar(ejd,tdut,s,c);
999               emag = 1.0;
1000              };
1001             if (eph == 1)     // get magnitude of partial eclipse
1002                 emag = (1.5432 + u - fabs(gam)) / (0.5460 + 2.0*u);
1003             if (emag < 0.025) // check if low mag eclipse is OK
1004              {
1005               eph = 0;
1006               u = 1.0 / 720; // 2 min steps
1007               for (int j=0; j < 288; j++)
1008                {
1009                 tt = ejd - 0.2 + double(j)*u;
1010                 tst = eclp.solar(tt,tdut,s,c);
1011                 if (tst > 0) eph = tst;
1012                };
1013              };
1014            }
1015          }
1016        } // end of solar eclipse check
1017 
1018       if (phase == 0.5)        // check for lunar eclipse
1019        {
1020         c = (1.5572 + u - fabs(gam)) / 0.5450;
1021         if (c > 0)
1022          {
1023           s = (1.0129 - u - fabs(gam)) / 0.5450;
1024           if (s < 0)   // penumbral eclipse
1025            {
1026             emag = c;
1027             if (emag > 1) eph = -2;
1028             else eph = -1;
1029            }
1030           else         // umbral eclipse
1031            {
1032             emag = s;
1033             if (emag > 1) eph = -4;
1034             else eph = -3;
1035            }
1036          }
1037        }
1038      }
1039    };
1040 
1041   if ((phase == 0.25) || (phase == 0.75)) // for first and last quarter
1042    {
1043     k = (0.1721 - 0.0004*tt) * sin (m)
1044         + 0.0021 * sin (2.0 * m)
1045         - 0.6280 * sin (p)
1046         + 0.0089 * sin (2.0 * p)
1047         - 0.0004 * sin (3.0 * p)
1048         + 0.0079 * sin (f)
1049         - 0.0119 * sin (m + p)
1050         - 0.0047 * sin (m - p)
1051         + 0.0003 * sin (f + m)
1052         - 0.0004 * sin (f - m)
1053         - 0.0006 * sin (f + p)
1054         + 0.0021 * sin (f - p)
1055         + 0.0003 * sin (m + 2.0*p)
1056         + 0.0004 * sin (m - 2.0*p)
1057         - 0.0003 * sin (2.0*m + p);
1058     if (phase == 0.25)
1059      {
1060       k += 0.0028 - 0.0004*cos(m) + 0.0003*cos(p);
1061      };
1062     if (phase == 0.75)
1063      {
1064       k += -0.0028 + 0.0004*cos(m) - 0.0003*cos(p);
1065      };
1066    };
1067 
1068   jd = jd + k;
1069 
1070   return jd;
1071  }
1072 
ckphase(double minmjd,double maxmjd,double yr,double deltdut,int & mp,PMJD p,double phase)1073 void EclSolar::ckphase (double minmjd, double maxmjd, double yr,
1074               double deltdut, int &mp, PMJD p, double phase)
1075   /* calculate the respective phase for one (MJD-) date and check whether
1076      this date is within the limits given by minmjd and maxmjd.
1077      Use yr as the year.fraction close to the desired date.
1078      If the date is within the limits store the result in the
1079      respective array.
1080      Also check for possible occurrences of eclipses and store the
1081      respective data in an array
1082   */
1083  {
1084   double td, ejd, emag;
1085   int j, eph;
1086 
1087   td = phmjd (yr, phase, deltdut*86400.0, eph, ejd, emag);
1088   // correct difference between UT and TDT
1089   td = td - deltdut;  // correct for difference between TDT and UT.
1090 
1091   // check whether the date is within the respective year
1092 
1093   if ((td >= minmjd) &&  (td <= maxmjd) && (mp < MAXLUN))
1094    {
1095     if (mp==0)
1096      {
1097       p[0]=td;
1098       mp=1;
1099      }
1100     else
1101      {
1102       if (p[mp-1] < (td - 0.1))
1103        {
1104         p[mp]=td;
1105         mp=mp+1;
1106        };
1107      };
1108    };
1109 
1110   // now the eclipses
1111   if (eph != 0)
1112    {
1113     td = ejd;
1114     td = td - deltdut;  // correct for difference between TDT and UT.
1115 
1116     // check whether the date is within the respective year
1117     if ((td >= minmjd)&&(td <= maxmjd)&&(eb_numecl<GBL_ECLBUF))
1118      {
1119       if (eb_numecl == 0)
1120        {
1121         eb_eclmjd[0] = td;
1122         eb_magnitude[0] = emag;
1123         eb_phase[0] = eph;
1124         eb_numecl = 1;
1125        }
1126       else
1127        {
1128         for (j=0; j<eb_numecl; j++)
1129          {          // check whether the date is already stored
1130           if (fabs(eb_eclmjd[j]- td) < 0.01) eph = 0;
1131          };
1132         if (eph != 0)
1133          {
1134           j = eb_numecl;
1135           eb_eclmjd[j] = td;
1136           eb_magnitude[j] = emag;
1137           eb_phase[j] = eph;
1138           eb_numecl += 1;
1139          };
1140        }
1141      }
1142    }
1143  }
1144 
dtmstr(double jdmoon,char * dts)1145 void EclSolar::dtmstr(double jdmoon, char *dts)
1146 //  Convert the Modified Julian Date jdmoon into a corresponding string
1147 //  *dts which has the format "MMM DD HH:MM"
1148 {
1149   int dd, mm, yy, deg, mnt;
1150   double hh, sec;
1151   char mchr[4];
1152 
1153   // convert MJD into corresponding date and time
1154   caldat(jdmoon, dd, mm, yy, hh);
1155   dms (hh,deg,mnt,sec);
1156   if (sec>30.0) {mnt++;};
1157   if (mnt>59)
1158    {
1159     deg++;
1160     mnt=0;
1161    };
1162 
1163   // store data in their positions
1164   GetMonth (mm, mchr);
1165   dts[0]=mchr[0];
1166   dts[1]=mchr[1];
1167   dts[2]=mchr[2];
1168   dts[3]=' ';
1169   sprintf(&dts[4],"%2i %2i:%02i", dd, deg, mnt);
1170   dts[12]=' ';
1171 }
1172 
1173 //------------------------- moonph ---------------------------------
1174 
moonph()1175 void EclSolar::moonph()
1176 {
1177   int mp0, mp25, mp5, mp75;  // counter of respective phase entries
1178   PMJD p0, p25, p5, p75;
1179   int day, month, year, j;
1180   double yr, yx, hour, deltdut;
1181   double minmjd, maxmjd;
1182   double glatsv, glongsv, gheightsv, lat, lng;
1183   char wbuf[700];
1184 
1185   eb_moonph_called = true;
1186 
1187 // assign input data from Window Input Structure
1188   year = eb_year;
1189   deltdut = eb_del_tdut / 86400.0;  // difference TDT - UT in days
1190   eb_lastyear = year;
1191   eb_lasttz = eb_tzone;
1192   eb_lastdlt = eb_del_tdut;
1193 
1194 // initializing counters
1195   eb_numecl = 0;
1196   mp0 = 0;
1197   mp25 = 0;
1198   mp5 = 0;
1199   mp75 = 0;
1200 
1201   yr = year - 0.2;
1202   yx = year + 1.2;
1203   day = 1;
1204   month = 1;
1205   hour = 0;
1206   minmjd = mjd(day, month, year, hour);
1207   day = 31;
1208   month = 12;
1209   hour = 24.0;
1210   maxmjd = mjd(day, month, year, hour);
1211 
1212   /* As the function phmjd finds the respective phases of the Moon
1213      only to within a few weeks of the time given by the input
1214      parameter (year.faction) the following loop starts well ahead of
1215      the beginning of the year in question and ends well after.
1216      The time step of two weeks utilized to increment the year.fraction
1217      parameter assures that we catch all the different lunations */
1218 
1219   while (yr < yx)
1220    {                      // calculate and check the phases
1221     ckphase(minmjd,maxmjd,yr,deltdut, mp0, p0, 0.0);
1222     ckphase(minmjd,maxmjd,yr,deltdut, mp25, p25, 0.25);
1223     ckphase(minmjd,maxmjd,yr,deltdut, mp5, p5, 0.5);
1224     ckphase(minmjd,maxmjd,yr,deltdut, mp75, p75, 0.75);
1225     yr+=0.02;  // increase by 1/50th of a year (about two weeks)
1226    };
1227 
1228   for (j=0; j<eb_numecl; j++)
1229   {
1230       if( (eb_phase[j] == 1) && (eb_magnitude[j] > 0.98))  // check for possible non-central eclipse
1231       {
1232           glatsv = eb_geolat;
1233           glongsv = eb_geolong;
1234           gheightsv = eb_geoheight;
1235           eb_eclselect = j+1;
1236           eb_start_called = false;
1237           eb_local_called = false;
1238 
1239           getMaxPos (lat, lng);
1240           eb_geolat = lat;
1241           eb_geolong = lng;
1242           eb_geoheight = 0;
1243 
1244           getLocalDetails(wbuf);
1245 
1246           if ((eb_spp[0] == 2) || (eb_spp[1] == 2)) eb_phase[j] = 2;
1247           if ((eb_spp[0] == 3) || (eb_spp[1] == 3)) eb_phase[j] = 3;
1248 
1249           eb_geolat = glatsv;
1250           eb_geolong = glongsv;
1251           eb_geoheight = gheightsv;
1252           eb_start_called = false;
1253           eb_local_called = false;
1254 
1255       }
1256 
1257   };
1258 
1259   eb_eclselect = 0;
1260 }
1261 
getlocmag(double jd,double ep2,double phi,double lamda,double height,const Vec3 & rs,const Vec3 & rm,int & totflg)1262 double EclSolar::getlocmag(double jd, double ep2, double phi, double lamda,
1263                  double height, const Vec3& rs, const Vec3& rm, int& totflg)
1264  {
1265   /* get magnitude of solar eclipse at local position.
1266      jd : MJD (UT) of event
1267      ep2 : correction for apparent sidereal time (in sec)
1268      phi, lamda : latitude and longitude of observer in radians
1269      height : height of observer in m
1270      rs, rm : geocentric position vector of the sun and the moon
1271      totflg : 1 if total or annular, 0 otherwise
1272 
1273      RETURN: The magnitude of the eclipse (0 if no eclipse)
1274   */
1275 
1276   const double ds = 218.245445;  // diameter of Sun in Earth radii
1277   const double dm = 0.544986;   // diameter of Moon in Earth radii
1278   Vec3  gm, gs, s;
1279   double dsun, dmoon, asep;
1280   double elev;
1281 
1282   gm = GeoPos(jd, ep2, phi, lamda, height);
1283   gs = rs - gm;
1284   gm = rm - gm;
1285 
1286   // correct for refraction
1287   s = EquHor(jd, ep2, phi, lamda, gs);
1288   s = carpol(s);
1289   elev = s[2];
1290 
1291   if (elev > -3.5e-2)   // cutoff of -2 deg for calculating refraction
1292    {
1293     elev = Refract(elev);
1294     s[2] = s[2] + elev;
1295     s = polcar (s);
1296     gs = HorEqu(jd, ep2, phi, lamda, s);
1297    };
1298 
1299   s = EquHor(jd, ep2, phi, lamda, gm);
1300   s = carpol(s);
1301   elev = s[2];
1302   if (elev > -3.5e-2)   // cutoff of -2 deg for calculating refraction
1303    {
1304     elev = Refract(elev);
1305     s[2] = s[2] + elev;
1306     s = polcar (s);
1307     gm = HorEqu(jd, ep2, phi, lamda, s);
1308    };
1309 
1310   dsun = atan(0.5*ds/abs(gs)); // apparent radius of the Sun
1311   dmoon = atan(0.5*dm/abs(gm)); // apparent radius of the Moon
1312 
1313   gs = vnorm(gs);
1314   gm = vnorm(gm);
1315   asep = fabs(dot(gs, gm));
1316   if (asep > 1.0) asep = 1.0;
1317   asep = acos(asep);    // apparent distance between Sun and Moon
1318 
1319   totflg = 0;
1320   if ((dsun+dmoon) > asep)  // we have an eclipse
1321    {
1322     if (fabs(dsun-dmoon) > asep) totflg = 1;
1323     asep = fabs(dsun + dmoon - asep) / (2.0 * dsun);
1324    }
1325   else asep = 0;
1326 
1327   return asep;
1328  }
1329 
1330 //------------------------- eclStart ---------------------------------
1331 
eclStart()1332 void EclSolar::eclStart()
1333 {
1334   /* get start and end times of the various phases of the eclipse
1335      j = index of the eclipse
1336      eb_spt[i] = start time in MJD for phase i
1337      eb_ept[i] = end time in MJD for phase i
1338      eb_spp[i] = kind of phase i
1339 
1340      Also set the global eb_jdstart and eb_jdstop
1341      to the (global jd times) of eclipse start and end.
1342 
1343    */
1344   int nump, eflg, pcur, pold, maxp, i, j, npflg, p;
1345   double jd, step, jd2, jdf, d1, d2;
1346   double azim, elev, dist, phi, lamda;
1347   bool eclstarted;
1348   Vec3 gx;
1349   Eclipse eclp;
1350 
1351   if (!eb_moonph_called)  // just in case - this should never happen!
1352   {
1353       moonph();
1354       putEclSelect(1);
1355   };
1356 
1357   eb_local_called = false;
1358   eb_start_called = true;
1359 
1360   j = eb_eclselect-1;
1361 
1362   eclstarted = false;
1363   nump = 0;
1364   maxp = 0;
1365   eflg = 0;  // end flag
1366   pold = 0;
1367   elev = 0;
1368   eb_maxps = -1.0;
1369   eb_maxelv = -1.0;
1370   p = eb_phase[j];
1371   jd = eb_eclmjd[j] - 0.5;  // start 12 hours before maximum
1372   jdf = jd + 1.5;  // emergency stop if something goes wrong with the loop
1373   step = eb_cstep / (24.0*60.0);  // stepwidth (best set to 1 minute)
1374 
1375   do
1376    {
1377      if (eb_lunactive) pcur = eclp.lunar(jd, eb_del_tdut);
1378      else pcur = eclp.solar (jd, eb_del_tdut,d1, d2);
1379     // now check the start and stop times for the phases
1380     if (pcur > pold)
1381      {
1382       eclstarted = true;
1383       if(nump < 4)
1384        {
1385         eb_spt[nump] = jd;
1386         eb_spp[nump] = pcur;
1387         nump++;
1388         maxp++;
1389         pold = pcur;
1390         // get time accurate to the second
1391         jd2 = jd - 1.0/86400.0;  // go in seconds steps
1392         for (i=0; i<60; i++)
1393         {
1394             if (eb_lunactive) pcur = eclp.lunar(jd, eb_del_tdut);
1395             else pcur = eclp.solar (jd2, eb_del_tdut,d1, d2);
1396 
1397             if (pcur == pold) eb_spt[nump] = jd2;
1398             else break;
1399             jd2 = jd2 - 1.0/86400.0;  // go in seconds steps
1400         };
1401        }
1402      }
1403     else if (eclstarted && (pcur < pold))
1404      {
1405         pold = pcur;
1406         // get time accurate to the second
1407         jd2 = jd - 1.0/86400.0;  // go in seconds steps
1408         for (i=0; i<60; i++)
1409         {
1410             if (eb_lunactive) pcur = eclp.lunar(jd, eb_del_tdut);
1411             else pcur = eclp.solar (jd2, eb_del_tdut,d1, d2);
1412             if (pcur == pold) jd = jd2;
1413             else break;
1414             jd2 = jd2 - 1.0/86400.0;  // go in seconds steps
1415         };
1416         pcur = pold;
1417 
1418       npflg = 1;
1419       if (nump > 1)
1420        {
1421         if (pcur > eb_spp[nump-2]) npflg = 0;
1422        }
1423       if (npflg)
1424        {
1425         nump--;
1426         if (nump >= 0) eb_ept[nump] = jd;
1427         if (nump > 1)
1428          {
1429           if (pcur < eb_spp[nump-1])
1430            {
1431             nump--;
1432             eb_ept[nump] = jd;
1433            }
1434          }
1435         if (nump <= 0) eflg = 1;
1436        }
1437      };
1438 
1439     jd += step;
1440     if (jd > jdf) eflg = 1;
1441 
1442    } while (eflg != 1);
1443 
1444   // now check for maximum eclipse conditions
1445 
1446   calcMaxPos(phi, lamda);
1447 
1448   if ((eb_lunactive == false) && (p > 3))  // central solar eclipse
1449   {
1450       eb_jdmaxps = eb_eclmjd[j];
1451       jd = eb_eclmjd[j] - 600.0/86400.0;  // start 10 min before approximate max time
1452       phi = eb_cmxlat * degrad;
1453       lamda = eb_cmxlng * degrad;
1454       for (i=0; i<1200; i++)
1455       {
1456           jd += 1.0/86400.0;  // go in seconds steps
1457           pcur = eclp.solar (jd, eb_del_tdut,d1, d2);
1458           if (pcur > 3)
1459           {
1460               gx = eclp.GetRSun();
1461               AppPos (jd, eclp.GetEp2(), d1, d2, 0.0, 1, gx, azim, elev, dist);
1462 
1463               if (elev > eb_maxelv)
1464               {
1465                 eb_maxelv = elev;
1466                 eb_jdmaxps = jd;
1467                 phi = d1;
1468                 lamda = d2;
1469                };
1470           }
1471       }
1472       eb_maxelv = elev / degrad;
1473       eb_cmxlat = phi / degrad;
1474       eb_cmxlng = lamda / degrad;
1475       if (eb_cmxlng < 0) eb_cmxlng += 360.0;
1476   };
1477 
1478   eb_jdstart = eb_spt[0];
1479   eb_jdstop = eb_ept[0];
1480   eb_nphase = maxp;
1481 
1482  }
1483 
calcMaxPos(double & lat,double & lng)1484 void EclSolar::calcMaxPos(double &lat, double &lng)
1485  {
1486   // Get the geographic position of the maximum eclipse
1487   // in case of a central eclipse the position is approximate
1488   // lat and lng are in decimal degrees
1489 
1490   int j, p;
1491   double t, mp2;
1492   Vec3 rm;
1493   Vec3 s2;
1494   Eclipse eclp;
1495 
1496   mp2 = 2.0 * M_PI;
1497 
1498   j = eb_eclselect-1;
1499 
1500   t = eb_eclmjd[j];
1501 
1502   if (eb_lunactive)
1503   {
1504       eclp.lunar(t, eb_del_tdut);
1505       rm = eclp.GetRMoon();
1506 
1507       // get sub lunar point at maximum
1508       s2 = carpol(rm);
1509       lat = s2[2];   // just preliminary
1510       lng = s2[1] - lsidtim (t, 0, eclp.GetEp2())*M_PI/12.0;
1511       if (lng > mp2) lng -= mp2;
1512       if (lng < -M_PI) lng += mp2;
1513       if (lng > M_PI) lng -= mp2;
1514       if (fabs(lat) < 1.53589) lat = atan(1.00674*tan(lat));
1515       lat /= degrad;
1516       lng /= degrad;
1517       if (lng < 0) lng += 360.0;
1518       eb_cmxlat = lat;
1519       eb_cmxlng = lng;
1520       return;
1521   }
1522   else p = eclp.solar(t,eb_del_tdut,lat,lng);
1523 
1524   if (p > 3)
1525    {
1526     eb_cmxlat = lat;
1527     eb_cmxlng = lng;
1528     eb_cmxlat /= degrad;
1529     eb_cmxlng /= degrad;
1530    }
1531   else
1532    {
1533     eclp.maxpos(t,eb_del_tdut,eb_cmxlat,eb_cmxlng);
1534     eb_cmxlat /= degrad;
1535     eb_cmxlng /= degrad;
1536    };
1537 
1538   if (eb_cmxlng < 0) eb_cmxlng += 360.0;
1539   lat = eb_cmxlat;
1540   lng = eb_cmxlng;
1541  }
1542 
getMaxPos(double & lat,double & lng)1543 void EclSolar::getMaxPos(double &lat, double &lng)
1544  {
1545   // Get the geographic position of the maximum eclipse
1546 
1547   if (!eb_start_called) eclStart();
1548 
1549   lat = eb_cmxlat;
1550   lng = eb_cmxlng;
1551  }
1552 
sunObscure(double l1,double l2,double mag)1553 double EclSolar::sunObscure(double l1, double l2, double mag)
1554 {
1555     // get the Obscuration of the Sun from penumbra l1, umbra l2 and
1556     // magnitude mag
1557 
1558     double s, a, b, c, m;
1559 
1560     m = l1 - mag * (l1 + l2);
1561     s = (l1 - l2) / (l1 + l2);
1562     c = (l1*l1 + l2*l2 - 2*m*m) / (l1*l1 - l2*l2);
1563     c = acos(c);
1564     b = (l1*l2 + m*m) / (m*(l1+l2));
1565     b = acos(b);
1566     a = M_PI - (b + c);
1567     s = (s*s*a + b) - s * sin(c);
1568 
1569     return s / M_PI;
1570 }
1571 
1572 /*------------------------- EclCentral ---------------------------------*/
1573 
eclPltCentral(bool firstc,double & lat,double & lng)1574 int EclSolar::eclPltCentral(bool firstc, double &lat, double &lng)
1575  {
1576   /* get next coordinates for central eclipse position
1577      first = 1 for first call (first point will be found)
1578      lat, lng : latitude and longitude in decimal degrees
1579 
1580      RETURN: phase (= 4 annular, = 5 total, = 6 annular/total
1581                     0 if there was no central eclipse)
1582   */
1583   double phi, lamda, d1, d2, jd, jd2;
1584   int kp, kp2, i;
1585   Eclipse eclp;
1586 
1587   if (!eb_start_called) eclStart();
1588 
1589   if (eb_lunactive)  // just in case
1590    {
1591     eb_finished2 = true;
1592     lat = 0.0;
1593     lng = 0.0;
1594     return 0;
1595    };
1596 
1597   eb_cphs = 0;
1598 
1599   if (firstc)   // find the first occurrence compliant with the step width
1600    {
1601     jd = eb_jdstart;
1602     kp = eclp.solar(jd, eb_del_tdut, phi, lamda);
1603 
1604     while ((kp < 4) && (jd < eb_jdstop))
1605      {
1606       jd += double(eb_cstep) / (24.0*60.0);
1607       eb_lastjd = jd;
1608       kp = eclp.solar(jd, eb_del_tdut, phi, lamda);
1609      };
1610 
1611     jd2 = jd;
1612     for (i=0; i<60; i++)  // get it right to the second
1613     {
1614         jd2 = jd2 - 1.0/86400.0;  // go in seconds steps
1615         kp2 = eclp.solar (jd2, eb_del_tdut,d1, d2);
1616         if (kp2 == kp)
1617         {
1618             phi = d1;
1619             lamda = d2;
1620         }
1621         else break;
1622     };
1623     eb_finished2 = false;
1624 
1625    }  // end of firstc
1626   else
1627    {
1628     if (eb_finished2) return 0;
1629 
1630     jd = eb_lastjd + double(eb_cstep) / (24.0*60.0);
1631     eb_lastjd = jd;
1632     if (jd > eb_jdstop)
1633     {
1634         eb_finished2 = true;
1635         return 0;
1636     };
1637 
1638     kp = eclp.solar(jd, eb_del_tdut, phi, lamda);
1639 
1640     if (kp <= 3)  // end of central eclipse.
1641     {
1642      eb_finished = true;
1643      for (i=0; i<60; i++)  // get it right to the second
1644      {
1645         jd-= 1.0/86400.0;  // go in seconds steps
1646         kp = eclp.solar (jd, eb_del_tdut, phi, lamda);
1647         if (kp > 3) break;
1648      };
1649     };
1650    };
1651 
1652   if (kp > 3)   // central eclipse
1653    {
1654     eb_cphs = kp;
1655 
1656     phi /= degrad;
1657     lamda /= degrad;
1658     if (lamda < 0.0) lamda += 360.0;
1659     if (lamda > 360.0) lamda -= 360.0;
1660     eb_clat = phi;
1661     eb_clng = lamda;
1662     lat = eb_clat;
1663     lng = eb_clng;
1664 
1665     //  djd = eclp.duration(jd, indata->del_tdut, width);
1666    }
1667   return kp;
1668  }
1669 
1670 //--------------------Northern and Southern Boundaries -------------------------
1671 
InitBound()1672 void EclSolar::InitBound()
1673  {
1674   /* Initialize the calculation for the northern and southern boundaries
1675      of solar eclipses (umbra or penumbra).
1676      The function EclStart must have been called first to enable InitBound
1677      to get the right eclipse.
1678      The calculation is done in Earth radii.
1679 
1680      OUTPUT:
1681             eb_ubm : Penumbra base vector
1682             eb_ube : Shadod base vector for upper boundary
1683             eb_udm : Penumbra base delta vector
1684             eb_ude : Shadow delta vector for upper boundary
1685             eb_lbe : Shadod base vector for lower boundary
1686             eb_lde : Shadow delta vector for lower boundary
1687   */
1688 
1689 //  const double dm = 0.272493;   // radius of Moon in Earth radii
1690   double dpn1, dpn2, pan1, pan2, s0;
1691   Vec3 shmv, u2m, u2e;
1692   Vec3 ax1, ax2;
1693   Eclipse eclp;
1694 
1695   if (!eb_start_called) eclStart();
1696   if (eb_lunactive) return;  // only for solar eclipses
1697 
1698   // beginning of the eclipse
1699   eclp.penumd (eb_jdstart, eb_del_tdut, eb_ubm, eb_ube, dpn1, pan1);
1700 
1701   // end of the eclipse
1702   eclp.penumd (eb_jdstop, eb_del_tdut, u2m, u2e, dpn2, pan2);
1703 
1704   dpn1 *= 0.5;
1705   dpn2 *= 0.5;
1706 
1707   if (eb_penamode == 0)
1708   {
1709     dpn1 *= eb_penangle;
1710     pan1 *= eb_penangle;
1711     dpn2 *= eb_penangle;
1712     pan2 *= eb_penangle;
1713   }
1714 
1715   if (eb_penamode > 0)
1716   {
1717       s0 = eb_penangle * tan(pan1);
1718       s0 = atan(s0);
1719       if (pan1 > 0)
1720       {
1721           dpn1 = dpn1 * s0 / pan1;
1722           pan1 = s0;
1723       }
1724       s0 = eb_penangle * tan(pan2);
1725       s0 = atan(s0);
1726       if (pan2 > 0)
1727       {
1728           dpn2 = dpn2 * s0 / pan2;
1729           pan2 = s0;
1730       }
1731   }
1732 
1733   // get apex of penumbra cone
1734   pan1 = tan(pan1);
1735   if (pan1 != 0) dpn1 = dpn1 / pan1;
1736   else dpn1 = 1.2 * abs(eb_ubm);  // avoid a crash
1737   pan2 = tan(pan2);
1738   if (pan2 != 0) dpn2 = dpn2 / pan2;
1739   else dpn2 = dpn1;  // avoid a crash
1740 
1741   // get vector perpendicular to movement of shadow
1742   s0 = - dot(eb_ubm, eb_ube);
1743   ax1 = eb_ubm + s0 * eb_ube;
1744   eb_ubm = eb_ubm + (s0 - dpn1) * eb_ube;
1745   s0 = - dot(u2m, u2e);
1746   ax2 = u2m + s0 * u2e;
1747   u2m = u2m + (s0 - dpn2) * u2e;
1748   shmv = ax1 - ax2;
1749   ax2 = ax1 * ax2;
1750   shmv = shmv * ax2;
1751   shmv = vnorm(shmv);
1752 
1753   // now get the delta vectors
1754   eb_udm = u2m - eb_ubm;
1755   eb_lbe = eb_ube - pan1 * shmv;
1756   eb_ube = eb_ube + pan1 * shmv;
1757   eb_ude = u2e + pan2 * shmv;
1758   eb_lde = u2e - pan2 * shmv;
1759   eb_ube = vnorm(eb_ube);
1760   eb_lbe = vnorm(eb_lbe);
1761   eb_ude = vnorm(eb_ude);
1762   eb_lde = vnorm(eb_lde);
1763   eb_lde = eb_lde - eb_lbe;
1764   eb_ude = eb_ude - eb_ube;
1765 
1766   // scale the delta vectors
1767   dpn1 = eb_jdstop - eb_jdstart;
1768   if (dpn1 == 0) dpn1 = 1.0;
1769   else dpn1 = 1.0 / dpn1;
1770   eb_udm *= dpn1;
1771   eb_ude *= dpn1;
1772   eb_lde *= dpn1;
1773  }
1774 
GNSBound(bool firstc,bool north,double & lat,double & lng)1775 int EclSolar::GNSBound(bool firstc, bool north, double& lat, double& lng)
1776  {
1777   /* Get the geographic coordinates of the northern or southern boundaries
1778      at time t.
1779      INPUT:  firstc :true for first call
1780              north : true for southern boundary
1781              see also InitBound
1782      OUTPUT:
1783             lat, lng : latitude and longitude of penumbra boundary.
1784             If lat > 90 no northern boundary exists.
1785 
1786      RETURN: 1 if time within eclipse, 0 if end of eclipse
1787    */
1788   const double flat = 0.996633;  // flatting of the Earth
1789 
1790   double s0, s, r0, r2, dlt, t;
1791   Vec3 vrm, vre;
1792 
1793   if (eb_lunactive)  // only solar eclipses
1794   {
1795    lng = 0.0;
1796    lat = 100.0;
1797    return 0;
1798   };
1799 
1800   if (firstc)
1801   {
1802    InitBound();
1803    t = eb_jdstart;
1804    eb_lastjd = t;
1805   }
1806   else
1807   {
1808    t = eb_lastjd + double(eb_cstep) / (24.0*60.0);
1809    eb_lastjd = t;
1810   };
1811 
1812   if (t >= eb_jdstop)
1813   {
1814    lng = 0.0;
1815    lat = 100.0;
1816    return 0;
1817   };
1818 
1819   // get shadow vector at time t
1820   vrm = eb_ubm + (t - eb_jdstart) * eb_udm;
1821   if (north) vre = eb_ube + (t - eb_jdstart) * eb_ude;
1822   else vre = eb_lbe + (t - eb_jdstart) * eb_lde;
1823   vre = vnorm (vre);  // direction of penumbra boundary
1824 
1825   s0 = - dot(vrm, vre);   // distance Moon - fundamental plane
1826   r2 = dot (vrm,vrm);
1827   dlt = s0*s0 + 1.0 - r2;
1828   r0 = 1.0 - dlt;
1829 
1830   if (r0 > 0) r0 = sqrt (r0);
1831   else r0 = 0;      // distance center of Earth - shadow axis
1832 
1833   // calculate the coordinates if there is an intersecton
1834   if (r0 < 1.0)  // there should be an intersection
1835    {
1836     if (dlt > 0) dlt = sqrt(dlt);
1837     else dlt = 0;
1838     s = s0 - dlt;  // distance Moon - fundamental plane
1839     vrm = vrm + s * vre;
1840     vrm = vnorm(vrm);
1841     vrm[2] *= flat;    // vector to intersection
1842     vre = carpol(vrm);
1843     lng = vre[1] - lsidtim(t,0,0)*0.261799387799; // geographic coordinates
1844     if (lng > 2*M_PI) lng -= 2.0*M_PI;
1845     if (lng < 0.0) lng += 2.0*M_PI;
1846     lat = sqrt(vrm[0]*vrm[0] + vrm[1]*vrm[1])*0.993305615;
1847     lat = atan23(vrm[2],lat);
1848     lat /= degrad;
1849     lng /= degrad;
1850 
1851     if (lng < 0.0) lng += 360.0;
1852     if (lng > 360.0) lng -= 360.0;
1853    }
1854   else
1855    {
1856     lat = 100.0;
1857     lng = 0;
1858    };
1859 
1860   return 1;
1861  }
1862 
1863 //------------------ Sunrise / Sunset Boundaries ---------------------------
1864 
InRSBound()1865 void EclSolar::InRSBound()
1866  {
1867   /* Initialize the calculation for the sunrise and sunset boundaries
1868      of solar eclipses.
1869      The function EclStart must have been called first to enable InRSBound
1870      to get the right eclipse.
1871      The calculation is done in Earth radii.
1872 
1873      OUTPUT:
1874             eb_ubm : Moon base vector
1875             eb_ube : Shadod base vector
1876             eb_udm : Moon delta vector
1877             eb_ude : Shadow delta vector for
1878             eb_dpb : Base value for diameter of penumbra
1879             eb_dpd : delta value for diameter of penumbra
1880   */
1881 
1882   double pan;
1883   Vec3 u2m, u2e;
1884   Eclipse eclp;
1885 
1886   if (!eb_start_called) eclStart();
1887 
1888   // beginning of the eclipse
1889   eclp.penumd (eb_jdstart, eb_del_tdut, eb_ubm, eb_ube, eb_dpb, pan);
1890 
1891   // end of the eclipse
1892   eclp.penumd (eb_jdstop, eb_del_tdut, u2m, u2e, eb_dpd, pan);
1893 
1894   // get the delta vectors
1895   eb_udm = u2m - eb_ubm;
1896   eb_ude = u2e - eb_ube;
1897   eb_dpd = eb_dpd - eb_dpb;
1898 
1899   // scale the delta vectors
1900 
1901   pan = eb_jdstop - eb_jdstart;
1902   if (pan == 0) pan = 1.0;
1903   else pan = 1.0 / pan;
1904   eb_udm *= pan;
1905   eb_ude *= pan;
1906   eb_dpd *= pan;
1907  }
1908 
iscrs(double vrc0,double vrc1,double dpn,double & vrx0,double & vrx1,double & vrx20,double & vrx21)1909 int EclSolar::iscrs(double vrc0, double vrc1, double dpn,
1910                        double& vrx0, double& vrx1, double& vrx20, double& vrx21)
1911  {
1912   /* calculate intersection vectors of the penumbral cone with the
1913      Earth in the fundamental plane to find the locations of rise
1914      and set.
1915 
1916      INPUT : components of vectors vrc (pointing to the center of the
1917              penumbra), vre (unit vector perpendicular to the
1918              fundamental plane).
1919              dpn : half diameter of penumbra in Earth radii
1920 
1921      OUTPUT: components of the intersection vectors vrx and vrx2 if
1922              successful.
1923 
1924      RETURN: 1 if intersection successful, 0 otherwise.
1925   */
1926   int rtn;
1927   double a, b, c, f1, f2, f3, f4;
1928 
1929   rtn = 1;
1930   f1 = vrc0*vrc0;
1931 
1932   if (f1 < 1.0e-60) rtn = 0;
1933   else
1934    {
1935     f2 = 1.0 - dpn*dpn + f1 + vrc1*vrc1;
1936     f3 = f2 / (2.0*vrc0);
1937     f4 = vrc1 / vrc0;
1938     a = 1.0 + vrc1*vrc1 / f1;
1939     b = f2*vrc1 / f1;
1940     c = f2*f2 / (4.0*f1) - 1.0;
1941     if (fabs(a) < 1.0e-60) rtn = 0;
1942     else
1943      {
1944       vrx21 = -0.5 * b / a; ;
1945       vrx0 = vrx21*vrx21 - c / a;
1946       if (vrx0 < 0) rtn = 0;
1947       else
1948        {
1949         vrx0 = sqrt(vrx0);
1950         vrx1 = vrx21 + vrx0;
1951         vrx21 = vrx21 - vrx0;
1952         vrx0 = f3 + f4*vrx1;
1953         vrx20 = f3 + f4*vrx21;
1954         vrx1 = -vrx1;
1955         vrx21 = -vrx21;
1956        };
1957      };
1958    };
1959 
1960   return rtn;
1961  }
1962 
GRSBound(bool firstc,double & lat1,double & lng1,double & lat2,double & lng2)1963 int EclSolar::GRSBound(bool firstc, double& lat1, double& lng1, double& lat2, double& lng2)
1964  {
1965   /* Get the geographic coordinates of the boundaries for rise/set
1966      at time t.
1967 
1968      INPUT: firstc : true for first call
1969 
1970      OUTPUT:
1971             lat1, lng1; lat2, lng2  : latitude and longitude of first
1972                                       and second point ofboundary.
1973                                       If lat > 90 no respective point exists.
1974 
1975     RETURN: 0 if end of eclipse, 1 otherwise
1976   */
1977   const double flat = 0.996633;  // flatting of the Earth
1978 
1979   double s0, r0, dpn, t;
1980   Vec3 vrm, vre, vrc, vrx, vrx2;
1981   Mat3 m1, m2;
1982 
1983   if (eb_lunactive)  // only solar eclipses
1984   {
1985    lng1 = 0.0;
1986    lat1 = 100.0;
1987    lng2 = 0.0;
1988    lat2 = 100.0;
1989    eb_finished = true;
1990    return 0;
1991   };
1992 
1993   if (firstc)
1994   {
1995    InRSBound();
1996    t = eb_jdstart + 1.0/86400.0; // just add a second to be beyond the limit
1997    eb_lastjd = t;
1998    eb_finished = false;
1999   }
2000   else
2001   {
2002    t = eb_lastjd + double(eb_cstep) / (24.0*60.0);
2003    eb_lastjd = t;
2004   };
2005 
2006   if (t >= eb_jdstop)
2007   {
2008    if (eb_finished)
2009     {
2010      lng1 = 0.0;
2011      lat1 = 100.0;
2012      lng2 = 0.0;
2013      lat2 = 100.0;
2014      return 0;
2015     };
2016    t = eb_jdstop - 1.0/86400.0;  // just to go to the very end and then stop
2017    eb_lastjd = t;
2018    eb_finished = true;
2019   };
2020 
2021   // get position of penumbra and shadow cone
2022   vrm = eb_ubm + (t - eb_jdstart) * eb_udm;
2023   vre = eb_ube + (t - eb_jdstart) * eb_ude;
2024   vre = vnorm (vre);  // direction of penumbra boundary
2025   dpn = eb_dpb + (t - eb_jdstart) * eb_dpd;
2026   dpn *= 0.5;  // half cone diameter
2027   vrx = carpol(vre);
2028 
2029   m1 = zrot(vrx[1] + M_PI/2.0);
2030   m2 = xrot(M_PI/2.0 - vrx[2]);
2031   m1 = m2 * m1;   // rotation from equatorial into fundamental plane
2032   m2 = mxtrn(m1); // rotation from fundamental plane into equatorial
2033 
2034   // get vector to center of shadow in the fundamental plane
2035   s0 = - dot(vrm, vre);
2036   vrc = vrm + s0 * vre;
2037 
2038   r0 = abs(vrc);  // distance between center of Earth and center of shadow
2039   vrc = mxvct(m1, vrc);
2040 
2041   lng1 = 0;
2042   lat1 = 100.0;
2043   lng2 = 0;
2044   lat2 = 100.0;
2045   // check whether intersection of Earth and shadow cone are possible
2046   // in fundamental plane
2047   if ((r0 > fabs(1.0 - dpn)) && (r0 < fabs(1.0 + dpn)))
2048    {
2049     // now find the intersections
2050     if (iscrs(vrc[0],vrc[1], dpn, vrx[0],vrx[1], vrx2[0],vrx2[1])) lat1 = 0;
2051     else
2052      {
2053       if (iscrs(vrc[1],vrc[0], dpn, vrx[1],vrx[0], vrx2[1],vrx2[0])) lat1 = 0;
2054      };
2055    };
2056 
2057   // calculate the coordinates if there is an intersecton
2058   if (lat1 < 100.0)  // there should be an intersection
2059    {
2060     vrx[2] = 0;
2061     vrx = mxvct(m2, vrx);
2062     vrx = vnorm(vrx);
2063     vrx[2] *= flat;    // vector to intersection
2064     vre = carpol(vrx);
2065     lng1 = vre[1] - lsidtim(t,0,0)*0.261799387799; // geographic coordinates
2066 
2067     if (lng1 > M_PI) lng1 -= 2.0*M_PI;
2068     if (lng1 < (-M_PI)) lng1 += 2.0*M_PI;
2069     lat1 = sqrt(vrx[0]*vrx[0] + vrx[1]*vrx[1])*0.993305615;
2070     lat1 = atan23(vrx[2],lat1);
2071     lat1 /= degrad;
2072     lng1 /= degrad;
2073    };
2074   if (lat1 < 100.0)  // intersection #2
2075    {
2076     vrx2[2] = 0;
2077     vrx2 = mxvct(m2, vrx2);
2078     vrx2 = vnorm(vrx2);
2079     vrx2[2] *= flat;    // vector to intersection
2080     vre = carpol(vrx2);
2081     lng2 = vre[1] - lsidtim(t,0,0)*0.261799387799; // geographic coordinates
2082     if (lng2 > M_PI) lng2 -= 2.0*M_PI;
2083     if (lng2 < (-M_PI)) lng2 += 2.0*M_PI;
2084     lat2 = sqrt(vrx2[0]*vrx2[0] + vrx2[1]*vrx2[1])*0.993305615;
2085     lat2 = atan23(vrx2[2],lat2);
2086     lat2 /= degrad;
2087     lng2 /= degrad;
2088    };
2089 
2090   return 1;
2091  }
2092 
2093 /*------------------------- EclDetails ---------------------------------*/
2094 
DegFDms(double h)2095 double EclSolar::DegFDms (double h)
2096  {
2097   /* convert degrees from d.fff -> d.mmsss where .mm are the minutes
2098      and sss are seconds (and fractions of seconds).
2099   */
2100   int mm, deg;
2101   double hh, t, sec;
2102 
2103   hh = fabs(h);
2104   dms (hh,deg,mm,sec);
2105   if (sec>=59.5)
2106    {
2107     mm++;
2108     sec = 0;
2109    };
2110   if (mm>59)
2111    {
2112     deg++;
2113     mm=0;
2114    };
2115   hh = double(deg);
2116   t = double(mm)/100.0;
2117   hh += t;
2118   t = sec/10000.0;
2119   hh += t;
2120   if (h < 0) hh = -hh;
2121 
2122   return hh;
2123  }
2124 
localStart(int j,double * spt,double * ept,int * spp,int p,char * otxt)2125 int EclSolar::localStart(int j, double *spt, double *ept, int *spp,
2126                                int p, char *otxt)
2127  {
2128   /* get start and end times of the various phases of the eclipse
2129      j = index of the eclipse
2130      spt[i] = start time in MJD for phase i
2131      ept[i] = end time in MJD for phase i
2132      spp[i] = kind of phase i
2133       p = phase
2134 
2135       RETURN: the number of different phases of this eclipse.
2136 
2137      The step width will be 1 min
2138    */
2139   int nump, eflg, pcur, pold, maxp, i, npflg;
2140   double magecl;  // local magnitude of eclipse at jd
2141   double jd, step, jdf, d1, d2, elrise;
2142   double azim, elev, dist, phi, lamda;
2143   char dts[13];
2144   char outb[127];
2145   Vec3 gx;
2146   Eclipse eclp;
2147 
2148   nump = 0;
2149   maxp = 0;
2150   eflg = 0;  // end flag
2151   pold = 0;
2152   elev = 0;
2153   eb_lccnt = 0;
2154   eb_maxps = -1.0;
2155   eb_maxelv = -1.0;
2156   phi = eb_geolat * M_PI / 180.0;
2157   lamda = eb_geolong * M_PI / 180.0;
2158   jd = eb_eclmjd[j] - 0.5;  // start 12 hours before maximum
2159   jdf = jd + 1.5;  // emergency stop if something goes wrong with the loop
2160   step = 1.0/(24.0*60.0);  // stepwidth 1 minute
2161   eb_ltotb = jd;
2162   eb_ltote = jd - 1.0;
2163 
2164   do
2165    {
2166     if(p < 0)
2167      {
2168       pcur = eclp.lunar (jd, eb_del_tdut);
2169       gx = eclp.GetRMoon();
2170       magecl = 1.0;  // just set it 1. It's not needed for lunar eclipses.
2171       elrise = -9.89e-3;  // allow for refraction during rise/set
2172      }
2173     else
2174      {
2175       pcur = eclp.solar (jd, eb_del_tdut,d1, d2);
2176       gx = eclp.GetRSun();
2177       magecl = 0;
2178       elrise = -1.45e-2;  // allow for refraction during rise/set
2179      };
2180 
2181     // check whether body is visible from local position.
2182     if (pcur > 0)
2183      {
2184       AppPos (jd, eclp.GetEp2(), phi, lamda, eb_geoheight, 1, gx,
2185                                  azim, elev, dist);
2186       if ((p > 0) && (elev >= elrise))
2187        {
2188         magecl = getlocmag(jd, eclp.GetEp2(), phi, lamda, eb_geoheight,
2189                                eclp.GetRSun(),eclp.GetRMoon(), i);
2190         if (magecl > eb_maxps)
2191          {
2192           eb_maxps = magecl;
2193           eb_maxelv = elev;
2194           eb_jdmaxps = jd;
2195          }
2196        }
2197      }
2198 
2199     if ((eb_lccnt == 0) && (pcur != 0))
2200      {
2201       if ((elev >= elrise) && (magecl > 0))
2202        {
2203         eb_lcb1 = jd;
2204         eb_lccnt = 1;
2205        }
2206      }
2207     if (eb_lccnt == 1)
2208      {
2209       if ((elev < elrise) || (pcur == 0) || (magecl == 0))
2210        {
2211         eb_lce1 = jd;
2212         eb_lccnt = 2;
2213        }
2214      }
2215     if ((eb_lccnt == 2) && (pcur != 0))
2216      {
2217       if ((elev >= elrise) && (magecl > 0))
2218        {
2219         eb_lcb2 = jd;
2220         eb_lccnt = 3;
2221        }
2222      }
2223     if (eb_lccnt == 3)
2224      {
2225       if ((elev < elrise) || (pcur == 0) || (magecl == 0))
2226        {
2227         eb_lce2 = jd;
2228         eb_lccnt = 4;
2229        }
2230      }
2231 
2232     // now check the start and stop times for the phases
2233     if (pcur > pold)
2234      {
2235       if(nump < 4)
2236        {
2237         spt[nump] = jd;
2238         spp[nump] = pcur;
2239         nump++;
2240         maxp++;
2241         pold = pcur;
2242        }
2243      }
2244     else if (pcur < pold)
2245      {
2246       npflg = 1;
2247       if (nump > 1)
2248        {
2249         if (pcur > spp[nump-2]) npflg = 0;
2250        }
2251       if (npflg)
2252        {
2253         nump--;
2254         if (nump >= 0) ept[nump] = jd;
2255         if (nump > 1)
2256          {
2257           if (pcur < spp[nump-1])
2258            {
2259             nump--;
2260             ept[nump] = jd;
2261            }
2262          }
2263         if (nump <= 0) eflg = 1;
2264        }
2265       pold = pcur;
2266      };
2267 
2268     jd += step;
2269     if (jd > jdf) eflg = 1;
2270 
2271    } while (eflg != 1);
2272 
2273   //  put the respective info into textfile otxt
2274   if(maxp > 0)
2275    {
2276     for (i=0; i<maxp; i++)
2277      {
2278       if (p < 0)
2279        {
2280         switch (spp[i])
2281          {
2282           case 1: strcpy(outb,"penumbral   ");
2283                   break;
2284           case 2: strcpy(outb,"tot.penumb  ");
2285                   break;
2286           case 3: strcpy(outb,"partial     ");
2287                   break;
2288           case 4: strcpy(outb,"totality    ");
2289                   break;
2290          }
2291        }
2292       else
2293        {
2294         switch (spp[i])
2295          {
2296           case 1: strcpy(outb,"partial     ");
2297                   break;
2298           case 2: strcpy(outb,"n-centr.Ann ");
2299                   break;
2300           case 3: strcpy(outb,"n-centr.Tot ");
2301                   break;
2302           case 4: strcpy(outb,"annularity  ");
2303                   break;
2304           case 5: strcpy(outb,"totality    ");
2305                   break;
2306          }
2307        }
2308 
2309         strcat(otxt,"\n ");
2310         strcat(otxt, outb);
2311         strcat(otxt,"\tbegins ");
2312         dtmstr((spt[i]+eb_tzone/24.0),dts);
2313         dts[12] = '\0';
2314         strcat(otxt,dts);
2315         strcat(otxt, "\tends ");
2316         dtmstr((ept[i]+eb_tzone/24.0),dts);
2317         dts[12] = '\0';
2318         strcat(otxt,dts);
2319      }
2320    }
2321   if (maxp > 0)
2322    {
2323     if (eb_lccnt == 1) eb_lce1 = ept[0];
2324     if (eb_lccnt == 3) eb_lce2 = ept[0];
2325    }
2326 
2327   if (eb_maxps > 0.8)  // check for local central phase
2328    {
2329     jd = eb_jdmaxps - 16.0/(24.0*60.0);
2330     eb_ltote = jd + 32.0/(24.0*60.0);
2331     eflg = 0;
2332     do
2333      {
2334       jd += 1.0/86400.0;  // go in seconds steps
2335       eclp.solar (jd, eb_del_tdut,d1, d2);
2336       gx = eclp.GetRSun();
2337       AppPos (jd, eclp.GetEp2(), phi, lamda, eb_geoheight, 1, gx,
2338                                       azim, elev, dist);
2339       if (elev >= elrise)
2340        {
2341         magecl = getlocmag(jd, eclp.GetEp2(), phi, lamda, eb_geoheight,
2342                                eclp.GetRSun(),eclp.GetRMoon(), i);
2343         if (magecl > eb_maxps)
2344          {
2345           eb_maxelv = elev;
2346           eb_maxps = magecl;
2347           eb_jdmaxps = jd;
2348          }
2349         if (i > 0)
2350          {
2351           eb_ltotb = jd;
2352           eflg = 1;
2353          }
2354        }
2355       if (jd > eb_ltote) eflg = 1;
2356 
2357      } while (!eflg);
2358 
2359     if (jd < eb_ltote)
2360      {
2361       eflg = 0;
2362       do
2363        {
2364         jd += 1.0/86400.0;  // go in seconds steps
2365         eclp.solar (jd, eb_del_tdut,d1, d2);
2366         gx = eclp.GetRSun();
2367         AppPos (jd, eclp.GetEp2(), phi, lamda, eb_geoheight, 1, gx,
2368                                         azim, elev, dist);
2369         if (elev < elrise) eflg = 1;
2370         magecl = getlocmag(jd, eclp.GetEp2(), phi, lamda, eb_geoheight,
2371                                eclp.GetRSun(),eclp.GetRMoon(), i);
2372         if (magecl > eb_maxps)
2373          {
2374           eb_maxelv = elev;
2375           eb_maxps = magecl;
2376           eb_jdmaxps = jd;
2377          }
2378         if (i == 0) eflg = 1;
2379         if (jd > eb_ltote) eflg = 1;
2380 
2381        } while (!eflg);
2382       eb_ltote = jd;
2383      }
2384     else eb_ltote = eb_ltotb - 1.0;
2385    }
2386 
2387   return maxp;
2388  }
2389 
2390 
getLocalDetails(char * otxt)2391 void EclSolar::getLocalDetails(char *otxt)
2392  {
2393   /* get the details of the eclipse selected in eclbuf.select
2394      and place the output into otxt
2395   */
2396   int j, p, i, ecloutbn;
2397   int dd, mm, yy, deg, mnt;
2398   double sec, hh;
2399 
2400   int spp[4], nump;
2401   double spt[4], ept[4];
2402   double jd, jdf;
2403   char dts[13];
2404   char outb[127];
2405 
2406   if (!eb_start_called) eclStart();
2407   eb_local_called = true;
2408 
2409   j = eb_eclselect-1;
2410   p = eb_phase[j];
2411 
2412   ecloutbn = 3;
2413   sprintf(otxt,"+++ Timezone: %g +++  TT - UTC: %g (sec) +++ Year: %5i +++\n\n", eb_tzone, eb_del_tdut, eb_year);
2414     switch (p)
2415      {
2416       case 1: strcpy(outb,"\t\tPartial Eclipse of the Sun");
2417               break;
2418       case 2: strcpy(outb,"\t\tNon-Central Annular Eclipse of the Sun");
2419               break;
2420       case 3: strcpy(outb,"\t\tNon-Central Total Eclipse of the Sun");
2421               break;
2422       case 4: strcpy(outb,"\t\tAnnular Eclipse of the Sun");
2423               break;
2424       case 5: strcpy(outb, "\t\tTotal Eclipse of the Sun");
2425               break;
2426       case 6: strcpy(outb, "\t\tAnnular/Total Solar Eclipse");
2427               break;
2428 
2429       case -1:
2430       case -2: strcpy(outb, "\t\tPenumbral Eclipse of the Moon");
2431                break;
2432       case -3: strcpy(outb, "\t\tPartial eclipse of the Moon");
2433                break;
2434       case -4: strcpy(outb, "\t\tTotal eclipse of the Moon");
2435                break;
2436      };
2437 
2438     strcat(otxt,outb);
2439     sprintf(outb,"\n\nMaximum Eclipse at ");
2440     strcat(otxt,outb);
2441     dtmstr((eb_eclmjd[j]+eb_tzone/24.0),dts);
2442     dts[12] = '\0';
2443     strcat(otxt,dts);
2444     if (p < 4)
2445      {
2446       sprintf(outb,"   with magnitude:%5.2f",eb_magnitude[j]);
2447       strcat(otxt, outb);
2448      }
2449 
2450     strcat(otxt,"\n");
2451     nump = localStart(j, spt, ept, spp, p, otxt);
2452     if ((p < 4) || (nump < 1)) ecloutbn = 5;
2453 
2454   if (ecloutbn == 3)
2455    {
2456     // get start and stop dates for central phase
2457     jd = spt[nump-1];
2458     for (i=0; i<nump; i++)
2459     if ((spt[i] < jd) && (spp[i] > 3)) jd = spt[i]; // start of central phase
2460     caldat(jd, dd, mm, yy, hh);
2461     dms (hh,deg,mnt,sec);
2462     sec = 0;
2463     i = mnt / eb_cstep;
2464     mnt = i* int(eb_cstep);   // cut to proper time step
2465     hh = ddd (deg, mnt, sec);
2466     jdf = ept[nump-1];
2467     for (i=0; i<nump; i++)
2468      if ((ept[i] > jdf) && (spp[i] > 3)) jdf = ept[i]; // end of central phase
2469    }
2470 
2471   // local circumstances
2472       strcat(otxt, "\n\n\nLocal Circumstances for ");
2473       jd = eb_geolat;
2474       jdf = eb_geolong;
2475     sprintf(outb,"\nLat: %g   Long: %g   height: %g m\n\n",
2476                                jd, jdf, eb_geoheight);
2477     strcat(otxt,outb);
2478     if (p != 0)
2479      {
2480       if (eb_lccnt > 0)
2481        {
2482         sprintf(outb,"Eclipse visible from ");
2483         dtmstr((eb_lcb1+eb_tzone/24.0),dts);
2484         dts[12] = '\0';
2485         strcat(outb," ");
2486         strcat(outb,dts);
2487         strcat(outb," to ");
2488         dtmstr((eb_lce1+eb_tzone/24.0),dts);
2489         dts[12] = '\0';
2490         strcat(outb,dts);
2491         if (eb_lccnt > 2)  // this case almost never happens
2492          {
2493           strcat(otxt,outb);
2494           strcpy(outb,"\n\tand from ");
2495           dtmstr((eb_lcb2+eb_tzone/24.0),dts);
2496           dts[12] = '\0';
2497           strcat(outb," ");
2498           strcat(outb,dts);
2499           strcat(outb," to ");
2500           dtmstr((eb_lce2+eb_tzone/24.0),dts);
2501           dts[12] = '\0';
2502           strcat(outb,dts);
2503          }
2504        }
2505       else sprintf(outb,"Eclipse not visible");
2506       strcat(otxt,outb);
2507      }
2508 
2509    // local solar eclipse magnitude
2510     if ((p > 0) && (eb_lccnt > 0))
2511      {
2512       sprintf(outb,"\nMaximum Eclipse at ");
2513       strcat(otxt,outb);
2514       dtmstr((eb_jdmaxps+eb_tzone/24.0),dts);
2515       dts[12] = '\0';
2516       strcat(otxt,dts);
2517       sprintf(outb,"   with magnitude %6.3f", eb_maxps);
2518       strcat(otxt,outb);
2519       sprintf(outb,"   elev:%4.1f", 180.0*eb_maxelv/M_PI);
2520       strcat(otxt,outb);
2521       if (eb_ltotb <= eb_ltote)
2522        {      // local central eclipse
2523         if ((p % 2) == 0) strcpy(outb, "\nannularity from");
2524         else strcpy(outb, "\ntotality from");
2525         if ((p == 1) || (p == 6))
2526                    strcpy(outb, "\ntotality/annularity from");
2527         strcat(otxt,outb);
2528         caldat((eb_ltotb+eb_tzone/24.0), dd, mm, yy, hh);
2529         caldat((eb_ltote+eb_tzone/24.0), dd, mm, yy, jd);
2530         hh = DegFDms(hh);
2531         jd = DegFDms(jd);
2532         jdf = (eb_ltote - eb_ltotb) * 86400.0;
2533         sprintf(outb,"%8.4f  to%8.4f   del.t:%3.0f sec \n",hh,jd, jdf);
2534         strcat(otxt,outb);
2535        }
2536      }
2537 
2538     eb_maxelv /= degrad;
2539  }
2540 
2541 //---------Northern and Southern Umbra Boundaries ---------------------
2542 
navCourse(double lat1,double lng1,double lat2,double lng2)2543 double EclSolar::navCourse (double lat1, double lng1, double lat2, double lng2)
2544 {
2545  // get course (in radians) from (lat1, lng1) to (lat2, lng2) over the Earth surface
2546  // the geographic coordinates are in decimal degrees
2547 
2548  double lt1, ln1, lt2, ln2, cd, an;
2549 
2550  lt1 = lat1 * degrad;
2551  ln1 = lng1 * degrad;
2552  lt2 = lat2 * degrad;
2553  ln2 = lng2 * degrad;
2554 
2555  cd = sin(lt1)*sin(lt2) + cos(lt1)*cos(lt2)*cos(ln2 - ln1);
2556  an = acos(cd);
2557  an = cos(lt1) * sin(an);
2558 
2559  if (an == 0) return 0;  // same spot. didn't move
2560 
2561  cd = (sin(lt2) - sin(lt1)*cd) / an;
2562  an = acos (cd);
2563 
2564  if (sin(ln2 - ln1) < 0) an = -an;
2565 
2566  return an;
2567 }
2568 
navNewPos(double d,double an,double lat1,double lng1,double & lat2,double & lng2)2569 void EclSolar::navNewPos (double d, double an, double lat1, double lng1, double &lat2, double &lng2)
2570 {
2571  /*
2572     starting from (lat1, lng1) along the great circle for d (radians) with course an (in radians)
2573     get the new position (lat2, lng2) (in decimal degrees) at the Earth surface
2574  */
2575 
2576  double cd, sd, lt1, ag;
2577 
2578  ag = an;
2579  if (ag > M_PI) ag -= 2*M_PI;
2580  if (ag < -M_PI) ag += 2*M_PI;
2581 
2582  cd = cos(d);
2583  lt1 = lat1 * degrad;
2584 
2585  sd = cd * sin(lt1) + sin(d) * cos(lt1) * cos(ag);
2586  lat2 = asin(sd);
2587 
2588  lng2 = cos(lt1) * cos(lat2);
2589 
2590  if (lng2 == 0)  // just in case to avoid a crash
2591  {
2592      lat2 = lat1;
2593      lng2 = lng1;
2594      return;
2595  }
2596 
2597  lng2 = (cd - sin(lt1) * sd) / lng2;
2598  lng2 = acos(lng2);
2599  lng2 /= degrad;
2600  if (ag > 0) lng2 = lng1 + lng2;
2601  else lng2 = lng1 -lng2;
2602  if(lng2 > 360.0) lng2 -= 360.0;
2603  if(lng2 < 0) lng2 += 360.0;
2604 
2605  lat2 /= degrad;
2606 
2607 }
2608 
centralBound(bool firstc,double & lat1,double & lng1,double & lat2,double & lng2)2609 int EclSolar::centralBound(bool firstc, double& lat1, double& lng1, double& lat2, double& lng2)
2610 {
2611   /* Get the geographic coordinates of the northern or southern boundaries
2612      of the umbra at time t.
2613      INPUT:  firstc :true for first call
2614 
2615      OUTPUT:
2616             lat, lng : latitude and longitude of umbra boundary in decimal degrees.
2617             If lat > 90 no boundary exists.
2618 
2619      RETURN: current phase if time within eclipse, <=3 if no central eclipse
2620    */
2621 
2622   bool lastp;
2623   int k;
2624   double dpn1, t;
2625   double lt1, ln1, lt2, ln2, an;
2626   Eclipse eclp;
2627 
2628   if (!eb_start_called) eclStart();
2629 
2630   lng1 = 0.0;
2631   lat1 = 100.0;
2632   lng2 = 0.0;
2633   lat2 = 100.0;
2634 
2635   lastp = false;
2636 
2637   if (eb_lunactive) return 0;  // only solar eclipses
2638 
2639 
2640   if (firstc)
2641   {
2642    k = eclPltCentral(true, lt1, ln1);
2643   }
2644   else k = eclPltCentral(false, lt1, ln1);
2645   t = eb_lastjd;
2646 
2647   if (k <= 3) return k;  // no central eclipse
2648 
2649   k = eclPltCentral(false, lt2, ln2);  // next step
2650   eb_lastjd = t;  // go back to the original step
2651 
2652   if (k <= 3)  // try the last step instead at the end
2653   {
2654       eb_lastjd -= 2.0 * eb_cstep / (24.0*60.0);
2655       k = eclPltCentral(false, lt2, ln2);
2656       eb_lastjd = t;
2657       lastp = true;
2658   };
2659 
2660   if (k <= 3) return k;  // no central eclipse
2661 
2662   eclp.solar(t, eb_del_tdut, lat1, lng1);
2663   lat1 = 100.0;
2664   lng1 = 0;
2665 
2666   an = navCourse (lt1, ln1, lt2, ln2);  // direction of shadow along Earth surface
2667   an += 0.5*M_PI; // direction perpendicular to shadow movement (right boundary)
2668 
2669   eclp.duration(t, eb_del_tdut, dpn1);  // dpn1 is width of umbra in km
2670   dpn1 = (dpn1 / 111.1) * 0.0174533;  // radians of umbra width
2671   dpn1 /= 2.0;
2672   navNewPos(dpn1, an, lt1, ln1, lat1, lng1);
2673 
2674   an -= M_PI;  // find opposite boundary
2675 
2676   navNewPos(dpn1, an, lt1, ln1, lat2, lng2);
2677 
2678   if (lastp)  // we went the opposite way at the end
2679   {
2680       lt1 = lat2;
2681       ln1 = lng2;
2682       lat2 = lat1;
2683       lng2 = lng1;
2684       lat1 = lt1;
2685       lng1 = ln1;
2686       return 0;  // end of eclipse
2687   }
2688   else return k;
2689  }
2690 
2691 //-------------------- Shadow Cone -------------------------
2692 
getShadowCone(double mjd,bool umbra,int numpts,double * lat,double * lng)2693 void EclSolar::getShadowCone(double mjd, bool umbra, int numpts, double* lat, double* lng)
2694 {
2695   /*  Get the geographic coordinates of the shadow cone at MJD-time mjd.
2696       if umbra is true the umbra cone will be returned
2697       if umbra is false the penumbra will be returned
2698 
2699      OUTPUT:
2700             lat and lng must be arrays of length numpts into which the numpts points will be placed
2701             if there is no (total) eclipse at the time, lat will be set 100.0, lng 0.
2702   */
2703 
2704   const double flat = 0.996633;  // flatting of the Earth
2705 
2706   int j, k1, k2, kmiss;
2707   double dpn1, pan1, s0, dlt, dta, ag;
2708   double s, r0, r2, dt1, dt2;
2709   Vec3 vrm, vre;
2710   Vec3 ubm, ube;
2711   Vec3 ax1, ax2;
2712   Mat3 mx1, mx2;
2713   Eclipse eclp;
2714 
2715   if (numpts < 2) return;
2716 
2717   for (j=0; j<numpts; j++)  // just in case you got to return empty-handed
2718   {
2719       lat[j] = 100.0;
2720       lng[j] = 0;
2721   }
2722 
2723   if (!eb_start_called) eclStart();
2724   if (eb_lunactive) return;  // only for solar eclipses
2725 
2726   if (umbra && (eb_phase[eb_eclselect-1] < 1)) return;
2727 
2728   // get the shadow details
2729   if(umbra)  eclp.umbra (mjd, eb_del_tdut, ubm, ube, dpn1, pan1);
2730   else eclp.penumd (mjd, eb_del_tdut, ubm, ube, dpn1, pan1);
2731 
2732   dpn1 *= 0.5;
2733 
2734   if (!umbra)
2735   {
2736       if (eb_penamode == 0)
2737       {
2738         dpn1 *= eb_penangle;
2739         pan1 *= eb_penangle;
2740       }
2741 
2742       if (eb_penamode > 0)
2743       {
2744           s0 = eb_penangle * tan(pan1);
2745           s0 = atan(s0);
2746           if (pan1 > 0)
2747           {
2748               dpn1 = dpn1 * s0 / pan1;
2749               pan1 = s0;
2750           }
2751       }
2752   };
2753 
2754   // get apex of umbra/penumbra cone
2755   pan1 = tan(pan1);
2756   if (pan1 < 0.0000174533) return;  // if cone is smaller that 0.001°
2757   dpn1 = dpn1 / pan1;
2758 
2759   s0 = - dot(ubm, ube);
2760   ubm = ubm + (s0 - dpn1) * ube;
2761 
2762   // get any vector perpendicular to the shadow
2763   ax1[0] = 0;
2764   ax1[1] = 0;
2765   ax1[2] = 1.0;
2766   ax2 = ax1 * ube;
2767   ax1 = vnorm(ax2) * pan1;
2768 
2769   ax2 = carpol(ube);
2770   mx1 = zrot(ax2[1]);
2771   mx2 = yrot(ax2[2]) * mx1;  // transform to a system where x points into the direction of the shadow
2772   mx1 = mxtrn(mx1);  // to get back to equatorial system after rotation
2773 
2774   ax2 = mxvct(mx2,ax1);  // vector which we will rotate numpts times
2775 
2776   // now loop for numpts points
2777   dta = 2.0*M_PI / double(numpts);
2778 
2779   for (j=0; j<numpts; j++)
2780   {
2781       ag = double(j) * dta;  // rotation angle of the cone vector
2782       mx2  = xrot(ag);
2783       ax1 = mxvct(mx2,ax2);
2784       ax1 = mxvct(mx1, ax1);
2785 
2786       vre = ube + ax1;
2787       vre = vnorm(vre);  // direction in which to find an intersection
2788       vrm[0] = ubm[0];
2789       vrm[1] = ubm[1];
2790       vrm[2] = ubm[2];
2791 
2792       s0 = - dot(vrm, vre); // distance Apex - fundamental plane
2793       r2 = dot (vrm,vrm);
2794       dlt = s0*s0 + 1.0 - r2;
2795       r0 = 1.0 - dlt;
2796 
2797       if (r0 > 0) r0 = sqrt (r0);
2798       else r0 = 0;   // distance center of Earth - shadow axis
2799 
2800       // calculate the coordinates if there is an intersecton
2801       if (r0 < 1.0)  // there should be an intersection
2802       {
2803        if (dlt > 0) dlt = sqrt(dlt);
2804        else dlt = 0;
2805        s = s0 - dlt;  // distance Apex - fundamental plane
2806        vrm = vrm + s * vre;
2807        vrm = vnorm(vrm);
2808        vrm[2] *= flat;    // vector to intersection
2809        vre = carpol(vrm);
2810        lng[j] = vre[1] - lsidtim(mjd,0,0)*0.261799387799; // geographic coordinates
2811        if (lng[j] > 2*M_PI) lng[j] -= 2.0*M_PI;
2812        if (lng[j] < 0.0) lng[j] += 2.0*M_PI;
2813        lat[j] = sqrt(vrm[0]*vrm[0] + vrm[1]*vrm[1])*0.993305615;
2814        lat[j] = atan23(vrm[2],lat[j]);
2815        lat[j] /= degrad;
2816        lng[j] /= degrad;
2817 
2818        if (lng[j] < 0.0) lng[j] += 360.0;
2819        if (lng[j] > 360.0) lng[j] -= 360.0;
2820       }
2821       else  // no intersection.
2822       {
2823           lat[j] = 100.0;
2824           lng[j] = 0;
2825       };
2826   }
2827 
2828   k1 = -1;
2829   k2 = -1;
2830   kmiss = 0;
2831   for (j=0; j<numpts; j++)  // check for missing points
2832   {
2833       if (lat[j] < 100.0)
2834       {
2835           if (k1 < 0) k1 = j;  // first valid point
2836           k2 = j;              // last valid point
2837       }
2838       else kmiss++;
2839   }
2840 
2841   if ((kmiss < 2) || (kmiss >= (numpts -1))) return;  // cone completely on Earth surface or not at all
2842 
2843   dt1 = double(k1) * dta;
2844   dt2 = double(k2) * dta;
2845   k1--;
2846   k2++;
2847   if (k1 < 0) k1 = numpts - 1;  // wrap around
2848   if (k2 >= (numpts-1)) k2 = 0;
2849   dta = 2.0*M_PI / double(numpts*20);
2850 
2851   for (j=1; j<20; j++) // go in smaller steps to get closer to the borderline
2852   {
2853       ag = dt1 - double(j) * dta;  // rotation angle of the cone vector
2854       mx2  = xrot(ag);
2855       ax1 = mxvct(mx2,ax2);
2856       ax1 = mxvct(mx1, ax1);
2857 
2858       vre = ube + ax1;
2859       vre = vnorm(vre);  // direction in which to find an intersection
2860       vrm[0] = ubm[0];
2861       vrm[1] = ubm[1];
2862       vrm[2] = ubm[2];
2863 
2864       s0 = - dot(vrm, vre); // distance Apex - fundamental plane
2865       r2 = dot (vrm,vrm);
2866       dlt = s0*s0 + 1.0 - r2;
2867       r0 = 1.0 - dlt;
2868 
2869       if (r0 > 0) r0 = sqrt (r0);
2870       else r0 = 0;   // distance center of Earth - shadow axis
2871 
2872       // calculate the coordinates if there is an intersecton
2873       if (r0 < 1.0)  // there should be an intersection
2874       {
2875        if (dlt > 0) dlt = sqrt(dlt);
2876        else dlt = 0;
2877        s = s0 - dlt;  // distance Apex - fundamental plane
2878        vrm = vrm + s * vre;
2879        vrm = vnorm(vrm);
2880        vrm[2] *= flat;    // vector to intersection
2881        vre = carpol(vrm);
2882        lng[k1] = vre[1] - lsidtim(mjd,0,0)*0.261799387799; // geographic coordinates
2883        if (lng[k1] > 2*M_PI) lng[k1] -= 2.0*M_PI;
2884        if (lng[k1] < 0.0) lng[k1] += 2.0*M_PI;
2885        lat[k1] = sqrt(vrm[0]*vrm[0] + vrm[1]*vrm[1])*0.993305615;
2886        lat[k1] = atan23(vrm[2],lat[k1]);
2887        lat[k1] /= degrad;
2888        lng[k1] /= degrad;
2889 
2890        if (lng[k1] < 0.0) lng[k1] += 360.0;
2891        if (lng[k1] > 360.0) lng[k1] -= 360.0;
2892       }
2893       else break;
2894   }
2895 
2896   for (j=1; j<20; j++) // go in smaller steps to get closer to the borderline
2897   {
2898       ag = dt2 + double(j) * dta;  // rotation angle of the cone vector
2899       mx2  = xrot(ag);
2900       ax1 = mxvct(mx2,ax2);
2901       ax1 = mxvct(mx1, ax1);
2902 
2903       vre = ube + ax1;
2904       vre = vnorm(vre);  // direction in which to find an intersection
2905       vrm[0] = ubm[0];
2906       vrm[1] = ubm[1];
2907       vrm[2] = ubm[2];
2908 
2909       s0 = - dot(vrm, vre); // distance Apex - fundamental plane
2910       r2 = dot (vrm,vrm);
2911       dlt = s0*s0 + 1.0 - r2;
2912       r0 = 1.0 - dlt;
2913 
2914       if (r0 > 0) r0 = sqrt (r0);
2915       else r0 = 0;   // distance center of Earth - shadow axis
2916 
2917       // calculate the coordinates if there is an intersecton
2918       if (r0 < 1.0)  // there should be an intersection
2919       {
2920        if (dlt > 0) dlt = sqrt(dlt);
2921        else dlt = 0;
2922        s = s0 - dlt;  // distance Apex - fundamental plane
2923        vrm = vrm + s * vre;
2924        vrm = vnorm(vrm);
2925        vrm[2] *= flat;    // vector to intersection
2926        vre = carpol(vrm);
2927        lng[k2] = vre[1] - lsidtim(mjd,0,0)*0.261799387799; // geographic coordinates
2928        if (lng[k2] > 2*M_PI) lng[k2] -= 2.0*M_PI;
2929        if (lng[k2] < 0.0) lng[k2] += 2.0*M_PI;
2930        lat[k2] = sqrt(vrm[0]*vrm[0] + vrm[1]*vrm[1])*0.993305615;
2931        lat[k2] = atan23(vrm[2],lat[k2]);
2932        lat[k2] /= degrad;
2933        lng[k2] /= degrad;
2934 
2935        if (lng[k2] < 0.0) lng[k2] += 360.0;
2936        if (lng[k2] > 360.0) lng[k2] -= 360.0;
2937       }
2938       else break;
2939   }
2940 
2941 }
2942 
2943