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 (VikLayerFuncGetTimestamp) NULL,
266 (VikLayerFuncSetMenuItemsSelection) NULL,
267 (VikLayerFuncGetMenuItemsSelection) NULL,
269 (VikLayerFuncAddMenuItems) gps_layer_add_menu_items,
270 (VikLayerFuncSublayerAddMenuItems) NULL,
272 (VikLayerFuncSublayerRenameRequest) NULL,
273 (VikLayerFuncSublayerToggleVisible) NULL,
274 (VikLayerFuncSublayerTooltip) NULL,
275 (VikLayerFuncLayerTooltip) gps_layer_tooltip,
276 (VikLayerFuncLayerSelected) NULL,
278 (VikLayerFuncMarshall) gps_layer_marshall,
279 (VikLayerFuncUnmarshall) gps_layer_unmarshall,
281 (VikLayerFuncSetParam) gps_layer_set_param,
282 (VikLayerFuncGetParam) gps_layer_get_param,
283 (VikLayerFuncChangeParam) NULL,
285 (VikLayerFuncReadFileData) NULL,
286 (VikLayerFuncWriteFileData) NULL,
288 (VikLayerFuncDeleteItem) NULL,
289 (VikLayerFuncCutItem) NULL,
290 (VikLayerFuncCopyItem) NULL,
291 (VikLayerFuncPasteItem) NULL,
292 (VikLayerFuncFreeCopiedItem) NULL,
293 (VikLayerFuncDragDropRequest) NULL,
295 (VikLayerFuncSelectClick) NULL,
296 (VikLayerFuncSelectMove) NULL,
297 (VikLayerFuncSelectRelease) NULL,
298 (VikLayerFuncSelectedViewportMenu) NULL,
301 enum {TRW_DOWNLOAD=0, TRW_UPLOAD,
302 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
306 static gchar * trw_names[] = {
307 N_("GPS Download"), N_("GPS Upload"),
308 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
309 N_("GPS Realtime Tracking"),
313 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
315 struct gps_data_t gpsd;
320 struct gps_fix_t fix;
321 gint satellites_used;
322 gboolean dirty; /* needs to be saved */
324 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
326 struct _VikGpsLayer {
328 VikTrwLayer * trw_children[NUM_TRW];
329 GList * children; /* used only for writing file */
330 int cur_read_child; /* used only for reading file */
331 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
333 gboolean realtime_tracking; /* set/reset only by the callback */
334 gboolean first_realtime_trackpoint;
338 VikTrack *realtime_track;
340 GIOChannel *realtime_io_channel;
341 guint realtime_io_watch_id;
342 guint realtime_retry_timer;
343 GdkGC *realtime_track_gc;
344 GdkGC *realtime_track_bg_gc;
345 GdkGC *realtime_track_pt_gc;
346 GdkGC *realtime_track_pt1_gc;
347 GdkGC *realtime_track_pt2_gc;
352 gint gpsd_retry_interval;
353 gboolean realtime_record;
354 gboolean realtime_jump_to_start;
355 guint vehicle_position;
356 gboolean realtime_update_statusbar;
357 VikTrackpoint *trkpt;
358 VikTrackpoint *trkpt_prev;
359 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
362 gboolean download_tracks;
363 gboolean download_routes;
364 gboolean download_waypoints;
365 gboolean upload_tracks;
366 gboolean upload_routes;
367 gboolean upload_waypoints;
371 * Overwrite the static setup with dynamically generated GPS Babel device list
373 static void gps_layer_inst_init ( VikGpsLayer *self )
376 // +1 for luck (i.e the NULL terminator)
377 gchar **new_protocols = g_malloc_n(1 + g_list_length(a_babel_device_list), sizeof(gpointer));
379 GList *gl = g_list_first ( a_babel_device_list );
381 // should be using label property but use name for now
382 // thus don't need to mess around converting label to name later on
383 new_protocols[new_proto++] = ((BabelDevice*)gl->data)->name;
384 gl = g_list_next ( gl );
386 new_protocols[new_proto] = NULL;
388 vik_gps_layer_interface.params[PARAM_PROTOCOL].widget_data = new_protocols;
391 GType vik_gps_layer_get_type ()
393 static GType val_type = 0;
397 static const GTypeInfo val_info =
399 sizeof (VikGpsLayerClass),
400 NULL, /* base_init */
401 NULL, /* base_finalize */
402 NULL, /* class init */
403 NULL, /* class_finalize */
404 NULL, /* class_data */
405 sizeof (VikGpsLayer),
407 (GInstanceInitFunc) gps_layer_inst_init,
409 val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGpsLayer", &val_info, 0 );
415 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp)
419 VikGpsLayer *rv = vik_gps_layer_new (vp);
420 vik_layer_rename ( VIK_LAYER(rv), vik_gps_layer_interface.name );
422 for (i = 0; i < NUM_TRW; i++) {
423 rv->trw_children[i] = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vp, FALSE ));
424 vik_layer_set_menu_items_selection(VIK_LAYER(rv->trw_children[i]), VIK_MENU_ITEM_ALL & ~(VIK_MENU_ITEM_CUT|VIK_MENU_ITEM_DELETE));
429 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl )
431 return vgl->protocol;
435 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *datalen )
437 VikLayer *child_layer;
440 GByteArray* b = g_byte_array_new ();
444 #define alm_append(obj, sz) \
446 g_byte_array_append ( b, (guint8 *)&len, sizeof(len) ); \
447 g_byte_array_append ( b, (guint8 *)(obj), len );
449 vik_layer_marshall_params(VIK_LAYER(vgl), &ld, &ll);
453 for (i = 0; i < NUM_TRW; i++) {
454 child_layer = VIK_LAYER(vgl->trw_children[i]);
455 vik_layer_marshall(child_layer, &ld, &ll);
463 g_byte_array_free(b, FALSE);
468 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
470 #define alm_size (*(gint *)data)
472 len -= sizeof(gint) + alm_size; \
473 data += sizeof(gint) + alm_size;
475 VikGpsLayer *rv = vik_gps_layer_new(vvp);
476 VikLayer *child_layer;
479 vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
483 while (len>0 && i < NUM_TRW) {
484 child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
486 rv->trw_children[i++] = (VikTrwLayer *)child_layer;
487 // NB no need to attach signal update handler here
488 // as this will always be performed later on in vik_gps_layer_realize()
492 // g_print("gps_layer_unmarshall ended with len=%d\n", len);
499 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
505 g_free(vgl->protocol);
506 // Backwards Compatibility: previous versions <v1.4 stored protocol as an array index
507 int index = data.s[0] - '0';
508 if (data.s[0] != '\0' &&
509 g_ascii_isdigit (data.s[0]) &&
511 index < OLD_NUM_PROTOCOLS)
512 // It is a single digit: activate compatibility
513 vgl->protocol = g_strdup(protocols_args[index]);
515 vgl->protocol = g_strdup(data.s);
516 g_debug("%s: %s", __FUNCTION__, vgl->protocol);
519 g_warning(_("Unknown GPS Protocol"));
523 g_free(vgl->serial_port);
524 // Backwards Compatibility: previous versions <v0.9.91 stored serial_port as an array index
525 int index = data.s[0] - '0';
526 if (data.s[0] != '\0' &&
527 g_ascii_isdigit (data.s[0]) &&
529 index < OLD_NUM_PORTS)
530 /* It is a single digit: activate compatibility */
531 vgl->serial_port = g_strdup(old_params_ports[index]);
533 vgl->serial_port = g_strdup(data.s);
534 g_debug("%s: %s", __FUNCTION__, vgl->serial_port);
537 g_warning(_("Unknown serial port device"));
539 case PARAM_DOWNLOAD_TRACKS:
540 vgl->download_tracks = data.b;
542 case PARAM_UPLOAD_TRACKS:
543 vgl->upload_tracks = data.b;
545 case PARAM_DOWNLOAD_ROUTES:
546 vgl->download_routes = data.b;
548 case PARAM_UPLOAD_ROUTES:
549 vgl->upload_routes = data.b;
551 case PARAM_DOWNLOAD_WAYPOINTS:
552 vgl->download_waypoints = data.b;
554 case PARAM_UPLOAD_WAYPOINTS:
555 vgl->upload_waypoints = data.b;
557 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
558 case PARAM_GPSD_HOST:
561 g_free(vgl->gpsd_host);
562 vgl->gpsd_host = g_strdup(data.s);
565 case PARAM_GPSD_PORT:
568 g_free(vgl->gpsd_port);
569 vgl->gpsd_port = g_strdup(data.s);
572 case PARAM_GPSD_RETRY_INTERVAL:
573 vgl->gpsd_retry_interval = strtol(data.s, NULL, 10);
575 case PARAM_REALTIME_REC:
576 vgl->realtime_record = data.b;
578 case PARAM_REALTIME_CENTER_START:
579 vgl->realtime_jump_to_start = data.b;
581 case PARAM_VEHICLE_POSITION:
582 vgl->vehicle_position = data.u;
584 case PARAM_REALTIME_UPDATE_STATUSBAR:
585 vgl->realtime_update_statusbar = data.b;
587 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
589 g_warning("gps_layer_set_param(): unknown parameter");
595 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation )
597 VikLayerParamData rv;
601 rv.s = vgl->protocol;
602 g_debug("%s: %s", __FUNCTION__, rv.s);
605 rv.s = vgl->serial_port;
606 g_debug("%s: %s", __FUNCTION__, rv.s);
608 case PARAM_DOWNLOAD_TRACKS:
609 rv.b = vgl->download_tracks;
611 case PARAM_UPLOAD_TRACKS:
612 rv.b = vgl->upload_tracks;
614 case PARAM_DOWNLOAD_ROUTES:
615 rv.b = vgl->download_routes;
617 case PARAM_UPLOAD_ROUTES:
618 rv.b = vgl->upload_routes;
620 case PARAM_DOWNLOAD_WAYPOINTS:
621 rv.b = vgl->download_waypoints;
623 case PARAM_UPLOAD_WAYPOINTS:
624 rv.b = vgl->upload_waypoints;
626 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
627 case PARAM_GPSD_HOST:
628 rv.s = vgl->gpsd_host ? vgl->gpsd_host : "";
630 case PARAM_GPSD_PORT:
631 rv.s = vgl->gpsd_port ? vgl->gpsd_port : g_strdup(DEFAULT_GPSD_PORT);
633 case PARAM_GPSD_RETRY_INTERVAL:
634 rv.s = g_strdup_printf("%d", vgl->gpsd_retry_interval);
636 case PARAM_REALTIME_REC:
637 rv.b = vgl->realtime_record;
639 case PARAM_REALTIME_CENTER_START:
640 rv.b = vgl->realtime_jump_to_start;
642 case PARAM_VEHICLE_POSITION:
643 rv.u = vgl->vehicle_position;
645 case PARAM_REALTIME_UPDATE_STATUSBAR:
646 rv.u = vgl->realtime_update_statusbar;
648 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
650 g_warning(_("%s: unknown parameter"), __FUNCTION__);
656 VikGpsLayer *vik_gps_layer_new (VikViewport *vp)
659 VikGpsLayer *vgl = VIK_GPS_LAYER ( g_object_new ( VIK_GPS_LAYER_TYPE, NULL ) );
660 vik_layer_set_type ( VIK_LAYER(vgl), VIK_LAYER_GPS );
661 for (i = 0; i < NUM_TRW; i++) {
662 vgl->trw_children[i] = NULL;
664 vgl->children = NULL;
665 vgl->cur_read_child = 0;
667 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
668 vgl->first_realtime_trackpoint = FALSE;
670 vgl->trkpt_prev = NULL;
672 vgl->realtime_io_channel = NULL;
673 vgl->realtime_io_watch_id = 0;
674 vgl->realtime_retry_timer = 0;
676 vgl->realtime_track_gc = vik_viewport_new_gc ( vp, "#203070", 2 );
677 vgl->realtime_track_bg_gc = vik_viewport_new_gc ( vp, "grey", 2 );
678 vgl->realtime_track_pt1_gc = vik_viewport_new_gc ( vp, "red", 2 );
679 vgl->realtime_track_pt2_gc = vik_viewport_new_gc ( vp, "green", 2 );
680 vgl->realtime_track_pt_gc = vgl->realtime_track_pt1_gc;
682 vgl->realtime_track = NULL;
683 #endif // VIK_CONFIG_REALTIME_GPS_TRACKING
685 vik_layer_set_defaults ( VIK_LAYER(vgl), vp );
690 static void vik_gps_layer_draw ( VikGpsLayer *vgl, VikViewport *vp )
694 VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( vp ));
696 for (i = 0; i < NUM_TRW; i++) {
697 vl = VIK_LAYER(vgl->trw_children[i]);
699 if ( vik_viewport_get_half_drawn ( vp ) ) {
700 vik_viewport_set_half_drawn ( vp, FALSE );
701 vik_viewport_snapshot_load( vp );
703 vik_viewport_snapshot_save( vp );
706 if (!vik_viewport_get_half_drawn(vp))
707 vik_layer_draw ( vl, vp );
709 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
710 if (vgl->realtime_tracking) {
711 if (VIK_LAYER(vgl) == trigger) {
712 if ( vik_viewport_get_half_drawn ( vp ) ) {
713 vik_viewport_set_half_drawn ( vp, FALSE );
714 vik_viewport_snapshot_load( vp );
716 vik_viewport_snapshot_save( vp );
719 if (!vik_viewport_get_half_drawn(vp))
720 realtime_tracking_draw(vgl, vp);
722 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
725 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode )
728 for (i = 0; i < NUM_TRW; i++) {
729 vik_layer_change_coord_mode(VIK_LAYER(vgl->trw_children[i]), mode);
733 static void gps_layer_add_menu_items( VikGpsLayer *vgl, GtkMenu *menu, gpointer vlp )
735 static gpointer pass_along[2];
740 item = gtk_menu_item_new();
741 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
742 gtk_widget_show ( item );
745 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS") );
746 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
747 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_upload_cb), pass_along );
748 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
749 gtk_widget_show ( item );
751 item = gtk_image_menu_item_new_with_mnemonic ( _("Download from _GPS") );
752 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
753 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_download_cb), pass_along );
754 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
755 gtk_widget_show ( item );
757 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
758 item = gtk_image_menu_item_new_with_mnemonic ( vgl->realtime_tracking ?
759 "_Stop Realtime Tracking" :
760 "_Start Realtime Tracking" );
761 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, vgl->realtime_tracking ?
762 gtk_image_new_from_stock (GTK_STOCK_MEDIA_STOP, GTK_ICON_SIZE_MENU) :
763 gtk_image_new_from_stock (GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_MENU) );
764 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_start_stop_tracking_cb), pass_along );
765 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
766 gtk_widget_show ( item );
768 item = gtk_menu_item_new();
769 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
770 gtk_widget_show ( item );
772 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _Realtime") );
773 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
774 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_realtime_cb), pass_along );
775 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
776 gtk_widget_show ( item );
777 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
779 item = gtk_image_menu_item_new_with_mnemonic ( _("E_mpty Upload") );
780 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
781 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_upload_cb), pass_along );
782 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
783 gtk_widget_show ( item );
785 item = gtk_image_menu_item_new_with_mnemonic ( _("_Empty Download") );
786 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
787 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_download_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 ( _("Empty _All") );
792 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
793 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_all_cb), pass_along );
794 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
795 gtk_widget_show ( item );
799 static void disconnect_layer_signal ( VikLayer *vl, VikGpsLayer *vgl )
801 guint number_handlers = g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, vgl);
802 if ( number_handlers != 1 ) {
803 g_critical(_("Unexpected number of disconnected handlers: %d"), number_handlers);
807 static void vik_gps_layer_free ( VikGpsLayer *vgl )
810 for (i = 0; i < NUM_TRW; i++) {
811 if (vgl->vl.realized)
812 disconnect_layer_signal(VIK_LAYER(vgl->trw_children[i]), vgl);
813 g_object_unref(vgl->trw_children[i]);
815 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
816 rt_gpsd_disconnect(vgl);
817 if (vgl->realtime_track_gc != NULL)
818 g_object_unref(vgl->realtime_track_gc);
819 if (vgl->realtime_track_bg_gc != NULL)
820 g_object_unref(vgl->realtime_track_bg_gc);
821 if (vgl->realtime_track_pt1_gc != NULL)
822 g_object_unref(vgl->realtime_track_pt1_gc);
823 if (vgl->realtime_track_pt2_gc != NULL)
824 g_object_unref(vgl->realtime_track_pt2_gc);
825 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
828 static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter )
833 // TODO set to garmin by default
834 //if (a_babel_device_list)
835 // device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
836 // Need to access uibuild widgets somehow....
838 for (ix = 0; ix < NUM_TRW; ix++) {
839 VikLayer * trw = VIK_LAYER(vgl->trw_children[ix]);
840 vik_treeview_add_layer ( VIK_LAYER(vgl)->vt, layer_iter, &iter,
841 _(trw_names[ix]), vgl, TRUE,
842 trw, trw->type, trw->type, vik_layer_get_timestamp(trw) );
843 if ( ! trw->visible )
844 vik_treeview_item_set_visible ( VIK_LAYER(vgl)->vt, &iter, FALSE );
845 vik_layer_realize ( trw, VIK_LAYER(vgl)->vt, &iter );
846 g_signal_connect_swapped ( G_OBJECT(trw), "update", G_CALLBACK(vik_layer_emit_update_secondary), vgl );
850 const GList *vik_gps_layer_get_children ( VikGpsLayer *vgl )
854 if (vgl->children == NULL) {
855 for (i = NUM_TRW - 1; i >= 0; i--)
856 vgl->children = g_list_prepend(vgl->children, vgl->trw_children[i]);
858 return vgl->children;
861 VikTrwLayer * vik_gps_layer_get_a_child(VikGpsLayer *vgl)
863 g_assert ((vgl->cur_read_child >= 0) && (vgl->cur_read_child < NUM_TRW));
865 VikTrwLayer * vtl = vgl->trw_children[vgl->cur_read_child];
866 if (++(vgl->cur_read_child) >= NUM_TRW)
867 vgl->cur_read_child = 0;
871 gboolean vik_gps_layer_is_empty ( VikGpsLayer *vgl )
873 if ( vgl->trw_children[0] )
878 static void gps_session_delete(GpsSession *sess)
880 vik_mutex_free(sess->mutex);
881 g_free(sess->babelargs);
885 static void set_total_count(gint cnt, GpsSession *sess)
889 g_mutex_lock(sess->mutex);
891 const gchar *tmp_str;
892 if (sess->direction == GPS_DOWN)
894 switch (sess->progress_type) {
895 case WPT: tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt); sess->total_count = cnt; break;
896 case TRK: tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt); sess->total_count = cnt; break;
899 // 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
900 gint mycnt = (cnt / 2) + 1;
901 tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt);
902 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 ProcessOptions po = { sess->babelargs, sess->port, NULL, NULL, NULL, NULL };
1185 result = a_babel_convert_from (sess->vtl, &po, (BabelStatusFunc) gps_download_progress_func, sess, NULL);
1188 result = a_babel_convert_to (sess->vtl, sess->track, sess->babelargs, sess->port,
1189 (BabelStatusFunc) gps_upload_progress_func, sess);
1193 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: couldn't find gpsbabel.") );
1196 g_mutex_lock(sess->mutex);
1198 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Done.") );
1199 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE );
1200 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE );
1202 /* Do not change the view if we are following the current GPS position */
1203 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1204 if (!sess->realtime_tracking)
1207 if ( sess->vvp && sess->direction == GPS_DOWN ) {
1208 vik_layer_post_read ( VIK_LAYER(sess->vtl), sess->vvp, TRUE );
1209 /* View the data available */
1210 vik_trw_layer_auto_set_view ( sess->vtl, sess->vvp ) ;
1211 vik_layer_emit_update ( VIK_LAYER(sess->vtl) ); // NB update from background thread
1217 g_mutex_unlock(sess->mutex);
1220 g_mutex_lock(sess->mutex);
1223 g_mutex_unlock(sess->mutex);
1226 g_mutex_unlock(sess->mutex);
1227 gps_session_delete(sess);
1229 g_thread_exit(NULL);
1234 * @vtl: The TrackWaypoint layer to operate on
1235 * @track: Operate on a particular track when specified
1236 * @dir: The direction of the transfer
1237 * @protocol: The GPS device communication protocol
1238 * @port: The GPS serial port
1239 * @tracking: If tracking then viewport display update will be skipped
1240 * @vvp: A viewport is required as the display may get updated
1241 * @vlp: A layers panel is needed for uploading as the items maybe modified
1242 * @do_tracks: Whether tracks shoud be processed
1243 * @do_waypoints: Whether waypoints shoud be processed
1244 * @turn_off: Whether we should attempt to turn off the GPS device after the transfer (only some devices support this)
1246 * Talk to a GPS Device using a thread which updates a dialog with the progress
1248 gint vik_gps_comm ( VikTrwLayer *vtl,
1255 VikLayersPanel *vlp,
1258 gboolean do_waypoints,
1261 GpsSession *sess = g_malloc(sizeof(GpsSession));
1262 char *tracks = NULL;
1263 char *routes = NULL;
1264 char *waypoints = NULL;
1266 sess->mutex = vik_mutex_new();
1267 sess->direction = dir;
1269 sess->track = track;
1270 sess->port = g_strdup(port);
1272 sess->window_title = (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload");
1275 // This must be done inside the main thread as the uniquify causes screen updates
1276 // (originally performed this nearer the point of upload in the thread)
1277 if ( dir == GPS_UP ) {
1278 // Enforce unique names in the layer upload to the GPS device
1279 // NB this may only be a Garmin device restriction (and may be not every Garmin device either...)
1280 // Thus this maintains the older code in built restriction
1281 if ( ! vik_trw_layer_uniquify ( sess->vtl, vlp ) )
1282 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(sess->vtl))), VIK_STATUSBAR_INFO,
1283 _("Warning - GPS Upload items may overwrite each other") );
1286 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1287 sess->realtime_tracking = tracking;
1303 sess->babelargs = g_strdup_printf("-D 9 %s %s %s -%c %s",
1304 tracks, routes, waypoints, (dir == GPS_DOWN) ? 'i' : 'o', protocol);
1308 // Only create dialog if we're going to do some transferring
1309 if ( do_tracks || do_waypoints || do_routes ) {
1310 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 );
1311 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog),
1312 GTK_RESPONSE_ACCEPT, FALSE );
1313 gtk_window_set_title ( GTK_WINDOW(sess->dialog), sess->window_title );
1315 sess->status_label = gtk_label_new (_("Status: detecting gpsbabel"));
1316 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->status_label, FALSE, FALSE, 5 );
1317 gtk_widget_show_all(sess->status_label);
1319 sess->gps_label = gtk_label_new (_("GPS device: N/A"));
1320 sess->ver_label = gtk_label_new ("");
1321 sess->id_label = gtk_label_new ("");
1322 sess->wp_label = gtk_label_new ("");
1323 sess->trk_label = gtk_label_new ("");
1324 sess->rte_label = gtk_label_new ("");
1326 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->gps_label, FALSE, FALSE, 5 );
1327 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->wp_label, FALSE, FALSE, 5 );
1328 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->trk_label, FALSE, FALSE, 5 );
1329 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->rte_label, FALSE, FALSE, 5 );
1331 gtk_widget_show_all(sess->dialog);
1333 sess->progress_label = sess->wp_label;
1334 sess->total_count = -1;
1336 // Starting gps read/write thread
1337 #if GLIB_CHECK_VERSION (2, 32, 0)
1338 g_thread_try_new ( "gps_comm_thread", (GThreadFunc)gps_comm_thread, sess, NULL );
1340 g_thread_create ( (GThreadFunc)gps_comm_thread, sess, FALSE, NULL );
1343 gtk_dialog_set_default_response ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT );
1344 gtk_dialog_run(GTK_DIALOG(sess->dialog));
1346 gtk_widget_destroy(sess->dialog);
1350 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No GPS items selected for transfer.") );
1353 g_mutex_lock(sess->mutex);
1356 sess->ok = FALSE; /* tell thread to stop */
1357 g_mutex_unlock(sess->mutex);
1361 // No need for thread for powering off device (should be quick operation...) - so use babel command directly:
1362 gchar *device_off = g_strdup_printf("-i %s,%s", protocol, "power_off");
1363 ProcessOptions po = { device_off, port, NULL, NULL, NULL, NULL };
1364 gboolean result = a_babel_convert_from (NULL, &po, NULL, NULL, NULL);
1366 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not turn off device.") );
1367 g_free ( device_off );
1369 g_mutex_unlock(sess->mutex);
1370 gps_session_delete(sess);
1376 static void gps_upload_cb( gpointer layer_and_vlp[2] )
1378 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1379 VikLayersPanel *vlp = VIK_LAYERS_PANEL(layer_and_vlp[1]);
1380 VikTrwLayer *vtl = vgl->trw_children[TRW_UPLOAD];
1381 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1382 VikViewport *vvp = vik_window_viewport(vw);
1383 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);
1386 static void gps_download_cb( gpointer layer_and_vlp[2] )
1388 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1389 VikTrwLayer *vtl = vgl->trw_children[TRW_DOWNLOAD];
1390 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1391 VikViewport *vvp = vik_window_viewport(vw);
1392 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1393 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);
1395 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);
1399 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] )
1401 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1402 // Get confirmation from the user
1403 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1404 _("Are you sure you want to delete GPS Upload data?"),
1407 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1408 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1409 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1412 static void gps_empty_download_cb( gpointer layer_and_vlp[2] )
1414 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1415 // Get confirmation from the user
1416 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1417 _("Are you sure you want to delete GPS Download data?"),
1420 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1421 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1422 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1425 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1426 static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] )
1428 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1429 // Get confirmation from the user
1430 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1431 _("Are you sure you want to delete GPS Realtime data?"),
1434 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1435 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1439 static void gps_empty_all_cb( gpointer layer_and_vlp[2] )
1441 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1442 // Get confirmation from the user
1443 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1444 _("Are you sure you want to delete All GPS data?"),
1447 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1448 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1449 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1450 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1451 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1452 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1453 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1454 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1455 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1459 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1460 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp)
1464 struct LatLon lnw, lse;
1465 vik_viewport_screen_to_coord ( vp, -20, -20, &nw );
1466 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp)+20, vik_viewport_get_width(vp)+20, &se );
1467 vik_coord_to_latlon ( &nw, &lnw );
1468 vik_coord_to_latlon ( &se, &lse );
1469 if ( vgl->realtime_fix.fix.latitude > lse.lat &&
1470 vgl->realtime_fix.fix.latitude < lnw.lat &&
1471 vgl->realtime_fix.fix.longitude > lnw.lon &&
1472 vgl->realtime_fix.fix.longitude < lse.lon &&
1473 !isnan (vgl->realtime_fix.fix.track) ) {
1476 gint half_back_x, half_back_y;
1477 gint half_back_bg_x, half_back_bg_y;
1480 gint side1_x, side1_y, side2_x, side2_y;
1481 gint side1bg_x, side1bg_y, side2bg_x, side2bg_y;
1483 ll.lat = vgl->realtime_fix.fix.latitude;
1484 ll.lon = vgl->realtime_fix.fix.longitude;
1485 vik_coord_load_from_latlon ( &gps, vik_viewport_get_coord_mode(vp), &ll);
1486 vik_viewport_coord_to_screen ( vp, &gps, &x, &y );
1488 gdouble heading_cos = cos(DEG2RAD(vgl->realtime_fix.fix.track));
1489 gdouble heading_sin = sin(DEG2RAD(vgl->realtime_fix.fix.track));
1491 half_back_y = y+8*heading_cos;
1492 half_back_x = x-8*heading_sin;
1493 half_back_bg_y = y+10*heading_cos;
1494 half_back_bg_x = x-10*heading_sin;
1496 pt_y = half_back_y-24*heading_cos;
1497 pt_x = half_back_x+24*heading_sin;
1498 //ptbg_y = half_back_bg_y-28*heading_cos;
1499 ptbg_x = half_back_bg_x+28*heading_sin;
1501 side1_y = half_back_y+9*heading_sin;
1502 side1_x = half_back_x+9*heading_cos;
1503 side1bg_y = half_back_bg_y+11*heading_sin;
1504 side1bg_x = half_back_bg_x+11*heading_cos;
1506 side2_y = half_back_y-9*heading_sin;
1507 side2_x = half_back_x-9*heading_cos;
1508 side2bg_y = half_back_bg_y-11*heading_sin;
1509 side2bg_x = half_back_bg_x-11*heading_cos;
1511 GdkPoint trian[3] = { { pt_x, pt_y }, {side1_x, side1_y}, {side2_x, side2_y} };
1512 GdkPoint trian_bg[3] = { { ptbg_x, pt_y }, {side1bg_x, side1bg_y}, {side2bg_x, side2bg_y} };
1514 vik_viewport_draw_polygon ( vp, vgl->realtime_track_bg_gc, TRUE, trian_bg, 3 );
1515 vik_viewport_draw_polygon ( vp, vgl->realtime_track_gc, TRUE, trian, 3 );
1516 vik_viewport_draw_rectangle ( vp,
1517 (vgl->realtime_fix.fix.mode > MODE_2D) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc,
1518 TRUE, x-2, y-2, 4, 4 );
1519 //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;
1523 static VikTrackpoint* create_realtime_trackpoint(VikGpsLayer *vgl, gboolean forced)
1528 /* Note that fix.time is a double, but it should not affect the precision
1530 time_t cur_timestamp = vgl->realtime_fix.fix.time;
1531 time_t last_timestamp = vgl->last_fix.fix.time;
1533 if (cur_timestamp < last_timestamp) {
1537 if (vgl->realtime_record && vgl->realtime_fix.dirty) {
1538 gboolean replace = FALSE;
1539 int heading = isnan(vgl->realtime_fix.fix.track) ? 0 : (int)floor(vgl->realtime_fix.fix.track);
1540 int last_heading = isnan(vgl->last_fix.fix.track) ? 0 : (int)floor(vgl->last_fix.fix.track);
1541 int alt = isnan(vgl->realtime_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->realtime_fix.fix.altitude);
1542 int last_alt = isnan(vgl->last_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->last_fix.fix.altitude);
1543 if (((last_tp = g_list_last(vgl->realtime_track->trackpoints)) != NULL) &&
1544 (vgl->realtime_fix.fix.mode > MODE_2D) &&
1545 (vgl->last_fix.fix.mode <= MODE_2D) &&
1546 ((cur_timestamp - last_timestamp) < 2)) {
1547 g_free(last_tp->data);
1548 vgl->realtime_track->trackpoints = g_list_delete_link(vgl->realtime_track->trackpoints, last_tp);
1552 ((cur_timestamp != last_timestamp) &&
1554 ((heading < last_heading) && (heading < (last_heading - 3))) ||
1555 ((heading > last_heading) && (heading > (last_heading + 3))) ||
1556 ((alt != VIK_DEFAULT_ALTITUDE) && (alt != last_alt)))))) {
1557 /* TODO: check for new segments */
1558 VikTrackpoint *tp = vik_trackpoint_new();
1559 tp->newsegment = FALSE;
1560 tp->has_timestamp = TRUE;
1561 tp->timestamp = vgl->realtime_fix.fix.time;
1563 /* speed only available for 3D fix. Check for NAN when use this speed */
1564 tp->speed = vgl->realtime_fix.fix.speed;
1565 tp->course = vgl->realtime_fix.fix.track;
1566 tp->nsats = vgl->realtime_fix.satellites_used;
1567 tp->fix_mode = vgl->realtime_fix.fix.mode;
1569 ll.lat = vgl->realtime_fix.fix.latitude;
1570 ll.lon = vgl->realtime_fix.fix.longitude;
1571 vik_coord_load_from_latlon(&tp->coord,
1572 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1574 vik_track_add_trackpoint ( vgl->realtime_track, tp, TRUE ); // Ensure bounds is recalculated
1575 vgl->realtime_fix.dirty = FALSE;
1576 vgl->realtime_fix.satellites_used = 0;
1577 vgl->last_fix = vgl->realtime_fix;
1584 #define VIK_SETTINGS_GPS_STATUSBAR_FORMAT "gps_statusbar_format"
1586 static void update_statusbar ( VikGpsLayer *vgl, VikWindow *vw )
1588 gchar *statusbar_format_code = NULL;
1589 gboolean need2free = FALSE;
1590 if ( !a_settings_get_string ( VIK_SETTINGS_GPS_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
1591 // Otherwise use default
1592 statusbar_format_code = g_strdup ( "GSA" );
1596 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, vgl->trkpt, vgl->trkpt_prev, vgl->realtime_track, vgl->last_fix.fix.climb );
1597 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
1601 g_free ( statusbar_format_code );
1605 static void gpsd_raw_hook(VglGpsd *vgpsd, gchar *data)
1607 gboolean update_all = FALSE;
1608 VikGpsLayer *vgl = vgpsd->vgl;
1610 if (!vgl->realtime_tracking) {
1611 g_warning("%s: receiving GPS data while not in realtime mode", __PRETTY_FUNCTION__);
1615 if ((vgpsd->gpsd.fix.mode >= MODE_2D) &&
1616 !isnan(vgpsd->gpsd.fix.latitude) &&
1617 !isnan(vgpsd->gpsd.fix.longitude)) {
1619 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1620 VikViewport *vvp = vik_window_viewport(vw);
1621 vgl->realtime_fix.fix = vgpsd->gpsd.fix;
1622 vgl->realtime_fix.satellites_used = vgpsd->gpsd.satellites_used;
1623 vgl->realtime_fix.dirty = TRUE;
1626 VikCoord vehicle_coord;
1628 ll.lat = vgl->realtime_fix.fix.latitude;
1629 ll.lon = vgl->realtime_fix.fix.longitude;
1630 vik_coord_load_from_latlon(&vehicle_coord,
1631 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1633 if ((vgl->vehicle_position == VEHICLE_POSITION_CENTERED) ||
1634 (vgl->realtime_jump_to_start && vgl->first_realtime_trackpoint)) {
1635 vik_viewport_set_center_coord(vvp, &vehicle_coord, FALSE);
1638 else if (vgl->vehicle_position == VEHICLE_POSITION_ON_SCREEN) {
1641 const int px = 20; /* adjust ment in pixels to make sure vehicle is inside the box */
1642 gint width = vik_viewport_get_width(vvp);
1643 gint height = vik_viewport_get_height(vvp);
1646 vik_viewport_coord_to_screen(vvp, &vehicle_coord, &vx, &vy);
1648 if (vx < (width/hdiv))
1649 vik_viewport_set_center_screen(vvp, vx - width/2 + width/hdiv + px, vy);
1650 else if (vx > (width - width/hdiv))
1651 vik_viewport_set_center_screen(vvp, vx + width/2 - width/hdiv - px, vy);
1652 else if (vy < (height/vdiv))
1653 vik_viewport_set_center_screen(vvp, vx, vy - height/2 + height/vdiv + px);
1654 else if (vy > (height - height/vdiv))
1655 vik_viewport_set_center_screen(vvp, vx, vy + height/2 - height/vdiv - px);
1660 vgl->first_realtime_trackpoint = FALSE;
1662 vgl->trkpt = create_realtime_trackpoint ( vgl, FALSE );
1665 if ( vgl->realtime_update_statusbar )
1666 update_statusbar ( vgl, vw );
1667 vgl->trkpt_prev = vgl->trkpt;
1670 vik_layer_emit_update ( update_all ? VIK_LAYER(vgl) : VIK_LAYER(vgl->trw_children[TRW_REALTIME]) ); // NB update from background thread
1674 static gboolean gpsd_data_available(GIOChannel *source, GIOCondition condition, gpointer data)
1676 VikGpsLayer *vgl = data;
1677 if (condition == G_IO_IN) {
1678 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1679 if (!gps_poll(&vgl->vgpsd->gpsd)) {
1680 #elif GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1681 if (gps_read(&vgl->vgpsd->gpsd) > -1) {
1682 // Reuse old function to perform operations on the new GPS data
1683 gpsd_raw_hook(vgl->vgpsd, NULL);
1690 g_warning("Disconnected from gpsd. Trying to reconnect");
1691 rt_gpsd_disconnect(vgl);
1692 rt_gpsd_connect(vgl, FALSE);
1695 return FALSE; /* no further calling */
1698 static gchar *make_track_name(VikTrwLayer *vtl)
1700 const gchar basename[] = "REALTIME";
1701 const gint bufsize = sizeof(basename) + 5;
1702 gchar *name = g_malloc(bufsize);
1703 strcpy(name, basename);
1706 while (vik_trw_layer_get_track(vtl, name) != NULL) {
1707 g_snprintf(name, bufsize, "%s#%d", basename, i);
1714 static gboolean rt_gpsd_try_connect(gpointer *data)
1716 VikGpsLayer *vgl = (VikGpsLayer *)data;
1717 #if GPSD_API_MAJOR_VERSION == 3
1718 struct gps_data_t *gpsd = gps_open(vgl->gpsd_host, vgl->gpsd_port);
1721 #elif GPSD_API_MAJOR_VERSION == 4
1722 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1724 if (gps_open_r(vgl->gpsd_host, vgl->gpsd_port, /*(struct gps_data_t *)*/vgl->vgpsd) != 0) {
1725 #elif GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1726 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1727 if (gps_open(vgl->gpsd_host, vgl->gpsd_port, &vgl->vgpsd->gpsd) != 0) {
1729 // Delibrately break compilation...
1731 g_warning("Failed to connect to gpsd at %s (port %s). Will retry in %d seconds",
1732 vgl->gpsd_host, vgl->gpsd_port, vgl->gpsd_retry_interval);
1733 return TRUE; /* keep timer running */
1736 #if GPSD_API_MAJOR_VERSION == 3
1737 vgl->vgpsd = realloc(gpsd, sizeof(VglGpsd));
1739 vgl->vgpsd->vgl = vgl;
1741 vgl->realtime_fix.dirty = vgl->last_fix.dirty = FALSE;
1742 /* track alt/time graph uses VIK_DEFAULT_ALTITUDE (0.0) as invalid */
1743 vgl->realtime_fix.fix.altitude = vgl->last_fix.fix.altitude = VIK_DEFAULT_ALTITUDE;
1744 vgl->realtime_fix.fix.speed = vgl->last_fix.fix.speed = NAN;
1746 if (vgl->realtime_record) {
1747 VikTrwLayer *vtl = vgl->trw_children[TRW_REALTIME];
1748 vgl->realtime_track = vik_track_new();
1749 vgl->realtime_track->visible = TRUE;
1750 vik_trw_layer_add_track(vtl, make_track_name(vtl), vgl->realtime_track);
1753 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1754 gps_set_raw_hook(&vgl->vgpsd->gpsd, gpsd_raw_hook);
1757 vgl->realtime_io_channel = g_io_channel_unix_new(vgl->vgpsd->gpsd.gps_fd);
1758 vgl->realtime_io_watch_id = g_io_add_watch( vgl->realtime_io_channel,
1759 G_IO_IN | G_IO_ERR | G_IO_HUP, gpsd_data_available, vgl);
1761 #if GPSD_API_MAJOR_VERSION == 3
1762 gps_query(&vgl->vgpsd->gpsd, "w+x");
1764 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1765 gps_stream(&vgl->vgpsd->gpsd, WATCH_ENABLE, NULL);
1768 return FALSE; /* no longer called by timeout */
1771 static gboolean rt_ask_retry(VikGpsLayer *vgl)
1773 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER(vgl),
1774 GTK_DIALOG_DESTROY_WITH_PARENT,
1775 GTK_MESSAGE_QUESTION,
1777 "Failed to connect to gpsd at %s (port %s)\n"
1778 "Should Viking keep trying (every %d seconds)?",
1779 vgl->gpsd_host, vgl->gpsd_port,
1780 vgl->gpsd_retry_interval);
1782 gint res = gtk_dialog_run(GTK_DIALOG(dialog));
1783 gtk_widget_destroy(dialog);
1784 return (res == GTK_RESPONSE_YES);
1787 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed)
1789 vgl->realtime_retry_timer = 0;
1790 if (rt_gpsd_try_connect((gpointer *)vgl)) {
1791 if (vgl->gpsd_retry_interval <= 0) {
1792 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);
1795 else if (ask_if_failed && !rt_ask_retry(vgl))
1798 vgl->realtime_retry_timer = g_timeout_add_seconds(vgl->gpsd_retry_interval,
1799 (GSourceFunc)rt_gpsd_try_connect, (gpointer *)vgl);
1804 static void rt_gpsd_disconnect(VikGpsLayer *vgl)
1806 if (vgl->realtime_retry_timer) {
1807 g_source_remove(vgl->realtime_retry_timer);
1808 vgl->realtime_retry_timer = 0;
1810 if (vgl->realtime_io_watch_id) {
1811 g_source_remove(vgl->realtime_io_watch_id);
1812 vgl->realtime_io_watch_id = 0;
1814 if (vgl->realtime_io_channel) {
1815 GError *error = NULL;
1816 g_io_channel_shutdown (vgl->realtime_io_channel, FALSE, &error);
1817 vgl->realtime_io_channel = NULL;
1820 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1821 gps_stream(&vgl->vgpsd->gpsd, WATCH_DISABLE, NULL);
1823 gps_close(&vgl->vgpsd->gpsd);
1824 #if GPSD_API_MAJOR_VERSION == 3
1826 #elif GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1832 if (vgl->realtime_record && vgl->realtime_track) {
1833 if ((vgl->realtime_track->trackpoints == NULL) || (vgl->realtime_track->trackpoints->next == NULL))
1834 vik_trw_layer_delete_track(vgl->trw_children[TRW_REALTIME], vgl->realtime_track);
1835 vgl->realtime_track = NULL;
1839 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2])
1841 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1842 vgl->realtime_tracking = (vgl->realtime_tracking == FALSE);
1844 /* Make sure we are still in the boat with libgps */
1845 g_assert((VIK_GPS_MODE_2D == MODE_2D) && (VIK_GPS_MODE_3D == MODE_3D));
1847 if (vgl->realtime_tracking) {
1848 vgl->first_realtime_trackpoint = TRUE;
1849 if (!rt_gpsd_connect(vgl, TRUE)) {
1850 vgl->first_realtime_trackpoint = FALSE;
1851 vgl->realtime_tracking = FALSE;
1855 else { /* stop realtime tracking */
1856 vgl->first_realtime_trackpoint = FALSE;
1858 rt_gpsd_disconnect(vgl);
1861 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */