]> git.street.me.uk Git - andy/viking.git/blame - src/datasource_gps.c
Merge branch 'Geotagging'
[andy/viking.git] / src / datasource_gps.c
CommitLineData
7b3479e3
EB
1/*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3 *
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
a482007a
GB
5 * Copyright (C) 2006, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2010, Rob Norris <rw_norris@hotmail.com>
7b3479e3
EB
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
8aad4ca3
GB
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
7b3479e3 26#include <string.h>
99163c34
RN
27#ifdef HAVE_UNISTD_H
28#include <unistd.h>
29#endif
4c77d5e0 30
99163c34 31#include <glib/gstdio.h>
7b3479e3 32#include <glib/gprintf.h>
4c77d5e0 33#include <glib/gi18n.h>
7b3479e3
EB
34
35#include "viking.h"
36#include "babel.h"
37#include "gpx.h"
38#include "acquire.h"
39
40static gboolean gps_acquire_in_progress = FALSE;
41
3f6db253 42static gint last_active = -1;
7f23d292
RN
43static gboolean last_get_tracks = TRUE;
44static gboolean last_get_waypoints = TRUE;
8627bd99 45
65f0ccab 46static gpointer datasource_gps_init_func ( );
7b3479e3 47static void datasource_gps_get_cmd_string ( gpointer add_widgets_data_not_used, gchar **babelargs, gchar **input_file );
65f0ccab 48static void datasource_gps_cleanup ( gpointer user_data );
7b3479e3 49static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_dialog_widgets_t *w );
65f0ccab
AF
50static void datasource_gps_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data );
51static void datasource_gps_add_progress_widgets ( GtkWidget *dialog, gpointer user_data );
2b756ea0 52static void datasource_gps_off ( gpointer add_widgets_data_not_used, gchar **babelargs, gchar **input_file );
7b3479e3
EB
53
54VikDataSourceInterface vik_datasource_gps_interface = {
4c77d5e0
GB
55 N_("Acquire from GPS"),
56 N_("Acquired from GPS"),
7b3479e3 57 VIK_DATASOURCE_GPSBABEL_DIRECT,
805d282e 58 VIK_DATASOURCE_CREATENEWLAYER,
28c82d8b
EB
59 VIK_DATASOURCE_INPUTTYPE_NONE,
60 TRUE,
d2525524 61 TRUE,
65f0ccab 62 (VikDataSourceInitFunc) datasource_gps_init_func,
92255687 63 (VikDataSourceCheckExistenceFunc) NULL,
65f0ccab 64 (VikDataSourceAddSetupWidgetsFunc) datasource_gps_add_setup_widgets,
7b3479e3 65 (VikDataSourceGetCmdStringFunc) datasource_gps_get_cmd_string,
0944940f 66 (VikDataSourceProcessFunc) NULL,
7b3479e3
EB
67 (VikDataSourceProgressFunc) datasource_gps_progress,
68 (VikDataSourceAddProgressWidgetsFunc) datasource_gps_add_progress_widgets,
2b756ea0
RN
69 (VikDataSourceCleanupFunc) datasource_gps_cleanup,
70 (VikDataSourceOffFunc) datasource_gps_off
7b3479e3
EB
71};
72
73/*********************************************************
74 * Definitions and routines for acquiring data from GPS
75 *********************************************************/
76
65f0ccab 77/* widgets in setup dialog specific to GPS */
7b3479e3
EB
78/* widgets in progress dialog specific to GPS */
79/* also counts needed for progress */
80typedef struct {
65f0ccab
AF
81 /* setup dialog */
82 GtkWidget *proto_l;
83 GtkComboBox *proto_b;
84 GtkWidget *ser_l;
85 GtkComboBox *ser_b;
2b756ea0
RN
86 GtkWidget *off_request_l;
87 GtkCheckButton *off_request_b;
d6c58ab9
RN
88 GtkWidget *get_tracks_l;
89 GtkCheckButton *get_tracks_b;
90 GtkWidget *get_waypoints_l;
91 GtkCheckButton *get_waypoints_b;
65f0ccab
AF
92
93 /* progress dialog */
7b3479e3
EB
94 GtkWidget *gps_label;
95 GtkWidget *ver_label;
96 GtkWidget *id_label;
97 GtkWidget *wp_label;
98 GtkWidget *trk_label;
99 GtkWidget *progress_label;
65f0ccab
AF
100
101 /* state */
7b3479e3
EB
102 int total_count;
103 int count;
65f0ccab
AF
104} gps_user_data_t;
105
106static gpointer datasource_gps_init_func ()
107{
108 return g_malloc (sizeof(gps_user_data_t));
109}
7b3479e3 110
65f0ccab 111static void datasource_gps_get_cmd_string ( gpointer user_data, gchar **babelargs, gchar **input_file )
7b3479e3 112{
cf7fdc15
GB
113 char *ser = NULL;
114 char *device = NULL;
d6c58ab9
RN
115 char *tracks = NULL;
116 char *waypoints = NULL;
65f0ccab
AF
117 gps_user_data_t *w = (gps_user_data_t *)user_data;
118
7b3479e3
EB
119 if (gps_acquire_in_progress) {
120 *babelargs = *input_file = NULL;
121 }
65f0ccab
AF
122
123 gps_acquire_in_progress = TRUE;
124
8627bd99
GB
125 last_active = gtk_combo_box_get_active(GTK_COMBO_BOX(w->proto_b));
126 device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
2b756ea0 127
7f23d292
RN
128 last_get_tracks = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->get_tracks_b));
129 if (last_get_tracks)
d6c58ab9
RN
130 tracks = "-t";
131 else
132 tracks = "";
7f23d292
RN
133 last_get_waypoints = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->get_waypoints_b));
134 if (last_get_waypoints)
d6c58ab9
RN
135 waypoints = "-w";
136 else
137 waypoints = "";
138
139 *babelargs = g_strdup_printf("-D 9 %s %s -i %s", tracks, waypoints, device);
cf7fdc15
GB
140 /* device points to static content => no free */
141 device = NULL;
d6c58ab9
RN
142 tracks = NULL;
143 waypoints = NULL;
144
cf7fdc15 145 ser = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w->ser_b));
cf7fdc15 146 *input_file = g_strdup(ser);
7b3479e3 147
4c77d5e0 148 g_debug(_("using cmdline '%s' and file '%s'\n"), *babelargs, *input_file);
7b3479e3
EB
149}
150
2b756ea0
RN
151
152static void datasource_gps_off ( gpointer user_data, gchar **babelargs, gchar **input_file )
153{
2b756ea0
RN
154 char *ser = NULL;
155 char *device = NULL;
2b756ea0
RN
156 gps_user_data_t *w = (gps_user_data_t *)user_data;
157
158 if (gps_acquire_in_progress) {
159 *babelargs = *input_file = NULL;
160 }
161
162 /* See if we should turn off the device */
163 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->off_request_b))) {
164 return;
165 }
166
8627bd99
GB
167 last_active = gtk_combo_box_get_active(GTK_COMBO_BOX(w->proto_b));
168 device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
169 if (!strcmp(device, "garmin")) {
2b756ea0 170 device = "garmin,power_off";
4136d029 171 }
8627bd99 172 else if (!strcmp(device, "navilink")) {
4136d029
RN
173 device = "navilink,power_off";
174 }
175 else {
2b756ea0
RN
176 return;
177 }
178
179 *babelargs = g_strdup_printf("-i %s", device);
180 /* device points to static content => no free */
181 device = NULL;
182
2b756ea0 183 ser = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w->ser_b));
2b756ea0
RN
184 *input_file = g_strdup(ser);
185}
186
187
65f0ccab 188static void datasource_gps_cleanup ( gpointer user_data )
7b3479e3 189{
65f0ccab 190 g_free ( user_data );
7b3479e3
EB
191 gps_acquire_in_progress = FALSE;
192}
193
194static void set_total_count(gint cnt, acq_dialog_widgets_t *w)
195{
cf7fdc15 196 gchar *s = NULL;
7b3479e3
EB
197 gdk_threads_enter();
198 if (w->ok) {
65f0ccab 199 gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
97634600
GB
200 const gchar *tmp_str;
201 if (gps_data->progress_label == gps_data->wp_label)
202 tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt);
203 else
204 tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt);
205 s = g_strdup_printf(tmp_str, cnt);
7b3479e3
EB
206 gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s );
207 gtk_widget_show ( gps_data->progress_label );
208 gps_data->total_count = cnt;
209 }
cf7fdc15 210 g_free(s); s = NULL;
7b3479e3
EB
211 gdk_threads_leave();
212}
213
214static void set_current_count(gint cnt, acq_dialog_widgets_t *w)
215{
cf7fdc15 216 gchar *s = NULL;
7b3479e3
EB
217 gdk_threads_enter();
218 if (w->ok) {
65f0ccab 219 gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
7b3479e3
EB
220
221 if (cnt < gps_data->total_count) {
4c77d5e0 222 s = g_strdup_printf(_("Downloaded %d out of %d %s..."), cnt, gps_data->total_count, (gps_data->progress_label == gps_data->wp_label) ? "waypoints" : "trackpoints");
7b3479e3 223 } else {
4c77d5e0 224 s = g_strdup_printf(_("Downloaded %d %s."), cnt, (gps_data->progress_label == gps_data->wp_label) ? "waypoints" : "trackpoints");
7b3479e3
EB
225 }
226 gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s );
227 }
cf7fdc15 228 g_free(s); s = NULL;
7b3479e3
EB
229 gdk_threads_leave();
230}
231
232static void set_gps_info(const gchar *info, acq_dialog_widgets_t *w)
233{
cf7fdc15 234 gchar *s = NULL;
7b3479e3
EB
235 gdk_threads_enter();
236 if (w->ok) {
4c77d5e0 237 s = g_strdup_printf(_("GPS Device: %s"), info);
65f0ccab 238 gtk_label_set_text ( GTK_LABEL(((gps_user_data_t *)w->user_data)->gps_label), s );
7b3479e3 239 }
cf7fdc15 240 g_free(s); s = NULL;
7b3479e3
EB
241 gdk_threads_leave();
242}
243
244/*
245 * This routine relies on gpsbabel's diagnostic output to display the progress information.
246 * These outputs differ when different GPS devices are used, so we will need to test
247 * them on several and add the corresponding support.
248 */
249static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_dialog_widgets_t *w )
250{
251 gchar *line;
65f0ccab 252 gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
7b3479e3 253
7b3479e3
EB
254 switch(c) {
255 case BABEL_DIAG_OUTPUT:
256 line = (gchar *)data;
257
258 /* tells us how many items there will be */
259 if (strstr(line, "Xfer Wpt")) {
260 gps_data->progress_label = gps_data->wp_label;
261 }
262 if (strstr(line, "Xfer Trk")) {
263 gps_data->progress_label = gps_data->trk_label;
264 }
265 if (strstr(line, "PRDDAT")) {
266 gchar **tokens = g_strsplit(line, " ", 0);
267 gchar info[128];
268 int ilen = 0;
269 int i;
c83b5ad9
QT
270 int n_tokens = 0;
271
272 while (tokens[n_tokens])
273 n_tokens++;
274
275 if (n_tokens > 8) {
276 for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
277 guint ch;
278 sscanf(tokens[i], "%x", &ch);
279 info[ilen++] = ch;
280 }
281 info[ilen++] = 0;
282 set_gps_info(info, w);
7b3479e3 283 }
c83b5ad9 284 g_strfreev(tokens);
7b3479e3 285 }
0a3f9142
RN
286 /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
287 if (strstr(line, "Unit:")) {
288 gchar **tokens = g_strsplit(line, "\t", 0);
289 int n_tokens = 0;
290 while (tokens[n_tokens])
291 n_tokens++;
292
293 if (n_tokens > 1) {
294 set_gps_info(tokens[1], w);
295 }
296 g_strfreev(tokens);
297 }
7b3479e3
EB
298 if (strstr(line, "RECORD")) {
299 int lsb, msb, cnt;
300
c83b5ad9
QT
301 if (strlen(line) > 20) {
302 sscanf(line+17, "%x", &lsb);
303 sscanf(line+20, "%x", &msb);
304 cnt = lsb + msb * 256;
305 set_total_count(cnt, w);
306 gps_data->count = 0;
307 }
7b3479e3
EB
308 }
309 if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
310 gps_data->count++;
311 set_current_count(gps_data->count, w);
312 }
313 break;
314 case BABEL_DONE:
315 break;
316 default:
317 break;
318 }
319}
320
8627bd99
GB
321void append_element (gpointer elem, gpointer user_data)
322{
323 GtkComboBox *combo = GTK_COMBO_BOX (user_data);
324 const gchar *text = ((BabelDevice*)elem)->label;
325 gtk_combo_box_append_text (combo, text);
326}
327
3f6db253
RN
328static gint find_entry = -1;
329static gint garmin_entry = -1;
330
331static void find_garmin (gpointer elem, gpointer user_data)
332{
333 const gchar *name = ((BabelDevice*)elem)->name;
334 find_entry++;
335 if (!strcmp(name, "garmin")) {
336 garmin_entry = find_entry;
337 }
338}
339
65f0ccab
AF
340void datasource_gps_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data )
341{
342 gps_user_data_t *w = (gps_user_data_t *)user_data;
d6c58ab9 343 GtkTable *box, *data_type_box;
65f0ccab 344
4c77d5e0 345 w->proto_l = gtk_label_new (_("GPS Protocol:"));
65f0ccab 346 w->proto_b = GTK_COMBO_BOX(gtk_combo_box_new_text ());
8627bd99 347 g_list_foreach (a_babel_device_list, append_element, w->proto_b);
3f6db253
RN
348
349 // Maintain default to Garmin devices (assumed most popular/numerous device)
350 if ( last_active < 0 ) {
351 find_entry = -1;
352 g_list_foreach (a_babel_device_list, find_garmin, NULL);
353 if ( garmin_entry < 0 )
354 // Not found - so set it to the first entry
355 last_active = 0;
356 else
357 // Found
358 last_active = garmin_entry;
359 }
360
8627bd99 361 gtk_combo_box_set_active (w->proto_b, last_active);
65f0ccab
AF
362 g_object_ref(w->proto_b);
363
4c77d5e0 364 w->ser_l = gtk_label_new (_("Serial Port:"));
65f0ccab 365 w->ser_b = GTK_COMBO_BOX(gtk_combo_box_entry_new_text ());
8d70f073
MA
366#ifdef WINDOWS
367 gtk_combo_box_append_text (w->ser_b, "com1");
368#else
99163c34
RN
369 /* Here just try to see if the device is available which gets passed onto gpsbabel
370 List USB devices first as these will generally only be present if autogenerated by udev or similar
371 User is still able to set their own free text entry */
372 if (g_access ("/dev/ttyUSB0", R_OK) == 0)
373 gtk_combo_box_append_text (w->ser_b, "/dev/ttyUSB0");
374 if (g_access ("/dev/ttyUSB1", R_OK) == 0)
375 gtk_combo_box_append_text (w->ser_b, "/dev/ttyUSB1");
376 if (g_access ("/dev/ttyS0", R_OK) == 0)
377 gtk_combo_box_append_text (w->ser_b, "/dev/ttyS0");
378 if (g_access ("/dev/ttyS1", R_OK) == 0)
379 gtk_combo_box_append_text (w->ser_b, "/dev/ttyS1");
8d70f073 380#endif
7963d365 381 gtk_combo_box_append_text (w->ser_b, "usb:");
65f0ccab
AF
382 gtk_combo_box_set_active (w->ser_b, 0);
383 g_object_ref(w->ser_b);
384
4136d029 385 w->off_request_l = gtk_label_new (_("Turn Off After Transfer\n(Garmin/NAViLink Only)"));
2b756ea0
RN
386 w->off_request_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
387
d6c58ab9
RN
388 w->get_tracks_l = gtk_label_new (_("Tracks:"));
389 w->get_tracks_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
7f23d292 390 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_tracks_b), last_get_tracks);
d6c58ab9
RN
391
392 w->get_waypoints_l = gtk_label_new (_("Waypoints:"));
393 w->get_waypoints_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
7f23d292 394 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_waypoints_b), last_get_waypoints);
d6c58ab9
RN
395
396 box = GTK_TABLE(gtk_table_new(2, 4, FALSE));
397 data_type_box = GTK_TABLE(gtk_table_new(4, 1, FALSE));
398
65f0ccab
AF
399 gtk_table_attach_defaults(box, GTK_WIDGET(w->proto_l), 0, 1, 0, 1);
400 gtk_table_attach_defaults(box, GTK_WIDGET(w->proto_b), 1, 2, 0, 1);
401 gtk_table_attach_defaults(box, GTK_WIDGET(w->ser_l), 0, 1, 1, 2);
402 gtk_table_attach_defaults(box, GTK_WIDGET(w->ser_b), 1, 2, 1, 2);
d6c58ab9
RN
403 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_tracks_l), 0, 1, 0, 1);
404 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_tracks_b), 1, 2, 0, 1);
405 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_waypoints_l), 2, 3, 0, 1);
406 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_waypoints_b), 3, 4, 0, 1);
407 gtk_table_attach_defaults(box, GTK_WIDGET(data_type_box), 0, 2, 2, 3);
408 gtk_table_attach_defaults(box, GTK_WIDGET(w->off_request_l), 0, 1, 3, 4);
409 gtk_table_attach_defaults(box, GTK_WIDGET(w->off_request_b), 1, 3, 3, 4);
65f0ccab
AF
410 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), GTK_WIDGET(box), FALSE, FALSE, 5 );
411
412 gtk_widget_show_all ( dialog );
413}
414
415void datasource_gps_add_progress_widgets ( GtkWidget *dialog, gpointer user_data )
7b3479e3
EB
416{
417 GtkWidget *gpslabel, *verlabel, *idlabel, *wplabel, *trklabel;
418
65f0ccab 419 gps_user_data_t *w_gps = (gps_user_data_t *)user_data;
7b3479e3 420
4c77d5e0 421 gpslabel = gtk_label_new (_("GPS device: N/A"));
7b3479e3
EB
422 verlabel = gtk_label_new ("");
423 idlabel = gtk_label_new ("");
424 wplabel = gtk_label_new ("");
425 trklabel = gtk_label_new ("");
426
427 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), gpslabel, FALSE, FALSE, 5 );
428 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), wplabel, FALSE, FALSE, 5 );
429 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), trklabel, FALSE, FALSE, 5 );
430
7b3479e3
EB
431 gtk_widget_show_all ( dialog );
432
433 w_gps->gps_label = gpslabel;
434 w_gps->id_label = idlabel;
435 w_gps->ver_label = verlabel;
436 w_gps->progress_label = w_gps->wp_label = wplabel;
437 w_gps->trk_label = trklabel;
438 w_gps->total_count = -1;
7b3479e3 439}