summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/ao.h26
-rw-r--r--src/core/ao_adc.h4
-rw-r--r--src/core/ao_cmd.c7
-rw-r--r--src/core/ao_config.c99
-rw-r--r--src/core/ao_config.h53
-rw-r--r--src/core/ao_data.c36
-rw-r--r--src/core/ao_data.h28
-rw-r--r--src/core/ao_debounce.c163
-rw-r--r--src/core/ao_debounce.h74
-rw-r--r--src/core/ao_eeprom.h43
-rw-r--r--src/core/ao_flight.c45
-rw-r--r--src/core/ao_flight.h7
-rw-r--r--src/core/ao_gps_report.c102
-rw-r--r--src/core/ao_gps_report_mega.c95
-rw-r--r--src/core/ao_gps_report_metrum.c97
-rw-r--r--src/core/ao_gps_show.c39
-rw-r--r--src/core/ao_ignite.c45
-rw-r--r--src/core/ao_int64.c158
-rw-r--r--src/core/ao_int64.h48
-rw-r--r--src/core/ao_kalman.c7
-rw-r--r--src/core/ao_log.c16
-rw-r--r--src/core/ao_log.h118
-rw-r--r--src/core/ao_log_mega.c3
-rw-r--r--src/core/ao_log_metrum.c175
-rw-r--r--src/core/ao_log_micro.c121
-rw-r--r--src/core/ao_log_micro.h39
-rw-r--r--src/core/ao_log_mini.c162
-rw-r--r--src/core/ao_log_telem.c2
-rw-r--r--src/core/ao_log_tiny.c2
-rw-r--r--src/core/ao_microflight.c143
-rw-r--r--src/core/ao_microkalman.c74
-rw-r--r--src/core/ao_product.c8
-rw-r--r--src/core/ao_pyro.c233
-rw-r--r--src/core/ao_pyro.h8
-rw-r--r--src/core/ao_quaternion.h249
-rw-r--r--src/core/ao_report_micro.c57
-rw-r--r--src/core/ao_sample.c104
-rw-r--r--src/core/ao_sample.h19
-rw-r--r--src/core/ao_sample_profile.c4
-rw-r--r--src/core/ao_storage.c2
-rw-r--r--src/core/ao_storage.h12
-rw-r--r--src/core/ao_task.c5
-rw-r--r--src/core/ao_task.h5
-rw-r--r--src/core/ao_telemetry.c140
-rw-r--r--src/core/ao_telemetry.h71
-rw-r--r--src/core/ao_usb.h3
46 files changed, 2666 insertions, 285 deletions
diff --git a/src/core/ao.h b/src/core/ao.h
index 0ad3e4aa..0b634a79 100644
--- a/src/core/ao.h
+++ b/src/core/ao.h
@@ -43,6 +43,12 @@
#define HAS_TASK 1
#endif
+#ifndef AO_PORT_TYPE
+#define AO_PORT_TYPE uint8_t
+#endif
+
+typedef AO_PORT_TYPE ao_port_t;
+
#if HAS_TASK
#include <ao_task.h>
#else
@@ -68,6 +74,8 @@
#define AO_PANIC_SPI 13 /* SPI communication failure */
#define AO_PANIC_CRASH 14 /* Processor crashed */
#define AO_PANIC_BUFIO 15 /* Mis-using bufio API */
+#define AO_PANIC_EXTI 16 /* Mis-using exti API */
+#define AO_PANIC_FAST_TIMER 17 /* Mis-using fast timer API */
#define AO_PANIC_SELF_TEST_CC1120 0x40 | 1 /* Self test failure */
#define AO_PANIC_SELF_TEST_HMC5883 0x40 | 2 /* Self test failure */
#define AO_PANIC_SELF_TEST_MPU6000 0x40 | 3 /* Self test failure */
@@ -174,7 +182,7 @@ void
ao_cmd_hex(void);
void
-ao_cmd_decimal(void);
+ao_cmd_decimal(void) __reentrant;
/* Read a single hex nibble off stdin. */
uint8_t
@@ -334,6 +342,10 @@ ao_spi_slave(void);
#define AO_GPS_DATE_VALID (1 << 6)
#define AO_GPS_COURSE_VALID (1 << 7)
+#define AO_GPS_NEW_DATA 1
+#define AO_GPS_NEW_TRACKING 2
+
+extern __xdata uint8_t ao_gps_new;
extern __pdata uint16_t ao_gps_tick;
extern __xdata uint8_t ao_gps_mutex;
extern __xdata struct ao_telemetry_location ao_gps_data;
@@ -380,6 +392,9 @@ void
ao_gps_tracking_print(__xdata struct ao_gps_tracking_orig *gps_tracking_data);
void
+ao_gps_show(void) __reentrant;
+
+void
ao_gps_init(void);
/*
@@ -693,6 +708,8 @@ struct ao_ignition {
uint8_t firing;
};
+extern __code char * __code ao_igniter_status_names[];
+
extern __xdata struct ao_ignition ao_ignition[2];
enum ao_igniter_status
@@ -722,7 +739,7 @@ extern __xdata uint8_t ao_force_freq;
#endif
#define AO_CONFIG_MAJOR 1
-#define AO_CONFIG_MINOR 14
+#define AO_CONFIG_MINOR 15
#define AO_AES_LEN 16
@@ -756,6 +773,11 @@ struct ao_config {
#if HAS_RADIO_AMP
uint8_t radio_amp; /* minor version 14 */
#endif
+#if HAS_GYRO
+ int16_t accel_zero_along; /* minor version 15 */
+ int16_t accel_zero_across; /* minor version 15 */
+ int16_t accel_zero_through; /* minor version 15 */
+#endif
};
#define AO_IGNITE_MODE_DUAL 0
diff --git a/src/core/ao_adc.h b/src/core/ao_adc.h
index 0dd87080..373db1c4 100644
--- a/src/core/ao_adc.h
+++ b/src/core/ao_adc.h
@@ -28,10 +28,6 @@ ao_adc_poll(void);
void
ao_adc_sleep(void);
-/* Get a copy of the last complete sample set */
-void
-ao_data_get(__xdata struct ao_data *packet);
-
/* Initialize the A/D converter */
void
ao_adc_init(void);
diff --git a/src/core/ao_cmd.c b/src/core/ao_cmd.c
index 188b8bb4..4ebaa607 100644
--- a/src/core/ao_cmd.c
+++ b/src/core/ao_cmd.c
@@ -206,9 +206,9 @@ ao_cmd_hex(void)
}
void
-ao_cmd_decimal(void)
+ao_cmd_decimal(void) __reentrant
{
- __pdata uint8_t r = ao_cmd_lex_error;
+ uint8_t r = ao_cmd_lex_error;
ao_cmd_lex_u32 = 0;
ao_cmd_white();
@@ -290,9 +290,6 @@ version(void)
, ao_log_format
#endif
);
-#if HAS_MS5607
- ao_ms5607_info();
-#endif
printf("software-version %s\n", ao_version);
}
#endif
diff --git a/src/core/ao_config.c b/src/core/ao_config.c
index 73608a55..a30ec64a 100644
--- a/src/core/ao_config.c
+++ b/src/core/ao_config.c
@@ -17,7 +17,7 @@
#include "ao.h"
#include "ao_log.h"
-#include <ao_storage.h>
+#include <ao_config.h>
#if HAS_FLIGHT
#include <ao_sample.h>
#include <ao_data.h>
@@ -28,6 +28,9 @@ __pdata uint8_t ao_config_loaded;
__pdata uint8_t ao_config_dirty;
__xdata uint8_t ao_config_mutex;
+#ifndef AO_CONFIG_DEFAULT_APRS_INTERVAL
+#define AO_CONFIG_DEFAULT_APRS_INTERVAL 0
+#endif
#define AO_CONFIG_DEFAULT_MAIN_DEPLOY 250
#define AO_CONFIG_DEFAULT_RADIO_CHANNEL 0
#define AO_CONFIG_DEFAULT_CALLSIGN "N0CALL"
@@ -47,20 +50,22 @@ __xdata uint8_t ao_config_mutex;
#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX ((uint32_t) 192 * (uint32_t) 1024)
#endif
#endif
+#ifndef AO_CONFIG_DEFAULT_RADIO_POWER
#define AO_CONFIG_DEFAULT_RADIO_POWER 0x60
+#endif
#define AO_CONFIG_DEFAULT_RADIO_AMP 0
#if HAS_EEPROM
static void
_ao_config_put(void)
{
- ao_storage_setup();
- ao_storage_erase(ao_storage_config);
- ao_storage_write(ao_storage_config, &ao_config, sizeof (ao_config));
+ ao_config_setup();
+ ao_config_erase();
+ ao_config_write(0, &ao_config, sizeof (ao_config));
#if HAS_FLIGHT
ao_log_write_erase(0);
#endif
- ao_storage_flush();
+ ao_config_flush();
}
void
@@ -92,8 +97,8 @@ _ao_config_get(void)
* but ao_storage_setup *also* sets ao_storage_config, which we
* need before calling ao_storage_read here
*/
- ao_storage_setup();
- ao_storage_read(ao_storage_config, &ao_config, sizeof (ao_config));
+ ao_config_setup();
+ ao_config_read(0, &ao_config, sizeof (ao_config));
#endif
if (ao_config.major != AO_CONFIG_MAJOR) {
ao_config.major = AO_CONFIG_MAJOR;
@@ -122,8 +127,10 @@ _ao_config_get(void)
ao_config.radio_cal = ao_radio_cal;
#endif
/* Fixups for minor version 4 */
+#if HAS_FLIGHT
if (minor < 4)
ao_config.flight_log_max = AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX;
+#endif
/* Fixupes for minor version 5 */
if (minor < 5)
ao_config.ignite_mode = AO_CONFIG_DEFAULT_IGNITE_MODE;
@@ -142,7 +149,7 @@ _ao_config_get(void)
memset(&ao_config.pyro, '\0', sizeof (ao_config.pyro));
#endif
if (minor < 13)
- ao_config.aprs_interval = 0;
+ ao_config.aprs_interval = AO_CONFIG_DEFAULT_APRS_INTERVAL;
#if HAS_RADIO_POWER
if (minor < 14)
ao_config.radio_power = AO_CONFIG_DEFAULT_RADIO_POWER;
@@ -151,6 +158,19 @@ _ao_config_get(void)
if (minor < 14)
ao_config.radio_amp = AO_CONFIG_DEFAULT_RADIO_AMP;
#endif
+#if HAS_GYRO
+ if (minor < 15) {
+ ao_config.accel_zero_along = 0;
+ ao_config.accel_zero_across = 0;
+ ao_config.accel_zero_through = 0;
+
+ /* Reset the main accel offsets to force
+ * re-calibration
+ */
+ ao_config.accel_plus_g = 0;
+ ao_config.accel_minus_g = 0;
+ }
+#endif
ao_config.minor = AO_CONFIG_MINOR;
ao_config_dirty = 1;
}
@@ -270,17 +290,34 @@ ao_config_accel_calibrate_show(void) __reentrant
{
printf("Accel cal +1g: %d -1g: %d\n",
ao_config.accel_plus_g, ao_config.accel_minus_g);
+#if HAS_GYRO
+ printf ("IMU cal along %d across %d through %d\n",
+ ao_config.accel_zero_along,
+ ao_config.accel_zero_across,
+ ao_config.accel_zero_through);
+#endif
}
#define ACCEL_CALIBRATE_SAMPLES 1024
#define ACCEL_CALIBRATE_SHIFT 10
+#if HAS_GYRO
+static int16_t accel_cal_along;
+static int16_t accel_cal_across;
+static int16_t accel_cal_through;
+#endif
+
static int16_t
ao_config_accel_calibrate_auto(char *orientation) __reentrant
{
uint16_t i;
int32_t accel_total;
uint8_t cal_data_ring;
+#if HAS_GYRO
+ int32_t accel_along_total = 0;
+ int32_t accel_across_total = 0;
+ int32_t accel_through_total = 0;
+#endif
printf("Orient antenna %s and press a key...", orientation);
flush();
@@ -294,10 +331,20 @@ ao_config_accel_calibrate_auto(char *orientation) __reentrant
ao_sleep(DATA_TO_XDATA(&ao_sample_data));
while (i && cal_data_ring != ao_sample_data) {
accel_total += (int32_t) ao_data_accel(&ao_data_ring[cal_data_ring]);
+#if HAS_GYRO
+ accel_along_total += (int32_t) ao_data_along(&ao_data_ring[cal_data_ring]);
+ accel_across_total += (int32_t) ao_data_across(&ao_data_ring[cal_data_ring]);
+ accel_through_total += (int32_t) ao_data_through(&ao_data_ring[cal_data_ring]);
+#endif
cal_data_ring = ao_data_ring_next(cal_data_ring);
i--;
}
}
+#if HAS_GYRO
+ accel_cal_along = accel_along_total >> ACCEL_CALIBRATE_SHIFT;
+ accel_cal_across = accel_across_total >> ACCEL_CALIBRATE_SHIFT;
+ accel_cal_through = accel_through_total >> ACCEL_CALIBRATE_SHIFT;
+#endif
return accel_total >> ACCEL_CALIBRATE_SHIFT;
}
@@ -305,12 +352,28 @@ void
ao_config_accel_calibrate_set(void) __reentrant
{
int16_t up, down;
+#if HAS_GYRO
+ int16_t accel_along_up, accel_along_down;
+ int16_t accel_across_up, accel_across_down;
+ int16_t accel_through_up, accel_through_down;
+#endif
+
ao_cmd_decimal();
if (ao_cmd_status != ao_cmd_success)
return;
if (ao_cmd_lex_i == 0) {
up = ao_config_accel_calibrate_auto("up");
+#if HAS_GYRO
+ accel_along_up = accel_cal_along;
+ accel_across_up = accel_cal_across;
+ accel_through_up = accel_cal_through;
+#endif
down = ao_config_accel_calibrate_auto("down");
+#if HAS_GYRO
+ accel_along_down = accel_cal_along;
+ accel_across_down = accel_cal_across;
+ accel_through_down = accel_cal_through;
+#endif
} else {
up = ao_cmd_lex_i;
ao_cmd_decimal();
@@ -326,6 +389,11 @@ ao_config_accel_calibrate_set(void) __reentrant
_ao_config_edit_start();
ao_config.accel_plus_g = up;
ao_config.accel_minus_g = down;
+#if HAS_GYRO
+ ao_config.accel_zero_along = (accel_along_up + accel_along_down) / 2;
+ ao_config.accel_zero_across = (accel_across_up + accel_across_down) / 2;
+ ao_config.accel_zero_through = (accel_through_up + accel_through_down) / 2;
+#endif
_ao_config_edit_finish();
}
#endif /* HAS_ACCEL */
@@ -399,7 +467,7 @@ void
ao_config_log_set(void) __reentrant
{
uint16_t block = (uint16_t) (ao_storage_block >> 10);
- uint16_t config = (uint16_t) (ao_storage_config >> 10);
+ uint16_t log_max = (uint16_t) (ao_storage_log_max >> 10);
ao_cmd_decimal();
if (ao_cmd_status != ao_cmd_success)
@@ -408,8 +476,8 @@ ao_config_log_set(void) __reentrant
printf("Storage must be empty before changing log size\n");
else if (block > 1024 && (ao_cmd_lex_i & (block - 1)))
printf("Flight log size must be multiple of %d kB\n", block);
- else if (ao_cmd_lex_i > config)
- printf("Flight log max %d kB\n", config);
+ else if (ao_cmd_lex_i > log_max)
+ printf("Flight log max %d kB\n", log_max);
else {
_ao_config_edit_start();
ao_config.flight_log_max = (uint32_t) ao_cmd_lex_i << 10;
@@ -589,7 +657,7 @@ static void
ao_config_show(void) __reentrant;
static void
-ao_config_write(void) __reentrant;
+ao_config_save(void) __reentrant;
__code struct ao_config_var ao_config_vars[] = {
#if HAS_FLIGHT
@@ -648,7 +716,7 @@ __code struct ao_config_var ao_config_vars[] = {
ao_config_show, 0 },
#if HAS_EEPROM
{ "w\0Write to eeprom",
- ao_config_write, 0 },
+ ao_config_save, 0 },
#endif
{ "?\0Help",
ao_config_help, 0 },
@@ -693,11 +761,14 @@ ao_config_show(void) __reentrant
for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
if (ao_config_vars[cmd].show)
(*ao_config_vars[cmd].show)();
+#if HAS_MS5607
+ ao_ms5607_info();
+#endif
}
#if HAS_EEPROM
static void
-ao_config_write(void) __reentrant
+ao_config_save(void) __reentrant
{
uint8_t saved = 0;
ao_mutex_get(&ao_config_mutex);
diff --git a/src/core/ao_config.h b/src/core/ao_config.h
new file mode 100644
index 00000000..e101af8e
--- /dev/null
+++ b/src/core/ao_config.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2013 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_CONFIG_H_
+#define _AO_CONFIG_H_
+
+#ifndef USE_STORAGE_CONFIG
+#define USE_STORAGE_CONFIG 1
+#endif
+
+#ifndef USE_EEPROM_CONFIG
+#define USE_EEPROM_CONFIG 0
+#endif
+
+#if USE_STORAGE_CONFIG
+
+#include <ao_storage.h>
+
+#define ao_config_setup() ao_storage_setup()
+#define ao_config_erase() ao_storage_erase(ao_storage_config)
+#define ao_config_write(pos,bytes, len) ao_storage_write(ao_storage_config+(pos), bytes, len)
+#define ao_config_read(pos,bytes, len) ao_storage_read(ao_storage_config+(pos), bytes, len)
+#define ao_config_flush() ao_storage_flush()
+
+#endif
+
+#if USE_EEPROM_CONFIG
+
+#include <ao_eeprom.h>
+
+#define ao_config_setup()
+#define ao_config_erase()
+#define ao_config_write(pos,bytes, len) ao_eeprom_write(pos, bytes, len)
+#define ao_config_read(pos,bytes, len) ao_eeprom_read(pos, bytes, len)
+#define ao_config_flush()
+
+#endif
+
+#endif /* _AO_CONFIG_H_ */
diff --git a/src/core/ao_data.c b/src/core/ao_data.c
new file mode 100644
index 00000000..6a3d02a1
--- /dev/null
+++ b/src/core/ao_data.c
@@ -0,0 +1,36 @@
+/*
+ * 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_data.h>
+
+volatile __xdata struct ao_data ao_data_ring[AO_DATA_RING];
+volatile __data uint8_t ao_data_head;
+volatile __data uint8_t ao_data_present;
+
+#ifndef ao_data_count
+void
+ao_data_get(__xdata struct ao_data *packet)
+{
+#if HAS_FLIGHT
+ uint8_t i = ao_data_ring_prev(ao_sample_data);
+#else
+ uint8_t i = ao_data_ring_prev(ao_data_head);
+#endif
+ memcpy(packet, (void *) &ao_data_ring[i], sizeof (struct ao_data));
+}
+#endif
diff --git a/src/core/ao_data.h b/src/core/ao_data.h
index 7e2f85d8..e1d8a139 100644
--- a/src/core/ao_data.h
+++ b/src/core/ao_data.h
@@ -18,6 +18,8 @@
#ifndef _AO_DATA_H_
#define _AO_DATA_H_
+#define GRAVITY 9.80665
+
#if HAS_ADC
#define AO_DATA_ADC (1 << 0)
#else
@@ -82,6 +84,10 @@ struct ao_data {
#define ao_data_ring_next(n) (((n) + 1) & (AO_DATA_RING - 1))
#define ao_data_ring_prev(n) (((n) - 1) & (AO_DATA_RING - 1))
+/* Get a copy of the last complete sample set */
+void
+ao_data_get(__xdata struct ao_data *packet);
+
extern volatile __xdata struct ao_data ao_data_ring[AO_DATA_RING];
extern volatile __data uint8_t ao_data_head;
extern volatile __data uint8_t ao_data_present;
@@ -97,7 +103,7 @@ extern volatile __data uint8_t ao_data_count;
* signaled by the timer tick
*/
#define AO_DATA_WAIT() do { \
- ao_sleep((void *) &ao_data_count); \
+ ao_sleep(DATA_TO_XDATA ((void *) &ao_data_count)); \
} while (0)
#endif /* AO_DATA_RING */
@@ -268,7 +274,11 @@ typedef int16_t accel_t;
/* MMA655X is hooked up so that positive values represent negative acceleration */
#define ao_data_accel(packet) ((packet)->mma655x)
+#if AO_MMA655X_INVERT
+#define ao_data_accel_cook(packet) (4095 - (packet)->mma655x)
+#else
#define ao_data_accel_cook(packet) ((packet)->mma655x)
+#endif
#define ao_data_set_accel(packet, accel) ((packet)->mma655x = (accel))
#define ao_data_accel_invert(accel) (4095 - (accel))
@@ -292,8 +302,8 @@ typedef int16_t accel_t;
#define HAS_GYRO 1
-typedef int16_t gyro_t;
-typedef int32_t angle_t;
+typedef int16_t gyro_t; /* in raw sample units */
+typedef int16_t angle_t; /* in degrees */
/* Y axis is aligned with the direction of motion (along) */
/* X axis is aligned in the other board axis (across) */
@@ -309,4 +319,16 @@ typedef int32_t angle_t;
#endif
+#if !HAS_MAG && HAS_HMC5883
+
+#define HAS_MAG 1
+
+typedef int16_t ao_mag_t; /* in raw sample units */
+
+#define ao_data_mag_along(packet) ((packet)->hmc5883.x)
+#define ao_data_mag_across(packet) ((packet)->hmc5883.y)
+#define ao_data_mag_through(packet) ((packet)->hmc5883.z)
+
+#endif
+
#endif /* _AO_DATA_H_ */
diff --git a/src/core/ao_debounce.c b/src/core/ao_debounce.c
new file mode 100644
index 00000000..b9d67729
--- /dev/null
+++ b/src/core/ao_debounce.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright © 2013 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_debounce.h>
+#include <ao_fast_timer.h>
+
+static uint8_t ao_debounce_initialized;
+static uint8_t ao_debounce_running;
+static struct ao_debounce *ao_debounce;
+
+static uint8_t values[64];
+static uint8_t n;
+
+#define d_step(n) (((n) + 1) & 63)
+
+static void
+_ao_debounce_set(struct ao_debounce *debounce, uint8_t value)
+{
+ if (value != debounce->value) {
+ values[n] = value;
+ n = (n + 1) & 63;
+ debounce->value = value;
+ debounce->_set(debounce, value);
+ }
+ _ao_debounce_stop(debounce);
+}
+
+void
+ao_debounce_dump(void)
+{
+ uint8_t s;
+
+ for (s = 0; s < n; s++) {
+ printf ("%d: %d\n",
+ s, values[s]);
+ }
+ n = 0;
+}
+
+/*
+ * Get the current value, set the result when we've
+ * reached the debounce count limit
+ */
+static void
+_ao_debounce_check(struct ao_debounce *debounce)
+{
+ uint8_t next = debounce->_get(debounce);
+
+ if (next == debounce->current) {
+ if (debounce->count < debounce->hold) {
+ if (++debounce->count == debounce->hold)
+ _ao_debounce_set(debounce, debounce->current);
+ }
+ } else {
+ debounce->count = 0;
+ debounce->current = next;
+ }
+}
+
+static void
+_ao_debounce_isr(void)
+{
+ struct ao_debounce *debounce, *next;
+
+ for (debounce = ao_debounce; debounce; debounce = next) {
+ next = debounce->next;
+ _ao_debounce_check(debounce);
+ }
+}
+
+static void
+ao_debounce_on(void)
+{
+ ao_fast_timer_on(_ao_debounce_isr);
+}
+
+static void
+ao_debounce_off(void)
+{
+ ao_fast_timer_off(_ao_debounce_isr);
+}
+
+/*
+ * Start monitoring one pin
+ */
+void
+_ao_debounce_start(struct ao_debounce *debounce)
+{
+ uint32_t m;
+
+ m = ao_arch_irqsave();
+ if (!debounce->running) {
+ debounce->running = 1;
+
+ /* Reset the counter */
+ debounce->count = 0;
+
+ /* Link into list */
+ debounce->next = ao_debounce;
+ ao_debounce = debounce;
+
+ /* Make sure the timer is running */
+ if (!ao_debounce_running++)
+ ao_debounce_on();
+
+ /* And go check the current value */
+ _ao_debounce_check(debounce);
+ }
+ ao_arch_irqrestore(m);
+}
+
+/*
+ * Stop monitoring one pin
+ */
+void
+_ao_debounce_stop(struct ao_debounce *debounce)
+{
+ struct ao_debounce **prev;
+ uint32_t m;
+
+ m = ao_arch_irqsave();
+ if (debounce->running) {
+ debounce->running = 0;
+
+ /* Unlink */
+ for (prev = &ao_debounce; (*prev); prev = &((*prev)->next)) {
+ if (*prev == debounce) {
+ *prev = debounce->next;
+ break;
+ }
+ }
+ debounce->next = NULL;
+
+ /* Turn off the timer if possible */
+ if (!--ao_debounce_running)
+ ao_debounce_off();
+ }
+ ao_arch_irqrestore(m);
+}
+
+void
+ao_debounce_init(void)
+{
+ if (ao_debounce_initialized)
+ return;
+ ao_debounce_initialized = 1;
+ ao_fast_timer_init();
+}
diff --git a/src/core/ao_debounce.h b/src/core/ao_debounce.h
new file mode 100644
index 00000000..19c620f5
--- /dev/null
+++ b/src/core/ao_debounce.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2013 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_DEBOUNCE_H_
+#define _AO_DEBOUNCE_H_
+
+struct ao_debounce {
+ struct ao_debounce *next;
+
+ /* time that pin value must be stable before accepting */
+ uint8_t hold;
+
+ /* last value reported to app; don't report it twice */
+ uint8_t value;
+
+ /* current value received from pins */
+ uint8_t current;
+
+ /* current count of intervals pin value has been stable */
+ uint8_t count;
+
+ /* This pin is running */
+ uint8_t running;
+
+ /* Get the current pin value */
+ uint8_t (*_get)(struct ao_debounce *debounce);
+
+ /* The stable value has changed */
+ void (*_set)(struct ao_debounce *debounce, uint8_t value);
+};
+
+static inline void
+ao_debounce_config(struct ao_debounce *debounce,
+ uint8_t (*_get)(struct ao_debounce *debounce),
+ void (*_set)(struct ao_debounce *debounce, uint8_t value),
+ uint8_t hold)
+{
+ debounce->next = 0;
+ debounce->hold = hold;
+ debounce->value = 0xff;
+ debounce->current = 0xff;
+ debounce->count = 0;
+ debounce->running = 0;
+ debounce->_get = _get;
+ debounce->_set = _set;
+}
+
+void
+_ao_debounce_start(struct ao_debounce *debounce);
+
+void
+_ao_debounce_stop(struct ao_debounce *debounce);
+
+void
+ao_debounce_init(void);
+
+void
+ao_debounce_dump(void);
+
+#endif /* _AO_DEBOUNCE_H_ */
diff --git a/src/core/ao_eeprom.h b/src/core/ao_eeprom.h
new file mode 100644
index 00000000..915522bf
--- /dev/null
+++ b/src/core/ao_eeprom.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2013 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_EEPROM_H_
+#define _AO_EEPROM_H_
+
+extern const ao_pos_t ao_eeprom_total;
+
+/*
+ * Write to eeprom
+ */
+
+uint8_t
+ao_eeprom_write(ao_pos_t pos32, __xdata void *v, uint16_t len);
+
+/*
+ * Read from eeprom
+ */
+uint8_t
+ao_eeprom_read(ao_pos_t pos, __xdata void *v, uint16_t len);
+
+/*
+ * Initialize eeprom
+ */
+
+void
+ao_eeprom_init(void);
+
+#endif /* _AO_EEPROM_H_ */
diff --git a/src/core/ao_flight.c b/src/core/ao_flight.c
index 9d9d4c6e..463ff4a2 100644
--- a/src/core/ao_flight.c
+++ b/src/core/ao_flight.c
@@ -20,6 +20,10 @@
#include <ao_log.h>
#endif
+#if HAS_MPU6000
+#include <ao_quaternion.h>
+#endif
+
#ifndef HAS_ACCEL
#error Please define HAS_ACCEL
#endif
@@ -42,6 +46,11 @@ __pdata enum ao_flight_state ao_flight_state; /* current flight state */
__pdata uint16_t ao_boost_tick; /* time of launch detect */
__pdata uint16_t ao_motor_number; /* number of motors burned so far */
+#if HAS_IMU
+/* Any sensor can set this to mark the flight computer as 'broken' */
+__xdata uint8_t ao_sensor_errors;
+#endif
+
/*
* track min/max data over a long interval to detect
* resting
@@ -95,6 +104,9 @@ ao_flight(void)
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 ||
+#if HAS_IMU
+ ao_sensor_errors ||
+#endif
ao_ground_height < -1000 ||
ao_ground_height > 7000)
{
@@ -117,14 +129,14 @@ ao_flight(void)
{
/* Set pad mode - we can fly! */
ao_flight_state = ao_flight_pad;
-#if HAS_USB && HAS_RADIO && !HAS_FLIGHT_DEBUG && !HAS_SAMPLE_PROFILE
+#if HAS_USB && !HAS_FLIGHT_DEBUG && !HAS_SAMPLE_PROFILE
/* Disable the USB controller in flight mode
* to save power
*/
ao_usb_disable();
#endif
-#if !HAS_ACCEL
+#if !HAS_ACCEL && PACKET_HAS_SLAVE
/* Disable packet mode in pad state on TeleMini */
ao_packet_slave_stop();
#endif
@@ -134,8 +146,10 @@ ao_flight(void)
ao_rdf_set(1);
ao_telemetry_set_interval(AO_TELEMETRY_INTERVAL_PAD);
#endif
+#if HAS_LED
/* signal successful initialization by turning off the LED */
ao_led_off(AO_LED_RED);
+#endif
} else {
/* Set idle mode */
ao_flight_state = ao_flight_idle;
@@ -145,8 +159,10 @@ ao_flight(void)
ao_packet_slave_start();
#endif
+#if HAS_LED
/* signal successful initialization by turning off the LED */
ao_led_off(AO_LED_RED);
+#endif
}
/* wakeup threads due to state change */
ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
@@ -190,8 +206,8 @@ ao_flight(void)
#if HAS_GPS
/* Record current GPS position by waking up GPS log tasks */
- ao_wakeup(&ao_gps_data);
- ao_wakeup(&ao_gps_tracking_data);
+ ao_gps_new = AO_GPS_NEW_DATA | AO_GPS_NEW_TRACKING;
+ ao_wakeup(&ao_gps_new);
#endif
ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
@@ -356,6 +372,18 @@ ao_flight(void)
ao_interval_end = ao_sample_tick + AO_INTERVAL_TICKS;
}
break;
+#if HAS_FLIGHT_DEBUG
+ case ao_flight_test:
+#if HAS_GYRO
+ printf ("angle %4d pitch %7d yaw %7d roll %7d\n",
+ ao_sample_orient,
+ ((ao_sample_pitch << 9) - ao_ground_pitch) >> 9,
+ ((ao_sample_yaw << 9) - ao_ground_yaw) >> 9,
+ ((ao_sample_roll << 9) - ao_ground_roll) >> 9);
+#endif
+ flush();
+ break;
+#endif /* HAS_FLIGHT_DEBUG */
default:
break;
}
@@ -406,8 +434,17 @@ ao_flight_dump(void)
printf (" error_avg %d\n", ao_error_h_sq_avg);
}
+static void
+ao_gyro_test(void)
+{
+ ao_flight_state = ao_flight_test;
+ ao_getchar();
+ ao_flight_state = ao_flight_idle;
+}
+
__code struct ao_cmds ao_flight_cmds[] = {
{ ao_flight_dump, "F\0Dump flight status" },
+ { ao_gyro_test, "G\0Test gyro code" },
{ 0, NULL },
};
#endif
diff --git a/src/core/ao_flight.h b/src/core/ao_flight.h
index b80202f0..c7c02ccf 100644
--- a/src/core/ao_flight.h
+++ b/src/core/ao_flight.h
@@ -33,13 +33,18 @@ enum ao_flight_state {
ao_flight_drogue = 6,
ao_flight_main = 7,
ao_flight_landed = 8,
- ao_flight_invalid = 9
+ ao_flight_invalid = 9,
+ ao_flight_test = 10
};
extern __pdata enum ao_flight_state ao_flight_state;
extern __pdata uint16_t ao_boost_tick;
extern __pdata uint16_t ao_motor_number;
+#if HAS_IMU
+extern __xdata uint8_t ao_sensor_errors;
+#endif
+
extern __pdata uint16_t ao_launch_time;
extern __pdata uint8_t ao_flight_force_idle;
diff --git a/src/core/ao_gps_report.c b/src/core/ao_gps_report.c
index c52ef621..07201ac2 100644
--- a/src/core/ao_gps_report.c
+++ b/src/core/ao_gps_report.c
@@ -22,78 +22,68 @@ ao_gps_report(void)
{
static __xdata struct ao_log_record gps_log;
static __xdata struct ao_telemetry_location gps_data;
+ static __xdata struct ao_telemetry_satellite gps_tracking_data;
uint8_t date_reported = 0;
+ uint8_t new;
for (;;) {
- ao_sleep(&ao_gps_data);
+ while ((new = ao_gps_new) == 0)
+ ao_sleep(&ao_gps_new);
ao_mutex_get(&ao_gps_mutex);
- ao_xmemcpy(&gps_data, &ao_gps_data, sizeof (ao_gps_data));
+ if (new & AO_GPS_NEW_DATA)
+ ao_xmemcpy(&gps_data, &ao_gps_data, sizeof (ao_gps_data));
+ if (new & AO_GPS_NEW_TRACKING)
+ ao_xmemcpy(&gps_tracking_data, &ao_gps_tracking_data, sizeof (ao_gps_tracking_data));
+ ao_gps_new = 0;
ao_mutex_put(&ao_gps_mutex);
- if (!(gps_data.flags & AO_GPS_VALID))
- continue;
-
- gps_log.tick = ao_gps_tick;
- gps_log.type = AO_LOG_GPS_TIME;
- gps_log.u.gps_time.hour = gps_data.hour;
- gps_log.u.gps_time.minute = gps_data.minute;
- gps_log.u.gps_time.second = gps_data.second;
- gps_log.u.gps_time.flags = gps_data.flags;
- ao_log_data(&gps_log);
- gps_log.type = AO_LOG_GPS_LAT;
- gps_log.u.gps_latitude = gps_data.latitude;
- ao_log_data(&gps_log);
- gps_log.type = AO_LOG_GPS_LON;
- gps_log.u.gps_longitude = gps_data.longitude;
- ao_log_data(&gps_log);
- gps_log.type = AO_LOG_GPS_ALT;
- gps_log.u.gps_altitude.altitude = gps_data.altitude;
- gps_log.u.gps_altitude.unused = 0xffff;
- ao_log_data(&gps_log);
- if (!date_reported && (gps_data.flags & AO_GPS_DATE_VALID)) {
- gps_log.type = AO_LOG_GPS_DATE;
- gps_log.u.gps_date.year = gps_data.year;
- gps_log.u.gps_date.month = gps_data.month;
- gps_log.u.gps_date.day = gps_data.day;
- gps_log.u.gps_date.extra = 0;
- date_reported = ao_log_data(&gps_log);
+ if ((new & AO_GPS_NEW_DATA) && (gps_data.flags & AO_GPS_VALID)) {
+ gps_log.tick = ao_gps_tick;
+ gps_log.type = AO_LOG_GPS_TIME;
+ gps_log.u.gps_time.hour = gps_data.hour;
+ gps_log.u.gps_time.minute = gps_data.minute;
+ gps_log.u.gps_time.second = gps_data.second;
+ gps_log.u.gps_time.flags = gps_data.flags;
+ ao_log_data(&gps_log);
+ gps_log.type = AO_LOG_GPS_LAT;
+ gps_log.u.gps_latitude = gps_data.latitude;
+ ao_log_data(&gps_log);
+ gps_log.type = AO_LOG_GPS_LON;
+ gps_log.u.gps_longitude = gps_data.longitude;
+ ao_log_data(&gps_log);
+ gps_log.type = AO_LOG_GPS_ALT;
+ gps_log.u.gps_altitude.altitude = gps_data.altitude;
+ gps_log.u.gps_altitude.unused = 0xffff;
+ ao_log_data(&gps_log);
+ if (!date_reported && (gps_data.flags & AO_GPS_DATE_VALID)) {
+ gps_log.type = AO_LOG_GPS_DATE;
+ gps_log.u.gps_date.year = gps_data.year;
+ gps_log.u.gps_date.month = gps_data.month;
+ gps_log.u.gps_date.day = gps_data.day;
+ gps_log.u.gps_date.extra = 0;
+ date_reported = ao_log_data(&gps_log);
+ }
}
- }
-}
-
-void
-ao_gps_tracking_report(void)
-{
- static __xdata struct ao_log_record gps_log;
- static __xdata struct ao_telemetry_satellite gps_tracking_data;
- uint8_t c, n;
-
- for (;;) {
- ao_sleep(&ao_gps_tracking_data);
- ao_mutex_get(&ao_gps_mutex);
- gps_log.tick = ao_gps_tick;
- ao_xmemcpy(&gps_tracking_data, &ao_gps_tracking_data, sizeof (ao_gps_tracking_data));
- ao_mutex_put(&ao_gps_mutex);
+ if (new & AO_GPS_NEW_TRACKING) {
+ uint8_t c, n;
- if (!(n = gps_tracking_data.channels))
- continue;
-
- gps_log.type = AO_LOG_GPS_SAT;
- for (c = 0; c < n; c++)
- if ((gps_log.u.gps_sat.svid = gps_tracking_data.sats[c].svid))
- {
- gps_log.u.gps_sat.c_n = gps_tracking_data.sats[c].c_n_1;
- ao_log_data(&gps_log);
+ if ((n = gps_tracking_data.channels) != 0) {
+ gps_log.type = AO_LOG_GPS_SAT;
+ for (c = 0; c < n; c++)
+ if ((gps_log.u.gps_sat.svid = gps_tracking_data.sats[c].svid))
+ {
+ gps_log.u.gps_sat.c_n = gps_tracking_data.sats[c].c_n_1;
+ ao_log_data(&gps_log);
+ }
}
+ }
}
}
__xdata struct ao_task ao_gps_report_task;
-__xdata struct ao_task ao_gps_tracking_report_task;
void
ao_gps_report_init(void)
{
ao_add_task(&ao_gps_report_task, ao_gps_report, "gps_report");
- ao_add_task(&ao_gps_tracking_report_task, ao_gps_tracking_report, "gps_tracking_report");
}
diff --git a/src/core/ao_gps_report_mega.c b/src/core/ao_gps_report_mega.c
index e3af4307..d13885dd 100644
--- a/src/core/ao_gps_report_mega.c
+++ b/src/core/ao_gps_report_mega.c
@@ -23,66 +23,62 @@ ao_gps_report_mega(void)
{
static __xdata struct ao_log_mega gps_log;
static __xdata struct ao_telemetry_location gps_data;
- uint8_t date_reported = 0;
-
- for (;;) {
- ao_sleep(&ao_gps_data);
- ao_mutex_get(&ao_gps_mutex);
- ao_xmemcpy(&gps_data, &ao_gps_data, sizeof (ao_gps_data));
- ao_mutex_put(&ao_gps_mutex);
-
- if (!(gps_data.flags & AO_GPS_VALID))
- continue;
-
- gps_log.tick = ao_gps_tick;
- gps_log.type = AO_LOG_GPS_TIME;
- gps_log.u.gps.latitude = gps_data.latitude;
- gps_log.u.gps.longitude = gps_data.longitude;
- gps_log.u.gps.altitude = gps_data.altitude;
-
- gps_log.u.gps.hour = gps_data.hour;
- gps_log.u.gps.minute = gps_data.minute;
- gps_log.u.gps.second = gps_data.second;
- gps_log.u.gps.flags = gps_data.flags;
- gps_log.u.gps.year = gps_data.year;
- gps_log.u.gps.month = gps_data.month;
- gps_log.u.gps.day = gps_data.day;
- ao_log_mega(&gps_log);
- }
-}
-
-void
-ao_gps_tracking_report_mega(void)
-{
- static __xdata struct ao_log_mega gps_log;
static __xdata struct ao_telemetry_satellite gps_tracking_data;
+ uint8_t date_reported = 0;
+ uint8_t new;
uint8_t c, n, i;
for (;;) {
- ao_sleep(&ao_gps_tracking_data);
+ while (!(new = ao_gps_new))
+ ao_sleep(&ao_gps_new);
ao_mutex_get(&ao_gps_mutex);
- ao_xmemcpy(&gps_tracking_data, &ao_gps_tracking_data, sizeof (ao_gps_tracking_data));
+ if (new & AO_GPS_NEW_DATA)
+ ao_xmemcpy(&gps_data, &ao_gps_data, sizeof (ao_gps_data));
+ if (new & AO_GPS_NEW_TRACKING)
+ ao_xmemcpy(&gps_tracking_data, &ao_gps_tracking_data, sizeof (ao_gps_tracking_data));
+ ao_gps_new = 0;
ao_mutex_put(&ao_gps_mutex);
- if (!(n = gps_tracking_data.channels))
- continue;
+ if ((new & AO_GPS_NEW_DATA) && (gps_data.flags & AO_GPS_VALID)) {
+ gps_log.tick = ao_gps_tick;
+ gps_log.type = AO_LOG_GPS_TIME;
+ gps_log.u.gps.latitude = gps_data.latitude;
+ gps_log.u.gps.longitude = gps_data.longitude;
+ gps_log.u.gps.altitude = gps_data.altitude;
- gps_log.type = AO_LOG_GPS_SAT;
- gps_log.tick = ao_gps_tick;
- i = 0;
- for (c = 0; c < n; c++)
- if ((gps_log.u.gps_sat.sats[i].svid = gps_tracking_data.sats[c].svid))
- {
- gps_log.u.gps_sat.sats[i].c_n = gps_tracking_data.sats[c].c_n_1;
- i++;
- }
- gps_log.u.gps_sat.channels = i;
- ao_log_mega(&gps_log);
+ gps_log.u.gps.hour = gps_data.hour;
+ gps_log.u.gps.minute = gps_data.minute;
+ gps_log.u.gps.second = gps_data.second;
+ gps_log.u.gps.flags = gps_data.flags;
+ gps_log.u.gps.year = gps_data.year;
+ gps_log.u.gps.month = gps_data.month;
+ gps_log.u.gps.day = gps_data.day;
+ gps_log.u.gps.course = gps_data.course;
+ gps_log.u.gps.ground_speed = gps_data.ground_speed;
+ gps_log.u.gps.climb_rate = gps_data.climb_rate;
+ gps_log.u.gps.pdop = gps_data.pdop;
+ gps_log.u.gps.hdop = gps_data.hdop;
+ gps_log.u.gps.vdop = gps_data.vdop;
+ gps_log.u.gps.mode = gps_data.mode;
+ ao_log_mega(&gps_log);
+ }
+ if ((new & AO_GPS_NEW_TRACKING) && (n = gps_tracking_data.channels) != 0) {
+ gps_log.tick = ao_gps_tick;
+ gps_log.type = AO_LOG_GPS_SAT;
+ i = 0;
+ for (c = 0; c < n; c++)
+ if ((gps_log.u.gps_sat.sats[i].svid = gps_tracking_data.sats[c].svid))
+ {
+ gps_log.u.gps_sat.sats[i].c_n = gps_tracking_data.sats[c].c_n_1;
+ i++;
+ }
+ gps_log.u.gps_sat.channels = i;
+ ao_log_mega(&gps_log);
+ }
}
}
__xdata struct ao_task ao_gps_report_mega_task;
-__xdata struct ao_task ao_gps_tracking_report_mega_task;
void
ao_gps_report_mega_init(void)
@@ -90,7 +86,4 @@ ao_gps_report_mega_init(void)
ao_add_task(&ao_gps_report_mega_task,
ao_gps_report_mega,
"gps_report");
- ao_add_task(&ao_gps_tracking_report_mega_task,
- ao_gps_tracking_report_mega,
- "gps_tracking_report");
}
diff --git a/src/core/ao_gps_report_metrum.c b/src/core/ao_gps_report_metrum.c
new file mode 100644
index 00000000..fa038976
--- /dev/null
+++ b/src/core/ao_gps_report_metrum.c
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#include "ao.h"
+#include "ao_log.h"
+
+void
+ao_gps_report_metrum(void)
+{
+ static __xdata struct ao_log_metrum gps_log;
+ static __xdata struct ao_telemetry_location gps_data;
+ static __xdata struct ao_telemetry_satellite gps_tracking_data;
+ uint8_t c, n, i, p, valid, packets;
+ uint8_t svid;
+ uint8_t date_reported = 0;
+ uint8_t new;
+
+ for (;;) {
+ while (!(new = ao_gps_new))
+ ao_sleep(&ao_gps_new);
+ ao_mutex_get(&ao_gps_mutex);
+ if (new & AO_GPS_NEW_DATA)
+ ao_xmemcpy(&gps_data, &ao_gps_data, sizeof (ao_gps_data));
+ if (new & AO_GPS_NEW_TRACKING)
+ ao_xmemcpy(&gps_tracking_data, &ao_gps_tracking_data, sizeof (ao_gps_tracking_data));
+ ao_gps_new = 0;
+ ao_mutex_put(&ao_gps_mutex);
+
+ if ((new & AO_GPS_NEW_DATA) && (gps_data.flags & AO_GPS_VALID)) {
+ gps_log.tick = ao_gps_tick;
+ gps_log.type = AO_LOG_GPS_POS;
+ gps_log.u.gps.latitude = gps_data.latitude;
+ gps_log.u.gps.longitude = gps_data.longitude;
+ gps_log.u.gps.altitude = gps_data.altitude;
+ ao_log_metrum(&gps_log);
+
+ gps_log.type = AO_LOG_GPS_TIME;
+ gps_log.u.gps_time.hour = gps_data.hour;
+ gps_log.u.gps_time.minute = gps_data.minute;
+ gps_log.u.gps_time.second = gps_data.second;
+ gps_log.u.gps_time.flags = gps_data.flags;
+ gps_log.u.gps_time.year = gps_data.year;
+ gps_log.u.gps_time.month = gps_data.month;
+ gps_log.u.gps_time.day = gps_data.day;
+ ao_log_metrum(&gps_log);
+ }
+
+ if ((new & AO_GPS_NEW_TRACKING) && (n = gps_tracking_data.channels)) {
+ gps_log.type = AO_LOG_GPS_SAT;
+ gps_log.tick = ao_gps_tick;
+ i = 0;
+ for (c = 0; c < n; c++) {
+ svid = gps_tracking_data.sats[c].svid;
+ if (svid != 0) {
+ if (i == 4) {
+ gps_log.u.gps_sat.channels = i;
+ gps_log.u.gps_sat.more = 1;
+ ao_log_metrum(&gps_log);
+ i = 0;
+ }
+ gps_log.u.gps_sat.sats[i].svid = svid;
+ gps_log.u.gps_sat.sats[i].c_n = gps_tracking_data.sats[c].c_n_1;
+ i++;
+ }
+ }
+ if (i) {
+ gps_log.u.gps_sat.channels = i;
+ gps_log.u.gps_sat.more = 0;
+ ao_log_metrum(&gps_log);
+ }
+ }
+ }
+}
+
+__xdata struct ao_task ao_gps_report_metrum_task;
+
+void
+ao_gps_report_metrum_init(void)
+{
+ ao_add_task(&ao_gps_report_metrum_task,
+ ao_gps_report_metrum,
+ "gps_report");
+}
diff --git a/src/core/ao_gps_show.c b/src/core/ao_gps_show.c
new file mode 100644
index 00000000..3a05e35a
--- /dev/null
+++ b/src/core/ao_gps_show.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2013 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_GPS_TEST
+#include <ao.h>
+#endif
+
+void
+ao_gps_show(void) __reentrant
+{
+ uint8_t i;
+ ao_mutex_get(&ao_gps_mutex);
+ printf ("Date: %02d/%02d/%02d\n", ao_gps_data.year, ao_gps_data.month, ao_gps_data.day);
+ printf ("Time: %02d:%02d:%02d\n", ao_gps_data.hour, ao_gps_data.minute, ao_gps_data.second);
+ printf ("Lat/Lon: %ld %ld\n", (long) ao_gps_data.latitude, (long) ao_gps_data.longitude);
+ printf ("Alt: %d\n", ao_gps_data.altitude);
+ printf ("Flags: 0x%x\n", ao_gps_data.flags);
+ printf ("Sats: %d", ao_gps_tracking_data.channels);
+ for (i = 0; i < ao_gps_tracking_data.channels; i++)
+ printf (" %d %d",
+ ao_gps_tracking_data.sats[i].svid,
+ ao_gps_tracking_data.sats[i].c_n_1);
+ printf ("\ndone\n");
+ ao_mutex_put(&ao_gps_mutex);
+}
diff --git a/src/core/ao_ignite.c b/src/core/ao_ignite.c
index 74bd0c5a..9f2ec0a7 100644
--- a/src/core/ao_ignite.c
+++ b/src/core/ao_ignite.c
@@ -17,7 +17,11 @@
#include "ao.h"
#include <ao_data.h>
+#if AO_PYRO_NUM
+#include <ao_pyro.h>
+#endif
+#if HAS_IGNITE
__xdata struct ao_ignition ao_ignition[2];
void
@@ -150,6 +154,8 @@ ao_igniter(void)
}
}
+#endif
+
void
ao_ignite_manual(void)
{
@@ -157,33 +163,50 @@ ao_ignite_manual(void)
if (!ao_match_word("DoIt"))
return;
ao_cmd_white();
- if (ao_cmd_lex_c == 'm') {
- if(ao_match_word("main"))
- ao_igniter_fire(ao_igniter_main);
- } else {
- if(ao_match_word("drogue"))
- ao_igniter_fire(ao_igniter_drogue);
+#if HAS_IGNITE
+ if (ao_cmd_lex_c == 'm' && ao_match_word("main")) {
+ ao_igniter_fire(ao_igniter_main);
+ return;
+ }
+ if (ao_cmd_lex_c == 'd' && ao_match_word("drogue")) {
+ ao_igniter_fire(ao_igniter_drogue);
+ return;
+ }
+#endif
+#if AO_PYRO_NUM
+ if ('0' <= ao_cmd_lex_c && ao_cmd_lex_c <= '9') {
+ ao_pyro_manual(ao_cmd_lex_c - '0');
+ return;
}
+#endif
+ ao_cmd_status = ao_cmd_syntax_error;
}
-static __code char * __code igniter_status_names[] = {
+__code char * __code ao_igniter_status_names[] = {
"unknown", "ready", "active", "open"
};
+#if HAS_IGNITE
void
ao_ignite_print_status(enum ao_igniter igniter, __code char *name) __reentrant
{
enum ao_igniter_status status = ao_igniter_status(igniter);
printf("Igniter: %6s Status: %s\n",
name,
- igniter_status_names[status]);
+ ao_igniter_status_names[status]);
}
+#endif
void
ao_ignite_test(void)
{
+#if HAS_IGNITE
ao_ignite_print_status(ao_igniter_drogue, "drogue");
ao_ignite_print_status(ao_igniter_main, "main");
+#endif
+#if AO_PYRO_NUM
+ ao_pyro_print_status();
+#endif
}
__code struct ao_cmds ao_ignite_cmds[] = {
@@ -192,6 +215,7 @@ __code struct ao_cmds ao_ignite_cmds[] = {
{ 0, NULL },
};
+#if HAS_IGNITE
__xdata struct ao_task ao_igniter_task;
void
@@ -200,11 +224,14 @@ ao_ignite_set_pins(void)
ao_enable_output(AO_IGNITER_DROGUE_PORT, AO_IGNITER_DROGUE_PIN, AO_IGNITER_DROGUE, 0);
ao_enable_output(AO_IGNITER_MAIN_PORT, AO_IGNITER_MAIN_PIN, AO_IGNITER_MAIN, 0);
}
+#endif
void
ao_igniter_init(void)
{
+#if HAS_IGNITE
ao_ignite_set_pins();
- ao_cmd_register(&ao_ignite_cmds[0]);
ao_add_task(&ao_igniter_task, ao_igniter, "igniter");
+#endif
+ ao_cmd_register(&ao_ignite_cmds[0]);
}
diff --git a/src/core/ao_int64.c b/src/core/ao_int64.c
new file mode 100644
index 00000000..aa23dbe0
--- /dev/null
+++ b/src/core/ao_int64.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright © 2013 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_int64.h>
+
+__pdata ao_int64_t *__data ao_64r, *__data ao_64a, *__data ao_64b;
+
+void ao_plus64(__pdata ao_int64_t *r, __pdata ao_int64_t *a, __pdata ao_int64_t *b) __FATTR {
+ __LOCAL uint32_t t;
+
+ r->high = a->high + b->high;
+ t = a->low + b->low;
+ if (t < a->low)
+ r->high++;
+ r->low = t;
+}
+
+void ao_minus64(__pdata ao_int64_t *r, __pdata ao_int64_t *a, __pdata ao_int64_t *b) __FATTR {
+ __LOCAL uint32_t t;
+
+ r->high = a->high - b->high;
+ t = a->low - b->low;
+ if (t > a->low)
+ r->high--;
+ r->low = t;
+}
+
+void ao_rshift64(__pdata ao_int64_t *r, __pdata ao_int64_t *a, uint8_t d) __FATTR {
+ if (d < 32) {
+ r->low = a->low >> d;
+ if (d)
+ r->low |= a->high << (32 - d);
+ r->high = (int32_t) a->high >> d;
+ } else {
+ d &= 0x1f;
+ r->low = (int32_t) a->high >> d;
+ r->high = 0;
+ }
+}
+
+void ao_lshift64(__pdata ao_int64_t *r, __pdata ao_int64_t *a, uint8_t d) __FATTR {
+ if (d < 32) {
+ r->high = a->high << d;
+ if (d)
+ r->high |= a->low >> (32 - d);
+ r->low = a->low << d;
+ } else {
+ d &= 0x1f;
+ r->high = a->low << d;
+ r->low = 0;
+ }
+}
+
+static void ao_umul64_32_32(__ARG ao_int64_t *r, uint32_t a, uint32_t b) __reentrant {
+ __LOCAL uint32_t s;
+ __LOCAL ao_int64_t t;
+ r->low = (uint32_t) (uint16_t) a * (uint16_t) b;
+ r->high = (uint32_t) (uint16_t) (a >> 16) * (uint16_t) (b >> 16);
+
+ s = (uint32_t) (uint16_t) (a >> 16) * (uint16_t) b;
+
+ t.high = s >> 16;
+ t.low = s << 16;
+ ao_plus64(r, r, &t);
+
+ s = (uint32_t) (uint16_t) a * (uint16_t) (b >> 16);
+
+ t.high = s >> 16;
+ t.low = s << 16;
+ ao_plus64(r, r, &t);
+}
+
+void ao_neg64(__pdata ao_int64_t *r, __pdata ao_int64_t *a) __FATTR {
+ r->high = ~a->high;
+ if (!(r->low = ~a->low + 1))
+ r->high++;
+}
+
+void ao_mul64_32_32(__ARG ao_int64_t *r, int32_t a, int32_t b) __FATTR {
+ uint8_t negative = 0;
+
+ if (a < 0) {
+ a = -a;
+ ++negative;
+ }
+ if (b < 0) {
+ b = -b;
+ --negative;
+ }
+ ao_umul64_32_32(r, a, b);
+ if (negative)
+ ao_neg64(r, r);
+}
+
+static void ao_umul64(__ARG ao_int64_t *r, __ARG ao_int64_t *a, __ARG ao_int64_t *b) __reentrant {
+ __LOCAL ao_int64_t r2, r3;
+
+ ao_umul64_32_32(&r2, a->high, b->low);
+ ao_umul64_32_32(&r3, a->low, b->high);
+ ao_umul64_32_32(r, a->low, b->low);
+
+ r->high += r2.low + r3.low;
+}
+
+static __ARG ao_int64_t ap, bp;
+
+void ao_mul64(__ARG ao_int64_t *r, __ARG ao_int64_t *a, __ARG ao_int64_t *b) __FATTR {
+ uint8_t negative = 0;
+
+ if (ao_int64_negativep(a)) {
+ ao_neg64(&ap, a);
+ a = &ap;
+ ++negative;
+ }
+ if (ao_int64_negativep(b)) {
+ ao_neg64(&bp, b);
+ b = &bp;
+ --negative;
+ }
+ ao_umul64(r, a, b);
+ if (negative)
+ ao_neg64(r, r);
+}
+
+static void ao_umul64_64_16(__ARG ao_int64_t *r, __ARG ao_int64_t *a, uint16_t b) __reentrant {
+ __LOCAL uint32_t h;
+
+ h = a->high * b;
+ ao_umul64_32_32(r, a->low, b);
+ r->high += h;
+}
+
+void ao_mul64_64_16(__ARG ao_int64_t *r, __ARG ao_int64_t *a, __ARG uint16_t b) __FATTR {
+ uint8_t negative = 0;
+
+ if ((int32_t) a->high < 0) {
+ ao_neg64(&ap, a);
+ a = &ap;
+ negative++;
+ } else
+ ao_umul64_64_16(r, a, b);
+ if (negative)
+ ao_neg64(r, r);
+}
diff --git a/src/core/ao_int64.h b/src/core/ao_int64.h
new file mode 100644
index 00000000..b16db58c
--- /dev/null
+++ b/src/core/ao_int64.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2013 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_INT64_H_
+#define _AO_INT64_H_
+
+#include <stdint.h>
+
+typedef struct {
+ uint32_t high;
+ uint32_t low;
+} ao_int64_t;
+
+#define __FATTR
+#define __ARG __pdata
+#define __LOCAL static __pdata
+
+void ao_plus64(__pdata ao_int64_t *ao_64r, __pdata ao_int64_t *ao_64a, __pdata ao_int64_t *ao_64b) __FATTR;
+void ao_minus64(__pdata ao_int64_t *ao_64r, __pdata ao_int64_t *ao_64a, __pdata ao_int64_t *ao_64b) __FATTR;
+void ao_neg64(__pdata ao_int64_t *ao_64r, __pdata ao_int64_t *ao_64a) __FATTR;
+void ao_rshift64(__pdata ao_int64_t *ao_64r, __pdata ao_int64_t *ao_64a, uint8_t d) __FATTR;
+void ao_lshift64(__pdata ao_int64_t *ao_64r, __pdata ao_int64_t *ao_64a, uint8_t d) __FATTR;
+void ao_mul64_32_32(__ARG ao_int64_t *r, __ARG int32_t a, __ARG int32_t b) __FATTR;
+void ao_mul64_64_16(__ARG ao_int64_t *r, __ARG ao_int64_t *a, __ARG uint16_t b) __FATTR;
+void ao_mul64(__ARG ao_int64_t * __ARG r, __ARG ao_int64_t * __ARG a, __ARG ao_int64_t *__ARG b) __FATTR;
+
+#define ao_int64_init32(r, a) (((r)->high = 0), (r)->low = (a))
+#define ao_int64_init64(r, a, b) (((r)->high = (a)), (r)->low = (b))
+
+#define ao_cast64(a) (((int64_t) (a)->high << 32) | (a)->low)
+
+#define ao_int64_negativep(a) (((int32_t) (a)->high) < 0)
+
+#endif /* _AO_INT64_H_ */
diff --git a/src/core/ao_kalman.c b/src/core/ao_kalman.c
index 59ffd8b2..7fd4f889 100644
--- a/src/core/ao_kalman.c
+++ b/src/core/ao_kalman.c
@@ -40,9 +40,9 @@ static __pdata int32_t ao_k_accel;
__pdata int16_t ao_height;
__pdata int16_t ao_speed;
__pdata int16_t ao_accel;
-__pdata int16_t ao_max_height;
+__xdata int16_t ao_max_height;
static __pdata int32_t ao_avg_height_scaled;
-__pdata int16_t ao_avg_height;
+__xdata int16_t ao_avg_height;
__pdata int16_t ao_error_h;
__pdata int16_t ao_error_h_sq_avg;
@@ -292,7 +292,4 @@ ao_kalman(void)
else
#endif
ao_avg_height = (ao_avg_height_scaled + 63) >> 7;
-#ifdef AO_FLIGHT_TEST
- ao_sample_prev_tick = ao_sample_tick;
-#endif
}
diff --git a/src/core/ao_log.c b/src/core/ao_log.c
index 7884ec3c..701c81ab 100644
--- a/src/core/ao_log.c
+++ b/src/core/ao_log.c
@@ -17,6 +17,7 @@
#include "ao.h"
#include <ao_log.h>
+#include <ao_config.h>
__pdata uint32_t ao_log_current_pos;
__pdata uint32_t ao_log_end_pos;
@@ -48,7 +49,7 @@ static __xdata struct ao_log_erase erase;
static uint32_t
ao_log_erase_pos(uint8_t i)
{
- return i * sizeof (struct ao_log_erase) + AO_STORAGE_ERASE_LOG;
+ return i * sizeof (struct ao_log_erase) + AO_CONFIG_MAX_SIZE;
}
void
@@ -56,14 +57,14 @@ ao_log_write_erase(uint8_t pos)
{
erase.unused = 0x00;
erase.flight = ao_flight_number;
- ao_storage_write(ao_log_erase_pos(pos), &erase, sizeof (erase));
- ao_storage_flush();
+ ao_config_write(ao_log_erase_pos(pos), &erase, sizeof (erase));
+ ao_config_flush();
}
static void
ao_log_read_erase(uint8_t pos)
{
- ao_storage_read(ao_log_erase_pos(pos), &erase, sizeof (erase));
+ ao_config_read(ao_log_erase_pos(pos), &erase, sizeof (erase));
}
@@ -87,7 +88,7 @@ ao_log_erase_mark(void)
static uint8_t
ao_log_slots()
{
- return (uint8_t) (ao_storage_config / ao_config.flight_log_max);
+ return (uint8_t) (ao_storage_log_max / ao_config.flight_log_max);
}
uint32_t
@@ -278,6 +279,11 @@ ao_log_init(void)
ao_cmd_register(&ao_log_cmds[0]);
+#ifndef HAS_ADC
+#error Define HAS_ADC for ao_log.c
+#endif
+#if HAS_ADC
/* Create a task to log events to eeprom */
ao_add_task(&ao_log_task, ao_log, "log");
+#endif
}
diff --git a/src/core/ao_log.h b/src/core/ao_log.h
index a68a40dd..09f31188 100644
--- a/src/core/ao_log.h
+++ b/src/core/ao_log.h
@@ -44,6 +44,9 @@ extern __pdata enum ao_flight_state ao_log_state;
#define AO_LOG_FORMAT_TELEMETRY 3 /* 32 byte ao_telemetry records */
#define AO_LOG_FORMAT_TELESCIENCE 4 /* 32 byte typed telescience records */
#define AO_LOG_FORMAT_TELEMEGA 5 /* 32 byte typed telemega records */
+#define AO_LOG_FORMAT_EASYMINI 6 /* 16-byte MS5607 baro only, 3.0V supply */
+#define AO_LOG_FORMAT_TELEMETRUM 7 /* 16-byte typed telemetrum records */
+#define AO_LOG_FORMAT_TELEMINI 8 /* 16-byte MS5607 baro only, 3.3V supply */
#define AO_LOG_FORMAT_NONE 127 /* No log at all */
extern __code uint8_t ao_log_format;
@@ -134,6 +137,7 @@ ao_log_full(void);
#define AO_LOG_GPS_ALT 'H'
#define AO_LOG_GPS_SAT 'V'
#define AO_LOG_GPS_DATE 'Y'
+#define AO_LOG_GPS_POS 'P'
#define AO_LOG_POS_NONE (~0UL)
@@ -235,7 +239,8 @@ struct ao_log_mega {
int16_t v_pbatt; /* 6 */
int16_t n_sense; /* 8 */
int16_t sense[10]; /* 10 */
- } volt; /* 30 */
+ uint16_t pyro; /* 30 */
+ } volt; /* 32 */
/* AO_LOG_GPS_TIME */
struct {
int32_t latitude; /* 4 */
@@ -248,8 +253,14 @@ struct ao_log_mega {
uint8_t year; /* 18 */
uint8_t month; /* 19 */
uint8_t day; /* 20 */
- uint8_t pad; /* 21 */
- } gps; /* 22 */
+ uint8_t course; /* 21 */
+ uint16_t ground_speed; /* 22 */
+ int16_t climb_rate; /* 24 */
+ uint8_t pdop; /* 26 */
+ uint8_t hdop; /* 27 */
+ uint8_t vdop; /* 28 */
+ uint8_t mode; /* 29 */
+ } gps; /* 30 */
/* AO_LOG_GPS_SAT */
struct {
uint16_t channels; /* 4 */
@@ -261,6 +272,98 @@ struct ao_log_mega {
} u;
};
+struct ao_log_metrum {
+ char type; /* 0 */
+ uint8_t csum; /* 1 */
+ uint16_t tick; /* 2 */
+ union { /* 4 */
+ /* AO_LOG_FLIGHT */
+ struct {
+ uint16_t flight; /* 4 */
+ int16_t ground_accel; /* 6 */
+ uint32_t ground_pres; /* 8 */
+ uint32_t ground_temp; /* 12 */
+ } flight; /* 16 */
+ /* AO_LOG_STATE */
+ struct {
+ uint16_t state; /* 4 */
+ uint16_t reason; /* 6 */
+ } state; /* 8 */
+ /* AO_LOG_SENSOR */
+ struct {
+ uint32_t pres; /* 4 */
+ uint32_t temp; /* 8 */
+ int16_t accel; /* 12 */
+ } sensor; /* 14 */
+ /* AO_LOG_TEMP_VOLT */
+ struct {
+ int16_t v_batt; /* 4 */
+ int16_t sense_a; /* 6 */
+ int16_t sense_m; /* 8 */
+ } volt; /* 10 */
+ /* AO_LOG_GPS_POS */
+ struct {
+ int32_t latitude; /* 4 */
+ int32_t longitude; /* 8 */
+ int16_t altitude; /* 12 */
+ } gps; /* 14 */
+ /* AO_LOG_GPS_TIME */
+ struct {
+ uint8_t hour; /* 4 */
+ uint8_t minute; /* 5 */
+ uint8_t second; /* 6 */
+ uint8_t flags; /* 7 */
+ uint8_t year; /* 8 */
+ uint8_t month; /* 9 */
+ uint8_t day; /* 10 */
+ uint8_t pad; /* 11 */
+ } gps_time; /* 12 */
+ /* AO_LOG_GPS_SAT (up to three packets) */
+ struct {
+ uint8_t channels; /* 4 */
+ uint8_t more; /* 5 */
+ struct {
+ uint8_t svid;
+ uint8_t c_n;
+ } sats[4]; /* 6 */
+ } gps_sat; /* 14 */
+ uint8_t raw[12]; /* 4 */
+ } u; /* 16 */
+};
+
+struct ao_log_mini {
+ char type; /* 0 */
+ uint8_t csum; /* 1 */
+ uint16_t tick; /* 2 */
+ union { /* 4 */
+ /* AO_LOG_FLIGHT */
+ struct {
+ uint16_t flight; /* 4 */
+ uint16_t r6;
+ uint32_t ground_pres; /* 8 */
+ } flight;
+ /* AO_LOG_STATE */
+ struct {
+ uint16_t state; /* 4 */
+ uint16_t reason; /* 6 */
+ } state;
+ /* AO_LOG_SENSOR */
+ struct {
+ uint8_t pres[3]; /* 4 */
+ uint8_t temp[3]; /* 7 */
+ int16_t sense_a; /* 10 */
+ int16_t sense_m; /* 12 */
+ int16_t v_batt; /* 14 */
+ } sensor; /* 16 */
+ } u; /* 16 */
+}; /* 16 */
+
+#define ao_log_pack24(dst,value) do { \
+ (dst)[0] = (value); \
+ (dst)[1] = (value) >> 8; \
+ (dst)[2] = (value) >> 16; \
+ } while (0)
+
/* Write a record to the eeprom log */
uint8_t
ao_log_data(__xdata struct ao_log_record *log) __reentrant;
@@ -268,7 +371,16 @@ ao_log_data(__xdata struct ao_log_record *log) __reentrant;
uint8_t
ao_log_mega(__xdata struct ao_log_mega *log) __reentrant;
+uint8_t
+ao_log_metrum(__xdata struct ao_log_metrum *log) __reentrant;
+
+uint8_t
+ao_log_mini(__xdata struct ao_log_mini *log) __reentrant;
+
void
ao_log_flush(void);
+void
+ao_gps_report_metrum_init(void);
+
#endif /* _AO_LOG_H_ */
diff --git a/src/core/ao_log_mega.c b/src/core/ao_log_mega.c
index abf953a6..768947d5 100644
--- a/src/core/ao_log_mega.c
+++ b/src/core/ao_log_mega.c
@@ -65,6 +65,7 @@ ao_log_dump_check_data(void)
return 1;
}
+#if HAS_ADC
static __data uint8_t ao_log_data_pos;
/* a hack to make sure that ao_log_megas fill the eeprom block in even units */
@@ -151,6 +152,7 @@ ao_log(void)
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];
+ log.u.volt.pyro = ao_pyro_fired;
ao_log_mega(&log);
next_other = log.tick + AO_OTHER_INTERVAL;
}
@@ -181,6 +183,7 @@ ao_log(void)
ao_sleep(&ao_log_running);
}
}
+#endif
uint16_t
ao_log_flight(uint8_t slot)
diff --git a/src/core/ao_log_metrum.c b/src/core/ao_log_metrum.c
new file mode 100644
index 00000000..43441e7a
--- /dev/null
+++ b/src/core/ao_log_metrum.c
@@ -0,0 +1,175 @@
+/*
+ * 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_metrum log;
+
+__code uint8_t ao_log_format = AO_LOG_FORMAT_TELEMETRUM;
+
+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_metrum); i++)
+ sum += *b++;
+ return -sum;
+}
+
+uint8_t
+ao_log_metrum(__xdata struct ao_log_metrum *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_metrum));
+ ao_log_current_pos += sizeof (struct ao_log_metrum);
+ }
+ } 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;
+}
+
+#if HAS_ADC
+static __data uint8_t ao_log_data_pos;
+
+/* a hack to make sure that ao_log_metrums fill the eeprom block in even units */
+typedef uint8_t check_log_size[1-(256 % sizeof(struct ao_log_metrum))] ;
+
+#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_metrum(&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;
+#if HAS_MS5607
+ log.u.sensor.pres = ao_data_ring[ao_log_data_pos].ms5607_raw.pres;
+ log.u.sensor.temp = ao_data_ring[ao_log_data_pos].ms5607_raw.temp;
+#endif
+ log.u.sensor.accel = ao_data_accel(&ao_data_ring[ao_log_data_pos]);
+ ao_log_metrum(&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.sense_a = ao_data_ring[ao_log_data_pos].adc.sense_a;
+ log.u.volt.sense_m = ao_data_ring[ao_log_data_pos].adc.sense_m;
+ ao_log_metrum(&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_metrum(&log);
+
+ if (ao_log_state == ao_flight_landed)
+ ao_log_stop();
+ }
+#endif
+
+ ao_log_flush();
+
+ /* 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);
+ }
+}
+#endif
+
+uint16_t
+ao_log_flight(uint8_t slot)
+{
+ if (!ao_storage_read(ao_log_pos(slot),
+ &log,
+ sizeof (struct ao_log_metrum)))
+ 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_log_micro.c b/src/core/ao_log_micro.c
new file mode 100644
index 00000000..d665efb5
--- /dev/null
+++ b/src/core/ao_log_micro.c
@@ -0,0 +1,121 @@
+/*
+ * 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_micropeak.h>
+#include <ao_log_micro.h>
+#include <ao_async.h>
+
+static uint16_t ao_log_offset = STARTING_LOG_OFFSET;
+
+void
+ao_log_micro_save(void)
+{
+ uint16_t n_samples = (ao_log_offset - STARTING_LOG_OFFSET) / sizeof (uint16_t);
+ ao_eeprom_write(PA_GROUND_OFFSET, &pa_ground, sizeof (pa_ground));
+ ao_eeprom_write(PA_MIN_OFFSET, &pa_min, sizeof (pa_min));
+ ao_eeprom_write(N_SAMPLES_OFFSET, &n_samples, sizeof (n_samples));
+}
+
+void
+ao_log_micro_restore(void)
+{
+ ao_eeprom_read(PA_GROUND_OFFSET, &pa_ground, sizeof (pa_ground));
+ ao_eeprom_read(PA_MIN_OFFSET, &pa_min, sizeof (pa_min));
+}
+
+void
+ao_log_micro_data(void)
+{
+ uint16_t low_bits = pa;
+
+ if (ao_log_offset < MAX_LOG_OFFSET) {
+ ao_eeprom_write(ao_log_offset, &low_bits, sizeof (low_bits));
+ ao_log_offset += sizeof (low_bits);
+ }
+}
+
+#define POLY 0x8408
+
+static uint16_t
+ao_log_micro_crc(uint16_t crc, uint8_t byte)
+{
+ uint8_t i;
+
+ for (i = 0; i < 8; i++) {
+ if ((crc & 0x0001) ^ (byte & 0x0001))
+ crc = (crc >> 1) ^ POLY;
+ else
+ crc = crc >> 1;
+ byte >>= 1;
+ }
+ return crc;
+}
+
+static void
+ao_log_hex_nibble(uint8_t b)
+{
+ if (b < 10)
+ ao_async_byte('0' + b);
+ else
+ ao_async_byte('a' - 10 + b);
+}
+
+static void
+ao_log_hex(uint8_t b)
+{
+ ao_log_hex_nibble(b>>4);
+ ao_log_hex_nibble(b&0xf);
+}
+
+static void
+ao_log_newline(void)
+{
+ ao_async_byte('\r');
+ ao_async_byte('\n');
+}
+
+void
+ao_log_micro_dump(void)
+{
+ uint16_t n_samples;
+ uint16_t nbytes;
+ uint8_t byte;
+ uint16_t b;
+ uint16_t crc = 0xffff;
+
+ ao_eeprom_read(N_SAMPLES_OFFSET, &n_samples, sizeof (n_samples));
+ if (n_samples == 0xffff)
+ n_samples = 0;
+ nbytes = STARTING_LOG_OFFSET + sizeof (uint16_t) * n_samples;
+ ao_async_start();
+ ao_async_byte('M');
+ ao_async_byte('P');
+ for (b = 0; b < nbytes; b++) {
+ if ((b & 0xf) == 0)
+ ao_log_newline();
+ ao_eeprom_read(b, &byte, 1);
+ ao_log_hex(byte);
+ crc = ao_log_micro_crc(crc, byte);
+ }
+ ao_log_newline();
+ crc = ~crc;
+ ao_log_hex(crc >> 8);
+ ao_log_hex(crc);
+ ao_log_newline();
+ ao_async_stop();
+}
diff --git a/src/core/ao_log_micro.h b/src/core/ao_log_micro.h
new file mode 100644
index 00000000..976852ee
--- /dev/null
+++ b/src/core/ao_log_micro.h
@@ -0,0 +1,39 @@
+/*
+ * 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_LOG_MICRO_H_
+#define _AO_LOG_MICRO_H_
+
+#define PA_GROUND_OFFSET 0
+#define PA_MIN_OFFSET 4
+#define N_SAMPLES_OFFSET 8
+#define STARTING_LOG_OFFSET 10
+#define MAX_LOG_OFFSET 512
+
+void
+ao_log_micro_save(void);
+
+void
+ao_log_micro_restore(void);
+
+void
+ao_log_micro_data(void);
+
+void
+ao_log_micro_dump(void);
+
+#endif /* _AO_LOG_MICRO_H_ */
diff --git a/src/core/ao_log_mini.c b/src/core/ao_log_mini.c
new file mode 100644
index 00000000..99a85982
--- /dev/null
+++ b/src/core/ao_log_mini.c
@@ -0,0 +1,162 @@
+/*
+ * 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_mini log;
+
+__code uint8_t ao_log_format = AO_LOG_FORMAT;
+
+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_mini); i++)
+ sum += *b++;
+ return -sum;
+}
+
+uint8_t
+ao_log_mini(__xdata struct ao_log_mini *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_mini));
+ ao_log_current_pos += sizeof (struct ao_log_mini);
+ }
+ } 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_minis fill the eeprom block in even units */
+typedef uint8_t check_log_size[1-(256 % sizeof(struct ao_log_mini))] ;
+
+#ifndef AO_SENSOR_INTERVAL_ASCENT
+#define AO_SENSOR_INTERVAL_ASCENT 1
+#define AO_SENSOR_INTERVAL_DESCENT 10
+#endif
+
+void
+ao_log(void)
+{
+ __pdata uint16_t next_sensor, next_other;
+
+ 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;
+ log.u.flight.flight = ao_flight_number;
+ log.u.flight.ground_pres = ao_ground_pres;
+ ao_log_mini(&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;
+ ao_log_pack24(log.u.sensor.pres,
+ ao_data_ring[ao_log_data_pos].ms5607_raw.pres);
+ ao_log_pack24(log.u.sensor.temp,
+ ao_data_ring[ao_log_data_pos].ms5607_raw.temp);
+ log.u.sensor.sense_a = ao_data_ring[ao_log_data_pos].adc.sense_a;
+ log.u.sensor.sense_m = ao_data_ring[ao_log_data_pos].adc.sense_m;
+ log.u.sensor.v_batt = ao_data_ring[ao_log_data_pos].adc.v_batt;
+ ao_log_mini(&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;
+ }
+ 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_mini(&log);
+
+ if (ao_log_state == ao_flight_landed)
+ ao_log_stop();
+ }
+#endif
+
+ ao_log_flush();
+
+ /* 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_mini)))
+ 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_log_telem.c b/src/core/ao_log_telem.c
index 23ebf7dd..095aca37 100644
--- a/src/core/ao_log_telem.c
+++ b/src/core/ao_log_telem.c
@@ -23,7 +23,7 @@ __code uint8_t ao_log_format = AO_LOG_FORMAT_TELEMETRY;
static __data uint8_t ao_log_monitor_pos;
__pdata enum ao_flight_state ao_flight_state;
-__pdata int16_t ao_max_height; /* max of ao_height */
+__xdata int16_t ao_max_height; /* max of ao_height */
__pdata int16_t sense_d, sense_m;
__pdata uint8_t ao_igniter_present;
diff --git a/src/core/ao_log_tiny.c b/src/core/ao_log_tiny.c
index 492658ea..67767dc9 100644
--- a/src/core/ao_log_tiny.c
+++ b/src/core/ao_log_tiny.c
@@ -105,7 +105,7 @@ ao_log(void)
*/
ao_sleep(DATA_TO_XDATA(&ao_sample_data));
while (ao_log_data != ao_sample_data) {
- sum += ao_data_ring[ao_log_data].adc.pres;
+ sum += ao_data_pres(&ao_data_ring[ao_log_data]);
count++;
ao_log_data = ao_data_ring_next(ao_log_data);
}
diff --git a/src/core/ao_microflight.c b/src/core/ao_microflight.c
new file mode 100644
index 00000000..f680e400
--- /dev/null
+++ b/src/core/ao_microflight.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright © 2013 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>
+#endif
+#include <ao_micropeak.h>
+#include <ao_log_micro.h>
+
+uint32_t pa;
+uint32_t pa_ground;
+uint32_t pa_min;
+
+static void
+ao_microsample(void)
+{
+ ao_pa_get();
+ ao_microkalman_predict();
+ ao_microkalman_correct();
+}
+
+#define NUM_PA_HIST (GROUND_AVG)
+
+#define SKIP_PA_HIST(i,j) (((i) + (j)) & (NUM_PA_HIST - 1))
+
+static uint32_t pa_hist[NUM_PA_HIST];
+
+void
+ao_microflight(void)
+{
+ int16_t sample_count;
+ uint16_t time;
+ uint32_t pa_interval_min, pa_interval_max;
+ int32_t pa_diff;
+ uint8_t h, i;
+ uint8_t accel_lock = 0;
+ uint32_t pa_sum = 0;
+
+ /* Wait for motion, averaging values to get ground pressure */
+
+ time = ao_time();
+ ao_pa_get();
+ ao_microkalman_init();
+ pa_ground = pa;
+ sample_count = 0;
+ h = 0;
+ for (;;) {
+ time += SAMPLE_SLEEP;
+ if ((sample_count & 0x1f) == 0)
+ ao_led_on(AO_LED_REPORT);
+ ao_delay_until(time);
+ ao_microsample();
+ if ((sample_count & 0x1f) == 0)
+ ao_led_off(AO_LED_REPORT);
+ pa_hist[h] = pa;
+ h = SKIP_PA_HIST(h,1);
+ pa_diff = pa_ground - ao_pa;
+
+ /* Check for a significant pressure change */
+ if (pa_diff > BOOST_DETECT)
+ break;
+
+ if (sample_count < GROUND_AVG * 2) {
+ if (sample_count < GROUND_AVG)
+ pa_sum += pa;
+ ++sample_count;
+ } else {
+ pa_ground = pa_sum >> GROUND_AVG_SHIFT;
+ pa_sum = 0;
+ sample_count = 0;
+ }
+ }
+
+ /* Go back and find the last sample close to the ground */
+ pa_min = pa_ground - LAND_DETECT;
+ for (i = SKIP_PA_HIST(h,-2); i != SKIP_PA_HIST(h,2); i = SKIP_PA_HIST(i,-2)) {
+ if (pa_hist[i] >= pa_min)
+ break;
+ }
+
+ /* Log the remaining samples so we get a complete history since leaving the ground */
+ for (; i != h; i = SKIP_PA_HIST(i,2)) {
+ pa = pa_hist[i];
+ ao_log_micro_data();
+ }
+
+ /* Now sit around until the pressure is stable again and record the max */
+
+ sample_count = 0;
+ pa_min = ao_pa;
+ pa_interval_min = ao_pa;
+ pa_interval_max = ao_pa;
+ for (;;) {
+ time += SAMPLE_SLEEP;
+ ao_delay_until(time);
+ if ((sample_count & 3) == 0)
+ ao_led_on(AO_LED_REPORT);
+ ao_microsample();
+ if ((sample_count & 3) == 0)
+ ao_led_off(AO_LED_REPORT);
+ if (sample_count & 1)
+ ao_log_micro_data();
+
+ /* If accelerating upwards, don't look for min pressure */
+ if (ao_pa_accel < ACCEL_LOCK_PA)
+ accel_lock = ACCEL_LOCK_TIME;
+ else if (accel_lock)
+ --accel_lock;
+ else if (ao_pa < pa_min)
+ pa_min = ao_pa;
+
+ if (sample_count == (GROUND_AVG - 1)) {
+ pa_diff = pa_interval_max - pa_interval_min;
+
+ /* Check to see if the pressure is now stable */
+ if (pa_diff < LAND_DETECT)
+ break;
+ sample_count = 0;
+ pa_interval_min = ao_pa;
+ pa_interval_max = ao_pa;
+ } else {
+ if (ao_pa < pa_interval_min)
+ pa_interval_min = ao_pa;
+ if (ao_pa > pa_interval_max)
+ pa_interval_max = ao_pa;
+ ++sample_count;
+ }
+ }
+}
diff --git a/src/core/ao_microkalman.c b/src/core/ao_microkalman.c
new file mode 100644
index 00000000..0684ea2b
--- /dev/null
+++ b/src/core/ao_microkalman.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2013 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>
+#endif
+#include <ao_micropeak.h>
+
+#define FIX_BITS 16
+
+#define to_fix16(x) ((int16_t) ((x) * 65536.0 + 0.5))
+#define to_fix32(x) ((int32_t) ((x) * 65536.0 + 0.5))
+#define from_fix8(x) ((x) >> 8)
+#define from_fix(x) ((x) >> 16)
+#define fix8_to_fix16(x) ((x) << 8)
+#define fix16_to_fix8(x) ((x) >> 8)
+
+#include <ao_kalman.h>
+
+/* Basic time step (96ms) */
+#define AO_MK_STEP to_fix16(0.096)
+/* step ** 2 / 2 */
+#define AO_MK_STEP_2_2 to_fix16(0.004608)
+
+uint32_t ao_k_pa; /* 24.8 fixed point */
+int32_t ao_k_pa_speed; /* 16.16 fixed point */
+int32_t ao_k_pa_accel; /* 16.16 fixed point */
+
+uint32_t ao_pa; /* integer portion */
+int16_t ao_pa_speed; /* integer portion */
+int16_t ao_pa_accel; /* integer portion */
+
+void
+ao_microkalman_init(void)
+{
+ ao_pa = pa;
+ ao_k_pa = pa << 8;
+}
+
+void
+ao_microkalman_predict(void)
+{
+ ao_k_pa += fix16_to_fix8((int32_t) ao_pa_speed * AO_MK_STEP + (int32_t) ao_pa_accel * AO_MK_STEP_2_2);
+ ao_k_pa_speed += (int32_t) ao_pa_accel * AO_MK_STEP;
+}
+
+void
+ao_microkalman_correct(void)
+{
+ int16_t e; /* Height error in Pa */
+
+ e = pa - from_fix8(ao_k_pa);
+
+ ao_k_pa += fix16_to_fix8((int32_t) e * AO_MK_BARO_K0_10);
+ ao_k_pa_speed += (int32_t) e * AO_MK_BARO_K1_10;
+ ao_k_pa_accel += (int32_t) e * AO_MK_BARO_K2_10;
+ ao_pa = from_fix8(ao_k_pa);
+ ao_pa_speed = from_fix(ao_k_pa_speed);
+ ao_pa_accel = from_fix(ao_k_pa_accel);
+}
diff --git a/src/core/ao_product.c b/src/core/ao_product.c
index ec91b978..b9327bac 100644
--- a/src/core/ao_product.c
+++ b/src/core/ao_product.c
@@ -27,6 +27,12 @@ const char ao_product[] = AO_iProduct_STRING;
#define LE_WORD(x) ((x)&0xFF),((uint8_t) (((uint16_t) (x))>>8))
#if HAS_USB
+
+/* Maximum power in mA */
+#ifndef AO_USB_MAX_POWER
+#define AO_USB_MAX_POWER 100
+#endif
+
#include "ao_usb.h"
/* USB descriptors in one giant block of bytes */
AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] =
@@ -55,7 +61,7 @@ AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] =
0x01, /* bConfigurationValue */
0x00, /* iConfiguration */
0xC0, /* bmAttributes */
- 0x32, /* bMaxPower */
+ AO_USB_MAX_POWER >> 1, /* bMaxPower, 2mA units */
/* Control class interface */
0x09,
diff --git a/src/core/ao_pyro.c b/src/core/ao_pyro.c
index aac8fda5..a260aa99 100644
--- a/src/core/ao_pyro.c
+++ b/src/core/ao_pyro.c
@@ -15,10 +15,12 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
+#ifndef AO_FLIGHT_TEST
#include <ao.h>
-#include <ao_pyro.h>
#include <ao_sample.h>
#include <ao_flight.h>
+#endif
+#include <ao_pyro.h>
#if IS_COMPANION
#include <ao_companion.h>
@@ -31,6 +33,42 @@
#define ao_lowbit(x) ((x) & (-x))
+#ifndef AO_FLIGHT_TEST
+enum ao_igniter_status
+ao_pyro_status(uint8_t p)
+{
+ __xdata struct ao_data packet;
+ __pdata int16_t value;
+
+ ao_arch_critical(
+ ao_data_get(&packet);
+ );
+
+ value = (AO_IGNITER_CLOSED>>1);
+ value = AO_SENSE_PYRO(&packet, p);
+ 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_pyro_print_status(void)
+{
+ uint8_t p;
+
+ for(p = 0; p < AO_PYRO_NUM; p++) {
+ enum ao_igniter_status status = ao_pyro_status(p);
+ printf("Igniter: %d Status: %s\n",
+ p, ao_igniter_status_names[status]);
+ }
+}
+#endif
+
+uint16_t ao_pyro_fired;
+
/*
* Given a pyro structure, figure out
* if the current flight state satisfies all
@@ -75,13 +113,13 @@ ao_pyro_ready(struct ao_pyro *pyro)
continue;
break;
-#if HAS_ORIENT
+#if HAS_GYRO
case ao_pyro_orient_less:
- if (ao_orient <= pyro->orient_less)
+ if (ao_sample_orient <= pyro->orient_less)
continue;
break;
case ao_pyro_orient_greater:
- if (ao_orient >= pyro->orient_greater)
+ if (ao_sample_orient >= pyro->orient_greater)
continue;
break;
#endif
@@ -130,107 +168,116 @@ ao_pyro_ready(struct ao_pyro *pyro)
return TRUE;
}
-#define ao_pyro_fire_port(port, bit, pin) do { \
- ao_gpio_set(port, bit, pin, 1); \
- ao_delay(AO_MS_TO_TICKS(50)); \
- ao_gpio_set(port, bit, pin, 0); \
- } while (0)
-
-
+#ifndef AO_FLIGHT_TEST
static void
-ao_pyro_fire(uint8_t p)
+ao_pyro_pin_set(uint8_t p, uint8_t v)
{
switch (p) {
#if AO_PYRO_NUM > 0
- case 0: ao_pyro_fire_port(AO_PYRO_PORT_0, AO_PYRO_PIN_0, AO_PYRO_0); break;
+ case 0: ao_gpio_set(AO_PYRO_PORT_0, AO_PYRO_PIN_0, AO_PYRO_0, v); break;
#endif
#if AO_PYRO_NUM > 1
- case 1: ao_pyro_fire_port(AO_PYRO_PORT_1, AO_PYRO_PIN_1, AO_PYRO_1); break;
+ case 1: ao_gpio_set(AO_PYRO_PORT_1, AO_PYRO_PIN_1, AO_PYRO_1, v); break;
#endif
#if AO_PYRO_NUM > 2
- case 2: ao_pyro_fire_port(AO_PYRO_PORT_2, AO_PYRO_PIN_2, AO_PYRO_2); break;
+ case 2: ao_gpio_set(AO_PYRO_PORT_2, AO_PYRO_PIN_2, AO_PYRO_2, v); break;
#endif
#if AO_PYRO_NUM > 3
- case 3: ao_pyro_fire_port(AO_PYRO_PORT_3, AO_PYRO_PIN_3, AO_PYRO_3); break;
+ case 3: ao_gpio_set(AO_PYRO_PORT_3, AO_PYRO_PIN_3, AO_PYRO_3, v); break;
#endif
#if AO_PYRO_NUM > 4
- case 4: ao_pyro_fire_port(AO_PYRO_PORT_4, AO_PYRO_PIN_4, AO_PYRO_4); break;
+ case 4: ao_gpio_set(AO_PYRO_PORT_4, AO_PYRO_PIN_4, AO_PYRO_4, v); break;
#endif
#if AO_PYRO_NUM > 5
- case 5: ao_pyro_fire_port(AO_PYRO_PORT_5, AO_PYRO_PIN_5, AO_PYRO_5); break;
+ case 5: ao_gpio_set(AO_PYRO_PORT_5, AO_PYRO_PIN_5, AO_PYRO_5, v); break;
#endif
#if AO_PYRO_NUM > 6
- case 6: ao_pyro_fire_port(AO_PYRO_PORT_6, AO_PYRO_PIN_6, AO_PYRO_6); break;
+ case 6: ao_gpio_set(AO_PYRO_PORT_6, AO_PYRO_PIN_6, AO_PYRO_6, v); break;
#endif
#if AO_PYRO_NUM > 7
- case 7: ao_pyro_fire_port(AO_PYRO_PORT_7, AO_PYRO_PIN_7, AO_PYRO_7); break;
+ case 7: ao_gpio_set(AO_PYRO_PORT_7, AO_PYRO_PIN_7, AO_PYRO_7, v); break;
#endif
default: break;
}
- ao_delay(AO_MS_TO_TICKS(50));
}
+#endif
uint8_t ao_pyro_wakeup;
static void
-ao_pyro(void)
+ao_pyro_pins_fire(uint16_t fire)
{
- uint8_t p, any_waiting;
- struct ao_pyro *pyro;
+ uint8_t p;
- ao_config_get();
- while (ao_flight_state < ao_flight_boost)
- ao_sleep(&ao_flight_state);
+ for (p = 0; p < AO_PYRO_NUM; p++) {
+ if (fire & (1 << p))
+ ao_pyro_pin_set(p, 1);
+ }
+ ao_delay(AO_MS_TO_TICKS(50));
+ for (p = 0; p < AO_PYRO_NUM; p++) {
+ if (fire & (1 << p)) {
+ ao_pyro_pin_set(p, 0);
+ ao_config.pyro[p].fired = 1;
+ ao_pyro_fired |= (1 << p);
+ }
+ }
+ ao_delay(AO_MS_TO_TICKS(50));
+}
- for (;;) {
- ao_alarm(AO_MS_TO_TICKS(100));
- ao_sleep(&ao_pyro_wakeup);
- ao_clear_alarm();
- any_waiting = 0;
- for (p = 0; p < AO_PYRO_NUM; p++) {
- pyro = &ao_config.pyro[p];
+static uint8_t
+ao_pyro_check(void)
+{
+ struct ao_pyro *pyro;
+ uint8_t p, any_waiting;
+ uint16_t fire = 0;
+
+ any_waiting = 0;
+ for (p = 0; p < AO_PYRO_NUM; p++) {
+ pyro = &ao_config.pyro[p];
- /* Ignore igniters which have already fired
- */
- if (pyro->fired)
- continue;
+ /* Ignore igniters which have already fired
+ */
+ if (pyro->fired)
+ continue;
- /* Ignore disabled igniters
- */
- if (!pyro->flags)
- continue;
+ /* Ignore disabled igniters
+ */
+ if (!pyro->flags)
+ continue;
- any_waiting = 1;
- /* Check pyro state to see if it shoule fire
- */
- if (!pyro->delay_done) {
- if (!ao_pyro_ready(pyro))
- continue;
-
- /* If there's a delay set, then remember when
- * it expires
- */
- if (pyro->flags & ao_pyro_delay)
- pyro->delay_done = ao_time() + pyro->delay;
- }
+ any_waiting = 1;
+ /* Check pyro state to see if it should fire
+ */
+ if (!pyro->delay_done) {
+ if (!ao_pyro_ready(pyro))
+ continue;
- /* Check to see if we're just waiting for
- * the delay to expire
+ /* If there's a delay set, then remember when
+ * it expires
*/
- if (pyro->delay_done) {
- if ((int16_t) (ao_time() - pyro->delay_done) < 0)
- continue;
+ if (pyro->flags & ao_pyro_delay) {
+ pyro->delay_done = ao_time() + pyro->delay;
+ if (!pyro->delay_done)
+ pyro->delay_done = 1;
}
+ }
- ao_pyro_fire(p);
+ /* Check to see if we're just waiting for
+ * the delay to expire
+ */
+ if (pyro->delay_done) {
+ if ((int16_t) (ao_time() - pyro->delay_done) < 0)
+ continue;
}
- if (!any_waiting)
- break;
+
+ fire |= (1 << p);
}
- ao_exit();
-}
-__xdata struct ao_task ao_pyro_task;
+ if (fire)
+ ao_pyro_pins_fire(fire);
+
+ return any_waiting;
+}
#define NO_VALUE 0xff
@@ -263,7 +310,7 @@ const struct {
{ "h<", ao_pyro_height_less, offsetof(struct ao_pyro, height_less), HELP("height less (m)") },
{ "h>", ao_pyro_height_greater, offsetof(struct ao_pyro, height_greater), HELP("height greater (m)") },
-#if HAS_ORIENT
+#if HAS_GYRO
{ "o<", ao_pyro_orient_less, offsetof(struct ao_pyro, orient_less), HELP("orient less (deg)") },
{ "o>", ao_pyro_orient_greater, offsetof(struct ao_pyro, orient_greater), HELP("orient greater (deg)") },
#endif
@@ -283,6 +330,34 @@ const struct {
{ "", ao_pyro_none, NO_VALUE, HELP(NULL) },
};
+#define NUM_PYRO_VALUES (sizeof ao_pyro_values / sizeof ao_pyro_values[0])
+
+#ifndef AO_FLIGHT_TEST
+static void
+ao_pyro(void)
+{
+ uint8_t any_waiting;
+
+ ao_config_get();
+ while (ao_flight_state < ao_flight_boost)
+ ao_sleep(&ao_flight_state);
+
+ for (;;) {
+ ao_alarm(AO_MS_TO_TICKS(100));
+ ao_sleep(&ao_pyro_wakeup);
+ ao_clear_alarm();
+ if (ao_flight_state >= ao_flight_landed)
+ break;
+ any_waiting = ao_pyro_check();
+ if (!any_waiting)
+ break;
+ }
+ ao_exit();
+}
+
+__xdata struct ao_task ao_pyro_task;
+
+
static void
ao_pyro_print_name(uint8_t v)
{
@@ -400,25 +475,17 @@ ao_pyro_set(void)
_ao_config_edit_finish();
}
-static void
-ao_pyro_manual(void)
+void
+ao_pyro_manual(uint8_t p)
{
- ao_cmd_white();
- if (!ao_match_word("DoIt"))
- return;
- ao_cmd_white();
- ao_cmd_decimal();
- if (ao_cmd_lex_i < 0 || AO_PYRO_NUM <= ao_cmd_lex_i)
+ printf ("ao_pyro_manual %d\n", p);
+ if (p >= AO_PYRO_NUM) {
+ ao_cmd_status = ao_cmd_syntax_error;
return;
- ao_pyro_fire(ao_cmd_lex_i);
-
+ }
+ ao_pyro_pins_fire(1 << p);
}
-const struct ao_cmds ao_pyro_cmds[] = {
- { ao_pyro_manual, "P DoIt <n>\0Fire igniter" },
- { 0, NULL }
-};
-
void
ao_pyro_init(void)
{
@@ -446,6 +513,6 @@ ao_pyro_init(void)
#if AO_PYRO_NUM > 7
ao_enable_output(AO_PYRO_PORT_7, AO_PYRO_PIN_7, AO_PYRO_7, 0);
#endif
- ao_cmd_register(&ao_pyro_cmds[0]);
ao_add_task(&ao_pyro_task, ao_pyro, "pyro");
}
+#endif
diff --git a/src/core/ao_pyro.h b/src/core/ao_pyro.h
index cde850ad..0c5642d6 100644
--- a/src/core/ao_pyro.h
+++ b/src/core/ao_pyro.h
@@ -63,6 +63,8 @@ struct ao_pyro {
extern uint8_t ao_pyro_wakeup;
+extern uint16_t ao_pyro_fired;
+
void
ao_pyro_set(void);
@@ -72,4 +74,10 @@ ao_pyro_show(void);
void
ao_pyro_init(void);
+void
+ao_pyro_manual(uint8_t p);
+
+void
+ao_pyro_print_status(void);
+
#endif
diff --git a/src/core/ao_quaternion.h b/src/core/ao_quaternion.h
new file mode 100644
index 00000000..044f1607
--- /dev/null
+++ b/src/core/ao_quaternion.h
@@ -0,0 +1,249 @@
+/*
+ * Copyright © 2013 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_QUATERNION_H_
+#define _AO_QUATERNION_H_
+
+#include <math.h>
+
+struct ao_quaternion {
+ float r; /* real bit */
+ float x, y, z; /* imaginary bits */
+};
+
+static inline void ao_quaternion_multiply(struct ao_quaternion *r,
+ const struct ao_quaternion *a,
+ const struct ao_quaternion *b)
+{
+ struct ao_quaternion t;
+#define T(_a,_b) (((a)->_a) * ((b)->_b))
+
+/*
+ * Quaternions
+ *
+ * ii = jj = kk = ijk = -1;
+ *
+ * kji = 1;
+ *
+ * ij = k; ji = -k;
+ * kj = -i; jk = i;
+ * ik = -j; ki = j;
+ *
+ * Multiplication p * q:
+ *
+ * (pr + ipx + jpy + kpz) (qr + iqx + jqy + kqz) =
+ *
+ * ( pr * qr + pr * iqx + pr * jqy + pr * kqz) +
+ * (ipx * qr + ipx * iqx + ipx * jqy + ipx * kqz) +
+ * (jpy * qr + jpy * iqx + jpy * jqy + jpy * kqz) +
+ * (kpz * qr + kpz * iqx + kpz * jqy + kpz * kqz) =
+ *
+ *
+ * (pr * qr) + i(pr * qx) + j(pr * qy) + k(pr * qz) +
+ * i(px * qr) - (px * qx) + k(px * qy) - j(px * qz) +
+ * j(py * qr) - k(py * qx) - (py * qy) + i(py * qz) +
+ * k(pz * qr) + j(pz * qx) - i(pz * qy) - (pz * qz) =
+ *
+ * 1 * ( (pr * qr) - (px * qx) - (py * qy) - (pz * qz) ) +
+ * i * ( (pr * qx) + (px * qr) + (py * qz) - (pz * qy) ) +
+ * j * ( (pr * qy) - (px * qz) + (py * qr) + (pz * qx) ) +
+ * k * ( (pr * qz) + (px * qy) - (py * qx) + (pz * qr);
+ */
+
+ t.r = T(r,r) - T(x,x) - T(y,y) - T(z,z);
+ t.x = T(r,x) + T(x,r) + T(y,z) - T(z,y);
+ t.y = T(r,y) - T(x,z) + T(y,r) + T(z,x);
+ t.z = T(r,z) + T(x,y) - T(y,x) + T(z,r);
+#undef T
+ *r = t;
+}
+
+static inline void ao_quaternion_conjugate(struct ao_quaternion *r,
+ const struct ao_quaternion *a)
+{
+ r->r = a->r;
+ r->x = -a->x;
+ r->y = -a->y;
+ r->z = -a->z;
+}
+
+static inline float ao_quaternion_normal(const struct ao_quaternion *a)
+{
+#define S(_a) (((a)->_a) * ((a)->_a))
+ return S(r) + S(x) + S(y) + S(z);
+#undef S
+}
+
+static inline void ao_quaternion_scale(struct ao_quaternion *r,
+ const struct ao_quaternion *a,
+ float b)
+{
+ r->r = a->r * b;
+ r->x = a->x * b;
+ r->y = a->y * b;
+ r->z = a->z * b;
+}
+
+static inline void ao_quaternion_normalize(struct ao_quaternion *r,
+ const struct ao_quaternion *a)
+{
+ float n = ao_quaternion_normal(a);
+
+ if (n > 0)
+ ao_quaternion_scale(r, a, 1/sqrtf(n));
+ else
+ *r = *a;
+}
+
+static inline float ao_quaternion_dot(const struct ao_quaternion *a,
+ const struct ao_quaternion *b)
+{
+#define T(_a) (((a)->_a) * ((b)->_a))
+ return T(r) + T(x) + T(y) + T(z);
+#undef T
+}
+
+
+static inline void ao_quaternion_rotate(struct ao_quaternion *r,
+ const struct ao_quaternion *a,
+ const struct ao_quaternion *b)
+{
+ struct ao_quaternion c;
+ struct ao_quaternion t;
+
+ ao_quaternion_multiply(&t, b, a);
+ ao_quaternion_conjugate(&c, b);
+ ao_quaternion_multiply(r, &t, &c);
+}
+
+/*
+ * Compute a rotation quaternion between two vectors
+ *
+ * cos(θ) + u * sin(θ)
+ *
+ * where θ is the angle between the two vectors and u
+ * is a unit vector axis of rotation
+ */
+
+static inline void ao_quaternion_vectors_to_rotation(struct ao_quaternion *r,
+ const struct ao_quaternion *a,
+ const struct ao_quaternion *b)
+{
+ /*
+ * The cross product will point orthogonally to the two
+ * vectors, forming our rotation axis. The length will be
+ * sin(θ), so these values are already multiplied by that.
+ */
+
+ float x = a->y * b->z - a->z * b->y;
+ float y = a->z * b->x - a->x * b->z;
+ float z = a->x * b->y - a->y * b->x;
+
+ float s_2 = x*x + y*y + z*z;
+ float s = sqrtf(s_2);
+
+ /* cos(θ) = a · b / (|a| |b|).
+ *
+ * a and b are both unit vectors, so the divisor is one
+ */
+ float c = a->x*b->x + a->y*b->y + a->z*b->z;
+
+ float c_half = sqrtf ((1 + c) / 2);
+ float s_half = sqrtf ((1 - c) / 2);
+
+ /*
+ * Divide out the sine factor from the
+ * cross product, then multiply in the
+ * half sine factor needed for the quaternion
+ */
+ float s_scale = s_half / s;
+
+ r->x = x * s_scale;
+ r->y = y * s_scale;
+ r->z = z * s_scale;
+
+ r->r = c_half;
+
+ ao_quaternion_normalize(r, r);
+}
+
+static inline void ao_quaternion_init_vector(struct ao_quaternion *r,
+ float x, float y, float z)
+{
+ r->r = 0;
+ r->x = x;
+ r->y = y;
+ r->z = z;
+}
+
+static inline void ao_quaternion_init_rotation(struct ao_quaternion *r,
+ float x, float y, float z,
+ float s, float c)
+{
+ r->r = c;
+ r->x = s * x;
+ r->y = s * y;
+ r->z = s * z;
+}
+
+static inline void ao_quaternion_init_zero_rotation(struct ao_quaternion *r)
+{
+ r->r = 1;
+ r->x = r->y = r->z = 0;
+}
+
+/*
+ * The sincosf from newlib just calls sinf and cosf. This is a bit
+ * faster, if slightly less precise
+ */
+
+static inline void
+ao_sincosf(float a, float *s, float *c) {
+ float _s = sinf(a);
+ *s = _s;
+ *c = sqrtf(1 - _s*_s);
+}
+
+/*
+ * Initialize a quaternion from 1/2 euler rotation angles (in radians).
+ *
+ * Yes, it would be nicer if there were a faster way, but because we
+ * sample the gyros at only 100Hz, we end up getting angles too large
+ * to take advantage of sin(x) ≃ x.
+ *
+ * We might be able to use just a couple of elements of the sin taylor
+ * series though, instead of the whole sin function?
+ */
+
+static inline void ao_quaternion_init_half_euler(struct ao_quaternion *r,
+ float x, float y, float z)
+{
+ float s_x, c_x;
+ float s_y, c_y;
+ float s_z, c_z;
+
+ ao_sincosf(x, &s_x, &c_x);
+ ao_sincosf(y, &s_y, &c_y);
+ ao_sincosf(z, &s_z, &c_z);
+
+ r->r = c_x * c_y * c_z + s_x * s_y * s_z;
+ r->x = s_x * c_y * c_z - c_x * s_y * s_z;
+ r->y = c_x * s_y * c_z + s_x * c_y * s_z;
+ r->z = c_x * c_y * s_z - s_x * s_y * c_z;
+}
+
+#endif /* _AO_QUATERNION_H_ */
diff --git a/src/core/ao_report_micro.c b/src/core/ao_report_micro.c
new file mode 100644
index 00000000..0e8e287f
--- /dev/null
+++ b/src/core/ao_report_micro.c
@@ -0,0 +1,57 @@
+/*
+ * 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>
+
+#define mid(time) ao_led_for(AO_LED_REPORT, time)
+#define pause(time) ao_delay(time)
+
+static void
+ao_report_digit(uint8_t digit) __reentrant
+{
+ if (!digit) {
+ mid(AO_MS_TO_TICKS(1000));
+ pause(AO_MS_TO_TICKS(300));
+ } else {
+ while (digit--) {
+ mid(AO_MS_TO_TICKS(300));
+ pause(AO_MS_TO_TICKS(300));
+ }
+ }
+ pause(AO_MS_TO_TICKS(1000));
+}
+
+void
+ao_report_altitude(void)
+{
+ __pdata alt_t agl = ao_max_height;
+ static __xdata uint8_t digits[11];
+ __pdata uint8_t ndigits, i;
+
+ if (agl < 0)
+ agl = 0;
+ ndigits = 0;
+ do {
+ digits[ndigits++] = agl % 10;
+ agl /= 10;
+ } while (agl);
+
+ i = ndigits;
+ do
+ ao_report_digit(digits[--i]);
+ while (i != 0);
+}
diff --git a/src/core/ao_sample.c b/src/core/ao_sample.c
index dec44f9f..adf8399d 100644
--- a/src/core/ao_sample.c
+++ b/src/core/ao_sample.c
@@ -20,6 +20,10 @@
#include <ao_data.h>
#endif
+#if HAS_GYRO
+#include <ao_quaternion.h>
+#endif
+
/*
* Current sensor values
*/
@@ -44,8 +48,7 @@ __pdata accel_t ao_sample_accel_through;
__pdata gyro_t ao_sample_roll;
__pdata gyro_t ao_sample_pitch;
__pdata gyro_t ao_sample_yaw;
-__pdata angle_t ao_sample_angle;
-__pdata angle_t ao_sample_roll_angle;
+__pdata angle_t ao_sample_orient;
#endif
__data uint8_t ao_sample_data;
@@ -67,9 +70,9 @@ __pdata int32_t ao_accel_scale; /* sensor to m/s² conversion */
__pdata accel_t ao_ground_accel_along;
__pdata accel_t ao_ground_accel_across;
__pdata accel_t ao_ground_accel_through;
-__pdata gyro_t ao_ground_pitch;
-__pdata gyro_t ao_ground_yaw;
-__pdata gyro_t ao_ground_roll;
+__pdata int32_t ao_ground_pitch;
+__pdata int32_t ao_ground_yaw;
+__pdata int32_t ao_ground_roll;
#endif
static __pdata uint8_t ao_preflight; /* in preflight mode */
@@ -86,6 +89,7 @@ __pdata int32_t ao_sample_accel_through_sum;
__pdata int32_t ao_sample_pitch_sum;
__pdata int32_t ao_sample_yaw_sum;
__pdata int32_t ao_sample_roll_sum;
+static struct ao_quaternion ao_rotation;
#endif
static void
@@ -120,20 +124,95 @@ ao_sample_preflight_set(void)
ao_ground_accel_along = ao_sample_accel_along_sum >> 9;
ao_ground_accel_across = ao_sample_accel_across_sum >> 9;
ao_ground_accel_through = ao_sample_accel_through_sum >> 9;
- ao_ground_pitch = ao_sample_pitch_sum >> 9;
- ao_ground_yaw = ao_sample_yaw_sum >> 9;
- ao_ground_roll = ao_sample_roll_sum >> 9;
+ ao_ground_pitch = ao_sample_pitch_sum;
+ ao_ground_yaw = ao_sample_yaw_sum;
+ ao_ground_roll = ao_sample_roll_sum;
ao_sample_accel_along_sum = 0;
ao_sample_accel_across_sum = 0;
ao_sample_accel_through_sum = 0;
ao_sample_pitch_sum = 0;
ao_sample_yaw_sum = 0;
ao_sample_roll_sum = 0;
- ao_sample_angle = 0;
+ ao_sample_orient = 0;
+
+ struct ao_quaternion orient;
+
+ /* Take the pad IMU acceleration values and compute our current direction
+ */
+
+ ao_quaternion_init_vector(&orient,
+ (ao_ground_accel_across - ao_config.accel_zero_across),
+ (ao_ground_accel_through - ao_config.accel_zero_through),
+ (ao_ground_accel_along - ao_config.accel_zero_along));
+
+ ao_quaternion_normalize(&orient,
+ &orient);
+
+ /* Here's up */
+
+ struct ao_quaternion up = { .r = 0, .x = 0, .y = 0, .z = 1 };
+
+ if (ao_config.pad_orientation != AO_PAD_ORIENTATION_ANTENNA_UP)
+ up.z = -1;
+
+ /* Compute rotation to get from up to our current orientation, set
+ * that as the current rotation vector
+ */
+ ao_quaternion_vectors_to_rotation(&ao_rotation, &up, &orient);
#endif
nsamples = 0;
}
+#if HAS_GYRO
+
+#define TIME_DIV 200.0f
+
+static void
+ao_sample_rotate(void)
+{
+#ifdef AO_FLIGHT_TEST
+ float dt = (ao_sample_tick - ao_sample_prev_tick) / TIME_DIV;
+#else
+ static const float dt = 1/TIME_DIV;
+#endif
+ float x = ao_mpu6000_gyro((float) ((ao_sample_pitch << 9) - ao_ground_pitch) / 512.0f) * dt;
+ float y = ao_mpu6000_gyro((float) ((ao_sample_yaw << 9) - ao_ground_yaw) / 512.0f) * dt;
+ float z = ao_mpu6000_gyro((float) ((ao_sample_roll << 9) - ao_ground_roll) / 512.0f) * dt;
+ struct ao_quaternion rot;
+
+ ao_quaternion_init_half_euler(&rot, x, y, z);
+ ao_quaternion_multiply(&ao_rotation, &rot, &ao_rotation);
+
+ /* And normalize to make sure it remains a unit vector */
+ ao_quaternion_normalize(&ao_rotation, &ao_rotation);
+
+ /* Compute pitch angle from vertical by taking the pad
+ * orientation vector and rotating it by the current total
+ * rotation value. That will be a unit vector pointing along
+ * the airframe axis. The Z value will be the cosine of the
+ * change in the angle from vertical since boost.
+ *
+ * rot = ao_rotation * vertical * ao_rotation°
+ * rot = ao_rotation * (0,0,0,1) * ao_rotation°
+ * = ((a.z, a.y, -a.x, a.r) * (a.r, -a.x, -a.y, -a.z)) .z
+ *
+ * = (-a.z * -a.z) + (a.y * -a.y) - (-a.x * -a.x) + (a.r * a.r)
+ * = a.z² - a.y² - a.x² + a.r²
+ *
+ * rot = ao_rotation * (0, 0, 0, -1) * ao_rotation°
+ * = ((-a.z, -a.y, a.x, -a.r) * (a.r, -a.x, -a.y, -a.z)) .z
+ *
+ * = (a.z * -a.z) + (-a.y * -a.y) - (a.x * -a.x) + (-a.r * a.r)
+ * = -a.z² + a.y² + a.x² - a.r²
+ */
+
+ float rotz;
+ rotz = ao_rotation.z * ao_rotation.z - ao_rotation.y * ao_rotation.y - ao_rotation.x * ao_rotation.x + ao_rotation.r * ao_rotation.r;
+
+ ao_sample_orient = acosf(rotz) * (float) (180.0/M_PI);
+}
+#endif
+
static void
ao_sample_preflight(void)
{
@@ -232,9 +311,12 @@ ao_sample(void)
ao_sample_preflight_update();
ao_kalman();
#if HAS_GYRO
- /* do quaternion stuff here... */
+ ao_sample_rotate();
#endif
}
+#ifdef AO_FLIGHT_TEST
+ ao_sample_prev_tick = ao_sample_tick;
+#endif
ao_sample_data = ao_data_ring_next(ao_sample_data);
}
return !ao_preflight;
@@ -264,7 +346,7 @@ ao_sample_init(void)
ao_sample_pitch = 0;
ao_sample_yaw = 0;
ao_sample_roll = 0;
- ao_sample_angle = 0;
+ ao_sample_orient = 0;
#endif
ao_sample_data = ao_data_head;
ao_preflight = TRUE;
diff --git a/src/core/ao_sample.h b/src/core/ao_sample.h
index a2dac979..16d4c507 100644
--- a/src/core/ao_sample.h
+++ b/src/core/ao_sample.h
@@ -64,8 +64,6 @@
* for all further flight computations
*/
-#define GRAVITY 9.80665
-
/*
* Above this height, the baro sensor doesn't work
*/
@@ -115,9 +113,16 @@ extern __pdata int32_t ao_accel_scale; /* sensor to m/s² conversion */
extern __pdata accel_t ao_ground_accel_along;
extern __pdata accel_t ao_ground_accel_across;
extern __pdata accel_t ao_ground_accel_through;
-extern __pdata gyro_t ao_ground_pitch;
-extern __pdata gyro_t ao_ground_yaw;
-extern __pdata gyro_t ao_ground_roll;
+extern __pdata int32_t ao_ground_pitch; /* * 512 */
+extern __pdata int32_t ao_ground_yaw; /* * 512 */
+extern __pdata int32_t ao_ground_roll; /* * 512 */
+extern __pdata accel_t ao_sample_accel_along;
+extern __pdata accel_t ao_sample_accel_across;
+extern __pdata accel_t ao_sample_accel_through;
+extern __pdata gyro_t ao_sample_roll;
+extern __pdata gyro_t ao_sample_pitch;
+extern __pdata gyro_t ao_sample_yaw;
+extern __pdata angle_t ao_sample_orient;
#endif
void ao_sample_init(void);
@@ -136,8 +141,8 @@ uint8_t ao_sample(void);
extern __pdata int16_t ao_height; /* meters */
extern __pdata int16_t ao_speed; /* m/s * 16 */
extern __pdata int16_t ao_accel; /* m/s² * 16 */
-extern __pdata int16_t ao_max_height; /* max of ao_height */
-extern __pdata int16_t ao_avg_height; /* running average of height */
+extern __xdata int16_t ao_max_height; /* max of ao_height */
+extern __xdata int16_t ao_avg_height; /* running average of height */
extern __pdata int16_t ao_error_h;
extern __pdata int16_t ao_error_h_sq_avg;
diff --git a/src/core/ao_sample_profile.c b/src/core/ao_sample_profile.c
index 1d9ed414..d3743d12 100644
--- a/src/core/ao_sample_profile.c
+++ b/src/core/ao_sample_profile.c
@@ -20,11 +20,11 @@
#include <ao_task.h>
#ifndef AO_SAMPLE_PROFILE_LOW_PC
-#define AO_SAMPLE_PROFILE_LOW_PC 0x08000000
+#define AO_SAMPLE_PROFILE_LOW_PC 0x08002000
#endif
#ifndef AO_SAMPLE_PROFILE_HIGH_PC
-#define AO_SAMPLE_PROFILE_HIGH_PC (AO_SAMPLE_PROFILE_LOW_PC + 44 * 1024)
+#define AO_SAMPLE_PROFILE_HIGH_PC 0x0800f000
#endif
#ifndef AO_SAMPLE_PROFILE_SHIFT
diff --git a/src/core/ao_storage.c b/src/core/ao_storage.c
index adf7e4d4..6eddae7f 100644
--- a/src/core/ao_storage.c
+++ b/src/core/ao_storage.c
@@ -154,7 +154,7 @@ ao_storage_zapall(void) __reentrant
ao_cmd_white();
if (!ao_match_word("DoIt"))
return;
- for (pos = 0; pos < ao_storage_config; pos += ao_storage_block)
+ for (pos = 0; pos < ao_storage_log_max; pos += ao_storage_block)
ao_storage_erase(pos);
}
diff --git a/src/core/ao_storage.h b/src/core/ao_storage.h
index ea946399..6cc6fcb7 100644
--- a/src/core/ao_storage.h
+++ b/src/core/ao_storage.h
@@ -35,14 +35,22 @@ extern __pdata ao_pos_t ao_storage_total;
/* Block size - device is erased in these units. At least 256 bytes */
extern __pdata ao_pos_t ao_storage_block;
+#ifndef USE_STORAGE_CONFIG
+#define USE_STORAGE_CONFIG 1
+#endif
+
+#if USE_STORAGE_CONFIG
/* Byte offset of config block. Will be ao_storage_block bytes long */
extern __pdata ao_pos_t ao_storage_config;
+#define ao_storage_log_max ao_storage_config
+#else
+#define ao_storage_log_max ao_storage_total
+#endif
+
/* Storage unit size - device reads and writes must be within blocks of this size. Usually 256 bytes. */
extern __pdata uint16_t ao_storage_unit;
-#define AO_STORAGE_ERASE_LOG (ao_storage_config + AO_CONFIG_MAX_SIZE)
-
/* Initialize above values. Can only be called once the OS is running */
void
ao_storage_setup(void) __reentrant;
diff --git a/src/core/ao_task.c b/src/core/ao_task.c
index 0aad6508..bafb4943 100644
--- a/src/core/ao_task.c
+++ b/src/core/ao_task.c
@@ -109,6 +109,8 @@ ao_task_validate_alarm_queue(void)
ao_panic(3);
}
}
+ if (ao_task_alarm_tick != ao_list_first_entry(&alarm_queue, struct ao_task, alarm_queue)->alarm)
+ ao_panic(4);
}
#else
#define ao_task_validate_alarm_queue()
@@ -123,6 +125,7 @@ ao_task_to_alarm_queue(struct ao_task *task)
ao_list_for_each_entry(alarm, &alarm_queue, struct ao_task, alarm_queue) {
if ((int16_t) (alarm->alarm - task->alarm) >= 0) {
ao_list_insert(&task->alarm_queue, alarm->alarm_queue.prev);
+ ao_task_alarm_tick = ao_list_first_entry(&alarm_queue, struct ao_task, alarm_queue)->alarm;
ao_task_validate_alarm_queue();
return;
}
@@ -420,7 +423,7 @@ ao_sleep(__xdata void *wchan)
}
void
-ao_wakeup(__xdata void *wchan)
+ao_wakeup(__xdata void *wchan) __reentrant
{
#if HAS_TASK_QUEUE
struct ao_task *sleep, *next;
diff --git a/src/core/ao_task.h b/src/core/ao_task.h
index 1a4b5b6b..9c56b480 100644
--- a/src/core/ao_task.h
+++ b/src/core/ao_task.h
@@ -45,7 +45,10 @@ struct ao_task {
#endif
};
+#ifndef AO_NUM_TASKS
#define AO_NUM_TASKS 16 /* maximum number of tasks */
+#endif
+
#define AO_NO_TASK 0 /* no task id */
extern __xdata struct ao_task * __xdata ao_tasks[AO_NUM_TASKS];
@@ -67,7 +70,7 @@ ao_sleep(__xdata void *wchan);
/* Wake all tasks sleeping on wchan */
void
-ao_wakeup(__xdata void *wchan);
+ao_wakeup(__xdata void *wchan) __reentrant;
/* set an alarm to go off in 'delay' ticks */
void
diff --git a/src/core/ao_telemetry.c b/src/core/ao_telemetry.c
index c3bbfec5..c118d007 100644
--- a/src/core/ao_telemetry.c
+++ b/src/core/ao_telemetry.c
@@ -19,9 +19,16 @@
#include "ao_log.h"
#include "ao_product.h"
+#ifndef HAS_RDF
+#define HAS_RDF 1
+#endif
+
static __pdata uint16_t ao_telemetry_interval;
static __pdata uint8_t ao_rdf = 0;
+
+#if HAS_RDF
static __pdata uint16_t ao_rdf_time;
+#endif
#if HAS_APRS
static __pdata uint16_t ao_aprs_time;
@@ -33,6 +40,10 @@ static __pdata uint16_t ao_aprs_time;
#define AO_SEND_MEGA 1
#endif
+#if defined (TELEMETRUM_V_2_0)
+#define AO_SEND_METRUM 1
+#endif
+
#if defined(TELEMETRUM_V_0_1) || defined(TELEMETRUM_V_0_2) || defined(TELEMETRUM_V_1_0) || defined(TELEMETRUM_V_1_1) || defined(TELEBALLOON_V_1_1) || defined(TELEMETRUM_V_1_2)
#define AO_TELEMETRY_SENSOR AO_TELEMETRY_SENSOR_TELEMETRUM
#endif
@@ -104,6 +115,7 @@ ao_send_mega_sensor(void)
telemetry.generic.tick = packet->tick;
telemetry.generic.type = AO_TELEMETRY_MEGA_SENSOR;
+ telemetry.mega_sensor.orient = ao_sample_orient;
telemetry.mega_sensor.accel = ao_data_accel(packet);
telemetry.mega_sensor.pres = ao_data_pres(packet);
telemetry.mega_sensor.temp = ao_data_temp(packet);
@@ -164,6 +176,87 @@ ao_send_mega_data(void)
}
#endif /* AO_SEND_MEGA */
+#ifdef AO_SEND_METRUM
+/* Send telemetrum sensor packet */
+static void
+ao_send_metrum_sensor(void)
+{
+ __xdata struct ao_data *packet = (__xdata struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
+
+ telemetry.generic.tick = packet->tick;
+ telemetry.generic.type = AO_TELEMETRY_METRUM_SENSOR;
+
+ telemetry.metrum_sensor.state = ao_flight_state;
+ telemetry.metrum_sensor.accel = ao_data_accel(packet);
+ telemetry.metrum_sensor.pres = ao_data_pres(packet);
+ telemetry.metrum_sensor.temp = ao_data_temp(packet);
+
+ telemetry.metrum_sensor.acceleration = ao_accel;
+ telemetry.metrum_sensor.speed = ao_speed;
+ telemetry.metrum_sensor.height = ao_height;
+
+ telemetry.metrum_sensor.v_batt = packet->adc.v_batt;
+ telemetry.metrum_sensor.sense_a = packet->adc.sense_a;
+ telemetry.metrum_sensor.sense_m = packet->adc.sense_m;
+
+ ao_radio_send(&telemetry, sizeof (telemetry));
+}
+
+static __pdata int8_t ao_telemetry_metrum_data_max;
+static __pdata int8_t ao_telemetry_metrum_data_cur;
+
+/* Send telemetrum data packet */
+static void
+ao_send_metrum_data(void)
+{
+ if (--ao_telemetry_metrum_data_cur <= 0) {
+ __xdata struct ao_data *packet = (__xdata struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
+ uint8_t i;
+
+ telemetry.generic.tick = packet->tick;
+ telemetry.generic.type = AO_TELEMETRY_METRUM_DATA;
+
+ telemetry.metrum_data.ground_pres = ao_ground_pres;
+ telemetry.metrum_data.ground_accel = ao_ground_accel;
+ telemetry.metrum_data.accel_plus_g = ao_config.accel_plus_g;
+ telemetry.metrum_data.accel_minus_g = ao_config.accel_minus_g;
+
+ ao_radio_send(&telemetry, sizeof (telemetry));
+ ao_telemetry_metrum_data_cur = ao_telemetry_metrum_data_max;
+ }
+}
+#endif /* AO_SEND_METRUM */
+
+#ifdef AO_SEND_MINI
+
+static void
+ao_send_mini(void)
+{
+ __xdata struct ao_data *packet = (__xdata struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
+
+ telemetry.generic.tick = packet->tick;
+ telemetry.generic.type = AO_TELEMETRY_MINI;
+
+ telemetry.mini.state = ao_flight_state;
+
+ telemetry.mini.v_batt = packet->adc.v_batt;
+ telemetry.mini.sense_a = packet->adc.sense_a;
+ telemetry.mini.sense_m = packet->adc.sense_m;
+
+ telemetry.mini.pres = ao_data_pres(packet);
+ telemetry.mini.temp = ao_data_temp(packet);
+
+ telemetry.mini.acceleration = ao_accel;
+ telemetry.mini.speed = ao_speed;
+ telemetry.mini.height = ao_height;
+
+ telemetry.mini.ground_pres = ao_ground_pres;
+
+ ao_radio_send(&telemetry, sizeof (telemetry));
+}
+
+#endif /* AO_SEND_MINI */
+
#ifdef AO_SEND_ALL_BARO
static uint8_t ao_baro_sample;
@@ -198,7 +291,11 @@ ao_send_configuration(void)
{
telemetry.generic.type = AO_TELEMETRY_CONFIGURATION;
telemetry.configuration.device = AO_idProduct_NUMBER;
+#if HAS_LOG
telemetry.configuration.flight = ao_log_full() ? 0 : ao_flight_number;
+#else
+ telemetry.configuration.flight = ao_flight_number;
+#endif
telemetry.configuration.config_major = AO_CONFIG_MAJOR;
telemetry.configuration.config_minor = AO_CONFIG_MINOR;
telemetry.configuration.apogee_delay = ao_config.apogee_delay;
@@ -295,12 +392,14 @@ ao_telemetry(void)
for (;;) {
while (ao_telemetry_interval == 0)
ao_sleep(&telemetry);
- time = ao_rdf_time = ao_time();
+ time = ao_time();
+#if HAS_RDF
+ ao_rdf_time = time;
+#endif
#if HAS_APRS
ao_aprs_time = time;
#endif
while (ao_telemetry_interval) {
-
#if HAS_APRS
if (!(ao_config.radio_enable & AO_RADIO_DISABLE_TELEMETRY))
#endif
@@ -308,14 +407,23 @@ ao_telemetry(void)
#ifdef AO_SEND_ALL_BARO
ao_send_baro();
#endif
+
#if HAS_FLIGHT
-#ifdef AO_SEND_MEGA
+# ifdef AO_SEND_MEGA
ao_send_mega_sensor();
ao_send_mega_data();
-#else
+# endif
+# ifdef AO_SEND_METRUM
+ ao_send_metrum_sensor();
+ ao_send_metrum_data();
+# endif
+# ifdef AO_SEND_MINI
+ ao_send_mini();
+# endif
+# ifdef AO_TELEMETRY_SENSOR
ao_send_sensor();
-#endif
-#endif
+# endif
+#endif /* HAS_FLIGHT */
#if HAS_COMPANION
if (ao_companion_running)
@@ -328,23 +436,25 @@ ao_telemetry(void)
#endif
}
#ifndef AO_SEND_ALL_BARO
+#if HAS_RDF
if (ao_rdf &&
#if HAS_APRS
!(ao_config.radio_enable & AO_RADIO_DISABLE_RDF) &&
-#endif
+#endif /* HAS_APRS */
(int16_t) (ao_time() - ao_rdf_time) >= 0)
{
#if HAS_IGNITE_REPORT
uint8_t c;
-#endif
+#endif /* HAS_IGNITE_REPORT */
ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS;
#if HAS_IGNITE_REPORT
if (ao_flight_state == ao_flight_pad && (c = ao_report_igniter()))
ao_radio_continuity(c);
else
-#endif
+#endif /* HAS_IGNITE_REPORT*/
ao_radio_rdf();
}
+#endif /* HAS_RDF */
#if HAS_APRS
if (ao_config.aprs_interval != 0 &&
(int16_t) (ao_time() - ao_aprs_time) >= 0)
@@ -352,8 +462,8 @@ ao_telemetry(void)
ao_aprs_time = ao_time() + AO_SEC_TO_TICKS(ao_config.aprs_interval);
ao_aprs_send();
}
-#endif
-#endif
+#endif /* HAS_APRS */
+#endif /* !AO_SEND_ALL_BARO */
time += ao_telemetry_interval;
delay = time - ao_time();
if (delay > 0) {
@@ -383,6 +493,12 @@ ao_telemetry_set_interval(uint16_t interval)
cur++;
ao_telemetry_mega_data_cur = cur;
#endif
+#if AO_SEND_METRUM
+ ao_telemetry_metrum_data_max = AO_SEC_TO_TICKS(1) / interval;
+ if (ao_telemetry_metrum_data_max > cur)
+ cur++;
+ ao_telemetry_metrum_data_cur = cur;
+#endif
#if HAS_COMPANION
if (!ao_companion_setup.update_period)
@@ -411,6 +527,7 @@ ao_telemetry_set_interval(uint16_t interval)
ao_wakeup(&telemetry);
}
+#if HAS_RDF
void
ao_rdf_set(uint8_t rdf)
{
@@ -421,6 +538,7 @@ ao_rdf_set(uint8_t rdf)
ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS;
}
}
+#endif
__xdata struct ao_task ao_telemetry_task;
diff --git a/src/core/ao_telemetry.h b/src/core/ao_telemetry.h
index f2d201de..237a35ab 100644
--- a/src/core/ao_telemetry.h
+++ b/src/core/ao_telemetry.h
@@ -162,7 +162,7 @@ struct ao_telemetry_mega_sensor {
uint16_t tick; /* 2 */
uint8_t type; /* 4 */
- uint8_t pad5; /* 5 */
+ uint8_t orient; /* 5 angle from vertical */
int16_t accel; /* 6 Z axis */
int32_t pres; /* 8 Pa * 10 */
@@ -207,6 +207,72 @@ struct ao_telemetry_mega_data {
};
+#define AO_TELEMETRY_METRUM_SENSOR 0x0A
+
+struct ao_telemetry_metrum_sensor {
+ uint16_t serial; /* 0 */
+ uint16_t tick; /* 2 */
+ uint8_t type; /* 4 */
+
+ uint8_t state; /* 5 flight state */
+ int16_t accel; /* 6 Z axis */
+
+ int32_t pres; /* 8 Pa * 10 */
+ int16_t temp; /* 12 °C * 100 */
+
+ int16_t acceleration; /* 14 m/s² * 16 */
+ int16_t speed; /* 16 m/s * 16 */
+ int16_t height; /* 18 m */
+
+ int16_t v_batt; /* 20 battery voltage */
+ int16_t sense_a; /* 22 apogee continuity sense */
+ int16_t sense_m; /* 24 main continuity sense */
+
+ uint8_t pad[6]; /* 26 */
+ /* 32 */
+};
+
+#define AO_TELEMETRY_METRUM_DATA 0x0B
+
+struct ao_telemetry_metrum_data {
+ uint16_t serial; /* 0 */
+ uint16_t tick; /* 2 */
+ uint8_t type; /* 4 */
+
+ int32_t ground_pres; /* 8 average pres on pad */
+ int16_t ground_accel; /* 12 average accel on pad */
+ int16_t accel_plus_g; /* 14 accel calibration at +1g */
+ int16_t accel_minus_g; /* 16 accel calibration at -1g */
+
+ uint8_t pad[14]; /* 18 */
+ /* 32 */
+};
+
+#define AO_TELEMETRY_MINI 0x10
+
+struct ao_telemetry_mini {
+ uint16_t serial; /* 0 */
+ uint16_t tick; /* 2 */
+ uint8_t type; /* 4 */
+
+ uint8_t state; /* 5 flight state */
+ int16_t v_batt; /* 6 battery voltage */
+ int16_t sense_a; /* 8 apogee continuity */
+ int16_t sense_m; /* 10 main continuity */
+
+ int32_t pres; /* 12 Pa * 10 */
+ int16_t temp; /* 16 °C * 100 */
+
+ int16_t acceleration; /* 18 m/s² * 16 */
+ int16_t speed; /* 20 m/s * 16 */
+ int16_t height; /* 22 m */
+
+ int32_t ground_pres; /* 24 average pres on pad */
+
+ int32_t pad28; /* 28 */
+ /* 32 */
+};
+
/* #define AO_SEND_ALL_BARO */
#define AO_TELEMETRY_BARO 0x80
@@ -240,6 +306,9 @@ union ao_telemetry_all {
struct ao_telemetry_companion companion;
struct ao_telemetry_mega_sensor mega_sensor;
struct ao_telemetry_mega_data mega_data;
+ struct ao_telemetry_metrum_sensor metrum_sensor;
+ struct ao_telemetry_metrum_data metrum_data;
+ struct ao_telemetry_mini mini;
struct ao_telemetry_baro baro;
};
diff --git a/src/core/ao_usb.h b/src/core/ao_usb.h
index 6bc77608..35e64e65 100644
--- a/src/core/ao_usb.h
+++ b/src/core/ao_usb.h
@@ -102,8 +102,11 @@ extern __code __at (0x00aa) uint8_t ao_usb_descriptors [];
#define AO_USB_INT_EP 1
#define AO_USB_INT_SIZE 8
+#ifndef AO_USB_OUT_EP
#define AO_USB_OUT_EP 4
#define AO_USB_IN_EP 5
+#endif
+
/*
* USB bulk packets can only come in 8, 16, 32 and 64
* byte sizes, so we'll use 64 for everything