2 * Copyright (C) 2012 Adam Boggs <boggs@aircrafter.org>
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * If gpsd connection fails, try to connect again periodically.
20 * If gps stops sending data there should be an indication that it's stale.
31 #include <glib/gstdio.h>
39 #include "gps-plugin.h"
41 #include "../aweather-location.h"
45 /* interval to update map with new gps data in seconds. */
46 #define GPS_UPDATE_INTERVAL (2)
48 /* Filename and search path to use for gps marker, should be configurable */
49 #define GPS_MARKER_ICON_PATH ".:" PKGDATADIR
50 #define GPS_MARKER_ICON "arrow.png"
52 /* number of track points per group and number of groups to maintain */
53 #define GPS_TRACK_POINTS (256)
54 #define GPS_TRACK_GROUPS (16)
55 #define GPS_TRACK_POINTS_FACTOR (1.5)
57 /* interval to update log file in seconds (default value for slider) */
58 #define GPS_LOG_DEFAULT_UPDATE_INTERVAL (30)
59 #define GPS_LOG_EXT "csv"
61 /* For updating the status bar conveniently */
62 #define GPS_STATUSBAR_CONTEXT "GPS"
65 #define GPS_STATUS(gps, format, args...) \
67 gchar *buf = g_strdup_printf(format, ##args); \
68 gtk_statusbar_push(GTK_STATUSBAR(gps->status_bar), \
69 gtk_statusbar_get_context_id( \
70 GTK_STATUSBAR(gps->status_bar), \
71 GPS_STATUSBAR_CONTEXT), \
76 #define GPS_STATUS(gps, format, args...) \
78 gchar *buf = g_strdup_printf(format, ##args); \
79 g_debug("STATUS: %s", buf); \
87 /* Find a readable file in a colon delimeted path */
88 static gchar *find_path(const gchar *path, const gchar *filename)
90 gchar *end_ptr, *fullpath;
92 end_ptr = (gchar *)path;
95 while (*end_ptr != ':' && *end_ptr != '\0')
97 fullpath = g_strdup_printf("%.*s/%s", (int)(end_ptr-path), path,
99 g_debug("GritsPluginGps: find_path - searching %s", fullpath);
100 if (access(fullpath, R_OK) == 0) {
101 g_debug("GritsPluginGps: find_path - found %s", fullpath);
102 return fullpath; /* caller frees */
107 if (*end_ptr == '\0') {
110 return find_path(end_ptr + 1, filename);
115 /********************
117 ********************/
119 static gchar *gps_get_status(struct gps_data_t *gps_data)
124 switch (gps_data->fix.mode) {
126 status_color = "red";
127 status_text = "No Signal";
130 status_color = "red";
131 status_text = "No Fix";
134 status_color = "yellow";
135 status_text = "2D Mode";
138 status_color = "green";
139 status_text = "3D Mode";
142 status_color = "black";
143 status_text = "Unknown";
146 return g_strdup_printf("<span foreground=\"%s\">%s</span>",
147 status_color, status_text);
151 static gchar *gps_get_online(struct gps_data_t *gps_data)
156 if (gps_data->online == -1.0) {
157 online_str = "Offline";
159 online_str = "Online";
162 switch (gps_data->status) {
164 status_str = "No Fix";
167 status_str = "Fix Acquired";
170 status_str = "DGPS Fix";
173 status_str = "Unknown Status";
177 return g_strdup_printf("%lf,%s,%s", gps_data->online, online_str, status_str);
181 static gchar *gps_get_latitude(struct gps_data_t *gps_data)
183 return g_strdup_printf("%10.4f", gps_data->fix.latitude);
186 static gchar *gps_get_longitude(struct gps_data_t *gps_data)
188 return g_strdup_printf("%10.4f", gps_data->fix.longitude);
191 static gchar *gps_get_elevation(struct gps_data_t *gps_data)
193 /* XXX Make units (m/ft) settable */
194 return g_strdup_printf("%5.0lf %4s",
195 (gps_data->fix.altitude * METERS_TO_FEET), "ft");
198 static gchar *gps_get_heading(struct gps_data_t *gps_data)
200 /* XXX Make units (m/ft) settable */
201 return g_strdup_printf("%5.0lf %4s", gps_data->fix.track, "deg");
204 static gchar *gps_get_speed(struct gps_data_t *gps_data)
206 /* XXX Make units (m/ft) settable */
207 return g_strdup_printf("%5.0f %4s",
208 (gps_data->fix.speed*3600.0*METERS_TO_FEET/5280.0), "mph");
213 const gchar *initial_val;
214 gchar *(*get_data)(struct gps_data_t *);
216 GtkWidget *label_widget;
217 GtkWidget *value_widget;
219 {"Status:", "No Data", gps_get_status, 14, NULL, NULL},
220 // {"Online:", "No Data", gps_get_online, 14, NULL, NULL},
221 {"Latitude:", "No Data", gps_get_latitude, 14, NULL, NULL},
222 {"Longitude:", "No Data", gps_get_longitude, 14, NULL, NULL},
223 {"Elevation:", "No Data", gps_get_elevation, 14, NULL, NULL},
224 {"Heading:", "No Data", gps_get_heading, 14, NULL, NULL},
225 {"Speed:", "No Data", gps_get_speed, 14, NULL, NULL},
233 static void gps_track_init(GpsTrack *track)
235 /* Save a spot at the end for the NULL termination */
236 track->points = (gpointer)g_new0(double*, GPS_TRACK_GROUPS + 1);
237 track->cur_point = 0;
238 track->cur_group = 0;
239 track->num_points = 1; /* starts at 1 so realloc logic works */
243 static void gps_track_clear(GpsTrack *track)
246 for (pi = 0; pi < GPS_TRACK_GROUPS; pi++) {
247 if (track->points[pi] != NULL) {
248 g_free(track->points[pi]);
249 track->points[pi] = NULL;
252 track->cur_point = 0;
253 track->cur_group = 0;
254 track->num_points = 1; /* starts at 1 so realloc logic works */
257 static void gps_track_free(GpsTrack *track)
259 gps_track_clear(track);
260 g_free(track->points);
263 /* add a new track group (points in a track group are connected, and
264 * separated from points in other track groups). */
265 static void gps_track_group_incr(GpsTrack *track)
267 gdouble (**points)[3] = track->points; /* for simplicity */
269 /* Just return if they increment it again before any points have
272 if (points[track->cur_group] == NULL) {
276 g_debug("GritsPluginGps: track_group_incr - track group %u->%u.",
277 track->cur_group, track->cur_group + 1);
280 track->cur_point = 0;
281 track->num_points = 1; /* starts at 1 so realloc logic works */
283 if (track->cur_group >= GPS_TRACK_GROUPS) {
284 g_debug("GritsPluginGps: track_group_incr - track group %u "
285 "is at max %u, shifting groups",
286 track->cur_group, GPS_TRACK_GROUPS);
288 /* Free the oldest one which falls off the end */
291 /* shift the rest down, last one should be NULL already */
292 /* note we alloc GPS_TRACK_GROUPS+1 */
293 for (int pi = 0; pi < GPS_TRACK_GROUPS; pi++) {
294 points[pi] = points[pi+1];
297 /* always write into the last group */
298 track->cur_group = GPS_TRACK_GROUPS - 1;
302 static void gps_track_add_point(GpsTrack *track,
303 gdouble lat, gdouble lon, gdouble elevation)
305 gdouble (**points)[3] = track->points; /* for simplicity */
307 g_debug("GritsPluginGps: track_add_point");
309 g_assert(track->cur_group < GPS_TRACK_GROUPS &&
310 (track->cur_point <= track->num_points));
312 /* resize/allocate the point group if the current one is full */
313 if (track->cur_point >= track->num_points - 1) {
314 guint new_size = track->num_points == 1 ?
316 track->num_points * GPS_TRACK_POINTS_FACTOR;
317 g_debug("GritsPluginGps: track_add_point - reallocating points "
318 "array from %u points to %u points.\n",
319 track->num_points, new_size);
320 points[track->cur_group] = (gpointer)g_renew(gdouble,
321 points[track->cur_group], 3*(new_size+1));
322 track->num_points = new_size;
325 g_assert(points[track->cur_group] != NULL);
327 /* Add the coordinate */
328 lle2xyz(lat, lon, elevation,
329 &points[track->cur_group][track->cur_point][0],
330 &points[track->cur_group][track->cur_point][1],
331 &points[track->cur_group][track->cur_point][2]);
335 /* make sure last point is always 0s so the line drawing stops. */
336 points[track->cur_group][track->cur_point][0] = 0.0;
337 points[track->cur_group][track->cur_point][1] = 0.0;
338 points[track->cur_group][track->cur_point][2] = 0.0;
346 static gchar *gps_get_date_string(double gps_time)
348 static gchar buf[256];
349 time_t int_time = (time_t)gps_time;
352 gmtime_r(&int_time, &tm_time);
354 snprintf(buf, sizeof(buf), "%04d-%02d-%02d",
355 tm_time.tm_year+1900, tm_time.tm_mon+1, tm_time.tm_mday);
360 static gchar *gps_get_time_string(time_t gps_time)
362 static gchar buf[256];
363 time_t int_time = (time_t)gps_time;
366 gmtime_r(&int_time, &tm_time);
368 snprintf(buf, sizeof(buf), "%02d:%02d:%02dZ",
369 tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
374 static gboolean gps_write_log(gpointer data)
376 GritsPluginGps *gps = (GritsPluginGps *)data;
377 struct gps_data_t *gps_data = &gps->gps_data;
381 gboolean new_file = FALSE;
383 if (gps_data == NULL) {
384 g_warning("Skipped write to GPS log file: "
385 "can not get GPS coordinates.");
386 GPS_STATUS(gps, "Skipped write to GPS log file: "
387 "can not get GPS coordinates.");
391 /* get filename from text entry box. If empty, generate a name from
392 * the date and time and set it. */
393 if (strlen(gtk_entry_get_text(
394 GTK_ENTRY(gps->ui.gps_log_filename_entry))) == 0) {
395 snprintf(filename, sizeof(filename),
397 gps_get_date_string(gps->gps_data.fix.time),
398 gps_get_time_string(gps->gps_data.fix.time),
400 gtk_entry_set_text(GTK_ENTRY(gps->ui.gps_log_filename_entry),
405 gtk_entry_get_text(GTK_ENTRY(gps->ui.gps_log_filename_entry)),
408 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
412 if ((fd = open(filename, O_CREAT|O_APPEND|O_WRONLY, 0644)) == -1) {
413 g_warning("Error opening log file %s: %s",
414 filename, strerror(errno));
419 /* write header and reset record counter */
420 snprintf(buf, sizeof(buf),
421 "No,Date,Time,Lat,Lon,Ele,Head,Speed,RTR\n");
422 if (write(fd, buf, strlen(buf)) == -1) {
423 g_warning("Error writing header to log file %s: %s",
424 filename, strerror(errno));
426 gps->ui.gps_log_number = 1;
429 /* Write log entry. Make sure this matches the header */
430 /* "No,Date,Time,Lat,Lon,Ele,Head,Speed,Fix,RTR\n" */
431 /* RTR values: T=time, B=button push, S=speed, D=distance */
432 snprintf(buf, sizeof(buf), "%d,%s,%s,%lf,%lf,%lf,%lf,%lf,%c\n",
433 gps->ui.gps_log_number++,
434 gps_get_date_string(gps->gps_data.fix.time),
435 gps_get_time_string(gps->gps_data.fix.time),
436 //gps_data->fix.time,
437 gps_data->fix.latitude,
438 gps_data->fix.longitude,
439 gps_data->fix.altitude * METERS_TO_FEET,
441 gps_data->fix.speed * METERS_TO_FEET,
442 'T'); /* position due to timer expired */
444 if (write(fd, buf, strlen(buf)) == -1) {
445 g_warning("Could not write log number %d to log file %s: %s",
446 gps->ui.gps_log_number-1, filename, strerror(errno));
450 GPS_STATUS(gps, "Updated GPS log file %s.", filename);
461 static gboolean gps_data_is_valid(struct gps_data_t *gps_data)
463 if (gps_data != NULL && gps_data->online != -1.0 &&
464 gps_data->fix.mode >= MODE_2D &&
465 gps_data->status > STATUS_NO_FIX) {
472 static void gps_update_status(GritsPluginGps *gps)
474 struct gps_data_t *gps_data = &gps->gps_data;
476 /* gps table update */
477 for (gint i = 0; i < G_N_ELEMENTS(gps_table); i++) {
478 gchar *str = gps_table[i].get_data(gps_data);
479 gtk_label_set_markup(GTK_LABEL(gps_table[i].value_widget), str);
484 /* external interface to update UI from latest GPS data. */
485 gboolean gps_redraw_all(gpointer data)
487 GritsPluginGps *gps = (GritsPluginGps *)data;
490 struct gps_data_t *gps_data = &gps->gps_data;
492 g_debug("GritsPluginGps: redraw_all");
495 if (!gps_data_is_valid(gps_data)) {
496 g_debug("GritsPluginGps: redraw_all - gps_data is not valid.");
497 /* XXX Change marker to indicate data is not valid */
501 /* update position labels */
502 gps_update_status(gps);
504 /* Update track and marker position */
505 if (gps_data_is_valid(gps_data) && gps->track.active) {
506 g_debug("GritsPluginGps: redraw_all - updating track group %u "
507 "point %u at lat = %f, long = %f, track = %f",
508 gps->track.cur_group,
509 gps->track.cur_point,
510 gps_data->fix.latitude,
511 gps_data->fix.longitude,
512 gps_data->fix.track);
514 gps_track_add_point(&gps->track,
515 gps_data->fix.latitude, gps_data->fix.longitude, 0.0);
517 if (gps->track.line) {
518 grits_viewer_remove(gps->viewer,
519 GRITS_OBJECT(gps->track.line));
520 gps->track.line = NULL;
523 gps->track.line = grits_line_new(gps->track.points);
524 gps->track.line->color[0] = 1.0;
525 gps->track.line->color[1] = 0;
526 gps->track.line->color[2] = 0.1;
527 gps->track.line->color[3] = 1.0;
528 gps->track.line->width = 3;
530 grits_viewer_add(gps->viewer, GRITS_OBJECT(gps->track.line),
531 GRITS_LEVEL_OVERLAY, FALSE);
532 grits_object_queue_draw(GRITS_OBJECT(gps->track.line));
535 if (gps_data_is_valid(gps_data)) {
537 grits_viewer_remove(gps->viewer,
538 GRITS_OBJECT(gps->marker));
542 gchar *path = find_path(GPS_MARKER_ICON_PATH, GPS_MARKER_ICON);
544 gps->marker = grits_marker_icon_new("GPS", path,
545 gps_data->fix.track, TRUE, GRITS_MARKER_DMASK_ICON);
548 /* if icon not found just use a point */
549 g_warning("Could not find GPS marker icon %s in path %s.",
550 GPS_MARKER_ICON, GPS_MARKER_ICON_PATH);
551 gps->marker = grits_marker_icon_new("GPS", NULL,
552 gps_data->fix.track, FALSE,
553 GRITS_MARKER_DMASK_POINT |
554 GRITS_MARKER_DMASK_LABEL);
557 GRITS_OBJECT(gps->marker)->center.lat = gps_data->fix.latitude;
558 GRITS_OBJECT(gps->marker)->center.lon = gps_data->fix.longitude;
559 GRITS_OBJECT(gps->marker)->center.elev = 0.0;
560 GRITS_OBJECT(gps->marker)->lod = EARTH_R * 5;
561 GRITS_MARKER(gps->marker)->ortho = FALSE;
563 grits_viewer_add(gps->viewer, GRITS_OBJECT(gps->marker),
564 GRITS_LEVEL_OVERLAY+1, FALSE);
565 grits_object_queue_draw(GRITS_OBJECT(gps->marker));
568 if (gps->follow_gps && gps_data_is_valid(gps_data)) {
569 /* Center map at current GPS position. */
570 g_debug("GritsPluginGps: redraw_all - centering map at "
571 "lat = %f, long = %f, track = %f",
572 gps_data->fix.latitude,
573 gps_data->fix.longitude,
574 gps_data->fix.track);
576 double lat, lon, elev;
577 grits_viewer_get_location(gps->viewer, &lat, &lon, &elev);
578 grits_viewer_set_location(gps->viewer, gps_data->fix.latitude,
579 gps_data->fix.longitude, elev);
580 //grits_viewer_set_rotation(gps->viewer, 0, 0, 0);
593 static void gps_init_status_info(GritsPluginGps *gps, GtkWidget *gbox)
595 gps->ui.gps_status_frame = gtk_frame_new("<b>GPS Data</b>");
596 GtkWidget *label = gtk_frame_get_label_widget(
597 GTK_FRAME(gps->ui.gps_status_frame));
598 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
599 gtk_frame_set_shadow_type(GTK_FRAME(gps->ui.gps_status_frame),
601 gps->ui.gps_status_table = gtk_table_new(5, 2, FALSE);
602 gtk_container_add(GTK_CONTAINER(gps->ui.gps_status_frame),
603 gps->ui.gps_status_table);
606 /* gps data table setup */
608 for (i = 0; i < G_N_ELEMENTS(gps_table); i++) {
609 gps_table[i].label_widget = gtk_label_new(gps_table[i].label);
610 gps_table[i].value_widget = gtk_label_new(gps_table[i].initial_val);
612 gtk_misc_set_alignment(GTK_MISC(gps_table[i].label_widget), 0, 0);
613 gtk_misc_set_alignment(GTK_MISC(gps_table[i].value_widget), 1, 0);
615 gtk_table_attach(GTK_TABLE(gps->ui.gps_status_table),
616 gps_table[i].label_widget, 0, 1, i, i+1,
617 GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
618 gtk_table_attach(GTK_TABLE(gps->ui.gps_status_table),
619 gps_table[i].value_widget, 1, 2, i, i+1,
620 GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
622 PangoFontDescription *roman = pango_font_description_new();
623 PangoFontDescription *mono = pango_font_description_from_string("monospace");
624 pango_font_description_set_size(roman, gps_table[i].font_size*PANGO_SCALE);
625 pango_font_description_set_size(mono, gps_table[i].font_size*PANGO_SCALE);
626 gtk_widget_modify_font(gps_table[i].label_widget, roman);
627 gtk_widget_modify_font(gps_table[i].value_widget, mono);
628 pango_font_description_free(roman);
629 pango_font_description_free(mono);
631 gtk_box_pack_start(GTK_BOX(gbox), gps->ui.gps_status_frame,
634 /* Start UI refresh task, which will reschedule itgps. */
636 gps->gps_update_timeout_id = g_timeout_add(
637 GPS_UPDATE_INTERVAL*1000,
638 gps_redraw_all, gps);
642 /* GPS Control Frame */
643 static gboolean on_gps_follow_clicked_event(GtkWidget *widget, gpointer user_data)
645 GritsPluginGps *gps = (GritsPluginGps *)user_data;
647 g_debug("GritsPluginGps: follow_clicked_event - button status %d",
648 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
649 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
650 gps->follow_gps = TRUE;
652 gps->follow_gps = FALSE;
658 static gboolean on_gps_track_enable_clicked_event(GtkWidget *widget, gpointer user_data)
660 GritsPluginGps *gps = (GritsPluginGps *)user_data;
662 g_debug("GritsPluginGps: track_enable_clicked_event");
664 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
665 /* start logging trip history */
666 GPS_STATUS(gps, "Enabled GPS track.");
667 gps->track.active = TRUE;
669 /* stop logging trip history */
670 GPS_STATUS(gps, "Disabled GPS track.");
671 gps->track.active = FALSE;
672 /* advance to the next track group, moving everything down if
674 gps_track_group_incr(&gps->track);
680 static gboolean on_gps_track_clear_clicked_event(GtkWidget *widget, gpointer user_data)
682 GritsPluginGps *gps = (GritsPluginGps *)user_data;
684 g_debug("GritsPluginGps: track_clear_clicked_event");
685 GPS_STATUS(gps, "Cleared GPS track.");
686 gps_track_clear(&gps->track);
691 static void gps_init_control_frame(GritsPluginGps *gps, GtkWidget *gbox)
693 /* Control checkboxes */
694 GtkWidget *gps_control_frame = gtk_frame_new("<b>GPS Control</b>");
695 GtkWidget *label = gtk_frame_get_label_widget(
696 GTK_FRAME(gps_control_frame));
697 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
698 gtk_frame_set_shadow_type(GTK_FRAME(gps_control_frame),
700 GtkWidget *cbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
701 gtk_container_add(GTK_CONTAINER(gps_control_frame), cbox);
702 gtk_box_pack_start(GTK_BOX(gbox), gps_control_frame, FALSE, FALSE, 0);
704 gps->ui.gps_follow_checkbox =
705 gtk_check_button_new_with_label("Follow GPS");
706 g_signal_connect(G_OBJECT(gps->ui.gps_follow_checkbox), "clicked",
707 G_CALLBACK(on_gps_follow_clicked_event),
709 gtk_box_pack_start(GTK_BOX(cbox), gps->ui.gps_follow_checkbox,
712 gps->ui.gps_track_checkbox =
713 gtk_check_button_new_with_label("Record Track");
714 g_signal_connect(G_OBJECT(gps->ui.gps_track_checkbox), "clicked",
715 G_CALLBACK(on_gps_track_enable_clicked_event),
717 gtk_box_pack_start(GTK_BOX(cbox), gps->ui.gps_track_checkbox,
720 gps->ui.gps_clear_button = gtk_button_new_with_label("Clear Track");
721 g_signal_connect(G_OBJECT(gps->ui.gps_clear_button), "clicked",
722 G_CALLBACK(on_gps_track_clear_clicked_event),
724 gtk_box_pack_start(GTK_BOX(cbox), gps->ui.gps_clear_button,
728 /* Track Log Frame */
729 static gboolean on_gps_log_clicked_event(GtkWidget *widget, gpointer user_data)
731 GritsPluginGps *gps = (GritsPluginGps *)user_data;
733 g_debug("GritsPluginGps: log_clicked_event");
735 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
738 /* Schedule log file write */
739 gps->ui.gps_log_timeout_id = g_timeout_add(
741 GTK_RANGE(gps->ui.gps_log_interval_slider))*1000,
744 /* button unchecked */
745 g_source_remove(gps->ui.gps_log_timeout_id);
746 gps->ui.gps_log_timeout_id = 0;
747 g_debug("GritsPluginGps: log_clicked_event - closed log file.");
753 static gboolean on_gps_log_interval_changed_event(GtkWidget *widget, gpointer user_data)
755 GritsPluginGps *gps = (GritsPluginGps *)user_data;
759 g_debug("GritsPluginGps: log_interval_changed_event - value = %f",
760 gtk_range_get_value(GTK_RANGE(widget)));
762 if (gtk_toggle_button_get_active(
763 GTK_TOGGLE_BUTTON(gps->ui.gps_log_checkbox))) {
764 g_assert(gps->ui.gps_log_timeout_id != 0);
766 /* disable old timeout */
767 g_source_remove(gps->ui.gps_log_timeout_id);
768 gps->ui.gps_log_timeout_id = 0;
770 /* Schedule new log file write */
771 gps->ui.gps_log_timeout_id = g_timeout_add(
772 gtk_range_get_value(GTK_RANGE(widget))*1000,
780 static void gps_init_track_log_frame(GritsPluginGps *gps, GtkWidget *gbox)
782 /* Track log box with enable checkbox and filename entry */
783 GtkWidget *gps_log_frame = gtk_frame_new("<b>Track Log</b>");
784 GtkWidget *label = gtk_frame_get_label_widget(GTK_FRAME(gps_log_frame));
785 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
786 gtk_frame_set_shadow_type(GTK_FRAME(gps_log_frame), GTK_SHADOW_NONE);
787 GtkWidget *lbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
788 gtk_container_add(GTK_CONTAINER(gps_log_frame), lbox);
789 gtk_box_pack_start(GTK_BOX(gbox), gps_log_frame,
792 gps->ui.gps_log_checkbox =
793 gtk_check_button_new_with_label("Log Position to File");
794 g_signal_connect(G_OBJECT(gps->ui.gps_log_checkbox), "clicked",
795 G_CALLBACK(on_gps_log_clicked_event),
797 gtk_box_pack_start(GTK_BOX(lbox), gps->ui.gps_log_checkbox,
800 /* Set up filename entry box */
801 GtkWidget *fbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
802 GtkWidget *filename_label = gtk_label_new("Filename:");
803 gtk_box_pack_start(GTK_BOX(fbox), filename_label, FALSE, FALSE, 0);
804 gps->ui.gps_log_filename_entry = gtk_entry_new();
805 gtk_box_pack_start(GTK_BOX(fbox), gps->ui.gps_log_filename_entry,
807 gtk_box_pack_start(GTK_BOX(lbox), fbox, FALSE, FALSE, 0);
809 /* set up gps log interval slider */
810 GtkWidget *ubox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
811 GtkWidget *interval_label = gtk_label_new("Update Interval:");
812 gtk_box_pack_start(GTK_BOX(ubox), interval_label, FALSE, FALSE, 0);
813 gps->ui.gps_log_interval_slider =
814 gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 1.0, 600.0, 30.0);
815 gtk_range_set_value(GTK_RANGE(gps->ui.gps_log_interval_slider),
816 GPS_LOG_DEFAULT_UPDATE_INTERVAL);
817 g_signal_connect(G_OBJECT(gps->ui.gps_log_interval_slider),
819 G_CALLBACK(on_gps_log_interval_changed_event),
821 gtk_range_set_increments(
822 GTK_RANGE(gps->ui.gps_log_interval_slider),
823 10.0 /* step */, 30.0 /* page up/down */);
824 #if ! GTK_CHECK_VERSION(3,0,0)
825 gtk_range_set_update_policy(
826 GTK_RANGE(gps->ui.gps_log_interval_slider),
829 gtk_box_pack_start(GTK_BOX(ubox), gps->ui.gps_log_interval_slider,
831 gtk_box_pack_start(GTK_BOX(lbox), ubox, FALSE, FALSE, 0);
839 static gboolean process_gps(GIOChannel *source, GIOCondition condition, gpointer data)
841 struct gps_data_t *gps_data = (struct gps_data_t *)data;
843 g_debug("GritsPluginGps: process_gps");
845 /* Process any data from the gps and call the hook function */
846 if (gps_data != NULL) {
847 gdouble track = gps_data->fix.track;
848 gint result = gps_read(gps_data);
849 if (isnan(gps_data->fix.track))
850 gps_data->fix.track = track;
851 g_debug("GritsPluginGps: process_gps - gps_read returned %d, "
852 "position %f, %f.", result,
853 gps_data->fix.latitude, gps_data->fix.longitude);
855 g_warning("GritsPluginGps: process_gps - gps_data == NULL.");
860 static gint initialize_gpsd(char *server, gchar *port, struct gps_data_t *gps_data)
862 #if GPSD_API_MAJOR_VERSION < 5
863 #error "GPSD protocol version 5 or greater required."
865 gint result = gps_open(server, port, gps_data);
867 g_warning("Unable to open gpsd connection to %s:%s: %d, %d, %s",
868 server, port, result, errno, gps_errstr(errno));
872 (void)gps_stream(gps_data, WATCH_ENABLE|WATCH_JSON, NULL);
873 g_debug("GritsPluginGps: initialize_gpsd - gpsd fd %u.", gps_data->gps_fd);
874 GIOChannel *channel = g_io_channel_unix_new(gps_data->gps_fd);
875 gint input_tag = g_io_add_watch(channel, G_IO_IN, process_gps, gps_data);
876 g_io_channel_unref(channel);
881 /**********************
882 * GPS Plugin Methods *
883 **********************/
886 GritsPluginGps *grits_plugin_gps_new(GritsViewer *viewer, GritsPrefs *prefs)
888 /* TODO: move to constructor if possible */
889 g_debug("GritsPluginGps: new");
890 GritsPluginGps *gps = g_object_new(GRITS_TYPE_PLUGIN_GPS, NULL);
891 gps->viewer = g_object_ref(viewer);
892 gps->prefs = g_object_ref(prefs);
894 gps->input_tag = initialize_gpsd("localhost", DEFAULT_GPSD_PORT, &gps->gps_data);
895 gps->follow_gps = FALSE;
897 gps_track_init(&gps->track);
898 gps_init_status_info(gps, gps->config);
899 gps_init_control_frame(gps, gps->config);
900 gps_init_track_log_frame(gps, gps->config);
905 static GtkWidget *grits_plugin_gps_get_config(GritsPlugin *_gps)
907 GritsPluginGps *gps = GRITS_PLUGIN_GPS(_gps);
912 static void grits_plugin_gps_plugin_init(GritsPluginInterface *iface);
913 G_DEFINE_TYPE_WITH_CODE(GritsPluginGps, grits_plugin_gps, G_TYPE_OBJECT,
914 G_IMPLEMENT_INTERFACE(GRITS_TYPE_PLUGIN,
915 grits_plugin_gps_plugin_init));
917 static void grits_plugin_gps_plugin_init(GritsPluginInterface *iface)
919 g_debug("GritsPluginGps: plugin_init");
920 /* Add methods to the interface */
921 iface->get_config = grits_plugin_gps_get_config;
924 static void grits_plugin_gps_init(GritsPluginGps *gps)
926 g_debug("GritsPluginGps: gps_init");
928 gps->config = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 15);
931 static void grits_plugin_gps_dispose(GObject *gobject)
933 GritsPluginGps *gps = GRITS_PLUGIN_GPS(gobject);
935 g_debug("GritsPluginGps: dispose");
937 if (gps->gps_update_timeout_id) {
938 g_source_remove(gps->gps_update_timeout_id);
939 gps->gps_update_timeout_id = 0;
941 if (gps->ui.gps_log_timeout_id) {
942 g_source_remove(gps->ui.gps_log_timeout_id);
943 gps->ui.gps_log_timeout_id = 0;
945 if (gps->input_tag) {
946 g_source_remove(gps->input_tag);
950 GritsViewer *viewer = gps->viewer;
953 grits_viewer_remove(viewer,
954 GRITS_OBJECT(gps->marker));
955 g_object_unref(gps->prefs);
956 g_object_unref(viewer);
959 /* Drop references */
960 G_OBJECT_CLASS(grits_plugin_gps_parent_class)->dispose(gobject);
963 static void grits_plugin_gps_finalize(GObject *gobject)
965 GritsPluginGps *gps = GRITS_PLUGIN_GPS(gobject);
967 g_debug("GritsPluginGps: finalize");
970 gps_track_free(&gps->track);
971 G_OBJECT_CLASS(grits_plugin_gps_parent_class)->finalize(gobject);
974 static void grits_plugin_gps_class_init(GritsPluginGpsClass *klass)
976 g_debug("GritsPluginGps: class_init");
977 GObjectClass *gobject_class = (GObjectClass*)klass;
978 gobject_class->dispose = grits_plugin_gps_dispose;
979 gobject_class->finalize = grits_plugin_gps_finalize;