summaryrefslogtreecommitdiff
path: root/micropeak
diff options
context:
space:
mode:
Diffstat (limited to 'micropeak')
-rw-r--r--micropeak/.gitignore6
-rw-r--r--micropeak/Makefile.am146
-rw-r--r--micropeak/MicroData.java370
-rw-r--r--micropeak/MicroFileChooser.java67
-rw-r--r--micropeak/MicroFrame.java37
-rw-r--r--micropeak/MicroGraph.java154
-rw-r--r--micropeak/MicroPeak.java171
-rw-r--r--micropeak/MicroSerial.java51
-rw-r--r--micropeak/MicroStats.java184
-rw-r--r--micropeak/MicroStatsTable.java138
-rw-r--r--micropeak/MicroUSB.java103
11 files changed, 1427 insertions, 0 deletions
diff --git a/micropeak/.gitignore b/micropeak/.gitignore
new file mode 100644
index 00000000..fc99b31c
--- /dev/null
+++ b/micropeak/.gitignore
@@ -0,0 +1,6 @@
+*.jar
+Manifest.txt
+classes
+*.stamp
+micropeak
+micropeak-test
diff --git a/micropeak/Makefile.am b/micropeak/Makefile.am
new file mode 100644
index 00000000..e0de690c
--- /dev/null
+++ b/micropeak/Makefile.am
@@ -0,0 +1,146 @@
+JAVAROOT=classes
+AM_JAVACFLAGS=-encoding UTF-8 -Xlint:deprecation
+
+CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:../altoslib/*:../altosuilib/*:../libaltos:$(JCOMMON)/jcommon.jar:$(JFREECHART)/jfreechart.jar"
+
+bin_SCRIPTS=micropeak
+
+micropeakdir=$(datadir)/java
+
+micropeak_JAVA= \
+ MicroPeak.java \
+ MicroData.java \
+ MicroFrame.java \
+ MicroGraph.java \
+ MicroSerial.java \
+ MicroStats.java \
+ MicroStatsTable.java \
+ MicroFileChooser.java \
+ MicroUSB.java
+
+JFREECHART_CLASS= \
+ jfreechart.jar
+
+JCOMMON_CLASS=\
+ jcommon.jar
+
+JAR=micropeak.jar
+
+FATJAR=micropeak-fat.jar
+
+LIBALTOS= \
+ libaltos.so \
+ libaltos.dylib \
+ altos.dll
+
+ALTOSLIB_CLASS=\
+ AltosLib.jar
+
+ALTOSUILIB_CLASS=\
+ AltosUILib.jar
+
+# Icons
+ICONDIR=$(top_srcdir)/icon
+
+JAVA_ICONS=\
+ $(ICONDIR)/micropeak-16.png \
+ $(ICONDIR)/micropeak-32.png \
+ $(ICONDIR)/micropeak-48.png \
+ $(ICONDIR)/micropeak-64.png \
+ $(ICONDIR)/micropeak-128.png \
+ $(ICONDIR)/micropeak-256.png
+
+# icon base names for jar
+ICONJAR= -C $(ICONDIR) micropeak-16.png \
+ -C $(ICONDIR) micropeak-32.png \
+ -C $(ICONDIR) micropeak-48.png \
+ -C $(ICONDIR) micropeak-64.png \
+ -C $(ICONDIR) micropeak-128.png \
+ -C $(ICONDIR) micropeak-256.png
+
+all-local: micropeak-test $(JAR)
+
+clean-local:
+ -rm -rf classes $(JAR) $(FATJAR) \
+ $(ALTOSLIB_CLASS) \
+ $(ALTOSUILIB_CLASS) \
+ $(JFREECHART_CLASS) $(JCOMMON_CLASS) $(LIBALTOS) Manifest.txt \
+ micropeak micropeak-test macosx linux windows
+
+micropeak: Makefile
+ echo "#!/bin/sh" > $@
+ echo 'exec java -cp "$(JCOMMON)/jcommon.jar:$(JFREECHART)/jfreechart.jar" -Djava.library.path="$(altoslibdir)" -jar "$(micropeakdir)/micropeak.jar" "$$@"' >> $@
+ chmod +x $@
+
+micropeak-test: Makefile
+ echo "#!/bin/sh" > $@
+ echo 'exec java -cp "./*:../libaltos/*:$(JCOMMON)/jcommon.jar:$(JFREECHART)/jfreechart.jar" -Djava.library.path="../libaltos/.libs" -jar micropeak.jar "$$@"' >> $@
+ chmod +x $@
+
+$(JAR): classmicropeak.stamp Manifest.txt $(JAVA_ICONS) $(ALTOSLIB_CLASS) $(ALTOSUILIB_CLASS)
+ jar cfm $@ Manifest.txt \
+ $(ICONJAR) \
+ -C classes org \
+ -C ../libaltos libaltosJNI
+
+$(FATJAR): classmicropeak.stamp Manifest-fat.txt $(ALTOSLIB_CLASS) $(ALTOSUILIB_CLASS) $(JFREECHART_CLASS) $(JCOMMON_CLASS) $(JAVA_ICONS)
+ jar cfm $@ Manifest-fat.txt \
+ $(ICONJAR) \
+ -C classes org \
+ -C ../libaltos libaltosJNI
+
+
+libaltos.so: build-libaltos
+ -rm -f "$@"
+ $(LN_S) ../libaltos/.libs/"$@" .
+
+libaltos.dylib:
+ -rm -f "$@"
+ $(LN_S) ../libaltos/"$@" .
+
+altos.dll: ../libaltos/altos.dll
+ -rm -f "$@"
+ $(LN_S) ../libaltos/"$@" .
+
+altos64.dll: ../libaltos/altos64.dll
+ -rm -f "$@"
+ $(LN_S) ../libaltos/"$@" .
+
+../libaltos/.libs/libaltos.so: build-libaltos
+
+../libaltos/altos.dll: build-altos-dll
+
+../libaltos/altos64.dll: build-altos64-dll
+
+build-libaltos:
+ +cd ../libaltos && make libaltos.la
+build-altos-dll:
+ +cd ../libaltos && make altos.dll
+
+build-altos64-dll:
+ +cd ../libaltos && make altos64.dll
+
+$(ALTOSLIB_CLASS):
+ -rm -f "$@"
+ $(LN_S) ../altoslib/"$@" .
+
+$(ALTOSUILIB_CLASS):
+ -rm -f "$@"
+ $(LN_S) ../altosuilib/"$@" .
+
+$(JFREECHART_CLASS):
+ -rm -f "$@"
+ $(LN_S) "$(JFREECHART)"/"$@" .
+
+$(JCOMMON_CLASS):
+ -rm -f "$@"
+ $(LN_S) "$(JCOMMON)"/"$@" .
+
+Manifest.txt: Makefile
+ echo 'Main-Class: org.altusmetrum.micropeak.MicroPeak' > $@
+ echo "Class-Path: AltosLib.jar AltosUILib.jar $(JCOMMON)/jcommon.jar $(JFREECHART)/jfreechart.jar" >> $@
+
+Manifest-fat.txt:
+ echo 'Main-Class: org.altusmetrum.micropeak.MicroPeak' > $@
+ echo "Class-Path: AltosLib.jar AltosUILib.jar jcommon.jar jfreechart.jar" >> $@
+
diff --git a/micropeak/MicroData.java b/micropeak/MicroData.java
new file mode 100644
index 00000000..ec9b83d8
--- /dev/null
+++ b/micropeak/MicroData.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.micropeak;
+
+import java.lang.*;
+import java.io.*;
+import java.util.*;
+import org.altusmetrum.AltosLib.*;
+
+abstract class MicroIterator implements Iterator<Double> {
+ int i;
+ MicroData data;
+
+ public boolean hasNext() {
+ return i < data.pressures.length;
+ }
+
+ public MicroIterator (MicroData data) {
+ this.data = data;
+ i = 0;
+ }
+
+ public void remove() {
+ }
+}
+
+class MicroHeightIterator extends MicroIterator {
+ public Double next() {
+ return data.height(i++);
+ }
+
+ public MicroHeightIterator(MicroData data) {
+ super(data);
+ }
+}
+
+class MicroHeightIterable implements Iterable<Double> {
+ MicroData data;
+
+ public Iterator<Double> iterator() {
+ return new MicroHeightIterator(data);
+ }
+
+ public MicroHeightIterable(MicroData data) {
+ this.data = data;
+ }
+}
+
+class MicroSpeedIterator extends MicroIterator {
+ public Double next() {
+ return data.speed(i++);
+ }
+ public MicroSpeedIterator(MicroData data) {
+ super(data);
+ }
+}
+
+class MicroSpeedIterable implements Iterable<Double> {
+ MicroData data;
+
+ public Iterator<Double> iterator() {
+ return new MicroSpeedIterator(data);
+ }
+
+ public MicroSpeedIterable(MicroData data) {
+ this.data = data;
+ }
+}
+
+class MicroAccelIterator extends MicroIterator {
+ public Double next() {
+ return data.acceleration(i++);
+ }
+ public MicroAccelIterator(MicroData data) {
+ super(data);
+ }
+}
+
+class MicroAccelIterable implements Iterable<Double> {
+ MicroData data;
+
+ public Iterator<Double> iterator() {
+ return new MicroAccelIterator(data);
+ }
+
+ public MicroAccelIterable(MicroData data) {
+ this.data = data;
+ }
+}
+
+public class MicroData {
+ public int ground_pressure;
+ public int min_pressure;
+ public int[] pressures;
+ private double time_step;
+ private double ground_altitude;
+ private ArrayList<Integer> bytes;
+
+
+ class FileEndedException extends Exception {
+ }
+
+ class NonHexcharException extends Exception {
+ }
+
+ class InvalidCrcException extends Exception {
+ }
+
+ private int getc(InputStream f) throws IOException, FileEndedException {
+ int c = f.read();
+
+ if (c == -1)
+ throw new FileEndedException();
+ bytes.add(c);
+ return c;
+ }
+
+ private int get_nonwhite(InputStream f) throws IOException, FileEndedException {
+ int c;
+
+ for (;;) {
+ c = getc(f);
+ if (!Character.isWhitespace(c))
+ return c;
+ }
+ }
+
+ private int get_hexc(InputStream f) throws IOException, FileEndedException, NonHexcharException {
+ int c = get_nonwhite(f);
+
+ if ('0' <= c && c <= '9')
+ return c - '0';
+ if ('a' <= c && c <= 'f')
+ return c - 'a' + 10;
+ if ('A' <= c && c <= 'F')
+ return c - 'A' + 10;
+ throw new NonHexcharException();
+ }
+
+ private static final int POLY = 0x8408;
+
+ private int log_crc(int crc, int b) {
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (((crc & 0x0001) ^ (b & 0x0001)) != 0)
+ crc = (crc >> 1) ^ POLY;
+ else
+ crc = crc >> 1;
+ b >>= 1;
+ }
+ return crc & 0xffff;
+ }
+
+ int file_crc;
+
+ private int get_hex(InputStream f) throws IOException, FileEndedException, NonHexcharException {
+ int a = get_hexc(f);
+ int b = get_hexc(f);
+
+ int h = (a << 4) + b;
+
+ file_crc = log_crc(file_crc, h);
+ return h;
+ }
+
+ private boolean find_header(InputStream f) throws IOException {
+ try {
+ for (;;) {
+ if (get_nonwhite(f) == 'M' && get_nonwhite(f) == 'P')
+ return true;
+ }
+ } catch (FileEndedException fe) {
+ return false;
+ }
+ }
+
+ private int get_32(InputStream f) throws IOException, FileEndedException, NonHexcharException {
+ int v = 0;
+ for (int i = 0; i < 4; i++) {
+ v += get_hex(f) << (i * 8);
+ }
+ return v;
+ }
+
+ private int get_16(InputStream f) throws IOException, FileEndedException, NonHexcharException {
+ int v = 0;
+ for (int i = 0; i < 2; i++) {
+ v += get_hex(f) << (i * 8);
+ }
+ return v;
+ }
+
+ private int swap16(int i) {
+ return ((i << 8) & 0xff00) | ((i >> 8) & 0xff);
+ }
+
+ public boolean crc_valid;
+
+ int mix_in (int high, int low) {
+ return high - (high & 0xffff) + low;
+ }
+
+ boolean closer (int target, int a, int b) {
+ return Math.abs (target - a) < Math.abs(target - b);
+ }
+
+ public double altitude(int i) {
+ return AltosConvert.pressure_to_altitude(pressures[i]);
+ }
+
+ public Iterable<Double> heights() {
+ return new MicroHeightIterable(this);
+ }
+
+ public Iterable<Double> speeds() {
+ return new MicroSpeedIterable(this);
+ }
+
+ public Iterable<Double> accels() {
+ return new MicroAccelIterable(this);
+ }
+
+ int fact(int n) {
+ if (n == 0)
+ return 1;
+ return n * fact(n-1);
+ }
+
+ int choose(int n, int k) {
+ return fact(n) / (fact(k) * fact(n-k));
+ }
+
+
+ public double avg_altitude(int center, int dist) {
+ int start = center - dist;
+ int stop = center + dist;
+
+ if (start < 0)
+ start = 0;
+ if (stop >= pressures.length)
+ stop = pressures.length - 1;
+
+ double sum = 0;
+ double div = 0;
+
+ int n = dist * 2;
+
+ for (int i = start; i <= stop; i++) {
+ int k = i - (center - dist);
+ int c = choose (n, k);
+
+ sum += c * pressures[i];
+ div += c;
+ }
+
+ double pres = sum / div;
+
+ double alt = AltosConvert.pressure_to_altitude(pres);
+ return alt;
+ }
+
+ public double height(int i) {
+ return altitude(i) - ground_altitude;
+ }
+
+ static final int speed_avg = 3;
+ static final int accel_avg = 5;
+
+ private double avg_speed(int center, int dist) {
+ if (center == 0)
+ return 0;
+
+ double ai = avg_altitude(center, dist);
+ double aj = avg_altitude(center - 1, dist);
+ double s = (ai - aj) / time_step;
+
+ return s;
+ }
+
+ public double speed(int i) {
+ return avg_speed(i, speed_avg);
+ }
+
+ public double acceleration(int i) {
+ if (i == 0)
+ return 0;
+ return (avg_speed(i, accel_avg) - avg_speed(i-1, accel_avg)) / time_step;
+ }
+
+ public double time(int i) {
+ return i * time_step;
+ }
+
+ public void save (OutputStream f) throws IOException {
+ for (int c : bytes)
+ f.write(c);
+ }
+
+ public MicroData (InputStream f) throws IOException {
+ bytes = new ArrayList<Integer>();
+ if (!find_header(f))
+ throw new IOException();
+ try {
+ file_crc = 0xffff;
+ ground_pressure = get_32(f);
+ min_pressure = get_32(f);
+ int nsamples = get_16(f);
+ pressures = new int[nsamples + 1];
+
+ ground_altitude = AltosConvert.pressure_to_altitude(ground_pressure);
+ int cur = ground_pressure;
+ pressures[0] = cur;
+ for (int i = 0; i < nsamples; i++) {
+ int k = get_16(f);
+ int same = mix_in(cur, k);
+ int up = mix_in(cur + 0x10000, k);
+ int down = mix_in(cur - 0x10000, k);
+
+ if (closer (cur, same, up)) {
+ if (closer (cur, same, down))
+ cur = same;
+ else
+ cur = down;
+ } else {
+ if (closer (cur, up, down))
+ cur = up;
+ else
+ cur = down;
+ }
+
+ pressures[i+1] = cur;
+ }
+
+ int current_crc = swap16(~file_crc & 0xffff);
+ int crc = get_16(f);
+
+ crc_valid = crc == current_crc;
+
+ time_step = 0.192;
+ } catch (FileEndedException fe) {
+ throw new IOException();
+ } catch (NonHexcharException ne) {
+ throw new IOException();
+ }
+ }
+
+ public MicroData() {
+ ground_pressure = 101000;
+ min_pressure = 101000;
+ pressures = new int[1];
+ pressures[0] = 101000;
+ }
+
+}
diff --git a/micropeak/MicroFileChooser.java b/micropeak/MicroFileChooser.java
new file mode 100644
index 00000000..d2540987
--- /dev/null
+++ b/micropeak/MicroFileChooser.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.micropeak;
+
+import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import java.io.*;
+import org.altusmetrum.AltosLib.*;
+import org.altusmetrum.altosuilib.*;
+
+public class MicroFileChooser extends JFileChooser {
+ JFrame frame;
+ String filename;
+ File file;
+
+ public String filename() {
+ return filename;
+ }
+
+ public File file() {
+ return file;
+ }
+
+ public InputStream runDialog() {
+ int ret;
+
+ ret = showOpenDialog(frame);
+ if (ret == APPROVE_OPTION) {
+ file = getSelectedFile();
+ if (file == null)
+ return null;
+ filename = file.getName();
+ try {
+ return new FileInputStream(file);
+ } catch (FileNotFoundException fe) {
+ JOptionPane.showMessageDialog(frame,
+ fe.getMessage(),
+ "Cannot open file",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ return null;
+ }
+
+ public MicroFileChooser(JFrame in_frame) {
+ frame = in_frame;
+ setDialogTitle("Select MicroPeak Data File");
+ setFileFilter(new FileNameExtensionFilter("MicroPeak data file",
+ "mpd"));
+ setCurrentDirectory(AltosUIPreferences.logdir());
+ }
+}
diff --git a/micropeak/MicroFrame.java b/micropeak/MicroFrame.java
new file mode 100644
index 00000000..03e3af0c
--- /dev/null
+++ b/micropeak/MicroFrame.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.micropeak;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.util.*;
+import org.altusmetrum.altosuilib.*;
+
+public class MicroFrame extends AltosUIFrame {
+ static String[] micro_icon_names = {
+ "/micropeak-16.png",
+ "/micropeak-32.png",
+ "/micropeak-48.png",
+ "/micropeak-64.png",
+ "/micropeak-128.png",
+ "/micropeak-256.png"
+ };
+
+ static { set_icon_names(micro_icon_names); }
+}
diff --git a/micropeak/MicroGraph.java b/micropeak/MicroGraph.java
new file mode 100644
index 00000000..38f54fe0
--- /dev/null
+++ b/micropeak/MicroGraph.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.micropeak;
+
+import java.io.*;
+import java.util.ArrayList;
+
+import java.awt.*;
+import javax.swing.*;
+import org.altusmetrum.AltosLib.*;
+
+import org.jfree.ui.*;
+import org.jfree.chart.*;
+import org.jfree.chart.plot.*;
+import org.jfree.chart.axis.*;
+import org.jfree.chart.renderer.*;
+import org.jfree.chart.renderer.xy.*;
+import org.jfree.chart.labels.*;
+import org.jfree.data.xy.*;
+import org.jfree.data.*;
+
+class MicroSeries extends XYSeries {
+ NumberAxis axis;
+ String label;
+ String units;
+ Color color;
+
+ String label() {
+ return String.format("%s (%s)", label, units);
+ }
+
+ void set_units(String units) {
+ this.units = units;
+
+ axis.setLabel(label());
+ }
+
+ public MicroSeries (String label, String units, Color color) {
+ super(label);
+ this.label = label;
+ this.units = units;
+ this.color = color;
+
+ axis = new NumberAxis(label());
+ axis.setLabelPaint(color);
+ axis.setTickLabelPaint(color);
+ }
+}
+
+public class MicroGraph implements AltosUnitsListener {
+
+ XYPlot plot;
+ JFreeChart chart;
+ ChartPanel panel;
+ NumberAxis xAxis;
+ MicroSeries heightSeries;
+ MicroSeries speedSeries;
+ MicroSeries accelSeries;
+
+ static final private Color red = new Color(194,31,31);
+ static final private Color green = new Color(31,194,31);
+ static final private Color blue = new Color(31,31,194);
+
+ MicroData data;
+
+ public JPanel panel() {
+ return panel;
+ }
+
+ private MicroSeries addSeries(int index, String label, String units, Color color) {
+ MicroSeries series = new MicroSeries(label, units, color);
+ XYSeriesCollection dataset = new XYSeriesCollection(series);
+ XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
+
+ renderer.setSeriesPaint(0, color);
+ renderer.setPlot(plot);
+ renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator(String.format("{1}s: {2}%s ({0})", units),
+ new java.text.DecimalFormat("0.00"),
+ new java.text.DecimalFormat("0.00")));
+ plot.setRangeAxis(index, series.axis);
+ plot.setDataset(index, dataset);
+ plot.setRenderer(index, renderer);
+ plot.mapDatasetToRangeAxis(index, index);
+ return series;
+ }
+
+ public void resetData() {
+ heightSeries.clear();
+ speedSeries.clear();
+ accelSeries.clear();
+ for (int i = 0; i < data.pressures.length; i++) {
+ double x = data.time(i);
+ heightSeries.add(x, AltosConvert.height.value(data.height(i)));
+ speedSeries.add(x, AltosConvert.speed.value(data.speed(i)));
+ accelSeries.add(x, AltosConvert.accel.value(data.acceleration(i)));
+ }
+ }
+
+ public void setData (MicroData data) {
+ this.data = data;
+ resetData();
+ }
+
+ public void units_changed(boolean imperial_units) {
+ if (data != null) {
+ heightSeries.set_units(AltosConvert.height.show_units());
+ speedSeries.set_units(AltosConvert.speed.show_units());
+ accelSeries.set_units(AltosConvert.accel.show_units());
+ resetData();
+ }
+ }
+
+ public MicroGraph() {
+
+ xAxis = new NumberAxis("Time (s)");
+
+ xAxis.setAutoRangeIncludesZero(true);
+
+ plot = new XYPlot();
+ plot.setDomainAxis(xAxis);
+ plot.setOrientation(PlotOrientation.VERTICAL);
+ plot.setDomainPannable(true);
+ plot.setRangePannable(true);
+
+ heightSeries = addSeries(0, "Height", AltosConvert.height.show_units(), red);
+ speedSeries = addSeries(1, "Speed", AltosConvert.speed.show_units(), green);
+ accelSeries = addSeries(2, "Acceleration", AltosConvert.accel.show_units(), blue);
+
+ chart = new JFreeChart("Flight", JFreeChart.DEFAULT_TITLE_FONT,
+ plot, true);
+
+ ChartUtilities.applyCurrentTheme(chart);
+ panel = new ChartPanel(chart);
+ panel.setMouseWheelEnabled(true);
+ panel.setPreferredSize(new java.awt.Dimension(800, 500));
+
+ AltosPreferences.register_units_listener(this);
+ }
+} \ No newline at end of file
diff --git a/micropeak/MicroPeak.java b/micropeak/MicroPeak.java
new file mode 100644
index 00000000..c69f7167
--- /dev/null
+++ b/micropeak/MicroPeak.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.micropeak;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import java.util.concurrent.*;
+import java.util.*;
+import org.altusmetrum.AltosLib.*;
+import org.altusmetrum.altosuilib.*;
+
+public class MicroPeak extends MicroFrame implements ActionListener, ItemListener {
+
+ File filename;
+ MicroGraph graph;
+ MicroStatsTable stats;
+ MicroData data;
+ Container container;
+ JTabbedPane pane;
+
+ private void RunFile(InputStream input) {
+ try {
+ data = new MicroData(input);
+ graph.setData(data);
+ stats.setData(data);
+ } catch (IOException ioe) {
+ }
+ try {
+ input.close();
+ } catch (IOException ioe) {
+ }
+ }
+
+ private void OpenFile(File filename) {
+ try {
+ RunFile (new FileInputStream(filename));
+ } catch (FileNotFoundException fne) {
+ }
+ }
+
+ private void SelectFile() {
+ MicroFileChooser chooser = new MicroFileChooser(this);
+ InputStream input = chooser.runDialog();
+
+ if (input != null)
+ RunFile(input);
+ }
+
+ private void Preferences() {
+ new AltosConfigureUI(this);
+ }
+
+ private void DownloadData() {
+ java.util.List<MicroUSB> devices = MicroUSB.list();
+ for (MicroUSB device : devices)
+ System.out.printf("device %s\n", device.toString());
+ }
+
+ public void actionPerformed(ActionEvent ev) {
+ if ("Exit".equals(ev.getActionCommand()))
+ System.exit(0);
+ else if ("Open".equals(ev.getActionCommand()))
+ SelectFile();
+ else if ("New".equals(ev.getActionCommand()))
+ new MicroPeak();
+ else if ("Download".equals(ev.getActionCommand()))
+ DownloadData();
+ else if ("Preferences".equals(ev.getActionCommand()))
+ Preferences();
+ }
+
+ public void itemStateChanged(ItemEvent e) {
+ }
+
+ public MicroPeak() {
+
+ AltosUIPreferences.set_component(this);
+
+ container = getContentPane();
+ pane = new JTabbedPane();
+
+ setTitle("MicroPeak");
+
+ JMenuBar menuBar = new JMenuBar();
+ setJMenuBar(menuBar);
+
+ JMenu fileMenu = new JMenu("File");
+ menuBar.add(fileMenu);
+
+ JMenuItem newAction = new JMenuItem("New");
+ fileMenu.add(newAction);
+ newAction.addActionListener(this);
+
+ JMenuItem openAction = new JMenuItem("Open");
+ fileMenu.add(openAction);
+ openAction.addActionListener(this);
+
+ JMenuItem downloadAction = new JMenuItem("Download");
+ fileMenu.add(downloadAction);
+ downloadAction.addActionListener(this);
+
+ JMenuItem preferencesAction = new JMenuItem("Preferences");
+ fileMenu.add(preferencesAction);
+ preferencesAction.addActionListener(this);
+
+ JMenuItem exitAction = new JMenuItem("Exit");
+ fileMenu.add(exitAction);
+ exitAction.addActionListener(this);
+
+ setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+ addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent e) {
+ System.exit(0);
+ }
+ });
+
+ graph = new MicroGraph();
+ stats = new MicroStatsTable();
+ pane.add(graph.panel, "Graph");
+ pane.add(stats, "Statistics");
+ pane.doLayout();
+ pane.validate();
+ container.add(pane);
+ container.doLayout();
+ container.validate();
+ doLayout();
+ validate();
+ Insets i = getInsets();
+ Dimension ps = pane.getPreferredSize();
+ ps.width += i.left + i.right;
+ ps.height += i.top + i.bottom;
+ setPreferredSize(ps);
+ setSize(ps);
+ setVisible(true);
+ }
+
+ public static void main(final String[] args) {
+ boolean opened = false;
+
+ try {
+ UIManager.setLookAndFeel(AltosUIPreferences.look_and_feel());
+ } catch (Exception e) {
+ }
+
+ for (int i = 0; i < args.length; i++) {
+ MicroPeak m = new MicroPeak();
+ m.OpenFile(new File(args[i]));
+ opened = true;
+ }
+ if (!opened)
+ new MicroPeak();
+ }
+} \ No newline at end of file
diff --git a/micropeak/MicroSerial.java b/micropeak/MicroSerial.java
new file mode 100644
index 00000000..a1a77a1d
--- /dev/null
+++ b/micropeak/MicroSerial.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.micropeak;
+
+import java.util.*;
+import java.io.*;
+import libaltosJNI.*;
+import org.altusmetrum.altosuilib.*;
+
+public class MicroSerial extends InputStream {
+ SWIGTYPE_p_altos_file file;
+
+ public int read() {
+ int c = libaltos.altos_getchar(file, 0);
+ if (AltosUIPreferences.serial_debug)
+ System.out.printf("%c", c);
+ return c;
+ }
+
+ public void close() {
+ if (file != null) {
+ libaltos.altos_close(file);
+ file = null;
+ }
+ }
+
+ public MicroSerial(MicroUSB usb) throws FileNotFoundException {
+ file = usb.open();
+ if (file == null) {
+ final String message = usb.getErrorString();
+ throw new FileNotFoundException(String.format("%s (%s)",
+ usb.toShortString(),
+ message));
+ }
+ }
+}
diff --git a/micropeak/MicroStats.java b/micropeak/MicroStats.java
new file mode 100644
index 00000000..6ae8a2b2
--- /dev/null
+++ b/micropeak/MicroStats.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.micropeak;
+
+import java.io.*;
+import org.altusmetrum.AltosLib.*;
+import org.altusmetrum.altosuilib.*;
+
+public class MicroStats {
+ double coast_height;
+ double coast_time;
+
+ double apogee_height;
+ double apogee_time;
+
+ double landed_height;
+ double landed_time;
+
+ double max_speed;
+ double max_accel;
+
+ MicroData data;
+
+ void find_landing() {
+ landed_height = 0;
+
+ int t = 0;
+ for (double height : data.heights()) {
+ landed_height = height;
+ t++;
+ }
+ landed_time = data.time(t);
+
+ t = 0;
+ boolean above = false;
+ for (double height : data.heights()) {
+ if (height > landed_height + 10) {
+ above = true;
+ } else {
+ if (above && height < landed_height + 2) {
+ above = false;
+ landed_time = data.time(t);
+ }
+ }
+ t++;
+ }
+ }
+
+ void find_apogee() {
+ apogee_height = 0;
+ apogee_time = 0;
+
+ int t = 0;
+ for (double height : data.heights()) {
+ if (height > apogee_height) {
+ apogee_height = height;
+ apogee_time = data.time(t);
+ }
+ t++;
+ }
+ }
+
+ void find_coast() {
+ coast_height = 0;
+ coast_time = 0;
+
+ int t = 0;
+ for (double accel : data.accels()) {
+ if (accel < -9.8)
+ break;
+ t++;
+ }
+ coast_time = data.time(t);
+
+ int coast_t = t;
+ t = 0;
+ for (double height : data.heights()) {
+ if (t >= coast_t) {
+ coast_height = height;
+ break;
+ }
+ t++;
+ }
+ }
+
+ void find_max_speed() {
+ max_speed = 0;
+ int t = 0;
+ for (double speed : data.speeds()) {
+ if (data.time(t) > apogee_time)
+ break;
+ if (speed > max_speed)
+ max_speed = speed;
+ t++;
+ }
+ }
+
+ void find_max_accel() {
+ max_accel = 0;
+
+ int t = 0;
+ for (double accel : data.accels()) {
+ if (data.time(t) > apogee_time)
+ break;
+ if (accel > max_accel)
+ max_accel = accel;
+ t++;
+ }
+ }
+
+ double boost_duration() {
+ return coast_time;
+ }
+
+ double boost_height() {
+ return coast_height;
+ }
+
+ double boost_speed() {
+ return coast_height / coast_time;
+ }
+
+ double boost_accel() {
+ return boost_speed() / boost_duration();
+ }
+
+ double coast_duration() {
+ return apogee_time - coast_time;
+ }
+
+ double coast_height() {
+ return apogee_height - coast_height;
+ }
+
+ double coast_speed() {
+ return coast_height() / coast_duration();
+ }
+
+ double coast_accel() {
+ return coast_speed() / coast_duration();
+ }
+
+ double descent_duration() {
+ return landed_time - apogee_time;
+ }
+
+ double descent_height() {
+ return apogee_height - landed_height;
+ }
+
+ double descent_speed() {
+ return descent_height() / descent_duration();
+ }
+
+ public MicroStats(MicroData data) {
+
+ this.data = data;
+
+ find_coast();
+ find_apogee();
+ find_landing();
+ find_max_speed();
+ find_max_accel();
+ }
+
+ public MicroStats() {
+ this(new MicroData());
+ }
+}
diff --git a/micropeak/MicroStatsTable.java b/micropeak/MicroStatsTable.java
new file mode 100644
index 00000000..f373e25d
--- /dev/null
+++ b/micropeak/MicroStatsTable.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.micropeak;
+
+import java.awt.*;
+import javax.swing.*;
+import org.altusmetrum.AltosLib.*;
+import org.altusmetrum.altosuilib.*;
+
+public class MicroStatsTable extends JComponent {
+ GridBagLayout layout;
+
+ class MicroStat {
+ JLabel label;
+ JTextField[] texts;
+
+ public void set_values(String ... values) {
+ for (int j = 0; j < values.length; j++) {
+ texts[j].setText(values[j]);
+ }
+ }
+
+ public MicroStat(GridBagLayout layout, int y, String label_text, String ... values) {
+ GridBagConstraints c = new GridBagConstraints();
+ c.insets = new Insets(AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad);
+ c.weighty = 1;
+
+ label = new JLabel(label_text);
+ label.setFont(AltosUILib.label_font);
+ label.setHorizontalAlignment(SwingConstants.LEFT);
+ c.gridx = 0; c.gridy = y;
+ c.anchor = GridBagConstraints.WEST;
+ c.fill = GridBagConstraints.VERTICAL;
+ c.weightx = 0;
+ layout.setConstraints(label, c);
+ add(label);
+
+ texts = new JTextField[values.length];
+ for (int j = 0; j < values.length; j++) {
+ JTextField value = new JTextField(values[j]);
+ value.setFont(AltosUILib.value_font);
+ value.setHorizontalAlignment(SwingConstants.RIGHT);
+ texts[j] = value;
+ c.gridx = j+1; c.gridy = y;
+ c.anchor = GridBagConstraints.EAST;
+ c.fill = GridBagConstraints.BOTH;
+ c.weightx = 1;
+ layout.setConstraints(value, c);
+ add(value);
+ }
+ }
+ }
+
+ MicroStat max_height, max_speed;
+ MicroStat max_accel, avg_accel;
+ MicroStat boost_duration;
+ MicroStat coast_duration;
+ MicroStat descent_speed;
+ MicroStat descent_duration;
+ MicroStat flight_time;
+
+ public void setStats(MicroStats stats) {
+ max_height.set_values(String.format("%5.0f m", stats.apogee_height),
+ String.format("%5.0f ft", AltosConvert.meters_to_feet(stats.apogee_height)));
+ max_speed.set_values(String.format("%5.0f m/s", stats.max_speed),
+ String.format("%5.0f mph", AltosConvert.meters_to_mph(stats.max_speed)),
+ String.format("Mach %4.1f", AltosConvert.meters_to_mach(stats.max_speed)));
+ max_accel.set_values(String.format("%5.0f m/s²", stats.max_accel),
+ String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.max_accel)),
+ String.format("%5.0f G", AltosConvert.meters_to_g(stats.max_accel)));
+ avg_accel.set_values(String.format("%5.0f m/s²", stats.boost_accel(),
+ String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.boost_accel())),
+ String.format("%5.0f G", AltosConvert.meters_to_g(stats.boost_accel()))));
+ boost_duration.set_values(String.format("%6.1f s", stats.boost_duration()));
+ coast_duration.set_values(String.format("%6.1f s", stats.coast_duration()));
+ descent_speed.set_values(String.format("%5.0f m/s", stats.descent_speed()),
+ String.format("%5.0f ft/s", AltosConvert.meters_to_feet(stats.descent_speed())));
+ descent_duration.set_values(String.format("%6.1f s", stats.descent_duration()));
+ flight_time.set_values(String.format("%6.1f s", stats.landed_time));
+ }
+
+ public void setData(MicroData data) {
+ setStats(new MicroStats(data));
+ }
+
+ public MicroStatsTable(MicroStats stats) {
+ layout = new GridBagLayout();
+
+ setLayout(layout);
+ int y = 0;
+ max_height = new MicroStat(layout, y++, "Maximum height",
+ String.format("%5.0f m", stats.apogee_height),
+ String.format("%5.0f ft", AltosConvert.meters_to_feet(stats.apogee_height)));
+ max_speed = new MicroStat(layout, y++, "Maximum speed",
+ String.format("%5.0f m/s", stats.max_speed),
+ String.format("%5.0f mph", AltosConvert.meters_to_mph(stats.max_speed)),
+ String.format("Mach %4.1f", AltosConvert.meters_to_mach(stats.max_speed)));
+ max_accel = new MicroStat(layout, y++, "Maximum boost acceleration",
+ String.format("%5.0f m/s²", stats.max_accel),
+ String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.max_accel)),
+ String.format("%5.0f G", AltosConvert.meters_to_g(stats.max_accel)));
+ avg_accel = new MicroStat(layout, y++, "Average boost acceleration",
+ String.format("%5.0f m/s²", stats.boost_accel(),
+ String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.boost_accel())),
+ String.format("%5.0f G", AltosConvert.meters_to_g(stats.boost_accel()))));
+ boost_duration = new MicroStat(layout, y++, "Boost duration",
+ String.format("%6.0f s", stats.boost_duration()));
+ coast_duration = new MicroStat(layout, y++, "Coast duration",
+ String.format("%6.1f s", stats.coast_duration()));
+ descent_speed = new MicroStat(layout, y++, "Descent rate",
+ String.format("%5.0f m/s", stats.descent_speed()),
+ String.format("%5.0f ft/s", AltosConvert.meters_to_feet(stats.descent_speed())));
+ descent_duration = new MicroStat(layout, y++, "Descent duration",
+ String.format("%6.1f s", stats.descent_duration()));
+ flight_time = new MicroStat(layout, y++, "Flight Time",
+ String.format("%6.0f s", stats.landed_time));
+ }
+
+ public MicroStatsTable() {
+ this(new MicroStats());
+ }
+
+} \ No newline at end of file
diff --git a/micropeak/MicroUSB.java b/micropeak/MicroUSB.java
new file mode 100644
index 00000000..d48610fe
--- /dev/null
+++ b/micropeak/MicroUSB.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package org.altusmetrum.micropeak;
+import java.util.*;
+import libaltosJNI.*;
+
+public class MicroUSB extends altos_device {
+
+ static boolean initialized = false;
+ static boolean loaded_library = false;
+
+ public static boolean load_library() {
+ if (!initialized) {
+ try {
+ System.loadLibrary("altos");
+ libaltos.altos_init();
+ loaded_library = true;
+ } catch (UnsatisfiedLinkError e) {
+ try {
+ System.loadLibrary("altos64");
+ libaltos.altos_init();
+ loaded_library = true;
+ } catch (UnsatisfiedLinkError e2) {
+ loaded_library = false;
+ }
+ }
+ initialized = true;
+ }
+ return loaded_library;
+ }
+
+ public String toString() {
+ String name = getName();
+ if (name == null)
+ name = "Altus Metrum";
+ return String.format("%-20.20s %4d %s",
+ name, getSerial(), getPath());
+ }
+
+ public String toShortString() {
+ String name = getName();
+ if (name == null)
+ name = "Altus Metrum";
+ return String.format("%s %d %s",
+ name, getSerial(), getPath());
+
+ }
+
+ public String getErrorString() {
+ altos_error error = new altos_error();
+
+ libaltos.altos_get_last_error(error);
+ return String.format("%s (%d)", error.getString(), error.getCode());
+ }
+
+ public SWIGTYPE_p_altos_file open() {
+ return libaltos.altos_open(this);
+ }
+
+ private boolean isMicro() {
+ if (getVendor() != 0x0403)
+ return false;
+ if (getProduct() != 0x6001)
+ return false;
+ return true;
+ }
+
+ static java.util.List<MicroUSB> list() {
+ if (!load_library())
+ return null;
+
+ SWIGTYPE_p_altos_list list = libaltos.altos_list_start();
+
+ ArrayList<MicroUSB> device_list = new ArrayList<MicroUSB>();
+ if (list != null) {
+ for (;;) {
+ MicroUSB device = new MicroUSB();
+ if (libaltos.altos_list_next(list, device) == 0)
+ break;
+ if (device.isMicro())
+ device_list.add(device);
+ }
+ libaltos.altos_list_finish(list);
+ }
+
+ return device_list;
+ }
+} \ No newline at end of file