1 /*
2     Gpredict: Real-time satellite tracking and orbit prediction program
3 
4     Copyright (C)  2001-2013  Alexandru Csete, OZ9AEC.
5     Parts are Copyright John A. Magliacane, KD2BD 1991-2003 (indicated below)
6 
7     Authors: Alexandru Csete <oz9aec@gmail.com>
8              John A. Magliacane, KD2BD.
9              Charles Suprin <hamaa1vs@gmail.com>
10 	     Daniel Estevez <daniel@destevez.net>
11 
12     Comments, questions and bugreports should be submitted via
13     http://sourceforge.net/projects/gpredict/
14     More details can be found at the project home page:
15 
16             http://gpredict.oz9aec.net/
17 
18     This program is free software; you can redistribute it and/or modify
19     it under the terms of the GNU General Public License as published by
20     the Free Software Foundation; either version 2 of the License, or
21     (at your option) any later version.
22 
23     This program is distributed in the hope that it will be useful,
24     but WITHOUT ANY WARRANTY; without even the implied warranty of
25     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26     GNU General Public License for more details.
27 
28     You should have received a copy of the GNU General Public License
29     along with this program; if not, visit http://www.fsf.org/
30 */
31 
32 #ifdef HAVE_CONFIG_H
33 #include <build-config.h>
34 #endif
35 
36 #include <glib.h>
37 #include <glib/gi18n.h>
38 
39 #include "gtk-sat-data.h"
40 #include "orbit-tools.h"
41 #include "predict-tools.h"
42 #include "sat-cfg.h"
43 #include "sat-log.h"
44 #include "sgpsdp/sgp4sdp4.h"
45 #include "time-tools.h"
46 
47 static pass_t  *get_pass_engine(sat_t * sat_in, qth_t * qth, gdouble start,
48                                 gdouble maxdt, gdouble min_el);
49 
50 /**
51  * \brief SGP4SDP4 driver for doing AOS/LOS calculations.
52  * \param sat Pointer to the satellite data.
53  * \param qth Pointer to the QTH data.
54  * \param t The time for calculation (Julian Date)
55  */
predict_calc(sat_t * sat,qth_t * qth,gdouble t)56 void predict_calc(sat_t * sat, qth_t * qth, gdouble t)
57 {
58     obs_set_t       obs_set;
59     geodetic_t      sat_geodetic;
60     geodetic_t      obs_geodetic;
61     double          age;
62 
63     obs_geodetic.lon = qth->lon * de2ra;
64     obs_geodetic.lat = qth->lat * de2ra;
65     obs_geodetic.alt = qth->alt / 1000.0;
66     obs_geodetic.theta = 0;
67 
68     sat->jul_utc = t;
69     sat->tsince = (sat->jul_utc - sat->jul_epoch) * xmnpda;
70 
71     /* call the norad routines according to the deep-space flag */
72     if (sat->flags & DEEP_SPACE_EPHEM_FLAG)
73         SDP4(sat, sat->tsince);
74     else
75         SGP4(sat, sat->tsince);
76 
77     Convert_Sat_State(&sat->pos, &sat->vel);
78 
79     /* get the velocity of the satellite */
80     Magnitude(&sat->vel);
81     sat->velo = sat->vel.w;
82     Calculate_Obs(sat->jul_utc, &sat->pos, &sat->vel, &obs_geodetic, &obs_set);
83     Calculate_LatLonAlt(sat->jul_utc, &sat->pos, &sat_geodetic);
84 
85     while (sat_geodetic.lon < -pi)
86         sat_geodetic.lon += twopi;
87 
88     while (sat_geodetic.lon > (pi))
89         sat_geodetic.lon -= twopi;
90 
91     sat->az = Degrees(obs_set.az);
92     sat->el = Degrees(obs_set.el);
93     sat->range = obs_set.range;
94     sat->range_rate = obs_set.range_rate;
95     sat->ssplat = Degrees(sat_geodetic.lat);
96     sat->ssplon = Degrees(sat_geodetic.lon);
97     sat->alt = sat_geodetic.alt;
98     sat->ma = Degrees(sat->phase);
99     sat->ma *= 256.0 / 360.0;
100     sat->phase = Degrees(sat->phase);
101 
102     /* same formulas, but the one from predict is nicer */
103     //sat->footprint = 2.0 * xkmper * acos (xkmper/sat->pos.w);
104     sat->footprint = 12756.33 * acos(xkmper / (xkmper + sat->alt));
105     age = sat->jul_utc - sat->jul_epoch;
106     sat->orbit = (long)floor((sat->tle.xno * xmnpda / twopi +
107                               age * sat->tle.bstar * ae) * age +
108                              (sat->tle.xmo + sat->tle.omegao) / twopi) + sat->tle.revnum ;
109 }
110 
111 /**
112  * \brief Find the AOS time of the next pass.
113  * \author Alexandru Csete, OZ9AEC
114  * \author John A. Magliacane, KD2BD
115  * \param sat Pointer to the satellite data.
116  * \param qth Pointer to the QTH data.
117  * \param start The time where calculation should start.
118  * \param maxdt The upper time limit in days (0.0 = no limit)
119  * \return The time of the next AOS or 0.0 if the satellite has no AOS.
120  *
121  * This function finds the time of AOS for the first coming pass taking place
122  * no earlier that start.
123  * If the satellite is currently within range, the function first calls
124  * find_los to get the next LOS time. Then the calculations are done using
125  * the new start time.
126  */
find_aos(sat_t * sat,qth_t * qth,gdouble start,gdouble maxdt)127 gdouble find_aos(sat_t * sat, qth_t * qth, gdouble start, gdouble maxdt)
128 {
129     gdouble         t = start;
130     gdouble         aostime = 0.0;
131 
132     /* make sure current sat values are in sync with the time */
133     predict_calc(sat, qth, start);
134 
135     /* check whether satellite has aos */
136     if (!has_aos(sat, qth))
137         return 0.0;
138 
139     if (sat->el > 0.0)
140         t = find_los(sat, qth, start, maxdt) + 0.014;   // +20 min
141 
142     /* invalid time (potentially returned by find_los) */
143     if (t < 0.1)
144         return 0.0;
145 
146     /* update satellite data */
147     predict_calc(sat, qth, t);
148 
149     /* use upper time limit */
150     if (maxdt > 0.0)
151     {
152 
153         /* coarse time steps */
154         while ((sat->el < -1.0) && (t <= (start + maxdt)))
155         {
156             t -= 0.00035 * (sat->el * ((sat->alt / 8400.0) + 0.46) - 2.0);
157             predict_calc(sat, qth, t);
158         }
159 
160         /* fine steps */
161         while ((aostime == 0.0) && (t <= (start + maxdt)))
162         {
163 
164             if (fabs(sat->el) < 0.005)
165             {
166                 aostime = t;
167             }
168             else
169             {
170                 t -= sat->el * sqrt(sat->alt) / 530000.0;
171                 predict_calc(sat, qth, t);
172             }
173 
174         }
175 
176     }
177     /* don't use upper time limit */
178     else
179     {
180         /* coarse time steps */
181         while (sat->el < -1.0)
182         {
183             t -= 0.00035 * (sat->el * ((sat->alt / 8400.0) + 0.46) - 2.0);
184             predict_calc(sat, qth, t);
185         }
186 
187         /* fine steps */
188         while (aostime == 0.0)
189         {
190 
191             if (fabs(sat->el) < 0.005)
192             {
193                 aostime = t;
194             }
195             else
196             {
197                 t -= sat->el * sqrt(sat->alt) / 530000.0;
198                 predict_calc(sat, qth, t);
199             }
200         }
201     }
202 
203     return aostime;
204 }
205 
206 /**
207  * \brief Find the LOS time of the next pass.
208  * \author Alexandru Csete, OZ9AEC
209  * \author John A. Magliacane, KD2BD
210  * \param sat Pointer to the satellite data.
211  * \param qth Pointer to the QTH data.
212  * \param start The time where calculation should start.
213  * \param maxdt The upper time limit in days (0.0 = no limit)
214  * \return The time of the next LOS or 0.0 if the satellite has no LOS.
215  *
216  * This function finds the time of LOS for the first coming pass taking place
217  * no earlier that start.
218  * If the satellite is currently out of range, the function first calls
219  * find_aos to get the next AOS time. Then the calculations are done using
220  * the new start time.
221  * The function has a built-in watchdog to ensure that we don't end up in
222  * lengthy loops.
223  */
find_los(sat_t * sat,qth_t * qth,gdouble start,gdouble maxdt)224 gdouble find_los(sat_t * sat, qth_t * qth, gdouble start, gdouble maxdt)
225 {
226     gdouble         t = start;
227     gdouble         lostime = 0.0;
228     gdouble         eltemp;
229 
230     predict_calc(sat, qth, start);
231 
232     /* check whether satellite has aos */
233     if (!has_aos(sat, qth))
234     {
235         return 0.0;
236     }
237 
238     if (sat->el < 0.0)
239         t = find_aos(sat, qth, start, maxdt) + 0.001;   // +1.5 min
240 
241     /* invalid time (potentially returned by find_aos) */
242     if (t < 0.01)
243         return 0.0;
244 
245     /* update satellite data */
246     predict_calc(sat, qth, t);
247 
248     /* use upper time limit */
249     if (maxdt > 0.0)
250     {
251         /* coarse steps */
252         while ((sat->el >= 1.0) && (t <= (start + maxdt)))
253         {
254             t += cos((sat->el - 1.0) * de2ra) * sqrt(sat->alt) / 25000.0;
255             predict_calc(sat, qth, t);
256         }
257 
258         /* fine steps */
259         while ((lostime == 0.0) && (t <= (start + maxdt)))
260         {
261             t += sat->el * sqrt(sat->alt) / 502500.0;
262             predict_calc(sat, qth, t);
263 
264             if (fabs(sat->el) < 0.005)
265             {
266                 /* Two things are true at LOS time, the elevation is a zero and
267                    sat is descending. This checks that those two are true. */
268                 eltemp = sat->el;
269 
270                 /* check elevation 1 second earlier */
271                 predict_calc(sat, qth, t - 1.0 / 86400.0);
272 
273                 if (sat->el > eltemp)
274                     lostime = t;
275             }
276         }
277     }
278     /* don't use upper limit */
279     else
280     {
281         /* coarse steps */
282         while (sat->el >= 1.0)
283         {
284             t += cos((sat->el - 1.0) * de2ra) * sqrt(sat->alt) / 25000.0;
285             predict_calc(sat, qth, t);
286         }
287 
288         /* fine steps */
289         while (lostime == 0.0)
290         {
291             t += sat->el * sqrt(sat->alt) / 502500.0;
292             predict_calc(sat, qth, t);
293 
294             if (fabs(sat->el) < 0.005)
295             {
296                 /* two things are true at LOS time
297                    The elevation is a zero and descending.
298                    This checks that those two are true.
299                  */
300                 eltemp = sat->el;
301 
302                 /*check elevation 1 second earlier */
303                 predict_calc(sat, qth, t - 1.0 / 86400.0);
304 
305                 if (sat->el > eltemp)
306                     lostime = t;
307             }
308         }
309     }
310 
311     return lostime;
312 }
313 
314 /**
315  * \brief Find AOS time of current pass.
316  * \param sat The satellite to find AOS for.
317  * \param qth The ground station.
318  * \param start Start time, prefereably now.
319  * \return The time of the previous AOS or 0.0 if the satellite has no AOS.
320  *
321  * This function can be used to find the AOS time in the past of the
322  * current pass.
323  */
find_prev_aos(sat_t * sat,qth_t * qth,gdouble start)324 gdouble find_prev_aos(sat_t * sat, qth_t * qth, gdouble start)
325 {
326     gdouble         aostime = start;
327 
328     /* make sure current sat values are in sync with the time */
329     predict_calc(sat, qth, start);
330 
331     /* check whether satellite has aos */
332     if (!has_aos(sat, qth))
333     {
334         return 0.0;
335     }
336 
337     while (sat->el >= 0.0)
338     {
339         aostime -= 0.0005;      // 0.75 min
340         predict_calc(sat, qth, aostime);
341     }
342 
343     return aostime;
344 }
345 
346 /**
347  * \brief Predict the next pass.
348  * \param sat Pointer to the satellite data.
349  * \param qth Pointer to the observer data.
350  * \param maxdt The maximum number of days to look ahead.
351  * \return Pointer newly allocated pass_t structure that should be freed
352  *         with free_pass when no longer needed, or NULL if no pass can be
353  *         found.
354  *
355  * This function simply wraps the get_pass function using the current time
356  * as parameter.
357  *
358  * \note the data in sat will be corrupt (future) and must be refreshed
359  *       by the caller, if the caller will need it later on (eg. if the caller
360  *       is GtkSatList).
361  */
get_next_pass(sat_t * sat,qth_t * qth,gdouble maxdt)362 pass_t         *get_next_pass(sat_t * sat, qth_t * qth, gdouble maxdt)
363 {
364     gdouble         now;
365 
366     /* get the current time and call the get_pass function */
367     now = get_current_daynum();
368 
369     return get_pass(sat, qth, now, maxdt);
370 }
371 
372 /**
373  * \brief Predict upcoming passes starting now
374  * \param sat Pointer to the satellite data.
375  * \param qth Pointer to the observer data.
376  * \param maxdt The maximum number of days to look ahead.
377  * \param num The number of passes to predict.
378  * \return A singly linked list of pass_t structures or NULL if
379  *         there was an error.
380  *
381  * This function simply wraps the get_passes function using the
382  * current time as parameter.
383  *
384  * \note the data in sat will be corrupt (future) and must be refreshed
385  *       by the caller, if the caller will need it later on (eg. if the caller
386  *       is GtkSatList).
387  */
get_next_passes(sat_t * sat,qth_t * qth,gdouble maxdt,guint num)388 GSList         *get_next_passes(sat_t * sat, qth_t * qth, gdouble maxdt,
389                                 guint num)
390 {
391     gdouble         now;
392 
393     /* get the current time and call the get_pass function */
394     now = get_current_daynum();
395 
396     return get_passes(sat, qth, now, maxdt, num);
397 }
398 
399 /**
400  * \brief Predict first pass after a certain time.
401  * \param sat Pointer to the satellite data.
402  * \param qth Pointer to the location data.
403  * \param start Starting time.
404  * \param maxdt The maximum number of days to look ahead (0 for no limit).
405  * \return Pointer to a newly allocated pass_t structure or NULL if
406  *         there was an error.
407  *
408  *   This function assumes that you want a pass that achieves the
409  *   minimum elevation of is configured for.
410  */
get_pass(sat_t * sat_in,qth_t * qth,gdouble start,gdouble maxdt)411 pass_t *get_pass(sat_t * sat_in, qth_t * qth, gdouble start, gdouble maxdt)
412 {
413     int      min_ele = sat_cfg_get_int(SAT_CFG_INT_PRED_MIN_EL);
414 
415     if (min_ele == 0)
416         min_ele = 1;
417 
418     return get_pass_engine(sat_in, qth, start, maxdt, min_ele);
419 }
420 
421 /**
422  * \brief Predict first pass after a certain time ignoring the min elevation.
423  * \param sat Pointer to the satellite data.
424  * \param qth Pointer to the location data.
425  * \param start Starting time.
426  * \param maxdt The maximum number of days to look ahead (0 for no limit).
427  * \return Pointer to a newly allocated pass_t structure or NULL if
428  *         there was an error.
429  * This function assumes that you want a pass that achieves the
430  * minimum elevation of is configured for.
431  */
get_pass_no_min_el(sat_t * sat_in,qth_t * qth,gdouble start,gdouble maxdt)432 pass_t         *get_pass_no_min_el(sat_t * sat_in, qth_t * qth, gdouble start,
433                                    gdouble maxdt)
434 {
435     return get_pass_engine(sat_in, qth, start, maxdt, 0.0);
436 }
437 
438 /**
439  * \brief Predict first pass after a certain time.
440  * \param sat Pointer to the satellite data.
441  * \param qth Pointer to the location data.
442  * \param start Starting time.
443  * \param maxdt The maximum number of days to look ahead (0 for no limit).
444  * \return Pointer to a newly allocated pass_t structure or NULL if
445  *         there was an error.
446  *
447  * This function will find the first upcoming pass with AOS no earlier than
448  * t = start and no later than t = (start+maxdt).
449  *
450  * \note For no time limit use maxdt = 0.0
451  *
452  * \note the data in sat will be corrupt (future) and must be refreshed
453  *       by the caller, if the caller will need it later on (eg. if the caller
454  *       is GtkSatList).
455  *
456  * \note Prepending to a singly linked list is much faster than appending.
457  *       Therefore, the elements are prepended whereafter the GSList is
458  *       reversed
459  */
get_pass_engine(sat_t * sat_in,qth_t * qth,gdouble start,gdouble maxdt,gdouble min_el)460 static pass_t  *get_pass_engine(sat_t * sat_in, qth_t * qth, gdouble start,
461                                 gdouble maxdt, gdouble min_el)
462 {
463     gdouble         aos = 0.0;  /* time of AOS */
464     gdouble         tca = 0.0;  /* time of TCA */
465     gdouble         los = 0.0;  /* time of LOS */
466     gdouble         dt = 0.0;   /* time diff */
467     gdouble         step = 0.0; /* time step */
468     gdouble         t0 = start;
469     gdouble         t;          /* current time counter */
470     gdouble         tres = 0.0; /* required time resolution */
471     gdouble         max_el = 0.0;       /* maximum elevation */
472     pass_t         *pass = NULL;
473     pass_detail_t  *detail = NULL;
474     gboolean        done = FALSE;
475     guint           iter = 0;   /* number of iterations */
476     sat_t          *sat, sat_working;
477 
478     /* FIXME: watchdog */
479 
480     /*copy sat_in to a working structure */
481     sat = memcpy(&sat_working, sat_in, sizeof(sat_t));
482 
483     /* get time resolution; sat-cfg stores it in seconds */
484     tres = sat_cfg_get_int(SAT_CFG_INT_PRED_RESOLUTION) / 86400.0;
485 
486     /* loop until we find a pass with elevation > SAT_CFG_INT_PRED_MIN_EL
487        or we run out of time
488        FIXME: we should have a safety break
489      */
490     while (!done)
491     {
492         /* Find los of next pass or of current pass */
493         los = find_los(sat, qth, t0, maxdt);    // See if a pass is ongoing
494         aos = find_aos(sat, qth, t0, start + maxdt - t0);
495 
496         if (aos > los)
497             // los is from an currently happening pass, find previous aos
498             aos = find_prev_aos(sat, qth, t0);
499 
500         /* aos = 0.0 means no aos */
501         if (aos == 0.0)
502             done = TRUE;
503 
504         /* check whether we are within time limits;
505            maxdt = 0 mean no time limit.
506          */
507         else if ((maxdt > 0.0) && (aos > (start + maxdt)))
508         {
509             done = TRUE;
510         }
511         else
512         {
513             //los = find_los (sat, qth, aos + 0.001, maxdt); // +1.5 min later
514             dt = los - aos;
515 
516             /* get time step, which will give us the max number of entries */
517             step = dt / sat_cfg_get_int(SAT_CFG_INT_PRED_NUM_ENTRIES);
518 
519             /* but if this is smaller than the required resolution
520                we go with the resolution
521              */
522             if (step < tres)
523                 step = tres;
524 
525             /* create a pass_t entry; FIXME: g_try_new in 2.8 */
526             pass = g_new(pass_t, 1);
527 
528             pass->aos = aos;
529             pass->los = los;
530             pass->max_el = 0.0;
531             pass->aos_az = 0.0;
532             pass->los_az = 0.0;
533             pass->maxel_az = 0.0;
534             pass->vis[0] = '-';
535             pass->vis[1] = '-';
536             pass->vis[2] = '-';
537             pass->vis[3] = 0;
538             pass->satname = g_strdup(sat->nickname);
539             pass->details = NULL;
540             /*copy qth data into the pass for later comparisons */
541             qth_small_save(qth, &(pass->qth_comp));
542 
543             /* iterate over each time step */
544             for (t = pass->aos; t <= pass->los; t += step)
545             {
546 
547                 /* calculate satellite data */
548                 predict_calc(sat, qth, t);
549 
550                 /* in the first iter we want to store
551                    pass->aos_az
552                  */
553                 if (t == pass->aos)
554                 {
555                     pass->aos_az = sat->az;
556                     pass->orbit = sat->orbit;
557                 }
558 
559                 /* append details to sat->details */
560                 detail = g_new(pass_detail_t, 1);
561                 detail->time = t;
562                 detail->pos.x = sat->pos.x;
563                 detail->pos.y = sat->pos.y;
564                 detail->pos.z = sat->pos.z;
565                 detail->pos.w = sat->pos.w;
566                 detail->vel.x = sat->vel.x;
567                 detail->vel.y = sat->vel.y;
568                 detail->vel.z = sat->vel.z;
569                 detail->vel.w = sat->vel.w;
570                 detail->velo = sat->velo;
571                 detail->az = sat->az;
572                 detail->el = sat->el;
573                 detail->range = sat->range;
574                 detail->range_rate = sat->range_rate;
575                 detail->lat = sat->ssplat;
576                 detail->lon = sat->ssplon;
577                 detail->alt = sat->alt;
578                 detail->ma = sat->ma;
579                 detail->phase = sat->phase;
580                 detail->footprint = sat->footprint;
581                 detail->orbit = sat->orbit;
582                 detail->vis = get_sat_vis(sat, qth, t);
583 
584                 /* also store visibility "bit" */
585                 switch (detail->vis)
586                 {
587                 case SAT_VIS_VISIBLE:
588                     pass->vis[0] = 'V';
589                     break;
590                 case SAT_VIS_DAYLIGHT:
591                     pass->vis[1] = 'D';
592                     break;
593                 case SAT_VIS_ECLIPSED:
594                     pass->vis[2] = 'E';
595                     break;
596                 default:
597                     break;
598                 }
599 
600                 pass->details = g_slist_prepend(pass->details, detail);
601 
602                 /* store elevation if greater than the
603                    previously stored one
604                  */
605                 if (sat->el > max_el)
606                 {
607                     max_el = sat->el;
608                     tca = t;
609                     pass->maxel_az = sat->az;
610                 }
611 
612                 /*     g_print ("TIME: %f\tAZ: %f\tEL: %f (MAX: %f)\n", */
613                 /*           t, sat->az, sat->el, max_el); */
614             }
615 
616             pass->details = g_slist_reverse(pass->details);
617 
618             /* calculate satellite data */
619             predict_calc(sat, qth, pass->los);
620             /* store los_az, max_el and tca */
621             pass->los_az = sat->az;
622             pass->max_el = max_el;
623             pass->tca = tca;
624 
625             /* check whether this pass is good */
626             if (max_el >= min_el)
627             {
628                 done = TRUE;
629             }
630             else
631             {
632                 done = FALSE;
633                 t0 = los + 0.014;       // +20 min
634                 free_pass(pass);
635                 pass = NULL;
636             }
637 
638             iter++;
639         }
640     }
641 
642     return pass;
643 }
644 
645 /**
646  * Predict passes after a certain time.
647  *
648  * This function calculates num upcoming passes with AOS no earlier
649  * than t = start and not later that t = (start+maxdt). The function will
650  * repeatedly call get_pass until the number of predicted passes is equal to
651  * num, the time has reached limit or the get_pass function returns NULL.
652  *
653  * \note For no time limit use maxdt = 0.0
654  *
655  * \note the data in sat will be corrupt (future) and must be refreshed
656  *       by the caller, if the caller will need it later on (eg. if the caller
657  *       is GtkSatList).
658  *
659  * \note Prepending to a singly linked list is much faster than appending.
660  *       Therefore, the elements are prepended whereafter the GSList is
661  *       reversed
662  */
get_passes(sat_t * sat,qth_t * qth,gdouble start,gdouble maxdt,guint num)663 GSList         *get_passes(sat_t * sat, qth_t * qth, gdouble start,
664                            gdouble maxdt, guint num)
665 {
666     GSList         *passes = NULL;
667     pass_t         *pass = NULL;
668     guint           i;
669     gdouble         t;
670 
671     /* if no number has been specified
672        set it to something big */
673     if (num == 0)
674         num = 100;
675 
676     t = start;
677 
678     for (i = 0; i < num; i++)
679     {
680         pass = get_pass(sat, qth, t, maxdt);
681 
682         if (pass != NULL)
683         {
684             passes = g_slist_prepend(passes, pass);
685             t = pass->los + 0.014;      // +20 min
686 
687             /* if maxdt > 0.0 check whether we have reached t = start+maxdt
688                if yes finish predictions
689              */
690             if ((maxdt > 0.0) && (t >= (start + maxdt)))
691             {
692                 i = num;
693             }
694         }
695         else
696         {
697             /* we can't get any more passes */
698             i = num;
699         }
700 
701     }
702 
703     if (passes != NULL)
704         passes = g_slist_reverse(passes);
705 
706     sat_log_log(SAT_LOG_LEVEL_INFO,
707                 _("%s: Found %d passes for %s in time window [%f;%f]"),
708                 __func__, g_slist_length(passes), sat->nickname, start,
709                 start + maxdt);
710 
711     return passes;
712 }
713 
copy_pass(pass_t * pass)714 pass_t         *copy_pass(pass_t * pass)
715 {
716     pass_t         *new;
717 
718     new = g_try_new(pass_t, 1);
719 
720     if (new != NULL)
721     {
722         new->aos = pass->aos;
723         new->los = pass->los;
724         new->tca = pass->tca;
725         new->max_el = pass->max_el;
726         new->aos_az = pass->aos_az;
727         new->los_az = pass->los_az;
728         new->orbit = pass->orbit;
729         new->maxel_az = pass->maxel_az;
730         new->vis[0] = pass->vis[0];
731         new->vis[1] = pass->vis[1];
732         new->vis[2] = pass->vis[2];
733         new->vis[3] = pass->vis[3];
734         new->details = copy_pass_details(pass->details);
735 
736         if (pass->satname != NULL)
737             new->satname = g_strdup(pass->satname);
738         else
739             new->satname = NULL;
740     }
741 
742     return new;
743 }
744 
copy_pass_details(GSList * details)745 GSList         *copy_pass_details(GSList * details)
746 {
747     GSList         *new = NULL;
748     guint           i, n;
749 
750     n = g_slist_length(details);
751     for (i = 0; i < n; i++)
752     {
753         new = g_slist_prepend(new,
754                               copy_pass_detail(PASS_DETAIL
755                                                (g_slist_nth_data
756                                                 (details, i))));
757     }
758 
759     new = g_slist_reverse(new);
760 
761     return new;
762 }
763 
copy_pass_detail(pass_detail_t * detail)764 pass_detail_t  *copy_pass_detail(pass_detail_t * detail)
765 {
766     pass_detail_t  *new;
767 
768     /* create a pass_t entry; FIXME: g_try_new in 2.8 */
769     new = g_new(pass_detail_t, 1);
770 
771     new->time = detail->time;
772     new->pos.x = detail->pos.x;
773     new->pos.y = detail->pos.y;
774     new->pos.z = detail->pos.z;
775     new->pos.w = detail->pos.w;
776     new->vel.x = detail->vel.x;
777     new->vel.y = detail->vel.y;
778     new->vel.z = detail->vel.z;
779     new->vel.w = detail->vel.w;
780     new->velo = detail->velo;
781     new->az = detail->az;
782     new->el = detail->el;
783     new->range = detail->range;
784     new->range_rate = detail->range_rate;
785     new->lat = detail->lat;
786     new->lon = detail->lon;
787     new->alt = detail->alt;
788     new->ma = detail->ma;
789     new->phase = detail->phase;
790     new->footprint = detail->footprint;
791     new->orbit = detail->orbit;
792     new->vis = detail->vis;
793 
794     return new;
795 }
796 
free_pass(pass_t * pass)797 void free_pass(pass_t * pass)
798 {
799     if (pass != NULL)
800     {
801         free_pass_details(pass->details);
802 
803         if (pass->satname != NULL)
804         {
805             g_free(pass->satname);
806             pass->satname = NULL;
807         }
808 
809         g_free(pass);
810         pass = NULL;
811     }
812 }
813 
814 /** \brief Free a list of passes. */
free_passes(GSList * passes)815 void free_passes(GSList * passes)
816 {
817     guint           n, i;
818     gpointer        pass;
819 
820     n = g_slist_length(passes);
821 
822     for (i = 0; i < n; i++)
823     {
824         pass = g_slist_nth_data(passes, i);
825 
826         /* free element data */
827         free_pass(PASS(pass));
828     }
829 
830     /* now free the list elements */
831     g_slist_free(passes);
832     passes = NULL;
833 }
834 
835 /**
836  * \brief Free a pass detail structure.
837  *
838  * This function is not rarely useful except for the
839  * free_pass function.
840  */
free_pass_detail(pass_detail_t * detail)841 void free_pass_detail(pass_detail_t * detail)
842 {
843     g_free(detail);
844     detail = NULL;
845 }
846 
847 /** Free the whole list of details. */
free_pass_details(GSList * details)848 void free_pass_details(GSList * details)
849 {
850     guint           n, i;
851     gpointer        detail;
852 
853     n = g_slist_length(details);
854 
855     for (i = 0; i < n; i++)
856     {
857 
858         detail = g_slist_nth_data(details, i);
859 
860         /* free element data */
861         free_pass_detail(PASS_DETAIL(detail));
862     }
863 
864     /* free list elements */
865     g_slist_free(details);
866     details = NULL;
867 }
868 
869 /**
870  * \brief Get current pass.
871  * \param sat Pointer to the satellite data.
872  * \param qth Pointer to the QTH data.
873  * \param start Time to start calculations; use 0.0 for now.
874  * \return Pointer to a newly allocated pass_t structure or NULL if
875  *         there was an error.
876  *
877  * Assuming that sat->el > 0.0 this function calculates the details of the
878  * current pass from AOS time to LOS time.
879  * First the function goes back in time to before the AOS, then it calls
880  * the get_pass_no_min_el function to get the details of the current pass
881  * disregarding any minimum elevation requirements.
882  *
883  * \note The start parameter has been introduced to allow correct use of this
884  *       function in non-realtime cases.
885  */
get_current_pass(sat_t * sat_in,qth_t * qth,gdouble start)886 pass_t         *get_current_pass(sat_t * sat_in, qth_t * qth, gdouble start)
887 {
888     gdouble         t, t0;
889     gdouble         el0;
890     sat_t          *sat, sat_working;
891     pass_t         *pass;
892 
893     /*copy sat_in to a working structure */
894     sat = memcpy(&sat_working, sat_in, sizeof(sat_t));
895 
896     if (start > 0.0)
897         t = start;
898     else
899         t = get_current_daynum();
900     predict_calc(sat, qth, t);
901 
902     /*save initial conditions for later comparison */
903     t0 = t;
904     el0 = sat->el;
905 
906     /* check whether satellite has aos */
907     if (!has_aos(sat, qth))
908         return NULL;
909 
910     /* find a time before AOS */
911     while (sat->el > 0.0)
912     {
913         predict_calc(sat, qth, t);
914         t -= 0.007;             // +10 min
915     }
916 
917     pass = get_pass_no_min_el(sat, qth, t, 0.0);
918     if (el0 > 0.0)
919     {
920         /* this function is only specified if the elevation
921            is greater than zero at the time it is called */
922         if (pass)
923         {
924             if (pass->aos > t0)
925                 sat_log_log(SAT_LOG_LEVEL_ERROR,
926                             _
927                             ("%s: Returning a pass for %s that starts after the seeded time."),
928                             __func__, sat->nickname);
929 
930             if (pass->los < t0)
931                 sat_log_log(SAT_LOG_LEVEL_ERROR,
932                             _
933                             ("%s: Returning a pass for %s that ends before the seeded time."),
934                             __func__, sat->nickname);
935         }
936     }
937 
938     return pass;
939 }
940