Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
[fw/altos] / src / ao_flight.c
index e0fd97f2464b901a61649037e61a9848e46b819e..81aecad3e26e0053059b65a90b7273a9504d627a 100644 (file)
@@ -84,9 +84,6 @@ __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)
 
-int32_t accel_vel_mach;
-int32_t accel_vel_boost;
-
 /*
  * Barometer calibration
  *
@@ -149,16 +146,103 @@ ao_flight(void)
        ao_raw_pres = 0;
        ao_flight_tick = 0;
        for (;;) {
-               ao_sleep(&ao_adc_ring);
+               ao_wakeup(DATA_TO_XDATA(&ao_flight_adc));
+               ao_sleep(DATA_TO_XDATA(&ao_adc_head));
                while (ao_flight_adc != ao_adc_head) {
                        __pdata uint8_t ticks;
                        __pdata int16_t ao_vel_change;
+                       __xdata struct ao_adc *ao_adc;
                        ao_flight_prev_tick = ao_flight_tick;
 
                        /* Capture a sample */
-                       ao_raw_accel = ao_adc_ring[ao_flight_adc].accel;
-                       ao_raw_pres = ao_adc_ring[ao_flight_adc].pres;
-                       ao_flight_tick = ao_adc_ring[ao_flight_adc].tick;
+                       ao_adc = &ao_adc_ring[ao_flight_adc];
+                       ao_flight_tick = ao_adc->tick;
+                       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
+                       ao_raw_pres = ao_adc->pres;
 
                        ao_flight_accel -= ao_flight_accel >> 4;
                        ao_flight_accel += ao_raw_accel >> 4;
@@ -199,57 +283,76 @@ ao_flight(void)
 
                        /* startup state:
                         *
-                        * Collect 1000 samples of acceleration and pressure
+                        * Collect 512 samples of acceleration and pressure
                         * data and average them to find the resting values
                         */
-                       if (nsamples < 1000) {
+                       if (nsamples < 512) {
                                ao_raw_accel_sum += ao_raw_accel;
                                ao_raw_pres_sum += ao_raw_pres;
                                ++nsamples;
                                continue;
                        }
-                       ao_ground_accel = (ao_raw_accel_sum / nsamples);
-                       ao_ground_pres = (ao_raw_pres_sum / nsamples);
+                       ao_ground_accel = ao_raw_accel_sum >> 9;
+                       ao_ground_pres = ao_raw_pres_sum >> 9;
                        ao_min_pres = ao_ground_pres;
                        ao_config_get();
                        ao_main_pres = ao_altitude_to_pres(ao_pres_to_altitude(ao_ground_pres) + ao_config.main_deploy);
                        ao_accel_2g = ao_config.accel_minus_g - ao_config.accel_plus_g;
-                       accel_vel_mach = ACCEL_VEL_MACH;
-                       accel_vel_boost = ACCEL_VEL_BOOST;
                        ao_flight_vel = 0;
                        ao_min_vel = 0;
                        ao_old_vel = ao_flight_vel;
                        ao_old_vel_tick = ao_flight_tick;
 
-                       /* Go to pad state if the nose is pointing up */
+                       /* 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
+                        */
                        ao_config_get();
-                       if (ao_config.accel_plus_g != 0 &&
-                           ao_config.accel_minus_g != 0 &&
-                           ao_flight_accel < ao_config.accel_plus_g + ACCEL_NOSE_UP &&
-                           !ao_flight_force_idle)
+                       if (ao_config.accel_plus_g == 0 ||
+                           ao_config.accel_minus_g == 0 ||
+                           ao_flight_accel < ao_config.accel_plus_g - ACCEL_NOSE_UP ||
+                           ao_flight_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;
+                               /* 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)
+                       {
+                               /* Set pad mode - we can fly! */
+                               ao_flight_state = ao_flight_pad;
+
                                /* Disable the USB controller in flight mode
                                 * to save power
                                 */
                                ao_usb_disable();
 
-                               /* Turn on telemetry system
-                                */
+                               /* Turn on telemetry system */
                                ao_rdf_set(1);
                                ao_telemetry_set_interval(AO_TELEMETRY_INTERVAL_PAD);
 
-                               ao_flight_state = ao_flight_pad;
-                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                               /* signal successful initialization by turning off the LED */
+                               ao_led_off(AO_LED_RED);
                        } else {
-                               ao_flight_state = ao_flight_idle;
-
-                               /* Turn on packet system in idle mode
-                                */
+                               /* Set idle mode */
+                               ao_flight_state = ao_flight_idle;
+                               /* Turn on packet system in idle mode */
                                ao_packet_slave_start();
-                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+
+                               /* signal successful initialization by turning off the LED */
+                               ao_led_off(AO_LED_RED);
                        }
-                       /* 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:
 
@@ -289,6 +392,10 @@ ao_flight(void)
                                /* disable RDF beacon */
                                ao_rdf_set(0);
 
+                               /* Record current GPS position by waking up GPS log tasks */
+                               ao_wakeup(&ao_gps_data);
+                               ao_wakeup(&ao_gps_tracking_data);
+
                                ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
                                break;
                        }
@@ -464,11 +571,5 @@ void
 ao_flight_init(void)
 {
        ao_flight_state = ao_flight_startup;
-       ao_interval_min_accel = 0;
-       ao_interval_max_accel = 0x7fff;
-       ao_interval_min_pres = 0;
-       ao_interval_max_pres = 0x7fff;
-       ao_interval_end = AO_INTERVAL_TICKS;
-
        ao_add_task(&flight_task, ao_flight, "flight");
 }