summaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/.gitignore9
-rw-r--r--src/test/Makefile47
-rw-r--r--src/test/ao_fec_test.c386
-rw-r--r--src/test/ao_flight_test.c738
-rw-r--r--src/test/ao_gps_test.c513
-rw-r--r--src/test/ao_gps_test_skytraq.c496
-rwxr-xr-xsrc/test/plottest16
-rwxr-xr-xsrc/test/run-baro31
-rwxr-xr-xsrc/test/run-full31
-rwxr-xr-xsrc/test/run-noisy23
-rwxr-xr-xsrc/test/run-one47
-rwxr-xr-xsrc/test/run-tests27
-rw-r--r--src/test/test-flights70
13 files changed, 2434 insertions, 0 deletions
diff --git a/src/test/.gitignore b/src/test/.gitignore
new file mode 100644
index 00000000..5d528ab9
--- /dev/null
+++ b/src/test/.gitignore
@@ -0,0 +1,9 @@
+ao_flight_test
+ao_flight_test_baro
+ao_flight_test_accel
+ao_gps_test
+ao_gps_test_skytraq
+ao_convert_test
+ao_convert_pa_test
+ao_fec_test
+ao_flight_test_noisy_accel
diff --git a/src/test/Makefile b/src/test/Makefile
new file mode 100644
index 00000000..db3cc04b
--- /dev/null
+++ b/src/test/Makefile
@@ -0,0 +1,47 @@
+vpath % ..:../core:../drivers:../util
+
+PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test ao_fec_test
+
+KALMAN=make-kalman
+
+CFLAGS=-I.. -I. -I../core -I../drivers -O3 -g -Wall
+
+all: $(PROGS)
+
+clean:
+ rm -f $(PROGS) run-out.baro run-out.full
+
+install:
+
+ao_flight_test: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c altitude.h ao_kalman.h
+ cc $(CFLAGS) -o $@ $<
+
+ao_flight_test_noisy_accel: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c altitude.h ao_kalman.h
+ cc -DNOISY_ACCEL=1 $(CFLAGS) -o $@ $<
+
+ao_flight_test_baro: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c altitude.h ao_kalman.h
+ cc $(CFLAGS) -o $@ -DHAS_ACCEL=0 ao_flight_test.c
+
+ao_flight_test_accel: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c altitude.h ao_kalman.h
+ cc $(CFLAGS) -o $@ -DFORCE_ACCEL=1 ao_flight_test.c
+
+ao_gps_test: ao_gps_test.c ao_gps_sirf.c ao_gps_print.c ao_host.h
+ cc $(CFLAGS) -o $@ $<
+
+ao_gps_test_skytraq: ao_gps_test_skytraq.c ao_gps_skytraq.c ao_gps_print.c ao_host.h
+ cc $(CFLAGS) -o $@ $<
+
+ao_convert_test: ao_convert_test.c ao_convert.c altitude.h
+ cc $(CFLAGS) -o $@ $<
+
+ao_convert_pa_test: ao_convert_pa_test.c ao_convert_pa.c altitude-pa.h
+ cc $(CFLAGS) -o $@ $<
+
+ao_kalman.h: $(KALMAN)
+ (cd .. && make ao_kalman.h)
+
+ao_fec_test: ao_fec_test.c ao_fec_tx.c ao_fec_rx.c
+ cc $(CFLAGS) -DAO_FEC_DEBUG=1 -o $@ ao_fec_test.c ../core/ao_fec_tx.c ../core/ao_fec_rx.c -lm
+
+check: ao_fec_test ao_flight_test ao_flight_test_baro run-tests
+ ./ao_fec_test && ./run-tests \ No newline at end of file
diff --git a/src/test/ao_fec_test.c b/src/test/ao_fec_test.c
new file mode 100644
index 00000000..216a4b79
--- /dev/null
+++ b/src/test/ao_fec_test.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright © 2012 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 <ao_fec.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#ifndef RANDOM_MAX
+#define RANDOM_MAX 0x7fffffff
+#endif
+
+static double
+rand_real(void) {
+ return (double) random() / (double) RANDOM_MAX;
+}
+
+static double
+gaussian_random(double mean, double dev)
+{
+ static int save_x_valid = 0;
+ static double save_x;
+ double x;
+
+ if (save_x_valid)
+ {
+ x = save_x;
+ save_x_valid = 0;
+ }
+ else
+ {
+ double w;
+ double normal_x1, normal_x2;
+
+ do {
+ normal_x1 = 2 * rand_real () - 1;
+ normal_x2 = 2 * rand_real () - 1;
+ w = normal_x1*normal_x1 + normal_x2*normal_x2;
+ } while (w >= 1 || w < 1E-30);
+
+ w = sqrt(log(w)*(-2./w));
+
+ /*
+ * normal_x1 and normal_x2 are independent normally
+ * distributed variates
+ */
+
+ x = normal_x1 * w;
+ /* save normal_x2 for next call */
+ save_x = normal_x2 * w;
+ save_x_valid = 1;
+ }
+ return x * dev + mean;
+}
+
+#define PREPARE_LEN(input_len) ((input_len) + AO_FEC_PREPARE_EXTRA)
+#define ENCODE_LEN(input_len) (PREPARE_LEN(input_len) * 2)
+#define DECODE_LEN(input_len) ((input_len) + AO_FEC_PREPARE_EXTRA)
+#define EXPAND_LEN(input_len) (ENCODE_LEN(input_len) * 8)
+
+static uint8_t ao_bit(uint8_t b) {
+ if (b)
+ return 0x00;
+ return 0xff;
+}
+
+static int
+ao_expand(uint8_t *bits, int bits_len, uint8_t *bytes)
+{
+ int i, bit;
+ uint8_t b;
+
+ for (i = 0; i < bits_len; i++) {
+ b = bits[i];
+ for (bit = 7; bit >= 0; bit--)
+ *bytes++ = ao_bit ((b >> bit) & 1);
+ }
+
+ return bits_len * 8;
+}
+
+static int
+ao_fuzz (uint8_t *in, int in_len, uint8_t *out, double dev)
+{
+ int i;
+ int errors = 0;
+ int s;
+
+ for (i = 0; i < in_len; i++) {
+ double error = gaussian_random(0, dev);
+ uint8_t byte = in[i];
+
+ if (error > 0) {
+ if (error > 0xff)
+ error = 0xff;
+ if (error >= 0x80)
+ errors++;
+ if (byte < 0x80)
+ byte += error;
+ else
+ byte -= error;
+ }
+
+ /* abcd efgh 8
+ * abcd efga 7
+ * abcd efab 6
+ * abcd eabc 5
+ * abcd abcd 4
+ * abca bcab 3
+ * abab abab 2
+ * aaaa aaaa 1
+ */
+
+#define SAVE 8
+#define SAVE_MASK (((1 << SAVE) - 1) << (8 - SAVE))
+
+ byte &= SAVE_MASK;
+ for (s = SAVE; s < 8; s += SAVE)
+ byte |= byte >> s;
+
+ out[i] = byte;
+ }
+ return errors;
+}
+
+static uint8_t
+ao_random_data(uint8_t *out, uint8_t out_len)
+{
+ uint8_t len = random() % (out_len + 1);
+ uint8_t i;
+
+ for (i = 0; i < len; i++)
+ out[i] = random();
+ return len;
+}
+
+
+static uint8_t real_packet[] = {
+ 0x40, 0x38, 0xcd, 0x38, 0x3d, 0x34, 0xca, 0x31, 0xc3, 0xc1, 0xc6, 0x35, 0xcc, 0x3a, 0x3c,
+ 0x3c, 0x3d, 0x3c, 0x37, 0xc5, 0xc1, 0xc0, 0xc1, 0xc1, 0xc3, 0xc0, 0xc1, 0xc6, 0x38, 0x3b, 0xc6,
+ 0xc0, 0xc6, 0x32, 0xc9, 0xc9, 0x34, 0xcf, 0x35, 0xcf, 0x3a, 0x3b, 0xc6, 0xc7, 0x35, 0xcf, 0x36,
+ 0xce, 0x37, 0xc8, 0xc8, 0x3a, 0x3c, 0xc9, 0xc8, 0x3a, 0x3c, 0xcc, 0x32, 0xcd, 0x32, 0xce, 0x32,
+ 0xc9, 0xc6, 0x37, 0x3e, 0x3d, 0xc8, 0xc3, 0xc6, 0x38, 0x3d, 0xcf, 0x34, 0x3d, 0x3b, 0xcb, 0x31,
+ 0xca, 0x35, 0x38, 0xcb, 0x31, 0xcc, 0x31, 0x3b, 0xc7, 0xbf, 0xbe, 0xbe, 0xc3, 0x35, 0x38, 0xcb,
+ 0x2f, 0xc9, 0xc2, 0x34, 0x3c, 0xcc, 0x31, 0xca, 0xc1, 0xc0, 0xc3, 0x33, 0x3f, 0x3f, 0x3e, 0x3c,
+ 0xcc, 0x31, 0xcc, 0xc3, 0xc4, 0x32, 0xd1, 0x34, 0x3f, 0x3c, 0xcc, 0x31, 0xcc, 0xc3, 0x33, 0xd0,
+ 0x31, 0xd1, 0x35, 0x3f, 0x3e, 0x3c, 0xca, 0xc5, 0x34, 0xcc, 0xc2, 0xc3, 0xc3, 0xc5, 0x36, 0x40,
+ 0x3e, 0x41, 0x3e, 0x41, 0x3e, 0x3f, 0x41, 0x40, 0xce, 0xc5, 0x33, 0xcf, 0xc7, 0x36, 0xd2, 0x32,
+ 0xd1, 0xc5, 0xc6, 0x35, 0xd4, 0x32, 0xd1, 0xc6, 0x34, 0x41, 0xd2, 0x30, 0xd0, 0xc2, 0xc2, 0xc5,
+ 0x31, 0xd3, 0x31, 0xd1, 0xc7, 0x33, 0xd4, 0xc6, 0x34, 0x42, 0x40, 0x40, 0x40, 0x41, 0x41, 0xd2,
+ 0xc4, 0xc4, 0x39, 0x3e, 0xce, 0x35, 0x3c, 0xcd, 0x31, 0x3d, 0xc8, 0xc3, 0x32, 0x3d, 0xcc, 0x2f,
+ 0xce, 0x30, 0xd0, 0x2e, 0xcf, 0x30, 0x3d, 0x3c, 0x3c, 0x3a, 0xcb, 0xbf, 0xbf, 0xc1, 0x31, 0x3d,
+ 0x3d, 0x3c, 0x3c, 0x3c, 0x3d, 0x3b, 0xcf, 0x2e, 0xd2, 0x2e, 0xd0, 0x2c, 0x3b, 0xd1, 0x2a, 0xcf,
+ 0xbe, 0xc0, 0xc0, 0xbf, 0xc1, 0xc1, 0x2c, 0x3f, 0xd0, 0xc0, 0x2e, 0xd0, 0x31, 0xd2, 0xc2, 0x2f,
+ 0x3e, 0x3e, 0x3d, 0xcf, 0x31, 0xcb, 0xc3, 0x32, 0xd1, 0xc4, 0x37, 0x3f, 0xce, 0xc4, 0x35, 0xd0,
+ 0x33, 0x3f, 0xce, 0x31, 0x3c, 0x3b, 0xcc, 0x31, 0xcd, 0x2f, 0xd1, 0x32, 0x3a, 0x3b, 0xcb, 0xc1,
+ 0xc0, 0x32, 0x3e, 0x3e, 0x3c, 0x3d, 0x3d, 0xce, 0x2d, 0xcd, 0xc3, 0xc2, 0x30, 0x3d, 0xcf, 0xc1,
+ 0xc2, 0x30, 0xd0, 0xc4, 0x31, 0xd4, 0x30, 0x40, 0x3e, 0xd0, 0x2f, 0xd0, 0x2f, 0xd1, 0xc2, 0x2e,
+ 0xd4, 0x2e, 0x3f, 0xce, 0xc2, 0x34, 0x3e, 0x3f, 0xd0, 0x30, 0xcf, 0x31, 0x3d, 0x3d, 0xcc, 0x2d,
+ 0xcf, 0x2f, 0xcf, 0x2e, 0x3b, 0xcf, 0x2c, 0x3b, 0x3b, 0x3a, 0x3d, 0x38, 0xcc, 0x2d, 0x3b, 0xcc,
+ 0xbe, 0x2d, 0xd1, 0x2c, 0x3c, 0x3d, 0xce, 0xc0, 0x2d, 0xd1, 0x2f, 0x3f, 0x3f, 0x3c, 0x3f, 0x3e,
+ 0x3e, 0x3c, 0xd3, 0x2a, 0xd1, 0xc2, 0x2e, 0x3c, 0xd1, 0x2f, 0x3d, 0xd1, 0xbe, 0xc1, 0x2d, 0xd2,
+ 0x2d, 0x3d, 0x3d, 0x3b, 0xcd, 0x31, 0xcc, 0x31, 0xce, 0x2e, 0x3d, 0x3e, 0x3a, 0xcd, 0x2d, 0xcc,
+ 0xc1, 0xc1, 0xc3, 0x2e, 0x3f, 0x3f, 0x3c, 0xcf, 0xc0, 0x31, 0xd1, 0xc2, 0xc3, 0x33, 0xd2, 0xc7,
+ 0x32, 0x40, 0xd3, 0xc4, 0xc4, 0x33, 0x40, 0x40, 0xd2, 0x2f, 0xd0, 0x2f, 0x3c, 0xd1, 0x2c, 0x3a,
+ 0x3d, 0xd0, 0x2a, 0xd0, 0x28, 0xcf, 0xc3, 0x2c, 0xd2, 0x2d, 0xd3, 0xc3, 0xc3, 0x2e, 0x3e, 0x41,
+ 0xd2, 0xc3, 0xc2, 0xc2, 0xc2, 0xc2, 0xc4, 0x32, 0xd2, 0x34, 0x3e, 0x3e, 0x3c, 0xd1, 0x30, 0x3d,
+ 0x3c, 0xce, 0x2e, 0x3b, 0x38, 0xcb, 0xbe, 0xc1, 0x2e, 0x3e, 0x3c, 0x3d, 0xd1, 0x2c, 0x3b, 0x3b,
+ 0xcd, 0xbf, 0x2d, 0xd0, 0x2f, 0xd0, 0x2d, 0xd1, 0x2d, 0x3d, 0xd0, 0x2b, 0x3b, 0xcf, 0x2b, 0x3a,
+ 0xcc, 0xbc, 0xc1, 0x2a, 0x3c, 0xce, 0x28, 0xd1, 0x2a, 0x3c, 0x3a, 0xcf, 0xbf, 0x2b, 0x3e, 0x3c,
+ 0xd2, 0x2b, 0x3d, 0x3a, 0x3a, 0xcf, 0x2c, 0xcc, 0xbe, 0xc1, 0xc0, 0xbf, 0xc1, 0x31, 0x3c, 0xce,
+ 0xc0, 0xc1, 0xc0, 0xc1, 0x31, 0x3c, 0xd1, 0xc2, 0x2e, 0xd1, 0xc3, 0xc4, 0x30, 0x3f, 0xd3, 0x2c,
+ 0xd3, 0xc2, 0x30, 0xd5, 0xc3, 0xc5, 0x30, 0xd5, 0xc4, 0xc5, 0xc5, 0x31, 0x41, 0xd4, 0xc4, 0x31,
+ 0x40, 0xd4, 0x2d, 0xd5, 0xc0, 0xc3, 0x2c, 0x3f, 0x3e, 0x3f, 0x3f, 0x3f, 0xd6, 0x2c, 0x3e, 0xd2,
+ 0x27, 0x37, 0xde, 0xc9, 0xe9, 0x2e, 0xc7, 0x3c, 0xd9, 0x07, 0xf3, 0x28, 0x8d, 0xa5, 0xc9, 0xca,
+ 0xe7, 0xcb, 0xfc, 0xb3, 0x3c, 0xd4, 0xd3, 0x9a, 0xe7, 0x2c, 0xc8, 0xf8, 0x44, 0xb3, 0xb0, 0xfa,
+ 0x1c, 0xbf, 0xc9, 0xff, 0xbb, 0x1d, 0x02, 0xdb, 0x45, 0xc1, 0x40, 0x1c, 0xec, 0x48, 0xda, 0x21,
+ 0x4c, 0xe3, 0x38, 0xdf, 0x34, 0xd8, 0x35, 0xd8, 0x31, 0xd7, 0x30, 0xd4, 0x2f, 0xd6, 0x2d, 0xd5,
+ 0x2b, 0xd5, 0x33, 0xce, 0x33, 0xce, 0x33, 0xd0, 0x34, 0xcf, 0x31, 0xcf, 0x30, 0xce, 0x30, 0xcf,
+ 0x30, 0x3d, 0xce, 0x2f, 0xcf, 0xc2, 0x30, 0x3f, 0x3d, 0xcf, 0xc1, 0x31, 0xd0, 0xc5, 0xc4, 0x33,
+ 0x41, 0x3e, 0xd1, 0x30, 0x3c, 0x3d, 0xce, 0x2f, 0xce, 0xc1, 0xc3, 0x2e, 0xd3, 0x30, 0x3e, 0x3e,
+ 0x3c, 0x3f, 0x3c, 0xd1, 0xc0, 0xc0, 0xc0, 0xc1, 0xc2, 0xc0, 0xc1, 0xc3, 0x2c, 0x3f, 0xd2, 0xbe,
+ 0xc1, 0x2e, 0xcf, 0xc4, 0x33, 0xd3, 0x34, 0xd0, 0x33, 0x3d, 0xcf, 0xc3, 0x32, 0xce, 0x33, 0xd2,
+ 0x32, 0xce, 0xc5, 0x34, 0x40, 0xce, 0xc5, 0x32, 0x3e, 0xd0, 0x31, 0xd0, 0x2f, 0xd2, 0x30, 0xce,
+ 0xc2, 0x33, 0x40, 0x3e, 0xd0, 0xc3, 0xc2, 0x2e, 0xd3, 0x31, 0xd0, 0xc7, 0x32, 0x40, 0x3e, 0xd3,
+ 0x2f, 0xd3, 0x2e, 0xd0, 0x2d, 0xd3, 0x2d, 0xd3, 0x2f, 0x3c, 0x3d, 0xd1, 0x2a, 0x3b, 0xd1, 0x28,
+ 0x3b, 0x3b, 0xc8, 0x31, 0x39, 0x3b, 0x38, 0xc6, 0xbf, 0x31, 0x3d, 0x3a, 0xca, 0xc0, 0x33, 0x3c,
+ 0xca, 0xc0, 0xc1, 0xc1, 0xc2, 0x35, 0x3e, 0x3c, 0x3d, 0x3f, 0xcc, 0xc0, 0xc3, 0x31, 0xce, 0xc5,
+ 0x33, 0xd0, 0xc4, 0x35, 0xd3, 0x33, 0xd3, 0x32, 0xd1, 0xc4, 0xc3, 0x35, 0xd3, 0xc6, 0xc4, 0x35,
+ 0xd2, 0xc6, 0x35, 0x41, 0x41, 0xd4, 0x33, 0xd1, 0xc4, 0x30, 0x41, 0xd2, 0x30, 0x3e, 0xce, 0xc1,
+ 0xc3, 0xc0, 0x31, 0xce, 0xc5, 0x34, 0x40, 0x3d, 0xd1, 0xc4, 0x32, 0x3e, 0xcf, 0xc2, 0xc3, 0x30,
+ 0xd4, 0x32, 0x3e, 0x3f, 0x3e, 0x3f, 0x3e, 0xd0, 0xc2, 0x31, 0x3e, 0x3f, 0x3f, 0xd1, 0xc2, 0x2f,
+ 0xd5, 0x2e, 0xd3, 0xc3, 0x2f, 0xd7, 0x30, 0x3e, 0xd2, 0x2c, 0x3e, 0x3c, 0xd0, 0xc1, 0xc1, 0xc3,
+ 0xc0, 0xc3, 0x2c, 0xd4, 0xc2, 0x2e, 0x40, 0xd2, 0xc3, 0x2d, 0xd5, 0xc3, 0x2f, 0x42, 0x40, 0xd6,
+ 0x2d, 0xd4, 0xc2, 0xc2, 0xc2, 0x32, 0xd4, 0xc3, 0xc5, 0xc4, 0x32, 0x40, 0xd3, 0x30, 0xd0, 0xc3,
+ 0xc4, 0x30, 0xd3, 0xc5, 0xc5, 0xc4, 0xc4, 0x33, 0x40, 0x40, 0xd4, 0x2f, 0xd2, 0x2d, 0x3d, 0xd1,
+ 0xc1, 0xc2, 0x2c, 0xd4, 0x2e, 0xd4, 0x2d, 0x3d, 0xd3, 0xc1, 0xc1, 0xc1, 0x2d, 0xd5, 0xc2, 0xc2,
+ 0xc2, 0x2d, 0xd9, 0xc4, 0xc5, 0x2f, 0x43, 0x40, 0xd6, 0xd7, 0xd7, 0x2b, 0x3e, 0xd5, 0x29, 0x3d,
+ 0xd4, 0x24, 0x3b, 0x3a, 0x3b, 0xce, 0x2a, 0x3a, 0xcc, 0xbe, 0x2d, 0x3b, 0x3b, 0x3d, 0x3b, 0x3b,
+ 0xce, 0x2b, 0x3a, 0xcc, 0x2b, 0xd0, 0x2b, 0x3b, 0x3b, 0x39, 0xcf, 0xbf, 0x2a, 0xd1, 0xc0, 0x2f,
+ 0x3e, 0xd0, 0x2d, 0x3a, 0xd1, 0xbe, 0x2b, 0xd2, 0xc3, 0xc2, 0xc0, 0x2d, 0xd7, 0x2c, 0xd7, 0xc2,
+ 0xc3, 0x2f, 0x43, 0x41, 0x40, 0xd4, 0xc3, 0xc3, 0xc3, 0xc2, 0x2c, 0x40, 0xd7, 0x2a, 0x3d, 0xd3,
+ 0x26, 0xd5, 0x2f, 0x3a, 0x3c, 0xca, 0xc3, 0x2c, 0xd3, 0x2e, 0x3c, 0x3d, 0x3d, 0x3d, 0xd0, 0xc2,
+ 0x2e, 0xd3, 0x2e, 0xd1, 0xc5, 0x2e, 0x3f, 0xd3, 0x2e, 0x3c, 0xd1, 0xc0, 0xc2, 0x2a, 0xd2, 0xc2,
+ 0xc4, 0x2e, 0xd7, 0x2e, 0x3e, 0xd3, 0xc1, 0xc3, 0xc1, 0x2d, 0x3f, 0xd3, 0xc2, 0x2c, 0x3f, 0xd3,
+ 0x2b, 0x3b, 0xd1, 0xbf, 0xc1, 0xbe, 0x29, 0x3f, 0xd4, 0x29, 0xd5, 0xbf, 0x29, 0xd7, 0xc0, 0x2d,
+ 0xd8, 0xc2, 0x33, 0x41, 0xd3, 0x30, 0x3d, 0xd0, 0x2c, 0xcf, 0xc1, 0xc1, 0xc2, 0x2e, 0x3f, 0xd3,
+ 0x2b, 0xcf, 0xc3, 0x2f, 0x40, 0x3f, 0x3e, 0xd3, 0x2a, 0x3d, 0x3d, 0xce, 0xc1, 0x2b, 0x3d, 0xd0,
+ 0x2b, 0xd3, 0x2b, 0x3b, 0x3d, 0xce, 0x29, 0x3b, 0x3b, 0x3b, 0xd1, 0xbe, 0x2b, 0xd2, 0x29, 0x3d,
+ 0x3a, 0xd3, 0x29, 0x3a, 0x3b, 0x3b, 0xd2, 0x26, 0x3b, 0xd0, 0xbf, 0xbe, 0xbc, 0xbf, 0xbe, 0xbc,
+ 0x27, 0x3d, 0x3a, 0x3c, 0xce, 0xc1, 0x2c, 0xd2, 0xc2, 0xc2, 0x2f, 0x40, 0xd2, 0xc2, 0xc2, 0x2e,
+ 0x40, 0xd4, 0x2a, 0x3b, 0xcf, 0x2b, 0xd2, 0x2a, 0x3c, 0xd0, 0xc1, 0x2a, 0x3d, 0xd0, 0xc1, 0xc0,
+ 0xbe, 0x2b, 0x3f, 0x3e, 0xd2, 0xc1, 0xc0, 0xc1, 0xc0, 0xc2, 0x2a, 0xd6, 0xc2, 0xc3, 0xc3, 0xc3,
+ 0x2c, 0x3e, 0x41, 0xd6, 0xc0, 0xc3, 0xc2, 0xc2, 0x2a, 0xd9, 0x28, 0x3f, 0xd5, 0xc1, 0xc2, 0xc0,
+ 0xc3, 0x28, 0xd5, 0xc2, 0xc5, 0x30, 0xd6, 0xc4, 0xc5, 0xc5, 0x31, 0x43, 0xd4, 0xc3, 0xc5, 0xc2,
+ 0xc2, 0x2f, 0xd6, 0xc5, 0xc2, 0x2d, 0x41, 0x41, 0x43, 0x40, 0x41, 0xd8, 0x2a, 0x3f, 0x3f, 0x3e,
+ 0x2c, 0x0a, 0x0d, 0x2a, 0xbb, 0x91, 0x5e, 0x2a, 0xca, 0x23, 0xf0, 0x0f, 0xbe, 0xd6, 0xfb, 0xc0,
+ 0x2c, 0x34, 0x7f, 0xd1, 0xc9, 0xc1, 0x1c, 0x06, 0x97, 0x1f, 0x21, 0xa8, 0x04, 0x5d, 0x07, 0xb0,
+ 0x49, 0xaf, 0x10, 0x60, 0xd1, 0xf1, 0xf3, 0xcb, 0x72, 0x16, 0x24, 0xe9, 0xd4, 0x28, 0xb2, 0x8c,
+ 0xf3, 0x3b, 0xe8, 0x06, 0xeb, 0x09, 0xec, 0x0a, 0xec, 0x0b, 0xee, 0x0b, 0xee, 0x0b, 0xf0, 0x0b,
+ 0xf0, 0x0a, 0xf3, 0x0b, 0xf2, 0x0a, 0xf5, 0x08, 0xf5, 0x0a, 0xf4, 0x08, 0xf5, 0x08, 0xf5, 0x09,
+ 0xf4, 0x07, 0x3f, 0xf5, 0x05, 0xf6, 0xbf, 0x06, 0x3f, 0x3f, 0xf8, 0xbe, 0x07, 0xf8, 0xc3, 0xc3,
+ 0x06, 0x41, 0x40, 0xf0, 0x0f, 0x3c, 0x3d, 0xef, 0x0e, 0xee, 0xbe, 0xbf, 0x11, 0xf2, 0x0e, 0x3e,
+ 0x3c, 0x3f, 0x3e, 0x3e, 0xf0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc3, 0xc0, 0xc1, 0xc1, 0x0b, 0x3f, 0xf5,
+ 0xc0, 0xc0, 0x08, 0xf7, 0xc2, 0x0c, 0xf6, 0x0d, 0xf9, 0x0a, 0x3f, 0xf9, 0xc0, 0x0b, 0xf6, 0x06,
+ 0xf8, 0x06, 0xf9, 0xc3, 0x08, 0x40, 0xfa, 0xc2, 0x09, 0x3e, 0xf7, 0x05, 0xf6, 0x04, 0xf7, 0x05,
+ 0xf8, 0xc1, 0xc3, 0xc1, 0x0a, 0x3e, 0x41, 0xf6, 0x07, 0xf7, 0x08, 0xf6, 0x07, 0x3e, 0x3c, 0x3f,
+ 0xf4, 0xbf, 0xbe, 0x06, 0xf8, 0x07, 0xf8, 0x05, 0xfb, 0xc0, 0xc1, 0x02, 0x40, 0x40, 0x3e, 0xfb,
+ 0x02, 0x3e, 0x3e, 0xf6, 0x01, 0xf9, 0x00, 0x3f, 0xf8, 0xbd, 0x02, 0xfc, 0xc1, 0xc0, 0x03, 0xfe,
+ 0xc5, 0xc4, 0xc2, 0xc5, 0xc2, 0x02, 0x41, 0x40, 0x40, 0x41, 0x40, 0xff, 0xff, 0x40, 0xfd, 0xfd,
+ 0xfc, 0xfd, 0x3e, 0xfc, 0xbe, 0xbf, 0xfe, 0xfc, 0xc1, 0xc0, 0xc1, 0xc1, 0x00, 0x01, 0x01, 0x02,
+ 0xc0, 0x00, 0x02, 0xc3, 0xc5, 0x02, 0x07, 0xc4, 0xc6, 0xc4, 0x02, 0x06, 0xc3, 0x01, 0x43, 0x43,
+ 0x42, 0x05, 0xfc, 0x04, 0xfd, 0x41, 0x3e, 0x05, 0xbf, 0xfb, 0x41, 0x3e, 0x3e, 0x3f, 0x3f, 0x40,
+ 0x02, 0xf9, 0x3e, 0x03, 0xbf, 0xbe, 0xf6, 0x3f, 0x05, 0xf6, 0x3c, 0x05, 0xbd, 0xbf, 0xbe, 0xf2,
+ 0x3f, 0x3c, 0x3d, 0x3e, 0x08, 0xee, 0x3d, 0x3b, 0x07, 0xbd, 0xf3, 0x0e, 0xc0, 0xc0, 0xf2, 0x0f,
+ 0xf2, 0x40, 0x0d, 0xef, 0x3f, 0x3e, 0x0a, 0xbf, 0xf0, 0x0e, 0xf1, 0x40, 0x0a, 0xef, 0x0e, 0xc1,
+ 0xc0, 0xc1, 0xc2, 0xc0, 0xee, 0x13, 0xc1, 0xee, 0x43, 0x10, 0xea, 0x15, 0xc0, 0xc0, 0xc0, 0xc3,
+ 0xc0, 0xec, 0x15, 0xc2, 0xf0, 0x16, 0xc2, 0xef, 0x1b, 0xc2, 0xf1, 0x44, 0x42, 0x19, 0xc2, 0xc3,
+ 0xed, 0x17, 0xe7, 0x21, 0xe4, 0x40, 0x3f, 0x20, 0xc0, 0xe2, 0x40, 0x3e, 0x1d, 0xc1, 0xc2, 0xc0,
+ 0xe2, 0x20, 0xe2, 0x3f, 0x3f, 0x3e, 0x1c, 0xdf, 0x3c, 0x3f, 0x1e, 0xbf, 0xc1, 0xbe, 0xdf, 0x3e,
+ 0x3d, 0x1f, 0xdc, 0x3c, 0x3b, 0x3d, 0x3a, 0x3d, 0x1f, 0xdb, 0x1e, 0xda, 0x20, 0xda, 0x21, 0xc1,
+ 0xc0, 0xc1, 0xdc, 0x3e, 0x23, 0xda, 0x3e, 0x20, 0xbf, 0xbd, 0xdb, 0x23, 0xbf, 0xdf, 0x26, 0xdc,
+ 0x26, 0xdc, 0x2e, 0xc2, 0xc3, 0xc3, 0xd4, 0x40, 0x2f, 0xc2, 0xd2, 0x3f, 0x3f, 0x3f, 0x2e, 0xd1,
+ 0x2d, 0xd3, 0x2c, 0xc3, 0xc3, 0xc3, 0xc2, 0xc3, 0xc2, 0xd3, 0x3e, 0x31, 0xd0, 0x2d, 0xc3, 0xc0,
+ 0xc1, 0xd1, 0x2f, 0xd3, 0x3f, 0x3f, 0x31, 0xc1, 0xcf, 0x31, 0xd2, 0x3e, 0x3f, 0x3c, 0x41, 0x3e,
+ 0x3e, 0x3f, 0x31, 0xd1, 0x30, 0xc0, 0xcf, 0x33, 0xd1, 0x3d, 0x41, 0x32, 0xc0, 0xc3, 0xce, 0x3e,
+ 0x32, 0xc3, 0xc3, 0xc2, 0xc0, 0xc3, 0xca, 0x3c, 0x37, 0xc5, 0xc3, 0xca, 0x34, 0xcf, 0x35, 0xc6,
+ 0xca, 0x3c, 0x39, 0xce, 0x3c, 0x37, 0xc2, 0xc8, 0x3c, 0x36, 0xcc, 0x30, 0xcb, 0x33, 0xc6, 0xc3,
+ 0xc8, 0x34, 0xcc, 0x3d, 0x37, 0xc5, 0xc9, 0x38, 0xc6, 0xca, 0x3b, 0x40, 0x40, 0x40, 0x3a, 0xc5,
+ 0xc8, 0x3f, 0x3e, 0x3b, 0xce, 0x3a, 0x3e, 0x38, 0xca, 0x39, 0x3c, 0x3c, 0x36, 0xcb, 0x36, 0x3b,
+ 0x37, 0xc8, 0x35, 0x3a, 0x36, 0xc8, 0x2f, 0xc4, 0xc5, 0x36, 0x38, 0xc7, 0xc0, 0xc7, 0x33, 0xc8,
+ 0xc3, 0xc5, 0x39, 0x3b, 0xc9, 0xc7, 0x34, 0xd1, 0x34, 0xcf, 0x35, 0xca, 0xc9, 0x36, 0xd0, 0x36,
+ 0xcc, 0xc7, 0x39, 0x41, 0x42, 0x42, 0x3e, 0xca, 0xc1, 0xc3, 0xc3, 0xc2, 0xc3, 0xc2, 0xc4, 0xc6,
+ 0x34, 0xd1, 0x35, 0xcf, 0x37, 0x3c, 0xce, 0x35, 0x3b, 0xcc, 0x30, 0xca, 0x33, 0x39, 0xc9, 0xbf,
+ 0xbf, 0xbf, 0xc3, 0x34, 0x3c, 0x3f, 0x3a, 0xcc, 0x2f, 0xcd, 0x32, 0x3c, 0x3a, 0xc8, 0xc2, 0x34,
+ 0x3a, 0xcb, 0xc0, 0xc0, 0x35, 0x3c, 0xce, 0x32, 0x3c, 0x3c, 0x3d, 0x3a, 0xcd, 0x2e, 0x3b, 0x39,
+ 0xc9, 0xbc, 0xbf, 0x30, 0xcc, 0xc0, 0xc3, 0x33, 0xce, 0xc5, 0xc2, 0xc4, 0x36, 0x3e, 0xd1, 0xc3,
+ 0xc2, 0xc3, 0xc5, 0x33, 0xd0, 0xc5, 0xc4, 0x35, 0x40, 0x40, 0x3e, 0x41, 0x3e, 0xd2, 0x30, 0x3f,
+ 0x3e, 0x3d, 0x21, 0xc8, 0x0d, 0x23, 0xec, 0xb1, 0x1a, 0xf0, 0xf3, 0x10, 0xc6, 0xbd, 0x5b, 0x0d,
+ 0xff, 0xe2, 0xe6, 0xd4, 0x72, 0xe2, 0xda, 0x0c, 0xf8, 0x1b, 0x04, 0x0b, 0xf7, 0xb4, 0xf0, 0x44,
+ 0xcd, 0xaf, 0x12, 0x1f, 0x11, 0xa1, 0x29, 0xb8, 0x1a, 0xdb, 0x09, 0x69, 0xf7, 0x20, 0xc4, 0x1c,
+ 0xc0, 0xc8, 0x1d, 0x45, 0xd9, 0x30, 0xd8, 0x2e, 0xd8, 0x2e, 0xd6, 0x2c, 0xd5, 0x2d, 0xd4, 0x2b,
+ 0xd5, 0x2b, 0xd1, 0x31, 0xd0, 0x32, 0xd2, 0x30, 0xd0, 0x31, 0xd0, 0x31, 0xce, 0x2f, 0xd3, 0x2e,
+ 0xce, 0x2f, 0xd1, 0x31, 0x3c, 0xd1, 0x2d, 0xd0, 0xc0, 0x2e, 0x3c, 0x3d, 0xd1, 0xc3, 0x2c, 0xd2,
+ 0xc3, 0xc5, 0x31, 0x40, 0x40, 0xd5, 0x2c, 0x3a, 0x3d, 0xd0, 0x2b, 0xd3, 0xc0, 0xc0, 0x2d, 0xd4,
+ 0x2c, 0x3f, 0x3e, 0x3c, 0x3f, 0x3c, 0xd2, 0xc1, 0xc2, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0x2a,
+};
+
+
+int
+ao_real_packet(void)
+{
+ uint8_t decode[64];
+ int ok;
+
+ ok = ao_fec_decode(real_packet, 576, decode, 34, NULL);
+
+ if (ok && decode[33] == AO_FEC_DECODE_CRC_OK) {
+ printf ("match\n");
+
+ ao_fec_dump_bytes(decode, 34, "Decode");
+ } else {
+ printf ("actual packet crc error\n");
+ ok = 0;
+ }
+ return ok;
+}
+
+#define EXPECT_DECODE_FAIL 0
+#define EXPECT_CRC_MISMATCH 6386
+#define EXPECT_DATA_MISMATCH 0
+#define NOISE_AMOUNT 0x50
+
+int
+main(int argc, char **argv)
+{
+ int trial;
+
+ uint8_t original[120];
+ uint8_t original_len;
+
+ uint8_t encode[ENCODE_LEN(sizeof(original))];
+ int encode_len;
+
+ uint8_t transmit[EXPAND_LEN(sizeof(original))];
+ int transmit_len;
+
+ uint8_t receive[EXPAND_LEN(sizeof(original))];
+ int receive_len;
+
+ uint8_t decode[DECODE_LEN(sizeof(original))];
+ int decode_ok;
+
+ int errors = 0;
+ int decode_fail = 0;
+ int crc_mismatch = 0;
+ int data_mismatch = 0;
+
+ if (!ao_real_packet())
+ errors++;
+
+ srandom(0);
+ for (trial = 0; trial < 100000; trial++) {
+
+ /* Compute some random data */
+ original_len = ao_random_data(original, sizeof(original));
+
+ /* Encode it */
+ encode_len = ao_fec_encode(original, original_len, encode);
+
+ /* Expand from 1-bit-per-symbol to 1-byte-per-symbol */
+ transmit_len = ao_expand(encode, encode_len, transmit);
+
+ /* Add gaussian noise to the signal */
+ (void) ao_fuzz(transmit, transmit_len, receive, NOISE_AMOUNT);
+ receive_len = transmit_len;
+
+ /* Decode it */
+ decode_ok = ao_fec_decode(receive, receive_len, decode, original_len + 2, NULL);
+
+ /* Check to see if we received the right data */
+
+ if (!decode_ok)
+ decode_fail++;
+ else if (decode[original_len +1] != AO_FEC_DECODE_CRC_OK)
+ crc_mismatch++;
+ else if (memcmp(original, decode, original_len) != 0)
+ data_mismatch++;
+ }
+
+
+ printf ("%d packets coded\n", trial);
+ printf ("decode_fail %d crc_mismatch %d data_mismatch %d\n",
+ decode_fail, crc_mismatch, data_mismatch);
+ if (decode_fail != EXPECT_DECODE_FAIL) {
+ printf ("expected %d decode failures\n", EXPECT_DECODE_FAIL);
+ errors++;
+ }
+ if (crc_mismatch != EXPECT_CRC_MISMATCH) {
+ printf ("expected %d crc mismatch\n", EXPECT_CRC_MISMATCH);
+ errors++;
+ }
+ if (data_mismatch != EXPECT_DATA_MISMATCH) {
+ printf ("expected %d data mismatch\n", EXPECT_DATA_MISMATCH);
+ errors++;
+ }
+ return errors;
+}
+
+
diff --git a/src/test/ao_flight_test.c b/src/test/ao_flight_test.c
new file mode 100644
index 00000000..b9e291ce
--- /dev/null
+++ b/src/test/ao_flight_test.c
@@ -0,0 +1,738 @@
+/*
+ * 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <math.h>
+
+#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_M_TO_HEIGHT(m) ((int16_t) (m))
+#define AO_MS_TO_SPEED(ms) ((int16_t) ((ms) * 16))
+#define AO_MSS_TO_ACCEL(mss) ((int16_t) ((mss) * 16))
+
+/*
+ * 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 */
+};
+
+#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
+#ifndef HAS_ACCEL
+#define HAS_ACCEL 1
+#define HAS_ACCEL_REF 0
+#endif
+
+#include <ao_data.h>
+
+#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)
+
+/*
+ * Above this height, the baro sensor doesn't work
+ */
+#define AO_MAX_BARO_HEIGHT 12000
+#define AO_BARO_SATURATE 13000
+#define AO_MIN_BARO_VALUE ao_altitude_to_pres(AO_BARO_SATURATE)
+
+/*
+ * Above this speed, baro measurements are unreliable
+ */
+#define AO_MAX_BARO_SPEED 200
+
+#define ACCEL_NOSE_UP (ao_accel_2g >> 2)
+
+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
+};
+
+extern enum ao_flight_state ao_flight_state;
+
+#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()
+
+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 int32_t ao_k_height;
+
+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;
+
+FILE *emulator_in;
+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 * 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
+#include "ao_convert.c"
+
+struct ao_config {
+ uint16_t main_deploy;
+ int16_t accel_plus_g;
+ int16_t accel_minus_g;
+ uint8_t pad_orientation;
+ uint16_t apogee_lockout;
+};
+
+#define AO_PAD_ORIENTATION_ANTENNA_UP 0
+#define AO_PAD_ORIENTATION_ANTENNA_DOWN 1
+
+#define ao_config_get()
+
+struct ao_config ao_config;
+
+#define DATA_TO_XDATA(x) (x)
+
+
+#define GRAVITY 9.80665
+extern int16_t ao_ground_accel, ao_flight_accel;
+extern int16_t ao_accel_2g;
+
+typedef int16_t accel_t;
+
+extern uint16_t ao_sample_tick;
+
+extern int16_t ao_sample_height;
+extern accel_t ao_sample_accel;
+extern int32_t ao_accel_scale;
+extern int16_t ao_ground_height;
+extern int16_t ao_sample_alt;
+
+int ao_sample_prev_tick;
+uint16_t prev_tick;
+
+#include "ao_kalman.c"
+#include "ao_sample.c"
+#include "ao_flight.c"
+
+#define to_double(f) ((f) / 65536.0)
+
+static int ao_records_read = 0;
+static int ao_eof_read = 0;
+static int ao_flight_ground_accel;
+static int ao_flight_started = 0;
+static int ao_test_max_height;
+static double ao_test_max_height_time;
+static int ao_test_main_height;
+static double ao_test_main_height_time;
+static double ao_test_landed_time;
+static double ao_test_landed_height;
+static double ao_test_landed_time;
+static int landed_set;
+static double landed_time;
+static double landed_height;
+
+void
+ao_test_exit(void)
+{
+ double drogue_error;
+ double main_error;
+ double landed_error;
+ double landed_time_error;
+
+ if (!ao_test_main_height_time) {
+ ao_test_main_height_time = ao_test_max_height_time;
+ ao_test_main_height = ao_test_max_height;
+ }
+ drogue_error = fabs(ao_test_max_height_time - drogue_time);
+ main_error = fabs(ao_test_main_height_time - main_time);
+ landed_error = fabs(ao_test_landed_height - landed_height);
+ landed_time_error = ao_test_landed_time - landed_time;
+ if (drogue_error > emulator_error_max || main_error > emulator_error_max) {
+ printf ("%s %s\n",
+ emulator_app, emulator_name);
+ if (emulator_info)
+ printf ("\t%s\n", emulator_info);
+ printf ("\tApogee error %g\n", drogue_error);
+ printf ("\tMain error %g\n", main_error);
+ printf ("\tLanded height error %g\n", landed_error);
+ printf ("\tLanded time error %g\n", landed_time_error);
+ printf ("\tActual: apogee: %d at %7.2f main: %d at %7.2f landed %7.2f at %7.2f\n",
+ ao_test_max_height, ao_test_max_height_time,
+ ao_test_main_height, ao_test_main_height_time,
+ ao_test_landed_height, ao_test_landed_time);
+ printf ("\tComputed: apogee: %d at %7.2f main: %d at %7.2f landed %7.2f at %7.2f\n",
+ drogue_height, drogue_time, main_height, main_time,
+ landed_height, landed_time);
+ exit (1);
+ }
+ exit(0);
+}
+
+void
+ao_insert(void)
+{
+ double time;
+
+ ao_data_ring[ao_data_head] = ao_data_static;
+ ao_data_head = ao_data_ring_next(ao_data_head);
+ if (ao_flight_state != ao_flight_startup) {
+ double height = ao_pres_to_altitude(ao_data_static.adc.pres_real) - ao_ground_height;
+ double accel = ((ao_flight_ground_accel - ao_data_static.adc.accel) * GRAVITY * 2.0) /
+ (ao_config.accel_minus_g - ao_config.accel_plus_g);
+
+ if (!tick_offset)
+ tick_offset = -ao_data_static.tick;
+ if ((prev_tick - ao_data_static.tick) > 0x400)
+ tick_offset += 65536;
+ prev_tick = ao_data_static.tick;
+ time = (double) (ao_data_static.tick + tick_offset) / 100;
+
+ if (ao_test_max_height < height) {
+ ao_test_max_height = height;
+ ao_test_max_height_time = time;
+ ao_test_landed_height = height;
+ ao_test_landed_time = time;
+ }
+ if (height > ao_config.main_deploy) {
+ ao_test_main_height_time = time;
+ ao_test_main_height = height;
+ }
+
+ if (ao_test_landed_height > height) {
+ ao_test_landed_height = height;
+ ao_test_landed_time = time;
+ }
+
+ if (ao_flight_state == ao_flight_landed && !landed_set) {
+ landed_set = 1;
+ landed_time = time;
+ landed_height = height;
+ }
+
+ if (!ao_summary) {
+ printf("%7.2f height %8.2f accel %8.3f state %-8.8s k_height %8.2f k_speed %8.3f k_accel %8.3f avg_height %5d drogue %4d main %4d error %5d\n",
+ time,
+ height,
+ accel,
+ ao_state_names[ao_flight_state],
+ ao_k_height / 65536.0,
+ ao_k_speed / 65536.0 / 16.0,
+ ao_k_accel / 65536.0 / 16.0,
+ ao_avg_height,
+ drogue_height,
+ main_height,
+ ao_error_h_sq_avg);
+
+// if (ao_flight_state == ao_flight_landed)
+// ao_test_exit();
+ }
+ }
+}
+
+#define AO_MAX_CALLSIGN 8
+#define AO_MAX_VERSION 8
+#define AO_MAX_TELEMETRY 128
+
+struct ao_telemetry_generic {
+ uint16_t serial; /* 0 */
+ uint16_t tick; /* 2 */
+ uint8_t type; /* 4 */
+ uint8_t payload[27]; /* 5 */
+ /* 32 */
+};
+
+#define AO_TELEMETRY_SENSOR_TELEMETRUM 0x01
+#define AO_TELEMETRY_SENSOR_TELEMINI 0x02
+#define AO_TELEMETRY_SENSOR_TELENANO 0x03
+
+struct ao_telemetry_sensor {
+ uint16_t serial; /* 0 */
+ uint16_t tick; /* 2 */
+ uint8_t type; /* 4 */
+
+ uint8_t state; /* 5 flight state */
+ int16_t accel; /* 6 accelerometer (TM only) */
+ int16_t pres; /* 8 pressure sensor */
+ int16_t temp; /* 10 temperature sensor */
+ int16_t v_batt; /* 12 battery voltage */
+ int16_t sense_d; /* 14 drogue continuity sense (TM/Tm) */
+ int16_t sense_m; /* 16 main continuity sense (TM/Tm) */
+
+ int16_t acceleration; /* 18 m/s² * 16 */
+ int16_t speed; /* 20 m/s * 16 */
+ int16_t height; /* 22 m */
+
+ int16_t ground_pres; /* 24 average pres on pad */
+ int16_t ground_accel; /* 26 average accel on pad */
+ int16_t accel_plus_g; /* 28 accel calibration at +1g */
+ int16_t accel_minus_g; /* 30 accel calibration at -1g */
+ /* 32 */
+};
+
+#define AO_TELEMETRY_CONFIGURATION 0x04
+
+struct ao_telemetry_configuration {
+ uint16_t serial; /* 0 */
+ uint16_t tick; /* 2 */
+ uint8_t type; /* 4 */
+
+ uint8_t device; /* 5 device type */
+ uint16_t flight; /* 6 flight number */
+ uint8_t config_major; /* 8 Config major version */
+ uint8_t config_minor; /* 9 Config minor version */
+ uint16_t apogee_delay; /* 10 Apogee deploy delay in seconds */
+ uint16_t main_deploy; /* 12 Main deploy alt in meters */
+ uint16_t flight_log_max; /* 14 Maximum flight log size in kB */
+ char callsign[AO_MAX_CALLSIGN]; /* 16 Radio operator identity */
+ char version[AO_MAX_VERSION]; /* 24 Software version */
+ /* 32 */
+};
+
+#define AO_TELEMETRY_LOCATION 0x05
+
+#define AO_GPS_MODE_NOT_VALID 'N'
+#define AO_GPS_MODE_AUTONOMOUS 'A'
+#define AO_GPS_MODE_DIFFERENTIAL 'D'
+#define AO_GPS_MODE_ESTIMATED 'E'
+#define AO_GPS_MODE_MANUAL 'M'
+#define AO_GPS_MODE_SIMULATED 'S'
+
+struct ao_telemetry_location {
+ uint16_t serial; /* 0 */
+ uint16_t tick; /* 2 */
+ uint8_t type; /* 4 */
+
+ uint8_t flags; /* 5 Number of sats and other flags */
+ int16_t altitude; /* 6 GPS reported altitude (m) */
+ int32_t latitude; /* 8 latitude (degrees * 10⁷) */
+ int32_t longitude; /* 12 longitude (degrees * 10⁷) */
+ uint8_t year; /* 16 (- 2000) */
+ uint8_t month; /* 17 (1-12) */
+ uint8_t day; /* 18 (1-31) */
+ uint8_t hour; /* 19 (0-23) */
+ uint8_t minute; /* 20 (0-59) */
+ uint8_t second; /* 21 (0-59) */
+ uint8_t pdop; /* 22 (m * 5) */
+ uint8_t hdop; /* 23 (m * 5) */
+ uint8_t vdop; /* 24 (m * 5) */
+ uint8_t mode; /* 25 */
+ uint16_t ground_speed; /* 26 cm/s */
+ int16_t climb_rate; /* 28 cm/s */
+ uint8_t course; /* 30 degrees / 2 */
+ uint8_t unused[1]; /* 31 */
+ /* 32 */
+};
+
+#define AO_TELEMETRY_SATELLITE 0x06
+
+struct ao_telemetry_satellite_info {
+ uint8_t svid;
+ uint8_t c_n_1;
+};
+
+struct ao_telemetry_satellite {
+ uint16_t serial; /* 0 */
+ uint16_t tick; /* 2 */
+ uint8_t type; /* 4 */
+ uint8_t channels; /* 5 number of reported sats */
+
+ struct ao_telemetry_satellite_info sats[12]; /* 6 */
+ uint8_t unused[2]; /* 30 */
+ /* 32 */
+};
+
+union ao_telemetry_all {
+ struct ao_telemetry_generic generic;
+ struct ao_telemetry_sensor sensor;
+ struct ao_telemetry_configuration configuration;
+ struct ao_telemetry_location location;
+ struct ao_telemetry_satellite satellite;
+};
+
+uint16_t
+uint16(uint8_t *bytes, int off)
+{
+ 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);
+}
+
+void
+ao_sleep(void *wchan)
+{
+ if (wchan == &ao_data_head) {
+ char type = 0;
+ uint16_t tick = 0;
+ uint16_t a = 0, b = 0;
+ uint8_t bytes[1024];
+ union ao_telemetry_all telem;
+ char line[1024];
+ char *saveptr;
+ char *l;
+ char *words[64];
+ int nword;
+
+ for (;;) {
+ if (ao_records_read > 2 && ao_flight_state == ao_flight_startup)
+ {
+ ao_data_static.adc.accel = ao_flight_ground_accel;
+ ao_insert();
+ return;
+ }
+
+ if (!fgets(line, sizeof (line), emulator_in)) {
+ if (++ao_eof_read >= 1000) {
+ if (!ao_summary)
+ printf ("no more data, exiting simulation\n");
+ ao_test_exit();
+ }
+ ao_data_static.tick += 10;
+ ao_insert();
+ return;
+ }
+ l = line;
+ for (nword = 0; nword < 64; nword++) {
+ words[nword] = strtok_r(l, " \t\n", &saveptr);
+ l = NULL;
+ if (words[nword] == NULL)
+ break;
+ }
+ if (nword == 4) {
+ type = words[0][0];
+ tick = strtoul(words[1], NULL, 16);
+ a = strtoul(words[2], NULL, 16);
+ b = strtoul(words[3], NULL, 16);
+ if (type == 'P')
+ type = 'A';
+ } else if (nword >= 6 && strcmp(words[0], "Accel") == 0) {
+ ao_config.accel_plus_g = atoi(words[3]);
+ ao_config.accel_minus_g = atoi(words[5]);
+ } else if (nword >= 4 && strcmp(words[0], "Main") == 0) {
+ ao_config.main_deploy = atoi(words[2]);
+ } else if (nword >= 3 && strcmp(words[0], "Apogee") == 0 &&
+ strcmp(words[1], "lockout:") == 0) {
+ ao_config.apogee_lockout = atoi(words[2]);
+ } else if (nword >= 36 && strcmp(words[0], "CALL") == 0) {
+ tick = atoi(words[10]);
+ if (!ao_flight_started) {
+ type = 'F';
+ a = atoi(words[26]);
+ ao_flight_started = 1;
+ } else {
+ type = 'A';
+ a = atoi(words[12]);
+ b = atoi(words[14]);
+ }
+ } else if (nword == 3 && strcmp(words[0], "BARO") == 0) {
+ tick = strtol(words[1], NULL, 16);
+ a = 16384 - 328;
+ b = strtol(words[2], NULL, 10);
+ type = 'A';
+ if (!ao_flight_started) {
+ ao_flight_ground_accel = 16384 - 328;
+ ao_config.accel_plus_g = 16384 - 328;
+ ao_config.accel_minus_g = 16384 + 328;
+ ao_flight_started = 1;
+ }
+ } else if (nword == 2 && strcmp(words[0], "TELEM") == 0) {
+ __xdata char *hex = words[1];
+ char elt[3];
+ int i, len;
+ uint8_t sum;
+
+ len = strlen(hex);
+ if (len > sizeof (bytes) * 2) {
+ len = sizeof (bytes)*2;
+ hex[len] = '\0';
+ }
+ for (i = 0; i < len; i += 2) {
+ elt[0] = hex[i];
+ elt[1] = hex[i+1];
+ elt[2] = '\0';
+ bytes[i/2] = (uint8_t) strtol(elt, NULL, 16);
+ }
+ len = i/2;
+ if (bytes[0] != len - 2) {
+ printf ("bad length %d != %d\n", bytes[0], len - 2);
+ continue;
+ }
+ sum = 0x5a;
+ for (i = 1; i < len-1; i++)
+ sum += bytes[i];
+ if (sum != bytes[len-1]) {
+ printf ("bad checksum\n");
+ continue;
+ }
+ if ((bytes[len-2] & 0x80) == 0) {
+ continue;
+ }
+ if (len == 36) {
+ ao_xmemcpy(&telem, bytes + 1, 32);
+ tick = telem.generic.tick;
+ switch (telem.generic.type) {
+ case AO_TELEMETRY_SENSOR_TELEMETRUM:
+ case AO_TELEMETRY_SENSOR_TELEMINI:
+ case AO_TELEMETRY_SENSOR_TELENANO:
+ if (!ao_flight_started) {
+ ao_flight_ground_accel = telem.sensor.ground_accel;
+ ao_config.accel_plus_g = telem.sensor.accel_plus_g;
+ ao_config.accel_minus_g = telem.sensor.accel_minus_g;
+ ao_flight_started = 1;
+ }
+ type = 'A';
+ a = telem.sensor.accel;
+ b = telem.sensor.pres;
+ break;
+ }
+ } else if (len == 99) {
+ ao_flight_started = 1;
+ tick = uint16(bytes, 21);
+ ao_flight_ground_accel = int16(bytes, 7);
+ ao_config.accel_plus_g = int16(bytes, 17);
+ ao_config.accel_minus_g = int16(bytes, 19);
+ type = 'A';
+ a = int16(bytes, 23);
+ b = int16(bytes, 25);
+ } else if (len == 98) {
+ ao_flight_started = 1;
+ tick = uint16(bytes, 20);
+ ao_flight_ground_accel = int16(bytes, 6);
+ ao_config.accel_plus_g = int16(bytes, 16);
+ ao_config.accel_minus_g = int16(bytes, 18);
+ type = 'A';
+ a = int16(bytes, 22);
+ b = int16(bytes, 24);
+ } else {
+ printf("unknown len %d\n", len);
+ continue;
+ }
+ }
+ if (type != 'F' && !ao_flight_started)
+ continue;
+
+ switch (type) {
+ case 'F':
+ ao_flight_ground_accel = a;
+ if (ao_config.accel_plus_g == 0) {
+ ao_config.accel_plus_g = a;
+ ao_config.accel_minus_g = a + 530;
+ }
+ if (ao_config.main_deploy == 0)
+ ao_config.main_deploy = 250;
+ ao_flight_started = 1;
+ break;
+ case 'S':
+ break;
+ case 'A':
+ ao_data_static.tick = tick;
+ ao_data_static.adc.accel = a;
+ ao_data_static.adc.pres_real = b;
+ if (b < AO_MIN_BARO_VALUE)
+ b = AO_MIN_BARO_VALUE;
+ ao_data_static.adc.pres = b;
+ ao_records_read++;
+ ao_insert();
+ return;
+ case 'T':
+ ao_data_static.tick = tick;
+ ao_data_static.adc.temp = a;
+ ao_data_static.adc.v_batt = b;
+ break;
+ case 'D':
+ case 'G':
+ case 'N':
+ case 'W':
+ case 'H':
+ break;
+ }
+ }
+
+ }
+}
+#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' },
+ { 0, 0, 0, 0},
+};
+
+void run_flight_fixed(char *name, FILE *f, int summary, char *info)
+{
+ emulator_name = name;
+ emulator_in = f;
+ emulator_info = info;
+ ao_summary = summary;
+ ao_flight_init();
+ ao_flight();
+}
+
+int
+main (int argc, char **argv)
+{
+ int summary = 0;
+ int c;
+ int i;
+ char *info = NULL;
+
+#if HAS_ACCEL
+ emulator_app="full";
+#else
+ emulator_app="baro";
+#endif
+ while ((c = getopt_long(argc, argv, "sdi:", options, NULL)) != -1) {
+ switch (c) {
+ case 's':
+ summary = 1;
+ break;
+ case 'd':
+ ao_flight_debug = 1;
+ break;
+ case 'i':
+ info = optarg;
+ break;
+ }
+ }
+
+ 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);
+}
diff --git a/src/test/ao_gps_test.c b/src/test/ao_gps_test.c
new file mode 100644
index 00000000..d75a12ec
--- /dev/null
+++ b/src/test/ao_gps_test.c
@@ -0,0 +1,513 @@
+/*
+ * 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.
+ */
+
+#define AO_GPS_TEST
+#include "ao_host.h"
+#include <termios.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#define AO_GPS_NUM_SAT_MASK (0xf << 0)
+#define AO_GPS_NUM_SAT_SHIFT (0)
+
+#define AO_GPS_VALID (1 << 4)
+#define AO_GPS_RUNNING (1 << 5)
+#define AO_GPS_DATE_VALID (1 << 6)
+#define AO_GPS_COURSE_VALID (1 << 7)
+
+struct ao_gps_orig {
+ uint8_t year;
+ uint8_t month;
+ uint8_t day;
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
+ uint8_t flags;
+ int32_t latitude; /* degrees * 10⁷ */
+ int32_t longitude; /* degrees * 10⁷ */
+ int16_t altitude; /* m */
+ uint16_t ground_speed; /* cm/s */
+ uint8_t course; /* degrees / 2 */
+ uint8_t hdop; /* * 5 */
+ int16_t climb_rate; /* cm/s */
+ uint16_t h_error; /* m */
+ uint16_t 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 ao_gps_sat_orig {
+ uint8_t svid;
+ uint8_t c_n_1;
+};
+
+#define AO_MAX_GPS_TRACKING 12
+
+struct ao_gps_tracking_orig {
+ uint8_t channels;
+ struct ao_gps_sat_orig sats[AO_MAX_GPS_TRACKING];
+};
+
+#define ao_telemetry_location ao_gps_orig
+#define ao_telemetry_satellite ao_gps_tracking_orig
+#define ao_telemetry_satellite_info ao_gps_sat_orig
+
+void
+ao_mutex_get(uint8_t *mutex)
+{
+}
+
+void
+ao_mutex_put(uint8_t *mutex)
+{
+}
+
+static int
+ao_gps_fd;
+
+static void
+ao_dbg_char(char c)
+{
+ char line[128];
+ line[0] = '\0';
+ if (c < ' ') {
+ if (c == '\n')
+ sprintf (line, "\n");
+ else
+ sprintf (line, "\\%02x", ((int) c) & 0xff);
+ } else {
+ sprintf (line, "%c", c);
+ }
+ write(1, line, strlen(line));
+}
+
+#define QUEUE_LEN 4096
+
+static char input_queue[QUEUE_LEN];
+int input_head, input_tail;
+
+#include <sys/time.h>
+
+int
+get_millis(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+static void
+check_sirf_message(char *from, uint8_t *msg, int len)
+{
+ uint16_t encoded_len, encoded_cksum;
+ uint16_t cksum;
+ uint8_t id;
+ int i;
+
+ if (msg[0] != 0xa0 || msg[1] != 0xa2) {
+ printf ("bad header\n");
+ return;
+ }
+ if (len < 7) {
+ printf("short\n");
+ return;
+ }
+ if (msg[len-1] != 0xb3 || msg[len-2] != 0xb0) {
+ printf ("bad trailer\n");
+ return;
+ }
+ encoded_len = (msg[2] << 8) | msg[3];
+ id = msg[4];
+/* printf ("%9d: %3d\n", get_millis(), id); */
+ if (encoded_len != len - 8) {
+ if (id != 52)
+ printf ("length mismatch (got %d, wanted %d)\n",
+ len - 8, encoded_len);
+ return;
+ }
+ encoded_cksum = (msg[len - 4] << 8) | msg[len-3];
+ cksum = 0;
+ for (i = 4; i < len - 4; i++)
+ cksum = (cksum + msg[i]) & 0x7fff;
+ if (encoded_cksum != cksum) {
+ printf ("cksum mismatch (got %04x wanted %04x)\n",
+ cksum, encoded_cksum);
+ return;
+ }
+ id = msg[4];
+ switch (id) {
+ case 41:{
+ int off = 4;
+
+ uint8_t id;
+ uint16_t nav_valid;
+ uint16_t nav_type;
+ uint16_t week;
+ uint32_t tow;
+ uint16_t year;
+ uint8_t month;
+ uint8_t day;
+ uint8_t hour;
+ uint8_t minute;
+ uint16_t second;
+ uint32_t sat_list;
+ int32_t lat;
+ int32_t lon;
+ int32_t alt_ell;
+ int32_t alt_msl;
+ int8_t datum;
+ uint16_t sog;
+ uint16_t cog;
+ int16_t mag_var;
+ int16_t climb_rate;
+ int16_t heading_rate;
+ uint32_t h_error;
+ uint32_t v_error;
+ uint32_t t_error;
+ uint16_t h_v_error;
+
+#define get_u8(u) u = (msg[off]); off+= 1
+#define get_u16(u) u = (msg[off] << 8) | (msg[off + 1]); off+= 2
+#define get_u32(u) u = (msg[off] << 24) | (msg[off + 1] << 16) | (msg[off+2] << 8) | (msg[off+3]); off+= 4
+
+ get_u8(id);
+ get_u16(nav_valid);
+ get_u16(nav_type);
+ get_u16(week);
+ get_u32(tow);
+ get_u16(year);
+ get_u8(month);
+ get_u8(day);
+ get_u8(hour);
+ get_u8(minute);
+ get_u16(second);
+ get_u32(sat_list);
+ get_u32(lat);
+ get_u32(lon);
+ get_u32(alt_ell);
+ get_u32(alt_msl);
+ get_u8(datum);
+ get_u16(sog);
+ get_u16(cog);
+ get_u16(mag_var);
+ get_u16(climb_rate);
+ get_u16(heading_rate);
+ get_u32(h_error);
+ get_u32(v_error);
+ get_u32(t_error);
+ get_u16(h_v_error);
+
+
+ (void) mag_var;
+ (void) id;
+ printf ("Geodetic Navigation Data (41):\n");
+ printf ("\tNav valid %04x\n", nav_valid);
+ printf ("\tNav type %04x\n", nav_type);
+ printf ("\tWeek %5d", week);
+ printf (" TOW %9d", tow);
+ printf (" %4d-%2d-%2d %02d:%02d:%07.4f\n",
+ year, month, day,
+ hour, minute, second / 1000.0);
+ printf ("\tsats: %08x\n", sat_list);
+ printf ("\tlat: %g", lat / 1.0e7);
+ printf (" lon: %g", lon / 1.0e7);
+ printf (" alt_ell: %g", alt_ell / 100.0);
+ printf (" alt_msll: %g", alt_msl / 100.0);
+ printf (" datum: %d\n", datum);
+ printf ("\tground speed: %g", sog / 100.0);
+ printf (" course: %g", cog / 100.0);
+ printf (" climb: %g", climb_rate / 100.0);
+ printf (" heading rate: %g\n", heading_rate / 100.0);
+ printf ("\th error: %g", h_error / 100.0);
+ printf (" v error: %g", v_error / 100.0);
+ printf (" t error: %g", t_error / 100.0);
+ printf (" h vel error: %g\n", h_v_error / 100.0);
+ break;
+ }
+ case 4: {
+ int off = 4;
+ uint8_t id;
+ int16_t gps_week;
+ uint32_t gps_tow;
+ uint8_t channels;
+ int j, k;
+
+ get_u8(id);
+ get_u16(gps_week);
+ get_u32(gps_tow);
+ get_u8(channels);
+
+ (void) id;
+ printf ("Measured Tracker Data (4):\n");
+ printf ("GPS week: %d\n", gps_week);
+ printf ("GPS time of week: %d\n", gps_tow);
+ printf ("channels: %d\n", channels);
+ for (j = 0; j < 12; j++) {
+ uint8_t svid, azimuth, elevation;
+ uint16_t state;
+ uint8_t c_n[10];
+ get_u8(svid);
+ get_u8(azimuth);
+ get_u8(elevation);
+ get_u16(state);
+ for (k = 0; k < 10; k++) {
+ get_u8(c_n[k]);
+ }
+ printf ("Sat %3d:", svid);
+ printf (" aziumuth: %6.1f", azimuth * 1.5);
+ printf (" elevation: %6.1f", elevation * 0.5);
+ printf (" state: 0x%02x", state);
+ printf (" c_n:");
+ for (k = 0; k < 10; k++)
+ printf(" %3d", c_n[k]);
+ if (state & SIRF_SAT_STATE_ACQUIRED)
+ printf(" acq,");
+ if (state & SIRF_SAT_STATE_CARRIER_PHASE_VALID)
+ printf(" car,");
+ if (state & SIRF_SAT_BIT_SYNC_COMPLETE)
+ printf(" bit,");
+ if (state & SIRF_SAT_SUBFRAME_SYNC_COMPLETE)
+ printf(" sub,");
+ if (state & SIRF_SAT_CARRIER_PULLIN_COMPLETE)
+ printf(" pullin,");
+ if (state & SIRF_SAT_CODE_LOCKED)
+ printf(" code,");
+ if (state & SIRF_SAT_ACQUISITION_FAILED)
+ printf(" fail,");
+ if (state & SIRF_SAT_EPHEMERIS_AVAILABLE)
+ printf(" ephem,");
+ printf ("\n");
+ }
+ break;
+ }
+ default:
+ return;
+ printf ("%s %4d:", from, encoded_len);
+ for (i = 4; i < len - 4; i++) {
+ if (((i - 4) & 0xf) == 0)
+ printf("\n ");
+ printf (" %3d", msg[i]);
+ }
+ printf ("\n");
+ }
+}
+
+static uint8_t sirf_message[4096];
+static int sirf_message_len;
+static uint8_t sirf_in_message[4096];
+static int sirf_in_len;
+
+char
+ao_serial1_getchar(void)
+{
+ char c;
+ uint8_t uc;
+
+ while (input_head == input_tail) {
+ for (;;) {
+ input_tail = read(ao_gps_fd, input_queue, QUEUE_LEN);
+ if (input_tail < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ perror ("getchar");
+ exit (1);
+ }
+ input_head = 0;
+ break;
+ }
+ }
+ c = input_queue[input_head];
+ input_head = (input_head + 1) % QUEUE_LEN;
+ uc = c;
+ if (sirf_in_len || uc == 0xa0) {
+ if (sirf_in_len < 4096)
+ sirf_in_message[sirf_in_len++] = uc;
+ if (uc == 0xb3) {
+ check_sirf_message("recv", sirf_in_message, sirf_in_len);
+ sirf_in_len = 0;
+ }
+ }
+ return c;
+}
+
+
+void
+ao_serial1_putchar(char c)
+{
+ int i;
+ uint8_t uc = (uint8_t) c;
+
+ if (sirf_message_len || uc == 0xa0) {
+ if (sirf_message_len < 4096)
+ sirf_message[sirf_message_len++] = uc;
+ if (uc == 0xb3) {
+ check_sirf_message("send", sirf_message, sirf_message_len);
+ sirf_message_len = 0;
+ }
+ }
+ for (;;) {
+ i = write(ao_gps_fd, &c, 1);
+ if (i == 1) {
+ if ((uint8_t) c == 0xb3 || c == '\r') {
+/* static const struct timespec delay = {
+ .tv_sec = 0,
+ .tv_nsec = 100 * 1000 * 1000
+ };
+*/
+ tcdrain(ao_gps_fd);
+// nanosleep(&delay, NULL);
+ }
+ break;
+ }
+ if (i < 0 && (errno == EINTR || errno == EAGAIN))
+ continue;
+ perror("putchar");
+ exit(1);
+ }
+}
+
+#define AO_SERIAL_SPEED_4800 0
+#define AO_SERIAL_SPEED_57600 1
+
+static void
+ao_serial1_set_speed(uint8_t speed)
+{
+ int fd = ao_gps_fd;
+ struct termios termios;
+
+ tcdrain(fd);
+ tcgetattr(fd, &termios);
+ switch (speed) {
+ case AO_SERIAL_SPEED_4800:
+ cfsetspeed(&termios, B4800);
+ break;
+ case AO_SERIAL_SPEED_57600:
+ cfsetspeed(&termios, B57600);
+ break;
+ }
+ tcsetattr(fd, TCSAFLUSH, &termios);
+ tcflush(fd, TCIFLUSH);
+}
+
+#define ao_time() 0
+
+#include "ao_gps_print.c"
+#include "ao_gps_sirf.c"
+
+void
+ao_dump_state(void *wchan)
+{
+ double lat, lon;
+ int i;
+ if (wchan == &ao_gps_data)
+ ao_gps_print(&ao_gps_data);
+ else
+ ao_gps_tracking_print(&ao_gps_tracking_data);
+ putchar('\n');
+ return;
+ printf ("%02d:%02d:%02d",
+ ao_gps_data.hour, ao_gps_data.minute,
+ ao_gps_data.second);
+ printf (" nsat %d %svalid",
+ ao_gps_data.flags & AO_GPS_NUM_SAT_MASK,
+ ao_gps_data.flags & AO_GPS_VALID ? "" : "not ");
+ printf (" lat %g lon %g alt %d",
+ ao_gps_data.latitude / 1.0e7,
+ ao_gps_data.longitude / 1.0e7,
+ ao_gps_data.altitude);
+ printf (" speed %g climb %g course %d",
+ ao_gps_data.ground_speed / 100.0,
+ ao_gps_data.climb_rate / 100.0,
+ ao_gps_data.course * 2);
+ printf (" hdop %g h_error %d v_error %d",
+ ao_gps_data.hdop / 5.0,
+ ao_gps_data.h_error, ao_gps_data.v_error);
+ printf("\n");
+ printf ("\t");
+ for (i = 0; i < 12; i++)
+ printf (" %2d(%02d)",
+ ao_gps_tracking_data.sats[i].svid,
+ ao_gps_tracking_data.sats[i].c_n_1);
+ printf ("\n");
+}
+
+int
+ao_gps_open(const char *tty)
+{
+ struct termios termios;
+ int fd;
+
+ fd = open (tty, O_RDWR);
+ if (fd < 0)
+ return -1;
+
+ tcgetattr(fd, &termios);
+ cfmakeraw(&termios);
+ cfsetspeed(&termios, B4800);
+ tcsetattr(fd, TCSAFLUSH, &termios);
+
+ tcdrain(fd);
+ tcflush(fd, TCIFLUSH);
+ return fd;
+}
+
+#include <getopt.h>
+
+static const struct option options[] = {
+ { .name = "tty", .has_arg = 1, .val = 'T' },
+ { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+ fprintf(stderr, "usage: %s [--tty <tty-name>]\n", program);
+ exit(1);
+}
+
+int
+main (int argc, char **argv)
+{
+ char *tty = "/dev/ttyUSB0";
+ int c;
+
+ while ((c = getopt_long(argc, argv, "T:", options, NULL)) != -1) {
+ switch (c) {
+ case 'T':
+ tty = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+ ao_gps_fd = ao_gps_open(tty);
+ if (ao_gps_fd < 0) {
+ perror (tty);
+ exit (1);
+ }
+ ao_gps_setup();
+ ao_gps();
+}
diff --git a/src/test/ao_gps_test_skytraq.c b/src/test/ao_gps_test_skytraq.c
new file mode 100644
index 00000000..846daa94
--- /dev/null
+++ b/src/test/ao_gps_test_skytraq.c
@@ -0,0 +1,496 @@
+/*
+ * 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.
+ */
+
+#define AO_GPS_TEST
+#include "ao_host.h"
+#include <termios.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#define AO_GPS_NUM_SAT_MASK (0xf << 0)
+#define AO_GPS_NUM_SAT_SHIFT (0)
+
+#define AO_GPS_VALID (1 << 4)
+#define AO_GPS_RUNNING (1 << 5)
+#define AO_GPS_DATE_VALID (1 << 6)
+#define AO_GPS_COURSE_VALID (1 << 7)
+
+struct ao_gps_orig {
+ uint8_t year;
+ uint8_t month;
+ uint8_t day;
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
+ uint8_t flags;
+ int32_t latitude; /* degrees * 10⁷ */
+ int32_t longitude; /* degrees * 10⁷ */
+ int16_t altitude; /* m */
+ uint16_t ground_speed; /* cm/s */
+ uint8_t course; /* degrees / 2 */
+ uint8_t hdop; /* * 5 */
+ int16_t climb_rate; /* cm/s */
+ uint16_t h_error; /* m */
+ uint16_t 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 ao_gps_sat_orig {
+ uint8_t svid;
+ uint8_t c_n_1;
+};
+
+#define AO_MAX_GPS_TRACKING 12
+
+struct ao_gps_tracking_orig {
+ uint8_t channels;
+ struct ao_gps_sat_orig sats[AO_MAX_GPS_TRACKING];
+};
+
+#define ao_telemetry_location ao_gps_orig
+#define ao_telemetry_satellite ao_gps_tracking_orig
+#define ao_telemetry_satellite_info ao_gps_sat_orig
+
+void
+ao_mutex_get(uint8_t *mutex)
+{
+}
+
+void
+ao_mutex_put(uint8_t *mutex)
+{
+}
+
+static int
+ao_gps_fd;
+
+#if 0
+static void
+ao_dbg_char(char c)
+{
+ char line[128];
+ line[0] = '\0';
+ if (c < ' ') {
+ if (c == '\n')
+ sprintf (line, "\n");
+ else
+ sprintf (line, "\\%02x", ((int) c) & 0xff);
+ } else {
+ sprintf (line, "%c", c);
+ }
+ write(1, line, strlen(line));
+}
+#endif
+
+#define QUEUE_LEN 4096
+
+static char input_queue[QUEUE_LEN];
+int input_head, input_tail;
+
+#include <sys/time.h>
+
+int
+get_millis(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+static void
+check_skytraq_message(char *from, uint8_t *msg, int len)
+{
+ uint16_t encoded_len, encoded_cksum;
+ uint16_t cksum;
+ uint8_t id;
+ int i;
+
+// fwrite(msg, 1, len, stdout);
+ return;
+ if (msg[0] != 0xa0 || msg[1] != 0xa2) {
+ printf ("bad header\n");
+ return;
+ }
+ if (len < 7) {
+ printf("short\n");
+ return;
+ }
+ if (msg[len-1] != 0xb3 || msg[len-2] != 0xb0) {
+ printf ("bad trailer\n");
+ return;
+ }
+ encoded_len = (msg[2] << 8) | msg[3];
+ id = msg[4];
+/* printf ("%9d: %3d\n", get_millis(), id); */
+ if (encoded_len != len - 8) {
+ if (id != 52)
+ printf ("length mismatch (got %d, wanted %d)\n",
+ len - 8, encoded_len);
+ return;
+ }
+ encoded_cksum = (msg[len - 4] << 8) | msg[len-3];
+ cksum = 0;
+ for (i = 4; i < len - 4; i++)
+ cksum = (cksum + msg[i]) & 0x7fff;
+ if (encoded_cksum != cksum) {
+ printf ("cksum mismatch (got %04x wanted %04x)\n",
+ cksum, encoded_cksum);
+ return;
+ }
+ id = msg[4];
+ switch (id) {
+ case 41:{
+ int off = 4;
+
+ uint8_t id;
+ uint16_t nav_valid;
+ uint16_t nav_type;
+ uint16_t week;
+ uint32_t tow;
+ uint16_t year;
+ uint8_t month;
+ uint8_t day;
+ uint8_t hour;
+ uint8_t minute;
+ uint16_t second;
+ uint32_t sat_list;
+ int32_t lat;
+ int32_t lon;
+ int32_t alt_ell;
+ int32_t alt_msl;
+ int8_t datum;
+ uint16_t sog;
+ uint16_t cog;
+ int16_t mag_var;
+ int16_t climb_rate;
+ int16_t heading_rate;
+ uint32_t h_error;
+ uint32_t v_error;
+ uint32_t t_error;
+ uint16_t h_v_error;
+
+#define get_u8(u) u = (msg[off]); off+= 1
+#define get_u16(u) u = (msg[off] << 8) | (msg[off + 1]); off+= 2
+#define get_u32(u) u = (msg[off] << 24) | (msg[off + 1] << 16) | (msg[off+2] << 8) | (msg[off+3]); off+= 4
+
+ get_u8(id);
+ get_u16(nav_valid);
+ get_u16(nav_type);
+ get_u16(week);
+ get_u32(tow);
+ get_u16(year);
+ get_u8(month);
+ get_u8(day);
+ get_u8(hour);
+ get_u8(minute);
+ get_u16(second);
+ get_u32(sat_list);
+ get_u32(lat);
+ get_u32(lon);
+ get_u32(alt_ell);
+ get_u32(alt_msl);
+ get_u8(datum);
+ get_u16(sog);
+ get_u16(cog);
+ get_u16(mag_var);
+ get_u16(climb_rate);
+ get_u16(heading_rate);
+ get_u32(h_error);
+ get_u32(v_error);
+ get_u32(t_error);
+ get_u16(h_v_error);
+
+
+ (void) mag_var;
+ (void) id;
+ printf ("Geodetic Navigation Data (41):\n");
+ printf ("\tNav valid %04x\n", nav_valid);
+ printf ("\tNav type %04x\n", nav_type);
+ printf ("\tWeek %5d", week);
+ printf (" TOW %9d", tow);
+ printf (" %4d-%2d-%2d %02d:%02d:%07.4f\n",
+ year, month, day,
+ hour, minute, second / 1000.0);
+ printf ("\tsats: %08x\n", sat_list);
+ printf ("\tlat: %g", lat / 1.0e7);
+ printf (" lon: %g", lon / 1.0e7);
+ printf (" alt_ell: %g", alt_ell / 100.0);
+ printf (" alt_msll: %g", alt_msl / 100.0);
+ printf (" datum: %d\n", datum);
+ printf ("\tground speed: %g", sog / 100.0);
+ printf (" course: %g", cog / 100.0);
+ printf (" climb: %g", climb_rate / 100.0);
+ printf (" heading rate: %g\n", heading_rate / 100.0);
+ printf ("\th error: %g", h_error / 100.0);
+ printf (" v error: %g", v_error / 100.0);
+ printf (" t error: %g", t_error / 100.0);
+ printf (" h vel error: %g\n", h_v_error / 100.0);
+ break;
+ }
+ case 4: {
+ int off = 4;
+ uint8_t id;
+ int16_t gps_week;
+ uint32_t gps_tow;
+ uint8_t channels;
+ int j, k;
+
+ get_u8(id);
+ get_u16(gps_week);
+ get_u32(gps_tow);
+ get_u8(channels);
+
+ (void) id;
+ printf ("Measured Tracker Data (4):\n");
+ printf ("GPS week: %d\n", gps_week);
+ printf ("GPS time of week: %d\n", gps_tow);
+ printf ("channels: %d\n", channels);
+ for (j = 0; j < 12; j++) {
+ uint8_t svid, azimuth, elevation;
+ uint16_t state;
+ uint8_t c_n[10];
+ get_u8(svid);
+ get_u8(azimuth);
+ get_u8(elevation);
+ get_u16(state);
+ for (k = 0; k < 10; k++) {
+ get_u8(c_n[k]);
+ }
+ printf ("Sat %3d:", svid);
+ printf (" aziumuth: %6.1f", azimuth * 1.5);
+ printf (" elevation: %6.1f", elevation * 0.5);
+ printf (" state: 0x%02x", state);
+ printf (" c_n:");
+ for (k = 0; k < 10; k++)
+ printf(" %3d", c_n[k]);
+ if (state & SIRF_SAT_STATE_ACQUIRED)
+ printf(" acq,");
+ if (state & SIRF_SAT_STATE_CARRIER_PHASE_VALID)
+ printf(" car,");
+ if (state & SIRF_SAT_BIT_SYNC_COMPLETE)
+ printf(" bit,");
+ if (state & SIRF_SAT_SUBFRAME_SYNC_COMPLETE)
+ printf(" sub,");
+ if (state & SIRF_SAT_CARRIER_PULLIN_COMPLETE)
+ printf(" pullin,");
+ if (state & SIRF_SAT_CODE_LOCKED)
+ printf(" code,");
+ if (state & SIRF_SAT_ACQUISITION_FAILED)
+ printf(" fail,");
+ if (state & SIRF_SAT_EPHEMERIS_AVAILABLE)
+ printf(" ephem,");
+ printf ("\n");
+ }
+ break;
+ }
+ default:
+ return;
+ printf ("%s %4d:", from, encoded_len);
+ for (i = 4; i < len - 4; i++) {
+ if (((i - 4) & 0xf) == 0)
+ printf("\n ");
+ printf (" %3d", msg[i]);
+ }
+ printf ("\n");
+ }
+}
+
+static uint8_t skytraq_message[4096];
+static int skytraq_message_len;
+static uint8_t skytraq_in_message[4096];
+static int skytraq_in_len;
+
+char
+ao_serial1_getchar(void)
+{
+ char c;
+ uint8_t uc;
+
+ while (input_head == input_tail) {
+ for (;;) {
+ input_tail = read(ao_gps_fd, input_queue, QUEUE_LEN);
+ if (input_tail < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ perror ("getchar");
+ exit (1);
+ }
+ input_head = 0;
+ break;
+ }
+ }
+ c = input_queue[input_head];
+ input_head = (input_head + 1) % QUEUE_LEN;
+ uc = c;
+// printf ("c: %02x %c\n", uc, uc);
+ if (skytraq_in_len || uc == '$') {
+ if (skytraq_in_len < 4096)
+ skytraq_in_message[skytraq_in_len++] = uc;
+ if (uc == 0x0a) {
+ check_skytraq_message("recv", skytraq_in_message, skytraq_in_len);
+ skytraq_in_len = 0;
+ }
+ }
+ return c;
+}
+
+
+void
+ao_serial1_putchar(char c)
+{
+ int i;
+ uint8_t uc = (uint8_t) c;
+
+ if (skytraq_message_len || uc == 0xa0) {
+ if (skytraq_message_len < 4096)
+ skytraq_message[skytraq_message_len++] = uc;
+ if (uc == 0x0a) {
+ check_skytraq_message("send", skytraq_message, skytraq_message_len);
+ skytraq_message_len = 0;
+ }
+ }
+ for (;;) {
+ i = write(ao_gps_fd, &c, 1);
+ if (i == 1) {
+ if ((uint8_t) c == 0xb3 || c == '\r') {
+/* static const struct timespec delay = {
+ .tv_sec = 0,
+ .tv_nsec = 100 * 1000 * 1000
+ };
+*/
+ tcdrain(ao_gps_fd);
+// nanosleep(&delay, NULL);
+ }
+ break;
+ }
+ if (i < 0 && (errno == EINTR || errno == EAGAIN))
+ continue;
+ perror("putchar");
+ exit(1);
+ }
+}
+
+#define AO_SERIAL_SPEED_4800 0
+#define AO_SERIAL_SPEED_9600 1
+#define AO_SERIAL_SPEED_57600 2
+
+static void
+ao_serial1_set_speed(uint8_t speed)
+{
+ int fd = ao_gps_fd;
+ struct termios termios;
+
+ tcdrain(fd);
+ tcgetattr(fd, &termios);
+ switch (speed) {
+ case AO_SERIAL_SPEED_4800:
+ cfsetspeed(&termios, B4800);
+ break;
+ case AO_SERIAL_SPEED_9600:
+ cfsetspeed(&termios, B38400);
+ break;
+ case AO_SERIAL_SPEED_57600:
+ cfsetspeed(&termios, B57600);
+ break;
+ }
+ tcsetattr(fd, TCSAFLUSH, &termios);
+ tcflush(fd, TCIFLUSH);
+}
+
+#define ao_time() 0
+
+#include "ao_gps_print.c"
+#include "ao_gps_skytraq.c"
+
+void
+ao_dump_state(void *wchan)
+{
+ if (wchan == &ao_gps_data)
+ ao_gps_print(&ao_gps_data);
+ else
+ ao_gps_tracking_print(&ao_gps_tracking_data);
+ putchar('\n');
+ return;
+}
+
+int
+ao_gps_open(const char *tty)
+{
+ struct termios termios;
+ int fd;
+
+ fd = open (tty, O_RDWR);
+ if (fd < 0)
+ return -1;
+
+ tcgetattr(fd, &termios);
+ cfmakeraw(&termios);
+ cfsetspeed(&termios, B4800);
+ tcsetattr(fd, TCSAFLUSH, &termios);
+
+ tcdrain(fd);
+ tcflush(fd, TCIFLUSH);
+ return fd;
+}
+
+#include <getopt.h>
+
+static const struct option options[] = {
+ { .name = "tty", .has_arg = 1, .val = 'T' },
+ { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+ fprintf(stderr, "usage: %s [--tty <tty-name>]\n", program);
+ exit(1);
+}
+
+int
+main (int argc, char **argv)
+{
+ char *tty = "/dev/ttyUSB0";
+ int c;
+
+ while ((c = getopt_long(argc, argv, "T:", options, NULL)) != -1) {
+ switch (c) {
+ case 'T':
+ tty = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+ ao_gps_fd = ao_gps_open(tty);
+ if (ao_gps_fd < 0) {
+ perror (tty);
+ exit (1);
+ }
+ ao_gps();
+ return 0;
+}
diff --git a/src/test/plottest b/src/test/plottest
new file mode 100755
index 00000000..76af5ee7
--- /dev/null
+++ b/src/test/plottest
@@ -0,0 +1,16 @@
+gnuplot -persist << EOF
+set ylabel "altitude (m)"
+set y2label "velocity (m/s), acceleration(m/s²)"
+set xlabel "time (s)"
+set xtics border out nomirror
+set ytics border out nomirror
+set y2tics border out nomirror
+plot "$1" using 1:3 with lines axes x1y1 title "raw height",\
+"$1" using 1:5 with lines axes x1y2 title "raw accel",\
+"$1" using 1:9 with lines axes x1y1 title "height",\
+"$1" using 1:11 with lines axes x1y2 title "speed",\
+"$1" using 1:13 with lines axes x1y2 title "accel",\
+"$1" using 1:15 with lines axes x1y1 title "drogue",\
+"$1" using 1:17 with lines axes x1y1 title "main",\
+"$1" using 1:19 with lines axes x1y1 title "error"
+EOF
diff --git a/src/test/run-baro b/src/test/run-baro
new file mode 100755
index 00000000..21bab9fb
--- /dev/null
+++ b/src/test/run-baro
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+for i in "$@"; do
+./ao_flight_test_baro "$i" > run-out.baro
+
+#./ao_flight_test_accel "$i" > run-out.accel
+#"run-out.accel" using 1:9 with lines lt 4 axes x1y1 title "accel height",\
+#"run-out.accel" using 1:11 with lines lt 4 axes x1y2 title "accel speed",\
+#"run-out.accel" using 1:13 with lines lt 4 axes x1y2 title "accel accel",\
+#"run-out.accel" using 1:15 with lines lt 4 axes x1y1 title "accel drogue",\
+#"run-out.accel" using 1:17 with lines lt 4 axes x1y1 title "accel main",\
+#
+
+gnuplot << EOF
+set ylabel "altitude (m)"
+set y2label "velocity (m/s), acceleration(m/s²)"
+set xlabel "time (s)"
+set xtics border out nomirror
+set ytics border out nomirror
+set y2tics border out nomirror
+set title "$i"
+plot "run-out.baro" using 1:3 with lines lw 2 lt 1 axes x1y1 title "raw height",\
+"run-out.baro" using 1:5 with lines lw 2 lt 2 axes x1y2 title "raw accel",\
+"run-out.baro" using 1:9 with lines lt 3 axes x1y1 title "baro height",\
+"run-out.baro" using 1:11 with lines lt 4 axes x1y2 title "baro speed",\
+"run-out.baro" using 1:13 with lines lt 5 axes x1y2 title "baro accel",\
+"run-out.baro" using 1:17 with lines lt 6 axes x1y1 title "baro drogue",\
+"run-out.baro" using 1:19 with lines lt 7 axes x1y1 title "baro main"
+pause mouse close
+EOF
+done \ No newline at end of file
diff --git a/src/test/run-full b/src/test/run-full
new file mode 100755
index 00000000..50ab7b5b
--- /dev/null
+++ b/src/test/run-full
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+for i in "$@"; do
+./ao_flight_test "$i" > run-out.full
+
+#./ao_flight_test_accel "$i" > run-out.accel
+#"run-out.accel" using 1:9 with lines lt 4 axes x1y1 title "accel height",\
+#"run-out.accel" using 1:11 with lines lt 4 axes x1y2 title "accel speed",\
+#"run-out.accel" using 1:13 with lines lt 4 axes x1y2 title "accel accel",\
+#"run-out.accel" using 1:15 with lines lt 4 axes x1y1 title "accel drogue",\
+#"run-out.accel" using 1:17 with lines lt 4 axes x1y1 title "accel main",\
+#
+
+gnuplot << EOF
+set ylabel "altitude (m)"
+set y2label "velocity (m/s), acceleration(m/s²)"
+set xlabel "time (s)"
+set xtics border out nomirror
+set ytics border out nomirror
+set y2tics border out nomirror
+set title "$i"
+plot "run-out.full" using 1:3 with lines lw 2 lt 1 axes x1y1 title "raw height",\
+"run-out.full" using 1:5 with lines lw 2 lt 2 axes x1y2 title "raw accel",\
+"run-out.full" using 1:9 with lines lt 3 axes x1y1 title "full height",\
+"run-out.full" using 1:11 with lines lt 4 axes x1y2 title "full speed",\
+"run-out.full" using 1:13 with lines lt 5 axes x1y2 title "full accel",\
+"run-out.full" using 1:17 with lines lt 6 axes x1y1 title "full drogue",\
+"run-out.full" using 1:19 with lines lt 7 axes x1y1 title "full main"
+pause mouse close
+EOF
+done \ No newline at end of file
diff --git a/src/test/run-noisy b/src/test/run-noisy
new file mode 100755
index 00000000..2e3cddc7
--- /dev/null
+++ b/src/test/run-noisy
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+for i in "$@"; do
+./ao_flight_test_noisy_accel "$i" > run-out.noisy
+
+gnuplot << EOF
+set ylabel "altitude (m)"
+set y2label "velocity (m/s), acceleration(m/s²)"
+set xlabel "time (s)"
+set xtics border out nomirror
+set ytics border out nomirror
+set y2tics border out nomirror
+set title "$i"
+plot "run-out.noisy" using 1:3 with lines lw 2 lt 1 axes x1y1 title "raw height",\
+"run-out.noisy" using 1:5 with lines lw 2 lt 2 axes x1y2 title "raw accel",\
+"run-out.noisy" using 1:9 with lines lt 3 axes x1y1 title "noisy height",\
+"run-out.noisy" using 1:11 with lines lt 4 axes x1y2 title "noisy speed",\
+"run-out.noisy" using 1:13 with lines lt 5 axes x1y2 title "noisy accel",\
+"run-out.noisy" using 1:17 with lines lt 6 axes x1y1 title "noisy drogue",\
+"run-out.noisy" using 1:19 with lines lt 7 axes x1y1 title "noisy main"
+pause mouse close
+EOF
+done \ No newline at end of file
diff --git a/src/test/run-one b/src/test/run-one
new file mode 100755
index 00000000..8fa0787c
--- /dev/null
+++ b/src/test/run-one
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+DIR=~/misc/rockets/flights
+
+for i in "$@"; do
+case "$i" in
+ */*)
+ file="$i"
+ ;;
+ *)
+ file="$DIR/$i"
+ ;;
+esac
+./ao_flight_test "$file" > run-out.full
+./ao_flight_test_baro "$file" > run-out.baro
+
+#./ao_flight_test_accel "$file" > run-out.accel
+#"run-out.accel" using 1:9 with lines lt 4 axes x1y1 title "accel height",\
+#"run-out.accel" using 1:11 with lines lt 4 axes x1y2 title "accel speed",\
+#"run-out.accel" using 1:13 with lines lt 4 axes x1y2 title "accel accel",\
+#"run-out.accel" using 1:15 with lines lt 4 axes x1y1 title "accel drogue",\
+#"run-out.accel" using 1:17 with lines lt 4 axes x1y1 title "accel main",\
+#
+
+gnuplot << EOF
+set ylabel "altitude (m)"
+set y2label "velocity (m/s), acceleration(m/s²)"
+set xlabel "time (s)"
+set xtics border out nomirror
+set ytics border out nomirror
+set y2tics border out nomirror
+set title "$i"
+plot "run-out.full" using 1:3 with lines lw 2 lt 1 axes x1y1 title "raw height",\
+"run-out.full" using 1:5 with lines lw 2 lt 1 axes x1y2 title "raw accel",\
+"run-out.full" using 1:9 with lines lt 2 axes x1y1 title "full height",\
+"run-out.full" using 1:11 with lines lt 2 axes x1y2 title "full speed",\
+"run-out.full" using 1:13 with lines lt 2 axes x1y2 title "full accel",\
+"run-out.full" using 1:15 with lines lt 2 axes x1y1 title "full drogue",\
+"run-out.full" using 1:17 with lines lt 2 axes x1y1 title "full main", \
+"run-out.baro" using 1:9 with lines lt 3 axes x1y1 title "baro height",\
+"run-out.baro" using 1:11 with lines lt 3 axes x1y2 title "baro speed",\
+"run-out.baro" using 1:13 with lines lt 3 axes x1y2 title "baro accel",\
+"run-out.baro" using 1:15 with lines lt 3 axes x1y1 title "baro drogue",\
+"run-out.baro" using 1:17 with lines lt 3 axes x1y1 title "baro main"
+pause mouse close
+EOF
+done \ No newline at end of file
diff --git a/src/test/run-tests b/src/test/run-tests
new file mode 100755
index 00000000..4edecf1a
--- /dev/null
+++ b/src/test/run-tests
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+DIR=~/misc/rockets/flights
+
+expect_baro=2
+expect_full=5
+
+bad_baro=0
+bad_full=0
+while read flight description; do
+ if ./ao_flight_test_baro -s -i "$description" $DIR/$flight; then
+ :
+ else
+ : $((bad_baro++))
+ fi
+ if ./ao_flight_test -s -i "$description" $DIR/$flight; then
+ :
+ else
+ : $((bad_full++))
+ fi
+done < test-flights
+echo baro errors $bad_baro expected $expect_baro
+echo full errors $bad_full expected $expect_full
+: $((miss_baro = bad_baro > expect_baro ? bad_baro - expect_baro : expect_baro - bad_baro))
+: $((miss_full = bad_full > expect_full ? bad_full - expect_full : expect_full - bad_full))
+: $((miss = miss_baro + miss_full))
+exit $miss \ No newline at end of file
diff --git a/src/test/test-flights b/src/test/test-flights
new file mode 100644
index 00000000..12f62ea9
--- /dev/null
+++ b/src/test/test-flights
@@ -0,0 +1,70 @@
+2009-06-03-serial-003-flight-002.eeprom PSAS flight on research motor.Lots of ugly bumps.
+2009-06-05-serial-004-flight-008.eeprom Add June 2009 Lil Nuke flight
+2009-06-06-serial-004-flight-009.eeprom Another Lil nuke flight? 700m, subsonic.
+2009-06-14-serial-004-flight-010.eeprom Lil nuke flies again. 450m, subsonic.
+2009-06-20-serial-003-flight-003.eeprom Transonic flight. 2100m, single deploy.
+2009-07-04-serial-001-flight-006.eeprom Subsonic, 1100m, noisy descent.
+2009-07-18-serial-001-flight-007.eeprom Transonic, 1900m, noisy descent.
+2009-08-22-serial-001-flight-001.eeprom Transonic 2200m. noisy descent
+2009-08-22-serial-010-flight-001.eeprom Subsonic, laggy baro. 400m
+2009-09-05-serial-010-flight-002.eeprom Subsonic, fairly quick baro. 600m
+2009-09-05-serial-011-flight-001.eeprom Subsonic, 1800m. Early Ejection confuses apogee detect.
+2009-09-11-serial-008-flight-003.eeprom Subsonic, 200m, early ejection.
+2009-09-12-serial-002-flight-002.eeprom Subsonic. 1200m. Very nice baro velocity
+2009-09-12-serial-008-flight-005.eeprom Low flight. 220m, 100m/s.
+2009-09-12-serial-008-flight-007.eeprom Model flight.
+2009-09-19-serial-011-flight-002.eeprom Subsonic, but noisy baro.
+2009-10-03-serial-008-flight-009.eeprom Transonic baro bump. 1600m.
+2009-10-03-serial-008-flight-010.eeprom Barely transonic, but other baro bumps.
+2009-10-03-serial-009-flight-001.eeprom Add Ofest ToT flight (drag race with Candy Cane). subsonic, but a bit of baro noise.
+2009-10-03-serial-011-flight-003.eeprom Add Ofest G-spot flight. transonic bump, ugly looking data.
+2009-10-17-serial-009-flight-002.eeprom Clean looking subsonic flight, small baro slump at max-Q
+2009-11-14-serial-003-flight-005.eeprom Model flight.
+2009-11-14-serial-003-flight-006.eeprom Model flight.
+2009-11-21-serial-013-flight-001.eeprom ToT AMW J450ST with Jason Chamberlin strontium nitrate igniter
+2009-11-21-serial-013-flight-002.eeprom ToT - CTI 1009J420CL pro38 6xl
+2010-02-13-serial-051-flight-002.eeprom G-Spot at Albuquerque Rocket Society launch site in Rio Rancho, NM Cesaroni 229H255WT-14A
+2010-02-13-serial-052-flight-002.eeprom Add Grappler flight at OROC February model launch
+2010-02-28-serial-052-flight-003.eeprom LDDD Tillamook airport, 2010-02-28, H252
+2010-02-28-serial-052-flight-004.eeprom LDDD Tillamook airport, 2010-02-28, I161
+2010-03-06-serial-010-flight-004.eeprom Robert's Lil Nuke at Hudson Ranch on a CTI 108G57CL Pro29
+2010-03-06-serial-013-flight-003.eeprom Trick-O-Treat at Hudson Ranch on a J595BS
+2010-03-06-serial-051-flight-003.eeprom G-Spot at Hudson Ranch on an H180W (apogee early, fools ao_flight_test_baro)
+2010-03-06-serial-053-flight-004.eeprom Horizon Rebuilt at Hudson Ranch on a J530IM with MAWD as backup
+2010-05-08-serial-229-flight-002.eeprom First customer flight on a sparky.
+2010-05-28-serial-215-flight-002.eeprom Mike's L1 cert flight. Congrats!
+2010-05-28-serial-224-flight-001.eeprom Bill Mott's amram
+2010-05-29-serial-052-flight-005.eeprom LDDD on J335 at MHM
+2010-05-30-serial-010-flight-005.eeprom Robert's Lil Nuke on a 84G88 Smoky Sam at Mile High Mayhem 2010 Version 0.1 TeleMetrum with 5010 GPS board.
+2010-05-30-serial-051-flight-004.eeprom Bdale's G-Spot on a CTI 298H159 Green3 at Mile High Mayhem 2010
+2010-05-30-serial-224-flight-003.eeprom Bill Mott's L3 cert flight
+2010-05-30-serial-226-flight-001.eeprom Robert's RG-2 on a 159G54RL at Mile High Mayhem. Very noise accel data, early apogee (fools full)
+2010-05-31-serial-010-flight-006.eeprom Robert's Lil Nuke with a v0.1 TeleMetrum and 5010 GPS board flying on a 159G118 Blue Streak on the last day of Mile High Mayhem 2010
+2010-05-31-serial-216-flight-001.eeprom Sharp Stick in the Sky on an "I something" EX motor from James Russell
+2010-05-31-serial-219-flight-001.eeprom Candy Cane on CTI K300 at MHM. (accel noise fools full)
+2010-05-31-serial-227-flight-003.eeprom Mike's Rocket on J285 for successful L2 cert at MHM
+2010-06-05-serial-220-flight-001.eeprom Mini-mmuchness on 159G54RL, Sunday at MHM 2010
+2010-06-26-serial-209-flight-003.eeprom Tripoli Colorado Spring Fling with COSROCS at the Buffalo Ranch
+2010-06-26-serial-215-flight-004.eeprom Tripoli Colorado Spring Fling with COSROCS at the Buffalo Ranch (accel noise fools full)
+2010-06-26-serial-220-flight-002.eeprom Mini Mmuchness on CTI H120CL to 1975m, OROC June 2010
+2010-06-26-serial-226-flight-002.eeprom Tripoli Colorado Spring Fling with COSROCS at the Buffalo Ranch
+2010-06-27-serial-221-flight-002.eeprom PSAS LV2c on N2000 at OROC june launch
+2010-07-17-serial-230-flight-001.eeprom Mike Ward's Level 1 cert flight on H225
+2010-07-18-serial-219-flight-002.eeprom MMuchness on M1230 for successful L3 cert flight
+2010-08-07-serial-216-flight-003.eeprom Sharp stick on I300T at Metra's august launch. Main out at apogee.
+2010-08-07-serial-220-flight-003.eeprom Mini-mmuchness flight at Metra after debconf. Unstable on G80. ABNORMAL FLIGHT. (fools full)
+2010-08-12-serial-236-flight-001.eeprom Edgar's L1 flight, Madcow Momba on Aerotech H128 (early apogee charge fools baro)
+2010-08-21-serial-010-flight-007.eeprom Robert's Lil Nuke, flying on a CTI Pro29 2-grain 110G250 Vmax. Just awesome! PFND.
+2010-08-21-serial-224-flight-004.eeprom Anthony Towns' LDDD clone, successful L1 cert on a CTI Pro38 2-grain 266H125 classic. Airframe was set up for apogee-only with motor based ejection. The BP charge was larger than necessary, caused nose cone to snap back against aft airframe. Minor damage to leading edge of aft airframe, big chunk of the nose cone skin cracked away. Same problem Bdale saw on Sharp Stick's nosecone, also from Performance Rocketry, where there was apparently an air bubble below the gel coat. Determination was that damage was cosmetic and would not affect cert... confirmed by successful L2 later in the day!
+2010-08-21-serial-224-flight-005.eeprom Anthony Towns' LDDD clone, successful L2 cert on a CTI Pro38 5-grain 58J357 blue streak. Perfect drogueless dual deploy! 0.8-0.9 grams aft charge and about 1.0 grams forward. Flew without ground testing charges due to time pressure before waiver closed for the day. About as close to the waiver as you'd ever want to be on a cert flight, again demonstrating that OpenRocket under-estimates apogee ... approximately 6800 ft predicted...
+2010-08-21-serial-233-flight-002.eeprom Tim van Milligan's 5.5" L2 airframe, flying on a CTI Pro38 6-grain 774J410 red, TM indicated a problem with the apogee igniter continuity, noticed during countdown, turned out to be a loose wire! Perfect dual deploy flight once that was fixed.
+2010-09-03-serial-051-flight-005.eeprom G-spot on an old Aerotech H125-20W single use 29mm motor. It appears the ejection at apogee actually happened much later, perhaps as much as 10 seconds late!
+2010-09-03-serial-215-flight-005.eeprom Horizon Rebuild on a K490 Green3 reload. PFND. About 1.4 grams each end. (accel noise fools full)
+2010-09-24-serial-236-flight-006.eeprom LDDD on I236 in Sheridan on 2010-09-24 to 1216m
+2010-09-25-serial-223-flight-001.eeprom Bdale's 10" Goblin, flying on a CTI Pro75 M1300 Imax dual thrust during Chili Blaster 2 at Hudson Ranch. First flight after rebuild to do dual deploy from one bay with ARRD and 15' surplus "+ sign" parachute.
+2010-10-17-serial-215-flight-006.eeprom Horizon Rebuild on CTI J595BS at Tripoli Colorado launch site near Hartsel, CO
+2011-01-30-serial-056-flight-001.eeprom Group project 5.5" Polecat Thumper airframe built in the Quay West Suites in Brisbane, Australia, during LCA 2011 by Bdale, Keith, Mike Beattie, and Anthony Towns. Flown at QRS club launch on an Aerotech J315R 54mm 2-grain motor.
+2011-01-30-serial-250-flight-002.eeprom Group project 5.5" Polecat Thumper airframe built in the Quay West Suites in Brisbane, Australia, during LCA 2011 by Bdale, Keith, Mike Beattie, and Anthony Towns. Flown at QRS club launch on an Aerotech J315R 54mm 2-grain motor.
+2011-02-19-serial-215-flight-007.eeprom Horizon Rebuild on a CTI Pro38 6xl J600R at Hudson Ranch 1.0 grams BP rear, 1.5 grams front Perfect drogueless dual deploy flight with no damage!
+2011-02-19-serial-216-flight-006.eeprom Sharp Stick on a CTI Pro38 3-grain I345WT at Hudson Ranch Added more kevlar to the aft end, flew with 1.3 grams BP rear and 1.0 front. Perfect drogueless dual deploy flight, and no damage!
+2011-02-19-serial-286-flight-001.eeprom Vertical Assault on a CTI Pro38 6xl J595BS at Hudson Ranch 1.5 grams BP rear, 1.8 grams BP front Perfect drogueless dual deploy flight, with no damage!