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