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 >k_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