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