2 * Copyright © 2009 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
29 #define GRAVITY 9.80665
34 #define AO_DATA_RING 64
35 #define ao_data_ring_next(n) (((n) + 1) & (AO_DATA_RING - 1))
36 #define ao_data_ring_prev(n) (((n) - 1) & (AO_DATA_RING - 1))
38 #define AO_GPS_NEW_DATA 1
39 #define AO_GPS_NEW_TRACKING 2
43 #if !defined(TELEMEGA) && !defined(TELEMETRUM_V2) && !defined(EASYMINI)
44 #define TELEMETRUM_V1 1
48 #define AO_ADC_NUM_SENSE 6
54 #define AO_CONFIG_MAX_SIZE 1024
55 #define AO_MMA655X_INVERT 0
58 int16_t sense[AO_ADC_NUM_SENSE];
66 #define AO_ADC_NUM_SENSE 2
69 #define AO_MMA655X_INVERT 0
71 #define AO_CONFIG_MAX_SIZE 1024
82 #define AO_ADC_NUM_SENSE 2
85 #define AO_CONFIG_MAX_SIZE 1024
96 * One set of samples read from the A/D converter
99 int16_t accel; /* accelerometer */
100 int16_t pres; /* pressure sensor */
101 int16_t pres_real; /* unclipped */
102 int16_t temp; /* temperature sensor */
103 int16_t v_batt; /* battery voltage */
104 int16_t sense_d; /* drogue continuity sense */
105 int16_t sense_m; /* main continuity sense */
110 #define HAS_ACCEL_REF 0
128 #include <ao_telemetry.h>
129 #include <ao_sample.h>
130 #include <ao_fake_flight.h>
134 struct ao_telemetry_location ao_gps_first;
135 struct ao_telemetry_location ao_gps_prev;
136 struct ao_telemetry_location ao_gps_static;
138 struct ao_telemetry_satellite ao_gps_tracking;
140 static inline double sqr(double a) { return a * a; }
143 cc_great_circle (double start_lat, double start_lon,
144 double end_lat, double end_lon,
145 double *dist, double *bearing)
147 const double rad = M_PI / 180;
148 const double earth_radius = 6371.2 * 1000; /* in meters */
149 double lat1 = rad * start_lat;
150 double lon1 = rad * -start_lon;
151 double lat2 = rad * end_lat;
152 double lon2 = rad * -end_lon;
154 // double d_lat = lat2 - lat1;
155 double d_lon = lon2 - lon1;
157 /* From http://en.wikipedia.org/wiki/Great-circle_distance */
158 double vdn = sqrt(sqr(cos(lat2) * sin(d_lon)) +
159 sqr(cos(lat1) * sin(lat2) -
160 sin(lat1) * cos(lat2) * cos(d_lon)));
161 double vdd = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(d_lon);
162 double d = atan2(vdn,vdd);
165 if (cos(lat1) < 1e-20) {
174 course = acos((sin(lat2)-sin(lat1)*cos(d)) /
176 if (sin(lon2-lon1) > 0)
177 course = 2 * M_PI-course;
179 *dist = d * earth_radius;
180 *bearing = course * 180/M_PI;
184 ao_distance_from_pad(void)
186 double dist, bearing;
190 cc_great_circle(ao_gps_first.latitude / 1e7,
191 ao_gps_first.longitude / 1e7,
192 ao_gps_static.latitude / 1e7,
193 ao_gps_static.longitude / 1e7,
201 double dist, bearing;
205 if (ao_gps_count < 2)
208 cc_great_circle(ao_gps_prev.latitude / 1e7,
209 ao_gps_prev.longitude / 1e7,
210 ao_gps_static.latitude / 1e7,
211 ao_gps_static.longitude / 1e7,
213 height = AO_TELEMETRY_LOCATION_ALTITUDE(&ao_gps_static) - AO_TELEMETRY_LOCATION_ALTITUDE(&ao_gps_prev);
215 angle = atan2(dist, height);
216 return angle * 180/M_PI;
220 #define to_fix16(x) ((int16_t) ((x) * 65536.0 + 0.5))
221 #define to_fix32(x) ((int32_t) ((x) * 65536.0 + 0.5))
222 #define from_fix(x) ((x) >> 16)
224 #define ACCEL_NOSE_UP (ao_accel_2g >> 2)
229 volatile struct ao_data ao_data_ring[AO_DATA_RING];
230 volatile uint8_t ao_data_head;
234 #define ao_led_off(l)
235 #define ao_timer_set_adc_interval(i)
236 #define ao_wakeup(wchan) ao_dump_state()
237 #define ao_cmd_register(c)
238 #define ao_usb_disable()
239 #define ao_telemetry_set_interval(x)
240 #define ao_rdf_set(rdf)
241 #define ao_packet_slave_start()
242 #define ao_packet_slave_stop()
246 ao_igniter_drogue = 0,
250 struct ao_data ao_data_static;
259 static ao_k_t ao_k_height;
264 return ao_data_static.tick;
268 ao_delay(int16_t interval)
274 ao_ignite(enum ao_igniter igniter)
276 double time = (double) (ao_data_static.tick + tick_offset) / 100;
278 if (igniter == ao_igniter_drogue) {
280 drogue_height = ao_k_height >> 16;
283 main_height = ao_k_height >> 16;
291 #define ao_add_task(t,f,n) ((void) (t))
293 #define ao_log_start()
294 #define ao_log_stop()
296 #define AO_MS_TO_TICKS(ms) ((ms) / 10)
297 #define AO_SEC_TO_TICKS(s) ((s) * 100)
299 #define AO_FLIGHT_TEST
303 struct ao_eeprom *eeprom;
304 uint32_t eeprom_offset;
311 double emulator_error_max = 4;
312 double emulator_height_error_max = 20; /* noise in the baro sensor */
318 ao_sleep(void *wchan);
320 const char * const ao_state_names[] = {
321 "startup", "idle", "pad", "boost", "fast",
322 "coast", "drogue", "main", "landed", "invalid"
330 #define ao_xmemcpy(d,s,c) memcpy(d,s,c)
331 #define ao_xmemset(d,v,c) memset(d,v,c)
332 #define ao_xmemcmp(d,s,c) memcmp(d,s,c)
334 #define AO_NEED_ALTITUDE_TO_PRES 1
335 #if TELEMEGA || TELEMETRUM_V2 || EASYMINI
336 #include "ao_convert_pa.c"
337 #include <ao_ms5607.h>
338 struct ao_ms5607_prom ao_ms5607_prom;
339 #include "ao_ms5607_convert.c"
341 #define AO_PYRO_NUM 4
345 #include "ao_convert.c"
348 #include <ao_config.h>
349 #include <ao_fake_flight.h>
350 #include <ao_eeprom_read.h>
353 #define ao_config_get()
355 struct ao_config ao_config;
357 #define DATA_TO_XDATA(x) (x)
360 extern int16_t ao_ground_accel, ao_flight_accel;
361 extern int16_t ao_accel_2g;
363 typedef int16_t accel_t;
365 uint16_t ao_serial_number;
366 uint16_t ao_flight_number;
368 extern uint16_t ao_sample_tick;
370 extern alt_t ao_sample_height;
371 extern accel_t ao_sample_accel;
372 extern int32_t ao_accel_scale;
373 extern alt_t ao_ground_height;
374 extern alt_t ao_sample_alt;
376 double ao_sample_qangle;
378 int ao_sample_prev_tick;
381 #include "ao_kalman.c"
383 #include "ao_sample.c"
384 #include "ao_flight.c"
386 #define AO_PYRO_NUM 4
395 #include "ao_eeprom_read.c"
396 #include "ao_eeprom_read_old.c"
398 #define to_double(f) ((f) / 65536.0)
400 static int ao_records_read = 0;
401 static int ao_eof_read = 0;
403 static int ao_flight_ground_accel;
405 static int ao_flight_started = 0;
408 static struct ao_mpu6000_sample ao_ground_mpu6000;
412 int ao_error_h_sq_avg;
427 fwrite(&ao_data_static, sizeof (ao_data_static), 1, fake_out);
432 uint16(uint8_t *bytes, int off)
434 return (uint16_t) bytes[off] | (((uint16_t) bytes[off+1]) << 8);
438 int16(uint8_t *bytes, int off)
440 return (int16_t) uint16(bytes, off);
444 uint32(uint8_t *bytes, int off)
446 return (uint32_t) bytes[off] | (((uint32_t) bytes[off+1]) << 8) |
447 (((uint32_t) bytes[off+2]) << 16) |
448 (((uint32_t) bytes[off+3]) << 24);
452 int32(uint8_t *bytes, int off)
454 return (int32_t) uint32(bytes, off);
458 uint24(uint8_t *bytes, int off)
460 return (uint32_t) bytes[off] | (((uint32_t) bytes[off+1]) << 8) |
461 (((uint32_t) bytes[off+2]) << 16);
465 int24(uint8_t *bytes, int off)
467 return (int32_t) uint24(bytes, off);
470 static int log_format;
473 ao_sleep(void *wchan)
479 if (ao_records_read > 2 && ao_records_read < 500)
482 ao_data_static.mpu6000 = ao_ground_mpu6000;
485 ao_data_static.adc.accel = ao_flight_ground_accel;
494 struct ao_log_mega *log_mega;
497 struct ao_log_metrum *log_metrum;
500 struct ao_log_mini *log_mini;
503 struct ao_log_record *log_record;
506 if (eeprom_offset >= eeprom->len) {
507 if (++ao_eof_read >= 100) {
510 ao_data_static.tick += 10;
514 switch (eeprom->log_format) {
516 case AO_LOG_FORMAT_TELEMEGA_OLD:
517 case AO_LOG_FORMAT_TELEMEGA:
518 log_mega = (struct ao_log_mega *) &eeprom->data[eeprom_offset];
519 eeprom_offset += sizeof (*log_mega);
520 switch (log_mega->type) {
522 ao_flight_number = log_mega->u.flight.flight;
523 ao_flight_ground_accel = log_mega->u.flight.ground_accel;
524 ao_flight_started = 1;
525 ao_ground_pres = log_mega->u.flight.ground_pres;
526 ao_ground_height = ao_pa_to_altitude(ao_ground_pres);
527 ao_ground_accel_along = log_mega->u.flight.ground_accel_along;
528 ao_ground_accel_across = log_mega->u.flight.ground_accel_across;
529 ao_ground_accel_through = log_mega->u.flight.ground_accel_through;
530 ao_ground_roll = log_mega->u.flight.ground_roll;
531 ao_ground_pitch = log_mega->u.flight.ground_pitch;
532 ao_ground_yaw = log_mega->u.flight.ground_yaw;
533 ao_ground_mpu6000.accel_x = ao_ground_accel_across;
534 ao_ground_mpu6000.accel_y = ao_ground_accel_along;
535 ao_ground_mpu6000.accel_z = ao_ground_accel_through;
536 ao_ground_mpu6000.gyro_x = ao_ground_pitch >> 9;
537 ao_ground_mpu6000.gyro_y = ao_ground_roll >> 9;
538 ao_ground_mpu6000.gyro_z = ao_ground_yaw >> 9;
543 ao_data_static.tick = log_mega->tick;
544 ao_data_static.ms5607_raw.pres = log_mega->u.sensor.pres;
545 ao_data_static.ms5607_raw.temp = log_mega->u.sensor.temp;
546 ao_data_static.mpu6000.accel_x = log_mega->u.sensor.accel_x;
547 ao_data_static.mpu6000.accel_y = log_mega->u.sensor.accel_y;
548 ao_data_static.mpu6000.accel_z = log_mega->u.sensor.accel_z;
549 ao_data_static.mpu6000.gyro_x = log_mega->u.sensor.gyro_x;
550 ao_data_static.mpu6000.gyro_y = log_mega->u.sensor.gyro_y;
551 ao_data_static.mpu6000.gyro_z = log_mega->u.sensor.gyro_z;
552 ao_data_static.hmc5883.x = log_mega->u.sensor.mag_x;
553 ao_data_static.hmc5883.y = log_mega->u.sensor.mag_y;
554 ao_data_static.hmc5883.z = log_mega->u.sensor.mag_z;
555 ao_data_static.mma655x = log_mega->u.sensor.accel;
556 if (ao_config.pad_orientation != AO_PAD_ORIENTATION_ANTENNA_UP)
557 ao_data_static.mma655x = ao_data_accel_invert(ao_data_static.mma655x);
561 case AO_LOG_TEMP_VOLT:
563 case AO_LOG_GPS_TIME:
564 ao_gps_prev = ao_gps_static;
565 ao_gps_static.tick = log_mega->tick;
566 ao_gps_static.latitude = log_mega->u.gps.latitude;
567 ao_gps_static.longitude = log_mega->u.gps.longitude;
569 int16_t altitude_low = log_mega->u.gps.altitude_low;
570 int16_t altitude_high = log_mega->u.gps.altitude_high;
571 int32_t altitude = altitude_low | ((int32_t) altitude_high << 16);
573 AO_TELEMETRY_LOCATION_SET_ALTITUDE(&ao_gps_static, altitude);
575 ao_gps_static.flags = log_mega->u.gps.flags;
577 ao_gps_first = ao_gps_static;
586 case AO_LOG_FORMAT_TELEMETRUM:
587 log_metrum = (struct ao_log_metrum *) &eeprom->data[eeprom_offset];
588 eeprom_offset += sizeof (*log_metrum);
589 switch (log_metrum->type) {
591 ao_flight_started = 1;
592 ao_flight_number = log_metrum->u.flight.flight;
593 ao_flight_ground_accel = log_metrum->u.flight.ground_accel;
594 ao_ground_pres = log_metrum->u.flight.ground_pres;
595 ao_ground_height = ao_pa_to_altitude(ao_ground_pres);
598 ao_data_static.tick = log_metrum->tick;
599 ao_data_static.ms5607_raw.pres = log_metrum->u.sensor.pres;
600 ao_data_static.ms5607_raw.temp = log_metrum->u.sensor.temp;
601 ao_data_static.mma655x = log_metrum->u.sensor.accel;
609 case AO_LOG_FORMAT_EASYMINI1:
610 case AO_LOG_FORMAT_EASYMINI2:
611 case AO_LOG_FORMAT_TELEMINI3:
612 log_mini = (struct ao_log_mini *) &eeprom->data[eeprom_offset];
613 eeprom_offset += sizeof (*log_mini);
614 switch (log_mini->type) {
616 ao_flight_started = 1;
617 ao_flight_number = log_mini->u.flight.flight;
618 ao_ground_pres = log_mini->u.flight.ground_pres;
619 ao_ground_height = ao_pa_to_altitude(ao_ground_pres);
622 ao_data_static.tick = log_mini->tick;
623 ao_data_static.ms5607_raw.pres = int24(log_mini->u.sensor.pres, 0);
624 ao_data_static.ms5607_raw.temp = int24(log_mini->u.sensor.temp, 0);
632 case AO_LOG_FORMAT_FULL:
633 case AO_LOG_FORMAT_TINY:
634 log_record = (struct ao_log_record *) &eeprom->data[eeprom_offset];
635 eeprom_offset += sizeof (*log_record);
636 switch (log_record->type) {
638 ao_flight_started = 1;
639 ao_flight_ground_accel = log_record->u.flight.ground_accel;
640 ao_flight_number = log_record->u.flight.flight;
643 case 'P': /* ancient telemini */
644 ao_data_static.tick = log_record->tick;
645 ao_data_static.adc.accel = log_record->u.sensor.accel;
646 ao_data_static.adc.pres_real = log_record->u.sensor.pres;
647 ao_data_static.adc.pres = log_record->u.sensor.pres;
651 case AO_LOG_TEMP_VOLT:
652 ao_data_static.tick = log_record->tick;;
653 ao_data_static.adc.temp = log_record->u.temp_volt.temp;
654 ao_data_static.adc.v_batt = log_record->u.temp_volt.v_batt;
660 fprintf (stderr, "invalid log format %d\n", log_format);
665 #define COUNTS_PER_G 264.8
672 static const struct option options[] = {
673 { .name = "summary", .has_arg = 0, .val = 's' },
674 { .name = "debug", .has_arg = 0, .val = 'd' },
675 { .name = "info", .has_arg = 1, .val = 'i' },
676 { .name = "out", .has_arg = 1, .val = 'o' },
680 void run_flight_fixed(char *name, FILE *f, int summary, char *info)
683 struct ao_fake_calib fake_calib;
685 emulator_name = name;
687 emulator_info = info;
688 ao_summary = summary;
693 eeprom = ao_eeprom_read(f);
695 eeprom = ao_eeprom_read_old(f);
698 fprintf(stderr, "%s: load failed\n", name);
702 ao_ms5607_prom = eeprom->ms5607_prom;
704 ao_config = eeprom->config;
705 ao_serial_number = eeprom->serial_number;
706 log_format = eeprom->log_format;
708 fprintf(fake_out, "F %x %x\n",
709 (int) sizeof (struct ao_fake_calib),
710 (int) sizeof (struct ao_data));
712 fake_calib.major = AO_FAKE_CALIB_MAJOR;
713 fake_calib.minor = AO_FAKE_CALIB_MINOR;
714 fake_calib.accel_plus_g = eeprom->config.accel_plus_g;
715 fake_calib.accel_minus_g = eeprom->config.accel_minus_g;
716 fake_calib.accel_zero_along = eeprom->config.accel_zero_along;
717 fake_calib.accel_zero_across = eeprom->config.accel_zero_across;
718 fake_calib.accel_zero_through = eeprom->config.accel_zero_through;
719 fake_calib.ms5607_prom = eeprom->ms5607_prom;
720 fwrite(&fake_calib, sizeof (fake_calib), 1, fake_out);
726 main (int argc, char **argv)
732 char *out_name = NULL;
739 while ((c = getopt_long(argc, argv, "sdi:o:", options, NULL)) != -1) {
756 fake_out = fopen(out_name, "w");
767 run_flight_fixed("<stdin>", stdin, summary, info);
769 for (i = optind; i < argc; i++) {
770 FILE *f = fopen(argv[i], "r");
775 run_flight_fixed(argv[i], f, summary, info);