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
-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
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);
--- /dev/null
+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
--- /dev/null
+.\"
+.\" 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
--- /dev/null
+/*
+ * 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;
+}
ccdbg-memory.c \
ccdbg-rom.c \
ccdbg-state.c \
+ cc-analyse.c \
+ cc-convert.c \
cc-log.c \
cc-usb.c \
cc-usb.h \
cc-util.c \
cc-bitbang.c \
cc-bitbang.h \
+ cc-logfile.c \
+ cc-telem.c \
cp-usb-async.c \
cp-usb-async.h
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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++;
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
* 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>
#ifndef _CC_H_
#define _CC_H_
+#include <stdio.h>
+
char *
cc_fullname (char *dir, char *file);
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_ */
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
])