altos: Add orientation tracking to ao_flight_test
[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; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #define _GNU_SOURCE
19
20 #include <stdint.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stddef.h>
24 #include <string.h>
25 #include <getopt.h>
26 #include <math.h>
27
28 #define GRAVITY 9.80665
29
30 #define AO_HERTZ        100
31
32 #define HAS_ADC 1
33 #define AO_DATA_RING    64
34 #define ao_data_ring_next(n)    (((n) + 1) & (AO_DATA_RING - 1))
35 #define ao_data_ring_prev(n)    (((n) - 1) & (AO_DATA_RING - 1))
36
37 #define AO_M_TO_HEIGHT(m)       ((int16_t) (m))
38 #define AO_MS_TO_SPEED(ms)      ((int16_t) ((ms) * 16))
39 #define AO_MSS_TO_ACCEL(mss)    ((int16_t) ((mss) * 16))
40
41 #define AO_GPS_NEW_DATA         1
42 #define AO_GPS_NEW_TRACKING     2
43
44 int ao_gps_new;
45
46 #if TELEMEGA
47 #define AO_ADC_NUM_SENSE        6
48 #define HAS_MS5607              1
49 #define HAS_MPU6000             1
50 #define HAS_MMA655X             1
51 #define HAS_HMC5883             1
52
53 struct ao_adc {
54         int16_t                 sense[AO_ADC_NUM_SENSE];
55         int16_t                 v_batt;
56         int16_t                 v_pbatt;
57         int16_t                 accel_ref;
58         int16_t                 accel;
59         int16_t                 temp;
60 };
61 #else
62 /*
63  * One set of samples read from the A/D converter
64  */
65 struct ao_adc {
66         int16_t         accel;          /* accelerometer */
67         int16_t         pres;           /* pressure sensor */
68         int16_t         pres_real;      /* unclipped */
69         int16_t         temp;           /* temperature sensor */
70         int16_t         v_batt;         /* battery voltage */
71         int16_t         sense_d;        /* drogue continuity sense */
72         int16_t         sense_m;        /* main continuity sense */
73 };
74
75 #ifndef HAS_ACCEL
76 #define HAS_ACCEL 1
77 #define HAS_ACCEL_REF 0
78 #endif
79
80 #endif
81
82 #define __pdata
83 #define __data
84 #define __xdata
85 #define __code
86 #define __reentrant
87
88 #define HAS_FLIGHT 1
89 #define HAS_IGNITE 1
90 #define HAS_USB 1
91 #define HAS_GPS 1
92
93 #include <ao_data.h>
94 #include <ao_log.h>
95
96 #define to_fix16(x) ((int16_t) ((x) * 65536.0 + 0.5))
97 #define to_fix32(x) ((int32_t) ((x) * 65536.0 + 0.5))
98 #define from_fix(x)     ((x) >> 16)
99
100 /*
101  * Above this height, the baro sensor doesn't work
102  */
103 #define AO_BARO_SATURATE        13000
104 #define AO_MIN_BARO_VALUE       ao_altitude_to_pres(AO_BARO_SATURATE)
105
106 /*
107  * Above this speed, baro measurements are unreliable
108  */
109 #define AO_MAX_BARO_SPEED       200
110
111 #define ACCEL_NOSE_UP   (ao_accel_2g >> 2)
112
113 extern enum ao_flight_state ao_flight_state;
114
115 #define FALSE 0
116 #define TRUE 1
117
118 volatile struct ao_data ao_data_ring[AO_DATA_RING];
119 volatile uint8_t ao_data_head;
120 int     ao_summary = 0;
121
122 #define ao_led_on(l)
123 #define ao_led_off(l)
124 #define ao_timer_set_adc_interval(i)
125 #define ao_wakeup(wchan) ao_dump_state()
126 #define ao_cmd_register(c)
127 #define ao_usb_disable()
128 #define ao_telemetry_set_interval(x)
129 #define ao_rdf_set(rdf)
130 #define ao_packet_slave_start()
131 #define ao_packet_slave_stop()
132 #define flush()
133
134 enum ao_igniter {
135         ao_igniter_drogue = 0,
136         ao_igniter_main = 1
137 };
138
139 struct ao_data ao_data_static;
140
141 int     drogue_height;
142 double  drogue_time;
143 int     main_height;
144 double  main_time;
145
146 int     tick_offset;
147
148 static int32_t  ao_k_height;
149
150 int16_t
151 ao_time(void)
152 {
153         return ao_data_static.tick;
154 }
155
156 void
157 ao_delay(int16_t interval)
158 {
159         return;
160 }
161
162 void
163 ao_ignite(enum ao_igniter igniter)
164 {
165         double time = (double) (ao_data_static.tick + tick_offset) / 100;
166
167         if (igniter == ao_igniter_drogue) {
168                 drogue_time = time;
169                 drogue_height = ao_k_height >> 16;
170         } else {
171                 main_time = time;
172                 main_height = ao_k_height >> 16;
173         }
174 }
175
176 struct ao_task {
177         int dummy;
178 };
179
180 #define ao_add_task(t,f,n) ((void) (t))
181
182 #define ao_log_start()
183 #define ao_log_stop()
184
185 #define AO_MS_TO_TICKS(ms)      ((ms) / 10)
186 #define AO_SEC_TO_TICKS(s)      ((s) * 100)
187
188 #define AO_FLIGHT_TEST
189
190 int     ao_flight_debug;
191
192 FILE *emulator_in;
193 char *emulator_app;
194 char *emulator_name;
195 char *emulator_info;
196 double emulator_error_max = 4;
197 double emulator_height_error_max = 20;  /* noise in the baro sensor */
198
199 void
200 ao_dump_state(void);
201
202 void
203 ao_sleep(void *wchan);
204
205 const char const * const ao_state_names[] = {
206         "startup", "idle", "pad", "boost", "fast",
207         "coast", "drogue", "main", "landed", "invalid"
208 };
209
210 struct ao_cmds {
211         void            (*func)(void);
212         const char      *help;
213 };
214
215 #define ao_xmemcpy(d,s,c) memcpy(d,s,c)
216 #define ao_xmemset(d,v,c) memset(d,v,c)
217 #define ao_xmemcmp(d,s,c) memcmp(d,s,c)
218
219 #define AO_NEED_ALTITUDE_TO_PRES 1
220 #if TELEMEGA
221 #include "ao_convert_pa.c"
222 #include <ao_ms5607.h>
223 struct ao_ms5607_prom   ms5607_prom;
224 #include "ao_ms5607_convert.c"
225 #define AO_PYRO_NUM     4
226 #include <ao_pyro.h>
227 #else
228 #include "ao_convert.c"
229 #endif
230
231 struct ao_config {
232         uint16_t        main_deploy;
233         int16_t         accel_plus_g;
234         int16_t         accel_minus_g;
235         uint8_t         pad_orientation;
236         uint16_t        apogee_lockout;
237 #if TELEMEGA
238         struct ao_pyro  pyro[AO_PYRO_NUM];      /* minor version 12 */
239         int16_t         accel_zero_along;
240         int16_t         accel_zero_across;
241         int16_t         accel_zero_through;
242 #endif
243 };
244
245 #define AO_PAD_ORIENTATION_ANTENNA_UP   0
246 #define AO_PAD_ORIENTATION_ANTENNA_DOWN 1
247
248 #define ao_config_get()
249
250 struct ao_config ao_config;
251
252 #define DATA_TO_XDATA(x) (x)
253
254
255 extern int16_t ao_ground_accel, ao_flight_accel;
256 extern int16_t ao_accel_2g;
257
258 typedef int16_t accel_t;
259
260 extern uint16_t ao_sample_tick;
261
262 extern alt_t    ao_sample_height;
263 extern accel_t  ao_sample_accel;
264 extern int32_t  ao_accel_scale;
265 extern alt_t    ao_ground_height;
266 extern alt_t    ao_sample_alt;
267
268 double ao_sample_qangle;
269
270 int ao_sample_prev_tick;
271 uint16_t        prev_tick;
272
273
274 #include "ao_kalman.c"
275 #include "ao_sqrt.c"
276 #include "ao_sample.c"
277 #include "ao_flight.c"
278 #if TELEMEGA
279 #define AO_PYRO_NUM     4
280
281 #define AO_PYRO_0       0
282 #define AO_PYRO_1       1
283 #define AO_PYRO_2       2
284 #define AO_PYRO_3       3
285
286 static void
287 ao_pyro_pin_set(uint8_t pin, uint8_t value)
288 {
289         printf ("set pyro %d %d\n", pin, value);
290 }
291
292 #include "ao_pyro.c"
293 #endif
294
295 #define to_double(f)    ((f) / 65536.0)
296
297 static int      ao_records_read = 0;
298 static int      ao_eof_read = 0;
299 static int      ao_flight_ground_accel;
300 static int      ao_flight_started = 0;
301 static int      ao_test_max_height;
302 static double   ao_test_max_height_time;
303 static int      ao_test_main_height;
304 static double   ao_test_main_height_time;
305 static double   ao_test_landed_time;
306 static double   ao_test_landed_height;
307 static double   ao_test_landed_time;
308 static int      landed_set;
309 static double   landed_time;
310 static double   landed_height;
311
312 #if HAS_MPU6000
313 static struct ao_mpu6000_sample ao_ground_mpu6000;
314 #endif
315
316 void
317 ao_test_exit(void)
318 {
319         double  drogue_error;
320         double  main_error;
321         double  landed_error;
322         double  landed_time_error;
323
324         if (!ao_test_main_height_time) {
325                 ao_test_main_height_time = ao_test_max_height_time;
326                 ao_test_main_height = ao_test_max_height;
327         }
328         drogue_error = fabs(ao_test_max_height_time - drogue_time);
329         main_error = fabs(ao_test_main_height_time - main_time);
330         landed_error = fabs(ao_test_landed_height - landed_height);
331         landed_time_error = ao_test_landed_time - landed_time;
332         if (drogue_error > emulator_error_max || main_error > emulator_error_max) {
333                 printf ("%s %s\n",
334                         emulator_app, emulator_name);
335                 if (emulator_info)
336                         printf ("\t%s\n", emulator_info);
337                 printf ("\tApogee error %g\n", drogue_error);
338                 printf ("\tMain error %g\n", main_error);
339                 printf ("\tLanded height error %g\n", landed_error);
340                 printf ("\tLanded time error %g\n", landed_time_error);
341                 printf ("\tActual: apogee: %d at %7.2f main: %d at %7.2f landed %7.2f at %7.2f\n",
342                         ao_test_max_height, ao_test_max_height_time,
343                         ao_test_main_height, ao_test_main_height_time,
344                         ao_test_landed_height, ao_test_landed_time);
345                 printf ("\tComputed: apogee: %d at %7.2f main: %d at %7.2f landed %7.2f at %7.2f\n",
346                         drogue_height, drogue_time, main_height, main_time,
347                         landed_height, landed_time);
348                 exit (1);
349         }
350         exit(0);
351 }
352
353 #ifdef TELEMEGA
354 struct ao_azel {
355         int     az;
356         int     el;
357 };
358
359 static void
360 azel (struct ao_azel *r, struct ao_quaternion *q)
361 {
362         double  v;
363
364         r->az = floor (atan2(q->y, q->x) * 180/M_PI + 0.5);
365         v = sqrt (q->x*q->x + q->y*q->y);
366         r->el = floor (atan2(q->z, v) * 180/M_PI + 0.5);
367 }
368 #endif
369
370 void
371 ao_insert(void)
372 {
373         double  time;
374
375         ao_data_ring[ao_data_head] = ao_data_static;
376         ao_data_head = ao_data_ring_next(ao_data_head);
377         if (ao_flight_state != ao_flight_startup) {
378 #if HAS_ACCEL
379                 double  accel = ((ao_flight_ground_accel - ao_data_accel_cook(&ao_data_static)) * GRAVITY * 2.0) /
380                         (ao_config.accel_minus_g - ao_config.accel_plus_g);
381 #else
382                 double  accel = 0.0;
383 #endif
384 #if TELEMEGA
385                 double  height;
386
387                 ao_ms5607_convert(&ao_data_static.ms5607_raw, &ao_data_static.ms5607_cooked);
388                 height = ao_pa_to_altitude(ao_data_static.ms5607_cooked.pres) - ao_ground_height;
389 #else
390                 double  height = ao_pres_to_altitude(ao_data_static.adc.pres_real) - ao_ground_height;
391 #endif
392
393                 if (!tick_offset)
394                         tick_offset = -ao_data_static.tick;
395                 if ((prev_tick - ao_data_static.tick) > 0x400)
396                         tick_offset += 65536;
397                 prev_tick = ao_data_static.tick;
398                 time = (double) (ao_data_static.tick + tick_offset) / 100;
399
400                 if (ao_test_max_height < height) {
401                         ao_test_max_height = height;
402                         ao_test_max_height_time = time;
403                         ao_test_landed_height = height;
404                         ao_test_landed_time = time;
405                 }
406                 if (height > ao_config.main_deploy) {
407                         ao_test_main_height_time = time;
408                         ao_test_main_height = height;
409                 }
410
411                 if (ao_test_landed_height > height) {
412                         ao_test_landed_height = height;
413                         ao_test_landed_time = time;
414                 }
415
416                 if (ao_flight_state == ao_flight_landed && !landed_set) {
417                         landed_set = 1;
418                         landed_time = time;
419                         landed_height = height;
420                 }
421
422                 if (!ao_summary) {
423 #if TELEMEGA
424                         static struct ao_quaternion     ao_ground_mag;
425                         static int                      ao_ground_mag_set;
426
427                         if (!ao_ground_mag_set) {
428                                 ao_quaternion_init_vector (&ao_ground_mag,
429                                                            ao_data_mag_across(&ao_data_static),
430                                                            ao_data_mag_through(&ao_data_static),
431                                                            ao_data_mag_along(&ao_data_static));
432                                 ao_quaternion_normalize(&ao_ground_mag, &ao_ground_mag);
433                                 ao_quaternion_rotate(&ao_ground_mag, &ao_ground_mag, &ao_rotation);
434                                 ao_ground_mag_set = 1;
435                         }
436
437                         struct ao_quaternion            ao_mag, ao_mag_rot;
438
439                         ao_quaternion_init_vector(&ao_mag,
440                                                   ao_data_mag_across(&ao_data_static),
441                                                   ao_data_mag_through(&ao_data_static),
442                                                   ao_data_mag_along(&ao_data_static));
443
444                         ao_quaternion_normalize(&ao_mag, &ao_mag);
445                         ao_quaternion_rotate(&ao_mag_rot, &ao_mag, &ao_rotation);
446                         
447                         float                           ao_dot;
448                         int                             ao_mag_angle;
449
450                         ao_dot = ao_quaternion_dot(&ao_mag_rot, &ao_ground_mag);
451
452                         struct ao_azel                  ground_azel, mag_azel, rot_azel;
453
454                         azel(&ground_azel, &ao_ground_mag);
455                         azel(&mag_azel, &ao_mag);
456                         azel(&rot_azel, &ao_mag_rot);
457
458                         ao_mag_angle = floor (acos(ao_dot) * 180 / M_PI + 0.5);
459
460                         static struct ao_quaternion     ao_x = { .r = 0, .x = 1, .y = 0, .z = 0 };
461                         struct ao_quaternion            ao_out;
462
463                         ao_quaternion_rotate(&ao_out, &ao_x, &ao_rotation);
464
465                         int     out = floor (atan2(ao_out.y, ao_out.x) * 180 / M_PI);
466
467                         printf ("%7.2f state %-8.8s height %8.4f tilt %4d rot %4d mag_tilt %4d mag_rot %4d\n",
468                                 time,
469                                 ao_state_names[ao_flight_state],
470                                 ao_k_height / 65536.0,
471                                 ao_sample_orient, out,
472                                 mag_azel.el,
473                                 mag_azel.az);
474                         
475
476 #if 0
477                         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",
478                                 ao_state_names[ao_flight_state],
479                                 ground_azel.az, ground_azel.el,
480                                 mag_azel.az, mag_azel.el,
481                                 rot_azel.az, rot_azel.el,
482                                 ground_azel.el - rot_azel.el,
483                                 ground_azel.az - rot_azel.az,
484                                 ao_mag_angle,
485                                 ao_sample_orient,
486                                 ao_ground_mag.x,
487                                 ao_ground_mag.y,
488                                 ao_ground_mag.z,
489                                 ao_mag.x,
490                                 ao_mag.y,
491                                 ao_mag.z,
492                                 ao_mag_rot.x,
493                                 ao_mag_rot.y,
494                                 ao_mag_rot.z);
495 #endif
496 #endif
497
498 #if 0
499                         printf("%7.2f height %8.2f accel %8.3f "
500 #if TELEMEGA
501                                "angle %5d "
502                                "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 "
503 #endif
504                                "state %-8.8s k_height %8.2f k_speed %8.3f k_accel %8.3f avg_height %5d drogue %4d main %4d error %5d\n",
505                                time,
506                                height,
507                                accel,
508 #if TELEMEGA
509                                ao_sample_orient,
510
511                                ao_mpu6000_accel(ao_data_static.mpu6000.accel_x),
512                                ao_mpu6000_accel(ao_data_static.mpu6000.accel_y),
513                                ao_mpu6000_accel(ao_data_static.mpu6000.accel_z),
514                                ao_mpu6000_gyro(ao_data_static.mpu6000.gyro_x - ao_ground_mpu6000.gyro_x),
515                                ao_mpu6000_gyro(ao_data_static.mpu6000.gyro_y - ao_ground_mpu6000.gyro_y),
516                                ao_mpu6000_gyro(ao_data_static.mpu6000.gyro_z - ao_ground_mpu6000.gyro_z),
517                                ao_data_static.hmc5883.x,
518                                ao_data_static.hmc5883.y,
519                                ao_data_static.hmc5883.z,
520                                ao_mag_angle,
521 #endif
522                                ao_state_names[ao_flight_state],
523                                ao_k_height / 65536.0,
524                                ao_k_speed / 65536.0 / 16.0,
525                                ao_k_accel / 65536.0 / 16.0,
526                                ao_avg_height,
527                                drogue_height,
528                                main_height,
529                                ao_error_h_sq_avg);
530 #endif
531                         
532 //                      if (ao_flight_state == ao_flight_landed)
533 //                              ao_test_exit();
534                 }
535         }
536 }
537
538 #define AO_MAX_CALLSIGN                 8
539 #define AO_MAX_VERSION                  8
540 #define AO_MAX_TELEMETRY                128
541
542 struct ao_telemetry_generic {
543         uint16_t        serial;         /* 0 */
544         uint16_t        tick;           /* 2 */
545         uint8_t         type;           /* 4 */
546         uint8_t         payload[27];    /* 5 */
547         /* 32 */
548 };
549
550 #define AO_TELEMETRY_SENSOR_TELEMETRUM  0x01
551 #define AO_TELEMETRY_SENSOR_TELEMINI    0x02
552 #define AO_TELEMETRY_SENSOR_TELENANO    0x03
553
554 struct ao_telemetry_sensor {
555         uint16_t        serial;         /*  0 */
556         uint16_t        tick;           /*  2 */
557         uint8_t         type;           /*  4 */
558
559         uint8_t         state;          /*  5 flight state */
560         int16_t         accel;          /*  6 accelerometer (TM only) */
561         int16_t         pres;           /*  8 pressure sensor */
562         int16_t         temp;           /* 10 temperature sensor */
563         int16_t         v_batt;         /* 12 battery voltage */
564         int16_t         sense_d;        /* 14 drogue continuity sense (TM/Tm) */
565         int16_t         sense_m;        /* 16 main continuity sense (TM/Tm) */
566
567         int16_t         acceleration;   /* 18 m/s² * 16 */
568         int16_t         speed;          /* 20 m/s * 16 */
569         int16_t         height;         /* 22 m */
570
571         int16_t         ground_pres;    /* 24 average pres on pad */
572         int16_t         ground_accel;   /* 26 average accel on pad */
573         int16_t         accel_plus_g;   /* 28 accel calibration at +1g */
574         int16_t         accel_minus_g;  /* 30 accel calibration at -1g */
575         /* 32 */
576 };
577
578 #define AO_TELEMETRY_CONFIGURATION      0x04
579
580 struct ao_telemetry_configuration {
581         uint16_t        serial;                         /*  0 */
582         uint16_t        tick;                           /*  2 */
583         uint8_t         type;                           /*  4 */
584
585         uint8_t         device;                         /*  5 device type */
586         uint16_t        flight;                         /*  6 flight number */
587         uint8_t         config_major;                   /*  8 Config major version */
588         uint8_t         config_minor;                   /*  9 Config minor version */
589         uint16_t        apogee_delay;                   /* 10 Apogee deploy delay in seconds */
590         uint16_t        main_deploy;                    /* 12 Main deploy alt in meters */
591         uint16_t        flight_log_max;                 /* 14 Maximum flight log size in kB */
592         char            callsign[AO_MAX_CALLSIGN];      /* 16 Radio operator identity */
593         char            version[AO_MAX_VERSION];        /* 24 Software version */
594         /* 32 */
595 };
596
597 #define AO_TELEMETRY_LOCATION           0x05
598
599 #define AO_GPS_MODE_NOT_VALID           'N'
600 #define AO_GPS_MODE_AUTONOMOUS          'A'
601 #define AO_GPS_MODE_DIFFERENTIAL        'D'
602 #define AO_GPS_MODE_ESTIMATED           'E'
603 #define AO_GPS_MODE_MANUAL              'M'
604 #define AO_GPS_MODE_SIMULATED           'S'
605
606 struct ao_telemetry_location {
607         uint16_t        serial;         /*  0 */
608         uint16_t        tick;           /*  2 */
609         uint8_t         type;           /*  4 */
610
611         uint8_t         flags;          /*  5 Number of sats and other flags */
612         int16_t         altitude;       /*  6 GPS reported altitude (m) */
613         int32_t         latitude;       /*  8 latitude (degrees * 10⁷) */
614         int32_t         longitude;      /* 12 longitude (degrees * 10⁷) */
615         uint8_t         year;           /* 16 (- 2000) */
616         uint8_t         month;          /* 17 (1-12) */
617         uint8_t         day;            /* 18 (1-31) */
618         uint8_t         hour;           /* 19 (0-23) */
619         uint8_t         minute;         /* 20 (0-59) */
620         uint8_t         second;         /* 21 (0-59) */
621         uint8_t         pdop;           /* 22 (m * 5) */
622         uint8_t         hdop;           /* 23 (m * 5) */
623         uint8_t         vdop;           /* 24 (m * 5) */
624         uint8_t         mode;           /* 25 */
625         uint16_t        ground_speed;   /* 26 cm/s */
626         int16_t         climb_rate;     /* 28 cm/s */
627         uint8_t         course;         /* 30 degrees / 2 */
628         uint8_t         unused[1];      /* 31 */
629         /* 32 */
630 };
631
632 #define AO_TELEMETRY_SATELLITE          0x06
633
634 struct ao_telemetry_satellite_info {
635         uint8_t         svid;
636         uint8_t         c_n_1;
637 };
638
639 struct ao_telemetry_satellite {
640         uint16_t                                serial;         /*  0 */
641         uint16_t                                tick;           /*  2 */
642         uint8_t                                 type;           /*  4 */
643         uint8_t                                 channels;       /*  5 number of reported sats */
644
645         struct ao_telemetry_satellite_info      sats[12];       /* 6 */
646         uint8_t                                 unused[2];      /* 30 */
647         /* 32 */
648 };
649
650 union ao_telemetry_all {
651         struct ao_telemetry_generic             generic;
652         struct ao_telemetry_sensor              sensor;
653         struct ao_telemetry_configuration       configuration;
654         struct ao_telemetry_location            location;
655         struct ao_telemetry_satellite           satellite;
656 };
657
658 uint16_t
659 uint16(uint8_t *bytes, int off)
660 {
661         return (uint16_t) bytes[off] | (((uint16_t) bytes[off+1]) << 8);
662 }
663
664 int16_t
665 int16(uint8_t *bytes, int off)
666 {
667         return (int16_t) uint16(bytes, off);
668 }
669
670 uint32_t
671 uint32(uint8_t *bytes, int off)
672 {
673         return (uint32_t) bytes[off] | (((uint32_t) bytes[off+1]) << 8) |
674                 (((uint32_t) bytes[off+2]) << 16) |
675                 (((uint32_t) bytes[off+3]) << 24);
676 }
677
678 int32_t
679 int32(uint8_t *bytes, int off)
680 {
681         return (int32_t) uint32(bytes, off);
682 }
683
684 static int log_format;
685
686 void
687 ao_sleep(void *wchan)
688 {
689         if (wchan == &ao_data_head) {
690                 char            type = 0;
691                 uint16_t        tick = 0;
692                 uint16_t        a = 0, b = 0;
693                 uint8_t         bytes[1024];
694                 union ao_telemetry_all  telem;
695                 char            line[1024];
696                 char            *saveptr;
697                 char            *l;
698                 char            *words[64];
699                 int             nword;
700
701 #if TELEMEGA
702                 if (ao_flight_state >= ao_flight_boost && ao_flight_state < ao_flight_landed)
703                         ao_pyro_check();
704 #endif
705                 for (;;) {
706                         if (ao_records_read > 2 && ao_flight_state == ao_flight_startup)
707                         {
708 #if TELEMEGA
709                                 ao_data_static.mpu6000 = ao_ground_mpu6000;
710 #else
711                                 ao_data_static.adc.accel = ao_flight_ground_accel;
712 #endif
713                                 ao_insert();
714                                 return;
715                         }
716
717                         if (!fgets(line, sizeof (line), emulator_in)) {
718                                 if (++ao_eof_read >= 1000) {
719                                         if (!ao_summary)
720                                                 printf ("no more data, exiting simulation\n");
721                                         ao_test_exit();
722                                 }
723                                 ao_data_static.tick += 10;
724                                 ao_insert();
725                                 return;
726                         }
727                         l = line;
728                         for (nword = 0; nword < 64; nword++) {
729                                 words[nword] = strtok_r(l, " \t\n", &saveptr);
730                                 l = NULL;
731                                 if (words[nword] == NULL)
732                                         break;
733                         }
734 #if TELEMEGA
735                         if (log_format == AO_LOG_FORMAT_TELEMEGA && nword == 30 && strlen(words[0]) == 1) {
736                                 int     i;
737                                 struct ao_ms5607_value  value;
738
739                                 type = words[0][0];
740                                 tick = strtoul(words[1], NULL, 16);
741 //                              printf ("%c %04x", type, tick);
742                                 for (i = 2; i < nword; i++) {
743                                         bytes[i - 2] = strtoul(words[i], NULL, 16);
744 //                                      printf(" %02x", bytes[i-2]);
745                                 }
746 //                              printf ("\n");
747                                 switch (type) {
748                                 case 'F':
749                                         ao_flight_ground_accel = int16(bytes, 2);
750                                         ao_flight_started = 1;
751                                         ao_ground_pres = int32(bytes, 4);
752                                         ao_ground_height = ao_pa_to_altitude(ao_ground_pres);
753                                         break;
754                                 case 'A':
755                                         ao_data_static.tick = tick;
756                                         ao_data_static.ms5607_raw.pres = int32(bytes, 0);
757                                         ao_data_static.ms5607_raw.temp = int32(bytes, 4);
758                                         ao_ms5607_convert(&ao_data_static.ms5607_raw, &value);
759                                         ao_data_static.mpu6000.accel_x = int16(bytes, 8);
760                                         ao_data_static.mpu6000.accel_y = int16(bytes, 10);
761                                         ao_data_static.mpu6000.accel_z = int16(bytes, 12);
762                                         ao_data_static.mpu6000.gyro_x = int16(bytes, 14);
763                                         ao_data_static.mpu6000.gyro_y = int16(bytes, 16);
764                                         ao_data_static.mpu6000.gyro_z = int16(bytes, 18);
765                                         ao_data_static.hmc5883.x = int16(bytes, 20);
766                                         ao_data_static.hmc5883.y = int16(bytes, 22);
767                                         ao_data_static.hmc5883.z = int16(bytes, 24);
768 #if HAS_MMA655X
769                                         ao_data_static.mma655x = int16(bytes, 26);
770 #endif
771                                         ao_records_read++;
772                                         ao_insert();
773                                         return;
774                                 }
775                                 continue;
776                         } else if (nword == 3 && strcmp(words[0], "ms5607") == 0) {
777                                 if (strcmp(words[1], "reserved:") == 0)
778                                         ms5607_prom.reserved = strtoul(words[2], NULL, 10);
779                                 else if (strcmp(words[1], "sens:") == 0)
780                                         ms5607_prom.sens = strtoul(words[2], NULL, 10);
781                                 else if (strcmp(words[1], "off:") == 0)
782                                         ms5607_prom.off = strtoul(words[2], NULL, 10);
783                                 else if (strcmp(words[1], "tcs:") == 0)
784                                         ms5607_prom.tcs = strtoul(words[2], NULL, 10);
785                                 else if (strcmp(words[1], "tco:") == 0)
786                                         ms5607_prom.tco = strtoul(words[2], NULL, 10);
787                                 else if (strcmp(words[1], "tref:") == 0)
788                                         ms5607_prom.tref = strtoul(words[2], NULL, 10);
789                                 else if (strcmp(words[1], "tempsens:") == 0)
790                                         ms5607_prom.tempsens = strtoul(words[2], NULL, 10);
791                                 else if (strcmp(words[1], "crc:") == 0)
792                                         ms5607_prom.crc = strtoul(words[2], NULL, 10);
793                                 continue;
794                         } else if (nword >= 3 && strcmp(words[0], "Pyro") == 0) {
795                                 int     p = strtoul(words[1], NULL, 10);
796                                 int     i, j;
797                                 struct ao_pyro  *pyro = &ao_config.pyro[p];
798
799                                 for (i = 2; i < nword; i++) {
800                                         for (j = 0; j < NUM_PYRO_VALUES; j++)
801                                                 if (!strcmp (words[2], ao_pyro_values[j].name))
802                                                         break;
803                                         if (j == NUM_PYRO_VALUES)
804                                                 continue;
805                                         pyro->flags |= ao_pyro_values[j].flag;
806                                         if (ao_pyro_values[j].offset != NO_VALUE && i + 1 < nword) {
807                                                 int16_t val = strtoul(words[++i], NULL, 10);
808                                                 *((int16_t *) ((char *) pyro + ao_pyro_values[j].offset)) = val;
809                                         }
810                                 }
811                         }
812 #else
813                         if (nword == 4 && log_format != AO_LOG_FORMAT_TELEMEGA) {
814                                 type = words[0][0];
815                                 tick = strtoul(words[1], NULL, 16);
816                                 a = strtoul(words[2], NULL, 16);
817                                 b = strtoul(words[3], NULL, 16);
818                                 if (type == 'P')
819                                         type = 'A';
820                         }
821 #endif
822                         else if (nword == 2 && strcmp(words[0], "log-format") == 0) {
823                                 log_format = strtoul(words[1], NULL, 10);
824                         } else if (nword >= 6 && strcmp(words[0], "Accel") == 0) {
825                                 ao_config.accel_plus_g = atoi(words[3]);
826                                 ao_config.accel_minus_g = atoi(words[5]);
827 #ifdef TELEMEGA
828                         } else if (nword >= 8 && strcmp(words[0], "IMU") == 0) {
829                                 ao_config.accel_zero_along = atoi(words[3]);
830                                 ao_config.accel_zero_across = atoi(words[5]);
831                                 ao_config.accel_zero_through = atoi(words[7]);
832                                 printf ("%d %d %d\n", ao_config.accel_zero_along, ao_config.accel_zero_across, ao_config.accel_zero_through);
833 #endif
834                         } else if (nword >= 4 && strcmp(words[0], "Main") == 0) {
835                                 ao_config.main_deploy = atoi(words[2]);
836                         } else if (nword >= 3 && strcmp(words[0], "Apogee") == 0 &&
837                                    strcmp(words[1], "lockout:") == 0) {
838                                 ao_config.apogee_lockout = atoi(words[2]);
839                         } else if (nword >= 36 && strcmp(words[0], "CALL") == 0) {
840                                 tick = atoi(words[10]);
841                                 if (!ao_flight_started) {
842                                         type = 'F';
843                                         a = atoi(words[26]);
844                                         ao_flight_started = 1;
845                                 } else {
846                                         type = 'A';
847                                         a = atoi(words[12]);
848                                         b = atoi(words[14]);
849                                 }
850                         } else if (nword == 3 && strcmp(words[0], "BARO") == 0) {
851                                 tick = strtol(words[1], NULL, 16);
852                                 a = 16384 - 328;
853                                 b = strtol(words[2], NULL, 10);
854                                 type = 'A';
855                                 if (!ao_flight_started) {
856                                         ao_flight_ground_accel = 16384 - 328;
857                                         ao_config.accel_plus_g = 16384 - 328;
858                                         ao_config.accel_minus_g = 16384 + 328;
859                                         ao_flight_started = 1;
860                                 }
861                         } else if (nword == 2 && strcmp(words[0], "TELEM") == 0) {
862                                 __xdata char    *hex = words[1];
863                                 char    elt[3];
864                                 int     i, len;
865                                 uint8_t sum;
866
867                                 len = strlen(hex);
868                                 if (len > sizeof (bytes) * 2) {
869                                         len = sizeof (bytes)*2;
870                                         hex[len] = '\0';
871                                 }
872                                 for (i = 0; i < len; i += 2) {
873                                         elt[0] = hex[i];
874                                         elt[1] = hex[i+1];
875                                         elt[2] = '\0';
876                                         bytes[i/2] = (uint8_t) strtol(elt, NULL, 16);
877                                 }
878                                 len = i/2;
879                                 if (bytes[0] != len - 2) {
880                                         printf ("bad length %d != %d\n", bytes[0], len - 2);
881                                         continue;
882                                 }
883                                 sum = 0x5a;
884                                 for (i = 1; i < len-1; i++)
885                                         sum += bytes[i];
886                                 if (sum != bytes[len-1]) {
887                                         printf ("bad checksum\n");
888                                         continue;
889                                 }
890                                 if ((bytes[len-2] & 0x80) == 0) {
891                                         continue;
892                                 }
893                                 if (len == 36) {
894                                         ao_xmemcpy(&telem, bytes + 1, 32);
895                                         tick = telem.generic.tick;
896                                         switch (telem.generic.type) {
897                                         case AO_TELEMETRY_SENSOR_TELEMETRUM:
898                                         case AO_TELEMETRY_SENSOR_TELEMINI:
899                                         case AO_TELEMETRY_SENSOR_TELENANO:
900                                                 if (!ao_flight_started) {
901                                                         ao_flight_ground_accel = telem.sensor.ground_accel;
902                                                         ao_config.accel_plus_g = telem.sensor.accel_plus_g;
903                                                         ao_config.accel_minus_g = telem.sensor.accel_minus_g;
904                                                         ao_flight_started = 1;
905                                                 }
906                                                 type = 'A';
907                                                 a = telem.sensor.accel;
908                                                 b = telem.sensor.pres;
909                                                 break;
910                                         }
911                                 } else if (len == 99) {
912                                         ao_flight_started = 1;
913                                         tick = uint16(bytes+1, 21);
914                                         ao_flight_ground_accel = int16(bytes+1, 7);
915                                         ao_config.accel_plus_g = int16(bytes+1, 17);
916                                         ao_config.accel_minus_g = int16(bytes+1, 19);
917                                         type = 'A';
918                                         a = int16(bytes+1, 23);
919                                         b = int16(bytes+1, 25);
920                                 } else if (len == 98) {
921                                         ao_flight_started = 1;
922                                         tick = uint16(bytes+1, 20);
923                                         ao_flight_ground_accel = int16(bytes+1, 6);
924                                         ao_config.accel_plus_g = int16(bytes+1, 16);
925                                         ao_config.accel_minus_g = int16(bytes+1, 18);
926                                         type = 'A';
927                                         a = int16(bytes+1, 22);
928                                         b = int16(bytes+1, 24);
929                                 } else {
930                                         printf("unknown len %d\n", len);
931                                         continue;
932                                 }
933                         }
934                         if (type != 'F' && !ao_flight_started)
935                                 continue;
936
937 #if TELEMEGA
938                         (void) a;
939                         (void) b;
940 #else
941                         switch (type) {
942                         case 'F':
943                                 ao_flight_ground_accel = a;
944                                 if (ao_config.accel_plus_g == 0) {
945                                         ao_config.accel_plus_g = a;
946                                         ao_config.accel_minus_g = a + 530;
947                                 }
948                                 if (ao_config.main_deploy == 0)
949                                         ao_config.main_deploy = 250;
950                                 ao_flight_started = 1;
951                                 break;
952                         case 'S':
953                                 break;
954                         case 'A':
955                                 ao_data_static.tick = tick;
956                                 ao_data_static.adc.accel = a;
957                                 ao_data_static.adc.pres_real = b;
958                                 if (b < AO_MIN_BARO_VALUE)
959                                         b = AO_MIN_BARO_VALUE;
960                                 ao_data_static.adc.pres = b;
961                                 ao_records_read++;
962                                 ao_insert();
963                                 return;
964                         case 'T':
965                                 ao_data_static.tick = tick;
966                                 ao_data_static.adc.temp = a;
967                                 ao_data_static.adc.v_batt = b;
968                                 break;
969                         case 'D':
970                         case 'G':
971                         case 'N':
972                         case 'W':
973                         case 'H':
974                                 break;
975                         }
976 #endif
977                 }
978
979         }
980 }
981 #define COUNTS_PER_G 264.8
982
983 void
984 ao_dump_state(void)
985 {
986 }
987
988 static const struct option options[] = {
989         { .name = "summary", .has_arg = 0, .val = 's' },
990         { .name = "debug", .has_arg = 0, .val = 'd' },
991         { .name = "info", .has_arg = 1, .val = 'i' },
992         { 0, 0, 0, 0},
993 };
994
995 void run_flight_fixed(char *name, FILE *f, int summary, char *info)
996 {
997         emulator_name = name;
998         emulator_in = f;
999         emulator_info = info;
1000         ao_summary = summary;
1001         ao_flight_init();
1002         ao_flight();
1003 }
1004
1005 int
1006 main (int argc, char **argv)
1007 {
1008         int     summary = 0;
1009         int     c;
1010         int     i;
1011         char    *info = NULL;
1012
1013 #if HAS_ACCEL
1014         emulator_app="full";
1015 #else
1016         emulator_app="baro";
1017 #endif
1018         while ((c = getopt_long(argc, argv, "sdi:", options, NULL)) != -1) {
1019                 switch (c) {
1020                 case 's':
1021                         summary = 1;
1022                         break;
1023                 case 'd':
1024                         ao_flight_debug = 1;
1025                         break;
1026                 case 'i':
1027                         info = optarg;
1028                         break;
1029                 }
1030         }
1031
1032         if (optind == argc)
1033                 run_flight_fixed("<stdin>", stdin, summary, info);
1034         else
1035                 for (i = optind; i < argc; i++) {
1036                         FILE    *f = fopen(argv[i], "r");
1037                         if (!f) {
1038                                 perror(argv[i]);
1039                                 continue;
1040                         }
1041                         run_flight_fixed(argv[i], f, summary, info);
1042                         fclose(f);
1043                 }
1044         exit(0);
1045 }