]> git.street.me.uk Git - andy/viking.git/blob - src/vikgpslayer.c
M_PI already defined
[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  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <stdlib.h>
27 #include <math.h>
28 #include "viking.h"
29 #include "vikgpslayer_pixmap.h"
30 #include "babel.h"
31
32 #include <string.h>
33 #include <glib.h>
34 #include <glib/gprintf.h>
35 #include <glib/gi18n.h>
36 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
37 #include <gps.h>
38 #endif
39
40 #define DISCONNECT_UPDATE_SIGNAL(vl, val) g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, val)
41 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp);
42 static void vik_gps_layer_realize ( VikGpsLayer *val, VikTreeview *vt, GtkTreeIter *layer_iter );
43 static void vik_gps_layer_free ( VikGpsLayer *val );
44 static void vik_gps_layer_draw ( VikGpsLayer *val, gpointer data );
45 VikGpsLayer *vik_gps_layer_new ();
46
47 static void gps_layer_marshall( VikGpsLayer *val, guint8 **data, gint *len );
48 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
49 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp );
50 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id );
51
52 static void gps_layer_change_coord_mode ( VikGpsLayer *val, VikCoordMode mode );
53 static void gps_layer_add_menu_items( VikGpsLayer *vtl, GtkMenu *menu, gpointer vlp );
54 static void gps_layer_drag_drop_request ( VikGpsLayer *val_src, VikGpsLayer *val_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
55
56 static void gps_upload_cb( gpointer layer_and_vlp[2] );
57 static void gps_download_cb( gpointer layer_and_vlp[2] );
58 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] );
59 static void gps_empty_download_cb( gpointer layer_and_vlp[2] );
60 static void gps_empty_all_cb( gpointer layer_and_vlp[2] );
61 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
62 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2] );
63 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp);
64 #endif
65
66 typedef enum {GARMIN_P = 0, MAGELLAN_P, NUM_PROTOCOLS} vik_gps_proto;
67 static gchar * params_protocols[] = {"Garmin", "Magellan", NULL};
68 static gchar * protocols_args[]   = {"garmin", "magellan"};
69 /*#define NUM_PROTOCOLS (sizeof(params_protocols)/sizeof(params_protocols[0]) - 1) */
70 static gchar * params_ports[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyUSB0", "/dev/ttyUSB1", "usb:", NULL};
71 #define NUM_PORTS (sizeof(params_ports)/sizeof(params_ports[0]) - 1)
72 typedef enum {GPS_DOWN=0, GPS_UP} gps_dir;
73
74 typedef struct {
75   GMutex *mutex;
76   gps_dir direction;
77   gchar *port;
78   gboolean ok;
79   gint total_count;
80   gint count;
81   VikTrwLayer *vtl;
82   gchar *cmd_args;
83   gchar * window_title;
84   GtkWidget *dialog;
85   GtkWidget *status_label;
86   GtkWidget *gps_label;
87   GtkWidget *ver_label;
88   GtkWidget *id_label;
89   GtkWidget *wp_label;
90   GtkWidget *progress_label;
91   GtkWidget *trk_label;
92 } GpsSession;
93 static void gps_session_delete(GpsSession *sess);
94
95 static gchar *params_groups[] = {
96   "Data Mode",
97 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
98   "Realtime Tracking Mode",
99 #endif
100 };
101
102 enum {GROUP_DATA_MODE, GROUP_REALTIME_MODE};
103
104 static VikLayerParam gps_layer_params[] = {
105   { "gps_protocol", VIK_LAYER_PARAM_UINT, GROUP_DATA_MODE, N_("GPS Protocol:"), VIK_LAYER_WIDGET_COMBOBOX, params_protocols, NULL},
106   { "gps_port", VIK_LAYER_PARAM_UINT, GROUP_DATA_MODE, N_("Serial Port:"), VIK_LAYER_WIDGET_COMBOBOX, params_ports, NULL},
107
108 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
109   { "record_tracking", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Recording tracks"), VIK_LAYER_WIDGET_CHECKBUTTON},
110   { "center_start_tracking", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Jump to current position on start"), VIK_LAYER_WIDGET_CHECKBUTTON},
111   { "centered_tracking", VIK_LAYER_PARAM_BOOLEAN, GROUP_REALTIME_MODE, N_("Keep current position at center"), VIK_LAYER_WIDGET_CHECKBUTTON},
112   { "gpsd_host", VIK_LAYER_PARAM_STRING, GROUP_REALTIME_MODE, N_("Gpsd Host:"), VIK_LAYER_WIDGET_ENTRY},
113   { "gpsd_port", VIK_LAYER_PARAM_STRING, GROUP_REALTIME_MODE, N_("Gpsd Port:"), VIK_LAYER_WIDGET_ENTRY},
114 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
115 };
116 enum {
117   PARAM_PROTOCOL=0, PARAM_PORT,
118 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
119   PARAM_REALTIME_REC, PARAM_REALTIME_CENTER_START, PARAM_REALTIME_CENTERED, PARAM_GPSD_HOST, PARAM_GPSD_PORT,
120 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
121   NUM_PARAMS};
122
123 VikLayerInterface vik_gps_layer_interface = {
124   "GPS",
125   &gpslayer_pixbuf,
126
127   NULL,
128   0,
129
130   gps_layer_params,
131   NUM_PARAMS,
132   params_groups,
133   sizeof(params_groups)/sizeof(params_groups[0]),
134
135   VIK_MENU_ITEM_ALL & ~(VIK_MENU_ITEM_CUT|VIK_MENU_ITEM_COPY),
136
137   (VikLayerFuncCreate)                  vik_gps_layer_create,
138   (VikLayerFuncRealize)                 vik_gps_layer_realize,
139   (VikLayerFuncPostRead)                NULL,
140   (VikLayerFuncFree)                    vik_gps_layer_free,
141
142   (VikLayerFuncProperties)              NULL,
143   (VikLayerFuncDraw)                    vik_gps_layer_draw,
144   (VikLayerFuncChangeCoordMode)         gps_layer_change_coord_mode,
145
146   (VikLayerFuncSetMenuItemsSelection)   NULL,
147   (VikLayerFuncGetMenuItemsSelection)   NULL,
148
149   (VikLayerFuncAddMenuItems)            gps_layer_add_menu_items,
150   (VikLayerFuncSublayerAddMenuItems)    NULL,
151
152   (VikLayerFuncSublayerRenameRequest)   NULL,
153   (VikLayerFuncSublayerToggleVisible)   NULL,
154
155   (VikLayerFuncMarshall)                gps_layer_marshall,
156   (VikLayerFuncUnmarshall)              gps_layer_unmarshall,
157
158   (VikLayerFuncSetParam)                gps_layer_set_param,
159   (VikLayerFuncGetParam)                gps_layer_get_param,
160
161   (VikLayerFuncReadFileData)            NULL,
162   (VikLayerFuncWriteFileData)           NULL,
163
164   (VikLayerFuncDeleteItem)              NULL,
165   (VikLayerFuncCopyItem)                NULL,
166   (VikLayerFuncPasteItem)               NULL,
167   (VikLayerFuncFreeCopiedItem)          NULL,
168   (VikLayerFuncDragDropRequest)         gps_layer_drag_drop_request,
169 };
170
171 enum {TRW_DOWNLOAD=0, TRW_UPLOAD,
172 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
173   TRW_REALTIME,
174 #endif
175   NUM_TRW};
176 static gchar * trw_names[] = {
177   N_("GPS Download"), N_("GPS Upload"),
178 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
179   N_("GPS Realtime Tracking"),
180 #endif
181 };
182
183 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
184 typedef struct {
185   struct gps_data_t gpsd;
186   VikGpsLayer *vgl;
187 } VglGpsd;
188
189 typedef struct {
190   struct gps_fix_t fix;
191   gint satellites_used;
192   gboolean dirty;   /* needs to be saved */
193 } GpsFix;
194 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
195
196 struct _VikGpsLayer {
197   VikLayer vl;
198   VikTrwLayer * trw_children[NUM_TRW];
199   GList * children;     /* used only for writing file */
200   int cur_read_child;   /* used only for reading file */
201 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
202   VglGpsd *vgpsd;
203   gboolean realtime_tracking;
204   GMutex *realtime_tracking_mutex;
205   GpsFix realtime_fix;
206   GpsFix last_fix;
207
208   enum unit realtime_gpsd_unit;
209
210   VikTrack *realtime_track;
211   gchar *realtime_track_name;
212
213   GIOChannel *realtime_io_channel;
214   guint realtime_io_watch_id;
215   GdkGC *realtime_track_gc;
216   GdkGC *realtime_track_bg_gc;
217   GdkGC *realtime_track_pt_gc;
218   GdkGC *realtime_track_pt1_gc;
219   GdkGC *realtime_track_pt2_gc;
220
221   /* params */
222   gchar *gpsd_host;
223   gchar *gpsd_port;
224   gboolean realtime_record;
225   gboolean realtime_jump_to_start;
226   gboolean realtime_keep_at_center;
227 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
228   guint protocol_id;
229   guint serial_port_id;
230 };
231
232 GType vik_gps_layer_get_type ()
233 {
234   static GType val_type = 0;
235
236   if (!val_type)
237   {
238     static const GTypeInfo val_info =
239     {
240       sizeof (VikGpsLayerClass),
241       NULL, /* base_init */
242       NULL, /* base_finalize */
243       NULL, /* class init */
244       NULL, /* class_finalize */
245       NULL, /* class_data */
246       sizeof (VikGpsLayer),
247       0,
248       NULL /* instance init */
249     };
250     val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGpsLayer", &val_info, 0 );
251   }
252
253   return val_type;
254 }
255
256 static VikGpsLayer *vik_gps_layer_create (VikViewport *vp)
257 {
258   int i;
259
260   VikGpsLayer *rv = vik_gps_layer_new (vp);
261   vik_layer_rename ( VIK_LAYER(rv), vik_gps_layer_interface.name );
262
263   for (i = 0; i < NUM_TRW; i++) {
264     rv->trw_children[i] = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vp, NULL, FALSE ));
265     vik_layer_set_menu_items_selection(VIK_LAYER(rv->trw_children[i]), VIK_MENU_ITEM_ALL & ~(VIK_MENU_ITEM_CUT|VIK_MENU_ITEM_DELETE));
266   }
267   return rv;
268 }
269
270 /* "Copy" */
271 static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *datalen )
272 {
273   VikLayer *child_layer;
274   guint8 *ld; 
275   gint ll;
276   GByteArray* b = g_byte_array_new ();
277   gint len;
278   gint i;
279
280 #define alm_append(obj, sz)     \
281   len = (sz);                   \
282   g_byte_array_append ( b, (guint8 *)&len, sizeof(len) );       \
283   g_byte_array_append ( b, (guint8 *)(obj), len );
284
285   vik_layer_marshall_params(VIK_LAYER(vgl), &ld, &ll);
286   alm_append(ld, ll);
287   g_free(ld);
288
289   for (i = 0; i < NUM_TRW; i++) {
290     child_layer = VIK_LAYER(vgl->trw_children[i]);
291     vik_layer_marshall(child_layer, &ld, &ll);
292     if (ld) {
293       alm_append(ld, ll);
294       g_free(ld);
295     }
296   }
297   *data = b->data;
298   *datalen = b->len;
299   g_byte_array_free(b, FALSE);
300 #undef alm_append
301 }
302
303 /* "Paste" */
304 static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
305 {
306 #define alm_size (*(gint *)data)
307 #define alm_next \
308   len -= sizeof(gint) + alm_size; \
309   data += sizeof(gint) + alm_size;
310   
311   VikGpsLayer *rv = vik_gps_layer_new();
312   VikLayer *child_layer;
313   gint i;
314
315   vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
316   alm_next;
317
318   i = 0;
319   while (len>0 && i < NUM_TRW) {
320     child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
321     if (child_layer) {
322       rv->trw_children[i++] = (VikTrwLayer *)child_layer;
323       g_signal_connect_swapped ( G_OBJECT(child_layer), "update", G_CALLBACK(vik_layer_emit_update_secondary), rv );
324     }
325     alm_next;
326   }
327   //  g_print("gps_layer_unmarshall ended with len=%d\n", len);
328   g_assert(len == 0);
329   return rv;
330 #undef alm_size
331 #undef alm_next
332 }
333
334 static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp )
335 {
336   switch ( id )
337   {
338     /* TODO: gpsd_host, gpsd_port */
339     case PARAM_PROTOCOL:
340       if (data.u < NUM_PROTOCOLS)
341         vgl->protocol_id = data.u;
342       else
343         g_warning(_("Unknown GPS Protocol"));
344       break;
345     case PARAM_PORT:
346       if (data.u < NUM_PORTS)
347         vgl->serial_port_id = data.u;
348       else
349         g_warning(_("Unknown serial port device"));
350       break;
351 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
352     case PARAM_GPSD_HOST:
353       if (vgl->gpsd_host)
354         g_free(vgl->gpsd_host);
355       vgl->gpsd_host = g_strdup(data.s);
356       break;
357     case PARAM_GPSD_PORT:
358       if (vgl->gpsd_port)
359         g_free(vgl->gpsd_port);
360       vgl->gpsd_port = g_strdup(data.s); /* TODO: check for number */
361       break;
362     case PARAM_REALTIME_REC:
363       vgl->realtime_record = data.b;
364       break;
365     case PARAM_REALTIME_CENTER_START:
366       vgl->realtime_jump_to_start = data.b;
367       break;
368     case PARAM_REALTIME_CENTERED:
369       vgl->realtime_keep_at_center = data.b;
370       break;
371 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
372     default:
373       g_warning("gps_layer_set_param(): unknown parameter");
374   }
375
376   return TRUE;
377 }
378
379 static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id )
380 {
381   VikLayerParamData rv;
382   switch ( id )
383   {
384     /* TODO: gpsd_host, gpsd_port */
385     case PARAM_PROTOCOL:
386       rv.u = vgl->protocol_id;
387       break;
388     case PARAM_PORT:
389       rv.u = vgl->serial_port_id;
390       break;
391 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
392     case PARAM_GPSD_HOST:
393       rv.s = vgl->gpsd_host ? vgl->gpsd_host : "";
394       break;
395     case PARAM_GPSD_PORT:
396       rv.s = vgl->gpsd_port ? vgl->gpsd_port : g_strdup(DEFAULT_GPSD_PORT);
397       break;
398     case PARAM_REALTIME_REC:
399       rv.b = vgl->realtime_record;
400       break;
401     case PARAM_REALTIME_CENTER_START:
402       rv.b = vgl->realtime_jump_to_start;
403       break;
404     case PARAM_REALTIME_CENTERED:
405       rv.b = vgl->realtime_keep_at_center;
406       break;
407 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
408     default:
409       g_warning(_("%s: unknown parameter"), __FUNCTION__);
410   }
411
412   return rv;
413 }
414
415 VikGpsLayer *vik_gps_layer_new (VikViewport *vp)
416 {
417   gint i;
418   VikGpsLayer *vgl = VIK_GPS_LAYER ( g_object_new ( VIK_GPS_LAYER_TYPE, NULL ) );
419   vik_layer_init ( VIK_LAYER(vgl), VIK_LAYER_GPS );
420   for (i = 0; i < NUM_TRW; i++) {
421     vgl->trw_children[i] = NULL;
422   }
423   vgl->children = NULL;
424   vgl->cur_read_child = 0;
425
426 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
427   vgl->realtime_tracking_mutex = g_mutex_new();
428   vgl->realtime_tracking = FALSE;
429   vgl->vgpsd = NULL;
430   vgl->realtime_track_gc = vik_viewport_new_gc ( vp, "#203070", 2 );
431   vgl->realtime_track_bg_gc = vik_viewport_new_gc ( vp, "grey", 2 );
432   vgl->realtime_track_pt1_gc = vik_viewport_new_gc ( vp, "red", 2 );
433   vgl->realtime_track_pt2_gc = vik_viewport_new_gc ( vp, "green", 2 );
434   vgl->realtime_track_pt_gc = vgl->realtime_track_pt1_gc;
435   vgl->realtime_gpsd_unit = gpsd_units();
436   // fprintf(stderr, "DEBUG: gpsd_unit = %d\n", vgl->realtime_gpsd_unit);
437   vgl->realtime_track = NULL;
438
439   /* Setting params here */
440   vgl->gpsd_host = g_strdup("localhost");
441   vgl->gpsd_port = g_strdup(DEFAULT_GPSD_PORT);
442   vgl->realtime_record = TRUE;
443   vgl->realtime_jump_to_start = TRUE;
444   vgl->realtime_keep_at_center = FALSE;
445 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
446   vgl->protocol_id = 0;
447   vgl->serial_port_id = 0;
448
449   return vgl;
450 }
451
452 static void vik_gps_layer_draw ( VikGpsLayer *vgl, gpointer data )
453 {
454   gint i;
455   VikLayer *vl;
456   VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( VIK_VIEWPORT(data) ));
457
458   for (i = 0; i < NUM_TRW; i++) {
459     vl = VIK_LAYER(vgl->trw_children[i]);
460     if (vl == trigger) {
461       if ( vik_viewport_get_half_drawn ( VIK_VIEWPORT(data) ) ) {
462         vik_viewport_set_half_drawn ( VIK_VIEWPORT(data), FALSE );
463         vik_viewport_snapshot_load( VIK_VIEWPORT(data) );
464       } else {
465         vik_viewport_snapshot_save( VIK_VIEWPORT(data) );
466       }
467     }
468     if (!vik_viewport_get_half_drawn( VIK_VIEWPORT(data)))
469       vik_layer_draw ( vl, data );
470   }
471 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
472   if (vgl->realtime_tracking) {
473     if (VIK_LAYER(vgl) == trigger) {
474       if ( vik_viewport_get_half_drawn ( VIK_VIEWPORT(data) ) ) {
475         vik_viewport_set_half_drawn ( VIK_VIEWPORT(data), FALSE );
476         vik_viewport_snapshot_load( VIK_VIEWPORT(data) );
477       } else {
478         vik_viewport_snapshot_save( VIK_VIEWPORT(data) );
479       }
480     }
481     if (!vik_viewport_get_half_drawn( VIK_VIEWPORT(data)))
482       realtime_tracking_draw(vgl, VIK_VIEWPORT(data));
483   }
484 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
485 }
486
487 static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode )
488 {
489   gint i;
490   for (i = 0; i < NUM_TRW; i++) {
491     vik_layer_change_coord_mode(VIK_LAYER(vgl->trw_children[i]), mode);
492   }
493 }
494
495 static void gps_layer_add_menu_items( VikGpsLayer *vgl, GtkMenu *menu, gpointer vlp )
496 {
497   static gpointer pass_along[2];
498   GtkWidget *item;
499   pass_along[0] = vgl;
500   pass_along[1] = vlp;
501
502   item = gtk_menu_item_new();
503   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
504   gtk_widget_show ( item );
505
506   item = gtk_menu_item_new_with_label ( _("Upload to GPS") );
507   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_upload_cb), pass_along );
508   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
509   gtk_widget_show ( item );
510
511   item = gtk_menu_item_new_with_label ( _("Download from GPS") );
512   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_download_cb), pass_along );
513   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
514   gtk_widget_show ( item );
515
516 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
517   item = gtk_menu_item_new_with_label ( vgl->realtime_tracking  ?
518                                        "Stop realtime tracking" :
519                                        "Start realtime tracking" );
520   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_start_stop_tracking_cb), pass_along );
521   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
522   gtk_widget_show ( item );
523
524   item = gtk_menu_item_new();
525   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
526   gtk_widget_show ( item );
527 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
528
529   item = gtk_menu_item_new_with_label ( _("Empty Upload") );
530   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_upload_cb), pass_along );
531   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
532   gtk_widget_show ( item );
533
534   item = gtk_menu_item_new_with_label ( _("Empty Download") );
535   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_download_cb), pass_along );
536   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
537   gtk_widget_show ( item );
538
539   item = gtk_menu_item_new_with_label ( _("Empty All") );
540   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_all_cb), pass_along );
541   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
542   gtk_widget_show ( item );
543
544 }
545
546 static void disconnect_layer_signal ( VikLayer *vl, VikGpsLayer *vgl )
547 {
548   g_assert(DISCONNECT_UPDATE_SIGNAL(vl,vgl)==1);
549 }
550
551 static void vik_gps_layer_free ( VikGpsLayer *vgl )
552 {
553   gint i;
554   for (i = 0; i < NUM_TRW; i++) {
555     if (vgl->vl.realized)
556       disconnect_layer_signal(VIK_LAYER(vgl->trw_children[i]), vgl);
557     g_object_unref(vgl->trw_children[i]);
558   }
559 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
560   if (vgl->realtime_track_gc != NULL)
561     g_object_unref(vgl->realtime_track_gc);
562   if (vgl->realtime_track_bg_gc != NULL)
563     g_object_unref(vgl->realtime_track_bg_gc);
564   if (vgl->realtime_track_pt1_gc != NULL)
565     g_object_unref(vgl->realtime_track_pt1_gc);
566   if (vgl->realtime_track_pt2_gc != NULL)
567     g_object_unref(vgl->realtime_track_pt2_gc);
568   if (vgl->realtime_tracking_mutex != NULL)
569    g_mutex_free(vgl->realtime_tracking_mutex);
570 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
571 }
572
573 gboolean vik_gps_layer_delete ( VikGpsLayer *vgl, GtkTreeIter *iter )
574 {
575   gint i;
576   VikLayer *l = VIK_LAYER( vik_treeview_item_get_pointer ( VIK_LAYER(vgl)->vt, iter ) );
577   gboolean was_visible = l->visible;
578
579   vik_treeview_item_delete ( VIK_LAYER(vgl)->vt, iter );
580   for (i = 0; i < NUM_TRW; i++) {
581     if (VIK_LAYER(vgl->trw_children[i]) == l)
582       vgl->trw_children[i] = NULL;
583   }
584   g_assert(DISCONNECT_UPDATE_SIGNAL(l,vgl)==1);
585   g_object_unref ( l );
586
587   return was_visible;
588 }
589
590 static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter )
591 {
592   GtkTreeIter iter;
593   int ix;
594
595   for (ix = 0; ix < NUM_TRW; ix++) {
596     VikLayer * trw = VIK_LAYER(vgl->trw_children[ix]);
597     vik_treeview_add_layer ( VIK_LAYER(vgl)->vt, layer_iter, &iter,
598         _(trw_names[ix]), vgl, 
599         trw, trw->type, trw->type );
600     if ( ! trw->visible )
601       vik_treeview_item_set_visible ( VIK_LAYER(vgl)->vt, &iter, FALSE );
602     vik_layer_realize ( trw, VIK_LAYER(vgl)->vt, &iter );
603     g_signal_connect_swapped ( G_OBJECT(trw), "update", G_CALLBACK(vik_layer_emit_update_secondary), vgl );
604   }
605 }
606
607 const GList *vik_gps_layer_get_children ( VikGpsLayer *vgl )
608 {
609   int i;
610
611   if (vgl->children == NULL) {
612     for (i = NUM_TRW - 1; i >= 0; i--)
613       vgl->children = g_list_prepend(vgl->children, vgl->trw_children[i]);
614   }
615   return vgl->children;
616 }
617
618 VikTrwLayer * vik_gps_layer_get_a_child(VikGpsLayer *vgl)
619 {
620   g_assert ((vgl->cur_read_child >= 0) && (vgl->cur_read_child < NUM_TRW));
621
622   VikTrwLayer * vtl = vgl->trw_children[vgl->cur_read_child];
623   if (++(vgl->cur_read_child) >= NUM_TRW)
624     vgl->cur_read_child = 0;
625   return(vtl);
626 }
627
628 gboolean vik_gps_layer_is_empty ( VikGpsLayer *vgl )
629 {
630   if ( vgl->trw_children[0] )
631     return FALSE;
632   return TRUE;
633 }
634
635 static void gps_layer_drag_drop_request ( VikGpsLayer *val_src, VikGpsLayer *val_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
636 {
637   VikTreeview *vt = VIK_LAYER(val_src)->vt;
638   VikLayer *vl = vik_treeview_item_get_pointer(vt, src_item_iter);
639   GtkTreeIter dest_iter;
640   gchar *dp;
641   gboolean target_exists;
642
643   dp = gtk_tree_path_to_string(dest_path);
644   target_exists = vik_treeview_get_iter_from_path_str(vt, &dest_iter, dp);
645
646   /* vik_gps_layer_delete unrefs, but we don't want that here.
647    * we're still using the layer. */
648   g_object_ref ( vl );
649   vik_gps_layer_delete(val_src, src_item_iter);
650
651   g_free(dp);
652 }
653
654 static void gps_session_delete(GpsSession *sess)
655 {
656   /* TODO */
657   g_mutex_free(sess->mutex);
658   g_free(sess->cmd_args);
659
660   g_free(sess);
661
662 }
663
664 static void set_total_count(gint cnt, GpsSession *sess)
665 {
666   gchar s[128];
667   gdk_threads_enter();
668   g_mutex_lock(sess->mutex);
669   if (sess->ok) {
670     const gchar *tmp_str;
671     if (sess->direction == GPS_DOWN)
672     {
673       if (sess->progress_label == sess->wp_label)
674         tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt);
675       else
676         tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt);
677     }
678     else 
679     {
680       if (sess->progress_label == sess->wp_label)
681         tmp_str = ngettext("Uploading %d waypoint...", "Uploading %d waypoints...", cnt);
682       else
683         tmp_str = ngettext("Uploading %d trackpoint...", "Uploading %d trackpoints...", cnt);
684     }
685
686     g_snprintf(s, 128, tmp_str, cnt);
687     gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
688     gtk_widget_show ( sess->progress_label );
689     sess->total_count = cnt;
690   }
691   g_mutex_unlock(sess->mutex);
692   gdk_threads_leave();
693 }
694
695 static void set_current_count(gint cnt, GpsSession *sess)
696 {
697   gchar s[128];
698   const gchar *tmp_str;
699
700   gdk_threads_enter();
701   g_mutex_lock(sess->mutex);
702   if (sess->ok) {
703     if (cnt < sess->total_count) {
704       if (sess->direction == GPS_DOWN)
705       {
706         if (sess->progress_label == sess->wp_label)
707           tmp_str = ngettext("Downloaded %d out of %d waypoint...", "Downloaded %d out of %d waypoints...", sess->total_count);
708         else
709           tmp_str = ngettext("Downloaded %d out of %d trackpoint...", "Downloaded %d out of %d trackpoints...", sess->total_count);
710       }
711       else {
712         if (sess->progress_label == sess->wp_label)
713           tmp_str = ngettext("Uploaded %d out of %d waypoint...", "Uploaded %d out of %d waypoints...", sess->total_count);
714         else
715           tmp_str = ngettext("Uploaded %d out of %d trackpoint...", "Uploaded %d out of %d trackpoints...", sess->total_count);
716       }
717       g_snprintf(s, 128, tmp_str, cnt, sess->total_count);
718     } else {
719       if (sess->direction == GPS_DOWN)
720       {
721         if (sess->progress_label == sess->wp_label)
722           tmp_str = ngettext("Downloaded %d waypoint", "Downloaded %d waypoints", cnt);
723         else
724           tmp_str = ngettext("Downloaded %d trackpoint", "Downloaded %d trackpoints", cnt);
725       }
726       else {
727         if (sess->progress_label == sess->wp_label)
728           tmp_str = ngettext("Uploaded %d waypoint", "Uploaded %d waypoints", cnt);
729         else
730           tmp_str = ngettext("Uploaded %d trackpoint", "Uploaded %d trackpoints", cnt);
731       }
732       g_snprintf(s, 128, tmp_str, cnt);
733     }     
734     gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
735   }
736   g_mutex_unlock(sess->mutex);
737   gdk_threads_leave();
738 }
739
740 static void set_gps_info(const gchar *info, GpsSession *sess)
741 {
742   gchar s[256];
743   gdk_threads_enter();
744   g_mutex_lock(sess->mutex);
745   if (sess->ok) {
746     g_snprintf(s, 256, _("GPS Device: %s"), info);
747     gtk_label_set_text ( GTK_LABEL(sess->gps_label), s );
748   }
749   g_mutex_unlock(sess->mutex);
750   gdk_threads_leave();
751 }
752
753 static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
754 {
755   gchar *line;
756
757   gdk_threads_enter ();
758   g_mutex_lock(sess->mutex);
759   if (!sess->ok) {
760     g_mutex_unlock(sess->mutex);
761     gps_session_delete(sess);
762     gdk_threads_leave();
763     g_thread_exit ( NULL );
764   }
765   g_mutex_unlock(sess->mutex);
766   gdk_threads_leave ();
767
768   switch(c) {
769   case BABEL_DIAG_OUTPUT:
770     line = (gchar *)data;
771
772     /* tells us how many items there will be */
773     if (strstr(line, "Xfer Wpt")) { 
774       sess->progress_label = sess->wp_label;
775     }
776     if (strstr(line, "Xfer Trk")) { 
777       sess->progress_label = sess->trk_label;
778     }
779     if (strstr(line, "PRDDAT")) {
780       gchar **tokens = g_strsplit(line, " ", 0);
781       gchar info[128];
782       int ilen = 0;
783       int i;
784       int n_tokens = 0;
785
786       while (tokens[n_tokens])
787         n_tokens++;
788
789       if (n_tokens > 8) {
790         for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
791           guint ch;
792           sscanf(tokens[i], "%x", &ch);
793           info[ilen++] = ch;
794         }
795         info[ilen++] = 0;
796         set_gps_info(info, sess);
797       }
798       g_strfreev(tokens);
799     }
800     if (strstr(line, "RECORD")) { 
801       int lsb, msb, cnt;
802
803       if (strlen(line) > 20) {
804         sscanf(line+17, "%x", &lsb); 
805         sscanf(line+20, "%x", &msb);
806         cnt = lsb + msb * 256;
807         set_total_count(cnt, sess);
808         sess->count = 0;
809       }
810     }
811     if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
812       sess->count++;
813       set_current_count(sess->count, sess);
814     }
815     break;
816   case BABEL_DONE:
817     break;
818   default:
819     break;
820   }
821
822 }
823
824 static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
825 {
826   gchar *line;
827   static int cnt = 0;
828
829   gdk_threads_enter ();
830   g_mutex_lock(sess->mutex);
831   if (!sess->ok) {
832     g_mutex_unlock(sess->mutex);
833     gps_session_delete(sess);
834     gdk_threads_leave();
835     g_thread_exit ( NULL );
836   }
837   g_mutex_unlock(sess->mutex);
838   gdk_threads_leave ();
839
840   switch(c) {
841   case BABEL_DIAG_OUTPUT:
842     line = (gchar *)data;
843
844     if (strstr(line, "PRDDAT")) {
845       gchar **tokens = g_strsplit(line, " ", 0);
846       gchar info[128];
847       int ilen = 0;
848       int i;
849       int n_tokens = 0;
850
851       while (tokens[n_tokens])
852         n_tokens++;
853
854       if (n_tokens > 8) {
855         for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
856           guint ch;
857           sscanf(tokens[i], "%x", &ch);
858           info[ilen++] = ch;
859         }
860         info[ilen++] = 0;
861         set_gps_info(info, sess);
862       }
863       g_strfreev(tokens);
864     }
865     if (strstr(line, "RECORD")) { 
866       int lsb, msb;
867
868       if (strlen(line) > 20) {
869         sscanf(line+17, "%x", &lsb); 
870         sscanf(line+20, "%x", &msb);
871         cnt = lsb + msb * 256;
872         /* set_total_count(cnt, sess); */
873         sess->count = 0;
874       }
875     }
876     if ( strstr(line, "WPTDAT")) {
877       if (sess->count == 0) {
878         sess->progress_label = sess->wp_label;
879         set_total_count(cnt, sess);
880       }
881       sess->count++;
882       set_current_count(sess->count, sess);
883
884     }
885     if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
886       if (sess->count == 0) {
887         sess->progress_label = sess->trk_label;
888         set_total_count(cnt, sess);
889       }
890       sess->count++;
891       set_current_count(sess->count, sess);
892     }
893     break;
894   case BABEL_DONE:
895     break;
896   default:
897     break;
898   }
899
900 }
901
902 static void gps_comm_thread(GpsSession *sess)
903 {
904   gboolean result;
905
906   if (sess->direction == GPS_DOWN)
907     result = a_babel_convert_from (sess->vtl, sess->cmd_args,
908         (BabelStatusFunc) gps_download_progress_func, sess->port, sess);
909   else
910     result = a_babel_convert_to (sess->vtl, sess->cmd_args,
911         (BabelStatusFunc) gps_upload_progress_func, sess->port, sess);
912
913   gdk_threads_enter();
914   if (!result) {
915     gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: couldn't find gpsbabel.") );
916   } 
917   else {
918     g_mutex_lock(sess->mutex);
919     if (sess->ok) {
920       gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Done.") );
921       gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE );
922       gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE );
923     } else {
924       /* canceled */
925     }
926     g_mutex_unlock(sess->mutex);
927   }
928
929   g_mutex_lock(sess->mutex);
930   if (sess->ok) {
931     sess->ok = FALSE;
932     g_mutex_unlock(sess->mutex);
933   }
934   else {
935     g_mutex_unlock(sess->mutex);
936     gps_session_delete(sess);
937   }
938   gdk_threads_leave();
939   g_thread_exit(NULL);
940 }
941
942 static gint gps_comm(VikTrwLayer *vtl, gps_dir dir, vik_gps_proto proto, gchar *port) {
943   GpsSession *sess = g_malloc(sizeof(GpsSession));
944
945   sess->mutex = g_mutex_new();
946   sess->direction = dir;
947   sess->vtl = vtl;
948   sess->port = g_strdup(port);
949   sess->ok = TRUE;
950   sess->cmd_args = g_strdup_printf("-D 9 -t -w -%c %s",
951       (dir == GPS_DOWN) ? 'i' : 'o', protocols_args[proto]);
952   sess->window_title = (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload");
953
954   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 );
955   gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog),
956       GTK_RESPONSE_ACCEPT, FALSE );
957   gtk_window_set_title ( GTK_WINDOW(sess->dialog), sess->window_title );
958
959   sess->status_label = gtk_label_new (_("Status: detecting gpsbabel"));
960   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(sess->dialog)->vbox),
961       sess->status_label, FALSE, FALSE, 5 );
962   gtk_widget_show_all(sess->status_label);
963
964   sess->gps_label = gtk_label_new (_("GPS device: N/A"));
965   sess->ver_label = gtk_label_new ("");
966   sess->id_label = gtk_label_new ("");
967   sess->wp_label = gtk_label_new ("");
968   sess->trk_label = gtk_label_new ("");
969
970   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(sess->dialog)->vbox), sess->gps_label, FALSE, FALSE, 5 );
971   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(sess->dialog)->vbox), sess->wp_label, FALSE, FALSE, 5 );
972   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(sess->dialog)->vbox), sess->trk_label, FALSE, FALSE, 5 );
973
974   gtk_widget_show_all(sess->dialog);
975
976   sess->progress_label = sess->wp_label;
977   sess->total_count = -1;
978
979   /* TODO: starting gps read/write thread here */
980   g_thread_create((GThreadFunc)gps_comm_thread, sess, FALSE, NULL );
981
982   gtk_dialog_run(GTK_DIALOG(sess->dialog));
983
984   gtk_widget_destroy(sess->dialog);
985
986   g_mutex_lock(sess->mutex);
987   if (sess->ok) {
988     sess->ok = FALSE;   /* tell thread to stop */
989     g_mutex_unlock(sess->mutex);
990   }
991   else {
992     g_mutex_unlock(sess->mutex);
993     gps_session_delete(sess);
994   }
995
996   return 0;
997 }
998
999 static void gps_upload_cb( gpointer layer_and_vlp[2] )
1000 {
1001   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1002   VikTrwLayer *vtl = vgl->trw_children[TRW_UPLOAD];
1003   gps_comm(vtl, GPS_UP, vgl->protocol_id, params_ports[vgl->serial_port_id]);
1004 }
1005
1006 static void gps_download_cb( gpointer layer_and_vlp[2] )
1007 {
1008   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1009   VikTrwLayer *vtl = vgl->trw_children[TRW_DOWNLOAD];
1010   gps_comm(vtl, GPS_DOWN, vgl->protocol_id, params_ports[vgl->serial_port_id]);
1011 }
1012
1013 static void gps_empty_upload_cb( gpointer layer_and_vlp[2] )
1014 {
1015   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1016   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1017   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1018 }
1019
1020 static void gps_empty_download_cb( gpointer layer_and_vlp[2] )
1021 {
1022   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1023   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1024   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1025 }
1026
1027 static void gps_empty_all_cb( gpointer layer_and_vlp[2] )
1028 {
1029   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1030   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1031   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1032   vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1033   vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1034 }
1035
1036 #ifdef VIK_CONFIG_REALTIME_GPS_TRACKING
1037 static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp)
1038 {
1039   struct LatLon ll;
1040   VikCoord nw, se;
1041   struct LatLon lnw, lse;
1042   vik_viewport_screen_to_coord ( vp, -20, -20, &nw );
1043   vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp)+20, vik_viewport_get_width(vp)+20, &se );
1044   vik_coord_to_latlon ( &nw, &lnw );
1045   vik_coord_to_latlon ( &se, &lse );
1046   if ( vgl->realtime_fix.fix.latitude > lse.lat &&
1047        vgl->realtime_fix.fix.latitude < lnw.lat &&
1048        vgl->realtime_fix.fix.longitude > lnw.lon &&
1049        vgl->realtime_fix.fix.longitude < lse.lon ) {
1050     VikCoord gps;
1051     gint x, y;
1052     gint half_back_x, half_back_y;
1053     gint half_back_bg_x, half_back_bg_y;
1054     gint pt_x, pt_y;
1055     gint ptbg_x, ptbg_y;
1056     gint side1_x, side1_y, side2_x, side2_y;
1057     gint side1bg_x, side1bg_y, side2bg_x, side2bg_y;
1058
1059     ll.lat = vgl->realtime_fix.fix.latitude;
1060     ll.lon = vgl->realtime_fix.fix.longitude;
1061     vik_coord_load_from_latlon ( &gps, vik_viewport_get_coord_mode(vp), &ll);
1062     vik_viewport_coord_to_screen ( vp, &gps, &x, &y );
1063
1064     gdouble heading_cos = cos(M_PI/180*vgl->realtime_fix.fix.track);
1065     gdouble heading_sin = sin(M_PI/180*vgl->realtime_fix.fix.track);
1066
1067     half_back_y = y+8*heading_cos;
1068     half_back_x = x-8*heading_sin;
1069     half_back_bg_y = y+10*heading_cos;
1070     half_back_bg_x = x-10*heading_sin;
1071
1072     pt_y = half_back_y-24*heading_cos;
1073     pt_x = half_back_x+24*heading_sin;
1074     ptbg_y = half_back_bg_y-28*heading_cos;
1075     ptbg_x = half_back_bg_x+28*heading_sin;
1076
1077     side1_y = half_back_y+9*heading_sin;
1078     side1_x = half_back_x+9*heading_cos;
1079     side1bg_y = half_back_bg_y+11*heading_sin;
1080     side1bg_x = half_back_bg_x+11*heading_cos;
1081
1082     side2_y = half_back_y-9*heading_sin;
1083     side2_x = half_back_x-9*heading_cos;
1084     side2bg_y = half_back_bg_y-11*heading_sin;
1085     side2bg_x = half_back_bg_x-11*heading_cos;
1086
1087      GdkPoint trian[3] = { { pt_x, pt_y }, {side1_x, side1_y}, {side2_x, side2_y} };
1088      GdkPoint trian_bg[3] = { { ptbg_x, pt_y }, {side1bg_x, side1bg_y}, {side2bg_x, side2bg_y} };
1089
1090      vik_viewport_draw_polygon ( vp, vgl->realtime_track_bg_gc, TRUE, trian_bg, 3 );
1091      vik_viewport_draw_polygon ( vp, vgl->realtime_track_gc, TRUE, trian, 3 );
1092      vik_viewport_draw_rectangle ( vp,
1093          (vgl->realtime_fix.fix.mode > MODE_2D) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc,
1094          TRUE, x-2, y-2, 4, 4 );
1095      //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;
1096   }
1097 }
1098
1099 /* lock/unlock realtime_tracking_mutex when call this */
1100 static void create_realtime_trackpoint(VikGpsLayer *vgl, gboolean forced)
1101 {
1102     struct LatLon ll;
1103     GList *last_tp;
1104
1105     /* Note that fix.time is a double, but it should not affect the precision
1106        for most GPS */
1107     time_t cur_timestamp = vgl->realtime_fix.fix.time;
1108     time_t last_timestamp = vgl->last_fix.fix.time;
1109
1110     if (cur_timestamp < last_timestamp) {
1111       return;
1112     }
1113
1114     if (vgl->realtime_record && vgl->realtime_fix.dirty) {
1115       gboolean replace = FALSE;
1116       int heading = (int)floor(vgl->realtime_fix.fix.track);
1117       int last_heading = (int)floor(vgl->last_fix.fix.track);
1118       int alt = isnan(vgl->realtime_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->realtime_fix.fix.altitude);
1119       int last_alt = isnan(vgl->last_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->last_fix.fix.altitude);
1120       if (((last_tp = g_list_last(vgl->realtime_track->trackpoints)) != NULL) &&
1121           (vgl->realtime_fix.fix.mode > MODE_2D) &&
1122           (vgl->last_fix.fix.mode <= MODE_2D) &&
1123           ((cur_timestamp - last_timestamp) < 2)) {
1124         g_free(last_tp->data);
1125         vgl->realtime_track->trackpoints = g_list_delete_link(vgl->realtime_track->trackpoints, last_tp);
1126         replace = TRUE;
1127       }
1128       if (replace ||
1129           ((cur_timestamp != last_timestamp) &&
1130           ((forced || 
1131             ((heading < last_heading) && (heading < (last_heading - 3))) || 
1132             ((heading > last_heading) && (heading > (last_heading + 3))) ||
1133             ((alt != VIK_DEFAULT_ALTITUDE) && (alt != last_alt)))))) {
1134         /* TODO: check for new segments */
1135         VikTrackpoint *tp = vik_trackpoint_new();
1136         tp->newsegment = FALSE;
1137         tp->has_timestamp = TRUE;
1138         tp->timestamp = vgl->realtime_fix.fix.time;
1139         tp->altitude = alt;
1140         /* speed only available for 3D fix. Check for NAN when use this speed */
1141         tp->speed = vgl->realtime_fix.fix.speed;  
1142         tp->course = vgl->realtime_fix.fix.track;
1143         tp->nsats = vgl->realtime_fix.satellites_used;
1144         tp->fix_mode = vgl->realtime_fix.fix.mode;
1145         tp->extended = TRUE;
1146
1147         ll.lat = vgl->realtime_fix.fix.latitude;
1148         ll.lon = vgl->realtime_fix.fix.longitude;
1149         vik_coord_load_from_latlon(&tp->coord,
1150              vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1151
1152         vgl->realtime_track->trackpoints = g_list_append(vgl->realtime_track->trackpoints, tp);
1153         vgl->realtime_fix.dirty = FALSE;
1154         vgl->realtime_fix.satellites_used = 0;
1155         vgl->last_fix = vgl->realtime_fix;
1156       }
1157     }
1158
1159 }
1160
1161 void gpsd_raw_hook(VglGpsd *vgpsd, gchar *data)
1162 {
1163   gboolean update_all = FALSE;
1164   VikGpsLayer *vgl = vgpsd->vgl;
1165
1166   if (!vgl->realtime_tracking) {
1167     g_warning("%s: receiving GPS data while not in realtime mode", __PRETTY_FUNCTION__);
1168     return;
1169   }
1170
1171   if ((vgpsd->gpsd.fix.mode >= MODE_2D) &&
1172       !isnan(vgpsd->gpsd.fix.latitude) &&
1173       !isnan(vgpsd->gpsd.fix.longitude) &&
1174       !isnan(vgpsd->gpsd.fix.track)) {
1175     g_mutex_lock(vgl->realtime_tracking_mutex);
1176     vgl->realtime_fix.fix = vgpsd->gpsd.fix;
1177     vgl->realtime_fix.satellites_used = vgpsd->gpsd.satellites_used;
1178     vgl->realtime_fix.dirty = TRUE;
1179
1180     if (vgl->realtime_keep_at_center ||
1181         (vgl->realtime_jump_to_start && !vgl->realtime_track->trackpoints)) {
1182       struct LatLon ll;
1183       VikCoord center;
1184
1185       ll.lat = vgl->realtime_fix.fix.latitude;
1186       ll.lon = vgl->realtime_fix.fix.longitude;
1187       vik_coord_load_from_latlon(&center,
1188              vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1189       VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1190       VikViewport *vvp = vik_window_viewport(vw);
1191       vik_viewport_set_center_coord(vvp, &center);
1192       update_all = TRUE;
1193     }
1194
1195     create_realtime_trackpoint(vgl, FALSE);
1196     g_mutex_unlock(vgl->realtime_tracking_mutex);
1197
1198     vik_layer_emit_update ( update_all ? VIK_LAYER(vgl) : VIK_LAYER(vgl->trw_children[TRW_REALTIME]));
1199   }
1200 }
1201
1202 static gboolean gpsd_data_available(GIOChannel *source, GIOCondition condition, gpointer data)
1203 {
1204   VikGpsLayer *vgl = data;
1205   gps_poll(&vgl->vgpsd->gpsd);
1206   return TRUE;
1207 }
1208
1209 static gchar *make_track_name(VikTrwLayer *vtl)
1210 {
1211   const gchar basename[] = "REALTIME";
1212   const gint bufsize = sizeof(basename) + 5;
1213   gchar *name = g_malloc(bufsize);
1214   strcpy(name, basename);
1215   gint i = 2;
1216
1217   while (vik_trw_layer_get_track(vtl, name) != NULL) {
1218     g_snprintf(name, bufsize, "%s#%d", basename, i);
1219     i++;
1220   }
1221   return(name);
1222
1223 }
1224
1225 static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2])
1226 {
1227   VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1228   vgl->realtime_tracking = (vgl->realtime_tracking == FALSE);
1229
1230   /* Make sure we are still in the boat with libgps */
1231   g_assert((VIK_GPS_MODE_2D == MODE_2D) && (VIK_GPS_MODE_3D == MODE_3D));
1232
1233   if (vgl->realtime_tracking) {
1234     struct gps_data_t *gpsd = gps_open(vgl->gpsd_host, vgl->gpsd_port);
1235     if (gpsd == NULL) {
1236       GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER(vgl),
1237                                   GTK_DIALOG_DESTROY_WITH_PARENT,
1238                                   GTK_MESSAGE_ERROR,
1239                                   GTK_BUTTONS_CLOSE,
1240                                   "Failed to connect to gpsd at %s (port %s)",
1241                                   vgl->gpsd_host, vgl->gpsd_port);
1242       gtk_dialog_run (GTK_DIALOG (dialog));
1243       gtk_widget_destroy (dialog);
1244
1245       vgl->realtime_tracking = FALSE;
1246       return;
1247     }
1248     vgl->vgpsd = g_realloc(gpsd, sizeof(VglGpsd));
1249     vgl->vgpsd->vgl = vgl;
1250
1251     vgl->realtime_fix.dirty = vgl->last_fix.dirty = FALSE;
1252     /* track alt/time graph uses VIK_DEFAULT_ALTITUDE (0.0) as invalid */
1253     vgl->realtime_fix.fix.altitude = vgl->last_fix.fix.altitude = VIK_DEFAULT_ALTITUDE;
1254     vgl->realtime_fix.fix.speed = vgl->last_fix.fix.speed = NAN;
1255
1256     if (vgl->realtime_record) {
1257       VikTrwLayer *vtl = vgl->trw_children[TRW_REALTIME];
1258       vgl->realtime_track = vik_track_new();
1259       vgl->realtime_track->visible = TRUE;
1260       vgl->realtime_track_name = make_track_name(vtl);
1261       vik_trw_layer_add_track(vtl, vgl->realtime_track_name, vgl->realtime_track);
1262     }
1263
1264     gps_set_raw_hook(&vgl->vgpsd->gpsd, gpsd_raw_hook);
1265     vgl->realtime_io_channel = g_io_channel_unix_new(vgl->vgpsd->gpsd.gps_fd);
1266     vgl->realtime_io_watch_id = g_io_add_watch( vgl->realtime_io_channel,
1267                                            G_IO_IN, gpsd_data_available, vgl);
1268
1269     gps_query(&vgl->vgpsd->gpsd, "w+x");
1270
1271   }
1272   else {  /* stop realtime tracking */
1273     /* TODO: handle race condition here , make sure vgpsd is NULL */
1274     g_mutex_lock(vgl->realtime_tracking_mutex);
1275     gps_close(&vgl->vgpsd->gpsd);
1276     vgl->vgpsd = NULL;
1277     // g_source_remove(vgl->realtime_timeout);
1278     g_source_remove(vgl->realtime_io_watch_id);
1279     g_io_channel_unref (vgl->realtime_io_channel);
1280
1281     if (vgl->realtime_record) {
1282       create_realtime_trackpoint(vgl, TRUE);
1283       if ((vgl->realtime_track->trackpoints == NULL) || (vgl->realtime_track->trackpoints->next == NULL))
1284         vik_trw_layer_delete_track(vgl->trw_children[TRW_REALTIME], vgl->realtime_track_name);
1285     }
1286     g_mutex_unlock(vgl->realtime_tracking_mutex);
1287   }
1288 }
1289 #endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
1290