diff options
| -rw-r--r-- | .gitignore | 4 | ||||
| -rw-r--r-- | ao-tools/Makefile.am | 2 | ||||
| -rw-r--r-- | ao-tools/ao-dumplog/ao-dumplog.c | 1 | ||||
| -rw-r--r-- | ao-tools/ao-postflight/Makefile.am | 12 | ||||
| -rw-r--r-- | ao-tools/ao-postflight/ao-postflight.1 | 29 | ||||
| -rw-r--r-- | ao-tools/ao-postflight/ao-postflight.c | 166 | ||||
| -rw-r--r-- | ao-tools/lib/Makefile.am | 4 | ||||
| -rw-r--r-- | ao-tools/lib/cc-analyse.c | 58 | ||||
| -rw-r--r-- | ao-tools/lib/cc-convert.c | 275 | ||||
| -rw-r--r-- | ao-tools/lib/cc-log.c | 114 | ||||
| -rw-r--r-- | ao-tools/lib/cc-logfile.c | 218 | ||||
| -rw-r--r-- | ao-tools/lib/cc-telem.c | 164 | ||||
| -rw-r--r-- | ao-tools/lib/cc-util.c | 2 | ||||
| -rw-r--r-- | ao-tools/lib/cc.h | 207 | ||||
| -rw-r--r-- | configure.ac | 1 | 
15 files changed, 1255 insertions, 2 deletions
| @@ -22,9 +22,13 @@ ao-teleterra.h  ao-tidongle.h  ao-tools/ao-bitbang/ao-bitbang  ao-tools/ao-dbg/ao-dbg +ao-tools/ao-dumplog/ao-dumplog  ao-tools/ao-eeprom/ao-eeprom +ao-tools/ao-list/ao-list  ao-tools/ao-load/ao-load +ao-tools/ao-postflight/ao-postflight  ao-tools/ao-rawload/ao-rawload +ao-tools/ao-view/ao-view  ao-view/Makefile  ao-view/ao-view  autom4te.cache diff --git a/ao-tools/Makefile.am b/ao-tools/Makefile.am index b61f045f..2850e909 100644 --- a/ao-tools/Makefile.am +++ b/ao-tools/Makefile.am @@ -1 +1 @@ -SUBDIRS=lib ao-rawload ao-dbg ao-dumplog ao-bitbang ao-eeprom ao-list ao-load ao-view +SUBDIRS=lib ao-rawload ao-dbg ao-dumplog ao-bitbang ao-eeprom ao-list ao-load ao-postflight ao-view diff --git a/ao-tools/ao-dumplog/ao-dumplog.c b/ao-tools/ao-dumplog/ao-dumplog.c index 4bccfd61..b930f0e5 100644 --- a/ao-tools/ao-dumplog/ao-dumplog.c +++ b/ao-tools/ao-dumplog/ao-dumplog.c @@ -86,6 +86,7 @@ main (int argc, char **argv)  			if (!out) {  				perror(filename);  			} +			fprintf (out, "%s\n", line);  		} else if (sscanf(line, "%c %x %x %x", &cmd, &tick, &a, &b) == 4) {  			if (out) {  				fprintf(out, "%s\n", line); diff --git a/ao-tools/ao-postflight/Makefile.am b/ao-tools/ao-postflight/Makefile.am new file mode 100644 index 00000000..301ac454 --- /dev/null +++ b/ao-tools/ao-postflight/Makefile.am @@ -0,0 +1,12 @@ +bin_PROGRAMS=ao-postflight + +AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS) $(GNOME_CFLAGS) +AO_POSTFLIGHT_LIBS=$(top_builddir)/ao-tools/lib/libao-tools.a + +ao_postflight_DEPENDENCIES = $(AO_POSTFLIGHT_LIBS) + +ao_postflight_LDADD=$(AO_POSTFLIGHT_LIBS) $(LIBUSB_LIBS) $(GNOME_LIBS) + +ao_postflight_SOURCES = ao-postflight.c + +man_MANS = ao-postflight.1 diff --git a/ao-tools/ao-postflight/ao-postflight.1 b/ao-tools/ao-postflight/ao-postflight.1 new file mode 100644 index 00000000..fe02587f --- /dev/null +++ b/ao-tools/ao-postflight/ao-postflight.1 @@ -0,0 +1,29 @@ +.\" +.\" 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; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" 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. +.\" +.\" +.TH AO-POSTFLIGHT 1 "ao-postflight" "" +.SH NAME +ao-postflight \- Analyse a flight log (either telemetry or eeprom) +.SH SYNOPSIS +.B "ao-postflight" +{flight.eeprom|flight.telem} +.SH DESCRIPTION +.I ao-postflight +reads the specified flight log and produces a summary of the flight on stdout. +.SH AUTHOR +Keith Packard diff --git a/ao-tools/ao-postflight/ao-postflight.c b/ao-tools/ao-postflight/ao-postflight.c new file mode 100644 index 00000000..f0e2c2ae --- /dev/null +++ b/ao-tools/ao-postflight/ao-postflight.c @@ -0,0 +1,166 @@ +/* + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#define _GNU_SOURCE +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> +#include "cc-usb.h" +#include "cc.h" + +#define NUM_BLOCK	512 + +static const struct option options[] = { +	{ 0, 0, 0, 0}, +}; + +static void usage(char *program) +{ +	fprintf(stderr, "usage: %s {flight-log} ...\n", program); +	exit(1); +} + +static const char *state_names[] = { +	"startup", +	"idle", +	"pad", +	"boost", +	"fast", +	"coast", +	"drogue", +	"main", +	"landed", +	"invalid" +}; + +void +analyse_flight(struct cc_flightraw *f) +{ +	double	height; +	double	accel; +	double	boost_start, boost_stop; +	double	min_pres; +	int	i; +	int	pres_i, accel_i; +	int	boost_start_set = 0; +	int	boost_stop_set = 0; +	enum ao_flight_state	state; +	double	state_start, state_stop; + +	printf ("Flight:  %9d\nSerial:  %9d\n", +		f->flight, f->serial); +	boost_start = f->accel.data[0].time; +	boost_stop = f->accel.data[f->accel.num-1].time; +	for (i = 0; i < f->state.num; i++) { +		if (f->state.data[i].value == ao_flight_boost && !boost_start_set) { +			boost_start = f->state.data[i].time; +			boost_start_set = 1; +		} +		if (f->state.data[i].value > ao_flight_boost && !boost_stop_set) { +			boost_stop = f->state.data[i].time; +			boost_stop_set = 1; +		} +	} + +	pres_i = cc_timedata_min(&f->pres, f->pres.data[0].time, +				 f->pres.data[f->pres.num-1].time); +	min_pres = f->pres.data[pres_i].value; +	height = cc_barometer_to_altitude(min_pres) - +		cc_barometer_to_altitude(f->ground_pres); +	printf ("Max height: %9.2fm    %9.2fft %9.2fs\n", +		height, height * 100 / 2.54 / 12, +		(f->pres.data[pres_i].time - boost_start) / 100.0); + +	accel_i = cc_timedata_min(&f->accel, boost_start, boost_stop); +	accel = cc_accelerometer_to_acceleration(f->accel.data[accel_i].value, +						 f->ground_accel); +	printf ("Max accel:  %9.2fm/s² %9.2fg  %9.2fs\n", +		accel, accel /  9.80665, +		(f->accel.data[accel_i].time - boost_start) / 100.0); +	for (i = 0; i < f->state.num; i++) { +		state = f->state.data[i].value; +		state_start = f->state.data[i].time; +		if (i < f->state.num - 1) +			state_stop = f->state.data[i+1].time; +		else +			state_stop = f->accel.data[f->accel.num-1].time; +		printf("State: %s\n", state_names[state]); +		printf("\tStart:      %9.2fs\n", (state_start - boost_start) / 100.0); +		printf("\tDuration:   %9.2fs\n", (state_stop - state_start) / 100.0); +		accel_i = cc_timedata_min(&f->accel, state_start, state_stop); +		accel = cc_accelerometer_to_acceleration(f->accel.data[accel_i].value, +							 f->ground_accel); +		printf("\tMax accel:  %9.2fm/s² %9.2fg  %9.2fs\n", +		       accel, accel / 9.80665, +		       (f->accel.data[accel_i].time - boost_start) / 100.0); + +		pres_i = cc_timedata_min(&f->pres, state_start, state_stop); +		min_pres = f->pres.data[pres_i].value; +		height = cc_barometer_to_altitude(min_pres) - +			cc_barometer_to_altitude(f->ground_pres); +		printf ("\tMax height: %9.2fm    %9.2fft %9.2fs\n", +			height, height * 100 / 2.54 / 12, +			(f->pres.data[pres_i].time - boost_start) / 100.0); +	} +} + +int +main (int argc, char **argv) +{ +	FILE			*file; +	int			i; +	int			ret = 0; +	struct cc_flightraw	*raw; +	int			c; +	int			serial; +	char			*s; + +	while ((c = getopt_long(argc, argv, "", options, NULL)) != -1) { +		switch (c) { +		default: +			usage(argv[0]); +			break; +		} +	} +	for (i = optind; i < argc; i++) { +		file = fopen(argv[i], "r"); +		if (!file) { +			perror(argv[i]); +			ret++; +			continue; +		} +		s = strstr(argv[i], "-serial-"); +		if (s) +			serial = atoi(s + 8); +		else +			serial = 0; +		raw = cc_log_read(file); +		if (!raw) { +			perror(argv[i]); +			ret++; +			continue; +		} +		if (!raw->serial) +			raw->serial = serial; +		analyse_flight(raw); +		cc_flightraw_free(raw); +	} +	return ret; +} diff --git a/ao-tools/lib/Makefile.am b/ao-tools/lib/Makefile.am index da13ede9..e682f757 100644 --- a/ao-tools/lib/Makefile.am +++ b/ao-tools/lib/Makefile.am @@ -14,6 +14,8 @@ libao_tools_a_SOURCES = \  	ccdbg-memory.c \  	ccdbg-rom.c \  	ccdbg-state.c \ +	cc-analyse.c \ +	cc-convert.c \  	cc-log.c \  	cc-usb.c \  	cc-usb.h \ @@ -22,5 +24,7 @@ libao_tools_a_SOURCES = \  	cc-util.c \  	cc-bitbang.c \  	cc-bitbang.h \ +	cc-logfile.c \ +	cc-telem.c \  	cp-usb-async.c \  	cp-usb-async.h diff --git a/ao-tools/lib/cc-analyse.c b/ao-tools/lib/cc-analyse.c new file mode 100644 index 00000000..6fd36cdc --- /dev/null +++ b/ao-tools/lib/cc-analyse.c @@ -0,0 +1,58 @@ +/* + * 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 "cc.h" + +int +cc_timedata_min(struct cc_timedata *d, double min_time, double max_time) +{ +	int	i; +	int	set = 0; +	int	min_i; +	double	min; + +	if (d->num == 0) +		return 0; +	for (i = 0; i < d->num; i++) +		if (min_time <= d->data[i].time && d->data[i].time <= max_time) +			if (!set || d->data[i].value < min) { +				min_i = i; +				min = d->data[i].value; +				set = 1; +			} +	return min_i; +} + +int +cc_timedata_max(struct cc_timedata *d, double min_time, double max_time) +{ +	int	i; +	double	max; +	int	max_i; +	int	set = 0; + +	if (d->num == 0) +		return 0; +	for (i = 0; i < d->num; i++) +		if (min_time <= d->data[i].time && d->data[i].time <= max_time) +			if (!set || d->data[i].value > max) { +				max_i = i; +				max = d->data[i].value; +				set = 1; +			} +	return max_i; +} diff --git a/ao-tools/lib/cc-convert.c b/ao-tools/lib/cc-convert.c new file mode 100644 index 00000000..ac6962ba --- /dev/null +++ b/ao-tools/lib/cc-convert.c @@ -0,0 +1,275 @@ +/* + * 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 "cc.h" +#include <math.h> + +/* + * Pressure Sensor Model, version 1.1 + * + * written by Holly Grimes + * + * Uses the International Standard Atmosphere as described in + *   "A Quick Derivation relating altitude to air pressure" (version 1.03) + *    from the Portland State Aerospace Society, except that the atmosphere + *    is divided into layers with each layer having a different lapse rate. + * + * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007 + *    at site <http://en.wikipedia.org/wiki/International_Standard_Atmosphere + * + * Height measurements use the local tangent plane.  The postive z-direction is up. + * + * All measurements are given in SI units (Kelvin, Pascal, meter, meters/second^2). + *   The lapse rate is given in Kelvin/meter, the gas constant for air is given + *   in Joules/(kilogram-Kelvin). + */ + +#define GRAVITATIONAL_ACCELERATION -9.80665 +#define AIR_GAS_CONSTANT	287.053 +#define NUMBER_OF_LAYERS	7 +#define MAXIMUM_ALTITUDE	84852.0 +#define MINIMUM_PRESSURE	0.3734 +#define LAYER0_BASE_TEMPERATURE	288.15 +#define LAYER0_BASE_PRESSURE	101325 + +/* lapse rate and base altitude for each layer in the atmosphere */ +static const double lapse_rate[NUMBER_OF_LAYERS] = { +	-0.0065, 0.0, 0.001, 0.0028, 0.0, -0.0028, -0.002 +}; + +static const int base_altitude[NUMBER_OF_LAYERS] = { +	0, 11000, 20000, 32000, 47000, 51000, 71000 +}; + +/* outputs atmospheric pressure associated with the given altitude. altitudes +   are measured with respect to the mean sea level */ +double +cc_altitude_to_pressure(double altitude) +{ + +   double base_temperature = LAYER0_BASE_TEMPERATURE; +   double base_pressure = LAYER0_BASE_PRESSURE; + +   double pressure; +   double base; /* base for function to determine pressure */ +   double exponent; /* exponent for function to determine pressure */ +   int layer_number; /* identifies layer in the atmosphere */ +   int delta_z; /* difference between two altitudes */ + +   if (altitude > MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */ +      return 0; + +   /* calculate the base temperature and pressure for the atmospheric layer +      associated with the inputted altitude */ +   for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) { +      delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; +      if (lapse_rate[layer_number] == 0.0) { +         exponent = GRAVITATIONAL_ACCELERATION * delta_z +              / AIR_GAS_CONSTANT / base_temperature; +         base_pressure *= exp(exponent); +      } +      else { +         base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; +         exponent = GRAVITATIONAL_ACCELERATION / +              (AIR_GAS_CONSTANT * lapse_rate[layer_number]); +         base_pressure *= pow(base, exponent); +      } +      base_temperature += delta_z * lapse_rate[layer_number]; +   } + +   /* calculate the pressure at the inputted altitude */ +   delta_z = altitude - base_altitude[layer_number]; +   if (lapse_rate[layer_number] == 0.0) { +      exponent = GRAVITATIONAL_ACCELERATION * delta_z +           / AIR_GAS_CONSTANT / base_temperature; +      pressure = base_pressure * exp(exponent); +   } +   else { +      base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; +      exponent = GRAVITATIONAL_ACCELERATION / +           (AIR_GAS_CONSTANT * lapse_rate[layer_number]); +      pressure = base_pressure * pow(base, exponent); +   } + +   return pressure; +} + + +/* outputs the altitude associated with the given pressure. the altitude +   returned is measured with respect to the mean sea level */ +double +cc_pressure_to_altitude(double pressure) +{ + +   double next_base_temperature = LAYER0_BASE_TEMPERATURE; +   double next_base_pressure = LAYER0_BASE_PRESSURE; + +   double altitude; +   double base_pressure; +   double base_temperature; +   double base; /* base for function to determine base pressure of next layer */ +   double exponent; /* exponent for function to determine base pressure +                             of next layer */ +   double coefficient; +   int layer_number; /* identifies layer in the atmosphere */ +   int delta_z; /* difference between two altitudes */ + +   if (pressure < 0)  /* illegal pressure */ +      return -1; +   if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */ +      return MAXIMUM_ALTITUDE; + +   /* calculate the base temperature and pressure for the atmospheric layer +      associated with the inputted pressure. */ +   layer_number = -1; +   do { +      layer_number++; +      base_pressure = next_base_pressure; +      base_temperature = next_base_temperature; +      delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; +      if (lapse_rate[layer_number] == 0.0) { +         exponent = GRAVITATIONAL_ACCELERATION * delta_z +              / AIR_GAS_CONSTANT / base_temperature; +         next_base_pressure *= exp(exponent); +      } +      else { +         base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; +         exponent = GRAVITATIONAL_ACCELERATION / +              (AIR_GAS_CONSTANT * lapse_rate[layer_number]); +         next_base_pressure *= pow(base, exponent); +      } +      next_base_temperature += delta_z * lapse_rate[layer_number]; +   } +   while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure); + +   /* calculate the altitude associated with the inputted pressure */ +   if (lapse_rate[layer_number] == 0.0) { +      coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION) +                                                    * base_temperature; +      altitude = base_altitude[layer_number] +                    + coefficient * log(pressure / base_pressure); +   } +   else { +      base = pressure / base_pressure; +      exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number] +                                       / GRAVITATIONAL_ACCELERATION; +      coefficient = base_temperature / lapse_rate[layer_number]; +      altitude = base_altitude[layer_number] +                      + coefficient * (pow(base, exponent) - 1); +   } + +   return altitude; +} + +/* + * Values for our MP3H6115A pressure sensor + * + * From the data sheet: + * + * Pressure range: 15-115 kPa + * Voltage at 115kPa: 2.82 + * Output scale: 27mV/kPa + * + * + * 27 mV/kPa * 2047 / 3300 counts/mV = 16.75 counts/kPa + * 2.82V * 2047 / 3.3 counts/V = 1749 counts/115 kPa + */ + +static const double counts_per_kPa = 27 * 2047 / 3300; +static const double counts_at_101_3kPa = 1674.0; + +double +cc_barometer_to_pressure(double count) +{ +	return ((count / 16.0) / 2047.0 + 0.095) / 0.009 * 1000.0; +} + +double +cc_barometer_to_altitude(double baro) +{ +	double Pa = cc_barometer_to_pressure(baro); +	return cc_pressure_to_altitude(Pa); +} + +static const double count_per_mss = 27.0; + +double +cc_accelerometer_to_acceleration(double accel, double ground_accel) +{ +	return (ground_accel - accel) / count_per_mss; +} + +double +cc_thermometer_to_temperature(double thermo) +{ +	return ((thermo / 32767 * 3.3) - 0.5) / 0.01; +} + +double +cc_battery_to_voltage(double battery) +{ +	return battery / 32767.0 * 5.0; +} + +double +cc_ignitor_to_voltage(double ignite) +{ +	return ignite / 32767 * 15.0; +} + +static inline double sqr(double a) { return a * a; } + +void +cc_great_circle (double start_lat, double start_lon, +		 double end_lat, double end_lon, +		 double *dist, double *bearing) +{ +	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 lat2 = rad * end_lat; +	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 = course * 180/M_PI; +} diff --git a/ao-tools/lib/cc-log.c b/ao-tools/lib/cc-log.c new file mode 100644 index 00000000..dd8177f4 --- /dev/null +++ b/ao-tools/lib/cc-log.c @@ -0,0 +1,114 @@ +/* + * 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 <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <gconf/gconf-client.h> +#include "cc.h" + +static char *cc_file_dir; + +#define ALTOS_DIR_PATH	"/apps/aoview/log_dir" +#define DEFAULT_DIR	"AltOS" + +static void +cc_file_save_conf(void) +{ +	GConfClient	*gconf_client; + +	g_type_init(); +	gconf_client = gconf_client_get_default(); +	if (gconf_client) +	{ +		gconf_client_set_string(gconf_client, +					ALTOS_DIR_PATH, +					cc_file_dir, +					NULL); +		g_object_unref(G_OBJECT(gconf_client)); +	} +} + +static void +cc_file_load_conf(void) +{ +	char *file_dir; +	GConfClient	*gconf_client; + +	g_type_init(); +	gconf_client = gconf_client_get_default(); +	if (gconf_client) +	{ +		file_dir = gconf_client_get_string(gconf_client, +						   ALTOS_DIR_PATH, +						   NULL); +		g_object_unref(G_OBJECT(gconf_client)); +		if (file_dir) +			cc_file_dir = strdup(file_dir); +	} +} + +void +cc_set_log_dir(char *dir) +{ +	cc_file_dir = strdup(dir); +	cc_file_save_conf(); +} + +char * +cc_get_log_dir(void) +{ +	cc_file_load_conf(); +	if (!cc_file_dir) { +		cc_file_dir = cc_fullname(getenv("HOME"), DEFAULT_DIR); +		cc_file_save_conf(); +	} +	return cc_file_dir; +} + +char * +cc_make_filename(int serial, char *ext) +{ +	char		base[50]; +	struct tm	tm; +	time_t		now; +	char		*full; +	int		r; +	int		sequence; + +	now = time(NULL); +	(void) localtime_r(&now, &tm); +	cc_mkdir(cc_get_log_dir()); +	sequence = 0; +	for (;;) { +		snprintf(base, sizeof (base), "%04d-%02d-%02d-serial-%03d-flight-%03d.%s", +			tm.tm_year + 1900, +			tm.tm_mon + 1, +			tm.tm_mday, +			serial, +			sequence, +			ext); +		full = cc_fullname(cc_get_log_dir(), base); +		r = access(full, F_OK); +		if (r < 0) +			return full; +		free(full); +		sequence++; +	} + +} diff --git a/ao-tools/lib/cc-logfile.c b/ao-tools/lib/cc-logfile.c new file mode 100644 index 00000000..444ff089 --- /dev/null +++ b/ao-tools/lib/cc-logfile.c @@ -0,0 +1,218 @@ +/* + * 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 "cc.h" +#include <stdio.h> +#include <stdlib.h> + +static int +timedata_add(struct cc_timedata *data, double time, double value) +{ +	struct cc_timedataelt	*newdata; +	int			newsize; +	if (data->size == data->num) { +		if (data->size == 0) +			newdata = malloc((newsize = 256) * sizeof (struct cc_timedataelt)); +		else +			newdata = realloc (data->data, (newsize = data->size * 2) +					   * sizeof (struct cc_timedataelt)); +		if (!newdata) +			return 0; +		data->size = newsize; +		data->data = newdata; +	} +	if (data->num && data->data[data->num-1].time > time) +		time += 65536; +	data->data[data->num].time = time; +	data->data[data->num].value = value; +	data->num++; +	return 1; +} + +static void +timedata_free(struct cc_timedata *data) +{ +	if (data->data) +		free(data->data); +} + +static int +gpsdata_add(struct cc_gpsdata *data, struct cc_gpselt *elt) +{ +	struct cc_gpselt	*newdata; +	int			newsize; +	if (data->size == data->num) { +		if (data->size == 0) +			newdata = malloc((newsize = 256) * sizeof (struct cc_gpselt)); +		else +			newdata = realloc (data->data, (newsize = data->size * 2) +					   * sizeof (struct cc_gpselt)); +		if (!newdata) +			return 0; +		data->size = newsize; +		data->data = newdata; +	} +	data->data[data->num] = *elt; +	data->num++; +	return 1; +} + +static void +gpsdata_free(struct cc_gpsdata *data) +{ +	if (data->data) +		free(data->data); +} + +#define AO_LOG_FLIGHT		'F' +#define AO_LOG_SENSOR		'A' +#define AO_LOG_TEMP_VOLT	'T' +#define AO_LOG_DEPLOY		'D' +#define AO_LOG_STATE		'S' +#define AO_LOG_GPS_TIME		'G' +#define AO_LOG_GPS_LAT		'N' +#define AO_LOG_GPS_LON		'W' +#define AO_LOG_GPS_ALT		'H' +#define AO_LOG_GPS_SAT		'V' + +#define AO_LOG_POS_NONE		(~0UL) + +static int +read_eeprom(const char *line, struct cc_flightraw *f, double *ground_pres, int *ground_pres_count) +{ +	char	type; +	int	tick; +	int	a, b; +	struct cc_gpselt	gps; +	int	serial; + +	if (sscanf(line, "serial-number %u", &serial) == 1) { +		f->serial = serial; +		return 1; +	} +	if (sscanf(line, "%c %x %x %x", &type, &tick, &a, &b) != 4) +		return 0; +	switch (type) { +	case AO_LOG_FLIGHT: +		f->ground_accel = a; +		f->ground_pres = 0; +		f->flight = b; +		*ground_pres = 0; +		*ground_pres_count = 0; +		break; +	case AO_LOG_SENSOR: +		timedata_add(&f->accel, tick, a); +		timedata_add(&f->pres, tick, b); +		if (*ground_pres_count < 20) { +			*ground_pres += b; +			(*ground_pres_count)++; +			if (*ground_pres_count >= 20) +				f->ground_pres = *ground_pres / *ground_pres_count; +		} +		break; +	case AO_LOG_TEMP_VOLT: +		timedata_add(&f->temp, tick, a); +		timedata_add(&f->volt, tick, b); +		break; +	case AO_LOG_DEPLOY: +		timedata_add(&f->drogue, tick, a); +		timedata_add(&f->main, tick, b); +		break; +	case AO_LOG_STATE: +		timedata_add(&f->state, tick, a); +		break; +	case AO_LOG_GPS_TIME: +		gps.time = tick; +		break; +	case AO_LOG_GPS_LAT: +		gps.lat = ((int32_t) (a + (b << 16))) / 10000000.0; +		break; +	case AO_LOG_GPS_LON: +		gps.lon = ((int32_t) (a + (b << 16))) / 10000000.0; +		break; +	case AO_LOG_GPS_ALT: +		gps.alt = ((int32_t) (a + (b << 16))); +		gpsdata_add(&f->gps, &gps); +		break; +	case AO_LOG_GPS_SAT: +		break; +	default: +		return 0; +	} +	return 1; +} + +static int +read_telem(const char *line, struct cc_flightraw *f) +{ +	struct cc_telem		telem; +	struct cc_gpselt	gps; +	if (!cc_telem_parse(line, &telem)) +		return 0; +	f->ground_accel = telem.ground_accel; +	f->ground_pres = telem.ground_pres; +	f->flight = 0; +	timedata_add(&f->accel, telem.tick, telem.flight_accel); +	timedata_add(&f->pres, telem.tick, telem.flight_pres); +	timedata_add(&f->temp, telem.tick, telem.temp); +	timedata_add(&f->volt, telem.tick, telem.batt); +	timedata_add(&f->drogue, telem.tick, telem.drogue); +	timedata_add(&f->main, telem.tick, telem.main); +	if (telem.gps.gps_locked) { +		gps.time = telem.tick; +		gps.lat = telem.gps.lat; +		gps.lon = telem.gps.lon; +		gps.alt = telem.gps.alt; +		gpsdata_add(&f->gps, &gps); +	} +	return 1; +} + +struct cc_flightraw * +cc_log_read(FILE *file) +{ +	struct cc_flightraw	*f; +	char			line[8192]; +	double			ground_pres; +	int			ground_pres_count; + +	f = calloc(1, sizeof (struct cc_flightraw)); +	if (!f) +		return NULL; +	while (fgets(line, sizeof (line), file)) { +		if (read_eeprom(line, f, &ground_pres, &ground_pres_count)) +			continue; +		if (read_telem(line, f)) +			continue; +		fprintf (stderr, "invalid line: %s", line); +	} +	return f; +} + +void +cc_flightraw_free(struct cc_flightraw *raw) +{ +	timedata_free(&raw->accel); +	timedata_free(&raw->pres); +	timedata_free(&raw->temp); +	timedata_free(&raw->volt); +	timedata_free(&raw->main); +	timedata_free(&raw->drogue); +	timedata_free(&raw->state); +	gpsdata_free(&raw->gps); +	free(raw); +} diff --git a/ao-tools/lib/cc-telem.c b/ao-tools/lib/cc-telem.c new file mode 100644 index 00000000..a6ac0313 --- /dev/null +++ b/ao-tools/lib/cc-telem.c @@ -0,0 +1,164 @@ +/* + * 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 "cc.h" +#include <string.h> +#include <stdlib.h> + +static void +cc_parse_string(char *target, int len, char *source) +{ +	strncpy(target, source, len-1); +	target[len-1] = '\0'; +} + +static void +cc_parse_int(int *target, char *source) +{ +	*target = strtol(source, NULL, 0); +} + +static void +cc_parse_hex(int *target, char *source) +{ +	*target = strtol(source, NULL, 16); +} + +static void +cc_parse_pos(double *target, char *source) +{ +	int	deg; +	double	min; +	char	dir; +	double	r; + +	if (sscanf(source, "%d°%lf'%c", °, &min, &dir) != 3) { +		*target = 0; +		return; +	} +	r = deg + min / 60.0; +	if (dir == 'S' || dir == 'W') +		r = -r; +	*target = r; +} + +#define PARSE_MAX_WORDS	512 + +int +cc_telem_parse(const char *input_line, struct cc_telem *telem) +{ +	char *saveptr; +	char *words[PARSE_MAX_WORDS]; +	int nword; +	char line_buf[8192], *line; +	int	tracking_pos; + +	/* avoid smashing our input parameter */ +	strncpy (line_buf, input_line, sizeof (line_buf)-1); +	line_buf[sizeof(line_buf) - 1] = '\0'; +	line = line_buf; +	for (nword = 0; nword < PARSE_MAX_WORDS; nword++) { +		words[nword] = strtok_r(line, " \t\n", &saveptr); +		line = NULL; +		if (words[nword] == NULL) +			break; +	} +	if (nword < 36) +		return FALSE; +	if (strcmp(words[0], "CALL") != 0) +		return FALSE; +	cc_parse_string(telem->callsign, sizeof (telem->callsign), words[1]); +	cc_parse_int(&telem->serial, words[3]); + +	cc_parse_int(&telem->rssi, words[5]); +	cc_parse_string(telem->state, sizeof (telem->state), words[9]); +	cc_parse_int(&telem->tick, words[10]); +	cc_parse_int(&telem->accel, words[12]); +	cc_parse_int(&telem->pres, words[14]); +	cc_parse_int(&telem->temp, words[16]); +	cc_parse_int(&telem->batt, words[18]); +	cc_parse_int(&telem->drogue, words[20]); +	cc_parse_int(&telem->main, words[22]); +	cc_parse_int(&telem->flight_accel, words[24]); +	cc_parse_int(&telem->ground_accel, words[26]); +	cc_parse_int(&telem->flight_vel, words[28]); +	cc_parse_int(&telem->flight_pres, words[30]); +	cc_parse_int(&telem->ground_pres, words[32]); +	cc_parse_int(&telem->gps.nsat, words[34]); +	if (strcmp (words[36], "unlocked") == 0) { +		telem->gps.gps_connected = 1; +		telem->gps.gps_locked = 0; +		telem->gps.gps_time.hour = telem->gps.gps_time.minute = telem->gps.gps_time.second = 0; +		telem->gps.lat = telem->gps.lon = 0; +		telem->gps.alt = 0; +		tracking_pos = 37; +	} else if (nword >= 40) { +		telem->gps.gps_locked = 1; +		telem->gps.gps_connected = 1; +		sscanf(words[36], "%d:%d:%d", &telem->gps.gps_time.hour, &telem->gps.gps_time.minute, &telem->gps.gps_time.second); +		cc_parse_pos(&telem->gps.lat, words[37]); +		cc_parse_pos(&telem->gps.lon, words[38]); +		sscanf(words[39], "%dm", &telem->gps.alt); +		tracking_pos = 46; +	} else { +		telem->gps.gps_connected = 0; +		telem->gps.gps_locked = 0; +		telem->gps.gps_time.hour = telem->gps.gps_time.minute = telem->gps.gps_time.second = 0; +		telem->gps.lat = telem->gps.lon = 0; +		telem->gps.alt = 0; +		tracking_pos = -1; +	} +	if (nword >= 46) { +		telem->gps.gps_extended = 1; +		sscanf(words[40], "%lfm/s", &telem->gps.ground_speed); +		sscanf(words[41], "%d", &telem->gps.course); +		sscanf(words[42], "%lfm/s", &telem->gps.climb_rate); +		sscanf(words[43], "%lf", &telem->gps.hdop); +		sscanf(words[44], "%d", &telem->gps.h_error); +		sscanf(words[45], "%d", &telem->gps.v_error); +	} else { +		telem->gps.gps_extended = 0; +		telem->gps.ground_speed = 0; +		telem->gps.course = 0; +		telem->gps.climb_rate = 0; +		telem->gps.hdop = 0; +		telem->gps.h_error = 0; +		telem->gps.v_error = 0; +	} +	if (tracking_pos >= 0 && nword >= tracking_pos + 2 && strcmp(words[tracking_pos], "SAT") == 0) { +		int	c, n, pos; +		cc_parse_int(&n, words[tracking_pos + 1]); +		pos = tracking_pos + 2; +		if (nword >= pos + n * 3) { +			telem->gps_tracking.channels = n; +			for (c = 0; c < n; c++) { +				cc_parse_int(&telem->gps_tracking.sats[c].svid, +						 words[pos + 0]); +				cc_parse_hex(&telem->gps_tracking.sats[c].state, +						 words[pos + 1]); +				cc_parse_int(&telem->gps_tracking.sats[c].c_n0, +						 words[pos + 2]); +				pos += 3; +			} +		} else { +			telem->gps_tracking.channels = 0; +		} +	} else { +		telem->gps_tracking.channels = 0; +	} +	return TRUE; +} diff --git a/ao-tools/lib/cc-util.c b/ao-tools/lib/cc-util.c index 7104470c..65488ee9 100644 --- a/ao-tools/lib/cc-util.c +++ b/ao-tools/lib/cc-util.c @@ -15,8 +15,8 @@   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.   */ -#include "cc.h"  #define _GNU_SOURCE +#include "cc.h"  #include <string.h>  #include <stdlib.h>  #include <unistd.h> diff --git a/ao-tools/lib/cc.h b/ao-tools/lib/cc.h index f92a29f7..3975cf1b 100644 --- a/ao-tools/lib/cc.h +++ b/ao-tools/lib/cc.h @@ -18,6 +18,8 @@  #ifndef _CC_H_  #define _CC_H_ +#include <stdio.h> +  char *  cc_fullname (char *dir, char *file); @@ -60,4 +62,209 @@ cc_get_log_dir(void);  char *  cc_make_filename(int serial, char *ext); +/* + * For sequential data which are not evenly spaced + */ + +struct cc_timedataelt { +	double	time; +	double	value; +}; + +struct cc_timedata { +	int			num; +	int			size; +	struct cc_timedataelt	*data; +}; + + +/* + * For GPS data + */ + +struct cc_gpselt { +	double		time; +	double		lat; +	double		lon; +	double		alt; +}; + +struct cc_gpsdata { +	int			num; +	int			size; +	double			time_offset; +	struct cc_gpselt	*data; +}; + +/* + * For sequential data which are evenly spaced + */ +struct cc_perioddata { +	int		num; +	double		start; +	double		step; +	double		*data; +}; + +enum ao_flight_state { +	ao_flight_startup = 0, +	ao_flight_idle = 1, +	ao_flight_pad = 2, +	ao_flight_boost = 3, +	ao_flight_fast = 4, +	ao_flight_coast = 5, +	ao_flight_drogue = 6, +	ao_flight_main = 7, +	ao_flight_landed = 8, +	ao_flight_invalid = 9 +}; + +struct cc_flightraw { +	int			flight; +	int			serial; +	double			ground_accel; +	double			ground_pres; +	struct cc_timedata	accel; +	struct cc_timedata	pres; +	struct cc_timedata	temp; +	struct cc_timedata	volt; +	struct cc_timedata	main; +	struct cc_timedata	drogue; +	struct cc_timedata	state; +	struct cc_gpsdata	gps; +}; + +struct cc_flightraw * +cc_log_read(FILE *file); + +void +cc_flightraw_free(struct cc_flightraw *raw); + +struct cc_flightcooked { +	struct cc_perioddata	accel_accel; +	struct cc_perioddata	accel_speed; +	struct cc_perioddata	accel_pos; +	struct cc_perioddata	pres_pos; +	struct cc_perioddata	pres_speed; +	struct cc_perioddata	pres_accel; +	struct cc_perioddata	gps_lat; +	struct cc_perioddata	gps_lon; +	struct cc_perioddata	gps_alt; +	struct cc_timedata	state; +}; + +/* + * Telemetry data contents + */ + + +struct cc_gps_time { +	int hour; +	int minute; +	int second; +}; + +struct cc_gps { +	int	nsat; +	int	gps_locked; +	int	gps_connected; +	struct cc_gps_time gps_time; +	double	lat;		/* degrees (+N -S) */ +	double	lon;		/* degrees (+E -W) */ +	int	alt;		/* m */ + +	int	gps_extended;	/* has extra data */ +	double	ground_speed;	/* m/s */ +	int	course;		/* degrees */ +	double	climb_rate;	/* m/s */ +	double	hdop;		/* unitless? */ +	int	h_error;	/* m */ +	int	v_error;	/* m */ +}; + +#define SIRF_SAT_STATE_ACQUIRED			(1 << 0) +#define SIRF_SAT_STATE_CARRIER_PHASE_VALID	(1 << 1) +#define SIRF_SAT_BIT_SYNC_COMPLETE		(1 << 2) +#define SIRF_SAT_SUBFRAME_SYNC_COMPLETE		(1 << 3) +#define SIRF_SAT_CARRIER_PULLIN_COMPLETE	(1 << 4) +#define SIRF_SAT_CODE_LOCKED			(1 << 5) +#define SIRF_SAT_ACQUISITION_FAILED		(1 << 6) +#define SIRF_SAT_EPHEMERIS_AVAILABLE		(1 << 7) + +struct cc_gps_sat { +	int	svid; +	int	state; +	int	c_n0; +}; + +struct cc_gps_tracking { +	int			channels; +	struct cc_gps_sat	sats[12]; +}; + +struct cc_telem { +	char	callsign[16]; +	int	serial; +	int	rssi; +	char	state[16]; +	int	tick; +	int	accel; +	int	pres; +	int	temp; +	int	batt; +	int	drogue; +	int	main; +	int	flight_accel; +	int	ground_accel; +	int	flight_vel; +	int	flight_pres; +	int	ground_pres; +	struct cc_gps	gps; +	struct cc_gps_tracking	gps_tracking; +}; + +int +cc_telem_parse(const char *input_line, struct cc_telem *telem); + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +/* Conversion functions */ +double +cc_pressure_to_altitude(double pressure); + +double +cc_altitude_to_pressure(double altitude); + +double +cc_barometer_to_pressure(double baro); + +double +cc_barometer_to_altitude(double baro); + +double +cc_accelerometer_to_acceleration(double accel, double ground_accel); + +double +cc_thermometer_to_temperature(double thermo); + +double +cc_battery_to_voltage(double battery); + +double +cc_ignitor_to_voltage(double ignite); + +void +cc_great_circle (double start_lat, double start_lon, +		 double end_lat, double end_lon, +		 double *dist, double *bearing); + +int +cc_timedata_min(struct cc_timedata *d, double min_time, double max_time); + +int +cc_timedata_max(struct cc_timedata *d, double min_time, double max_time); +  #endif /* _CC_H_ */ diff --git a/configure.ac b/configure.ac index 73a33ac3..c668df04 100644 --- a/configure.ac +++ b/configure.ac @@ -82,6 +82,7 @@ ao-tools/ao-bitbang/Makefile  ao-tools/ao-eeprom/Makefile  ao-tools/ao-list/Makefile  ao-tools/ao-load/Makefile +ao-tools/ao-postflight/Makefile  ao-tools/ao-view/Makefile  ao-utils/Makefile  ]) | 
