--- /dev/null
+JAVAROOT=classes
+AM_JAVACFLAGS=-encoding UTF-8 -Xlint:deprecation
+
+CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:../altoslib/*:../libaltos:$(JCOMMON)/jcommon.jar:$(JFREECHART)/jfreechart.jar"
+
+bin_SCRIPTS=micropeak
+
+micropeakdir=$(datadir)/java
+
+micropeak_JAVA= \
+ MicroPeak.java \
+ MicroData.java \
+ MicroGraph.java \
+ MicroSerial.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
+
+all-local: micropeak-test $(JAR)
+
+clean-local:
+ -rm -rf classes $(JAR) $(FATJAR) \
+ $(ALTOSLIB_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)
+ jar cfm $@ Manifest.txt \
+ $(ICONJAR) \
+ -C classes org \
+ -C ../libaltos libaltosJNI
+
+$(FATJAR): classmicropeak.stamp Manifest-fat.txt $(ALTOSLIB_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/"$@" .
+
+$(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 $(JCOMMON)/jcommon.jar $(JFREECHART)/jfreechart.jar" >> $@
+
+Manifest-fat.txt:
+ echo 'Main-Class: org.altusmetrum.micropeak.MicroPeak' > $@
+ echo "Class-Path: AltosLib.jar jcommon.jar jfreechart.jar" >> $@
+
--- /dev/null
+/*
+ * 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.*;
+
+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]);
+ }
+
+ 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();
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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.*;
+
+public class MicroGraph {
+
+ XYPlot plot;
+ JFreeChart chart;
+ ChartPanel panel;
+ NumberAxis xAxis;
+ XYSeries heightSeries;
+ XYSeries speedSeries;
+ XYSeries accelSeries;
+
+ MicroData data;
+
+ public JPanel panel() {
+ return panel;
+ }
+
+ private void addSeries(XYSeries series, int index, String label, String units) {
+ XYSeriesCollection dataset = new XYSeriesCollection(series);
+ NumberAxis axis = new NumberAxis(String.format("%s (%s)", label, units));
+ XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
+
+ 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, axis);
+ plot.setDataset(index, dataset);
+ plot.setRenderer(index, renderer);
+ plot.mapDatasetToRangeAxis(index, index);
+ }
+
+ public MicroGraph(MicroData data) {
+
+ this.data = data;
+
+ heightSeries = new XYSeries("Height");
+ speedSeries = new XYSeries("Speed");
+ accelSeries = new XYSeries("Acceleration");
+
+ for (int i = 0; i < data.pressures.length; i++) {
+ double x = data.time(i);
+ heightSeries.add(x, data.height(i));
+ speedSeries.add(x, data.speed(i));
+ accelSeries.add(x, data.acceleration(i));
+ }
+
+ xAxis = new NumberAxis("Time (s)");
+
+ xAxis.setAutoRangeIncludesZero(true);
+
+ plot = new XYPlot();
+ plot.setDomainAxis(xAxis);
+ plot.setOrientation(PlotOrientation.VERTICAL);
+ plot.setDomainPannable(true);
+ plot.setRangePannable(true);
+
+ addSeries(heightSeries, 0, "Height", "m");
+ addSeries(speedSeries, 1, "Speed", "m/s");
+ addSeries(accelSeries, 2, "Acceleration", "m/s²");
+
+ 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));
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.*;
+
+public class MicroPeak extends JFrame implements ActionListener, ItemListener {
+
+ File filename;
+ MicroGraph graph;
+ MicroData data;
+ Container pane;
+
+ private void OpenFile(File filename) {
+ try {
+ FileInputStream input = new FileInputStream(filename);
+ try {
+ data = new MicroData(input);
+ graph = new MicroGraph(data);
+ pane.add(graph.panel);
+ } catch (IOException ioe) {
+ }
+ try {
+ input.close();
+ } catch (IOException ioe) {
+ }
+ } catch (FileNotFoundException fne) {
+ }
+ }
+
+ private void SelectFile() {
+ }
+
+ 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) {
+ System.out.printf("action %s %s\n", ev.getActionCommand(), ev.paramString());
+ 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();
+ }
+
+ public void itemStateChanged(ItemEvent e) {
+ }
+
+ public MicroPeak(File filename) {
+
+ this.filename = filename;
+
+ pane = getContentPane();
+
+// JLabel label = new JLabel ("Hello, World");
+// pane.add(label);
+
+ setSize(800, 500);
+
+ 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 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);
+ }
+ });
+
+ if (filename != null)
+ this.OpenFile(filename);
+ setVisible(true);
+ }
+
+ public MicroPeak() {
+ this(null);
+ }
+
+ public static void main(final String[] args) {
+ boolean opened = false;
+
+ for (int i = 0; i < args.length; i++) {
+ new MicroPeak(new File(args[i]));
+ opened = true;
+ }
+ if (!opened)
+ new MicroPeak();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.*;
+
+public class MicroSerial extends InputStream {
+ SWIGTYPE_p_altos_file file;
+
+ public int read() {
+ return libaltos.altos_getchar(file, 0);
+ }
+
+ 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));
+ }
+ }
+}
--- /dev/null
+/*
+ * 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