]> git.street.me.uk Git - andy/viking.git/blob - src/datasource_gps.c
Use $(top_srcdir) to locate AUTHORS file
[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 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   (VikDataSourceInitFunc)               datasource_gps_init_func,
62   (VikDataSourceCheckExistenceFunc)     NULL,
63   (VikDataSourceAddSetupWidgetsFunc)    datasource_gps_add_setup_widgets,
64   (VikDataSourceGetCmdStringFunc)       datasource_gps_get_cmd_string,
65   (VikDataSourceProgressFunc)           datasource_gps_progress,
66   (VikDataSourceAddProgressWidgetsFunc) datasource_gps_add_progress_widgets,
67   (VikDataSourceCleanupFunc)            datasource_gps_cleanup,
68   (VikDataSourceOffFunc)                datasource_gps_off
69 };
70
71 /*********************************************************
72  * Definitions and routines for acquiring data from GPS
73  *********************************************************/
74
75 /* widgets in setup dialog specific to GPS */
76 /* widgets in progress dialog specific to GPS */
77 /* also counts needed for progress */
78 typedef struct {
79   /* setup dialog */
80   GtkWidget *proto_l;
81   GtkComboBox *proto_b;
82   GtkWidget *ser_l;
83   GtkComboBox *ser_b;
84   GtkWidget *off_request_l;
85   GtkCheckButton *off_request_b;
86
87   /* progress dialog */
88   GtkWidget *gps_label;
89   GtkWidget *ver_label;
90   GtkWidget *id_label;
91   GtkWidget *wp_label;
92   GtkWidget *trk_label;
93   GtkWidget *progress_label;
94
95   /* state */
96   int total_count;
97   int count;
98 } gps_user_data_t;
99
100 static gpointer datasource_gps_init_func ()
101 {
102   return g_malloc (sizeof(gps_user_data_t));
103 }
104
105 static void datasource_gps_get_cmd_string ( gpointer user_data, gchar **babelargs, gchar **input_file )
106 {
107   char *proto = NULL;
108   char *ser = NULL;
109   char *device = NULL;
110 #ifndef USE_NEW_COMBO_BOX
111   GtkTreeIter iter;
112 #endif
113   gps_user_data_t *w = (gps_user_data_t *)user_data;
114
115   if (gps_acquire_in_progress) {
116     *babelargs = *input_file = NULL;
117   }
118   
119   gps_acquire_in_progress = TRUE;
120
121 #ifdef USE_NEW_COMBO_BOX
122   proto = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w->proto_b));
123 #else
124   proto = gtk_combo_box_get_active_iter(GTK_COMBO_BOX(w->proto_b),&iter);
125 #endif
126   if (!strcmp(proto, "Garmin")) {
127     device = "garmin";
128   } else {
129     device = "magellan";
130   }
131
132   *babelargs = g_strdup_printf("-D 9 -t -w -i %s", device);
133   /* device points to static content => no free */
134   device = NULL;
135   
136   /* Old stuff */
137 #ifdef USE_NEW_COMBO_BOX
138   ser = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w->ser_b));
139 #else
140   ser = gtk_combo_box_get_active_iter(GTK_COMBO_BOX(w->ser_b),&iter);
141 #endif
142   *input_file = g_strdup(ser);
143
144   g_debug(_("using cmdline '%s' and file '%s'\n"), *babelargs, *input_file);
145 }
146
147
148 static void datasource_gps_off ( gpointer user_data, gchar **babelargs, gchar **input_file )
149 {
150   char *proto = NULL;
151   char *ser = NULL;
152   char *device = NULL;
153 #ifndef USE_NEW_COMBO_BOX
154   GtkTreeIter iter;
155 #endif
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   
167 #ifdef USE_NEW_COMBO_BOX
168   proto = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w->proto_b));
169 #else
170   proto = gtk_combo_box_get_active_iter(GTK_COMBO_BOX(w->proto_b),&iter);
171 #endif
172   if (!strcmp(proto, "Garmin")) {
173     device = "garmin,power_off";
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   /* Old stuff */
183 #ifdef USE_NEW_COMBO_BOX
184   ser = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w->ser_b));
185 #else
186   ser = gtk_combo_box_get_active_iter(GTK_COMBO_BOX(w->ser_b),&iter);
187 #endif
188   *input_file = g_strdup(ser);
189 }
190
191
192 static void datasource_gps_cleanup ( gpointer user_data )
193 {
194   g_free ( user_data );
195   gps_acquire_in_progress = FALSE;
196 }
197
198 static void set_total_count(gint cnt, acq_dialog_widgets_t *w)
199 {
200   gchar *s = NULL;
201   gdk_threads_enter();
202   if (w->ok) {
203     gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
204     const gchar *tmp_str;
205     if (gps_data->progress_label == gps_data->wp_label)
206       tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt);
207     else
208       tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt);
209     s = g_strdup_printf(tmp_str, cnt);
210     gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s );
211     gtk_widget_show ( gps_data->progress_label );
212     gps_data->total_count = cnt;
213   }
214   g_free(s); s = NULL;
215   gdk_threads_leave();
216 }
217
218 static void set_current_count(gint cnt, acq_dialog_widgets_t *w)
219 {
220   gchar *s = NULL;
221   gdk_threads_enter();
222   if (w->ok) {
223     gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
224
225     if (cnt < gps_data->total_count) {
226       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");
227     } else {
228       s = g_strdup_printf(_("Downloaded %d %s."), cnt, (gps_data->progress_label == gps_data->wp_label) ? "waypoints" : "trackpoints");
229     }     
230     gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s );
231   }
232   g_free(s); s = NULL;
233   gdk_threads_leave();
234 }
235
236 static void set_gps_info(const gchar *info, acq_dialog_widgets_t *w)
237 {
238   gchar *s = NULL;
239   gdk_threads_enter();
240   if (w->ok) {
241     s = g_strdup_printf(_("GPS Device: %s"), info);
242     gtk_label_set_text ( GTK_LABEL(((gps_user_data_t *)w->user_data)->gps_label), s );
243   }
244   g_free(s); s = NULL;
245   gdk_threads_leave();
246 }
247
248 /* 
249  * This routine relies on gpsbabel's diagnostic output to display the progress information. 
250  * These outputs differ when different GPS devices are used, so we will need to test
251  * them on several and add the corresponding support.
252  */
253 static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_dialog_widgets_t *w )
254 {
255   gchar *line;
256   gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
257
258   switch(c) {
259   case BABEL_DIAG_OUTPUT:
260     line = (gchar *)data;
261
262     /* tells us how many items there will be */
263     if (strstr(line, "Xfer Wpt")) { 
264       gps_data->progress_label = gps_data->wp_label;
265     }
266     if (strstr(line, "Xfer Trk")) { 
267       gps_data->progress_label = gps_data->trk_label;
268     }
269     if (strstr(line, "PRDDAT")) {
270       gchar **tokens = g_strsplit(line, " ", 0);
271       gchar info[128];
272       int ilen = 0;
273       int i;
274       int n_tokens = 0;
275
276       while (tokens[n_tokens])
277         n_tokens++;
278
279       if (n_tokens > 8) {
280         for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
281           guint ch;
282           sscanf(tokens[i], "%x", &ch);
283           info[ilen++] = ch;
284         }
285         info[ilen++] = 0;
286         set_gps_info(info, w);
287       }
288       g_strfreev(tokens);
289     }
290     /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
291     if (strstr(line, "Unit:")) {
292       gchar **tokens = g_strsplit(line, "\t", 0);
293       int n_tokens = 0;
294       while (tokens[n_tokens])
295         n_tokens++;
296
297       if (n_tokens > 1) {
298         set_gps_info(tokens[1], w);
299       }
300       g_strfreev(tokens);
301     }
302     if (strstr(line, "RECORD")) { 
303       int lsb, msb, cnt;
304
305       if (strlen(line) > 20) {
306        sscanf(line+17, "%x", &lsb); 
307        sscanf(line+20, "%x", &msb);
308        cnt = lsb + msb * 256;
309        set_total_count(cnt, w);
310        gps_data->count = 0;
311       }
312     }
313     if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
314       gps_data->count++;
315       set_current_count(gps_data->count, w);
316     }
317     break;
318   case BABEL_DONE:
319     break;
320   default:
321     break;
322   }
323 }
324
325 void datasource_gps_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data )
326 {
327   gps_user_data_t *w = (gps_user_data_t *)user_data;
328   GtkTable*  box;
329
330   w->proto_l = gtk_label_new (_("GPS Protocol:"));
331   w->proto_b = GTK_COMBO_BOX(gtk_combo_box_new_text ());
332   gtk_combo_box_append_text (w->proto_b, "Garmin");
333   gtk_combo_box_append_text (w->proto_b, "Magellan");
334   gtk_combo_box_set_active (w->proto_b, 0);
335   g_object_ref(w->proto_b);
336
337   w->ser_l = gtk_label_new (_("Serial Port:"));
338   w->ser_b = GTK_COMBO_BOX(gtk_combo_box_entry_new_text ());
339 #ifdef WINDOWS
340   gtk_combo_box_append_text (w->ser_b, "com1");
341 #else
342   /* Here just try to see if the device is available which gets passed onto gpsbabel
343      List USB devices first as these will generally only be present if autogenerated by udev or similar
344      User is still able to set their own free text entry */
345   if (g_access ("/dev/ttyUSB0", R_OK) == 0)
346     gtk_combo_box_append_text (w->ser_b, "/dev/ttyUSB0");
347   if (g_access ("/dev/ttyUSB1", R_OK) == 0)
348     gtk_combo_box_append_text (w->ser_b, "/dev/ttyUSB1");
349   if (g_access ("/dev/ttyS0", R_OK) == 0)
350     gtk_combo_box_append_text (w->ser_b, "/dev/ttyS0");
351   if (g_access ("/dev/ttyS1", R_OK) == 0)
352     gtk_combo_box_append_text (w->ser_b, "/dev/ttyS1");
353 #endif
354   gtk_combo_box_append_text (w->ser_b, "usb:");
355   gtk_combo_box_set_active (w->ser_b, 0);
356   g_object_ref(w->ser_b);
357
358   w->off_request_l = gtk_label_new (_("Turn Off After Transfer\n(Garmin Only)"));
359   w->off_request_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
360
361   box = GTK_TABLE(gtk_table_new(2, 3, FALSE));
362   gtk_table_attach_defaults(box, GTK_WIDGET(w->proto_l), 0, 1, 0, 1);
363   gtk_table_attach_defaults(box, GTK_WIDGET(w->proto_b), 1, 2, 0, 1);
364   gtk_table_attach_defaults(box, GTK_WIDGET(w->ser_l), 0, 1, 1, 2);
365   gtk_table_attach_defaults(box, GTK_WIDGET(w->ser_b), 1, 2, 1, 2);
366   gtk_table_attach_defaults(box, GTK_WIDGET(w->off_request_l), 0, 1, 2, 3);
367   gtk_table_attach_defaults(box, GTK_WIDGET(w->off_request_b), 1, 3, 2, 3);
368   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), GTK_WIDGET(box), FALSE, FALSE, 5 );
369
370   gtk_widget_show_all ( dialog );
371 }
372
373 void datasource_gps_add_progress_widgets ( GtkWidget *dialog, gpointer user_data )
374 {
375   GtkWidget *gpslabel, *verlabel, *idlabel, *wplabel, *trklabel;
376
377   gps_user_data_t *w_gps = (gps_user_data_t *)user_data;
378
379   gpslabel = gtk_label_new (_("GPS device: N/A"));
380   verlabel = gtk_label_new ("");
381   idlabel = gtk_label_new ("");
382   wplabel = gtk_label_new ("");
383   trklabel = gtk_label_new ("");
384
385   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), gpslabel, FALSE, FALSE, 5 );
386   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), wplabel, FALSE, FALSE, 5 );
387   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), trklabel, FALSE, FALSE, 5 );
388
389   gtk_widget_show_all ( dialog );
390
391   w_gps->gps_label = gpslabel;
392   w_gps->id_label = idlabel;
393   w_gps->ver_label = verlabel;
394   w_gps->progress_label = w_gps->wp_label = wplabel;
395   w_gps->trk_label = trklabel;
396   w_gps->total_count = -1;
397 }