altos: Split up flight code into separate flight/sample/kalman bits
authorKeith Packard <keithp@keithp.com>
Tue, 29 Mar 2011 00:54:44 +0000 (17:54 -0700)
committerKeith Packard <keithp@keithp.com>
Tue, 29 Mar 2011 00:54:44 +0000 (17:54 -0700)
The flight code mashed together data processing, filtering and actual
flight managament into one giant pile. Split things up so that we
have:

 ao_sample.c: Sensor data processing. Reads the ring, handles calibration
 ao_kalman.c: Filter the data to track the accel/speed/height values
 ao_flight.c: Flight state management, specific to rocketry.

The plan is to re-use ao_sample.c and ao_kalman.c for hardware not
specifically designed for rocketry, like TeleNano.

Signed-off-by: Keith Packard <keithp@keithp.com>
src/Makefile.proto
src/ao.h
src/ao_adc.c
src/ao_config.c
src/ao_flight.c
src/ao_flight_test.c
src/ao_kalman.c [new file with mode: 0644]
src/ao_log_big.c
src/ao_sample.c [new file with mode: 0644]
src/ao_stdio.c

index 85c0c46e4c1ea7684bf79fd75d57a7d32365f711..8dd21a64f6d9186665b97b4c6ede394040fe1b2f 100644 (file)
@@ -147,6 +147,8 @@ SKY_DRIVER_SRC = \
 #
 TM_TASK_SRC = \
        ao_flight.c \
+       ao_sample.c \
+       ao_kalman.c \
        ao_log.c \
        ao_log_big.c \
        ao_report.c \
@@ -179,6 +181,8 @@ TMINI_DRIVER_SRC = \
 
 TMINI_TASK_SRC = \
        ao_flight.c \
+       ao_sample.c \
+       ao_kalman.c \
        ao_log.c \
        ao_log_tiny.c \
        ao_report.c \
@@ -207,6 +211,8 @@ TNANO_DRIVER_SRC = \
 
 TNANO_TASK_SRC = \
        ao_flight.c \
+       ao_sample.c \
+       ao_kalman.c \
        ao_log.c \
        ao_log_tiny.c \
        ao_report.c \
index b88bef1f0adc5707de57ab47c2167236b08576c6..42c3edda85ec41c1121470a673077b6dc7a2995c 100644 (file)
--- a/src/ao.h
+++ b/src/ao.h
@@ -695,22 +695,10 @@ enum ao_flight_state {
        ao_flight_invalid = 9
 };
 
-extern __data uint8_t                  ao_flight_adc;
 extern __pdata enum ao_flight_state    ao_flight_state;
-extern __pdata uint16_t                        ao_flight_tick;
-extern __xdata int16_t                 ao_ground_pres;
-extern __pdata int16_t                 ao_ground_accel;
+
 extern __pdata uint16_t                        ao_launch_time;
 extern __xdata uint8_t                 ao_flight_force_idle;
-extern __pdata int16_t                 ao_ground_height;
-extern __pdata int16_t                 ao_max_height;
-extern __pdata int16_t                 ao_height;      /* meters */
-extern __pdata int16_t                 ao_speed;       /* m/s * 16 */
-extern __pdata int16_t                 ao_accel;       /* m/s² * 16 */
-
-#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))
 
 /* Flight thread */
 void
@@ -720,6 +708,121 @@ ao_flight(void);
 void
 ao_flight_init(void);
 
+/*
+ * ao_sample.c
+ */
+
+/*
+ * Barometer calibration
+ *
+ * We directly sample the barometer. The specs say:
+ *
+ * Pressure range: 15-115 kPa
+ * Voltage at 115kPa: 2.82
+ * Output scale: 27mV/kPa
+ *
+ * If we want to detect launch with the barometer, we need
+ * a large enough bump to not be fooled by noise. At typical
+ * launch elevations (0-2000m), a 200Pa pressure change cooresponds
+ * to about a 20m elevation change. This is 5.4mV, or about 3LSB.
+ * As all of our calculations are done in 16 bits, we'll actually see a change
+ * of 16 times this though
+ *
+ * 27 mV/kPa * 32767 / 3300 counts/mV = 268.1 counts/kPa
+ */
+
+/* Accelerometer calibration
+ *
+ * We're sampling the accelerometer through a resistor divider which
+ * consists of 5k and 10k resistors. This multiplies the values by 2/3.
+ * That goes into the cc1111 A/D converter, which is running at 11 bits
+ * of precision with the bits in the MSB of the 16 bit value. Only positive
+ * values are used, so values should range from 0-32752 for 0-3.3V. The
+ * specs say we should see 40mV/g (uncalibrated), multiply by 2/3 for what
+ * the A/D converter sees (26.67 mV/g). We should see 32752/3300 counts/mV,
+ * for a final computation of:
+ *
+ * 26.67 mV/g * 32767/3300 counts/mV = 264.8 counts/g
+ *
+ * Zero g was measured at 16000 (we would expect 16384).
+ * Note that this value is only require to tell if the
+ * rocket is standing upright. Once that is determined,
+ * the value of the accelerometer is averaged for 100 samples
+ * to find the resting accelerometer value, which is used
+ * for all further flight computations
+ */
+
+#define GRAVITY 9.80665
+
+/*
+ * Above this height, the baro sensor doesn't work
+ */
+#define AO_MAX_BARO_HEIGHT     12000
+
+/*
+ * Above this speed, baro measurements are unreliable
+ */
+#define AO_MAX_BARO_SPEED      200
+
+#define ACCEL_NOSE_UP  (ao_accel_2g >> 2)
+
+/*
+ * Speed and acceleration are scaled by 16 to provide a bit more
+ * resolution while still having reasonable range. Note that this
+ * limits speed to 2047m/s (around mach 6) and acceleration to
+ * 2047m/s² (over 200g)
+ */
+
+#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))
+
+extern __pdata uint16_t        ao_sample_tick;         /* time of last data */
+extern __pdata int16_t ao_sample_pres;         /* most recent pressure sensor reading */
+extern __pdata int16_t ao_sample_alt;          /* MSL of ao_sample_pres */
+extern __pdata int16_t ao_sample_height;       /* AGL of ao_sample_pres */
+extern __data uint8_t  ao_sample_adc;          /* Ring position of last processed sample */
+
+#if HAS_ACCEL
+extern __pdata int16_t ao_sample_accel;        /* most recent accel sensor reading */
+#endif
+
+extern __xdata int16_t ao_ground_pres;         /* startup pressure */
+extern __xdata int16_t ao_ground_height;       /* MSL of ao_ground_pres */
+
+#if HAS_ACCEL
+extern __xdata int16_t ao_ground_accel;        /* startup acceleration */
+extern __xdata int16_t         ao_accel_2g;            /* factory accel calibration */
+extern __xdata int32_t ao_accel_scale;         /* sensor to m/s² conversion */
+#endif
+
+void ao_sample_init(void);
+
+/* returns FALSE in preflight mode, TRUE in flight mode */
+uint8_t ao_sample(void);
+
+/*
+ * ao_kalman.c
+ */
+
+#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)
+
+extern __pdata int16_t                 ao_height;      /* meters */
+extern __pdata int16_t                 ao_speed;       /* m/s * 16 */
+extern __pdata int16_t                 ao_accel;       /* m/s² * 16 */
+extern __pdata int16_t                 ao_max_height;  /* max of ao_height */
+
+extern __pdata int16_t                 ao_error_h;
+extern __pdata int16_t                 ao_error_h_sq_avg;
+
+#if HAS_ACCEL
+extern __pdata int16_t                 ao_error_a;
+#endif
+
+void ao_kalman(void);
+
 /*
  * ao_report.c
  */
@@ -1054,7 +1157,7 @@ extern __xdata uint8_t ao_stdin_ready;
 void
 ao_add_stdio(char (*pollchar)(void),
             void (*putchar)(char) __reentrant,
-            void (*flush)(void));
+            void (*flush)(void)) __reentrant;
 
 /*
  * ao_ignite.c
index d77e775331141b7bbdc38ce37aadd78dcf174476..48568383ad804f8721de6a7acb7af5b5af1d0542 100644 (file)
@@ -41,7 +41,7 @@ ao_adc_poll(void)
 void
 ao_adc_get(__xdata struct ao_adc *packet)
 {
-       uint8_t i = ao_adc_ring_prev(ao_flight_adc);
+       uint8_t i = ao_adc_ring_prev(ao_sample_adc);
        memcpy(packet, &ao_adc_ring[i], sizeof (struct ao_adc));
 }
 
index 771b21a119781a3d2dbb2ee3d95491555a614e8a..319febb94891b24be2b98fc37b004376b23d6afe 100644 (file)
@@ -215,10 +215,10 @@ ao_config_accel_calibrate_auto(char *orientation) __reentrant
        puts("Calibrating..."); flush();
        i = ACCEL_CALIBRATE_SAMPLES;
        accel_total = 0;
-       cal_adc_ring = ao_flight_adc;
+       cal_adc_ring = ao_sample_adc;
        while (i) {
-               ao_sleep(DATA_TO_XDATA(&ao_flight_adc));
-               while (i && cal_adc_ring != ao_flight_adc) {
+               ao_sleep(DATA_TO_XDATA(&ao_sample_adc));
+               while (i && cal_adc_ring != ao_sample_adc) {
                        accel_total += (int32_t) ao_adc_ring[cal_adc_ring].accel;
                        cal_adc_ring = ao_adc_ring_next(cal_adc_ring);
                        i--;
index 88f0544f83a076ce4a702a966c23c0703c3376a9..94fbf1784bf4cdbdc9885b5ad6054ac983fcafc6 100644 (file)
 /* Main flight thread. */
 
 __pdata enum ao_flight_state   ao_flight_state;        /* current flight state */
-__pdata uint16_t               ao_flight_tick;         /* time of last data */
-__pdata uint16_t               ao_flight_prev_tick;    /* time of previous data */
-__xdata int16_t                        ao_ground_pres;         /* startup pressure */
 __pdata uint16_t               ao_launch_tick;         /* time of launch detect */
-#if HAS_ACCEL
-__pdata int16_t                        ao_ground_accel;        /* startup acceleration */
-#endif
 
 /*
  * track min/max data over a long interval to detect
@@ -50,59 +44,7 @@ __pdata uint16_t             ao_interval_end;
 __pdata int16_t                        ao_interval_min_height;
 __pdata int16_t                        ao_interval_max_height;
 
-__data uint8_t ao_flight_adc;
-__pdata int16_t ao_raw_pres;
-__xdata uint8_t ao_flight_force_idle;
-
-#if HAS_ACCEL
-__pdata int16_t ao_raw_accel, ao_raw_accel_prev;
-__pdata int16_t ao_accel_2g;
-
-/* Accelerometer calibration
- *
- * We're sampling the accelerometer through a resistor divider which
- * consists of 5k and 10k resistors. This multiplies the values by 2/3.
- * That goes into the cc1111 A/D converter, which is running at 11 bits
- * of precision with the bits in the MSB of the 16 bit value. Only positive
- * values are used, so values should range from 0-32752 for 0-3.3V. The
- * specs say we should see 40mV/g (uncalibrated), multiply by 2/3 for what
- * the A/D converter sees (26.67 mV/g). We should see 32752/3300 counts/mV,
- * for a final computation of:
- *
- * 26.67 mV/g * 32767/3300 counts/mV = 264.8 counts/g
- *
- * Zero g was measured at 16000 (we would expect 16384).
- * Note that this value is only require to tell if the
- * rocket is standing upright. Once that is determined,
- * the value of the accelerometer is averaged for 100 samples
- * to find the resting accelerometer value, which is used
- * for all further flight computations
- */
-
-#define GRAVITY 9.80665
-
-#define ACCEL_NOSE_UP  (ao_accel_2g >> 2)
-
-#endif
-
-/*
- * Barometer calibration
- *
- * We directly sample the barometer. The specs say:
- *
- * Pressure range: 15-115 kPa
- * Voltage at 115kPa: 2.82
- * Output scale: 27mV/kPa
- *
- * If we want to detect launch with the barometer, we need
- * a large enough bump to not be fooled by noise. At typical
- * launch elevations (0-2000m), a 200Pa pressure change cooresponds
- * to about a 20m elevation change. This is 5.4mV, or about 3LSB.
- * As all of our calculations are done in 16 bits, we'll actually see a change
- * of 16 times this though
- *
- * 27 mV/kPa * 32767 / 3300 counts/mV = 268.1 counts/kPa
- */
+__xdata uint8_t                        ao_flight_force_idle;
 
 /* We also have a clock, which can be used to sanity check things in
  * case of other failures
@@ -110,228 +52,6 @@ __pdata int16_t ao_accel_2g;
 
 #define BOOST_TICKS_MAX        AO_SEC_TO_TICKS(15)
 
-#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)
-
-#include "ao_kalman.h"
-
-__pdata int16_t                        ao_ground_height;
-__pdata int16_t                        ao_height;
-__pdata int16_t                        ao_speed;
-__pdata int16_t                        ao_accel;
-__pdata int16_t                        ao_max_height;
-
-static __pdata int32_t         ao_k_height;
-static __pdata int32_t         ao_k_speed;
-static __pdata int32_t         ao_k_accel;
-
-#define AO_K_STEP_100          to_fix16(0.01)
-#define AO_K_STEP_2_2_100      to_fix16(0.00005)
-
-#define AO_K_STEP_10           to_fix16(0.1)
-#define AO_K_STEP_2_2_10       to_fix16(0.005)
-
-/*
- * Above this height, the baro sensor doesn't work
- */
-#define AO_MAX_BARO_HEIGHT     12000
-
-/*
- * Above this speed, baro measurements are unreliable
- */
-#define AO_MAX_BARO_SPEED      200
-
-static void
-ao_kalman_predict(void)
-{
-#ifdef AO_FLIGHT_TEST
-       if (ao_flight_tick - ao_flight_prev_tick > 5) {
-               ao_k_height += ((int32_t) ao_speed * AO_K_STEP_10 +
-                               (int32_t) ao_accel * AO_K_STEP_2_2_10) >> 4;
-               ao_k_speed += (int32_t) ao_accel * AO_K_STEP_10;
-
-               return;
-       }
-       if (ao_flight_debug) {
-               printf ("predict speed %g + (%g * %g) = %g\n",
-                       ao_k_speed / (65536.0 * 16.0), ao_accel / 16.0, AO_K_STEP_100 / 65536.0,
-                       (ao_k_speed + (int32_t) ao_accel * AO_K_STEP_100) / (65536.0 * 16.0));
-       }
-#endif
-       ao_k_height += ((int32_t) ao_speed * AO_K_STEP_100 +
-                       (int32_t) ao_accel * AO_K_STEP_2_2_100) >> 4;
-       ao_k_speed += (int32_t) ao_accel * AO_K_STEP_100;
-}
-
-static __pdata int16_t ao_error_h;
-static __pdata int16_t ao_raw_alt;
-static __pdata int16_t ao_raw_height;
-static __pdata int16_t ao_error_h_sq_avg;
-
-static void
-ao_kalman_err_height(void)
-{
-       int16_t e;
-       int16_t height_distrust;
-#if HAS_ACCEL
-       int16_t speed_distrust;
-#endif
-
-       ao_error_h = ao_raw_height - (int16_t) (ao_k_height >> 16);
-
-       e = ao_error_h;
-       if (e < 0)
-               e = -e;
-       if (e > 127)
-               e = 127;
-#if HAS_ACCEL
-       ao_error_h_sq_avg -= ao_error_h_sq_avg >> 2;
-       ao_error_h_sq_avg += (e * e) >> 2;
-#else
-       ao_error_h_sq_avg -= ao_error_h_sq_avg >> 4;
-       ao_error_h_sq_avg += (e * e) >> 4;
-#endif
-
-       height_distrust = ao_raw_height - AO_MAX_BARO_HEIGHT;
-#if HAS_ACCEL
-       /* speed is stored * 16, but we need to ramp between 200 and 328, so
-        * we want to multiply by 2. The result is a shift by 3.
-        */
-       speed_distrust = (ao_speed - AO_MS_TO_SPEED(AO_MAX_BARO_SPEED)) >> (4 - 1);
-       if (speed_distrust <= 0)
-               speed_distrust = 0;
-       else if (speed_distrust > height_distrust)
-               height_distrust = speed_distrust;
-#endif
-       if (height_distrust <= 0)
-               height_distrust = 0;
-
-       if (height_distrust) {
-#ifdef AO_FLIGHT_TEST
-               int     old_ao_error_h = ao_error_h;
-#endif
-               if (height_distrust > 0x100)
-                       height_distrust = 0x100;
-               ao_error_h = (int16_t) (((int32_t) ao_error_h * (0x100 - height_distrust)) >> 8);
-#ifdef AO_FLIGHT_TEST
-               if (ao_flight_debug) {
-                       printf("over height %g over speed %g distrust: %g height: error %d -> %d\n",
-                              (double) (ao_raw_height - AO_MAX_BARO_HEIGHT),
-                              (ao_speed - AO_MS_TO_SPEED(AO_MAX_BARO_SPEED)) / 16.0,
-                              height_distrust / 256.0,
-                              old_ao_error_h, ao_error_h);
-               }
-#endif
-       }
-}
-
-static void
-ao_kalman_correct_baro(void)
-{
-       ao_kalman_err_height();
-#ifdef AO_FLIGHT_TEST
-       if (ao_flight_tick - ao_flight_prev_tick > 5) {
-               ao_k_height += (int32_t) AO_BARO_K0_10 * ao_error_h;
-               ao_k_speed  += (int32_t) AO_BARO_K1_10 * ao_error_h;
-               ao_k_accel  += (int32_t) AO_BARO_K2_10 * ao_error_h;
-               return;
-       }
-#endif
-       ao_k_height += (int32_t) AO_BARO_K0_100 * ao_error_h;
-       ao_k_speed  += (int32_t) AO_BARO_K1_100 * ao_error_h;
-       ao_k_accel  += (int32_t) AO_BARO_K2_100 * ao_error_h;
-}
-
-#if HAS_ACCEL
-static __pdata int16_t ao_error_a;
-static __pdata int32_t ao_accel_scale;
-
-static void
-ao_kalman_err_accel(void)
-{
-       int32_t accel;
-
-       accel = (ao_ground_accel - ao_raw_accel) * ao_accel_scale;
-
-       /* Can't use ao_accel here as it is the pre-prediction value still */
-       ao_error_a = (accel - ao_k_accel) >> 16;
-}
-
-static void
-ao_kalman_correct_both(void)
-{
-       ao_kalman_err_height();
-       ao_kalman_err_accel();
-
-#ifdef AO_FLIGHT_TEST
-       if (ao_flight_tick - ao_flight_prev_tick > 5) {
-               if (ao_flight_debug) {
-                       printf ("correct speed %g + (%g * %g) + (%g * %g) = %g\n",
-                               ao_k_speed / (65536.0 * 16.0),
-                               (double) ao_error_h, AO_BOTH_K10_10 / 65536.0,
-                               (double) ao_error_a, AO_BOTH_K11_10 / 65536.0,
-                               (ao_k_speed +
-                                (int32_t) AO_BOTH_K10_10 * ao_error_h +
-                                (int32_t) AO_BOTH_K11_10 * ao_error_a) / (65536.0 * 16.0));
-               }
-               ao_k_height +=
-                       (int32_t) AO_BOTH_K00_10 * ao_error_h +
-                       (int32_t) AO_BOTH_K01_10 * ao_error_a;
-               ao_k_speed +=
-                       (int32_t) AO_BOTH_K10_10 * ao_error_h +
-                       (int32_t) AO_BOTH_K11_10 * ao_error_a;
-               ao_k_accel +=
-                       (int32_t) AO_BOTH_K20_10 * ao_error_h +
-                       (int32_t) AO_BOTH_K21_10 * ao_error_a;
-               return;
-       }
-       if (ao_flight_debug) {
-               printf ("correct speed %g + (%g * %g) + (%g * %g) = %g\n",
-                       ao_k_speed / (65536.0 * 16.0),
-                       (double) ao_error_h, AO_BOTH_K10_100 / 65536.0,
-                       (double) ao_error_a, AO_BOTH_K11_100 / 65536.0,
-                       (ao_k_speed +
-                        (int32_t) AO_BOTH_K10_100 * ao_error_h +
-                        (int32_t) AO_BOTH_K11_100 * ao_error_a) / (65536.0 * 16.0));
-       }
-#endif
-       ao_k_height +=
-               (int32_t) AO_BOTH_K00_100 * ao_error_h +
-               (int32_t) AO_BOTH_K01_100 * ao_error_a;
-       ao_k_speed +=
-               (int32_t) AO_BOTH_K10_100 * ao_error_h +
-               (int32_t) AO_BOTH_K11_100 * ao_error_a;
-       ao_k_accel +=
-               (int32_t) AO_BOTH_K20_100 * ao_error_h +
-               (int32_t) AO_BOTH_K21_100 * ao_error_a;
-}
-
-#ifdef FORCE_ACCEL
-static void
-ao_kalman_correct_accel(void)
-{
-       ao_kalman_err_accel();
-
-       if (ao_flight_tick - ao_flight_prev_tick > 5) {
-               ao_k_height +=(int32_t) AO_ACCEL_K0_10 * ao_error_a;
-               ao_k_speed  += (int32_t) AO_ACCEL_K1_10 * ao_error_a;
-               ao_k_accel  += (int32_t) AO_ACCEL_K2_10 * ao_error_a;
-               return;
-       }
-       ao_k_height += (int32_t) AO_ACCEL_K0_100 * ao_error_a;
-       ao_k_speed  += (int32_t) AO_ACCEL_K1_100 * ao_error_a;
-       ao_k_accel  += (int32_t) AO_ACCEL_K2_100 * ao_error_a;
-}
-#endif
-#endif /* HAS_ACCEL */
-
-__xdata int32_t ao_raw_pres_sum;
-
-#ifdef HAS_ACCEL
-__xdata int32_t ao_raw_accel_sum;
-#endif
-
 /* Landing is detected by getting constant readings from both pressure and accelerometer
  * for a fairly long time (AO_INTERVAL_TICKS)
  */
@@ -342,162 +62,20 @@ __xdata int32_t ao_raw_accel_sum;
 void
 ao_flight(void)
 {
-       __pdata static uint16_t nsamples = 0;
-
-       ao_flight_adc = ao_adc_head;
-       ao_raw_pres = 0;
-#if HAS_ACCEL
-       ao_raw_accel_prev = 0;
-       ao_raw_accel = 0;
-#endif
-       ao_flight_tick = 0;
+       ao_sample_init();
+       ao_flight_state = ao_flight_startup;
        for (;;) {
-               ao_wakeup(DATA_TO_XDATA(&ao_flight_adc));
-               ao_sleep(DATA_TO_XDATA(&ao_adc_head));
-               while (ao_flight_adc != ao_adc_head) {
-                       __xdata struct ao_adc *ao_adc;
-                       ao_flight_prev_tick = ao_flight_tick;
-
-                       /* Capture a sample */
-                       ao_adc = &ao_adc_ring[ao_flight_adc];
-                       ao_flight_tick = ao_adc->tick;
-                       ao_raw_pres = ao_adc->pres;
-                       ao_raw_alt = ao_pres_to_altitude(ao_raw_pres);
-                       ao_raw_height = ao_raw_alt - ao_ground_height;
-#if HAS_ACCEL
-                       ao_raw_accel = ao_adc->accel;
-#if HAS_ACCEL_REF
-                       /*
-                        * Ok, the math here is a bit tricky.
-                        *
-                        * ao_raw_accel:  ADC output for acceleration
-                        * ao_accel_ref:  ADC output for the 5V reference.
-                        * ao_cook_accel: Corrected acceleration value
-                        * Vcc:           3.3V supply to the CC1111
-                        * Vac:           5V supply to the accelerometer
-                        * accel:         input voltage to accelerometer ADC pin
-                        * ref:           input voltage to 5V reference ADC pin
-                        *
-                        *
-                        * Measured acceleration is ratiometric to Vcc:
-                        *
-                        *     ao_raw_accel   accel
-                        *     ------------ = -----
-                        *        32767        Vcc
-                        *
-                        * Measured 5v reference is also ratiometric to Vcc:
-                        *
-                        *     ao_accel_ref    ref
-                        *     ------------ = -----
-                        *        32767        Vcc
-                        *
-                        *
-                        *      ao_accel_ref = 32767 * (ref / Vcc)
-                        *
-                        * Acceleration is measured ratiometric to the 5V supply,
-                        * so what we want is:
-                        *
-                        *      ao_cook_accel    accel
-                        *      ------------- =  -----
-                        *          32767         ref
-                        *
-                        *
-                        *                      accel    Vcc
-                        *                    = ----- *  ---
-                        *                       Vcc     ref
-                        *
-                        *                      ao_raw_accel       32767
-                        *                    = ------------ *  ------------
-                        *                         32737        ao_accel_ref
-                        *
-                        * Multiply through by 32767:
-                        *
-                        *                      ao_raw_accel * 32767
-                        *      ao_cook_accel = --------------------
-                        *                          ao_accel_ref
-                        *
-                        * Now, the tricky part. Getting this to compile efficiently
-                        * and keeping all of the values in-range.
-                        *
-                        * First off, we need to use a shift of 16 instead of * 32767 as SDCC
-                        * does the obvious optimizations for byte-granularity shifts:
-                        *
-                        *      ao_cook_accel = (ao_raw_accel << 16) / ao_accel_ref
-                        *
-                        * Next, lets check our input ranges:
-                        *
-                        *      0 <= ao_raw_accel <= 0x7fff             (singled ended ADC conversion)
-                        *      0x7000 <= ao_accel_ref <= 0x7fff        (the 5V ref value is close to 0x7fff)
-                        *
-                        * Plugging in our input ranges, we get an output range of 0 - 0x12490,
-                        * which is 17 bits. That won't work. If we take the accel ref and shift
-                        * by a bit, we'll change its range:
-                        *
-                        *      0xe000 <= ao_accel_ref<<1 <= 0xfffe
-                        *
-                        *      ao_cook_accel = (ao_raw_accel << 16) / (ao_accel_ref << 1)
-                        *
-                        * Now the output range is 0 - 0x9248, which nicely fits in 16 bits. It
-                        * is, however, one bit too large for our signed computations. So, we
-                        * take the result and shift that by a bit:
-                        *
-                        *      ao_cook_accel = ((ao_raw_accel << 16) / (ao_accel_ref << 1)) >> 1
-                        *
-                        * This finally creates an output range of 0 - 0x4924. As the ADC only
-                        * provides 11 bits of data, we haven't actually lost any precision,
-                        * just dropped a bit of noise off the low end.
-                        */
-                       ao_raw_accel = (uint16_t) ((((uint32_t) ao_raw_accel << 16) / (ao_accel_ref[ao_flight_adc] << 1))) >> 1;
-                       ao_adc->accel = ao_raw_accel;
-#endif
-#endif
 
-                       if (ao_flight_state > ao_flight_idle) {
-                               ao_kalman_predict();
-#if HAS_ACCEL
-                               if (ao_flight_state <= ao_flight_coast) {
-#ifdef FORCE_ACCEL
-                                       ao_kalman_correct_accel();
-#else
-                                       ao_kalman_correct_both();
-#endif
-                               } else
-#endif
-                                       ao_kalman_correct_baro();
-                               ao_height = from_fix(ao_k_height);
-                               ao_speed = from_fix(ao_k_speed);
-                               ao_accel = from_fix(ao_k_accel);
-                               if (ao_height > ao_max_height)
-                                       ao_max_height = ao_height;
-                       }
-                       ao_flight_adc = ao_adc_ring_next(ao_flight_adc);
-               }
+               /*
+                * Process ADC samples, just looping
+                * until the sensors are calibrated.
+                */
+               if (!ao_sample())
+                       continue;
 
                switch (ao_flight_state) {
                case ao_flight_startup:
 
-                       /* startup state:
-                        *
-                        * Collect 512 samples of acceleration and pressure
-                        * data and average them to find the resting values
-                        */
-                       if (nsamples < 512) {
-#if HAS_ACCEL
-                               ao_raw_accel_sum += ao_raw_accel;
-#endif
-                               ao_raw_pres_sum += ao_raw_pres;
-                               ++nsamples;
-                               continue;
-                       }
-                       ao_config_get();
-#if HAS_ACCEL
-                       ao_ground_accel = ao_raw_accel_sum >> 9;
-                       ao_accel_2g = ao_config.accel_minus_g - ao_config.accel_plus_g;
-                       ao_accel_scale = to_fix32(GRAVITY * 2 * 16) / ao_accel_2g;
-#endif
-                       ao_ground_pres = ao_raw_pres_sum >> 9;
-                       ao_ground_height = ao_pres_to_altitude(ao_ground_pres);
-
                        /* Check to see what mode we should go to.
                         *  - Invalid mode if accel cal appears to be out
                         *  - pad mode if we're upright,
@@ -574,7 +152,7 @@ ao_flight(void)
                                )
                        {
                                ao_flight_state = ao_flight_boost;
-                               ao_launch_tick = ao_flight_tick;
+                               ao_launch_tick = ao_sample_tick;
 
                                /* start logging data */
                                ao_log_start();
@@ -608,7 +186,7 @@ ao_flight(void)
                         * (15 seconds) has past.
                         */
                        if ((ao_accel < AO_MSS_TO_ACCEL(-2.5) && ao_height > AO_M_TO_HEIGHT(100)) ||
-                           (int16_t) (ao_flight_tick - ao_launch_tick) > BOOST_TICKS_MAX)
+                           (int16_t) (ao_sample_tick - ao_launch_tick) > BOOST_TICKS_MAX)
                        {
 #if HAS_ACCEL
                                ao_flight_state = ao_flight_fast;
@@ -646,7 +224,7 @@ ao_flight(void)
                         */
                        if (ao_speed < 0
 #if !HAS_ACCEL
-                           && (ao_raw_alt >= AO_MAX_BARO_HEIGHT || ao_error_h_sq_avg < 100)
+                           && (ao_sample_alt >= AO_MAX_BARO_HEIGHT || ao_error_h_sq_avg < 100)
 #endif
                                )
                        {
@@ -662,7 +240,7 @@ ao_flight(void)
                                 */
 
                                /* initialize interval values */
-                               ao_interval_end = ao_flight_tick + AO_INTERVAL_TICKS;
+                               ao_interval_end = ao_sample_tick + AO_INTERVAL_TICKS;
 
                                ao_interval_min_height = ao_interval_max_height = ao_height;
 
@@ -706,7 +284,7 @@ ao_flight(void)
                        if (ao_height > ao_interval_max_height)
                                ao_interval_max_height = ao_height;
 
-                       if ((int16_t) (ao_flight_tick - ao_interval_end) >= 0) {
+                       if ((int16_t) (ao_sample_tick - ao_interval_end) >= 0) {
                                if (ao_height < AO_M_TO_HEIGHT(1000) &&
                                    ao_interval_max_height - ao_interval_min_height < AO_M_TO_HEIGHT(5))
                                {
@@ -720,7 +298,7 @@ ao_flight(void)
                                        ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
                                }
                                ao_interval_min_height = ao_interval_max_height = ao_height;
-                               ao_interval_end = ao_flight_tick + AO_INTERVAL_TICKS;
+                               ao_interval_end = ao_sample_tick + AO_INTERVAL_TICKS;
                        }
                        break;
                case ao_flight_landed:
index 91aa0f738064703eee090fd6f6b5398a6815ffff..f41acbca282dc50fafa0bf8f515f81b8fce4b033 100644 (file)
@@ -53,6 +53,22 @@ struct ao_adc {
 #define __code
 #define __reentrant
 
+#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
+
+/*
+ * 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,
@@ -66,6 +82,11 @@ enum ao_flight_state {
        ao_flight_invalid = 9
 };
 
+extern enum ao_flight_state ao_flight_state;
+
+#define FALSE 0
+#define TRUE 1
+
 struct ao_adc ao_adc_ring[AO_ADC_RING];
 uint8_t ao_adc_head;
 int    ao_summary = 0;
@@ -171,15 +192,25 @@ struct ao_config ao_config;
 #define HAS_ACCEL_REF 0
 #endif
 
-#include "ao_flight.c"
-
-#define to_double(f)   ((f) / 65536.0)
-
 #define GRAVITY 9.80665
-extern int16_t ao_ground_accel, ao_raw_accel;
+extern int16_t ao_ground_accel, ao_flight_accel;
 extern int16_t ao_accel_2g;
 
+extern uint16_t        ao_sample_tick;
+
+extern int16_t ao_sample_height;
+extern int16_t ao_sample_accel;
+extern int32_t ao_accel_scale;
+
+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;
@@ -224,7 +255,7 @@ ao_insert(void)
        ao_adc_ring[ao_adc_head] = ao_adc_static;
        ao_adc_head = ao_adc_ring_next(ao_adc_head);
        if (ao_flight_state != ao_flight_startup) {
-               double  height = ao_pres_to_altitude(ao_raw_pres) - ao_ground_height;
+               double  height = ao_pres_to_altitude(ao_sample_pres) - ao_ground_height;
                double  accel = ((ao_flight_ground_accel - ao_adc_static.accel) * GRAVITY * 2.0) /
                        (ao_config.accel_minus_g - ao_config.accel_plus_g);
 
diff --git a/src/ao_kalman.c b/src/ao_kalman.c
new file mode 100644 (file)
index 0000000..ee99f37
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * Copyright © 2011 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.
+ */
+
+#ifndef AO_FLIGHT_TEST
+#include "ao.h"
+#endif
+
+#include "ao_kalman.h"
+
+static __pdata int32_t         ao_k_height;
+static __pdata int32_t         ao_k_speed;
+static __pdata int32_t         ao_k_accel;
+
+#define AO_K_STEP_100          to_fix16(0.01)
+#define AO_K_STEP_2_2_100      to_fix16(0.00005)
+
+#define AO_K_STEP_10           to_fix16(0.1)
+#define AO_K_STEP_2_2_10       to_fix16(0.005)
+
+__pdata int16_t                        ao_height;
+__pdata int16_t                        ao_speed;
+__pdata int16_t                        ao_accel;
+__pdata int16_t                        ao_max_height;
+
+__pdata int16_t                        ao_error_h;
+__pdata int16_t                        ao_error_h_sq_avg;
+
+#if HAS_ACCEL
+__pdata int16_t                        ao_error_a;
+#endif
+
+static void
+ao_kalman_predict(void)
+{
+#ifdef AO_FLIGHT_TEST
+       if (ao_sample_tick - ao_sample_prev_tick > 5) {
+               ao_k_height += ((int32_t) ao_speed * AO_K_STEP_10 +
+                               (int32_t) ao_accel * AO_K_STEP_2_2_10) >> 4;
+               ao_k_speed += (int32_t) ao_accel * AO_K_STEP_10;
+
+               return;
+       }
+       if (ao_flight_debug) {
+               printf ("predict speed %g + (%g * %g) = %g\n",
+                       ao_k_speed / (65536.0 * 16.0), ao_accel / 16.0, AO_K_STEP_100 / 65536.0,
+                       (ao_k_speed + (int32_t) ao_accel * AO_K_STEP_100) / (65536.0 * 16.0));
+       }
+#endif
+       ao_k_height += ((int32_t) ao_speed * AO_K_STEP_100 +
+                       (int32_t) ao_accel * AO_K_STEP_2_2_100) >> 4;
+       ao_k_speed += (int32_t) ao_accel * AO_K_STEP_100;
+}
+
+static void
+ao_kalman_err_height(void)
+{
+       int16_t e;
+       int16_t height_distrust;
+#if HAS_ACCEL
+       int16_t speed_distrust;
+#endif
+
+       ao_error_h = ao_sample_height - (int16_t) (ao_k_height >> 16);
+
+       e = ao_error_h;
+       if (e < 0)
+               e = -e;
+       if (e > 127)
+               e = 127;
+#if HAS_ACCEL
+       ao_error_h_sq_avg -= ao_error_h_sq_avg >> 2;
+       ao_error_h_sq_avg += (e * e) >> 2;
+#else
+       ao_error_h_sq_avg -= ao_error_h_sq_avg >> 4;
+       ao_error_h_sq_avg += (e * e) >> 4;
+#endif
+
+       height_distrust = ao_sample_height - AO_MAX_BARO_HEIGHT;
+#if HAS_ACCEL
+       /* speed is stored * 16, but we need to ramp between 200 and 328, so
+        * we want to multiply by 2. The result is a shift by 3.
+        */
+       speed_distrust = (ao_speed - AO_MS_TO_SPEED(AO_MAX_BARO_SPEED)) >> (4 - 1);
+       if (speed_distrust <= 0)
+               speed_distrust = 0;
+       else if (speed_distrust > height_distrust)
+               height_distrust = speed_distrust;
+#endif
+       if (height_distrust <= 0)
+               height_distrust = 0;
+
+       if (height_distrust) {
+#ifdef AO_FLIGHT_TEST
+               int     old_ao_error_h = ao_error_h;
+#endif
+               if (height_distrust > 0x100)
+                       height_distrust = 0x100;
+               ao_error_h = (int16_t) (((int32_t) ao_error_h * (0x100 - height_distrust)) >> 8);
+#ifdef AO_FLIGHT_TEST
+               if (ao_flight_debug) {
+                       printf("over height %g over speed %g distrust: %g height: error %d -> %d\n",
+                              (double) (ao_sample_height - AO_MAX_BARO_HEIGHT),
+                              (ao_speed - AO_MS_TO_SPEED(AO_MAX_BARO_SPEED)) / 16.0,
+                              height_distrust / 256.0,
+                              old_ao_error_h, ao_error_h);
+               }
+#endif
+       }
+}
+
+static void
+ao_kalman_correct_baro(void)
+{
+       ao_kalman_err_height();
+#ifdef AO_FLIGHT_TEST
+       if (ao_sample_tick - ao_sample_prev_tick > 5) {
+               ao_k_height += (int32_t) AO_BARO_K0_10 * ao_error_h;
+               ao_k_speed  += (int32_t) AO_BARO_K1_10 * ao_error_h;
+               ao_k_accel  += (int32_t) AO_BARO_K2_10 * ao_error_h;
+               return;
+       }
+#endif
+       ao_k_height += (int32_t) AO_BARO_K0_100 * ao_error_h;
+       ao_k_speed  += (int32_t) AO_BARO_K1_100 * ao_error_h;
+       ao_k_accel  += (int32_t) AO_BARO_K2_100 * ao_error_h;
+}
+
+#if HAS_ACCEL
+
+static void
+ao_kalman_err_accel(void)
+{
+       int32_t accel;
+
+       accel = (ao_ground_accel - ao_sample_accel) * ao_accel_scale;
+
+       /* Can't use ao_accel here as it is the pre-prediction value still */
+       ao_error_a = (accel - ao_k_accel) >> 16;
+}
+
+static void
+ao_kalman_correct_both(void)
+{
+       ao_kalman_err_height();
+       ao_kalman_err_accel();
+
+#ifdef AO_FLIGHT_TEST
+       if (ao_sample_tick - ao_sample_prev_tick > 5) {
+               if (ao_flight_debug) {
+                       printf ("correct speed %g + (%g * %g) + (%g * %g) = %g\n",
+                               ao_k_speed / (65536.0 * 16.0),
+                               (double) ao_error_h, AO_BOTH_K10_10 / 65536.0,
+                               (double) ao_error_a, AO_BOTH_K11_10 / 65536.0,
+                               (ao_k_speed +
+                                (int32_t) AO_BOTH_K10_10 * ao_error_h +
+                                (int32_t) AO_BOTH_K11_10 * ao_error_a) / (65536.0 * 16.0));
+               }
+               ao_k_height +=
+                       (int32_t) AO_BOTH_K00_10 * ao_error_h +
+                       (int32_t) AO_BOTH_K01_10 * ao_error_a;
+               ao_k_speed +=
+                       (int32_t) AO_BOTH_K10_10 * ao_error_h +
+                       (int32_t) AO_BOTH_K11_10 * ao_error_a;
+               ao_k_accel +=
+                       (int32_t) AO_BOTH_K20_10 * ao_error_h +
+                       (int32_t) AO_BOTH_K21_10 * ao_error_a;
+               return;
+       }
+       if (ao_flight_debug) {
+               printf ("correct speed %g + (%g * %g) + (%g * %g) = %g\n",
+                       ao_k_speed / (65536.0 * 16.0),
+                       (double) ao_error_h, AO_BOTH_K10_100 / 65536.0,
+                       (double) ao_error_a, AO_BOTH_K11_100 / 65536.0,
+                       (ao_k_speed +
+                        (int32_t) AO_BOTH_K10_100 * ao_error_h +
+                        (int32_t) AO_BOTH_K11_100 * ao_error_a) / (65536.0 * 16.0));
+       }
+#endif
+       ao_k_height +=
+               (int32_t) AO_BOTH_K00_100 * ao_error_h +
+               (int32_t) AO_BOTH_K01_100 * ao_error_a;
+       ao_k_speed +=
+               (int32_t) AO_BOTH_K10_100 * ao_error_h +
+               (int32_t) AO_BOTH_K11_100 * ao_error_a;
+       ao_k_accel +=
+               (int32_t) AO_BOTH_K20_100 * ao_error_h +
+               (int32_t) AO_BOTH_K21_100 * ao_error_a;
+}
+
+#ifdef FORCE_ACCEL
+static void
+ao_kalman_correct_accel(void)
+{
+       ao_kalman_err_accel();
+
+       if (ao_sample_tick - ao_sample_prev_tick > 5) {
+               ao_k_height +=(int32_t) AO_ACCEL_K0_10 * ao_error_a;
+               ao_k_speed  += (int32_t) AO_ACCEL_K1_10 * ao_error_a;
+               ao_k_accel  += (int32_t) AO_ACCEL_K2_10 * ao_error_a;
+               return;
+       }
+       ao_k_height += (int32_t) AO_ACCEL_K0_100 * ao_error_a;
+       ao_k_speed  += (int32_t) AO_ACCEL_K1_100 * ao_error_a;
+       ao_k_accel  += (int32_t) AO_ACCEL_K2_100 * ao_error_a;
+}
+#endif
+#endif /* HAS_ACCEL */
+
+void
+ao_kalman(void)
+{
+       ao_kalman_predict();
+#if HAS_ACCEL
+       if (ao_flight_state <= ao_flight_coast) {
+#ifdef FORCE_ACCEL
+               ao_kalman_correct_accel();
+#else
+               ao_kalman_correct_both();
+#endif
+       } else
+#endif
+               ao_kalman_correct_baro();
+       ao_height = from_fix(ao_k_height);
+       ao_speed = from_fix(ao_k_speed);
+       ao_accel = from_fix(ao_k_accel);
+       if (ao_height > ao_max_height)
+               ao_max_height = ao_height;
+#ifdef AO_FLIGHT_TEST
+       ao_sample_prev_tick = ao_sample_tick;
+#endif
+}
index ab6b02f582e88a4b96fbf174d6793d3a949f63d0..0c6cff85835d3727318a298e9537a97df51ae80d 100644 (file)
@@ -82,7 +82,7 @@ ao_log(void)
                ao_sleep(&ao_log_running);
 
        log.type = AO_LOG_FLIGHT;
-       log.tick = ao_flight_tick;
+       log.tick = ao_sample_tick;
 #if HAS_ACCEL
        log.u.flight.ground_accel = ao_ground_accel;
 #endif
@@ -92,12 +92,12 @@ ao_log(void)
        /* Write the whole contents of the ring to the log
         * when starting up.
         */
-       ao_log_adc_pos = ao_adc_ring_next(ao_flight_adc);
+       ao_log_adc_pos = ao_adc_ring_next(ao_sample_adc);
        next_other = next_sensor = ao_adc_ring[ao_log_adc_pos].tick;
        ao_log_state = ao_flight_startup;
        for (;;) {
                /* Write samples to EEPROM */
-               while (ao_log_adc_pos != ao_flight_adc) {
+               while (ao_log_adc_pos != ao_sample_adc) {
                        log.tick = ao_adc_ring[ao_log_adc_pos].tick;
                        if ((int16_t) (log.tick - next_sensor) >= 0) {
                                log.type = AO_LOG_SENSOR;
@@ -126,7 +126,7 @@ ao_log(void)
                if (ao_flight_state != ao_log_state) {
                        ao_log_state = ao_flight_state;
                        log.type = AO_LOG_STATE;
-                       log.tick = ao_flight_tick;
+                       log.tick = ao_sample_tick;
                        log.u.state.state = ao_log_state;
                        log.u.state.reason = 0;
                        ao_log_data(&log);
diff --git a/src/ao_sample.c b/src/ao_sample.c
new file mode 100644 (file)
index 0000000..ef40339
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * Copyright © 2011 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.
+ */
+
+#ifndef AO_FLIGHT_TEST
+#include "ao.h"
+#endif
+
+/*
+ * Current sensor values
+ */
+
+__pdata uint16_t       ao_sample_tick;         /* time of last data */
+__pdata int16_t                ao_sample_pres;
+__pdata int16_t                ao_sample_alt;
+__pdata int16_t                ao_sample_height;
+#if HAS_ACCEL
+__pdata int16_t                ao_sample_accel;
+#endif
+
+__data uint8_t         ao_sample_adc;
+
+/*
+ * Sensor calibration values
+ */
+
+__xdata int16_t                ao_ground_pres;         /* startup pressure */
+__xdata int16_t                ao_ground_height;       /* MSL of ao_ground_pres */
+
+#if HAS_ACCEL
+__xdata int16_t                ao_ground_accel;        /* startup acceleration */
+__xdata int16_t                ao_accel_2g;            /* factory accel calibration */
+__xdata int32_t                ao_accel_scale;         /* sensor to m/s² conversion */
+#endif
+
+static __xdata uint8_t ao_preflight;           /* in preflight mode */
+
+static __xdata uint16_t        nsamples;
+__xdata int32_t ao_sample_pres_sum;
+#if HAS_ACCEL
+__xdata int32_t ao_sample_accel_sum;
+#endif
+
+static void
+ao_sample_preflight(void)
+{
+       /* startup state:
+        *
+        * Collect 512 samples of acceleration and pressure
+        * data and average them to find the resting values
+        */
+       if (nsamples < 512) {
+#if HAS_ACCEL
+               ao_sample_accel_sum += ao_sample_accel;
+#endif
+               ao_sample_pres_sum += ao_sample_pres;
+               ++nsamples;
+               ao_preflight = FALSE;
+       }
+       ao_config_get();
+#if HAS_ACCEL
+       ao_ground_accel = ao_sample_accel_sum >> 9;
+       ao_accel_2g = ao_config.accel_minus_g - ao_config.accel_plus_g;
+       ao_accel_scale = to_fix32(GRAVITY * 2 * 16) / ao_accel_2g;
+#endif
+       ao_ground_pres = ao_sample_pres_sum >> 9;
+       ao_ground_height = ao_pres_to_altitude(ao_ground_pres);
+}
+
+uint8_t
+ao_sample(void)
+{
+       ao_wakeup(DATA_TO_XDATA(&ao_sample_adc));
+       ao_sleep(DATA_TO_XDATA(&ao_adc_head));
+       while (ao_sample_adc != ao_adc_head) {
+               __xdata struct ao_adc *ao_adc;
+
+               /* Capture a sample */
+               ao_adc = &ao_adc_ring[ao_sample_adc];
+               ao_sample_tick = ao_adc->tick;
+               ao_sample_pres = ao_adc->pres;
+               ao_sample_alt = ao_pres_to_altitude(ao_sample_pres);
+               ao_sample_height = ao_sample_alt - ao_ground_height;
+#if HAS_ACCEL
+               ao_sample_accel = ao_adc->accel;
+#if HAS_ACCEL_REF
+               /*
+                * Ok, the math here is a bit tricky.
+                *
+                * ao_sample_accel:  ADC output for acceleration
+                * ao_accel_ref:  ADC output for the 5V reference.
+                * ao_cook_accel: Corrected acceleration value
+                * Vcc:           3.3V supply to the CC1111
+                * Vac:           5V supply to the accelerometer
+                * accel:         input voltage to accelerometer ADC pin
+                * ref:           input voltage to 5V reference ADC pin
+                *
+                *
+                * Measured acceleration is ratiometric to Vcc:
+                *
+                *     ao_sample_accel   accel
+                *     ------------ = -----
+                *        32767        Vcc
+                *
+                * Measured 5v reference is also ratiometric to Vcc:
+                *
+                *     ao_accel_ref    ref
+                *     ------------ = -----
+                *        32767        Vcc
+                *
+                *
+                *      ao_accel_ref = 32767 * (ref / Vcc)
+                *
+                * Acceleration is measured ratiometric to the 5V supply,
+                * so what we want is:
+                *
+                *      ao_cook_accel    accel
+                *      ------------- =  -----
+                *          32767         ref
+                *
+                *
+                *                      accel    Vcc
+                *                    = ----- *  ---
+                *                       Vcc     ref
+                *
+                *                      ao_sample_accel       32767
+                *                    = ------------ *  ------------
+                *                         32737        ao_accel_ref
+                *
+                * Multiply through by 32767:
+                *
+                *                      ao_sample_accel * 32767
+                *      ao_cook_accel = --------------------
+                *                          ao_accel_ref
+                *
+                * Now, the tricky part. Getting this to compile efficiently
+                * and keeping all of the values in-range.
+                *
+                * First off, we need to use a shift of 16 instead of * 32767 as SDCC
+                * does the obvious optimizations for byte-granularity shifts:
+                *
+                *      ao_cook_accel = (ao_sample_accel << 16) / ao_accel_ref
+                *
+                * Next, lets check our input ranges:
+                *
+                *      0 <= ao_sample_accel <= 0x7fff          (singled ended ADC conversion)
+                *      0x7000 <= ao_accel_ref <= 0x7fff        (the 5V ref value is close to 0x7fff)
+                *
+                * Plugging in our input ranges, we get an output range of 0 - 0x12490,
+                * which is 17 bits. That won't work. If we take the accel ref and shift
+                * by a bit, we'll change its range:
+                *
+                *      0xe000 <= ao_accel_ref<<1 <= 0xfffe
+                *
+                *      ao_cook_accel = (ao_sample_accel << 16) / (ao_accel_ref << 1)
+                *
+                * Now the output range is 0 - 0x9248, which nicely fits in 16 bits. It
+                * is, however, one bit too large for our signed computations. So, we
+                * take the result and shift that by a bit:
+                *
+                *      ao_cook_accel = ((ao_sample_accel << 16) / (ao_accel_ref << 1)) >> 1
+                *
+                * This finally creates an output range of 0 - 0x4924. As the ADC only
+                * provides 11 bits of data, we haven't actually lost any precision,
+                * just dropped a bit of noise off the low end.
+                */
+               ao_sample_accel = (uint16_t) ((((uint32_t) ao_sample_accel << 16) / (ao_accel_ref[ao_sample_adc] << 1))) >> 1;
+               ao_adc->accel = ao_sample_accel;
+#endif
+#endif
+
+               if (ao_preflight)
+                       ao_sample_preflight();
+               else
+                       ao_kalman();
+               ao_sample_adc = ao_adc_ring_next(ao_sample_adc);
+       }
+       return !ao_preflight;
+}
+
+void
+ao_sample_init(void)
+{
+       nsamples = 0;
+       ao_sample_pres_sum = 0;
+       ao_sample_pres = 0;
+#if HAS_ACCEL
+       ao_sample_accel_sum = 0;
+       ao_sample_accel = 0;
+#endif
+       ao_sample_adc = ao_adc_head;
+       ao_preflight = TRUE;
+}
index 78bbd3c3dab3e3d52d0731be50827e983c642dcb..6e1f5effde50409d1f89967381a9d5054ce8a05a 100644 (file)
@@ -66,7 +66,7 @@ getchar(void) __reentrant __critical
 void
 ao_add_stdio(char (*pollchar)(void),
             void (*putchar)(char),
-            void (*flush)(void))
+            void (*flush)(void)) __reentrant
 {
        if (ao_num_stdios == AO_NUM_STDIOS)
                ao_panic(AO_PANIC_STDIO);