1 /*
2     Gpredict: Real-time satellite tracking and orbit prediction program
3 
4     Copyright (C)  2001-2017  Alexandru Csete, OZ9AEC.
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, visit http://www.fsf.org/
18 */
19 #ifdef HAVE_CONFIG_H
20 #include <build-config.h>
21 #endif
22 #include <glib/gi18n.h>
23 #include <gtk/gtk.h>
24 
25 #include "config-keys.h"
26 #include "orbit-tools.h"
27 #include "predict-tools.h"
28 #include "sat-cfg.h"
29 #include "sat-info.h"
30 #include "sat-log.h"
31 #include "sat-pass-dialogs.h"
32 #include "sgpsdp/sgp4sdp4.h"
33 #include "trsp-conf.h"
34 
35 
36 /**
37  * Show satellite info in a dialog (callback)
38  *
39  * @param menuitem The menuitem from where the function is invoked.
40  * @param data Pointer to parent window or NULL.
41  */
show_sat_info_menu_cb(GtkWidget * menuitem,gpointer data)42 void show_sat_info_menu_cb(GtkWidget * menuitem, gpointer data)
43 {
44     sat_t          *sat;
45 
46     sat = SAT(g_object_get_data(G_OBJECT(menuitem), "sat"));
47     show_sat_info(sat, data);
48 }
49 
50 
51 /* Create transponder table. */
create_transponder_table(guint catnum)52 static GtkWidget *create_transponder_table(guint catnum)
53 {
54     GtkWidget      *vbox, *label, *swin;
55     GSList         *trsplist = NULL;
56     trsp_t         *trsp = NULL;
57     guint           i, n;
58     gchar          *text;
59 
60 
61     trsplist = read_transponders(catnum);
62     if (trsplist == NULL)
63     {
64         swin = gtk_label_new(_("No transponders"));
65     }
66     else
67     {
68         vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
69         gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
70         gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
71 
72         /* Add each transponder to vbox */
73         n = g_slist_length(trsplist);
74         for (i = 0; i < n; i++)
75         {
76             gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(" "), FALSE, FALSE,
77                                0);
78 
79             trsp = (trsp_t *) g_slist_nth_data(trsplist, i);
80 
81             /* transponder name */
82             text = g_strdup_printf("<b>%s</b>", trsp->name);
83             label = gtk_label_new(NULL);
84             g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
85             gtk_label_set_markup(GTK_LABEL(label), text);
86             g_free(text);
87             gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
88             gtk_box_pack_start(GTK_BOX(vbox),
89                                gtk_separator_new(GTK_ORIENTATION_HORIZONTAL),
90                                FALSE, FALSE, 0);
91 
92             /* uplink */
93             if (trsp->uplow > 0.0)
94             {
95                 if (trsp->uphigh > trsp->uplow)
96                 {
97                     /* we have a range */
98                     text =
99                         g_strdup_printf(_
100                                         ("Uplink: %.4f \342\200\222 %.4f MHz"),
101                                         trsp->uplow / 1.0e6,
102                                         trsp->uphigh / 1.0e6);
103                 }
104                 else
105                 {
106                     text =
107                         g_strdup_printf(_("Uplink: %.4f MHz"),
108                                         trsp->uplow / 1.0e6);
109                 }
110                 label = gtk_label_new(text);
111                 g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
112                 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
113                 g_free(text);
114             }
115 
116             /* downlink */
117             if (trsp->downlow > 0.0)
118             {
119                 if (trsp->downhigh > trsp->downlow)
120                 {
121                     /* we have a range */
122                     text =
123                         g_strdup_printf(_
124                                         ("Downlink: %.4f \342\200\222 %.4f MHz"),
125                                         trsp->downlow / 1.0e6,
126                                         trsp->downhigh / 1.0e6);
127                 }
128                 else
129                 {
130                     text =
131                         g_strdup_printf(_("Downlink: %.4f MHz"),
132                                         trsp->downlow / 1.0e6);
133                 }
134                 label = gtk_label_new(text);
135                 g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
136                 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
137                 g_free(text);
138             }
139 
140             /* inverting */
141             if ((trsp->downhigh > trsp->downlow) &&
142                 (trsp->uphigh > trsp->uplow))
143             {
144                 text =
145                     g_strdup_printf(_("Inverting: %s"),
146                                     trsp->invert ? "YES" : "NO");
147                 label = gtk_label_new(text);
148                 g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
149                 g_free(text);
150                 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
151             }
152 
153             /* mode string */
154             if (trsp->mode)
155             {
156                 text = g_strdup_printf(_("Mode: %s"), trsp->mode);
157                 label = gtk_label_new(text);
158                 g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
159                 g_free(text);
160                 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
161             }
162             if (trsp->baud)
163             {
164                 text = g_strdup_printf(_("Baudrate: %9.2f"), trsp->baud);
165                 label = gtk_label_new(text);
166                 g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
167                 g_free(text);
168                 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
169             }
170         }
171         free_transponders(trsplist);
172 
173         /* pack into a scrolled window */
174         swin = gtk_scrolled_window_new(NULL, NULL);
175         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
176                                        GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
177         gtk_container_add (GTK_CONTAINER (swin), vbox);
178     }
179 
180     return swin;
181 }
182 
183 /*  BBBBB.BBBBBBBB] - Epoch Time -- 2-digit year, followed by 3-digit sequential
184                       day of the year, followed by the time represented as the
185                       fractional portion of one day, but...
186 
187     we now have the converted fields, tle->epoch_year, tle->epoch_day and tle->epoch_fod
188 
189 */
epoch_to_str(sat_t * sat)190 static gchar   *epoch_to_str(sat_t * sat)
191 {
192     GDate          *epd;
193     guint           h, m, s, sec;
194     gchar          *buff;
195     gchar          *fmt;
196     struct tm       tms;
197     time_t          t;
198     guint           size;
199 
200     /* http://celestrak.com/columns/v04n03/#FAQ02
201        ... While talking about the epoch, this is perhaps a good place to answer
202        the other time-related questions. First, how is the epoch time format
203        interpreted? This question is best answered by using an example. An epoch
204        of 98001.00000000 corresponds to 0000 UT on 1998 January 01st in other
205        words, midnight between 1997 December 31 and 1998 January 01. An epoch of
206        98000.00000000 would actually correspond to the beginning of 1997 December
207        31st strange as that might seem. Note that the epoch day starts at UT
208        midnight (not noon) and that all times are measured mean solar rather than
209        sidereal time units (the answer to our third question).
210      */
211     epd = g_date_new_dmy(1, 1, sat->tle.epoch_year);
212     g_date_add_days(epd, sat->tle.epoch_day - 1);
213 
214     /* convert date to struct tm */
215     g_date_to_struct_tm(epd, &tms);
216 
217     /* add HMS */
218     sec = (guint) floor(sat->tle.epoch_fod * 86400);    /* fraction of day in seconds */
219 
220     /* hour */
221     h = (guint) floor(sec / 3600);
222     tms.tm_hour = h;
223 
224     /* minutes */
225     m = (guint) floor((sec - (h * 3600)) / 60);
226     tms.tm_min = m;
227 
228     s = (guint) floor(sec - (h * 3600) - (m * 60));
229     tms.tm_sec = s;
230 
231     /* get format string */
232     fmt = sat_cfg_get_str(SAT_CFG_STR_TIME_FORMAT);
233 
234     /* format either local time or UTC depending on check box */
235     t = mktime(&tms);
236     buff = g_try_malloc(51);
237 
238     if (sat_cfg_get_bool(SAT_CFG_BOOL_USE_LOCAL_TIME))
239         size = strftime(buff, 50, fmt, localtime(&t));
240     else
241         size = strftime(buff, 50, fmt, gmtime(&t));
242 
243     if (size < 50)
244         buff[size] = '\0';
245     else
246         buff[50] = '\0';
247 
248     g_date_free(epd);
249     g_free(fmt);
250 
251     return buff;
252 }
253 
254 /**
255  * Show satellite info in a dialog
256  *
257  * @param sat Pointer to the sat info.
258  * @param data Pointer to parent window or NULL.
259  *
260  * FIXME: see nice drawing at http://www.amsat.org/amsat-new/tools/keps_tutorial.php
261  */
show_sat_info(sat_t * sat,gpointer data)262 void show_sat_info(sat_t * sat, gpointer data)
263 {
264     GtkWidget      *dialog;
265     GtkWidget      *notebook;
266     GtkWidget      *table;
267     GtkWidget      *label;
268     GtkWindow      *toplevel = NULL;
269     gchar          *str;
270 
271 
272     if (data != NULL)
273         toplevel = GTK_WINDOW(data);
274 
275     /* create table */
276     table = gtk_grid_new();
277     gtk_grid_set_column_spacing(GTK_GRID(table), 5);
278     gtk_grid_set_row_spacing(GTK_GRID(table), 5);
279     gtk_container_set_border_width(GTK_CONTAINER(table), 10);
280 
281     /* satellite name */
282     label = gtk_label_new(NULL);
283     gtk_label_set_markup(GTK_LABEL(label), _("<b>Satellite name:</b>"));
284     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
285     gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1);
286 
287     label = gtk_label_new(NULL);
288     str = g_markup_printf_escaped(_("<b>%s</b>"), sat->nickname);
289     gtk_label_set_markup(GTK_LABEL(label), str);
290     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
291     gtk_grid_attach(GTK_GRID(table), label, 1, 0, 1, 1);
292     g_free(str);
293 
294     /* operational status */
295     label = gtk_label_new(_("Operational Status:"));
296     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
297     gtk_grid_attach(GTK_GRID(table), label, 0, 1, 1, 1);
298 
299     switch (sat->tle.status)
300     {
301 
302     case OP_STAT_OPERATIONAL:
303         label = gtk_label_new(_("Operational"));
304         break;
305 
306     case OP_STAT_NONOP:
307         label = gtk_label_new(_("Non-operational"));
308         break;
309 
310     case OP_STAT_PARTIAL:
311         label = gtk_label_new(_("Partially operational"));
312         break;
313 
314     case OP_STAT_STDBY:
315         label = gtk_label_new(_("Backup/Standby"));
316         break;
317 
318     case OP_STAT_SPARE:
319         label = gtk_label_new(_("Spare"));
320         break;
321 
322     case OP_STAT_EXTENDED:
323         label = gtk_label_new(_("Extended Mission"));
324         break;
325 
326     default:
327         label = gtk_label_new(_("Unknown"));
328         break;
329 
330     }
331 
332     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
333     gtk_grid_attach(GTK_GRID(table), label, 1, 1, 1, 1);
334 
335     /* Catnum */
336     label = gtk_label_new(_("Catalogue number:"));
337     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
338     gtk_grid_attach(GTK_GRID(table), label, 0, 2, 1, 1);
339 
340     str = g_strdup_printf("%d", sat->tle.catnr);
341     label = gtk_label_new(str);
342     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
343     gtk_grid_attach(GTK_GRID(table), label, 1, 2, 1, 1);
344     g_free(str);
345 
346     /* international designator */
347     label = gtk_label_new(_("International designator:"));
348     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
349     gtk_grid_attach(GTK_GRID(table), label, 0, 3, 1, 1);
350 
351     label = gtk_label_new(sat->tle.idesg);
352     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
353     gtk_grid_attach(GTK_GRID(table), label, 1, 3, 1, 1);
354 
355     /* elset number */
356     label = gtk_label_new(_("Element set number:"));
357     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
358     gtk_grid_attach(GTK_GRID(table), label, 0, 4, 1, 1);
359 
360     str = g_strdup_printf("%d", sat->tle.elset);
361     label = gtk_label_new(str);
362     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
363     gtk_grid_attach(GTK_GRID(table), label, 1, 4, 1, 1);
364     g_free(str);
365 
366     /* elset epoch */
367     label = gtk_label_new(_("Epoch time:"));
368     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
369     gtk_grid_attach(GTK_GRID(table), label, 0, 5, 1, 1);
370 
371     str = epoch_to_str(sat);
372     label = gtk_label_new(str);
373     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
374     gtk_grid_attach(GTK_GRID(table), label, 1, 5, 1, 1);
375     g_free(str);
376 
377     /* Revolution Number @ Epoch */
378     label = gtk_label_new(_("Orbit number @ epoch:"));
379     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
380     gtk_grid_attach(GTK_GRID(table), label, 0, 6, 1, 1);
381 
382     str = g_strdup_printf("%d", sat->tle.revnum);
383     label = gtk_label_new(str);
384     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
385     gtk_grid_attach(GTK_GRID(table), label, 1, 6, 1, 1);
386     g_free(str);
387 
388     /* ephermis type left out, since it is always 0 */
389 
390     /* separator */
391     gtk_grid_attach(GTK_GRID(table),
392                     gtk_separator_new(GTK_ORIENTATION_HORIZONTAL),
393                     0, 7, 2, 1);
394 
395     /* Orbit inclination */
396     label = gtk_label_new(_("Inclination:"));
397     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
398     gtk_grid_attach(GTK_GRID(table), label, 0, 8, 1, 1);
399 
400     str = g_strdup_printf("%.4f\302\260", sat->tle.xincl / de2ra);
401     label = gtk_label_new(str);
402     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
403     gtk_grid_attach(GTK_GRID(table), label, 1, 8, 1, 1);
404     g_free(str);
405 
406     /* RAAN */
407     label = gtk_label_new(_("RAAN:"));
408     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
409     gtk_grid_attach(GTK_GRID(table), label, 0, 9, 1, 1);
410 
411     str = g_strdup_printf("%.4f\302\260", sat->tle.xnodeo / de2ra);
412     label = gtk_label_new(str);
413     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
414     gtk_grid_attach(GTK_GRID(table), label, 1, 9, 1, 1);
415     g_free(str);
416 
417     /* Eccentricity */
418     label = gtk_label_new(_("Eccentricity:"));
419     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
420     gtk_grid_attach(GTK_GRID(table), label, 0, 10, 1, 1);
421 
422     str = g_strdup_printf("%.7f", sat->tle.eo);
423     label = gtk_label_new(str);
424     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
425     gtk_grid_attach(GTK_GRID(table), label, 1, 10, 1, 1);
426     g_free(str);
427 
428     /* Argument of perigee */
429     label = gtk_label_new(_("Arg. of perigee:"));
430     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
431     gtk_grid_attach(GTK_GRID(table), label, 0, 11, 1, 1);
432 
433     str = g_strdup_printf("%.4f\302\260", sat->tle.omegao / de2ra);
434     label = gtk_label_new(str);
435     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
436     gtk_grid_attach(GTK_GRID(table), label, 1, 11, 1, 1);
437     g_free(str);
438 
439     /* Mean Anomaly */
440     label = gtk_label_new(_("Mean anomaly:"));
441     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
442     gtk_grid_attach(GTK_GRID(table), label, 0, 12, 1, 1);
443 
444     str = g_strdup_printf("%.4f\302\260", sat->tle.xmo / de2ra);
445     label = gtk_label_new(str);
446     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
447     gtk_grid_attach(GTK_GRID(table), label, 1, 12, 1, 1);
448     g_free(str);
449 
450     /* Mean Motion */
451     label = gtk_label_new(_("Mean motion:"));
452     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
453     gtk_grid_attach(GTK_GRID(table), label, 0, 13, 1, 1);
454 
455     str = g_strdup_printf("%.8f [rev/day]", sat->meanmo);
456     label = gtk_label_new(str);
457     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
458     gtk_grid_attach(GTK_GRID(table), label, 1, 13, 1, 1);
459     g_free(str);
460 
461     /* one half of the first time derivative of mean motion */
462     label = gtk_label_new(_("\302\275 d/dt (mean motion):"));
463     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
464     gtk_grid_attach(GTK_GRID(table), label, 0, 14, 1, 1);
465 
466     str = g_strdup_printf("%.5e [rev/day<sup>2</sup>]",
467                           sat->tle.xndt2o / (twopi / xmnpda / xmnpda));
468     label = gtk_label_new(NULL);
469     gtk_label_set_markup(GTK_LABEL(label), str);
470     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
471     gtk_grid_attach(GTK_GRID(table), label, 1, 14, 1, 1);
472     g_free(str);
473 
474     /* one sixth of the second time derivative of mean motion */
475     label = gtk_label_new(NULL);
476     gtk_label_set_markup(GTK_LABEL(label),
477                          _("1/6 d<sup>2</sup>/dt<sup>2</sup> (mean motion):"));
478     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
479     gtk_grid_attach(GTK_GRID(table), label, 0, 15, 1, 1);
480 
481     str = g_strdup_printf("%.5e [rev/day<sup>3</sup>]",
482                           sat->tle.xndd6o * xmnpda / (twopi / xmnpda / xmnpda));
483     label = gtk_label_new(NULL);
484     gtk_label_set_markup(GTK_LABEL(label), str);
485     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
486     gtk_grid_attach(GTK_GRID(table), label, 1, 15, 1, 1);
487     g_free(str);
488 
489     /* B* drag term */
490     label = gtk_label_new(_("B* drag term:"));
491     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
492     gtk_grid_attach(GTK_GRID(table), label, 0, 16, 1, 1);
493 
494     str = g_strdup_printf("%.5e [R<sub>E</sub><sup>-1</sup>]",
495                           sat->tle.bstar * ae);
496     label = gtk_label_new(NULL);
497     gtk_label_set_markup(GTK_LABEL(label), str);
498     g_object_set(label, "xalign", 0.0, "yalign", 0.5, NULL);
499     gtk_grid_attach(GTK_GRID(table), label, 1, 16, 1, 1);
500     g_free(str);
501 
502     /* Orbit type */
503 
504     /* Next Event */
505 
506     gtk_widget_show_all(table);
507 
508     notebook = gtk_notebook_new();
509     gtk_notebook_append_page(GTK_NOTEBOOK(notebook), table,
510                              gtk_label_new(_("Orbit Info")));
511     gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
512                              create_transponder_table(sat->tle.catnr),
513                              gtk_label_new(_("Transponders")));
514 
515     /* create dialog window with NULL parent */
516     dialog = gtk_dialog_new_with_buttons(_("Satellite Info"),
517                                          toplevel,
518                                          GTK_DIALOG_DESTROY_WITH_PARENT,
519                                          "_OK",
520                                          GTK_RESPONSE_ACCEPT, NULL);
521 
522     /* allow interaction with other windows */
523     gtk_window_set_modal(GTK_WINDOW(dialog), FALSE);
524 
525     g_signal_connect(dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL);
526 
527     GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
528     gtk_box_pack_start(GTK_BOX(content_area), notebook, TRUE, TRUE, 0);
529 
530     gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
531 
532     gtk_widget_show_all(dialog);
533 }
534