1 /*
2   Gpredict: Real-time satellite tracking and orbit prediction program
3 
4   Copyright (C)  2001-2017  Alexandru Csete, OZ9AEC
5   Copyright (C)       2011  Charles Suprin, AA1 VS
6 
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11 
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16 
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, visit http://www.fsf.org/
19 */
20 #include <gtk/gtk.h>
21 #include <glib/gi18n.h>
22 #include <goocanvas.h>
23 
24 #ifdef HAVE_CONFIG_H
25 #include <build-config.h>
26 #endif
27 
28 #include "config-keys.h"
29 #include "gpredict-utils.h"
30 #include "gtk-polar-view.h"
31 #include "gtk-polar-view-popup.h"
32 #include "gtk-sat-data.h"
33 #include "mod-cfg-get-param.h"
34 #include "orbit-tools.h"
35 #include "sat-cfg.h"
36 #include "sat-info.h"
37 #include "sat-log.h"
38 #include "sgpsdp/sgp4sdp4.h"
39 #include "time-tools.h"
40 
41 #define POLV_DEFAULT_SIZE 100
42 #define POLV_DEFAULT_MARGIN 25
43 #define MARKER_SIZE_HALF 2
44 
45 /* extra size for line outside 0 deg circle (inside margin) */
46 #define POLV_LINE_EXTRA 5
47 
48 static void     update_sat(gpointer key, gpointer value, gpointer data);
49 static void     update_track(gpointer key, gpointer value, gpointer data);
50 
51 static GtkVBoxClass *parent_class = NULL;
52 
gtk_polar_view_store_showtracks(GtkPolarView * pv)53 static void gtk_polar_view_store_showtracks(GtkPolarView * pv)
54 {
55     mod_cfg_set_integer_list_boolean(pv->cfgdata,
56                                      pv->showtracks_on,
57                                      MOD_CFG_POLAR_SECTION,
58                                      MOD_CFG_POLAR_SHOWTRACKS);
59     mod_cfg_set_integer_list_boolean(pv->cfgdata,
60                                      pv->showtracks_off,
61                                      MOD_CFG_POLAR_SECTION,
62                                      MOD_CFG_POLAR_HIDETRACKS);
63 }
64 
65 /** Load the satellites that we should not highlight coverage */
gtk_polar_view_load_showtracks(GtkPolarView * pv)66 static void gtk_polar_view_load_showtracks(GtkPolarView * pv)
67 {
68     mod_cfg_get_integer_list_boolean(pv->cfgdata,
69                                      MOD_CFG_POLAR_SECTION,
70                                      MOD_CFG_POLAR_HIDETRACKS,
71                                      pv->showtracks_off);
72 
73     mod_cfg_get_integer_list_boolean(pv->cfgdata,
74                                      MOD_CFG_POLAR_SECTION,
75                                      MOD_CFG_POLAR_SHOWTRACKS,
76                                      pv->showtracks_on);
77 }
78 
gtk_polar_view_destroy(GtkWidget * widget)79 static void gtk_polar_view_destroy(GtkWidget * widget)
80 {
81     gtk_polar_view_store_showtracks(GTK_POLAR_VIEW(widget));
82 
83     (*GTK_WIDGET_CLASS(parent_class)->destroy) (widget);
84 }
85 
gtk_polar_view_class_init(GtkPolarViewClass * class)86 static void gtk_polar_view_class_init(GtkPolarViewClass * class)
87 {
88     GtkWidgetClass *widget_class = (GtkWidgetClass *) class;
89 
90     widget_class->destroy = gtk_polar_view_destroy;
91 
92     parent_class = g_type_class_peek_parent(class);
93 }
94 
gtk_polar_view_init(GtkPolarView * polview)95 static void gtk_polar_view_init(GtkPolarView * polview)
96 {
97     polview->sats = NULL;
98     polview->qth = NULL;
99     polview->obj = NULL;
100     polview->naos = 0.0;
101     polview->ncat = 0;
102     polview->size = 0;
103     polview->r = 0;
104     polview->cx = 0;
105     polview->cy = 0;
106     polview->refresh = 0;
107     polview->counter = 0;
108     polview->swap = 0;
109     polview->qthinfo = FALSE;
110     polview->eventinfo = FALSE;
111     polview->cursinfo = FALSE;
112     polview->extratick = FALSE;
113     polview->resize = FALSE;
114 }
115 
gtk_polar_view_get_type()116 GType gtk_polar_view_get_type()
117 {
118     static GType    gtk_polar_view_type = 0;
119 
120     if (!gtk_polar_view_type)
121     {
122         static const GTypeInfo gtk_polar_view_info = {
123             sizeof(GtkPolarViewClass),
124             NULL,               /* base init */
125             NULL,               /* base finalise */
126             (GClassInitFunc) gtk_polar_view_class_init,
127             NULL,               /* class finalise */
128             NULL,               /* class data */
129             sizeof(GtkPolarView),
130             5,                  /* n_preallocs */
131             (GInstanceInitFunc) gtk_polar_view_init,
132             NULL
133         };
134 
135         gtk_polar_view_type = g_type_register_static(GTK_TYPE_BOX,
136                                                      "GtkPolarView",
137                                                      &gtk_polar_view_info, 0);
138     }
139 
140     return gtk_polar_view_type;
141 }
142 
143 /**
144  * Manage new size allocation.
145  *
146  * This function is called when the canvas receives a new size allocation,
147  * e.g. when the container is re-sized. The function re-calculates the graph
148  * dimensions based on the new canvas size.
149  */
size_allocate_cb(GtkWidget * widget,GtkAllocation * allocation,gpointer data)150 static void size_allocate_cb(GtkWidget * widget, GtkAllocation * allocation,
151                              gpointer data)
152 {
153     (void)widget;
154     (void)allocation;
155     (void)data;
156     GTK_POLAR_VIEW(data)->resize = TRUE;
157 }
158 
159 /**
160  * Manage canvas realise signals.
161  *
162  * This function is used to re-initialise the graph dimensions when
163  * the graph is realized, i.e. displayed for the first time. This is
164  * necessary in order to compensate for missing "re-allocate" signals for
165  * graphs that have not yet been realised, e.g. when opening several module
166  */
on_canvas_realized(GtkWidget * canvas,gpointer data)167 static void on_canvas_realized(GtkWidget * canvas, gpointer data)
168 {
169     GtkAllocation   aloc;
170 
171     gtk_widget_get_allocation(canvas, &aloc);
172     size_allocate_cb(canvas, &aloc, data);
173 }
174 
175 /**
176  * Manage button press events
177  *
178  * This function is called when a mouse button is pressed on a satellite object.
179  * If the pressed button is #3 (right button) the satellite popup menu will be
180  * created and executed.
181  */
on_button_press(GooCanvasItem * item,GooCanvasItem * target,GdkEventButton * event,gpointer data)182 static gboolean on_button_press(GooCanvasItem * item,
183                                 GooCanvasItem * target,
184                                 GdkEventButton * event, gpointer data)
185 {
186     GooCanvasItemModel *model = goo_canvas_item_get_model(item);
187     GtkPolarView   *polv = GTK_POLAR_VIEW(data);
188     gint            catnum =
189         GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model), "catnum"));
190     gint           *catpoint = NULL;
191     sat_t          *sat = NULL;
192 
193     (void)target;
194 
195     switch (event->button)
196     {
197         /* double-left-click */
198     case 1:
199         if (event->type == GDK_2BUTTON_PRESS)
200         {
201             catpoint = g_try_new0(gint, 1);
202             *catpoint = catnum;
203 
204             sat = SAT(g_hash_table_lookup(polv->sats, catpoint));
205             if (sat != NULL)
206             {
207                 show_sat_info(sat, gtk_widget_get_toplevel(GTK_WIDGET(data)));
208             }
209             else
210             {
211                 /* double-clicked on map */
212             }
213         }
214 
215         g_free(catpoint);
216         break;
217         /* pop-up menu */
218     case 3:
219         catpoint = g_try_new0(gint, 1);
220         *catpoint = catnum;
221 
222         sat = SAT(g_hash_table_lookup(polv->sats, catpoint));
223 
224         if (sat != NULL)
225         {
226             gtk_polar_view_popup_exec(sat, polv->qth,
227                                       polv, event,
228                                       gtk_widget_get_toplevel(GTK_WIDGET
229                                                               (polv)));
230         }
231         else
232         {
233             sat_log_log(SAT_LOG_LEVEL_ERROR,
234                         _
235                         ("%s:%d: Could not find satellite (%d) in hash table"),
236                         __FILE__, __LINE__, catnum);
237         }
238         g_free(catpoint);
239         break;
240     default:
241         break;
242     }
243 
244     return TRUE;
245 }
246 
247 /**
248  * Clear selection.
249  *
250  * This function is used to clear the old selection when a new satellite
251  * is selected.
252  */
clear_selection(gpointer key,gpointer val,gpointer data)253 static void clear_selection(gpointer key, gpointer val, gpointer data)
254 {
255     gint           *old = key;
256     gint           *new = data;
257     sat_obj_t      *obj = SAT_OBJ(val);
258     guint32         col;
259 
260     if ((*old != *new) && (obj->selected))
261     {
262         obj->selected = FALSE;
263 
264         col = sat_cfg_get_int(SAT_CFG_INT_POLAR_SAT_COL);
265 
266         g_object_set(obj->marker,
267                      "fill-color-rgba", col, "stroke-color-rgba", col, NULL);
268         g_object_set(obj->label,
269                      "fill-color-rgba", col, "stroke-color-rgba", col, NULL);
270     }
271 }
272 
273 /**
274  * Manage button release events.
275  *
276  * This function is called when the mouse button is released above
277  * a satellite object. It will act as a button click and if the released
278  * button is the left one, the click will correspond to selecting or
279  * deselecting a satellite
280  */
on_button_release(GooCanvasItem * item,GooCanvasItem * target,GdkEventButton * event,gpointer data)281 static gboolean on_button_release(GooCanvasItem * item,
282                                   GooCanvasItem * target,
283                                   GdkEventButton * event, gpointer data)
284 {
285     GooCanvasItemModel *model = goo_canvas_item_get_model(item);
286     GtkPolarView   *polv = GTK_POLAR_VIEW(data);
287     gint            catnum =
288         GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model), "catnum"));
289     gint           *catpoint = NULL;
290     sat_obj_t      *obj = NULL;
291     guint32         color;
292 
293     (void)target;
294 
295     catpoint = g_try_new0(gint, 1);
296     *catpoint = catnum;
297 
298     switch (event->button)
299     {
300         /* Select / de-select satellite */
301     case 1:
302         obj = SAT_OBJ(g_hash_table_lookup(polv->obj, catpoint));
303         if (obj == NULL)
304         {
305             sat_log_log(SAT_LOG_LEVEL_ERROR,
306                         _
307                         ("%s:%d: Can not find clicked object (%d) in hash table"),
308                         __FILE__, __LINE__, catnum);
309         }
310         else
311         {
312             obj->selected = !obj->selected;
313 
314             if (obj->selected)
315             {
316                 color = mod_cfg_get_int(polv->cfgdata,
317                                         MOD_CFG_POLAR_SECTION,
318                                         MOD_CFG_POLAR_SAT_SEL_COL,
319                                         SAT_CFG_INT_POLAR_SAT_SEL_COL);
320             }
321             else
322             {
323                 color = mod_cfg_get_int(polv->cfgdata,
324                                         MOD_CFG_POLAR_SECTION,
325                                         MOD_CFG_POLAR_SAT_COL,
326                                         SAT_CFG_INT_POLAR_SAT_COL);
327                 *catpoint = 0;
328 
329                 g_object_set(polv->sel, "text", "", NULL);
330             }
331 
332             g_object_set(obj->marker,
333                          "fill-color-rgba", color,
334                          "stroke-color-rgba", color, NULL);
335             g_object_set(obj->label,
336                          "fill-color-rgba", color,
337                          "stroke-color-rgba", color, NULL);
338 
339             /* clear other selections */
340             g_hash_table_foreach(polv->obj, clear_selection, catpoint);
341         }
342         break;
343 
344     default:
345         break;
346     }
347 
348     g_free(catpoint);
349 
350     return TRUE;
351 }
352 
353 /** Convert Az/El to canvas based XY coordinates. */
azel_to_xy(GtkPolarView * p,gdouble az,gdouble el,gfloat * x,gfloat * y)354 static void azel_to_xy(GtkPolarView * p, gdouble az, gdouble el, gfloat * x,
355                        gfloat * y)
356 {
357     gdouble         rel;
358 
359     if (el < 0.0)
360     {
361         /* FIXME: generate bug report */
362 
363         *x = 0.0;
364         *y = 0.0;
365 
366         return;
367     }
368 
369     /* convert angles to radians */
370     az = de2ra * az;
371     el = de2ra * el;
372 
373     /* radius @ el */
374     rel = p->r - (2 * p->r * el) / M_PI;
375 
376     switch (p->swap)
377     {
378 
379     case POLAR_VIEW_NWSE:
380         az = 2 * M_PI - az;
381         break;
382 
383     case POLAR_VIEW_SENW:
384         az = M_PI - az;
385         break;
386 
387     case POLAR_VIEW_SWNE:
388         az = M_PI + az;
389         break;
390 
391     default:
392         break;
393     }
394 
395     *x = (gfloat) (p->cx + rel * sin(az));
396     *y = (gfloat) (p->cy - rel * cos(az));
397 }
398 
399 /** Convert canvas based coordinates to Az/El. */
xy_to_azel(GtkPolarView * p,gfloat x,gfloat y,gfloat * az,gfloat * el)400 static void xy_to_azel(GtkPolarView * p, gfloat x, gfloat y, gfloat * az,
401                        gfloat * el)
402 {
403     gfloat          rel;
404 
405     /* distance from center to cursor */
406     rel = p->r - sqrt((x - p->cx) * (x - p->cx) + (y - p->cy) * (y - p->cy));
407 
408     /* scale according to p->r = 90 deg */
409     *el = 90.0 * rel / p->r;
410 
411     if (x >= p->cx)
412     {
413         /* 1. and 2. quadrant */
414         *az = atan2(x - p->cx, p->cy - y) / de2ra;
415     }
416     else
417     {
418         /* 3 and 4. quadrant */
419         *az = 360 + atan2(x - p->cx, p->cy - y) / de2ra;
420     }
421 
422     /* correct for orientation */
423     switch (p->swap)
424     {
425 
426     case POLAR_VIEW_NWSE:
427         *az = 360.0 - *az;
428         break;
429 
430     case POLAR_VIEW_SENW:
431         if (*az <= 180)
432             *az = 180.0 - *az;
433         else
434             *az = 540.0 - *az;
435         break;
436 
437     case POLAR_VIEW_SWNE:
438         if (*az >= 180.0)
439             *az = *az - 180.0;
440         else
441             *az = 180.0 + *az;
442         break;
443 
444     default:
445         break;
446     }
447 }
448 
449 /** Manage mouse motion events. */
on_motion_notify(GooCanvasItem * item,GooCanvasItem * target,GdkEventMotion * event,gpointer data)450 static gboolean on_motion_notify(GooCanvasItem * item,
451                                  GooCanvasItem * target,
452                                  GdkEventMotion * event, gpointer data)
453 {
454     GtkPolarView   *polv = GTK_POLAR_VIEW(data);
455     gfloat          az, el;
456     gchar          *text;
457 
458     (void)item;
459     (void)target;
460 
461     if (polv->cursinfo)
462     {
463 
464         xy_to_azel(polv, event->x, event->y, &az, &el);
465 
466         if (el > 0.0)
467         {
468             /* cursor track */
469             text = g_strdup_printf("AZ %.0f\302\260\nEL %.0f\302\260", az, el);
470             g_object_set(polv->curs, "text", text, NULL);
471             g_free(text);
472         }
473         else
474         {
475             g_object_set(polv->curs, "text", "", NULL);
476         }
477     }
478 
479     return TRUE;
480 }
481 
482 /**
483  * Finish canvas item setup.
484  *
485  * @param canvas
486  * @param item
487  * @param model
488  * @param data Pointer to the GtkPolarView object.
489  *
490  * This function is called when a canvas item is created. Its purpose is to connect
491  * the corresponding signals to the created items.
492  */
on_item_created(GooCanvas * canvas,GooCanvasItem * item,GooCanvasItemModel * model,gpointer data)493 static void on_item_created(GooCanvas * canvas,
494                             GooCanvasItem * item,
495                             GooCanvasItemModel * model, gpointer data)
496 {
497     (void)canvas;
498 
499     if (!goo_canvas_item_model_get_parent(model))
500     {
501         /* root item / canvas */
502         g_signal_connect(item, "motion_notify_event",
503                          (GCallback) on_motion_notify, data);
504     }
505 
506     else if (!g_object_get_data(G_OBJECT(item), "skip-signal-connection"))
507     {
508         g_signal_connect(item, "button_press_event",
509                          (GCallback) on_button_press, data);
510         g_signal_connect(item, "button_release_event",
511                          (GCallback) on_button_release, data);
512     }
513 }
514 
515 /**
516  * Transform pole coordinates.
517  *
518  * This function transforms the pols coordinates (x,y) taking into account
519  * the orientation of the polar plot.
520  */
521 static void
correct_pole_coor(GtkPolarView * polv,polar_view_pole_t pole,gfloat * x,gfloat * y,GooCanvasAnchorType * anch)522 correct_pole_coor(GtkPolarView * polv,
523                   polar_view_pole_t pole,
524                   gfloat * x, gfloat * y, GooCanvasAnchorType * anch)
525 {
526 
527     switch (pole)
528     {
529     case POLAR_VIEW_POLE_N:
530         if ((polv->swap == POLAR_VIEW_SENW) || (polv->swap == POLAR_VIEW_SWNE))
531         {
532             /* North and South are swapped */
533             *y = *y + POLV_LINE_EXTRA;
534             *anch = GOO_CANVAS_ANCHOR_NORTH;
535         }
536         else
537         {
538             *y = *y - POLV_LINE_EXTRA;
539             *anch = GOO_CANVAS_ANCHOR_SOUTH;
540         }
541 
542         break;
543 
544     case POLAR_VIEW_POLE_E:
545         if ((polv->swap == POLAR_VIEW_NWSE) || (polv->swap == POLAR_VIEW_SWNE))
546         {
547             /* East and West are swapped */
548             *x = *x - POLV_LINE_EXTRA;
549             *anch = GOO_CANVAS_ANCHOR_EAST;
550         }
551         else
552         {
553             *x = *x + POLV_LINE_EXTRA;
554             *anch = GOO_CANVAS_ANCHOR_WEST;
555         }
556         break;
557 
558     case POLAR_VIEW_POLE_S:
559         if ((polv->swap == POLAR_VIEW_SENW) || (polv->swap == POLAR_VIEW_SWNE))
560         {
561             /* North and South are swapped */
562             *y = *y - POLV_LINE_EXTRA;
563             *anch = GOO_CANVAS_ANCHOR_SOUTH;
564         }
565         else
566         {
567             *y = *y + POLV_LINE_EXTRA;
568             *anch = GOO_CANVAS_ANCHOR_NORTH;
569         }
570         break;
571 
572     case POLAR_VIEW_POLE_W:
573         if ((polv->swap == POLAR_VIEW_NWSE) || (polv->swap == POLAR_VIEW_SWNE))
574         {
575             /* East and West are swapped */
576             *x = *x + POLV_LINE_EXTRA;
577             *anch = GOO_CANVAS_ANCHOR_WEST;
578         }
579         else
580         {
581             *x = *x - POLV_LINE_EXTRA;
582             *anch = GOO_CANVAS_ANCHOR_EAST;
583         }
584         break;
585 
586     default:
587         /* FIXME: bug */
588         break;
589     }
590 }
591 
create_canvas_model(GtkPolarView * polv)592 static GooCanvasItemModel *create_canvas_model(GtkPolarView * polv)
593 {
594     GooCanvasItemModel *root;
595     gfloat          x, y;
596     guint32         col;
597     GooCanvasAnchorType anch = GOO_CANVAS_ANCHOR_CENTER;
598 
599     root = goo_canvas_group_model_new(NULL, NULL);
600 
601     /* graph dimensions */
602     polv->size = POLV_DEFAULT_SIZE;
603     polv->r = (polv->size / 2) - POLV_DEFAULT_MARGIN;
604     polv->cx = POLV_DEFAULT_SIZE / 2;
605     polv->cy = POLV_DEFAULT_SIZE / 2;
606 
607     col = mod_cfg_get_int(polv->cfgdata,
608                           MOD_CFG_POLAR_SECTION,
609                           MOD_CFG_POLAR_AXIS_COL, SAT_CFG_INT_POLAR_AXIS_COL);
610 
611     polv->bgd = goo_canvas_rect_model_new(root, 0.0, 0.0,
612                                           POLV_DEFAULT_SIZE, POLV_DEFAULT_SIZE,
613                                           "fill-color-rgba", 0xFFFFFFFF,
614                                           "stroke-color-rgba", 0xFFFFFFFF,
615                                           NULL);
616 
617     /* Add elevation circles at 0, 30 and 60 deg */
618     polv->C00 = goo_canvas_ellipse_model_new(root,
619                                              polv->cx, polv->cy,
620                                              polv->r, polv->r,
621                                              "line-width", 1.0,
622                                              "stroke-color-rgba", col, NULL);
623 
624     polv->C30 = goo_canvas_ellipse_model_new(root,
625                                              polv->cx, polv->cy,
626                                              0.6667 * polv->r,
627                                              0.6667 * polv->r, "line-width",
628                                              1.0, "stroke-color-rgba", col,
629                                              NULL);
630 
631     polv->C60 = goo_canvas_ellipse_model_new(root,
632                                              polv->cx, polv->cy,
633                                              0.333 * polv->r, 0.3333 * polv->r,
634                                              "line-width", 1.0,
635                                              "stroke-color-rgba", col, NULL);
636 
637     /* add horixontal and vertical guidance lines */
638     polv->hl = goo_canvas_polyline_model_new_line(root,
639                                                   polv->cx - polv->r -
640                                                   POLV_LINE_EXTRA, polv->cy,
641                                                   polv->cx + polv->r +
642                                                   POLV_LINE_EXTRA, polv->cy,
643                                                   "stroke-color-rgba", col,
644                                                   "line-width", 1.0, NULL);
645 
646     polv->vl = goo_canvas_polyline_model_new_line(root,
647                                                   polv->cx,
648                                                   polv->cy - polv->r -
649                                                   POLV_LINE_EXTRA, polv->cx,
650                                                   polv->cy + polv->r +
651                                                   POLV_LINE_EXTRA,
652                                                   "stroke-color-rgba", col,
653                                                   "line-width", 1.0, NULL);
654 
655     /* N, S, E and W labels.  */
656     col = mod_cfg_get_int(polv->cfgdata,
657                           MOD_CFG_POLAR_SECTION,
658                           MOD_CFG_POLAR_TICK_COL, SAT_CFG_INT_POLAR_TICK_COL);
659     azel_to_xy(polv, 0.0, 0.0, &x, &y);
660     correct_pole_coor(polv, POLAR_VIEW_POLE_N, &x, &y, &anch);
661     polv->N = goo_canvas_text_model_new(root, _("N"),
662                                         x,
663                                         y,
664                                         -1,
665                                         anch,
666                                         "font", "Sans 10",
667                                         "fill-color-rgba", col, NULL);
668 
669     azel_to_xy(polv, 180.0, 0.0, &x, &y);
670     correct_pole_coor(polv, POLAR_VIEW_POLE_S, &x, &y, &anch);
671     polv->S = goo_canvas_text_model_new(root, _("S"),
672                                         x,
673                                         y,
674                                         -1,
675                                         anch,
676                                         "font", "Sans 10",
677                                         "fill-color-rgba", col, NULL);
678 
679     azel_to_xy(polv, 90.0, 0.0, &x, &y);
680     correct_pole_coor(polv, POLAR_VIEW_POLE_E, &x, &y, &anch);
681     polv->E = goo_canvas_text_model_new(root, _("E"),
682                                         x,
683                                         y,
684                                         -1,
685                                         anch,
686                                         "font", "Sans 10",
687                                         "fill-color-rgba", col, NULL);
688 
689     azel_to_xy(polv, 270.0, 0.0, &x, &y);
690     correct_pole_coor(polv, POLAR_VIEW_POLE_W, &x, &y, &anch);
691     polv->W = goo_canvas_text_model_new(root, _("W"),
692                                         x,
693                                         y,
694                                         -1,
695                                         anch,
696                                         "font", "Sans 10",
697                                         "fill-color-rgba", col, NULL);
698 
699     /* cursor text */
700     col = mod_cfg_get_int(polv->cfgdata,
701                           MOD_CFG_POLAR_SECTION,
702                           MOD_CFG_POLAR_INFO_COL, SAT_CFG_INT_POLAR_INFO_COL);
703     polv->curs = goo_canvas_text_model_new(root, "",
704                                            polv->cx - polv->r -
705                                            2 * POLV_LINE_EXTRA,
706                                            polv->cy + polv->r +
707                                            POLV_LINE_EXTRA, -1,
708                                            GOO_CANVAS_ANCHOR_W, "font",
709                                            "Sans 8", "fill-color-rgba", col,
710                                            NULL);
711 
712     /* location info */
713     polv->locnam = goo_canvas_text_model_new(root, polv->qth->name,
714                                              polv->cx - polv->r -
715                                              2 * POLV_LINE_EXTRA,
716                                              polv->cy - polv->r -
717                                              POLV_LINE_EXTRA, -1,
718                                              GOO_CANVAS_ANCHOR_SW, "font",
719                                              "Sans 8", "fill-color-rgba", col,
720                                              NULL);
721 
722     /* next event */
723     polv->next = goo_canvas_text_model_new(root, "",
724                                            polv->cx + polv->r +
725                                            2 * POLV_LINE_EXTRA,
726                                            polv->cy - polv->r -
727                                            POLV_LINE_EXTRA, -1,
728                                            GOO_CANVAS_ANCHOR_E, "font",
729                                            "Sans 8", "fill-color-rgba", col,
730                                            "alignment", PANGO_ALIGN_RIGHT,
731                                            NULL);
732 
733     /* selected satellite text */
734     polv->sel = goo_canvas_text_model_new(root, "",
735                                           polv->cx + polv->r +
736                                           2 * POLV_LINE_EXTRA,
737                                           polv->cy + polv->r + POLV_LINE_EXTRA,
738                                           -1, GOO_CANVAS_ANCHOR_E, "font",
739                                           "Sans 8", "fill-color-rgba", col,
740                                           "alignment", PANGO_ALIGN_RIGHT,
741                                           NULL);
742 
743     return root;
744 }
745 
746 /**
747  * Create a new GtkPolarView widget.
748  *
749  * @param cfgdata The configuration data of the parent module.
750  * @param sats Pointer to the hash table containing the asociated satellites.
751  * @param qth Pointer to the ground station data.
752  */
gtk_polar_view_new(GKeyFile * cfgdata,GHashTable * sats,qth_t * qth)753 GtkWidget      *gtk_polar_view_new(GKeyFile * cfgdata, GHashTable * sats,
754                                    qth_t * qth)
755 {
756     GtkPolarView       *polv;
757     GooCanvasItemModel *root;
758 
759     polv = GTK_POLAR_VIEW(g_object_new(GTK_TYPE_POLAR_VIEW, NULL));
760 
761     polv->cfgdata = cfgdata;
762     polv->sats = sats;
763     polv->qth = qth;
764 
765     polv->obj = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, NULL);
766     polv->showtracks_on = g_hash_table_new_full(g_int_hash, g_int_equal,
767                                                 g_free, NULL);
768     polv->showtracks_off = g_hash_table_new_full(g_int_hash, g_int_equal,
769                                                  g_free, NULL);
770 
771     /* get settings */
772     polv->refresh = mod_cfg_get_int(cfgdata, MOD_CFG_POLAR_SECTION,
773                                     MOD_CFG_POLAR_REFRESH,
774                                     SAT_CFG_INT_POLAR_REFRESH);
775 
776     polv->showtrack = mod_cfg_get_bool(cfgdata, MOD_CFG_POLAR_SECTION,
777                                        MOD_CFG_POLAR_SHOW_TRACK_AUTO,
778                                        SAT_CFG_BOOL_POL_SHOW_TRACK_AUTO);
779 
780     polv->counter = 1;
781 
782     polv->swap = mod_cfg_get_int(cfgdata, MOD_CFG_POLAR_SECTION,
783                                  MOD_CFG_POLAR_ORIENTATION,
784                                  SAT_CFG_INT_POLAR_ORIENTATION);
785 
786     polv->qthinfo = mod_cfg_get_bool(cfgdata, MOD_CFG_POLAR_SECTION,
787                                      MOD_CFG_POLAR_SHOW_QTH_INFO,
788                                      SAT_CFG_BOOL_POL_SHOW_QTH_INFO);
789 
790     polv->eventinfo = mod_cfg_get_bool(cfgdata, MOD_CFG_POLAR_SECTION,
791                                        MOD_CFG_POLAR_SHOW_NEXT_EVENT,
792                                        SAT_CFG_BOOL_POL_SHOW_NEXT_EV);
793 
794     polv->cursinfo = mod_cfg_get_bool(cfgdata, MOD_CFG_POLAR_SECTION,
795                                       MOD_CFG_POLAR_SHOW_CURS_TRACK,
796                                       SAT_CFG_BOOL_POL_SHOW_CURS_TRACK);
797 
798     polv->extratick = mod_cfg_get_bool(cfgdata, MOD_CFG_POLAR_SECTION,
799                                        MOD_CFG_POLAR_SHOW_EXTRA_AZ_TICKS,
800                                        SAT_CFG_BOOL_POL_SHOW_EXTRA_AZ_TICKS);
801     gtk_polar_view_load_showtracks(polv);
802 
803     /* create the canvas */
804     polv->canvas = goo_canvas_new();
805     g_object_set(G_OBJECT(polv->canvas), "has-tooltip", TRUE, NULL);
806     gtk_widget_set_size_request(polv->canvas, POLV_DEFAULT_SIZE, POLV_DEFAULT_SIZE);
807     goo_canvas_set_bounds(GOO_CANVAS(polv->canvas), 0, 0,
808                           POLV_DEFAULT_SIZE, POLV_DEFAULT_SIZE);
809 
810     /* connect size-request signal */
811     g_signal_connect(polv->canvas, "size-allocate",
812                      G_CALLBACK(size_allocate_cb), polv);
813     g_signal_connect(polv->canvas, "item_created",
814                      (GCallback) on_item_created, polv);
815     g_signal_connect_after(polv->canvas, "realize",
816                            (GCallback) on_canvas_realized, polv);
817     gtk_widget_show(polv->canvas);
818 
819     /* Create the canvas model */
820     root = create_canvas_model(polv);
821     goo_canvas_set_root_item_model(GOO_CANVAS(polv->canvas), root);
822 
823     g_object_unref(root);
824 
825     //gtk_box_pack_start (GTK_BOX (polv), GTK_POLAR_VIEW (polv)->swin, TRUE, TRUE, 0);
826     gtk_box_pack_start(GTK_BOX(polv), polv->canvas, TRUE, TRUE, 0);
827 
828     return GTK_WIDGET(polv);
829 }
830 
update_polv_size(GtkPolarView * polv)831 static void update_polv_size(GtkPolarView * polv)
832 {
833     GtkAllocation   allocation;
834     GooCanvasPoints *prec;
835     gfloat          x, y;
836     GooCanvasAnchorType anch = GOO_CANVAS_ANCHOR_CENTER;
837 
838 
839     if (gtk_widget_get_realized(GTK_WIDGET(polv)))
840     {
841         /* get graph dimensions */
842         gtk_widget_get_allocation(GTK_WIDGET(polv), &allocation);
843 
844         polv->size = MIN(allocation.width, allocation.height);
845         polv->r = (polv->size / 2) - POLV_DEFAULT_MARGIN;
846         polv->cx = allocation.width / 2;
847         polv->cy = allocation.height / 2;
848 
849         /* update canvas bounds to match new size */
850         goo_canvas_set_bounds(GOO_CANVAS(GTK_POLAR_VIEW(polv)->canvas), 0, 0,
851                               allocation.width, allocation.height);
852 
853         /* background item */
854         g_object_set(polv->bgd, "width", (gdouble) allocation.width,
855                      "height", (gdouble) allocation.height, NULL);
856 
857         /* update coordinate system */
858         g_object_set(polv->C00,
859                      "center-x", (gdouble) polv->cx,
860                      "center-y", (gdouble) polv->cy,
861                      "radius-x", (gdouble) polv->r,
862                      "radius-y", (gdouble) polv->r, NULL);
863         g_object_set(polv->C30,
864                      "center-x", (gdouble) polv->cx,
865                      "center-y", (gdouble) polv->cy,
866                      "radius-x", (gdouble) 0.6667 * polv->r,
867                      "radius-y", (gdouble) 0.6667 * polv->r, NULL);
868         g_object_set(polv->C60,
869                      "center-x", (gdouble) polv->cx,
870                      "center-y", (gdouble) polv->cy,
871                      "radius-x", (gdouble) 0.333 * polv->r,
872                      "radius-y", (gdouble) 0.333 * polv->r, NULL);
873 
874         /* horizontal line */
875         prec = goo_canvas_points_new(2);
876         prec->coords[0] = polv->cx - polv->r - POLV_LINE_EXTRA;
877         prec->coords[1] = polv->cy;
878         prec->coords[2] = polv->cx + polv->r + POLV_LINE_EXTRA;
879         prec->coords[3] = polv->cy;
880         g_object_set(polv->hl, "points", prec, NULL);
881 
882         /* vertical line */
883         prec->coords[0] = polv->cx;
884         prec->coords[1] = polv->cy - polv->r - POLV_LINE_EXTRA;
885         prec->coords[2] = polv->cx;
886         prec->coords[3] = polv->cy + polv->r + POLV_LINE_EXTRA;
887         g_object_set(polv->vl, "points", prec, NULL);
888 
889         /* free memory */
890         goo_canvas_points_unref(prec);
891 
892         /* N/E/S/W */
893         azel_to_xy(polv, 0.0, 0.0, &x, &y);
894         correct_pole_coor(polv, POLAR_VIEW_POLE_N, &x, &y, &anch);
895         g_object_set(polv->N, "x", x, "y", y, NULL);
896 
897         azel_to_xy(polv, 90.0, 0.0, &x, &y);
898         correct_pole_coor(polv, POLAR_VIEW_POLE_E, &x, &y, &anch);
899         g_object_set(polv->E, "x", x, "y", y, NULL);
900 
901         azel_to_xy(polv, 180.0, 0.0, &x, &y);
902         correct_pole_coor(polv, POLAR_VIEW_POLE_S, &x, &y, &anch);
903         g_object_set(polv->S, "x", x, "y", y, NULL);
904 
905         azel_to_xy(polv, 270.0, 0.0, &x, &y);
906         correct_pole_coor(polv, POLAR_VIEW_POLE_W, &x, &y, &anch);
907         g_object_set(polv->W, "x", x, "y", y, NULL);
908 
909         /* cursor track */
910         g_object_set(polv->curs,
911                      "x", (gfloat) (polv->cx - polv->r - 2 * POLV_LINE_EXTRA),
912                      "y", (gfloat) (polv->cy + polv->r + POLV_LINE_EXTRA),
913                      NULL);
914 
915         /* location name */
916         g_object_set(polv->locnam,
917                      "x", (gfloat) (polv->cx - polv->r - 2 * POLV_LINE_EXTRA),
918                      "y", (gfloat) (polv->cy - polv->r - POLV_LINE_EXTRA),
919                      NULL);
920 
921         /* next event */
922         g_object_set(polv->next,
923                      "x", (gfloat) (polv->cx + polv->r + 2 * POLV_LINE_EXTRA),
924                      "y", (gfloat) (polv->cy - polv->r - POLV_LINE_EXTRA),
925                      NULL);
926 
927         /* selection info */
928         g_object_set(polv->sel,
929                      "x", (gfloat) polv->cx + polv->r + 2 * POLV_LINE_EXTRA,
930                      "y", (gfloat) polv->cy + polv->r + POLV_LINE_EXTRA, NULL);
931 
932         g_hash_table_foreach(polv->sats, update_sat, polv);
933 
934         /* sky tracks */
935         g_hash_table_foreach(polv->obj, update_track, polv);
936     }
937 }
938 
gtk_polar_view_update(GtkWidget * widget)939 void gtk_polar_view_update(GtkWidget * widget)
940 {
941     GtkPolarView   *polv = GTK_POLAR_VIEW(widget);
942     gdouble         number, now;
943     gchar          *buff;
944     guint           h, m, s;
945     sat_t          *sat = NULL;
946     gint           *catnr;
947 
948     if (polv->resize)
949     {
950         update_polv_size(polv);
951         polv->resize = FALSE;
952     }
953 
954     /* check refresh rate and refresh sats if time */
955     /* FIXME need to add location based update */
956     if (polv->counter < polv->refresh)
957     {
958         polv->counter++;
959     }
960     else
961     {
962         /* reset data */
963         polv->counter = 1;
964         polv->naos = 0.0;
965         polv->ncat = 0;
966 
967         /* update sats */
968         g_hash_table_foreach(polv->sats, update_sat, polv);
969 
970         /* update countdown to NEXT AOS label */
971         if (polv->eventinfo)
972         {
973 
974             if (polv->ncat > 0)
975             {
976                 catnr = g_try_new0(gint, 1);
977                 *catnr = polv->ncat;
978                 sat = SAT(g_hash_table_lookup(polv->sats, catnr));
979                 g_free(catnr);
980 
981                 /* last desperate sanity check */
982                 if (sat != NULL)
983                 {
984 
985                     now = polv->tstamp; //get_current_daynum ();
986                     number = polv->naos - now;
987 
988                     /* convert julian date to seconds */
989                     s = (guint) (number * 86400);
990 
991                     /* extract hours */
992                     h = (guint) floor(s / 3600);
993                     s -= 3600 * h;
994 
995                     /* extract minutes */
996                     m = (guint) floor(s / 60);
997                     s -= 60 * m;
998 
999                     if (h > 0)
1000                         buff =
1001                             g_strdup_printf(_("Next: %s\nin %02d:%02d:%02d"),
1002                                             sat->nickname, h, m, s);
1003                     else
1004                         buff = g_strdup_printf(_("Next: %s\nin %02d:%02d"),
1005                                                sat->nickname, m, s);
1006 
1007 
1008                     g_object_set(polv->next, "text", buff, NULL);
1009 
1010                     g_free(buff);
1011                 }
1012                 else
1013                 {
1014                     sat_log_log(SAT_LOG_LEVEL_ERROR,
1015                                 _("%s: Can not find NEXT satellite."),
1016                                 __func__);
1017                     g_object_set(polv->next, "text", _("Next: ERR"), NULL);
1018                 }
1019             }
1020             else
1021             {
1022                 g_object_set(polv->next, "text", _("Next: N/A"), NULL);
1023             }
1024         }
1025         else
1026         {
1027             g_object_set(polv->next, "text", "", NULL);
1028         }
1029     }
1030 }
1031 
1032 /** Convert LOS timestamp to human readable countdown string */
los_time_to_str(GtkPolarView * polv,sat_t * sat)1033 static gchar   *los_time_to_str(GtkPolarView * polv, sat_t * sat)
1034 {
1035     guint           h, m, s;
1036     gdouble         number, now;
1037     gchar          *text = NULL;
1038 
1039     now = polv->tstamp;         //get_current_daynum ();
1040     number = sat->los - now;
1041 
1042     /* convert julian date to seconds */
1043     s = (guint) (number * 86400);
1044 
1045     /* extract hours */
1046     h = (guint) floor(s / 3600);
1047     s -= 3600 * h;
1048 
1049     /* extract minutes */
1050     m = (guint) floor(s / 60);
1051     s -= 60 * m;
1052 
1053     if (h > 0)
1054     {
1055         text = g_strdup_printf(_("LOS in %02d:%02d:%02d"), h, m, s);
1056     }
1057     else
1058     {
1059         text = g_strdup_printf(_("LOS in %02d:%02d"), m, s);
1060     }
1061 
1062     return text;
1063 }
1064 
update_sat(gpointer key,gpointer value,gpointer data)1065 static void update_sat(gpointer key, gpointer value, gpointer data)
1066 {
1067     gint           *catnum;
1068     sat_t          *sat = SAT(value);
1069     GtkPolarView   *polv = GTK_POLAR_VIEW(data);
1070     sat_obj_t      *obj = NULL;
1071     gfloat          x, y;
1072     GooCanvasItemModel *root;
1073     gint            idx, i;
1074     gdouble         now;        // = get_current_daynum ();
1075     gchar          *text;
1076     gchar          *losstr;
1077     gchar          *tooltip;
1078     guint32         colour;
1079 
1080     (void)key;                  /* avoid unused parameter compiler warning */
1081 
1082     catnum = g_new0(gint, 1);
1083     *catnum = sat->tle.catnr;
1084 
1085     now = polv->tstamp;
1086 
1087     /* update next AOS */
1088     if (sat->aos > now)
1089     {
1090         if ((sat->aos < polv->naos) || (polv->naos == 0.0))
1091         {
1092             polv->naos = sat->aos;
1093             polv->ncat = sat->tle.catnr;
1094         }
1095     }
1096 
1097     /* if sat is out of range */
1098     if ((sat->el < 0.00) || decayed(sat))
1099     {
1100 
1101         obj = SAT_OBJ(g_hash_table_lookup(polv->obj, catnum));
1102 
1103         /* if sat is on canvas */
1104         if (obj != NULL)
1105         {
1106             /* remove sat from canvas */
1107             root = goo_canvas_get_root_item_model(GOO_CANVAS(polv->canvas));
1108 
1109             idx = goo_canvas_item_model_find_child(root, obj->marker);
1110             if (idx != -1)
1111             {
1112                 goo_canvas_item_model_remove_child(root, idx);
1113             }
1114 
1115             idx = goo_canvas_item_model_find_child(root, obj->label);
1116             if (idx != -1)
1117             {
1118                 goo_canvas_item_model_remove_child(root, idx);
1119             }
1120 
1121             /* remove sky track */
1122             if (obj->showtrack)
1123             {
1124                 gtk_polar_view_delete_track(polv, obj, sat);
1125             }
1126 
1127             /* free pass info */
1128             free_pass(obj->pass);
1129             obj->pass = NULL;
1130 
1131             /* if this was the selected satellite we need to
1132                clear the info text
1133              */
1134             if (obj->selected)
1135             {
1136                 g_object_set(polv->sel, "text", "", NULL);
1137             }
1138 
1139             g_free(obj);
1140 
1141             /* remove sat object from hash table */
1142             g_hash_table_remove(polv->obj, catnum);
1143 
1144             /* FIXME: remove track from chart */
1145         }
1146 
1147         g_free(catnum);
1148     }
1149 
1150     /* sat is within range */
1151     else
1152     {
1153         obj = SAT_OBJ(g_hash_table_lookup(polv->obj, catnum));
1154         azel_to_xy(polv, sat->az, sat->el, &x, &y);
1155 
1156         /* if sat is already on canvas */
1157         if (obj != NULL)
1158         {
1159             /* update LOS count down */
1160             if (sat->los > 0.0)
1161             {
1162                 losstr = los_time_to_str(polv, sat);
1163             }
1164             else
1165             {
1166                 losstr =
1167                     g_strdup_printf(_("%s\nAlways in range"), sat->nickname);
1168             }
1169 
1170             /* update label */
1171             g_object_set(obj->label, "text", sat->nickname, NULL);
1172 
1173             /* update tooltip */
1174             tooltip = g_markup_printf_escaped("<b>%s</b>\n"
1175                                               "Az: %5.1f\302\260\n"
1176                                               "El: %5.1f\302\260\n"
1177                                               "%s",
1178                                               sat->nickname,
1179                                               sat->az, sat->el, losstr);
1180 
1181             g_object_set(obj->marker,
1182                          "x", x - MARKER_SIZE_HALF,
1183                          "y", y - MARKER_SIZE_HALF, "tooltip", tooltip, NULL);
1184             g_object_set(obj->label,
1185                          "x", x, "y", y + 2, "tooltip", tooltip, NULL);
1186 
1187             g_free(tooltip);
1188 
1189             /* update selection info if satellite is
1190                selected
1191              */
1192             if (obj->selected)
1193             {
1194                 text = g_strdup_printf("%s\n%s", sat->nickname, losstr);
1195                 g_object_set(polv->sel, "text", text, NULL);
1196                 g_free(text);
1197             }
1198 
1199             /* Current pass and sky track needs update if they were calculated at
1200              * a different location or time (time controller)
1201              */
1202             if (obj->pass)
1203             {
1204                 /** FIXME: threshold */
1205                 gboolean        qth_upd =
1206                     qth_small_dist(polv->qth, (obj->pass->qth_comp)) > 1.0;
1207                 gboolean        time_upd = !((obj->pass->aos <= now) &&
1208                                              (obj->pass->los >= now));
1209 
1210                 if (qth_upd || time_upd)
1211                 {
1212                     sat_log_log(SAT_LOG_LEVEL_DEBUG,
1213                                 _
1214                                 ("%s:%s: Updating satellite pass SAT:%d Q:%d T:%d\n"),
1215                                 __FILE__, __func__, *catnum, qth_upd,
1216                                 time_upd);
1217 
1218                     root =
1219                         goo_canvas_get_root_item_model(GOO_CANVAS
1220                                                        (polv->canvas));
1221 
1222                     /* remove sky track */
1223                     if (obj->showtrack)
1224                     {
1225                         idx =
1226                             goo_canvas_item_model_find_child(root, obj->track);
1227                         if (idx != -1)
1228                             goo_canvas_item_model_remove_child(root, idx);
1229 
1230                         for (i = 0; i < TRACK_TICK_NUM; i++)
1231                         {
1232                             idx =
1233                                 goo_canvas_item_model_find_child(root,
1234                                                                  obj->trtick
1235                                                                  [i]);
1236                             if (idx != -1)
1237                                 goo_canvas_item_model_remove_child(root, idx);
1238                         }
1239                     }
1240 
1241                     /* free pass info */
1242                     free_pass(obj->pass);
1243                     obj->pass = NULL;
1244 
1245                     /*compute new pass */
1246                     obj->pass = get_current_pass(sat, polv->qth, now);
1247 
1248                     /* Finally, create the sky track if necessary */
1249                     if (obj->showtrack)
1250                         gtk_polar_view_create_track(polv, obj, sat);
1251                 }
1252             }
1253             g_free(losstr);
1254             g_free(catnum);     // FIXME: why free here, what about else?
1255         }
1256         else
1257         {
1258             /* add sat to canvas */
1259             obj = g_try_new(sat_obj_t, 1);
1260 
1261             if (obj != NULL)
1262             {
1263                 /* space was allocated now use it */
1264                 obj->selected = FALSE;
1265 
1266                 if (g_hash_table_lookup_extended
1267                     (polv->showtracks_on, catnum, NULL, NULL))
1268                 {
1269                     obj->showtrack = TRUE;
1270                 }
1271                 else if (g_hash_table_lookup_extended
1272                          (polv->showtracks_off, catnum, NULL, NULL))
1273                 {
1274                     obj->showtrack = FALSE;
1275                 }
1276                 else
1277                 {
1278                     obj->showtrack = polv->showtrack;
1279                 }
1280                 obj->istarget = FALSE;
1281 
1282                 root =
1283                     goo_canvas_get_root_item_model(GOO_CANVAS(polv->canvas));
1284 
1285                 colour = mod_cfg_get_int(polv->cfgdata,
1286                                          MOD_CFG_POLAR_SECTION,
1287                                          MOD_CFG_POLAR_SAT_COL,
1288                                          SAT_CFG_INT_POLAR_SAT_COL);
1289 
1290                 /* create tooltip */
1291                 tooltip = g_markup_printf_escaped("<b>%s</b>\n"
1292                                                   "Az: %5.1f\302\260\n"
1293                                                   "El: %5.1f\302\260\n",
1294                                                   sat->nickname,
1295                                                   sat->az, sat->el);
1296 
1297                 obj->marker = goo_canvas_rect_model_new(root,
1298                                                         x - MARKER_SIZE_HALF,
1299                                                         y - MARKER_SIZE_HALF,
1300                                                         2 * MARKER_SIZE_HALF,
1301                                                         2 * MARKER_SIZE_HALF,
1302                                                         "fill-color-rgba",
1303                                                         colour,
1304                                                         "stroke-color-rgba",
1305                                                         colour, "tooltip",
1306                                                         tooltip, NULL);
1307                 obj->label =
1308                     goo_canvas_text_model_new(root, sat->nickname, x, y + 2,
1309                                               -1, GOO_CANVAS_ANCHOR_NORTH,
1310                                               "font", "Sans 8",
1311                                               "fill-color-rgba", colour,
1312                                               "tooltip", tooltip, NULL);
1313 
1314                 g_free(tooltip);
1315                 if (goo_canvas_item_model_find_child(root, obj->marker) != -1)
1316                     goo_canvas_item_model_raise(obj->marker, NULL);
1317                 else
1318                     sat_log_log(SAT_LOG_LEVEL_ERROR,
1319                                 _
1320                                 ("%s: marker added to polarview not showing %d."),
1321                                 __func__, *catnum);
1322 
1323                 if (goo_canvas_item_model_find_child(root, obj->label) != -1)
1324                     goo_canvas_item_model_raise(obj->label, NULL);
1325                 else
1326                     sat_log_log(SAT_LOG_LEVEL_ERROR,
1327                                 _
1328                                 ("%s: label added to polarview not showing %d."),
1329                                 __func__, *catnum);
1330 
1331                 g_object_set_data(G_OBJECT(obj->marker), "catnum",
1332                                   GINT_TO_POINTER(*catnum));
1333                 g_object_set_data(G_OBJECT(obj->label), "catnum",
1334                                   GINT_TO_POINTER(*catnum));
1335 
1336                 /* get info about the current pass */
1337                 obj->pass = get_current_pass(sat, polv->qth, now);
1338 
1339                 /* add sat to hash table */
1340                 g_hash_table_insert(polv->obj, catnum, obj);
1341 
1342                 /* Finally, create the sky track if necessary */
1343                 if (obj->showtrack)
1344                     gtk_polar_view_create_track(polv, obj, sat);
1345             }
1346             else
1347             {
1348                 /* obj == NULL */
1349                 sat_log_log(SAT_LOG_LEVEL_ERROR,
1350                             _("%s: Cannot allocate memory for satellite %d."),
1351                             __func__, sat->tle.catnr);
1352                 return;
1353             }
1354         }
1355     }
1356 }
1357 
1358 /**  Update sky track drawing after size allocate. */
update_track(gpointer key,gpointer value,gpointer data)1359 static void update_track(gpointer key, gpointer value, gpointer data)
1360 {
1361     sat_obj_t      *obj = SAT_OBJ(value);;
1362     GtkPolarView   *pv = GTK_POLAR_VIEW(data);
1363     guint           num, i;
1364     GooCanvasPoints *points;
1365     gfloat          x, y;
1366     pass_detail_t  *detail;
1367     guint           tres, ttidx;
1368 
1369     (void)key;
1370 
1371     if (obj->showtrack)
1372     {
1373         if (obj->pass == NULL)
1374         {
1375             sat_log_log(SAT_LOG_LEVEL_ERROR,
1376                         _("%s:%d: Failed to get satellite pass."),
1377                         __FILE__, __LINE__);
1378             return;
1379         }
1380 
1381         /* create points */
1382         num = g_slist_length(obj->pass->details);
1383         if (num == 0)
1384         {
1385             sat_log_log(SAT_LOG_LEVEL_ERROR,
1386                         _("%s:%d: Pass had no points in it."),
1387                         __FILE__, __LINE__);
1388             return;
1389         }
1390 
1391         points = goo_canvas_points_new(num);
1392 
1393         /* first point should be (aos_az,0.0) */
1394         azel_to_xy(pv, obj->pass->aos_az, 0.0, &x, &y);
1395         points->coords[0] = (double)x;
1396         points->coords[1] = (double)y;
1397 
1398         /* time tick 0 */
1399         g_object_set(obj->trtick[0], "x", (gdouble) x, "y", (gdouble) y, NULL);
1400 
1401         /* time resolution for time ticks; we need
1402            3 additional points to AOS and LOS ticks.
1403          */
1404         tres = (num - 2) / (TRACK_TICK_NUM - 1);
1405         ttidx = 1;
1406 
1407         for (i = 1; i < num - 1; i++)
1408         {
1409             detail = PASS_DETAIL(g_slist_nth_data(obj->pass->details, i));
1410             if (detail->el >= 0)
1411                 azel_to_xy(pv, detail->az, detail->el, &x, &y);
1412             points->coords[2 * i] = (double)x;
1413             points->coords[2 * i + 1] = (double)y;
1414 
1415             if (!(i % tres))
1416             {
1417                 /* update time tick */
1418                 if (ttidx < TRACK_TICK_NUM)
1419                     g_object_set(obj->trtick[ttidx],
1420                                  "x", (gdouble) x, "y", (gdouble) y, NULL);
1421                 ttidx++;
1422             }
1423         }
1424 
1425         /* last point should be (los_az, 0.0)  */
1426         azel_to_xy(pv, obj->pass->los_az, 0.0, &x, &y);
1427         points->coords[2 * (num - 1)] = (double)x;
1428         points->coords[2 * (num - 1) + 1] = (double)y;
1429 
1430         g_object_set(obj->track, "points", points, NULL);
1431 
1432         goo_canvas_points_unref(points);
1433     }
1434 }
1435 
create_time_tick(GtkPolarView * pv,gdouble time,gfloat x,gfloat y)1436 static GooCanvasItemModel *create_time_tick(GtkPolarView * pv, gdouble time,
1437                                             gfloat x, gfloat y)
1438 {
1439     GooCanvasItemModel *item;
1440     gchar           buff[6];
1441     GooCanvasAnchorType anchor;
1442     GooCanvasItemModel *root;
1443     guint32         col;
1444 
1445     root = goo_canvas_get_root_item_model(GOO_CANVAS(pv->canvas));
1446 
1447     col = mod_cfg_get_int(pv->cfgdata,
1448                           MOD_CFG_POLAR_SECTION,
1449                           MOD_CFG_POLAR_TRACK_COL,
1450                           SAT_CFG_INT_POLAR_TRACK_COL);
1451 
1452     daynum_to_str(buff, 6, "%H:%M", time);
1453 
1454     if (x > pv->cx)
1455     {
1456         anchor = GOO_CANVAS_ANCHOR_EAST;
1457         x -= 5;
1458     }
1459     else
1460     {
1461         anchor = GOO_CANVAS_ANCHOR_WEST;
1462         x += 5;
1463     }
1464 
1465     item = goo_canvas_text_model_new(root, buff,
1466                                      (gdouble) x, (gdouble) y,
1467                                      -1, anchor,
1468                                      "font", "Sans 7",
1469                                      "fill-color-rgba", col, NULL);
1470 
1471     return item;
1472 }
1473 
1474 /**
1475  * Create a sky track for a satellite.
1476  *
1477  * @param pv Pointer to the GtkPolarView object.
1478  * @param obj Pointer to the sat_obj_t object.
1479  * @param sat Pointer to the sat_t object.
1480  *
1481  * Note: This function is only used when the the satellite comes within range
1482  *       and the ALWAYS_SHOW_SKY_TRACK option is TRUE.
1483  */
gtk_polar_view_create_track(GtkPolarView * pv,sat_obj_t * obj,sat_t * sat)1484 void gtk_polar_view_create_track(GtkPolarView * pv, sat_obj_t * obj,
1485                                  sat_t * sat)
1486 {
1487     guint           i;
1488     GooCanvasItemModel *root;
1489     pass_detail_t  *detail;
1490     guint           num;
1491     GooCanvasPoints *points;
1492     gfloat          x, y;
1493     guint32         col;
1494     guint           tres, ttidx;
1495 
1496     (void)sat;
1497 
1498     /* get satellite object */
1499     /*obj = SAT_OBJ(g_object_get_data (G_OBJECT (item), "obj"));
1500        sat = SAT(g_object_get_data (G_OBJECT (item), "sat"));
1501        qth = (qth_t *)(g_object_get_data (G_OBJECT (item), "qth")); */
1502 
1503     if (obj == NULL)
1504     {
1505         sat_log_log(SAT_LOG_LEVEL_ERROR,
1506                     _("%s:%d: Failed to get satellite object."),
1507                     __FILE__, __LINE__);
1508         return;
1509     }
1510 
1511     if (obj->pass == NULL)
1512     {
1513         sat_log_log(SAT_LOG_LEVEL_ERROR,
1514                     _("%s:%d: Failed to get satellite pass."),
1515                     __FILE__, __LINE__);
1516         return;
1517     }
1518 
1519     root = goo_canvas_get_root_item_model(GOO_CANVAS(pv->canvas));
1520 
1521     /* add sky track */
1522 
1523     /* create points */
1524     num = g_slist_length(obj->pass->details);
1525     if (num == 0)
1526     {
1527         sat_log_log(SAT_LOG_LEVEL_ERROR,
1528                     _("%s:%d: Pass had no points in it."), __FILE__, __LINE__);
1529         return;
1530     }
1531 
1532     /* time resolution for time ticks; we need
1533        3 additional points to AOS and LOS ticks.
1534      */
1535     tres = (num - 2) / (TRACK_TICK_NUM - 1);
1536 
1537     points = goo_canvas_points_new(num);
1538 
1539 
1540 
1541     /* first point should be (aos_az,0.0) */
1542     azel_to_xy(pv, obj->pass->aos_az, 0.0, &x, &y);
1543     points->coords[0] = (double)x;
1544     points->coords[1] = (double)y;
1545     obj->trtick[0] = create_time_tick(pv, obj->pass->aos, x, y);
1546 
1547     ttidx = 1;
1548 
1549     for (i = 1; i < num - 1; i++)
1550     {
1551         detail = PASS_DETAIL(g_slist_nth_data(obj->pass->details, i));
1552         if (detail->el >= 0.0)
1553             azel_to_xy(pv, detail->az, detail->el, &x, &y);
1554         points->coords[2 * i] = (double)x;
1555         points->coords[2 * i + 1] = (double)y;
1556 
1557         if (!(i % tres))
1558         {
1559             /* create a time tick */
1560             if (ttidx < TRACK_TICK_NUM)
1561                 obj->trtick[ttidx] = create_time_tick(pv, detail->time, x, y);
1562             ttidx++;
1563         }
1564     }
1565 
1566     /* last point should be (los_az, 0.0)  */
1567     azel_to_xy(pv, obj->pass->los_az, 0.0, &x, &y);
1568     points->coords[2 * (num - 1)] = (double)x;
1569     points->coords[2 * (num - 1) + 1] = (double)y;
1570 
1571     /* create poly-line */
1572     col = mod_cfg_get_int(pv->cfgdata,
1573                           MOD_CFG_POLAR_SECTION,
1574                           MOD_CFG_POLAR_TRACK_COL,
1575                           SAT_CFG_INT_POLAR_TRACK_COL);
1576 
1577     obj->track = goo_canvas_polyline_model_new(root, FALSE, 0,
1578                                                "points", points,
1579                                                "line-width", 1.0,
1580                                                "stroke-color-rgba", col,
1581                                                "line-cap",
1582                                                CAIRO_LINE_CAP_SQUARE,
1583                                                "line-join",
1584                                                CAIRO_LINE_JOIN_MITER, NULL);
1585     goo_canvas_points_unref(points);
1586 }
1587 
gtk_polar_view_delete_track(GtkPolarView * pv,sat_obj_t * obj,sat_t * sat)1588 void gtk_polar_view_delete_track(GtkPolarView * pv, sat_obj_t * obj,
1589                                  sat_t * sat)
1590 {
1591     gint            idx, i;
1592     GooCanvasItemModel *root;
1593 
1594     (void)sat;
1595 
1596     root = goo_canvas_get_root_item_model(GOO_CANVAS(pv->canvas));
1597     idx = goo_canvas_item_model_find_child(root, obj->track);
1598 
1599     if (idx != -1)
1600     {
1601         goo_canvas_item_model_remove_child(root, idx);
1602     }
1603 
1604     for (i = 0; i < TRACK_TICK_NUM; i++)
1605     {
1606         idx = goo_canvas_item_model_find_child(root, obj->trtick[i]);
1607 
1608         if (idx != -1)
1609         {
1610             goo_canvas_item_model_remove_child(root, idx);
1611         }
1612     }
1613 }
1614 
1615 /** Reload reference to satellites (e.g. after TLE update). */
gtk_polar_view_reload_sats(GtkWidget * polv,GHashTable * sats)1616 void gtk_polar_view_reload_sats(GtkWidget * polv, GHashTable * sats)
1617 {
1618     GTK_POLAR_VIEW(polv)->sats = sats;
1619 
1620     GTK_POLAR_VIEW(polv)->naos = 0.0;
1621     GTK_POLAR_VIEW(polv)->ncat = 0;
1622 }
1623 
1624 /** Select a satellite */
gtk_polar_view_select_sat(GtkWidget * widget,gint catnum)1625 void gtk_polar_view_select_sat(GtkWidget * widget, gint catnum)
1626 {
1627     GtkPolarView   *polv = GTK_POLAR_VIEW(widget);
1628     gint           *catpoint = NULL;
1629     sat_obj_t      *obj = NULL;
1630     guint32         color;
1631 
1632     catpoint = g_try_new0(gint, 1);
1633     *catpoint = catnum;
1634 
1635     obj = SAT_OBJ(g_hash_table_lookup(polv->obj, catpoint));
1636     if (obj == NULL)
1637     {
1638         sat_log_log(SAT_LOG_LEVEL_DEBUG,
1639                     _("%s Requested satellite (%d) is not within range"),
1640                     __func__, catnum);
1641     }
1642     else
1643     {
1644         obj->selected = TRUE;
1645 
1646         color = mod_cfg_get_int(polv->cfgdata,
1647                                 MOD_CFG_POLAR_SECTION,
1648                                 MOD_CFG_POLAR_SAT_SEL_COL,
1649                                 SAT_CFG_INT_POLAR_SAT_SEL_COL);
1650 
1651         g_object_set(obj->marker,
1652                      "fill-color-rgba", color,
1653                      "stroke-color-rgba", color, NULL);
1654         g_object_set(obj->label,
1655                      "fill-color-rgba", color,
1656                      "stroke-color-rgba", color, NULL);
1657 
1658     }
1659 
1660     /* clear previous selection, if any */
1661     g_hash_table_foreach(polv->obj, clear_selection, catpoint);
1662 
1663     g_free(catpoint);
1664 }
1665