diff options
Diffstat (limited to 'aoview')
| -rw-r--r-- | aoview/aoview.glade | 35 | ||||
| -rw-r--r-- | aoview/aoview.h | 23 | ||||
| -rw-r--r-- | aoview/aoview_label.c | 26 | ||||
| -rw-r--r-- | aoview/aoview_monitor.c | 93 | ||||
| -rw-r--r-- | aoview/aoview_replay.c | 2 | ||||
| -rw-r--r-- | aoview/aoview_state.c | 252 | 
6 files changed, 280 insertions, 151 deletions
diff --git a/aoview/aoview.glade b/aoview/aoview.glade index d828a85e..a2dc830f 100644 --- a/aoview/aoview.glade +++ b/aoview/aoview.glade @@ -3,7 +3,7 @@    <!-- interface-requires gtk+ 2.16 -->    <!-- interface-naming-policy project-wide -->    <widget class="GtkWindow" id="aoview"> -    <property name="width_request">300</property> +    <property name="width_request">550</property>      <property name="height_request">700</property>      <property name="visible">True</property>      <property name="title" translatable="yes">AltOS View</property> @@ -288,13 +288,13 @@            <widget class="GtkTable" id="table1">              <property name="visible">True</property>              <property name="n_rows">2</property> -            <property name="n_columns">3</property> +            <property name="n_columns">4</property>              <property name="row_spacing">3</property>              <property name="homogeneous">True</property>              <child>                <widget class="GtkLabel" id="height_label">                  <property name="visible">True</property> -                <property name="label" translatable="yes">Height</property> +                <property name="label" translatable="yes">Height (m)</property>                  <property name="justify">center</property>                </widget>              </child> @@ -311,7 +311,7 @@              <child>                <widget class="GtkLabel" id="rssi_label">                  <property name="visible">True</property> -                <property name="label" translatable="yes">RSSI</property> +                <property name="label" translatable="yes">RSSI (dBm)</property>                </widget>                <packing>                  <property name="left_attach">2</property> @@ -322,7 +322,7 @@                <widget class="GtkLabel" id="height_value">                  <property name="visible">True</property>                  <property name="ypad">2</property> -                <property name="label" translatable="yes">0m</property> +                <property name="label" translatable="yes">0</property>                  <property name="selectable">True</property>                </widget>                <packing> @@ -348,7 +348,7 @@                <widget class="GtkLabel" id="rssi_value">                  <property name="visible">True</property>                  <property name="ypad">2</property> -                <property name="label" translatable="yes">-50dBm</property> +                <property name="label" translatable="yes">-50</property>                  <property name="selectable">True</property>                </widget>                <packing> @@ -358,6 +358,29 @@                  <property name="bottom_attach">2</property>                </packing>              </child> +            <child> +              <widget class="GtkLabel" id="speed_label"> +                <property name="visible">True</property> +                <property name="label" translatable="yes">Speed (m/s)</property> +              </widget> +              <packing> +                <property name="left_attach">3</property> +                <property name="right_attach">4</property> +              </packing> +            </child> +            <child> +              <widget class="GtkLabel" id="speed_value"> +                <property name="visible">True</property> +                <property name="label" translatable="yes">0</property> +                <property name="selectable">True</property> +              </widget> +              <packing> +                <property name="left_attach">3</property> +                <property name="right_attach">4</property> +                <property name="top_attach">1</property> +                <property name="bottom_attach">2</property> +              </packing> +            </child>            </widget>            <packing>              <property name="expand">False</property> diff --git a/aoview/aoview.h b/aoview/aoview.h index 5c118a11..ac64833f 100644 --- a/aoview/aoview.h +++ b/aoview/aoview.h @@ -35,6 +35,7 @@  #include <sys/types.h>  #include <sys/stat.h>  #include <assert.h> +#include <math.h>  #include <gtk/gtk.h>  #include <glade/glade.h> @@ -50,7 +51,7 @@ struct usbdev {  	int	idVendor;  }; -struct aostate { +struct aodata {  	char	callsign[16];  	int	serial;  	int	rssi; @@ -83,9 +84,17 @@ struct aostate {  	double	hdop;		/* unitless? */  	int	h_error;	/* m */  	int	v_error;	/* m */ +}; + +struct aostate { +	struct aodata	data;  	/* derived data */ +	struct aodata	prev_data; + +	double		report_time; +  	gboolean	ascent;	/* going up? */  	int	ground_altitude; @@ -96,11 +105,16 @@ struct aostate {  	double	temperature;  	double	main_sense;  	double	drogue_sense; +	double	baro_speed;  	int	max_height;  	double	max_acceleration;  	double	max_speed; +	double	lat; +	double	lon; +	int	gps_valid; +  	double	pad_lat;  	double	pad_lon;  	double	pad_alt; @@ -112,8 +126,13 @@ struct aostate {  	double	distance;  	double	bearing;  	int	gps_height; + +	int	speak_tick; +	int	speak_altitude;  }; +extern struct aostate aostate; +  /* GPS is 'stable' when we've seen at least this many samples */  #define MIN_PAD_SAMPLES	10 @@ -162,7 +181,7 @@ void  aoview_usbdev_free(struct usbdev *usbdev);  void -aoview_state_notify(struct aostate *state); +aoview_state_notify(struct aodata *data);  void  aoview_state_new(void); diff --git a/aoview/aoview_label.c b/aoview/aoview_label.c index 88b747ab..24313626 100644 --- a/aoview/aoview_label.c +++ b/aoview/aoview_label.c @@ -22,12 +22,14 @@ static struct {  	char		*initial_value;  	GtkLabel	*widget;  } label_widgets[] = { -	{ "height_label", "Height", NULL }, +	{ "height_label", "Height (m)", NULL },  	{ "state_label", "State", NULL }, -	{ "rssi_label", "RSSI", NULL }, -	{ "height_value", "0m", NULL }, +	{ "rssi_label", "RSSI (dBm)", NULL }, +	{ "speed_label", "Speed (m/s)", NULL }, +	{ "height_value", "0", NULL },  	{ "state_value", "pad", NULL }, -	{ "rssi_value", "-50dBm", NULL }, +	{ "rssi_value", "-50", NULL }, +	{ "speed_value", "0", NULL },  };  static void @@ -44,13 +46,19 @@ void  aoview_label_show(struct aostate *state)  {  	char	line[1024]; -	sprintf(line, "%dm", state->height); -	aoview_label_assign(label_widgets[3].widget, line); +	sprintf(line, "%d", state->height); +	aoview_label_assign(label_widgets[4].widget, line); -	aoview_label_assign(label_widgets[4].widget, state->state); +	aoview_label_assign(label_widgets[5].widget, state->data.state); -	sprintf(line, "%ddBm", state->rssi); -	aoview_label_assign(label_widgets[5].widget, line); +	sprintf(line, "%d", state->data.rssi); +	aoview_label_assign(label_widgets[6].widget, line); + +	if (state->ascent) +		sprintf(line, "%6.0f", fabs(state->speed)); +	else +		sprintf(line, "%6.0f", fabs(state->baro_speed)); +	aoview_label_assign(label_widgets[7].widget, line);  }  void diff --git a/aoview/aoview_monitor.c b/aoview/aoview_monitor.c index 5810be5b..43381800 100644 --- a/aoview/aoview_monitor.c +++ b/aoview/aoview_monitor.c @@ -65,8 +65,6 @@ aoview_parse_pos(double *target, char *source)  	*target = r;  } -static struct aostate	state; -  gboolean  aoview_monitor_parse(const char *input_line)  { @@ -74,6 +72,7 @@ aoview_monitor_parse(const char *input_line)  	char *words[64];  	int nword;  	char line_buf[8192], *line; +	struct aodata	data;  	/* avoid smashing our input parameter */  	strncpy (line_buf, input_line, sizeof (line_buf)-1); @@ -89,61 +88,55 @@ aoview_monitor_parse(const char *input_line)  		return FALSE;  	if (strcmp(words[0], "CALL") != 0)  		return FALSE; -	aoview_parse_string(state.callsign, sizeof (state.callsign), words[1]); -	aoview_parse_int(&state.serial, words[3]); - -	aoview_parse_int(&state.rssi, words[5]); -	aoview_parse_string(state.state, sizeof (state.state), words[9]); -	aoview_parse_int(&state.tick, words[10]); -	aoview_parse_int(&state.accel, words[12]); -	aoview_parse_int(&state.pres, words[14]); -	aoview_parse_int(&state.temp, words[16]); -	aoview_parse_int(&state.batt, words[18]); -	aoview_parse_int(&state.drogue, words[20]); -	aoview_parse_int(&state.main, words[22]); -	aoview_parse_int(&state.flight_accel, words[24]); -	aoview_parse_int(&state.ground_accel, words[26]); -	aoview_parse_int(&state.flight_vel, words[28]); -	aoview_parse_int(&state.flight_pres, words[30]); -	aoview_parse_int(&state.ground_pres, words[32]); -	aoview_parse_int(&state.nsat, words[34]); +	aoview_parse_string(data.callsign, sizeof (data.callsign), words[1]); +	aoview_parse_int(&data.serial, words[3]); + +	aoview_parse_int(&data.rssi, words[5]); +	aoview_parse_string(data.state, sizeof (data.state), words[9]); +	aoview_parse_int(&data.tick, words[10]); +	aoview_parse_int(&data.accel, words[12]); +	aoview_parse_int(&data.pres, words[14]); +	aoview_parse_int(&data.temp, words[16]); +	aoview_parse_int(&data.batt, words[18]); +	aoview_parse_int(&data.drogue, words[20]); +	aoview_parse_int(&data.main, words[22]); +	aoview_parse_int(&data.flight_accel, words[24]); +	aoview_parse_int(&data.ground_accel, words[26]); +	aoview_parse_int(&data.flight_vel, words[28]); +	aoview_parse_int(&data.flight_pres, words[30]); +	aoview_parse_int(&data.ground_pres, words[32]); +	aoview_parse_int(&data.nsat, words[34]);  	if (strcmp (words[36], "unlocked") != 0 && nword >= 40) { -		state.locked = 1; -		sscanf(words[36], "%d:%d:%d", &state.gps_time.hour, &state.gps_time.minute, &state.gps_time.second); -		aoview_parse_pos(&state.lat, words[37]); -		aoview_parse_pos(&state.lon, words[38]); -		sscanf(words[39], "%dm", &state.alt); +		data.locked = 1; +		sscanf(words[36], "%d:%d:%d", &data.gps_time.hour, &data.gps_time.minute, &data.gps_time.second); +		aoview_parse_pos(&data.lat, words[37]); +		aoview_parse_pos(&data.lon, words[38]); +		sscanf(words[39], "%dm", &data.alt);  	} else { -		state.locked = 0; -		state.gps_time.hour = state.gps_time.minute = state.gps_time.second = 0; -		state.lat = state.lon = 0; -		state.alt = 0; +		data.locked = 0; +		data.gps_time.hour = data.gps_time.minute = data.gps_time.second = 0; +		data.lat = data.lon = 0; +		data.alt = 0;  	}  	if (nword >= 46) { -		sscanf(words[40], "%lfm/s", &state.ground_speed); -		sscanf(words[41], "%d", &state.course); -		sscanf(words[42], "%lfm/s", &state.climb_rate); -		sscanf(words[43], "%lf", &state.hdop); -		sscanf(words[44], "%d", &state.h_error); -		sscanf(words[45], "%d", &state.v_error); +		sscanf(words[40], "%lfm/s", &data.ground_speed); +		sscanf(words[41], "%d", &data.course); +		sscanf(words[42], "%lfm/s", &data.climb_rate); +		sscanf(words[43], "%lf", &data.hdop); +		sscanf(words[44], "%d", &data.h_error); +		sscanf(words[45], "%d", &data.v_error);  	} else { -		state.ground_speed = 0; -		state.course = 0; -		state.climb_rate = 0; -		state.hdop = 0; -		state.h_error = 0; -		state.v_error = 0; +		data.ground_speed = 0; +		data.course = 0; +		data.climb_rate = 0; +		data.hdop = 0; +		data.h_error = 0; +		data.v_error = 0;  	} -	aoview_state_notify(&state); +	aoview_state_notify(&data);  	return TRUE;  } -void -aoview_monitor_reset(void) -{ -	memset(&state, '\0', sizeof (state)); -} -  static void  aoview_monitor_callback(gpointer user_data,  			struct aoview_serial *serial, @@ -166,7 +159,7 @@ aoview_monitor_callback(gpointer user_data,  				monitor_line[monitor_pos] = '\0';  				if (monitor_pos) {  					if (aoview_monitor_parse(monitor_line)) { -						aoview_log_set_serial(state.serial); +						aoview_log_set_serial(aostate.data.serial);  						if (aoview_log_get_serial())  							aoview_log_printf ("%s\n", monitor_line);  					} @@ -186,7 +179,7 @@ aoview_monitor_connect(char *tty)  	if (!monitor_serial)  		return FALSE;  	aoview_table_clear(); -	aoview_monitor_reset(); +	aoview_state_reset();  	aoview_serial_set_callback(monitor_serial,  				   aoview_monitor_callback,  				   monitor_serial, diff --git a/aoview/aoview_replay.c b/aoview/aoview_replay.c index 3eadb442..da7b5d6a 100644 --- a/aoview/aoview_replay.c +++ b/aoview/aoview_replay.c @@ -107,7 +107,7 @@ aoview_replay_open(GtkWidget *widget, gpointer data)  		gtk_widget_destroy(dialog);  	} else {  		replay_tick = -1; -		aoview_monitor_reset(); +		aoview_state_reset();  		aoview_replay_read(NULL);  	}  	gtk_widget_hide(GTK_WIDGET(replay_dialog)); diff --git a/aoview/aoview_state.c b/aoview/aoview_state.c index 030db99f..d5e978b6 100644 --- a/aoview/aoview_state.c +++ b/aoview/aoview_state.c @@ -86,28 +86,54 @@ static char *ascent_states[] = {  	0,  }; +static double +aoview_time(void) +{ +	struct timespec	now; + +	clock_gettime(CLOCK_MONOTONIC, &now); +	return (double) now.tv_sec + (double) now.tv_nsec / 1.0e9; +} +  /*   * Fill out the derived data fields   */  static void -aoview_state_derive(struct aostate *state) +aoview_state_derive(struct aodata *data, struct aostate *state)  {  	int	i; +	double	new_height; +	double	height_change; +	double	time_change; +	int	tick_count; -	state->ground_altitude = aoview_pres_to_altitude(state->ground_pres); -	state->height = aoview_pres_to_altitude(state->flight_pres) - state->ground_altitude; -	state->acceleration = (state->ground_accel - state->flight_accel) / 27.0; -	state->speed = state->flight_vel / 2700.0; -	state->temperature = ((state->temp / 32767.0 * 3.3) - 0.5) / 0.01; -	state->drogue_sense = state->drogue / 32767.0 * 15.0; -	state->main_sense = state->main / 32767.0 * 15.0; -	state->battery = state->batt / 32767.0 * 5.0; -	if (!strcmp(state->state, "pad")) { -		if (state->locked && state->nsat > 4) { +	state->report_time = aoview_time(); + +	state->prev_data = state->data; +	state->data = *data; +	tick_count = data->tick; +	if (tick_count < state->prev_data.tick) +		tick_count += 65536; +	time_change = (tick_count - state->prev_data.tick) / 100.0; + +	state->ground_altitude = aoview_pres_to_altitude(data->ground_pres); +	new_height = aoview_pres_to_altitude(data->flight_pres) - state->ground_altitude; +	height_change = new_height - state->height; +	state->height = new_height; +	if (time_change) +		state->baro_speed = (state->baro_speed * 3 + (height_change / time_change)) / 4.0; +	state->acceleration = (data->ground_accel - data->flight_accel) / 27.0; +	state->speed = data->flight_vel / 2700.0; +	state->temperature = ((data->temp / 32767.0 * 3.3) - 0.5) / 0.01; +	state->drogue_sense = data->drogue / 32767.0 * 15.0; +	state->main_sense = data->main / 32767.0 * 15.0; +	state->battery = data->batt / 32767.0 * 5.0; +	if (!strcmp(data->state, "pad")) { +		if (data->locked && data->nsat > 4) {  			state->npad++; -			state->pad_lat_total += state->lat; -			state->pad_lon_total += state->lon; -			state->pad_alt_total += state->alt; +			state->pad_lat_total += data->lat; +			state->pad_lon_total += data->lon; +			state->pad_alt_total += data->alt;  			state->pad_lat = state->pad_lat_total / state->npad;  			state->pad_lon = state->pad_lon_total / state->npad;  			state->pad_alt = state->pad_alt_total / state->npad; @@ -115,7 +141,7 @@ aoview_state_derive(struct aostate *state)  	}  	state->ascent = FALSE;  	for (i = 0; ascent_states[i]; i++) -		if (!strcmp(state->state, ascent_states[i])) +		if (!strcmp(data->state, ascent_states[i]))  			state->ascent = TRUE;  	/* Only look at accelerometer data on the way up */ @@ -126,59 +152,117 @@ aoview_state_derive(struct aostate *state)  	if (state->height > state->max_height)  		state->max_height = state->height; -	aoview_great_circle(state->pad_lat, state->pad_lon, state->lat, state->lon, -			    &state->distance, &state->bearing); +	if (data->locked) { +		state->lat = data->lat; +		state->lon = data->lon; +		aoview_great_circle(state->pad_lat, state->pad_lon, data->lat, data->lon, +				    &state->distance, &state->bearing); +		state->gps_valid = 1; +	}  	if (state->npad) { -		state->gps_height = state->alt - state->pad_alt; +		state->gps_height = data->alt - state->pad_alt;  	} else {  		state->gps_height = 0;  	}  }  void -aoview_state_speak(struct aostate *state) +aoview_speak_state(struct aostate *state)  { -	static char	last_state[32]; -	int		i; -	gboolean	report = FALSE; -	int		this_tick; -	static int	last_tick; -	static int	last_altitude; -	int		this_altitude; - -	if (strcmp(state->state, last_state)) { -		aoview_voice_speak("%s\n", state->state); -		if (!strcmp(state->state, "drogue")) +	if (strcmp(state->data.state, state->prev_data.state)) { +		aoview_voice_speak("%s\n", state->data.state); +		if (!strcmp(state->data.state, "drogue"))  			aoview_voice_speak("apogee %d meters\n",  					   (int) state->max_height); -		report = TRUE; -		strcpy(last_state, state->state); -	} -	this_altitude = aoview_pres_to_altitude(state->flight_pres) - aoview_pres_to_altitude(state->ground_pres); -	this_tick = state->tick; -	while (this_tick < last_tick) -		this_tick += 65536; -	if (strcmp(state->state, "pad") != 0) { -		if (this_altitude / 1000 != last_altitude / 1000) -			report = TRUE; -		if (this_tick - last_tick >= 10 * 100) -			report = TRUE; +		if (!strcmp(state->prev_data.state, "boost")) +			aoview_voice_speak("max speed %d meters per second\n", +					   (int) state->max_speed);  	} -	if (report) { -		aoview_voice_speak("%d meters\n", -				   this_altitude); -		if (state->ascent) -			aoview_voice_speak("%d meters per second\n", -					   state->flight_vel / 2700); -		last_tick = state->tick; -		last_altitude = this_altitude; +} + +void +aoview_speak_height(struct aostate *state) +{ +	aoview_voice_speak("%d meters\n", state->height); +} + +struct aostate aostate; + +static guint aostate_timeout; + +#define COMPASS_LIMIT(n)	((n * 22.5) + 22.5/2) + +static char *compass_points[] = { +	"north", +	"north north east", +	"north east", +	"east north east", +	"east", +	"east south east", +	"south east", +	"south south east", +	"south", +	"south south west", +	"south west", +	"west south west", +	"west", +	"west north west", +	"north west", +	"north north west", +}; + +static char * +aoview_compass_point(double bearing) +{ +	int	i; +	while (bearing < 0) +		bearing += 360.0; +	while (bearing >= 360.0) +		bearing -= 360.0; + +	i = floor ((bearing - 22.5/2) / 22.5 + 0.5); +	if (i < 0) i = 0; +	if (i >= sizeof (compass_points) / sizeof (compass_points[0])) +		i = 0; +	return compass_points[i]; +} + +static gboolean +aoview_state_timeout(gpointer data) +{ +	double	now = aoview_time(); + +	if (strlen(aostate.data.state) > 0 && strcmp(aostate.data.state, "pad") != 0) +		aoview_speak_height(&aostate); +	if (now - aostate.report_time >= 20 || !strcmp(aostate.data.state, "landed")) { +		if (!aostate.ascent) { +			if (fabs(aostate.baro_speed) < 20 && aostate.height < 100) +				aoview_voice_speak("rocket landed safely\n"); +			else +				aoview_voice_speak("rocket may have crashed\n"); +			if (aostate.gps_valid) { +				aoview_voice_speak("rocket reported %s of pad distance %d meters\n", +						   aoview_compass_point(aostate.bearing), +						   (int) aostate.distance); +			} +		} +		aostate_timeout = 0; +		return FALSE;  	} +	return TRUE; +} + +void +aoview_state_reset(void) +{ +	memset(&aostate, '\0', sizeof (aostate));  }  void -aoview_state_notify(struct aostate *state) +aoview_state_notify(struct aodata *data)  { -	aoview_state_derive(state); +	struct aostate *state = &aostate; +	aoview_state_derive(data, state);  	aoview_table_start();  	if (state->npad >= MIN_PAD_SAMPLES) @@ -186,40 +270,40 @@ aoview_state_notify(struct aostate *state)  	else  		aoview_table_add_row("Ground state", "waiting for gps (%d)",  				     MIN_PAD_SAMPLES - state->npad); -	aoview_table_add_row("Rocket state", "%s", state->state); -	aoview_table_add_row("Callsign", "%s", state->callsign); -	aoview_table_add_row("Rocket serial", "%d", state->serial); - -	aoview_table_add_row("RSSI", "%ddBm", state->rssi); -	aoview_table_add_row("Height", "%dm", state->height); -	aoview_table_add_row("Max height", "%dm", state->max_height); -	aoview_table_add_row("Acceleration", "%gm/s²", state->acceleration); -	aoview_table_add_row("Max acceleration", "%gm/s²", state->max_acceleration); -	aoview_table_add_row("Speed", "%gm/s", state->speed); -	aoview_table_add_row("Max Speed", "%gm/s", state->max_speed); -	aoview_table_add_row("Temperature", "%g°C", state->temperature); -	aoview_table_add_row("Battery", "%gV", state->battery); -	aoview_table_add_row("Drogue", "%gV", state->drogue_sense); -	aoview_table_add_row("Main", "%gV", state->main_sense); +	aoview_table_add_row("Rocket state", "%s", state->data.state); +	aoview_table_add_row("Callsign", "%s", state->data.callsign); +	aoview_table_add_row("Rocket serial", "%d", state->data.serial); + +	aoview_table_add_row("RSSI", "%6ddBm", state->data.rssi); +	aoview_table_add_row("Height", "%6dm", state->height); +	aoview_table_add_row("Max height", "%6dm", state->max_height); +	aoview_table_add_row("Acceleration", "%7.1fm/s²", state->acceleration); +	aoview_table_add_row("Max acceleration", "%7.1fm/s²", state->max_acceleration); +	aoview_table_add_row("Speed", "%7.1fm/s", state->ascent ? state->speed : state->baro_speed); +	aoview_table_add_row("Max Speed", "%7.1fm/s", state->max_speed); +	aoview_table_add_row("Temperature", "%6.2f°C", state->temperature); +	aoview_table_add_row("Battery", "%5.2fV", state->battery); +	aoview_table_add_row("Drogue", "%5.2fV", state->drogue_sense); +	aoview_table_add_row("Main", "%5.2fV", state->main_sense);  	aoview_table_add_row("Pad altitude", "%dm", state->ground_altitude); -	aoview_table_add_row("Satellites", "%d", state->nsat); -	if (state->locked) { -		aoview_state_add_deg("Latitude", state->lat, 'N', 'S'); -		aoview_state_add_deg("Longitude", state->lon, 'E', 'W'); +	aoview_table_add_row("Satellites", "%d", state->data.nsat); +	if (state->data.locked) { +		aoview_state_add_deg("Latitude", state->data.lat, 'N', 'S'); +		aoview_state_add_deg("Longitude", state->data.lon, 'E', 'W');  		aoview_table_add_row("GPS height", "%d", state->gps_height);  		aoview_table_add_row("GPS time", "%02d:%02d:%02d", -				     state->gps_time.hour, -				     state->gps_time.minute, -				     state->gps_time.second); -		aoview_table_add_row("GPS ground speed", "%fm/s %d°", -				     state->ground_speed, -				     state->course); -		aoview_table_add_row("GPS climb rate", "%fm/s", -				     state->climb_rate); +				     state->data.gps_time.hour, +				     state->data.gps_time.minute, +				     state->data.gps_time.second); +		aoview_table_add_row("GPS ground speed", "%7.1fm/s %d°", +				     state->data.ground_speed, +				     state->data.course); +		aoview_table_add_row("GPS climb rate", "%7.1fm/s", +				     state->data.climb_rate);  		aoview_table_add_row("GPS precision", "%f(hdop) %dm(h) %dm(v)\n", -				     state->hdop, state->h_error, state->v_error); -		aoview_table_add_row("Distance from pad", "%gm", state->distance); -		aoview_table_add_row("Direction from pad", "%g°", state->bearing); +				     state->data.hdop, state->data.h_error, state->data.v_error); +		aoview_table_add_row("Distance from pad", "%5.0fm", state->distance); +		aoview_table_add_row("Direction from pad", "%4.0f°", state->bearing);  	} else {  		aoview_table_add_row("GPS", "unlocked");  	} @@ -230,7 +314,9 @@ aoview_state_notify(struct aostate *state)  	}  	aoview_table_finish();  	aoview_label_show(state); -	aoview_state_speak(state); +	aoview_speak_state(state); +	if (!aostate_timeout && strcmp(state->data.state, "pad") != 0) +		aostate_timeout = g_timeout_add_seconds(10, aoview_state_timeout, NULL);  }  void  | 
