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