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;
321 struct gps_fix_t fix;
322 gint satellites_used;
323 gboolean dirty; /* needs to be saved */
325 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
327 struct _VikGpsLayer {
329 VikTrwLayer * trw_children[NUM_TRW];
330 GList * children; /* used only for writing file */
331 int cur_read_child; /* used only for reading file */
332 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
334 gboolean realtime_tracking; /* set/reset only by the callback */
335 gboolean first_realtime_trackpoint;
339 VikTrack *realtime_track;
341 GIOChannel *realtime_io_channel;
342 guint realtime_io_watch_id;
343 guint realtime_retry_timer;
344 GdkGC *realtime_track_gc;
345 GdkGC *realtime_track_bg_gc;
346 GdkGC *realtime_track_pt_gc;
347 GdkGC *realtime_track_pt1_gc;
348 GdkGC *realtime_track_pt2_gc;
353 gint gpsd_retry_interval;
354 gboolean realtime_record;
355 gboolean realtime_jump_to_start;
356 guint vehicle_position;
357 gboolean realtime_update_statusbar;
358 VikTrackpoint *trkpt;
359 VikTrackpoint *trkpt_prev;
360 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
363 gboolean download_tracks;
364 gboolean download_routes;
365 gboolean download_waypoints;
366 gboolean upload_tracks;
367 gboolean upload_routes;
368 gboolean upload_waypoints;
372 * Overwrite the static setup with dynamically generated GPS Babel device list
374 static void gps_layer_inst_init ( VikGpsLayer *self )
377 // +1 for luck (i.e the NULL terminator)
378 gchar **new_protocols = g_malloc_n(1 + g_list_length(a_babel_device_list), sizeof(gpointer));
380 GList *gl = g_list_first ( a_babel_device_list );
382 // should be using label property but use name for now
383 // thus don't need to mess around converting label to name later on
384 new_protocols[new_proto++] = ((BabelDevice*)gl->data)->name;
385 gl = g_list_next ( gl );
387 new_protocols[new_proto] = NULL;
389 vik_gps_layer_interface.params[PARAM_PROTOCOL].widget_data = new_protocols;
392 GType vik_gps_layer_get_type ()
394 static GType val_type = 0;
398 static const GTypeInfo val_info =
400 sizeof (VikGpsLayerClass),
401 NULL, /* base_init */
402 NULL, /* base_finalize */
403 NULL, /* class init */
404 NULL, /* class_finalize */
405 NULL, /* class_data */
406 sizeof (VikGpsLayer),
408 (GInstanceInitFunc) gps_layer_inst_init,
410 val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGpsLayer", &val_info, 0 );
416 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp)
420 VikGpsLayer *rv = vik_gps_layer_new (vp);
421 vik_layer_rename ( VIK_LAYER(rv), vik_gps_layer_interface.name );
423 for (i = 0; i < NUM_TRW; i++) {
424 rv->trw_children[i] = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vp, FALSE ));
425 vik_layer_set_menu_items_selection(VIK_LAYER(rv->trw_children[i]), VIK_MENU_ITEM_ALL & ~(VIK_MENU_ITEM_CUT|VIK_MENU_ITEM_DELETE));
430 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl )
432 return vgl->protocol;
436 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *datalen )
438 VikLayer *child_layer;
441 GByteArray* b = g_byte_array_new ();
445 #define alm_append(obj, sz) \
447 g_byte_array_append ( b, (guint8 *)&len, sizeof(len) ); \
448 g_byte_array_append ( b, (guint8 *)(obj), len );
450 vik_layer_marshall_params(VIK_LAYER(vgl), &ld, &ll);
454 for (i = 0; i < NUM_TRW; i++) {
455 child_layer = VIK_LAYER(vgl->trw_children[i]);
456 vik_layer_marshall(child_layer, &ld, &ll);
464 g_byte_array_free(b, FALSE);
469 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
471 #define alm_size (*(gint *)data)
473 len -= sizeof(gint) + alm_size; \
474 data += sizeof(gint) + alm_size;
476 VikGpsLayer *rv = vik_gps_layer_new(vvp);
477 VikLayer *child_layer;
480 vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
484 while (len>0 && i < NUM_TRW) {
485 child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
487 rv->trw_children[i++] = (VikTrwLayer *)child_layer;
488 // NB no need to attach signal update handler here
489 // as this will always be performed later on in vik_gps_layer_realize()
493 // g_print("gps_layer_unmarshall ended with len=%d\n", len);
500 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
506 g_free(vgl->protocol);
507 // Backwards Compatibility: previous versions <v1.4 stored protocol as an array index
508 int index = data.s[0] - '0';
509 if (data.s[0] != '\0' &&
510 g_ascii_isdigit (data.s[0]) &&
512 index < OLD_NUM_PROTOCOLS)
513 // It is a single digit: activate compatibility
514 vgl->protocol = g_strdup(protocols_args[index]);
516 vgl->protocol = g_strdup(data.s);
517 g_debug("%s: %s", __FUNCTION__, vgl->protocol);
520 g_warning(_("Unknown GPS Protocol"));
524 g_free(vgl->serial_port);
525 // Backwards Compatibility: previous versions <v0.9.91 stored serial_port as an array index
526 int index = data.s[0] - '0';
527 if (data.s[0] != '\0' &&
528 g_ascii_isdigit (data.s[0]) &&
530 index < OLD_NUM_PORTS)
531 /* It is a single digit: activate compatibility */
532 vgl->serial_port = g_strdup(old_params_ports[index]);
534 vgl->serial_port = g_strdup(data.s);
535 g_debug("%s: %s", __FUNCTION__, vgl->serial_port);
538 g_warning(_("Unknown serial port device"));
540 case PARAM_DOWNLOAD_TRACKS:
541 vgl->download_tracks = data.b;
543 case PARAM_UPLOAD_TRACKS:
544 vgl->upload_tracks = data.b;
546 case PARAM_DOWNLOAD_ROUTES:
547 vgl->download_routes = data.b;
549 case PARAM_UPLOAD_ROUTES:
550 vgl->upload_routes = data.b;
552 case PARAM_DOWNLOAD_WAYPOINTS:
553 vgl->download_waypoints = data.b;
555 case PARAM_UPLOAD_WAYPOINTS:
556 vgl->upload_waypoints = data.b;
558 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
559 case PARAM_GPSD_HOST:
562 g_free(vgl->gpsd_host);
563 vgl->gpsd_host = g_strdup(data.s);
566 case PARAM_GPSD_PORT:
569 g_free(vgl->gpsd_port);
570 vgl->gpsd_port = g_strdup(data.s);
573 case PARAM_GPSD_RETRY_INTERVAL:
574 vgl->gpsd_retry_interval = strtol(data.s, NULL, 10);
576 case PARAM_REALTIME_REC:
577 vgl->realtime_record = data.b;
579 case PARAM_REALTIME_CENTER_START:
580 vgl->realtime_jump_to_start = data.b;
582 case PARAM_VEHICLE_POSITION:
583 vgl->vehicle_position = data.u;
585 case PARAM_REALTIME_UPDATE_STATUSBAR:
586 vgl->realtime_update_statusbar = data.b;
588 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
590 g_warning("gps_layer_set_param(): unknown parameter");
596 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation )
598 VikLayerParamData rv;
602 rv.s = vgl->protocol;
603 g_debug("%s: %s", __FUNCTION__, rv.s);
606 rv.s = vgl->serial_port;
607 g_debug("%s: %s", __FUNCTION__, rv.s);
609 case PARAM_DOWNLOAD_TRACKS:
610 rv.b = vgl->download_tracks;
612 case PARAM_UPLOAD_TRACKS:
613 rv.b = vgl->upload_tracks;
615 case PARAM_DOWNLOAD_ROUTES:
616 rv.b = vgl->download_routes;
618 case PARAM_UPLOAD_ROUTES:
619 rv.b = vgl->upload_routes;
621 case PARAM_DOWNLOAD_WAYPOINTS:
622 rv.b = vgl->download_waypoints;
624 case PARAM_UPLOAD_WAYPOINTS:
625 rv.b = vgl->upload_waypoints;
627 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
628 case PARAM_GPSD_HOST:
629 rv.s = vgl->gpsd_host ? vgl->gpsd_host : "";
631 case PARAM_GPSD_PORT:
632 rv.s = vgl->gpsd_port ? vgl->gpsd_port : g_strdup(DEFAULT_GPSD_PORT);
634 case PARAM_GPSD_RETRY_INTERVAL:
635 rv.s = g_strdup_printf("%d", vgl->gpsd_retry_interval);
637 case PARAM_REALTIME_REC:
638 rv.b = vgl->realtime_record;
640 case PARAM_REALTIME_CENTER_START:
641 rv.b = vgl->realtime_jump_to_start;
643 case PARAM_VEHICLE_POSITION:
644 rv.u = vgl->vehicle_position;
646 case PARAM_REALTIME_UPDATE_STATUSBAR:
647 rv.u = vgl->realtime_update_statusbar;
649 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
651 g_warning(_("%s: unknown parameter"), __FUNCTION__);
657 VikGpsLayer *vik_gps_layer_new (VikViewport *vp)
660 VikGpsLayer *vgl = VIK_GPS_LAYER ( g_object_new ( VIK_GPS_LAYER_TYPE, NULL ) );
661 vik_layer_set_type ( VIK_LAYER(vgl), VIK_LAYER_GPS );
662 for (i = 0; i < NUM_TRW; i++) {
663 vgl->trw_children[i] = NULL;
665 vgl->children = NULL;
666 vgl->cur_read_child = 0;
668 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
669 vgl->first_realtime_trackpoint = FALSE;
671 vgl->trkpt_prev = NULL;
673 vgl->realtime_io_channel = NULL;
674 vgl->realtime_io_watch_id = 0;
675 vgl->realtime_retry_timer = 0;
677 vgl->realtime_track_gc = vik_viewport_new_gc ( vp, "#203070", 2 );
678 vgl->realtime_track_bg_gc = vik_viewport_new_gc ( vp, "grey", 2 );
679 vgl->realtime_track_pt1_gc = vik_viewport_new_gc ( vp, "red", 2 );
680 vgl->realtime_track_pt2_gc = vik_viewport_new_gc ( vp, "green", 2 );
681 vgl->realtime_track_pt_gc = vgl->realtime_track_pt1_gc;
683 vgl->realtime_track = NULL;
684 #endif // VIK_CONFIG_REALTIME_GPS_TRACKING
686 vik_layer_set_defaults ( VIK_LAYER(vgl), vp );
691 static void vik_gps_layer_draw ( VikGpsLayer *vgl, VikViewport *vp )
695 VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( vp ));
697 for (i = 0; i < NUM_TRW; i++) {
698 vl = VIK_LAYER(vgl->trw_children[i]);
700 if ( vik_viewport_get_half_drawn ( vp ) ) {
701 vik_viewport_set_half_drawn ( vp, FALSE );
702 vik_viewport_snapshot_load( vp );
704 vik_viewport_snapshot_save( vp );
707 if (!vik_viewport_get_half_drawn(vp))
708 vik_layer_draw ( vl, vp );
710 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
711 if (vgl->realtime_tracking) {
712 if (VIK_LAYER(vgl) == trigger) {
713 if ( vik_viewport_get_half_drawn ( vp ) ) {
714 vik_viewport_set_half_drawn ( vp, FALSE );
715 vik_viewport_snapshot_load( vp );
717 vik_viewport_snapshot_save( vp );
720 if (!vik_viewport_get_half_drawn(vp))
721 realtime_tracking_draw(vgl, vp);
723 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
726 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode )
729 for (i = 0; i < NUM_TRW; i++) {
730 vik_layer_change_coord_mode(VIK_LAYER(vgl->trw_children[i]), mode);
734 static void gps_layer_add_menu_items( VikGpsLayer *vgl, GtkMenu *menu, gpointer vlp )
736 static gpointer pass_along[2];
741 item = gtk_menu_item_new();
742 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
743 gtk_widget_show ( item );
746 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS") );
747 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
748 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_upload_cb), pass_along );
749 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
750 gtk_widget_show ( item );
752 item = gtk_image_menu_item_new_with_mnemonic ( _("Download from _GPS") );
753 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
754 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_download_cb), pass_along );
755 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
756 gtk_widget_show ( item );
758 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
759 item = gtk_image_menu_item_new_with_mnemonic ( vgl->realtime_tracking ?
760 "_Stop Realtime Tracking" :
761 "_Start Realtime Tracking" );
762 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, vgl->realtime_tracking ?
763 gtk_image_new_from_stock (GTK_STOCK_MEDIA_STOP, GTK_ICON_SIZE_MENU) :
764 gtk_image_new_from_stock (GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_MENU) );
765 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_start_stop_tracking_cb), pass_along );
766 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
767 gtk_widget_show ( item );
769 item = gtk_menu_item_new();
770 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
771 gtk_widget_show ( item );
773 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _Realtime") );
774 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
775 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_realtime_cb), pass_along );
776 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
777 gtk_widget_show ( item );
778 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
780 item = gtk_image_menu_item_new_with_mnemonic ( _("E_mpty Upload") );
781 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
782 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_upload_cb), pass_along );
783 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
784 gtk_widget_show ( item );
786 item = gtk_image_menu_item_new_with_mnemonic ( _("_Empty Download") );
787 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
788 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_download_cb), pass_along );
789 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
790 gtk_widget_show ( item );
792 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _All") );
793 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
794 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_all_cb), pass_along );
795 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
796 gtk_widget_show ( item );
800 static void disconnect_layer_signal ( VikLayer *vl, VikGpsLayer *vgl )
802 guint number_handlers = g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, vgl);
803 if ( number_handlers != 1 ) {
804 g_critical(_("Unexpected number of disconnected handlers: %d"), number_handlers);
808 static void vik_gps_layer_free ( VikGpsLayer *vgl )
811 for (i = 0; i < NUM_TRW; i++) {
812 if (vgl->vl.realized)
813 disconnect_layer_signal(VIK_LAYER(vgl->trw_children[i]), vgl);
814 g_object_unref(vgl->trw_children[i]);
816 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
817 rt_gpsd_disconnect(vgl);
818 if (vgl->realtime_track_gc != NULL)
819 g_object_unref(vgl->realtime_track_gc);
820 if (vgl->realtime_track_bg_gc != NULL)
821 g_object_unref(vgl->realtime_track_bg_gc);
822 if (vgl->realtime_track_pt1_gc != NULL)
823 g_object_unref(vgl->realtime_track_pt1_gc);
824 if (vgl->realtime_track_pt2_gc != NULL)
825 g_object_unref(vgl->realtime_track_pt2_gc);
826 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
829 static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter )
834 // TODO set to garmin by default
835 //if (a_babel_device_list)
836 // device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
837 // Need to access uibuild widgets somehow....
839 for (ix = 0; ix < NUM_TRW; ix++) {
840 VikLayer * trw = VIK_LAYER(vgl->trw_children[ix]);
841 vik_treeview_add_layer ( VIK_LAYER(vgl)->vt, layer_iter, &iter,
842 _(trw_names[ix]), vgl, TRUE,
843 trw, trw->type, trw->type, vik_layer_get_timestamp(trw) );
844 if ( ! trw->visible )
845 vik_treeview_item_set_visible ( VIK_LAYER(vgl)->vt, &iter, FALSE );
846 vik_layer_realize ( trw, VIK_LAYER(vgl)->vt, &iter );
847 g_signal_connect_swapped ( G_OBJECT(trw), "update", G_CALLBACK(vik_layer_emit_update_secondary), vgl );
851 const GList *vik_gps_layer_get_children ( VikGpsLayer *vgl )
855 if (vgl->children == NULL) {
856 for (i = NUM_TRW - 1; i >= 0; i--)
857 vgl->children = g_list_prepend(vgl->children, vgl->trw_children[i]);
859 return vgl->children;
862 VikTrwLayer * vik_gps_layer_get_a_child(VikGpsLayer *vgl)
864 g_assert ((vgl->cur_read_child >= 0) && (vgl->cur_read_child < NUM_TRW));
866 VikTrwLayer * vtl = vgl->trw_children[vgl->cur_read_child];
867 if (++(vgl->cur_read_child) >= NUM_TRW)
868 vgl->cur_read_child = 0;
872 gboolean vik_gps_layer_is_empty ( VikGpsLayer *vgl )
874 if ( vgl->trw_children[0] )
879 static void gps_session_delete(GpsSession *sess)
881 vik_mutex_free(sess->mutex);
882 g_free(sess->babelargs);
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);
903 sess->total_count = mycnt;
910 switch (sess->progress_type) {
911 case WPT: tmp_str = ngettext("Uploading %d waypoint...", "Uploading %d waypoints...", cnt); break;
912 case TRK: tmp_str = ngettext("Uploading %d trackpoint...", "Uploading %d trackpoints...", cnt); break;
913 default: tmp_str = ngettext("Uploading %d routepoint...", "Uploading %d routepoints...", cnt); break;
917 g_snprintf(s, 128, tmp_str, cnt);
918 gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
919 gtk_widget_show ( sess->progress_label );
920 sess->total_count = cnt;
922 g_mutex_unlock(sess->mutex);
926 static void set_current_count(gint cnt, GpsSession *sess)
929 const gchar *tmp_str;
932 g_mutex_lock(sess->mutex);
934 if (cnt < sess->total_count) {
935 if (sess->direction == GPS_DOWN)
937 switch (sess->progress_type) {
938 case WPT: tmp_str = ngettext("Downloaded %d out of %d waypoint...", "Downloaded %d out of %d waypoints...", sess->total_count); break;
939 case TRK: tmp_str = ngettext("Downloaded %d out of %d trackpoint...", "Downloaded %d out of %d trackpoints...", sess->total_count); break;
940 default: tmp_str = ngettext("Downloaded %d out of %d routepoint...", "Downloaded %d out of %d routepoints...", sess->total_count); break;
944 switch (sess->progress_type) {
945 case WPT: tmp_str = ngettext("Uploaded %d out of %d waypoint...", "Uploaded %d out of %d waypoints...", sess->total_count); break;
946 case TRK: tmp_str = ngettext("Uploaded %d out of %d trackpoint...", "Uploaded %d out of %d trackpoints...", sess->total_count); break;
947 default: tmp_str = ngettext("Uploaded %d out of %d routepoint...", "Uploaded %d out of %d routepoints...", sess->total_count); break;
950 g_snprintf(s, 128, tmp_str, cnt, sess->total_count);
952 if (sess->direction == GPS_DOWN)
954 switch (sess->progress_type) {
955 case WPT: tmp_str = ngettext("Downloaded %d waypoint", "Downloaded %d waypoints", cnt); break;
956 case TRK: tmp_str = ngettext("Downloaded %d trackpoint", "Downloaded %d trackpoints", cnt); break;
957 default: tmp_str = ngettext("Downloaded %d routepoint", "Downloaded %d routepoints", cnt); break;
961 switch (sess->progress_type) {
962 case WPT: tmp_str = ngettext("Uploaded %d waypoint", "Uploaded %d waypoints", cnt); break;
963 case TRK: tmp_str = ngettext("Uploaded %d trackpoint", "Uploaded %d trackpoints", cnt); break;
964 default: tmp_str = ngettext("Uploaded %d routepoint", "Uploaded %d routepoints", cnt); break;
967 g_snprintf(s, 128, tmp_str, cnt);
969 gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
971 g_mutex_unlock(sess->mutex);
975 static void set_gps_info(const gchar *info, GpsSession *sess)
979 g_mutex_lock(sess->mutex);
981 g_snprintf(s, 256, _("GPS Device: %s"), info);
982 gtk_label_set_text ( GTK_LABEL(sess->gps_label), s );
984 g_mutex_unlock(sess->mutex);
989 * Common processing for GPS Device information
990 * It doesn't matter whether we're uploading or downloading
992 static void process_line_for_gps_info ( const gchar *line, GpsSession *sess )
994 if (strstr(line, "PRDDAT")) {
995 gchar **tokens = g_strsplit(line, " ", 0);
1001 while (tokens[n_tokens])
1004 // I'm not entirely clear what information this is trying to get...
1005 // Obviously trying to decipher some kind of text/naming scheme
1006 // Anyway this will be superceded if there is 'Unit:' information
1008 for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
1010 sscanf(tokens[i], "%x", &ch);
1014 set_gps_info(info, sess);
1019 /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
1020 if (strstr(line, "Unit:")) {
1021 gchar **tokens = g_strsplit(line, "\t", 0);
1023 while (tokens[n_tokens])
1027 set_gps_info(tokens[1], sess);
1033 static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1037 gdk_threads_enter ();
1038 g_mutex_lock(sess->mutex);
1040 g_mutex_unlock(sess->mutex);
1041 gps_session_delete(sess);
1042 gdk_threads_leave();
1043 g_thread_exit ( NULL );
1045 g_mutex_unlock(sess->mutex);
1046 gdk_threads_leave ();
1049 case BABEL_DIAG_OUTPUT:
1050 line = (gchar *)data;
1052 gdk_threads_enter();
1053 g_mutex_lock(sess->mutex);
1055 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1057 g_mutex_unlock(sess->mutex);
1058 gdk_threads_leave();
1060 /* tells us the type of items that will follow */
1061 if (strstr(line, "Xfer Wpt")) {
1062 sess->progress_label = sess->wp_label;
1063 sess->progress_type = WPT;
1065 if (strstr(line, "Xfer Trk")) {
1066 sess->progress_label = sess->trk_label;
1067 sess->progress_type = TRK;
1069 if (strstr(line, "Xfer Rte")) {
1070 sess->progress_label = sess->rte_label;
1071 sess->progress_type = RTE;
1074 process_line_for_gps_info ( line, sess );
1076 if (strstr(line, "RECORD")) {
1079 if (strlen(line) > 20) {
1080 sscanf(line+17, "%x", &lsb);
1081 sscanf(line+20, "%x", &msb);
1082 cnt = lsb + msb * 256;
1083 set_total_count(cnt, sess);
1087 if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1089 set_current_count(sess->count, sess);
1100 static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1105 gdk_threads_enter ();
1106 g_mutex_lock(sess->mutex);
1108 g_mutex_unlock(sess->mutex);
1109 gps_session_delete(sess);
1110 gdk_threads_leave();
1111 g_thread_exit ( NULL );
1113 g_mutex_unlock(sess->mutex);
1114 gdk_threads_leave ();
1117 case BABEL_DIAG_OUTPUT:
1118 line = (gchar *)data;
1120 gdk_threads_enter();
1121 g_mutex_lock(sess->mutex);
1123 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1125 g_mutex_unlock(sess->mutex);
1126 gdk_threads_leave();
1128 process_line_for_gps_info ( line, sess );
1130 if (strstr(line, "RECORD")) {
1133 if (strlen(line) > 20) {
1134 sscanf(line+17, "%x", &lsb);
1135 sscanf(line+20, "%x", &msb);
1136 cnt = lsb + msb * 256;
1137 /* set_total_count(cnt, sess); */
1141 if ( strstr(line, "WPTDAT")) {
1142 if (sess->count == 0) {
1143 sess->progress_label = sess->wp_label;
1144 sess->progress_type = WPT;
1145 set_total_count(cnt, sess);
1148 set_current_count(sess->count, sess);
1150 if ( strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1151 if (sess->count == 0) {
1152 sess->progress_label = sess->rte_label;
1153 sess->progress_type = RTE;
1154 // 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
1155 // Anyway since we're uploading - we should know how many points we're going to put!
1156 cnt = (cnt / 2) + 1;
1157 set_total_count(cnt, sess);
1160 set_current_count(sess->count, sess);
1162 if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
1163 if (sess->count == 0) {
1164 sess->progress_label = sess->trk_label;
1165 sess->progress_type = TRK;
1166 set_total_count(cnt, sess);
1169 set_current_count(sess->count, sess);
1180 static void gps_comm_thread(GpsSession *sess)
1184 if (sess->direction == GPS_DOWN) {
1185 ProcessOptions po = { sess->babelargs, sess->port, NULL, NULL, NULL, NULL };
1186 result = a_babel_convert_from (sess->vtl, &po, (BabelStatusFunc) gps_download_progress_func, sess, NULL);
1189 result = a_babel_convert_to (sess->vtl, sess->track, sess->babelargs, sess->port,
1190 (BabelStatusFunc) gps_upload_progress_func, sess);
1194 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: couldn't find gpsbabel.") );
1197 g_mutex_lock(sess->mutex);
1199 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Done.") );
1200 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE );
1201 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE );
1203 /* Do not change the view if we are following the current GPS position */
1204 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1205 if (!sess->realtime_tracking)
1208 if ( sess->vvp && sess->direction == GPS_DOWN ) {
1209 vik_layer_post_read ( VIK_LAYER(sess->vtl), sess->vvp, TRUE );
1210 /* View the data available */
1211 vik_trw_layer_auto_set_view ( sess->vtl, sess->vvp ) ;
1212 vik_layer_emit_update ( VIK_LAYER(sess->vtl) ); // NB update from background thread
1218 g_mutex_unlock(sess->mutex);
1221 g_mutex_lock(sess->mutex);
1224 g_mutex_unlock(sess->mutex);
1227 g_mutex_unlock(sess->mutex);
1228 gps_session_delete(sess);
1230 g_thread_exit(NULL);
1235 * @vtl: The TrackWaypoint layer to operate on
1236 * @track: Operate on a particular track when specified
1237 * @dir: The direction of the transfer
1238 * @protocol: The GPS device communication protocol
1239 * @port: The GPS serial port
1240 * @tracking: If tracking then viewport display update will be skipped
1241 * @vvp: A viewport is required as the display may get updated
1242 * @vlp: A layers panel is needed for uploading as the items maybe modified
1243 * @do_tracks: Whether tracks shoud be processed
1244 * @do_waypoints: Whether waypoints shoud be processed
1245 * @turn_off: Whether we should attempt to turn off the GPS device after the transfer (only some devices support this)
1247 * Talk to a GPS Device using a thread which updates a dialog with the progress
1249 gint vik_gps_comm ( VikTrwLayer *vtl,
1256 VikLayersPanel *vlp,
1259 gboolean do_waypoints,
1262 GpsSession *sess = g_malloc(sizeof(GpsSession));
1263 char *tracks = NULL;
1264 char *routes = NULL;
1265 char *waypoints = NULL;
1267 sess->mutex = vik_mutex_new();
1268 sess->direction = dir;
1270 sess->track = track;
1271 sess->port = g_strdup(port);
1273 sess->window_title = (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload");
1276 // This must be done inside the main thread as the uniquify causes screen updates
1277 // (originally performed this nearer the point of upload in the thread)
1278 if ( dir == GPS_UP ) {
1279 // Enforce unique names in the layer upload to the GPS device
1280 // NB this may only be a Garmin device restriction (and may be not every Garmin device either...)
1281 // Thus this maintains the older code in built restriction
1282 if ( ! vik_trw_layer_uniquify ( sess->vtl, vlp ) )
1283 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(sess->vtl))), VIK_STATUSBAR_INFO,
1284 _("Warning - GPS Upload items may overwrite each other") );
1287 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1288 sess->realtime_tracking = tracking;
1304 sess->babelargs = g_strdup_printf("-D 9 %s %s %s -%c %s",
1305 tracks, routes, waypoints, (dir == GPS_DOWN) ? 'i' : 'o', protocol);
1309 // Only create dialog if we're going to do some transferring
1310 if ( do_tracks || do_waypoints || do_routes ) {
1311 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 );
1312 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog),
1313 GTK_RESPONSE_ACCEPT, FALSE );
1314 gtk_window_set_title ( GTK_WINDOW(sess->dialog), sess->window_title );
1316 sess->status_label = gtk_label_new (_("Status: detecting gpsbabel"));
1317 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->status_label, FALSE, FALSE, 5 );
1318 gtk_widget_show_all(sess->status_label);
1320 sess->gps_label = gtk_label_new (_("GPS device: N/A"));
1321 sess->ver_label = gtk_label_new ("");
1322 sess->id_label = gtk_label_new ("");
1323 sess->wp_label = gtk_label_new ("");
1324 sess->trk_label = gtk_label_new ("");
1325 sess->rte_label = gtk_label_new ("");
1327 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->gps_label, FALSE, FALSE, 5 );
1328 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->wp_label, FALSE, FALSE, 5 );
1329 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->trk_label, FALSE, FALSE, 5 );
1330 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->rte_label, FALSE, FALSE, 5 );
1332 gtk_widget_show_all(sess->dialog);
1334 sess->progress_label = sess->wp_label;
1335 sess->total_count = -1;
1337 // Starting gps read/write thread
1338 #if GLIB_CHECK_VERSION (2, 32, 0)
1339 g_thread_try_new ( "gps_comm_thread", (GThreadFunc)gps_comm_thread, sess, NULL );
1341 g_thread_create ( (GThreadFunc)gps_comm_thread, sess, FALSE, NULL );
1344 gtk_dialog_set_default_response ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT );
1345 gtk_dialog_run(GTK_DIALOG(sess->dialog));
1347 gtk_widget_destroy(sess->dialog);
1351 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No GPS items selected for transfer.") );
1354 g_mutex_lock(sess->mutex);
1357 sess->ok = FALSE; /* tell thread to stop */
1358 g_mutex_unlock(sess->mutex);
1362 // No need for thread for powering off device (should be quick operation...) - so use babel command directly:
1363 gchar *device_off = g_strdup_printf("-i %s,%s", protocol, "power_off");
1364 ProcessOptions po = { device_off, port, NULL, NULL, NULL, NULL };
1365 gboolean result = a_babel_convert_from (NULL, &po, NULL, NULL, NULL);
1367 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not turn off device.") );
1368 g_free ( device_off );
1370 g_mutex_unlock(sess->mutex);
1371 gps_session_delete(sess);
1377 static void gps_upload_cb( gpointer layer_and_vlp[2] )
1379 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1380 VikLayersPanel *vlp = VIK_LAYERS_PANEL(layer_and_vlp[1]);
1381 VikTrwLayer *vtl = vgl->trw_children[TRW_UPLOAD];
1382 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1383 VikViewport *vvp = vik_window_viewport(vw);
1384 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);
1387 static void gps_download_cb( gpointer layer_and_vlp[2] )
1389 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1390 VikTrwLayer *vtl = vgl->trw_children[TRW_DOWNLOAD];
1391 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1392 VikViewport *vvp = vik_window_viewport(vw);
1393 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1394 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);
1396 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);
1400 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] )
1402 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1403 // Get confirmation from the user
1404 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1405 _("Are you sure you want to delete GPS Upload data?"),
1408 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1409 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1410 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1413 static void gps_empty_download_cb( gpointer layer_and_vlp[2] )
1415 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1416 // Get confirmation from the user
1417 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1418 _("Are you sure you want to delete GPS Download data?"),
1421 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1422 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1423 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1426 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1427 static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] )
1429 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1430 // Get confirmation from the user
1431 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1432 _("Are you sure you want to delete GPS Realtime data?"),
1435 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1436 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1440 static void gps_empty_all_cb( gpointer layer_and_vlp[2] )
1442 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1443 // Get confirmation from the user
1444 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1445 _("Are you sure you want to delete All GPS data?"),
1448 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1449 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1450 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1451 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1452 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1453 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1454 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1455 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1456 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1460 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1461 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp)
1465 struct LatLon lnw, lse;
1466 vik_viewport_screen_to_coord ( vp, -20, -20, &nw );
1467 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp)+20, vik_viewport_get_width(vp)+20, &se );
1468 vik_coord_to_latlon ( &nw, &lnw );
1469 vik_coord_to_latlon ( &se, &lse );
1470 if ( vgl->realtime_fix.fix.latitude > lse.lat &&
1471 vgl->realtime_fix.fix.latitude < lnw.lat &&
1472 vgl->realtime_fix.fix.longitude > lnw.lon &&
1473 vgl->realtime_fix.fix.longitude < lse.lon &&
1474 !isnan (vgl->realtime_fix.fix.track) ) {
1477 gint half_back_x, half_back_y;
1478 gint half_back_bg_x, half_back_bg_y;
1481 gint side1_x, side1_y, side2_x, side2_y;
1482 gint side1bg_x, side1bg_y, side2bg_x, side2bg_y;
1484 ll.lat = vgl->realtime_fix.fix.latitude;
1485 ll.lon = vgl->realtime_fix.fix.longitude;
1486 vik_coord_load_from_latlon ( &gps, vik_viewport_get_coord_mode(vp), &ll);
1487 vik_viewport_coord_to_screen ( vp, &gps, &x, &y );
1489 gdouble heading_cos = cos(DEG2RAD(vgl->realtime_fix.fix.track));
1490 gdouble heading_sin = sin(DEG2RAD(vgl->realtime_fix.fix.track));
1492 half_back_y = y+8*heading_cos;
1493 half_back_x = x-8*heading_sin;
1494 half_back_bg_y = y+10*heading_cos;
1495 half_back_bg_x = x-10*heading_sin;
1497 pt_y = half_back_y-24*heading_cos;
1498 pt_x = half_back_x+24*heading_sin;
1499 //ptbg_y = half_back_bg_y-28*heading_cos;
1500 ptbg_x = half_back_bg_x+28*heading_sin;
1502 side1_y = half_back_y+9*heading_sin;
1503 side1_x = half_back_x+9*heading_cos;
1504 side1bg_y = half_back_bg_y+11*heading_sin;
1505 side1bg_x = half_back_bg_x+11*heading_cos;
1507 side2_y = half_back_y-9*heading_sin;
1508 side2_x = half_back_x-9*heading_cos;
1509 side2bg_y = half_back_bg_y-11*heading_sin;
1510 side2bg_x = half_back_bg_x-11*heading_cos;
1512 GdkPoint trian[3] = { { pt_x, pt_y }, {side1_x, side1_y}, {side2_x, side2_y} };
1513 GdkPoint trian_bg[3] = { { ptbg_x, pt_y }, {side1bg_x, side1bg_y}, {side2bg_x, side2bg_y} };
1515 vik_viewport_draw_polygon ( vp, vgl->realtime_track_bg_gc, TRUE, trian_bg, 3 );
1516 vik_viewport_draw_polygon ( vp, vgl->realtime_track_gc, TRUE, trian, 3 );
1517 vik_viewport_draw_rectangle ( vp,
1518 (vgl->realtime_fix.fix.mode > MODE_2D) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc,
1519 TRUE, x-2, y-2, 4, 4 );
1520 //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;
1524 static VikTrackpoint* create_realtime_trackpoint(VikGpsLayer *vgl, gboolean forced)
1529 /* Note that fix.time is a double, but it should not affect the precision
1531 time_t cur_timestamp = vgl->realtime_fix.fix.time;
1532 time_t last_timestamp = vgl->last_fix.fix.time;
1534 if (cur_timestamp < last_timestamp) {
1538 if (vgl->realtime_record && vgl->realtime_fix.dirty) {
1539 gboolean replace = FALSE;
1540 int heading = isnan(vgl->realtime_fix.fix.track) ? 0 : (int)floor(vgl->realtime_fix.fix.track);
1541 int last_heading = isnan(vgl->last_fix.fix.track) ? 0 : (int)floor(vgl->last_fix.fix.track);
1542 int alt = isnan(vgl->realtime_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->realtime_fix.fix.altitude);
1543 int last_alt = isnan(vgl->last_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->last_fix.fix.altitude);
1544 if (((last_tp = g_list_last(vgl->realtime_track->trackpoints)) != NULL) &&
1545 (vgl->realtime_fix.fix.mode > MODE_2D) &&
1546 (vgl->last_fix.fix.mode <= MODE_2D) &&
1547 ((cur_timestamp - last_timestamp) < 2)) {
1548 g_free(last_tp->data);
1549 vgl->realtime_track->trackpoints = g_list_delete_link(vgl->realtime_track->trackpoints, last_tp);
1553 ((cur_timestamp != last_timestamp) &&
1555 ((heading < last_heading) && (heading < (last_heading - 3))) ||
1556 ((heading > last_heading) && (heading > (last_heading + 3))) ||
1557 ((alt != VIK_DEFAULT_ALTITUDE) && (alt != last_alt)))))) {
1558 /* TODO: check for new segments */
1559 VikTrackpoint *tp = vik_trackpoint_new();
1560 tp->newsegment = FALSE;
1561 tp->has_timestamp = TRUE;
1562 tp->timestamp = vgl->realtime_fix.fix.time;
1564 /* speed only available for 3D fix. Check for NAN when use this speed */
1565 tp->speed = vgl->realtime_fix.fix.speed;
1566 tp->course = vgl->realtime_fix.fix.track;
1567 tp->nsats = vgl->realtime_fix.satellites_used;
1568 tp->fix_mode = vgl->realtime_fix.fix.mode;
1570 ll.lat = vgl->realtime_fix.fix.latitude;
1571 ll.lon = vgl->realtime_fix.fix.longitude;
1572 vik_coord_load_from_latlon(&tp->coord,
1573 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1575 vik_track_add_trackpoint ( vgl->realtime_track, tp, TRUE ); // Ensure bounds is recalculated
1576 vgl->realtime_fix.dirty = FALSE;
1577 vgl->realtime_fix.satellites_used = 0;
1578 vgl->last_fix = vgl->realtime_fix;
1585 #define VIK_SETTINGS_GPS_STATUSBAR_FORMAT "gps_statusbar_format"
1587 static void update_statusbar ( VikGpsLayer *vgl, VikWindow *vw )
1589 gchar *statusbar_format_code = NULL;
1590 gboolean need2free = FALSE;
1591 if ( !a_settings_get_string ( VIK_SETTINGS_GPS_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
1592 // Otherwise use default
1593 statusbar_format_code = g_strdup ( "GSA" );
1597 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, vgl->trkpt, vgl->trkpt_prev, vgl->realtime_track, vgl->last_fix.fix.climb );
1598 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
1602 g_free ( statusbar_format_code );
1606 static void gpsd_raw_hook(VglGpsd *vgpsd, gchar *data)
1608 gboolean update_all = FALSE;
1609 VikGpsLayer *vgl = vgpsd->vgl;
1611 if (!vgl->realtime_tracking) {
1612 g_warning("%s: receiving GPS data while not in realtime mode", __PRETTY_FUNCTION__);
1616 if ((vgpsd->gpsd.fix.mode >= MODE_2D) &&
1617 !isnan(vgpsd->gpsd.fix.latitude) &&
1618 !isnan(vgpsd->gpsd.fix.longitude)) {
1620 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1621 VikViewport *vvp = vik_window_viewport(vw);
1622 vgl->realtime_fix.fix = vgpsd->gpsd.fix;
1623 vgl->realtime_fix.satellites_used = vgpsd->gpsd.satellites_used;
1624 vgl->realtime_fix.dirty = TRUE;
1627 VikCoord vehicle_coord;
1629 ll.lat = vgl->realtime_fix.fix.latitude;
1630 ll.lon = vgl->realtime_fix.fix.longitude;
1631 vik_coord_load_from_latlon(&vehicle_coord,
1632 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1634 if ((vgl->vehicle_position == VEHICLE_POSITION_CENTERED) ||
1635 (vgl->realtime_jump_to_start && vgl->first_realtime_trackpoint)) {
1636 vik_viewport_set_center_coord(vvp, &vehicle_coord, FALSE);
1639 else if (vgl->vehicle_position == VEHICLE_POSITION_ON_SCREEN) {
1642 const int px = 20; /* adjust ment in pixels to make sure vehicle is inside the box */
1643 gint width = vik_viewport_get_width(vvp);
1644 gint height = vik_viewport_get_height(vvp);
1647 vik_viewport_coord_to_screen(vvp, &vehicle_coord, &vx, &vy);
1649 if (vx < (width/hdiv))
1650 vik_viewport_set_center_screen(vvp, vx - width/2 + width/hdiv + px, vy);
1651 else if (vx > (width - width/hdiv))
1652 vik_viewport_set_center_screen(vvp, vx + width/2 - width/hdiv - px, vy);
1653 else if (vy < (height/vdiv))
1654 vik_viewport_set_center_screen(vvp, vx, vy - height/2 + height/vdiv + px);
1655 else if (vy > (height - height/vdiv))
1656 vik_viewport_set_center_screen(vvp, vx, vy + height/2 - height/vdiv - px);
1661 vgl->first_realtime_trackpoint = FALSE;
1663 vgl->trkpt = create_realtime_trackpoint ( vgl, FALSE );
1666 if ( vgl->realtime_update_statusbar )
1667 update_statusbar ( vgl, vw );
1668 vgl->trkpt_prev = vgl->trkpt;
1671 vik_layer_emit_update ( update_all ? VIK_LAYER(vgl) : VIK_LAYER(vgl->trw_children[TRW_REALTIME]) ); // NB update from background thread
1675 static gboolean gpsd_data_available(GIOChannel *source, GIOCondition condition, gpointer data)
1677 VikGpsLayer *vgl = data;
1678 if (condition == G_IO_IN) {
1679 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1680 if (!gps_poll(&vgl->vgpsd->gpsd)) {
1681 #elif GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1682 if (gps_read(&vgl->vgpsd->gpsd) > -1) {
1683 // Reuse old function to perform operations on the new GPS data
1684 gpsd_raw_hook(vgl->vgpsd, NULL);
1691 g_warning("Disconnected from gpsd. Trying to reconnect");
1692 rt_gpsd_disconnect(vgl);
1693 (void)rt_gpsd_connect(vgl, FALSE);
1696 return FALSE; /* no further calling */
1699 static gchar *make_track_name(VikTrwLayer *vtl)
1701 const gchar basename[] = "REALTIME";
1702 const gint bufsize = sizeof(basename) + 5;
1703 gchar *name = g_malloc(bufsize);
1704 strcpy(name, basename);
1707 while (vik_trw_layer_get_track(vtl, name) != NULL) {
1708 g_snprintf(name, bufsize, "%s#%d", basename, i);
1715 static gboolean rt_gpsd_try_connect(gpointer *data)
1717 VikGpsLayer *vgl = (VikGpsLayer *)data;
1718 #if GPSD_API_MAJOR_VERSION == 3
1719 struct gps_data_t *gpsd = gps_open(vgl->gpsd_host, vgl->gpsd_port);
1722 #elif GPSD_API_MAJOR_VERSION == 4
1723 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1725 if (gps_open_r(vgl->gpsd_host, vgl->gpsd_port, /*(struct gps_data_t *)*/vgl->vgpsd) != 0) {
1726 #elif GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1727 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1728 vgl->vgpsd->gpsd_open = gps_open ( vgl->gpsd_host, vgl->gpsd_port, &vgl->vgpsd->gpsd );
1729 if ( vgl->vgpsd->gpsd_open != 0 ) {
1731 // Delibrately break compilation...
1733 g_warning("Failed to connect to gpsd at %s (port %s). Will retry in %d seconds",
1734 vgl->gpsd_host, vgl->gpsd_port, vgl->gpsd_retry_interval);
1735 return TRUE; /* keep timer running */
1738 #if GPSD_API_MAJOR_VERSION == 3
1739 vgl->vgpsd = realloc(gpsd, sizeof(VglGpsd));
1741 vgl->vgpsd->vgl = vgl;
1743 vgl->realtime_fix.dirty = vgl->last_fix.dirty = FALSE;
1744 /* track alt/time graph uses VIK_DEFAULT_ALTITUDE (0.0) as invalid */
1745 vgl->realtime_fix.fix.altitude = vgl->last_fix.fix.altitude = VIK_DEFAULT_ALTITUDE;
1746 vgl->realtime_fix.fix.speed = vgl->last_fix.fix.speed = NAN;
1748 if (vgl->realtime_record) {
1749 VikTrwLayer *vtl = vgl->trw_children[TRW_REALTIME];
1750 vgl->realtime_track = vik_track_new();
1751 vgl->realtime_track->visible = TRUE;
1752 vik_trw_layer_add_track(vtl, make_track_name(vtl), vgl->realtime_track);
1755 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1756 gps_set_raw_hook(&vgl->vgpsd->gpsd, gpsd_raw_hook);
1759 vgl->realtime_io_channel = g_io_channel_unix_new(vgl->vgpsd->gpsd.gps_fd);
1760 vgl->realtime_io_watch_id = g_io_add_watch( vgl->realtime_io_channel,
1761 G_IO_IN | G_IO_ERR | G_IO_HUP, gpsd_data_available, vgl);
1763 #if GPSD_API_MAJOR_VERSION == 3
1764 gps_query(&vgl->vgpsd->gpsd, "w+x");
1766 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1767 if ( gps_stream(&vgl->vgpsd->gpsd, WATCH_ENABLE, NULL) == -1 )
1768 g_critical ( "gps_stream error" );
1771 return FALSE; /* no longer called by timeout */
1774 static gboolean rt_ask_retry(VikGpsLayer *vgl)
1776 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER(vgl),
1777 GTK_DIALOG_DESTROY_WITH_PARENT,
1778 GTK_MESSAGE_QUESTION,
1780 "Failed to connect to gpsd at %s (port %s)\n"
1781 "Should Viking keep trying (every %d seconds)?",
1782 vgl->gpsd_host, vgl->gpsd_port,
1783 vgl->gpsd_retry_interval);
1785 gint res = gtk_dialog_run(GTK_DIALOG(dialog));
1786 gtk_widget_destroy(dialog);
1787 return (res == GTK_RESPONSE_YES);
1790 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed)
1792 vgl->realtime_retry_timer = 0;
1793 if (rt_gpsd_try_connect((gpointer *)vgl)) {
1794 if (vgl->gpsd_retry_interval <= 0) {
1795 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);
1798 else if (ask_if_failed && !rt_ask_retry(vgl))
1801 vgl->realtime_retry_timer = g_timeout_add_seconds(vgl->gpsd_retry_interval,
1802 (GSourceFunc)rt_gpsd_try_connect, (gpointer *)vgl);
1807 static void rt_gpsd_disconnect(VikGpsLayer *vgl)
1809 if (vgl->realtime_retry_timer) {
1810 g_source_remove(vgl->realtime_retry_timer);
1811 vgl->realtime_retry_timer = 0;
1813 if (vgl->realtime_io_watch_id) {
1814 g_source_remove(vgl->realtime_io_watch_id);
1815 vgl->realtime_io_watch_id = 0;
1817 if (vgl->realtime_io_channel) {
1818 GError *error = NULL;
1819 g_io_channel_shutdown (vgl->realtime_io_channel, FALSE, &error);
1820 vgl->realtime_io_channel = NULL;
1823 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1824 gps_stream(&vgl->vgpsd->gpsd, WATCH_DISABLE, NULL);
1826 if ( vgl->vgpsd->gpsd_open == 0 )
1827 (void)gps_close(&vgl->vgpsd->gpsd);
1828 #if GPSD_API_MAJOR_VERSION == 3
1830 #elif GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1836 if (vgl->realtime_record && vgl->realtime_track) {
1837 if ((vgl->realtime_track->trackpoints == NULL) || (vgl->realtime_track->trackpoints->next == NULL))
1838 vik_trw_layer_delete_track(vgl->trw_children[TRW_REALTIME], vgl->realtime_track);
1839 vgl->realtime_track = NULL;
1843 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2])
1845 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1846 vgl->realtime_tracking = (vgl->realtime_tracking == FALSE);
1848 /* Make sure we are still in the boat with libgps */
1849 g_assert((VIK_GPS_MODE_2D == MODE_2D) && (VIK_GPS_MODE_3D == MODE_3D));
1851 if (vgl->realtime_tracking) {
1852 vgl->first_realtime_trackpoint = TRUE;
1853 if (!rt_gpsd_connect(vgl, TRUE)) {
1854 vgl->first_realtime_trackpoint = FALSE;
1855 vgl->realtime_tracking = FALSE;
1859 else { /* stop realtime tracking */
1860 vgl->first_realtime_trackpoint = FALSE;
1862 rt_gpsd_disconnect(vgl);
1865 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */