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