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"
42 #include <glib/gstdio.h>
43 #include <glib/gprintf.h>
44 #include <glib/gi18n.h>
45 #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 );
56 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *len );
57 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
58 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
59 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation );
61 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl );
63 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode );
64 static void gps_layer_add_menu_items( VikGpsLayer *vtl, GtkMenu *menu, gpointer vlp );
66 static void gps_upload_cb( gpointer layer_and_vlp[2] );
67 static void gps_download_cb( gpointer layer_and_vlp[2] );
68 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] );
69 static void gps_empty_download_cb( gpointer layer_and_vlp[2] );
70 static void gps_empty_all_cb( gpointer layer_and_vlp[2] );
71 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
72 static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] );
73 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2] );
74 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp);
75 static void rt_gpsd_disconnect(VikGpsLayer *vgl);
76 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed);
79 // Shouldn't need to use these much any more as the protocol is now saved as a string.
80 // They are kept for compatibility loading old .vik files
81 typedef enum {GARMIN_P = 0, MAGELLAN_P, DELORME_P, NAVILINK_P, OLD_NUM_PROTOCOLS} vik_gps_proto;
82 static gchar * protocols_args[] = {"garmin", "magellan", "delbin", "navilink", NULL};
84 static gchar * params_ports[] = {"com1", "usb:", NULL};
86 static gchar * params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
88 /* NUM_PORTS not actually used */
89 /* #define NUM_PORTS (sizeof(params_ports)/sizeof(params_ports[0]) - 1) */
90 /* Compatibility with previous versions */
92 static gchar * old_params_ports[] = {"com1", "usb:", NULL};
94 static gchar * old_params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
96 #define OLD_NUM_PORTS (sizeof(old_params_ports)/sizeof(old_params_ports[0]) - 1)
101 gboolean thread_complete;
102 vik_gps_dir direction;
104 gint wpt_total_count;
106 gint trk_total_count;
108 gint rte_total_count;
114 GtkWidget *status_label;
115 GtkWidget *gps_label;
116 GtkWidget *wpt_label;
117 GtkWidget *trk_label;
118 GtkWidget *rte_label;
119 vik_gps_xfer_type progress_type;
123 gint id_status_working;
129 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
130 gboolean realtime_tracking;
133 static void gps_session_delete(GpsSession *sess);
135 static gchar *params_groups[] = {
137 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
138 N_("Realtime Tracking Mode"),
142 enum {GROUP_DATA_MODE, GROUP_REALTIME_MODE};
145 static VikLayerParamData gps_protocol_default ( void )
147 VikLayerParamData data;
148 data.s = g_strdup ( "garmin" );
152 static VikLayerParamData gps_port_default ( void )
154 VikLayerParamData data;
155 data.s = g_strdup ( "usb:" );
157 /* Attempt to auto set default USB serial port entry */
158 /* Ordered to make lowest device favourite if available */
159 if (g_access ("/dev/ttyUSB1", R_OK) == 0) {
161 g_free ( (gchar *)data.s );
162 data.s = g_strdup ("/dev/ttyUSB1");
164 if (g_access ("/dev/ttyUSB0", R_OK) == 0) {
166 g_free ( (gchar *)data.s );
167 data.s = g_strdup ("/dev/ttyUSB0");
173 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
174 static gchar *params_vehicle_position[] = {
175 N_("Keep vehicle at center"),
176 N_("Keep vehicle on screen"),
181 VEHICLE_POSITION_CENTERED = 0,
182 VEHICLE_POSITION_ON_SCREEN,
183 VEHICLE_POSITION_NONE,
186 static VikLayerParamData moving_map_method_default ( void ) { return VIK_LPD_UINT ( VEHICLE_POSITION_ON_SCREEN ); }
188 static VikLayerParamData gpsd_host_default ( void )
190 VikLayerParamData data;
191 data.s = g_strdup ( "localhost" );
195 static VikLayerParamData gpsd_port_default ( void )
197 VikLayerParamData data;
198 data.s = g_strdup ( DEFAULT_GPSD_PORT );
202 static VikLayerParamData gpsd_retry_interval_default ( void )
204 VikLayerParamData data;
205 data.s = g_strdup ( "10" );
211 static VikLayerParam gps_layer_params[] = {
212 // NB gps_layer_inst_init() is performed after parameter registeration
213 // thus to give the protocols some potential values use the old static list
214 // TODO: find another way to use gps_layer_inst_init()?
215 { 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
216 { 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 },
217 { 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 },
218 { 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 },
219 { 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 },
220 { 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 },
221 { 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 },
222 { 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 },
223 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
224 { 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 },
225 { 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 },
226 { 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 },
227 { 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 },
228 { 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 },
229 { 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 },
230 { 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 },
231 { 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 },
232 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
235 PARAM_PROTOCOL=0, PARAM_PORT,
236 PARAM_DOWNLOAD_TRACKS, PARAM_UPLOAD_TRACKS,
237 PARAM_DOWNLOAD_ROUTES, PARAM_UPLOAD_ROUTES,
238 PARAM_DOWNLOAD_WAYPOINTS, PARAM_UPLOAD_WAYPOINTS,
239 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
241 PARAM_REALTIME_CENTER_START,
242 PARAM_VEHICLE_POSITION,
243 PARAM_REALTIME_UPDATE_STATUSBAR,
247 PARAM_GPSD_RETRY_INTERVAL,
248 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
251 VikLayerInterface vik_gps_layer_interface = {
263 sizeof(params_groups)/sizeof(params_groups[0]),
267 (VikLayerFuncCreate) vik_gps_layer_create,
268 (VikLayerFuncRealize) vik_gps_layer_realize,
269 (VikLayerFuncPostRead) NULL,
270 (VikLayerFuncFree) vik_gps_layer_free,
272 (VikLayerFuncProperties) NULL,
273 (VikLayerFuncDraw) vik_gps_layer_draw,
274 (VikLayerFuncChangeCoordMode) gps_layer_change_coord_mode,
276 (VikLayerFuncGetTimestamp) NULL,
278 (VikLayerFuncSetMenuItemsSelection) NULL,
279 (VikLayerFuncGetMenuItemsSelection) NULL,
281 (VikLayerFuncAddMenuItems) gps_layer_add_menu_items,
282 (VikLayerFuncSublayerAddMenuItems) NULL,
284 (VikLayerFuncSublayerRenameRequest) NULL,
285 (VikLayerFuncSublayerToggleVisible) NULL,
286 (VikLayerFuncSublayerTooltip) NULL,
287 (VikLayerFuncLayerTooltip) gps_layer_tooltip,
288 (VikLayerFuncLayerSelected) NULL,
290 (VikLayerFuncMarshall) gps_layer_marshall,
291 (VikLayerFuncUnmarshall) gps_layer_unmarshall,
293 (VikLayerFuncSetParam) gps_layer_set_param,
294 (VikLayerFuncGetParam) gps_layer_get_param,
295 (VikLayerFuncChangeParam) NULL,
297 (VikLayerFuncReadFileData) NULL,
298 (VikLayerFuncWriteFileData) NULL,
300 (VikLayerFuncDeleteItem) NULL,
301 (VikLayerFuncCutItem) NULL,
302 (VikLayerFuncCopyItem) NULL,
303 (VikLayerFuncPasteItem) NULL,
304 (VikLayerFuncFreeCopiedItem) NULL,
305 (VikLayerFuncDragDropRequest) NULL,
307 (VikLayerFuncSelectClick) NULL,
308 (VikLayerFuncSelectMove) NULL,
309 (VikLayerFuncSelectRelease) NULL,
310 (VikLayerFuncSelectedViewportMenu) NULL,
313 enum {TRW_DOWNLOAD=0, TRW_UPLOAD,
314 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
318 static gchar * trw_names[] = {
319 N_("GPS Download"), N_("GPS Upload"),
320 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
321 N_("GPS Realtime Tracking"),
325 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
327 struct gps_data_t gpsd;
333 struct gps_fix_t fix;
334 gint satellites_used;
335 gboolean dirty; /* needs to be saved */
337 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
339 struct _VikGpsLayer {
341 VikTrwLayer * trw_children[NUM_TRW];
342 GList * children; /* used only for writing file */
343 int cur_read_child; /* used only for reading file */
344 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
346 gboolean connected_to_gpsd;
347 gboolean realtime_tracking;
348 gboolean first_realtime_trackpoint;
352 VikTrack *realtime_track;
354 GIOChannel *realtime_io_channel;
355 guint realtime_io_watch_id;
356 guint realtime_retry_timer;
357 GdkGC *realtime_track_gc;
358 GdkGC *realtime_track_bg_gc;
359 GdkGC *realtime_track_pt_gc;
360 GdkGC *realtime_track_pt1_gc;
361 GdkGC *realtime_track_pt2_gc;
364 gboolean auto_connect_to_gpsd;
367 gint gpsd_retry_interval;
368 gboolean realtime_record;
369 gboolean realtime_jump_to_start;
370 guint vehicle_position;
371 gboolean realtime_update_statusbar;
372 VikTrackpoint *trkpt;
373 VikTrackpoint *trkpt_prev;
374 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
377 gboolean download_tracks;
378 gboolean download_routes;
379 gboolean download_waypoints;
380 gboolean upload_tracks;
381 gboolean upload_routes;
382 gboolean upload_waypoints;
386 * Overwrite the static setup with dynamically generated GPS Babel device list
388 static void gps_layer_inst_init ( VikGpsLayer *self )
391 // +1 for luck (i.e the NULL terminator)
392 gchar **new_protocols = g_malloc_n(1 + g_list_length(a_babel_device_list), sizeof(gpointer));
394 GList *gl = g_list_first ( a_babel_device_list );
396 // should be using label property but use name for now
397 // thus don't need to mess around converting label to name later on
398 new_protocols[new_proto++] = ((BabelDevice*)gl->data)->name;
399 gl = g_list_next ( gl );
401 new_protocols[new_proto] = NULL;
403 vik_gps_layer_interface.params[PARAM_PROTOCOL].widget_data = new_protocols;
406 GType vik_gps_layer_get_type ()
408 static GType val_type = 0;
412 static const GTypeInfo val_info =
414 sizeof (VikGpsLayerClass),
415 NULL, /* base_init */
416 NULL, /* base_finalize */
417 NULL, /* class init */
418 NULL, /* class_finalize */
419 NULL, /* class_data */
420 sizeof (VikGpsLayer),
422 (GInstanceInitFunc) gps_layer_inst_init,
424 val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGpsLayer", &val_info, 0 );
430 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp)
434 VikGpsLayer *rv = vik_gps_layer_new (vp);
435 vik_layer_rename ( VIK_LAYER(rv), vik_gps_layer_interface.name );
437 for (i = 0; i < NUM_TRW; i++) {
438 rv->trw_children[i] = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vp, FALSE ));
439 vik_layer_set_menu_items_selection(VIK_LAYER(rv->trw_children[i]), VIK_MENU_ITEM_ALL & ~(VIK_MENU_ITEM_CUT|VIK_MENU_ITEM_DELETE));
444 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl )
446 static gchar buf1[256];
448 static gchar rbuf[512];
451 g_snprintf (buf1, sizeof(buf1), "%s:%s", vgl->protocol, vgl->serial_port);
453 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
454 static gchar buf2[256];
456 if ( vgl->connected_to_gpsd )
457 g_snprintf (buf2, sizeof(buf2), "GPSD:%s:%s %s", vgl->gpsd_host, vgl->gpsd_port, _("Connected"));
459 g_snprintf (buf2, sizeof(buf2), "GPSD:%s:%s %s", vgl->gpsd_host, vgl->gpsd_port, _("Disconnected"));
461 g_snprintf (rbuf, sizeof(rbuf), "%s\n%s", buf1, buf2);
463 g_snprintf (rbuf, sizeof(rbuf), "%s", buf1);
469 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *datalen )
471 VikLayer *child_layer;
474 GByteArray* b = g_byte_array_new ();
478 #define alm_append(obj, sz) \
480 g_byte_array_append ( b, (guint8 *)&len, sizeof(len) ); \
481 g_byte_array_append ( b, (guint8 *)(obj), len );
483 vik_layer_marshall_params(VIK_LAYER(vgl), &ld, &ll);
487 for (i = 0; i < NUM_TRW; i++) {
488 child_layer = VIK_LAYER(vgl->trw_children[i]);
489 vik_layer_marshall(child_layer, &ld, &ll);
497 g_byte_array_free(b, FALSE);
502 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
504 #define alm_size (*(gint *)data)
506 len -= sizeof(gint) + alm_size; \
507 data += sizeof(gint) + alm_size;
509 VikGpsLayer *rv = vik_gps_layer_new(vvp);
510 VikLayer *child_layer;
513 vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
517 while (len>0 && i < NUM_TRW) {
518 child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
520 rv->trw_children[i++] = (VikTrwLayer *)child_layer;
521 // NB no need to attach signal update handler here
522 // as this will always be performed later on in vik_gps_layer_realize()
526 // g_print("gps_layer_unmarshall ended with len=%d\n", len);
533 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
539 g_free(vgl->protocol);
540 // Backwards Compatibility: previous versions <v1.4 stored protocol as an array index
541 int index = data.s[0] - '0';
542 if (data.s[0] != '\0' &&
543 g_ascii_isdigit (data.s[0]) &&
545 index < OLD_NUM_PROTOCOLS)
546 // It is a single digit: activate compatibility
547 vgl->protocol = g_strdup(protocols_args[index]);
549 vgl->protocol = g_strdup(data.s);
550 g_debug("%s: %s", __FUNCTION__, vgl->protocol);
553 g_warning(_("Unknown GPS Protocol"));
557 g_free(vgl->serial_port);
558 // Backwards Compatibility: previous versions <v0.9.91 stored serial_port as an array index
559 int index = data.s[0] - '0';
560 if (data.s[0] != '\0' &&
561 g_ascii_isdigit (data.s[0]) &&
563 index < OLD_NUM_PORTS)
564 /* It is a single digit: activate compatibility */
565 vgl->serial_port = g_strdup(old_params_ports[index]);
567 vgl->serial_port = g_strdup(data.s);
568 g_debug("%s: %s", __FUNCTION__, vgl->serial_port);
571 g_warning(_("Unknown serial port device"));
573 case PARAM_DOWNLOAD_TRACKS:
574 vgl->download_tracks = data.b;
576 case PARAM_UPLOAD_TRACKS:
577 vgl->upload_tracks = data.b;
579 case PARAM_DOWNLOAD_ROUTES:
580 vgl->download_routes = data.b;
582 case PARAM_UPLOAD_ROUTES:
583 vgl->upload_routes = data.b;
585 case PARAM_DOWNLOAD_WAYPOINTS:
586 vgl->download_waypoints = data.b;
588 case PARAM_UPLOAD_WAYPOINTS:
589 vgl->upload_waypoints = data.b;
591 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
592 case PARAM_GPSD_CONNECT:
593 vgl->auto_connect_to_gpsd = data.b;
595 case PARAM_GPSD_HOST:
598 g_free(vgl->gpsd_host);
599 vgl->gpsd_host = g_strdup(data.s);
602 case PARAM_GPSD_PORT:
605 g_free(vgl->gpsd_port);
606 vgl->gpsd_port = g_strdup(data.s);
609 case PARAM_GPSD_RETRY_INTERVAL:
610 vgl->gpsd_retry_interval = strtol(data.s, NULL, 10);
612 case PARAM_REALTIME_REC:
613 vgl->realtime_record = data.b;
615 case PARAM_REALTIME_CENTER_START:
616 vgl->realtime_jump_to_start = data.b;
618 case PARAM_VEHICLE_POSITION:
619 vgl->vehicle_position = data.u;
621 case PARAM_REALTIME_UPDATE_STATUSBAR:
622 vgl->realtime_update_statusbar = data.b;
624 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
626 g_warning("gps_layer_set_param(): unknown parameter");
632 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation )
634 VikLayerParamData rv;
638 rv.s = vgl->protocol;
639 g_debug("%s: %s", __FUNCTION__, rv.s);
642 rv.s = vgl->serial_port;
643 g_debug("%s: %s", __FUNCTION__, rv.s);
645 case PARAM_DOWNLOAD_TRACKS:
646 rv.b = vgl->download_tracks;
648 case PARAM_UPLOAD_TRACKS:
649 rv.b = vgl->upload_tracks;
651 case PARAM_DOWNLOAD_ROUTES:
652 rv.b = vgl->download_routes;
654 case PARAM_UPLOAD_ROUTES:
655 rv.b = vgl->upload_routes;
657 case PARAM_DOWNLOAD_WAYPOINTS:
658 rv.b = vgl->download_waypoints;
660 case PARAM_UPLOAD_WAYPOINTS:
661 rv.b = vgl->upload_waypoints;
663 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
664 case PARAM_GPSD_CONNECT:
665 rv.b = vgl->auto_connect_to_gpsd;
667 case PARAM_GPSD_HOST:
668 rv.s = vgl->gpsd_host ? vgl->gpsd_host : "";
670 case PARAM_GPSD_PORT:
671 rv.s = vgl->gpsd_port ? vgl->gpsd_port : g_strdup(DEFAULT_GPSD_PORT);
673 case PARAM_GPSD_RETRY_INTERVAL:
674 rv.s = g_strdup_printf("%d", vgl->gpsd_retry_interval);
676 case PARAM_REALTIME_REC:
677 rv.b = vgl->realtime_record;
679 case PARAM_REALTIME_CENTER_START:
680 rv.b = vgl->realtime_jump_to_start;
682 case PARAM_VEHICLE_POSITION:
683 rv.u = vgl->vehicle_position;
685 case PARAM_REALTIME_UPDATE_STATUSBAR:
686 rv.u = vgl->realtime_update_statusbar;
688 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
690 g_warning(_("%s: unknown parameter"), __FUNCTION__);
696 VikGpsLayer *vik_gps_layer_new (VikViewport *vp)
699 VikGpsLayer *vgl = VIK_GPS_LAYER ( g_object_new ( VIK_GPS_LAYER_TYPE, NULL ) );
700 vik_layer_set_type ( VIK_LAYER(vgl), VIK_LAYER_GPS );
701 for (i = 0; i < NUM_TRW; i++) {
702 vgl->trw_children[i] = NULL;
704 vgl->children = NULL;
705 vgl->cur_read_child = 0;
707 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
708 vgl->first_realtime_trackpoint = FALSE;
710 vgl->trkpt_prev = NULL;
712 vgl->realtime_io_channel = NULL;
713 vgl->realtime_io_watch_id = 0;
714 vgl->realtime_retry_timer = 0;
716 vgl->realtime_track_gc = vik_viewport_new_gc ( vp, "#203070", 2 );
717 vgl->realtime_track_bg_gc = vik_viewport_new_gc ( vp, "grey", 2 );
718 vgl->realtime_track_pt1_gc = vik_viewport_new_gc ( vp, "red", 2 );
719 vgl->realtime_track_pt2_gc = vik_viewport_new_gc ( vp, "green", 2 );
720 vgl->realtime_track_pt_gc = vgl->realtime_track_pt1_gc;
722 vgl->realtime_track = NULL;
723 #endif // VIK_CONFIG_REALTIME_GPS_TRACKING
725 vik_layer_set_defaults ( VIK_LAYER(vgl), vp );
730 static void vik_gps_layer_draw ( VikGpsLayer *vgl, VikViewport *vp )
734 VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( vp ));
736 for (i = 0; i < NUM_TRW; i++) {
737 vl = VIK_LAYER(vgl->trw_children[i]);
739 if ( vik_viewport_get_half_drawn ( vp ) ) {
740 vik_viewport_set_half_drawn ( vp, FALSE );
741 vik_viewport_snapshot_load( vp );
743 vik_viewport_snapshot_save( vp );
746 if (!vik_viewport_get_half_drawn(vp))
747 vik_layer_draw ( vl, vp );
749 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
750 if (vgl->realtime_tracking) {
751 if (VIK_LAYER(vgl) == trigger) {
752 if ( vik_viewport_get_half_drawn ( vp ) ) {
753 vik_viewport_set_half_drawn ( vp, FALSE );
754 vik_viewport_snapshot_load( vp );
756 vik_viewport_snapshot_save( vp );
759 if (!vik_viewport_get_half_drawn(vp))
760 realtime_tracking_draw(vgl, vp);
762 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
765 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode )
768 for (i = 0; i < NUM_TRW; i++) {
769 vik_layer_change_coord_mode(VIK_LAYER(vgl->trw_children[i]), mode);
773 static void gps_layer_add_menu_items( VikGpsLayer *vgl, GtkMenu *menu, gpointer vlp )
775 static gpointer pass_along[2];
780 item = gtk_menu_item_new();
781 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
782 gtk_widget_show ( item );
785 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS") );
786 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
787 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_upload_cb), pass_along );
788 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
789 gtk_widget_show ( item );
791 item = gtk_image_menu_item_new_with_mnemonic ( _("Download from _GPS") );
792 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
793 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_download_cb), pass_along );
794 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
795 gtk_widget_show ( item );
797 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
798 item = gtk_image_menu_item_new_with_mnemonic ( vgl->realtime_tracking ?
799 "_Stop Realtime Tracking" :
800 "_Start Realtime Tracking" );
801 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, vgl->realtime_tracking ?
802 gtk_image_new_from_stock (GTK_STOCK_MEDIA_STOP, GTK_ICON_SIZE_MENU) :
803 gtk_image_new_from_stock (GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_MENU) );
804 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_start_stop_tracking_cb), pass_along );
805 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
806 gtk_widget_show ( item );
808 item = gtk_menu_item_new();
809 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
810 gtk_widget_show ( item );
812 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _Realtime") );
813 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
814 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_realtime_cb), pass_along );
815 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
816 gtk_widget_show ( item );
817 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
819 item = gtk_image_menu_item_new_with_mnemonic ( _("E_mpty Upload") );
820 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
821 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_upload_cb), pass_along );
822 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
823 gtk_widget_show ( item );
825 item = gtk_image_menu_item_new_with_mnemonic ( _("_Empty Download") );
826 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
827 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_download_cb), pass_along );
828 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
829 gtk_widget_show ( item );
831 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _All") );
832 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
833 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_all_cb), pass_along );
834 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
835 gtk_widget_show ( item );
839 static void disconnect_layer_signal ( VikLayer *vl, VikGpsLayer *vgl )
841 guint number_handlers = g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, vgl);
842 if ( number_handlers != 1 ) {
843 g_critical(_("Unexpected number of disconnected handlers: %d"), number_handlers);
847 static void vik_gps_layer_free ( VikGpsLayer *vgl )
850 for (i = 0; i < NUM_TRW; i++) {
851 if (vgl->vl.realized)
852 disconnect_layer_signal(VIK_LAYER(vgl->trw_children[i]), vgl);
853 g_object_unref(vgl->trw_children[i]);
855 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
856 rt_gpsd_disconnect(vgl);
857 if (vgl->realtime_track_gc != NULL)
858 g_object_unref(vgl->realtime_track_gc);
859 if (vgl->realtime_track_bg_gc != NULL)
860 g_object_unref(vgl->realtime_track_bg_gc);
861 if (vgl->realtime_track_pt1_gc != NULL)
862 g_object_unref(vgl->realtime_track_pt1_gc);
863 if (vgl->realtime_track_pt2_gc != NULL)
864 g_object_unref(vgl->realtime_track_pt2_gc);
865 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
868 static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter )
873 // TODO set to garmin by default
874 //if (a_babel_device_list)
875 // device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
876 // Need to access uibuild widgets somehow....
878 for (ix = 0; ix < NUM_TRW; ix++) {
879 VikLayer * trw = VIK_LAYER(vgl->trw_children[ix]);
880 vik_treeview_add_layer ( VIK_LAYER(vgl)->vt, layer_iter, &iter,
881 _(trw_names[ix]), vgl, TRUE,
882 trw, trw->type, trw->type, vik_layer_get_timestamp(trw) );
883 if ( ! trw->visible )
884 vik_treeview_item_set_visible ( VIK_LAYER(vgl)->vt, &iter, FALSE );
885 vik_layer_realize ( trw, VIK_LAYER(vgl)->vt, &iter );
886 g_signal_connect_swapped ( G_OBJECT(trw), "update", G_CALLBACK(vik_layer_emit_update_secondary), vgl );
889 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
890 if ( vgl->auto_connect_to_gpsd ) {
891 vgl->realtime_tracking = TRUE;
892 vgl->first_realtime_trackpoint = TRUE;
893 (void)rt_gpsd_connect ( vgl, FALSE );
895 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
898 const GList *vik_gps_layer_get_children ( VikGpsLayer *vgl )
902 if (vgl->children == NULL) {
903 for (i = NUM_TRW - 1; i >= 0; i--)
904 vgl->children = g_list_prepend(vgl->children, vgl->trw_children[i]);
906 return vgl->children;
909 VikTrwLayer * vik_gps_layer_get_a_child(VikGpsLayer *vgl)
911 g_assert ((vgl->cur_read_child >= 0) && (vgl->cur_read_child < NUM_TRW));
913 VikTrwLayer * vtl = vgl->trw_children[vgl->cur_read_child];
914 if (++(vgl->cur_read_child) >= NUM_TRW)
915 vgl->cur_read_child = 0;
919 gboolean vik_gps_layer_is_empty ( VikGpsLayer *vgl )
921 if ( vgl->trw_children[0] )
926 static void gps_session_delete(GpsSession *sess)
928 vik_mutex_free(sess->mutex);
929 // Remove any outstanding GUI update requests
930 if ( sess->id_status_working )
931 g_source_remove ( sess->id_status_working );
932 if ( sess->id_status_end )
933 g_source_remove ( sess->id_status_end );
935 g_source_remove ( sess->id_info );
936 if ( sess->id_total_count )
937 g_source_remove ( sess->id_total_count );
938 if ( sess->id_count )
939 g_source_remove ( sess->id_count );
940 g_free(sess->babelargs);
946 static gboolean show_total_count(GpsSession *sess)
948 g_mutex_lock(sess->mutex);
950 const gchar *tmp_str;
952 GtkWidget *progress_label;
953 if (sess->direction == GPS_DOWN)
955 switch (sess->progress_type) {
957 tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", sess->wpt_total_count);
958 tc = sess->wpt_total_count;
959 progress_label = sess->wpt_label;
962 tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", sess->trk_total_count);
963 tc = sess->trk_total_count;
964 progress_label = sess->trk_label;
967 tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", sess->rte_total_count);
968 tc = sess->rte_total_count;
969 progress_label = sess->rte_label;
973 // 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
974 gint mycnt = (cnt / 2) + 1;
975 tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt);
984 switch (sess->progress_type) {
986 tmp_str = ngettext("Uploading %d waypoint...", "Uploading %d waypoints...", sess->wpt_total_count);
987 tc = sess->wpt_total_count;
988 progress_label = sess->wpt_label;
991 tmp_str = ngettext("Uploading %d trackpoint...", "Uploading %d trackpoints...", sess->trk_total_count);
992 tc = sess->trk_total_count;
993 progress_label = sess->trk_label;
996 tmp_str = ngettext("Uploading %d routepoint...", "Uploading %d routepoints...", sess->rte_total_count);
997 tc = sess->rte_total_count;
998 progress_label = sess->rte_label;
1004 g_snprintf(s, 128, tmp_str, tc);
1005 gtk_label_set_text ( GTK_LABEL(progress_label), s );
1006 gtk_widget_show ( progress_label );
1008 sess->id_total_count = 0;
1009 g_mutex_unlock(sess->mutex);
1013 static gboolean show_current_count(GpsSession *sess)
1015 g_mutex_lock(sess->mutex);
1018 gint count, total_count;
1019 const gchar *tmp_str;
1020 GtkWidget *progress_label;
1021 if (sess->wpt_count < sess->wpt_total_count) {
1022 if (sess->direction == GPS_DOWN) {
1023 switch (sess->progress_type) {
1025 tmp_str = ngettext("Downloaded %d out of %d waypoint...", "Downloaded %d out of %d waypoints...", sess->wpt_total_count);
1026 count = sess->wpt_count;
1027 total_count = sess->wpt_total_count;
1028 progress_label = sess->wpt_label;
1031 tmp_str = ngettext("Downloaded %d out of %d trackpoint...", "Downloaded %d out of %d trackpoints...", sess->trk_total_count);
1032 count = sess->trk_count;
1033 total_count = sess->trk_total_count;
1034 progress_label = sess->trk_label;
1037 tmp_str = ngettext("Downloaded %d out of %d routepoint...", "Downloaded %d out of %d routepoints...", sess->rte_total_count);
1038 count = sess->rte_count;
1039 total_count = sess->rte_total_count;
1040 progress_label = sess->rte_label;
1045 switch (sess->progress_type) {
1047 tmp_str = ngettext("Uploaded %d out of %d waypoint...", "Uploaded %d out of %d waypoints...", sess->wpt_total_count);
1048 count = sess->wpt_count;
1049 total_count = sess->wpt_total_count;
1050 progress_label = sess->wpt_label;
1053 tmp_str = ngettext("Uploaded %d out of %d trackpoint...", "Uploaded %d out of %d trackpoints...", sess->trk_total_count);
1054 count = sess->trk_count;
1055 total_count = sess->trk_total_count;
1056 progress_label = sess->trk_label;
1059 tmp_str = ngettext("Uploaded %d out of %d routepoint...", "Uploaded %d out of %d routepoints...", sess->rte_total_count);
1060 count = sess->rte_count;
1061 total_count = sess->rte_total_count;
1062 progress_label = sess->rte_label;
1067 if (sess->direction == GPS_DOWN) {
1068 switch (sess->progress_type) {
1070 tmp_str = ngettext("Downloaded %d waypoint", "Downloaded %d waypoints", sess->wpt_count);
1071 count = sess->wpt_count;
1072 total_count = sess->wpt_total_count;
1073 progress_label = sess->wpt_label;
1076 tmp_str = ngettext("Downloaded %d trackpoint", "Downloaded %d trackpoints", sess->trk_count);
1077 count = sess->trk_count;
1078 total_count = sess->trk_total_count;
1079 progress_label = sess->trk_label;
1082 tmp_str = ngettext("Downloaded %d routepoint", "Downloaded %d routepoints", sess->rte_count);
1083 count = sess->rte_count;
1084 total_count = sess->rte_total_count;
1085 progress_label = sess->rte_label;
1090 switch (sess->progress_type) {
1092 tmp_str = ngettext("Uploaded %d waypoint", "Uploaded %d waypoints", sess->wpt_count);
1093 count = sess->wpt_count;
1094 total_count = sess->wpt_total_count;
1095 progress_label = sess->wpt_label;
1098 tmp_str = ngettext("Uploaded %d trackpoint", "Uploaded %d trackpoints", sess->trk_count);
1099 count = sess->trk_count;
1100 total_count = sess->trk_total_count;
1101 progress_label = sess->trk_label;
1104 tmp_str = ngettext("Uploaded %d routepoint", "Uploaded %d routepoints", sess->rte_count);
1105 count = sess->rte_count;
1106 total_count = sess->rte_total_count;
1107 progress_label = sess->rte_label;
1112 g_snprintf(s, 128, tmp_str, count, total_count);
1113 gtk_label_set_text ( GTK_LABEL(progress_label), s );
1116 g_mutex_unlock(sess->mutex);
1120 static gboolean show_gps_info(GpsSession *sess)
1122 g_mutex_lock(sess->mutex);
1125 g_snprintf(s, 256, _("GPS Device: %s"), sess->info);
1126 gtk_label_set_text ( GTK_LABEL(sess->gps_label), s );
1129 g_mutex_unlock(sess->mutex);
1134 * Common processing for GPS Device information
1135 * It doesn't matter whether we're uploading or downloading
1137 static void process_line_for_gps_info ( const gchar *line, GpsSession *sess )
1139 if (strstr(line, "PRDDAT")) {
1140 gchar **tokens = g_strsplit(line, " ", 0);
1146 while (tokens[n_tokens])
1149 // I'm not entirely clear what information this is trying to get...
1150 // Obviously trying to decipher some kind of text/naming scheme
1151 // Anyway this will be superceded if there is 'Unit:' information
1153 for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
1155 sscanf(tokens[i], "%x", &ch);
1159 sess->info = g_strdup (info);
1160 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1165 /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
1166 if (strstr(line, "Unit:")) {
1167 gchar **tokens = g_strsplit(line, "\t", 0);
1169 while (tokens[n_tokens])
1173 sess->info = g_strdup (tokens[1]);
1174 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1179 if (strstr(line, "[ERROR] GPS")) {
1180 gchar **tokens = g_strsplit(line, "\n", 0);
1181 sess->info = g_strdup(tokens[0]);
1182 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1186 if (strstr(line, "an't in")) {
1187 gchar **tokens = g_strsplit(line, "\n", 0);
1188 sess->info = g_strdup(tokens[0]);
1189 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1193 if (strstr(line, "Can't get waypoint")) {
1194 gchar **tokens = g_strsplit(line, "\n", 0);
1195 sess->info = g_strdup(tokens[0]);
1196 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1201 static gboolean show_gps_status_working ( GpsSession *sess )
1203 g_mutex_lock(sess->mutex);
1205 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1207 sess->id_status_working = 0;
1208 g_mutex_unlock(sess->mutex);
1212 static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1217 //gps_session_delete(sess);
1218 sess->thread_complete = TRUE;
1219 g_debug ("THREAD EXIT INTERUPPT");
1220 g_thread_exit ( NULL );
1224 case BABEL_DIAG_OUTPUT:
1225 line = (gchar *)data;
1227 sess->id_status_working = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_working, sess );
1229 /* tells us the type of items that will follow */
1230 if (strstr(line, "Xfer Wpt")) {
1231 sess->progress_type = WPT;
1233 if (strstr(line, "Xfer Trk")) {
1234 sess->progress_type = TRK;
1236 if (strstr(line, "Xfer Rte")) {
1237 sess->progress_type = RTE;
1240 process_line_for_gps_info ( line, sess );
1242 if (strstr(line, "RECORD")) {
1245 if (strlen(line) > 20) {
1246 sscanf(line+17, "%x", &lsb);
1247 sscanf(line+20, "%x", &msb);
1248 cnt = lsb + msb * 256;
1249 if ( sess->progress_type == RTE ) {
1250 // 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
1251 gint mycnt = (cnt / 2) + 1;
1252 sess->rte_total_count = mycnt;
1253 sess->rte_count = 0;
1255 else if ( sess->progress_type == WPT ) {
1256 sess->wpt_total_count = cnt;
1257 sess->wpt_count = 0;
1260 sess->trk_total_count = cnt;
1261 sess->trk_count = 0;
1263 sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess);
1266 if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1267 if ( strstr(line, "WPTDAT") )
1269 else if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") )
1272 // "RTEHDR" || "RTEWPT"
1274 sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess);
1284 static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1290 //gps_session_delete(sess);
1291 sess->thread_complete = TRUE;
1292 g_thread_exit ( NULL );
1296 case BABEL_DIAG_OUTPUT:
1297 line = (gchar *)data;
1299 sess->id_status_working = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_working, sess );
1301 process_line_for_gps_info ( line, sess );
1303 if (strstr(line, "RECORD")) {
1306 if (strlen(line) > 20) {
1307 sscanf(line+17, "%x", &lsb);
1308 sscanf(line+20, "%x", &msb);
1309 cnt = lsb + msb * 256;
1310 //sess->count = 0; ?? wpt, trk and/or rte?? or none
1313 if ( strstr(line, "WPTDAT")) {
1314 sess->progress_type = WPT;
1315 if (sess->wpt_count == 0) {
1316 sess->wpt_total_count = cnt;
1317 sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess );
1320 sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess );
1322 if ( strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1323 sess->progress_type = RTE;
1324 if (sess->rte_count == 0) {
1325 // 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
1326 // Anyway since we're uploading - we should know how many points we're going to put!
1327 cnt = (cnt / 2) + 1;
1328 sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess);
1331 sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess);
1333 if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
1334 sess->progress_type = TRK;
1335 if (sess->trk_count == 0) {
1336 sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess);
1339 sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess);
1349 static gboolean show_gps_status_end ( GpsSession *sess )
1351 g_mutex_lock(sess->mutex);
1352 // (Download)Failure could be due to a number of reasons: such as no/wrong device attached or GPSBabel not installed
1353 if (!sess->result) {
1354 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: No result.") );
1358 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Done.") );
1359 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE );
1360 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE );
1363 sess->id_status_end = 0;
1364 g_mutex_unlock(sess->mutex);
1371 static void gps_comm_thread(GpsSession *sess)
1373 if (sess->direction == GPS_DOWN) {
1374 ProcessOptions po = { sess->babelargs, sess->port, NULL, NULL, NULL, NULL };
1375 sess->result = a_babel_convert_from (sess->vtl, &po, (BabelStatusFunc) gps_download_progress_func, sess, NULL);
1378 sess->result = a_babel_convert_to (sess->vtl, sess->track, sess->babelargs, sess->port,
1379 (BabelStatusFunc) gps_upload_progress_func, sess);
1382 sess->id_status_end = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_end, sess );
1386 /* Do not change the view if we are following the current GPS position */
1387 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1388 if (!sess->realtime_tracking)
1391 if ( sess->vvp && sess->direction == GPS_DOWN ) {
1392 vik_layer_post_read ( VIK_LAYER(sess->vtl), sess->vvp, TRUE );
1393 /* View the data available */
1394 vik_trw_layer_auto_set_view ( sess->vtl, sess->vvp ) ;
1395 vik_layer_emit_update ( VIK_LAYER(sess->vtl) ); // NB update request from background thread
1404 // Thread has completed successfully, but now set to false to avoid 'tell thread to stop' test after dialog run
1408 sess->thread_complete = TRUE;
1409 g_thread_exit(NULL);
1414 * @vtl: The TrackWaypoint layer to operate on
1415 * @track: Operate on a particular track when specified
1416 * @dir: The direction of the transfer
1417 * @protocol: The GPS device communication protocol
1418 * @port: The GPS serial port
1419 * @tracking: If tracking then viewport display update will be skipped
1420 * @vvp: A viewport is required as the display may get updated
1421 * @vlp: A layers panel is needed for uploading as the items maybe modified
1422 * @do_tracks: Whether tracks shoud be processed
1423 * @do_waypoints: Whether waypoints shoud be processed
1424 * @turn_off: Whether we should attempt to turn off the GPS device after the transfer (only some devices support this)
1426 * Talk to a GPS Device using a thread which updates a dialog with the progress
1428 gint vik_gps_comm ( VikTrwLayer *vtl,
1435 VikLayersPanel *vlp,
1438 gboolean do_waypoints,
1441 GpsSession *sess = g_malloc0(sizeof(GpsSession));
1442 char *tracks = NULL;
1443 char *routes = NULL;
1444 char *waypoints = NULL;
1446 sess->mutex = vik_mutex_new();
1447 sess->direction = dir;
1449 sess->track = track;
1450 sess->port = g_strdup(port);
1454 // This must be done inside the main thread as the uniquify causes screen updates
1455 // (originally performed this nearer the point of upload in the thread)
1456 if ( dir == GPS_UP ) {
1457 // Enforce unique names in the layer upload to the GPS device
1458 // NB this may only be a Garmin device restriction (and may be not every Garmin device either...)
1459 // Thus this maintains the older code in built restriction
1460 if ( ! vik_trw_layer_uniquify ( sess->vtl, vlp ) )
1461 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(sess->vtl))), VIK_STATUSBAR_INFO,
1462 _("Warning - GPS Upload items may overwrite each other") );
1465 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1466 sess->realtime_tracking = tracking;
1482 sess->babelargs = g_strdup_printf("-D 9 %s %s %s -%c %s",
1483 tracks, routes, waypoints, (dir == GPS_DOWN) ? 'i' : 'o', protocol);
1487 GtkWidget *dialog = NULL;
1489 // Only create dialog if we're going to do some transferring
1490 if ( do_tracks || do_waypoints || do_routes ) {
1491 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 );
1492 sess->dialog = dialog;
1493 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog),
1494 GTK_RESPONSE_ACCEPT, FALSE );
1495 gtk_window_set_title ( GTK_WINDOW(sess->dialog), (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload") );
1497 sess->status_label = gtk_label_new (_("Status: detecting gpsbabel"));
1498 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->status_label, FALSE, FALSE, 5 );
1500 sess->gps_label = gtk_label_new (_("GPS device: N/A"));
1501 sess->wpt_label = gtk_label_new ("");
1502 sess->trk_label = gtk_label_new ("");
1503 sess->rte_label = gtk_label_new ("");
1505 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->gps_label, FALSE, FALSE, 5 );
1506 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->wpt_label, FALSE, FALSE, 5 );
1507 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->trk_label, FALSE, FALSE, 5 );
1508 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->rte_label, FALSE, FALSE, 5 );
1510 gtk_widget_show_all(sess->dialog);
1512 gtk_dialog_set_default_response ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT );
1513 // Starting gps read/write thread
1514 #if GLIB_CHECK_VERSION (2, 32, 0)
1515 g_thread_try_new ( "gps_comm_thread", (GThreadFunc)gps_comm_thread, sess, NULL );
1517 g_thread_create ( (GThreadFunc)gps_comm_thread, sess, FALSE, NULL );
1520 gtk_dialog_run(GTK_DIALOG(dialog));
1524 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No GPS items selected for transfer.") );
1528 sess->ok = FALSE; /* tell thread to stop */
1532 // No need for thread for powering off device (should be quick operation...) - so use babel command directly:
1533 gchar *device_off = g_strdup_printf("-i %s,%s", protocol, "power_off");
1534 ProcessOptions po = { device_off, port, NULL, NULL, NULL, NULL };
1535 gboolean result = a_babel_convert_from (NULL, &po, NULL, NULL, NULL);
1537 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not turn off device.") );
1538 g_free ( device_off );
1543 while ( !sess->thread_complete ) {
1544 g_usleep (G_USEC_PER_SEC/10);
1546 gtk_widget_destroy(dialog);
1548 gps_session_delete(sess);
1552 static void gps_upload_cb( gpointer layer_and_vlp[2] )
1554 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1555 VikLayersPanel *vlp = VIK_LAYERS_PANEL(layer_and_vlp[1]);
1556 VikTrwLayer *vtl = vgl->trw_children[TRW_UPLOAD];
1557 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1558 VikViewport *vvp = vik_window_viewport(vw);
1559 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);
1562 static void gps_download_cb( gpointer layer_and_vlp[2] )
1564 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1565 VikTrwLayer *vtl = vgl->trw_children[TRW_DOWNLOAD];
1566 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1567 VikViewport *vvp = vik_window_viewport(vw);
1568 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1569 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);
1571 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);
1575 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] )
1577 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1578 // Get confirmation from the user
1579 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1580 _("Are you sure you want to delete GPS Upload data?"),
1583 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1584 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1585 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1588 static void gps_empty_download_cb( gpointer layer_and_vlp[2] )
1590 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1591 // Get confirmation from the user
1592 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1593 _("Are you sure you want to delete GPS Download data?"),
1596 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1597 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1598 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1601 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1602 static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] )
1604 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1605 // Get confirmation from the user
1606 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1607 _("Are you sure you want to delete GPS Realtime data?"),
1610 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1611 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1615 static void gps_empty_all_cb( gpointer layer_and_vlp[2] )
1617 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1618 // Get confirmation from the user
1619 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1620 _("Are you sure you want to delete All GPS data?"),
1623 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1624 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1625 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1626 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1627 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1628 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1629 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1630 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1631 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1635 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1636 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp)
1640 struct LatLon lnw, lse;
1641 vik_viewport_screen_to_coord ( vp, -20, -20, &nw );
1642 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp)+20, vik_viewport_get_width(vp)+20, &se );
1643 vik_coord_to_latlon ( &nw, &lnw );
1644 vik_coord_to_latlon ( &se, &lse );
1645 if ( vgl->realtime_fix.fix.latitude > lse.lat &&
1646 vgl->realtime_fix.fix.latitude < lnw.lat &&
1647 vgl->realtime_fix.fix.longitude > lnw.lon &&
1648 vgl->realtime_fix.fix.longitude < lse.lon &&
1649 !isnan (vgl->realtime_fix.fix.track) ) {
1652 gint half_back_x, half_back_y;
1653 gint half_back_bg_x, half_back_bg_y;
1656 gint side1_x, side1_y, side2_x, side2_y;
1657 gint side1bg_x, side1bg_y, side2bg_x, side2bg_y;
1659 ll.lat = vgl->realtime_fix.fix.latitude;
1660 ll.lon = vgl->realtime_fix.fix.longitude;
1661 vik_coord_load_from_latlon ( &gps, vik_viewport_get_coord_mode(vp), &ll);
1662 vik_viewport_coord_to_screen ( vp, &gps, &x, &y );
1664 gdouble heading_cos = cos(DEG2RAD(vgl->realtime_fix.fix.track));
1665 gdouble heading_sin = sin(DEG2RAD(vgl->realtime_fix.fix.track));
1667 half_back_y = y+8*heading_cos;
1668 half_back_x = x-8*heading_sin;
1669 half_back_bg_y = y+10*heading_cos;
1670 half_back_bg_x = x-10*heading_sin;
1672 pt_y = half_back_y-24*heading_cos;
1673 pt_x = half_back_x+24*heading_sin;
1674 //ptbg_y = half_back_bg_y-28*heading_cos;
1675 ptbg_x = half_back_bg_x+28*heading_sin;
1677 side1_y = half_back_y+9*heading_sin;
1678 side1_x = half_back_x+9*heading_cos;
1679 side1bg_y = half_back_bg_y+11*heading_sin;
1680 side1bg_x = half_back_bg_x+11*heading_cos;
1682 side2_y = half_back_y-9*heading_sin;
1683 side2_x = half_back_x-9*heading_cos;
1684 side2bg_y = half_back_bg_y-11*heading_sin;
1685 side2bg_x = half_back_bg_x-11*heading_cos;
1687 GdkPoint trian[3] = { { pt_x, pt_y }, {side1_x, side1_y}, {side2_x, side2_y} };
1688 GdkPoint trian_bg[3] = { { ptbg_x, pt_y }, {side1bg_x, side1bg_y}, {side2bg_x, side2bg_y} };
1690 vik_viewport_draw_polygon ( vp, vgl->realtime_track_bg_gc, TRUE, trian_bg, 3 );
1691 vik_viewport_draw_polygon ( vp, vgl->realtime_track_gc, TRUE, trian, 3 );
1692 vik_viewport_draw_rectangle ( vp,
1693 (vgl->realtime_fix.fix.mode > MODE_2D) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc,
1694 TRUE, x-2, y-2, 4, 4 );
1695 //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;
1699 static VikTrackpoint* create_realtime_trackpoint(VikGpsLayer *vgl, gboolean forced)
1704 /* Note that fix.time is a double, but it should not affect the precision
1706 time_t cur_timestamp = vgl->realtime_fix.fix.time;
1707 time_t last_timestamp = vgl->last_fix.fix.time;
1709 if (cur_timestamp < last_timestamp) {
1713 if (vgl->realtime_record && vgl->realtime_fix.dirty) {
1714 gboolean replace = FALSE;
1715 int heading = isnan(vgl->realtime_fix.fix.track) ? 0 : (int)floor(vgl->realtime_fix.fix.track);
1716 int last_heading = isnan(vgl->last_fix.fix.track) ? 0 : (int)floor(vgl->last_fix.fix.track);
1717 int alt = isnan(vgl->realtime_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->realtime_fix.fix.altitude);
1718 int last_alt = isnan(vgl->last_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->last_fix.fix.altitude);
1719 if (((last_tp = g_list_last(vgl->realtime_track->trackpoints)) != NULL) &&
1720 (vgl->realtime_fix.fix.mode > MODE_2D) &&
1721 (vgl->last_fix.fix.mode <= MODE_2D) &&
1722 ((cur_timestamp - last_timestamp) < 2)) {
1723 g_free(last_tp->data);
1724 vgl->realtime_track->trackpoints = g_list_delete_link(vgl->realtime_track->trackpoints, last_tp);
1728 ((cur_timestamp != last_timestamp) &&
1730 ((heading < last_heading) && (heading < (last_heading - 3))) ||
1731 ((heading > last_heading) && (heading > (last_heading + 3))) ||
1732 ((alt != VIK_DEFAULT_ALTITUDE) && (alt != last_alt)))))) {
1733 /* TODO: check for new segments */
1734 VikTrackpoint *tp = vik_trackpoint_new();
1735 tp->newsegment = FALSE;
1736 tp->has_timestamp = TRUE;
1737 tp->timestamp = vgl->realtime_fix.fix.time;
1739 /* speed only available for 3D fix. Check for NAN when use this speed */
1740 tp->speed = vgl->realtime_fix.fix.speed;
1741 tp->course = vgl->realtime_fix.fix.track;
1742 tp->nsats = vgl->realtime_fix.satellites_used;
1743 tp->fix_mode = vgl->realtime_fix.fix.mode;
1745 ll.lat = vgl->realtime_fix.fix.latitude;
1746 ll.lon = vgl->realtime_fix.fix.longitude;
1747 vik_coord_load_from_latlon(&tp->coord,
1748 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1750 vik_track_add_trackpoint ( vgl->realtime_track, tp, TRUE ); // Ensure bounds is recalculated
1751 vgl->realtime_fix.dirty = FALSE;
1752 vgl->realtime_fix.satellites_used = 0;
1753 vgl->last_fix = vgl->realtime_fix;
1760 #define VIK_SETTINGS_GPS_STATUSBAR_FORMAT "gps_statusbar_format"
1762 static void update_statusbar ( VikGpsLayer *vgl, VikWindow *vw )
1764 gchar *statusbar_format_code = NULL;
1765 gboolean need2free = FALSE;
1766 if ( !a_settings_get_string ( VIK_SETTINGS_GPS_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
1767 // Otherwise use default
1768 statusbar_format_code = g_strdup ( "GSA" );
1772 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, vgl->trkpt, vgl->trkpt_prev, vgl->realtime_track, vgl->last_fix.fix.climb );
1773 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
1777 g_free ( statusbar_format_code );
1781 static void gpsd_raw_hook(VglGpsd *vgpsd, gchar *data)
1783 gboolean update_all = FALSE;
1784 VikGpsLayer *vgl = vgpsd->vgl;
1786 if (!vgl->realtime_tracking) {
1787 g_warning("%s: receiving GPS data while not in realtime mode", __PRETTY_FUNCTION__);
1791 if ((vgpsd->gpsd.fix.mode >= MODE_2D) &&
1792 !isnan(vgpsd->gpsd.fix.latitude) &&
1793 !isnan(vgpsd->gpsd.fix.longitude)) {
1795 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1796 VikViewport *vvp = vik_window_viewport(vw);
1797 vgl->realtime_fix.fix = vgpsd->gpsd.fix;
1798 vgl->realtime_fix.satellites_used = vgpsd->gpsd.satellites_used;
1799 vgl->realtime_fix.dirty = TRUE;
1802 VikCoord vehicle_coord;
1804 ll.lat = vgl->realtime_fix.fix.latitude;
1805 ll.lon = vgl->realtime_fix.fix.longitude;
1806 vik_coord_load_from_latlon(&vehicle_coord,
1807 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1809 if ((vgl->vehicle_position == VEHICLE_POSITION_CENTERED) ||
1810 (vgl->realtime_jump_to_start && vgl->first_realtime_trackpoint)) {
1811 vik_viewport_set_center_coord(vvp, &vehicle_coord, FALSE);
1814 else if (vgl->vehicle_position == VEHICLE_POSITION_ON_SCREEN) {
1817 const int px = 20; /* adjust ment in pixels to make sure vehicle is inside the box */
1818 gint width = vik_viewport_get_width(vvp);
1819 gint height = vik_viewport_get_height(vvp);
1822 vik_viewport_coord_to_screen(vvp, &vehicle_coord, &vx, &vy);
1824 if (vx < (width/hdiv))
1825 vik_viewport_set_center_screen(vvp, vx - width/2 + width/hdiv + px, vy);
1826 else if (vx > (width - width/hdiv))
1827 vik_viewport_set_center_screen(vvp, vx + width/2 - width/hdiv - px, vy);
1828 else if (vy < (height/vdiv))
1829 vik_viewport_set_center_screen(vvp, vx, vy - height/2 + height/vdiv + px);
1830 else if (vy > (height - height/vdiv))
1831 vik_viewport_set_center_screen(vvp, vx, vy + height/2 - height/vdiv - px);
1836 vgl->first_realtime_trackpoint = FALSE;
1838 vgl->trkpt = create_realtime_trackpoint ( vgl, FALSE );
1841 if ( vgl->realtime_update_statusbar )
1842 update_statusbar ( vgl, vw );
1843 vgl->trkpt_prev = vgl->trkpt;
1846 vik_layer_emit_update ( update_all ? VIK_LAYER(vgl) : VIK_LAYER(vgl->trw_children[TRW_REALTIME]) ); // NB update from background thread
1850 static gboolean gpsd_data_available(GIOChannel *source, GIOCondition condition, gpointer data)
1852 VikGpsLayer *vgl = data;
1853 if (condition == G_IO_IN) {
1854 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1855 if (!gps_poll(&vgl->vgpsd->gpsd)) {
1856 #elif GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1857 if (gps_read(&vgl->vgpsd->gpsd) > -1) {
1858 // Reuse old function to perform operations on the new GPS data
1859 gpsd_raw_hook(vgl->vgpsd, NULL);
1866 g_warning("Disconnected from gpsd. Trying to reconnect");
1867 rt_gpsd_disconnect(vgl);
1868 (void)rt_gpsd_connect(vgl, FALSE);
1871 return FALSE; /* no further calling */
1877 * returns allocated string for a new realtime track name
1879 * free string after use
1881 static gchar *make_track_name(VikTrwLayer *vtl)
1883 const gchar basename[] = "REALTIME";
1884 const gint bufsize = sizeof(basename) + 5;
1885 gchar *name = g_malloc(bufsize);
1886 strcpy(name, basename);
1889 while (vik_trw_layer_get_track(vtl, name) != NULL) {
1890 g_snprintf(name, bufsize, "%s#%d", basename, i);
1896 static gboolean rt_gpsd_try_connect(gpointer *data)
1898 VikGpsLayer *vgl = (VikGpsLayer *)data;
1899 #if GPSD_API_MAJOR_VERSION == 3
1900 struct gps_data_t *gpsd = gps_open(vgl->gpsd_host, vgl->gpsd_port);
1903 #elif GPSD_API_MAJOR_VERSION == 4
1904 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1906 if (gps_open_r(vgl->gpsd_host, vgl->gpsd_port, /*(struct gps_data_t *)*/vgl->vgpsd) != 0) {
1907 #elif GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1908 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1909 vgl->vgpsd->gpsd_open = gps_open ( vgl->gpsd_host, vgl->gpsd_port, &vgl->vgpsd->gpsd );
1910 if ( vgl->vgpsd->gpsd_open != 0 ) {
1912 // Delibrately break compilation...
1914 g_debug("Failed to connect to gpsd at %s (port %s). Will retry in %d seconds",
1915 vgl->gpsd_host, vgl->gpsd_port, vgl->gpsd_retry_interval);
1916 return TRUE; /* keep timer running */
1919 #if GPSD_API_MAJOR_VERSION == 3
1920 vgl->vgpsd = realloc(gpsd, sizeof(VglGpsd));
1922 vgl->vgpsd->vgl = vgl;
1924 vgl->realtime_fix.dirty = vgl->last_fix.dirty = FALSE;
1925 /* track alt/time graph uses VIK_DEFAULT_ALTITUDE (0.0) as invalid */
1926 vgl->realtime_fix.fix.altitude = vgl->last_fix.fix.altitude = VIK_DEFAULT_ALTITUDE;
1927 vgl->realtime_fix.fix.speed = vgl->last_fix.fix.speed = NAN;
1929 if (vgl->realtime_record) {
1930 VikTrwLayer *vtl = vgl->trw_children[TRW_REALTIME];
1931 vgl->realtime_track = vik_track_new();
1932 vgl->realtime_track->visible = TRUE;
1933 gchar *name = make_track_name(vtl);
1934 vik_trw_layer_add_track(vtl, name, vgl->realtime_track);
1938 vgl->connected_to_gpsd = TRUE;
1940 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1941 gps_set_raw_hook(&vgl->vgpsd->gpsd, gpsd_raw_hook);
1944 vgl->realtime_io_channel = g_io_channel_unix_new(vgl->vgpsd->gpsd.gps_fd);
1945 vgl->realtime_io_watch_id = g_io_add_watch( vgl->realtime_io_channel,
1946 G_IO_IN | G_IO_ERR | G_IO_HUP, gpsd_data_available, vgl);
1948 #if GPSD_API_MAJOR_VERSION == 3
1949 gps_query(&vgl->vgpsd->gpsd, "w+x");
1951 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1952 if ( gps_stream(&vgl->vgpsd->gpsd, WATCH_ENABLE, NULL) == -1 )
1953 g_critical ( "gps_stream error" );
1956 return FALSE; /* no longer called by timeout */
1959 static gboolean rt_ask_retry(VikGpsLayer *vgl)
1961 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER(vgl),
1962 GTK_DIALOG_DESTROY_WITH_PARENT,
1963 GTK_MESSAGE_QUESTION,
1965 "Failed to connect to gpsd at %s (port %s)\n"
1966 "Should Viking keep trying (every %d seconds)?",
1967 vgl->gpsd_host, vgl->gpsd_port,
1968 vgl->gpsd_retry_interval);
1970 gint res = gtk_dialog_run(GTK_DIALOG(dialog));
1971 gtk_widget_destroy(dialog);
1972 return (res == GTK_RESPONSE_YES);
1975 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed)
1977 vgl->realtime_retry_timer = 0;
1978 if (rt_gpsd_try_connect((gpointer *)vgl)) {
1979 if (vgl->gpsd_retry_interval <= 0) {
1980 g_warning("Failed to connect to gpsd but will not retry because retry intervel was set to %d (which is 0 or negative)", vgl->gpsd_retry_interval);
1983 else if (ask_if_failed && !rt_ask_retry(vgl))
1986 vgl->realtime_retry_timer = g_timeout_add_seconds(vgl->gpsd_retry_interval,
1987 (GSourceFunc)rt_gpsd_try_connect, (gpointer *)vgl);
1992 static void rt_gpsd_disconnect(VikGpsLayer *vgl)
1994 if (vgl->realtime_retry_timer) {
1995 g_source_remove(vgl->realtime_retry_timer);
1996 vgl->realtime_retry_timer = 0;
1998 if (vgl->realtime_io_watch_id) {
1999 g_source_remove(vgl->realtime_io_watch_id);
2000 vgl->realtime_io_watch_id = 0;
2002 if (vgl->realtime_io_channel) {
2003 GError *error = NULL;
2004 g_io_channel_shutdown (vgl->realtime_io_channel, FALSE, &error);
2005 vgl->realtime_io_channel = NULL;
2008 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
2009 gps_stream(&vgl->vgpsd->gpsd, WATCH_DISABLE, NULL);
2011 if ( vgl->vgpsd->gpsd_open == 0 )
2012 (void)gps_close(&vgl->vgpsd->gpsd);
2013 #if GPSD_API_MAJOR_VERSION == 3
2015 #elif GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
2021 if (vgl->realtime_record && vgl->realtime_track) {
2022 if ((vgl->realtime_track->trackpoints == NULL) || (vgl->realtime_track->trackpoints->next == NULL))
2023 vik_trw_layer_delete_track(vgl->trw_children[TRW_REALTIME], vgl->realtime_track);
2024 vgl->realtime_track = NULL;
2026 vgl->connected_to_gpsd = FALSE;
2029 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2])
2031 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
2032 vgl->realtime_tracking = (vgl->realtime_tracking == FALSE);
2034 /* Make sure we are still in the boat with libgps */
2035 g_assert((VIK_GPS_MODE_2D == MODE_2D) && (VIK_GPS_MODE_3D == MODE_3D));
2037 if (vgl->realtime_tracking) {
2038 vgl->first_realtime_trackpoint = TRUE;
2039 if (!rt_gpsd_connect(vgl, TRUE)) {
2040 vgl->first_realtime_trackpoint = FALSE;
2041 vgl->realtime_tracking = FALSE;
2045 else { /* stop realtime tracking */
2046 vgl->first_realtime_trackpoint = FALSE;
2048 rt_gpsd_disconnect(vgl);
2051 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */