]> git.street.me.uk Git - andy/viking.git/blob - src/datasource_gps.c
Tidy up: simpler and better use of trackpoint free methods in TRW Layer.
[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   (VikDataSourceProcessFunc)            NULL,
67   (VikDataSourceProgressFunc)           datasource_gps_progress,
68   (VikDataSourceAddProgressWidgetsFunc) datasource_gps_add_progress_widgets,
69   (VikDataSourceCleanupFunc)            datasource_gps_cleanup,
70   (VikDataSourceOffFunc)                datasource_gps_off
71 };
72
73 /*********************************************************
74  * Definitions and routines for acquiring data from GPS
75  *********************************************************/
76
77 /* widgets in setup dialog specific to GPS */
78 /* widgets in progress dialog specific to GPS */
79 /* also counts needed for progress */
80 typedef struct {
81   /* setup dialog */
82   GtkWidget *proto_l;
83   GtkComboBox *proto_b;
84   GtkWidget *ser_l;
85   GtkComboBox *ser_b;
86   GtkWidget *off_request_l;
87   GtkCheckButton *off_request_b;
88   GtkWidget *get_tracks_l;
89   GtkCheckButton *get_tracks_b;
90   GtkWidget *get_waypoints_l;
91   GtkCheckButton *get_waypoints_b;
92
93   /* progress dialog */
94   GtkWidget *gps_label;
95   GtkWidget *ver_label;
96   GtkWidget *id_label;
97   GtkWidget *wp_label;
98   GtkWidget *trk_label;
99   GtkWidget *progress_label;
100
101   /* state */
102   int total_count;
103   int count;
104 } gps_user_data_t;
105
106 static gpointer datasource_gps_init_func ()
107 {
108   return g_malloc (sizeof(gps_user_data_t));
109 }
110
111 static void datasource_gps_get_cmd_string ( gpointer user_data, gchar **babelargs, gchar **input_file )
112 {
113   char *ser = NULL;
114   char *device = NULL;
115   char *tracks = NULL;
116   char *waypoints = NULL;
117   gps_user_data_t *w = (gps_user_data_t *)user_data;
118
119   if (gps_acquire_in_progress) {
120     *babelargs = *input_file = NULL;
121   }
122   
123   gps_acquire_in_progress = TRUE;
124
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;
127
128   last_get_tracks = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->get_tracks_b));
129   if (last_get_tracks)
130     tracks = "-t";
131   else
132     tracks = "";
133   last_get_waypoints = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->get_waypoints_b));
134   if (last_get_waypoints)
135     waypoints = "-w";
136   else
137     waypoints = "";
138
139   *babelargs = g_strdup_printf("-D 9 %s %s -i %s", tracks, waypoints, device);
140   /* device points to static content => no free */
141   device = NULL;
142   tracks = NULL;
143   waypoints = NULL;
144
145   ser = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w->ser_b));
146   *input_file = g_strdup(ser);
147
148   g_debug(_("using cmdline '%s' and file '%s'\n"), *babelargs, *input_file);
149 }
150
151
152 static void datasource_gps_off ( gpointer user_data, gchar **babelargs, gchar **input_file )
153 {
154   char *ser = NULL;
155   char *device = NULL;
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   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")) {
170     device = "garmin,power_off";
171   }
172   else if (!strcmp(device, "navilink")) {
173     device = "navilink,power_off";
174   }
175   else {
176     return;
177   }
178
179   *babelargs = g_strdup_printf("-i %s", device);
180   /* device points to static content => no free */
181   device = NULL;
182   
183   ser = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w->ser_b));
184   *input_file = g_strdup(ser);
185 }
186
187
188 static void datasource_gps_cleanup ( gpointer user_data )
189 {
190   g_free ( user_data );
191   gps_acquire_in_progress = FALSE;
192 }
193
194 static void set_total_count(gint cnt, acq_dialog_widgets_t *w)
195 {
196   gchar *s = NULL;
197   gdk_threads_enter();
198   if (w->ok) {
199     gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
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);
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   }
210   g_free(s); s = NULL;
211   gdk_threads_leave();
212 }
213
214 static void set_current_count(gint cnt, acq_dialog_widgets_t *w)
215 {
216   gchar *s = NULL;
217   gdk_threads_enter();
218   if (w->ok) {
219     gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
220
221     if (cnt < gps_data->total_count) {
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");
223     } else {
224       s = g_strdup_printf(_("Downloaded %d %s."), cnt, (gps_data->progress_label == gps_data->wp_label) ? "waypoints" : "trackpoints");
225     }     
226     gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s );
227   }
228   g_free(s); s = NULL;
229   gdk_threads_leave();
230 }
231
232 static void set_gps_info(const gchar *info, acq_dialog_widgets_t *w)
233 {
234   gchar *s = NULL;
235   gdk_threads_enter();
236   if (w->ok) {
237     s = g_strdup_printf(_("GPS Device: %s"), info);
238     gtk_label_set_text ( GTK_LABEL(((gps_user_data_t *)w->user_data)->gps_label), s );
239   }
240   g_free(s); s = NULL;
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  */
249 static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_dialog_widgets_t *w )
250 {
251   gchar *line;
252   gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
253
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;
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);
283       }
284       g_strfreev(tokens);
285     }
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     }
298     if (strstr(line, "RECORD")) { 
299       int lsb, msb, cnt;
300
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       }
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
321 void 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
328 static gint find_entry = -1;
329 static gint garmin_entry = -1;
330
331 static 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
340 void 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;
343   GtkTable *box, *data_type_box;
344
345   w->proto_l = gtk_label_new (_("GPS Protocol:"));
346   w->proto_b = GTK_COMBO_BOX(gtk_combo_box_new_text ());
347   g_list_foreach (a_babel_device_list, append_element, w->proto_b);
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
361   gtk_combo_box_set_active (w->proto_b, last_active);
362   g_object_ref(w->proto_b);
363
364   w->ser_l = gtk_label_new (_("Serial Port:"));
365   w->ser_b = GTK_COMBO_BOX(gtk_combo_box_entry_new_text ());
366 #ifdef WINDOWS
367   gtk_combo_box_append_text (w->ser_b, "com1");
368 #else
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");
380 #endif
381   gtk_combo_box_append_text (w->ser_b, "usb:");
382   gtk_combo_box_set_active (w->ser_b, 0);
383   g_object_ref(w->ser_b);
384
385   w->off_request_l = gtk_label_new (_("Turn Off After Transfer\n(Garmin/NAViLink Only)"));
386   w->off_request_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
387
388   w->get_tracks_l = gtk_label_new (_("Tracks:"));
389   w->get_tracks_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
390   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_tracks_b), last_get_tracks);
391
392   w->get_waypoints_l = gtk_label_new (_("Waypoints:"));
393   w->get_waypoints_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
394   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_waypoints_b), last_get_waypoints);
395
396   box = GTK_TABLE(gtk_table_new(2, 4, FALSE));
397   data_type_box = GTK_TABLE(gtk_table_new(4, 1, FALSE));
398
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);
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);
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
415 void datasource_gps_add_progress_widgets ( GtkWidget *dialog, gpointer user_data )
416 {
417   GtkWidget *gpslabel, *verlabel, *idlabel, *wplabel, *trklabel;
418
419   gps_user_data_t *w_gps = (gps_user_data_t *)user_data;
420
421   gpslabel = gtk_label_new (_("GPS device: N/A"));
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
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;
439 }