2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2006, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2012, Rob Norris <rw_norris@hotmail.com>
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.
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.
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
31 #include <glib/gstdio.h>
32 #include <glib/gprintf.h>
33 #include <glib/gi18n.h>
35 #include "datasource_gps.h"
41 static gboolean gps_acquire_in_progress = FALSE;
43 static gint last_active = -1;
44 static gboolean last_get_tracks = TRUE;
45 static gboolean last_get_routes = TRUE;
46 static gboolean last_get_waypoints = TRUE;
48 static gpointer datasource_gps_init_func ( );
49 static void datasource_gps_get_cmd_string ( gpointer add_widgets_data_not_used, gchar **babelargs, gchar **input_file, gpointer not_used );
50 static void datasource_gps_cleanup ( gpointer user_data );
51 static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_dialog_widgets_t *w );
52 static void datasource_gps_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data );
53 static void datasource_gps_add_progress_widgets ( GtkWidget *dialog, gpointer user_data );
54 static void datasource_gps_off ( gpointer add_widgets_data_not_used, gchar **babelargs, gchar **input_file );
56 VikDataSourceInterface vik_datasource_gps_interface = {
57 N_("Acquire from GPS"),
58 N_("Acquired from GPS"),
59 VIK_DATASOURCE_CREATENEWLAYER,
60 VIK_DATASOURCE_INPUTTYPE_NONE,
64 (VikDataSourceInitFunc) datasource_gps_init_func,
65 (VikDataSourceCheckExistenceFunc) NULL,
66 (VikDataSourceAddSetupWidgetsFunc) datasource_gps_add_setup_widgets,
67 (VikDataSourceGetCmdStringFunc) datasource_gps_get_cmd_string,
68 (VikDataSourceProcessFunc) a_babel_convert_from,
69 (VikDataSourceProgressFunc) datasource_gps_progress,
70 (VikDataSourceAddProgressWidgetsFunc) datasource_gps_add_progress_widgets,
71 (VikDataSourceCleanupFunc) datasource_gps_cleanup,
72 (VikDataSourceOffFunc) datasource_gps_off
75 /*********************************************************
76 * Definitions and routines for acquiring data from GPS
77 *********************************************************/
79 /* widgets in setup dialog specific to GPS */
80 /* widgets in progress dialog specific to GPS */
81 /* also counts needed for progress */
88 GtkWidget *off_request_l;
89 GtkCheckButton *off_request_b;
90 GtkWidget *get_tracks_l;
91 GtkCheckButton *get_tracks_b;
92 GtkWidget *get_routes_l;
93 GtkCheckButton *get_routes_b;
94 GtkWidget *get_waypoints_l;
95 GtkCheckButton *get_waypoints_b;
102 GtkWidget *trk_label;
103 GtkWidget *rte_label;
104 GtkWidget *progress_label;
105 vik_gps_xfer_type progress_type;
112 static gpointer datasource_gps_init_func ()
114 return g_malloc (sizeof(gps_user_data_t));
118 * datasource_gps_get_protocol:
120 * Method to get the communication protocol of the GPS device from the widget structure
122 gchar* datasource_gps_get_protocol ( gpointer user_data )
124 // Uses the list of supported devices
125 gps_user_data_t *w = (gps_user_data_t *)user_data;
126 last_active = gtk_combo_box_get_active(GTK_COMBO_BOX(w->proto_b));
127 if (a_babel_device_list)
128 return ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
134 * datasource_gps_get_descriptor:
136 * Method to get the descriptor from the widget structure
137 * "Everything is a file"
138 * Could actually be normal file or a serial port
140 gchar* datasource_gps_get_descriptor ( gpointer user_data )
142 gps_user_data_t *w = (gps_user_data_t *)user_data;
143 return gtk_combo_box_get_active_text(GTK_COMBO_BOX(w->ser_b));
147 * datasource_gps_get_do_tracks:
149 * Method to get the track handling behaviour from the widget structure
151 gboolean datasource_gps_get_do_tracks ( gpointer user_data )
153 gps_user_data_t *w = (gps_user_data_t *)user_data;
154 last_get_tracks = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->get_tracks_b));
155 return last_get_tracks;
159 * datasource_gps_get_do_routes:
161 * Method to get the route handling behaviour from the widget structure
163 gboolean datasource_gps_get_do_routes ( gpointer user_data )
165 gps_user_data_t *w = (gps_user_data_t *)user_data;
166 last_get_routes = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->get_routes_b));
167 return last_get_routes;
171 * datasource_gps_get_do_waypoints:
173 * Method to get the waypoint handling behaviour from the widget structure
175 gboolean datasource_gps_get_do_waypoints ( gpointer user_data )
177 gps_user_data_t *w = (gps_user_data_t *)user_data;
178 last_get_waypoints = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->get_waypoints_b));
179 return last_get_waypoints;
182 static void datasource_gps_get_cmd_string ( gpointer user_data, gchar **babelargs, gchar **input_file, gpointer not_used )
187 char *waypoints = NULL;
189 if (gps_acquire_in_progress) {
190 *babelargs = *input_file = NULL;
193 gps_acquire_in_progress = TRUE;
195 device = datasource_gps_get_protocol ( user_data );
197 if ( datasource_gps_get_do_tracks ( user_data ) )
202 if ( datasource_gps_get_do_routes ( user_data ) )
207 if ( datasource_gps_get_do_waypoints ( user_data ) )
212 *babelargs = g_strdup_printf("-D 9 %s %s %s -i %s", tracks, routes, waypoints, device);
213 /* device points to static content => no free */
219 *input_file = g_strdup(datasource_gps_get_descriptor(user_data));
221 g_debug(_("using cmdline '%s' and file '%s'\n"), *babelargs, *input_file);
225 * datasource_gps_get_off:
227 * Method to get the off behaviour from the widget structure
229 gboolean datasource_gps_get_off ( gpointer user_data )
231 gps_user_data_t *w = (gps_user_data_t *)user_data;
232 return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->off_request_b));
235 static void datasource_gps_off ( gpointer user_data, gchar **babelargs, gchar **input_file )
239 gps_user_data_t *w = (gps_user_data_t *)user_data;
241 if (gps_acquire_in_progress) {
242 *babelargs = *input_file = NULL;
245 /* See if we should turn off the device */
246 if (!datasource_gps_get_off ( user_data )){
250 if (!a_babel_device_list)
252 last_active = gtk_combo_box_get_active(GTK_COMBO_BOX(w->proto_b));
253 device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
254 if (!strcmp(device, "garmin")) {
255 device = "garmin,power_off";
257 else if (!strcmp(device, "navilink")) {
258 device = "navilink,power_off";
264 *babelargs = g_strdup_printf("-i %s", device);
265 /* device points to static content => no free */
268 ser = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w->ser_b));
269 *input_file = g_strdup(ser);
273 static void datasource_gps_cleanup ( gpointer user_data )
275 g_free ( user_data );
276 gps_acquire_in_progress = FALSE;
280 * datasource_gps_clean_up:
282 * External method to tidy up
284 void datasource_gps_clean_up ( gpointer user_data )
286 datasource_gps_cleanup ( user_data );
289 static void set_total_count(gint cnt, acq_dialog_widgets_t *w)
294 gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
295 const gchar *tmp_str;
296 switch (gps_data->progress_type) {
297 case WPT: tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt); gps_data->total_count = cnt; break;
298 case TRK: tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt); gps_data->total_count = cnt; break;
301 // Maybe a gpsbabel bug/feature (upto at least v1.4.3 or maybe my Garmin device) but the count always seems x2 too many for routepoints
302 gint mycnt = (cnt / 2) + 1;
303 tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt);
304 gps_data->total_count = mycnt;
308 s = g_strdup_printf(tmp_str, cnt);
309 gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s );
310 gtk_widget_show ( gps_data->progress_label );
316 static void set_current_count(gint cnt, acq_dialog_widgets_t *w)
321 gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
323 if (cnt < gps_data->total_count) {
324 switch (gps_data->progress_type) {
325 case WPT: s = g_strdup_printf(_("Downloaded %d out of %d %s..."), cnt, gps_data->total_count, "waypoints"); break;
326 case TRK: s = g_strdup_printf(_("Downloaded %d out of %d %s..."), cnt, gps_data->total_count, "trackpoints"); break;
327 default: s = g_strdup_printf(_("Downloaded %d out of %d %s..."), cnt, gps_data->total_count, "routepoints"); break;
330 switch (gps_data->progress_type) {
331 case WPT: s = g_strdup_printf(_("Downloaded %d %s."), cnt, "waypoints"); break;
332 case TRK: s = g_strdup_printf(_("Downloaded %d %s."), cnt, "trackpoints"); break;
333 default: s = g_strdup_printf(_("Downloaded %d %s."), cnt, "routepoints"); break;
336 gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s );
342 static void set_gps_info(const gchar *info, acq_dialog_widgets_t *w)
347 s = g_strdup_printf(_("GPS Device: %s"), info);
348 gtk_label_set_text ( GTK_LABEL(((gps_user_data_t *)w->user_data)->gps_label), s );
355 * This routine relies on gpsbabel's diagnostic output to display the progress information.
356 * These outputs differ when different GPS devices are used, so we will need to test
357 * them on several and add the corresponding support.
359 static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_dialog_widgets_t *w )
362 gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
365 case BABEL_DIAG_OUTPUT:
366 line = (gchar *)data;
370 gtk_label_set_text ( GTK_LABEL(w->status), _("Status: Working...") );
374 /* tells us the type of items that will follow */
375 if (strstr(line, "Xfer Wpt")) {
376 gps_data->progress_label = gps_data->wp_label;
377 gps_data->progress_type = WPT;
379 if (strstr(line, "Xfer Trk")) {
380 gps_data->progress_label = gps_data->trk_label;
381 gps_data->progress_type = TRK;
383 if (strstr(line, "Xfer Rte")) {
384 gps_data->progress_label = gps_data->rte_label;
385 gps_data->progress_type = RTE;
388 if (strstr(line, "PRDDAT")) {
389 gchar **tokens = g_strsplit(line, " ", 0);
395 while (tokens[n_tokens])
399 for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
401 sscanf(tokens[i], "%x", &ch);
405 set_gps_info(info, w);
409 /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
410 if (strstr(line, "Unit:")) {
411 gchar **tokens = g_strsplit(line, "\t", 0);
413 while (tokens[n_tokens])
417 set_gps_info(tokens[1], w);
421 /* tells us how many items there will be */
422 if (strstr(line, "RECORD")) {
425 if (strlen(line) > 20) {
426 sscanf(line+17, "%x", &lsb);
427 sscanf(line+20, "%x", &msb);
428 cnt = lsb + msb * 256;
429 set_total_count(cnt, w);
433 if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
435 set_current_count(gps_data->count, w);
445 void append_element (gpointer elem, gpointer user_data)
447 GtkComboBox *combo = GTK_COMBO_BOX (user_data);
448 const gchar *text = ((BabelDevice*)elem)->label;
449 gtk_combo_box_append_text (combo, text);
452 static gint find_entry = -1;
453 static gint garmin_entry = -1;
455 static void find_garmin (gpointer elem, gpointer user_data)
457 const gchar *name = ((BabelDevice*)elem)->name;
459 if (!strcmp(name, "garmin")) {
460 garmin_entry = find_entry;
464 static void datasource_gps_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data )
466 gps_user_data_t *w = (gps_user_data_t *)user_data;
467 GtkTable *box, *data_type_box;
469 w->proto_l = gtk_label_new (_("GPS Protocol:"));
470 w->proto_b = GTK_COMBO_BOX(gtk_combo_box_new_text ());
471 g_list_foreach (a_babel_device_list, append_element, w->proto_b);
473 // Maintain default to Garmin devices (assumed most popular/numerous device)
474 if ( last_active < 0 ) {
476 g_list_foreach (a_babel_device_list, find_garmin, NULL);
477 if ( garmin_entry < 0 )
478 // Not found - so set it to the first entry
482 last_active = garmin_entry;
485 gtk_combo_box_set_active (w->proto_b, last_active);
486 g_object_ref(w->proto_b);
488 w->ser_l = gtk_label_new (_("Serial Port:"));
489 w->ser_b = GTK_COMBO_BOX(gtk_combo_box_entry_new_text ());
491 gtk_combo_box_append_text (w->ser_b, "com1");
493 /* Here just try to see if the device is available which gets passed onto gpsbabel
494 List USB devices first as these will generally only be present if autogenerated by udev or similar
495 User is still able to set their own free text entry */
496 if (g_access ("/dev/ttyUSB0", R_OK) == 0)
497 gtk_combo_box_append_text (w->ser_b, "/dev/ttyUSB0");
498 if (g_access ("/dev/ttyUSB1", R_OK) == 0)
499 gtk_combo_box_append_text (w->ser_b, "/dev/ttyUSB1");
500 if (g_access ("/dev/ttyS0", R_OK) == 0)
501 gtk_combo_box_append_text (w->ser_b, "/dev/ttyS0");
502 if (g_access ("/dev/ttyS1", R_OK) == 0)
503 gtk_combo_box_append_text (w->ser_b, "/dev/ttyS1");
505 gtk_combo_box_append_text (w->ser_b, "usb:");
506 gtk_combo_box_set_active (w->ser_b, 0);
507 g_object_ref(w->ser_b);
509 w->off_request_l = gtk_label_new (_("Turn Off After Transfer\n(Garmin/NAViLink Only)"));
510 w->off_request_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
512 w->get_tracks_l = gtk_label_new (_("Tracks:"));
513 w->get_tracks_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
514 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_tracks_b), last_get_tracks);
516 w->get_routes_l = gtk_label_new (_("Routes:"));
517 w->get_routes_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
518 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_routes_b), last_get_routes);
520 w->get_waypoints_l = gtk_label_new (_("Waypoints:"));
521 w->get_waypoints_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
522 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_waypoints_b), last_get_waypoints);
524 box = GTK_TABLE(gtk_table_new(2, 4, FALSE));
525 data_type_box = GTK_TABLE(gtk_table_new(4, 1, FALSE));
527 gtk_table_attach_defaults(box, GTK_WIDGET(w->proto_l), 0, 1, 0, 1);
528 gtk_table_attach_defaults(box, GTK_WIDGET(w->proto_b), 1, 2, 0, 1);
529 gtk_table_attach_defaults(box, GTK_WIDGET(w->ser_l), 0, 1, 1, 2);
530 gtk_table_attach_defaults(box, GTK_WIDGET(w->ser_b), 1, 2, 1, 2);
531 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_tracks_l), 0, 1, 0, 1);
532 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_tracks_b), 1, 2, 0, 1);
533 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_routes_l), 2, 3, 0, 1);
534 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_routes_b), 3, 4, 0, 1);
535 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_waypoints_l), 4, 5, 0, 1);
536 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_waypoints_b), 5, 6, 0, 1);
537 gtk_table_attach_defaults(box, GTK_WIDGET(data_type_box), 0, 2, 2, 3);
538 gtk_table_attach_defaults(box, GTK_WIDGET(w->off_request_l), 0, 1, 3, 4);
539 gtk_table_attach_defaults(box, GTK_WIDGET(w->off_request_b), 1, 3, 3, 4);
540 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), GTK_WIDGET(box), FALSE, FALSE, 5 );
542 gtk_widget_show_all ( dialog );
546 * datasource_gps_setup:
547 * @dialog: The GTK dialog. The caller is responsible for managing the dialog creation/deletion
548 * @xfer: The default type of items enabled for transfer, others disabled.
549 * @xfer_all: When specified all items are enabled for transfer.
551 * Returns: A gpointer to the private structure for GPS progress/information widgets
552 * Pass this pointer back into the other exposed datasource_gps_X functions
554 gpointer datasource_gps_setup ( GtkWidget *dialog, vik_gps_xfer_type xfer, gboolean xfer_all )
556 gps_user_data_t *w_gps = (gps_user_data_t *)datasource_gps_init_func();
557 datasource_gps_add_setup_widgets ( dialog, NULL, w_gps );
559 gboolean way = xfer_all;
560 gboolean trk = xfer_all;
561 gboolean rte = xfer_all;
563 // Selectively turn bits on
566 case WPT: way = TRUE; break;
567 case RTE: rte = TRUE; break;
568 default: trk = TRUE; break;
573 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(w_gps->get_tracks_b), trk );
574 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_tracks_l), trk );
575 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_tracks_b), trk );
577 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(w_gps->get_routes_b), rte );
578 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_routes_l), rte );
579 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_routes_b), rte );
581 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(w_gps->get_waypoints_b), way );
582 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_waypoints_l), way );
583 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_waypoints_b), way );
585 return (gpointer)w_gps;
588 void datasource_gps_add_progress_widgets ( GtkWidget *dialog, gpointer user_data )
590 GtkWidget *gpslabel, *verlabel, *idlabel, *wplabel, *trklabel, *rtelabel;
592 gps_user_data_t *w_gps = (gps_user_data_t *)user_data;
594 gpslabel = gtk_label_new (_("GPS device: N/A"));
595 verlabel = gtk_label_new ("");
596 idlabel = gtk_label_new ("");
597 wplabel = gtk_label_new ("");
598 trklabel = gtk_label_new ("");
599 rtelabel = gtk_label_new ("");
601 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), gpslabel, FALSE, FALSE, 5 );
602 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), wplabel, FALSE, FALSE, 5 );
603 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), trklabel, FALSE, FALSE, 5 );
604 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), rtelabel, FALSE, FALSE, 5 );
606 gtk_widget_show_all ( dialog );
608 w_gps->gps_label = gpslabel;
609 w_gps->id_label = idlabel;
610 w_gps->ver_label = verlabel;
611 w_gps->progress_label = w_gps->wp_label = wplabel;
612 w_gps->trk_label = trklabel;
613 w_gps->rte_label = rtelabel;
614 w_gps->total_count = -1;