Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
[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       1
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
268 int16_t
269 ao_time(void)
270 {
271         return ao_data_static.tick;
272 }
273
274 void
275 ao_delay(int16_t interval)
276 {
277         return;
278 }
279
280 void
281 ao_ignite(enum ao_igniter igniter)
282 {
283         double time = (double) (ao_data_static.tick + tick_offset) / 100;
284
285         if (igniter == ao_igniter_drogue) {
286                 drogue_time = time;
287                 drogue_height = ao_k_height >> 16;
288         } else {
289                 main_time = time;
290                 main_height = ao_k_height >> 16;
291         }
292 }
293
294 struct ao_task {
295         int dummy;
296 };
297
298 #define ao_add_task(t,f,n) ((void) (t))
299
300 #define ao_log_start()
301 #define ao_log_stop()
302
303 #define AO_MS_TO_TICKS(ms)      ((ms) / 10)
304 #define AO_SEC_TO_TICKS(s)      ((s) * 100)
305
306 #define AO_FLIGHT_TEST
307
308 int     ao_flight_debug;
309
310 FILE *emulator_in;
311 char *emulator_app;
312 char *emulator_name;
313 char *emulator_info;
314 double emulator_error_max = 4;
315 double emulator_height_error_max = 20;  /* noise in the baro sensor */
316
317 void
318 ao_dump_state(void);
319
320 void
321 ao_sleep(void *wchan);
322
323 const char const * const ao_state_names[] = {
324         "startup", "idle", "pad", "boost", "fast",
325         "coast", "drogue", "main", "landed", "invalid"
326 };
327
328 struct ao_cmds {
329         void            (*func)(void);
330         const char      *help;
331 };
332
333 #define ao_xmemcpy(d,s,c) memcpy(d,s,c)
334 #define ao_xmemset(d,v,c) memset(d,v,c)
335 #define ao_xmemcmp(d,s,c) memcmp(d,s,c)
336
337 #define AO_NEED_ALTITUDE_TO_PRES 1
338 #if TELEMEGA || TELEMETRUM_V2 || EASYMINI
339 #include "ao_convert_pa.c"
340 #include <ao_ms5607.h>
341 struct ao_ms5607_prom   ao_ms5607_prom;
342 #include "ao_ms5607_convert.c"
343 #define AO_PYRO_NUM     4
344 #include <ao_pyro.h>
345 #else
346 #include "ao_convert.c"
347 #endif
348
349 #include <ao_config.h>
350 #include <ao_fake_flight.h>
351
352 #define ao_config_get()
353
354 struct ao_config ao_config;
355
356 #define DATA_TO_XDATA(x) (x)
357
358
359 extern int16_t ao_ground_accel, ao_flight_accel;
360 extern int16_t ao_accel_2g;
361
362 typedef int16_t accel_t;
363
364 extern uint16_t ao_sample_tick;
365
366 extern alt_t    ao_sample_height;
367 extern accel_t  ao_sample_accel;
368 extern int32_t  ao_accel_scale;
369 extern alt_t    ao_ground_height;
370 extern alt_t    ao_sample_alt;
371
372 double ao_sample_qangle;
373
374 int ao_sample_prev_tick;
375 uint16_t        prev_tick;
376
377
378 #include "ao_kalman.c"
379 #include "ao_sqrt.c"
380 #include "ao_sample.c"
381 #include "ao_flight.c"
382 #if TELEMEGA
383 #define AO_PYRO_NUM     4
384
385 #define AO_PYRO_0       0
386 #define AO_PYRO_1       1
387 #define AO_PYRO_2       2
388 #define AO_PYRO_3       3
389
390 #define PYRO_DBG        1
391
392 static void
393 ao_pyro_pin_set(uint8_t pin, uint8_t value)
394 {
395         printf ("set pyro %d %d\n", pin, value);
396 }
397
398 #include "ao_pyro.c"
399 #endif
400
401 #define to_double(f)    ((f) / 65536.0)
402
403 static int      ao_records_read = 0;
404 static int      ao_eof_read = 0;
405 static int      ao_flight_ground_accel;
406 static int      ao_flight_started = 0;
407 static int      ao_test_max_height;
408 static double   ao_test_max_height_time;
409 static int      ao_test_main_height;
410 static double   ao_test_main_height_time;
411 static double   ao_test_landed_time;
412 static double   ao_test_landed_height;
413 static double   ao_test_landed_time;
414 static int      landed_set;
415 static double   landed_time;
416 static double   landed_height;
417
418 #if HAS_MPU6000
419 static struct ao_mpu6000_sample ao_ground_mpu6000;
420 #endif
421
422 void
423 ao_test_exit(void)
424 {
425         double  drogue_error;
426         double  main_error;
427         double  landed_error;
428         double  landed_time_error;
429
430         if (!ao_test_main_height_time) {
431                 ao_test_main_height_time = ao_test_max_height_time;
432                 ao_test_main_height = ao_test_max_height;
433         }
434         drogue_error = fabs(ao_test_max_height_time - drogue_time);
435         main_error = fabs(ao_test_main_height_time - main_time);
436         landed_error = fabs(ao_test_landed_height - landed_height);
437         landed_time_error = ao_test_landed_time - landed_time;
438         if (drogue_error > emulator_error_max || main_error > emulator_error_max) {
439                 printf ("%s %s\n",
440                         emulator_app, emulator_name);
441                 if (emulator_info)
442                         printf ("\t%s\n", emulator_info);
443                 printf ("\tApogee error %g\n", drogue_error);
444                 printf ("\tMain error %g\n", main_error);
445                 printf ("\tLanded height error %g\n", landed_error);
446                 printf ("\tLanded time error %g\n", landed_time_error);
447                 printf ("\tActual: apogee: %d at %7.2f main: %d at %7.2f landed %7.2f at %7.2f\n",
448                         ao_test_max_height, ao_test_max_height_time,
449                         ao_test_main_height, ao_test_main_height_time,
450                         ao_test_landed_height, ao_test_landed_time);
451                 printf ("\tComputed: apogee: %d at %7.2f main: %d at %7.2f landed %7.2f at %7.2f\n",
452                         drogue_height, drogue_time, main_height, main_time,
453                         landed_height, landed_time);
454                 exit (1);
455         }
456         exit(0);
457 }
458
459 #ifdef TELEMEGA
460 struct ao_azel {
461         int     az;
462         int     el;
463 };
464
465 static void
466 azel (struct ao_azel *r, struct ao_quaternion *q)
467 {
468         double  v;
469
470         r->az = floor (atan2(q->y, q->x) * 180/M_PI + 0.5);
471         v = sqrt (q->x*q->x + q->y*q->y);
472         r->el = floor (atan2(q->z, v) * 180/M_PI + 0.5);
473 }
474 #endif
475
476 void
477 ao_insert(void)
478 {
479         double  time;
480
481         ao_data_ring[ao_data_head] = ao_data_static;
482         ao_data_head = ao_data_ring_next(ao_data_head);
483         if (ao_flight_state != ao_flight_startup) {
484 #if HAS_ACCEL
485                 double  accel = ((ao_flight_ground_accel - ao_data_accel_cook(&ao_data_static)) * GRAVITY * 2.0) /
486                         (ao_config.accel_minus_g - ao_config.accel_plus_g);
487 #else
488                 double  accel = 0.0;
489 #endif
490 #if TELEMEGA || TELEMETRUM_V2 || EASYMINI
491                 double  height;
492
493                 ao_ms5607_convert(&ao_data_static.ms5607_raw, &ao_data_static.ms5607_cooked);
494                 height = ao_pa_to_altitude(ao_data_static.ms5607_cooked.pres) - ao_ground_height;
495 #else
496                 double  height = ao_pres_to_altitude(ao_data_static.adc.pres_real) - ao_ground_height;
497 #endif
498
499                 (void) accel;
500                 if (!tick_offset)
501                         tick_offset = -ao_data_static.tick;
502                 if ((prev_tick - ao_data_static.tick) > 0x400)
503                         tick_offset += 65536;
504                 prev_tick = ao_data_static.tick;
505                 time = (double) (ao_data_static.tick + tick_offset) / 100;
506
507                 if (ao_test_max_height < height) {
508                         ao_test_max_height = height;
509                         ao_test_max_height_time = time;
510                         ao_test_landed_height = height;
511                         ao_test_landed_time = time;
512                 }
513                 if (height > ao_config.main_deploy) {
514                         ao_test_main_height_time = time;
515                         ao_test_main_height = height;
516                 }
517
518                 if (ao_test_landed_height > height) {
519                         ao_test_landed_height = height;
520                         ao_test_landed_time = time;
521                 }
522
523                 if (ao_flight_state == ao_flight_landed && !landed_set) {
524                         landed_set = 1;
525                         landed_time = time;
526                         landed_height = height;
527                 }
528
529                 if (!ao_summary) {
530 #if TELEMEGA
531                         static struct ao_quaternion     ao_ground_mag;
532                         static int                      ao_ground_mag_set;
533
534                         if (!ao_ground_mag_set) {
535                                 ao_quaternion_init_vector (&ao_ground_mag,
536                                                            ao_data_mag_across(&ao_data_static),
537                                                            ao_data_mag_through(&ao_data_static),
538                                                            ao_data_mag_along(&ao_data_static));
539                                 ao_quaternion_normalize(&ao_ground_mag, &ao_ground_mag);
540                                 ao_quaternion_rotate(&ao_ground_mag, &ao_ground_mag, &ao_rotation);
541                                 ao_ground_mag_set = 1;
542                         }
543
544                         struct ao_quaternion            ao_mag, ao_mag_rot;
545
546                         ao_quaternion_init_vector(&ao_mag,
547                                                   ao_data_mag_across(&ao_data_static),
548                                                   ao_data_mag_through(&ao_data_static),
549                                                   ao_data_mag_along(&ao_data_static));
550
551                         ao_quaternion_normalize(&ao_mag, &ao_mag);
552                         ao_quaternion_rotate(&ao_mag_rot, &ao_mag, &ao_rotation);
553                         
554                         float                           ao_dot;
555                         int                             ao_mag_angle;
556
557                         ao_dot = ao_quaternion_dot(&ao_mag_rot, &ao_ground_mag);
558
559                         struct ao_azel                  ground_azel, mag_azel, rot_azel;
560
561                         azel(&ground_azel, &ao_ground_mag);
562                         azel(&mag_azel, &ao_mag);
563                         azel(&rot_azel, &ao_mag_rot);
564
565                         ao_mag_angle = floor (acos(ao_dot) * 180 / M_PI + 0.5);
566
567                         (void) ao_mag_angle;
568
569                         static struct ao_quaternion     ao_x = { .r = 0, .x = 1, .y = 0, .z = 0 };
570                         struct ao_quaternion            ao_out;
571
572                         ao_quaternion_rotate(&ao_out, &ao_x, &ao_rotation);
573
574 #if 0
575                         int     out = floor (atan2(ao_out.y, ao_out.x) * 180 / M_PI);
576
577                         printf ("%7.2f state %-8.8s height %8.4f tilt %4d rot %4d mag_tilt %4d mag_rot %4d\n",
578                                 time,
579                                 ao_state_names[ao_flight_state],
580                                 ao_k_height / 65536.0,
581                                 ao_sample_orient, out,
582                                 mag_azel.el,
583                                 mag_azel.az);
584 #endif
585 #if 0
586                         printf ("%7.2f state %-8.8s height %8.4f tilt %4d rot %4d dist %12.2f gps_tilt %4d gps_sats %2d\n",
587                                 time,
588                                 ao_state_names[ao_flight_state],
589                                 ao_k_height / 65536.0,
590                                 ao_sample_orient, out,
591                                 ao_distance_from_pad(),
592                                 (int) floor (ao_gps_angle() + 0.5),
593                                 (ao_gps_static.flags & 0xf) * 10);
594
595 #endif
596 #if 0
597                         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",
598                                 ao_state_names[ao_flight_state],
599                                 ground_azel.az, ground_azel.el,
600                                 mag_azel.az, mag_azel.el,
601                                 rot_azel.az, rot_azel.el,
602                                 ground_azel.el - rot_azel.el,
603                                 ground_azel.az - rot_azel.az,
604                                 ao_mag_angle,
605                                 ao_sample_orient,
606                                 ao_ground_mag.x,
607                                 ao_ground_mag.y,
608                                 ao_ground_mag.z,
609                                 ao_mag.x,
610                                 ao_mag.y,
611                                 ao_mag.z,
612                                 ao_mag_rot.x,
613                                 ao_mag_rot.y,
614                                 ao_mag_rot.z);
615 #endif
616 #endif
617
618 #if 1
619                         printf("%7.2f height %8.2f accel %8.3f "
620 #if TELEMEGA && 1
621                                "angle %5d "
622                                "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 "
623 #endif
624                                "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",
625                                time,
626                                height,
627                                accel,
628 #if TELEMEGA && 1
629                                ao_sample_orient,
630
631                                ao_mpu6000_accel(ao_data_static.mpu6000.accel_x),
632                                ao_mpu6000_accel(ao_data_static.mpu6000.accel_y),
633                                ao_mpu6000_accel(ao_data_static.mpu6000.accel_z),
634                                ao_mpu6000_gyro(ao_data_static.mpu6000.gyro_x - ao_ground_mpu6000.gyro_x),
635                                ao_mpu6000_gyro(ao_data_static.mpu6000.gyro_y - ao_ground_mpu6000.gyro_y),
636                                ao_mpu6000_gyro(ao_data_static.mpu6000.gyro_z - ao_ground_mpu6000.gyro_z),
637                                ao_data_static.hmc5883.x,
638                                ao_data_static.hmc5883.y,
639                                ao_data_static.hmc5883.z,
640                                ao_mag_angle,
641 #endif
642                                ao_state_names[ao_flight_state],
643                                ao_k_height / 65536.0,
644                                ao_k_speed / 65536.0 / 16.0,
645                                ao_k_accel / 65536.0 / 16.0,
646                                ao_avg_height,
647                                drogue_height,
648                                main_height,
649                                ao_error_h_sq_avg);
650 #endif
651                         
652 //                      if (ao_flight_state == ao_flight_landed)
653 //                              ao_test_exit();
654                 }
655         }
656 }
657
658
659 uint16_t
660 uint16(uint8_t *bytes, int off)
661 {
662         return (uint16_t) bytes[off] | (((uint16_t) bytes[off+1]) << 8);
663 }
664
665 int16_t
666 int16(uint8_t *bytes, int off)
667 {
668         return (int16_t) uint16(bytes, off);
669 }
670
671 uint32_t
672 uint32(uint8_t *bytes, int off)
673 {
674         return (uint32_t) bytes[off] | (((uint32_t) bytes[off+1]) << 8) |
675                 (((uint32_t) bytes[off+2]) << 16) |
676                 (((uint32_t) bytes[off+3]) << 24);
677 }
678
679 int32_t
680 int32(uint8_t *bytes, int off)
681 {
682         return (int32_t) uint32(bytes, off);
683 }
684
685 uint32_t
686 uint24(uint8_t *bytes, int off)
687 {
688         return (uint32_t) bytes[off] | (((uint32_t) bytes[off+1]) << 8) |
689                 (((uint32_t) bytes[off+2]) << 16);
690 }
691
692 int32_t
693 int24(uint8_t *bytes, int off)
694 {
695         return (int32_t) uint24(bytes, off);
696 }
697
698 static int log_format;
699
700 void
701 ao_sleep(void *wchan)
702 {
703         if (wchan == &ao_data_head) {
704                 char            type = 0;
705                 uint16_t        tick = 0;
706                 uint16_t        a = 0, b = 0;
707                 uint8_t         bytes[1024];
708                 union ao_telemetry_all  telem;
709                 char            line[1024];
710                 char            *saveptr;
711                 char            *l;
712                 char            *words[64];
713                 int             nword;
714
715 #if TELEMEGA
716                 if (ao_flight_state >= ao_flight_boost && ao_flight_state < ao_flight_landed)
717                         ao_pyro_check();
718 #endif
719                 for (;;) {
720                         if (ao_records_read > 2 && ao_flight_state == ao_flight_startup)
721                         {
722
723 #if TELEMEGA
724                                 ao_data_static.mpu6000 = ao_ground_mpu6000;
725 #endif
726 #if TELEMETRUM_V1
727                                 ao_data_static.adc.accel = ao_flight_ground_accel;
728 #endif
729
730                                 ao_insert();
731                                 return;
732                         }
733
734                         if (!fgets(line, sizeof (line), emulator_in)) {
735                                 if (++ao_eof_read >= 1000) {
736                                         if (!ao_summary)
737                                                 printf ("no more data, exiting simulation\n");
738                                         ao_test_exit();
739                                 }
740                                 ao_data_static.tick += 10;
741                                 ao_insert();
742                                 return;
743                         }
744                         l = line;
745                         for (nword = 0; nword < 64; nword++) {
746                                 words[nword] = strtok_r(l, " \t\n", &saveptr);
747                                 l = NULL;
748                                 if (words[nword] == NULL)
749                                         break;
750                         }
751 #if TELEMEGA
752                         if ((log_format == AO_LOG_FORMAT_TELEMEGA_OLD || log_format == AO_LOG_FORMAT_TELEMEGA) && nword == 30 && strlen(words[0]) == 1) {
753                                 int     i;
754                                 struct ao_ms5607_value  value;
755
756                                 type = words[0][0];
757                                 tick = strtoul(words[1], NULL, 16);
758 //                              printf ("%c %04x", type, tick);
759                                 for (i = 2; i < nword; i++) {
760                                         bytes[i - 2] = strtoul(words[i], NULL, 16);
761 //                                      printf(" %02x", bytes[i-2]);
762                                 }
763 //                              printf ("\n");
764                                 switch (type) {
765                                 case 'F':
766                                         ao_flight_ground_accel = int16(bytes, 2);
767                                         ao_flight_started = 1;
768                                         ao_ground_pres = int32(bytes, 4);
769                                         ao_ground_height = ao_pa_to_altitude(ao_ground_pres);
770                                         ao_ground_accel_along = int16(bytes, 8);
771                                         ao_ground_accel_across = int16(bytes, 10);
772                                         ao_ground_accel_through = int16(bytes, 12);
773                                         ao_ground_roll = int16(bytes, 14);
774                                         ao_ground_pitch = int16(bytes, 16);
775                                         ao_ground_yaw = int16(bytes, 18);
776                                         ao_ground_mpu6000.accel_x = ao_ground_accel_across;
777                                         ao_ground_mpu6000.accel_y = ao_ground_accel_along;
778                                         ao_ground_mpu6000.accel_z = ao_ground_accel_through;
779                                         ao_ground_mpu6000.gyro_x = ao_ground_pitch >> 9;
780                                         ao_ground_mpu6000.gyro_y = ao_ground_roll >> 9;
781                                         ao_ground_mpu6000.gyro_z = ao_ground_yaw >> 9;
782                                         break;
783                                 case 'A':
784                                         ao_data_static.tick = tick;
785                                         ao_data_static.ms5607_raw.pres = int32(bytes, 0);
786                                         ao_data_static.ms5607_raw.temp = int32(bytes, 4);
787                                         ao_ms5607_convert(&ao_data_static.ms5607_raw, &value);
788                                         ao_data_static.mpu6000.accel_x = int16(bytes, 8);
789                                         ao_data_static.mpu6000.accel_y = int16(bytes, 10);
790                                         ao_data_static.mpu6000.accel_z = int16(bytes, 12);
791                                         ao_data_static.mpu6000.gyro_x = int16(bytes, 14);
792                                         ao_data_static.mpu6000.gyro_y = int16(bytes, 16);
793                                         ao_data_static.mpu6000.gyro_z = int16(bytes, 18);
794                                         ao_data_static.hmc5883.x = int16(bytes, 20);
795                                         ao_data_static.hmc5883.y = int16(bytes, 22);
796                                         ao_data_static.hmc5883.z = int16(bytes, 24);
797 #if HAS_MMA655X
798                                         ao_data_static.mma655x = int16(bytes, 26);
799                                         if (ao_config.pad_orientation != AO_PAD_ORIENTATION_ANTENNA_UP)
800                                                 ao_data_static.mma655x = ao_data_accel_invert(ao_data_static.mma655x);
801 #endif
802                                         ao_records_read++;
803                                         ao_insert();
804                                         return;
805                                 case 'G':
806                                         ao_gps_prev = ao_gps_static;
807                                         ao_gps_static.tick = tick;
808                                         ao_gps_static.latitude = int32(bytes, 0);
809                                         ao_gps_static.longitude = int32(bytes, 4);
810                                         {
811                                                 int32_t altitude = int32(bytes, 8);
812                                                 AO_TELEMETRY_LOCATION_SET_ALTITUDE(&ao_gps_static, altitude);
813                                         }
814                                         ao_gps_static.flags = bytes[13];
815                                         if (!ao_gps_count)
816                                                 ao_gps_first = ao_gps_static;
817                                         ao_gps_count++;
818                                         break;
819                                 }
820                                 continue;
821                         } else if (nword == 3 && strcmp(words[0], "ms5607") == 0) {
822                                 if (strcmp(words[1], "reserved:") == 0)
823                                         ao_ms5607_prom.reserved = strtoul(words[2], NULL, 10);
824                                 else if (strcmp(words[1], "sens:") == 0)
825                                         ao_ms5607_prom.sens = strtoul(words[2], NULL, 10);
826                                 else if (strcmp(words[1], "off:") == 0)
827                                         ao_ms5607_prom.off = strtoul(words[2], NULL, 10);
828                                 else if (strcmp(words[1], "tcs:") == 0)
829                                         ao_ms5607_prom.tcs = strtoul(words[2], NULL, 10);
830                                 else if (strcmp(words[1], "tco:") == 0)
831                                         ao_ms5607_prom.tco = strtoul(words[2], NULL, 10);
832                                 else if (strcmp(words[1], "tref:") == 0)
833                                         ao_ms5607_prom.tref = strtoul(words[2], NULL, 10);
834                                 else if (strcmp(words[1], "tempsens:") == 0)
835                                         ao_ms5607_prom.tempsens = strtoul(words[2], NULL, 10);
836                                 else if (strcmp(words[1], "crc:") == 0)
837                                         ao_ms5607_prom.crc = strtoul(words[2], NULL, 10);
838                                 continue;
839                         } else if (nword >= 3 && strcmp(words[0], "Pyro") == 0) {
840                                 int     p = strtoul(words[1], NULL, 10);
841                                 int     i, j;
842                                 struct ao_pyro  *pyro = &ao_config.pyro[p];
843
844                                 for (i = 2; i < nword; i++) {
845                                         for (j = 0; j < NUM_PYRO_VALUES; j++)
846                                                 if (!strcmp (words[i], ao_pyro_values[j].name))
847                                                         break;
848                                         if (j == NUM_PYRO_VALUES)
849                                                 continue;
850                                         pyro->flags |= ao_pyro_values[j].flag;
851                                         if (ao_pyro_values[j].offset != NO_VALUE && i + 1 < nword) {
852                                                 int16_t val = strtoul(words[++i], NULL, 10);
853                                                 printf("pyro %d condition %s value %d\n", p, words[i-1], val);
854                                                 *((int16_t *) ((char *) pyro + ao_pyro_values[j].offset)) = val;
855                                         }
856                                 }
857                         }
858 #endif
859 #if EASYMINI
860                         if ((log_format == AO_LOG_FORMAT_EASYMINI1 || log_format == AO_LOG_FORMAT_EASYMINI2) && nword == 14 && strlen(words[0]) == 1) {
861                                 int     i;
862                                 struct ao_ms5607_value  value;
863
864                                 type = words[0][0];
865                                 tick = strtoul(words[1], NULL, 16);
866 //                              printf ("%c %04x", type, tick);
867                                 for (i = 2; i < nword; i++) {
868                                         bytes[i - 2] = strtoul(words[i], NULL, 16);
869 //                                      printf(" %02x", bytes[i-2]);
870                                 }
871 //                              printf ("\n");
872                                 switch (type) {
873                                 case 'F':
874                                         ao_flight_started = 1;
875                                         ao_ground_pres = uint32(bytes, 4);
876                                         ao_ground_height = ao_pa_to_altitude(ao_ground_pres);
877 #if 0
878                                         printf("ground pres %d height %d\n", ao_ground_pres, ao_ground_height);
879                                         printf("sens %d off %d tcs %d tco %d tref %d tempsens %d crc %d\n",
880                                                ao_ms5607_prom.sens,
881                                                ao_ms5607_prom.off,
882                                                ao_ms5607_prom.tcs,
883                                                ao_ms5607_prom.tco,
884                                                ao_ms5607_prom.tref,
885                                                ao_ms5607_prom.tempsens,
886                                                ao_ms5607_prom.crc);
887 #endif
888                                         break;
889                                 case 'A':
890                                         ao_data_static.tick = tick;
891                                         ao_data_static.ms5607_raw.pres = int24(bytes, 0);
892                                         ao_data_static.ms5607_raw.temp = int24(bytes, 3);
893 #if 0
894                                         printf("raw pres %d temp %d\n",
895                                                ao_data_static.ms5607_raw.pres,
896                                                ao_data_static.ms5607_raw.temp);
897 #endif
898                                         ao_ms5607_convert(&ao_data_static.ms5607_raw, &value);
899 //                                      printf("pres %d height %d\n", value.pres, ao_pa_to_altitude(value.pres));
900                                         ao_records_read++;
901                                         ao_insert();
902                                         return;
903                                 }
904                                 continue;
905                         } else if (nword == 3 && strcmp(words[0], "ms5607") == 0) {
906                                 if (strcmp(words[1], "reserved:") == 0)
907                                         ao_ms5607_prom.reserved = strtoul(words[2], NULL, 10);
908                                 else if (strcmp(words[1], "sens:") == 0)
909                                         ao_ms5607_prom.sens = strtoul(words[2], NULL, 10);
910                                 else if (strcmp(words[1], "off:") == 0)
911                                         ao_ms5607_prom.off = strtoul(words[2], NULL, 10);
912                                 else if (strcmp(words[1], "tcs:") == 0)
913                                         ao_ms5607_prom.tcs = strtoul(words[2], NULL, 10);
914                                 else if (strcmp(words[1], "tco:") == 0)
915                                         ao_ms5607_prom.tco = strtoul(words[2], NULL, 10);
916                                 else if (strcmp(words[1], "tref:") == 0)
917                                         ao_ms5607_prom.tref = strtoul(words[2], NULL, 10);
918                                 else if (strcmp(words[1], "tempsens:") == 0)
919                                         ao_ms5607_prom.tempsens = strtoul(words[2], NULL, 10);
920                                 else if (strcmp(words[1], "crc:") == 0)
921                                         ao_ms5607_prom.crc = strtoul(words[2], NULL, 10);
922                                 continue;
923                         }
924 #endif
925 #if TELEMETRUM_V2
926                         if (log_format == AO_LOG_FORMAT_TELEMETRUM && nword == 14 && strlen(words[0]) == 1) {
927                                 int     i;
928                                 struct ao_ms5607_value  value;
929
930                                 type = words[0][0];
931                                 tick = strtoul(words[1], NULL, 16);
932 //                              printf ("%c %04x", type, tick);
933                                 for (i = 2; i < nword; i++) {
934                                         bytes[i - 2] = strtoul(words[i], NULL, 16);
935 //                                      printf(" %02x", bytes[i-2]);
936                                 }
937 //                              printf ("\n");
938                                 switch (type) {
939                                 case 'F':
940                                         ao_flight_ground_accel = int16(bytes, 2);
941                                         ao_flight_started = 1;
942                                         ao_ground_pres = int32(bytes, 4);
943                                         ao_ground_height = ao_pa_to_altitude(ao_ground_pres);
944                                         break;
945                                 case 'A':
946                                         ao_data_static.tick = tick;
947                                         ao_data_static.ms5607_raw.pres = int32(bytes, 0);
948                                         ao_data_static.ms5607_raw.temp = int32(bytes, 4);
949                                         ao_ms5607_convert(&ao_data_static.ms5607_raw, &value);
950                                         ao_data_static.mma655x = int16(bytes, 8);
951                                         ao_records_read++;
952                                         ao_insert();
953                                         return;
954                                 }
955                                 continue;
956                         } else if (nword == 3 && strcmp(words[0], "ms5607") == 0) {
957                                 if (strcmp(words[1], "reserved:") == 0)
958                                         ao_ms5607_prom.reserved = strtoul(words[2], NULL, 10);
959                                 else if (strcmp(words[1], "sens:") == 0)
960                                         ao_ms5607_prom.sens = strtoul(words[2], NULL, 10);
961                                 else if (strcmp(words[1], "off:") == 0)
962                                         ao_ms5607_prom.off = strtoul(words[2], NULL, 10);
963                                 else if (strcmp(words[1], "tcs:") == 0)
964                                         ao_ms5607_prom.tcs = strtoul(words[2], NULL, 10);
965                                 else if (strcmp(words[1], "tco:") == 0)
966                                         ao_ms5607_prom.tco = strtoul(words[2], NULL, 10);
967                                 else if (strcmp(words[1], "tref:") == 0)
968                                         ao_ms5607_prom.tref = strtoul(words[2], NULL, 10);
969                                 else if (strcmp(words[1], "tempsens:") == 0)
970                                         ao_ms5607_prom.tempsens = strtoul(words[2], NULL, 10);
971                                 else if (strcmp(words[1], "crc:") == 0)
972                                         ao_ms5607_prom.crc = strtoul(words[2], NULL, 10);
973                                 continue;
974                         }
975 #endif
976 #if TELEMETRUM_V1
977                         if (nword == 4 && log_format != AO_LOG_FORMAT_TELEMEGA) {
978                                 type = words[0][0];
979                                 tick = strtoul(words[1], NULL, 16);
980                                 a = strtoul(words[2], NULL, 16);
981                                 b = strtoul(words[3], NULL, 16);
982                                 if (type == 'P')
983                                         type = 'A';
984                         }
985 #endif
986                         else if (nword == 2 && strcmp(words[0], "log-format") == 0) {
987                                 log_format = strtoul(words[1], NULL, 10);
988                         } else if (nword >= 6 && strcmp(words[0], "Accel") == 0) {
989                                 ao_config.accel_plus_g = atoi(words[3]);
990                                 ao_config.accel_minus_g = atoi(words[5]);
991 #ifdef TELEMEGA
992                         } else if (nword >= 8 && strcmp(words[0], "IMU") == 0) {
993                                 ao_config.accel_zero_along = atoi(words[3]);
994                                 ao_config.accel_zero_across = atoi(words[5]);
995                                 ao_config.accel_zero_through = atoi(words[7]);
996 #endif
997                         } else if (nword >= 4 && strcmp(words[0], "Main") == 0) {
998                                 ao_config.main_deploy = atoi(words[2]);
999                         } else if (nword >= 3 && strcmp(words[0], "Apogee") == 0 &&
1000                                    strcmp(words[1], "lockout:") == 0) {
1001                                 ao_config.apogee_lockout = atoi(words[2]);
1002                         } else if (nword >= 3 && strcmp(words[0], "Pad") == 0 &&
1003                                    strcmp(words[1], "orientation:") == 0) {
1004                                 ao_config.pad_orientation = atoi(words[2]);
1005                         } else if (nword >= 36 && strcmp(words[0], "CALL") == 0) {
1006                                 tick = atoi(words[10]);
1007                                 if (!ao_flight_started) {
1008                                         type = 'F';
1009                                         a = atoi(words[26]);
1010                                         ao_flight_started = 1;
1011                                 } else {
1012                                         type = 'A';
1013                                         a = atoi(words[12]);
1014                                         b = atoi(words[14]);
1015                                 }
1016                         } else if (nword == 3 && strcmp(words[0], "BARO") == 0) {
1017                                 tick = strtol(words[1], NULL, 16);
1018                                 a = 16384 - 328;
1019                                 b = strtol(words[2], NULL, 10);
1020                                 type = 'A';
1021                                 if (!ao_flight_started) {
1022                                         ao_flight_ground_accel = 16384 - 328;
1023                                         ao_config.accel_plus_g = 16384 - 328;
1024                                         ao_config.accel_minus_g = 16384 + 328;
1025                                         ao_flight_started = 1;
1026                                 }
1027                         } else if (nword == 2 && strcmp(words[0], "TELEM") == 0) {
1028                                 __xdata char    *hex = words[1];
1029                                 char    elt[3];
1030                                 int     i, len;
1031                                 uint8_t sum;
1032
1033                                 len = strlen(hex);
1034                                 if (len > sizeof (bytes) * 2) {
1035                                         len = sizeof (bytes)*2;
1036                                         hex[len] = '\0';
1037                                 }
1038                                 for (i = 0; i < len; i += 2) {
1039                                         elt[0] = hex[i];
1040                                         elt[1] = hex[i+1];
1041                                         elt[2] = '\0';
1042                                         bytes[i/2] = (uint8_t) strtol(elt, NULL, 16);
1043                                 }
1044                                 len = i/2;
1045                                 if (bytes[0] != len - 2) {
1046                                         printf ("bad length %d != %d\n", bytes[0], len - 2);
1047                                         continue;
1048                                 }
1049                                 sum = 0x5a;
1050                                 for (i = 1; i < len-1; i++)
1051                                         sum += bytes[i];
1052                                 if (sum != bytes[len-1]) {
1053                                         printf ("bad checksum\n");
1054                                         continue;
1055                                 }
1056                                 if ((bytes[len-2] & 0x80) == 0) {
1057                                         continue;
1058                                 }
1059                                 if (len == 36) {
1060                                         ao_xmemcpy(&telem, bytes + 1, 32);
1061                                         tick = telem.generic.tick;
1062                                         switch (telem.generic.type) {
1063                                         case AO_TELEMETRY_SENSOR_TELEMETRUM:
1064                                         case AO_TELEMETRY_SENSOR_TELEMINI:
1065                                         case AO_TELEMETRY_SENSOR_TELENANO:
1066                                                 if (!ao_flight_started) {
1067                                                         ao_flight_ground_accel = telem.sensor.ground_accel;
1068                                                         ao_config.accel_plus_g = telem.sensor.accel_plus_g;
1069                                                         ao_config.accel_minus_g = telem.sensor.accel_minus_g;
1070                                                         ao_flight_started = 1;
1071                                                 }
1072                                                 type = 'A';
1073                                                 a = telem.sensor.accel;
1074                                                 b = telem.sensor.pres;
1075                                                 break;
1076                                         }
1077                                 } else if (len == 99) {
1078                                         ao_flight_started = 1;
1079                                         tick = uint16(bytes+1, 21);
1080                                         ao_flight_ground_accel = int16(bytes+1, 7);
1081                                         ao_config.accel_plus_g = int16(bytes+1, 17);
1082                                         ao_config.accel_minus_g = int16(bytes+1, 19);
1083                                         type = 'A';
1084                                         a = int16(bytes+1, 23);
1085                                         b = int16(bytes+1, 25);
1086                                 } else if (len == 98) {
1087                                         ao_flight_started = 1;
1088                                         tick = uint16(bytes+1, 20);
1089                                         ao_flight_ground_accel = int16(bytes+1, 6);
1090                                         ao_config.accel_plus_g = int16(bytes+1, 16);
1091                                         ao_config.accel_minus_g = int16(bytes+1, 18);
1092                                         type = 'A';
1093                                         a = int16(bytes+1, 22);
1094                                         b = int16(bytes+1, 24);
1095                                 } else {
1096                                         printf("unknown len %d\n", len);
1097                                         continue;
1098                                 }
1099                         }
1100                         if (type != 'F' && !ao_flight_started)
1101                                 continue;
1102
1103 #if TELEMEGA || TELEMETRUM_V2 || EASYMINI
1104                         (void) a;
1105                         (void) b;
1106 #else
1107                         switch (type) {
1108                         case 'F':
1109                                 ao_flight_ground_accel = a;
1110                                 if (ao_config.accel_plus_g == 0) {
1111                                         ao_config.accel_plus_g = a;
1112                                         ao_config.accel_minus_g = a + 530;
1113                                 }
1114                                 if (ao_config.main_deploy == 0)
1115                                         ao_config.main_deploy = 250;
1116                                 ao_flight_started = 1;
1117                                 break;
1118                         case 'S':
1119                                 break;
1120                         case 'A':
1121                                 ao_data_static.tick = tick;
1122                                 ao_data_static.adc.accel = a;
1123                                 ao_data_static.adc.pres_real = b;
1124                                 ao_data_static.adc.pres = b;
1125                                 ao_records_read++;
1126                                 ao_insert();
1127                                 return;
1128                         case 'T':
1129                                 ao_data_static.tick = tick;
1130                                 ao_data_static.adc.temp = a;
1131                                 ao_data_static.adc.v_batt = b;
1132                                 break;
1133                         case 'D':
1134                         case 'G':
1135                         case 'N':
1136                         case 'W':
1137                         case 'H':
1138                                 break;
1139                         }
1140 #endif
1141                 }
1142
1143         }
1144 }
1145 #define COUNTS_PER_G 264.8
1146
1147 void
1148 ao_dump_state(void)
1149 {
1150 }
1151
1152 static const struct option options[] = {
1153         { .name = "summary", .has_arg = 0, .val = 's' },
1154         { .name = "debug", .has_arg = 0, .val = 'd' },
1155         { .name = "info", .has_arg = 1, .val = 'i' },
1156         { 0, 0, 0, 0},
1157 };
1158
1159 void run_flight_fixed(char *name, FILE *f, int summary, char *info)
1160 {
1161         emulator_name = name;
1162         emulator_in = f;
1163         emulator_info = info;
1164         ao_summary = summary;
1165
1166         ao_flight_init();
1167         ao_flight();
1168 }
1169
1170 int
1171 main (int argc, char **argv)
1172 {
1173         int     summary = 0;
1174         int     c;
1175         int     i;
1176         char    *info = NULL;
1177
1178 #if HAS_ACCEL
1179         emulator_app="full";
1180 #else
1181         emulator_app="baro";
1182 #endif
1183         while ((c = getopt_long(argc, argv, "sdi:", options, NULL)) != -1) {
1184                 switch (c) {
1185                 case 's':
1186                         summary = 1;
1187                         break;
1188                 case 'd':
1189                         ao_flight_debug = 1;
1190                         break;
1191                 case 'i':
1192                         info = optarg;
1193                         break;
1194                 }
1195         }
1196
1197         if (optind == argc)
1198                 run_flight_fixed("<stdin>", stdin, summary, info);
1199         else
1200                 for (i = optind; i < argc; i++) {
1201                         FILE    *f = fopen(argv[i], "r");
1202                         if (!f) {
1203                                 perror(argv[i]);
1204                                 continue;
1205                         }
1206                         run_flight_fixed(argv[i], f, summary, info);
1207                         fclose(f);
1208                 }
1209         exit(0);
1210 }