altos: Add nickle kalman implementation.
[fw/altos] / src / ao_flight.c
index 81aecad3e26e0053059b65a90b7273a9504d627a..493913b2c1b2a91732bde0fc9cbf407ee2609196 100644 (file)
 #include "ao.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
+
+#ifndef USE_KALMAN
+#error Please define USE_KALMAN
+#endif
+
 /* 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 */
-__pdata int16_t                        ao_flight_accel;        /* filtered acceleration */
 __pdata int16_t                        ao_flight_pres;         /* filtered pressure */
 __pdata int16_t                        ao_ground_pres;         /* startup pressure */
-__pdata int16_t                        ao_ground_accel;        /* startup acceleration */
 __pdata int16_t                        ao_min_pres;            /* minimum recorded pressure */
 __pdata uint16_t               ao_launch_tick;         /* time of launch detect */
 __pdata int16_t                        ao_main_pres;           /* pressure to eject main */
+#if HAS_ACCEL
+__pdata int16_t                        ao_flight_accel;        /* filtered acceleration */
+__pdata int16_t                        ao_ground_accel;        /* startup acceleration */
+#endif
 
 /*
  * track min/max data over a long interval to detect
  * resting
  */
 __pdata uint16_t               ao_interval_end;
-__pdata int16_t                        ao_interval_cur_min_accel;
-__pdata int16_t                        ao_interval_cur_max_accel;
 __pdata int16_t                        ao_interval_cur_min_pres;
 __pdata int16_t                        ao_interval_cur_max_pres;
-__pdata int16_t                        ao_interval_min_accel;
-__pdata int16_t                        ao_interval_max_accel;
 __pdata int16_t                        ao_interval_min_pres;
 __pdata int16_t                        ao_interval_max_pres;
+#if HAS_ACCEL
+__pdata int16_t                        ao_interval_cur_min_accel;
+__pdata int16_t                        ao_interval_cur_max_accel;
+__pdata int16_t                        ao_interval_min_accel;
+__pdata int16_t                        ao_interval_max_accel;
+#endif
 
 __data uint8_t ao_flight_adc;
-__pdata int16_t ao_raw_accel, ao_raw_accel_prev, ao_raw_pres;
-__pdata int16_t ao_accel_2g;
-
+__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
@@ -84,6 +106,8 @@ __xdata uint8_t ao_flight_force_idle;
 #define ACCEL_VEL_MACH VEL_MPS_TO_COUNT(200)
 #define ACCEL_VEL_BOOST        VEL_MPS_TO_COUNT(5)
 
+#endif
+
 /*
  * Barometer calibration
  *
@@ -117,6 +141,7 @@ __xdata uint8_t ao_flight_force_idle;
 
 #define BOOST_TICKS_MAX        AO_SEC_TO_TICKS(15)
 
+#if HAS_ACCEL
 /* This value is scaled in a weird way. It's a running total of accelerometer
  * readings minus the ground accelerometer reading. That means it measures
  * velocity, and quite accurately too. As it gets updated 100 times a second,
@@ -126,7 +151,66 @@ __pdata int32_t    ao_flight_vel;
 __pdata int32_t ao_min_vel;
 __pdata int32_t        ao_old_vel;
 __pdata int16_t ao_old_vel_tick;
-__xdata int32_t ao_raw_accel_sum, ao_raw_pres_sum;
+__xdata int32_t ao_raw_accel_sum;
+#endif
+
+#if USE_KALMAN
+__pdata int16_t                        ao_ground_height;
+__pdata int32_t                        ao_k_max_height;
+__pdata int32_t                        ao_k_height;
+__pdata int32_t                        ao_k_speed;
+__pdata int32_t                        ao_k_accel;
+
+#define to_fix16(x) ((int16_t) ((x) * 65536.0 + 0.5))
+#define to_fix32(x) ((int32_t) ((x) * 65536.0 + 0.5))
+
+#define from_fix(x)    ((x) >> 16)
+
+#define AO_K0_100      to_fix16(0.05680323)
+#define AO_K1_100      to_fix16(0.16608182)
+#define AO_K2_100      to_fix16(0.24279580)
+
+#define AO_K_STEP_100          to_fix16(0.01)
+#define AO_K_STEP_2_2_100      to_fix16(0.00005)
+
+#define AO_K0_10       to_fix16(0.23772023)
+#define AO_K1_10       to_fix16(0.32214149)
+#define AO_K2_10       to_fix16(0.21827159)
+
+#define AO_K_STEP_10           to_fix16(0.1)
+#define AO_K_STEP_2_2_10       to_fix16(0.005)
+
+static void
+ao_kalman_baro(void)
+{
+       int16_t err = ((ao_pres_to_altitude(ao_raw_pres) - ao_ground_height))
+               - (int16_t) (ao_k_height >> 16);
+
+#ifdef AO_FLIGHT_TEST
+       if (ao_flight_tick - ao_flight_prev_tick > 5) {
+               ao_k_height += ((ao_k_speed >> 16) * AO_K_STEP_10 +
+                               (ao_k_accel >> 16) * AO_K_STEP_2_2_10);
+               ao_k_speed += (ao_k_accel >> 16) * AO_K_STEP_10;
+
+               /* correct */
+               ao_k_height += (int32_t) AO_K0_10 * err;
+               ao_k_speed += (int32_t) AO_K1_10 * err;
+               ao_k_accel += (int32_t) AO_K2_10 * err;
+               return;
+       }
+#endif
+       ao_k_height += ((ao_k_speed >> 16) * AO_K_STEP_100 +
+                       (ao_k_accel >> 16) * AO_K_STEP_2_2_100);
+       ao_k_speed += (ao_k_accel >> 16) * AO_K_STEP_100;
+
+       /* correct */
+       ao_k_height += (int32_t) AO_K0_100 * err;
+       ao_k_speed += (int32_t) AO_K1_100 * err;
+       ao_k_accel += (int32_t) AO_K2_100 * err;
+}
+#endif
+
+__xdata int32_t ao_raw_pres_sum;
 
 /* Landing is detected by getting constant readings from both pressure and accelerometer
  * for a fairly long time (AO_INTERVAL_TICKS)
@@ -141,22 +225,31 @@ 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;
-       ao_raw_pres = 0;
+#endif
        ao_flight_tick = 0;
        for (;;) {
                ao_wakeup(DATA_TO_XDATA(&ao_flight_adc));
                ao_sleep(DATA_TO_XDATA(&ao_adc_head));
                while (ao_flight_adc != ao_adc_head) {
+#if HAS_ACCEL
                        __pdata uint8_t ticks;
                        __pdata int16_t ao_vel_change;
+#endif
                        __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_flight_pres -= ao_flight_pres >> 4;
+                       ao_flight_pres += ao_raw_pres >> 4;
+
+#if HAS_ACCEL
                        ao_raw_accel = ao_adc->accel;
 #if HAS_ACCEL_REF
                        /*
@@ -242,12 +335,9 @@ ao_flight(void)
                        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
-                       ao_raw_pres = ao_adc->pres;
 
                        ao_flight_accel -= ao_flight_accel >> 4;
                        ao_flight_accel += ao_raw_accel >> 4;
-                       ao_flight_pres -= ao_flight_pres >> 4;
-                       ao_flight_pres += ao_raw_pres >> 4;
                        /* Update velocity
                         *
                         * The accelerometer is mounted so that
@@ -264,12 +354,18 @@ ao_flight(void)
                                ao_flight_vel += (int32_t) ao_vel_change;
                        else
                                ao_flight_vel += (int32_t) ao_vel_change * (int32_t) ticks;
+#endif
 
+#if USE_KALMAN
+                       if (ao_flight_state > ao_flight_idle)
+                               ao_kalman_baro();
+#endif
                        ao_flight_adc = ao_adc_ring_next(ao_flight_adc);
                }
 
                if (ao_flight_pres < ao_min_pres)
                        ao_min_pres = ao_flight_pres;
+#if HAS_ACCEL
                if (ao_flight_vel >= 0) {
                        if (ao_flight_vel < ao_min_vel)
                            ao_min_vel = ao_flight_vel;
@@ -277,6 +373,7 @@ ao_flight(void)
                        if (-ao_flight_vel < ao_min_vel)
                            ao_min_vel = -ao_flight_vel;
                }
+#endif
 
                switch (ao_flight_state) {
                case ao_flight_startup:
@@ -287,21 +384,30 @@ ao_flight(void)
                         * 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;
                        }
+#if HAS_ACCEL
                        ao_ground_accel = ao_raw_accel_sum >> 9;
+#endif
                        ao_ground_pres = ao_raw_pres_sum >> 9;
                        ao_min_pres = ao_ground_pres;
                        ao_config_get();
+#if USE_KALMAN
+                       ao_ground_height = ao_pres_to_altitude(ao_ground_pres);
+#endif
                        ao_main_pres = ao_altitude_to_pres(ao_pres_to_altitude(ao_ground_pres) + ao_config.main_deploy);
+#if HAS_ACCEL
                        ao_accel_2g = ao_config.accel_minus_g - ao_config.accel_plus_g;
                        ao_flight_vel = 0;
                        ao_min_vel = 0;
                        ao_old_vel = ao_flight_vel;
                        ao_old_vel_tick = ao_flight_tick;
+#endif
 
                        /* Check to see what mode we should go to.
                         *  - Invalid mode if accel cal appears to be out
@@ -309,6 +415,7 @@ ao_flight(void)
                         *  - idle mode otherwise
                         */
                        ao_config_get();
+#if HAS_ACCEL
                        if (ao_config.accel_plus_g == 0 ||
                            ao_config.accel_minus_g == 0 ||
                            ao_flight_accel < ao_config.accel_plus_g - ACCEL_NOSE_UP ||
@@ -318,21 +425,26 @@ ao_flight(void)
                                 * (or uncalibrated values), so we go into invalid mode
                                 */
                                ao_flight_state = ao_flight_invalid;
-                               /* Allow packet mode in invalid flight state,
-                                * Still need to be able to fix the problem!
-                                */
-                               ao_packet_slave_start();
 
-                       } else if (ao_flight_accel < ao_config.accel_plus_g + ACCEL_NOSE_UP &&
-                                  !ao_flight_force_idle)
+                       } else
+#endif
+                               if (!ao_flight_force_idle
+#if HAS_ACCEL
+                                   && ao_flight_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
+
+                               /* Disable packet mode in pad state */
+                               ao_packet_slave_stop();
 
                                /* Turn on telemetry system */
                                ao_rdf_set(1);
@@ -344,9 +456,6 @@ ao_flight(void)
                                /* Set idle mode */
                                ao_flight_state = ao_flight_idle;
  
-                               /* Turn on packet system in idle mode */
-                               ao_packet_slave_start();
-
                                /* signal successful initialization by turning off the LED */
                                ao_led_off(AO_LED_RED);
                        }
@@ -356,6 +465,7 @@ ao_flight(void)
                        break;
                case ao_flight_pad:
 
+#if HAS_ACCEL
                        /* Trim velocity
                         *
                         * Once a second, remove any velocity from
@@ -366,6 +476,7 @@ ao_flight(void)
                                ao_flight_vel -= ao_old_vel;
                                ao_old_vel = ao_flight_vel;
                        }
+#endif
                        /* pad to boost:
                         *
                         * accelerometer: > 2g AND velocity > 5m/s
@@ -376,11 +487,35 @@ ao_flight(void)
                         * the barometer, but we use both to make sure this
                         * transition is detected
                         */
-                       if ((ao_flight_accel < ao_ground_accel - ACCEL_BOOST &&
-                            ao_flight_vel > ACCEL_VEL_BOOST) ||
+#if USE_KALMAN
+#if HAS_ACCEL
+                       /*
+                        * With an accelerometer, either to detect launch
+                        */
+                       if ((ao_k_accel > to_fix32(20) &&
+                            ao_k_speed > to_fix32(5)) ||
+                           ao_k_height > to_fix32(20))
+#else
+                       /*
+                        * Without an accelerometer, the barometer is far too
+                        * noisy to rely on speed or acceleration data
+                        */
+                       if (ao_k_height > to_fix32(20))
+#endif
+#else
+                       if (
+#if HAS_ACCEL
+                               (ao_flight_accel < ao_ground_accel - ACCEL_BOOST &&
+                                ao_flight_vel > ACCEL_VEL_BOOST) ||
+#endif
                            ao_flight_pres < ao_ground_pres - BARO_LAUNCH)
+#endif
                        {
+#if HAS_ACCEL || USE_KALMAN
                                ao_flight_state = ao_flight_boost;
+#else
+                               ao_flight_state = ao_flight_coast;
+#endif
                                ao_launch_tick = ao_flight_tick;
 
                                /* start logging data */
@@ -392,14 +527,17 @@ ao_flight(void)
                                /* disable RDF beacon */
                                ao_rdf_set(0);
 
+#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;
                        }
                        break;
+#if HAS_ACCEL || USE_KALMAN
                case ao_flight_boost:
 
                        /* boost to fast:
@@ -412,8 +550,13 @@ ao_flight(void)
                         * deceleration, or by waiting until the maximum burn duration
                         * (15 seconds) has past.
                         */
+#if USE_KALMAN
+                       if ((ao_k_accel < to_fix32(-10) && ao_k_height > to_fix32(100)) ||
+                           (int16_t) (ao_flight_tick - ao_launch_tick) > BOOST_TICKS_MAX)
+#else
                        if (ao_flight_accel > ao_ground_accel + ACCEL_COAST ||
                            (int16_t) (ao_flight_tick - ao_launch_tick) > BOOST_TICKS_MAX)
+#endif
                        {
                                ao_flight_state = ao_flight_fast;
                                ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
@@ -437,19 +580,34 @@ ao_flight(void)
                         * how big a pressure change the mach transition
                         * generates would be useful here.
                         */
+#if USE_KALMAN
+                       if (ao_k_speed < to_fix32(200) ||
+                           ao_k_height < ao_k_max_height - to_fix32(500))
+#else
                        if (ao_flight_vel < ACCEL_VEL_MACH ||
                            ao_flight_pres > ao_min_pres + BARO_COAST)
+#endif
                        {
+#if HAS_ACCEL
                                /* set min velocity to current velocity for
                                 * apogee detect
                                 */
                                ao_min_vel = abs(ao_flight_vel);
+#endif
                                ao_flight_state = ao_flight_coast;
                                ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
                        }
                        break;
+#endif /* HAS_ACCEL */
                case ao_flight_coast:
 
+#if USE_KALMAN
+                       /* apogee detect: coast to drogue deploy:
+                        *
+                        * speed: < 0
+                        */
+                       if (ao_k_speed < 0)
+#else
                        /* apogee detect: coast to drogue deploy:
                         *
                         * barometer: fall at least 10m
@@ -461,6 +619,7 @@ ao_flight(void)
                         * we'll trust to a single sensor for this test
                         */
                        if (ao_flight_pres > ao_min_pres + BARO_APOGEE)
+#endif
                        {
                                /* ignite the drogue charge */
                                ao_ignite(ao_igniter_drogue);
@@ -468,8 +627,10 @@ ao_flight(void)
                                /* slow down the telemetry system */
                                ao_telemetry_set_interval(AO_TELEMETRY_INTERVAL_RECOVER);
 
+#if !USE_KALMAN
                                /* slow down the ADC sample rate */
                                ao_timer_set_adc_interval(10);
+#endif
 
                                /*
                                 * Start recording min/max accel and pres for a while
@@ -478,8 +639,10 @@ ao_flight(void)
                                /* Set the 'last' limits to max range to prevent
                                 * early resting detection
                                 */
+#if HAS_ACCEL
                                ao_interval_min_accel = 0;
                                ao_interval_max_accel = 0x7fff;
+#endif
                                ao_interval_min_pres = 0;
                                ao_interval_max_pres = 0x7fff;
 
@@ -487,7 +650,9 @@ ao_flight(void)
                                ao_interval_end = ao_flight_tick + AO_INTERVAL_TICKS;
 
                                ao_interval_cur_min_pres = ao_interval_cur_max_pres = ao_flight_pres;
+#if HAS_ACCEL
                                ao_interval_cur_min_accel = ao_interval_cur_max_accel = ao_flight_accel;
+#endif
 
                                /* and enter drogue state */
                                ao_flight_state = ao_flight_drogue;
@@ -509,7 +674,11 @@ ao_flight(void)
                         * at that point. Perhaps also use the drogue sense lines
                         * to notice continutity?
                         */
+#if USE_KALMAN
+                       if (from_fix(ao_k_height) < ao_config.main_deploy)
+#else
                        if (ao_flight_pres >= ao_main_pres)
+#endif
                        {
                                ao_ignite(ao_igniter_main);
                                ao_flight_state = ao_flight_main;
@@ -530,21 +699,28 @@ ao_flight(void)
                                ao_interval_cur_min_pres = ao_flight_pres;
                        if (ao_flight_pres > ao_interval_cur_max_pres)
                                ao_interval_cur_max_pres = ao_flight_pres;
+#if HAS_ACCEL
                        if (ao_flight_accel < ao_interval_cur_min_accel)
                                ao_interval_cur_min_accel = ao_flight_accel;
                        if (ao_flight_accel > ao_interval_cur_max_accel)
                                ao_interval_cur_max_accel = ao_flight_accel;
+#endif
 
                        if ((int16_t) (ao_flight_tick - ao_interval_end) >= 0) {
                                ao_interval_max_pres = ao_interval_cur_max_pres;
                                ao_interval_min_pres = ao_interval_cur_min_pres;
+                               ao_interval_cur_min_pres = ao_interval_cur_max_pres = ao_flight_pres;
+#if HAS_ACCEL
                                ao_interval_max_accel = ao_interval_cur_max_accel;
                                ao_interval_min_accel = ao_interval_cur_min_accel;
-                               ao_interval_end = ao_flight_tick + AO_INTERVAL_TICKS;
-                               ao_interval_cur_min_pres = ao_interval_cur_max_pres = ao_flight_pres;
                                ao_interval_cur_min_accel = ao_interval_cur_max_accel = ao_flight_accel;
+#endif
+                               ao_interval_end = ao_flight_tick + AO_INTERVAL_TICKS;
 
-                               if ((uint16_t) (ao_interval_max_accel - ao_interval_min_accel) < (uint16_t) ACCEL_INT_LAND &&
+                               if (
+#if HAS_ACCEL
+                                       (uint16_t) (ao_interval_max_accel - ao_interval_min_accel) < (uint16_t) ACCEL_INT_LAND &&
+#endif
                                    ao_flight_pres > ao_ground_pres - BARO_LAND &&
                                    (uint16_t) (ao_interval_max_pres - ao_interval_min_pres) < (uint16_t) BARO_INT_LAND)
                                {