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