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