altos/test: Add fake flight creation tool fake-flight
authorKeith Packard <keithp@keithp.com>
Mon, 18 Sep 2017 16:56:14 +0000 (09:56 -0700)
committerKeith Packard <keithp@keithp.com>
Mon, 18 Sep 2017 16:59:44 +0000 (09:59 -0700)
This reads eeprom data and generates a fake flight record

Signed-off-by: Keith Packard <keithp@keithp.com>
src/test/Makefile
src/test/ao_eeprom_read.c
src/test/ao_flight_test.c
src/test/ao_make_fake.c [new file with mode: 0644]

index 088084303edc74cea9d6ff108e12a803aa63daa8..308ecc729cf569f9b390a6104f59952549c342b6 100644 (file)
@@ -4,12 +4,16 @@ PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noi
        ao_flight_test_metrum ao_flight_test_mini \
        ao_gps_test ao_gps_test_skytraq ao_gps_test_ublox ao_convert_test ao_convert_pa_test ao_fec_test \
        ao_aprs_test ao_micropeak_test ao_fat_test ao_aes_test ao_int64_test \
-       ao_ms5607_convert_test ao_quaternion_test ao_lisp_test
+       ao_ms5607_convert_test ao_quaternion_test ao_lisp_test \
+       ao_make_fake_mm
 
 INCS=ao_kalman.h ao_ms5607.h ao_log.h ao_data.h altitude-pa.h altitude.h ao_quaternion.h ao_eeprom_read.h
 TEST_SRC=ao_flight_test.c
 TEST_SRC_ALL=ao_flight_test.c ao_eeprom_read.c ao_eeprom_read_old.c
 TEST_LIB=-ljson-c
+FAKE_SRC_ALL=ao_make_fake.c ao_eeprom_read.c ao_eeprom_read_old.c
+FAKE_SRC=ao_make_fake.c
+FAKE_LIB=-ljson-c
 
 KALMAN=make-kalman 
 
@@ -43,6 +47,9 @@ ao_flight_test_metrum: $(TEST_SRC_ALL) ao_host.h ao_flight.c ao_sample.c ao_kalm
 ao_flight_test_mini: $(TEST_SRC_ALL) ao_host.h ao_flight.c ao_sample.c ao_kalman.c ao_pyro.c ao_pyro.h $(INCS)
        cc -DEASYMINI=1 -DHAS_ACCEL=0 $(CFLAGS) -o $@ $(TEST_SRC)  $(TEST_LIB) -lm
 
+ao_make_fake_mm: $(FAKE_SRC_ALL) ao_host.h ao_flight.c ao_sample.c ao_kalman.c ao_pyro.c ao_pyro.h $(INCS)
+       cc -DTELEMEGA=1 $(CFLAGS) -o $@ $(FAKE_SRC) $(FAKE_LIB) -lm
+
 ao_gps_test: ao_gps_test.c ao_gps_sirf.c ao_gps_print.c ao_host.h
        cc $(CFLAGS) -o $@ $<
 
index 803ef36cae27011f0c7d9bb663e684f51692209f..e5d76fa3ebee5b5494643ec0d8e2b4bc27889367 100644 (file)
@@ -217,13 +217,11 @@ ao_eeprom_get_config(struct ao_eeprom *ao_eeprom, struct json_object *obj)
        if (!ao_eeprom_get_pyro(config, obj))
                return 0;
 #endif
-       config->ignite_mode =           ao_json_get_int(obj, "ignite_mode", 0);
-       config->ignite_mode =           ao_json_get_int(obj, "ignite_mode", 0);
-       config->ignite_mode =           ao_json_get_int(obj, "ignite_mode", 0);
-       config->ignite_mode =           ao_json_get_int(obj, "ignite_mode", 0);
-       config->ignite_mode =           ao_json_get_int(obj, "ignite_mode", 0);
-       config->ignite_mode =           ao_json_get_int(obj, "ignite_mode", 0);
-
+#if HAS_GYRO
+       config->accel_zero_along =      ao_json_get_int(obj, "accel_zero_along", 0);
+       config->accel_zero_across =     ao_json_get_int(obj, "accel_zero_across", 0);
+       config->accel_zero_through =    ao_json_get_int(obj, "accel_zero_through", 0);
+#endif
        if (!ao_eeprom_get_ms5607(&ao_eeprom->ms5607_prom, obj))
                return 0;
 
index 298848d6a46b4a7921bd8fddb9ba4605020e3e25..cc46198346f4a4b2643756154ed0e6425e8bcca5 100644 (file)
@@ -774,10 +774,11 @@ ao_sleep(void *wchan)
 #endif
 
                                if (eeprom_offset >= eeprom->len) {
-                                       if (++ao_eof_read >= 1000)
+                                       if (++ao_eof_read >= 1000) {
                                                if (!ao_summary)
                                                        printf ("no more data, exiting simulation\n");
-                                       ao_test_exit();
+                                               ao_test_exit();
+                                       }
                                        ao_data_static.tick += 10;
                                        ao_insert();
                                        return;
diff --git a/src/test/ao_make_fake.c b/src/test/ao_make_fake.c
new file mode 100644 (file)
index 0000000..111a4ff
--- /dev/null
@@ -0,0 +1,779 @@
+/*
+ * 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <getopt.h>
+#include <math.h>
+
+#define GRAVITY 9.80665
+
+#define AO_HERTZ       100
+
+#define HAS_ADC 1
+#define AO_DATA_RING   64
+#define ao_data_ring_next(n)   (((n) + 1) & (AO_DATA_RING - 1))
+#define ao_data_ring_prev(n)   (((n) - 1) & (AO_DATA_RING - 1))
+
+#define AO_GPS_NEW_DATA                1
+#define AO_GPS_NEW_TRACKING    2
+
+int ao_gps_new;
+
+#if !defined(TELEMEGA) && !defined(TELEMETRUM_V2) && !defined(EASYMINI)
+#define TELEMETRUM_V1 1
+#endif
+
+#if TELEMEGA
+#define AO_ADC_NUM_SENSE       6
+#define HAS_MS5607             1
+#define HAS_MPU6000            1
+#define HAS_MMA655X            1
+#define HAS_HMC5883            1
+#define HAS_BEEP               1
+#define AO_CONFIG_MAX_SIZE     1024
+#define AO_MMA655X_INVERT      0
+
+struct ao_adc {
+       int16_t                 sense[AO_ADC_NUM_SENSE];
+       int16_t                 v_batt;
+       int16_t                 v_pbatt;
+       int16_t                 temp;
+};
+#endif
+
+#if TELEMETRUM_V2
+#define AO_ADC_NUM_SENSE       2
+#define HAS_MS5607             1
+#define HAS_MMA655X            1
+#define AO_MMA655X_INVERT      0
+#define HAS_BEEP               1
+#define AO_CONFIG_MAX_SIZE     1024
+
+struct ao_adc {
+       int16_t                 sense_a;
+       int16_t                 sense_m;
+       int16_t                 v_batt;
+       int16_t                 temp;
+};
+#endif
+
+#if EASYMINI
+#define AO_ADC_NUM_SENSE       2
+#define HAS_MS5607             1
+#define HAS_BEEP               1
+#define AO_CONFIG_MAX_SIZE     1024
+
+struct ao_adc {
+       int16_t                 sense_a;
+       int16_t                 sense_m;
+       int16_t                 v_batt;
+};
+#endif
+
+#if TELEMETRUM_V1
+/*
+ * One set of samples read from the A/D converter
+ */
+struct ao_adc {
+       int16_t         accel;          /* accelerometer */
+       int16_t         pres;           /* pressure sensor */
+       int16_t         pres_real;      /* unclipped */
+       int16_t         temp;           /* temperature sensor */
+       int16_t         v_batt;         /* battery voltage */
+       int16_t         sense_d;        /* drogue continuity sense */
+       int16_t         sense_m;        /* main continuity sense */
+};
+
+#ifndef HAS_ACCEL
+#define HAS_ACCEL 1
+#define HAS_ACCEL_REF 0
+#endif
+
+#endif
+
+#define __pdata
+#define __data
+#define __xdata
+#define __code
+#define __reentrant
+
+#define HAS_FLIGHT 1
+#define HAS_IGNITE 1
+#define HAS_USB 1
+#define HAS_GPS 1
+
+#include <ao_data.h>
+#include <ao_log.h>
+#include <ao_telemetry.h>
+#include <ao_sample.h>
+#include <ao_fake_flight.h>
+
+#if TELEMEGA
+int ao_gps_count;
+struct ao_telemetry_location ao_gps_first;
+struct ao_telemetry_location ao_gps_prev;
+struct ao_telemetry_location ao_gps_static;
+
+struct ao_telemetry_satellite ao_gps_tracking;
+
+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;
+}
+
+double
+ao_distance_from_pad(void)
+{
+       double  dist, bearing;
+       if (!ao_gps_count)
+               return 0;
+
+       cc_great_circle(ao_gps_first.latitude / 1e7,
+                       ao_gps_first.longitude / 1e7,
+                       ao_gps_static.latitude / 1e7,
+                       ao_gps_static.longitude / 1e7,
+                       &dist, &bearing);
+       return dist;
+}
+
+double
+ao_gps_angle(void)
+{
+       double  dist, bearing;
+       double  height;
+       double  angle;
+
+       if (ao_gps_count < 2)
+               return 0;
+
+       cc_great_circle(ao_gps_prev.latitude / 1e7,
+                       ao_gps_prev.longitude / 1e7,
+                       ao_gps_static.latitude / 1e7,
+                       ao_gps_static.longitude / 1e7,
+                       &dist, &bearing);
+       height = AO_TELEMETRY_LOCATION_ALTITUDE(&ao_gps_static) - AO_TELEMETRY_LOCATION_ALTITUDE(&ao_gps_prev);
+
+       angle = atan2(dist, height);
+       return angle * 180/M_PI;
+}
+#endif
+
+#define to_fix16(x) ((int16_t) ((x) * 65536.0 + 0.5))
+#define to_fix32(x) ((int32_t) ((x) * 65536.0 + 0.5))
+#define from_fix(x)    ((x) >> 16)
+
+#define ACCEL_NOSE_UP  (ao_accel_2g >> 2)
+
+#define FALSE 0
+#define TRUE 1
+
+volatile struct ao_data ao_data_ring[AO_DATA_RING];
+volatile uint8_t ao_data_head;
+int    ao_summary = 0;
+
+#define ao_led_on(l)
+#define ao_led_off(l)
+#define ao_timer_set_adc_interval(i)
+#define ao_wakeup(wchan) ao_dump_state()
+#define ao_cmd_register(c)
+#define ao_usb_disable()
+#define ao_telemetry_set_interval(x)
+#define ao_rdf_set(rdf)
+#define ao_packet_slave_start()
+#define ao_packet_slave_stop()
+#define flush()
+
+enum ao_igniter {
+       ao_igniter_drogue = 0,
+       ao_igniter_main = 1
+};
+
+struct ao_data ao_data_static;
+
+int    drogue_height;
+double drogue_time;
+int    main_height;
+double main_time;
+
+int    tick_offset;
+
+static ao_k_t  ao_k_height;
+
+int16_t
+ao_time(void)
+{
+       return ao_data_static.tick;
+}
+
+void
+ao_delay(int16_t interval)
+{
+       return;
+}
+
+void
+ao_ignite(enum ao_igniter igniter)
+{
+       double time = (double) (ao_data_static.tick + tick_offset) / 100;
+
+       if (igniter == ao_igniter_drogue) {
+               drogue_time = time;
+               drogue_height = ao_k_height >> 16;
+       } else {
+               main_time = time;
+               main_height = ao_k_height >> 16;
+       }
+}
+
+struct ao_task {
+       int dummy;
+};
+
+#define ao_add_task(t,f,n) ((void) (t))
+
+#define ao_log_start()
+#define ao_log_stop()
+
+#define AO_MS_TO_TICKS(ms)     ((ms) / 10)
+#define AO_SEC_TO_TICKS(s)     ((s) * 100)
+
+#define AO_FLIGHT_TEST
+
+int    ao_flight_debug;
+
+struct ao_eeprom       *eeprom;
+uint32_t               eeprom_offset;
+
+FILE *emulator_in;
+FILE *fake_out;
+char *emulator_app;
+char *emulator_name;
+char *emulator_info;
+double emulator_error_max = 4;
+double emulator_height_error_max = 20; /* noise in the baro sensor */
+
+void
+ao_dump_state(void);
+
+void
+ao_sleep(void *wchan);
+
+const char * const ao_state_names[] = {
+       "startup", "idle", "pad", "boost", "fast",
+       "coast", "drogue", "main", "landed", "invalid"
+};
+
+struct ao_cmds {
+       void            (*func)(void);
+       const char      *help;
+};
+
+#define ao_xmemcpy(d,s,c) memcpy(d,s,c)
+#define ao_xmemset(d,v,c) memset(d,v,c)
+#define ao_xmemcmp(d,s,c) memcmp(d,s,c)
+
+#define AO_NEED_ALTITUDE_TO_PRES 1
+#if TELEMEGA || TELEMETRUM_V2 || EASYMINI
+#include "ao_convert_pa.c"
+#include <ao_ms5607.h>
+struct ao_ms5607_prom  ao_ms5607_prom;
+#include "ao_ms5607_convert.c"
+#if TELEMEGA
+#define AO_PYRO_NUM    4
+#include <ao_pyro.h>
+#endif
+#else
+#include "ao_convert.c"
+#endif
+
+#include <ao_config.h>
+#include <ao_fake_flight.h>
+#include <ao_eeprom_read.h>
+#include <ao_log.h>
+
+#define ao_config_get()
+
+struct ao_config ao_config;
+
+#define DATA_TO_XDATA(x) (x)
+
+
+extern int16_t ao_ground_accel, ao_flight_accel;
+extern int16_t ao_accel_2g;
+
+typedef int16_t        accel_t;
+
+uint16_t       ao_serial_number;
+uint16_t       ao_flight_number;
+
+extern uint16_t        ao_sample_tick;
+
+extern alt_t   ao_sample_height;
+extern accel_t ao_sample_accel;
+extern int32_t ao_accel_scale;
+extern alt_t   ao_ground_height;
+extern alt_t   ao_sample_alt;
+
+double ao_sample_qangle;
+
+int ao_sample_prev_tick;
+uint16_t       prev_tick;
+
+#include "ao_kalman.c"
+#include "ao_sqrt.c"
+#include "ao_sample.c"
+#include "ao_flight.c"
+#if TELEMEGA
+#define AO_PYRO_NUM    4
+
+#define AO_PYRO_0      0
+#define AO_PYRO_1      1
+#define AO_PYRO_2      2
+#define AO_PYRO_3      3
+
+#define PYRO_DBG       1
+#endif
+#include "ao_eeprom_read.c"
+#include "ao_eeprom_read_old.c"
+
+#define to_double(f)   ((f) / 65536.0)
+
+static int     ao_records_read = 0;
+static int     ao_eof_read = 0;
+#if !EASYMINI
+static int     ao_flight_ground_accel;
+#endif
+static int     ao_flight_started = 0;
+
+#if HAS_MPU6000
+static struct ao_mpu6000_sample        ao_ground_mpu6000;
+#endif
+
+#if HAS_ACCEL
+int ao_error_h_sq_avg;
+#endif
+
+void
+ao_test_exit(void)
+{
+       putc(0, fake_out);
+       fflush(fake_out);
+       exit(0);
+}
+
+void
+ao_insert(void)
+{
+       putc(1, fake_out);
+       fwrite(&ao_data_static, sizeof (ao_data_static), 1, fake_out);
+}
+
+
+uint16_t
+uint16(uint8_t *bytes, int off)
+{
+       return (uint16_t) bytes[off] | (((uint16_t) bytes[off+1]) << 8);
+}
+
+int16_t
+int16(uint8_t *bytes, int off)
+{
+       return (int16_t) uint16(bytes, off);
+}
+
+uint32_t
+uint32(uint8_t *bytes, int off)
+{
+       return (uint32_t) bytes[off] | (((uint32_t) bytes[off+1]) << 8) |
+               (((uint32_t) bytes[off+2]) << 16) |
+               (((uint32_t) bytes[off+3]) << 24);
+}
+
+int32_t
+int32(uint8_t *bytes, int off)
+{
+       return (int32_t) uint32(bytes, off);
+}
+
+uint32_t
+uint24(uint8_t *bytes, int off)
+{
+       return (uint32_t) bytes[off] | (((uint32_t) bytes[off+1]) << 8) |
+               (((uint32_t) bytes[off+2]) << 16);
+}
+
+int32_t
+int24(uint8_t *bytes, int off)
+{
+       return (int32_t) uint24(bytes, off);
+}
+
+static int log_format;
+
+void
+ao_sleep(void *wchan)
+{
+}
+
+void fake_read(void)
+{
+       if (ao_records_read > 2 && ao_records_read < 500)
+       {
+#if TELEMEGA
+               ao_data_static.mpu6000 = ao_ground_mpu6000;
+#endif
+#if TELEMETRUM_V1
+               ao_data_static.adc.accel = ao_flight_ground_accel;
+#endif
+               ++ao_records_read;
+               ao_insert();
+               return;
+       }
+
+       if (eeprom) {
+#if TELEMEGA
+               struct ao_log_mega      *log_mega;
+#endif
+#if TELEMETRUM_V2
+               struct ao_log_metrum    *log_metrum;
+#endif
+#if EASYMINI
+               struct ao_log_mini      *log_mini;
+#endif
+#if TELEMETRUM_V1
+               struct ao_log_record    *log_record;
+#endif
+
+               if (eeprom_offset >= eeprom->len) {
+                       if (++ao_eof_read >= 100) {
+                               ao_test_exit();
+                       }
+                       ao_data_static.tick += 10;
+                       ao_insert();
+                       return;
+               }
+               switch (eeprom->log_format) {
+#if TELEMEGA
+               case AO_LOG_FORMAT_TELEMEGA_OLD:
+               case AO_LOG_FORMAT_TELEMEGA:
+                       log_mega = (struct ao_log_mega *) &eeprom->data[eeprom_offset];
+                       eeprom_offset += sizeof (*log_mega);
+                       switch (log_mega->type) {
+                       case AO_LOG_FLIGHT:
+                               ao_flight_number = log_mega->u.flight.flight;
+                               ao_flight_ground_accel = log_mega->u.flight.ground_accel;
+                               ao_flight_started = 1;
+                               ao_ground_pres = log_mega->u.flight.ground_pres;
+                               ao_ground_height = ao_pa_to_altitude(ao_ground_pres);
+                               ao_ground_accel_along = log_mega->u.flight.ground_accel_along;
+                               ao_ground_accel_across = log_mega->u.flight.ground_accel_across;
+                               ao_ground_accel_through = log_mega->u.flight.ground_accel_through;
+                               ao_ground_roll = log_mega->u.flight.ground_roll;
+                               ao_ground_pitch = log_mega->u.flight.ground_pitch;
+                               ao_ground_yaw = log_mega->u.flight.ground_yaw;
+                               ao_ground_mpu6000.accel_x = ao_ground_accel_across;
+                               ao_ground_mpu6000.accel_y = ao_ground_accel_along;
+                               ao_ground_mpu6000.accel_z = ao_ground_accel_through;
+                               ao_ground_mpu6000.gyro_x = ao_ground_pitch >> 9;
+                               ao_ground_mpu6000.gyro_y = ao_ground_roll >> 9;
+                               ao_ground_mpu6000.gyro_z = ao_ground_yaw >> 9;
+                               break;
+                       case AO_LOG_STATE:
+                               break;
+                       case AO_LOG_SENSOR:
+                               ao_data_static.tick = log_mega->tick;
+                               ao_data_static.ms5607_raw.pres = log_mega->u.sensor.pres;
+                               ao_data_static.ms5607_raw.temp = log_mega->u.sensor.temp;
+                               ao_data_static.mpu6000.accel_x = log_mega->u.sensor.accel_x;
+                               ao_data_static.mpu6000.accel_y = log_mega->u.sensor.accel_y;
+                               ao_data_static.mpu6000.accel_z = log_mega->u.sensor.accel_z;
+                               ao_data_static.mpu6000.gyro_x = log_mega->u.sensor.gyro_x;
+                               ao_data_static.mpu6000.gyro_y = log_mega->u.sensor.gyro_y;
+                               ao_data_static.mpu6000.gyro_z = log_mega->u.sensor.gyro_z;
+                               ao_data_static.hmc5883.x = log_mega->u.sensor.mag_x;
+                               ao_data_static.hmc5883.y = log_mega->u.sensor.mag_y;
+                               ao_data_static.hmc5883.z = log_mega->u.sensor.mag_z;
+                               ao_data_static.mma655x = log_mega->u.sensor.accel;
+                               if (ao_config.pad_orientation != AO_PAD_ORIENTATION_ANTENNA_UP)
+                                       ao_data_static.mma655x = ao_data_accel_invert(ao_data_static.mma655x);
+                               ao_records_read++;
+                               ao_insert();
+                               return;
+                       case AO_LOG_TEMP_VOLT:
+                               break;
+                       case AO_LOG_GPS_TIME:
+                               ao_gps_prev = ao_gps_static;
+                               ao_gps_static.tick = log_mega->tick;
+                               ao_gps_static.latitude = log_mega->u.gps.latitude;
+                               ao_gps_static.longitude = log_mega->u.gps.longitude;
+                               {
+                                       int16_t altitude_low = log_mega->u.gps.altitude_low;
+                                       int16_t altitude_high = log_mega->u.gps.altitude_high;
+                                       int32_t altitude = altitude_low | ((int32_t) altitude_high << 16);
+
+                                       AO_TELEMETRY_LOCATION_SET_ALTITUDE(&ao_gps_static, altitude);
+                               }
+                               ao_gps_static.flags = log_mega->u.gps.flags;
+                               if (!ao_gps_count)
+                                       ao_gps_first = ao_gps_static;
+                               ao_gps_count++;
+                               break;
+                       case AO_LOG_GPS_SAT:
+                               break;
+                       }
+                       break;
+#endif
+#if TELEMETRUM_V2
+               case AO_LOG_FORMAT_TELEMETRUM:
+                       log_metrum = (struct ao_log_metrum *) &eeprom->data[eeprom_offset];
+                       eeprom_offset += sizeof (*log_metrum);
+                       switch (log_metrum->type) {
+                       case AO_LOG_FLIGHT:
+                               ao_flight_started = 1;
+                               ao_flight_number = log_metrum->u.flight.flight;
+                               ao_flight_ground_accel = log_metrum->u.flight.ground_accel;
+                               ao_ground_pres = log_metrum->u.flight.ground_pres;
+                               ao_ground_height = ao_pa_to_altitude(ao_ground_pres);
+                               break;
+                       case AO_LOG_SENSOR:
+                               ao_data_static.tick = log_metrum->tick;
+                               ao_data_static.ms5607_raw.pres = log_metrum->u.sensor.pres;
+                               ao_data_static.ms5607_raw.temp = log_metrum->u.sensor.temp;
+                               ao_data_static.mma655x = log_metrum->u.sensor.accel;
+                               ao_records_read++;
+                               ao_insert();
+                               return;
+                       }
+                       break;
+#endif
+#if EASYMINI
+               case AO_LOG_FORMAT_EASYMINI1:
+               case AO_LOG_FORMAT_EASYMINI2:
+               case AO_LOG_FORMAT_TELEMINI3:
+                       log_mini = (struct ao_log_mini *) &eeprom->data[eeprom_offset];
+                       eeprom_offset += sizeof (*log_mini);
+                       switch (log_mini->type) {
+                       case AO_LOG_FLIGHT:
+                               ao_flight_started = 1;
+                               ao_flight_number = log_mini->u.flight.flight;
+                               ao_ground_pres = log_mini->u.flight.ground_pres;
+                               ao_ground_height = ao_pa_to_altitude(ao_ground_pres);
+                               break;
+                       case AO_LOG_SENSOR:
+                               ao_data_static.tick = log_mini->tick;
+                               ao_data_static.ms5607_raw.pres = int24(log_mini->u.sensor.pres, 0);
+                               ao_data_static.ms5607_raw.temp = int24(log_mini->u.sensor.temp, 0);
+                               ao_records_read++;
+                               ao_insert();
+                               return;
+                       }
+                       break;
+#endif
+#if TELEMETRUM_V1
+               case AO_LOG_FORMAT_FULL:
+               case AO_LOG_FORMAT_TINY:
+                       log_record = (struct ao_log_record *) &eeprom->data[eeprom_offset];
+                       eeprom_offset += sizeof (*log_record);
+                       switch (log_record->type) {
+                       case AO_LOG_FLIGHT:
+                               ao_flight_started = 1;
+                               ao_flight_ground_accel = log_record->u.flight.ground_accel;
+                               ao_flight_number = log_record->u.flight.flight;
+                               break;
+                       case AO_LOG_SENSOR:
+                       case 'P':       /* ancient telemini */
+                               ao_data_static.tick = log_record->tick;
+                               ao_data_static.adc.accel = log_record->u.sensor.accel;
+                               ao_data_static.adc.pres_real = log_record->u.sensor.pres;
+                               ao_data_static.adc.pres = log_record->u.sensor.pres;
+                               ao_records_read++;
+                               ao_insert();
+                               return;
+                       case AO_LOG_TEMP_VOLT:
+                               ao_data_static.tick = log_record->tick;;
+                               ao_data_static.adc.temp = log_record->u.temp_volt.temp;
+                               ao_data_static.adc.v_batt = log_record->u.temp_volt.v_batt;
+                               break;
+                       }
+                       break;
+#endif
+               default:
+                       fprintf (stderr, "invalid log format %d\n", log_format);
+                       ao_test_exit();
+               }
+       }
+}
+#define COUNTS_PER_G 264.8
+
+void
+ao_dump_state(void)
+{
+}
+
+static const struct option options[] = {
+       { .name = "summary", .has_arg = 0, .val = 's' },
+       { .name = "debug", .has_arg = 0, .val = 'd' },
+       { .name = "info", .has_arg = 1, .val = 'i' },
+       { .name = "out", .has_arg = 1, .val = 'o' },
+       { 0, 0, 0, 0},
+};
+
+void run_flight_fixed(char *name, FILE *f, int summary, char *info)
+{
+       char                    c;
+       struct ao_fake_calib    fake_calib;
+
+       emulator_name = name;
+       emulator_in = f;
+       emulator_info = info;
+       ao_summary = summary;
+
+       c = getc(f);
+       ungetc(c, f);
+       if (c == '{')
+               eeprom = ao_eeprom_read(f);
+       else
+               eeprom = ao_eeprom_read_old(f);
+
+       if (!eeprom) {
+               fprintf(stderr, "%s: load failed\n", name);
+               exit(1);
+       }
+#if HAS_MS5607
+       ao_ms5607_prom = eeprom->ms5607_prom;
+#endif
+       ao_config = eeprom->config;
+       ao_serial_number = eeprom->serial_number;
+       log_format = eeprom->log_format;
+
+       fprintf(fake_out, "F %x %x\n",
+              (int) sizeof (struct ao_fake_calib),
+              (int) sizeof (struct ao_data));
+
+       fake_calib.major = AO_FAKE_CALIB_MAJOR;
+       fake_calib.minor = AO_FAKE_CALIB_MINOR;
+       fake_calib.accel_plus_g = eeprom->config.accel_plus_g;
+       fake_calib.accel_minus_g = eeprom->config.accel_minus_g;
+       fake_calib.accel_zero_along = eeprom->config.accel_zero_along;
+       fake_calib.accel_zero_across = eeprom->config.accel_zero_across;
+       fake_calib.accel_zero_through = eeprom->config.accel_zero_through;
+       fake_calib.ms5607_prom = eeprom->ms5607_prom;
+       fwrite(&fake_calib, sizeof (fake_calib), 1, fake_out);
+       for (;;)
+               fake_read();
+}
+
+int
+main (int argc, char **argv)
+{
+       int     summary = 0;
+       int     c;
+       int     i;
+       char    *info = NULL;
+       char    *out_name = NULL;
+
+#if HAS_ACCEL
+       emulator_app="full";
+#else
+       emulator_app="baro";
+#endif
+       while ((c = getopt_long(argc, argv, "sdi:o:", options, NULL)) != -1) {
+               switch (c) {
+               case 's':
+                       summary = 1;
+                       break;
+               case 'd':
+                       ao_flight_debug = 1;
+                       break;
+               case 'i':
+                       info = optarg;
+                       break;
+               case 'o':
+                       out_name = optarg;
+                       break;
+               }
+       }
+       if (out_name) {
+               fake_out = fopen(out_name, "w");
+               if (!fake_out) {
+                       perror(out_name);
+                       exit(1);
+               }
+       }
+
+       if (!fake_out)
+               fake_out = stdout;
+
+       if (optind == argc)
+               run_flight_fixed("<stdin>", stdin, summary, info);
+       else
+               for (i = optind; i < argc; i++) {
+                       FILE    *f = fopen(argv[i], "r");
+                       if (!f) {
+                               perror(argv[i]);
+                               continue;
+                       }
+                       run_flight_fixed(argv[i], f, summary, info);
+                       fclose(f);
+               }
+       exit(0);
+}