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_waypoints = TRUE;
47 static gpointer datasource_gps_init_func ( );
48 static void datasource_gps_get_cmd_string ( gpointer add_widgets_data_not_used, gchar **babelargs, gchar **input_file );
49 static void datasource_gps_cleanup ( gpointer user_data );
50 static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_dialog_widgets_t *w );
51 static void datasource_gps_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data );
52 static void datasource_gps_add_progress_widgets ( GtkWidget *dialog, gpointer user_data );
53 static void datasource_gps_off ( gpointer add_widgets_data_not_used, gchar **babelargs, gchar **input_file );
55 VikDataSourceInterface vik_datasource_gps_interface = {
56 N_("Acquire from GPS"),
57 N_("Acquired from GPS"),
58 VIK_DATASOURCE_GPSBABEL_DIRECT,
59 VIK_DATASOURCE_CREATENEWLAYER,
60 VIK_DATASOURCE_INPUTTYPE_NONE,
63 (VikDataSourceInitFunc) datasource_gps_init_func,
64 (VikDataSourceCheckExistenceFunc) NULL,
65 (VikDataSourceAddSetupWidgetsFunc) datasource_gps_add_setup_widgets,
66 (VikDataSourceGetCmdStringFunc) datasource_gps_get_cmd_string,
67 (VikDataSourceProcessFunc) NULL,
68 (VikDataSourceProgressFunc) datasource_gps_progress,
69 (VikDataSourceAddProgressWidgetsFunc) datasource_gps_add_progress_widgets,
70 (VikDataSourceCleanupFunc) datasource_gps_cleanup,
71 (VikDataSourceOffFunc) datasource_gps_off
74 /*********************************************************
75 * Definitions and routines for acquiring data from GPS
76 *********************************************************/
78 /* widgets in setup dialog specific to GPS */
79 /* widgets in progress dialog specific to GPS */
80 /* also counts needed for progress */
87 GtkWidget *off_request_l;
88 GtkCheckButton *off_request_b;
89 GtkWidget *get_tracks_l;
90 GtkCheckButton *get_tracks_b;
91 GtkWidget *get_waypoints_l;
92 GtkCheckButton *get_waypoints_b;
100 GtkWidget *progress_label;
107 static gpointer datasource_gps_init_func ()
109 return g_malloc (sizeof(gps_user_data_t));
113 * datasource_gps_get_protocol:
115 * Method to get the communication protocol of the GPS device from the widget structure
117 gchar* datasource_gps_get_protocol ( gpointer user_data )
119 // Uses the list of supported devices
120 gps_user_data_t *w = (gps_user_data_t *)user_data;
121 last_active = gtk_combo_box_get_active(GTK_COMBO_BOX(w->proto_b));
122 if (a_babel_device_list)
123 return ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
129 * datasource_gps_get_descriptor:
131 * Method to get the descriptor from the widget structure
132 * "Everything is a file"
133 * Could actually be normal file or a serial port
135 gchar* datasource_gps_get_descriptor ( gpointer user_data )
137 gps_user_data_t *w = (gps_user_data_t *)user_data;
138 return gtk_combo_box_get_active_text(GTK_COMBO_BOX(w->ser_b));
142 * datasource_gps_get_do_tracks:
144 * Method to get the track handling behaviour from the widget structure
146 gboolean datasource_gps_get_do_tracks ( gpointer user_data )
148 gps_user_data_t *w = (gps_user_data_t *)user_data;
149 last_get_tracks = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->get_tracks_b));
150 return last_get_tracks;
154 * datasource_gps_get_do_waypoints:
156 * Method to get the waypoint handling behaviour from the widget structure
158 gboolean datasource_gps_get_do_waypoints ( gpointer user_data )
160 gps_user_data_t *w = (gps_user_data_t *)user_data;
161 last_get_waypoints = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->get_waypoints_b));
162 return last_get_waypoints;
165 static void datasource_gps_get_cmd_string ( gpointer user_data, gchar **babelargs, gchar **input_file )
169 char *waypoints = NULL;
171 if (gps_acquire_in_progress) {
172 *babelargs = *input_file = NULL;
175 gps_acquire_in_progress = TRUE;
177 device = datasource_gps_get_protocol ( user_data );
179 if ( datasource_gps_get_do_tracks ( user_data ) )
184 if ( datasource_gps_get_do_waypoints ( user_data ) )
189 *babelargs = g_strdup_printf("-D 9 %s %s -i %s", tracks, waypoints, device);
190 /* device points to static content => no free */
195 *input_file = g_strdup(datasource_gps_get_descriptor(user_data));
197 g_debug(_("using cmdline '%s' and file '%s'\n"), *babelargs, *input_file);
201 * datasource_gps_get_off:
203 * Method to get the off behaviour from the widget structure
205 gboolean datasource_gps_get_off ( gpointer user_data )
207 gps_user_data_t *w = (gps_user_data_t *)user_data;
208 return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->off_request_b));
211 static void datasource_gps_off ( gpointer user_data, gchar **babelargs, gchar **input_file )
215 gps_user_data_t *w = (gps_user_data_t *)user_data;
217 if (gps_acquire_in_progress) {
218 *babelargs = *input_file = NULL;
221 /* See if we should turn off the device */
222 if (!datasource_gps_get_off ( user_data )){
226 if (!a_babel_device_list)
228 last_active = gtk_combo_box_get_active(GTK_COMBO_BOX(w->proto_b));
229 device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
230 if (!strcmp(device, "garmin")) {
231 device = "garmin,power_off";
233 else if (!strcmp(device, "navilink")) {
234 device = "navilink,power_off";
240 *babelargs = g_strdup_printf("-i %s", device);
241 /* device points to static content => no free */
244 ser = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w->ser_b));
245 *input_file = g_strdup(ser);
249 static void datasource_gps_cleanup ( gpointer user_data )
251 g_free ( user_data );
252 gps_acquire_in_progress = FALSE;
256 * datasource_gps_clean_up:
258 * External method to tidy up
260 void datasource_gps_clean_up ( gpointer user_data )
262 datasource_gps_cleanup ( user_data );
265 static void set_total_count(gint cnt, acq_dialog_widgets_t *w)
270 gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
271 const gchar *tmp_str;
272 if (gps_data->progress_label == gps_data->wp_label)
273 tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt);
275 tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt);
276 s = g_strdup_printf(tmp_str, cnt);
277 gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s );
278 gtk_widget_show ( gps_data->progress_label );
279 gps_data->total_count = cnt;
285 static void set_current_count(gint cnt, acq_dialog_widgets_t *w)
290 gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
292 if (cnt < gps_data->total_count) {
293 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");
295 s = g_strdup_printf(_("Downloaded %d %s."), cnt, (gps_data->progress_label == gps_data->wp_label) ? "waypoints" : "trackpoints");
297 gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s );
303 static void set_gps_info(const gchar *info, acq_dialog_widgets_t *w)
308 s = g_strdup_printf(_("GPS Device: %s"), info);
309 gtk_label_set_text ( GTK_LABEL(((gps_user_data_t *)w->user_data)->gps_label), s );
316 * This routine relies on gpsbabel's diagnostic output to display the progress information.
317 * These outputs differ when different GPS devices are used, so we will need to test
318 * them on several and add the corresponding support.
320 static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_dialog_widgets_t *w )
323 gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
326 case BABEL_DIAG_OUTPUT:
327 line = (gchar *)data;
331 gtk_label_set_text ( GTK_LABEL(w->status), _("Status: Working...") );
335 /* tells us how many items there will be */
336 if (strstr(line, "Xfer Wpt")) {
337 gps_data->progress_label = gps_data->wp_label;
339 if (strstr(line, "Xfer Trk")) {
340 gps_data->progress_label = gps_data->trk_label;
342 if (strstr(line, "PRDDAT")) {
343 gchar **tokens = g_strsplit(line, " ", 0);
349 while (tokens[n_tokens])
353 for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
355 sscanf(tokens[i], "%x", &ch);
359 set_gps_info(info, w);
363 /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
364 if (strstr(line, "Unit:")) {
365 gchar **tokens = g_strsplit(line, "\t", 0);
367 while (tokens[n_tokens])
371 set_gps_info(tokens[1], w);
375 if (strstr(line, "RECORD")) {
378 if (strlen(line) > 20) {
379 sscanf(line+17, "%x", &lsb);
380 sscanf(line+20, "%x", &msb);
381 cnt = lsb + msb * 256;
382 set_total_count(cnt, w);
386 if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") ) {
388 set_current_count(gps_data->count, w);
398 void append_element (gpointer elem, gpointer user_data)
400 GtkComboBox *combo = GTK_COMBO_BOX (user_data);
401 const gchar *text = ((BabelDevice*)elem)->label;
402 gtk_combo_box_append_text (combo, text);
405 static gint find_entry = -1;
406 static gint garmin_entry = -1;
408 static void find_garmin (gpointer elem, gpointer user_data)
410 const gchar *name = ((BabelDevice*)elem)->name;
412 if (!strcmp(name, "garmin")) {
413 garmin_entry = find_entry;
417 static void datasource_gps_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data )
419 gps_user_data_t *w = (gps_user_data_t *)user_data;
420 GtkTable *box, *data_type_box;
422 w->proto_l = gtk_label_new (_("GPS Protocol:"));
423 w->proto_b = GTK_COMBO_BOX(gtk_combo_box_new_text ());
424 g_list_foreach (a_babel_device_list, append_element, w->proto_b);
426 // Maintain default to Garmin devices (assumed most popular/numerous device)
427 if ( last_active < 0 ) {
429 g_list_foreach (a_babel_device_list, find_garmin, NULL);
430 if ( garmin_entry < 0 )
431 // Not found - so set it to the first entry
435 last_active = garmin_entry;
438 gtk_combo_box_set_active (w->proto_b, last_active);
439 g_object_ref(w->proto_b);
441 w->ser_l = gtk_label_new (_("Serial Port:"));
442 w->ser_b = GTK_COMBO_BOX(gtk_combo_box_entry_new_text ());
444 gtk_combo_box_append_text (w->ser_b, "com1");
446 /* Here just try to see if the device is available which gets passed onto gpsbabel
447 List USB devices first as these will generally only be present if autogenerated by udev or similar
448 User is still able to set their own free text entry */
449 if (g_access ("/dev/ttyUSB0", R_OK) == 0)
450 gtk_combo_box_append_text (w->ser_b, "/dev/ttyUSB0");
451 if (g_access ("/dev/ttyUSB1", R_OK) == 0)
452 gtk_combo_box_append_text (w->ser_b, "/dev/ttyUSB1");
453 if (g_access ("/dev/ttyS0", R_OK) == 0)
454 gtk_combo_box_append_text (w->ser_b, "/dev/ttyS0");
455 if (g_access ("/dev/ttyS1", R_OK) == 0)
456 gtk_combo_box_append_text (w->ser_b, "/dev/ttyS1");
458 gtk_combo_box_append_text (w->ser_b, "usb:");
459 gtk_combo_box_set_active (w->ser_b, 0);
460 g_object_ref(w->ser_b);
462 w->off_request_l = gtk_label_new (_("Turn Off After Transfer\n(Garmin/NAViLink Only)"));
463 w->off_request_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
465 w->get_tracks_l = gtk_label_new (_("Tracks:"));
466 w->get_tracks_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
467 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_tracks_b), last_get_tracks);
469 w->get_waypoints_l = gtk_label_new (_("Waypoints:"));
470 w->get_waypoints_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
471 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_waypoints_b), last_get_waypoints);
473 box = GTK_TABLE(gtk_table_new(2, 4, FALSE));
474 data_type_box = GTK_TABLE(gtk_table_new(4, 1, FALSE));
476 gtk_table_attach_defaults(box, GTK_WIDGET(w->proto_l), 0, 1, 0, 1);
477 gtk_table_attach_defaults(box, GTK_WIDGET(w->proto_b), 1, 2, 0, 1);
478 gtk_table_attach_defaults(box, GTK_WIDGET(w->ser_l), 0, 1, 1, 2);
479 gtk_table_attach_defaults(box, GTK_WIDGET(w->ser_b), 1, 2, 1, 2);
480 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_tracks_l), 0, 1, 0, 1);
481 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_tracks_b), 1, 2, 0, 1);
482 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_waypoints_l), 2, 3, 0, 1);
483 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_waypoints_b), 3, 4, 0, 1);
484 gtk_table_attach_defaults(box, GTK_WIDGET(data_type_box), 0, 2, 2, 3);
485 gtk_table_attach_defaults(box, GTK_WIDGET(w->off_request_l), 0, 1, 3, 4);
486 gtk_table_attach_defaults(box, GTK_WIDGET(w->off_request_b), 1, 3, 3, 4);
487 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), GTK_WIDGET(box), FALSE, FALSE, 5 );
489 gtk_widget_show_all ( dialog );
493 * datasource_gps_setup:
494 * @dialog: The GTK dialog. The caller is responsible for managing the dialog creation/deletion
495 * @only_tracks: When only tracks are specified, waypoints will be disabled.
497 * Returns: A gpointer to the private structure for GPS progress/information widgets
498 * Pass this pointer back into the other exposed datasource_gps_X functions
500 gpointer datasource_gps_setup ( GtkWidget *dialog, gboolean only_tracks )
502 gps_user_data_t *w_gps = (gps_user_data_t *)datasource_gps_init_func();
503 datasource_gps_add_setup_widgets ( dialog, NULL, w_gps );
506 // Indicate tracks enabled (although no option to turn off):
507 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(w_gps->get_tracks_b), TRUE);
508 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_tracks_b), FALSE );
510 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(w_gps->get_waypoints_b), FALSE);
511 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_waypoints_l), FALSE );
512 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_waypoints_b), FALSE );
514 return (gpointer)w_gps;
517 void datasource_gps_add_progress_widgets ( GtkWidget *dialog, gpointer user_data )
519 GtkWidget *gpslabel, *verlabel, *idlabel, *wplabel, *trklabel;
521 gps_user_data_t *w_gps = (gps_user_data_t *)user_data;
523 gpslabel = gtk_label_new (_("GPS device: N/A"));
524 verlabel = gtk_label_new ("");
525 idlabel = gtk_label_new ("");
526 wplabel = gtk_label_new ("");
527 trklabel = gtk_label_new ("");
529 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), gpslabel, FALSE, FALSE, 5 );
530 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), wplabel, FALSE, FALSE, 5 );
531 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), trklabel, FALSE, FALSE, 5 );
533 gtk_widget_show_all ( dialog );
535 w_gps->gps_label = gpslabel;
536 w_gps->id_label = idlabel;
537 w_gps->ver_label = verlabel;
538 w_gps->progress_label = w_gps->wp_label = wplabel;
539 w_gps->trk_label = trklabel;
540 w_gps->total_count = -1;