diff options
| -rw-r--r-- | src/core/ao_data.h | 191 | ||||
| -rw-r--r-- | src/core/ao_flight_mm.c | 362 | ||||
| -rw-r--r-- | src/core/ao_log_mega.c | 174 | ||||
| -rw-r--r-- | src/core/ao_sample_mm.c | 135 | 
4 files changed, 862 insertions, 0 deletions
diff --git a/src/core/ao_data.h b/src/core/ao_data.h new file mode 100644 index 00000000..d28730a8 --- /dev/null +++ b/src/core/ao_data.h @@ -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 index 00000000..27087e55 --- /dev/null +++ b/src/core/ao_flight_mm.c @@ -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 index 00000000..1763b9eb --- /dev/null +++ b/src/core/ao_log_mega.c @@ -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 index 00000000..8cfadc40 --- /dev/null +++ b/src/core/ao_sample_mm.c @@ -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; +}  | 
