2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2006-2008, Quy Tonthat <qtonthat@gmail.com>
6 * Copyright (C) 2016, Rob Norris <rw_norris@hotmail.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 #include "icons/icons.h"
35 #include "viktrwlayer.h"
43 #include <glib/gstdio.h>
44 #include <glib/gprintf.h>
45 #include <glib/gi18n.h>
46 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
51 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp);
52 static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter );
53 static void vik_gps_layer_free ( VikGpsLayer *vgl );
54 static void vik_gps_layer_draw ( VikGpsLayer *vgl, VikViewport *vp );
55 static VikGpsLayer *vik_gps_layer_new ( VikViewport *vp );
56 static void vik_gps_layer_post_read ( VikGpsLayer *vgl, VikViewport *vp, gboolean from_file );
58 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *len );
59 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
60 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
61 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation );
63 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl );
65 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode );
66 static void gps_layer_add_menu_items( VikGpsLayer *vtl, GtkMenu *menu, gpointer vlp );
68 static void gps_upload_cb( gpointer layer_and_vlp[2] );
69 static void gps_download_cb( gpointer layer_and_vlp[2] );
70 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] );
71 static void gps_empty_download_cb( gpointer layer_and_vlp[2] );
72 static void gps_empty_all_cb( gpointer layer_and_vlp[2] );
73 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
74 static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] );
75 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2] );
76 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp);
77 static void rt_gpsd_disconnect(VikGpsLayer *vgl);
78 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed);
81 // Shouldn't need to use these much any more as the protocol is now saved as a string.
82 // They are kept for compatibility loading old .vik files
83 typedef enum {GARMIN_P = 0, MAGELLAN_P, DELORME_P, NAVILINK_P, OLD_NUM_PROTOCOLS} vik_gps_proto;
84 static gchar * protocols_args[] = {"garmin", "magellan", "delbin", "navilink", NULL};
86 static gchar * params_ports[] = {"com1", "usb:", NULL};
88 static gchar * params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
90 /* NUM_PORTS not actually used */
91 /* #define NUM_PORTS (sizeof(params_ports)/sizeof(params_ports[0]) - 1) */
92 /* Compatibility with previous versions */
94 static gchar * old_params_ports[] = {"com1", "usb:", NULL};
96 static gchar * old_params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
98 #define OLD_NUM_PORTS (sizeof(old_params_ports)/sizeof(old_params_ports[0]) - 1)
103 gboolean thread_complete;
104 vik_gps_dir direction;
106 gint wpt_total_count;
108 gint trk_total_count;
110 gint rte_total_count;
116 GtkWidget *status_label;
117 GtkWidget *gps_label;
118 GtkWidget *wpt_label;
119 GtkWidget *trk_label;
120 GtkWidget *rte_label;
121 vik_gps_xfer_type progress_type;
125 gint id_status_working;
131 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
132 gboolean realtime_tracking;
135 static void gps_session_delete(GpsSession *sess);
137 static gchar *params_groups[] = {
139 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
140 N_("Realtime Tracking Mode"),
144 enum {GROUP_DATA_MODE, GROUP_REALTIME_MODE};
147 static VikLayerParamData gps_protocol_default ( void )
149 VikLayerParamData data;
150 data.s = g_strdup ( "garmin" );
154 static VikLayerParamData gps_port_default ( void )
156 VikLayerParamData data;
157 data.s = g_strdup ( "usb:" );
159 /* Attempt to auto set default USB serial port entry */
160 /* Ordered to make lowest device favourite if available */
161 if (g_access ("/dev/ttyUSB1", R_OK) == 0) {
163 g_free ( (gchar *)data.s );
164 data.s = g_strdup ("/dev/ttyUSB1");
166 if (g_access ("/dev/ttyUSB0", R_OK) == 0) {
168 g_free ( (gchar *)data.s );
169 data.s = g_strdup ("/dev/ttyUSB0");
175 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
176 static gchar *params_vehicle_position[] = {
177 N_("Keep vehicle at center"),
178 N_("Keep vehicle on screen"),
183 VEHICLE_POSITION_CENTERED = 0,
184 VEHICLE_POSITION_ON_SCREEN,
185 VEHICLE_POSITION_NONE,
188 static VikLayerParamData moving_map_method_default ( void ) { return VIK_LPD_UINT ( VEHICLE_POSITION_ON_SCREEN ); }
190 static VikLayerParamData gpsd_host_default ( void )
192 VikLayerParamData data;
193 data.s = g_strdup ( "localhost" );
197 static VikLayerParamData gpsd_port_default ( void )
199 VikLayerParamData data;
200 data.s = g_strdup ( DEFAULT_GPSD_PORT );
204 static VikLayerParamData gpsd_retry_interval_default ( void )
206 VikLayerParamData data;
207 data.s = g_strdup ( "10" );
213 static VikLayerParam gps_layer_params[] = {
214 // NB gps_layer_inst_init() is performed after parameter registeration
215 // thus to give the protocols some potential values use the old static list
216 // TODO: find another way to use gps_layer_inst_init()?
217 { 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
218 { 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 },
219 { 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 },
220 { 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 },
221 { 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 },
222 { 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 },
223 { 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 },
224 { 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 },
225 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
226 { 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 },
227 { 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 },
228 { 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 },
229 { 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 },
230 { VIK_LAYER_GPS, "auto_connect", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Auto Connect"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, N_("Automatically connect to GPSD"), vik_lpd_false_default, NULL, NULL },
231 { 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 },
232 { 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 },
233 { 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 },
234 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
237 PARAM_PROTOCOL=0, PARAM_PORT,
238 PARAM_DOWNLOAD_TRACKS, PARAM_UPLOAD_TRACKS,
239 PARAM_DOWNLOAD_ROUTES, PARAM_UPLOAD_ROUTES,
240 PARAM_DOWNLOAD_WAYPOINTS, PARAM_UPLOAD_WAYPOINTS,
241 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
243 PARAM_REALTIME_CENTER_START,
244 PARAM_VEHICLE_POSITION,
245 PARAM_REALTIME_UPDATE_STATUSBAR,
249 PARAM_GPSD_RETRY_INTERVAL,
250 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
253 VikLayerInterface vik_gps_layer_interface = {
265 sizeof(params_groups)/sizeof(params_groups[0]),
269 (VikLayerFuncCreate) vik_gps_layer_create,
270 (VikLayerFuncRealize) vik_gps_layer_realize,
271 (VikLayerFuncPostRead) vik_gps_layer_post_read,
272 (VikLayerFuncFree) vik_gps_layer_free,
274 (VikLayerFuncProperties) NULL,
275 (VikLayerFuncDraw) vik_gps_layer_draw,
276 (VikLayerFuncChangeCoordMode) gps_layer_change_coord_mode,
278 (VikLayerFuncGetTimestamp) NULL,
280 (VikLayerFuncSetMenuItemsSelection) NULL,
281 (VikLayerFuncGetMenuItemsSelection) NULL,
283 (VikLayerFuncAddMenuItems) gps_layer_add_menu_items,
284 (VikLayerFuncSublayerAddMenuItems) NULL,
286 (VikLayerFuncSublayerRenameRequest) NULL,
287 (VikLayerFuncSublayerToggleVisible) NULL,
288 (VikLayerFuncSublayerTooltip) NULL,
289 (VikLayerFuncLayerTooltip) gps_layer_tooltip,
290 (VikLayerFuncLayerSelected) NULL,
292 (VikLayerFuncMarshall) gps_layer_marshall,
293 (VikLayerFuncUnmarshall) gps_layer_unmarshall,
295 (VikLayerFuncSetParam) gps_layer_set_param,
296 (VikLayerFuncGetParam) gps_layer_get_param,
297 (VikLayerFuncChangeParam) NULL,
299 (VikLayerFuncReadFileData) NULL,
300 (VikLayerFuncWriteFileData) NULL,
302 (VikLayerFuncDeleteItem) NULL,
303 (VikLayerFuncCutItem) NULL,
304 (VikLayerFuncCopyItem) NULL,
305 (VikLayerFuncPasteItem) NULL,
306 (VikLayerFuncFreeCopiedItem) NULL,
307 (VikLayerFuncDragDropRequest) NULL,
309 (VikLayerFuncSelectClick) NULL,
310 (VikLayerFuncSelectMove) NULL,
311 (VikLayerFuncSelectRelease) NULL,
312 (VikLayerFuncSelectedViewportMenu) NULL,
315 enum {TRW_DOWNLOAD=0, TRW_UPLOAD,
316 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
320 static gchar * trw_names[] = {
321 N_("GPS Download"), N_("GPS Upload"),
322 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
323 N_("GPS Realtime Tracking"),
327 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
329 struct gps_data_t gpsd;
335 struct gps_fix_t fix;
336 gint satellites_used;
337 gboolean dirty; /* needs to be saved */
339 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
341 struct _VikGpsLayer {
343 VikTrwLayer * trw_children[NUM_TRW];
344 GList * children; /* used only for writing file */
345 int cur_read_child; /* used only for reading file */
346 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
348 gboolean connected_to_gpsd;
349 gboolean realtime_tracking;
350 gboolean first_realtime_trackpoint;
354 VikTrack *realtime_track;
356 GIOChannel *realtime_io_channel;
357 guint realtime_io_watch_id;
358 guint realtime_retry_timer;
359 GdkGC *realtime_track_gc;
360 GdkGC *realtime_track_bg_gc;
361 GdkGC *realtime_track_pt_gc;
362 GdkGC *realtime_track_pt1_gc;
363 GdkGC *realtime_track_pt2_gc;
366 gboolean auto_connect_to_gpsd;
369 gint gpsd_retry_interval;
370 gboolean realtime_record;
371 gboolean realtime_jump_to_start;
372 guint vehicle_position;
373 gboolean realtime_update_statusbar;
374 VikTrackpoint *trkpt;
375 VikTrackpoint *trkpt_prev;
376 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
379 gboolean download_tracks;
380 gboolean download_routes;
381 gboolean download_waypoints;
382 gboolean upload_tracks;
383 gboolean upload_routes;
384 gboolean upload_waypoints;
388 * Overwrite the static setup with dynamically generated GPS Babel device list
390 static void gps_layer_inst_init ( VikGpsLayer *self )
393 // +1 for luck (i.e the NULL terminator)
394 gchar **new_protocols = g_malloc_n(1 + g_list_length(a_babel_device_list), sizeof(gpointer));
396 GList *gl = g_list_first ( a_babel_device_list );
398 // should be using label property but use name for now
399 // thus don't need to mess around converting label to name later on
400 new_protocols[new_proto++] = ((BabelDevice*)gl->data)->name;
401 gl = g_list_next ( gl );
403 new_protocols[new_proto] = NULL;
405 vik_gps_layer_interface.params[PARAM_PROTOCOL].widget_data = new_protocols;
408 GType vik_gps_layer_get_type ()
410 static GType val_type = 0;
414 static const GTypeInfo val_info =
416 sizeof (VikGpsLayerClass),
417 NULL, /* base_init */
418 NULL, /* base_finalize */
419 NULL, /* class init */
420 NULL, /* class_finalize */
421 NULL, /* class_data */
422 sizeof (VikGpsLayer),
424 (GInstanceInitFunc) gps_layer_inst_init,
426 val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGpsLayer", &val_info, 0 );
432 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp)
436 VikGpsLayer *rv = vik_gps_layer_new (vp);
437 vik_layer_rename ( VIK_LAYER(rv), vik_gps_layer_interface.name );
439 for (i = 0; i < NUM_TRW; i++) {
440 rv->trw_children[i] = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vp, FALSE ));
441 vik_layer_set_menu_items_selection(VIK_LAYER(rv->trw_children[i]), VIK_MENU_ITEM_ALL & ~(VIK_MENU_ITEM_CUT|VIK_MENU_ITEM_DELETE));
446 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl )
448 static gchar buf1[256];
450 static gchar rbuf[512];
453 g_snprintf (buf1, sizeof(buf1), "%s:%s", vgl->protocol, vgl->serial_port);
455 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
456 static gchar buf2[256];
458 if ( vgl->connected_to_gpsd )
459 g_snprintf (buf2, sizeof(buf2), "GPSD:%s:%s %s", vgl->gpsd_host, vgl->gpsd_port, _("Connected"));
461 g_snprintf (buf2, sizeof(buf2), "GPSD:%s:%s %s", vgl->gpsd_host, vgl->gpsd_port, _("Disconnected"));
463 g_snprintf (rbuf, sizeof(rbuf), "%s\n%s", buf1, buf2);
465 g_snprintf (rbuf, sizeof(rbuf), "%s", buf1);
471 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *datalen )
473 VikLayer *child_layer;
476 GByteArray* b = g_byte_array_new ();
480 #define alm_append(obj, sz) \
482 g_byte_array_append ( b, (guint8 *)&len, sizeof(len) ); \
483 g_byte_array_append ( b, (guint8 *)(obj), len );
485 vik_layer_marshall_params(VIK_LAYER(vgl), &ld, &ll);
489 for (i = 0; i < NUM_TRW; i++) {
490 child_layer = VIK_LAYER(vgl->trw_children[i]);
491 vik_layer_marshall(child_layer, &ld, &ll);
499 g_byte_array_free(b, FALSE);
504 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
506 #define alm_size (*(gint *)data)
508 len -= sizeof(gint) + alm_size; \
509 data += sizeof(gint) + alm_size;
511 VikGpsLayer *rv = vik_gps_layer_new(vvp);
512 VikLayer *child_layer;
515 vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
519 while (len>0 && i < NUM_TRW) {
520 child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
522 rv->trw_children[i++] = (VikTrwLayer *)child_layer;
523 // NB no need to attach signal update handler here
524 // as this will always be performed later on in vik_gps_layer_realize()
528 // g_print("gps_layer_unmarshall ended with len=%d\n", len);
535 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
541 g_free(vgl->protocol);
542 // Backwards Compatibility: previous versions <v1.4 stored protocol as an array index
543 int index = data.s[0] - '0';
544 if (data.s[0] != '\0' &&
545 g_ascii_isdigit (data.s[0]) &&
547 index < OLD_NUM_PROTOCOLS)
548 // It is a single digit: activate compatibility
549 vgl->protocol = g_strdup(protocols_args[index]);
551 vgl->protocol = g_strdup(data.s);
552 g_debug("%s: %s", __FUNCTION__, vgl->protocol);
555 g_warning(_("Unknown GPS Protocol"));
559 g_free(vgl->serial_port);
560 // Backwards Compatibility: previous versions <v0.9.91 stored serial_port as an array index
561 int index = data.s[0] - '0';
562 if (data.s[0] != '\0' &&
563 g_ascii_isdigit (data.s[0]) &&
565 index < OLD_NUM_PORTS)
566 /* It is a single digit: activate compatibility */
567 vgl->serial_port = g_strdup(old_params_ports[index]);
569 vgl->serial_port = g_strdup(data.s);
570 g_debug("%s: %s", __FUNCTION__, vgl->serial_port);
573 g_warning(_("Unknown serial port device"));
575 case PARAM_DOWNLOAD_TRACKS:
576 vgl->download_tracks = data.b;
578 case PARAM_UPLOAD_TRACKS:
579 vgl->upload_tracks = data.b;
581 case PARAM_DOWNLOAD_ROUTES:
582 vgl->download_routes = data.b;
584 case PARAM_UPLOAD_ROUTES:
585 vgl->upload_routes = data.b;
587 case PARAM_DOWNLOAD_WAYPOINTS:
588 vgl->download_waypoints = data.b;
590 case PARAM_UPLOAD_WAYPOINTS:
591 vgl->upload_waypoints = data.b;
593 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
594 case PARAM_GPSD_CONNECT:
595 vgl->auto_connect_to_gpsd = data.b;
597 case PARAM_GPSD_HOST:
600 g_free(vgl->gpsd_host);
601 vgl->gpsd_host = g_strdup(data.s);
604 case PARAM_GPSD_PORT:
607 g_free(vgl->gpsd_port);
608 vgl->gpsd_port = g_strdup(data.s);
611 case PARAM_GPSD_RETRY_INTERVAL:
612 vgl->gpsd_retry_interval = strtol(data.s, NULL, 10);
614 case PARAM_REALTIME_REC:
615 vgl->realtime_record = data.b;
617 case PARAM_REALTIME_CENTER_START:
618 vgl->realtime_jump_to_start = data.b;
620 case PARAM_VEHICLE_POSITION:
621 vgl->vehicle_position = data.u;
623 case PARAM_REALTIME_UPDATE_STATUSBAR:
624 vgl->realtime_update_statusbar = data.b;
626 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
628 g_warning("gps_layer_set_param(): unknown parameter");
634 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation )
636 VikLayerParamData rv;
640 rv.s = vgl->protocol;
641 g_debug("%s: %s", __FUNCTION__, rv.s);
644 rv.s = vgl->serial_port;
645 g_debug("%s: %s", __FUNCTION__, rv.s);
647 case PARAM_DOWNLOAD_TRACKS:
648 rv.b = vgl->download_tracks;
650 case PARAM_UPLOAD_TRACKS:
651 rv.b = vgl->upload_tracks;
653 case PARAM_DOWNLOAD_ROUTES:
654 rv.b = vgl->download_routes;
656 case PARAM_UPLOAD_ROUTES:
657 rv.b = vgl->upload_routes;
659 case PARAM_DOWNLOAD_WAYPOINTS:
660 rv.b = vgl->download_waypoints;
662 case PARAM_UPLOAD_WAYPOINTS:
663 rv.b = vgl->upload_waypoints;
665 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
666 case PARAM_GPSD_CONNECT:
667 rv.b = vgl->auto_connect_to_gpsd;
669 case PARAM_GPSD_HOST:
670 rv.s = vgl->gpsd_host ? vgl->gpsd_host : "";
672 case PARAM_GPSD_PORT:
673 rv.s = vgl->gpsd_port ? vgl->gpsd_port : g_strdup(DEFAULT_GPSD_PORT);
675 case PARAM_GPSD_RETRY_INTERVAL:
676 rv.s = g_strdup_printf("%d", vgl->gpsd_retry_interval);
678 case PARAM_REALTIME_REC:
679 rv.b = vgl->realtime_record;
681 case PARAM_REALTIME_CENTER_START:
682 rv.b = vgl->realtime_jump_to_start;
684 case PARAM_VEHICLE_POSITION:
685 rv.u = vgl->vehicle_position;
687 case PARAM_REALTIME_UPDATE_STATUSBAR:
688 rv.u = vgl->realtime_update_statusbar;
690 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
692 g_warning(_("%s: unknown parameter"), __FUNCTION__);
698 VikGpsLayer *vik_gps_layer_new (VikViewport *vp)
701 VikGpsLayer *vgl = VIK_GPS_LAYER ( g_object_new ( VIK_GPS_LAYER_TYPE, NULL ) );
702 vik_layer_set_type ( VIK_LAYER(vgl), VIK_LAYER_GPS );
703 for (i = 0; i < NUM_TRW; i++) {
704 vgl->trw_children[i] = NULL;
706 vgl->children = NULL;
707 vgl->cur_read_child = 0;
709 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
710 vgl->first_realtime_trackpoint = FALSE;
712 vgl->trkpt_prev = NULL;
714 vgl->realtime_io_channel = NULL;
715 vgl->realtime_io_watch_id = 0;
716 vgl->realtime_retry_timer = 0;
718 vgl->realtime_track_gc = vik_viewport_new_gc ( vp, "#203070", 2 );
719 vgl->realtime_track_bg_gc = vik_viewport_new_gc ( vp, "grey", 2 );
720 vgl->realtime_track_pt1_gc = vik_viewport_new_gc ( vp, "red", 2 );
721 vgl->realtime_track_pt2_gc = vik_viewport_new_gc ( vp, "green", 2 );
722 vgl->realtime_track_pt_gc = vgl->realtime_track_pt1_gc;
724 vgl->realtime_track = NULL;
725 #endif // VIK_CONFIG_REALTIME_GPS_TRACKING
727 vik_layer_set_defaults ( VIK_LAYER(vgl), vp );
732 static void vik_gps_layer_post_read ( VikGpsLayer *vgl, VikViewport *vvp, gboolean from_file )
734 for (guint i = 0; i < NUM_TRW; i++) {
735 trw_layer_calculate_bounds_waypoints ( vgl->trw_children[i] );
736 trw_layer_calculate_bounds_tracks ( vgl->trw_children[i] );
740 static void vik_gps_layer_draw ( VikGpsLayer *vgl, VikViewport *vp )
744 VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( vp ));
746 for (i = 0; i < NUM_TRW; i++) {
747 vl = VIK_LAYER(vgl->trw_children[i]);
749 if ( vik_viewport_get_half_drawn ( vp ) ) {
750 vik_viewport_set_half_drawn ( vp, FALSE );
751 vik_viewport_snapshot_load( vp );
753 vik_viewport_snapshot_save( vp );
756 if (!vik_viewport_get_half_drawn(vp))
757 vik_layer_draw ( vl, vp );
759 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
760 if (vgl->realtime_tracking) {
761 if (VIK_LAYER(vgl) == trigger) {
762 if ( vik_viewport_get_half_drawn ( vp ) ) {
763 vik_viewport_set_half_drawn ( vp, FALSE );
764 vik_viewport_snapshot_load( vp );
766 vik_viewport_snapshot_save( vp );
769 if (!vik_viewport_get_half_drawn(vp))
770 realtime_tracking_draw(vgl, vp);
772 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
775 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode )
778 for (i = 0; i < NUM_TRW; i++) {
779 vik_layer_change_coord_mode(VIK_LAYER(vgl->trw_children[i]), mode);
783 static void gps_layer_add_menu_items( VikGpsLayer *vgl, GtkMenu *menu, gpointer vlp )
785 static gpointer pass_along[2];
790 item = gtk_menu_item_new();
791 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
792 gtk_widget_show ( item );
795 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS") );
796 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
797 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_upload_cb), pass_along );
798 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
799 gtk_widget_show ( item );
801 item = gtk_image_menu_item_new_with_mnemonic ( _("Download from _GPS") );
802 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
803 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_download_cb), pass_along );
804 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
805 gtk_widget_show ( item );
807 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
808 item = gtk_image_menu_item_new_with_mnemonic ( vgl->realtime_tracking ?
809 "_Stop Realtime Tracking" :
810 "_Start Realtime Tracking" );
811 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, vgl->realtime_tracking ?
812 gtk_image_new_from_stock (GTK_STOCK_MEDIA_STOP, GTK_ICON_SIZE_MENU) :
813 gtk_image_new_from_stock (GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_MENU) );
814 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_start_stop_tracking_cb), pass_along );
815 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
816 gtk_widget_show ( item );
818 item = gtk_menu_item_new();
819 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
820 gtk_widget_show ( item );
822 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _Realtime") );
823 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
824 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_realtime_cb), pass_along );
825 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
826 gtk_widget_show ( item );
827 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
829 item = gtk_image_menu_item_new_with_mnemonic ( _("E_mpty Upload") );
830 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
831 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_upload_cb), pass_along );
832 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
833 gtk_widget_show ( item );
835 item = gtk_image_menu_item_new_with_mnemonic ( _("_Empty Download") );
836 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
837 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_download_cb), pass_along );
838 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
839 gtk_widget_show ( item );
841 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _All") );
842 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
843 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_all_cb), pass_along );
844 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
845 gtk_widget_show ( item );
849 static void disconnect_layer_signal ( VikLayer *vl, VikGpsLayer *vgl )
851 guint number_handlers = g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, vgl);
852 if ( number_handlers != 1 ) {
853 g_critical(_("Unexpected number of disconnected handlers: %d"), number_handlers);
857 static void vik_gps_layer_free ( VikGpsLayer *vgl )
860 for (i = 0; i < NUM_TRW; i++) {
861 if (vgl->vl.realized)
862 disconnect_layer_signal(VIK_LAYER(vgl->trw_children[i]), vgl);
863 g_object_unref(vgl->trw_children[i]);
865 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
866 rt_gpsd_disconnect(vgl);
867 if (vgl->realtime_track_gc != NULL)
868 g_object_unref(vgl->realtime_track_gc);
869 if (vgl->realtime_track_bg_gc != NULL)
870 g_object_unref(vgl->realtime_track_bg_gc);
871 if (vgl->realtime_track_pt1_gc != NULL)
872 g_object_unref(vgl->realtime_track_pt1_gc);
873 if (vgl->realtime_track_pt2_gc != NULL)
874 g_object_unref(vgl->realtime_track_pt2_gc);
875 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
878 static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter )
883 // TODO set to garmin by default
884 //if (a_babel_device_list)
885 // device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
886 // Need to access uibuild widgets somehow....
888 for (ix = 0; ix < NUM_TRW; ix++) {
889 VikLayer * trw = VIK_LAYER(vgl->trw_children[ix]);
890 vik_treeview_add_layer ( VIK_LAYER(vgl)->vt, layer_iter, &iter,
891 _(trw_names[ix]), vgl, TRUE,
892 trw, trw->type, trw->type, vik_layer_get_timestamp(trw) );
893 if ( ! trw->visible )
894 vik_treeview_item_set_visible ( VIK_LAYER(vgl)->vt, &iter, FALSE );
895 vik_layer_realize ( trw, VIK_LAYER(vgl)->vt, &iter );
896 g_signal_connect_swapped ( G_OBJECT(trw), "update", G_CALLBACK(vik_layer_emit_update_secondary), vgl );
899 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
900 if ( vgl->auto_connect_to_gpsd ) {
901 vgl->realtime_tracking = TRUE;
902 vgl->first_realtime_trackpoint = TRUE;
903 (void)rt_gpsd_connect ( vgl, FALSE );
905 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
908 const GList *vik_gps_layer_get_children ( VikGpsLayer *vgl )
912 if (vgl->children == NULL) {
913 for (i = NUM_TRW - 1; i >= 0; i--)
914 vgl->children = g_list_prepend(vgl->children, vgl->trw_children[i]);
916 return vgl->children;
919 VikTrwLayer * vik_gps_layer_get_a_child(VikGpsLayer *vgl)
921 g_assert ((vgl->cur_read_child >= 0) && (vgl->cur_read_child < NUM_TRW));
923 VikTrwLayer * vtl = vgl->trw_children[vgl->cur_read_child];
924 if (++(vgl->cur_read_child) >= NUM_TRW)
925 vgl->cur_read_child = 0;
929 gboolean vik_gps_layer_is_empty ( VikGpsLayer *vgl )
931 if ( vgl->trw_children[0] )
936 static void gps_session_delete(GpsSession *sess)
938 vik_mutex_free(sess->mutex);
939 // Remove any outstanding GUI update requests
940 if ( sess->id_status_working )
941 g_source_remove ( sess->id_status_working );
942 if ( sess->id_status_end )
943 g_source_remove ( sess->id_status_end );
945 g_source_remove ( sess->id_info );
946 if ( sess->id_total_count )
947 g_source_remove ( sess->id_total_count );
948 if ( sess->id_count )
949 g_source_remove ( sess->id_count );
950 g_free(sess->babelargs);
956 static gboolean show_total_count(GpsSession *sess)
958 g_mutex_lock(sess->mutex);
960 const gchar *tmp_str;
962 GtkWidget *progress_label;
963 if (sess->direction == GPS_DOWN)
965 switch (sess->progress_type) {
967 tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", sess->wpt_total_count);
968 tc = sess->wpt_total_count;
969 progress_label = sess->wpt_label;
972 tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", sess->trk_total_count);
973 tc = sess->trk_total_count;
974 progress_label = sess->trk_label;
977 tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", sess->rte_total_count);
978 tc = sess->rte_total_count;
979 progress_label = sess->rte_label;
983 // 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
984 gint mycnt = (cnt / 2) + 1;
985 tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt);
994 switch (sess->progress_type) {
996 tmp_str = ngettext("Uploading %d waypoint...", "Uploading %d waypoints...", sess->wpt_total_count);
997 tc = sess->wpt_total_count;
998 progress_label = sess->wpt_label;
1001 tmp_str = ngettext("Uploading %d trackpoint...", "Uploading %d trackpoints...", sess->trk_total_count);
1002 tc = sess->trk_total_count;
1003 progress_label = sess->trk_label;
1006 tmp_str = ngettext("Uploading %d routepoint...", "Uploading %d routepoints...", sess->rte_total_count);
1007 tc = sess->rte_total_count;
1008 progress_label = sess->rte_label;
1014 g_snprintf(s, 128, tmp_str, tc);
1015 gtk_label_set_text ( GTK_LABEL(progress_label), s );
1016 gtk_widget_show ( progress_label );
1018 sess->id_total_count = 0;
1019 g_mutex_unlock(sess->mutex);
1023 static gboolean show_current_count(GpsSession *sess)
1025 g_mutex_lock(sess->mutex);
1028 gint count, total_count;
1029 const gchar *tmp_str;
1030 GtkWidget *progress_label;
1031 if (sess->wpt_count < sess->wpt_total_count) {
1032 if (sess->direction == GPS_DOWN) {
1033 switch (sess->progress_type) {
1035 tmp_str = ngettext("Downloaded %d out of %d waypoint...", "Downloaded %d out of %d waypoints...", sess->wpt_total_count);
1036 count = sess->wpt_count;
1037 total_count = sess->wpt_total_count;
1038 progress_label = sess->wpt_label;
1041 tmp_str = ngettext("Downloaded %d out of %d trackpoint...", "Downloaded %d out of %d trackpoints...", sess->trk_total_count);
1042 count = sess->trk_count;
1043 total_count = sess->trk_total_count;
1044 progress_label = sess->trk_label;
1047 tmp_str = ngettext("Downloaded %d out of %d routepoint...", "Downloaded %d out of %d routepoints...", sess->rte_total_count);
1048 count = sess->rte_count;
1049 total_count = sess->rte_total_count;
1050 progress_label = sess->rte_label;
1055 switch (sess->progress_type) {
1057 tmp_str = ngettext("Uploaded %d out of %d waypoint...", "Uploaded %d out of %d waypoints...", sess->wpt_total_count);
1058 count = sess->wpt_count;
1059 total_count = sess->wpt_total_count;
1060 progress_label = sess->wpt_label;
1063 tmp_str = ngettext("Uploaded %d out of %d trackpoint...", "Uploaded %d out of %d trackpoints...", sess->trk_total_count);
1064 count = sess->trk_count;
1065 total_count = sess->trk_total_count;
1066 progress_label = sess->trk_label;
1069 tmp_str = ngettext("Uploaded %d out of %d routepoint...", "Uploaded %d out of %d routepoints...", sess->rte_total_count);
1070 count = sess->rte_count;
1071 total_count = sess->rte_total_count;
1072 progress_label = sess->rte_label;
1077 if (sess->direction == GPS_DOWN) {
1078 switch (sess->progress_type) {
1080 tmp_str = ngettext("Downloaded %d waypoint", "Downloaded %d waypoints", sess->wpt_count);
1081 count = sess->wpt_count;
1082 total_count = sess->wpt_total_count;
1083 progress_label = sess->wpt_label;
1086 tmp_str = ngettext("Downloaded %d trackpoint", "Downloaded %d trackpoints", sess->trk_count);
1087 count = sess->trk_count;
1088 total_count = sess->trk_total_count;
1089 progress_label = sess->trk_label;
1092 tmp_str = ngettext("Downloaded %d routepoint", "Downloaded %d routepoints", sess->rte_count);
1093 count = sess->rte_count;
1094 total_count = sess->rte_total_count;
1095 progress_label = sess->rte_label;
1100 switch (sess->progress_type) {
1102 tmp_str = ngettext("Uploaded %d waypoint", "Uploaded %d waypoints", sess->wpt_count);
1103 count = sess->wpt_count;
1104 total_count = sess->wpt_total_count;
1105 progress_label = sess->wpt_label;
1108 tmp_str = ngettext("Uploaded %d trackpoint", "Uploaded %d trackpoints", sess->trk_count);
1109 count = sess->trk_count;
1110 total_count = sess->trk_total_count;
1111 progress_label = sess->trk_label;
1114 tmp_str = ngettext("Uploaded %d routepoint", "Uploaded %d routepoints", sess->rte_count);
1115 count = sess->rte_count;
1116 total_count = sess->rte_total_count;
1117 progress_label = sess->rte_label;
1122 g_snprintf(s, 128, tmp_str, count, total_count);
1123 gtk_label_set_text ( GTK_LABEL(progress_label), s );
1126 g_mutex_unlock(sess->mutex);
1130 static gboolean show_gps_info(GpsSession *sess)
1132 g_mutex_lock(sess->mutex);
1135 g_snprintf(s, 256, _("GPS Device: %s"), sess->info);
1136 gtk_label_set_text ( GTK_LABEL(sess->gps_label), s );
1139 g_mutex_unlock(sess->mutex);
1144 * Common processing for GPS Device information
1145 * It doesn't matter whether we're uploading or downloading
1147 static void process_line_for_gps_info ( const gchar *line, GpsSession *sess )
1149 if (strstr(line, "PRDDAT")) {
1150 gchar **tokens = g_strsplit(line, " ", 0);
1156 while (tokens[n_tokens])
1159 // I'm not entirely clear what information this is trying to get...
1160 // Obviously trying to decipher some kind of text/naming scheme
1161 // Anyway this will be superceded if there is 'Unit:' information
1163 for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
1165 sscanf(tokens[i], "%x", &ch);
1169 sess->info = g_strdup (info);
1170 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1175 /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
1176 if (strstr(line, "Unit:")) {
1177 gchar **tokens = g_strsplit(line, "\t", 0);
1179 while (tokens[n_tokens])
1183 sess->info = g_strdup (tokens[1]);
1184 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1189 if (strstr(line, "[ERROR] GPS")) {
1190 gchar **tokens = g_strsplit(line, "\n", 0);
1191 sess->info = g_strdup(tokens[0]);
1192 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1196 if (strstr(line, "an't in")) {
1197 gchar **tokens = g_strsplit(line, "\n", 0);
1198 sess->info = g_strdup(tokens[0]);
1199 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1203 if (strstr(line, "Can't get waypoint")) {
1204 gchar **tokens = g_strsplit(line, "\n", 0);
1205 sess->info = g_strdup(tokens[0]);
1206 sess->id_info = gdk_threads_add_idle ( (GSourceFunc)show_gps_info, sess );
1211 static gboolean show_gps_status_working ( GpsSession *sess )
1213 g_mutex_lock(sess->mutex);
1215 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1217 sess->id_status_working = 0;
1218 g_mutex_unlock(sess->mutex);
1222 static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1227 //gps_session_delete(sess);
1228 sess->thread_complete = TRUE;
1229 g_debug ("THREAD EXIT INTERUPPT");
1230 g_thread_exit ( NULL );
1234 case BABEL_DIAG_OUTPUT:
1235 line = (gchar *)data;
1237 sess->id_status_working = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_working, sess );
1239 /* tells us the type of items that will follow */
1240 if (strstr(line, "Xfer Wpt")) {
1241 sess->progress_type = WPT;
1243 if (strstr(line, "Xfer Trk")) {
1244 sess->progress_type = TRK;
1246 if (strstr(line, "Xfer Rte")) {
1247 sess->progress_type = RTE;
1250 process_line_for_gps_info ( line, sess );
1252 if (strstr(line, "RECORD")) {
1255 if (strlen(line) > 20) {
1256 sscanf(line+17, "%x", &lsb);
1257 sscanf(line+20, "%x", &msb);
1258 cnt = lsb + msb * 256;
1259 if ( sess->progress_type == RTE ) {
1260 // 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
1261 gint mycnt = (cnt / 2) + 1;
1262 sess->rte_total_count = mycnt;
1263 sess->rte_count = 0;
1265 else if ( sess->progress_type == WPT ) {
1266 sess->wpt_total_count = cnt;
1267 sess->wpt_count = 0;
1270 sess->trk_total_count = cnt;
1271 sess->trk_count = 0;
1273 sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess);
1276 if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1277 if ( strstr(line, "WPTDAT") )
1279 else if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") )
1282 // "RTEHDR" || "RTEWPT"
1284 sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess);
1294 static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1300 //gps_session_delete(sess);
1301 sess->thread_complete = TRUE;
1302 g_thread_exit ( NULL );
1306 case BABEL_DIAG_OUTPUT:
1307 line = (gchar *)data;
1309 sess->id_status_working = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_working, sess );
1311 process_line_for_gps_info ( line, sess );
1313 if (strstr(line, "RECORD")) {
1316 if (strlen(line) > 20) {
1317 sscanf(line+17, "%x", &lsb);
1318 sscanf(line+20, "%x", &msb);
1319 cnt = lsb + msb * 256;
1320 //sess->count = 0; ?? wpt, trk and/or rte?? or none
1323 if ( strstr(line, "WPTDAT")) {
1324 sess->progress_type = WPT;
1325 if (sess->wpt_count == 0) {
1326 sess->wpt_total_count = cnt;
1327 sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess );
1330 sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess );
1332 if ( strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1333 sess->progress_type = RTE;
1334 if (sess->rte_count == 0) {
1335 // 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
1336 // Anyway since we're uploading - we should know how many points we're going to put!
1337 cnt = (cnt / 2) + 1;
1338 sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess);
1341 sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess);
1343 if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
1344 sess->progress_type = TRK;
1345 if (sess->trk_count == 0) {
1346 sess->id_total_count = gdk_threads_add_idle ( (GSourceFunc)show_total_count, sess);
1349 sess->id_count = gdk_threads_add_idle ( (GSourceFunc)show_current_count, sess);
1359 static gboolean show_gps_status_end ( GpsSession *sess )
1361 g_mutex_lock(sess->mutex);
1362 // (Download)Failure could be due to a number of reasons: such as no/wrong device attached or GPSBabel not installed
1363 if (!sess->result) {
1364 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: No result.") );
1368 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Done.") );
1369 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE );
1370 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE );
1373 sess->id_status_end = 0;
1374 g_mutex_unlock(sess->mutex);
1381 static void gps_comm_thread(GpsSession *sess)
1383 if (sess->direction == GPS_DOWN) {
1384 ProcessOptions po = { sess->babelargs, sess->port, NULL, NULL, NULL, NULL };
1385 sess->result = a_babel_convert_from (sess->vtl, &po, (BabelStatusFunc) gps_download_progress_func, sess, NULL);
1388 sess->result = a_babel_convert_to (sess->vtl, sess->track, sess->babelargs, sess->port,
1389 (BabelStatusFunc) gps_upload_progress_func, sess);
1392 sess->id_status_end = gdk_threads_add_idle ( (GSourceFunc)show_gps_status_end, sess );
1396 /* Do not change the view if we are following the current GPS position */
1397 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1398 if (!sess->realtime_tracking)
1401 if ( sess->vvp && sess->direction == GPS_DOWN ) {
1402 vik_layer_post_read ( VIK_LAYER(sess->vtl), sess->vvp, TRUE );
1403 /* View the data available */
1404 vik_trw_layer_auto_set_view ( sess->vtl, sess->vvp ) ;
1405 vik_layer_emit_update ( VIK_LAYER(sess->vtl) ); // NB update request from background thread
1414 // Thread has completed successfully, but now set to false to avoid 'tell thread to stop' test after dialog run
1418 sess->thread_complete = TRUE;
1419 g_thread_exit(NULL);
1424 * @vtl: The TrackWaypoint layer to operate on
1425 * @track: Operate on a particular track when specified
1426 * @dir: The direction of the transfer
1427 * @protocol: The GPS device communication protocol
1428 * @port: The GPS serial port
1429 * @tracking: If tracking then viewport display update will be skipped
1430 * @vvp: A viewport is required as the display may get updated
1431 * @vlp: A layers panel is needed for uploading as the items maybe modified
1432 * @do_tracks: Whether tracks shoud be processed
1433 * @do_waypoints: Whether waypoints shoud be processed
1434 * @turn_off: Whether we should attempt to turn off the GPS device after the transfer (only some devices support this)
1436 * Talk to a GPS Device using a thread which updates a dialog with the progress
1438 gint vik_gps_comm ( VikTrwLayer *vtl,
1445 VikLayersPanel *vlp,
1448 gboolean do_waypoints,
1451 GpsSession *sess = g_malloc0(sizeof(GpsSession));
1452 char *tracks = NULL;
1453 char *routes = NULL;
1454 char *waypoints = NULL;
1456 sess->mutex = vik_mutex_new();
1457 sess->direction = dir;
1459 sess->track = track;
1460 sess->port = g_strdup(port);
1464 // This must be done inside the main thread as the uniquify causes screen updates
1465 // (originally performed this nearer the point of upload in the thread)
1466 if ( dir == GPS_UP ) {
1467 // Enforce unique names in the layer upload to the GPS device
1468 // NB this may only be a Garmin device restriction (and may be not every Garmin device either...)
1469 // Thus this maintains the older code in built restriction
1470 if ( ! vik_trw_layer_uniquify ( sess->vtl, vlp ) )
1471 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(sess->vtl))), VIK_STATUSBAR_INFO,
1472 _("Warning - GPS Upload items may overwrite each other") );
1475 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1476 sess->realtime_tracking = tracking;
1492 sess->babelargs = g_strdup_printf("-D 9 %s %s %s -%c %s",
1493 tracks, routes, waypoints, (dir == GPS_DOWN) ? 'i' : 'o', protocol);
1497 GtkWidget *dialog = NULL;
1499 // Only create dialog if we're going to do some transferring
1500 if ( do_tracks || do_waypoints || do_routes ) {
1501 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 );
1502 sess->dialog = dialog;
1503 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog),
1504 GTK_RESPONSE_ACCEPT, FALSE );
1505 gtk_window_set_title ( GTK_WINDOW(sess->dialog), (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload") );
1507 sess->status_label = gtk_label_new (_("Status: detecting gpsbabel"));
1508 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->status_label, FALSE, FALSE, 5 );
1510 sess->gps_label = gtk_label_new (_("GPS device: N/A"));
1511 sess->wpt_label = gtk_label_new ("");
1512 sess->trk_label = gtk_label_new ("");
1513 sess->rte_label = gtk_label_new ("");
1515 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->gps_label, FALSE, FALSE, 5 );
1516 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->wpt_label, FALSE, FALSE, 5 );
1517 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->trk_label, FALSE, FALSE, 5 );
1518 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->rte_label, FALSE, FALSE, 5 );
1520 gtk_widget_show_all(sess->dialog);
1522 gtk_dialog_set_default_response ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT );
1523 // Starting gps read/write thread
1524 #if GLIB_CHECK_VERSION (2, 32, 0)
1525 g_thread_try_new ( "gps_comm_thread", (GThreadFunc)gps_comm_thread, sess, NULL );
1527 g_thread_create ( (GThreadFunc)gps_comm_thread, sess, FALSE, NULL );
1530 gtk_dialog_run(GTK_DIALOG(dialog));
1534 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No GPS items selected for transfer.") );
1538 sess->ok = FALSE; /* tell thread to stop */
1542 // No need for thread for powering off device (should be quick operation...) - so use babel command directly:
1543 gchar *device_off = g_strdup_printf("-i %s,%s", protocol, "power_off");
1544 ProcessOptions po = { device_off, port, NULL, NULL, NULL, NULL };
1545 gboolean result = a_babel_convert_from (NULL, &po, NULL, NULL, NULL);
1547 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not turn off device.") );
1548 g_free ( device_off );
1553 while ( !sess->thread_complete ) {
1554 g_usleep (G_USEC_PER_SEC/10);
1556 gtk_widget_destroy(dialog);
1558 gps_session_delete(sess);
1562 static void gps_upload_cb( gpointer layer_and_vlp[2] )
1564 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1565 VikLayersPanel *vlp = VIK_LAYERS_PANEL(layer_and_vlp[1]);
1566 VikTrwLayer *vtl = vgl->trw_children[TRW_UPLOAD];
1567 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1568 VikViewport *vvp = vik_window_viewport(vw);
1569 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);
1572 static void gps_download_cb( gpointer layer_and_vlp[2] )
1574 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1575 VikTrwLayer *vtl = vgl->trw_children[TRW_DOWNLOAD];
1576 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1577 VikViewport *vvp = vik_window_viewport(vw);
1578 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1579 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);
1581 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);
1585 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] )
1587 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1588 // Get confirmation from the user
1589 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1590 _("Are you sure you want to delete GPS Upload data?"),
1593 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1594 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1595 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1598 static void gps_empty_download_cb( gpointer layer_and_vlp[2] )
1600 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1601 // Get confirmation from the user
1602 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1603 _("Are you sure you want to delete GPS Download data?"),
1606 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1607 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1608 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1611 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1612 static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] )
1614 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1615 // Get confirmation from the user
1616 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1617 _("Are you sure you want to delete GPS Realtime data?"),
1620 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1621 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1622 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_REALTIME]);
1626 static void gps_empty_all_cb( gpointer layer_and_vlp[2] )
1628 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1629 // Get confirmation from the user
1630 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1631 _("Are you sure you want to delete All GPS data?"),
1634 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1635 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1636 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1637 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1638 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1639 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1640 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1641 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1642 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1643 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_REALTIME]);
1647 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1648 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp)
1652 struct LatLon lnw, lse;
1653 vik_viewport_screen_to_coord ( vp, -20, -20, &nw );
1654 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp)+20, vik_viewport_get_width(vp)+20, &se );
1655 vik_coord_to_latlon ( &nw, &lnw );
1656 vik_coord_to_latlon ( &se, &lse );
1657 if ( vgl->realtime_fix.fix.latitude > lse.lat &&
1658 vgl->realtime_fix.fix.latitude < lnw.lat &&
1659 vgl->realtime_fix.fix.longitude > lnw.lon &&
1660 vgl->realtime_fix.fix.longitude < lse.lon &&
1661 !isnan (vgl->realtime_fix.fix.track) ) {
1664 gint half_back_x, half_back_y;
1665 gint half_back_bg_x, half_back_bg_y;
1668 gint side1_x, side1_y, side2_x, side2_y;
1669 gint side1bg_x, side1bg_y, side2bg_x, side2bg_y;
1671 ll.lat = vgl->realtime_fix.fix.latitude;
1672 ll.lon = vgl->realtime_fix.fix.longitude;
1673 vik_coord_load_from_latlon ( &gps, vik_viewport_get_coord_mode(vp), &ll);
1674 vik_viewport_coord_to_screen ( vp, &gps, &x, &y );
1676 gdouble heading_cos = cos(DEG2RAD(vgl->realtime_fix.fix.track));
1677 gdouble heading_sin = sin(DEG2RAD(vgl->realtime_fix.fix.track));
1679 half_back_y = y+8*heading_cos;
1680 half_back_x = x-8*heading_sin;
1681 half_back_bg_y = y+10*heading_cos;
1682 half_back_bg_x = x-10*heading_sin;
1684 pt_y = half_back_y-24*heading_cos;
1685 pt_x = half_back_x+24*heading_sin;
1686 //ptbg_y = half_back_bg_y-28*heading_cos;
1687 ptbg_x = half_back_bg_x+28*heading_sin;
1689 side1_y = half_back_y+9*heading_sin;
1690 side1_x = half_back_x+9*heading_cos;
1691 side1bg_y = half_back_bg_y+11*heading_sin;
1692 side1bg_x = half_back_bg_x+11*heading_cos;
1694 side2_y = half_back_y-9*heading_sin;
1695 side2_x = half_back_x-9*heading_cos;
1696 side2bg_y = half_back_bg_y-11*heading_sin;
1697 side2bg_x = half_back_bg_x-11*heading_cos;
1699 GdkPoint trian[3] = { { pt_x, pt_y }, {side1_x, side1_y}, {side2_x, side2_y} };
1700 GdkPoint trian_bg[3] = { { ptbg_x, pt_y }, {side1bg_x, side1bg_y}, {side2bg_x, side2bg_y} };
1702 vik_viewport_draw_polygon ( vp, vgl->realtime_track_bg_gc, TRUE, trian_bg, 3 );
1703 vik_viewport_draw_polygon ( vp, vgl->realtime_track_gc, TRUE, trian, 3 );
1704 vik_viewport_draw_rectangle ( vp,
1705 (vgl->realtime_fix.fix.mode > MODE_2D) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc,
1706 TRUE, x-2, y-2, 4, 4 );
1707 //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;
1711 static VikTrackpoint* create_realtime_trackpoint(VikGpsLayer *vgl, gboolean forced)
1716 /* Note that fix.time is a double, but it should not affect the precision
1718 time_t cur_timestamp = vgl->realtime_fix.fix.time;
1719 time_t last_timestamp = vgl->last_fix.fix.time;
1721 if (cur_timestamp < last_timestamp) {
1725 if (vgl->realtime_record && vgl->realtime_fix.dirty) {
1726 gboolean replace = FALSE;
1727 int heading = isnan(vgl->realtime_fix.fix.track) ? 0 : (int)floor(vgl->realtime_fix.fix.track);
1728 int last_heading = isnan(vgl->last_fix.fix.track) ? 0 : (int)floor(vgl->last_fix.fix.track);
1729 int alt = isnan(vgl->realtime_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->realtime_fix.fix.altitude);
1730 int last_alt = isnan(vgl->last_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->last_fix.fix.altitude);
1731 if (((last_tp = g_list_last(vgl->realtime_track->trackpoints)) != NULL) &&
1732 (vgl->realtime_fix.fix.mode > MODE_2D) &&
1733 (vgl->last_fix.fix.mode <= MODE_2D) &&
1734 ((cur_timestamp - last_timestamp) < 2)) {
1735 g_free(last_tp->data);
1736 vgl->realtime_track->trackpoints = g_list_delete_link(vgl->realtime_track->trackpoints, last_tp);
1740 ((cur_timestamp != last_timestamp) &&
1742 ((heading < last_heading) && (heading < (last_heading - 3))) ||
1743 ((heading > last_heading) && (heading > (last_heading + 3))) ||
1744 ((alt != VIK_DEFAULT_ALTITUDE) && (alt != last_alt)))))) {
1745 /* TODO: check for new segments */
1746 VikTrackpoint *tp = vik_trackpoint_new();
1747 tp->newsegment = FALSE;
1748 tp->has_timestamp = TRUE;
1749 tp->timestamp = vgl->realtime_fix.fix.time;
1751 /* speed only available for 3D fix. Check for NAN when use this speed */
1752 tp->speed = vgl->realtime_fix.fix.speed;
1753 tp->course = vgl->realtime_fix.fix.track;
1754 tp->nsats = vgl->realtime_fix.satellites_used;
1755 tp->fix_mode = vgl->realtime_fix.fix.mode;
1757 ll.lat = vgl->realtime_fix.fix.latitude;
1758 ll.lon = vgl->realtime_fix.fix.longitude;
1759 vik_coord_load_from_latlon(&tp->coord,
1760 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1762 vik_track_add_trackpoint ( vgl->realtime_track, tp, TRUE ); // Ensure bounds is recalculated
1763 vgl->realtime_fix.dirty = FALSE;
1764 vgl->realtime_fix.satellites_used = 0;
1765 vgl->last_fix = vgl->realtime_fix;
1772 #define VIK_SETTINGS_GPS_STATUSBAR_FORMAT "gps_statusbar_format"
1774 static void update_statusbar ( VikGpsLayer *vgl, VikWindow *vw )
1776 gchar *statusbar_format_code = NULL;
1777 gboolean need2free = FALSE;
1778 if ( !a_settings_get_string ( VIK_SETTINGS_GPS_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
1779 // Otherwise use default
1780 statusbar_format_code = g_strdup ( "GSA" );
1784 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, vgl->trkpt, vgl->trkpt_prev, vgl->realtime_track, vgl->last_fix.fix.climb );
1785 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
1789 g_free ( statusbar_format_code );
1793 static void gpsd_raw_hook(VglGpsd *vgpsd, gchar *data)
1795 gboolean update_all = FALSE;
1796 VikGpsLayer *vgl = vgpsd->vgl;
1798 if (!vgl->realtime_tracking) {
1799 g_warning("%s: receiving GPS data while not in realtime mode", __PRETTY_FUNCTION__);
1803 if ((vgpsd->gpsd.fix.mode >= MODE_2D) &&
1804 !isnan(vgpsd->gpsd.fix.latitude) &&
1805 !isnan(vgpsd->gpsd.fix.longitude)) {
1807 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1808 VikViewport *vvp = vik_window_viewport(vw);
1809 vgl->realtime_fix.fix = vgpsd->gpsd.fix;
1810 vgl->realtime_fix.satellites_used = vgpsd->gpsd.satellites_used;
1811 vgl->realtime_fix.dirty = TRUE;
1814 VikCoord vehicle_coord;
1816 ll.lat = vgl->realtime_fix.fix.latitude;
1817 ll.lon = vgl->realtime_fix.fix.longitude;
1818 vik_coord_load_from_latlon(&vehicle_coord,
1819 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1821 if ((vgl->vehicle_position == VEHICLE_POSITION_CENTERED) ||
1822 (vgl->realtime_jump_to_start && vgl->first_realtime_trackpoint)) {
1823 vik_viewport_set_center_coord(vvp, &vehicle_coord, FALSE);
1826 else if (vgl->vehicle_position == VEHICLE_POSITION_ON_SCREEN) {
1829 const int px = 20; /* adjust ment in pixels to make sure vehicle is inside the box */
1830 gint width = vik_viewport_get_width(vvp);
1831 gint height = vik_viewport_get_height(vvp);
1834 vik_viewport_coord_to_screen(vvp, &vehicle_coord, &vx, &vy);
1836 if (vx < (width/hdiv))
1837 vik_viewport_set_center_screen(vvp, vx - width/2 + width/hdiv + px, vy);
1838 else if (vx > (width - width/hdiv))
1839 vik_viewport_set_center_screen(vvp, vx + width/2 - width/hdiv - px, vy);
1840 else if (vy < (height/vdiv))
1841 vik_viewport_set_center_screen(vvp, vx, vy - height/2 + height/vdiv + px);
1842 else if (vy > (height - height/vdiv))
1843 vik_viewport_set_center_screen(vvp, vx, vy + height/2 - height/vdiv - px);
1848 vgl->first_realtime_trackpoint = FALSE;
1850 vgl->trkpt = create_realtime_trackpoint ( vgl, FALSE );
1853 if ( vgl->realtime_update_statusbar )
1854 update_statusbar ( vgl, vw );
1855 vgl->trkpt_prev = vgl->trkpt;
1858 vik_layer_emit_update ( update_all ? VIK_LAYER(vgl) : VIK_LAYER(vgl->trw_children[TRW_REALTIME]) ); // NB update from background thread
1862 static gboolean gpsd_data_available(GIOChannel *source, GIOCondition condition, gpointer data)
1864 VikGpsLayer *vgl = data;
1865 if (condition == G_IO_IN) {
1866 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1867 if (!gps_poll(&vgl->vgpsd->gpsd)) {
1868 #elif GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1869 if (gps_read(&vgl->vgpsd->gpsd) > -1) {
1870 // Reuse old function to perform operations on the new GPS data
1871 gpsd_raw_hook(vgl->vgpsd, NULL);
1878 g_warning("Disconnected from gpsd. Trying to reconnect");
1879 rt_gpsd_disconnect(vgl);
1880 (void)rt_gpsd_connect(vgl, FALSE);
1883 return FALSE; /* no further calling */
1889 * returns allocated string for a new realtime track name
1891 * free string after use
1893 static gchar *make_track_name(VikTrwLayer *vtl)
1895 const gchar basename[] = "REALTIME";
1896 const gint bufsize = sizeof(basename) + 5;
1897 gchar *name = g_malloc(bufsize);
1898 strcpy(name, basename);
1901 while (vik_trw_layer_get_track(vtl, name) != NULL) {
1902 g_snprintf(name, bufsize, "%s#%d", basename, i);
1908 static gboolean rt_gpsd_try_connect(gpointer *data)
1910 VikGpsLayer *vgl = (VikGpsLayer *)data;
1911 #if GPSD_API_MAJOR_VERSION == 3
1912 struct gps_data_t *gpsd = gps_open(vgl->gpsd_host, vgl->gpsd_port);
1915 #elif GPSD_API_MAJOR_VERSION == 4
1916 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1918 if (gps_open_r(vgl->gpsd_host, vgl->gpsd_port, /*(struct gps_data_t *)*/vgl->vgpsd) != 0) {
1919 #elif GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1920 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1921 vgl->vgpsd->gpsd_open = gps_open ( vgl->gpsd_host, vgl->gpsd_port, &vgl->vgpsd->gpsd );
1922 if ( vgl->vgpsd->gpsd_open != 0 ) {
1924 // Delibrately break compilation...
1926 g_debug("Failed to connect to gpsd at %s (port %s). Will retry in %d seconds",
1927 vgl->gpsd_host, vgl->gpsd_port, vgl->gpsd_retry_interval);
1928 return TRUE; /* keep timer running */
1931 #if GPSD_API_MAJOR_VERSION == 3
1932 vgl->vgpsd = realloc(gpsd, sizeof(VglGpsd));
1934 vgl->vgpsd->vgl = vgl;
1936 vgl->realtime_fix.dirty = vgl->last_fix.dirty = FALSE;
1937 /* track alt/time graph uses VIK_DEFAULT_ALTITUDE (0.0) as invalid */
1938 vgl->realtime_fix.fix.altitude = vgl->last_fix.fix.altitude = VIK_DEFAULT_ALTITUDE;
1939 vgl->realtime_fix.fix.speed = vgl->last_fix.fix.speed = NAN;
1941 if (vgl->realtime_record) {
1942 VikTrwLayer *vtl = vgl->trw_children[TRW_REALTIME];
1943 vgl->realtime_track = vik_track_new();
1944 vgl->realtime_track->visible = TRUE;
1945 gchar *name = make_track_name(vtl);
1946 vik_trw_layer_add_track(vtl, name, vgl->realtime_track);
1950 vgl->connected_to_gpsd = TRUE;
1952 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1953 gps_set_raw_hook(&vgl->vgpsd->gpsd, gpsd_raw_hook);
1956 vgl->realtime_io_channel = g_io_channel_unix_new(vgl->vgpsd->gpsd.gps_fd);
1957 vgl->realtime_io_watch_id = g_io_add_watch( vgl->realtime_io_channel,
1958 G_IO_IN | G_IO_ERR | G_IO_HUP, gpsd_data_available, vgl);
1960 #if GPSD_API_MAJOR_VERSION == 3
1961 gps_query(&vgl->vgpsd->gpsd, "w+x");
1963 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1964 if ( gps_stream(&vgl->vgpsd->gpsd, WATCH_ENABLE, NULL) == -1 )
1965 g_critical ( "gps_stream error" );
1968 return FALSE; /* no longer called by timeout */
1971 static gboolean rt_ask_retry(VikGpsLayer *vgl)
1973 gchar *msg = g_strdup_printf ( _("Failed to connect to gpsd at %s (port %s)\n"
1974 "Should Viking keep trying (every %d seconds)?"),
1975 vgl->gpsd_host, vgl->gpsd_port, vgl->gpsd_retry_interval );
1976 gboolean ans = a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vgl), msg, NULL );
1981 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed)
1983 vgl->realtime_retry_timer = 0;
1984 if (rt_gpsd_try_connect((gpointer *)vgl)) {
1985 if (vgl->gpsd_retry_interval <= 0) {
1986 g_warning("Failed to connect to gpsd but will not retry because retry interval was set to %d (which is 0 or negative)", vgl->gpsd_retry_interval);
1989 else if (ask_if_failed && !rt_ask_retry(vgl))
1992 vgl->realtime_retry_timer = g_timeout_add_seconds(vgl->gpsd_retry_interval,
1993 (GSourceFunc)rt_gpsd_try_connect, (gpointer *)vgl);
1998 static void rt_gpsd_disconnect(VikGpsLayer *vgl)
2000 if (vgl->realtime_retry_timer) {
2001 g_source_remove(vgl->realtime_retry_timer);
2002 vgl->realtime_retry_timer = 0;
2004 if (vgl->realtime_io_watch_id) {
2005 g_source_remove(vgl->realtime_io_watch_id);
2006 vgl->realtime_io_watch_id = 0;
2008 if (vgl->realtime_io_channel) {
2009 GError *error = NULL;
2010 g_io_channel_shutdown (vgl->realtime_io_channel, FALSE, &error);
2011 vgl->realtime_io_channel = NULL;
2014 #if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
2015 gps_stream(&vgl->vgpsd->gpsd, WATCH_DISABLE, NULL);
2017 if ( vgl->vgpsd->gpsd_open == 0 )
2018 (void)gps_close(&vgl->vgpsd->gpsd);
2019 #if GPSD_API_MAJOR_VERSION == 3
2021 #elif GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
2027 if (vgl->realtime_record && vgl->realtime_track) {
2028 if ((vgl->realtime_track->trackpoints == NULL) || (vgl->realtime_track->trackpoints->next == NULL))
2029 vik_trw_layer_delete_track(vgl->trw_children[TRW_REALTIME], vgl->realtime_track);
2030 vgl->realtime_track = NULL;
2032 vgl->connected_to_gpsd = FALSE;
2035 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2])
2037 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
2038 vgl->realtime_tracking = (vgl->realtime_tracking == FALSE);
2040 /* Make sure we are still in the boat with libgps */
2041 g_assert((VIK_GPS_MODE_2D == MODE_2D) && (VIK_GPS_MODE_3D == MODE_3D));
2043 if (vgl->realtime_tracking) {
2044 vgl->first_realtime_trackpoint = TRUE;
2045 if (!rt_gpsd_connect(vgl, TRUE)) {
2046 vgl->first_realtime_trackpoint = FALSE;
2047 vgl->realtime_tracking = FALSE;
2051 else { /* stop realtime tracking */
2052 vgl->first_realtime_trackpoint = FALSE;
2054 rt_gpsd_disconnect(vgl);
2057 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */