2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2006-2008, Quy Tonthat <qtonthat@gmail.com>
6 * Copyright (C) 2016, Rob Norris <rw_norris@hotmail.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 #include "icons/icons.h"
35 #include "viktrwlayer.h"
43 #include <glib/gstdio.h>
44 #include <glib/gprintf.h>
45 #include <glib/gi18n.h>
46 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
50 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp);
51 static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter );
52 static void vik_gps_layer_free ( VikGpsLayer *vgl );
53 static void vik_gps_layer_draw ( VikGpsLayer *vgl, VikViewport *vp );
54 static VikGpsLayer *vik_gps_layer_new ( VikViewport *vp );
55 static void vik_gps_layer_post_read ( VikGpsLayer *vgl, VikViewport *vp, gboolean from_file );
57 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *len );
58 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
59 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
60 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation );
62 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl );
64 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode );
65 static void gps_layer_add_menu_items( VikGpsLayer *vtl, GtkMenu *menu, gpointer vlp );
67 static void gps_upload_cb( gpointer layer_and_vlp[2] );
68 static void gps_download_cb( gpointer layer_and_vlp[2] );
69 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] );
70 static void gps_empty_download_cb( gpointer layer_and_vlp[2] );
71 static void gps_empty_all_cb( gpointer layer_and_vlp[2] );
72 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
73 static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] );
74 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2] );
75 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp);
76 static void rt_gpsd_disconnect(VikGpsLayer *vgl);
77 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed);
80 // Shouldn't need to use these much any more as the protocol is now saved as a string.
81 // They are kept for compatibility loading old .vik files
82 typedef enum {GARMIN_P = 0, MAGELLAN_P, DELORME_P, NAVILINK_P, OLD_NUM_PROTOCOLS} vik_gps_proto;
83 static gchar * protocols_args[] = {"garmin", "magellan", "delbin", "navilink", NULL};
85 static gchar * params_ports[] = {"com1", "usb:", NULL};
87 static gchar * params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
89 /* NUM_PORTS not actually used */
90 /* #define NUM_PORTS (sizeof(params_ports)/sizeof(params_ports[0]) - 1) */
91 /* Compatibility with previous versions */
93 static gchar * old_params_ports[] = {"com1", "usb:", NULL};
95 static gchar * old_params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
97 #define OLD_NUM_PORTS (sizeof(old_params_ports)/sizeof(old_params_ports[0]) - 1)
102 gboolean thread_complete;
103 vik_gps_dir direction;
105 gint wpt_total_count;
107 gint trk_total_count;
109 gint rte_total_count;
115 GtkWidget *status_label;
116 GtkWidget *gps_label;
117 GtkWidget *wpt_label;
118 GtkWidget *trk_label;
119 GtkWidget *rte_label;
120 vik_gps_xfer_type progress_type;
124 gint id_status_working;
130 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
131 gboolean realtime_tracking;
134 static void gps_session_delete(GpsSession *sess);
136 static gchar *params_groups[] = {
138 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
139 N_("Realtime Tracking Mode"),
143 enum {GROUP_DATA_MODE, GROUP_REALTIME_MODE};
146 static VikLayerParamData gps_protocol_default ( void )
148 VikLayerParamData data;
149 data.s = g_strdup ( "garmin" );
153 static VikLayerParamData gps_port_default ( void )
155 VikLayerParamData data;
156 data.s = g_strdup ( "usb:" );
158 /* Attempt to auto set default USB serial port entry */
159 /* Ordered to make lowest device favourite if available */
160 if (g_access ("/dev/ttyUSB1", R_OK) == 0) {
162 g_free ( (gchar *)data.s );
163 data.s = g_strdup ("/dev/ttyUSB1");
165 if (g_access ("/dev/ttyUSB0", R_OK) == 0) {
167 g_free ( (gchar *)data.s );
168 data.s = g_strdup ("/dev/ttyUSB0");
174 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
175 static gchar *params_vehicle_position[] = {
176 N_("Keep vehicle at center"),
177 N_("Keep vehicle on screen"),
182 VEHICLE_POSITION_CENTERED = 0,
183 VEHICLE_POSITION_ON_SCREEN,
184 VEHICLE_POSITION_NONE,
187 static VikLayerParamData moving_map_method_default ( void ) { return VIK_LPD_UINT ( VEHICLE_POSITION_ON_SCREEN ); }
189 static VikLayerParamData gpsd_host_default ( void )
191 VikLayerParamData data;
192 data.s = g_strdup ( "localhost" );
196 static VikLayerParamData gpsd_port_default ( void )
198 VikLayerParamData data;
199 data.s = g_strdup ( DEFAULT_GPSD_PORT );
203 static VikLayerParamData gpsd_retry_interval_default ( void )
205 VikLayerParamData data;
206 data.s = g_strdup ( "10" );
212 static VikLayerParam gps_layer_params[] = {
213 // NB gps_layer_inst_init() is performed after parameter registeration
214 // thus to give the protocols some potential values use the old static list
215 // TODO: find another way to use gps_layer_inst_init()?
216 { VIK_LAYER_GPS, "gps_protocol", VIK_LAYER_PARAM_STRING, GROUP_DATA_MODE, N_("GPS Protocol:"), VIK_LAYER_WIDGET_COMBOBOX, protocols_args, NULL, NULL, gps_protocol_default, NULL, NULL }, // List reassigned at runtime
217 { VIK_LAYER_GPS, "gps_port", VIK_LAYER_PARAM_STRING, GROUP_DATA_MODE, N_("Serial Port:"), VIK_LAYER_WIDGET_COMBOBOX, params_ports, NULL, NULL, gps_port_default, NULL, NULL },
218 { VIK_LAYER_GPS, "gps_download_tracks", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Download Tracks:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
219 { VIK_LAYER_GPS, "gps_upload_tracks", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Upload Tracks:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
220 { VIK_LAYER_GPS, "gps_download_routes", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Download Routes:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
221 { VIK_LAYER_GPS, "gps_upload_routes", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Upload Routes:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
222 { VIK_LAYER_GPS, "gps_download_waypoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Download Waypoints:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
223 { VIK_LAYER_GPS, "gps_upload_waypoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_DATA_MODE, N_("Upload Waypoints:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
224 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
225 { VIK_LAYER_GPS, "record_tracking", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Recording tracks"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
226 { VIK_LAYER_GPS, "center_start_tracking", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Jump to current position on start"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
227 { VIK_LAYER_GPS, "moving_map_method", VIK_LAYER_PARAM_UINT, GROUP_REALTIME_MODE, N_("Moving Map Method:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_vehicle_position, NULL, NULL, moving_map_method_default, NULL, NULL },
228 { VIK_LAYER_GPS, "realtime_update_statusbar", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Update Statusbar:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, N_("Display information in the statusbar on GPS updates"), vik_lpd_true_default, NULL, NULL },
229 { VIK_LAYER_GPS, "auto_connect", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Auto Connect"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, N_("Automatically connect to GPSD"), vik_lpd_false_default, NULL, NULL },
230 { VIK_LAYER_GPS, "gpsd_host", VIK_LAYER_PARAM_STRING, GROUP_REALTIME_MODE, N_("Gpsd Host:"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, gpsd_host_default, NULL, NULL },
231 { VIK_LAYER_GPS, "gpsd_port", VIK_LAYER_PARAM_STRING, GROUP_REALTIME_MODE, N_("Gpsd Port:"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, gpsd_port_default, NULL, NULL },
232 { VIK_LAYER_GPS, "gpsd_retry_interval", VIK_LAYER_PARAM_STRING, GROUP_REALTIME_MODE, N_("Gpsd Retry Interval (seconds):"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, gpsd_retry_interval_default, NULL, NULL },
233 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
236 PARAM_PROTOCOL=0, PARAM_PORT,
237 PARAM_DOWNLOAD_TRACKS, PARAM_UPLOAD_TRACKS,
238 PARAM_DOWNLOAD_ROUTES, PARAM_UPLOAD_ROUTES,
239 PARAM_DOWNLOAD_WAYPOINTS, PARAM_UPLOAD_WAYPOINTS,
240 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
242 PARAM_REALTIME_CENTER_START,
243 PARAM_VEHICLE_POSITION,
244 PARAM_REALTIME_UPDATE_STATUSBAR,
248 PARAM_GPSD_RETRY_INTERVAL,
249 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
252 VikLayerInterface vik_gps_layer_interface = {
264 sizeof(params_groups)/sizeof(params_groups[0]),
268 (VikLayerFuncCreate) vik_gps_layer_create,
269 (VikLayerFuncRealize) vik_gps_layer_realize,
270 (VikLayerFuncPostRead) vik_gps_layer_post_read,
271 (VikLayerFuncFree) vik_gps_layer_free,
273 (VikLayerFuncProperties) NULL,
274 (VikLayerFuncDraw) vik_gps_layer_draw,
275 (VikLayerFuncChangeCoordMode) gps_layer_change_coord_mode,
277 (VikLayerFuncGetTimestamp) NULL,
279 (VikLayerFuncSetMenuItemsSelection) NULL,
280 (VikLayerFuncGetMenuItemsSelection) NULL,
282 (VikLayerFuncAddMenuItems) gps_layer_add_menu_items,
283 (VikLayerFuncSublayerAddMenuItems) NULL,
285 (VikLayerFuncSublayerRenameRequest) NULL,
286 (VikLayerFuncSublayerToggleVisible) NULL,
287 (VikLayerFuncSublayerTooltip) NULL,
288 (VikLayerFuncLayerTooltip) gps_layer_tooltip,
289 (VikLayerFuncLayerSelected) NULL,
291 (VikLayerFuncMarshall) gps_layer_marshall,
292 (VikLayerFuncUnmarshall) gps_layer_unmarshall,
294 (VikLayerFuncSetParam) gps_layer_set_param,
295 (VikLayerFuncGetParam) gps_layer_get_param,
296 (VikLayerFuncChangeParam) NULL,
298 (VikLayerFuncReadFileData) NULL,
299 (VikLayerFuncWriteFileData) NULL,
301 (VikLayerFuncDeleteItem) NULL,
302 (VikLayerFuncCutItem) NULL,
303 (VikLayerFuncCopyItem) NULL,
304 (VikLayerFuncPasteItem) NULL,
305 (VikLayerFuncFreeCopiedItem) NULL,
306 (VikLayerFuncDragDropRequest) NULL,
308 (VikLayerFuncSelectClick) NULL,
309 (VikLayerFuncSelectMove) NULL,
310 (VikLayerFuncSelectRelease) NULL,
311 (VikLayerFuncSelectedViewportMenu) NULL,
314 enum {TRW_DOWNLOAD=0, TRW_UPLOAD,
315 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
319 static gchar * trw_names[] = {
320 N_("GPS Download"), N_("GPS Upload"),
321 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
322 N_("GPS Realtime Tracking"),
326 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
328 struct gps_data_t gpsd;
334 struct gps_fix_t fix;
335 gint satellites_used;
336 gboolean dirty; /* needs to be saved */
338 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
340 struct _VikGpsLayer {
342 VikTrwLayer * trw_children[NUM_TRW];
343 GList * children; /* used only for writing file */
344 int cur_read_child; /* used only for reading file */
345 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
347 gboolean connected_to_gpsd;
348 gboolean realtime_tracking;
349 gboolean first_realtime_trackpoint;
353 VikTrack *realtime_track;
355 GIOChannel *realtime_io_channel;
356 guint realtime_io_watch_id;
357 guint realtime_retry_timer;
358 GdkGC *realtime_track_gc;
359 GdkGC *realtime_track_bg_gc;
360 GdkGC *realtime_track_pt_gc;
361 GdkGC *realtime_track_pt1_gc;
362 GdkGC *realtime_track_pt2_gc;
365 gboolean auto_connect_to_gpsd;
368 gint gpsd_retry_interval;
369 gboolean realtime_record;
370 gboolean realtime_jump_to_start;
371 guint vehicle_position;
372 gboolean realtime_update_statusbar;
373 VikTrackpoint *trkpt;
374 VikTrackpoint *trkpt_prev;
375 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
378 gboolean download_tracks;
379 gboolean download_routes;
380 gboolean download_waypoints;
381 gboolean upload_tracks;
382 gboolean upload_routes;
383 gboolean upload_waypoints;
387 * Overwrite the static setup with dynamically generated GPS Babel device list
389 static void gps_layer_inst_init ( VikGpsLayer *self )
392 // +1 for luck (i.e the NULL terminator)
393 gchar **new_protocols = g_malloc_n(1 + g_list_length(a_babel_device_list), sizeof(gpointer));
395 GList *gl = g_list_first ( a_babel_device_list );
397 // should be using label property but use name for now
398 // thus don't need to mess around converting label to name later on
399 new_protocols[new_proto++] = ((BabelDevice*)gl->data)->name;
400 gl = g_list_next ( gl );
402 new_protocols[new_proto] = NULL;
404 vik_gps_layer_interface.params[PARAM_PROTOCOL].widget_data = new_protocols;
407 GType vik_gps_layer_get_type ()
409 static GType val_type = 0;
413 static const GTypeInfo val_info =
415 sizeof (VikGpsLayerClass),
416 NULL, /* base_init */
417 NULL, /* base_finalize */
418 NULL, /* class init */
419 NULL, /* class_finalize */
420 NULL, /* class_data */
421 sizeof (VikGpsLayer),
423 (GInstanceInitFunc) gps_layer_inst_init,
425 val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGpsLayer", &val_info, 0 );
431 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp)
435 VikGpsLayer *rv = vik_gps_layer_new (vp);
436 vik_layer_rename ( VIK_LAYER(rv), vik_gps_layer_interface.name );
438 for (i = 0; i < NUM_TRW; i++) {
439 rv->trw_children[i] = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vp, FALSE ));
440 vik_layer_set_menu_items_selection(VIK_LAYER(rv->trw_children[i]), VIK_MENU_ITEM_ALL & ~(VIK_MENU_ITEM_CUT|VIK_MENU_ITEM_DELETE));
445 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl )
447 static gchar buf1[256];
449 static gchar rbuf[512];
452 g_snprintf (buf1, sizeof(buf1), "%s:%s", vgl->protocol, vgl->serial_port);
454 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
455 static gchar buf2[256];
457 if ( vgl->connected_to_gpsd )
458 g_snprintf (buf2, sizeof(buf2), "GPSD:%s:%s %s", vgl->gpsd_host, vgl->gpsd_port, _("Connected"));
460 g_snprintf (buf2, sizeof(buf2), "GPSD:%s:%s %s", vgl->gpsd_host, vgl->gpsd_port, _("Disconnected"));
462 g_snprintf (rbuf, sizeof(rbuf), "%s\n%s", buf1, buf2);
464 g_snprintf (rbuf, sizeof(rbuf), "%s", buf1);
470 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *datalen )
472 VikLayer *child_layer;
475 GByteArray* b = g_byte_array_new ();
479 #define alm_append(obj, sz) \
481 g_byte_array_append ( b, (guint8 *)&len, sizeof(len) ); \
482 g_byte_array_append ( b, (guint8 *)(obj), len );
484 vik_layer_marshall_params(VIK_LAYER(vgl), &ld, &ll);
488 for (i = 0; i < NUM_TRW; i++) {
489 child_layer = VIK_LAYER(vgl->trw_children[i]);
490 vik_layer_marshall(child_layer, &ld, &ll);
498 g_byte_array_free(b, FALSE);
503 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
505 #define alm_size (*(gint *)data)
507 len -= sizeof(gint) + alm_size; \
508 data += sizeof(gint) + alm_size;
510 VikGpsLayer *rv = vik_gps_layer_new(vvp);
511 VikLayer *child_layer;
514 vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
518 while (len>0 && i < NUM_TRW) {
519 child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
521 rv->trw_children[i++] = (VikTrwLayer *)child_layer;
522 // NB no need to attach signal update handler here
523 // as this will always be performed later on in vik_gps_layer_realize()
527 // g_print("gps_layer_unmarshall ended with len=%d\n", len);
534 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
540 g_free(vgl->protocol);
541 // Backwards Compatibility: previous versions <v1.4 stored protocol as an array index
542 int index = data.s[0] - '0';
543 if (data.s[0] != '\0' &&
544 g_ascii_isdigit (data.s[0]) &&
546 index < OLD_NUM_PROTOCOLS)
547 // It is a single digit: activate compatibility
548 vgl->protocol = g_strdup(protocols_args[index]);
550 vgl->protocol = g_strdup(data.s);
551 g_debug("%s: %s", __FUNCTION__, vgl->protocol);
554 g_warning(_("Unknown GPS Protocol"));
558 g_free(vgl->serial_port);
559 // Backwards Compatibility: previous versions <v0.9.91 stored serial_port as an array index
560 int index = data.s[0] - '0';
561 if (data.s[0] != '\0' &&
562 g_ascii_isdigit (data.s[0]) &&
564 index < OLD_NUM_PORTS)
565 /* It is a single digit: activate compatibility */
566 vgl->serial_port = g_strdup(old_params_ports[index]);
568 vgl->serial_port = g_strdup(data.s);
569 g_debug("%s: %s", __FUNCTION__, vgl->serial_port);
572 g_warning(_("Unknown serial port device"));
574 case PARAM_DOWNLOAD_TRACKS:
575 vgl->download_tracks = data.b;
577 case PARAM_UPLOAD_TRACKS:
578 vgl->upload_tracks = data.b;
580 case PARAM_DOWNLOAD_ROUTES:
581 vgl->download_routes = data.b;
583 case PARAM_UPLOAD_ROUTES:
584 vgl->upload_routes = data.b;
586 case PARAM_DOWNLOAD_WAYPOINTS:
587 vgl->download_waypoints = data.b;
589 case PARAM_UPLOAD_WAYPOINTS:
590 vgl->upload_waypoints = data.b;
592 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
593 case PARAM_GPSD_CONNECT:
594 vgl->auto_connect_to_gpsd = data.b;
596 case PARAM_GPSD_HOST:
599 g_free(vgl->gpsd_host);
600 vgl->gpsd_host = g_strdup(data.s);
603 case PARAM_GPSD_PORT:
606 g_free(vgl->gpsd_port);
607 vgl->gpsd_port = g_strdup(data.s);
610 case PARAM_GPSD_RETRY_INTERVAL:
611 vgl->gpsd_retry_interval = strtol(data.s, NULL, 10);
613 case PARAM_REALTIME_REC:
614 vgl->realtime_record = data.b;
616 case PARAM_REALTIME_CENTER_START:
617 vgl->realtime_jump_to_start = data.b;
619 case PARAM_VEHICLE_POSITION:
620 vgl->vehicle_position = data.u;
622 case PARAM_REALTIME_UPDATE_STATUSBAR:
623 vgl->realtime_update_statusbar = data.b;
625 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
627 g_warning("gps_layer_set_param(): unknown parameter");
633 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation )
635 VikLayerParamData rv;
639 rv.s = vgl->protocol;
640 g_debug("%s: %s", __FUNCTION__, rv.s);
643 rv.s = vgl->serial_port;
644 g_debug("%s: %s", __FUNCTION__, rv.s);
646 case PARAM_DOWNLOAD_TRACKS:
647 rv.b = vgl->download_tracks;
649 case PARAM_UPLOAD_TRACKS:
650 rv.b = vgl->upload_tracks;
652 case PARAM_DOWNLOAD_ROUTES:
653 rv.b = vgl->download_routes;
655 case PARAM_UPLOAD_ROUTES:
656 rv.b = vgl->upload_routes;
658 case PARAM_DOWNLOAD_WAYPOINTS:
659 rv.b = vgl->download_waypoints;
661 case PARAM_UPLOAD_WAYPOINTS:
662 rv.b = vgl->upload_waypoints;
664 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
665 case PARAM_GPSD_CONNECT:
666 rv.b = vgl->auto_connect_to_gpsd;
668 case PARAM_GPSD_HOST:
669 rv.s = vgl->gpsd_host ? vgl->gpsd_host : "";
671 case PARAM_GPSD_PORT:
672 rv.s = vgl->gpsd_port ? vgl->gpsd_port : g_strdup(DEFAULT_GPSD_PORT);
674 case PARAM_GPSD_RETRY_INTERVAL:
675 rv.s = g_strdup_printf("%d", vgl->gpsd_retry_interval);
677 case PARAM_REALTIME_REC:
678 rv.b = vgl->realtime_record;
680 case PARAM_REALTIME_CENTER_START:
681 rv.b = vgl->realtime_jump_to_start;
683 case PARAM_VEHICLE_POSITION:
684 rv.u = vgl->vehicle_position;
686 case PARAM_REALTIME_UPDATE_STATUSBAR:
687 rv.u = vgl->realtime_update_statusbar;
689 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
691 g_warning(_("%s: unknown parameter"), __FUNCTION__);
697 VikGpsLayer *vik_gps_layer_new (VikViewport *vp)
700 VikGpsLayer *vgl = VIK_GPS_LAYER ( g_object_new ( VIK_GPS_LAYER_TYPE, NULL ) );
701 vik_layer_set_type ( VIK_LAYER(vgl), VIK_LAYER_GPS );
702 for (i = 0; i < NUM_TRW; i++) {
703 vgl->trw_children[i] = NULL;
705 vgl->children = NULL;
706 vgl->cur_read_child = 0;
708 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
709 vgl->first_realtime_trackpoint = FALSE;
711 vgl->trkpt_prev = NULL;
713 vgl->realtime_io_channel = NULL;
714 vgl->realtime_io_watch_id = 0;
715 vgl->realtime_retry_timer = 0;
717 vgl->realtime_track_gc = vik_viewport_new_gc ( vp, "#203070", 2 );
718 vgl->realtime_track_bg_gc = vik_viewport_new_gc ( vp, "grey", 2 );
719 vgl->realtime_track_pt1_gc = vik_viewport_new_gc ( vp, "red", 2 );
720 vgl->realtime_track_pt2_gc = vik_viewport_new_gc ( vp, "green", 2 );
721 vgl->realtime_track_pt_gc = vgl->realtime_track_pt1_gc;
723 vgl->realtime_track = NULL;
724 #endif // VIK_CONFIG_REALTIME_GPS_TRACKING
726 vik_layer_set_defaults ( VIK_LAYER(vgl), vp );
731 static void vik_gps_layer_post_read ( VikGpsLayer *vgl, VikViewport *vvp, gboolean from_file )
733 for (guint i = 0; i < NUM_TRW; i++) {
734 trw_layer_calculate_bounds_waypoints ( vgl->trw_children[i] );
735 trw_layer_calculate_bounds_tracks ( vgl->trw_children[i] );
739 static void vik_gps_layer_draw ( VikGpsLayer *vgl, VikViewport *vp )
743 VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( vp ));
745 for (i = 0; i < NUM_TRW; i++) {
746 vl = VIK_LAYER(vgl->trw_children[i]);
748 if ( vik_viewport_get_half_drawn ( vp ) ) {
749 vik_viewport_set_half_drawn ( vp, FALSE );
750 vik_viewport_snapshot_load( vp );
752 vik_viewport_snapshot_save( vp );
755 if (!vik_viewport_get_half_drawn(vp))
756 vik_layer_draw ( vl, vp );
758 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
759 if (vgl->realtime_tracking) {
760 if (VIK_LAYER(vgl) == trigger) {
761 if ( vik_viewport_get_half_drawn ( vp ) ) {
762 vik_viewport_set_half_drawn ( vp, FALSE );
763 vik_viewport_snapshot_load( vp );
765 vik_viewport_snapshot_save( vp );
768 if (!vik_viewport_get_half_drawn(vp))
769 realtime_tracking_draw(vgl, vp);
771 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
774 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode )
777 for (i = 0; i < NUM_TRW; i++) {
778 vik_layer_change_coord_mode(VIK_LAYER(vgl->trw_children[i]), mode);
782 static void gps_layer_add_menu_items( VikGpsLayer *vgl, GtkMenu *menu, gpointer vlp )
784 static gpointer pass_along[2];
789 item = gtk_menu_item_new();
790 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
791 gtk_widget_show ( item );
794 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS") );
795 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
796 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_upload_cb), pass_along );
797 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
798 gtk_widget_show ( item );
800 item = gtk_image_menu_item_new_with_mnemonic ( _("Download from _GPS") );
801 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
802 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_download_cb), pass_along );
803 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
804 gtk_widget_show ( item );
806 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
807 item = gtk_image_menu_item_new_with_mnemonic ( vgl->realtime_tracking ?
808 "_Stop Realtime Tracking" :
809 "_Start Realtime Tracking" );
810 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, vgl->realtime_tracking ?
811 gtk_image_new_from_stock (GTK_STOCK_MEDIA_STOP, GTK_ICON_SIZE_MENU) :
812 gtk_image_new_from_stock (GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_MENU) );
813 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_start_stop_tracking_cb), pass_along );
814 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
815 gtk_widget_show ( item );
817 item = gtk_menu_item_new();
818 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
819 gtk_widget_show ( item );
821 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _Realtime") );
822 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
823 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_realtime_cb), pass_along );
824 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
825 gtk_widget_show ( item );
826 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
828 item = gtk_image_menu_item_new_with_mnemonic ( _("E_mpty Upload") );
829 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
830 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_upload_cb), pass_along );
831 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
832 gtk_widget_show ( item );
834 item = gtk_image_menu_item_new_with_mnemonic ( _("_Empty Download") );
835 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
836 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_download_cb), pass_along );
837 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
838 gtk_widget_show ( item );
840 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _All") );
841 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
842 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_all_cb), pass_along );
843 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
844 gtk_widget_show ( item );
848 static void disconnect_layer_signal ( VikLayer *vl, VikGpsLayer *vgl )
850 guint number_handlers = g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, vgl);
851 if ( number_handlers != 1 ) {
852 g_critical(_("Unexpected number of disconnected handlers: %d"), number_handlers);
856 static void vik_gps_layer_free ( VikGpsLayer *vgl )
859 for (i = 0; i < NUM_TRW; i++) {
860 if (vgl->vl.realized)
861 disconnect_layer_signal(VIK_LAYER(vgl->trw_children[i]), vgl);
862 g_object_unref(vgl->trw_children[i]);
864 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
865 rt_gpsd_disconnect(vgl);
866 if (vgl->realtime_track_gc != NULL)
867 g_object_unref(vgl->realtime_track_gc);
868 if (vgl->realtime_track_bg_gc != NULL)
869 g_object_unref(vgl->realtime_track_bg_gc);
870 if (vgl->realtime_track_pt1_gc != NULL)
871 g_object_unref(vgl->realtime_track_pt1_gc);
872 if (vgl->realtime_track_pt2_gc != NULL)
873 g_object_unref(vgl->realtime_track_pt2_gc);
874 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
877 static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter )
882 // TODO set to garmin by default
883 //if (a_babel_device_list)
884 // device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
885 // Need to access uibuild widgets somehow....
887 for (ix = 0; ix < NUM_TRW; ix++) {
888 VikLayer * trw = VIK_LAYER(vgl->trw_children[ix]);
889 vik_treeview_add_layer ( VIK_LAYER(vgl)->vt, layer_iter, &iter,
890 _(trw_names[ix]), vgl, TRUE,
891 trw, trw->type, trw->type, vik_layer_get_timestamp(trw) );
892 if ( ! trw->visible )
893 vik_treeview_item_set_visible ( VIK_LAYER(vgl)->vt, &iter, FALSE );
894 vik_layer_realize ( trw, VIK_LAYER(vgl)->vt, &iter );
895 g_signal_connect_swapped ( G_OBJECT(trw), "update", G_CALLBACK(vik_layer_emit_update_secondary), vgl );
898 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
899 if ( vgl->auto_connect_to_gpsd ) {
900 vgl->realtime_tracking = TRUE;
901 vgl->first_realtime_trackpoint = TRUE;
902 (void)rt_gpsd_connect ( vgl, FALSE );
904 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
907 const GList *vik_gps_layer_get_children ( VikGpsLayer *vgl )
911 if (vgl->children == NULL) {
912 for (i = NUM_TRW - 1; i >= 0; i--)
913 vgl->children = g_list_prepend(vgl->children, vgl->trw_children[i]);
915 return vgl->children;
918 VikTrwLayer * vik_gps_layer_get_a_child(VikGpsLayer *vgl)
920 g_assert ((vgl->cur_read_child >= 0) && (vgl->cur_read_child < NUM_TRW));
922 VikTrwLayer * vtl = vgl->trw_children[vgl->cur_read_child];
923 if (++(vgl->cur_read_child) >= NUM_TRW)
924 vgl->cur_read_child = 0;
928 gboolean vik_gps_layer_is_empty ( VikGpsLayer *vgl )
930 if ( vgl->trw_children[0] )
935 static void gps_session_delete(GpsSession *sess)
937 vik_mutex_free(sess->mutex);
938 // Remove any outstanding GUI update requests
939 if ( sess->id_status_working )
940 g_source_remove ( sess->id_status_working );
941 if ( sess->id_status_end )
942 g_source_remove ( sess->id_status_end );
944 g_source_remove ( sess->id_info );
945 if ( sess->id_total_count )
946 g_source_remove ( sess->id_total_count );
947 if ( sess->id_count )
948 g_source_remove ( sess->id_count );
949 g_free(sess->babelargs);
955 static gboolean show_total_count(GpsSession *sess)
957 g_mutex_lock(sess->mutex);
959 const gchar *tmp_str;
961 GtkWidget *progress_label;
962 if (sess->direction == GPS_DOWN)
964 switch (sess->progress_type) {
966 tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", sess->wpt_total_count);
967 tc = sess->wpt_total_count;
968 progress_label = sess->wpt_label;
971 tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", sess->trk_total_count);
972 tc = sess->trk_total_count;
973 progress_label = sess->trk_label;
976 tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", sess->rte_total_count);
977 tc = sess->rte_total_count;
978 progress_label = sess->rte_label;
982 // Maybe a gpsbabel bug/feature (upto at least v1.4.3 or maybe my Garmin device) but the count always seems x2 too many for routepoints
983 gint mycnt = (cnt / 2) + 1;
984 tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt);
993 switch (sess->progress_type) {
995 tmp_str = ngettext("Uploading %d waypoint...", "Uploading %d waypoints...", sess->wpt_total_count);
996 tc = sess->wpt_total_count;
997 progress_label = sess->wpt_label;
1000 tmp_str = ngettext("Uploading %d trackpoint...", "Uploading %d trackpoints...", sess->trk_total_count);
1001 tc = sess->trk_total_count;
1002 progress_label = sess->trk_label;
1005 tmp_str = ngettext("Uploading %d routepoint...", "Uploading %d routepoints...", sess->rte_total_count);
1006 tc = sess->rte_total_count;
1007 progress_label = sess->rte_label;
1013 g_snprintf(s, 128, tmp_str, tc);
1014 gtk_label_set_text ( GTK_LABEL(progress_label), s );
1015 gtk_widget_show ( progress_label );
1017 sess->id_total_count = 0;
1018 g_mutex_unlock(sess->mutex);
1022 static gboolean show_current_count(GpsSession *sess)
1024 g_mutex_lock(sess->mutex);
1027 gint count, total_count;
1028 const gchar *tmp_str;
1029 GtkWidget *progress_label;
1030 if (sess->wpt_count < sess->wpt_total_count) {
1031 if (sess->direction == GPS_DOWN) {
1032 switch (sess->progress_type) {
1034 tmp_str = ngettext("Downloaded %d out of %d waypoint...", "Downloaded %d out of %d waypoints...", sess->wpt_total_count);
1035 count = sess->wpt_count;
1036 total_count = sess->wpt_total_count;
1037 progress_label = sess->wpt_label;
1040 tmp_str = ngettext("Downloaded %d out of %d trackpoint...", "Downloaded %d out of %d trackpoints...", sess->trk_total_count);
1041 count = sess->trk_count;
1042 total_count = sess->trk_total_count;
1043 progress_label = sess->trk_label;
1046 tmp_str = ngettext("Downloaded %d out of %d routepoint...", "Downloaded %d out of %d routepoints...", sess->rte_total_count);
1047 count = sess->rte_count;
1048 total_count = sess->rte_total_count;
1049 progress_label = sess->rte_label;
1054 switch (sess->progress_type) {
1056 tmp_str = ngettext("Uploaded %d out of %d waypoint...", "Uploaded %d out of %d waypoints...", sess->wpt_total_count);
1057 count = sess->wpt_count;
1058 total_count = sess->wpt_total_count;
1059 progress_label = sess->wpt_label;
1062 tmp_str = ngettext("Uploaded %d out of %d trackpoint...", "Uploaded %d out of %d trackpoints...", sess->trk_total_count);
1063 count = sess->trk_count;
1064 total_count = sess->trk_total_count;
1065 progress_label = sess->trk_label;
1068 tmp_str = ngettext("Uploaded %d out of %d routepoint...", "Uploaded %d out of %d routepoints...", sess->rte_total_count);
1069 count = sess->rte_count;
1070 total_count = sess->rte_total_count;
1071 progress_label = sess->rte_label;
1076 if (sess->direction == GPS_DOWN) {
1077 switch (sess->progress_type) {
1079 tmp_str = ngettext("Downloaded %d waypoint", "Downloaded %d waypoints", sess->wpt_count);
1080 count = sess->wpt_count;
1081 total_count = sess->wpt_total_count;
1082 progress_label = sess->wpt_label;
1085 tmp_str = ngettext("Downloaded %d trackpoint", "Downloaded %d trackpoints", sess->trk_count);
1086 count = sess->trk_count;
1087 total_count = sess->trk_total_count;
1088 progress_label = sess->trk_label;
1091 tmp_str = ngettext("Downloaded %d routepoint", "Downloaded %d routepoints", sess->rte_count);
1092 count = sess->rte_count;
1093 total_count = sess->rte_total_count;
1094 progress_label = sess->rte_label;
1099 switch (sess->progress_type) {
1101 tmp_str = ngettext("Uploaded %d waypoint", "Uploaded %d waypoints", sess->wpt_count);
1102 count = sess->wpt_count;
1103 total_count = sess->wpt_total_count;
1104 progress_label = sess->wpt_label;
1107 tmp_str = ngettext("Uploaded %d trackpoint", "Uploaded %d trackpoints", sess->trk_count);
1108 count = sess->trk_count;
1109 total_count = sess->trk_total_count;
1110 progress_label = sess->trk_label;
1113 tmp_str = ngettext("Uploaded %d routepoint", "Uploaded %d routepoints", sess->rte_count);
1114 count = sess->rte_count;
1115 total_count = sess->rte_total_count;
1116 progress_label = sess->rte_label;
1121 g_snprintf(s, 128, tmp_str, count, total_count);
1122 gtk_label_set_text ( GTK_LABEL(progress_label), s );
1125 g_mutex_unlock(sess->mutex);
1129 static gboolean show_gps_info(GpsSession *sess)
1131 g_mutex_lock(sess->mutex);
1134 g_snprintf(s, 256, _("GPS Device: %s"), sess->info);
1135 gtk_label_set_text ( GTK_LABEL(sess->gps_label), s );
1138 g_mutex_unlock(sess->mutex);
1143 * Common processing for GPS Device information
1144 * It doesn't matter whether we're uploading or downloading
1146 static void process_line_for_gps_info ( const gchar *line, GpsSession *sess )
1148 if (strstr(line, "PRDDAT")) {
1149 gchar **tokens = g_strsplit(line, " ", 0);
1155 while (tokens[n_tokens])
1158 // I'm not entirely clear what information this is trying to get...
1159 // Obviously trying to decipher some kind of text/naming scheme
1160 // Anyway this will be superceded if there is 'Unit:' information
1162 for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
1164 sscanf(tokens[i], "%x", &ch);
1168 sess->info = g_strdup (info);
1169 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1174 /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
1175 if (strstr(line, "Unit:")) {
1176 gchar **tokens = g_strsplit(line, "\t", 0);
1178 while (tokens[n_tokens])
1182 sess->info = g_strdup (tokens[1]);
1183 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1188 if (strstr(line, "[ERROR] GPS")) {
1189 gchar **tokens = g_strsplit(line, "\n", 0);
1190 sess->info = g_strdup(tokens[0]);
1191 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1195 if (strstr(line, "an't in")) {
1196 gchar **tokens = g_strsplit(line, "\n", 0);
1197 sess->info = g_strdup(tokens[0]);
1198 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1202 if (strstr(line, "Can't get waypoint")) {
1203 gchar **tokens = g_strsplit(line, "\n", 0);
1204 sess->info = g_strdup(tokens[0]);
1205 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1210 static gboolean show_gps_status_working ( GpsSession *sess )
1212 g_mutex_lock(sess->mutex);
1214 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1216 sess->id_status_working = 0;
1217 g_mutex_unlock(sess->mutex);
1221 static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1226 //gps_session_delete(sess);
1227 sess->thread_complete = TRUE;
1228 g_debug ("THREAD EXIT INTERUPPT");
1229 g_thread_exit ( NULL );
1233 case BABEL_DIAG_OUTPUT:
1234 line = (gchar *)data;
1236 sess->id_status_working = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_working, sess );
1238 /* tells us the type of items that will follow */
1239 if (strstr(line, "Xfer Wpt")) {
1240 sess->progress_type = WPT;
1242 if (strstr(line, "Xfer Trk")) {
1243 sess->progress_type = TRK;
1245 if (strstr(line, "Xfer Rte")) {
1246 sess->progress_type = RTE;
1249 process_line_for_gps_info ( line, sess );
1251 if (strstr(line, "RECORD")) {
1254 if (strlen(line) > 20) {
1255 sscanf(line+17, "%x", &lsb);
1256 sscanf(line+20, "%x", &msb);
1257 cnt = lsb + msb * 256;
1258 if ( sess->progress_type == RTE ) {
1259 // Maybe a gpsbabel bug/feature (upto at least v1.4.3 or maybe my Garmin device) but the count always seems x2 too many for routepoints
1260 gint mycnt = (cnt / 2) + 1;
1261 sess->rte_total_count = mycnt;
1262 sess->rte_count = 0;
1264 else if ( sess->progress_type == WPT ) {
1265 sess->wpt_total_count = cnt;
1266 sess->wpt_count = 0;
1269 sess->trk_total_count = cnt;
1270 sess->trk_count = 0;
1272 sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess);
1275 if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1276 if ( strstr(line, "WPTDAT") )
1278 else if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") )
1281 // "RTEHDR" || "RTEWPT"
1283 sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess);
1293 static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1299 //gps_session_delete(sess);
1300 sess->thread_complete = TRUE;
1301 g_thread_exit ( NULL );
1305 case BABEL_DIAG_OUTPUT:
1306 line = (gchar *)data;
1308 sess->id_status_working = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_working, sess );
1310 process_line_for_gps_info ( line, sess );
1312 if (strstr(line, "RECORD")) {
1315 if (strlen(line) > 20) {
1316 sscanf(line+17, "%x", &lsb);
1317 sscanf(line+20, "%x", &msb);
1318 cnt = lsb + msb * 256;
1319 //sess->count = 0; ?? wpt, trk and/or rte?? or none
1322 if ( strstr(line, "WPTDAT")) {
1323 sess->progress_type = WPT;
1324 if (sess->wpt_count == 0) {
1325 sess->wpt_total_count = cnt;
1326 sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess );
1329 sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess );
1331 if ( strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1332 sess->progress_type = RTE;
1333 if (sess->rte_count == 0) {
1334 // Maybe a gpsbabel bug/feature (upto at least v1.4.3 or maybe my Garmin device) but the count always seems x2 too many for routepoints
1335 // Anyway since we're uploading - we should know how many points we're going to put!
1336 cnt = (cnt / 2) + 1;
1337 sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess);
1340 sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess);
1342 if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
1343 sess->progress_type = TRK;
1344 if (sess->trk_count == 0) {
1345 sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess);
1348 sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess);
1358 static gboolean show_gps_status_end ( GpsSession *sess )
1360 g_mutex_lock(sess->mutex);
1361 // (Download)Failure could be due to a number of reasons: such as no/wrong device attached or GPSBabel not installed
1362 if (!sess->result) {
1363 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: No result.") );
1367 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Done.") );
1368 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE );
1369 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE );
1372 sess->id_status_end = 0;
1373 g_mutex_unlock(sess->mutex);
1380 static void gps_comm_thread(GpsSession *sess)
1382 if (sess->direction == GPS_DOWN) {
1383 ProcessOptions po = { sess->babelargs, sess->port, NULL, NULL, NULL, NULL };
1384 sess->result = a_babel_convert_from (sess->vtl, &po, (BabelStatusFunc) gps_download_progress_func, sess, NULL);
1387 sess->result = a_babel_convert_to (sess->vtl, sess->track, sess->babelargs, sess->port,
1388 (BabelStatusFunc) gps_upload_progress_func, sess);
1391 sess->id_status_end = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_end, sess );
1395 /* Do not change the view if we are following the current GPS position */
1396 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1397 if (!sess->realtime_tracking)
1400 if ( sess->vvp && sess->direction == GPS_DOWN ) {
1401 vik_layer_post_read ( VIK_LAYER(sess->vtl), sess->vvp, TRUE );
1402 /* View the data available */
1403 vik_trw_layer_auto_set_view ( sess->vtl, sess->vvp ) ;
1404 vik_layer_emit_update ( VIK_LAYER(sess->vtl) ); // NB update request from background thread
1413 // Thread has completed successfully, but now set to false to avoid 'tell thread to stop' test after dialog run
1417 sess->thread_complete = TRUE;
1418 g_thread_exit(NULL);
1423 * @vtl: The TrackWaypoint layer to operate on
1424 * @track: Operate on a particular track when specified
1425 * @dir: The direction of the transfer
1426 * @protocol: The GPS device communication protocol
1427 * @port: The GPS serial port
1428 * @tracking: If tracking then viewport display update will be skipped
1429 * @vvp: A viewport is required as the display may get updated
1430 * @vlp: A layers panel is needed for uploading as the items maybe modified
1431 * @do_tracks: Whether tracks shoud be processed
1432 * @do_waypoints: Whether waypoints shoud be processed
1433 * @turn_off: Whether we should attempt to turn off the GPS device after the transfer (only some devices support this)
1435 * Talk to a GPS Device using a thread which updates a dialog with the progress
1437 gint vik_gps_comm ( VikTrwLayer *vtl,
1444 VikLayersPanel *vlp,
1447 gboolean do_waypoints,
1450 GpsSession *sess = g_malloc0(sizeof(GpsSession));
1451 char *tracks = NULL;
1452 char *routes = NULL;
1453 char *waypoints = NULL;
1455 sess->mutex = vik_mutex_new();
1456 sess->direction = dir;
1458 sess->track = track;
1459 sess->port = g_strdup(port);
1463 // This must be done inside the main thread as the uniquify causes screen updates
1464 // (originally performed this nearer the point of upload in the thread)
1465 if ( dir == GPS_UP ) {
1466 // Enforce unique names in the layer upload to the GPS device
1467 // NB this may only be a Garmin device restriction (and may be not every Garmin device either...)
1468 // Thus this maintains the older code in built restriction
1469 if ( ! vik_trw_layer_uniquify ( sess->vtl, vlp ) )
1470 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(sess->vtl))), VIK_STATUSBAR_INFO,
1471 _("Warning - GPS Upload items may overwrite each other") );
1474 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1475 sess->realtime_tracking = tracking;
1491 sess->babelargs = g_strdup_printf("-D 9 %s %s %s -%c %s",
1492 tracks, routes, waypoints, (dir == GPS_DOWN) ? 'i' : 'o', protocol);
1496 GtkWidget *dialog = NULL;
1498 // Only create dialog if we're going to do some transferring
1499 if ( do_tracks || do_waypoints || do_routes ) {
1500 dialog = gtk_dialog_new_with_buttons ( "", VIK_GTK_WINDOW_FROM_LAYER(vtl), 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL );
1501 sess->dialog = dialog;
1502 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog),
1503 GTK_RESPONSE_ACCEPT, FALSE );
1504 gtk_window_set_title ( GTK_WINDOW(sess->dialog), (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload") );
1506 sess->status_label = gtk_label_new (_("Status: detecting gpsbabel"));
1507 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->status_label, FALSE, FALSE, 5 );
1509 sess->gps_label = gtk_label_new (_("GPS device: N/A"));
1510 sess->wpt_label = gtk_label_new ("");
1511 sess->trk_label = gtk_label_new ("");
1512 sess->rte_label = gtk_label_new ("");
1514 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->gps_label, FALSE, FALSE, 5 );
1515 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->wpt_label, FALSE, FALSE, 5 );
1516 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->trk_label, FALSE, FALSE, 5 );
1517 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->rte_label, FALSE, FALSE, 5 );
1519 gtk_widget_show_all(sess->dialog);
1521 gtk_dialog_set_default_response ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT );
1522 // Starting gps read/write thread
1523 #if GLIB_CHECK_VERSION (2, 32, 0)
1524 g_thread_try_new ( "gps_comm_thread", (GThreadFunc)gps_comm_thread, sess, NULL );
1526 g_thread_create ( (GThreadFunc)gps_comm_thread, sess, FALSE, NULL );
1529 gtk_dialog_run(GTK_DIALOG(dialog));
1533 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No GPS items selected for transfer.") );
1537 sess->ok = FALSE; /* tell thread to stop */
1541 // No need for thread for powering off device (should be quick operation...) - so use babel command directly:
1542 gchar *device_off = g_strdup_printf("-i %s,%s", protocol, "power_off");
1543 ProcessOptions po = { device_off, port, NULL, NULL, NULL, NULL };
1544 gboolean result = a_babel_convert_from (NULL, &po, NULL, NULL, NULL);
1546 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not turn off device.") );
1547 g_free ( device_off );
1552 while ( !sess->thread_complete ) {
1553 g_usleep (G_USEC_PER_SEC/10);
1555 gtk_widget_destroy(dialog);
1557 gps_session_delete(sess);
1561 static void gps_upload_cb( gpointer layer_and_vlp[2] )
1563 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1564 VikLayersPanel *vlp = VIK_LAYERS_PANEL(layer_and_vlp[1]);
1565 VikTrwLayer *vtl = vgl->trw_children[TRW_UPLOAD];
1566 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1567 VikViewport *vvp = vik_window_viewport(vw);
1568 vik_gps_comm(vtl, NULL, GPS_UP, vgl->protocol, vgl->serial_port, FALSE, vvp, vlp, vgl->upload_tracks, vgl->upload_routes, vgl->upload_waypoints, FALSE);
1571 static void gps_download_cb( gpointer layer_and_vlp[2] )
1573 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1574 VikTrwLayer *vtl = vgl->trw_children[TRW_DOWNLOAD];
1575 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1576 VikViewport *vvp = vik_window_viewport(vw);
1577 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1578 vik_gps_comm(vtl, NULL, GPS_DOWN, vgl->protocol, vgl->serial_port, vgl->realtime_tracking, vvp, NULL, vgl->download_tracks, vgl->download_routes, vgl->download_waypoints, FALSE);
1580 vik_gps_comm(vtl, NULL, GPS_DOWN, vgl->protocol, vgl->serial_port, FALSE, vvp, NULL, vgl->download_tracks, vgl->download_routes, vgl->download_waypoints, FALSE);
1584 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] )
1586 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1587 // Get confirmation from the user
1588 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1589 _("Are you sure you want to delete GPS Upload data?"),
1592 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1593 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1594 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1597 static void gps_empty_download_cb( gpointer layer_and_vlp[2] )
1599 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1600 // Get confirmation from the user
1601 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1602 _("Are you sure you want to delete GPS Download data?"),
1605 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1606 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1607 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1610 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1611 static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] )
1613 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1614 // Get confirmation from the user
1615 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1616 _("Are you sure you want to delete GPS Realtime data?"),
1619 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1620 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1621 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_REALTIME]);
1625 static void gps_empty_all_cb( gpointer layer_and_vlp[2] )
1627 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1628 // Get confirmation from the user
1629 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1630 _("Are you sure you want to delete All GPS data?"),
1633 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1634 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1635 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1636 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1637 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1638 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1639 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1640 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1641 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1642 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_REALTIME]);
1646 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1647 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp)
1651 struct LatLon lnw, lse;
1652 vik_viewport_screen_to_coord ( vp, -20, -20, &nw );
1653 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp)+20, vik_viewport_get_width(vp)+20, &se );
1654 vik_coord_to_latlon ( &nw, &lnw );
1655 vik_coord_to_latlon ( &se, &lse );
1656 if ( vgl->realtime_fix.fix.latitude > lse.lat &&
1657 vgl->realtime_fix.fix.latitude < lnw.lat &&
1658 vgl->realtime_fix.fix.longitude > lnw.lon &&
1659 vgl->realtime_fix.fix.longitude < lse.lon &&
1660 !isnan (vgl->realtime_fix.fix.track) ) {
1663 gint half_back_x, half_back_y;
1664 gint half_back_bg_x, half_back_bg_y;
1667 gint side1_x, side1_y, side2_x, side2_y;
1668 gint side1bg_x, side1bg_y, side2bg_x, side2bg_y;
1670 ll.lat = vgl->realtime_fix.fix.latitude;
1671 ll.lon = vgl->realtime_fix.fix.longitude;
1672 vik_coord_load_from_latlon ( &gps, vik_viewport_get_coord_mode(vp), &ll);
1673 vik_viewport_coord_to_screen ( vp, &gps, &x, &y );
1675 gdouble heading_cos = cos(DEG2RAD(vgl->realtime_fix.fix.track));
1676 gdouble heading_sin = sin(DEG2RAD(vgl->realtime_fix.fix.track));
1678 half_back_y = y+8*heading_cos;
1679 half_back_x = x-8*heading_sin;
1680 half_back_bg_y = y+10*heading_cos;
1681 half_back_bg_x = x-10*heading_sin;
1683 pt_y = half_back_y-24*heading_cos;
1684 pt_x = half_back_x+24*heading_sin;
1685 //ptbg_y = half_back_bg_y-28*heading_cos;
1686 ptbg_x = half_back_bg_x+28*heading_sin;
1688 side1_y = half_back_y+9*heading_sin;
1689 side1_x = half_back_x+9*heading_cos;
1690 side1bg_y = half_back_bg_y+11*heading_sin;
1691 side1bg_x = half_back_bg_x+11*heading_cos;
1693 side2_y = half_back_y-9*heading_sin;
1694 side2_x = half_back_x-9*heading_cos;
1695 side2bg_y = half_back_bg_y-11*heading_sin;
1696 side2bg_x = half_back_bg_x-11*heading_cos;
1698 GdkPoint trian[3] = { { pt_x, pt_y }, {side1_x, side1_y}, {side2_x, side2_y} };
1699 GdkPoint trian_bg[3] = { { ptbg_x, pt_y }, {side1bg_x, side1bg_y}, {side2bg_x, side2bg_y} };
1701 vik_viewport_draw_polygon ( vp, vgl->realtime_track_bg_gc, TRUE, trian_bg, 3 );
1702 vik_viewport_draw_polygon ( vp, vgl->realtime_track_gc, TRUE, trian, 3 );
1703 vik_viewport_draw_rectangle ( vp,
1704 (vgl->realtime_fix.fix.mode > MODE_2D) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc,
1705 TRUE, x-2, y-2, 4, 4 );
1706 //vgl->realtime_track_pt_gc = (vgl->realtime_track_pt_gc == vgl->realtime_track_pt1_gc) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc;
1710 static VikTrackpoint* create_realtime_trackpoint(VikGpsLayer *vgl, gboolean forced)
1715 /* Note that fix.time is a double, but it should not affect the precision
1717 time_t cur_timestamp = vgl->realtime_fix.fix.time;
1718 time_t last_timestamp = vgl->last_fix.fix.time;
1720 if (cur_timestamp < last_timestamp) {
1724 if (vgl->realtime_record && vgl->realtime_fix.dirty) {
1725 gboolean replace = FALSE;
1726 int heading = isnan(vgl->realtime_fix.fix.track) ? 0 : (int)floor(vgl->realtime_fix.fix.track);
1727 int last_heading = isnan(vgl->last_fix.fix.track) ? 0 : (int)floor(vgl->last_fix.fix.track);
1728 int alt = isnan(vgl->realtime_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->realtime_fix.fix.altitude);
1729 int last_alt = isnan(vgl->last_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->last_fix.fix.altitude);
1730 if (((last_tp = g_list_last(vgl->realtime_track->trackpoints)) != NULL) &&
1731 (vgl->realtime_fix.fix.mode > MODE_2D) &&
1732 (vgl->last_fix.fix.mode <= MODE_2D) &&
1733 ((cur_timestamp - last_timestamp) < 2)) {
1734 g_free(last_tp->data);
1735 vgl->realtime_track->trackpoints = g_list_delete_link(vgl->realtime_track->trackpoints, last_tp);
1739 ((cur_timestamp != last_timestamp) &&
1741 ((heading < last_heading) && (heading < (last_heading - 3))) ||
1742 ((heading > last_heading) && (heading > (last_heading + 3))) ||
1743 ((alt != VIK_DEFAULT_ALTITUDE) && (alt != last_alt)))))) {
1744 /* TODO: check for new segments */
1745 VikTrackpoint *tp = vik_trackpoint_new();
1746 tp->newsegment = FALSE;
1747 tp->has_timestamp = TRUE;
1748 tp->timestamp = vgl->realtime_fix.fix.time;
1750 /* speed only available for 3D fix. Check for NAN when use this speed */
1751 tp->speed = vgl->realtime_fix.fix.speed;
1752 tp->course = vgl->realtime_fix.fix.track;
1753 tp->nsats = vgl->realtime_fix.satellites_used;
1754 tp->fix_mode = vgl->realtime_fix.fix.mode;
1756 ll.lat = vgl->realtime_fix.fix.latitude;
1757 ll.lon = vgl->realtime_fix.fix.longitude;
1758 vik_coord_load_from_latlon(&tp->coord,
1759 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1761 vik_track_add_trackpoint ( vgl->realtime_track, tp, TRUE ); // Ensure bounds is recalculated
1762 vgl->realtime_fix.dirty = FALSE;
1763 vgl->realtime_fix.satellites_used = 0;
1764 vgl->last_fix = vgl->realtime_fix;
1771 #define VIK_SETTINGS_GPS_STATUSBAR_FORMAT "gps_statusbar_format"
1773 static void update_statusbar ( VikGpsLayer *vgl, VikWindow *vw )
1775 gchar *statusbar_format_code = NULL;
1776 gboolean need2free = FALSE;
1777 if ( !a_settings_get_string ( VIK_SETTINGS_GPS_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
1778 // Otherwise use default
1779 statusbar_format_code = g_strdup ( "GSA" );
1783 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, vgl->trkpt, vgl->trkpt_prev, vgl->realtime_track, vgl->last_fix.fix.climb );
1784 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
1788 g_free ( statusbar_format_code );
1792 static void gpsd_raw_hook(VglGpsd *vgpsd, gchar *data)
1794 gboolean update_all = FALSE;
1795 VikGpsLayer *vgl = vgpsd->vgl;
1797 if (!vgl->realtime_tracking) {
1798 g_warning("%s: receiving GPS data while not in realtime mode", __PRETTY_FUNCTION__);
1802 if ((vgpsd->gpsd.fix.mode >= MODE_2D) &&
1803 !isnan(vgpsd->gpsd.fix.latitude) &&
1804 !isnan(vgpsd->gpsd.fix.longitude)) {
1806 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1807 VikViewport *vvp = vik_window_viewport(vw);
1808 vgl->realtime_fix.fix = vgpsd->gpsd.fix;
1809 vgl->realtime_fix.satellites_used = vgpsd->gpsd.satellites_used;
1810 vgl->realtime_fix.dirty = TRUE;
1813 VikCoord vehicle_coord;
1815 ll.lat = vgl->realtime_fix.fix.latitude;
1816 ll.lon = vgl->realtime_fix.fix.longitude;
1817 vik_coord_load_from_latlon(&vehicle_coord,
1818 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1820 if ((vgl->vehicle_position == VEHICLE_POSITION_CENTERED) ||
1821 (vgl->realtime_jump_to_start && vgl->first_realtime_trackpoint)) {
1822 vik_viewport_set_center_coord(vvp, &vehicle_coord, FALSE);
1825 else if (vgl->vehicle_position == VEHICLE_POSITION_ON_SCREEN) {
1828 const int px = 20; /* adjust ment in pixels to make sure vehicle is inside the box */
1829 gint width = vik_viewport_get_width(vvp);
1830 gint height = vik_viewport_get_height(vvp);
1833 vik_viewport_coord_to_screen(vvp, &vehicle_coord, &vx, &vy);
1835 if (vx < (width/hdiv))
1836 vik_viewport_set_center_screen(vvp, vx - width/2 + width/hdiv + px, vy);
1837 else if (vx > (width - width/hdiv))
1838 vik_viewport_set_center_screen(vvp, vx + width/2 - width/hdiv - px, vy);
1839 else if (vy < (height/vdiv))
1840 vik_viewport_set_center_screen(vvp, vx, vy - height/2 + height/vdiv + px);
1841 else if (vy > (height - height/vdiv))
1842 vik_viewport_set_center_screen(vvp, vx, vy + height/2 - height/vdiv - px);
1847 vgl->first_realtime_trackpoint = FALSE;
1849 vgl->trkpt = create_realtime_trackpoint ( vgl, FALSE );
1852 if ( vgl->realtime_update_statusbar )
1853 update_statusbar ( vgl, vw );
1854 vgl->trkpt_prev = vgl->trkpt;
1857 vik_layer_emit_update ( update_all ? VIK_LAYER(vgl) : VIK_LAYER(vgl->trw_children[TRW_REALTIME]) ); // NB update from background thread
1861 static gboolean gpsd_data_available(GIOChannel *source, GIOCondition condition, gpointer data)
1863 VikGpsLayer *vgl = data;
1864 if (condition == G_IO_IN) {
1865 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1866 if (!gps_poll(&vgl->vgpsd->gpsd)) {
1867 #elif GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1868 if (gps_read(&vgl->vgpsd->gpsd) > -1) {
1869 // Reuse old function to perform operations on the new GPS data
1870 gpsd_raw_hook(vgl->vgpsd, NULL);
1877 g_warning("Disconnected from gpsd. Trying to reconnect");
1878 rt_gpsd_disconnect(vgl);
1879 (void)rt_gpsd_connect(vgl, FALSE);
1882 return FALSE; /* no further calling */
1888 * returns allocated string for a new realtime track name
1890 * free string after use
1892 static gchar *make_track_name(VikTrwLayer *vtl)
1894 const gchar basename[] = "REALTIME";
1895 const gint bufsize = sizeof(basename) + 5;
1896 gchar *name = g_malloc(bufsize);
1897 strcpy(name, basename);
1900 while (vik_trw_layer_get_track(vtl, name) != NULL) {
1901 g_snprintf(name, bufsize, "%s#%d", basename, i);
1907 static gboolean rt_gpsd_try_connect(gpointer *data)
1909 VikGpsLayer *vgl = (VikGpsLayer *)data;
1910 #if GPSD_API_MAJOR_VERSION == 3
1911 struct gps_data_t *gpsd = gps_open(vgl->gpsd_host, vgl->gpsd_port);
1914 #elif GPSD_API_MAJOR_VERSION == 4
1915 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1917 if (gps_open_r(vgl->gpsd_host, vgl->gpsd_port, /*(struct gps_data_t *)*/vgl->vgpsd) != 0) {
1918 #elif GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1919 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1920 vgl->vgpsd->gpsd_open = gps_open ( vgl->gpsd_host, vgl->gpsd_port, &vgl->vgpsd->gpsd );
1921 if ( vgl->vgpsd->gpsd_open != 0 ) {
1923 // Delibrately break compilation...
1925 g_debug("Failed to connect to gpsd at %s (port %s). Will retry in %d seconds",
1926 vgl->gpsd_host, vgl->gpsd_port, vgl->gpsd_retry_interval);
1927 return TRUE; /* keep timer running */
1930 #if GPSD_API_MAJOR_VERSION == 3
1931 vgl->vgpsd = realloc(gpsd, sizeof(VglGpsd));
1933 vgl->vgpsd->vgl = vgl;
1935 vgl->realtime_fix.dirty = vgl->last_fix.dirty = FALSE;
1936 /* track alt/time graph uses VIK_DEFAULT_ALTITUDE (0.0) as invalid */
1937 vgl->realtime_fix.fix.altitude = vgl->last_fix.fix.altitude = VIK_DEFAULT_ALTITUDE;
1938 vgl->realtime_fix.fix.speed = vgl->last_fix.fix.speed = NAN;
1940 if (vgl->realtime_record) {
1941 VikTrwLayer *vtl = vgl->trw_children[TRW_REALTIME];
1942 vgl->realtime_track = vik_track_new();
1943 vgl->realtime_track->visible = TRUE;
1944 gchar *name = make_track_name(vtl);
1945 vik_trw_layer_add_track(vtl, name, vgl->realtime_track);
1949 vgl->connected_to_gpsd = TRUE;
1951 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1952 gps_set_raw_hook(&vgl->vgpsd->gpsd, gpsd_raw_hook);
1955 vgl->realtime_io_channel = g_io_channel_unix_new(vgl->vgpsd->gpsd.gps_fd);
1956 vgl->realtime_io_watch_id = g_io_add_watch( vgl->realtime_io_channel,
1957 G_IO_IN | G_IO_ERR | G_IO_HUP, gpsd_data_available, vgl);
1959 #if GPSD_API_MAJOR_VERSION == 3
1960 gps_query(&vgl->vgpsd->gpsd, "w+x");
1962 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1963 if ( gps_stream(&vgl->vgpsd->gpsd, WATCH_ENABLE, NULL) == -1 )
1964 g_critical ( "gps_stream error" );
1967 return FALSE; /* no longer called by timeout */
1970 static gboolean rt_ask_retry(VikGpsLayer *vgl)
1972 gchar *msg = g_strdup_printf ( _("Failed to connect to gpsd at %s (port %s)\n"
1973 "Should Viking keep trying (every %d seconds)?"),
1974 vgl->gpsd_host, vgl->gpsd_port, vgl->gpsd_retry_interval );
1975 gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vgl), msg, NULL );
1980 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed)
1982 vgl->realtime_retry_timer = 0;
1983 if (rt_gpsd_try_connect((gpointer *)vgl)) {
1984 if (vgl->gpsd_retry_interval <= 0) {
1985 g_warning("Failed to connect to gpsd but will not retry because retry interval was set to %d (which is 0 or negative)", vgl->gpsd_retry_interval);
1988 else if (ask_if_failed && !rt_ask_retry(vgl))
1991 vgl->realtime_retry_timer = g_timeout_add_seconds(vgl->gpsd_retry_interval,
1992 (GSourceFunc)rt_gpsd_try_connect, (gpointer *)vgl);
1997 static void rt_gpsd_disconnect(VikGpsLayer *vgl)
1999 if (vgl->realtime_retry_timer) {
2000 g_source_remove(vgl->realtime_retry_timer);
2001 vgl->realtime_retry_timer = 0;
2003 if (vgl->realtime_io_watch_id) {
2004 g_source_remove(vgl->realtime_io_watch_id);
2005 vgl->realtime_io_watch_id = 0;
2007 if (vgl->realtime_io_channel) {
2008 GError *error = NULL;
2009 g_io_channel_shutdown (vgl->realtime_io_channel, FALSE, &error);
2010 vgl->realtime_io_channel = NULL;
2013 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
2014 gps_stream(&vgl->vgpsd->gpsd, WATCH_DISABLE, NULL);
2016 if ( vgl->vgpsd->gpsd_open == 0 )
2017 (void)gps_close(&vgl->vgpsd->gpsd);
2018 #if GPSD_API_MAJOR_VERSION == 3
2020 #elif GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
2026 if (vgl->realtime_record && vgl->realtime_track) {
2027 if ((vgl->realtime_track->trackpoints == NULL) || (vgl->realtime_track->trackpoints->next == NULL))
2028 vik_trw_layer_delete_track(vgl->trw_children[TRW_REALTIME], vgl->realtime_track);
2029 vgl->realtime_track = NULL;
2031 vgl->connected_to_gpsd = FALSE;
2034 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2])
2036 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
2037 vgl->realtime_tracking = (vgl->realtime_tracking == FALSE);
2039 /* Make sure we are still in the boat with libgps */
2040 g_assert((VIK_GPS_MODE_2D == MODE_2D) && (VIK_GPS_MODE_3D == MODE_3D));
2042 if (vgl->realtime_tracking) {
2043 vgl->first_realtime_trackpoint = TRUE;
2044 if (!rt_gpsd_connect(vgl, TRUE)) {
2045 vgl->first_realtime_trackpoint = FALSE;
2046 vgl->realtime_tracking = FALSE;
2050 else { /* stop realtime tracking */
2051 vgl->first_realtime_trackpoint = FALSE;
2053 rt_gpsd_disconnect(vgl);
2056 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */