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>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include "icons/icons.h"
41 #include <glib/gstdio.h>
42 #include <glib/gprintf.h>
43 #include <glib/gi18n.h>
44 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
49 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp);
50 static void vik_gps_layer_realize ( VikGpsLayer *val, VikTreeview *vt, GtkTreeIter *layer_iter );
51 static void vik_gps_layer_free ( VikGpsLayer *val );
52 static void vik_gps_layer_draw ( VikGpsLayer *val, VikViewport *vp );
53 static VikGpsLayer *vik_gps_layer_new ( VikViewport *vp );
55 static void gps_layer_marshall( VikGpsLayer *val, guint8 **data, gint *len );
56 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
57 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
58 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation );
60 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl );
62 static void gps_layer_change_coord_mode ( VikGpsLayer *val, VikCoordMode mode );
63 static void gps_layer_add_menu_items( VikGpsLayer *vtl, GtkMenu *menu, gpointer vlp );
65 static void gps_upload_cb( gpointer layer_and_vlp[2] );
66 static void gps_download_cb( gpointer layer_and_vlp[2] );
67 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] );
68 static void gps_empty_download_cb( gpointer layer_and_vlp[2] );
69 static void gps_empty_all_cb( gpointer layer_and_vlp[2] );
70 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
71 static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] );
72 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2] );
73 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp);
74 static void rt_gpsd_disconnect(VikGpsLayer *vgl);
75 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed);
78 // Shouldn't need to use these much any more as the protocol is now saved as a string.
79 // They are kept for compatibility loading old .vik files
80 typedef enum {GARMIN_P = 0, MAGELLAN_P, DELORME_P, NAVILINK_P, OLD_NUM_PROTOCOLS} vik_gps_proto;
81 static gchar * protocols_args[] = {"garmin", "magellan", "delbin", "navilink", NULL};
83 static gchar * params_ports[] = {"com1", "usb:", NULL};
85 static gchar * params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
87 /* NUM_PORTS not actually used */
88 /* #define NUM_PORTS (sizeof(params_ports)/sizeof(params_ports[0]) - 1) */
89 /* Compatibility with previous versions */
91 static gchar * old_params_ports[] = {"com1", "usb:", NULL};
93 static gchar * old_params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
95 #define OLD_NUM_PORTS (sizeof(old_params_ports)/sizeof(old_params_ports[0]) - 1)
99 vik_gps_dir direction;
109 GtkWidget *status_label;
110 GtkWidget *gps_label;
111 GtkWidget *ver_label;
114 GtkWidget *trk_label;
115 GtkWidget *rte_label;
116 GtkWidget *progress_label;
117 vik_gps_xfer_type progress_type;
119 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
120 gboolean realtime_tracking;
123 static void gps_session_delete(GpsSession *sess);
125 static gchar *params_groups[] = {
127 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
128 N_("Realtime Tracking Mode"),
132 enum {GROUP_DATA_MODE, GROUP_REALTIME_MODE};
135 static VikLayerParamData gps_protocol_default ( void )
137 VikLayerParamData data;
138 data.s = g_strdup ( "garmin" );
142 static VikLayerParamData gps_port_default ( void )
144 VikLayerParamData data;
145 data.s = g_strdup ( "usb:" );
147 /* Attempt to auto set default USB serial port entry */
148 /* Ordered to make lowest device favourite if available */
149 if (g_access ("/dev/ttyUSB1", R_OK) == 0) {
151 g_free ( (gchar *)data.s );
152 data.s = g_strdup ("/dev/ttyUSB1");
154 if (g_access ("/dev/ttyUSB0", R_OK) == 0) {
156 g_free ( (gchar *)data.s );
157 data.s = g_strdup ("/dev/ttyUSB0");
163 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
164 static gchar *params_vehicle_position[] = {
165 N_("Keep vehicle at center"),
166 N_("Keep vehicle on screen"),
171 VEHICLE_POSITION_CENTERED = 0,
172 VEHICLE_POSITION_ON_SCREEN,
173 VEHICLE_POSITION_NONE,
176 static VikLayerParamData moving_map_method_default ( void ) { return VIK_LPD_UINT ( VEHICLE_POSITION_ON_SCREEN ); }
178 static VikLayerParamData gpsd_host_default ( void )
180 VikLayerParamData data;
181 data.s = g_strdup ( "localhost" );
185 static VikLayerParamData gpsd_port_default ( void )
187 VikLayerParamData data;
188 data.s = g_strdup ( DEFAULT_GPSD_PORT );
192 static VikLayerParamData gpsd_retry_interval_default ( void )
194 VikLayerParamData data;
195 data.s = g_strdup ( "10" );
201 static VikLayerParam gps_layer_params[] = {
202 // NB gps_layer_inst_init() is performed after parameter registeration
203 // thus to give the protocols some potential values use the old static list
204 // TODO: find another way to use gps_layer_inst_init()?
205 { 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
206 { 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 },
207 { 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 },
208 { 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 },
209 { 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 },
210 { 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 },
211 { 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 },
212 { 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 },
213 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
214 { 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 },
215 { 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 },
216 { 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 },
217 { 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 },
218 { 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 },
219 { 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 },
220 { 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 },
221 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
224 PARAM_PROTOCOL=0, PARAM_PORT,
225 PARAM_DOWNLOAD_TRACKS, PARAM_UPLOAD_TRACKS,
226 PARAM_DOWNLOAD_ROUTES, PARAM_UPLOAD_ROUTES,
227 PARAM_DOWNLOAD_WAYPOINTS, PARAM_UPLOAD_WAYPOINTS,
228 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
230 PARAM_REALTIME_CENTER_START,
231 PARAM_VEHICLE_POSITION,
232 PARAM_REALTIME_UPDATE_STATUSBAR,
235 PARAM_GPSD_RETRY_INTERVAL,
236 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
239 VikLayerInterface vik_gps_layer_interface = {
251 sizeof(params_groups)/sizeof(params_groups[0]),
255 (VikLayerFuncCreate) vik_gps_layer_create,
256 (VikLayerFuncRealize) vik_gps_layer_realize,
257 (VikLayerFuncPostRead) NULL,
258 (VikLayerFuncFree) vik_gps_layer_free,
260 (VikLayerFuncProperties) NULL,
261 (VikLayerFuncDraw) vik_gps_layer_draw,
262 (VikLayerFuncChangeCoordMode) gps_layer_change_coord_mode,
264 (VikLayerFuncSetMenuItemsSelection) NULL,
265 (VikLayerFuncGetMenuItemsSelection) NULL,
267 (VikLayerFuncAddMenuItems) gps_layer_add_menu_items,
268 (VikLayerFuncSublayerAddMenuItems) NULL,
270 (VikLayerFuncSublayerRenameRequest) NULL,
271 (VikLayerFuncSublayerToggleVisible) NULL,
272 (VikLayerFuncSublayerTooltip) NULL,
273 (VikLayerFuncLayerTooltip) gps_layer_tooltip,
274 (VikLayerFuncLayerSelected) NULL,
276 (VikLayerFuncMarshall) gps_layer_marshall,
277 (VikLayerFuncUnmarshall) gps_layer_unmarshall,
279 (VikLayerFuncSetParam) gps_layer_set_param,
280 (VikLayerFuncGetParam) gps_layer_get_param,
281 (VikLayerFuncChangeParam) NULL,
283 (VikLayerFuncReadFileData) NULL,
284 (VikLayerFuncWriteFileData) NULL,
286 (VikLayerFuncDeleteItem) NULL,
287 (VikLayerFuncCutItem) NULL,
288 (VikLayerFuncCopyItem) NULL,
289 (VikLayerFuncPasteItem) NULL,
290 (VikLayerFuncFreeCopiedItem) NULL,
291 (VikLayerFuncDragDropRequest) NULL,
293 (VikLayerFuncSelectClick) NULL,
294 (VikLayerFuncSelectMove) NULL,
295 (VikLayerFuncSelectRelease) NULL,
296 (VikLayerFuncSelectedViewportMenu) NULL,
299 enum {TRW_DOWNLOAD=0, TRW_UPLOAD,
300 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
304 static gchar * trw_names[] = {
305 N_("GPS Download"), N_("GPS Upload"),
306 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
307 N_("GPS Realtime Tracking"),
311 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
313 struct gps_data_t gpsd;
318 struct gps_fix_t fix;
319 gint satellites_used;
320 gboolean dirty; /* needs to be saved */
322 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
324 struct _VikGpsLayer {
326 VikTrwLayer * trw_children[NUM_TRW];
327 GList * children; /* used only for writing file */
328 int cur_read_child; /* used only for reading file */
329 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
331 gboolean realtime_tracking; /* set/reset only by the callback */
332 gboolean first_realtime_trackpoint;
336 VikTrack *realtime_track;
338 GIOChannel *realtime_io_channel;
339 guint realtime_io_watch_id;
340 guint realtime_retry_timer;
341 GdkGC *realtime_track_gc;
342 GdkGC *realtime_track_bg_gc;
343 GdkGC *realtime_track_pt_gc;
344 GdkGC *realtime_track_pt1_gc;
345 GdkGC *realtime_track_pt2_gc;
350 gint gpsd_retry_interval;
351 gboolean realtime_record;
352 gboolean realtime_jump_to_start;
353 guint vehicle_position;
354 gboolean realtime_update_statusbar;
355 VikTrackpoint *trkpt;
356 VikTrackpoint *trkpt_prev;
357 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
360 gboolean download_tracks;
361 gboolean download_routes;
362 gboolean download_waypoints;
363 gboolean upload_tracks;
364 gboolean upload_routes;
365 gboolean upload_waypoints;
369 * Overwrite the static setup with dynamically generated GPS Babel device list
371 static void gps_layer_inst_init ( VikGpsLayer *self )
374 // +1 for luck (i.e the NULL terminator)
375 gchar **new_protocols = g_malloc(1 + g_list_length(a_babel_device_list)*sizeof(gpointer));
377 GList *gl = g_list_first ( a_babel_device_list );
379 // should be using label property but use name for now
380 // thus don't need to mess around converting label to name later on
381 new_protocols[new_proto++] = ((BabelDevice*)gl->data)->name;
382 gl = g_list_next ( gl );
384 new_protocols[new_proto] = NULL;
386 vik_gps_layer_interface.params[PARAM_PROTOCOL].widget_data = new_protocols;
389 GType vik_gps_layer_get_type ()
391 static GType val_type = 0;
395 static const GTypeInfo val_info =
397 sizeof (VikGpsLayerClass),
398 NULL, /* base_init */
399 NULL, /* base_finalize */
400 NULL, /* class init */
401 NULL, /* class_finalize */
402 NULL, /* class_data */
403 sizeof (VikGpsLayer),
405 (GInstanceInitFunc) gps_layer_inst_init,
407 val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGpsLayer", &val_info, 0 );
413 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp)
417 VikGpsLayer *rv = vik_gps_layer_new (vp);
418 vik_layer_rename ( VIK_LAYER(rv), vik_gps_layer_interface.name );
420 for (i = 0; i < NUM_TRW; i++) {
421 rv->trw_children[i] = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vp, NULL, FALSE ));
422 vik_layer_set_menu_items_selection(VIK_LAYER(rv->trw_children[i]), VIK_MENU_ITEM_ALL & ~(VIK_MENU_ITEM_CUT|VIK_MENU_ITEM_DELETE));
427 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl )
429 return vgl->protocol;
433 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *datalen )
435 VikLayer *child_layer;
438 GByteArray* b = g_byte_array_new ();
442 #define alm_append(obj, sz) \
444 g_byte_array_append ( b, (guint8 *)&len, sizeof(len) ); \
445 g_byte_array_append ( b, (guint8 *)(obj), len );
447 vik_layer_marshall_params(VIK_LAYER(vgl), &ld, &ll);
451 for (i = 0; i < NUM_TRW; i++) {
452 child_layer = VIK_LAYER(vgl->trw_children[i]);
453 vik_layer_marshall(child_layer, &ld, &ll);
461 g_byte_array_free(b, FALSE);
466 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
468 #define alm_size (*(gint *)data)
470 len -= sizeof(gint) + alm_size; \
471 data += sizeof(gint) + alm_size;
473 VikGpsLayer *rv = vik_gps_layer_new(vvp);
474 VikLayer *child_layer;
477 vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
481 while (len>0 && i < NUM_TRW) {
482 child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
484 rv->trw_children[i++] = (VikTrwLayer *)child_layer;
485 // NB no need to attach signal update handler here
486 // as this will always be performed later on in vik_gps_layer_realize()
490 // g_print("gps_layer_unmarshall ended with len=%d\n", len);
497 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
503 g_free(vgl->protocol);
504 // Backwards Compatibility: previous versions <v1.4 stored protocol as an array index
505 int index = data.s[0] - '0';
506 if (data.s[0] != '\0' &&
507 g_ascii_isdigit (data.s[0]) &&
509 index < OLD_NUM_PROTOCOLS)
510 // It is a single digit: activate compatibility
511 vgl->protocol = g_strdup(protocols_args[index]);
513 vgl->protocol = g_strdup(data.s);
514 g_debug("%s: %s", __FUNCTION__, vgl->protocol);
517 g_warning(_("Unknown GPS Protocol"));
521 g_free(vgl->serial_port);
522 // Backwards Compatibility: previous versions <v0.9.91 stored serial_port as an array index
523 int index = data.s[0] - '0';
524 if (data.s[0] != '\0' &&
525 g_ascii_isdigit (data.s[0]) &&
527 index < OLD_NUM_PORTS)
528 /* It is a single digit: activate compatibility */
529 vgl->serial_port = g_strdup(old_params_ports[index]);
531 vgl->serial_port = g_strdup(data.s);
532 g_debug("%s: %s", __FUNCTION__, vgl->serial_port);
535 g_warning(_("Unknown serial port device"));
537 case PARAM_DOWNLOAD_TRACKS:
538 vgl->download_tracks = data.b;
540 case PARAM_UPLOAD_TRACKS:
541 vgl->upload_tracks = data.b;
543 case PARAM_DOWNLOAD_ROUTES:
544 vgl->download_routes = data.b;
546 case PARAM_UPLOAD_ROUTES:
547 vgl->upload_routes = data.b;
549 case PARAM_DOWNLOAD_WAYPOINTS:
550 vgl->download_waypoints = data.b;
552 case PARAM_UPLOAD_WAYPOINTS:
553 vgl->upload_waypoints = data.b;
555 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
556 case PARAM_GPSD_HOST:
559 g_free(vgl->gpsd_host);
560 vgl->gpsd_host = g_strdup(data.s);
563 case PARAM_GPSD_PORT:
566 g_free(vgl->gpsd_port);
567 vgl->gpsd_port = g_strdup(data.s);
570 case PARAM_GPSD_RETRY_INTERVAL:
571 vgl->gpsd_retry_interval = strtol(data.s, NULL, 10);
573 case PARAM_REALTIME_REC:
574 vgl->realtime_record = data.b;
576 case PARAM_REALTIME_CENTER_START:
577 vgl->realtime_jump_to_start = data.b;
579 case PARAM_VEHICLE_POSITION:
580 vgl->vehicle_position = data.u;
582 case PARAM_REALTIME_UPDATE_STATUSBAR:
583 vgl->realtime_update_statusbar = data.b;
585 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
587 g_warning("gps_layer_set_param(): unknown parameter");
593 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation )
595 VikLayerParamData rv;
599 rv.s = vgl->protocol;
600 g_debug("%s: %s", __FUNCTION__, rv.s);
603 rv.s = vgl->serial_port;
604 g_debug("%s: %s", __FUNCTION__, rv.s);
606 case PARAM_DOWNLOAD_TRACKS:
607 rv.b = vgl->download_tracks;
609 case PARAM_UPLOAD_TRACKS:
610 rv.b = vgl->upload_tracks;
612 case PARAM_DOWNLOAD_ROUTES:
613 rv.b = vgl->download_routes;
615 case PARAM_UPLOAD_ROUTES:
616 rv.b = vgl->upload_routes;
618 case PARAM_DOWNLOAD_WAYPOINTS:
619 rv.b = vgl->download_waypoints;
621 case PARAM_UPLOAD_WAYPOINTS:
622 rv.b = vgl->upload_waypoints;
624 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
625 case PARAM_GPSD_HOST:
626 rv.s = vgl->gpsd_host ? vgl->gpsd_host : "";
628 case PARAM_GPSD_PORT:
629 rv.s = vgl->gpsd_port ? vgl->gpsd_port : g_strdup(DEFAULT_GPSD_PORT);
631 case PARAM_GPSD_RETRY_INTERVAL:
632 rv.s = g_strdup_printf("%d", vgl->gpsd_retry_interval);
634 case PARAM_REALTIME_REC:
635 rv.b = vgl->realtime_record;
637 case PARAM_REALTIME_CENTER_START:
638 rv.b = vgl->realtime_jump_to_start;
640 case PARAM_VEHICLE_POSITION:
641 rv.u = vgl->vehicle_position;
643 case PARAM_REALTIME_UPDATE_STATUSBAR:
644 rv.u = vgl->realtime_update_statusbar;
646 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
648 g_warning(_("%s: unknown parameter"), __FUNCTION__);
654 VikGpsLayer *vik_gps_layer_new (VikViewport *vp)
657 VikGpsLayer *vgl = VIK_GPS_LAYER ( g_object_new ( VIK_GPS_LAYER_TYPE, NULL ) );
658 vik_layer_set_type ( VIK_LAYER(vgl), VIK_LAYER_GPS );
659 for (i = 0; i < NUM_TRW; i++) {
660 vgl->trw_children[i] = NULL;
662 vgl->children = NULL;
663 vgl->cur_read_child = 0;
665 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
666 vgl->first_realtime_trackpoint = FALSE;
668 vgl->trkpt_prev = NULL;
670 vgl->realtime_io_channel = NULL;
671 vgl->realtime_io_watch_id = 0;
672 vgl->realtime_retry_timer = 0;
674 vgl->realtime_track_gc = vik_viewport_new_gc ( vp, "#203070", 2 );
675 vgl->realtime_track_bg_gc = vik_viewport_new_gc ( vp, "grey", 2 );
676 vgl->realtime_track_pt1_gc = vik_viewport_new_gc ( vp, "red", 2 );
677 vgl->realtime_track_pt2_gc = vik_viewport_new_gc ( vp, "green", 2 );
678 vgl->realtime_track_pt_gc = vgl->realtime_track_pt1_gc;
680 vgl->realtime_track = NULL;
681 #endif // VIK_CONFIG_REALTIME_GPS_TRACKING
683 vik_layer_set_defaults ( VIK_LAYER(vgl), vp );
688 static void vik_gps_layer_draw ( VikGpsLayer *vgl, VikViewport *vp )
692 VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( vp ));
694 for (i = 0; i < NUM_TRW; i++) {
695 vl = VIK_LAYER(vgl->trw_children[i]);
697 if ( vik_viewport_get_half_drawn ( vp ) ) {
698 vik_viewport_set_half_drawn ( vp, FALSE );
699 vik_viewport_snapshot_load( vp );
701 vik_viewport_snapshot_save( vp );
704 if (!vik_viewport_get_half_drawn(vp))
705 vik_layer_draw ( vl, vp );
707 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
708 if (vgl->realtime_tracking) {
709 if (VIK_LAYER(vgl) == trigger) {
710 if ( vik_viewport_get_half_drawn ( vp ) ) {
711 vik_viewport_set_half_drawn ( vp, FALSE );
712 vik_viewport_snapshot_load( vp );
714 vik_viewport_snapshot_save( vp );
717 if (!vik_viewport_get_half_drawn(vp))
718 realtime_tracking_draw(vgl, vp);
720 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
723 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode )
726 for (i = 0; i < NUM_TRW; i++) {
727 vik_layer_change_coord_mode(VIK_LAYER(vgl->trw_children[i]), mode);
731 static void gps_layer_add_menu_items( VikGpsLayer *vgl, GtkMenu *menu, gpointer vlp )
733 static gpointer pass_along[2];
738 item = gtk_menu_item_new();
739 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
740 gtk_widget_show ( item );
743 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS") );
744 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
745 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_upload_cb), pass_along );
746 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
747 gtk_widget_show ( item );
749 item = gtk_image_menu_item_new_with_mnemonic ( _("Download from _GPS") );
750 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
751 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_download_cb), pass_along );
752 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
753 gtk_widget_show ( item );
755 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
756 item = gtk_image_menu_item_new_with_mnemonic ( vgl->realtime_tracking ?
757 "_Stop Realtime Tracking" :
758 "_Start Realtime Tracking" );
759 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, vgl->realtime_tracking ?
760 gtk_image_new_from_stock (GTK_STOCK_MEDIA_STOP, GTK_ICON_SIZE_MENU) :
761 gtk_image_new_from_stock (GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_MENU) );
762 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_start_stop_tracking_cb), pass_along );
763 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
764 gtk_widget_show ( item );
766 item = gtk_menu_item_new();
767 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
768 gtk_widget_show ( item );
770 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _Realtime") );
771 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
772 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_realtime_cb), pass_along );
773 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
774 gtk_widget_show ( item );
775 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
777 item = gtk_image_menu_item_new_with_mnemonic ( _("E_mpty Upload") );
778 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
779 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_upload_cb), pass_along );
780 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
781 gtk_widget_show ( item );
783 item = gtk_image_menu_item_new_with_mnemonic ( _("_Empty Download") );
784 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
785 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_download_cb), pass_along );
786 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
787 gtk_widget_show ( item );
789 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _All") );
790 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
791 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_all_cb), pass_along );
792 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
793 gtk_widget_show ( item );
797 static void disconnect_layer_signal ( VikLayer *vl, VikGpsLayer *vgl )
799 guint number_handlers = g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, vgl);
800 if ( number_handlers != 1 ) {
801 g_critical(_("Unexpected number of disconnected handlers: %d"), number_handlers);
805 static void vik_gps_layer_free ( VikGpsLayer *vgl )
808 for (i = 0; i < NUM_TRW; i++) {
809 if (vgl->vl.realized)
810 disconnect_layer_signal(VIK_LAYER(vgl->trw_children[i]), vgl);
811 g_object_unref(vgl->trw_children[i]);
813 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
814 rt_gpsd_disconnect(vgl);
815 if (vgl->realtime_track_gc != NULL)
816 g_object_unref(vgl->realtime_track_gc);
817 if (vgl->realtime_track_bg_gc != NULL)
818 g_object_unref(vgl->realtime_track_bg_gc);
819 if (vgl->realtime_track_pt1_gc != NULL)
820 g_object_unref(vgl->realtime_track_pt1_gc);
821 if (vgl->realtime_track_pt2_gc != NULL)
822 g_object_unref(vgl->realtime_track_pt2_gc);
823 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
826 static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter )
831 // TODO set to garmin by default
832 //if (a_babel_device_list)
833 // device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
834 // Need to access uibuild widgets somehow....
836 for (ix = 0; ix < NUM_TRW; ix++) {
837 VikLayer * trw = VIK_LAYER(vgl->trw_children[ix]);
838 vik_treeview_add_layer ( VIK_LAYER(vgl)->vt, layer_iter, &iter,
839 _(trw_names[ix]), vgl, TRUE,
840 trw, trw->type, trw->type );
841 if ( ! trw->visible )
842 vik_treeview_item_set_visible ( VIK_LAYER(vgl)->vt, &iter, FALSE );
843 vik_layer_realize ( trw, VIK_LAYER(vgl)->vt, &iter );
844 g_signal_connect_swapped ( G_OBJECT(trw), "update", G_CALLBACK(vik_layer_emit_update_secondary), vgl );
848 const GList *vik_gps_layer_get_children ( VikGpsLayer *vgl )
852 if (vgl->children == NULL) {
853 for (i = NUM_TRW - 1; i >= 0; i--)
854 vgl->children = g_list_prepend(vgl->children, vgl->trw_children[i]);
856 return vgl->children;
859 VikTrwLayer * vik_gps_layer_get_a_child(VikGpsLayer *vgl)
861 g_assert ((vgl->cur_read_child >= 0) && (vgl->cur_read_child < NUM_TRW));
863 VikTrwLayer * vtl = vgl->trw_children[vgl->cur_read_child];
864 if (++(vgl->cur_read_child) >= NUM_TRW)
865 vgl->cur_read_child = 0;
869 gboolean vik_gps_layer_is_empty ( VikGpsLayer *vgl )
871 if ( vgl->trw_children[0] )
876 static void gps_session_delete(GpsSession *sess)
879 g_mutex_free(sess->mutex);
880 g_free(sess->cmd_args);
886 static void set_total_count(gint cnt, GpsSession *sess)
890 g_mutex_lock(sess->mutex);
892 const gchar *tmp_str;
893 if (sess->direction == GPS_DOWN)
895 switch (sess->progress_type) {
896 case WPT: tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt); sess->total_count = cnt; break;
897 case TRK: tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt); sess->total_count = cnt; break;
900 // 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
901 gint mycnt = (cnt / 2) + 1;
902 tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt); break;
903 sess->total_count = mycnt;
909 switch (sess->progress_type) {
910 case WPT: tmp_str = ngettext("Uploading %d waypoint...", "Uploading %d waypoints...", cnt); break;
911 case TRK: tmp_str = ngettext("Uploading %d trackpoint...", "Uploading %d trackpoints...", cnt); break;
912 default: tmp_str = ngettext("Uploading %d routepoint...", "Uploading %d routepoints...", cnt); break;
916 g_snprintf(s, 128, tmp_str, cnt);
917 gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
918 gtk_widget_show ( sess->progress_label );
919 sess->total_count = cnt;
921 g_mutex_unlock(sess->mutex);
925 static void set_current_count(gint cnt, GpsSession *sess)
928 const gchar *tmp_str;
931 g_mutex_lock(sess->mutex);
933 if (cnt < sess->total_count) {
934 if (sess->direction == GPS_DOWN)
936 switch (sess->progress_type) {
937 case WPT: tmp_str = ngettext("Downloaded %d out of %d waypoint...", "Downloaded %d out of %d waypoints...", sess->total_count); break;
938 case TRK: tmp_str = ngettext("Downloaded %d out of %d trackpoint...", "Downloaded %d out of %d trackpoints...", sess->total_count); break;
939 default: tmp_str = ngettext("Downloaded %d out of %d routepoint...", "Downloaded %d out of %d routepoints...", sess->total_count); break;
943 switch (sess->progress_type) {
944 case WPT: tmp_str = ngettext("Uploaded %d out of %d waypoint...", "Uploaded %d out of %d waypoints...", sess->total_count); break;
945 case TRK: tmp_str = ngettext("Uploaded %d out of %d trackpoint...", "Uploaded %d out of %d trackpoints...", sess->total_count); break;
946 default: tmp_str = ngettext("Uploaded %d out of %d routepoint...", "Uploaded %d out of %d routepoints...", sess->total_count); break;
949 g_snprintf(s, 128, tmp_str, cnt, sess->total_count);
951 if (sess->direction == GPS_DOWN)
953 switch (sess->progress_type) {
954 case WPT: tmp_str = ngettext("Downloaded %d waypoint", "Downloaded %d waypoints", cnt); break;
955 case TRK: tmp_str = ngettext("Downloaded %d trackpoint", "Downloaded %d trackpoints", cnt); break;
956 default: tmp_str = ngettext("Downloaded %d routepoint", "Downloaded %d routepoints", cnt); break;
960 switch (sess->progress_type) {
961 case WPT: tmp_str = ngettext("Uploaded %d waypoint", "Uploaded %d waypoints", cnt); break;
962 case TRK: tmp_str = ngettext("Uploaded %d trackpoint", "Uploaded %d trackpoints", cnt); break;
963 default: tmp_str = ngettext("Uploaded %d routepoint", "Uploaded %d routepoints", cnt); break;
966 g_snprintf(s, 128, tmp_str, cnt);
968 gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
970 g_mutex_unlock(sess->mutex);
974 static void set_gps_info(const gchar *info, GpsSession *sess)
978 g_mutex_lock(sess->mutex);
980 g_snprintf(s, 256, _("GPS Device: %s"), info);
981 gtk_label_set_text ( GTK_LABEL(sess->gps_label), s );
983 g_mutex_unlock(sess->mutex);
988 * Common processing for GPS Device information
989 * It doesn't matter whether we're uploading or downloading
991 static void process_line_for_gps_info ( const gchar *line, GpsSession *sess )
993 if (strstr(line, "PRDDAT")) {
994 gchar **tokens = g_strsplit(line, " ", 0);
1000 while (tokens[n_tokens])
1003 // I'm not entirely clear what information this is trying to get...
1004 // Obviously trying to decipher some kind of text/naming scheme
1005 // Anyway this will be superceded if there is 'Unit:' information
1007 for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
1009 sscanf(tokens[i], "%x", &ch);
1013 set_gps_info(info, sess);
1018 /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
1019 if (strstr(line, "Unit:")) {
1020 gchar **tokens = g_strsplit(line, "\t", 0);
1022 while (tokens[n_tokens])
1026 set_gps_info(tokens[1], sess);
1032 static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1036 gdk_threads_enter ();
1037 g_mutex_lock(sess->mutex);
1039 g_mutex_unlock(sess->mutex);
1040 gps_session_delete(sess);
1041 gdk_threads_leave();
1042 g_thread_exit ( NULL );
1044 g_mutex_unlock(sess->mutex);
1045 gdk_threads_leave ();
1048 case BABEL_DIAG_OUTPUT:
1049 line = (gchar *)data;
1051 gdk_threads_enter();
1052 g_mutex_lock(sess->mutex);
1054 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1056 g_mutex_unlock(sess->mutex);
1057 gdk_threads_leave();
1059 /* tells us the type of items that will follow */
1060 if (strstr(line, "Xfer Wpt")) {
1061 sess->progress_label = sess->wp_label;
1062 sess->progress_type = WPT;
1064 if (strstr(line, "Xfer Trk")) {
1065 sess->progress_label = sess->trk_label;
1066 sess->progress_type = TRK;
1068 if (strstr(line, "Xfer Rte")) {
1069 sess->progress_label = sess->rte_label;
1070 sess->progress_type = RTE;
1073 process_line_for_gps_info ( line, sess );
1075 if (strstr(line, "RECORD")) {
1078 if (strlen(line) > 20) {
1079 sscanf(line+17, "%x", &lsb);
1080 sscanf(line+20, "%x", &msb);
1081 cnt = lsb + msb * 256;
1082 set_total_count(cnt, sess);
1086 if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1088 set_current_count(sess->count, sess);
1099 static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1104 gdk_threads_enter ();
1105 g_mutex_lock(sess->mutex);
1107 g_mutex_unlock(sess->mutex);
1108 gps_session_delete(sess);
1109 gdk_threads_leave();
1110 g_thread_exit ( NULL );
1112 g_mutex_unlock(sess->mutex);
1113 gdk_threads_leave ();
1116 case BABEL_DIAG_OUTPUT:
1117 line = (gchar *)data;
1119 gdk_threads_enter();
1120 g_mutex_lock(sess->mutex);
1122 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1124 g_mutex_unlock(sess->mutex);
1125 gdk_threads_leave();
1127 process_line_for_gps_info ( line, sess );
1129 if (strstr(line, "RECORD")) {
1132 if (strlen(line) > 20) {
1133 sscanf(line+17, "%x", &lsb);
1134 sscanf(line+20, "%x", &msb);
1135 cnt = lsb + msb * 256;
1136 /* set_total_count(cnt, sess); */
1140 if ( strstr(line, "WPTDAT")) {
1141 if (sess->count == 0) {
1142 sess->progress_label = sess->wp_label;
1143 sess->progress_type = WPT;
1144 set_total_count(cnt, sess);
1147 set_current_count(sess->count, sess);
1149 if ( strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1150 if (sess->count == 0) {
1151 sess->progress_label = sess->rte_label;
1152 sess->progress_type = RTE;
1153 // 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
1154 // Anyway since we're uploading - we should know how many points we're going to put!
1155 cnt = (cnt / 2) + 1;
1156 set_total_count(cnt, sess);
1159 set_current_count(sess->count, sess);
1161 if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
1162 if (sess->count == 0) {
1163 sess->progress_label = sess->trk_label;
1164 sess->progress_type = TRK;
1165 set_total_count(cnt, sess);
1168 set_current_count(sess->count, sess);
1179 static void gps_comm_thread(GpsSession *sess)
1183 if (sess->direction == GPS_DOWN)
1184 result = a_babel_convert_from (sess->vtl, sess->cmd_args, sess->port,
1185 (BabelStatusFunc) gps_download_progress_func, sess, NULL);
1187 result = a_babel_convert_to (sess->vtl, sess->track, sess->cmd_args, sess->port,
1188 (BabelStatusFunc) gps_upload_progress_func, sess);
1192 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: couldn't find gpsbabel.") );
1195 g_mutex_lock(sess->mutex);
1197 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Done.") );
1198 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE );
1199 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE );
1201 /* Do not change the view if we are following the current GPS position */
1202 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1203 if (!sess->realtime_tracking)
1206 if ( sess->vvp && sess->direction == GPS_DOWN ) {
1207 /* View the data available */
1208 vik_trw_layer_auto_set_view ( sess->vtl, sess->vvp) ;
1209 vik_layer_emit_update ( VIK_LAYER(sess->vtl) ); // NB update from background thread
1215 g_mutex_unlock(sess->mutex);
1218 g_mutex_lock(sess->mutex);
1221 g_mutex_unlock(sess->mutex);
1224 g_mutex_unlock(sess->mutex);
1225 gps_session_delete(sess);
1227 g_thread_exit(NULL);
1232 * @vtl: The TrackWaypoint layer to operate on
1233 * @track: Operate on a particular track when specified
1234 * @dir: The direction of the transfer
1235 * @protocol: The GPS device communication protocol
1236 * @port: The GPS serial port
1237 * @tracking: If tracking then viewport display update will be skipped
1238 * @vvp: A viewport is required as the display may get updated
1239 * @vlp: A layers panel is needed for uploading as the items maybe modified
1240 * @do_tracks: Whether tracks shoud be processed
1241 * @do_waypoints: Whether waypoints shoud be processed
1242 * @turn_off: Whether we should attempt to turn off the GPS device after the transfer (only some devices support this)
1244 * Talk to a GPS Device using a thread which updates a dialog with the progress
1246 gint vik_gps_comm ( VikTrwLayer *vtl,
1253 VikLayersPanel *vlp,
1256 gboolean do_waypoints,
1259 GpsSession *sess = g_malloc(sizeof(GpsSession));
1260 char *tracks = NULL;
1261 char *routes = NULL;
1262 char *waypoints = NULL;
1264 sess->mutex = g_mutex_new();
1265 sess->direction = dir;
1267 sess->track = track;
1268 sess->port = g_strdup(port);
1270 sess->window_title = (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload");
1273 // This must be done inside the main thread as the uniquify causes screen updates
1274 // (originally performed this nearer the point of upload in the thread)
1275 if ( dir == GPS_UP ) {
1276 // Enforce unique names in the layer upload to the GPS device
1277 // NB this may only be a Garmin device restriction (and may be not every Garmin device either...)
1278 // Thus this maintains the older code in built restriction
1279 if ( ! vik_trw_layer_uniquify ( sess->vtl, vlp ) )
1280 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(sess->vtl))), VIK_STATUSBAR_INFO,
1281 _("Warning - GPS Upload items may overwrite each other") );
1284 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1285 sess->realtime_tracking = tracking;
1301 sess->cmd_args = g_strdup_printf("-D 9 %s %s %s -%c %s",
1302 tracks, routes, waypoints, (dir == GPS_DOWN) ? 'i' : 'o', protocol);
1306 // Only create dialog if we're going to do some transferring
1307 if ( do_tracks || do_waypoints || do_routes ) {
1308 sess->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 );
1309 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog),
1310 GTK_RESPONSE_ACCEPT, FALSE );
1311 gtk_window_set_title ( GTK_WINDOW(sess->dialog), sess->window_title );
1313 sess->status_label = gtk_label_new (_("Status: detecting gpsbabel"));
1314 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->status_label, FALSE, FALSE, 5 );
1315 gtk_widget_show_all(sess->status_label);
1317 sess->gps_label = gtk_label_new (_("GPS device: N/A"));
1318 sess->ver_label = gtk_label_new ("");
1319 sess->id_label = gtk_label_new ("");
1320 sess->wp_label = gtk_label_new ("");
1321 sess->trk_label = gtk_label_new ("");
1322 sess->rte_label = gtk_label_new ("");
1324 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->gps_label, FALSE, FALSE, 5 );
1325 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->wp_label, FALSE, FALSE, 5 );
1326 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->trk_label, FALSE, FALSE, 5 );
1327 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->rte_label, FALSE, FALSE, 5 );
1329 gtk_widget_show_all(sess->dialog);
1331 sess->progress_label = sess->wp_label;
1332 sess->total_count = -1;
1334 // Starting gps read/write thread
1335 #if GLIB_CHECK_VERSION (2, 32, 0)
1336 g_thread_try_new ( "gps_comm_thread", (GThreadFunc)gps_comm_thread, sess, NULL );
1338 g_thread_create ( (GThreadFunc)gps_comm_thread, sess, FALSE, NULL );
1341 gtk_dialog_set_default_response ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT );
1342 gtk_dialog_run(GTK_DIALOG(sess->dialog));
1344 gtk_widget_destroy(sess->dialog);
1348 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No GPS items selected for transfer.") );
1351 g_mutex_lock(sess->mutex);
1354 sess->ok = FALSE; /* tell thread to stop */
1355 g_mutex_unlock(sess->mutex);
1359 // No need for thread for powering off device (should be quick operation...) - so use babel command directly:
1360 gchar *device_off = g_strdup_printf("-i %s,%s", protocol, "power_off");
1361 gboolean result = a_babel_convert_from (NULL, (const char*)device_off, (const char*)port, NULL, NULL, NULL);
1363 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not turn off device.") );
1364 g_free ( device_off );
1366 g_mutex_unlock(sess->mutex);
1367 gps_session_delete(sess);
1373 static void gps_upload_cb( gpointer layer_and_vlp[2] )
1375 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1376 VikLayersPanel *vlp = VIK_LAYERS_PANEL(layer_and_vlp[1]);
1377 VikTrwLayer *vtl = vgl->trw_children[TRW_UPLOAD];
1378 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1379 VikViewport *vvp = vik_window_viewport(vw);
1380 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);
1383 static void gps_download_cb( gpointer layer_and_vlp[2] )
1385 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1386 VikTrwLayer *vtl = vgl->trw_children[TRW_DOWNLOAD];
1387 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1388 VikViewport *vvp = vik_window_viewport(vw);
1389 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1390 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);
1392 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);
1396 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] )
1398 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1399 // Get confirmation from the user
1400 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1401 _("Are you sure you want to delete GPS Upload data?"),
1404 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1405 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1406 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1409 static void gps_empty_download_cb( gpointer layer_and_vlp[2] )
1411 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1412 // Get confirmation from the user
1413 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1414 _("Are you sure you want to delete GPS Download data?"),
1417 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1418 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1419 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1422 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1423 static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] )
1425 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1426 // Get confirmation from the user
1427 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1428 _("Are you sure you want to delete GPS Realtime data?"),
1431 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1432 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1436 static void gps_empty_all_cb( gpointer layer_and_vlp[2] )
1438 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1439 // Get confirmation from the user
1440 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1441 _("Are you sure you want to delete All GPS data?"),
1444 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1445 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1446 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1447 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1448 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1449 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1450 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1451 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1452 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1456 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1457 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp)
1461 struct LatLon lnw, lse;
1462 vik_viewport_screen_to_coord ( vp, -20, -20, &nw );
1463 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp)+20, vik_viewport_get_width(vp)+20, &se );
1464 vik_coord_to_latlon ( &nw, &lnw );
1465 vik_coord_to_latlon ( &se, &lse );
1466 if ( vgl->realtime_fix.fix.latitude > lse.lat &&
1467 vgl->realtime_fix.fix.latitude < lnw.lat &&
1468 vgl->realtime_fix.fix.longitude > lnw.lon &&
1469 vgl->realtime_fix.fix.longitude < lse.lon &&
1470 !isnan (vgl->realtime_fix.fix.track) ) {
1473 gint half_back_x, half_back_y;
1474 gint half_back_bg_x, half_back_bg_y;
1477 gint side1_x, side1_y, side2_x, side2_y;
1478 gint side1bg_x, side1bg_y, side2bg_x, side2bg_y;
1480 ll.lat = vgl->realtime_fix.fix.latitude;
1481 ll.lon = vgl->realtime_fix.fix.longitude;
1482 vik_coord_load_from_latlon ( &gps, vik_viewport_get_coord_mode(vp), &ll);
1483 vik_viewport_coord_to_screen ( vp, &gps, &x, &y );
1485 gdouble heading_cos = cos(DEG2RAD(vgl->realtime_fix.fix.track));
1486 gdouble heading_sin = sin(DEG2RAD(vgl->realtime_fix.fix.track));
1488 half_back_y = y+8*heading_cos;
1489 half_back_x = x-8*heading_sin;
1490 half_back_bg_y = y+10*heading_cos;
1491 half_back_bg_x = x-10*heading_sin;
1493 pt_y = half_back_y-24*heading_cos;
1494 pt_x = half_back_x+24*heading_sin;
1495 //ptbg_y = half_back_bg_y-28*heading_cos;
1496 ptbg_x = half_back_bg_x+28*heading_sin;
1498 side1_y = half_back_y+9*heading_sin;
1499 side1_x = half_back_x+9*heading_cos;
1500 side1bg_y = half_back_bg_y+11*heading_sin;
1501 side1bg_x = half_back_bg_x+11*heading_cos;
1503 side2_y = half_back_y-9*heading_sin;
1504 side2_x = half_back_x-9*heading_cos;
1505 side2bg_y = half_back_bg_y-11*heading_sin;
1506 side2bg_x = half_back_bg_x-11*heading_cos;
1508 GdkPoint trian[3] = { { pt_x, pt_y }, {side1_x, side1_y}, {side2_x, side2_y} };
1509 GdkPoint trian_bg[3] = { { ptbg_x, pt_y }, {side1bg_x, side1bg_y}, {side2bg_x, side2bg_y} };
1511 vik_viewport_draw_polygon ( vp, vgl->realtime_track_bg_gc, TRUE, trian_bg, 3 );
1512 vik_viewport_draw_polygon ( vp, vgl->realtime_track_gc, TRUE, trian, 3 );
1513 vik_viewport_draw_rectangle ( vp,
1514 (vgl->realtime_fix.fix.mode > MODE_2D) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc,
1515 TRUE, x-2, y-2, 4, 4 );
1516 //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;
1520 static VikTrackpoint* create_realtime_trackpoint(VikGpsLayer *vgl, gboolean forced)
1525 /* Note that fix.time is a double, but it should not affect the precision
1527 time_t cur_timestamp = vgl->realtime_fix.fix.time;
1528 time_t last_timestamp = vgl->last_fix.fix.time;
1530 if (cur_timestamp < last_timestamp) {
1534 if (vgl->realtime_record && vgl->realtime_fix.dirty) {
1535 gboolean replace = FALSE;
1536 int heading = isnan(vgl->realtime_fix.fix.track) ? 0 : (int)floor(vgl->realtime_fix.fix.track);
1537 int last_heading = isnan(vgl->last_fix.fix.track) ? 0 : (int)floor(vgl->last_fix.fix.track);
1538 int alt = isnan(vgl->realtime_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->realtime_fix.fix.altitude);
1539 int last_alt = isnan(vgl->last_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->last_fix.fix.altitude);
1540 if (((last_tp = g_list_last(vgl->realtime_track->trackpoints)) != NULL) &&
1541 (vgl->realtime_fix.fix.mode > MODE_2D) &&
1542 (vgl->last_fix.fix.mode <= MODE_2D) &&
1543 ((cur_timestamp - last_timestamp) < 2)) {
1544 g_free(last_tp->data);
1545 vgl->realtime_track->trackpoints = g_list_delete_link(vgl->realtime_track->trackpoints, last_tp);
1549 ((cur_timestamp != last_timestamp) &&
1551 ((heading < last_heading) && (heading < (last_heading - 3))) ||
1552 ((heading > last_heading) && (heading > (last_heading + 3))) ||
1553 ((alt != VIK_DEFAULT_ALTITUDE) && (alt != last_alt)))))) {
1554 /* TODO: check for new segments */
1555 VikTrackpoint *tp = vik_trackpoint_new();
1556 tp->newsegment = FALSE;
1557 tp->has_timestamp = TRUE;
1558 tp->timestamp = vgl->realtime_fix.fix.time;
1560 /* speed only available for 3D fix. Check for NAN when use this speed */
1561 tp->speed = vgl->realtime_fix.fix.speed;
1562 tp->course = vgl->realtime_fix.fix.track;
1563 tp->nsats = vgl->realtime_fix.satellites_used;
1564 tp->fix_mode = vgl->realtime_fix.fix.mode;
1566 ll.lat = vgl->realtime_fix.fix.latitude;
1567 ll.lon = vgl->realtime_fix.fix.longitude;
1568 vik_coord_load_from_latlon(&tp->coord,
1569 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1571 vik_track_add_trackpoint ( vgl->realtime_track, tp, TRUE ); // Ensure bounds is recalculated
1572 vgl->realtime_fix.dirty = FALSE;
1573 vgl->realtime_fix.satellites_used = 0;
1574 vgl->last_fix = vgl->realtime_fix;
1581 #define VIK_SETTINGS_GPS_STATUSBAR_FORMAT "gps_statusbar_format"
1583 static void update_statusbar ( VikGpsLayer *vgl, VikWindow *vw )
1585 gchar *statusbar_format_code = NULL;
1586 gboolean need2free = FALSE;
1587 if ( !a_settings_get_string ( VIK_SETTINGS_GPS_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
1588 // Otherwise use default
1589 statusbar_format_code = g_strdup ( "GSA" );
1593 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, vgl->trkpt, vgl->trkpt_prev, vgl->realtime_track );
1594 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
1598 g_free ( statusbar_format_code );
1602 static void gpsd_raw_hook(VglGpsd *vgpsd, gchar *data)
1604 gboolean update_all = FALSE;
1605 VikGpsLayer *vgl = vgpsd->vgl;
1607 if (!vgl->realtime_tracking) {
1608 g_warning("%s: receiving GPS data while not in realtime mode", __PRETTY_FUNCTION__);
1612 if ((vgpsd->gpsd.fix.mode >= MODE_2D) &&
1613 !isnan(vgpsd->gpsd.fix.latitude) &&
1614 !isnan(vgpsd->gpsd.fix.longitude)) {
1616 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1617 VikViewport *vvp = vik_window_viewport(vw);
1618 vgl->realtime_fix.fix = vgpsd->gpsd.fix;
1619 vgl->realtime_fix.satellites_used = vgpsd->gpsd.satellites_used;
1620 vgl->realtime_fix.dirty = TRUE;
1623 VikCoord vehicle_coord;
1625 ll.lat = vgl->realtime_fix.fix.latitude;
1626 ll.lon = vgl->realtime_fix.fix.longitude;
1627 vik_coord_load_from_latlon(&vehicle_coord,
1628 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1630 if ((vgl->vehicle_position == VEHICLE_POSITION_CENTERED) ||
1631 (vgl->realtime_jump_to_start && vgl->first_realtime_trackpoint)) {
1632 vik_viewport_set_center_coord(vvp, &vehicle_coord);
1635 else if (vgl->vehicle_position == VEHICLE_POSITION_ON_SCREEN) {
1638 const int px = 20; /* adjust ment in pixels to make sure vehicle is inside the box */
1639 gint width = vik_viewport_get_width(vvp);
1640 gint height = vik_viewport_get_height(vvp);
1643 vik_viewport_coord_to_screen(vvp, &vehicle_coord, &vx, &vy);
1645 if (vx < (width/hdiv))
1646 vik_viewport_set_center_screen(vvp, vx - width/2 + width/hdiv + px, vy);
1647 else if (vx > (width - width/hdiv))
1648 vik_viewport_set_center_screen(vvp, vx + width/2 - width/hdiv - px, vy);
1649 else if (vy < (height/vdiv))
1650 vik_viewport_set_center_screen(vvp, vx, vy - height/2 + height/vdiv + px);
1651 else if (vy > (height - height/vdiv))
1652 vik_viewport_set_center_screen(vvp, vx, vy + height/2 - height/vdiv - px);
1657 vgl->first_realtime_trackpoint = FALSE;
1659 vgl->trkpt = create_realtime_trackpoint ( vgl, FALSE );
1662 if ( vgl->realtime_update_statusbar )
1663 update_statusbar ( vgl, vw );
1664 vgl->trkpt_prev = vgl->trkpt;
1667 vik_layer_emit_update ( update_all ? VIK_LAYER(vgl) : VIK_LAYER(vgl->trw_children[TRW_REALTIME]) ); // NB update from background thread
1671 static gboolean gpsd_data_available(GIOChannel *source, GIOCondition condition, gpointer data)
1673 VikGpsLayer *vgl = data;
1674 if (condition == G_IO_IN) {
1675 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1676 if (!gps_poll(&vgl->vgpsd->gpsd)) {
1677 #elif GPSD_API_MAJOR_VERSION == 5
1678 if (gps_read(&vgl->vgpsd->gpsd) > -1) {
1679 // Reuse old function to perform operations on the new GPS data
1680 gpsd_raw_hook(vgl->vgpsd, NULL);
1687 g_warning("Disconnected from gpsd. Trying to reconnect");
1688 rt_gpsd_disconnect(vgl);
1689 rt_gpsd_connect(vgl, FALSE);
1692 return FALSE; /* no further calling */
1695 static gchar *make_track_name(VikTrwLayer *vtl)
1697 const gchar basename[] = "REALTIME";
1698 const gint bufsize = sizeof(basename) + 5;
1699 gchar *name = g_malloc(bufsize);
1700 strcpy(name, basename);
1703 while (vik_trw_layer_get_track(vtl, name) != NULL) {
1704 g_snprintf(name, bufsize, "%s#%d", basename, i);
1711 static gboolean rt_gpsd_try_connect(gpointer *data)
1713 VikGpsLayer *vgl = (VikGpsLayer *)data;
1714 #if GPSD_API_MAJOR_VERSION == 3
1715 struct gps_data_t *gpsd = gps_open(vgl->gpsd_host, vgl->gpsd_port);
1718 #elif GPSD_API_MAJOR_VERSION == 4
1719 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1721 if (gps_open_r(vgl->gpsd_host, vgl->gpsd_port, /*(struct gps_data_t *)*/vgl->vgpsd) != 0) {
1722 #elif GPSD_API_MAJOR_VERSION == 5
1723 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1724 if (gps_open(vgl->gpsd_host, vgl->gpsd_port, &vgl->vgpsd->gpsd) != 0) {
1726 // Delibrately break compilation...
1728 g_warning("Failed to connect to gpsd at %s (port %s). Will retry in %d seconds",
1729 vgl->gpsd_host, vgl->gpsd_port, vgl->gpsd_retry_interval);
1730 return TRUE; /* keep timer running */
1733 #if GPSD_API_MAJOR_VERSION == 3
1734 vgl->vgpsd = realloc(gpsd, sizeof(VglGpsd));
1736 vgl->vgpsd->vgl = vgl;
1738 vgl->realtime_fix.dirty = vgl->last_fix.dirty = FALSE;
1739 /* track alt/time graph uses VIK_DEFAULT_ALTITUDE (0.0) as invalid */
1740 vgl->realtime_fix.fix.altitude = vgl->last_fix.fix.altitude = VIK_DEFAULT_ALTITUDE;
1741 vgl->realtime_fix.fix.speed = vgl->last_fix.fix.speed = NAN;
1743 if (vgl->realtime_record) {
1744 VikTrwLayer *vtl = vgl->trw_children[TRW_REALTIME];
1745 vgl->realtime_track = vik_track_new();
1746 vgl->realtime_track->visible = TRUE;
1747 vik_trw_layer_add_track(vtl, make_track_name(vtl), vgl->realtime_track);
1750 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1751 gps_set_raw_hook(&vgl->vgpsd->gpsd, gpsd_raw_hook);
1754 vgl->realtime_io_channel = g_io_channel_unix_new(vgl->vgpsd->gpsd.gps_fd);
1755 vgl->realtime_io_watch_id = g_io_add_watch( vgl->realtime_io_channel,
1756 G_IO_IN | G_IO_ERR | G_IO_HUP, gpsd_data_available, vgl);
1758 #if GPSD_API_MAJOR_VERSION == 3
1759 gps_query(&vgl->vgpsd->gpsd, "w+x");
1761 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5
1762 gps_stream(&vgl->vgpsd->gpsd, WATCH_ENABLE, NULL);
1765 return FALSE; /* no longer called by timeout */
1768 static gboolean rt_ask_retry(VikGpsLayer *vgl)
1770 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER(vgl),
1771 GTK_DIALOG_DESTROY_WITH_PARENT,
1772 GTK_MESSAGE_QUESTION,
1774 "Failed to connect to gpsd at %s (port %s)\n"
1775 "Should Viking keep trying (every %d seconds)?",
1776 vgl->gpsd_host, vgl->gpsd_port,
1777 vgl->gpsd_retry_interval);
1779 gint res = gtk_dialog_run(GTK_DIALOG(dialog));
1780 gtk_widget_destroy(dialog);
1781 return (res == GTK_RESPONSE_YES);
1784 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed)
1786 vgl->realtime_retry_timer = 0;
1787 if (rt_gpsd_try_connect((gpointer *)vgl)) {
1788 if (vgl->gpsd_retry_interval <= 0) {
1789 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);
1792 else if (ask_if_failed && !rt_ask_retry(vgl))
1795 vgl->realtime_retry_timer = g_timeout_add_seconds(vgl->gpsd_retry_interval,
1796 (GSourceFunc)rt_gpsd_try_connect, (gpointer *)vgl);
1801 static void rt_gpsd_disconnect(VikGpsLayer *vgl)
1803 if (vgl->realtime_retry_timer) {
1804 g_source_remove(vgl->realtime_retry_timer);
1805 vgl->realtime_retry_timer = 0;
1807 if (vgl->realtime_io_watch_id) {
1808 g_source_remove(vgl->realtime_io_watch_id);
1809 vgl->realtime_io_watch_id = 0;
1811 if (vgl->realtime_io_channel) {
1812 GError *error = NULL;
1813 g_io_channel_shutdown (vgl->realtime_io_channel, FALSE, &error);
1814 vgl->realtime_io_channel = NULL;
1817 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5
1818 gps_stream(&vgl->vgpsd->gpsd, WATCH_DISABLE, NULL);
1820 gps_close(&vgl->vgpsd->gpsd);
1821 #if GPSD_API_MAJOR_VERSION == 3
1823 #elif GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5
1829 if (vgl->realtime_record && vgl->realtime_track) {
1830 if ((vgl->realtime_track->trackpoints == NULL) || (vgl->realtime_track->trackpoints->next == NULL))
1831 vik_trw_layer_delete_track(vgl->trw_children[TRW_REALTIME], vgl->realtime_track);
1832 vgl->realtime_track = NULL;
1836 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2])
1838 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1839 vgl->realtime_tracking = (vgl->realtime_tracking == FALSE);
1841 /* Make sure we are still in the boat with libgps */
1842 g_assert((VIK_GPS_MODE_2D == MODE_2D) && (VIK_GPS_MODE_3D == MODE_3D));
1844 if (vgl->realtime_tracking) {
1845 vgl->first_realtime_trackpoint = TRUE;
1846 if (!rt_gpsd_connect(vgl, TRUE)) {
1847 vgl->first_realtime_trackpoint = FALSE;
1848 vgl->realtime_tracking = FALSE;
1852 else { /* stop realtime tracking */
1853 vgl->first_realtime_trackpoint = FALSE;
1855 rt_gpsd_disconnect(vgl);
1858 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */