Add DSP code to filter data, allowing for integration/differentiation
[fw/altos] / ao-tools / ao-postflight / ao-postflight.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 #include <string.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <getopt.h>
25 #include "cc-usb.h"
26 #include "cc.h"
27
28 #define NUM_BLOCK       512
29
30 static const char *state_names[] = {
31         "startup",
32         "idle",
33         "pad",
34         "boost",
35         "fast",
36         "coast",
37         "drogue",
38         "main",
39         "landed",
40         "invalid"
41 };
42
43 void
44 analyse_flight(struct cc_flightraw *f, FILE *summary_file, FILE *detail_file)
45 {
46         double  height;
47         double  accel;
48         double  speed;
49         double  boost_start, boost_stop;
50         double  min_pres;
51         int     i;
52         int     pres_i, accel_i, speed_i;
53         int     boost_start_set = 0;
54         int     boost_stop_set = 0;
55         enum ao_flight_state    state;
56         double  state_start, state_stop;
57         struct cc_flightcooked *cooked;
58         double  apogee;
59
60         fprintf(summary_file, "Flight:  %9d\nSerial:  %9d\n",
61                 f->flight, f->serial);
62         boost_start = f->accel.data[0].time;
63         boost_stop = f->accel.data[f->accel.num-1].time;
64         for (i = 0; i < f->state.num; i++) {
65                 if (f->state.data[i].value == ao_flight_boost && !boost_start_set) {
66                         boost_start = f->state.data[i].time;
67                         boost_start_set = 1;
68                 }
69                 if (f->state.data[i].value > ao_flight_boost && !boost_stop_set) {
70                         boost_stop = f->state.data[i].time;
71                         boost_stop_set = 1;
72                 }
73         }
74
75         pres_i = cc_timedata_min(&f->pres, f->pres.data[0].time,
76                                  f->pres.data[f->pres.num-1].time);
77         if (pres_i >= 0)
78         {
79                 min_pres = f->pres.data[pres_i].value;
80                 height = cc_barometer_to_altitude(min_pres) -
81                         cc_barometer_to_altitude(f->ground_pres);
82                 fprintf(summary_file, "Max height: %9.2fm    %9.2fft   %9.2fs\n",
83                         height, height * 100 / 2.54 / 12,
84                         (f->pres.data[pres_i].time - boost_start) / 100.0);
85                 apogee = f->pres.data[pres_i].time;
86         }
87
88         cooked = cc_flight_cook(f);
89         if (cooked) {
90                 speed_i = cc_perioddata_max(&cooked->accel_speed, boost_start, boost_stop);
91                 if (speed_i >= 0) {
92                         speed = cooked->accel_speed.data[speed_i];
93                         fprintf(summary_file, "Max speed:  %9.2fm/s  %9.2fft/s %9.2fs\n",
94                                speed, speed * 100 / 2.4 / 12.0,
95                                (cooked->accel_speed.start + speed_i * cooked->accel_speed.step - boost_start) / 100.0);
96                 }
97         }
98         accel_i = cc_timedata_min(&f->accel, boost_start, boost_stop);
99         if (accel_i >= 0)
100         {
101                 accel = cc_accelerometer_to_acceleration(f->accel.data[accel_i].value,
102                                                          f->ground_accel);
103                 fprintf(summary_file, "Max accel:  %9.2fm/s² %9.2fg    %9.2fs\n",
104                         accel, accel /  9.80665,
105                         (f->accel.data[accel_i].time - boost_start) / 100.0);
106         }
107
108         for (i = 0; i < f->state.num; i++) {
109                 state = f->state.data[i].value;
110                 state_start = f->state.data[i].time;
111                 while (i < f->state.num - 1 && f->state.data[i+1].value == state)
112                         i++;
113                 if (i < f->state.num - 1)
114                         state_stop = f->state.data[i + 1].time;
115                 else
116                         state_stop = f->accel.data[f->accel.num-1].time;
117                 fprintf(summary_file, "State: %s\n", state_names[state]);
118                 fprintf(summary_file, "\tStart:      %9.2fs\n", (state_start - boost_start) / 100.0);
119                 fprintf(summary_file, "\tDuration:   %9.2fs\n", (state_stop - state_start) / 100.0);
120                 accel_i = cc_timedata_min(&f->accel, state_start, state_stop);
121                 if (accel_i >= 0)
122                 {
123                         accel = cc_accelerometer_to_acceleration(f->accel.data[accel_i].value,
124                                                                  f->ground_accel);
125                         fprintf(summary_file, "\tMax accel:  %9.2fm/s² %9.2fg    %9.2fs\n",
126                                accel, accel / 9.80665,
127                                (f->accel.data[accel_i].time - boost_start) / 100.0);
128                 }
129
130                 if (cooked) {
131                         if (state_start < apogee) {
132                                 speed_i = cc_perioddata_max(&cooked->accel_speed, state_start, state_stop);
133                                 if (speed_i >= 0)
134                                         speed = cooked->accel_speed.data[speed_i];
135                         } else {
136                                 speed_i = cc_perioddata_max(&cooked->pres_speed, state_start, state_stop);
137                                 if (speed_i >= 0)
138                                         speed = cooked->pres_speed.data[speed_i];
139                         }
140                         if (speed_i >= 0)
141                                 fprintf(summary_file, "\tMax speed:  %9.2fm/s  %9.2fft/s %9.2fs\n",
142                                        speed, speed * 100 / 2.4 / 12.0,
143                                        (cooked->accel_speed.start + speed_i * cooked->accel_speed.step - boost_start) / 100.0);
144                 }
145                 pres_i = cc_timedata_min(&f->pres, state_start, state_stop);
146                 if (pres_i >= 0)
147                 {
148                         min_pres = f->pres.data[pres_i].value;
149                         height = cc_barometer_to_altitude(min_pres) -
150                                 cc_barometer_to_altitude(f->ground_pres);
151                         fprintf(summary_file, "\tMax height: %9.2fm    %9.2fft   %9.2fs\n",
152                                 height, height * 100 / 2.54 / 12,
153                                 (f->pres.data[pres_i].time - boost_start) / 100.0);
154                 }
155         }
156         if (cooked && detail_file) {
157                 double  apogee_time;
158                 double  max_height = 0;
159                 int     i;
160
161                 for (i = 0; i < cooked->pres_pos.num; i++) {
162                         if (cooked->pres_pos.data[i] > max_height) {
163                                 max_height = cooked->pres_pos.data[i];
164                                 apogee_time = cooked->pres_pos.start + cooked->pres_pos.step * i;
165                         }
166                 }
167                 fprintf(detail_file, "%9s %9s %9s %9s\n",
168                        "time", "height", "speed", "accel");
169                 for (i = 0; i < cooked->pres_pos.num; i++) {
170                         double  time = (cooked->accel_accel.start + i * cooked->accel_accel.step - boost_start) / 100.0;
171                         double  accel = cooked->accel_accel.data[i];
172                         double  pos = cooked->pres_pos.data[i];
173                         double  speed;
174                         if (cooked->pres_pos.start + cooked->pres_pos.step * i < apogee_time)
175                                 speed = cooked->accel_speed.data[i];
176                         else
177                                 speed = cooked->pres_speed.data[i];
178                         fprintf(detail_file, "%9.2f %9.2f %9.2f %9.2f\n",
179                                time, pos, speed, accel);
180                 }
181         }
182 }
183
184 static const struct option options[] = {
185         { .name = "summary", .has_arg = 1, .val = 'S' },
186         { .name = "detail", .has_arg = 1, .val = 'D' },
187         { 0, 0, 0, 0},
188 };
189
190 static void usage(char *program)
191 {
192         fprintf(stderr, "usage: %s [--summary=<summary-file>] [--detail=<detail-file] {flight-log} ...\n", program);
193         exit(1);
194 }
195
196 int
197 main (int argc, char **argv)
198 {
199         FILE                    *file;
200         FILE                    *summary_file;
201         FILE                    *detail_file;
202         int                     i;
203         int                     ret = 0;
204         struct cc_flightraw     *raw;
205         int                     c;
206         int                     serial;
207         char                    *s;
208         char                    *summary_name, *detail_name;
209
210         while ((c = getopt_long(argc, argv, "S:D:", options, NULL)) != -1) {
211                 switch (c) {
212                 case 'S':
213                         summary_name = optarg;
214                         break;
215                 case 'D':
216                         detail_name = optarg;
217                         break;
218                 default:
219                         usage(argv[0]);
220                         break;
221                 }
222         }
223         summary_file = stdout;
224         detail_file = NULL;
225         if (summary_name) {
226                 summary_file = fopen(summary_name, "w");
227                 if (!summary_file) {
228                         perror (summary_name);
229                         exit(1);
230                 }
231         }
232         if (detail_name) {
233                 if (!strcmp (summary_name, detail_name))
234                         detail_file = summary_file;
235                 else {
236                         detail_file = fopen(detail_name, "w");
237                         if (!detail_file) {
238                                 perror(detail_name);
239                                 exit(1);
240                         }
241                 }
242         }
243         for (i = optind; i < argc; i++) {
244                 file = fopen(argv[i], "r");
245                 if (!file) {
246                         perror(argv[i]);
247                         ret++;
248                         continue;
249                 }
250                 s = strstr(argv[i], "-serial-");
251                 if (s)
252                         serial = atoi(s + 8);
253                 else
254                         serial = 0;
255                 raw = cc_log_read(file);
256                 if (!raw) {
257                         perror(argv[i]);
258                         ret++;
259                         continue;
260                 }
261                 if (!raw->serial)
262                         raw->serial = serial;
263                 analyse_flight(raw, summary_file, detail_file);
264                 cc_flightraw_free(raw);
265         }
266         return ret;
267 }