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