__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;
+
+__xdata uint8_t ao_flight_force_idle;
/* Accelerometer calibration
*
#define GRAVITY 9.80665
/* convert m/s to velocity count */
-#define VEL_MPS_TO_COUNT(mps) ((int32_t) (((mps) / GRAVITY) * ACCEL_G * 100))
-
-#define ACCEL_G 265
-#define ACCEL_ZERO_G 16000
-#define ACCEL_NOSE_UP (ACCEL_G * 2 /3)
-#define ACCEL_BOOST ACCEL_G * 2
-#define ACCEL_INT_LAND (ACCEL_G / 10)
-#define ACCEL_VEL_LAND VEL_MPS_TO_COUNT(10)
+#define VEL_MPS_TO_COUNT(mps) (((int32_t) (((mps) / GRAVITY) * (AO_HERTZ/2))) * (int32_t) ao_accel_2g)
+
+#define ACCEL_NOSE_UP (ao_accel_2g >> 2)
+#define ACCEL_BOOST ao_accel_2g
+#define ACCEL_COAST (ao_accel_2g >> 3)
+#define ACCEL_INT_LAND (ao_accel_2g >> 3)
#define ACCEL_VEL_MACH VEL_MPS_TO_COUNT(200)
-#define ACCEL_VEL_APOGEE VEL_MPS_TO_COUNT(2)
-#define ACCEL_VEL_MAIN VEL_MPS_TO_COUNT(100)
#define ACCEL_VEL_BOOST VEL_MPS_TO_COUNT(5)
/*
/* 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(20)
+#define AO_INTERVAL_TICKS AO_SEC_TO_TICKS(5)
#define abs(a) ((a) < 0 ? -(a) : (a))
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;
* so subtract instead of add.
*/
ticks = ao_flight_tick - ao_flight_prev_tick;
- ao_vel_change = (((ao_raw_accel >> 1) + (ao_raw_accel_prev >> 1)) - ao_ground_accel);
+ ao_vel_change = ao_ground_accel - (((ao_raw_accel + 1) >> 1) + ((ao_raw_accel_prev + 1) >> 1));
ao_raw_accel_prev = ao_raw_accel;
/* one is a common interval */
if (ticks == 1)
- ao_flight_vel -= (int32_t) ao_vel_change;
+ ao_flight_vel += (int32_t) ao_vel_change;
else
- ao_flight_vel -= (int32_t) ao_vel_change * (int32_t) ticks;
+ ao_flight_vel += (int32_t) ao_vel_change * (int32_t) ticks;
ao_flight_adc = ao_adc_ring_next(ao_flight_adc);
}
/* 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;
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_flight_accel < ao_config.accel_zero_g - ACCEL_NOSE_UP) {
+ 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 the Green LED in idle mode
- */
- ao_led_on(AO_LED_GREEN);
- ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+ /* 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);
}
- /* 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:
/* 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;
}
* deceleration, or by waiting until the maximum burn duration
* (15 seconds) has past.
*/
- if (ao_flight_accel > ao_ground_accel + (ACCEL_G >> 2) ||
+ if (ao_flight_accel > ao_ground_accel + ACCEL_COAST ||
(int16_t) (ao_flight_tick - ao_launch_tick) > BOOST_TICKS_MAX)
{
ao_flight_state = ao_flight_fast;
/* apogee detect: coast to drogue deploy:
*
- * accelerometer: abs(velocity) > min_velocity + 2m/s
- * OR
* barometer: fall at least 10m
*
- * If the barometer saturates because the flight
- * goes over its measuring range (about 53k'),
- * requiring a 10m fall will avoid prematurely
- * detecting apogee; the accelerometer will take
- * over in that case and the integrated velocity
- * measurement should suffice to find apogee
+ * It would be nice to use the accelerometer
+ * to detect apogee as well, but tests have
+ * shown that flights far from vertical would
+ * grossly mis-detect apogee. So, for now,
+ * we'll trust to a single sensor for this test
*/
- if (/* abs(ao_flight_vel) > ao_min_vel + ACCEL_VEL_APOGEE || */
- ao_flight_pres > ao_min_pres + BARO_APOGEE)
+ if (ao_flight_pres > ao_min_pres + BARO_APOGEE)
{
/* ignite the drogue charge */
ao_ignite(ao_igniter_drogue);
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;
- }
- if ((uint16_t) (ao_interval_max_accel - ao_interval_min_accel) < (uint16_t) ACCEL_INT_LAND &&
- ao_flight_pres > ao_ground_pres - BARO_LAND &&
- (uint16_t) (ao_interval_max_pres - ao_interval_min_pres) < (uint16_t) BARO_INT_LAND)
- {
- ao_flight_state = ao_flight_landed;
+ if ((uint16_t) (ao_interval_max_accel - ao_interval_min_accel) < (uint16_t) ACCEL_INT_LAND &&
+ ao_flight_pres > ao_ground_pres - BARO_LAND &&
+ (uint16_t) (ao_interval_max_pres - ao_interval_min_pres) < (uint16_t) BARO_INT_LAND)
+ {
+ ao_flight_state = ao_flight_landed;
- /* turn off the ADC capture */
- ao_timer_set_adc_interval(0);
- /* Enable RDF beacon */
- ao_rdf_set(1);
+ /* turn off the ADC capture */
+ ao_timer_set_adc_interval(0);
+ /* Enable RDF beacon */
+ ao_rdf_set(1);
- ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+ ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+ }
}
break;
case ao_flight_landed:
}
}
-#define AO_ACCEL_COUNT_TO_MSS(count) ((count) / 27)
-#define AO_VEL_COUNT_TO_MS(count) ((int16_t) ((count) / 2700))
-
-static void
-ao_flight_status(void) __reentrant
-{
- printf("STATE: %7s accel: %d speed: %d altitude: %d main: %d\n",
- ao_state_names[ao_flight_state],
- AO_ACCEL_COUNT_TO_MSS(ACCEL_ZERO_G - ao_flight_accel),
- AO_VEL_COUNT_TO_MS(ao_flight_vel),
- ao_pres_to_altitude(ao_flight_pres),
- ao_pres_to_altitude(ao_main_pres));
-}
-
static __xdata struct ao_task flight_task;
-__code struct ao_cmds ao_flight_cmds[] = {
- { 'f', ao_flight_status, "f Display current flight state" },
- { 0, ao_flight_status, NULL }
-};
-
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");
- ao_cmd_register(&ao_flight_cmds[0]);
}