]> git.street.me.uk Git - andy/viking.git/blame_incremental - src/vikgpslayer.c
Add TIFF as an image file type that gets listed in the applied filter.
[andy/viking.git] / src / vikgpslayer.c
... / ...
CommitLineData
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
49static VikGpsLayer *vik_gps_layer_create (VikViewport *vp);
50static void vik_gps_layer_realize ( VikGpsLayer *val, VikTreeview *vt, GtkTreeIter *layer_iter );
51static void vik_gps_layer_free ( VikGpsLayer *val );
52static void vik_gps_layer_draw ( VikGpsLayer *val, VikViewport *vp );
53static VikGpsLayer *vik_gps_layer_new ( VikViewport *vp );
54
55static void gps_layer_marshall( VikGpsLayer *val, guint8 **data, gint *len );
56static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
57static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
58static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation );
59
60static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl );
61
62static void gps_layer_change_coord_mode ( VikGpsLayer *val, VikCoordMode mode );
63static void gps_layer_add_menu_items( VikGpsLayer *vtl, GtkMenu *menu, gpointer vlp );
64
65static void gps_upload_cb( gpointer layer_and_vlp[2] );
66static void gps_download_cb( gpointer layer_and_vlp[2] );
67static void gps_empty_upload_cb( gpointer layer_and_vlp[2] );
68static void gps_empty_download_cb( gpointer layer_and_vlp[2] );
69static void gps_empty_all_cb( gpointer layer_and_vlp[2] );
70#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
71static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] );
72static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2] );
73static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp);
74static void rt_gpsd_disconnect(VikGpsLayer *vgl);
75static 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
80typedef enum {GARMIN_P = 0, MAGELLAN_P, DELORME_P, NAVILINK_P, OLD_NUM_PROTOCOLS} vik_gps_proto;
81static gchar * protocols_args[] = {"garmin", "magellan", "delbin", "navilink", NULL};
82#ifdef WINDOWS
83static gchar * params_ports[] = {"com1", "usb:", NULL};
84#else
85static 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
91static gchar * old_params_ports[] = {"com1", "usb:", NULL};
92#else
93static 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
97typedef 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 *cmd_args;
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;
123static void gps_session_delete(GpsSession *sess);
124
125static 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
132enum {GROUP_DATA_MODE, GROUP_REALTIME_MODE};
133
134
135static VikLayerParamData gps_protocol_default ( void )
136{
137 VikLayerParamData data;
138 data.s = g_strdup ( "garmin" );
139 return data;
140}
141
142static 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)
164static gchar *params_vehicle_position[] = {
165 N_("Keep vehicle at center"),
166 N_("Keep vehicle on screen"),
167 N_("Disable"),
168 NULL
169};
170enum {
171 VEHICLE_POSITION_CENTERED = 0,
172 VEHICLE_POSITION_ON_SCREEN,
173 VEHICLE_POSITION_NONE,
174};
175
176static VikLayerParamData moving_map_method_default ( void ) { return VIK_LPD_UINT ( VEHICLE_POSITION_ON_SCREEN ); }
177
178static VikLayerParamData gpsd_host_default ( void )
179{
180 VikLayerParamData data;
181 data.s = g_strdup ( "localhost" );
182 return data;
183}
184
185static VikLayerParamData gpsd_port_default ( void )
186{
187 VikLayerParamData data;
188 data.s = g_strdup ( DEFAULT_GPSD_PORT );
189 return data;
190}
191
192static VikLayerParamData gpsd_retry_interval_default ( void )
193{
194 VikLayerParamData data;
195 data.s = g_strdup ( "10" );
196 return data;
197}
198
199#endif
200
201static 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};
223enum {
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
239VikLayerInterface 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 (VikLayerFuncSetMenuItemsSelection) NULL,
265 (VikLayerFuncGetMenuItemsSelection) NULL,
266
267 (VikLayerFuncAddMenuItems) gps_layer_add_menu_items,
268 (VikLayerFuncSublayerAddMenuItems) NULL,
269
270 (VikLayerFuncSublayerRenameRequest) NULL,
271 (VikLayerFuncSublayerToggleVisible) NULL,
272 (VikLayerFuncSublayerTooltip) NULL,
273 (VikLayerFuncLayerTooltip) gps_layer_tooltip,
274 (VikLayerFuncLayerSelected) NULL,
275
276 (VikLayerFuncMarshall) gps_layer_marshall,
277 (VikLayerFuncUnmarshall) gps_layer_unmarshall,
278
279 (VikLayerFuncSetParam) gps_layer_set_param,
280 (VikLayerFuncGetParam) gps_layer_get_param,
281 (VikLayerFuncChangeParam) NULL,
282
283 (VikLayerFuncReadFileData) NULL,
284 (VikLayerFuncWriteFileData) NULL,
285
286 (VikLayerFuncDeleteItem) NULL,
287 (VikLayerFuncCutItem) NULL,
288 (VikLayerFuncCopyItem) NULL,
289 (VikLayerFuncPasteItem) NULL,
290 (VikLayerFuncFreeCopiedItem) NULL,
291 (VikLayerFuncDragDropRequest) NULL,
292
293 (VikLayerFuncSelectClick) NULL,
294 (VikLayerFuncSelectMove) NULL,
295 (VikLayerFuncSelectRelease) NULL,
296 (VikLayerFuncSelectedViewportMenu) NULL,
297};
298
299enum {TRW_DOWNLOAD=0, TRW_UPLOAD,
300#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
301 TRW_REALTIME,
302#endif
303 NUM_TRW};
304static gchar * trw_names[] = {
305 N_("GPS Download"), N_("GPS Upload"),
306#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
307 N_("GPS Realtime Tracking"),
308#endif
309};
310
311#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
312typedef struct {
313 struct gps_data_t gpsd;
314 VikGpsLayer *vgl;
315} VglGpsd;
316
317typedef struct {
318 struct gps_fix_t fix;
319 gint satellites_used;
320 gboolean dirty; /* needs to be saved */
321} GpsFix;
322#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
323
324struct _VikGpsLayer {
325 VikLayer vl;
326 VikTrwLayer * trw_children[NUM_TRW];
327 GList * children; /* used only for writing file */
328 int cur_read_child; /* used only for reading file */
329#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
330 VglGpsd *vgpsd;
331 gboolean realtime_tracking; /* set/reset only by the callback */
332 gboolean first_realtime_trackpoint;
333 GpsFix realtime_fix;
334 GpsFix last_fix;
335
336 VikTrack *realtime_track;
337
338 GIOChannel *realtime_io_channel;
339 guint realtime_io_watch_id;
340 guint realtime_retry_timer;
341 GdkGC *realtime_track_gc;
342 GdkGC *realtime_track_bg_gc;
343 GdkGC *realtime_track_pt_gc;
344 GdkGC *realtime_track_pt1_gc;
345 GdkGC *realtime_track_pt2_gc;
346
347 /* params */
348 gchar *gpsd_host;
349 gchar *gpsd_port;
350 gint gpsd_retry_interval;
351 gboolean realtime_record;
352 gboolean realtime_jump_to_start;
353 guint vehicle_position;
354 gboolean realtime_update_statusbar;
355 VikTrackpoint *trkpt;
356 VikTrackpoint *trkpt_prev;
357#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
358 gchar *protocol;
359 gchar *serial_port;
360 gboolean download_tracks;
361 gboolean download_routes;
362 gboolean download_waypoints;
363 gboolean upload_tracks;
364 gboolean upload_routes;
365 gboolean upload_waypoints;
366};
367
368/**
369 * Overwrite the static setup with dynamically generated GPS Babel device list
370 */
371static void gps_layer_inst_init ( VikGpsLayer *self )
372{
373 gint new_proto = 0;
374 // +1 for luck (i.e the NULL terminator)
375 gchar **new_protocols = g_malloc(1 + g_list_length(a_babel_device_list)*sizeof(gpointer));
376
377 GList *gl = g_list_first ( a_babel_device_list );
378 while ( gl ) {
379 // should be using label property but use name for now
380 // thus don't need to mess around converting label to name later on
381 new_protocols[new_proto++] = ((BabelDevice*)gl->data)->name;
382 gl = g_list_next ( gl );
383 }
384 new_protocols[new_proto] = NULL;
385
386 vik_gps_layer_interface.params[PARAM_PROTOCOL].widget_data = new_protocols;
387}
388
389GType vik_gps_layer_get_type ()
390{
391 static GType val_type = 0;
392
393 if (!val_type)
394 {
395 static const GTypeInfo val_info =
396 {
397 sizeof (VikGpsLayerClass),
398 NULL, /* base_init */
399 NULL, /* base_finalize */
400 NULL, /* class init */
401 NULL, /* class_finalize */
402 NULL, /* class_data */
403 sizeof (VikGpsLayer),
404 0,
405 (GInstanceInitFunc) gps_layer_inst_init,
406 };
407 val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGpsLayer", &val_info, 0 );
408 }
409
410 return val_type;
411}
412
413static VikGpsLayer *vik_gps_layer_create (VikViewport *vp)
414{
415 int i;
416
417 VikGpsLayer *rv = vik_gps_layer_new (vp);
418 vik_layer_rename ( VIK_LAYER(rv), vik_gps_layer_interface.name );
419
420 for (i = 0; i < NUM_TRW; i++) {
421 rv->trw_children[i] = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vp, FALSE ));
422 vik_layer_set_menu_items_selection(VIK_LAYER(rv->trw_children[i]), VIK_MENU_ITEM_ALL & ~(VIK_MENU_ITEM_CUT|VIK_MENU_ITEM_DELETE));
423 }
424 return rv;
425}
426
427static const gchar* gps_layer_tooltip ( VikGpsLayer *vgl )
428{
429 return vgl->protocol;
430}
431
432/* "Copy" */
433static void gps_layer_marshall( VikGpsLayer *vgl, guint8 **data, gint *datalen )
434{
435 VikLayer *child_layer;
436 guint8 *ld;
437 gint ll;
438 GByteArray* b = g_byte_array_new ();
439 gint len;
440 gint i;
441
442#define alm_append(obj, sz) \
443 len = (sz); \
444 g_byte_array_append ( b, (guint8 *)&len, sizeof(len) ); \
445 g_byte_array_append ( b, (guint8 *)(obj), len );
446
447 vik_layer_marshall_params(VIK_LAYER(vgl), &ld, &ll);
448 alm_append(ld, ll);
449 g_free(ld);
450
451 for (i = 0; i < NUM_TRW; i++) {
452 child_layer = VIK_LAYER(vgl->trw_children[i]);
453 vik_layer_marshall(child_layer, &ld, &ll);
454 if (ld) {
455 alm_append(ld, ll);
456 g_free(ld);
457 }
458 }
459 *data = b->data;
460 *datalen = b->len;
461 g_byte_array_free(b, FALSE);
462#undef alm_append
463}
464
465/* "Paste" */
466static VikGpsLayer *gps_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
467{
468#define alm_size (*(gint *)data)
469#define alm_next \
470 len -= sizeof(gint) + alm_size; \
471 data += sizeof(gint) + alm_size;
472
473 VikGpsLayer *rv = vik_gps_layer_new(vvp);
474 VikLayer *child_layer;
475 gint i;
476
477 vik_layer_unmarshall_params ( VIK_LAYER(rv), data+sizeof(gint), alm_size, vvp );
478 alm_next;
479
480 i = 0;
481 while (len>0 && i < NUM_TRW) {
482 child_layer = vik_layer_unmarshall ( data + sizeof(gint), alm_size, vvp );
483 if (child_layer) {
484 rv->trw_children[i++] = (VikTrwLayer *)child_layer;
485 // NB no need to attach signal update handler here
486 // as this will always be performed later on in vik_gps_layer_realize()
487 }
488 alm_next;
489 }
490 // g_print("gps_layer_unmarshall ended with len=%d\n", len);
491 g_assert(len == 0);
492 return rv;
493#undef alm_size
494#undef alm_next
495}
496
497static gboolean gps_layer_set_param ( VikGpsLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
498{
499 switch ( id )
500 {
501 case PARAM_PROTOCOL:
502 if (data.s) {
503 g_free(vgl->protocol);
504 // Backwards Compatibility: previous versions <v1.4 stored protocol as an array index
505 int index = data.s[0] - '0';
506 if (data.s[0] != '\0' &&
507 g_ascii_isdigit (data.s[0]) &&
508 data.s[1] == '\0' &&
509 index < OLD_NUM_PROTOCOLS)
510 // It is a single digit: activate compatibility
511 vgl->protocol = g_strdup(protocols_args[index]);
512 else
513 vgl->protocol = g_strdup(data.s);
514 g_debug("%s: %s", __FUNCTION__, vgl->protocol);
515 }
516 else
517 g_warning(_("Unknown GPS Protocol"));
518 break;
519 case PARAM_PORT:
520 if (data.s) {
521 g_free(vgl->serial_port);
522 // Backwards Compatibility: previous versions <v0.9.91 stored serial_port as an array index
523 int index = data.s[0] - '0';
524 if (data.s[0] != '\0' &&
525 g_ascii_isdigit (data.s[0]) &&
526 data.s[1] == '\0' &&
527 index < OLD_NUM_PORTS)
528 /* It is a single digit: activate compatibility */
529 vgl->serial_port = g_strdup(old_params_ports[index]);
530 else
531 vgl->serial_port = g_strdup(data.s);
532 g_debug("%s: %s", __FUNCTION__, vgl->serial_port);
533 }
534 else
535 g_warning(_("Unknown serial port device"));
536 break;
537 case PARAM_DOWNLOAD_TRACKS:
538 vgl->download_tracks = data.b;
539 break;
540 case PARAM_UPLOAD_TRACKS:
541 vgl->upload_tracks = data.b;
542 break;
543 case PARAM_DOWNLOAD_ROUTES:
544 vgl->download_routes = data.b;
545 break;
546 case PARAM_UPLOAD_ROUTES:
547 vgl->upload_routes = data.b;
548 break;
549 case PARAM_DOWNLOAD_WAYPOINTS:
550 vgl->download_waypoints = data.b;
551 break;
552 case PARAM_UPLOAD_WAYPOINTS:
553 vgl->upload_waypoints = data.b;
554 break;
555#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
556 case PARAM_GPSD_HOST:
557 if (data.s) {
558 if (vgl->gpsd_host)
559 g_free(vgl->gpsd_host);
560 vgl->gpsd_host = g_strdup(data.s);
561 }
562 break;
563 case PARAM_GPSD_PORT:
564 if (data.s) {
565 if (vgl->gpsd_port)
566 g_free(vgl->gpsd_port);
567 vgl->gpsd_port = g_strdup(data.s);
568 }
569 break;
570 case PARAM_GPSD_RETRY_INTERVAL:
571 vgl->gpsd_retry_interval = strtol(data.s, NULL, 10);
572 break;
573 case PARAM_REALTIME_REC:
574 vgl->realtime_record = data.b;
575 break;
576 case PARAM_REALTIME_CENTER_START:
577 vgl->realtime_jump_to_start = data.b;
578 break;
579 case PARAM_VEHICLE_POSITION:
580 vgl->vehicle_position = data.u;
581 break;
582 case PARAM_REALTIME_UPDATE_STATUSBAR:
583 vgl->realtime_update_statusbar = data.b;
584 break;
585#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
586 default:
587 g_warning("gps_layer_set_param(): unknown parameter");
588 }
589
590 return TRUE;
591}
592
593static VikLayerParamData gps_layer_get_param ( VikGpsLayer *vgl, guint16 id, gboolean is_file_operation )
594{
595 VikLayerParamData rv;
596 switch ( id )
597 {
598 case PARAM_PROTOCOL:
599 rv.s = vgl->protocol;
600 g_debug("%s: %s", __FUNCTION__, rv.s);
601 break;
602 case PARAM_PORT:
603 rv.s = vgl->serial_port;
604 g_debug("%s: %s", __FUNCTION__, rv.s);
605 break;
606 case PARAM_DOWNLOAD_TRACKS:
607 rv.b = vgl->download_tracks;
608 break;
609 case PARAM_UPLOAD_TRACKS:
610 rv.b = vgl->upload_tracks;
611 break;
612 case PARAM_DOWNLOAD_ROUTES:
613 rv.b = vgl->download_routes;
614 break;
615 case PARAM_UPLOAD_ROUTES:
616 rv.b = vgl->upload_routes;
617 break;
618 case PARAM_DOWNLOAD_WAYPOINTS:
619 rv.b = vgl->download_waypoints;
620 break;
621 case PARAM_UPLOAD_WAYPOINTS:
622 rv.b = vgl->upload_waypoints;
623 break;
624#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
625 case PARAM_GPSD_HOST:
626 rv.s = vgl->gpsd_host ? vgl->gpsd_host : "";
627 break;
628 case PARAM_GPSD_PORT:
629 rv.s = vgl->gpsd_port ? vgl->gpsd_port : g_strdup(DEFAULT_GPSD_PORT);
630 break;
631 case PARAM_GPSD_RETRY_INTERVAL:
632 rv.s = g_strdup_printf("%d", vgl->gpsd_retry_interval);
633 break;
634 case PARAM_REALTIME_REC:
635 rv.b = vgl->realtime_record;
636 break;
637 case PARAM_REALTIME_CENTER_START:
638 rv.b = vgl->realtime_jump_to_start;
639 break;
640 case PARAM_VEHICLE_POSITION:
641 rv.u = vgl->vehicle_position;
642 break;
643 case PARAM_REALTIME_UPDATE_STATUSBAR:
644 rv.u = vgl->realtime_update_statusbar;
645 break;
646#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
647 default:
648 g_warning(_("%s: unknown parameter"), __FUNCTION__);
649 }
650
651 return rv;
652}
653
654VikGpsLayer *vik_gps_layer_new (VikViewport *vp)
655{
656 gint i;
657 VikGpsLayer *vgl = VIK_GPS_LAYER ( g_object_new ( VIK_GPS_LAYER_TYPE, NULL ) );
658 vik_layer_set_type ( VIK_LAYER(vgl), VIK_LAYER_GPS );
659 for (i = 0; i < NUM_TRW; i++) {
660 vgl->trw_children[i] = NULL;
661 }
662 vgl->children = NULL;
663 vgl->cur_read_child = 0;
664
665#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
666 vgl->first_realtime_trackpoint = FALSE;
667 vgl->trkpt = NULL;
668 vgl->trkpt_prev = NULL;
669 vgl->vgpsd = NULL;
670 vgl->realtime_io_channel = NULL;
671 vgl->realtime_io_watch_id = 0;
672 vgl->realtime_retry_timer = 0;
673 if ( vp ) {
674 vgl->realtime_track_gc = vik_viewport_new_gc ( vp, "#203070", 2 );
675 vgl->realtime_track_bg_gc = vik_viewport_new_gc ( vp, "grey", 2 );
676 vgl->realtime_track_pt1_gc = vik_viewport_new_gc ( vp, "red", 2 );
677 vgl->realtime_track_pt2_gc = vik_viewport_new_gc ( vp, "green", 2 );
678 vgl->realtime_track_pt_gc = vgl->realtime_track_pt1_gc;
679 }
680 vgl->realtime_track = NULL;
681#endif // VIK_CONFIG_REALTIME_GPS_TRACKING
682
683 vik_layer_set_defaults ( VIK_LAYER(vgl), vp );
684
685 return vgl;
686}
687
688static void vik_gps_layer_draw ( VikGpsLayer *vgl, VikViewport *vp )
689{
690 gint i;
691 VikLayer *vl;
692 VikLayer *trigger = VIK_LAYER(vik_viewport_get_trigger( vp ));
693
694 for (i = 0; i < NUM_TRW; i++) {
695 vl = VIK_LAYER(vgl->trw_children[i]);
696 if (vl == trigger) {
697 if ( vik_viewport_get_half_drawn ( vp ) ) {
698 vik_viewport_set_half_drawn ( vp, FALSE );
699 vik_viewport_snapshot_load( vp );
700 } else {
701 vik_viewport_snapshot_save( vp );
702 }
703 }
704 if (!vik_viewport_get_half_drawn(vp))
705 vik_layer_draw ( vl, vp );
706 }
707#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
708 if (vgl->realtime_tracking) {
709 if (VIK_LAYER(vgl) == trigger) {
710 if ( vik_viewport_get_half_drawn ( vp ) ) {
711 vik_viewport_set_half_drawn ( vp, FALSE );
712 vik_viewport_snapshot_load( vp );
713 } else {
714 vik_viewport_snapshot_save( vp );
715 }
716 }
717 if (!vik_viewport_get_half_drawn(vp))
718 realtime_tracking_draw(vgl, vp);
719 }
720#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
721}
722
723static void gps_layer_change_coord_mode ( VikGpsLayer *vgl, VikCoordMode mode )
724{
725 gint i;
726 for (i = 0; i < NUM_TRW; i++) {
727 vik_layer_change_coord_mode(VIK_LAYER(vgl->trw_children[i]), mode);
728 }
729}
730
731static void gps_layer_add_menu_items( VikGpsLayer *vgl, GtkMenu *menu, gpointer vlp )
732{
733 static gpointer pass_along[2];
734 GtkWidget *item;
735 pass_along[0] = vgl;
736 pass_along[1] = vlp;
737
738 item = gtk_menu_item_new();
739 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
740 gtk_widget_show ( item );
741
742 /* Now with icons */
743 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS") );
744 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
745 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_upload_cb), pass_along );
746 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
747 gtk_widget_show ( item );
748
749 item = gtk_image_menu_item_new_with_mnemonic ( _("Download from _GPS") );
750 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
751 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_download_cb), pass_along );
752 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
753 gtk_widget_show ( item );
754
755#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
756 item = gtk_image_menu_item_new_with_mnemonic ( vgl->realtime_tracking ?
757 "_Stop Realtime Tracking" :
758 "_Start Realtime Tracking" );
759 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, vgl->realtime_tracking ?
760 gtk_image_new_from_stock (GTK_STOCK_MEDIA_STOP, GTK_ICON_SIZE_MENU) :
761 gtk_image_new_from_stock (GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_MENU) );
762 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_start_stop_tracking_cb), pass_along );
763 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
764 gtk_widget_show ( item );
765
766 item = gtk_menu_item_new();
767 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
768 gtk_widget_show ( item );
769
770 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _Realtime") );
771 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
772 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_realtime_cb), pass_along );
773 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
774 gtk_widget_show ( item );
775#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
776
777 item = gtk_image_menu_item_new_with_mnemonic ( _("E_mpty Upload") );
778 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
779 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_upload_cb), pass_along );
780 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
781 gtk_widget_show ( item );
782
783 item = gtk_image_menu_item_new_with_mnemonic ( _("_Empty Download") );
784 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
785 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_download_cb), pass_along );
786 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
787 gtk_widget_show ( item );
788
789 item = gtk_image_menu_item_new_with_mnemonic ( _("Empty _All") );
790 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
791 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(gps_empty_all_cb), pass_along );
792 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
793 gtk_widget_show ( item );
794
795}
796
797static void disconnect_layer_signal ( VikLayer *vl, VikGpsLayer *vgl )
798{
799 guint number_handlers = g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, vgl);
800 if ( number_handlers != 1 ) {
801 g_critical(_("Unexpected number of disconnected handlers: %d"), number_handlers);
802 }
803}
804
805static void vik_gps_layer_free ( VikGpsLayer *vgl )
806{
807 gint i;
808 for (i = 0; i < NUM_TRW; i++) {
809 if (vgl->vl.realized)
810 disconnect_layer_signal(VIK_LAYER(vgl->trw_children[i]), vgl);
811 g_object_unref(vgl->trw_children[i]);
812 }
813#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
814 rt_gpsd_disconnect(vgl);
815 if (vgl->realtime_track_gc != NULL)
816 g_object_unref(vgl->realtime_track_gc);
817 if (vgl->realtime_track_bg_gc != NULL)
818 g_object_unref(vgl->realtime_track_bg_gc);
819 if (vgl->realtime_track_pt1_gc != NULL)
820 g_object_unref(vgl->realtime_track_pt1_gc);
821 if (vgl->realtime_track_pt2_gc != NULL)
822 g_object_unref(vgl->realtime_track_pt2_gc);
823#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */
824}
825
826static void vik_gps_layer_realize ( VikGpsLayer *vgl, VikTreeview *vt, GtkTreeIter *layer_iter )
827{
828 GtkTreeIter iter;
829 int ix;
830
831 // TODO set to garmin by default
832 //if (a_babel_device_list)
833 // device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
834 // Need to access uibuild widgets somehow....
835
836 for (ix = 0; ix < NUM_TRW; ix++) {
837 VikLayer * trw = VIK_LAYER(vgl->trw_children[ix]);
838 vik_treeview_add_layer ( VIK_LAYER(vgl)->vt, layer_iter, &iter,
839 _(trw_names[ix]), vgl, TRUE,
840 trw, trw->type, trw->type );
841 if ( ! trw->visible )
842 vik_treeview_item_set_visible ( VIK_LAYER(vgl)->vt, &iter, FALSE );
843 vik_layer_realize ( trw, VIK_LAYER(vgl)->vt, &iter );
844 g_signal_connect_swapped ( G_OBJECT(trw), "update", G_CALLBACK(vik_layer_emit_update_secondary), vgl );
845 }
846}
847
848const GList *vik_gps_layer_get_children ( VikGpsLayer *vgl )
849{
850 int i;
851
852 if (vgl->children == NULL) {
853 for (i = NUM_TRW - 1; i >= 0; i--)
854 vgl->children = g_list_prepend(vgl->children, vgl->trw_children[i]);
855 }
856 return vgl->children;
857}
858
859VikTrwLayer * vik_gps_layer_get_a_child(VikGpsLayer *vgl)
860{
861 g_assert ((vgl->cur_read_child >= 0) && (vgl->cur_read_child < NUM_TRW));
862
863 VikTrwLayer * vtl = vgl->trw_children[vgl->cur_read_child];
864 if (++(vgl->cur_read_child) >= NUM_TRW)
865 vgl->cur_read_child = 0;
866 return(vtl);
867}
868
869gboolean vik_gps_layer_is_empty ( VikGpsLayer *vgl )
870{
871 if ( vgl->trw_children[0] )
872 return FALSE;
873 return TRUE;
874}
875
876static void gps_session_delete(GpsSession *sess)
877{
878 /* TODO */
879 g_mutex_free(sess->mutex);
880 g_free(sess->cmd_args);
881
882 g_free(sess);
883
884}
885
886static 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); break;
903 sess->total_count = mycnt;
904 }
905 }
906 }
907 else
908 {
909 switch (sess->progress_type) {
910 case WPT: tmp_str = ngettext("Uploading %d waypoint...", "Uploading %d waypoints...", cnt); break;
911 case TRK: tmp_str = ngettext("Uploading %d trackpoint...", "Uploading %d trackpoints...", cnt); break;
912 default: tmp_str = ngettext("Uploading %d routepoint...", "Uploading %d routepoints...", cnt); break;
913 }
914 }
915
916 g_snprintf(s, 128, tmp_str, cnt);
917 gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
918 gtk_widget_show ( sess->progress_label );
919 sess->total_count = cnt;
920 }
921 g_mutex_unlock(sess->mutex);
922 gdk_threads_leave();
923}
924
925static void set_current_count(gint cnt, GpsSession *sess)
926{
927 gchar s[128];
928 const gchar *tmp_str;
929
930 gdk_threads_enter();
931 g_mutex_lock(sess->mutex);
932 if (sess->ok) {
933 if (cnt < sess->total_count) {
934 if (sess->direction == GPS_DOWN)
935 {
936 switch (sess->progress_type) {
937 case WPT: tmp_str = ngettext("Downloaded %d out of %d waypoint...", "Downloaded %d out of %d waypoints...", sess->total_count); break;
938 case TRK: tmp_str = ngettext("Downloaded %d out of %d trackpoint...", "Downloaded %d out of %d trackpoints...", sess->total_count); break;
939 default: tmp_str = ngettext("Downloaded %d out of %d routepoint...", "Downloaded %d out of %d routepoints...", sess->total_count); break;
940 }
941 }
942 else {
943 switch (sess->progress_type) {
944 case WPT: tmp_str = ngettext("Uploaded %d out of %d waypoint...", "Uploaded %d out of %d waypoints...", sess->total_count); break;
945 case TRK: tmp_str = ngettext("Uploaded %d out of %d trackpoint...", "Uploaded %d out of %d trackpoints...", sess->total_count); break;
946 default: tmp_str = ngettext("Uploaded %d out of %d routepoint...", "Uploaded %d out of %d routepoints...", sess->total_count); break;
947 }
948 }
949 g_snprintf(s, 128, tmp_str, cnt, sess->total_count);
950 } else {
951 if (sess->direction == GPS_DOWN)
952 {
953 switch (sess->progress_type) {
954 case WPT: tmp_str = ngettext("Downloaded %d waypoint", "Downloaded %d waypoints", cnt); break;
955 case TRK: tmp_str = ngettext("Downloaded %d trackpoint", "Downloaded %d trackpoints", cnt); break;
956 default: tmp_str = ngettext("Downloaded %d routepoint", "Downloaded %d routepoints", cnt); break;
957 }
958 }
959 else {
960 switch (sess->progress_type) {
961 case WPT: tmp_str = ngettext("Uploaded %d waypoint", "Uploaded %d waypoints", cnt); break;
962 case TRK: tmp_str = ngettext("Uploaded %d trackpoint", "Uploaded %d trackpoints", cnt); break;
963 default: tmp_str = ngettext("Uploaded %d routepoint", "Uploaded %d routepoints", cnt); break;
964 }
965 }
966 g_snprintf(s, 128, tmp_str, cnt);
967 }
968 gtk_label_set_text ( GTK_LABEL(sess->progress_label), s );
969 }
970 g_mutex_unlock(sess->mutex);
971 gdk_threads_leave();
972}
973
974static void set_gps_info(const gchar *info, GpsSession *sess)
975{
976 gchar s[256];
977 gdk_threads_enter();
978 g_mutex_lock(sess->mutex);
979 if (sess->ok) {
980 g_snprintf(s, 256, _("GPS Device: %s"), info);
981 gtk_label_set_text ( GTK_LABEL(sess->gps_label), s );
982 }
983 g_mutex_unlock(sess->mutex);
984 gdk_threads_leave();
985}
986
987/*
988 * Common processing for GPS Device information
989 * It doesn't matter whether we're uploading or downloading
990 */
991static void process_line_for_gps_info ( const gchar *line, GpsSession *sess )
992{
993 if (strstr(line, "PRDDAT")) {
994 gchar **tokens = g_strsplit(line, " ", 0);
995 gchar info[128];
996 int ilen = 0;
997 int i;
998 int n_tokens = 0;
999
1000 while (tokens[n_tokens])
1001 n_tokens++;
1002
1003 // I'm not entirely clear what information this is trying to get...
1004 // Obviously trying to decipher some kind of text/naming scheme
1005 // Anyway this will be superceded if there is 'Unit:' information
1006 if (n_tokens > 8) {
1007 for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
1008 guint ch;
1009 sscanf(tokens[i], "%x", &ch);
1010 info[ilen++] = ch;
1011 }
1012 info[ilen++] = 0;
1013 set_gps_info(info, sess);
1014 }
1015 g_strfreev(tokens);
1016 }
1017
1018 /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
1019 if (strstr(line, "Unit:")) {
1020 gchar **tokens = g_strsplit(line, "\t", 0);
1021 int n_tokens = 0;
1022 while (tokens[n_tokens])
1023 n_tokens++;
1024
1025 if (n_tokens > 1) {
1026 set_gps_info(tokens[1], sess);
1027 }
1028 g_strfreev(tokens);
1029 }
1030}
1031
1032static void gps_download_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1033{
1034 gchar *line;
1035
1036 gdk_threads_enter ();
1037 g_mutex_lock(sess->mutex);
1038 if (!sess->ok) {
1039 g_mutex_unlock(sess->mutex);
1040 gps_session_delete(sess);
1041 gdk_threads_leave();
1042 g_thread_exit ( NULL );
1043 }
1044 g_mutex_unlock(sess->mutex);
1045 gdk_threads_leave ();
1046
1047 switch(c) {
1048 case BABEL_DIAG_OUTPUT:
1049 line = (gchar *)data;
1050
1051 gdk_threads_enter();
1052 g_mutex_lock(sess->mutex);
1053 if (sess->ok) {
1054 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1055 }
1056 g_mutex_unlock(sess->mutex);
1057 gdk_threads_leave();
1058
1059 /* tells us the type of items that will follow */
1060 if (strstr(line, "Xfer Wpt")) {
1061 sess->progress_label = sess->wp_label;
1062 sess->progress_type = WPT;
1063 }
1064 if (strstr(line, "Xfer Trk")) {
1065 sess->progress_label = sess->trk_label;
1066 sess->progress_type = TRK;
1067 }
1068 if (strstr(line, "Xfer Rte")) {
1069 sess->progress_label = sess->rte_label;
1070 sess->progress_type = RTE;
1071 }
1072
1073 process_line_for_gps_info ( line, sess );
1074
1075 if (strstr(line, "RECORD")) {
1076 int lsb, msb, cnt;
1077
1078 if (strlen(line) > 20) {
1079 sscanf(line+17, "%x", &lsb);
1080 sscanf(line+20, "%x", &msb);
1081 cnt = lsb + msb * 256;
1082 set_total_count(cnt, sess);
1083 sess->count = 0;
1084 }
1085 }
1086 if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1087 sess->count++;
1088 set_current_count(sess->count, sess);
1089 }
1090 break;
1091 case BABEL_DONE:
1092 break;
1093 default:
1094 break;
1095 }
1096
1097}
1098
1099static void gps_upload_progress_func(BabelProgressCode c, gpointer data, GpsSession * sess )
1100{
1101 gchar *line;
1102 static int cnt = 0;
1103
1104 gdk_threads_enter ();
1105 g_mutex_lock(sess->mutex);
1106 if (!sess->ok) {
1107 g_mutex_unlock(sess->mutex);
1108 gps_session_delete(sess);
1109 gdk_threads_leave();
1110 g_thread_exit ( NULL );
1111 }
1112 g_mutex_unlock(sess->mutex);
1113 gdk_threads_leave ();
1114
1115 switch(c) {
1116 case BABEL_DIAG_OUTPUT:
1117 line = (gchar *)data;
1118
1119 gdk_threads_enter();
1120 g_mutex_lock(sess->mutex);
1121 if (sess->ok) {
1122 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Status: Working...") );
1123 }
1124 g_mutex_unlock(sess->mutex);
1125 gdk_threads_leave();
1126
1127 process_line_for_gps_info ( line, sess );
1128
1129 if (strstr(line, "RECORD")) {
1130 int lsb, msb;
1131
1132 if (strlen(line) > 20) {
1133 sscanf(line+17, "%x", &lsb);
1134 sscanf(line+20, "%x", &msb);
1135 cnt = lsb + msb * 256;
1136 /* set_total_count(cnt, sess); */
1137 sess->count = 0;
1138 }
1139 }
1140 if ( strstr(line, "WPTDAT")) {
1141 if (sess->count == 0) {
1142 sess->progress_label = sess->wp_label;
1143 sess->progress_type = WPT;
1144 set_total_count(cnt, sess);
1145 }
1146 sess->count++;
1147 set_current_count(sess->count, sess);
1148 }
1149 if ( strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
1150 if (sess->count == 0) {
1151 sess->progress_label = sess->rte_label;
1152 sess->progress_type = RTE;
1153 // Maybe a gpsbabel bug/feature (upto at least v1.4.3 or maybe my Garmin device) but the count always seems x2 too many for routepoints
1154 // Anyway since we're uploading - we should know how many points we're going to put!
1155 cnt = (cnt / 2) + 1;
1156 set_total_count(cnt, sess);
1157 }
1158 sess->count++;
1159 set_current_count(sess->count, sess);
1160 }
1161 if ( strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
1162 if (sess->count == 0) {
1163 sess->progress_label = sess->trk_label;
1164 sess->progress_type = TRK;
1165 set_total_count(cnt, sess);
1166 }
1167 sess->count++;
1168 set_current_count(sess->count, sess);
1169 }
1170 break;
1171 case BABEL_DONE:
1172 break;
1173 default:
1174 break;
1175 }
1176
1177}
1178
1179static void gps_comm_thread(GpsSession *sess)
1180{
1181 gboolean result;
1182
1183 if (sess->direction == GPS_DOWN)
1184 result = a_babel_convert_from (sess->vtl, sess->cmd_args, sess->port,
1185 (BabelStatusFunc) gps_download_progress_func, sess, NULL);
1186 else {
1187 result = a_babel_convert_to (sess->vtl, sess->track, sess->cmd_args, sess->port,
1188 (BabelStatusFunc) gps_upload_progress_func, sess);
1189 }
1190
1191 if (!result) {
1192 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Error: couldn't find gpsbabel.") );
1193 }
1194 else {
1195 g_mutex_lock(sess->mutex);
1196 if (sess->ok) {
1197 gtk_label_set_text ( GTK_LABEL(sess->status_label), _("Done.") );
1198 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT, TRUE );
1199 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_REJECT, FALSE );
1200
1201 /* Do not change the view if we are following the current GPS position */
1202#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1203 if (!sess->realtime_tracking)
1204#endif
1205 {
1206 if ( sess->vvp && sess->direction == GPS_DOWN ) {
1207 /* View the data available */
1208 vik_trw_layer_auto_set_view ( sess->vtl, sess->vvp) ;
1209 vik_layer_emit_update ( VIK_LAYER(sess->vtl) ); // NB update from background thread
1210 }
1211 }
1212 } else {
1213 /* canceled */
1214 }
1215 g_mutex_unlock(sess->mutex);
1216 }
1217
1218 g_mutex_lock(sess->mutex);
1219 if (sess->ok) {
1220 sess->ok = FALSE;
1221 g_mutex_unlock(sess->mutex);
1222 }
1223 else {
1224 g_mutex_unlock(sess->mutex);
1225 gps_session_delete(sess);
1226 }
1227 g_thread_exit(NULL);
1228}
1229
1230/**
1231 * vik_gps_comm:
1232 * @vtl: The TrackWaypoint layer to operate on
1233 * @track: Operate on a particular track when specified
1234 * @dir: The direction of the transfer
1235 * @protocol: The GPS device communication protocol
1236 * @port: The GPS serial port
1237 * @tracking: If tracking then viewport display update will be skipped
1238 * @vvp: A viewport is required as the display may get updated
1239 * @vlp: A layers panel is needed for uploading as the items maybe modified
1240 * @do_tracks: Whether tracks shoud be processed
1241 * @do_waypoints: Whether waypoints shoud be processed
1242 * @turn_off: Whether we should attempt to turn off the GPS device after the transfer (only some devices support this)
1243 *
1244 * Talk to a GPS Device using a thread which updates a dialog with the progress
1245 */
1246gint vik_gps_comm ( VikTrwLayer *vtl,
1247 VikTrack *track,
1248 vik_gps_dir dir,
1249 gchar *protocol,
1250 gchar *port,
1251 gboolean tracking,
1252 VikViewport *vvp,
1253 VikLayersPanel *vlp,
1254 gboolean do_tracks,
1255 gboolean do_routes,
1256 gboolean do_waypoints,
1257 gboolean turn_off )
1258{
1259 GpsSession *sess = g_malloc(sizeof(GpsSession));
1260 char *tracks = NULL;
1261 char *routes = NULL;
1262 char *waypoints = NULL;
1263
1264 sess->mutex = g_mutex_new();
1265 sess->direction = dir;
1266 sess->vtl = vtl;
1267 sess->track = track;
1268 sess->port = g_strdup(port);
1269 sess->ok = TRUE;
1270 sess->window_title = (dir == GPS_DOWN) ? _("GPS Download") : _("GPS Upload");
1271 sess->vvp = vvp;
1272
1273 // This must be done inside the main thread as the uniquify causes screen updates
1274 // (originally performed this nearer the point of upload in the thread)
1275 if ( dir == GPS_UP ) {
1276 // Enforce unique names in the layer upload to the GPS device
1277 // NB this may only be a Garmin device restriction (and may be not every Garmin device either...)
1278 // Thus this maintains the older code in built restriction
1279 if ( ! vik_trw_layer_uniquify ( sess->vtl, vlp ) )
1280 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(sess->vtl))), VIK_STATUSBAR_INFO,
1281 _("Warning - GPS Upload items may overwrite each other") );
1282 }
1283
1284#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1285 sess->realtime_tracking = tracking;
1286#endif
1287
1288 if (do_tracks)
1289 tracks = "-t";
1290 else
1291 tracks = "";
1292 if (do_routes)
1293 routes = "-r";
1294 else
1295 routes = "";
1296 if (do_waypoints)
1297 waypoints = "-w";
1298 else
1299 waypoints = "";
1300
1301 sess->cmd_args = g_strdup_printf("-D 9 %s %s %s -%c %s",
1302 tracks, routes, waypoints, (dir == GPS_DOWN) ? 'i' : 'o', protocol);
1303 tracks = NULL;
1304 waypoints = NULL;
1305
1306 // Only create dialog if we're going to do some transferring
1307 if ( do_tracks || do_waypoints || do_routes ) {
1308 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 );
1309 gtk_dialog_set_response_sensitive ( GTK_DIALOG(sess->dialog),
1310 GTK_RESPONSE_ACCEPT, FALSE );
1311 gtk_window_set_title ( GTK_WINDOW(sess->dialog), sess->window_title );
1312
1313 sess->status_label = gtk_label_new (_("Status: detecting gpsbabel"));
1314 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->status_label, FALSE, FALSE, 5 );
1315 gtk_widget_show_all(sess->status_label);
1316
1317 sess->gps_label = gtk_label_new (_("GPS device: N/A"));
1318 sess->ver_label = gtk_label_new ("");
1319 sess->id_label = gtk_label_new ("");
1320 sess->wp_label = gtk_label_new ("");
1321 sess->trk_label = gtk_label_new ("");
1322 sess->rte_label = gtk_label_new ("");
1323
1324 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->gps_label, FALSE, FALSE, 5 );
1325 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->wp_label, FALSE, FALSE, 5 );
1326 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->trk_label, FALSE, FALSE, 5 );
1327 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(sess->dialog))), sess->rte_label, FALSE, FALSE, 5 );
1328
1329 gtk_widget_show_all(sess->dialog);
1330
1331 sess->progress_label = sess->wp_label;
1332 sess->total_count = -1;
1333
1334 // Starting gps read/write thread
1335#if GLIB_CHECK_VERSION (2, 32, 0)
1336 g_thread_try_new ( "gps_comm_thread", (GThreadFunc)gps_comm_thread, sess, NULL );
1337#else
1338 g_thread_create ( (GThreadFunc)gps_comm_thread, sess, FALSE, NULL );
1339#endif
1340
1341 gtk_dialog_set_default_response ( GTK_DIALOG(sess->dialog), GTK_RESPONSE_ACCEPT );
1342 gtk_dialog_run(GTK_DIALOG(sess->dialog));
1343
1344 gtk_widget_destroy(sess->dialog);
1345 }
1346 else {
1347 if ( !turn_off )
1348 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No GPS items selected for transfer.") );
1349 }
1350
1351 g_mutex_lock(sess->mutex);
1352
1353 if (sess->ok) {
1354 sess->ok = FALSE; /* tell thread to stop */
1355 g_mutex_unlock(sess->mutex);
1356 }
1357 else {
1358 if ( turn_off ) {
1359 // No need for thread for powering off device (should be quick operation...) - so use babel command directly:
1360 gchar *device_off = g_strdup_printf("-i %s,%s", protocol, "power_off");
1361 gboolean result = a_babel_convert_from (NULL, (const char*)device_off, (const char*)port, NULL, NULL, NULL);
1362 if ( !result )
1363 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not turn off device.") );
1364 g_free ( device_off );
1365 }
1366 g_mutex_unlock(sess->mutex);
1367 gps_session_delete(sess);
1368 }
1369
1370 return 0;
1371}
1372
1373static void gps_upload_cb( gpointer layer_and_vlp[2] )
1374{
1375 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1376 VikLayersPanel *vlp = VIK_LAYERS_PANEL(layer_and_vlp[1]);
1377 VikTrwLayer *vtl = vgl->trw_children[TRW_UPLOAD];
1378 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1379 VikViewport *vvp = vik_window_viewport(vw);
1380 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);
1381}
1382
1383static void gps_download_cb( gpointer layer_and_vlp[2] )
1384{
1385 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1386 VikTrwLayer *vtl = vgl->trw_children[TRW_DOWNLOAD];
1387 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1388 VikViewport *vvp = vik_window_viewport(vw);
1389#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1390 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);
1391#else
1392 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);
1393#endif
1394}
1395
1396static void gps_empty_upload_cb( gpointer layer_and_vlp[2] )
1397{
1398 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1399 // Get confirmation from the user
1400 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1401 _("Are you sure you want to delete GPS Upload data?"),
1402 NULL ) )
1403 return;
1404 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1405 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1406 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1407}
1408
1409static void gps_empty_download_cb( gpointer layer_and_vlp[2] )
1410{
1411 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1412 // Get confirmation from the user
1413 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1414 _("Are you sure you want to delete GPS Download data?"),
1415 NULL ) )
1416 return;
1417 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1418 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1419 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1420}
1421
1422#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1423static void gps_empty_realtime_cb( gpointer layer_and_vlp[2] )
1424{
1425 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1426 // Get confirmation from the user
1427 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1428 _("Are you sure you want to delete GPS Realtime data?"),
1429 NULL ) )
1430 return;
1431 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1432 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1433}
1434#endif
1435
1436static void gps_empty_all_cb( gpointer layer_and_vlp[2] )
1437{
1438 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1439 // Get confirmation from the user
1440 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_WIDGET(layer_and_vlp[1]),
1441 _("Are you sure you want to delete All GPS data?"),
1442 NULL ) )
1443 return;
1444 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_UPLOAD]);
1445 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_UPLOAD]);
1446 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_UPLOAD]);
1447 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_DOWNLOAD]);
1448 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_DOWNLOAD]);
1449 vik_trw_layer_delete_all_routes ( vgl-> trw_children[TRW_DOWNLOAD]);
1450#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1451 vik_trw_layer_delete_all_waypoints ( vgl-> trw_children[TRW_REALTIME]);
1452 vik_trw_layer_delete_all_tracks ( vgl-> trw_children[TRW_REALTIME]);
1453#endif
1454}
1455
1456#if defined (VIK_CONFIG_REALTIME_GPS_TRACKING) && defined (GPSD_API_MAJOR_VERSION)
1457static void realtime_tracking_draw(VikGpsLayer *vgl, VikViewport *vp)
1458{
1459 struct LatLon ll;
1460 VikCoord nw, se;
1461 struct LatLon lnw, lse;
1462 vik_viewport_screen_to_coord ( vp, -20, -20, &nw );
1463 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp)+20, vik_viewport_get_width(vp)+20, &se );
1464 vik_coord_to_latlon ( &nw, &lnw );
1465 vik_coord_to_latlon ( &se, &lse );
1466 if ( vgl->realtime_fix.fix.latitude > lse.lat &&
1467 vgl->realtime_fix.fix.latitude < lnw.lat &&
1468 vgl->realtime_fix.fix.longitude > lnw.lon &&
1469 vgl->realtime_fix.fix.longitude < lse.lon &&
1470 !isnan (vgl->realtime_fix.fix.track) ) {
1471 VikCoord gps;
1472 gint x, y;
1473 gint half_back_x, half_back_y;
1474 gint half_back_bg_x, half_back_bg_y;
1475 gint pt_x, pt_y;
1476 gint ptbg_x;
1477 gint side1_x, side1_y, side2_x, side2_y;
1478 gint side1bg_x, side1bg_y, side2bg_x, side2bg_y;
1479
1480 ll.lat = vgl->realtime_fix.fix.latitude;
1481 ll.lon = vgl->realtime_fix.fix.longitude;
1482 vik_coord_load_from_latlon ( &gps, vik_viewport_get_coord_mode(vp), &ll);
1483 vik_viewport_coord_to_screen ( vp, &gps, &x, &y );
1484
1485 gdouble heading_cos = cos(DEG2RAD(vgl->realtime_fix.fix.track));
1486 gdouble heading_sin = sin(DEG2RAD(vgl->realtime_fix.fix.track));
1487
1488 half_back_y = y+8*heading_cos;
1489 half_back_x = x-8*heading_sin;
1490 half_back_bg_y = y+10*heading_cos;
1491 half_back_bg_x = x-10*heading_sin;
1492
1493 pt_y = half_back_y-24*heading_cos;
1494 pt_x = half_back_x+24*heading_sin;
1495 //ptbg_y = half_back_bg_y-28*heading_cos;
1496 ptbg_x = half_back_bg_x+28*heading_sin;
1497
1498 side1_y = half_back_y+9*heading_sin;
1499 side1_x = half_back_x+9*heading_cos;
1500 side1bg_y = half_back_bg_y+11*heading_sin;
1501 side1bg_x = half_back_bg_x+11*heading_cos;
1502
1503 side2_y = half_back_y-9*heading_sin;
1504 side2_x = half_back_x-9*heading_cos;
1505 side2bg_y = half_back_bg_y-11*heading_sin;
1506 side2bg_x = half_back_bg_x-11*heading_cos;
1507
1508 GdkPoint trian[3] = { { pt_x, pt_y }, {side1_x, side1_y}, {side2_x, side2_y} };
1509 GdkPoint trian_bg[3] = { { ptbg_x, pt_y }, {side1bg_x, side1bg_y}, {side2bg_x, side2bg_y} };
1510
1511 vik_viewport_draw_polygon ( vp, vgl->realtime_track_bg_gc, TRUE, trian_bg, 3 );
1512 vik_viewport_draw_polygon ( vp, vgl->realtime_track_gc, TRUE, trian, 3 );
1513 vik_viewport_draw_rectangle ( vp,
1514 (vgl->realtime_fix.fix.mode > MODE_2D) ? vgl->realtime_track_pt2_gc : vgl->realtime_track_pt1_gc,
1515 TRUE, x-2, y-2, 4, 4 );
1516 //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;
1517 }
1518}
1519
1520static VikTrackpoint* create_realtime_trackpoint(VikGpsLayer *vgl, gboolean forced)
1521{
1522 struct LatLon ll;
1523 GList *last_tp;
1524
1525 /* Note that fix.time is a double, but it should not affect the precision
1526 for most GPS */
1527 time_t cur_timestamp = vgl->realtime_fix.fix.time;
1528 time_t last_timestamp = vgl->last_fix.fix.time;
1529
1530 if (cur_timestamp < last_timestamp) {
1531 return NULL;
1532 }
1533
1534 if (vgl->realtime_record && vgl->realtime_fix.dirty) {
1535 gboolean replace = FALSE;
1536 int heading = isnan(vgl->realtime_fix.fix.track) ? 0 : (int)floor(vgl->realtime_fix.fix.track);
1537 int last_heading = isnan(vgl->last_fix.fix.track) ? 0 : (int)floor(vgl->last_fix.fix.track);
1538 int alt = isnan(vgl->realtime_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->realtime_fix.fix.altitude);
1539 int last_alt = isnan(vgl->last_fix.fix.altitude) ? VIK_DEFAULT_ALTITUDE : floor(vgl->last_fix.fix.altitude);
1540 if (((last_tp = g_list_last(vgl->realtime_track->trackpoints)) != NULL) &&
1541 (vgl->realtime_fix.fix.mode > MODE_2D) &&
1542 (vgl->last_fix.fix.mode <= MODE_2D) &&
1543 ((cur_timestamp - last_timestamp) < 2)) {
1544 g_free(last_tp->data);
1545 vgl->realtime_track->trackpoints = g_list_delete_link(vgl->realtime_track->trackpoints, last_tp);
1546 replace = TRUE;
1547 }
1548 if (replace ||
1549 ((cur_timestamp != last_timestamp) &&
1550 ((forced ||
1551 ((heading < last_heading) && (heading < (last_heading - 3))) ||
1552 ((heading > last_heading) && (heading > (last_heading + 3))) ||
1553 ((alt != VIK_DEFAULT_ALTITUDE) && (alt != last_alt)))))) {
1554 /* TODO: check for new segments */
1555 VikTrackpoint *tp = vik_trackpoint_new();
1556 tp->newsegment = FALSE;
1557 tp->has_timestamp = TRUE;
1558 tp->timestamp = vgl->realtime_fix.fix.time;
1559 tp->altitude = alt;
1560 /* speed only available for 3D fix. Check for NAN when use this speed */
1561 tp->speed = vgl->realtime_fix.fix.speed;
1562 tp->course = vgl->realtime_fix.fix.track;
1563 tp->nsats = vgl->realtime_fix.satellites_used;
1564 tp->fix_mode = vgl->realtime_fix.fix.mode;
1565
1566 ll.lat = vgl->realtime_fix.fix.latitude;
1567 ll.lon = vgl->realtime_fix.fix.longitude;
1568 vik_coord_load_from_latlon(&tp->coord,
1569 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1570
1571 vik_track_add_trackpoint ( vgl->realtime_track, tp, TRUE ); // Ensure bounds is recalculated
1572 vgl->realtime_fix.dirty = FALSE;
1573 vgl->realtime_fix.satellites_used = 0;
1574 vgl->last_fix = vgl->realtime_fix;
1575 return tp;
1576 }
1577 }
1578 return NULL;
1579}
1580
1581#define VIK_SETTINGS_GPS_STATUSBAR_FORMAT "gps_statusbar_format"
1582
1583static void update_statusbar ( VikGpsLayer *vgl, VikWindow *vw )
1584{
1585 gchar *statusbar_format_code = NULL;
1586 gboolean need2free = FALSE;
1587 if ( !a_settings_get_string ( VIK_SETTINGS_GPS_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
1588 // Otherwise use default
1589 statusbar_format_code = g_strdup ( "GSA" );
1590 need2free = TRUE;
1591 }
1592
1593 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, vgl->trkpt, vgl->trkpt_prev, vgl->realtime_track );
1594 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
1595 g_free ( msg );
1596
1597 if ( need2free )
1598 g_free ( statusbar_format_code );
1599
1600}
1601
1602static void gpsd_raw_hook(VglGpsd *vgpsd, gchar *data)
1603{
1604 gboolean update_all = FALSE;
1605 VikGpsLayer *vgl = vgpsd->vgl;
1606
1607 if (!vgl->realtime_tracking) {
1608 g_warning("%s: receiving GPS data while not in realtime mode", __PRETTY_FUNCTION__);
1609 return;
1610 }
1611
1612 if ((vgpsd->gpsd.fix.mode >= MODE_2D) &&
1613 !isnan(vgpsd->gpsd.fix.latitude) &&
1614 !isnan(vgpsd->gpsd.fix.longitude)) {
1615
1616 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vgl));
1617 VikViewport *vvp = vik_window_viewport(vw);
1618 vgl->realtime_fix.fix = vgpsd->gpsd.fix;
1619 vgl->realtime_fix.satellites_used = vgpsd->gpsd.satellites_used;
1620 vgl->realtime_fix.dirty = TRUE;
1621
1622 struct LatLon ll;
1623 VikCoord vehicle_coord;
1624
1625 ll.lat = vgl->realtime_fix.fix.latitude;
1626 ll.lon = vgl->realtime_fix.fix.longitude;
1627 vik_coord_load_from_latlon(&vehicle_coord,
1628 vik_trw_layer_get_coord_mode(vgl->trw_children[TRW_REALTIME]), &ll);
1629
1630 if ((vgl->vehicle_position == VEHICLE_POSITION_CENTERED) ||
1631 (vgl->realtime_jump_to_start && vgl->first_realtime_trackpoint)) {
1632 vik_viewport_set_center_coord(vvp, &vehicle_coord, FALSE);
1633 update_all = TRUE;
1634 }
1635 else if (vgl->vehicle_position == VEHICLE_POSITION_ON_SCREEN) {
1636 const int hdiv = 6;
1637 const int vdiv = 6;
1638 const int px = 20; /* adjust ment in pixels to make sure vehicle is inside the box */
1639 gint width = vik_viewport_get_width(vvp);
1640 gint height = vik_viewport_get_height(vvp);
1641 gint vx, vy;
1642
1643 vik_viewport_coord_to_screen(vvp, &vehicle_coord, &vx, &vy);
1644 update_all = TRUE;
1645 if (vx < (width/hdiv))
1646 vik_viewport_set_center_screen(vvp, vx - width/2 + width/hdiv + px, vy);
1647 else if (vx > (width - width/hdiv))
1648 vik_viewport_set_center_screen(vvp, vx + width/2 - width/hdiv - px, vy);
1649 else if (vy < (height/vdiv))
1650 vik_viewport_set_center_screen(vvp, vx, vy - height/2 + height/vdiv + px);
1651 else if (vy > (height - height/vdiv))
1652 vik_viewport_set_center_screen(vvp, vx, vy + height/2 - height/vdiv - px);
1653 else
1654 update_all = FALSE;
1655 }
1656
1657 vgl->first_realtime_trackpoint = FALSE;
1658
1659 vgl->trkpt = create_realtime_trackpoint ( vgl, FALSE );
1660
1661 if ( vgl->trkpt ) {
1662 if ( vgl->realtime_update_statusbar )
1663 update_statusbar ( vgl, vw );
1664 vgl->trkpt_prev = vgl->trkpt;
1665 }
1666
1667 vik_layer_emit_update ( update_all ? VIK_LAYER(vgl) : VIK_LAYER(vgl->trw_children[TRW_REALTIME]) ); // NB update from background thread
1668 }
1669}
1670
1671static gboolean gpsd_data_available(GIOChannel *source, GIOCondition condition, gpointer data)
1672{
1673 VikGpsLayer *vgl = data;
1674 if (condition == G_IO_IN) {
1675#if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1676 if (!gps_poll(&vgl->vgpsd->gpsd)) {
1677#elif GPSD_API_MAJOR_VERSION == 5
1678 if (gps_read(&vgl->vgpsd->gpsd) > -1) {
1679 // Reuse old function to perform operations on the new GPS data
1680 gpsd_raw_hook(vgl->vgpsd, NULL);
1681#else
1682 // Broken compile
1683#endif
1684 return TRUE;
1685 }
1686 else {
1687 g_warning("Disconnected from gpsd. Trying to reconnect");
1688 rt_gpsd_disconnect(vgl);
1689 rt_gpsd_connect(vgl, FALSE);
1690 }
1691 }
1692 return FALSE; /* no further calling */
1693}
1694
1695static gchar *make_track_name(VikTrwLayer *vtl)
1696{
1697 const gchar basename[] = "REALTIME";
1698 const gint bufsize = sizeof(basename) + 5;
1699 gchar *name = g_malloc(bufsize);
1700 strcpy(name, basename);
1701 gint i = 2;
1702
1703 while (vik_trw_layer_get_track(vtl, name) != NULL) {
1704 g_snprintf(name, bufsize, "%s#%d", basename, i);
1705 i++;
1706 }
1707 return(name);
1708
1709}
1710
1711static gboolean rt_gpsd_try_connect(gpointer *data)
1712{
1713 VikGpsLayer *vgl = (VikGpsLayer *)data;
1714#if GPSD_API_MAJOR_VERSION == 3
1715 struct gps_data_t *gpsd = gps_open(vgl->gpsd_host, vgl->gpsd_port);
1716
1717 if (gpsd == NULL) {
1718#elif GPSD_API_MAJOR_VERSION == 4
1719 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1720
1721 if (gps_open_r(vgl->gpsd_host, vgl->gpsd_port, /*(struct gps_data_t *)*/vgl->vgpsd) != 0) {
1722#elif GPSD_API_MAJOR_VERSION == 5
1723 vgl->vgpsd = g_malloc(sizeof(VglGpsd));
1724 if (gps_open(vgl->gpsd_host, vgl->gpsd_port, &vgl->vgpsd->gpsd) != 0) {
1725#else
1726// Delibrately break compilation...
1727#endif
1728 g_warning("Failed to connect to gpsd at %s (port %s). Will retry in %d seconds",
1729 vgl->gpsd_host, vgl->gpsd_port, vgl->gpsd_retry_interval);
1730 return TRUE; /* keep timer running */
1731 }
1732
1733#if GPSD_API_MAJOR_VERSION == 3
1734 vgl->vgpsd = realloc(gpsd, sizeof(VglGpsd));
1735#endif
1736 vgl->vgpsd->vgl = vgl;
1737
1738 vgl->realtime_fix.dirty = vgl->last_fix.dirty = FALSE;
1739 /* track alt/time graph uses VIK_DEFAULT_ALTITUDE (0.0) as invalid */
1740 vgl->realtime_fix.fix.altitude = vgl->last_fix.fix.altitude = VIK_DEFAULT_ALTITUDE;
1741 vgl->realtime_fix.fix.speed = vgl->last_fix.fix.speed = NAN;
1742
1743 if (vgl->realtime_record) {
1744 VikTrwLayer *vtl = vgl->trw_children[TRW_REALTIME];
1745 vgl->realtime_track = vik_track_new();
1746 vgl->realtime_track->visible = TRUE;
1747 vik_trw_layer_add_track(vtl, make_track_name(vtl), vgl->realtime_track);
1748 }
1749
1750#if GPSD_API_MAJOR_VERSION == 3 || GPSD_API_MAJOR_VERSION == 4
1751 gps_set_raw_hook(&vgl->vgpsd->gpsd, gpsd_raw_hook);
1752#endif
1753
1754 vgl->realtime_io_channel = g_io_channel_unix_new(vgl->vgpsd->gpsd.gps_fd);
1755 vgl->realtime_io_watch_id = g_io_add_watch( vgl->realtime_io_channel,
1756 G_IO_IN | G_IO_ERR | G_IO_HUP, gpsd_data_available, vgl);
1757
1758#if GPSD_API_MAJOR_VERSION == 3
1759 gps_query(&vgl->vgpsd->gpsd, "w+x");
1760#endif
1761#if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5
1762 gps_stream(&vgl->vgpsd->gpsd, WATCH_ENABLE, NULL);
1763#endif
1764
1765 return FALSE; /* no longer called by timeout */
1766}
1767
1768static gboolean rt_ask_retry(VikGpsLayer *vgl)
1769{
1770 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER(vgl),
1771 GTK_DIALOG_DESTROY_WITH_PARENT,
1772 GTK_MESSAGE_QUESTION,
1773 GTK_BUTTONS_YES_NO,
1774 "Failed to connect to gpsd at %s (port %s)\n"
1775 "Should Viking keep trying (every %d seconds)?",
1776 vgl->gpsd_host, vgl->gpsd_port,
1777 vgl->gpsd_retry_interval);
1778
1779 gint res = gtk_dialog_run(GTK_DIALOG(dialog));
1780 gtk_widget_destroy(dialog);
1781 return (res == GTK_RESPONSE_YES);
1782}
1783
1784static gboolean rt_gpsd_connect(VikGpsLayer *vgl, gboolean ask_if_failed)
1785{
1786 vgl->realtime_retry_timer = 0;
1787 if (rt_gpsd_try_connect((gpointer *)vgl)) {
1788 if (vgl->gpsd_retry_interval <= 0) {
1789 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);
1790 return FALSE;
1791 }
1792 else if (ask_if_failed && !rt_ask_retry(vgl))
1793 return FALSE;
1794 else
1795 vgl->realtime_retry_timer = g_timeout_add_seconds(vgl->gpsd_retry_interval,
1796 (GSourceFunc)rt_gpsd_try_connect, (gpointer *)vgl);
1797 }
1798 return TRUE;
1799}
1800
1801static void rt_gpsd_disconnect(VikGpsLayer *vgl)
1802{
1803 if (vgl->realtime_retry_timer) {
1804 g_source_remove(vgl->realtime_retry_timer);
1805 vgl->realtime_retry_timer = 0;
1806 }
1807 if (vgl->realtime_io_watch_id) {
1808 g_source_remove(vgl->realtime_io_watch_id);
1809 vgl->realtime_io_watch_id = 0;
1810 }
1811 if (vgl->realtime_io_channel) {
1812 GError *error = NULL;
1813 g_io_channel_shutdown (vgl->realtime_io_channel, FALSE, &error);
1814 vgl->realtime_io_channel = NULL;
1815 }
1816 if (vgl->vgpsd) {
1817#if GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5
1818 gps_stream(&vgl->vgpsd->gpsd, WATCH_DISABLE, NULL);
1819#endif
1820 gps_close(&vgl->vgpsd->gpsd);
1821#if GPSD_API_MAJOR_VERSION == 3
1822 free(vgl->vgpsd);
1823#elif GPSD_API_MAJOR_VERSION == 4 || GPSD_API_MAJOR_VERSION == 5
1824 g_free(vgl->vgpsd);
1825#endif
1826 vgl->vgpsd = NULL;
1827 }
1828
1829 if (vgl->realtime_record && vgl->realtime_track) {
1830 if ((vgl->realtime_track->trackpoints == NULL) || (vgl->realtime_track->trackpoints->next == NULL))
1831 vik_trw_layer_delete_track(vgl->trw_children[TRW_REALTIME], vgl->realtime_track);
1832 vgl->realtime_track = NULL;
1833 }
1834}
1835
1836static void gps_start_stop_tracking_cb( gpointer layer_and_vlp[2])
1837{
1838 VikGpsLayer *vgl = (VikGpsLayer *)layer_and_vlp[0];
1839 vgl->realtime_tracking = (vgl->realtime_tracking == FALSE);
1840
1841 /* Make sure we are still in the boat with libgps */
1842 g_assert((VIK_GPS_MODE_2D == MODE_2D) && (VIK_GPS_MODE_3D == MODE_3D));
1843
1844 if (vgl->realtime_tracking) {
1845 vgl->first_realtime_trackpoint = TRUE;
1846 if (!rt_gpsd_connect(vgl, TRUE)) {
1847 vgl->first_realtime_trackpoint = FALSE;
1848 vgl->realtime_tracking = FALSE;
1849 vgl->trkpt = NULL;
1850 }
1851 }
1852 else { /* stop realtime tracking */
1853 vgl->first_realtime_trackpoint = FALSE;
1854 vgl->trkpt = NULL;
1855 rt_gpsd_disconnect(vgl);
1856 }
1857}
1858#endif /* VIK_CONFIG_REALTIME_GPS_TRACKING */