2 * Copyright © 2012 Keith Packard <keithp@keithp.com>
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.
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.
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.
18 package org.altusmetrum.micropeak;
23 import org.altusmetrum.AltosLib.*;
25 class MicroIterator implements Iterator<MicroDataPoint> {
29 public boolean hasNext() {
30 return i < data.pressures.length;
33 public MicroDataPoint next() {
34 return new MicroDataPoint(data, i++);
37 public MicroIterator (MicroData data) {
42 public void remove() {
46 class MicroIterable implements Iterable<MicroDataPoint> {
50 public Iterator<MicroDataPoint> iterator() {
51 return new MicroIterator(data);
54 public MicroIterable(MicroData data) {
59 public class MicroData {
60 public int ground_pressure;
61 public int min_pressure;
62 public int[] pressures;
63 private double time_step;
64 private double ground_altitude;
65 private ArrayList<Integer> bytes;
69 class FileEndedException extends Exception {
72 class NonHexcharException extends Exception {
75 class InvalidCrcException extends Exception {
78 private int getc(InputStream f) throws IOException, FileEndedException {
82 throw new FileEndedException();
87 private int get_nonwhite(InputStream f) throws IOException, FileEndedException {
92 if (!Character.isWhitespace(c))
97 private int get_hexc(InputStream f) throws IOException, FileEndedException, NonHexcharException {
98 int c = get_nonwhite(f);
100 if ('0' <= c && c <= '9')
102 if ('a' <= c && c <= 'f')
104 if ('A' <= c && c <= 'F')
106 throw new NonHexcharException();
109 private static final int POLY = 0x8408;
111 private int log_crc(int crc, int b) {
114 for (i = 0; i < 8; i++) {
115 if (((crc & 0x0001) ^ (b & 0x0001)) != 0)
116 crc = (crc >> 1) ^ POLY;
126 private int get_hex(InputStream f) throws IOException, FileEndedException, NonHexcharException {
130 int h = (a << 4) + b;
132 file_crc = log_crc(file_crc, h);
136 private boolean find_header(InputStream f) throws IOException {
139 if (get_nonwhite(f) == 'M' && get_nonwhite(f) == 'P')
142 } catch (FileEndedException fe) {
147 private int get_32(InputStream f) throws IOException, FileEndedException, NonHexcharException {
149 for (int i = 0; i < 4; i++) {
150 v += get_hex(f) << (i * 8);
155 private int get_16(InputStream f) throws IOException, FileEndedException, NonHexcharException {
157 for (int i = 0; i < 2; i++) {
158 v += get_hex(f) << (i * 8);
163 private int swap16(int i) {
164 return ((i << 8) & 0xff00) | ((i >> 8) & 0xff);
167 public boolean crc_valid;
169 int mix_in (int high, int low) {
170 return high - (high & 0xffff) + low;
173 boolean closer (int target, int a, int b) {
174 return Math.abs (target - a) < Math.abs(target - b);
177 public double altitude(int i) {
178 return AltosConvert.pressure_to_altitude(pressures[i]);
181 public Iterable<MicroDataPoint> points() {
182 return new MicroIterable(this);
188 return n * fact(n-1);
191 int choose(int n, int k) {
192 return fact(n) / (fact(k) * fact(n-k));
196 public double avg_altitude(int center, int dist) {
197 int start = center - dist;
198 int stop = center + dist;
202 if (stop >= pressures.length)
203 stop = pressures.length - 1;
210 for (int i = start; i <= stop; i++) {
211 int k = i - (center - dist);
212 int c = choose (n, k);
214 sum += c * pressures[i];
218 double pres = sum / div;
220 double alt = AltosConvert.pressure_to_altitude(pres);
224 public double pressure(int i) {
228 public double height(int i) {
229 return altitude(i) - ground_altitude;
232 public double apogee_pressure() {
236 public double apogee_altitude() {
237 return AltosConvert.pressure_to_altitude(apogee_pressure());
240 public double apogee_height() {
241 return apogee_altitude() - ground_altitude;
244 static final int speed_avg = 3;
245 static final int accel_avg = 5;
247 private double avg_speed(int center, int dist) {
251 double ai = avg_altitude(center, dist);
252 double aj = avg_altitude(center - 1, dist);
253 double s = (ai - aj) / time_step;
258 public double speed(int i) {
259 return avg_speed(i, speed_avg);
262 public double acceleration(int i) {
265 return (avg_speed(i, accel_avg) - avg_speed(i-1, accel_avg)) / time_step;
268 public double time(int i) {
269 return i * time_step;
272 public void save (OutputStream f) throws IOException {
278 public void export (Writer f) throws IOException {
279 PrintWriter pw = new PrintWriter(f);
280 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");
281 for (MicroDataPoint point : points()) {
282 pw.printf("%6.3f,%10.0f,%10.1f,%10.1f,%11.2f,%11.2f,%12.4f,%12.2f,%13.2f,%10.4f\n",
286 AltosConvert.meters_to_feet(point.height),
288 AltosConvert.meters_to_mph(point.speed),
289 AltosConvert.meters_to_mach(point.speed),
291 AltosConvert.meters_to_feet(point.accel),
292 AltosConvert.meters_to_g(point.accel));
296 public void set_name(String name) {
300 public MicroData (InputStream f, String name) throws IOException, InterruptedException {
302 bytes = new ArrayList<Integer>();
304 throw new IOException("No MicroPeak data header found");
307 ground_pressure = get_32(f);
308 min_pressure = get_32(f);
309 int nsamples = get_16(f);
310 pressures = new int[nsamples + 1];
312 ground_altitude = AltosConvert.pressure_to_altitude(ground_pressure);
313 int cur = ground_pressure;
315 for (int i = 0; i < nsamples; i++) {
317 int same = mix_in(cur, k);
318 int up = mix_in(cur + 0x10000, k);
319 int down = mix_in(cur - 0x10000, k);
321 if (closer (cur, same, up)) {
322 if (closer (cur, same, down))
327 if (closer (cur, up, down))
333 pressures[i+1] = cur;
336 int current_crc = swap16(~file_crc & 0xffff);
339 crc_valid = crc == current_crc;
342 } catch (FileEndedException fe) {
343 throw new IOException("File Ended Unexpectedly");
344 } catch (NonHexcharException ne) {
345 throw new IOException("Non hexadecimal character found");
350 ground_pressure = 101000;
351 min_pressure = 101000;
352 pressures = new int[1];
353 pressures[0] = 101000;