test/ao_flight_test: Show recorded pyro firing
[fw/altos] / src / test / ao_flight_test.c
1 /*
2  * Copyright © 2009 Keith Packard <keithp@keithp.com>
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 #define _GNU_SOURCE
20
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stddef.h>
25 #include <string.h>
26 #include <getopt.h>
27 #include <math.h>
28
29 #define GRAVITY 9.80665
30
31 #define AO_HERTZ        100
32
33 #define HAS_ADC 1
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))
37
38 #if 0
39 #define AO_M_TO_HEIGHT(m)       ((int16_t) (m))
40 #define AO_MS_TO_SPEED(ms)      ((int16_t) ((ms) * 16))
41 #define AO_MSS_TO_ACCEL(mss)    ((int16_t) ((mss) * 16))
42 #endif
43
44 #define AO_GPS_NEW_DATA         1
45 #define AO_GPS_NEW_TRACKING     2
46
47 int ao_gps_new;
48
49 #if !defined(TELEMEGA) && !defined(TELEMETRUM_V2) && !defined(EASYMINI)
50 #define TELEMETRUM_V1 1
51 #endif
52
53 #if TELEMEGA
54 #define AO_ADC_NUM_SENSE        6
55 #define HAS_MS5607              1
56 #define HAS_MPU6000             1
57 #define HAS_MMA655X             1
58 #define HAS_HMC5883             1
59 #define HAS_BEEP                1
60 #define AO_CONFIG_MAX_SIZE      1024
61 #define AO_MMA655X_INVERT       0
62
63 struct ao_adc {
64         int16_t                 sense[AO_ADC_NUM_SENSE];
65         int16_t                 v_batt;
66         int16_t                 v_pbatt;
67         int16_t                 temp;
68 };
69 #endif
70
71 #if TELEMETRUM_V2
72 #define AO_ADC_NUM_SENSE        2
73 #define HAS_MS5607              1
74 #define HAS_MMA655X             1
75 #define AO_MMA655X_INVERT       0
76 #define HAS_BEEP                1
77 #define AO_CONFIG_MAX_SIZE      1024
78
79 struct ao_adc {
80         int16_t                 sense_a;
81         int16_t                 sense_m;
82         int16_t                 v_batt;
83         int16_t                 temp;
84 };
85 #endif
86
87 #if EASYMINI
88 #define AO_ADC_NUM_SENSE        2
89 #define HAS_MS5607              1
90 #define HAS_BEEP                1
91 #define AO_CONFIG_MAX_SIZE      1024
92
93 struct ao_adc {
94         int16_t                 sense_a;
95         int16_t                 sense_m;
96         int16_t                 v_batt;
97 };
98 #endif
99
100 #if TELEMETRUM_V1
101 /*
102  * One set of samples read from the A/D converter
103  */
104 struct ao_adc {
105         int16_t         accel;          /* accelerometer */
106         int16_t         pres;           /* pressure sensor */
107         int16_t         pres_real;      /* unclipped */
108         int16_t         temp;           /* temperature sensor */
109         int16_t         v_batt;         /* battery voltage */
110         int16_t         sense_d;        /* drogue continuity sense */
111         int16_t         sense_m;        /* main continuity sense */
112 };
113
114 #ifndef HAS_ACCEL
115 #define HAS_ACCEL 1
116 #define HAS_ACCEL_REF 0
117 #endif
118
119 #endif
120
121 #define __pdata
122 #define __data
123 #define __xdata
124 #define __code
125 #define __reentrant
126
127 #define HAS_FLIGHT 1
128 #define HAS_IGNITE 1
129 #define HAS_USB 1
130 #define HAS_GPS 1
131
132 #include <ao_data.h>
133 #include <ao_log.h>
134 #include <ao_telemetry.h>
135 #include <ao_sample.h>
136
137 #if TELEMEGA
138 int ao_gps_count;
139 struct ao_telemetry_location ao_gps_first;
140 struct ao_telemetry_location ao_gps_prev;
141 struct ao_telemetry_location ao_gps_static;
142
143 struct ao_telemetry_satellite ao_gps_tracking;
144
145 static inline double sqr(double a) { return a * a; }
146
147 void
148 cc_great_circle (double start_lat, double start_lon,
149                  double end_lat, double end_lon,
150                  double *dist, double *bearing)
151 {
152         const double rad = M_PI / 180;
153         const double earth_radius = 6371.2 * 1000;      /* in meters */
154         double lat1 = rad * start_lat;
155         double lon1 = rad * -start_lon;
156         double lat2 = rad * end_lat;
157         double lon2 = rad * -end_lon;
158
159 //      double d_lat = lat2 - lat1;
160         double d_lon = lon2 - lon1;
161
162         /* From http://en.wikipedia.org/wiki/Great-circle_distance */
163         double vdn = sqrt(sqr(cos(lat2) * sin(d_lon)) +
164                           sqr(cos(lat1) * sin(lat2) -
165                               sin(lat1) * cos(lat2) * cos(d_lon)));
166         double vdd = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(d_lon);
167         double d = atan2(vdn,vdd);
168         double course;
169
170         if (cos(lat1) < 1e-20) {
171                 if (lat1 > 0)
172                         course = M_PI;
173                 else
174                         course = -M_PI;
175         } else {
176                 if (d < 1e-10)
177                         course = 0;
178                 else
179                         course = acos((sin(lat2)-sin(lat1)*cos(d)) /
180                                       (sin(d)*cos(lat1)));
181                 if (sin(lon2-lon1) > 0)
182                         course = 2 * M_PI-course;
183         }
184         *dist = d * earth_radius;
185         *bearing = course * 180/M_PI;
186 }
187
188 double
189 ao_distance_from_pad(void)
190 {
191         double  dist, bearing;
192         if (!ao_gps_count)
193                 return 0;
194
195         cc_great_circle(ao_gps_first.latitude / 1e7,
196                         ao_gps_first.longitude / 1e7,
197                         ao_gps_static.latitude / 1e7,
198                         ao_gps_static.longitude / 1e7,
199                         &dist, &bearing);
200         return dist;
201 }
202
203 double
204 ao_gps_angle(void)
205 {
206         double  dist, bearing;
207         double  height;
208         double  angle;
209
210         if (ao_gps_count < 2)
211                 return 0;
212
213         cc_great_circle(ao_gps_prev.latitude / 1e7,
214                         ao_gps_prev.longitude / 1e7,
215                         ao_gps_static.latitude / 1e7,
216                         ao_gps_static.longitude / 1e7,
217                         &dist, &bearing);
218         height = AO_TELEMETRY_LOCATION_ALTITUDE(&ao_gps_static) - AO_TELEMETRY_LOCATION_ALTITUDE(&ao_gps_prev);
219
220         angle = atan2(dist, height);
221         return angle * 180/M_PI;
222 }
223 #endif
224
225 #define to_fix16(x) ((int16_t) ((x) * 65536.0 + 0.5))
226 #define to_fix32(x) ((int32_t) ((x) * 65536.0 + 0.5))
227 #define from_fix(x)     ((x) >> 16)
228
229 #define ACCEL_NOSE_UP   (ao_accel_2g >> 2)
230
231 extern enum ao_flight_state ao_flight_state;
232
233 #define FALSE 0
234 #define TRUE 1
235
236 volatile struct ao_data ao_data_ring[AO_DATA_RING];
237 volatile uint8_t ao_data_head;
238 int     ao_summary = 0;
239
240 #define ao_led_on(l)
241 #define ao_led_off(l)
242 #define ao_timer_set_adc_interval(i)
243 #define ao_wakeup(wchan) ao_dump_state()
244 #define ao_cmd_register(c)
245 #define ao_usb_disable()
246 #define ao_telemetry_set_interval(x)
247 #define ao_rdf_set(rdf)
248 #define ao_packet_slave_start()
249 #define ao_packet_slave_stop()
250 #define flush()
251
252 enum ao_igniter {
253         ao_igniter_drogue = 0,
254         ao_igniter_main = 1
255 };
256
257 struct ao_data ao_data_static;
258
259 int     drogue_height;
260 double  drogue_time;
261 int     main_height;
262 double  main_time;
263
264 int     tick_offset;
265
266 static ao_k_t   ao_k_height;
267 static double   simple_speed;
268
269 int16_t
270 ao_time(void)
271 {
272         return ao_data_static.tick;
273 }
274
275 void
276 ao_delay(int16_t interval)
277 {
278         return;
279 }
280
281 void
282 ao_ignite(enum ao_igniter igniter)
283 {
284         double time = (double) (ao_data_static.tick + tick_offset) / 100;
285
286         if (igniter == ao_igniter_drogue) {
287                 drogue_time = time;
288                 drogue_height = ao_k_height >> 16;
289         } else {
290                 main_time = time;
291                 main_height = ao_k_height >> 16;
292         }
293 }
294
295 struct ao_task {
296         int dummy;
297 };
298
299 #define ao_add_task(t,f,n) ((void) (t))
300
301 #define ao_log_start()
302 #define ao_log_stop()
303
304 #define AO_MS_TO_TICKS(ms)      ((ms) / 10)
305 #define AO_SEC_TO_TICKS(s)      ((s) * 100)
306
307 #define AO_FLIGHT_TEST
308
309 int     ao_flight_debug;
310
311 struct ao_eeprom        *eeprom;
312 uint32_t                eeprom_offset;
313
314 FILE *emulator_in;
315 char *emulator_app;
316 char *emulator_name;
317 char *emulator_info;
318 double emulator_error_max = 4;
319 double emulator_height_error_max = 20;  /* noise in the baro sensor */
320
321 void
322 ao_dump_state(void);
323
324 void
325 ao_sleep(void *wchan);
326
327 const char * const ao_state_names[] = {
328         "startup", "idle", "pad", "boost", "fast",
329         "coast", "drogue", "main", "landed", "invalid"
330 };
331
332 struct ao_cmds {
333         void            (*func)(void);
334         const char      *help;
335 };
336
337 #define ao_xmemcpy(d,s,c) memcpy(d,s,c)
338 #define ao_xmemset(d,v,c) memset(d,v,c)
339 #define ao_xmemcmp(d,s,c) memcmp(d,s,c)
340
341 #define AO_NEED_ALTITUDE_TO_PRES 1
342 #if TELEMEGA || TELEMETRUM_V2 || EASYMINI
343 #include "ao_convert_pa.c"
344 #include <ao_ms5607.h>
345 struct ao_ms5607_prom   ao_ms5607_prom;
346 #include "ao_ms5607_convert.c"
347 #if TELEMEGA
348 #define AO_PYRO_NUM     4
349 #include <ao_pyro.h>
350 #endif
351 #else
352 #include "ao_convert.c"
353 #endif
354
355 #include <ao_config.h>
356 #include <ao_fake_flight.h>
357 #include <ao_eeprom_read.h>
358 #include <ao_log.h>
359
360 #define ao_config_get()
361
362 struct ao_config ao_config;
363
364 #define DATA_TO_XDATA(x) (x)
365
366
367 extern int16_t ao_ground_accel, ao_flight_accel;
368 extern int16_t ao_accel_2g;
369
370 typedef int16_t accel_t;
371
372 uint16_t        ao_serial_number;
373 uint16_t        ao_flight_number;
374
375 extern uint16_t ao_sample_tick;
376
377 extern alt_t    ao_sample_height;
378 extern accel_t  ao_sample_accel;
379 extern int32_t  ao_accel_scale;
380 extern alt_t    ao_ground_height;
381 extern alt_t    ao_sample_alt;
382
383 double ao_sample_qangle;
384
385 int ao_sample_prev_tick;
386 uint16_t        prev_tick;
387
388
389 #include "ao_kalman.c"
390 #include "ao_sqrt.c"
391 #include "ao_sample.c"
392 #include "ao_flight.c"
393 #if TELEMEGA
394 #define AO_PYRO_NUM     4
395
396 #define AO_PYRO_0       0
397 #define AO_PYRO_1       1
398 #define AO_PYRO_2       2
399 #define AO_PYRO_3       3
400
401 #define PYRO_DBG        1
402
403 static void
404 ao_pyro_pin_set(uint8_t pin, uint8_t value)
405 {
406         printf ("set pyro %d %d\n", pin, value);
407 }
408
409 #include "ao_pyro.c"
410 #endif
411 #include "ao_eeprom_read.c"
412 #include "ao_eeprom_read_old.c"
413
414 #define to_double(f)    ((f) / 65536.0)
415
416 static int      ao_records_read = 0;
417 static int      ao_eof_read = 0;
418 #if !EASYMINI
419 static int      ao_flight_ground_accel;
420 #endif
421 static int      ao_flight_started = 0;
422 static int      ao_test_max_height;
423 static double   ao_test_max_height_time;
424 static int      ao_test_main_height;
425 static double   ao_test_main_height_time;
426 static double   ao_test_landed_time;
427 static double   ao_test_landed_height;
428 static double   ao_test_landed_time;
429 static int      landed_set;
430 static double   landed_time;
431 static double   landed_height;
432 #if AO_PYRO_NUM
433 static uint16_t pyros_fired;
434 #endif
435
436 #if HAS_MPU6000
437 static struct ao_mpu6000_sample ao_ground_mpu6000;
438 #endif
439
440 #if HAS_ACCEL
441 int ao_error_h_sq_avg;
442 #endif
443
444 void
445 ao_test_exit(void)
446 {
447         double  drogue_error;
448         double  main_error;
449         double  landed_error;
450         double  landed_time_error;
451
452         if (!ao_test_main_height_time) {
453                 ao_test_main_height_time = ao_test_max_height_time;
454                 ao_test_main_height = ao_test_max_height;
455         }
456         drogue_error = fabs(ao_test_max_height_time - drogue_time);
457         main_error = fabs(ao_test_main_height_time - main_time);
458         landed_error = fabs(ao_test_landed_height - landed_height);
459         landed_time_error = ao_test_landed_time - landed_time;
460         if (drogue_error > emulator_error_max || main_error > emulator_error_max) {
461                 printf ("%s %s\n",
462                         emulator_app, emulator_name);
463                 if (emulator_info)
464                         printf ("\t%s\n", emulator_info);
465                 printf ("\tApogee error %g\n", drogue_error);
466                 printf ("\tMain error %g\n", main_error);
467                 printf ("\tLanded height error %g\n", landed_error);
468                 printf ("\tLanded time error %g\n", landed_time_error);
469                 printf ("\tActual: apogee: %d at %7.2f main: %d at %7.2f landed %7.2f at %7.2f\n",
470                         ao_test_max_height, ao_test_max_height_time,
471                         ao_test_main_height, ao_test_main_height_time,
472                         ao_test_landed_height, ao_test_landed_time);
473                 printf ("\tComputed: apogee: %d at %7.2f main: %d at %7.2f landed %7.2f at %7.2f\n",
474                         drogue_height, drogue_time, main_height, main_time,
475                         landed_height, landed_time);
476                 exit (1);
477         }
478         exit(0);
479 }
480
481 #ifdef TELEMEGA
482 struct ao_azel {
483         int     az;
484         int     el;
485 };
486
487 static void
488 azel (struct ao_azel *r, struct ao_quaternion *q)
489 {
490         double  v;
491
492         r->az = floor (atan2(q->y, q->x) * 180/M_PI + 0.5);
493         v = sqrt (q->x*q->x + q->y*q->y);
494         r->el = floor (atan2(q->z, v) * 180/M_PI + 0.5);
495 }
496 #endif
497
498 void
499 ao_insert(void)
500 {
501         double  time;
502
503         ao_data_ring[ao_data_head] = ao_data_static;
504         if (ao_flight_state != ao_flight_startup) {
505 #if HAS_ACCEL
506                 double  accel = ((ao_flight_ground_accel - ao_data_accel_cook(&ao_data_static)) * GRAVITY * 2.0) /
507                         (ao_config.accel_minus_g - ao_config.accel_plus_g);
508 #else
509                 double  accel = 0.0;
510 #endif
511
512                 (void) accel;
513                 if (!tick_offset)
514                         tick_offset = -ao_data_static.tick;
515                 if ((prev_tick - ao_data_static.tick) > 0x400)
516                         tick_offset += 65536;
517                 simple_speed += accel * (ao_data_static.tick - prev_tick) / 100.0;
518                 prev_tick = ao_data_static.tick;
519                 time = (double) (ao_data_static.tick + tick_offset) / 100;
520
521 #if TELEMEGA || TELEMETRUM_V2 || EASYMINI
522                 ao_ms5607_convert(&ao_data_static.ms5607_raw, &ao_data_static.ms5607_cooked);
523                 double height = ao_pa_to_altitude(ao_data_static.ms5607_cooked.pres) - ao_ground_height;
524
525                 /* Hack to skip baro spike at accidental drogue charge
526                  * firing in 2015-09-26-serial-2093-flight-0012.eeprom
527                  * so we can test the kalman filter with this data. Just
528                  * keep reporting the same baro value across the pressure spike
529                  */
530                 {
531                         static struct ao_ms5607_sample save;
532                         if (ao_serial_number == 2093 && ao_flight_number == 12 && 32.5 < time && time < 33.7) {
533                                 ao_data_ring[ao_data_head].ms5607_raw = save;
534                         } else {
535                                 save = ao_data_static.ms5607_raw;
536                         }
537                 }
538 #else
539                 double  height = ao_pres_to_altitude(ao_data_static.adc.pres_real) - ao_ground_height;
540 #endif
541
542                 if (ao_test_max_height < height) {
543                         ao_test_max_height = height;
544                         ao_test_max_height_time = time;
545                         ao_test_landed_height = height;
546                         ao_test_landed_time = time;
547                 }
548                 if (height > ao_config.main_deploy) {
549                         ao_test_main_height_time = time;
550                         ao_test_main_height = height;
551                 }
552
553                 if (ao_test_landed_height > height) {
554                         ao_test_landed_height = height;
555                         ao_test_landed_time = time;
556                 }
557
558                 if (ao_flight_state == ao_flight_landed && !landed_set) {
559                         landed_set = 1;
560                         landed_time = time;
561                         landed_height = height;
562                 }
563
564                 if (!ao_summary) {
565 #if TELEMEGA
566                         static struct ao_quaternion     ao_ground_mag;
567                         static int                      ao_ground_mag_set;
568
569                         if (!ao_ground_mag_set) {
570                                 ao_quaternion_init_vector (&ao_ground_mag,
571                                                            ao_data_mag_across(&ao_data_static),
572                                                            ao_data_mag_through(&ao_data_static),
573                                                            ao_data_mag_along(&ao_data_static));
574                                 ao_quaternion_normalize(&ao_ground_mag, &ao_ground_mag);
575                                 ao_quaternion_rotate(&ao_ground_mag, &ao_ground_mag, &ao_rotation);
576                                 ao_ground_mag_set = 1;
577                         }
578
579                         struct ao_quaternion            ao_mag, ao_mag_rot;
580
581                         ao_quaternion_init_vector(&ao_mag,
582                                                   ao_data_mag_across(&ao_data_static),
583                                                   ao_data_mag_through(&ao_data_static),
584                                                   ao_data_mag_along(&ao_data_static));
585
586                         ao_quaternion_normalize(&ao_mag, &ao_mag);
587                         ao_quaternion_rotate(&ao_mag_rot, &ao_mag, &ao_rotation);
588
589                         float                           ao_dot;
590                         int                             ao_mag_angle;
591
592                         ao_dot = ao_quaternion_dot(&ao_mag_rot, &ao_ground_mag);
593
594                         struct ao_azel                  ground_azel, mag_azel, rot_azel;
595
596                         azel(&ground_azel, &ao_ground_mag);
597                         azel(&mag_azel, &ao_mag);
598                         azel(&rot_azel, &ao_mag_rot);
599
600                         ao_mag_angle = floor (acos(ao_dot) * 180 / M_PI + 0.5);
601
602                         (void) ao_mag_angle;
603
604                         static struct ao_quaternion     ao_x = { .r = 0, .x = 1, .y = 0, .z = 0 };
605                         struct ao_quaternion            ao_out;
606
607                         ao_quaternion_rotate(&ao_out, &ao_x, &ao_rotation);
608
609 #if 0
610                         int     out = floor (atan2(ao_out.y, ao_out.x) * 180 / M_PI);
611
612                         printf ("%7.2f state %-8.8s height %8.4f tilt %4d rot %4d mag_tilt %4d mag_rot %4d\n",
613                                 time,
614                                 ao_state_names[ao_flight_state],
615                                 ao_k_height / 65536.0,
616                                 ao_sample_orient, out,
617                                 mag_azel.el,
618                                 mag_azel.az);
619 #endif
620 #if 0
621                         printf ("%7.2f state %-8.8s height %8.4f tilt %4d rot %4d dist %12.2f gps_tilt %4d gps_sats %2d\n",
622                                 time,
623                                 ao_state_names[ao_flight_state],
624                                 ao_k_height / 65536.0,
625                                 ao_sample_orient, out,
626                                 ao_distance_from_pad(),
627                                 (int) floor (ao_gps_angle() + 0.5),
628                                 (ao_gps_static.flags & 0xf) * 10);
629
630 #endif
631 #if 0
632                         printf ("\t\tstate %-8.8s ground az: %4d el %4d mag az %4d el %4d rot az %4d el %4d el_diff %4d az_diff %4d angle %4d tilt %4d ground %8.5f %8.5f %8.5f cur %8.5f %8.5f %8.5f rot %8.5f %8.5f %8.5f\n",
633                                 ao_state_names[ao_flight_state],
634                                 ground_azel.az, ground_azel.el,
635                                 mag_azel.az, mag_azel.el,
636                                 rot_azel.az, rot_azel.el,
637                                 ground_azel.el - rot_azel.el,
638                                 ground_azel.az - rot_azel.az,
639                                 ao_mag_angle,
640                                 ao_sample_orient,
641                                 ao_ground_mag.x,
642                                 ao_ground_mag.y,
643                                 ao_ground_mag.z,
644                                 ao_mag.x,
645                                 ao_mag.y,
646                                 ao_mag.z,
647                                 ao_mag_rot.x,
648                                 ao_mag_rot.y,
649                                 ao_mag_rot.z);
650 #endif
651 #endif
652
653 #if 1
654                         printf("%7.2f height %8.2f accel %8.3f accel_speed %8.3f "
655                                "state %-8.8s k_height %8.2f k_speed %8.3f k_accel %8.3f avg_height %5d drogue %4d main %4d error %5d"
656 #if TELEMEGA
657                                " angle %5d "
658                                "accel_x %8.3f accel_y %8.3f accel_z %8.3f gyro_x %8.3f gyro_y %8.3f gyro_z %8.3f mag_x %8d mag_y %8d, mag_z %8d mag_angle %4d "
659 #endif
660                                "\n",
661                                time,
662                                height,
663                                accel,
664                                simple_speed > -100.0 ? simple_speed : -100.0,
665                                ao_state_names[ao_flight_state],
666                                ao_k_height / 65536.0,
667                                ao_k_speed / 65536.0 / 16.0,
668                                ao_k_accel / 65536.0 / 16.0,
669                                ao_avg_height,
670                                drogue_height,
671                                main_height,
672                                ao_error_h_sq_avg
673 #if TELEMEGA
674                                , ao_sample_orient,
675
676                                ao_mpu6000_accel(ao_data_static.mpu6000.accel_x),
677                                ao_mpu6000_accel(ao_data_static.mpu6000.accel_y),
678                                ao_mpu6000_accel(ao_data_static.mpu6000.accel_z),
679                                ao_mpu6000_gyro(ao_data_static.mpu6000.gyro_x - ao_ground_mpu6000.gyro_x),
680                                ao_mpu6000_gyro(ao_data_static.mpu6000.gyro_y - ao_ground_mpu6000.gyro_y),
681                                ao_mpu6000_gyro(ao_data_static.mpu6000.gyro_z - ao_ground_mpu6000.gyro_z),
682                                ao_data_static.hmc5883.x,
683                                ao_data_static.hmc5883.y,
684                                ao_data_static.hmc5883.z,
685                                ao_mag_angle
686 #endif
687                                 );
688 #endif
689
690 //                      if (ao_flight_state == ao_flight_landed)
691 //                              ao_test_exit();
692                 }
693         }
694         ao_data_head = ao_data_ring_next(ao_data_head);
695 }
696
697
698 uint16_t
699 uint16(uint8_t *bytes, int off)
700 {
701         return (uint16_t) bytes[off] | (((uint16_t) bytes[off+1]) << 8);
702 }
703
704 int16_t
705 int16(uint8_t *bytes, int off)
706 {
707         return (int16_t) uint16(bytes, off);
708 }
709
710 uint32_t
711 uint32(uint8_t *bytes, int off)
712 {
713         return (uint32_t) bytes[off] | (((uint32_t) bytes[off+1]) << 8) |
714                 (((uint32_t) bytes[off+2]) << 16) |
715                 (((uint32_t) bytes[off+3]) << 24);
716 }
717
718 int32_t
719 int32(uint8_t *bytes, int off)
720 {
721         return (int32_t) uint32(bytes, off);
722 }
723
724 uint32_t
725 uint24(uint8_t *bytes, int off)
726 {
727         return (uint32_t) bytes[off] | (((uint32_t) bytes[off+1]) << 8) |
728                 (((uint32_t) bytes[off+2]) << 16);
729 }
730
731 int32_t
732 int24(uint8_t *bytes, int off)
733 {
734         return (int32_t) uint24(bytes, off);
735 }
736
737 static int log_format;
738
739 void
740 ao_sleep(void *wchan)
741 {
742         if (wchan == &ao_data_head) {
743 #if TELEMEGA
744                 if (ao_flight_state >= ao_flight_boost && ao_flight_state < ao_flight_landed)
745                         ao_pyro_check();
746 #endif
747                 for (;;) {
748                         if (ao_records_read > 2 && ao_flight_state == ao_flight_startup)
749                         {
750
751 #if TELEMEGA
752                                 ao_data_static.mpu6000 = ao_ground_mpu6000;
753 #endif
754 #if TELEMETRUM_V1
755                                 ao_data_static.adc.accel = ao_flight_ground_accel;
756 #endif
757
758                                 ao_insert();
759                                 return;
760                         }
761
762                         if (eeprom) {
763 #if TELEMEGA
764                                 struct ao_log_mega      *log_mega;
765 #endif
766 #if TELEMETRUM_V2
767                                 struct ao_log_metrum    *log_metrum;
768 #endif
769 #if EASYMINI
770                                 struct ao_log_mini      *log_mini;
771 #endif
772 #if TELEMETRUM_V1
773                                 struct ao_log_record    *log_record;
774 #endif
775
776                                 if (eeprom_offset >= eeprom->len) {
777                                         if (++ao_eof_read >= 1000)
778                                                 if (!ao_summary)
779                                                         printf ("no more data, exiting simulation\n");
780                                         ao_test_exit();
781                                         ao_data_static.tick += 10;
782                                         ao_insert();
783                                         return;
784                                 }
785                                 switch (eeprom->log_format) {
786 #if TELEMEGA
787                                 case AO_LOG_FORMAT_TELEMEGA_OLD:
788                                 case AO_LOG_FORMAT_TELEMEGA:
789                                         log_mega = (struct ao_log_mega *) &eeprom->data[eeprom_offset];
790                                         eeprom_offset += sizeof (*log_mega);
791                                         switch (log_mega->type) {
792                                         case AO_LOG_FLIGHT:
793                                                 ao_flight_number = log_mega->u.flight.flight;
794                                                 ao_flight_ground_accel = log_mega->u.flight.ground_accel;
795                                                 ao_flight_started = 1;
796                                                 ao_ground_pres = log_mega->u.flight.ground_pres;
797                                                 ao_ground_height = ao_pa_to_altitude(ao_ground_pres);
798                                                 ao_ground_accel_along = log_mega->u.flight.ground_accel_along;
799                                                 ao_ground_accel_across = log_mega->u.flight.ground_accel_across;
800                                                 ao_ground_accel_through = log_mega->u.flight.ground_accel_through;
801                                                 ao_ground_roll = log_mega->u.flight.ground_roll;
802                                                 ao_ground_pitch = log_mega->u.flight.ground_pitch;
803                                                 ao_ground_yaw = log_mega->u.flight.ground_yaw;
804                                                 ao_ground_mpu6000.accel_x = ao_ground_accel_across;
805                                                 ao_ground_mpu6000.accel_y = ao_ground_accel_along;
806                                                 ao_ground_mpu6000.accel_z = ao_ground_accel_through;
807                                                 ao_ground_mpu6000.gyro_x = ao_ground_pitch >> 9;
808                                                 ao_ground_mpu6000.gyro_y = ao_ground_roll >> 9;
809                                                 ao_ground_mpu6000.gyro_z = ao_ground_yaw >> 9;
810                                                 break;
811                                         case AO_LOG_STATE:
812                                                 break;
813                                         case AO_LOG_SENSOR:
814                                                 ao_data_static.tick = log_mega->tick;
815                                                 ao_data_static.ms5607_raw.pres = log_mega->u.sensor.pres;
816                                                 ao_data_static.ms5607_raw.temp = log_mega->u.sensor.temp;
817                                                 ao_data_static.mpu6000.accel_x = log_mega->u.sensor.accel_x;
818                                                 ao_data_static.mpu6000.accel_y = log_mega->u.sensor.accel_y;
819                                                 ao_data_static.mpu6000.accel_z = log_mega->u.sensor.accel_z;
820                                                 ao_data_static.mpu6000.gyro_x = log_mega->u.sensor.gyro_x;
821                                                 ao_data_static.mpu6000.gyro_y = log_mega->u.sensor.gyro_y;
822                                                 ao_data_static.mpu6000.gyro_z = log_mega->u.sensor.gyro_z;
823                                                 ao_data_static.hmc5883.x = log_mega->u.sensor.mag_x;
824                                                 ao_data_static.hmc5883.y = log_mega->u.sensor.mag_y;
825                                                 ao_data_static.hmc5883.z = log_mega->u.sensor.mag_z;
826                                                 ao_data_static.mma655x = log_mega->u.sensor.accel;
827                                                 if (ao_config.pad_orientation != AO_PAD_ORIENTATION_ANTENNA_UP)
828                                                         ao_data_static.mma655x = ao_data_accel_invert(ao_data_static.mma655x);
829                                                 ao_records_read++;
830                                                 ao_insert();
831                                                 return;
832                                         case AO_LOG_TEMP_VOLT:
833                                                 if (pyros_fired != log_mega->u.volt.pyro) {
834                                                         printf("pyro changed %x -> %x\n", pyros_fired, log_mega->u.volt.pyro);
835                                                         pyros_fired = log_mega->u.volt.pyro;
836                                                 }
837                                                 break;
838                                         case AO_LOG_GPS_TIME:
839                                                 ao_gps_prev = ao_gps_static;
840                                                 ao_gps_static.tick = log_mega->tick;
841                                                 ao_gps_static.latitude = log_mega->u.gps.latitude;
842                                                 ao_gps_static.longitude = log_mega->u.gps.longitude;
843                                                 {
844                                                         int16_t altitude_low = log_mega->u.gps.altitude_low;
845                                                         int16_t altitude_high = log_mega->u.gps.altitude_high;
846                                                         int32_t altitude = altitude_low | ((int32_t) altitude_high << 16);
847
848                                                         AO_TELEMETRY_LOCATION_SET_ALTITUDE(&ao_gps_static, altitude);
849                                                 }
850                                                 ao_gps_static.flags = log_mega->u.gps.flags;
851                                                 if (!ao_gps_count)
852                                                         ao_gps_first = ao_gps_static;
853                                                 ao_gps_count++;
854                                                 break;
855                                         case AO_LOG_GPS_SAT:
856                                                 break;
857                                         }
858                                         break;
859 #endif
860 #if TELEMETRUM_V2
861                                 case AO_LOG_FORMAT_TELEMETRUM:
862                                         log_metrum = (struct ao_log_metrum *) &eeprom->data[eeprom_offset];
863                                         eeprom_offset += sizeof (*log_metrum);
864                                         switch (log_metrum->type) {
865                                         case AO_LOG_FLIGHT:
866                                                 ao_flight_started = 1;
867                                                 ao_flight_number = log_metrum->u.flight.flight;
868                                                 ao_flight_ground_accel = log_metrum->u.flight.ground_accel;
869                                                 ao_ground_pres = log_metrum->u.flight.ground_pres;
870                                                 ao_ground_height = ao_pa_to_altitude(ao_ground_pres);
871                                                 break;
872                                         case AO_LOG_SENSOR:
873                                                 ao_data_static.tick = log_metrum->tick;
874                                                 ao_data_static.ms5607_raw.pres = log_metrum->u.sensor.pres;
875                                                 ao_data_static.ms5607_raw.temp = log_metrum->u.sensor.temp;
876                                                 ao_data_static.mma655x = log_metrum->u.sensor.accel;
877                                                 ao_records_read++;
878                                                 ao_insert();
879                                                 return;
880                                         }
881                                         break;
882 #endif
883 #if EASYMINI
884                                 case AO_LOG_FORMAT_EASYMINI1:
885                                 case AO_LOG_FORMAT_EASYMINI2:
886                                 case AO_LOG_FORMAT_TELEMINI3:
887                                         log_mini = (struct ao_log_mini *) &eeprom->data[eeprom_offset];
888                                         eeprom_offset += sizeof (*log_mini);
889                                         switch (log_mini->type) {
890                                         case AO_LOG_FLIGHT:
891                                                 ao_flight_started = 1;
892                                                 ao_flight_number = log_mini->u.flight.flight;
893                                                 ao_ground_pres = log_mini->u.flight.ground_pres;
894                                                 ao_ground_height = ao_pa_to_altitude(ao_ground_pres);
895                                                 break;
896                                         case AO_LOG_SENSOR:
897                                                 ao_data_static.tick = log_mini->tick;
898                                                 ao_data_static.ms5607_raw.pres = int24(log_mini->u.sensor.pres, 0);
899                                                 ao_data_static.ms5607_raw.temp = int24(log_mini->u.sensor.temp, 0);
900                                                 ao_records_read++;
901                                                 ao_insert();
902                                                 return;
903                                         }
904                                         break;
905 #endif
906 #if TELEMETRUM_V1
907                                 case AO_LOG_FORMAT_FULL:
908                                 case AO_LOG_FORMAT_TINY:
909                                         log_record = (struct ao_log_record *) &eeprom->data[eeprom_offset];
910                                         eeprom_offset += sizeof (*log_record);
911                                         switch (log_record->type) {
912                                         case AO_LOG_FLIGHT:
913                                                 ao_flight_started = 1;
914                                                 ao_flight_ground_accel = log_record->u.flight.ground_accel;
915                                                 ao_flight_number = log_record->u.flight.flight;
916                                                 break;
917                                         case AO_LOG_SENSOR:
918                                         case 'P':       /* ancient telemini */
919                                                 ao_data_static.tick = log_record->tick;
920                                                 ao_data_static.adc.accel = log_record->u.sensor.accel;
921                                                 ao_data_static.adc.pres_real = log_record->u.sensor.pres;
922                                                 ao_data_static.adc.pres = log_record->u.sensor.pres;
923                                                 ao_records_read++;
924                                                 ao_insert();
925                                                 return;
926                                         case AO_LOG_TEMP_VOLT:
927                                                 ao_data_static.tick = log_record->tick;;
928                                                 ao_data_static.adc.temp = log_record->u.temp_volt.temp;
929                                                 ao_data_static.adc.v_batt = log_record->u.temp_volt.v_batt;
930                                                 break;
931                                         }
932                                         break;
933 #endif
934                                 default:
935                                         printf ("invalid log format %d\n", log_format);
936                                         ao_test_exit();
937                                 }
938                         }
939                 }
940
941         }
942 }
943 #define COUNTS_PER_G 264.8
944
945 void
946 ao_dump_state(void)
947 {
948 }
949
950 static const struct option options[] = {
951         { .name = "summary", .has_arg = 0, .val = 's' },
952         { .name = "debug", .has_arg = 0, .val = 'd' },
953         { .name = "info", .has_arg = 1, .val = 'i' },
954         { 0, 0, 0, 0},
955 };
956
957 void run_flight_fixed(char *name, FILE *f, int summary, char *info)
958 {
959         emulator_name = name;
960         emulator_in = f;
961         emulator_info = info;
962         ao_summary = summary;
963
964         if (strstr(name, ".eeprom") != NULL) {
965                 char    c;
966
967                 c = getc(f);
968                 ungetc(c, f);
969                 if (c == '{')
970                         eeprom = ao_eeprom_read(f);
971                 else
972                         eeprom = ao_eeprom_read_old(f);
973
974                 if (eeprom) {
975 #if HAS_MS5607
976                         ao_ms5607_prom = eeprom->ms5607_prom;
977 #endif
978                         ao_config = eeprom->config;
979                         ao_serial_number = eeprom->serial_number;
980                         log_format = eeprom->log_format;
981                 }
982         }
983
984         ao_flight_init();
985         ao_flight();
986 }
987
988 int
989 main (int argc, char **argv)
990 {
991         int     summary = 0;
992         int     c;
993         int     i;
994         char    *info = NULL;
995
996 #if HAS_ACCEL
997         emulator_app="full";
998 #else
999         emulator_app="baro";
1000 #endif
1001         while ((c = getopt_long(argc, argv, "sdi:", options, NULL)) != -1) {
1002                 switch (c) {
1003                 case 's':
1004                         summary = 1;
1005                         break;
1006                 case 'd':
1007                         ao_flight_debug = 1;
1008                         break;
1009                 case 'i':
1010                         info = optarg;
1011                         break;
1012                 }
1013         }
1014
1015         if (optind == argc)
1016                 run_flight_fixed("<stdin>", stdin, summary, info);
1017         else
1018                 for (i = optind; i < argc; i++) {
1019                         FILE    *f = fopen(argv[i], "r");
1020                         if (!f) {
1021                                 perror(argv[i]);
1022                                 continue;
1023                         }
1024                         run_flight_fixed(argv[i], f, summary, info);
1025                         fclose(f);
1026                 }
1027         exit(0);
1028 }