/** \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 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_in, 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 */ sat_t *sat,sat_working; /* FIXME: watchdog */ /*copy sat_in to a working structure*/ sat = memcpy(&sat_working,sat_in,sizeof(sat_t)); /* 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) { /* Find los of next pass or of current pass */ los = find_los (sat, qth, t0, maxdt); // See if a pass is ongoing aos = find_aos (sat, qth, t0, maxdt); /* sat_log_log(SAT_LOG_LEVEL_MSG, "%s:%s:%d: found aos %f and los %f for t0=%f", */ /* __FILE__, */ /* __FUNCTION__, */ /* __LINE__, */ /* aos, */ /* los, */ /* t0); */ if (aos > los) { // los is from an currently happening pass, find previous aos aos = find_prev_aos(sat, qth, t0); } /* 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->nickname); 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); /* calculate satellite data */ predict_calc (sat, qth, pass->los); /* 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 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.1) 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))) { if (fabs (sat->el) < 0.005) { 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) { if (fabs (sat->el) < 0.005) { 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.005) 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.005) lostime = t; } } return lostime; }
/** \brief Create and show ground track for a satellite. * \param satmap The satellite map widget. * \param sat Pointer to the satellite object. * \param qth Pointer to the QTH data. * \param obj the satellite object. * * Gpredict allows the user to require the ground track for any number of orbits * ahead. Therfore, the resulting ground track may cross the map boundaries many * times, and using one single polyline for the whole ground track would look very * silly. To avoid this, the points will be split into several polylines. * */ void ground_track_create (GtkSatMap *satmap, sat_t *sat, qth_t *qth, sat_map_obj_t *obj) { long this_orbit; /* current orbit number */ long max_orbit; /* target orbit number, ie. this + num - 1 */ double t0; /* time when this_orbit starts */ double t; ssp_t *this_ssp; sat_log_log (SAT_LOG_LEVEL_DEBUG, _("%s: Creating ground track for %s"), __FUNCTION__, sat->nickname); /* just to be safe... if empty GSList is not NULL => segfault */ obj->track_data.latlon = NULL; /* get configuration parameters */ this_orbit = sat->orbit; max_orbit = sat->orbit -1 + mod_cfg_get_int (satmap->cfgdata, MOD_CFG_MAP_SECTION, MOD_CFG_MAP_TRACK_NUM, SAT_CFG_INT_MAP_TRACK_NUM); sat_log_log (SAT_LOG_LEVEL_DEBUG, _("%s: Start orbit: %d"), __FUNCTION__, this_orbit); sat_log_log (SAT_LOG_LEVEL_DEBUG, _("%s: End orbit %d"), __FUNCTION__, max_orbit); /* find the time when the current orbit started */ /* Iterate backwards in time until we reach sat->orbit < this_orbit. Use predict_calc from predict-tools.c as SGP/SDP driver. As a built-in safety, we stop iteration if the orbit crossing is more than 24 hours back in time. */ t0 = satmap->tstamp;//get_current_daynum (); /* use == instead of >= as it is more robust */ for (t = t0; (sat->orbit == this_orbit) && ((t + 1.0) > t0); t -= 0.0007) { predict_calc (sat, qth, t); } /* set it so that we are in the same orbit as this_orbit and not a different one */ t += 2*0.0007; t0 = t; predict_calc (sat, qth, t0); sat_log_log (SAT_LOG_LEVEL_DEBUG, _("%s: T0: %f (%d)"), __FUNCTION__, t0, sat->orbit); /* calculate (lat,lon) for the required orbits */ while ((sat->orbit <= max_orbit) && (sat->orbit >= this_orbit) && (!decayed(sat))) { /* We use 30 sec time steps. If resolution is too fine, the line drawing routine will filter out unnecessary points */ t += 0.00035; predict_calc (sat, qth, t); /* store this SSP */ /* Note: g_slist_append() has to traverse the entire list to find the end, which is inefficient when adding multiple elements. Therefore, we use g_slist_prepend() and reverse the entire list when we are done. */ this_ssp = g_try_new (ssp_t, 1); if (this_ssp == NULL) { sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s: MAYDAY: Insufficient memory for ground track!"), __FUNCTION__); return; } this_ssp->lat = sat->ssplat; this_ssp->lon = sat->ssplon; obj->track_data.latlon = g_slist_prepend (obj->track_data.latlon, this_ssp); } /* log if there is a problem with the orbit calculation */ if (sat->orbit != (max_orbit+1)) { sat_log_log (SAT_LOG_LEVEL_ERROR, _("%s: Problem computing ground track for %s"), __FUNCTION__, sat->nickname); return; } /* Reset satellite structure to eliminate glitches in single sat view and other places when new ground track is layed out */ predict_calc(sat, qth, satmap->tstamp); /* reverse GSList */ obj->track_data.latlon = g_slist_reverse (obj->track_data.latlon); /* split points into polylines */ create_polylines (satmap, sat, qth, obj); /* misc book-keeping */ obj->track_orbit = this_orbit; }