Merge branch 'master' of git://git.gag.com/fw/altos
authorAnthony Towns <aj@erisian.com.au>
Tue, 28 Sep 2010 02:55:47 +0000 (12:55 +1000)
committerAnthony Towns <aj@erisian.com.au>
Tue, 28 Sep 2010 02:55:47 +0000 (12:55 +1000)
ao-tools/altosui/AltosCsvReader.java [new file with mode: 0644]
ao-tools/altosui/AltosDataPoint.java [new file with mode: 0644]
ao-tools/altosui/AltosGraph.java [new file with mode: 0644]
ao-tools/altosui/AltosGraphDataChooser.java [new file with mode: 0644]
ao-tools/altosui/AltosGraphTime.java [new file with mode: 0644]
ao-tools/altosui/AltosGraphUI.java [new file with mode: 0644]
ao-tools/altosui/AltosUI.java
ao-tools/altosui/Makefile.am

diff --git a/ao-tools/altosui/AltosCsvReader.java b/ao-tools/altosui/AltosCsvReader.java
new file mode 100644 (file)
index 0000000..600788f
--- /dev/null
@@ -0,0 +1,113 @@
+
+// Copyright (c) 2010 Anthony Towns
+// GPL v2 or later
+
+package altosui;
+
+import java.lang.UnsupportedOperationException;
+import java.util.HashMap;
+import java.util.NoSuchElementException;
+import java.util.Iterator;
+import java.io.*;
+import com.csvreader.CsvReader;
+
+import altosui.AltosDataPoint;
+
+class AltosCsvReader implements Iterable<AltosDataPoint>
+{
+    public CsvReader csv;
+    public AltosDataPoint next = null;
+
+    static protected String [] headers = "version serial flight call time rssi state state_name acceleration pressure altitude height accel_speed baro_speed temperature battery_voltage drogue_voltage main_voltage connected locked nsat latitude longitude altitude year month day hour minute second pad_dist pad_range pad_az pad_el".split(" ");
+
+    AltosCsvReader(Reader stream) {
+        csv = new CsvReader(stream);
+        csv.setComment('#');
+        csv.setUseComments(true);
+        csv.setHeaders(headers);
+    }
+    AltosCsvReader(String filename) throws FileNotFoundException {
+        csv = new CsvReader(filename);
+        csv.setComment('#');
+        csv.setUseComments(true);
+        csv.setHeaders(headers);
+    }
+
+    public Iterator<AltosDataPoint> iterator() {
+        return new Iterator<AltosDataPoint>() {
+            public void remove() { 
+                throw new UnsupportedOperationException(); 
+            }
+            public boolean hasNext() {
+                if (next == null) {
+                    try {
+                        if (csv.readRecord()) {
+                            next = new CsvRow();
+                        } else {
+                            close();
+                            return false;
+                        }
+                    } catch (IOException e) {
+                        close();
+                        return false;
+                    }
+                }
+                return true;
+            }
+            public AltosDataPoint next() {
+                if (!hasNext())
+                    throw new NoSuchElementException();
+                AltosDataPoint res = next;
+                next = null;
+                return res;
+            }
+        };
+    }
+
+    public void close() {
+        csv.close();
+    }
+
+    private class CsvRow extends HashMap<String,String>
+            implements AltosDataPoint 
+    {
+        CsvRow() throws IOException {
+            for (int i = 0; i < headers.length; i++) {
+                this.put(headers[i], csv.get(headers[i]).trim());
+            }
+        }
+
+        private int intField(String name) {
+            return Integer.parseInt(get(name).trim());
+        }
+        private double doubleField(String name) {
+            return Double.valueOf(get(name)).doubleValue();
+        }
+        private String stringField(String name) {
+            return get(name);
+        }
+
+        public int version() { return intField("version"); }
+        public int serial() { return intField("serial"); }
+        public int flight() { return intField("flight"); }
+        public String callsign() { return stringField("call"); }
+        public double time() { return doubleField("time"); }
+        public double rssi() { return doubleField("rssi"); }
+
+        public int state() { return intField("state"); }
+        public String state_name() { return stringField("state_name"); }
+
+        public double acceleration() { return doubleField("acceleration"); }
+        public double pressure() { return doubleField("pressure"); }
+        public double altitude() { return doubleField("altitude"); }
+        public double height() { return doubleField("height"); }
+        public double accel_speed() { return doubleField("accel_speed"); }
+        public double baro_speed() { return doubleField("baro_speed"); }
+        public double temperature() { return doubleField("temperature"); }
+        public double battery_voltage() { 
+            return doubleField("battery_voltage"); 
+        }
+        public double drogue_voltage() { return doubleField("drogue_voltage"); }
+        public double main_voltage() { return doubleField("main_voltage"); }
+    }
+}
diff --git a/ao-tools/altosui/AltosDataPoint.java b/ao-tools/altosui/AltosDataPoint.java
new file mode 100644 (file)
index 0000000..66313e0
--- /dev/null
@@ -0,0 +1,29 @@
+
+// Copyright (c) 2010 Anthony Towns
+// GPL v2 or later
+
+package altosui;
+
+interface AltosDataPoint {
+    int version();
+    int serial();
+    int flight();
+    String callsign();
+    double time();
+    double rssi();
+
+    int state();
+    String state_name();
+
+    double acceleration();
+    double pressure();
+    double altitude();
+    double height();
+    double accel_speed();
+    double baro_speed();
+    double temperature();
+    double battery_voltage();
+    double drogue_voltage();
+    double main_voltage();
+}
+
diff --git a/ao-tools/altosui/AltosGraph.java b/ao-tools/altosui/AltosGraph.java
new file mode 100644 (file)
index 0000000..fa3b87c
--- /dev/null
@@ -0,0 +1,27 @@
+
+// Copyright (c) 2010 Anthony Towns
+// GPL v2 or later
+
+package altosui;
+
+import java.io.*;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.ChartUtilities;
+
+import altosui.AltosDataPoint;
+
+abstract class AltosGraph {
+    public String filename;
+    public abstract void addData(AltosDataPoint d);
+    public abstract JFreeChart createChart();
+    public void toPNG() throws java.io.IOException { toPNG(300, 500); }
+    public void toPNG(int width, int height)
+        throws java.io.IOException
+    {
+        File pngout = new File(filename);
+        JFreeChart chart = createChart();
+        ChartUtilities.saveChartAsPNG(pngout, chart, width, height);
+        System.out.println("Created " + filename);
+    }
+}
diff --git a/ao-tools/altosui/AltosGraphDataChooser.java b/ao-tools/altosui/AltosGraphDataChooser.java
new file mode 100644 (file)
index 0000000..1bf2845
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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 altosui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.swing.table.*;
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import java.util.prefs.*;
+
+import altosui.AltosPreferences;
+import altosui.AltosReader;
+import altosui.AltosCsvReader;
+import altosui.AltosEepromReader;
+import altosui.AltosTelemetryReader;
+
+public class AltosGraphDataChooser extends JFileChooser {
+       JFrame  frame;
+       String  filename;
+       File    file;
+
+       public String filename() {
+               return filename;
+       }
+
+       public File file() {
+               return file;
+       }
+
+       public Iterable<AltosDataPoint> runDialog() {
+               int     ret;
+
+               ret = showOpenDialog(frame);
+               if (ret == APPROVE_OPTION) {
+                       file = getSelectedFile();
+                       if (file == null)
+                               return null;
+                       filename = file.getName();
+                       try {
+                               return new AltosCsvReader(new FileReader(file));
+                       } catch (FileNotFoundException fe) {
+                               JOptionPane.showMessageDialog(frame,
+                                                             filename,
+                                                             "Cannot open file",
+                                                             JOptionPane.ERROR_MESSAGE);
+                       }
+               }
+               return null;
+       }
+
+       public AltosGraphDataChooser(JFrame in_frame) {
+               frame = in_frame;
+               setDialogTitle("Select Flight Record File");
+               setFileFilter(new FileNameExtensionFilter("Flight data file",
+                                                         "csv"));
+               setCurrentDirectory(AltosPreferences.logdir());
+       }
+}
diff --git a/ao-tools/altosui/AltosGraphTime.java b/ao-tools/altosui/AltosGraphTime.java
new file mode 100644 (file)
index 0000000..c0f99c5
--- /dev/null
@@ -0,0 +1,222 @@
+
+// Copyright (c) 2010 Anthony Towns
+// GPL v2 or later
+
+package altosui;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.jfree.chart.ChartUtilities;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.AxisLocation;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.labels.StandardXYToolTipGenerator;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.plot.ValueMarker;
+import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
+import org.jfree.chart.renderer.xy.XYItemRenderer;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+import org.jfree.ui.RectangleAnchor;
+import org.jfree.ui.TextAnchor;
+
+import altosui.AltosDataPoint;
+import altosui.AltosGraph;
+
+class AltosGraphTime extends AltosGraph {
+    static interface Element {
+        void attachGraph(AltosGraphTime g);
+        void gotTimeData(double time, AltosDataPoint d);
+        void addToPlot(AltosGraphTime g, XYPlot plot);
+    }
+
+    static class TimeAxis implements Element {
+        private int axis;
+        private Color color;
+        private String label;
+        private AxisLocation locn;
+        private double min_y = Double.NaN;
+
+        public TimeAxis(int axis, String label, Color color, AxisLocation locn)
+        {
+            this.axis = axis;
+            this.color = color;
+            this.label = label;
+            this.locn = locn;
+        }
+
+        public void setLowerBound(double min_y) {
+            this.min_y = min_y;
+        }
+
+        public void attachGraph(AltosGraphTime g) { return; }
+        public void gotTimeData(double time, AltosDataPoint d) { return; }
+
+        public void addToPlot(AltosGraphTime g, XYPlot plot) {
+            NumberAxis numAxis = new NumberAxis(label);
+            if (!Double.isNaN(min_y))
+                numAxis.setLowerBound(min_y);
+            plot.setRangeAxis(axis, numAxis);
+            plot.setRangeAxisLocation(axis, locn);
+            numAxis.setLabelPaint(color);
+            numAxis.setTickLabelPaint(color);
+            numAxis.setAutoRangeIncludesZero(false);
+        }
+    }
+
+    abstract static class TimeSeries implements Element {
+        protected XYSeries series;
+        private String axisName;
+        private Color color;
+
+        public TimeSeries(String axisName, String label, Color color) {
+            this.series = new XYSeries(label);
+            this.axisName = axisName;
+            this.color = color;
+        }
+
+        public void attachGraph(AltosGraphTime g) {
+            g.setAxis(this, axisName, color);
+        }
+        abstract public void gotTimeData(double time, AltosDataPoint d);
+
+        public void addToPlot(AltosGraphTime g, XYPlot plot) {
+            XYSeriesCollection dataset = new XYSeriesCollection();
+            dataset.addSeries(this.series);
+
+            XYItemRenderer renderer = new StandardXYItemRenderer();
+            renderer.setSeriesPaint(0, color);
+
+            int dataNum = g.getDataNum(this);
+            int axisNum = g.getAxisNum(this);
+
+            plot.setDataset(dataNum, dataset);
+            plot.mapDatasetToRangeAxis(dataNum, axisNum);
+            plot.setRenderer(dataNum, renderer);
+        }
+    }
+
+    static class StateMarker implements Element {
+        private double val = Double.NaN;
+        private String name;
+        private int state;
+
+        StateMarker(int state, String name) {
+            this.state = state;
+            this.name = name;
+        }
+
+        public void attachGraph(AltosGraphTime g) { return; }
+        public void gotTimeData(double time, AltosDataPoint d) {
+            if (Double.isNaN(val) || time < val) {
+                if (d.state() == state) {
+                    val = time;
+                }
+            }
+        }
+
+        public void addToPlot(AltosGraphTime g, XYPlot plot) {
+            if (Double.isNaN(val))
+                return;
+
+            ValueMarker m = new ValueMarker(val);
+            m.setLabel(name);
+            m.setLabelAnchor(RectangleAnchor.TOP_RIGHT);
+            m.setLabelTextAnchor(TextAnchor.TOP_LEFT);
+            plot.addDomainMarker(m);
+        }
+    }
+
+    private String title;
+    private ArrayList<Element> elements;
+    private HashMap<String,Integer> axes;
+    private HashMap<Element,Integer> datasets;
+    private ArrayList<Integer> datasetAxis;
+
+    public AltosGraphTime(String title) {
+        this.filename = title.toLowerCase().replaceAll("[^a-z0-9]","_")+".png";
+        this.title = title;
+        this.elements = new ArrayList<Element>();
+        this.axes = new HashMap<String,Integer>();
+        this.datasets = new HashMap<Element,Integer>();
+        this.datasetAxis = new ArrayList<Integer>();
+    }
+
+    public AltosGraphTime addElement(Element e) {
+        e.attachGraph(this);
+        elements.add(e);
+        return this;
+    }
+
+    public void setAxis(Element ds, String axisName, Color color) {
+        Integer axisNum = axes.get(axisName);
+        int dsNum = datasetAxis.size();
+        if (axisNum == null) {
+            axisNum = newAxis(axisName, color);
+        }
+        datasets.put(ds, dsNum);
+        datasetAxis.add(axisNum);
+    }
+
+    public int getAxisNum(Element ds) {
+        return datasetAxis.get( datasets.get(ds) ).intValue();
+    }
+    public int getDataNum(Element ds) {
+        return datasets.get(ds).intValue();
+    }
+
+    private Integer newAxis(String name, Color color) {
+        int cnt = axes.size();
+        AxisLocation locn = AxisLocation.BOTTOM_OR_LEFT;
+        if (cnt > 0) {
+            locn = AxisLocation.TOP_OR_RIGHT;
+        }
+        Integer res = new Integer(cnt);
+        axes.put(name, res);
+        this.addElement(new TimeAxis(cnt, name, color, locn));
+        return res;
+    }
+
+    public void addData(AltosDataPoint d) {
+        double time = d.time();
+        for (Element e : elements) {
+            e.gotTimeData(time, d);
+        }
+    }
+
+    public JFreeChart createChart() {
+        NumberAxis xAxis = new NumberAxis("Time (s)");
+        xAxis.setAutoRangeIncludesZero(false);
+        XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
+        XYPlot plot = new XYPlot();
+        plot.setDomainAxis(xAxis);
+        plot.setRenderer(renderer);
+        plot.setOrientation(PlotOrientation.VERTICAL);
+
+        renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
+        JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT,
+                                plot, true);
+        ChartUtilities.applyCurrentTheme(chart);
+
+        plot.setDomainPannable(true);
+        plot.setRangePannable(true);
+   
+        for (Element e : elements) {
+            e.addToPlot(this, plot);
+        }
+
+        return chart;
+    }
+
+    public void toPNG() throws java.io.IOException {
+        if (axes.size() > 1) {
+            toPNG(800, 500);
+        } else {
+            toPNG(300, 500);
+        }
+    }
+}
diff --git a/ao-tools/altosui/AltosGraphUI.java b/ao-tools/altosui/AltosGraphUI.java
new file mode 100644 (file)
index 0000000..73f9502
--- /dev/null
@@ -0,0 +1,301 @@
+
+// Copyright (c) 2010 Anthony Towns
+// GPL v2 or later
+
+package altosui;
+
+import java.io.*;
+import java.util.ArrayList;
+
+import javax.swing.JFrame;
+import java.awt.Color;
+
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.ChartUtilities;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.AxisLocation;
+import org.jfree.ui.ApplicationFrame;
+import org.jfree.ui.RefineryUtilities;
+
+import altosui.AltosCsvReader;
+import altosui.AltosDataPoint;
+import altosui.AltosGraphTime;
+
+public class AltosGraphUI extends JFrame 
+{
+    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);
+    static final private Color black = new Color(31,31,31);
+
+    static private class OverallGraphs {
+        AltosGraphTime.Element height = 
+            new AltosGraphTime.TimeSeries("Height (m)", "Height (AGL)", red) {
+                public void gotTimeData(double time, AltosDataPoint d) {
+                    series.add(time, d.height()); 
+                } 
+            };
+    
+        AltosGraphTime.Element speed =
+            new AltosGraphTime.TimeSeries("Speed (m/s)", "Vertical Speed", green) { 
+                public void gotTimeData(double time, AltosDataPoint d) {
+                    if (d.state() < 4) {
+                        series.add(time, d.accel_speed());
+                    } else {
+                        series.add(time, d.baro_speed());
+                    }
+                }
+            };
+    
+        AltosGraphTime.Element acceleration =
+            new AltosGraphTime.TimeSeries("Acceleration (m/s\u00B2)", 
+                    "Axial Acceleration", blue) 
+            {
+                public void gotTimeData(double time, AltosDataPoint d) {
+                    series.add(time, d.acceleration());
+                }
+            };
+    
+        AltosGraphTime.Element temperature =
+            new AltosGraphTime.TimeSeries("Temperature (\u00B0C)", 
+                    "Board temperature", red) 
+            {
+                public void gotTimeData(double time, AltosDataPoint d) {
+                    series.add(time, d.temperature());
+                }
+            };
+    
+        AltosGraphTime.Element drogue_voltage =
+            new AltosGraphTime.TimeSeries("Voltage (V)", "Drogue Continuity", blue) 
+            {
+                public void gotTimeData(double time, AltosDataPoint d) {
+                    series.add(time, d.drogue_voltage());
+                }
+            };
+    
+        AltosGraphTime.Element main_voltage =
+            new AltosGraphTime.TimeSeries("Voltage (V)", "Main Continuity", green) 
+            {
+                public void gotTimeData(double time, AltosDataPoint d) {
+                    series.add(time, d.main_voltage());
+                }
+            };
+    
+        AltosGraphTime.Element e_pad    = new AltosGraphTime.StateMarker(2, "Pad");
+        AltosGraphTime.Element e_boost  = new AltosGraphTime.StateMarker(3, "Boost");
+        AltosGraphTime.Element e_fast   = new AltosGraphTime.StateMarker(4, "Fast");
+        AltosGraphTime.Element e_coast  = new AltosGraphTime.StateMarker(5, "Coast");
+        AltosGraphTime.Element e_drogue = new AltosGraphTime.StateMarker(6, "Drogue");
+        AltosGraphTime.Element e_main   = new AltosGraphTime.StateMarker(7, "Main");
+        AltosGraphTime.Element e_landed = new AltosGraphTime.StateMarker(8, "Landed");
+    
+        protected AltosGraphTime myAltosGraphTime(String suffix) {
+            return (new AltosGraphTime("Overall " + suffix))
+                .addElement(e_boost)
+                .addElement(e_drogue)
+                .addElement(e_main)
+                .addElement(e_landed);
+        }
+    
+        public ArrayList<AltosGraph> graphs() {
+            ArrayList<AltosGraph> graphs = new ArrayList<AltosGraph>();
+    
+            graphs.add( myAltosGraphTime("Summary")
+                    .addElement(height)
+                    .addElement(speed)
+                    .addElement(acceleration) );
+    
+            graphs.add( myAltosGraphTime("Altitude")
+                    .addElement(height) );
+    
+            graphs.add( myAltosGraphTime("Speed")
+                    .addElement(speed) );
+    
+            graphs.add( myAltosGraphTime("Acceleration")
+                    .addElement(acceleration) );
+    
+            graphs.add( myAltosGraphTime("Temperature")
+                    .addElement(temperature) );
+    
+            graphs.add( myAltosGraphTime("Continuity")
+                    .addElement(drogue_voltage)
+                    .addElement(main_voltage) );
+    
+            return graphs;
+        }
+    }
+    
+    static private class AscentGraphs extends OverallGraphs {
+        protected AltosGraphTime myAltosGraphTime(String suffix) {
+            return (new AltosGraphTime("Ascent " + suffix) {
+                public void addData(AltosDataPoint d) {
+                    int state = d.state();
+                    if (3 <= state && state <= 5) {
+                        super.addData(d);
+                    }
+                }
+            }).addElement(e_boost)
+              .addElement(e_fast)
+              .addElement(e_coast);
+        }
+    }
+    
+    static private class DescentGraphs extends OverallGraphs {
+        protected AltosGraphTime myAltosGraphTime(String suffix) {
+            return (new AltosGraphTime("Descent " + suffix) {
+                public void addData(AltosDataPoint d) {
+                    int state = d.state();
+                    if (6 <= state && state <= 7) {
+                        super.addData(d);
+                    }
+                }
+            }).addElement(e_drogue)
+              .addElement(e_main);
+            // ((XYGraph)graph[8]).ymin = new Double(-50);
+        }
+    }
+
+    public AltosGraphUI(JFrame frame)
+    {
+        super("Altos Graph");
+
+        AltosGraphDataChooser chooser;
+        chooser = new AltosGraphDataChooser(frame);
+        Iterable<AltosDataPoint> reader = chooser.runDialog();
+        if (reader == null)
+            return;
+        
+        init(reader, 0);
+    }
+
+    public AltosGraphUI(Iterable<AltosDataPoint> data, int which) 
+    {
+        super("Altos Graph");
+        init(data, which);
+    }
+
+    private void init(Iterable<AltosDataPoint> data, int which) {
+        AltosGraph graph = createGraph(data, which);
+
+        JFreeChart chart = graph.createChart();
+        ChartPanel chartPanel = new ChartPanel(chart);
+        chartPanel.setMouseWheelEnabled(true);
+        chartPanel.setPreferredSize(new java.awt.Dimension(800, 500));
+        setContentPane(chartPanel);
+
+        pack();
+
+        RefineryUtilities.centerFrameOnScreen(this);
+
+        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+        setVisible(true);
+    }
+
+    private static AltosGraph createGraph(Iterable<AltosDataPoint> data,
+            int which)
+    {
+        return createGraphsWhich(data, which).get(0);
+    }
+
+    private static ArrayList<AltosGraph> createGraphs(
+            Iterable<AltosDataPoint> data)
+    {
+        return createGraphsWhich(data, -1);
+    }
+
+    private static ArrayList<AltosGraph> createGraphsWhich(
+            Iterable<AltosDataPoint> data, int which)
+    {
+        ArrayList<AltosGraph> graph = new ArrayList<AltosGraph>();
+        graph.addAll((new OverallGraphs()).graphs());
+        graph.addAll((new AscentGraphs()).graphs());
+        graph.addAll((new DescentGraphs()).graphs());
+
+        if (which > 0) {
+            if (which >= graph.size()) {
+                which = 0;
+            }
+            AltosGraph g = graph.get(which);
+            graph = new ArrayList<AltosGraph>();
+            graph.add(g);
+        }
+
+        for (AltosDataPoint dp : data) {
+            for (AltosGraph g : graph) {
+                g.addData(dp);
+            }
+        }
+
+        return graph;
+    }
+
+    public static void main(String[] args) 
+        throws java.io.FileNotFoundException, java.io.IOException 
+    {
+        if (args.length < 1 || 2 < args.length)
+        {
+            System.out.println("Please specify telemetry csv");
+            return;
+        }
+
+        AltosCsvReader csv = new AltosCsvReader(args[0]);
+        if (args.length == 1) {
+            for (AltosGraph g : createGraphs(csv)) {
+                g.toPNG();
+            }
+        } else {
+            int which = Integer.parseInt(args[1].trim());
+            AltosGraphUI demo = new AltosGraphUI(csv, which);
+        }
+    }
+}
+
+/* gnuplot bits...
+ *
+300x400
+
+--------------------------------------------------------
+TOO HARD! :)
+
+"ascent-gps-accuracy.png" "Vertical error margin to apogee - GPS v Baro (m)"
+    5:($7 < 6 ? $24-$11 : 1/0)
+"descent-gps-accuracy.png" "Vertical error margin during descent - GPS v Baro (m)"
+    5:($7 < 6 ? 1/0 : $24-$11)
+
+set output "overall-gps-accuracy.png"
+set ylabel "distance above sea level (m)"
+plot "telemetry.csv" using 5:11 with lines ti "baro altitude" axis x1y1, \
+    "telemetry.csv" using 5:24 with lines ti "gps altitude" axis x1y1
+
+set term png tiny size 700,700 enhanced
+set xlabel "m"
+set ylabel "m"
+set polar
+set grid polar
+set rrange[*:*]
+set angles degrees
+
+set output "overall-gps-path.png"
+#:30 with yerrorlines
+plot "telemetry.csv" using (90-$33):($7 == 2 ? $31 : 1/0) with lines ti "pad", \
+    "telemetry.csv" using (90-$33):($7 == 3 ? $31 : 1/0) with lines ti "boost", \
+    "telemetry.csv" using (90-$33):($7 == 4 ? $31 : 1/0) with lines ti "fast", \
+    "telemetry.csv" using (90-$33):($7 == 5 ? $31 : 1/0) with lines ti "coast", \
+    "telemetry.csv" using (90-$33):($7 == 6 ? $31 : 1/0) with lines ti "drogue", \
+    "telemetry.csv" using (90-$33):($7 == 7 ? $31 : 1/0) with lines ti "main", \
+    "telemetry.csv" using (90-$33):($7 == 8 ? $31 : 1/0) with lines ti "landed"
+
+set output "ascent-gps-path.png"
+plot "telemetry.csv" using (90-$33):($7 == 2 ? $31 : 1/0):30 with lines ti "pad", \
+    "telemetry.csv" using (90-$33):($7 == 3 ? $31 : 1/0):20 with lines ti "boost", \
+    "telemetry.csv" using (90-$33):($7 == 4 ? $31 : 1/0):10 with lines ti "fast", \
+    "telemetry.csv" using (90-$33):($7 == 5 ? $31 : 1/0):5 with lines ti "coast"
+
+set output "descent-gps-path.png"
+plot "telemetry.csv" using (90-$33):($7 == 6 ? $31 : 1/0) with lines ti "drogue", \
+    "telemetry.csv" using (90-$33):($7 == 7 ? $31 : 1/0) with lines ti "main", \
+    "telemetry.csv" using (90-$33):($7 == 8 ? $31 : 1/0) with lines ti "landed"
+
+ */
+
+
index 29eda2ece4566f793bbe1cb386a3849d00e28596..de0673a209e396630fb3b166a2e1586bf2b7f7bb 100644 (file)
@@ -245,6 +245,13 @@ public class AltosUI extends JFrame {
                new AltosCSVUI(AltosUI.this);
        }
 
+       /* Load a flight log CSV file and display a pretty graph.
+        */
+
+       private void GraphData() {
+               new AltosGraphUI(AltosUI.this);
+       }
+
        /* Create the AltosUI menus
         */
        private void createMenu() {
@@ -291,6 +298,14 @@ public class AltosUI extends JFrame {
                                });
                        menu.add(item);
 
+                       item = new JMenuItem("Graph Data",KeyEvent.VK_F);
+                       item.addActionListener(new ActionListener() {
+                                       public void actionPerformed(ActionEvent e) {
+                                               GraphData();
+                                       }
+                               });
+                       menu.add(item);
+
                        item = new JMenuItem("Quit",KeyEvent.VK_Q);
                        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
                                                                   ActionEvent.CTRL_MASK));
@@ -472,4 +487,4 @@ public class AltosUI extends JFrame {
                        altosui.setVisible(true);
                }
        }
-}
\ No newline at end of file
+}
index 7070af22e7fda87a0786a7e5d818b08a793f266a..ab8ca7d4a80163b4ed385a64ac65c12702a136b8 100644 (file)
@@ -5,7 +5,7 @@ man_MANS=altosui.1
 
 altoslibdir=$(libdir)/altos
 
-CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:../libaltos:$(FREETTS)/*:/usr/share/java/*"
+CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:../libaltos:$(JFREECHART)/*:$(FREETTS)/*:/usr/share/java/*"
 
 bin_SCRIPTS=altosui
 
@@ -53,8 +53,19 @@ altosui_JAVA = \
        AltosTelemetry.java \
        AltosTelemetryIterable.java \
        AltosUI.java \
+    AltosCsvReader.java \
+    AltosDataPoint.java \
+    AltosGraph.java \
+    AltosGraphTime.java \
+    AltosGraphUI.java \
+    AltosGraphDataChooser.java \
        AltosVoice.java
 
+JFREECHART_CLASS= \
+    jfreechart.jar \
+    jcommon.jar \
+    csv.jar
+
 FREETTS_CLASS= \
        cmudict04.jar \
        cmulex.jar \
@@ -87,7 +98,7 @@ LINUX_DIST=Altos-Linux-$(VERSION).tar.bz2
 MACOSX_DIST=Altos-Mac-$(VERSION).zip
 WINDOWS_DIST=Altos-Windows-$(VERSION_DASH).exe
 
-FAT_FILES=$(FATJAR) $(FREETTS_CLASS)
+FAT_FILES=$(FATJAR) $(FREETTS_CLASS) $(JFREECHART_CLASS)
 
 LINUX_FILES=$(FAT_FILES) libaltos.so $(FIRMWARE)
 LINUX_EXTRA=altosui-fat
@@ -102,7 +113,7 @@ all-local: classes/altosui $(JAR) altosui altosui-test
 clean-local:
        -rm -rf classes $(JAR) $(FATJAR) \
                $(LINUX_DIST) $(MACOSX_DIST) windows $(WINDOWS_DIST) $(FREETTS_CLASS) \
-               $(LIBALTOS) Manifest.txt Manifest-fat.txt altos-windows.log \
+               $(JFREECHART_CLASS) $(LIBALTOS) Manifest.txt Manifest-fat.txt altos-windows.log \
                altosui altosui-test macosx linux
 
 if FATINSTALL
@@ -149,7 +160,7 @@ $(JAR): classaltosui.stamp Manifest.txt $(JAVA_ICON)
                -C classes altosui \
                -C ../libaltos libaltosJNI
 
-$(FATJAR): classaltosui.stamp Manifest-fat.txt $(FREETTS_CLASS) $(LIBALTOS) $(JAVA_ICON)
+$(FATJAR): classaltosui.stamp Manifest-fat.txt $(FREETTS_CLASS) $(JFREECHART_CLASS) $(LIBALTOS) $(JAVA_ICON)
        jar cfm $@ Manifest-fat.txt \
                -C $(top_srcdir)/icon altus-metrum-16x16.jpg \
                -C classes altosui \
@@ -157,11 +168,11 @@ $(FATJAR): classaltosui.stamp Manifest-fat.txt $(FREETTS_CLASS) $(LIBALTOS) $(JA
 
 Manifest.txt: Makefile
        echo 'Main-Class: altosui.AltosUI' > $@
-       echo "Class-Path: $(FREETTS)/freetts.jar" >> $@
+       echo "Class-Path: $(FREETTS)/freetts.jar $(JFREECHART)/jfreechart.jar $(JFREECHAR)/jcommon.jar $(JFREECHART)/csv.jar" >> $@
 
 Manifest-fat.txt:
        echo 'Main-Class: altosui.AltosUI' > $@
-       echo "Class-Path: freetts.jar" >> $@
+       echo "Class-Path: freetts.jar jfreechart.jar jcommon.jar csv.jar" >> $@
 
 altosui: Makefile
        echo "#!/bin/sh" > $@