From: Keith Packard Date: Fri, 24 Apr 2009 05:17:44 +0000 (-0700) Subject: Add igniters and update flight control algorithm X-Git-Tag: 0.1~26 X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=commitdiff_plain;h=b32f2f0090ff967edac07ae4d7a9895ed0b96d31 Add igniters and update flight control algorithm --- diff --git a/Makefile b/Makefile index 2a6b7adc..a391c510 100644 --- a/Makefile +++ b/Makefile @@ -61,7 +61,8 @@ TELE_DRIVER_SRC = \ # TM_DRIVER_SRC = \ ao_adc.c \ - ao_ee.c + ao_ee.c \ + ao_ignite.c # # Tasks run on TeleMetrum @@ -193,3 +194,6 @@ clean: rm -f $(PROGS) $(PCDB) $(PLNK) $(PMAP) $(PMEM) $(PAOM) install: + +ao_flight_test: ao_flight.c ao_flight_test.c + cc -g -o $@ ao_flight_test.c diff --git a/ao.h b/ao.h index b10f1b34..137a0143 100644 --- a/ao.h +++ b/ao.h @@ -29,7 +29,7 @@ /* Stack runs from above the allocated __data space to 0xfe, which avoids * writing to 0xff as that triggers the stack overflow indicator */ -#define AO_STACK_START 0x68 +#define AO_STACK_START 0x70 #define AO_STACK_END 0xfe #define AO_STACK_SIZE (AO_STACK_END - AO_STACK_START + 1) @@ -123,6 +123,8 @@ ao_timer_init(void); */ #define AO_ADC_RING 64 +#define ao_adc_ring_next(n) (((n) + 1) & (AO_ADC_RING - 1)) +#define ao_adc_ring_prev(n) (((n) - 1) & (AO_ADC_RING - 1)) /* * One set of samples read from the A/D converter @@ -544,7 +546,7 @@ enum ao_flight_state { }; extern __xdata struct ao_adc ao_flight_data; -extern __pdata enum flight_state ao_flight_state; +extern __pdata enum ao_flight_state ao_flight_state; extern __pdata uint16_t ao_flight_tick; extern __pdata int16_t ao_flight_accel; extern __pdata int16_t ao_flight_pres; @@ -734,5 +736,30 @@ ao_monitor_init(void); void flush(void); +/* + * ao_ignite.c + */ + +enum ao_igniter { + ao_igniter_drogue = 0, + ao_igniter_main = 1 +}; + +void +ao_ignite(enum ao_igniter igniter); + +enum ao_igniter_status { + ao_igniter_unknown, /* unknown status (ambiguous voltage) */ + ao_igniter_ready, /* continuity detected */ + ao_igniter_active, /* igniter firing */ + ao_igniter_open, /* open circuit detected */ +}; + +enum ao_igniter_status +ao_igniter_status(enum ao_igniter igniter); + +void +ao_igniter_init(void); + #endif /* _AO_H_ */ diff --git a/ao_adc.c b/ao_adc.c index 82e1b01b..639c5f6c 100644 --- a/ao_adc.c +++ b/ao_adc.c @@ -35,10 +35,7 @@ ao_adc_sleep(void) void ao_adc_get(__xdata struct ao_adc *packet) { - uint8_t i = ao_adc_head; - if (i == 0) - i = AO_ADC_RING; - i--; + uint8_t i = ao_adc_ring_prev(ao_adc_head); memcpy(packet, &ao_adc_ring[i], sizeof (struct ao_adc)); } @@ -58,9 +55,7 @@ ao_adc_isr(void) interrupt 1 } else { /* record this conversion series */ ao_adc_ring[ao_adc_head].tick = ao_time(); - ao_adc_head++; - if (ao_adc_head == AO_ADC_RING) - ao_adc_head = 0; + ao_adc_head = ao_adc_ring_next(ao_adc_head); ao_wakeup(ao_adc_ring); } } diff --git a/ao_flight.c b/ao_flight.c index 3aff866a..5998f291 100644 --- a/ao_flight.c +++ b/ao_flight.c @@ -15,12 +15,13 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#ifndef AO_FLIGHT_TEST #include "ao.h" +#endif /* Main flight thread. */ -__xdata struct ao_adc ao_flight_data; /* last acquired data */ -__pdata enum flight_state ao_flight_state; /* current flight state */ +__pdata enum ao_flight_state ao_flight_state; /* current flight state */ __pdata uint16_t ao_flight_tick; /* time of last data */ __pdata int16_t ao_flight_accel; /* filtered acceleration */ __pdata int16_t ao_flight_pres; /* filtered pressure */ @@ -44,6 +45,9 @@ __pdata int16_t ao_interval_max_accel; __pdata int16_t ao_interval_min_pres; __pdata int16_t ao_interval_max_pres; +__data uint8_t ao_flight_adc; +__xdata int16_t ao_accel, ao_prev_accel, ao_pres; + #define AO_INTERVAL_TICKS AO_SEC_TO_TICKS(5) /* Accelerometer calibration @@ -66,6 +70,7 @@ __pdata int16_t ao_interval_max_pres; #define ACCEL_ZERO_G 16000 #define ACCEL_NOSE_UP (ACCEL_ZERO_G - ACCEL_G * 2 /3) #define ACCEL_BOOST (ACCEL_NOSE_UP - ACCEL_G * 2) +#define ACCEL_LAND (ACCEL_G / 10) /* * Barometer calibration @@ -96,23 +101,45 @@ __pdata int16_t ao_interval_max_pres; * case of other failures */ -#define BOOST_TICKS_MAX AO_SEC_TO_TICKS(10) +#define BOOST_TICKS_MAX AO_SEC_TO_TICKS(15) + +/* 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, + * it's scaled by 100 + */ +__data int32_t ao_flight_vel; + +/* convert m/s to velocity count */ +#define VEL_MPS_TO_COUNT(mps) ((int32_t) ((int32_t) (mps) * (int32_t) 100 / (int32_t) ACCEL_G)) void ao_flight(void) { __pdata static uint8_t nsamples = 0; + ao_flight_adc = ao_adc_head; + ao_prev_accel = 0; + ao_accel = 0; + ao_pres = 0; for (;;) { ao_sleep(&ao_adc_ring); - ao_adc_get(&ao_flight_data); + while (ao_flight_adc != ao_adc_head) { + ao_accel = ao_adc_ring[ao_flight_adc].accel; + ao_pres = ao_adc_ring[ao_flight_adc].pres; + ao_flight_tick = ao_adc_ring[ao_flight_adc].tick; + ao_flight_vel += (int32_t) (((ao_accel + ao_prev_accel) >> 4) - (ao_ground_accel << 1)); + ao_prev_accel = ao_accel; + ao_flight_adc = ao_adc_ring_next(ao_flight_adc); + } ao_flight_accel -= ao_flight_accel >> 4; - ao_flight_accel += ao_flight_data.accel >> 4; + ao_flight_accel += ao_accel >> 4; ao_flight_pres -= ao_flight_pres >> 4; - ao_flight_pres += ao_flight_data.pres >> 4; - ao_flight_tick = ao_time(); + ao_flight_pres += ao_pres >> 4; - ao_flight_tick = ao_time(); + if (ao_flight_pres < ao_min_pres) + ao_min_pres = ao_flight_pres; + 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; @@ -131,11 +158,12 @@ ao_flight(void) ao_ground_pres = ao_flight_pres; ao_min_pres = ao_flight_pres; ao_main_pres = ao_ground_pres - BARO_MAIN; + ao_flight_vel = 0; ao_interval_end = ao_flight_tick; - /* Go to launchpad state if the nose is pointing up and the battery is charged */ - if (ao_flight_accel < ACCEL_NOSE_UP && ao_flight_data.v_batt > 23000) { + /* Go to launchpad state if the nose is pointing up */ + if (ao_flight_accel < ACCEL_NOSE_UP) { ao_flight_state = ao_flight_launchpad; ao_wakeup(DATA_TO_XDATA(&ao_flight_state)); } else { @@ -152,6 +180,12 @@ ao_flight(void) ao_led_off(AO_LED_RED); break; case ao_flight_launchpad: + + /* pad to boost: + * + * accelerometer: > 2g + * barometer: > 20m vertical motion + */ if (ao_flight_accel < ACCEL_BOOST || ao_flight_pres + BARO_LAUNCH < ao_ground_pres) { @@ -162,8 +196,14 @@ ao_flight(void) } break; case ao_flight_boost: - if (ao_flight_accel > ACCEL_ZERO_G || - (int16_t) (ao_flight_data.tick - ao_launch_time) > BOOST_TICKS_MAX) + + /* boost to coast: + * + * accelerometer: start to fall at > 1/4 G + * time: boost for more than 15 seconds + */ + if (ao_flight_accel > ao_ground_accel + (ACCEL_G >> 2) || + (int16_t) (ao_flight_tick - ao_launch_time) > BOOST_TICKS_MAX) { ao_flight_state = ao_flight_coast; ao_wakeup(DATA_TO_XDATA(&ao_flight_state)); @@ -171,31 +211,60 @@ ao_flight(void) } break; case ao_flight_coast: - if (ao_flight_pres < ao_min_pres) - ao_min_pres = ao_flight_pres; - if (ao_flight_pres - BARO_APOGEE > ao_min_pres) { + + /* coast to apogee detect: + * + * accelerometer: integrated velocity < 200 m/s + * barometer: fall at least 500m from max altitude + */ + if (ao_flight_vel < VEL_MPS_TO_COUNT(200) || + ao_flight_pres - (5 * BARO_kPa) > ao_min_pres) + { ao_flight_state = ao_flight_apogee; ao_wakeup(DATA_TO_XDATA(&ao_flight_state)); } break; case ao_flight_apogee: -// ao_ignite(AO_IGNITE_DROGUE); - ao_flight_state = ao_flight_drogue; - ao_wakeup(DATA_TO_XDATA(&ao_flight_state)); + + /* apogee to drogue deploy: + * + * accelerometer: integrated velocity < 10m/s + * barometer: fall at least 10m + */ + if (ao_flight_vel < VEL_MPS_TO_COUNT(-10) || + ao_flight_pres - BARO_APOGEE > ao_min_pres) + { + ao_ignite(ao_igniter_drogue); + ao_flight_state = ao_flight_drogue; + ao_wakeup(DATA_TO_XDATA(&ao_flight_state)); + } break; case ao_flight_drogue: - if (ao_flight_pres >= ao_main_pres) { -// ao_ignite(AO_IGNITE_MAIN); + + /* drogue to main deploy: + * + * accelerometer: abs(velocity) > 50m/s + * barometer: reach main deploy altitude + */ + if (ao_flight_vel < VEL_MPS_TO_COUNT(-50) || + ao_flight_vel > VEL_MPS_TO_COUNT(50) || + ao_flight_pres >= ao_main_pres) + { + ao_ignite(ao_igniter_main); ao_flight_state = ao_flight_main; ao_wakeup(DATA_TO_XDATA(&ao_flight_state)); } - if ((ao_interval_max_pres - ao_interval_min_pres) < BARO_LAND) { - ao_flight_state = ao_flight_landed; - ao_wakeup(DATA_TO_XDATA(&ao_flight_state)); - } - break; + /* fall through... */ case ao_flight_main: - if ((ao_interval_max_pres - ao_interval_min_pres) < BARO_LAND) { + + /* drogue/main to land: + * + * accelerometer: value stable + * barometer: altitude stable + */ + if ((ao_interval_max_accel - ao_interval_min_accel) < ACCEL_LAND || + (ao_interval_max_pres - ao_interval_min_pres) < BARO_LAND) + { ao_flight_state = ao_flight_landed; ao_wakeup(DATA_TO_XDATA(&ao_flight_state)); } diff --git a/ao_flight_test.c b/ao_flight_test.c new file mode 100644 index 00000000..a88d2b14 --- /dev/null +++ b/ao_flight_test.c @@ -0,0 +1,205 @@ +/* + * Copyright © 2009 Keith Packard + * + * 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 +#include +#include + +#define AO_ADC_RING 64 +#define ao_adc_ring_next(n) (((n) + 1) & (AO_ADC_RING - 1)) +#define ao_adc_ring_prev(n) (((n) - 1) & (AO_ADC_RING - 1)) + +/* + * One set of samples read from the A/D converter + */ +struct ao_adc { + uint16_t tick; /* tick when the sample was read */ + int16_t accel; /* accelerometer */ + int16_t pres; /* pressure sensor */ + int16_t temp; /* temperature sensor */ + int16_t v_batt; /* battery voltage */ + int16_t sense_d; /* drogue continuity sense */ + int16_t sense_m; /* main continuity sense */ +}; + +#define __pdata +#define __data +#define __xdata + +enum ao_flight_state { + ao_flight_startup = 0, + ao_flight_idle = 1, + ao_flight_launchpad = 2, + ao_flight_boost = 3, + ao_flight_coast = 4, + ao_flight_apogee = 5, + ao_flight_drogue = 6, + ao_flight_main = 7, + ao_flight_landed = 8, + ao_flight_invalid = 9 +}; + +struct ao_adc ao_adc_ring[AO_ADC_RING]; +uint8_t ao_adc_head; + +#define ao_led_on(l) +#define ao_led_off(l) +#define ao_timer_set_adc_interval(i) +#define ao_wakeup(wchan) ao_dump_state() + +enum ao_igniter { + ao_igniter_drogue = 0, + ao_igniter_main = 1 +}; + +void +ao_ignite(enum ao_igniter igniter) +{ + printf ("ignite %s\n", igniter == ao_igniter_drogue ? "drogue" : "main"); +} + +struct ao_task { + int dummy; +}; + +#define ao_add_task(t,f,n) + +#define ao_log_start() +#define ao_log_stop() + +#define AO_MS_TO_TICKS(ms) ((ms) / 10) +#define AO_SEC_TO_TICKS(s) ((s) * 100) + +#define AO_FLIGHT_TEST + +struct ao_adc ao_adc_static; + +FILE *emulator_in; + +void +ao_dump_state(void); + +void +ao_sleep(void *wchan); + +#include "ao_flight.c" + +void +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) { + printf("tick %04x accel %04x pres %04x\n", + ao_adc_static.tick, + ao_adc_static.accel, + ao_adc_static.pres); + } +} + +static int ao_records_read = 0; + +void +ao_sleep(void *wchan) +{ + ao_dump_state(); + if (wchan == &ao_adc_ring) { + char type; + uint16_t tick; + uint16_t a, b; + int ret; + + for (;;) { + if (ao_records_read > 20 && ao_flight_state == ao_flight_startup) + { + ao_insert(); + return; + } + + ret = fscanf(emulator_in, "%c %hx %hx %hx\n", &type, &tick, &a, &b); + if (ret == EOF) { + ao_insert(); + return; + } + if (ret != 4) + continue; + switch (type) { + case 'F': + break; + case 'S': + break; + case 'A': + ao_adc_static.tick = tick; + ao_adc_static.accel = a; + ao_adc_static.pres = b; + ao_records_read++; + ao_insert(); + return; + case 'T': + ao_adc_static.tick = tick; + ao_adc_static.temp = a; + ao_adc_static.v_batt = b; + break; + case 'D': + case 'G': + case 'N': + case 'W': + case 'H': + break; + } + } + + } +} + +const char const * const ao_state_names[] = { + "startup", "idle", "pad", "boost", "coast", + "apogee", "drogue", "main", "landed", "invalid" +}; + +static int16_t altitude[2048] = { +#include "altitude.h" +}; + +#define GRAVITY 9.80665 +#define COUNTS_PER_G 264.8 + +void +ao_dump_state(void) +{ + if (ao_flight_state == ao_flight_startup) + return; + printf ("\t%s accel %g vel %g alt %d\n", + ao_state_names[ao_flight_state], + (ao_flight_accel - ao_ground_accel) / COUNTS_PER_G * GRAVITY, + (double) ao_flight_vel, + altitude[ao_flight_pres >> 4]); + if (ao_flight_state == ao_flight_landed) + exit(0); +} + +int +main (int argc, char **argv) +{ + emulator_in = fopen (argv[1], "r"); + if (!emulator_in) { + perror(argv[1]); + exit(1); + } + ao_flight_init(); + ao_flight(); +} diff --git a/ao_ignite.c b/ao_ignite.c new file mode 100644 index 00000000..acb9399b --- /dev/null +++ b/ao_ignite.c @@ -0,0 +1,123 @@ +/* + * Copyright © 2009 Keith Packard + * + * 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" + +#define AO_IGNITER_DROGUE P2_3 +#define AO_IGNITER_MAIN P2_4 + +/* XXX test these values with real igniters */ +#define AO_IGNITER_OPEN 100 +#define AO_IGNITER_CLOSED 20000 +#define AO_IGNITER_FIRE_TIME AO_MS_TO_TICKS(50) +#define AO_IGNITER_CHARGE_TIME AO_MS_TO_TICKS(200) + +struct ao_ignition { + uint8_t request; + uint8_t fired; + uint8_t firing; +}; + +__xdata struct ao_ignition ao_ignition[2]; + +void +ao_ignite(enum ao_igniter igniter) __critical +{ + ao_ignition[igniter].request = 1; + ao_wakeup(&ao_ignition[0]); +} + +enum ao_igniter_status +ao_igniter_status(enum ao_igniter igniter) +{ + __xdata struct ao_adc adc; + __xdata int16_t value; + __xdata uint8_t request, firing, fired; + + __critical { + ao_adc_sleep(); + ao_adc_get(&adc); + request = ao_ignition[igniter].request; + fired = ao_ignition[igniter].fired; + firing = ao_ignition[igniter].firing; + } + if (firing || (request && !fired)) + return ao_igniter_active; + + value = (AO_IGNITER_CLOSED>>1); + switch (igniter) { + case ao_igniter_drogue: + value = adc.sense_d; + break; + case ao_igniter_main: + value = adc.sense_m; + break; + } + if (value < AO_IGNITER_OPEN) + return ao_igniter_open; + else if (value > AO_IGNITER_CLOSED) + return ao_igniter_ready; + else + return ao_igniter_unknown; +} + +void +ao_igniter_fire(enum ao_igniter igniter) __critical +{ + ao_ignition[igniter].firing = 1; + switch (igniter) { + case ao_igniter_drogue: + AO_IGNITER_DROGUE = 1; + ao_delay(AO_IGNITER_FIRE_TIME); + AO_IGNITER_DROGUE = 0; + break; + case ao_igniter_main: + AO_IGNITER_MAIN = 1; + ao_delay(AO_IGNITER_FIRE_TIME); + AO_IGNITER_MAIN = 0; + break; + } + ao_ignition[igniter].firing = 0; +} + +void +ao_igniter(void) +{ + __xdata enum ao_ignter igniter; + __xdata enum ao_igniter_status status; + + for (;;) { + ao_sleep(&ao_ignition); + for (igniter = ao_igniter_drogue; igniter <= ao_igniter_main; igniter++) { + if (ao_ignition[igniter].request && !ao_ignition[igniter].fired) { + ao_igniter_fire(igniter); + ao_delay(AO_IGNITER_CHARGE_TIME); + status = ao_igniter_status(igniter); + if (status == ao_igniter_open) + ao_ignition[igniter].fired = 1; + } + } + } +} + +__xdata struct ao_task ao_igniter_task; + +void +ao_igniter_init(void) +{ + ao_add_task(&ao_igniter_task, ao_igniter, "igniter"); +} diff --git a/ao_log.c b/ao_log.c index 1b473c14..c74893f8 100644 --- a/ao_log.c +++ b/ao_log.c @@ -153,9 +153,7 @@ ao_log(void) log.u.deploy.main = ao_adc_ring[ao_log_adc_pos].sense_m; ao_log_data(&log); } - ao_log_adc_pos++; - if (ao_log_adc_pos == AO_ADC_RING) - ao_log_adc_pos = 0; + ao_log_adc_pos = ao_adc_ring_next(ao_log_adc_pos); } /* Wait for a while */ diff --git a/ao_monitor.c b/ao_monitor.c index 3db5e42d..845b63bf 100644 --- a/ao_monitor.c +++ b/ao_monitor.c @@ -52,6 +52,7 @@ ao_monitor(void) recv.telemetry.adc.sense_m); ao_gps_print(&recv.telemetry.gps); ao_usb_flush(); + ao_led_for(AO_LED_GREEN, AO_MS_TO_TICKS(10)); } } diff --git a/ao_telemetrum.c b/ao_telemetrum.c index d2e05eb5..0a4b3a49 100644 --- a/ao_telemetrum.c +++ b/ao_telemetrum.c @@ -41,6 +41,7 @@ main(void) ao_gps_init(); ao_telemetry_init(); ao_radio_init(); + ao_igniter_init(); ao_dbg_init(); ao_start_scheduler(); }