micropeak: Use altosuilib graphing functions
[fw/altos] / micropeak / MicroData.java
1 /*
2  * Copyright © 2012 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 package org.altusmetrum.micropeak;
19
20 import java.lang.*;
21 import java.io.*;
22 import java.util.*;
23 import org.altusmetrum.altoslib_1.*;
24 import org.altusmetrum.altosuilib_1.*;
25
26 class MicroIterator implements Iterator<MicroDataPoint> {
27         int             i;
28         MicroData       data;
29
30         public boolean hasNext() {
31                 return i < data.pressures.length;
32         }
33
34         public MicroDataPoint next() {
35                 return new MicroDataPoint(data, i++);
36         }
37
38         public MicroIterator (MicroData data) {
39                 this.data = data;
40                 i = 0;
41         }
42
43         public void remove() {
44         }
45 }
46
47 class MicroIterable implements Iterable<MicroDataPoint> {
48
49         MicroData       data;
50
51         public Iterator<MicroDataPoint> iterator() {
52                 return new MicroIterator(data);
53         }
54
55         public MicroIterable(MicroData data) {
56                 this.data = data;
57         }
58 }
59
60 class MicroUIIterator implements Iterator<AltosUIDataPoint> {
61         int             i;
62         MicroData       data;
63
64         public boolean hasNext() {
65                 return i < data.pressures.length;
66         }
67
68         public AltosUIDataPoint next() {
69                 return new MicroDataPoint(data, i++);
70         }
71
72         public MicroUIIterator (MicroData data) {
73                 this.data = data;
74                 i = 0;
75         }
76
77         public void remove() {
78         }
79 }
80
81 class MicroUIIterable implements Iterable<AltosUIDataPoint> {
82         MicroData       data;
83
84         public Iterator<AltosUIDataPoint> iterator() {
85                 return new MicroUIIterator(data);
86         }
87
88         public MicroUIIterable(MicroData data) {
89                 this.data = data;
90         }
91 }
92
93 public class MicroData implements AltosUIDataSet {
94         public int              ground_pressure;
95         public int              min_pressure;
96         public int[]            pressures;
97         private double          time_step;
98         private double          ground_altitude;
99         private ArrayList<Integer>      bytes;
100         String                  name;
101         
102         class FileEndedException extends Exception {
103         }
104
105         class NonHexcharException extends Exception {
106         }
107
108         class InvalidCrcException extends Exception {
109         }
110
111         private int getc(InputStream f) throws IOException, FileEndedException {
112                 int     c = f.read();
113
114                 if (c == -1)
115                         throw new FileEndedException();
116                 bytes.add(c);
117                 return c;
118         }
119
120         private int get_nonwhite(InputStream f) throws IOException, FileEndedException {
121                 int     c;
122
123                 for (;;) {
124                         c = getc(f);
125                         if (!Character.isWhitespace(c))
126                                 return c;
127                 }
128         }
129
130         private int get_hexc(InputStream f) throws IOException, FileEndedException, NonHexcharException {
131                 int     c = get_nonwhite(f);
132
133                 if ('0' <= c && c <= '9')
134                         return c - '0';
135                 if ('a' <= c && c <= 'f')
136                         return c - 'a' + 10;
137                 if ('A' <= c && c <= 'F')
138                         return c - 'A' + 10;
139                 throw new NonHexcharException();
140         }
141
142         private static final int POLY = 0x8408;
143
144         private int log_crc(int crc, int b) {
145                 int     i;
146
147                 for (i = 0; i < 8; i++) {
148                         if (((crc & 0x0001) ^ (b & 0x0001)) != 0)
149                                 crc = (crc >> 1) ^ POLY;
150                         else
151                                 crc = crc >> 1;
152                         b >>= 1;
153                 }
154                 return crc & 0xffff;
155         }
156
157         int     file_crc;
158
159         private int get_hex(InputStream f) throws IOException, FileEndedException, NonHexcharException {
160                 int     a = get_hexc(f);
161                 int     b = get_hexc(f);
162
163                 int h = (a << 4) + b;
164
165                 file_crc = log_crc(file_crc, h);
166                 return h;
167         }
168
169         private boolean find_header(InputStream f) throws IOException {
170                 try {
171                         for (;;) {
172                                 if (get_nonwhite(f) == 'M' && get_nonwhite(f) == 'P')
173                                         return true;
174                         }
175                 } catch (FileEndedException fe) {
176                         return false;
177                 }
178         } 
179
180         private int get_32(InputStream f)  throws IOException, FileEndedException, NonHexcharException {
181                 int     v = 0;
182                 for (int i = 0; i < 4; i++) {
183                         v += get_hex(f) << (i * 8);
184                 }
185                 return v;
186         }
187
188         private int get_16(InputStream f) throws IOException, FileEndedException, NonHexcharException {
189                 int     v = 0;
190                 for (int i = 0; i < 2; i++) {
191                         v += get_hex(f) << (i * 8);
192                 }
193                 return v;
194         }
195
196         private int swap16(int i) {
197                 return ((i << 8) & 0xff00) | ((i >> 8) & 0xff);
198         }
199
200         public boolean  crc_valid;
201
202         int mix_in (int high, int low) {
203                 return  high - (high & 0xffff) + low;
204         }
205
206         boolean closer (int target, int a, int b) {
207                 return Math.abs (target - a) < Math.abs(target - b);
208         }
209
210         public double altitude(int i) {
211                 return AltosConvert.pressure_to_altitude(pressures[i]);
212         }
213
214         public String name() {
215                 return name;
216         }
217
218         public Iterable<AltosUIDataPoint> dataPoints() {
219                 return new MicroUIIterable(this);
220         }
221
222         public Iterable<MicroDataPoint> points() {
223                 return new MicroIterable(this);
224         }
225
226         int fact(int n) {
227                 if (n == 0)
228                         return 1;
229                 return n * fact(n-1);
230         }
231
232         int choose(int n, int k) {
233                 return fact(n) / (fact(k) * fact(n-k));
234         }
235
236
237         public double avg_altitude(int center, int dist) {
238                 int     start = center - dist;
239                 int     stop = center + dist;
240
241                 if (start < 0)
242                         start = 0;
243                 if (stop >= pressures.length)
244                         stop = pressures.length - 1;
245
246                 double  sum = 0;
247                 double  div = 0;
248
249                 int     n = dist * 2;
250
251                 for (int i = start; i <= stop; i++) {
252                         int     k = i - (center - dist);
253                         int     c = choose (n, k);
254
255                         sum += c * pressures[i];
256                         div += c;
257                 }
258
259                 double pres = sum / div;
260
261                 double alt = AltosConvert.pressure_to_altitude(pres);
262                 return alt;
263         }
264
265         public double pressure(int i) {
266                 return pressures[i];
267         }
268
269         public double height(int i) {
270                 return altitude(i) - ground_altitude;
271         }
272
273         public double apogee_pressure() {
274                 return min_pressure;
275         }
276
277         public double apogee_altitude() {
278                 return AltosConvert.pressure_to_altitude(apogee_pressure());
279         }
280
281         public double apogee_height() {
282                 return apogee_altitude() - ground_altitude;
283         }
284
285         static final int speed_avg = 3;
286         static final int accel_avg = 5;
287
288         private double avg_speed(int center, int dist) {
289                 if (center == 0)
290                         return 0;
291
292                 double ai = avg_altitude(center, dist);
293                 double aj = avg_altitude(center - 1, dist);
294                 double s = (ai - aj) / time_step;
295
296                 return s;
297         }
298
299         public double speed(int i) {
300                 return avg_speed(i, speed_avg);
301         }
302
303         public double acceleration(int i) {
304                 if (i == 0)
305                         return 0;
306                 return (avg_speed(i, accel_avg) - avg_speed(i-1, accel_avg)) / time_step;
307         }
308
309         public double time(int i) {
310                 return i * time_step;
311         }
312
313         public void save (OutputStream f) throws IOException {
314                 for (int c : bytes)
315                         f.write(c);
316                 f.write('\n');
317         }
318
319         public void export (Writer f) throws IOException {
320                 PrintWriter     pw = new PrintWriter(f);
321                 pw.printf("  Time, Press(Pa), Height(m), Height(f), Speed(m/s), Speed(mph), Speed(mach), Accel(m/s²), Accel(ft/s²),  Accel(g)\n");
322                 for (MicroDataPoint point : points()) {
323                         pw.printf("%6.3f,%10.0f,%10.1f,%10.1f,%11.2f,%11.2f,%12.4f,%12.2f,%13.2f,%10.4f\n",
324                                   point.time,
325                                   point.pressure,
326                                   point.height,
327                                   AltosConvert.meters_to_feet(point.height),
328                                   point.speed,
329                                   AltosConvert.meters_to_mph(point.speed),
330                                   AltosConvert.meters_to_mach(point.speed),
331                                   point.accel,
332                                   AltosConvert.meters_to_feet(point.accel),
333                                   AltosConvert.meters_to_g(point.accel));
334                 }
335         }
336
337         public void set_name(String name) {
338                 this.name = name;
339         }
340
341         public MicroData (InputStream f, String name) throws IOException, InterruptedException {
342                 this.name = name;
343                 bytes = new ArrayList<Integer>();
344                 if (!find_header(f))
345                         throw new IOException("No MicroPeak data header found");
346                 try {
347                         file_crc = 0xffff;
348                         ground_pressure = get_32(f);
349                         min_pressure = get_32(f);
350                         int nsamples = get_16(f);
351                         pressures = new int[nsamples + 1];
352
353                         ground_altitude = AltosConvert.pressure_to_altitude(ground_pressure);
354                         int cur = ground_pressure;
355                         pressures[0] = cur;
356                         for (int i = 0; i < nsamples; i++) {
357                                 int     k = get_16(f);
358                                 int     same = mix_in(cur, k);
359                                 int     up = mix_in(cur + 0x10000, k);
360                                 int     down = mix_in(cur - 0x10000, k);
361
362                                 if (closer (cur, same, up)) {
363                                         if (closer (cur, same, down))
364                                                 cur = same;
365                                         else
366                                                 cur = down;
367                                 } else {
368                                         if (closer (cur, up, down))
369                                                 cur = up;
370                                         else
371                                                 cur = down;
372                                 }
373                                 
374                                 pressures[i+1] = cur;
375                         }
376
377                         int current_crc = swap16(~file_crc & 0xffff);
378                         int crc = get_16(f);
379
380                         crc_valid = crc == current_crc;
381
382                         time_step = 0.192;
383                 } catch (FileEndedException fe) {
384                         throw new IOException("File Ended Unexpectedly");
385                 } catch (NonHexcharException ne) {
386                         throw new IOException("Non hexadecimal character found");
387                 }
388         }
389
390         public MicroData() {
391                 ground_pressure = 101000;
392                 min_pressure = 101000;
393                 pressures = new int[1];
394                 pressures[0] = 101000;
395         }
396         
397 }