mirror of
https://github.com/csete/gpredict
synced 2026-08-13 17:39:46 -04:00
git-svn-id: https://gpredict.svn.sourceforge.net/svnroot/gpredict/trunk@41 ab6734ba-2843-0410-b83b-99c217ef6c0f
1027 lines
25 KiB
C
1027 lines
25 KiB
C
/* -*- 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 <oz9aec@gmail.com>
|
|
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 <glib.h>
|
|
#include <glib/gi18n.h>
|
|
#include "sgpsdp/sgp4sdp4.h"
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <build-config.h>
|
|
#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))) {
|
|
|
|
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 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);
|
|
}
|