]> git.street.me.uk Git - andy/viking.git/blob - src/vikgpslayer.c
Only call gps_close() after a successful gps_open().
[andy/viking.git] / src / vikgpslayer.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5  * Copyright (C) 2006-2008, Quy Tonthat <qtonthat@gmail.com>
6  *
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.
11  *
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.
16  *
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
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <stdlib.h>
28 #ifdef HAVE_MATH_H
29 #include <math.h>
30 #endif
31 #include "viking.h"
32 #include "icons/icons.h"
33 #include "babel.h"
34
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 #ifdef HAVE_STRING_H
39 #include <string.h>
40 #endif
41 #include <glib/gstdio.h>
42 #include <glib/gprintf.h>
43 #include <glib/gi18n.h>
44 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
45 #include <gps.h>
46 #include "vikutils.h"
47 #endif
48
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 );
54
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 );
59
60 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl );
61
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 );
64
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);
76 #endif
77
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};
82 #ifdef WINDOWS
83 static gchar * params_ports[] = {"com1", "usb:", NULL};
84 #else
85 static gchar * params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
86 #endif
87 /* NUM_PORTS not actually used */
88 /* #define NUM_PORTS (sizeof(params_ports)/sizeof(params_ports[0]) - 1) */
89 /* Compatibility with previous versions */
90 #ifdef WINDOWS
91 static gchar * old_params_ports[] = {"com1", "usb:", NULL};
92 #else
93 static gchar * old_params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
94 #endif
95 #define OLD_NUM_PORTS (sizeof(old_params_ports)/sizeof(old_params_ports[0]) - 1)
96
97 typedef struct {
98   GMutex *mutex;
99   vik_gps_dir direction;
100   gchar *port;
101   gboolean ok;
102   gint total_count;
103   gint count;
104   VikTrwLayer *vtl;
105   VikTrack *track;
106   gchar *babelargs;
107   gchar *window_title;
108   GtkWidget *dialog;
109   GtkWidget *status_label;
110   GtkWidget *gps_label;
111   GtkWidget *ver_label;
112   GtkWidget *id_label;
113   GtkWidget *wp_label;
114   GtkWidget *trk_label;
115   GtkWidget *rte_label;
116   GtkWidget *progress_label;
117   vik_gps_xfer_type progress_type;
118   VikViewport *vvp;
119 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
120   gboolean realtime_tracking;
121 #endif
122 } GpsSession;
123 static void gps_session_delete(GpsSession *sess);
124
125 static gchar *params_groups[] = {
126   N_("Data Mode"),
127 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
128   N_("Realtime Tracking Mode"),
129 #endif
130 };
131
132 enum {GROUP_DATA_MODE, GROUP_REALTIME_MODE};
133
134
135 static VikLayerParamData gps_protocol_default ( void )
136 {
137   VikLayerParamData data;
138   data.s = g_strdup ( "garmin" );
139   return data;
140 }
141
142 static VikLayerParamData gps_port_default ( void )
143 {
144   VikLayerParamData data;
145   data.s = g_strdup ( "usb:" );
146 #ifndef WINDOWS
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) {
150         if ( data.s )
151           g_free ( (gchar *)data.s );
152     data.s = g_strdup ("/dev/ttyUSB1");
153   }
154   if (g_access ("/dev/ttyUSB0", R_OK) == 0) {
155         if ( data.s )
156           g_free ( (gchar *)data.s );
157     data.s = g_strdup ("/dev/ttyUSB0");
158   }
159 #endif
160   return data;
161 }
162
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"),
167   N_("Disable"),
168   NULL
169 };
170 enum {
171   VEHICLE_POSITION_CENTERED = 0,
172   VEHICLE_POSITION_ON_SCREEN,
173   VEHICLE_POSITION_NONE,
174 };
175
176 static VikLayerParamData moving_map_method_default ( void ) { return VIK_LPD_UINT ( VEHICLE_POSITION_ON_SCREEN ); }
177
178 static VikLayerParamData gpsd_host_default ( void )
179 {
180   VikLayerParamData data;
181   data.s = g_strdup ( "localhost" );
182   return data;
183 }
184
185 static VikLayerParamData gpsd_port_default ( void )
186 {
187   VikLayerParamData data;
188   data.s = g_strdup ( DEFAULT_GPSD_PORT );
189   return data;
190 }
191
192 static VikLayerParamData gpsd_retry_interval_default ( void )
193 {
194   VikLayerParamData data;
195   data.s = g_strdup ( "10" );
196   return data;
197 }
198
199 #endif
200
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 */
222 };
223 enum {
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)
229   PARAM_REALTIME_REC,
230   PARAM_REALTIME_CENTER_START,
231   PARAM_VEHICLE_POSITION,
232   PARAM_REALTIME_UPDATE_STATUSBAR,
233   PARAM_GPSD_HOST,
234   PARAM_GPSD_PORT,
235   PARAM_GPSD_RETRY_INTERVAL,
236 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
237   NUM_PARAMS};
238
239 VikLayerInterface vik_gps_layer_interface = {
240   "GPS",
241   N_("GPS"),
242   NULL,
243   &vikgpslayer_pixbuf,
244
245   NULL,
246   0,
247
248   gps_layer_params,
249   NUM_PARAMS,
250   params_groups,
251   sizeof(params_groups)/sizeof(params_groups[0]),
252
253   VIK_MENU_ITEM_ALL,
254
255   (VikLayerFuncCreate)                  vik_gps_layer_create,
256   (VikLayerFuncRealize)                 vik_gps_layer_realize,
257   (VikLayerFuncPostRead)                NULL,
258   (VikLayerFuncFree)                    vik_gps_layer_free,
259
260   (VikLayerFuncProperties)              NULL,
261   (VikLayerFuncDraw)                    vik_gps_layer_draw,
262   (VikLayerFuncChangeCoordMode)         gps_layer_change_coord_mode,
263
264   (VikLayerFuncGetTimestamp)            NULL,
265
266   (VikLayerFuncSetMenuItemsSelection)   NULL,
267   (VikLayerFuncGetMenuItemsSelection)   NULL,
268
269   (VikLayerFuncAddMenuItems)            gps_layer_add_menu_items,
270   (VikLayerFuncSublayerAddMenuItems)    NULL,
271
272   (VikLayerFuncSublayerRenameRequest)   NULL,
273   (VikLayerFuncSublayerToggleVisible)   NULL,
274   (VikLayerFuncSublayerTooltip)         NULL,
275   (VikLayerFuncLayerTooltip)            gps_layer_tooltip,
276   (VikLayerFuncLayerSelected)           NULL,
277
278   (VikLayerFuncMarshall)                gps_layer_marshall,
279   (VikLayerFuncUnmarshall)              gps_layer_unmarshall,
280
281   (VikLayerFuncSetParam)                gps_layer_set_param,
282   (VikLayerFuncGetParam)                gps_layer_get_param,
283   (VikLayerFuncChangeParam)             NULL,
284
285   (VikLayerFuncReadFileData)            NULL,
286   (VikLayerFuncWriteFileData)           NULL,
287
288   (VikLayerFuncDeleteItem)              NULL,
289   (VikLayerFuncCutItem)                 NULL,
290   (VikLayerFuncCopyItem)                NULL,
291   (VikLayerFuncPasteItem)               NULL,
292   (VikLayerFuncFreeCopiedItem)          NULL,
293   (VikLayerFuncDragDropRequest)         NULL,
294
295   (VikLayerFuncSelectClick)             NULL,
296   (VikLayerFuncSelectMove)              NULL,
297   (VikLayerFuncSelectRelease)           NULL,
298   (VikLayerFuncSelectedViewportMenu)    NULL,
299 };
300
301 enum {TRW_DOWNLOAD=0, TRW_UPLOAD,
302 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
303   TRW_REALTIME,
304 #endif
305   NUM_TRW};
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"),
310 #endif
311 };
312
313 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
314 typedef struct {
315   struct gps_data_t gpsd;
316   VikGpsLayer *vgl;
317   int gpsd_open;
318 } VglGpsd;
319
320 typedef struct {
321   struct gps_fix_t fix;
322   gint satellites_used;
323   gboolean dirty;   /* needs to be saved */
324 } GpsFix;
325 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
326
327 struct _VikGpsLayer {
328   VikLayer vl;
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)
333   VglGpsd *vgpsd;
334   gboolean realtime_tracking;  /* set/reset only by the callback */
335   gboolean first_realtime_trackpoint;
336   GpsFix realtime_fix;
337   GpsFix last_fix;
338
339   VikTrack *realtime_track;
340
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;
349
350   /* params */
351   gchar *gpsd_host;
352   gchar *gpsd_port;
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 */
361   gchar *protocol;
362   gchar *serial_port;
363   gboolean download_tracks;
364   gboolean download_routes;
365   gboolean download_waypoints;
366   gboolean upload_tracks;
367   gboolean upload_routes;
368   gboolean upload_waypoints;
369 };
370
371 /**
372  * Overwrite the static setup with dynamically generated GPS Babel device list
373  */
374 static void gps_layer_inst_init ( VikGpsLayer *self )
375 {
376   gint new_proto = 0;
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));
379
380   GList *gl = g_list_first ( a_babel_device_list );
381   while ( gl ) {
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 );
386   }
387   new_protocols[new_proto] = NULL;
388
389   vik_gps_layer_interface.params[PARAM_PROTOCOL].widget_data = new_protocols;
390 }
391
392 GType vik_gps_layer_get_type ()
393 {
394   static GType val_type = 0;
395
396   if (!val_type)
397   {
398     static const GTypeInfo val_info =
399     {
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),
407       0,
408       (GInstanceInitFunc) gps_layer_inst_init,
409     };
410     val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGpsLayer", &val_info, 0 );
411   }
412
413   return val_type;
414 }
415
416 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp)
417 {
418   int i;
419
420   VikGpsLayer *rv = vik_gps_layer_new (vp);
421   vik_layer_rename ( VIK_LAYER(rv), vik_gps_layer_interface.name );
422
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));
426   }
427   return rv;
428 }
429
430 static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl )
431 {
432   return vgl->protocol;
433 }
434
435 /* "Copy" */
436 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *datalen )
437 {
438   VikLayer *child_layer;
439   guint8 *ld; 
440   gint ll;
441   GByteArray* b = g_byte_array_new ();
442   gint len;
443   gint i;
444
445 #define alm_append(obj, sz)     \
446   len = (sz);                   \
447   g_byte_array_append ( b, (guint8 *)&len, sizeof(len) );       \
448   g_byte_array_append ( b, (guint8 *)(obj), len );
449
450   vik_layer_marshall_params(VIK_LAYER(vgl), &ld, &ll);
451   alm_append(ld, ll);
452   g_free(ld);
453
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);
457     if (ld) {
458       alm_append(ld, ll);
459       g_free(ld);
460     }
461   }
462   *data = b->data;
463   *datalen = b->len;
464   g_byte_array_free(b, FALSE);
465 #undef alm_append
466 }
467
468 /* "Paste" */
469 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
470 {
471 #define alm_size (*(gint *)data)
472 #define alm_next \
473   len -= sizeof(gint) + alm_size; \
474   data += sizeof(gint) + alm_size;
475   
476   VikGpsLayer *rv = vik_gps_layer_new(vvp);
477   VikLayer *child_layer;
478   gint i;
479
480   vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
481   alm_next;
482
483   i = 0;
484   while (len>0 && i < NUM_TRW) {
485     child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
486     if (child_layer) {
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()
490     }
491     alm_next;
492   }
493   //  g_print("gps_layer_unmarshall ended with len=%d\n", len);
494   g_assert(len == 0);
495   return rv;
496 #undef alm_size
497 #undef alm_next
498 }
499
500 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
501 {
502   switch ( id )
503   {
504     case PARAM_PROTOCOL:
505       if (data.s) {
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]) &&
511             data.s[1] == '\0' &&
512             index < OLD_NUM_PROTOCOLS)
513           // It is a single digit: activate compatibility
514           vgl->protocol = g_strdup(protocols_args[index]);
515         else
516           vgl->protocol = g_strdup(data.s);
517         g_debug("%s: %s", __FUNCTION__, vgl->protocol);
518       }
519       else
520         g_warning(_("Unknown GPS Protocol"));
521       break;
522     case PARAM_PORT:
523       if (data.s) {
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]) &&
529             data.s[1] == '\0' &&
530             index < OLD_NUM_PORTS)
531           /* It is a single digit: activate compatibility */
532           vgl->serial_port = g_strdup(old_params_ports[index]);
533         else
534           vgl->serial_port = g_strdup(data.s);
535         g_debug("%s: %s", __FUNCTION__, vgl->serial_port);
536       }
537       else
538         g_warning(_("Unknown serial port device"));
539       break;
540     case PARAM_DOWNLOAD_TRACKS:
541       vgl->download_tracks = data.b;
542       break;
543     case PARAM_UPLOAD_TRACKS:
544       vgl->upload_tracks = data.b;
545       break;
546     case PARAM_DOWNLOAD_ROUTES:
547       vgl->download_routes = data.b;
548       break;
549     case PARAM_UPLOAD_ROUTES:
550       vgl->upload_routes = data.b;
551       break;
552     case PARAM_DOWNLOAD_WAYPOINTS:
553       vgl->download_waypoints = data.b;
554       break;
555     case PARAM_UPLOAD_WAYPOINTS:
556       vgl->upload_waypoints = data.b;
557       break;
558 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
559     case PARAM_GPSD_HOST:
560       if (data.s) {
561         if (vgl->gpsd_host)
562           g_free(vgl->gpsd_host);
563         vgl->gpsd_host = g_strdup(data.s);
564       }
565       break;
566     case PARAM_GPSD_PORT:
567       if (data.s) {
568         if (vgl->gpsd_port)
569           g_free(vgl->gpsd_port);
570         vgl->gpsd_port = g_strdup(data.s);
571       }
572       break;
573     case PARAM_GPSD_RETRY_INTERVAL:
574       vgl->gpsd_retry_interval = strtol(data.s, NULL, 10);
575       break;
576     case PARAM_REALTIME_REC:
577       vgl->realtime_record = data.b;
578       break;
579     case PARAM_REALTIME_CENTER_START:
580       vgl->realtime_jump_to_start = data.b;
581       break;
582     case PARAM_VEHICLE_POSITION:
583       vgl->vehicle_position = data.u;
584       break;
585     case PARAM_REALTIME_UPDATE_STATUSBAR:
586       vgl->realtime_update_statusbar = data.b;
587       break;
588 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
589     default:
590       g_warning("gps_layer_set_param(): unknown parameter");
591   }
592
593   return TRUE;
594 }
595
596 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation )
597 {
598   VikLayerParamData rv;
599   switch ( id )
600   {
601     case PARAM_PROTOCOL:
602       rv.s = vgl->protocol;
603       g_debug("%s: %s", __FUNCTION__, rv.s);
604       break;
605     case PARAM_PORT:
606       rv.s = vgl->serial_port;
607       g_debug("%s: %s", __FUNCTION__, rv.s);
608       break;
609     case PARAM_DOWNLOAD_TRACKS:
610       rv.b = vgl->download_tracks;
611       break;
612     case PARAM_UPLOAD_TRACKS:
613       rv.b = vgl->upload_tracks;
614       break;
615     case PARAM_DOWNLOAD_ROUTES:
616       rv.b = vgl->download_routes;
617       break;
618     case PARAM_UPLOAD_ROUTES:
619       rv.b = vgl->upload_routes;
620       break;
621     case PARAM_DOWNLOAD_WAYPOINTS:
622       rv.b = vgl->download_waypoints;
623       break;
624     case PARAM_UPLOAD_WAYPOINTS:
625       rv.b = vgl->upload_waypoints;
626       break;
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 : "";
630       break;
631     case PARAM_GPSD_PORT:
632       rv.s = vgl->gpsd_port ? vgl->gpsd_port : g_strdup(DEFAULT_GPSD_PORT);
633       break;
634     case PARAM_GPSD_RETRY_INTERVAL:
635       rv.s = g_strdup_printf("%d", vgl->gpsd_retry_interval);
636       break;
637     case PARAM_REALTIME_REC:
638       rv.b = vgl->realtime_record;
639       break;
640     case PARAM_REALTIME_CENTER_START:
641       rv.b = vgl->realtime_jump_to_start;
642       break;
643     case PARAM_VEHICLE_POSITION:
644       rv.u = vgl->vehicle_position;
645       break;
646     case PARAM_REALTIME_UPDATE_STATUSBAR:
647       rv.u = vgl->realtime_update_statusbar;
648       break;
649 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
650     default:
651       g_warning(_("%s: unknown parameter"), __FUNCTION__);
652   }
653
654   return rv;
655 }
656
657 VikGpsLayer *vik_gps_layer_new (VikViewport *vp)
658 {
659   gint i;
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;
664   }
665   vgl->children = NULL;
666   vgl->cur_read_child = 0;
667
668 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
669   vgl->first_realtime_trackpoint = FALSE;
670   vgl->trkpt = NULL;
671   vgl->trkpt_prev = NULL;
672   vgl->vgpsd = NULL;
673   vgl->realtime_io_channel = NULL;
674   vgl->realtime_io_watch_id = 0;
675   vgl->realtime_retry_timer = 0;
676   if ( vp ) {
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;
682   }
683   vgl->realtime_track = NULL;
684 #endif // VIK_CONFIG_REALTIME_GPS_TRACKING
685
686   vik_layer_set_defaults ( VIK_LAYER(vgl), vp );
687
688   return vgl;
689 }
690
691 static void vik_gps_layer_draw ( VikGpsLayer *vgl, VikViewport *vp )
692 {
693   gint i;
694   VikLayer *vl;
695   VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( vp ));
696
697   for (i = 0; i < NUM_TRW; i++) {
698     vl = VIK_LAYER(vgl->trw_children[i]);
699     if (vl == trigger) {
700       if ( vik_viewport_get_half_drawn ( vp ) ) {
701         vik_viewport_set_half_drawn ( vp, FALSE );
702         vik_viewport_snapshot_load( vp );
703       } else {
704         vik_viewport_snapshot_save( vp );
705       }
706     }
707     if (!vik_viewport_get_half_drawn(vp))
708       vik_layer_draw ( vl, vp );
709   }
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 );
716       } else {
717         vik_viewport_snapshot_save( vp );
718       }
719     }
720     if (!vik_viewport_get_half_drawn(vp))
721       realtime_tracking_draw(vgl, vp);
722   }
723 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
724 }
725
726 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode )
727 {
728   gint i;
729   for (i = 0; i < NUM_TRW; i++) {
730     vik_layer_change_coord_mode(VIK_LAYER(vgl->trw_children[i]), mode);
731   }
732 }
733
734 static void gps_layer_add_menu_items( VikGpsLayer *vgl, GtkMenu *menu, gpointer vlp )
735 {
736   static gpointer pass_along[2];
737   GtkWidget *item;
738   pass_along[0] = vgl;
739   pass_along[1] = vlp;
740
741   item = gtk_menu_item_new();
742   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
743   gtk_widget_show ( item );
744
745   /* Now with icons */
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 );
751
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 );
757
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 );
768
769   item = gtk_menu_item_new();
770   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
771   gtk_widget_show ( item );
772
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 */
779
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 );
785
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 );
791
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 );
797
798 }
799
800 static void disconnect_layer_signal ( VikLayer *vl, VikGpsLayer *vgl )
801 {
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);
805   }
806 }
807
808 static void vik_gps_layer_free ( VikGpsLayer *vgl )
809 {
810   gint i;
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]);
815   }
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 */
827 }
828
829 static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter )
830 {
831   GtkTreeIter iter;
832   int ix;
833
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....
838
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 );
848   }
849 }
850
851 const GList *vik_gps_layer_get_children ( VikGpsLayer *vgl )
852 {
853   int i;
854
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]);
858   }
859   return vgl->children;
860 }
861
862 VikTrwLayer * vik_gps_layer_get_a_child(VikGpsLayer *vgl)
863 {
864   g_assert ((vgl->cur_read_child >= 0) && (vgl->cur_read_child < NUM_TRW));
865
866   VikTrwLayer * vtl = vgl->trw_children[vgl->cur_read_child];
867   if (++(vgl->cur_read_child) >= NUM_TRW)
868     vgl->cur_read_child = 0;
869   return(vtl);
870 }
871
872 gboolean vik_gps_layer_is_empty ( VikGpsLayer *vgl )
873 {
874   if ( vgl->trw_children[0] )
875     return FALSE;
876   return TRUE;
877 }
878
879 static void gps_session_delete(GpsSession *sess)
880 {
881   vik_mutex_free(sess->mutex);
882   g_free(sess->babelargs);
883   g_free(sess);
884 }
885
886 static void set_total_count(gint cnt, GpsSession *sess)
887 {
888   gchar s[128];
889   gdk_threads_enter();
890   g_mutex_lock(sess->mutex);
891   if (sess->ok) {
892     const gchar *tmp_str;
893     if (sess->direction == GPS_DOWN)
894     {
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;
898       default:
899         {
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;
904           break;
905         }
906       }
907     }
908     else
909     {
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;
914       }
915     }
916
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;
921   }
922   g_mutex_unlock(sess->mutex);
923   gdk_threads_leave();
924 }
925
926 static void set_current_count(gint cnt, GpsSession *sess)
927 {
928   gchar s[128];
929   const gchar *tmp_str;
930
931   gdk_threads_enter();
932   g_mutex_lock(sess->mutex);
933   if (sess->ok) {
934     if (cnt < sess->total_count) {
935       if (sess->direction == GPS_DOWN)
936       {
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;
941         }
942       }
943       else {
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;
948         }
949       }
950       g_snprintf(s, 128, tmp_str, cnt, sess->total_count);
951     } else {
952       if (sess->direction == GPS_DOWN)
953       {
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;
958         }
959       }
960       else {
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;
965         }
966       }
967       g_snprintf(s, 128, tmp_str, cnt);
968     }     
969     gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
970   }
971   g_mutex_unlock(sess->mutex);
972   gdk_threads_leave();
973 }
974
975 static void set_gps_info(const gchar *info, GpsSession *sess)
976 {
977   gchar s[256];
978   gdk_threads_enter();
979   g_mutex_lock(sess->mutex);
980   if (sess->ok) {
981     g_snprintf(s, 256, _("GPS Device: %s"), info);
982     gtk_label_set_text ( GTK_LABEL(sess->gps_label), s );
983   }
984   g_mutex_unlock(sess->mutex);
985   gdk_threads_leave();
986 }
987
988 /*
989  * Common processing for GPS Device information
990  * It doesn't matter whether we're uploading or downloading
991  */
992 static void process_line_for_gps_info ( const gchar *line, GpsSession *sess )
993 {
994   if (strstr(line, "PRDDAT")) {
995     gchar **tokens = g_strsplit(line, " ", 0);
996     gchar info[128];
997     int ilen = 0;
998     int i;
999     int n_tokens = 0;
1000
1001     while (tokens[n_tokens])
1002       n_tokens++;
1003
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
1007     if (n_tokens > 8) {
1008       for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
1009         guint ch;
1010         sscanf(tokens[i], "%x", &ch);
1011         info[ilen++] = ch;
1012       }
1013       info[ilen++] = 0;
1014       set_gps_info(info, sess);
1015     }
1016     g_strfreev(tokens);
1017   }
1018
1019   /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
1020   if (strstr(line, "Unit:")) {
1021     gchar **tokens = g_strsplit(line, "\t", 0);
1022     int n_tokens = 0;
1023     while (tokens[n_tokens])
1024       n_tokens++;
1025
1026     if (n_tokens > 1) {
1027       set_gps_info(tokens[1], sess);
1028     }
1029     g_strfreev(tokens);
1030   }
1031 }
1032
1033 static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1034 {
1035   gchar *line;
1036
1037   gdk_threads_enter ();
1038   g_mutex_lock(sess->mutex);
1039   if (!sess->ok) {
1040     g_mutex_unlock(sess->mutex);
1041     gps_session_delete(sess);
1042     gdk_threads_leave();
1043     g_thread_exit ( NULL );
1044   }
1045   g_mutex_unlock(sess->mutex);
1046   gdk_threads_leave ();
1047
1048   switch(c) {
1049   case BABEL_DIAG_OUTPUT:
1050     line = (gchar *)data;
1051
1052     gdk_threads_enter();
1053     g_mutex_lock(sess->mutex);
1054     if (sess->ok) {
1055       gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1056     }
1057     g_mutex_unlock(sess->mutex);
1058     gdk_threads_leave();
1059
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;
1064     }
1065     if (strstr(line, "Xfer Trk")) {
1066       sess->progress_label = sess->trk_label;
1067       sess->progress_type = TRK;
1068     }
1069     if (strstr(line, "Xfer Rte")) {
1070       sess->progress_label = sess->rte_label;
1071       sess->progress_type = RTE;
1072     }
1073
1074     process_line_for_gps_info ( line, sess );
1075
1076     if (strstr(line, "RECORD")) {
1077       int lsb, msb, cnt;
1078
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);
1084         sess->count = 0;
1085       }
1086     }
1087     if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1088       sess->count++;
1089       set_current_count(sess->count, sess);
1090     }
1091     break;
1092   case BABEL_DONE:
1093     break;
1094   default:
1095     break;
1096   }
1097
1098 }
1099
1100 static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1101 {
1102   gchar *line;
1103   static int cnt = 0;
1104
1105   gdk_threads_enter ();
1106   g_mutex_lock(sess->mutex);
1107   if (!sess->ok) {
1108     g_mutex_unlock(sess->mutex);
1109     gps_session_delete(sess);
1110     gdk_threads_leave();
1111     g_thread_exit ( NULL );
1112   }
1113   g_mutex_unlock(sess->mutex);
1114   gdk_threads_leave ();
1115
1116   switch(c) {
1117   case BABEL_DIAG_OUTPUT:
1118     line = (gchar *)data;
1119
1120     gdk_threads_enter();
1121     g_mutex_lock(sess->mutex);
1122     if (sess->ok) {
1123       gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1124     }
1125     g_mutex_unlock(sess->mutex);
1126     gdk_threads_leave();
1127
1128     process_line_for_gps_info ( line, sess );
1129
1130     if (strstr(line, "RECORD")) { 
1131       int lsb, msb;
1132
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); */
1138         sess->count = 0;
1139       }
1140     }
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);
1146       }
1147       sess->count++;
1148       set_current_count(sess->count, sess);
1149     }
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);
1158        }
1159        sess->count++;
1160        set_current_count(sess->count, sess);
1161     }
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);
1167       }
1168       sess->count++;
1169       set_current_count(sess->count, sess);
1170     }
1171     break;
1172   case BABEL_DONE:
1173     break;
1174   default:
1175     break;
1176   }
1177
1178 }
1179
1180 static void gps_comm_thread(GpsSession *sess)
1181 {
1182   gboolean result;
1183
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);
1187   }
1188   else {
1189     result = a_babel_convert_to (sess->vtl, sess->track, sess->babelargs, sess->port,
1190         (BabelStatusFunc) gps_upload_progress_func, sess);
1191   }
1192
1193   if (!result) {
1194     gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: couldn't find gpsbabel.") );
1195   } 
1196   else {
1197     g_mutex_lock(sess->mutex);
1198     if (sess->ok) {
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 );
1202
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)
1206 #endif
1207       {
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
1213         }
1214       }
1215     } else {
1216       /* canceled */
1217     }
1218     g_mutex_unlock(sess->mutex);
1219   }
1220
1221   g_mutex_lock(sess->mutex);
1222   if (sess->ok) {
1223     sess->ok = FALSE;
1224     g_mutex_unlock(sess->mutex);
1225   }
1226   else {
1227     g_mutex_unlock(sess->mutex);
1228     gps_session_delete(sess);
1229   }
1230   g_thread_exit(NULL);
1231 }
1232
1233 /**
1234  * vik_gps_comm:
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)
1246  *
1247  * Talk to a GPS Device using a thread which updates a dialog with the progress
1248  */
1249 gint vik_gps_comm ( VikTrwLayer *vtl,
1250                     VikTrack *track,
1251                     vik_gps_dir dir,
1252                     gchar *protocol,
1253                     gchar *port,
1254                     gboolean tracking,
1255                     VikViewport *vvp,
1256                     VikLayersPanel *vlp,
1257                     gboolean do_tracks,
1258                     gboolean do_routes,
1259                     gboolean do_waypoints,
1260                     gboolean turn_off )
1261 {
1262   GpsSession *sess = g_malloc(sizeof(GpsSession));
1263   char *tracks = NULL;
1264   char *routes = NULL;
1265   char *waypoints = NULL;
1266
1267   sess->mutex = vik_mutex_new();
1268   sess->direction = dir;
1269   sess->vtl = vtl;
1270   sess->track = track;
1271   sess->port = g_strdup(port);
1272   sess->ok = TRUE;
1273   sess->window_title = (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload");
1274   sess->vvp = vvp;
1275
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") );
1285   }
1286
1287 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1288   sess->realtime_tracking = tracking;
1289 #endif
1290
1291   if (do_tracks)
1292     tracks = "-t";
1293   else
1294     tracks = "";
1295   if (do_routes)
1296     routes = "-r";
1297   else
1298     routes = "";
1299   if (do_waypoints)
1300     waypoints = "-w";
1301   else
1302     waypoints = "";
1303
1304   sess->babelargs = g_strdup_printf("-D 9 %s %s %s -%c %s",
1305                                    tracks, routes, waypoints, (dir == GPS_DOWN) ? 'i' : 'o', protocol);
1306   tracks = NULL;
1307   waypoints = NULL;
1308
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 );
1315
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);
1319
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 ("");
1326
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 );
1331
1332     gtk_widget_show_all(sess->dialog);
1333
1334     sess->progress_label = sess->wp_label;
1335     sess->total_count = -1;
1336
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 );
1340 #else
1341     g_thread_create ( (GThreadFunc)gps_comm_thread, sess, FALSE, NULL );
1342 #endif
1343
1344     gtk_dialog_set_default_response ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT );
1345     gtk_dialog_run(GTK_DIALOG(sess->dialog));
1346
1347     gtk_widget_destroy(sess->dialog);
1348   }
1349   else {
1350     if ( !turn_off )
1351       a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No GPS items selected for transfer.") );
1352   }
1353
1354   g_mutex_lock(sess->mutex);
1355
1356   if (sess->ok) {
1357     sess->ok = FALSE;   /* tell thread to stop */
1358     g_mutex_unlock(sess->mutex);
1359   }
1360   else {
1361     if ( turn_off ) {
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);
1366       if ( !result )
1367         a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not turn off device.") );
1368       g_free ( device_off );
1369     }
1370     g_mutex_unlock(sess->mutex);
1371     gps_session_delete(sess);
1372   }
1373
1374   return 0;
1375 }
1376
1377 static void gps_upload_cb( gpointer layer_and_vlp[2] )
1378 {
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);
1385 }
1386
1387 static void gps_download_cb( gpointer layer_and_vlp[2] )
1388 {
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);
1395 #else
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);
1397 #endif
1398 }
1399
1400 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] )
1401 {
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?"),
1406                               NULL ) )
1407     return;
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]);
1411 }
1412
1413 static void gps_empty_download_cb( gpointer layer_and_vlp[2] )
1414 {
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?"),
1419                               NULL ) )
1420     return;
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]);
1424 }
1425
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] )
1428 {
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?"),
1433                               NULL ) )
1434     return;
1435   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1436   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1437 }
1438 #endif
1439
1440 static void gps_empty_all_cb( gpointer layer_and_vlp[2] )
1441 {
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?"),
1446                               NULL ) )
1447     return;
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]);
1457 #endif
1458 }
1459
1460 #if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1461 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp)
1462 {
1463   struct LatLon ll;
1464   VikCoord nw, se;
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) ) {
1475     VikCoord gps;
1476     gint x, y;
1477     gint half_back_x, half_back_y;
1478     gint half_back_bg_x, half_back_bg_y;
1479     gint pt_x, pt_y;
1480     gint ptbg_x;
1481     gint side1_x, side1_y, side2_x, side2_y;
1482     gint side1bg_x, side1bg_y, side2bg_x, side2bg_y;
1483
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 );
1488
1489     gdouble heading_cos = cos(DEG2RAD(vgl->realtime_fix.fix.track));
1490     gdouble heading_sin = sin(DEG2RAD(vgl->realtime_fix.fix.track));
1491
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;
1496
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;
1501
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;
1506
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;
1511
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} };
1514
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;
1521   }
1522 }
1523
1524 static VikTrackpoint* create_realtime_trackpoint(VikGpsLayer *vgl, gboolean forced)
1525 {
1526     struct LatLon ll;
1527     GList *last_tp;
1528
1529     /* Note that fix.time is a double, but it should not affect the precision
1530        for most GPS */
1531     time_t cur_timestamp = vgl->realtime_fix.fix.time;
1532     time_t last_timestamp = vgl->last_fix.fix.time;
1533
1534     if (cur_timestamp < last_timestamp) {
1535       return NULL;
1536     }
1537
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);
1550         replace = TRUE;
1551       }
1552       if (replace ||
1553           ((cur_timestamp != last_timestamp) &&
1554           ((forced || 
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;
1563         tp->altitude = alt;
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;
1569
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);
1574
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;
1579         return tp;
1580       }
1581     }
1582     return NULL;
1583 }
1584
1585 #define VIK_SETTINGS_GPS_STATUSBAR_FORMAT "gps_statusbar_format"
1586
1587 static void update_statusbar ( VikGpsLayer *vgl, VikWindow *vw )
1588 {
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" );
1594     need2free = TRUE;
1595   }
1596
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 );
1599   g_free ( msg );
1600
1601   if ( need2free )
1602     g_free ( statusbar_format_code );
1603
1604 }
1605
1606 static void gpsd_raw_hook(VglGpsd *vgpsd, gchar *data)
1607 {
1608   gboolean update_all = FALSE;
1609   VikGpsLayer *vgl = vgpsd->vgl;
1610
1611   if (!vgl->realtime_tracking) {
1612     g_warning("%s: receiving GPS data while not in realtime mode", __PRETTY_FUNCTION__);
1613     return;
1614   }
1615
1616   if ((vgpsd->gpsd.fix.mode >= MODE_2D) &&
1617       !isnan(vgpsd->gpsd.fix.latitude) &&
1618       !isnan(vgpsd->gpsd.fix.longitude)) {
1619
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;
1625
1626     struct LatLon ll;
1627     VikCoord vehicle_coord;
1628
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);
1633
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);
1637       update_all = TRUE;
1638     }
1639     else if (vgl->vehicle_position == VEHICLE_POSITION_ON_SCREEN) {
1640       const int hdiv = 6;
1641       const int vdiv = 6;
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);
1645       gint vx, vy;
1646
1647       vik_viewport_coord_to_screen(vvp, &vehicle_coord, &vx, &vy);
1648       update_all = TRUE;
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);
1657       else
1658         update_all = FALSE;
1659     }
1660
1661     vgl->first_realtime_trackpoint = FALSE;
1662
1663     vgl->trkpt = create_realtime_trackpoint ( vgl, FALSE );
1664
1665     if ( vgl->trkpt ) {
1666       if ( vgl->realtime_update_statusbar )
1667         update_statusbar ( vgl, vw );
1668       vgl->trkpt_prev = vgl->trkpt;
1669     }
1670
1671     vik_layer_emit_update ( update_all ? VIK_LAYER(vgl) : VIK_LAYER(vgl->trw_children[TRW_REALTIME]) ); // NB update from background thread
1672   }
1673 }
1674
1675 static gboolean gpsd_data_available(GIOChannel *source, GIOCondition condition, gpointer data)
1676 {
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);
1685 #else
1686       // Broken compile
1687 #endif
1688       return TRUE;
1689     }
1690     else {
1691       g_warning("Disconnected from gpsd. Trying to reconnect");
1692       rt_gpsd_disconnect(vgl);
1693       (void)rt_gpsd_connect(vgl, FALSE);
1694     }
1695   }
1696   return FALSE; /* no further calling */
1697 }
1698
1699 static gchar *make_track_name(VikTrwLayer *vtl)
1700 {
1701   const gchar basename[] = "REALTIME";
1702   const gint bufsize = sizeof(basename) + 5;
1703   gchar *name = g_malloc(bufsize);
1704   strcpy(name, basename);
1705   gint i = 2;
1706
1707   while (vik_trw_layer_get_track(vtl, name) != NULL) {
1708     g_snprintf(name, bufsize, "%s#%d", basename, i);
1709     i++;
1710   }
1711   return(name);
1712
1713 }
1714
1715 static gboolean rt_gpsd_try_connect(gpointer *data)
1716 {
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);
1720
1721   if (gpsd == NULL) {
1722 #elif GPSD_API_MAJOR_VERSION == 4
1723   vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1724
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 ) {
1730 #else
1731 // Delibrately break compilation...
1732 #endif
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 */
1736   }
1737
1738 #if GPSD_API_MAJOR_VERSION == 3
1739   vgl->vgpsd = realloc(gpsd, sizeof(VglGpsd));
1740 #endif
1741   vgl->vgpsd->vgl = vgl;
1742
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;
1747
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);
1753   }
1754
1755 #if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1756   gps_set_raw_hook(&vgl->vgpsd->gpsd, gpsd_raw_hook);
1757 #endif
1758
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);
1762
1763 #if GPSD_API_MAJOR_VERSION == 3
1764   gps_query(&vgl->vgpsd->gpsd, "w+x");
1765 #endif
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" );
1769 #endif
1770
1771   return FALSE;  /* no longer called by timeout */
1772 }
1773
1774 static gboolean rt_ask_retry(VikGpsLayer *vgl)
1775 {
1776   GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER(vgl),
1777                           GTK_DIALOG_DESTROY_WITH_PARENT,
1778                           GTK_MESSAGE_QUESTION,
1779                           GTK_BUTTONS_YES_NO,
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);
1784
1785   gint res = gtk_dialog_run(GTK_DIALOG(dialog));
1786   gtk_widget_destroy(dialog);
1787   return (res == GTK_RESPONSE_YES);
1788 }
1789
1790 static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed)
1791 {
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);
1796       return FALSE;
1797     }
1798     else if (ask_if_failed && !rt_ask_retry(vgl))
1799       return FALSE;
1800     else
1801       vgl->realtime_retry_timer = g_timeout_add_seconds(vgl->gpsd_retry_interval,
1802         (GSourceFunc)rt_gpsd_try_connect, (gpointer *)vgl);
1803   }
1804   return TRUE;
1805 }
1806
1807 static void rt_gpsd_disconnect(VikGpsLayer *vgl)
1808 {
1809   if (vgl->realtime_retry_timer) {
1810     g_source_remove(vgl->realtime_retry_timer);
1811     vgl->realtime_retry_timer = 0;
1812   }
1813   if (vgl->realtime_io_watch_id) {
1814     g_source_remove(vgl->realtime_io_watch_id);
1815     vgl->realtime_io_watch_id = 0;
1816   }
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;
1821   }
1822   if (vgl->vgpsd) {
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);
1825 #endif
1826     if ( vgl->vgpsd->gpsd_open == 0 )
1827       (void)gps_close(&vgl->vgpsd->gpsd);
1828 #if GPSD_API_MAJOR_VERSION == 3
1829     free(vgl->vgpsd);
1830 #elif GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5 || GPSD_API_MAJOR_VERSION == 6
1831     g_free(vgl->vgpsd);
1832 #endif
1833     vgl->vgpsd = NULL;
1834   }
1835
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;
1840   }
1841 }
1842
1843 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2])
1844 {
1845   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1846   vgl->realtime_tracking = (vgl->realtime_tracking == FALSE);
1847
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));
1850
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;
1856       vgl->trkpt = NULL;
1857     }
1858   }
1859   else {  /* stop realtime tracking */
1860     vgl->first_realtime_trackpoint = FALSE;
1861     vgl->trkpt = NULL;
1862     rt_gpsd_disconnect(vgl);
1863   }
1864 }
1865 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */