/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* Gpredict: Real-time satellite tracking and orbit prediction program Copyright (C) 2001-2007 Alexandru Csete, OZ9AEC. Parts are Copyright John A. Magliacane, KD2BD 1991-2003 (indicated below) Authors: Alexandru Csete John A. Magliacane, KD2BD. Comments, questions and bugreports should be submitted via http://sourceforge.net/projects/groundstation/ More details can be found at the project home page: http://groundstation.sourceforge.net/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, visit http://www.fsf.org/ */ #include #include #include "sgpsdp/sgp4sdp4.h" #ifdef HAVE_CONFIG_H # include #endif #include "gtk-sat-data.h" #include "time-tools.h" #include "sat-cfg.h" #include "orbit-tools.h" #include "predict-tools.h" /** \brief SGP4SDP4 driver for doing AOS/LOS calculations. * \param sat Pointer to the satellite data. * \param qth Pointer to the QTH data. * \param t The time for calculation (Julian Date) * */ void predict_calc (sat_t *sat, qth_t *qth, gdouble t) { obs_set_t obs_set; geodetic_t sat_geodetic; geodetic_t obs_geodetic; double age; obs_geodetic.lon = qth->lon * de2ra; obs_geodetic.lat = qth->lat * de2ra; obs_geodetic.alt = qth->alt / 1000.0; obs_geodetic.theta = 0; sat->jul_utc = t; sat->tsince = (sat->jul_utc - sat->jul_epoch) * xmnpda; /* call the norad routines according to the deep-space flag */ if (sat->flags & DEEP_SPACE_EPHEM_FLAG) SDP4 (sat, sat->tsince); else SGP4 (sat, sat->tsince); Convert_Sat_State (&sat->pos, &sat->vel); /* get the velocity of the satellite */ Magnitude (&sat->vel); sat->velo = sat->vel.w; Calculate_Obs (sat->jul_utc, &sat->pos, &sat->vel, &obs_geodetic, &obs_set); Calculate_LatLonAlt (sat->jul_utc, &sat->pos, &sat_geodetic); while (sat_geodetic.lon < -pi) sat_geodetic.lon += twopi; while (sat_geodetic.lon > (pi)) sat_geodetic.lon -= twopi; sat->az = Degrees (obs_set.az); sat->el = Degrees (obs_set.el); sat->range = obs_set.range; sat->range_rate = obs_set.range_rate; sat->ssplat = Degrees (sat_geodetic.lat); sat->ssplon = Degrees (sat_geodetic.lon); sat->alt = sat_geodetic.alt; sat->ma = Degrees (sat->phase); sat->ma *= 256.0/360.0; sat->phase = Degrees (sat->phase); /* same formulas, but the one from predict is nicer */ //sat->footprint = 2.0 * xkmper * acos (xkmper/sat->pos.w); sat->footprint = 12756.33 * acos (xkmper / (xkmper+sat->alt)); age = sat->jul_utc - sat->jul_epoch; sat->orbit = (long) floor((sat->tle.xno * xmnpda/twopi + age * sat->tle.bstar * ae) * age + sat->tle.xmo/twopi) + sat->tle.revnum - 1; } /** \brief Find the AOS time of the next pass. * \author Alexandru Csete, OZ9AEC * \author John A. Magliacane, KD2BD * \param sat Pointer to the satellite data. * \param qth Pointer to the QTH data. * \param start The time where calculation should start. * \param maxdt The upper time limit in days (0.0 = no limit) * \return The time of the next AOS or 0.0 if the satellite has no AOS. * * This function finds the time of AOS for the first coming pass taking place * no earlier that start. * If the satellite is currently within range, the function first calls * find_los to get the next LOS time. Then the calculations are done using * the new start time. * */ gdouble find_aos (sat_t *sat, qth_t *qth, gdouble start, gdouble maxdt) { gdouble t = start; gdouble aostime = 0.0; /* make sure current sat values are in sync with the time */ predict_calc (sat, qth, start); /* check whether satellite has aos */ if ((sat->otype == ORBIT_TYPE_GEO) || (sat->otype == ORBIT_TYPE_DECAYED) || !has_aos (sat, qth)) { return 0.0; } if (sat->el > 0.0) t = find_los (sat, qth, start, maxdt) + 0.014; // +20 min /* invalid time (potentially returned by find_los) */ if (t < 0.014) return 0.0; /* update satellite data */ predict_calc (sat, qth, t); /* use upper time limit */ if (maxdt > 0.0) { /* coarse time steps */ while ((sat->el < -1.0) && (t <= (start + maxdt))) { t -= 0.00035 * (sat->el * ((sat->alt / 8400.0) + 0.46) - 2.0); predict_calc (sat, qth, t); } /* fine steps */ while ((aostime == 0.0) && (t <= (start + maxdt))) { /* FIXME: should change to el < -0.3? accuracy seems to be less than +/- 1 sec except when the sat is around 0.0 EL */ if (fabs (sat->el) < 0.03) { aostime = t; } else { t -= sat->el * sqrt (sat->alt) / 530000.0; predict_calc (sat, qth, t); } } } /* don't use upper time limit */ else { /* coarse time steps */ while (sat->el < -1.0) { t -= 0.00035 * (sat->el * ((sat->alt / 8400.0) + 0.46) - 2.0); predict_calc (sat, qth, t); } /* fine steps */ while (aostime == 0.0) { /* FIXME: should change to el < -0.3? accuracy seems to be less than +/- 1 sec except when the sat is around 0.0 EL */ if (fabs (sat->el) < 0.03) { aostime = t; } else { t -= sat->el * sqrt (sat->alt) / 530000.0; predict_calc (sat, qth, t); } } } return aostime; } /** \brief Find the LOS time of the next pass. * \author Alexandru Csete, OZ9AEC * \author John A. Magliacane, KD2BD * \param sat Pointer to the satellite data. * \param qth Pointer to the QTH data. * \param start The time where calculation should start. * \param maxdt The upper time limit in days (0.0 = no limit) * \return The time of the next LOS or 0.0 if the satellite has no LOS. * * This function finds the time of LOS for the first coming pass taking place * no earlier that start. * If the satellite is currently out of range, the function first calls * find_aos to get the next AOS time. Then the calculations are done using * the new start time. * The function has a built-in watchdog to ensure that we don't end up in * lengthy loops. * */ gdouble find_los (sat_t *sat, qth_t *qth, gdouble start, gdouble maxdt) { gdouble t = start; gdouble lostime = 0.0; predict_calc (sat, qth, start); /* check whether satellite has aos */ if ((sat->otype == ORBIT_TYPE_GEO) || (sat->otype == ORBIT_TYPE_DECAYED) || !has_aos (sat, qth)) { return 0.0; } if (sat->el < 0.0) t = find_aos (sat, qth, start, maxdt) + 0.001; // +1.5 min /* invalid time (potentially returned by find_aos) */ if (t < 0.01) return 0.0; /* update satellite data */ predict_calc (sat, qth, t); /* use upper time limit */ if (maxdt > 0.0) { /* coarse steps */ while ((sat->el >= 1.0) && (t <= (start + maxdt))) { t += cos((sat->el - 1.0) * de2ra) * sqrt(sat->alt) / 25000.0; predict_calc (sat, qth, t); } /* fine steps */ while ((lostime == 0.0) && (t <= (start + maxdt))) { t += sat->el * sqrt(sat->alt)/502500.0; predict_calc (sat, qth, t); if (fabs(sat->el) < 0.03) lostime = t; } } /* don't use upper limit */ else { /* coarse steps */ while (sat->el >= 1.0) { t += cos((sat->el - 1.0) * de2ra) * sqrt(sat->alt) / 25000.0; predict_calc (sat, qth, t); } /* fine steps */ while (lostime == 0.0) { t += sat->el * sqrt(sat->alt)/502500.0; predict_calc (sat, qth, t); if (fabs(sat->el) < 0.03) lostime = t; } } return lostime; } /** \brief Find AOS time of current pass. * \param sat The satellite to find AOS for. * \param qth The ground station. * \param start Start time, prefereably now. * \return The time of the previous AOS or 0.0 if the satellite has no AOS. * * This function can be used to find the AOS time in the past of the * current pass. */ gdouble find_prev_aos (sat_t *sat, qth_t *qth, gdouble start) { gdouble aostime = start; /* make sure current sat values are in sync with the time */ predict_calc (sat, qth, start); /* check whether satellite has aos */ if ((sat->otype == ORBIT_TYPE_GEO) || (sat->otype == ORBIT_TYPE_DECAYED) || !has_aos (sat, qth)) { return 0.0; } while (sat->el >= 0.0) { aostime -= 0.0005; // 0.75 min predict_calc (sat, qth, aostime); } return aostime; } /** \brief Predict the next pass. * \param sat Pointer to the satellite data. * \param qth Pointer to the observer data. * \param maxdt The maximum number of days to look ahead. * \return Pointer newly allocated pass_t structure that should be freed * with free_pass when no longer needed, or NULL if no pass can be * found. * * This function simply wraps the get_pass function using the current time * as parameter. * * \note the data in sat will be corrupt (future) and must be refreshed * by the caller, if the caller will need it later on (eg. if the caller * is GtkSatList). * */ pass_t * get_next_pass (sat_t *sat, qth_t *qth, gdouble maxdt) { gdouble now; /* get the current time and call the get_pass function */ now = get_current_daynum (); return get_pass (sat, qth, now, maxdt); } /** \brief Predict upcoming passes starting now * \param sat Pointer to the satellite data. * \param qth Pointer to the observer data. * \param maxdt The maximum number of days to look ahead. * \param num The number of passes to predict. * \return A singly linked list of pass_t structures or NULL if * there was an error. * * This function simply wraps the get_passes function using the * current time as parameter. * * \note the data in sat will be corrupt (future) and must be refreshed * by the caller, if the caller will need it later on (eg. if the caller * is GtkSatList). */ GSList * get_next_passes (sat_t *sat, qth_t *qth, gdouble maxdt, guint num) { gdouble now; /* get the current time and call the get_pass function */ now = get_current_daynum (); return get_passes (sat, qth, now, maxdt, num); } /** \brief Predict first pass after a certain time. * \param sat Pointer to the satellite data. * \param qth Pointer to the location data. * \param start Starting time. * \param maxdt The maximum number of days to look ahead (0 for no limit). * \return Pointer to a newly allocated pass_t structure or NULL if * there was an error. * * This function will find the first upcoming pass with AOS no earlier than * t = start and no later than t = (start+maxdt). * * \note For no time limit use maxdt = 0.0 * * \note the data in sat will be corrupt (future) and must be refreshed * by the caller, if the caller will need it later on (eg. if the caller * is GtkSatList). * * \note Prepending to a singly linked list is much faster than appending. * Therefore, the elements are prepended whereafter the GSList is * reversed */ pass_t * get_pass (sat_t *sat, qth_t *qth, gdouble start, gdouble maxdt) { gdouble aos = 0.0; /* time of AOS */ gdouble tca = 0.0; /* time of TCA */ gdouble los = 0.0; /* time of LOS */ gdouble dt = 0.0; /* time diff */ gdouble step = 0.0; /* time step */ gdouble t0 = start; gdouble t; /* current time counter */ gdouble tres = 0.0; /* required time resolution */ gdouble max_el = 0.0; /* maximum elevation */ pass_t *pass = NULL; pass_detail_t *detail = NULL; gboolean done = FALSE; guint iter = 0; /* number of iterations */ /* FIXME: watchdog */ /* get time resolution; sat-cfg stores it in seconds */ tres = sat_cfg_get_int (SAT_CFG_INT_PRED_RESOLUTION) / 86400.0; /* loop until we find a pass with elevation > SAT_CFG_INT_PRED_MIN_EL or we run out of time FIXME: we should have a safety break */ while (!done) { aos = find_aos (sat, qth, t0, maxdt); /* aos = 0.0 means no aos */ if (aos == 0.0) { done = TRUE; } /* check whether we are within time limits; maxdt = 0 mean no time limit. */ else if ((maxdt > 0.0) && (aos > (start + maxdt)) ) { done = TRUE; } else { los = find_los (sat, qth, aos + 0.001, maxdt); // +1.5 min later dt = los - aos; /* get time step, which will give us the max number of entries */ step = dt / sat_cfg_get_int (SAT_CFG_INT_PRED_NUM_ENTRIES); /* but if this is smaller than the required resolution we go with the resolution */ if (step < tres) step = tres; /* create a pass_t entry; FIXME: g_try_new in 2.8 */ pass = g_new (pass_t, 1); pass->aos = aos; pass->los = los; pass->max_el = 0.0; pass->aos_az = 0.0; pass->los_az = 0.0; pass->maxel_az = 0.0; pass->vis[0] = '-'; pass->vis[1] = '-'; pass->vis[2] = '-'; pass->vis[3] = 0; pass->satname = g_strdup (sat->tle.sat_name); pass->details = NULL; /* iterate over each time step */ for (t = pass->aos; t <= pass->los; t += step) { /* calculate satellite data */ predict_calc (sat, qth, t); /* in the first iter we want to store pass->aos_az */ if (t == pass->aos) { pass->aos_az = sat->az; pass->orbit = sat->orbit; } /* append details to sat->details */ detail = g_new (pass_detail_t, 1); detail->time = t; detail->pos.x = sat->pos.x; detail->pos.y = sat->pos.y; detail->pos.z = sat->pos.z; detail->pos.w = sat->pos.w; detail->vel.x = sat->vel.x; detail->vel.y = sat->vel.y; detail->vel.z = sat->vel.z; detail->vel.w = sat->vel.w; detail->velo = sat->velo; detail->az = sat->az; detail->el = sat->el; detail->range = sat->range; detail->range_rate = sat->range_rate; detail->lat = sat->ssplat; detail->lon = sat->ssplon; detail->alt = sat->alt; detail->ma = sat->ma; detail->phase = sat->phase; detail->footprint = sat->footprint; detail->orbit = sat->orbit; detail->vis = get_sat_vis (sat, qth, t); /* also store visibility "bit" */ switch (detail->vis) { case SAT_VIS_VISIBLE: pass->vis[0] = 'V'; break; case SAT_VIS_DAYLIGHT: pass->vis[1] = 'D'; break; case SAT_VIS_ECLIPSED: pass->vis[2] = 'E'; break; default: break; } pass->details = g_slist_prepend (pass->details, detail); /* store elevation if greater than the previously stored one */ if (sat->el > max_el) { max_el = sat->el; tca = t; pass->maxel_az = sat->az; } /* g_print ("TIME: %f\tAZ: %f\tEL: %f (MAX: %f)\n", */ /* t, sat->az, sat->el, max_el); */ } pass->details = g_slist_reverse (pass->details); /* store los_az, max_el and tca */ pass->los_az = sat->az; pass->max_el = max_el; pass->tca = tca; /* check whether this pass is good */ if (max_el >= sat_cfg_get_int (SAT_CFG_INT_PRED_MIN_EL)) { done = TRUE; } else { done = FALSE; t0 = los + 0.014; // +20 min free_pass (pass); pass = NULL; } iter++; } } return pass; } /** \brief Predict passes after a certain time. * * * This function calculates num upcoming passes with AOS no earlier * than t = start and not later that t = (start+maxdt). The function will * repeatedly call get_pass until * the number of predicted passes is equal to num, the time has reached * limit or the get_pass function returns NULL. * * \note For no time limit use maxdt = 0.0 * * \note the data in sat will be corrupt (future) and must be refreshed * by the caller, if the caller will need it later on (eg. if the caller * is GtkSatList). * * \note Prepending to a singly linked list is much faster than appending. * Therefore, the elements are prepended whereafter the GSList is * reversed */ GSList * get_passes (sat_t *sat, qth_t *qth, gdouble start, gdouble maxdt, guint num) { GSList *passes = NULL; pass_t *pass = NULL; guint i; gdouble t; /* if no number has been specified set it to something big */ if (num == 0) num = 100; t = start; for (i = 0; i < num; i++) { pass = get_pass (sat, qth, t, maxdt); if (pass != NULL) { passes = g_slist_prepend (passes, pass); t = pass->los + 0.014; // +20 min /* if maxdt > 0.0 check whether we have reached t = start+maxdt if yes finish predictions */ if ((maxdt > 0.0) && (t >= (start+maxdt))) { i = num; } } else { /* we can't get any more passes */ i = num; } } if (passes != NULL) passes = g_slist_reverse (passes); return passes; } pass_t * copy_pass (pass_t *pass) { pass_t *new; new = g_try_new (pass_t, 1); if (new != NULL) { new->aos = pass->aos; new->los = pass->los; new->max_el = pass->max_el; new->aos_az = pass->aos_az; new->los_az = pass->los_az; new->orbit = pass->orbit; new->maxel_az = pass->maxel_az; new->vis[0] = pass->vis[0]; new->vis[1] = pass->vis[1]; new->vis[2] = pass->vis[2]; new->vis[3] = pass->vis[3]; new->details = copy_pass_details (pass->details); if (pass->satname != NULL) new->satname = g_strdup (pass->satname); else new->satname = NULL; } return new; } GSList * copy_pass_details (GSList *details) { GSList *new = NULL; guint i,n; n = g_slist_length (details); for (i = 0; i < n; i++) { new = g_slist_prepend (new, copy_pass_detail (PASS_DETAIL (g_slist_nth_data (details, i)))); } new = g_slist_reverse (new); return new; } pass_detail_t * copy_pass_detail (pass_detail_t *detail) { pass_detail_t *new; /* create a pass_t entry; FIXME: g_try_new in 2.8 */ new = g_new (pass_detail_t, 1); new->time = detail->time; new->pos.x = detail->pos.x; new->pos.y = detail->pos.y; new->pos.z = detail->pos.z; new->pos.w = detail->pos.w; new->vel.x = detail->vel.x; new->vel.y = detail->vel.y; new->vel.z = detail->vel.z; new->vel.w = detail->vel.w; new->velo = detail->velo; new->az = detail->az; new->el = detail->el; new->range = detail->range; new->range_rate = detail->range_rate; new->lat = detail->lat; new->lon = detail->lon; new->alt = detail->alt; new->ma = detail->ma; new->phase = detail->phase; new->footprint = detail->footprint; new->orbit = detail->orbit; new->vis = detail->vis; return new; } /** \brief Free a pass_t structure. */ void free_pass (pass_t *pass) { free_pass_details (pass->details); if (pass->satname != NULL) { g_free (pass->satname); pass->satname = NULL; } g_free (pass); pass = NULL; } /** \brief Free a list of passes. */ void free_passes (GSList *passes) { guint n,i; gpointer pass; n = g_slist_length (passes); for (i = 0; i < n; i++) { pass = g_slist_nth_data (passes, i); /* free element data */ free_pass (PASS (pass)); } /* now free the list elements */ g_slist_free (passes); passes = NULL; } /** \brief Free a pass detail structure. * * This function is not rarely useful except for the * free_pass function. * */ void free_pass_detail (pass_detail_t *detail) { g_free (detail); detail = NULL; } /** \brief Free the whole list of details. * */ void free_pass_details (GSList *details) { guint n,i; gpointer detail; n = g_slist_length (details); for (i = 0; i < n; i++) { detail = g_slist_nth_data (details, i); /* free element data */ free_pass_detail (PASS_DETAIL (detail)); } /* free list elements */ g_slist_free (details); details = NULL; } /** \brief Predict first pass after a certain time disergarding any minimum El setting. * \param sat Pointer to the satellite data. * \param qth Pointer to the location data. * \param start Starting time. * \param maxdt The maximum number of days to look ahead (0 for no limit). * \return Pointer to a newly allocated pass_t structure or NULL if * there was an error. * * This function will find the first upcoming pass with AOS no earlier than * t = start and no later than t = (start+maxdt). Since the intented use of this * function is to get the details of the current pass of a satellite, this function * does not care of the minimum elevation setting in sat-cfg. * * \note For no time limit use maxdt = 0.0 * * \note the data in sat will be corrupt (future) and must be refreshed * by the caller, if the caller will need it later on (eg. if the caller * is GtkSatList). * * \note Prepending to a singly linked list is much faster than appending. * Therefore, the elements are prepended whereafter the GSList is * reversed */ pass_t * get_pass_no_min_el (sat_t *sat, qth_t *qth, gdouble start, gdouble maxdt) { gdouble aos = 0.0; /* time of AOS */ gdouble tca = 0.0; /* time of TCA */ gdouble los = 0.0; /* time of LOS */ gdouble dt = 0.0; /* time diff */ gdouble step = 0.0; /* time step */ gdouble t0 = start; gdouble t; /* current time counter */ gdouble tres = 0.0; /* required time resolution */ gdouble max_el = 0.0; /* maximum elevation */ pass_t *pass = NULL; pass_detail_t *detail = NULL; gboolean done = FALSE; /* FIXME: watchdog */ /* get time resolution; sat-cfg stores it in seconds */ tres = sat_cfg_get_int (SAT_CFG_INT_PRED_RESOLUTION) / 86400.0; aos = find_aos (sat, qth, t0, maxdt); /* aos = 0.0 means no aos */ if (aos == 0.0) { done = TRUE; } /* check whether we are within time limits; maxdt = 0 mean no time limit. */ else if ((maxdt > 0.0) && (aos > (start + maxdt))) { done = TRUE; } else { los = find_los (sat, qth, aos + 0.001, maxdt); // +1.5 min later dt = los - aos; /* get time step, which will give us the max number of entries */ step = dt / sat_cfg_get_int (SAT_CFG_INT_PRED_NUM_ENTRIES); /* but if this is smaller than the required resolution we go with the resolution */ if (step < tres) step = tres; /* create a pass_t entry; FIXME: g_try_new in 2.8 */ pass = g_new (pass_t, 1); pass->aos = aos; pass->los = los; pass->max_el = 0.0; pass->aos_az = 0.0; pass->los_az = 0.0; pass->maxel_az = 0.0; pass->vis[0] = '-'; pass->vis[1] = '-'; pass->vis[2] = '-'; pass->vis[3] = 0; pass->satname = g_strdup (sat->tle.sat_name); pass->details = NULL; /* iterate over each time step */ for (t = pass->aos; t <= pass->los; t += step) { /* calculate satellite data */ predict_calc (sat, qth, t); /* in the first iter we want to store pass->aos_az */ if (t == pass->aos) { pass->aos_az = sat->az; pass->orbit = sat->orbit; } /* append details to sat->details */ detail = g_new (pass_detail_t, 1); detail->time = t; detail->pos.x = sat->pos.x; detail->pos.y = sat->pos.y; detail->pos.z = sat->pos.z; detail->pos.w = sat->pos.w; detail->vel.x = sat->vel.x; detail->vel.y = sat->vel.y; detail->vel.z = sat->vel.z; detail->vel.w = sat->vel.w; detail->velo = sat->velo; detail->az = sat->az; detail->el = sat->el; detail->range = sat->range; detail->range_rate = sat->range_rate; detail->lat = sat->ssplat; detail->lon = sat->ssplon; detail->alt = sat->alt; detail->ma = sat->ma; detail->phase = sat->phase; detail->footprint = sat->footprint; detail->orbit = sat->orbit; detail->vis = get_sat_vis (sat, qth, t); /* also store visibility "bit" */ switch (detail->vis) { case SAT_VIS_VISIBLE: pass->vis[0] = 'V'; break; case SAT_VIS_DAYLIGHT: pass->vis[1] = 'D'; break; case SAT_VIS_ECLIPSED: pass->vis[2] = 'E'; break; default: break; } pass->details = g_slist_prepend (pass->details, detail); /* store elevation if greater than the previously stored one */ if (sat->el > max_el) { max_el = sat->el; tca = t; pass->maxel_az = sat->az; } /* g_print ("TIME: %f\tAZ: %f\tEL: %f (MAX: %f)\n", */ /* t, sat->az, sat->el, max_el); */ } pass->details = g_slist_reverse (pass->details); /* store los_az, max_el and tca */ pass->los_az = sat->az; pass->max_el = max_el; pass->tca = tca; } return pass; } /** \brief Get current pass. * \param sat Pointer to the satellite data. * \param qth Pointer to the QTH data. * \param start Time to start calculations; use 0.0 for now. * \return Pointer to a newly allocated pass_t structure or NULL if * there was an error. * * Assuming that sat->el > 0.0 this function calculates the details of the * current pass from AOS time to LOS time. * First the function goes back in time to before the AOS, then it calls * the get_pass_no_min_el function to get the details of the current pass * disregarding any minimum elevation requirements. * * \note The start parameter has been introduced to allow correct use of this * function in non-realtime cases. * */ pass_t * get_current_pass (sat_t *sat, qth_t *qth, gdouble start) { gdouble t; if (start > 0.0) t = start; else t = get_current_daynum (); predict_calc (sat, qth, t); /* find a time before AOS */ while (sat->el > -2.0) { predict_calc (sat, qth, t); t -= 0.007; // +10 min } return get_pass_no_min_el (sat, qth, t, 0.0); }