altos: Split out mm-specific versions of sampling code
authorKeith Packard <keithp@keithp.com>
Sun, 27 May 2012 22:47:30 +0000 (16:47 -0600)
committerKeith Packard <keithp@keithp.com>
Sun, 27 May 2012 22:47:30 +0000 (16:47 -0600)
Avoid breaking telemetrum (too much) by splitting this stuff apart.

Signed-off-by: Keith Packard <keithp@keithp.com>
src/core/ao_data.h [new file with mode: 0644]
src/core/ao_flight_mm.c [new file with mode: 0644]
src/core/ao_log_mega.c [new file with mode: 0644]
src/core/ao_sample_mm.c [new file with mode: 0644]

diff --git a/src/core/ao_data.h b/src/core/ao_data.h
new file mode 100644 (file)
index 0000000..d28730a
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+#ifndef _AO_DATA_H_
+#define _AO_DATA_H_
+
+#if HAS_MS5607
+#include <ao_ms5607.h>
+#endif
+
+#if HAS_MPU6000
+#include <ao_mpu6000.h>
+#endif
+
+struct ao_data {
+       uint16_t                        tick;
+#if HAS_ADC
+       struct ao_adc                   adc;
+#endif
+#if HAS_ACCEL_REF
+       uint16_t                        accel_ref;
+#endif
+#if HAS_MS5607
+       struct ao_ms5607_sample         ms5607;
+#endif
+#if HAS_MPU6000
+       struct ao_mpu6000_sample        mpu6000;
+#endif
+};
+
+#define ao_data_ring_next(n)   (((n) + 1) & (AO_DATA_RING - 1))
+#define ao_data_ring_prev(n)   (((n) - 1) & (AO_DATA_RING - 1))
+
+extern volatile __xdata struct ao_data ao_data_ring[AO_DATA_RING];
+extern volatile __data uint8_t         ao_data_head;
+
+#if HAS_MS5607
+
+typedef int32_t        pres_t;
+typedef int32_t alt_t;
+
+static inline pres_t ao_data_pres(struct ao_data *packet)
+{
+       struct ao_ms5607_value  value;
+
+       ao_ms5607_convert(&packet->ms5607, &value);
+       return value.pres;
+}
+
+#define pres_to_altitude(p)    ao_pa_to_altitude(p)
+
+#else
+
+typedef int16_t pres_t;
+typedef int16_t alt_t;
+
+#define ao_data_pres(packet)   ((packet)->adc.pres)
+#define pres_to_altitude(p)    ao_pres_to_altitude(p)
+
+#endif
+
+/*
+ * Need a few macros to pull data from the sensors:
+ *
+ * ao_data_accel_sample        - pull raw sensor and convert to normalized values
+ * ao_data_accel       - pull normalized value (lives in the same memory)
+ * ao_data_set_accel   - store normalized value back in the sensor location
+ * ao_data_accel_invert        - flip rocket ends for positive acceleration
+ */
+
+#if HAS_MPU6000
+
+typedef int16_t accel_t;
+
+/* MPU6000 is hooked up so that positive y is positive acceleration */
+#define ao_data_accel(packet)                  ((packet)->mpu6000.accel_y)
+#define ao_data_accel_sample(packet)           (-ao_data_accel(packet))
+#define ao_data_set_accel(packet, accel)       ((packet)->mpu6000.accel_y = (accel))
+#define ao_data_accel_invert(a)                        (-(a))
+
+#else
+
+typedef int16_t accel_t;
+#define ao_data_accel(packet)                  ((packet)->adc.accel)
+#define ao_data_set_accel(packet, accel)       ((packet)->adc.accel = (accel))
+#define ao_data_accel_invert(a)                        (0x7fff -(a))
+
+/*
+ * 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
+ *                    = ------------ *  ------------
+ *                         32767        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.
+ */
+#if HAS_ACCEL_REF
+#define ao_data_accel_sample(packet) \
+       ((uint16_t) ((((uint32_t) (packet)->adc.accel << 16) / ((packet)->accel_ref << 1))) >> 1)
+#else
+#define ao_data_accel(packet) ((packet)->adc.accel)
+#endif /* HAS_ACCEL_REF */
+
+#endif /* else some other pressure sensor */
+
+#endif /* _AO_DATA_H_ */
diff --git a/src/core/ao_flight_mm.c b/src/core/ao_flight_mm.c
new file mode 100644 (file)
index 0000000..27087e5
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * 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.
+ */
+
+#ifndef AO_FLIGHT_TEST
+#include "ao.h"
+#include <ao_log.h>
+#endif
+
+#ifndef HAS_ACCEL
+#error Please define HAS_ACCEL
+#endif
+
+#ifndef HAS_GPS
+#error Please define HAS_GPS
+#endif
+
+#ifndef HAS_USB
+#error Please define HAS_USB
+#endif
+
+/* Main flight thread. */
+
+__pdata enum ao_flight_state   ao_flight_state;        /* current flight state */
+__pdata uint16_t               ao_boost_tick;          /* time of launch detect */
+
+/*
+ * track min/max data over a long interval to detect
+ * resting
+ */
+static __data uint16_t         ao_interval_end;
+static __data int16_t          ao_interval_min_height;
+static __data int16_t          ao_interval_max_height;
+static __data int16_t          ao_coast_avg_accel;
+
+__pdata uint8_t                        ao_flight_force_idle;
+
+/* We also have a clock, which can be used to sanity check things in
+ * case of other failures
+ */
+
+#define BOOST_TICKS_MAX        AO_SEC_TO_TICKS(15)
+
+/* Landing is detected by getting constant readings from both pressure and accelerometer
+ * for a fairly long time (AO_INTERVAL_TICKS)
+ */
+#define AO_INTERVAL_TICKS      AO_SEC_TO_TICKS(10)
+
+#define abs(a) ((a) < 0 ? -(a) : (a))
+
+void
+ao_flight(void)
+{
+       ao_sample_init();
+       ao_flight_state = ao_flight_startup;
+       for (;;) {
+
+               /*
+                * Process ADC samples, just looping
+                * until the sensors are calibrated.
+                */
+               if (!ao_sample())
+                       continue;
+
+               switch (ao_flight_state) {
+               case ao_flight_startup:
+
+                       /* Check to see what mode we should go to.
+                        *  - Invalid mode if accel cal appears to be out
+                        *  - pad mode if we're upright,
+                        *  - idle mode otherwise
+                        */
+#if HAS_ACCEL
+                       if (ao_config.accel_plus_g == 0 ||
+                           ao_config.accel_minus_g == 0 ||
+                           ao_ground_accel < ao_config.accel_plus_g - ACCEL_NOSE_UP ||
+                           ao_ground_accel > ao_config.accel_minus_g + ACCEL_NOSE_UP)
+                       {
+                               /* Detected an accel value outside -1.5g to 1.5g
+                                * (or uncalibrated values), so we go into invalid mode
+                                */
+                               ao_flight_state = ao_flight_invalid;
+
+#if HAS_RADIO
+                               /* Turn on packet system in invalid mode on TeleMetrum */
+                               ao_packet_slave_start();
+#endif
+                       } else
+#endif
+                               if (!ao_flight_force_idle
+#if HAS_ACCEL
+                                   && ao_ground_accel < ao_config.accel_plus_g + ACCEL_NOSE_UP
+#endif
+                                       )
+                       {
+                               /* Set pad mode - we can fly! */
+                               ao_flight_state = ao_flight_pad;
+#if HAS_USB
+                               /* Disable the USB controller in flight mode
+                                * to save power
+                                */
+                               ao_usb_disable();
+#endif
+
+#if !HAS_ACCEL
+                               /* Disable packet mode in pad state on TeleMini */
+                               ao_packet_slave_stop();
+#endif
+
+#if HAS_RADIO
+                               /* Turn on telemetry system */
+                               ao_rdf_set(1);
+                               ao_telemetry_set_interval(AO_TELEMETRY_INTERVAL_PAD);
+#endif
+                               /* signal successful initialization by turning off the LED */
+                               ao_led_off(AO_LED_RED);
+                       } else {
+                               /* Set idle mode */
+                               ao_flight_state = ao_flight_idle;
+#if HAS_ACCEL && HAS_RADIO
+                               /* Turn on packet system in idle mode on TeleMetrum */
+                               ao_packet_slave_start();
+#endif
+
+                               /* signal successful initialization by turning off the LED */
+                               ao_led_off(AO_LED_RED);
+                       }
+                       /* wakeup threads due to state change */
+                       ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+
+                       break;
+               case ao_flight_pad:
+
+                       /* pad to boost:
+                        *
+                        * barometer: > 20m vertical motion
+                        *             OR
+                        * accelerometer: > 2g AND velocity > 5m/s
+                        *
+                        * The accelerometer should always detect motion before
+                        * the barometer, but we use both to make sure this
+                        * transition is detected. If the device
+                        * doesn't have an accelerometer, then ignore the
+                        * speed and acceleration as they are quite noisy
+                        * on the pad.
+                        */
+                       if (ao_height > AO_M_TO_HEIGHT(20)
+#if HAS_ACCEL
+                           || (ao_accel > AO_MSS_TO_ACCEL(20) &&
+                               ao_speed > AO_MS_TO_SPEED(5))
+#endif
+                               )
+                       {
+                               ao_flight_state = ao_flight_boost;
+                               ao_boost_tick = ao_sample_tick;
+
+                               /* start logging data */
+                               ao_log_start();
+
+#if HAS_RADIO
+                               /* Increase telemetry rate */
+                               ao_telemetry_set_interval(AO_TELEMETRY_INTERVAL_FLIGHT);
+
+                               /* disable RDF beacon */
+                               ao_rdf_set(0);
+#endif
+
+#if HAS_GPS
+                               /* Record current GPS position by waking up GPS log tasks */
+                               ao_wakeup(&ao_gps_data);
+                               ao_wakeup(&ao_gps_tracking_data);
+#endif
+
+                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                       }
+                       break;
+               case ao_flight_boost:
+
+                       /* boost to fast:
+                        *
+                        * accelerometer: start to fall at > 1/4 G
+                        *              OR
+                        * time: boost for more than 15 seconds
+                        *
+                        * Detects motor burn out by the switch from acceleration to
+                        * deceleration, or by waiting until the maximum burn duration
+                        * (15 seconds) has past.
+                        */
+                       if ((ao_accel < AO_MSS_TO_ACCEL(-2.5) && ao_height > AO_M_TO_HEIGHT(100)) ||
+                           (int16_t) (ao_sample_tick - ao_boost_tick) > BOOST_TICKS_MAX)
+                       {
+#if HAS_ACCEL
+                               ao_flight_state = ao_flight_fast;
+                               ao_coast_avg_accel = ao_accel;
+#else
+                               ao_flight_state = ao_flight_coast;
+#endif
+                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                       }
+                       break;
+#if HAS_ACCEL
+               case ao_flight_fast:
+                       /*
+                        * This is essentially the same as coast,
+                        * but the barometer is being ignored as
+                        * it may be unreliable.
+                        */
+                       if (ao_speed < AO_MS_TO_SPEED(AO_MAX_BARO_SPEED))
+                       {
+                               ao_flight_state = ao_flight_coast;
+                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                       } else
+                               goto check_re_boost;
+                       break;
+#endif
+               case ao_flight_coast:
+
+                       /*
+                        * By customer request - allow the user
+                        * to lock out apogee detection for a specified
+                        * number of seconds.
+                        */
+                       if (ao_config.apogee_lockout) {
+                               if ((ao_sample_tick - ao_boost_tick) <
+                                   AO_SEC_TO_TICKS(ao_config.apogee_lockout))
+                                       break;
+                       }
+
+                       /* apogee detect: coast to drogue deploy:
+                        *
+                        * speed: < 0
+                        *
+                        * Also make sure the model altitude is tracking
+                        * the measured altitude reasonably closely; otherwise
+                        * we're probably transsonic.
+                        */
+                       if (ao_speed < 0
+#if !HAS_ACCEL
+                           && (ao_sample_alt >= AO_MAX_BARO_HEIGHT || ao_error_h_sq_avg < 100)
+#endif
+                               )
+                       {
+#if HAS_IGNITE
+                               /* ignite the drogue charge */
+                               ao_ignite(ao_igniter_drogue);
+#endif
+
+#if HAS_RADIO
+                               /* slow down the telemetry system */
+                               ao_telemetry_set_interval(AO_TELEMETRY_INTERVAL_RECOVER);
+
+                               /* Turn the RDF beacon back on */
+                               ao_rdf_set(1);
+#endif
+
+                               /* and enter drogue state */
+                               ao_flight_state = ao_flight_drogue;
+                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                       }
+#if HAS_ACCEL
+                       else {
+                       check_re_boost:
+                               ao_coast_avg_accel = ao_coast_avg_accel - (ao_coast_avg_accel >> 6) + (ao_accel >> 6);
+                               if (ao_coast_avg_accel > AO_MSS_TO_ACCEL(20)) {
+                                       ao_boost_tick = ao_sample_tick;
+                                       ao_flight_state = ao_flight_boost;
+                                       ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                               }
+                       }
+#endif
+
+                       break;
+               case ao_flight_drogue:
+
+                       /* drogue to main deploy:
+                        *
+                        * barometer: reach main deploy altitude
+                        *
+                        * Would like to use the accelerometer for this test, but
+                        * the orientation of the flight computer is unknown after
+                        * drogue deploy, so we ignore it. Could also detect
+                        * high descent rate using the pressure sensor to
+                        * recognize drogue deploy failure and eject the main
+                        * at that point. Perhaps also use the drogue sense lines
+                        * to notice continutity?
+                        */
+                       if (ao_height <= ao_config.main_deploy)
+                       {
+#if HAS_IGNITE
+                               ao_ignite(ao_igniter_main);
+#endif
+
+                               /*
+                                * Start recording min/max height
+                                * to figure out when the rocket has landed
+                                */
+
+                               /* initialize interval values */
+                               ao_interval_end = ao_sample_tick + AO_INTERVAL_TICKS;
+
+                               ao_interval_min_height = ao_interval_max_height = ao_avg_height;
+
+                               ao_flight_state = ao_flight_main;
+                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                       }
+                       break;
+
+                       /* fall through... */
+               case ao_flight_main:
+
+                       /* main to land:
+                        *
+                        * barometer: altitude stable
+                        */
+
+                       if (ao_avg_height < ao_interval_min_height)
+                               ao_interval_min_height = ao_avg_height;
+                       if (ao_avg_height > ao_interval_max_height)
+                               ao_interval_max_height = ao_avg_height;
+
+                       if ((int16_t) (ao_sample_tick - ao_interval_end) >= 0) {
+                               if (ao_interval_max_height - ao_interval_min_height <= AO_M_TO_HEIGHT(4))
+                               {
+                                       ao_flight_state = ao_flight_landed;
+
+                                       /* turn off the ADC capture */
+                                       ao_timer_set_adc_interval(0);
+
+                                       ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                               }
+                               ao_interval_min_height = ao_interval_max_height = ao_avg_height;
+                               ao_interval_end = ao_sample_tick + AO_INTERVAL_TICKS;
+                       }
+                       break;
+               case ao_flight_landed:
+                       break;
+               }
+       }
+}
+
+static __xdata struct ao_task  flight_task;
+
+void
+ao_flight_init(void)
+{
+       ao_flight_state = ao_flight_startup;
+       ao_add_task(&flight_task, ao_flight, "flight");
+}
diff --git a/src/core/ao_log_mega.c b/src/core/ao_log_mega.c
new file mode 100644 (file)
index 0000000..1763b9e
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * 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.h"
+#include <ao_log.h>
+#include <ao_data.h>
+#include <ao_flight.h>
+
+static __xdata uint8_t ao_log_mutex;
+static __xdata struct ao_log_mega log;
+
+static uint8_t
+ao_log_csum(__xdata uint8_t *b) __reentrant
+{
+       uint8_t sum = 0x5a;
+       uint8_t i;
+
+       for (i = 0; i < sizeof (struct ao_log_mega); i++)
+               sum += *b++;
+       return -sum;
+}
+
+uint8_t
+ao_log_mega(__xdata struct ao_log_mega *log) __reentrant
+{
+       uint8_t wrote = 0;
+       /* set checksum */
+       log->csum = 0;
+       log->csum = ao_log_csum((__xdata uint8_t *) log);
+       ao_mutex_get(&ao_log_mutex); {
+               if (ao_log_current_pos >= ao_log_end_pos && ao_log_running)
+                       ao_log_stop();
+               if (ao_log_running) {
+                       wrote = 1;
+                       ao_storage_write(ao_log_current_pos,
+                                        log,
+                                        sizeof (struct ao_log_mega));
+                       ao_log_current_pos += sizeof (struct ao_log_mega);
+               }
+       } ao_mutex_put(&ao_log_mutex);
+       return wrote;
+}
+
+static uint8_t
+ao_log_dump_check_data(void)
+{
+       if (ao_log_csum((uint8_t *) &log) != 0)
+               return 0;
+       return 1;
+}
+
+static __data uint8_t  ao_log_data_pos;
+
+/* a hack to make sure that ao_log_megas fill the eeprom block in even units */
+typedef uint8_t check_log_size[1-(256 % sizeof(struct ao_log_mega))] ;
+
+#ifndef AO_SENSOR_INTERVAL_ASCENT
+#define AO_SENSOR_INTERVAL_ASCENT      1
+#define AO_SENSOR_INTERVAL_DESCENT     10
+#define AO_OTHER_INTERVAL              32
+#endif
+
+void
+ao_log(void)
+{
+       __pdata uint16_t        next_sensor, next_other;
+       uint8_t                 i;
+
+       ao_storage_setup();
+
+       ao_log_scan();
+
+       while (!ao_log_running)
+               ao_sleep(&ao_log_running);
+
+#if HAS_FLIGHT
+       log.type = AO_LOG_FLIGHT;
+       log.tick = ao_sample_tick;
+#if HAS_ACCEL
+       log.u.flight.ground_accel = ao_ground_accel;
+#endif
+       log.u.flight.ground_pres = ao_ground_pres;
+       log.u.flight.flight = ao_flight_number;
+       ao_log_mega(&log);
+#endif
+
+       /* Write the whole contents of the ring to the log
+        * when starting up.
+        */
+       ao_log_data_pos = ao_data_ring_next(ao_data_head);
+       next_other = next_sensor = ao_data_ring[ao_log_data_pos].tick;
+       ao_log_state = ao_flight_startup;
+       for (;;) {
+               /* Write samples to EEPROM */
+               while (ao_log_data_pos != ao_data_head) {
+                       log.tick = ao_data_ring[ao_log_data_pos].tick;
+                       if ((int16_t) (log.tick - next_sensor) >= 0) {
+                               log.type = AO_LOG_SENSOR;
+                               log.u.sensor.pres = ao_data_ring[ao_log_data_pos].ms5607.pres;
+                               log.u.sensor.temp = ao_data_ring[ao_log_data_pos].ms5607.temp;
+                               log.u.sensor.accel_x = ao_data_ring[ao_log_data_pos].mpu6000.accel_x;
+                               log.u.sensor.accel_y = ao_data_ring[ao_log_data_pos].mpu6000.accel_y;
+                               log.u.sensor.accel_z = ao_data_ring[ao_log_data_pos].mpu6000.accel_z;
+                               log.u.sensor.gyro_x = ao_data_ring[ao_log_data_pos].mpu6000.gyro_x;
+                               log.u.sensor.gyro_y = ao_data_ring[ao_log_data_pos].mpu6000.gyro_y;
+                               log.u.sensor.gyro_z = ao_data_ring[ao_log_data_pos].mpu6000.gyro_z;
+                               ao_log_mega(&log);
+                               if (ao_log_state <= ao_flight_coast)
+                                       next_sensor = log.tick + AO_SENSOR_INTERVAL_ASCENT;
+                               else
+                                       next_sensor = log.tick + AO_SENSOR_INTERVAL_DESCENT;
+                       }
+                       if ((int16_t) (log.tick - next_other) >= 0) {
+                               log.type = AO_LOG_TEMP_VOLT;
+                               log.u.volt.v_batt = ao_data_ring[ao_log_data_pos].adc.v_batt;
+                               log.u.volt.v_pbatt = ao_data_ring[ao_log_data_pos].adc.v_pbatt;
+                               log.u.volt.n_sense = AO_ADC_NUM_SENSE;
+                               for (i = 0; i < AO_ADC_NUM_SENSE; i++)
+                                       log.u.volt.sense[i] = ao_data_ring[ao_log_data_pos].adc.sense[i];
+                               ao_log_mega(&log);
+                               next_other = log.tick + AO_OTHER_INTERVAL;
+                       }
+                       ao_log_data_pos = ao_data_ring_next(ao_log_data_pos);
+               }
+#if HAS_FLIGHT
+               /* Write state change to EEPROM */
+               if (ao_flight_state != ao_log_state) {
+                       ao_log_state = ao_flight_state;
+                       log.type = AO_LOG_STATE;
+                       log.tick = ao_time();
+                       log.u.state.state = ao_log_state;
+                       log.u.state.reason = 0;
+                       ao_log_mega(&log);
+
+                       if (ao_log_state == ao_flight_landed)
+                               ao_log_stop();
+               }
+#endif
+
+               /* Wait for a while */
+               ao_delay(AO_MS_TO_TICKS(100));
+
+               /* Stop logging when told to */
+               while (!ao_log_running)
+                       ao_sleep(&ao_log_running);
+       }
+}
+
+uint16_t
+ao_log_flight(uint8_t slot)
+{
+       if (!ao_storage_read(ao_log_pos(slot),
+                            &log,
+                            sizeof (struct ao_log_mega)))
+               return 0;
+
+       if (ao_log_dump_check_data() && log.type == AO_LOG_FLIGHT)
+               return log.u.flight.flight;
+       return 0;
+}
diff --git a/src/core/ao_sample_mm.c b/src/core/ao_sample_mm.c
new file mode 100644 (file)
index 0000000..8cfadc4
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * 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"
+#include <ao_data.h>
+#endif
+
+/*
+ * Current sensor values
+ */
+
+#ifndef PRES_TYPE
+#define PRES_TYPE int32_t
+#define ALT_TYPE int32_t
+#define ACCEL_TYPE int16_t
+#endif
+
+__pdata uint16_t       ao_sample_tick;         /* time of last data */
+__pdata pres_t         ao_sample_pres;
+__pdata alt_t          ao_sample_alt;
+__pdata alt_t          ao_sample_height;
+#if HAS_ACCEL
+__pdata accel_t                ao_sample_accel;
+#endif
+
+__data uint8_t         ao_sample_data;
+
+/*
+ * Sensor calibration values
+ */
+
+__pdata pres_t         ao_ground_pres;         /* startup pressure */
+__pdata alt_t          ao_ground_height;       /* MSL of ao_ground_pres */
+
+#if HAS_ACCEL
+__pdata accel_t                ao_ground_accel;        /* startup acceleration */
+__pdata accel_t                ao_accel_2g;            /* factory accel calibration */
+__pdata int32_t                ao_accel_scale;         /* sensor to m/s² conversion */
+#endif
+
+static __pdata uint8_t ao_preflight;           /* in preflight mode */
+
+static __pdata uint16_t        nsamples;
+__pdata int32_t ao_sample_pres_sum;
+#if HAS_ACCEL
+__pdata 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;
+       } else {
+               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 = pres_to_altitude(ao_ground_pres);
+               ao_preflight = FALSE;
+       }
+}
+
+
+uint8_t
+ao_sample(void)
+{
+       ao_config_get();
+       ao_wakeup(DATA_TO_XDATA(&ao_sample_data));
+       ao_sleep((void *) DATA_TO_XDATA(&ao_data_head));
+       while (ao_sample_data != ao_data_head) {
+               __xdata struct ao_data *ao_data;
+
+               /* Capture a sample */
+               ao_data = (struct ao_data *) &ao_data_ring[ao_sample_data];
+               ao_sample_tick = ao_data->tick;
+               ao_sample_pres = ao_data_pres(ao_data);
+               ao_sample_alt = pres_to_altitude(ao_sample_pres);
+               ao_sample_height = ao_sample_alt - ao_ground_height;
+#if HAS_ACCEL
+               ao_sample_accel = ao_data_accel_sample(ao_data);
+               if (ao_config.pad_orientation != AO_PAD_ORIENTATION_ANTENNA_UP)
+                       ao_sample_accel = ao_data_accel_invert(ao_sample_accel);
+               ao_data_set_accel(ao_data, ao_sample_accel);
+#endif
+
+               if (ao_preflight)
+                       ao_sample_preflight();
+               else
+                       ao_kalman();
+               ao_sample_data = ao_data_ring_next(ao_sample_data);
+       }
+       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_data = ao_data_head;
+       ao_preflight = TRUE;
+}