diff options
| -rw-r--r-- | aoview/Makefile.am | 7 | ||||
| -rw-r--r-- | aoview/aoview.glade | 153 | ||||
| -rw-r--r-- | aoview/aoview.h | 43 | ||||
| -rw-r--r-- | aoview/aoview_file.c | 2 | ||||
| -rw-r--r-- | aoview/aoview_label.c | 65 | ||||
| -rw-r--r-- | aoview/aoview_main.c | 4 | ||||
| -rw-r--r-- | aoview/aoview_monitor.c | 22 | ||||
| -rw-r--r-- | aoview/aoview_replay.c | 127 | ||||
| -rw-r--r-- | aoview/aoview_state.c | 242 | 
9 files changed, 530 insertions, 135 deletions
| diff --git a/aoview/Makefile.am b/aoview/Makefile.am index 86811bfe..28c3d646 100644 --- a/aoview/Makefile.am +++ b/aoview/Makefile.am @@ -5,7 +5,7 @@ if USE_FLITE  FLITE_PROG=aoview_flite  endif -bin_PROGRAMS=aoview $(FLITE_PROG) +bin_PROGRAMS=aoview $(FLITE_PROG) aoview_slowtelem  aoview_LDADD=$(GNOME_LIBS) @@ -23,11 +23,16 @@ aoview_SOURCES = \  	aoview_file.c \  	aoview_eeprom.c \  	aoview_voice.c \ +	aoview_replay.c \ +	aoview_label.c \  	aoview.h  aoview_flite_SOURCES = \  	aoview_flite.c +aoview_slowtelem_SOURCES = \ +	aoview_slowtelem.c +  aoview_flite_LDADD=@FLITE_LIBS@  BUILT_SOURCES = aoview_glade.h diff --git a/aoview/aoview.glade b/aoview/aoview.glade index 153db1e3..a43cddea 100644 --- a/aoview/aoview.glade +++ b/aoview/aoview.glade @@ -176,6 +176,22 @@                          </child>                        </widget>                      </child> +                    <child> +                      <widget class="GtkImageMenuItem" id="ao_replay"> +                        <property name="label" translatable="yes">_Replay</property> +                        <property name="visible">True</property> +                        <property name="use_underline">True</property> +                        <property name="use_stock">False</property> +                        <signal name="activate_item" handler="gtk_widget_show" object="ao_replay_dialog" after="yes"/> +                        <signal name="activate" handler="gtk_widget_show" object="ao_replay_dialog"/> +                        <child internal-child="image"> +                          <widget class="GtkImage" id="image6"> +                            <property name="visible">True</property> +                            <property name="stock">gtk-media-play</property> +                          </widget> +                        </child> +                      </widget> +                    </child>                    </widget>                  </child>                </widget> @@ -269,6 +285,86 @@            </packing>          </child>          <child> +          <widget class="GtkTable" id="table1"> +            <property name="visible">True</property> +            <property name="n_rows">2</property> +            <property name="n_columns">3</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="justify">center</property> +              </widget> +            </child> +            <child> +              <widget class="GtkLabel" id="state_label"> +                <property name="visible">True</property> +                <property name="label" translatable="yes">State</property> +              </widget> +              <packing> +                <property name="left_attach">1</property> +                <property name="right_attach">2</property> +              </packing> +            </child> +            <child> +              <widget class="GtkLabel" id="rssi_label"> +                <property name="visible">True</property> +                <property name="label" translatable="yes">RSSI</property> +              </widget> +              <packing> +                <property name="left_attach">2</property> +                <property name="right_attach">3</property> +              </packing> +            </child> +            <child> +              <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="selectable">True</property> +              </widget> +              <packing> +                <property name="top_attach">1</property> +                <property name="bottom_attach">2</property> +              </packing> +            </child> +            <child> +              <widget class="GtkLabel" id="state_value"> +                <property name="visible">True</property> +                <property name="ypad">2</property> +                <property name="label" translatable="yes">pad</property> +                <property name="selectable">True</property> +              </widget> +              <packing> +                <property name="left_attach">1</property> +                <property name="right_attach">2</property> +                <property name="top_attach">1</property> +                <property name="bottom_attach">2</property> +              </packing> +            </child> +            <child> +              <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="selectable">True</property> +              </widget> +              <packing> +                <property name="left_attach">2</property> +                <property name="right_attach">3</property> +                <property name="top_attach">1</property> +                <property name="bottom_attach">2</property> +              </packing> +            </child> +          </widget> +          <packing> +            <property name="expand">False</property> +            <property name="position">1</property> +          </packing> +        </child> +        <child>            <widget class="GtkTreeView" id="dataview">              <property name="visible">True</property>              <property name="can_focus">True</property> @@ -277,7 +373,7 @@              <property name="enable_grid_lines">both</property>            </widget>            <packing> -            <property name="position">1</property> +            <property name="position">2</property>            </packing>          </child>        </widget> @@ -539,4 +635,59 @@ You should have received a copy of the GNU General Public License along with AoV        </widget>      </child>    </widget> +  <widget class="GtkFileChooserDialog" id="ao_replay_dialog"> +    <property name="border_width">5</property> +    <property name="destroy_with_parent">True</property> +    <property name="type_hint">dialog</property> +    <property name="skip_taskbar_hint">True</property> +    <property name="transient_for">aoview</property> +    <property name="has_separator">False</property> +    <child internal-child="vbox"> +      <widget class="GtkVBox" id="dialog-vbox10"> +        <property name="visible">True</property> +        <property name="orientation">vertical</property> +        <property name="spacing">2</property> +        <child internal-child="action_area"> +          <widget class="GtkHButtonBox" id="dialog-action_area10"> +            <property name="visible">True</property> +            <property name="layout_style">end</property> +            <child> +              <widget class="GtkButton" id="ao_replay_cancel"> +                <property name="label" translatable="yes">gtk-cancel</property> +                <property name="visible">True</property> +                <property name="can_focus">True</property> +                <property name="receives_default">True</property> +                <property name="use_stock">True</property> +                <signal name="clicked" handler="gtk_widget_hide" object="ao_replay_dialog"/> +              </widget> +              <packing> +                <property name="expand">False</property> +                <property name="fill">False</property> +                <property name="position">0</property> +              </packing> +            </child> +            <child> +              <widget class="GtkButton" id="ao_replay_ok"> +                <property name="label" translatable="yes">gtk-ok</property> +                <property name="visible">True</property> +                <property name="can_focus">True</property> +                <property name="receives_default">True</property> +                <property name="use_stock">True</property> +              </widget> +              <packing> +                <property name="expand">False</property> +                <property name="fill">False</property> +                <property name="position">1</property> +              </packing> +            </child> +          </widget> +          <packing> +            <property name="expand">False</property> +            <property name="pack_type">end</property> +            <property name="position">0</property> +          </packing> +        </child> +      </widget> +    </child> +  </widget>  </glade-interface> diff --git a/aoview/aoview.h b/aoview/aoview.h index 1ad9e160..d49bd6f4 100644 --- a/aoview/aoview.h +++ b/aoview/aoview.h @@ -83,14 +83,48 @@ struct aostate {  	double	hdop;		/* unitless? */  	int	h_error;	/* m */  	int	v_error;	/* m */ + +	/* derived data */ + +	gboolean	ascent;	/* going up? */ + +	int	ground_altitude; +	int	height; +	double	speed; +	double	acceleration; +	double	battery; +	double	temperature; +	double	main_sense; +	double	drogue_sense; + +	int	max_height; +	double	max_acceleration; +	double	max_speed; + +	double	pad_lat; +	double	pad_lon; +	double	pad_alt; +	double	pad_lat_total; +	double	pad_lon_total; +	double	pad_alt_total; +	int	npad; + +	double	distance; +	double	bearing;  }; +/* GPS is 'stable' when we've seen at least this many samples */ +#define MIN_PAD_SAMPLES	10 +  void  aoview_monitor_disconnect(void);  gboolean  aoview_monitor_connect(char *tty); +gboolean +aoview_monitor_parse(char *line); +  struct aoview_serial *  aoview_serial_open(const char *tty); @@ -182,6 +216,8 @@ aoview_table_clear(void);  struct aoview_file; +extern char *aoview_file_dir; +  void  aoview_file_finish(struct aoview_file *file); @@ -227,4 +263,11 @@ void aoview_voice_close(void);  void aoview_voice_speak(char *format, ...); +/* aoview_label.c */ + +void aoview_label_init(GladeXML *xml); + +void +aoview_label_show(struct aostate *state); +  #endif /* _AOVIEW_H_ */ diff --git a/aoview/aoview_file.c b/aoview/aoview_file.c index 4a8841d6..5288c2f7 100644 --- a/aoview/aoview_file.c +++ b/aoview/aoview_file.c @@ -17,7 +17,7 @@  #include "aoview.h" -static char *aoview_file_dir; +char *aoview_file_dir;  #define ALTOS_DIR_PATH	"/apps/aoview/log_dir"  #define DEFAULT_DIR	"AltOS" diff --git a/aoview/aoview_label.c b/aoview/aoview_label.c new file mode 100644 index 00000000..88b747ab --- /dev/null +++ b/aoview/aoview_label.c @@ -0,0 +1,65 @@ +/* + * Copyright © 2009 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "aoview.h" + +static struct { +	char		*name; +	char		*initial_value; +	GtkLabel	*widget; +} label_widgets[] = { +	{ "height_label", "Height", NULL }, +	{ "state_label", "State", NULL }, +	{ "rssi_label", "RSSI", NULL }, +	{ "height_value", "0m", NULL }, +	{ "state_value", "pad", NULL }, +	{ "rssi_value", "-50dBm", NULL }, +}; + +static void +aoview_label_assign(GtkLabel *widget, char *value) +{ +	char	*markup; + +	markup = g_markup_printf_escaped("<span font_weight=\"bold\" size=\"xx-large\">%s</span>", value); +	gtk_label_set_markup(widget, markup); +	g_free(markup); +} + +void +aoview_label_show(struct aostate *state) +{ +	char	line[1024]; +	sprintf(line, "%dm", state->height); +	aoview_label_assign(label_widgets[3].widget, line); + +	aoview_label_assign(label_widgets[4].widget, state->state); + +	sprintf(line, "%ddBm", state->rssi); +	aoview_label_assign(label_widgets[5].widget, line); +} + +void +aoview_label_init(GladeXML *xml) +{ +	int i; +	for (i = 0; i < sizeof(label_widgets)/sizeof(label_widgets[0]); i++) { +		label_widgets[i].widget = GTK_LABEL(glade_xml_get_widget(xml, label_widgets[i].name)); +		aoview_label_assign(label_widgets[i].widget, label_widgets[i].initial_value); +		assert(label_widgets[i].widget); +	} +} diff --git a/aoview/aoview_main.c b/aoview/aoview_main.c index 6833a84a..99de1473 100644 --- a/aoview/aoview_main.c +++ b/aoview/aoview_main.c @@ -91,6 +91,10 @@ int main(int argc, char **argv)  	aoview_eeprom_init(xml); +	aoview_replay_init(xml); + +	aoview_label_init(xml); +  	gtk_main();  	return 0; diff --git a/aoview/aoview_monitor.c b/aoview/aoview_monitor.c index d01ca771..faa24474 100644 --- a/aoview/aoview_monitor.c +++ b/aoview/aoview_monitor.c @@ -66,16 +66,15 @@ aoview_parse_pos(double *target, char *source)  	*target = r;  } -static void +static struct aostate	state; + +gboolean  aoview_monitor_parse(char *line)  {  	char *saveptr;  	char *words[64];  	int nword; -	struct aostate	state; -	if (aoview_log_get_serial()) -		aoview_log_printf ("%s\n", line);  	for (nword = 0; nword < 64; nword++) {  		words[nword] = strtok_r(line, " \t\n", &saveptr);  		line = NULL; @@ -83,12 +82,11 @@ aoview_monitor_parse(char *line)  			break;  	}  	if (nword < 36) -		return; +		return FALSE;  	if (strcmp(words[0], "CALL") != 0) -		return; +		return FALSE;  	aoview_parse_string(state.callsign, sizeof (state.callsign), words[1]);  	aoview_parse_int(&state.serial, words[3]); -	aoview_log_set_serial(state.serial);  	aoview_parse_int(&state.rssi, words[5]);  	aoview_parse_string(state.state, sizeof (state.state), words[9]); @@ -133,6 +131,7 @@ aoview_monitor_parse(char *line)  		state.v_error = 0;  	}  	aoview_state_notify(&state); +	return TRUE;  }  static void @@ -155,8 +154,13 @@ aoview_monitor_callback(gpointer user_data,  				continue;  			if (c == '\n') {  				monitor_line[monitor_pos] = '\0'; -				if (monitor_pos) -				aoview_monitor_parse(monitor_line); +				if (monitor_pos) { +					if (aoview_monitor_parse(monitor_line)) { +						aoview_log_set_serial(state.serial); +						if (aoview_log_get_serial()) +							aoview_log_printf ("%s\n", monitor_line); +					} +				}  				monitor_pos = 0;  			} else if (monitor_pos < MONITOR_LEN)  				monitor_line[monitor_pos++] = c; diff --git a/aoview/aoview_replay.c b/aoview/aoview_replay.c new file mode 100644 index 00000000..42728961 --- /dev/null +++ b/aoview/aoview_replay.c @@ -0,0 +1,127 @@ +/* + * Copyright © 2009 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "aoview.h" + +static GtkFileChooser	*replay_dialog; +static GtkWidget	*replay_ok; +static FILE		*replay_file; +static int		replay_tick; + +static int +find_tick(char *line, gboolean *is_pad) +{ +	char	*state = strstr(line, "STATE"); +	if (!state) +		return -1; +	state = strchr(state, ' '); +	if (!state) +		return -1; +	while (*state == ' ') +		state++; +	*is_pad = strncmp(state, "pad", 3) == 0; +	while (*state && !isdigit(*state)) +		state++; +	return atoi(state); +} + +static void +aoview_replay_close(void) +{ +	if (replay_file) { +		fclose(replay_file); +		replay_file = NULL; +	} +} + +static char	replay_line[1024]; + +static gboolean +aoview_replay_read(gpointer data); + +static gboolean +aoview_replay_execute(gpointer data) +{ +	aoview_monitor_parse(replay_line); +	g_idle_add(aoview_replay_read, NULL); +	return FALSE; +} + +static gboolean +aoview_replay_read(gpointer data) +{ +	int		tick; +	gboolean	is_pad; + +	if (!replay_file) +		return FALSE; +	if (fgets(replay_line, sizeof (replay_line), replay_file)) { +		tick = find_tick(replay_line, &is_pad); +		if (tick >= 0 && replay_tick >= 0 && !is_pad) { +			while (tick < replay_tick) +				tick += 65536; +			g_timeout_add((tick - replay_tick) * 10, +				      aoview_replay_execute, +				      NULL); +		} else { +			aoview_replay_execute(NULL); +		} +		replay_tick = tick; +	} else { +		aoview_replay_close(); +	} +	return FALSE; +} + +static void +aoview_replay_open(GtkWidget *widget, gpointer data) +{ +	char		*replay_file_name; +	GtkWidget	*dialog; + +	aoview_replay_close(); +	replay_file_name = gtk_file_chooser_get_filename(replay_dialog); +	replay_file = fopen(replay_file_name, "r"); +	if (!replay_file) { +		dialog = gtk_message_dialog_new(GTK_WINDOW(replay_dialog), +						GTK_DIALOG_DESTROY_WITH_PARENT, +						GTK_MESSAGE_ERROR, +						GTK_BUTTONS_CLOSE, +						"Error loading file '%s': %s", +						replay_file_name, g_strerror(errno)); +		gtk_dialog_run(GTK_DIALOG(dialog)); +		gtk_widget_destroy(dialog); +	} else { +		replay_tick = -1; +		aoview_replay_read(NULL); +	} +	gtk_widget_hide(GTK_WIDGET(replay_dialog)); +} + +void +aoview_replay_init(GladeXML *xml) +{ +	replay_dialog = GTK_FILE_CHOOSER(glade_xml_get_widget(xml, "ao_replay_dialog")); +	assert(replay_dialog); +	gtk_file_chooser_set_current_folder(replay_dialog, aoview_file_dir); + +	replay_ok = glade_xml_get_widget(xml, "ao_replay_ok"); +	assert(replay_ok); +	g_signal_connect(G_OBJECT(replay_ok), "clicked", +			 G_CALLBACK(aoview_replay_open), +			 replay_dialog); +} diff --git a/aoview/aoview_state.c b/aoview/aoview_state.c index 13b0f73b..4ba1854e 100644 --- a/aoview/aoview_state.c +++ b/aoview/aoview_state.c @@ -18,41 +18,47 @@  #include "aoview.h"  #include <math.h> -static double	pad_lat_total; -static double	pad_lon_total; -static int	pad_alt_total; -static int	npad_gps; -static int	prev_tick; -static double	prev_accel; -static double	pad_lat; -static double	pad_lon; -static double	pad_alt; -static double	min_pres; -static double	min_accel; - -#define NUM_PAD_SAMPLES	10 +static inline double sqr(a) { return a * a; };  static void  aoview_great_circle (double start_lat, double start_lon,  		     double end_lat, double end_lon,  		     double *dist, double *bearing)  { -	double rad = M_PI / 180; -	double earth_radius = 6371.2; +	const double rad = M_PI / 180; +	const double earth_radius = 6371.2 * 1000;	/* in meters */  	double lat1 = rad * start_lat; -	double lon1 = -rad * start_lon; +	double lon1 = rad * -start_lon;  	double lat2 = rad * end_lat; -	double lon2 = -rad * end_lon; - -	double d = acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lon1-lon2)); -	double argacos = (sin(lat2)-sin(lat1)*cos(d))/(sin(d)*cos(lat1)); -	double crs; -	if (sin(lon2-lon1) < 0) -		crs = acos(argacos); -	else -		crs = 2 * M_PI - acos(argacos); +	double lon2 = rad * -end_lon; + +	double d_lat = lat2 - lat1; +	double d_lon = lon2 - lon1; + +	/* From http://en.wikipedia.org/wiki/Great-circle_distance */ +	double vdn = sqrt(sqr(cos(lat2) * sin(d_lon)) + +			  sqr(cos(lat1) * sin(lat2) - +			      sin(lat1) * cos(lat2) * cos(d_lon))); +	double vdd = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(d_lon); +	double d = atan2(vdn,vdd); +	double course; + +	if (cos(lat1) < 1e-20) { +		if (lat1 > 0) +			course = M_PI; +		else +			course = -M_PI; +	} else { +		if (d < 1e-10) +			course = 0; +		else +			course = acos((sin(lat2)-sin(lat1)*cos(d)) / +				      (sin(d)*cos(lat1))); +		if (sin(lon2-lon1) > 0) +			course = 2 * M_PI-course; +	}  	*dist = d * earth_radius; -	*bearing = crs * 180/M_PI; +	*bearing = course * 180/M_PI;  }  static void @@ -80,117 +86,119 @@ static char *ascent_states[] = {  	0,  }; +/* + * Fill out the derived data fields + */ +static void +aoview_state_derive(struct aostate *state) +{ +	int	i; + +	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->npad++; +			state->pad_lat_total += state->lat; +			state->pad_lon_total += state->lon; +			state->pad_alt_total += state->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; +		} +	} +	state->ascent = FALSE; +	for (i = 0; ascent_states[i]; i++) +		if (!strcmp(state->state, ascent_states[i])) +			state->ascent = TRUE; + +	/* Only look at accelerometer data on the way up */ +	if (state->ascent && state->acceleration > state->max_acceleration) +		state->max_acceleration = state->acceleration; +	if (state->ascent && state->speed > state->max_speed) +		state->max_speed = state->speed; + +	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); +} +  void  aoview_state_speak(struct aostate *state)  {  	static char	last_state[32];  	int		i;  	gboolean	report = FALSE; -	static time_t	last_time; -	time_t		this_time; +	int		this_tick; +	static int	last_tick;  	static int	last_altitude;  	int		this_altitude;  	if (strcmp(state->state, last_state)) { -		aoview_voice_speak("rocket state now %s\n", state->state); +		aoview_voice_speak("%s\n", state->state);  		if (!strcmp(state->state, "drogue")) -			aoview_voice_speak("maximum altitude %d meters\n", -					   aoview_pres_to_altitude(min_pres) - -					   aoview_pres_to_altitude(state->ground_pres)); +			aoview_voice_speak("apogee %d meters\n", +					   (int) state->max_height);  		report = TRUE;  		strcpy(last_state, state->state);  	} -	this_time = time(NULL);  	this_altitude = aoview_pres_to_altitude(state->flight_pres) - aoview_pres_to_altitude(state->ground_pres); -	if (this_time - last_time >= 10) -		report = TRUE; -	if (this_altitude / 1000 != last_altitude / 1000) -		report = TRUE; - +	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 (report) { -		aoview_voice_speak("altitude %d meters\n", +		aoview_voice_speak("%d meters\n",  				   this_altitude); -		for (i = 0; ascent_states[i]; i++) -			if (!strcmp(ascent_states[i], state->state)) { -				aoview_voice_speak("speed %d meters per second\n", -						   state->flight_vel / 2700); -				break; -			} +		if (state->ascent) +			aoview_voice_speak("%d meters per second\n", +					   state->flight_vel / 2700); +		last_tick = state->tick; +		last_altitude = this_altitude; +		printf ("report at tick %d height %d\n", +			state->tick, this_altitude);  	} - -	last_time = this_time; -	last_altitude = this_altitude;  }  void  aoview_state_notify(struct aostate *state)  { -	int	altitude; -	double	accel; -	int	ticks; -	double	dist; -	double	bearing; -	double	temp; -	double	velocity; -	double	battery; -	double	drogue_sense, main_sense; -	double	max_accel; - -	if (!strcmp(state->state, "pad")) { -		if (state->locked && npad_gps < NUM_PAD_SAMPLES) { -			pad_lat_total += state->lat; -			pad_lon_total += state->lon; -			pad_alt_total += state->alt; -			npad_gps++; -		} -		if (state->locked && npad_gps <= NUM_PAD_SAMPLES) { -			pad_lat = pad_lat_total / npad_gps; -			pad_lon = pad_lon_total / npad_gps; -			pad_alt = pad_alt_total / npad_gps; -		} -		min_pres = state->ground_pres; -		min_accel = state->ground_accel; -	} -	if (state->flight_pres < min_pres) -		min_pres = state->flight_pres; -	if (state->flight_accel < min_accel) -		min_accel = state->flight_accel; -	altitude = aoview_pres_to_altitude(state->flight_pres) - aoview_pres_to_altitude(state->ground_pres); -	accel = (state->ground_accel - state->flight_accel) / 27.0; -	velocity = state->flight_vel / 2700.0; -	max_accel = (state->ground_accel - min_accel) / 27.0; -	ticks = state->tick - prev_tick; -	temp = ((state->temp / 32767.0 * 3.3) - 0.5) / 0.01; -	battery = (state->batt / 32767.0 * 5.0); -	drogue_sense = (state->drogue / 32767.0 * 15.0); -	main_sense = (state->main / 32767.0 * 15.0); - -	prev_accel = accel; -	prev_tick = state->tick; +	aoview_state_derive(state);  	aoview_table_start(); -	if (npad_gps >= NUM_PAD_SAMPLES) +	if (state->npad >= MIN_PAD_SAMPLES)  		aoview_table_add_row("Ground state", "ready");  	else  		aoview_table_add_row("Ground state", "waiting for gps (%d)", -				     NUM_PAD_SAMPLES - npad_gps); +				     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", altitude); -	aoview_table_add_row("Max height", "%dm", -			     aoview_pres_to_altitude(min_pres) - -			     aoview_pres_to_altitude(state->ground_pres)); -	aoview_table_add_row("Acceleration", "%gm/s²", accel); -	aoview_table_add_row("Max acceleration", "%gm/s²", max_accel); -	aoview_table_add_row("Velocity", "%gm/s", velocity); -	aoview_table_add_row("Temperature", "%g°C", temp); -	aoview_table_add_row("Battery", "%gV", battery); -	aoview_table_add_row("Drogue", "%gV", drogue_sense); -	aoview_table_add_row("Main", "%gV", main_sense); -	aoview_table_add_row("Pad altitude", "%dm", aoview_pres_to_altitude(state->ground_pres)); +	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("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'); @@ -207,36 +215,24 @@ aoview_state_notify(struct aostate *state)  				     state->climb_rate);  		aoview_table_add_row("GPS precision", "%f(hdop) %dm(h) %dm(v)\n",  				     state->hdop, state->h_error, state->v_error); -		aoview_great_circle(pad_lat, pad_lon, state->lat, state->lon, -				    &dist, &bearing); -		aoview_table_add_row("Distance from pad", "%gm", dist * 1000); -		aoview_table_add_row("Direction from pad", "%g°", bearing); +		aoview_table_add_row("Distance from pad", "%gm", state->distance); +		aoview_table_add_row("Direction from pad", "%g°", state->bearing);  	} else {  		aoview_table_add_row("GPS", "unlocked");  	} -	if (npad_gps) { -		aoview_state_add_deg("Pad latitude", pad_lat, 'N', 'S'); -		aoview_state_add_deg("Pad longitude", pad_lon, 'E', 'W'); -		aoview_table_add_row("Pad GPS alt", "%gm", pad_alt); +	if (state->npad) { +		aoview_state_add_deg("Pad latitude", state->pad_lat, 'N', 'S'); +		aoview_state_add_deg("Pad longitude", state->pad_lon, 'E', 'W'); +		aoview_table_add_row("Pad GPS alt", "%gm", state->pad_alt);  	}  	aoview_table_finish(); +	aoview_label_show(state);  	aoview_state_speak(state);  }  void  aoview_state_new(void)  { -	pad_lat_total = 0; -	pad_lon_total = 0; -	pad_alt_total = 0; -	npad_gps = 0; -	prev_tick = 0; -	prev_accel = 0; -	pad_lat = 0; -	pad_lon = 0; -	pad_alt = 0; -	min_pres = 32767; -	min_accel = 32767;  }  void | 
