diff options
Diffstat (limited to 'micropeak')
-rw-r--r-- | micropeak/.gitignore | 6 | ||||
-rw-r--r-- | micropeak/Makefile.am | 146 | ||||
-rw-r--r-- | micropeak/MicroData.java | 370 | ||||
-rw-r--r-- | micropeak/MicroFileChooser.java | 67 | ||||
-rw-r--r-- | micropeak/MicroFrame.java | 37 | ||||
-rw-r--r-- | micropeak/MicroGraph.java | 154 | ||||
-rw-r--r-- | micropeak/MicroPeak.java | 171 | ||||
-rw-r--r-- | micropeak/MicroSerial.java | 51 | ||||
-rw-r--r-- | micropeak/MicroStats.java | 184 | ||||
-rw-r--r-- | micropeak/MicroStatsTable.java | 138 | ||||
-rw-r--r-- | micropeak/MicroUSB.java | 103 |
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 |