X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=src%2Fplugins%2Fgps-plugin.c;h=600f62ab1d0a305f828ee30f572cf3a6892b9116;hb=10a7c438a16e1a499ba0ccd4d4da717657fbd343;hp=928a75839e98028b8f8c9aa24c4386375bc845ce;hpb=9f97218d0dac0dd130a0d8c2e254d21fa3e308a9;p=aweather diff --git a/src/plugins/gps-plugin.c b/src/plugins/gps-plugin.c index 928a758..600f62a 100644 --- a/src/plugins/gps-plugin.c +++ b/src/plugins/gps-plugin.c @@ -15,6 +15,11 @@ * along with this program. If not, see . */ + /* TODO: + * If gpsd connection fails, try to connect again periodically. + * If gps stops sending data there should be an indication that it's stale. + */ + #define _XOPEN_SOURCE #include #include @@ -38,16 +43,15 @@ /* interval to update map with new gps data in seconds. */ #define GPS_UPDATE_INTERVAL (2) +/* number of track points per group and number of groups to maintain */ +#define NUM_TRACK_POINTS (6) +#define NUM_TRACK_GROUPS (4) +#define NUM_TRACK_POINTS_FACTOR (1.5) + /* interval to update log file in seconds (default value for slider) */ #define GPS_LOG_DEFAULT_UPDATE_INTERVAL (30) #define GPS_LOG_EXT "csv" -/* interval to update SpotterNetwork in seconds. Should not be less - * than 3 minutes per SpotterNetwork policy. - */ -#define GPS_SN_UPDATE_INTERVAL (5 * 60) -#define GPS_SN_URI "http://www.spotternetwork.org/update.php" - /* For updating the status bar conveniently */ #define GPS_STATUSBAR_CONTEXT "GPS" #if 0 @@ -65,7 +69,7 @@ #define GPS_STATUS(gps_state, format, args...) \ do { \ char *buf = g_strdup_printf(format, ##args); \ - g_warning("STATUS: %s", buf); \ + g_debug("STATUS: %s", buf); \ } while (0) @@ -74,17 +78,11 @@ static char *gps_get_time_string(time_t gps_time); static char *gps_get_date_string(double gps_time); static void process_gps( gpointer, gint, GdkInputCondition); -/* SpotterNetwork support */ -static void gps_init_spotter_network_frame(GritsPluginGPS *gps_state, - GtkWidget *gbox); -static gboolean gps_update_sn(gpointer data); -static void gps_sn_update_complete(SoupSession *, SoupMessage *, gpointer); -static gboolean on_gps_sn_clicked_event (GtkWidget *widget, gpointer user_data); -static gboolean on_gps_sn_active_clicked_event (GtkWidget *widget, gpointer user_data); - +#ifdef GPS_RANGE_RINGS static void gps_init_range_rings(GritsPluginGPS *gps_state, GtkWidget *gbox); static gboolean on_gps_rangering_clicked_event (GtkWidget *widget, gpointer user_data); +#endif static void gps_init_status_info(GritsPluginGPS *gps_state, GtkWidget *gbox); @@ -96,7 +94,16 @@ static gboolean on_gps_follow_clicked_event (GtkWidget *widget, gpointer user_da static void gps_init_track_log_frame(GritsPluginGPS *gps_state, GtkWidget *gbox); static gboolean on_gps_log_clicked_event (GtkWidget *widget, gpointer user_data); -static gboolean on_gps_track_clicked_event (GtkWidget *widget, gpointer user_data); + +/* Track management */ +static void gps_track_init(struct gps_track_t *track); +static void gps_track_free(struct gps_track_t *track); +static void gps_track_clear(struct gps_track_t *track); +static void gps_track_add_point(struct gps_track_t *track, gdouble lat, gdouble lon, gdouble elevation); +static void gps_track_group_incr(struct gps_track_t *track); + +static gboolean on_gps_track_enable_clicked_event(GtkWidget *widget, gpointer user_data); +static gboolean on_gps_track_clear_clicked_event(GtkWidget *widget, gpointer user_data); static gboolean gps_write_log(gpointer data); static char *gps_get_status(struct gps_data_t *); @@ -126,15 +133,6 @@ struct gps_status_info gps_table[] = { {"Speed:", "No Data", gps_get_speed, 14, NULL, NULL}, }; -static void _gtk_bin_set_child(GtkBin *bin, GtkWidget *new) -{ - GtkWidget *old = gtk_bin_get_child(bin); - if (old) - gtk_widget_destroy(old); - gtk_container_add(GTK_CONTAINER(bin), new); - gtk_widget_show_all(new); -} - static gboolean gps_data_is_valid(struct gps_data_t *gps_data) { @@ -198,40 +196,71 @@ static void gps_init_control_frame(GritsPluginGPS *gps_state, GtkWidget *gbox) { /* Control checkboxes */ - GtkWidget *gps_control_frame = gtk_frame_new ("GPS Control"); - GtkWidget *cbox = gtk_vbox_new (FALSE, 2); - gtk_container_add (GTK_CONTAINER (gps_control_frame), cbox); - gtk_box_pack_start (GTK_BOX(gbox), gps_control_frame, FALSE, FALSE, 0); + GtkWidget *gps_control_frame = gtk_frame_new("GPS Control"); + GtkWidget *cbox = gtk_vbox_new(FALSE, 2); + gtk_container_add(GTK_CONTAINER(gps_control_frame), cbox); + gtk_box_pack_start(GTK_BOX(gbox), gps_control_frame, FALSE, FALSE, 0); gps_state->ui.gps_follow_checkbox = gtk_check_button_new_with_label("Follow GPS"); - g_signal_connect (G_OBJECT (gps_state->ui.gps_follow_checkbox), "clicked", + g_signal_connect(G_OBJECT(gps_state->ui.gps_follow_checkbox), "clicked", G_CALLBACK (on_gps_follow_clicked_event), (gpointer)gps_state); - gtk_box_pack_start (GTK_BOX(cbox), gps_state->ui.gps_follow_checkbox, + gtk_box_pack_start(GTK_BOX(cbox), gps_state->ui.gps_follow_checkbox, + FALSE, FALSE, 0); + + gps_state->ui.gps_track_checkbox = gtk_check_button_new_with_label("Record Track"); + g_signal_connect(G_OBJECT(gps_state->ui.gps_track_checkbox), "clicked", + G_CALLBACK (on_gps_track_enable_clicked_event), + (gpointer)gps_state); + gtk_box_pack_start(GTK_BOX(cbox), gps_state->ui.gps_track_checkbox, FALSE, FALSE, 0); - gps_state->ui.gps_track_checkbox = gtk_check_button_new_with_label("Show Track"); - g_signal_connect (G_OBJECT (gps_state->ui.gps_track_checkbox), "clicked", - G_CALLBACK (on_gps_track_clicked_event), + + gps_state->ui.gps_clear_button = gtk_button_new_with_label("Clear Track"); + g_signal_connect(G_OBJECT(gps_state->ui.gps_clear_button), "clicked", + G_CALLBACK (on_gps_track_clear_clicked_event), (gpointer)gps_state); - gtk_box_pack_start (GTK_BOX(cbox), gps_state->ui.gps_track_checkbox, + gtk_box_pack_start(GTK_BOX(cbox), gps_state->ui.gps_clear_button, FALSE, FALSE, 0); } static gboolean -on_gps_track_clicked_event (GtkWidget *widget, gpointer user_data) +on_gps_track_enable_clicked_event(GtkWidget *widget, gpointer user_data) { - g_debug("on_gps_track_clicked_event called!"); + GritsPluginGPS *gps_state = (GritsPluginGPS *)user_data; + + g_debug("on_gps_track_enable_clicked_event called"); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) { - /* XXX start logging trip history */ + /* start logging trip history */ GPS_STATUS(gps_state, "Enabled GPS track."); + gps_state->track.active = TRUE; } else { - /* XXX stop logging trip history */ + /* stop logging trip history */ GPS_STATUS(gps_state, "Disabled GPS track."); + gps_state->track.active = FALSE; + /* advance to the next track group, moving everything down if + * it's full. + */ + gps_track_group_incr(&gps_state->track); } return FALSE; } +static gboolean +on_gps_track_clear_clicked_event(GtkWidget *widget, gpointer user_data) +{ + GritsPluginGPS *gps_state = (GritsPluginGPS *)user_data; + + g_debug("on_gps_track_clear_clicked_event called"); + + GPS_STATUS(gps_state, "Cleared GPS track."); + + gps_track_clear(&gps_state->track); + + return FALSE; +} + static gboolean on_gps_log_interval_changed_event(GtkWidget *widget, gpointer user_data) { @@ -432,163 +461,6 @@ on_gps_log_clicked_event (GtkWidget *widget, gpointer user_data) return FALSE; } -static void -gps_init_spotter_network_frame(GritsPluginGPS *gps_state, GtkWidget *gbox) -{ - /* Spotter network control box with enable checkbox, active - * checkbox, and sn ID entry - */ - GtkWidget *gps_sn_frame = gtk_frame_new ("SpotterNetwork Control"); - GtkWidget *sbox = gtk_vbox_new (FALSE, 2); - gtk_container_add (GTK_CONTAINER (gps_sn_frame), sbox); - - /* create spotter network update checkbox */ - gps_state->ui.gps_sn_checkbox = gtk_check_button_new_with_label("Update SpotterNetwork"); - g_signal_connect (G_OBJECT (gps_state->ui.gps_sn_checkbox), "clicked", - G_CALLBACK (on_gps_sn_clicked_event), - (gpointer)gps_state); - gtk_box_pack_start (GTK_BOX(sbox), gps_state->ui.gps_sn_checkbox, - FALSE, FALSE, 0); - - /* Create spotter network active checkbox */ - gps_state->ui.gps_sn_active_checkbox = gtk_check_button_new_with_label("Active"); - g_signal_connect (G_OBJECT (gps_state->ui.gps_sn_active_checkbox), - "clicked", - G_CALLBACK (on_gps_sn_active_clicked_event), - (gpointer)gps_state); - gtk_box_pack_start (GTK_BOX(sbox), - gps_state->ui.gps_sn_active_checkbox, - FALSE, FALSE, 0); - - /* Create spotter network ID entry box and label */ - GtkWidget *ebox = gtk_hbox_new (FALSE, 2); - GtkWidget *sn_label = gtk_label_new ("SpotterID:"); - gtk_box_pack_start (GTK_BOX(ebox), sn_label, FALSE, FALSE, 0); - gps_state->ui.gps_sn_entry = gtk_entry_new(); - gtk_box_pack_start (GTK_BOX(ebox), gps_state->ui.gps_sn_entry, - FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(sbox), ebox, FALSE, FALSE, 0); - - gtk_box_pack_start (GTK_BOX(gbox), gps_sn_frame, FALSE, FALSE, 0); -} - -static gboolean -on_gps_sn_clicked_event (GtkWidget *widget, gpointer user_data) -{ - GritsPluginGPS *gps_state = (GritsPluginGPS *)user_data; - g_debug("on_gps_sn_clicked_event called"); - - /* When pressed, log a position report to spotternetwork and - * schedule another one for the future. - */ - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) { - /* Change naumber of concurrent connections option? */ - assert(gps_state->ui.soup_session == NULL); - gps_state->ui.soup_session = soup_session_async_new_with_options( - SOUP_SESSION_USER_AGENT, - "Mozilla/5.0 (Windows; U; Windows NT 5.1; " - "en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11", - NULL); - - /* XXX don't log right away to give the user a chance to cancel */ - gps_update_sn(gps_state); - gps_state->ui.gps_sn_timeout_id = g_timeout_add( - GPS_SN_UPDATE_INTERVAL*1000, - gps_update_sn, gps_state); - } else { - /* stop any pending timer events */ - g_source_remove(gps_state->ui.gps_sn_timeout_id); - gps_state->ui.gps_sn_timeout_id = 0; - soup_session_abort(gps_state->ui.soup_session); - g_object_unref(gps_state->ui.soup_session); - gps_state->ui.soup_session = NULL; - } - - return FALSE; -} - -static gboolean -on_gps_sn_active_clicked_event (GtkWidget *widget, gpointer user_data) -{ - GritsPluginGPS *gps_state = (GritsPluginGPS *)user_data; - - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) { - gps_state->ui.gps_sn_active = TRUE; - } else { - gps_state->ui.gps_sn_active = FALSE; - } - - return FALSE; -} - -/* Send position report to spotternetwork */ -static gboolean -gps_update_sn(gpointer user_data) -{ - GritsPluginGPS *gps_state = (GritsPluginGPS *)user_data; - struct gps_data_t *gps_data = &gps_state->gps_data; - SoupMessage *msg; - char uri[256]; - - /* Make sure gps data is valid */ - if (!gps_data_is_valid(gps_data)) { - g_warning("Skipping SpotterNetwork update, GPS data is invalid!"); - GPS_STATUS(gps_state, "Skipped SpotterNetwork update, " - "GPS data is invalid!"); - return TRUE; - } - - /* Make sure SpotterNetwork ID is valid */ - if (gtk_entry_get_text_length(GTK_ENTRY(gps_state->ui.gps_sn_entry)) == 0) { - g_warning("Skipping SpotterNetwork update, ID \"%s\" is invalid!", - gtk_entry_get_text(GTK_ENTRY(gps_state->ui.gps_sn_entry))); - GPS_STATUS(gps_state, "Skipping SpotterNetwork update, " - "ID \"%s\" is invalid!", - gtk_entry_get_text(GTK_ENTRY(gps_state->ui.gps_sn_entry))); - return TRUE; - } - - snprintf(uri, sizeof(uri), - "%s?lat=%lf&lon=%lf&elev=%lf&dir=%lf&mph=%lf&gps=1&active=%d&ver=1&id=%s", - GPS_SN_URI, - gps_data->fix.latitude, - gps_data->fix.longitude, - gps_data->fix.altitude * METERS_TO_FEET, - gps_data->fix.track, - gps_data->fix.speed, - gps_state->gps_sn_active == TRUE ? 1 : 0, - gtk_entry_get_text(GTK_ENTRY(gps_state->ui.gps_sn_entry))); - - g_debug("gps_update_sn called uri: %s", uri); - - msg = soup_message_new (SOUP_METHOD_GET, uri); - if (msg) { - soup_session_queue_message (gps_state->ui.soup_session, msg, - gps_sn_update_complete, gps_state); - } else { - g_warning("unable to create spotternetwork message"); - } - - /* reschedule next update */ - return TRUE; -} - -static void -gps_sn_update_complete (SoupSession *session, SoupMessage *msg, gpointer user_data) -{ - if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - g_debug("gps_sn_update_complete: %s", - msg->response_body->data); - GPS_STATUS(gps_state, "Updated SpotterNetwork."); - } else { - GPS_STATUS(gps_state, "Could not update SpotterNetwork: %s", - msg->reason_phrase); - g_warning("Could not update SpotterNetwork: %s", - msg->reason_phrase); - } -} - - static void gps_init_status_info(GritsPluginGPS *gps_state, GtkWidget *gbox) @@ -629,6 +501,7 @@ gps_init_status_info(GritsPluginGPS *gps_state, GtkWidget *gbox) } +#ifdef GPS_RANGE_RINGS static void gps_init_range_rings(GritsPluginGPS *gps_state, GtkWidget *gbox) { @@ -660,6 +533,7 @@ on_gps_rangering_clicked_event (GtkWidget *widget, gpointer user_data) return FALSE; } +#endif /* GPS_RANGE_RINGS */ /* external interface to update UI from latest GPS data. */ gboolean gps_redraw_all(gpointer data) @@ -681,28 +555,52 @@ gboolean gps_redraw_all(gpointer data) /* update position labels */ update_gps_status(gps_state); - if (gps_data_is_valid(gps_data)) { - /* Update track and marker position */ - g_debug("Updating track at lat = %f, long = %f, track = %f", + /* Update track and marker position */ + if (gps_data_is_valid(gps_data) && gps_state->track.active) { + g_debug("Updating track group %u point %u at " + "lat = %f, long = %f, track = %f", + gps_state->track.cur_group, gps_state->track.cur_point, gps_data->fix.latitude, gps_data->fix.longitude, gps_data->fix.track); + gps_track_add_point(&gps_state->track, + gps_data->fix.latitude, gps_data->fix.longitude, 0.0); + + if (gps_state->track.line) { + grits_viewer_remove(gps_state->viewer, + GRITS_OBJECT(gps_state->track.line)); + gps_state->track.line = NULL; + } + + gps_state->track.line = grits_line_new(gps_state->track.points); + gps_state->track.line->color[0] = 1.0; + gps_state->track.line->color[1] = 0; + gps_state->track.line->color[2] = 0.1; + gps_state->track.line->color[3] = 0.5; + gps_state->track.line->width = 3; + + grits_viewer_add(gps_state->viewer, GRITS_OBJECT(gps_state->track.line), + GRITS_LEVEL_OVERLAY, TRUE); + } + + if (gps_data_is_valid(gps_data)) { if (gps_state->marker) { grits_viewer_remove(gps_state->viewer, GRITS_OBJECT(gps_state->marker)); - g_object_unref(gps_state->viewer); gps_state->marker = NULL; } - gps_state->marker = grits_marker_new("gps"); + gps_state->marker = grits_marker_icon_new("GPS", "car.png", + gps_data->fix.track, TRUE); GRITS_OBJECT(gps_state->marker)->center.lat = gps_data->fix.latitude; GRITS_OBJECT(gps_state->marker)->center.lon = gps_data->fix.longitude; GRITS_OBJECT(gps_state->marker)->center.elev = 0.0; GRITS_OBJECT(gps_state->marker)->lod = EARTH_R; + grits_viewer_add(gps_state->viewer, GRITS_OBJECT(gps_state->marker), - GRITS_LEVEL_OVERLAY, FALSE); + GRITS_LEVEL_OVERLAY, TRUE); grits_viewer_refresh(gps_state->viewer); } @@ -717,13 +615,131 @@ gboolean gps_redraw_all(gpointer data) grits_viewer_get_location(gps_state->viewer, &lat, &lon, &elev); grits_viewer_set_location(gps_state->viewer, gps_data->fix.latitude, gps_data->fix.longitude, elev); - grits_viewer_set_rotation(gps_state->viewer, 0, 0, 0); + //grits_viewer_set_rotation(gps_state->viewer, 0, 0, 0); } /* reschedule */ return TRUE; } + +/******************* Track handling routines *****************/ + +static void +gps_track_init(struct gps_track_t *track) +{ + /* Save a spot at the end for the NULL termination */ + track->points = (gpointer)g_new0(double*, NUM_TRACK_GROUPS + 1); + track->cur_point = 0; + track->cur_group = 0; + track->num_points = 1; /* starts at 1 so realloc logic works */ + track->line = NULL; +} + +static void +gps_track_clear(struct gps_track_t *track) +{ + int pi; + for (pi = 0; pi < NUM_TRACK_GROUPS; pi++) { + if (track->points[pi] != NULL) { + g_free(track->points[pi]); + track->points[pi] = NULL; + } + } + track->cur_point = 0; + track->cur_group = 0; + track->num_points = 1; /* starts at 1 so realloc logic works */ +} + +static void +gps_track_free(struct gps_track_t *track) +{ + gps_track_clear(track); + g_free(track->points); +} + +/* add a new track group (points in a track group are connected, and + * separated from points in other track groups). + */ +static void +gps_track_group_incr(struct gps_track_t *track) +{ + gdouble (**points)[3] = track->points; /* for simplicity */ + + /* Just return if they increment it again before any points have + * been added. + */ + if (points[track->cur_group] == NULL) { + return; + } + + g_debug("track_group_incr: incrementing track group to %u.", + track->cur_group + 1); + + track->cur_group++; + track->cur_point = 0; + track->num_points = 1; /* starts at 1 so realloc logic works */ + + if (track->cur_group >= NUM_TRACK_GROUPS) { + g_debug("track_group_incr: current track group %u is at max %u, " + "shifting groups.", + track->cur_group, NUM_TRACK_GROUPS); + + /* Free the oldest one which falls off the end */ + g_free(points[0]); + + /* shift the rest down, last one should always be NULL already */ + /* note we alloc NUM_TRACK_GROUPS+1 */ + for (int pi = 0; pi < NUM_TRACK_GROUPS; pi++) { + points[pi] = points[pi+1]; + } + + /* always write into the last group */ + track->cur_group = NUM_TRACK_GROUPS - 1; + } +} + +static void +gps_track_add_point(struct gps_track_t *track, gdouble lat, gdouble lon, + gdouble elevation) +{ + gdouble (**points)[3] = track->points; /* for simplicity */ + + g_debug("GritsPluginGPS: track_add_point"); + + g_assert(track->cur_group < NUM_TRACK_GROUPS && + (track->cur_point <= track->num_points)); + + /* resize/allocate the point group if the current one is full */ + if (track->cur_point >= track->num_points - 1) { + guint new_size = track->num_points == 1 ? + NUM_TRACK_POINTS : + track->num_points * NUM_TRACK_POINTS_FACTOR; + g_debug("GritsPluginGPS: track_add_point: reallocating points " + "array from %u points to %u points.\n", + track->num_points, new_size); + points[track->cur_group] = (gpointer)g_renew(gdouble, + points[track->cur_group], 3*(new_size+1)); + track->num_points = new_size; + } + + g_assert(points[track->cur_group] != NULL); + + /* Add the coordinate */ + lle2xyz(lat, lon, elevation, + &points[track->cur_group][track->cur_point][0], + &points[track->cur_group][track->cur_point][1], + &points[track->cur_group][track->cur_point][2]); + + track->cur_point++; + + /* make sure last point is always 0s so the line drawing stops. */ + points[track->cur_group][track->cur_point][0] = 0.0; + points[track->cur_group][track->cur_point][1] = 0.0; + points[track->cur_group][track->cur_point][2] = 0.0; +} + + static char *gps_get_status(struct gps_data_t *gps_data) { @@ -879,17 +895,19 @@ GritsPluginGPS *grits_plugin_gps_new(GritsViewer *viewer, GritsPrefs *prefs) initialize_gpsd("localhost", DEFAULT_GPSD_PORT, &self->gps_data); self->follow_gps = FALSE; - self->gps_sn_active = FALSE; - + + gps_track_init(&self->track); gps_init_status_info(self, self->hbox); gps_init_control_frame(self, self->hbox); gps_init_track_log_frame(self, self->hbox); - gps_init_spotter_network_frame(self, self->hbox); +#ifdef GPS_RANGE_RINGS gps_init_range_rings(self, self->hbox); +#endif return self; } + static GtkWidget *grits_plugin_gps_get_config(GritsPlugin *_self) { GritsPluginGPS *self = GRITS_PLUGIN_GPS(_self); @@ -925,9 +943,20 @@ static void grits_plugin_gps_init(GritsPluginGPS *self) static void grits_plugin_gps_dispose(GObject *gobject) { - g_debug("GritsPluginGPS: dispose"); GritsPluginGPS *self = GRITS_PLUGIN_GPS(gobject); - g_signal_handler_disconnect(self->config, self->tab_id); + + g_debug("GritsPluginGPS: dispose"); + + if (self->viewer) { + if (self->marker) { + grits_viewer_remove(self->viewer, + GRITS_OBJECT(self->marker)); + } + g_object_unref(self->viewer); + self->viewer = NULL; + } + + gps_track_free(&self->track); /* Drop references */ G_OBJECT_CLASS(grits_plugin_gps_parent_class)->dispose(gobject); @@ -935,8 +964,10 @@ static void grits_plugin_gps_dispose(GObject *gobject) static void grits_plugin_gps_finalize(GObject *gobject) { - g_debug("GritsPluginGPS: finalize"); GritsPluginGPS *self = GRITS_PLUGIN_GPS(gobject); + + g_debug("GritsPluginGPS: finalize"); + /* Free data */ gtk_widget_destroy(self->config); G_OBJECT_CLASS(grits_plugin_gps_parent_class)->finalize(gobject);