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 ( acq_vik_t *avt );
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 ( acq_vik_t *avt )
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;
144 #if GTK_CHECK_VERSION (2, 24, 0)
145 return gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(w->ser_b));
147 return gtk_combo_box_get_active_text(GTK_COMBO_BOX(w->ser_b));
152 * datasource_gps_get_do_tracks:
154 * Method to get the track handling behaviour from the widget structure
156 gboolean datasource_gps_get_do_tracks ( gpointer user_data )
158 gps_user_data_t *w = (gps_user_data_t *)user_data;
159 last_get_tracks = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->get_tracks_b));
160 return last_get_tracks;
164 * datasource_gps_get_do_routes:
166 * Method to get the route handling behaviour from the widget structure
168 gboolean datasource_gps_get_do_routes ( gpointer user_data )
170 gps_user_data_t *w = (gps_user_data_t *)user_data;
171 last_get_routes = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->get_routes_b));
172 return last_get_routes;
176 * datasource_gps_get_do_waypoints:
178 * Method to get the waypoint handling behaviour from the widget structure
180 gboolean datasource_gps_get_do_waypoints ( gpointer user_data )
182 gps_user_data_t *w = (gps_user_data_t *)user_data;
183 last_get_waypoints = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->get_waypoints_b));
184 return last_get_waypoints;
187 static void datasource_gps_get_cmd_string ( gpointer user_data, gchar **babelargs, gchar **input_file, gpointer not_used )
192 char *waypoints = NULL;
194 if (gps_acquire_in_progress) {
195 *babelargs = *input_file = NULL;
198 gps_acquire_in_progress = TRUE;
200 device = datasource_gps_get_protocol ( user_data );
202 if ( datasource_gps_get_do_tracks ( user_data ) )
207 if ( datasource_gps_get_do_routes ( user_data ) )
212 if ( datasource_gps_get_do_waypoints ( user_data ) )
217 *babelargs = g_strdup_printf("-D 9 %s %s %s -i %s", tracks, routes, waypoints, device);
218 /* device points to static content => no free */
224 *input_file = g_strdup(datasource_gps_get_descriptor(user_data));
226 g_debug(_("using cmdline '%s' and file '%s'\n"), *babelargs, *input_file);
230 * datasource_gps_get_off:
232 * Method to get the off behaviour from the widget structure
234 gboolean datasource_gps_get_off ( gpointer user_data )
236 gps_user_data_t *w = (gps_user_data_t *)user_data;
237 return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->off_request_b));
240 static void datasource_gps_off ( gpointer user_data, gchar **babelargs, gchar **input_file )
244 gps_user_data_t *w = (gps_user_data_t *)user_data;
246 if (gps_acquire_in_progress) {
247 *babelargs = *input_file = NULL;
250 /* See if we should turn off the device */
251 if (!datasource_gps_get_off ( user_data )){
255 if (!a_babel_device_list)
257 last_active = gtk_combo_box_get_active(GTK_COMBO_BOX(w->proto_b));
258 device = ((BabelDevice*)g_list_nth_data(a_babel_device_list, last_active))->name;
259 if (!strcmp(device, "garmin")) {
260 device = "garmin,power_off";
262 else if (!strcmp(device, "navilink")) {
263 device = "navilink,power_off";
269 *babelargs = g_strdup_printf("-i %s", device);
270 /* device points to static content => no free */
273 #if GTK_CHECK_VERSION (2, 24, 0)
274 ser = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(w->ser_b));
276 ser = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w->ser_b));
278 *input_file = g_strdup(ser);
282 static void datasource_gps_cleanup ( gpointer user_data )
284 g_free ( user_data );
285 gps_acquire_in_progress = FALSE;
289 * datasource_gps_clean_up:
291 * External method to tidy up
293 void datasource_gps_clean_up ( gpointer user_data )
295 datasource_gps_cleanup ( user_data );
298 static void set_total_count(gint cnt, acq_dialog_widgets_t *w)
303 gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
304 const gchar *tmp_str;
305 switch (gps_data->progress_type) {
306 case WPT: tmp_str = ngettext("Downloading %d waypoint...", "Downloading %d waypoints...", cnt); gps_data->total_count = cnt; break;
307 case TRK: tmp_str = ngettext("Downloading %d trackpoint...", "Downloading %d trackpoints...", cnt); gps_data->total_count = cnt; break;
310 // 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
311 gint mycnt = (cnt / 2) + 1;
312 tmp_str = ngettext("Downloading %d routepoint...", "Downloading %d routepoints...", mycnt);
313 gps_data->total_count = mycnt;
317 s = g_strdup_printf(tmp_str, cnt);
318 gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s );
319 gtk_widget_show ( gps_data->progress_label );
325 static void set_current_count(gint cnt, acq_dialog_widgets_t *w)
330 gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
332 if (cnt < gps_data->total_count) {
333 switch (gps_data->progress_type) {
334 case WPT: s = g_strdup_printf(_("Downloaded %d out of %d %s..."), cnt, gps_data->total_count, "waypoints"); break;
335 case TRK: s = g_strdup_printf(_("Downloaded %d out of %d %s..."), cnt, gps_data->total_count, "trackpoints"); break;
336 default: s = g_strdup_printf(_("Downloaded %d out of %d %s..."), cnt, gps_data->total_count, "routepoints"); break;
339 switch (gps_data->progress_type) {
340 case WPT: s = g_strdup_printf(_("Downloaded %d %s."), cnt, "waypoints"); break;
341 case TRK: s = g_strdup_printf(_("Downloaded %d %s."), cnt, "trackpoints"); break;
342 default: s = g_strdup_printf(_("Downloaded %d %s."), cnt, "routepoints"); break;
345 gtk_label_set_text ( GTK_LABEL(gps_data->progress_label), s );
351 static void set_gps_info(const gchar *info, acq_dialog_widgets_t *w)
356 s = g_strdup_printf(_("GPS Device: %s"), info);
357 gtk_label_set_text ( GTK_LABEL(((gps_user_data_t *)w->user_data)->gps_label), s );
364 * This routine relies on gpsbabel's diagnostic output to display the progress information.
365 * These outputs differ when different GPS devices are used, so we will need to test
366 * them on several and add the corresponding support.
368 static void datasource_gps_progress ( BabelProgressCode c, gpointer data, acq_dialog_widgets_t *w )
371 gps_user_data_t *gps_data = (gps_user_data_t *)w->user_data;
374 case BABEL_DIAG_OUTPUT:
375 line = (gchar *)data;
379 gtk_label_set_text ( GTK_LABEL(w->status), _("Status: Working...") );
383 /* tells us the type of items that will follow */
384 if (strstr(line, "Xfer Wpt")) {
385 gps_data->progress_label = gps_data->wp_label;
386 gps_data->progress_type = WPT;
388 if (strstr(line, "Xfer Trk")) {
389 gps_data->progress_label = gps_data->trk_label;
390 gps_data->progress_type = TRK;
392 if (strstr(line, "Xfer Rte")) {
393 gps_data->progress_label = gps_data->rte_label;
394 gps_data->progress_type = RTE;
397 if (strstr(line, "PRDDAT")) {
398 gchar **tokens = g_strsplit(line, " ", 0);
404 while (tokens[n_tokens])
408 for (i=8; tokens[i] && ilen < sizeof(info)-2 && strcmp(tokens[i], "00"); i++) {
410 sscanf(tokens[i], "%x", &ch);
414 set_gps_info(info, w);
418 /* eg: "Unit:\teTrex Legend HCx Software Version 2.90\n" */
419 if (strstr(line, "Unit:")) {
420 gchar **tokens = g_strsplit(line, "\t", 0);
422 while (tokens[n_tokens])
426 set_gps_info(tokens[1], w);
430 /* tells us how many items there will be */
431 if (strstr(line, "RECORD")) {
434 if (strlen(line) > 20) {
435 sscanf(line+17, "%x", &lsb);
436 sscanf(line+20, "%x", &msb);
437 cnt = lsb + msb * 256;
438 set_total_count(cnt, w);
442 if ( strstr(line, "WPTDAT") || strstr(line, "TRKHDR") || strstr(line, "TRKDAT") || strstr(line, "RTEHDR") || strstr(line, "RTEWPT") ) {
444 set_current_count(gps_data->count, w);
454 void append_element (gpointer elem, gpointer user_data)
456 const gchar *text = ((BabelDevice*)elem)->label;
457 vik_combo_box_text_append (GTK_WIDGET(user_data), text);
460 static gint find_entry = -1;
461 static gint garmin_entry = -1;
463 static void find_garmin (gpointer elem, gpointer user_data)
465 const gchar *name = ((BabelDevice*)elem)->name;
467 if (!strcmp(name, "garmin")) {
468 garmin_entry = find_entry;
472 static void datasource_gps_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data )
474 gps_user_data_t *w = (gps_user_data_t *)user_data;
475 GtkTable *box, *data_type_box;
477 w->proto_l = gtk_label_new (_("GPS Protocol:"));
478 w->proto_b = vik_combo_box_text_new ();
479 g_list_foreach (a_babel_device_list, append_element, w->proto_b);
481 // Maintain default to Garmin devices (assumed most popular/numerous device)
482 if ( last_active < 0 ) {
484 g_list_foreach (a_babel_device_list, find_garmin, NULL);
485 if ( garmin_entry < 0 )
486 // Not found - so set it to the first entry
490 last_active = garmin_entry;
493 gtk_combo_box_set_active (GTK_COMBO_BOX(w->proto_b), last_active);
494 g_object_ref(w->proto_b);
496 w->ser_l = gtk_label_new (_("Serial Port:"));
497 #if GTK_CHECK_VERSION (2, 24, 0)
498 w->ser_b = gtk_combo_box_text_new_with_entry ();
500 w->ser_b = gtk_combo_box_entry_new_text ();
503 vik_combo_box_text_append (w->ser_b, "com1");
505 /* Here just try to see if the device is available which gets passed onto gpsbabel
506 List USB devices first as these will generally only be present if autogenerated by udev or similar
507 User is still able to set their own free text entry */
508 if (g_access ("/dev/ttyUSB0", R_OK) == 0)
509 vik_combo_box_text_append (w->ser_b, "/dev/ttyUSB0");
510 if (g_access ("/dev/ttyUSB1", R_OK) == 0)
511 vik_combo_box_text_append (w->ser_b, "/dev/ttyUSB1");
512 if (g_access ("/dev/ttyS0", R_OK) == 0)
513 vik_combo_box_text_append (w->ser_b, "/dev/ttyS0");
514 if (g_access ("/dev/ttyS1", R_OK) == 0)
515 vik_combo_box_text_append (w->ser_b, "/dev/ttyS1");
517 vik_combo_box_text_append (w->ser_b, "usb:");
518 gtk_combo_box_set_active (GTK_COMBO_BOX(w->ser_b), 0);
519 g_object_ref(w->ser_b);
521 w->off_request_l = gtk_label_new (_("Turn Off After Transfer\n(Garmin/NAViLink Only)"));
522 w->off_request_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
524 w->get_tracks_l = gtk_label_new (_("Tracks:"));
525 w->get_tracks_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
526 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_tracks_b), last_get_tracks);
528 w->get_routes_l = gtk_label_new (_("Routes:"));
529 w->get_routes_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
530 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_routes_b), last_get_routes);
532 w->get_waypoints_l = gtk_label_new (_("Waypoints:"));
533 w->get_waypoints_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
534 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->get_waypoints_b), last_get_waypoints);
536 box = GTK_TABLE(gtk_table_new(2, 4, FALSE));
537 data_type_box = GTK_TABLE(gtk_table_new(4, 1, FALSE));
539 gtk_table_attach_defaults(box, GTK_WIDGET(w->proto_l), 0, 1, 0, 1);
540 gtk_table_attach_defaults(box, GTK_WIDGET(w->proto_b), 1, 2, 0, 1);
541 gtk_table_attach_defaults(box, GTK_WIDGET(w->ser_l), 0, 1, 1, 2);
542 gtk_table_attach_defaults(box, GTK_WIDGET(w->ser_b), 1, 2, 1, 2);
543 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_tracks_l), 0, 1, 0, 1);
544 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_tracks_b), 1, 2, 0, 1);
545 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_routes_l), 2, 3, 0, 1);
546 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_routes_b), 3, 4, 0, 1);
547 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_waypoints_l), 4, 5, 0, 1);
548 gtk_table_attach_defaults(data_type_box, GTK_WIDGET(w->get_waypoints_b), 5, 6, 0, 1);
549 gtk_table_attach_defaults(box, GTK_WIDGET(data_type_box), 0, 2, 2, 3);
550 gtk_table_attach_defaults(box, GTK_WIDGET(w->off_request_l), 0, 1, 3, 4);
551 gtk_table_attach_defaults(box, GTK_WIDGET(w->off_request_b), 1, 3, 3, 4);
552 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), GTK_WIDGET(box), FALSE, FALSE, 5 );
554 gtk_widget_show_all ( dialog );
558 * datasource_gps_setup:
559 * @dialog: The GTK dialog. The caller is responsible for managing the dialog creation/deletion
560 * @xfer: The default type of items enabled for transfer, others disabled.
561 * @xfer_all: When specified all items are enabled for transfer.
563 * Returns: A gpointer to the private structure for GPS progress/information widgets
564 * Pass this pointer back into the other exposed datasource_gps_X functions
566 gpointer datasource_gps_setup ( GtkWidget *dialog, vik_gps_xfer_type xfer, gboolean xfer_all )
568 gps_user_data_t *w_gps = (gps_user_data_t *)datasource_gps_init_func ( NULL );
569 datasource_gps_add_setup_widgets ( dialog, NULL, w_gps );
571 gboolean way = xfer_all;
572 gboolean trk = xfer_all;
573 gboolean rte = xfer_all;
575 // Selectively turn bits on
578 case WPT: way = TRUE; break;
579 case RTE: rte = TRUE; break;
580 default: trk = TRUE; break;
585 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(w_gps->get_tracks_b), trk );
586 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_tracks_l), trk );
587 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_tracks_b), trk );
589 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(w_gps->get_routes_b), rte );
590 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_routes_l), rte );
591 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_routes_b), rte );
593 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(w_gps->get_waypoints_b), way );
594 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_waypoints_l), way );
595 gtk_widget_set_sensitive ( GTK_WIDGET(w_gps->get_waypoints_b), way );
597 return (gpointer)w_gps;
600 void datasource_gps_add_progress_widgets ( GtkWidget *dialog, gpointer user_data )
602 GtkWidget *gpslabel, *verlabel, *idlabel, *wplabel, *trklabel, *rtelabel;
604 gps_user_data_t *w_gps = (gps_user_data_t *)user_data;
606 gpslabel = gtk_label_new (_("GPS device: N/A"));
607 verlabel = gtk_label_new ("");
608 idlabel = gtk_label_new ("");
609 wplabel = gtk_label_new ("");
610 trklabel = gtk_label_new ("");
611 rtelabel = gtk_label_new ("");
613 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), gpslabel, FALSE, FALSE, 5 );
614 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), wplabel, FALSE, FALSE, 5 );
615 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), trklabel, FALSE, FALSE, 5 );
616 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), rtelabel, FALSE, FALSE, 5 );
618 gtk_widget_show_all ( dialog );
620 w_gps->gps_label = gpslabel;
621 w_gps->id_label = idlabel;
622 w_gps->ver_label = verlabel;
623 w_gps->progress_label = w_gps->wp_label = wplabel;
624 w_gps->trk_label = trklabel;
625 w_gps->rte_label = rtelabel;
626 w_gps->total_count = -1;