// Copyright (c) 2010 Anthony Towns // GPL v2 or later package altosui; import java.io.*; import java.util.ArrayList; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.*; import org.altusmetrum.AltosLib.*; 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; public class AltosGraphUI extends AltosFrame { JTabbedPane pane; 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 final private Color yellow = new Color(194,194,31); static final private Color cyan = new Color(31,194,194); static final private Color magenta = new Color(194,31,194); static private class OverallGraphs { AltosGraphTime.Element height = new AltosGraphTime.TimeSeries(String.format("Height (%s)", AltosConvert.height.show_units()), "Height (AGL)", red) { public void gotTimeData(double time, AltosDataPoint d) { double height = d.height(); if (height != AltosRecord.MISSING) series.add(time, AltosConvert.height.value(height)); } }; AltosGraphTime.Element speed = new AltosGraphTime.TimeSeries(String.format("Speed (%s)", AltosConvert.speed.show_units()), "Vertical Speed", green) { public void gotTimeData(double time, AltosDataPoint d) { double speed; if (d.state() < Altos.ao_flight_drogue && d.has_accel()) { speed = d.accel_speed(); } else { speed = d.baro_speed(); } if (speed != AltosRecord.MISSING) series.add(time, AltosConvert.speed.value(speed)); } }; AltosGraphTime.Element acceleration = new AltosGraphTime.TimeSeries(String.format("Acceleration (%s)", AltosConvert.accel.show_units()), "Axial Acceleration", blue) { public void gotTimeData(double time, AltosDataPoint d) { double acceleration = d.acceleration(); if (acceleration != AltosRecord.MISSING) series.add(time, AltosConvert.accel.value(acceleration)); } }; AltosGraphTime.Element temperature = new AltosGraphTime.TimeSeries("Temperature (\u00B0C)", "Board temperature", red) { public void gotTimeData(double time, AltosDataPoint d) { double temp = d.temperature(); if (temp != AltosRecord.MISSING) series.add(time, d.temperature()); } }; AltosGraphTime.Element drogue_voltage = new AltosGraphTime.TimeSeries("Voltage (V)", "Drogue Continuity", yellow) { public void gotTimeData(double time, AltosDataPoint d) { double v = d.drogue_voltage(); if (v != AltosRecord.MISSING) series.add(time, v); } }; AltosGraphTime.Element main_voltage = new AltosGraphTime.TimeSeries("Voltage (V)", "Main Continuity", magenta) { public void gotTimeData(double time, AltosDataPoint d) { double v = d.main_voltage(); if (v != AltosRecord.MISSING) series.add(time, v); } }; AltosGraphTime.Element e_pad = new AltosGraphTime.StateMarker(Altos.ao_flight_pad, "Pad"); AltosGraphTime.Element e_boost = new AltosGraphTime.StateMarker(Altos.ao_flight_boost, "Boost"); AltosGraphTime.Element e_fast = new AltosGraphTime.StateMarker(Altos.ao_flight_fast, "Fast"); AltosGraphTime.Element e_coast = new AltosGraphTime.StateMarker(Altos.ao_flight_coast, "Coast"); AltosGraphTime.Element e_drogue = new AltosGraphTime.StateMarker(Altos.ao_flight_drogue, "Drogue"); AltosGraphTime.Element e_main = new AltosGraphTime.StateMarker(Altos.ao_flight_main, "Main"); AltosGraphTime.Element e_landed = new AltosGraphTime.StateMarker(Altos.ao_flight_landed, "Landed"); protected AltosGraphTime myAltosGraphTime(String suffix) { return (new AltosGraphTime("Overall " + suffix)) .addElement(e_boost) .addElement(e_fast) .addElement(e_coast) .addElement(e_drogue) .addElement(e_main) .addElement(e_landed); } public ArrayList graphs() { ArrayList graphs = new ArrayList(); graphs.add( myAltosGraphTime("Summary") .addElement(height) .addElement(speed) .addElement(acceleration) ); graphs.add( myAltosGraphTime("Summary") .addElement(height) .addElement(speed)); 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 (Altos.ao_flight_boost <= state && state <= Altos.ao_flight_coast) { 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 (Altos.ao_flight_drogue <= state && state <= Altos.ao_flight_main) { super.addData(d); } } }).addElement(e_drogue) .addElement(e_main); // ((XYGraph)graph[8]).ymin = new Double(-50); } } public AltosGraphUI(AltosRecordIterable records, String name) throws InterruptedException, IOException { super(String.format("Altos Graph %s", name)); AltosDataPointReader reader = new AltosDataPointReader (records); if (reader == null) return; if (reader.has_accel) init(reader, records, 0); else init(reader, records, 1); } // public AltosGraphUI(AltosDataPointReader data, int which) // { // super("Altos Graph"); // init(data, which); // } private void init(AltosDataPointReader data, AltosRecordIterable records, int which) throws InterruptedException, IOException { pane = new JTabbedPane(); 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)); pane.add(graph.title, chartPanel); AltosFlightStatsTable stats = new AltosFlightStatsTable(new AltosFlightStats(records)); pane.add("Flight Statistics", stats); setContentPane (pane); pack(); RefineryUtilities.centerFrameOnScreen(this); setDefaultCloseOperation(DISPOSE_ON_CLOSE); setVisible(true); } private static AltosGraph createGraph(Iterable data, int which) { return createGraphsWhich(data, which).get(0); } private static ArrayList createGraphs( Iterable data) { return createGraphsWhich(data, -1); } private static ArrayList createGraphsWhich( Iterable data, int which) { ArrayList graph = new ArrayList(); 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(); graph.add(g); } for (AltosDataPoint dp : data) { for (AltosGraph g : graph) { g.addData(dp); } } return graph; } } /* 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" */