From 6ac604d11de44cd824f09e4b467264a2b74be7bd Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 10 Aug 2011 13:35:26 -0700 Subject: [PATCH] Altosui: Add flight statistics tab to graph window Provide basic flight stats alongside the flight graph. Signed-off-by: Keith Packard --- altosui/Altos.java | 13 ++++ altosui/AltosFlightStats.java | 95 ++++++++++++++++++++++++++ altosui/AltosFlightStatsTable.java | 104 +++++++++++++++++++++++++++++ altosui/AltosGraph.java | 1 + altosui/AltosGraphTime.java | 1 - altosui/AltosGraphUI.java | 36 ++++++---- altosui/AltosState.java | 3 + altosui/Makefile.am | 2 + 8 files changed, 242 insertions(+), 13 deletions(-) create mode 100644 altosui/AltosFlightStats.java create mode 100644 altosui/AltosFlightStatsTable.java diff --git a/altosui/Altos.java b/altosui/Altos.java index b257ad7b..d90c4183 100644 --- a/altosui/Altos.java +++ b/altosui/Altos.java @@ -142,6 +142,19 @@ public class Altos { "invalid", }; + static String[] state_to_string_capital = { + "Startup", + "Idle", + "Pad", + "Boost", + "Fast", + "Coast", + "Drogue", + "Main", + "Landed", + "Invalid", + }; + static public int state(String state) { if (!map_initialized) initialize_map(); diff --git a/altosui/AltosFlightStats.java b/altosui/AltosFlightStats.java new file mode 100644 index 00000000..e38142f0 --- /dev/null +++ b/altosui/AltosFlightStats.java @@ -0,0 +1,95 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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 java.util.concurrent.*; + +public class AltosFlightStats { + double max_height; + double max_speed; + double max_acceleration; + double[] state_speed = new double[Altos.ao_flight_invalid + 1]; + double[] state_baro_speed = new double[Altos.ao_flight_invalid + 1]; + double[] state_accel = new double[Altos.ao_flight_invalid + 1]; + int[] state_count = new int[Altos.ao_flight_invalid + 1]; + double[] state_start = new double[Altos.ao_flight_invalid + 1]; + double[] state_end = new double[Altos.ao_flight_invalid + 1]; + + public AltosFlightStats(AltosFlightReader reader) throws InterruptedException, IOException { + AltosState state = null; + AltosState new_state = null; + double boost_time = -1; + double start_time; + + for (;;) { + try { + AltosRecord record = reader.read(); + if (record == null) + break; + new_state = new AltosState(record, state); + if (state == null) { + start_time = new_state.time; + } + state = new_state; + if (0 <= state.state && state.state < Altos.ao_flight_invalid) { + if (boost_time == -1 && state.state >= Altos.ao_flight_boost) + boost_time = state.time; + state_accel[state.state] += state.acceleration; + state_speed[state.state] += state.speed; + state_baro_speed[state.state] += state.baro_speed; + state_count[state.state]++; + if (state_start[state.state] == 0.0) + state_start[state.state] = state.time; + if (state_end[state.state] < state.time) + state_end[state.state] = state.time; + max_height = state.max_height; + max_speed = state.max_speed; + max_acceleration = state.max_acceleration; + } + } catch (ParseException pp) { + System.out.printf("Parse error: %d \"%s\"\n", pp.getErrorOffset(), pp.getMessage()); + } catch (AltosCRCException ce) { + + } + } + for (int s = Altos.ao_flight_startup; s <= Altos.ao_flight_landed; s++) { + if (state_count[s] > 0) { + state_speed[s] /= state_count[s]; + state_baro_speed[s] /= state_count[s]; + state_accel[s] /= state_count[s]; + } + } + } + + public AltosFlightStats(AltosRecordIterable iterable, String filename) throws InterruptedException, IOException { + this(new AltosReplayReader(iterable.iterator(), filename)); + } + + public AltosFlightStats(AltosRecordIterable iterable) throws InterruptedException, IOException { + this(iterable, ""); + } +} diff --git a/altosui/AltosFlightStatsTable.java b/altosui/AltosFlightStatsTable.java new file mode 100644 index 00000000..8676d306 --- /dev/null +++ b/altosui/AltosFlightStatsTable.java @@ -0,0 +1,104 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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 java.util.concurrent.*; + +public class AltosFlightStatsTable extends JComponent { + GridBagLayout layout; + + class FlightStat { + JLabel label; + JTextField value; + + public FlightStat(GridBagLayout layout, int y, String label_text, String ... values) { + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad, Altos.tab_elt_pad); + c.weighty = 1; + + label = new JLabel(label_text); + label.setFont(Altos.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); + + for (int j = 0; j < values.length; j++) { + value = new JTextField(values[j]); + value.setFont(Altos.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = j+1; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + } + } + + } + + public AltosFlightStatsTable(AltosFlightStats stats) { + layout = new GridBagLayout(); + + setLayout(layout); + int y = 0; + new FlightStat(layout, y++, "Maximum height", + String.format("%5.0f m", stats.max_height), + String.format("%5.0f ft", stats.max_height * 100 / 2.54 / 12)); + new FlightStat(layout, y++, "Maximum speed", + String.format("%5.0f m/s", stats.max_speed), + String.format("%5.0f ft/s", stats.max_speed * 100 / 2.54 / 12), + String.format("Mach %5.3f", stats.max_speed / 343.0)); + new FlightStat(layout, y++, "Maximum acceleration", + String.format("%5.0f m/s²", stats.max_acceleration), + String.format("%5.0f ft/s²", stats.max_acceleration * 100 / 2.54 /12), + String.format("%5.2f G", stats.max_acceleration / 9.80665)); + new FlightStat(layout, y++, "Average boost acceleration", + String.format("%5.0f m/s²", stats.state_accel[Altos.ao_flight_boost]), + String.format("%5.0f ft/s²", stats.state_accel[Altos.ao_flight_boost] * 100 / 2.54 /12), + String.format("%5.2f G", stats.state_accel[Altos.ao_flight_boost] / 9.80665)); + new FlightStat(layout, y++, "Drogue descent rate", + String.format("%5.0f m/s", stats.state_baro_speed[Altos.ao_flight_drogue]), + String.format("%5.0f ft/s", stats.state_baro_speed[Altos.ao_flight_drogue] * 100 / 2.54 / 12)); + new FlightStat(layout, y++, "Main descent rate", + String.format("%5.0f m/s", stats.state_baro_speed[Altos.ao_flight_main]), + String.format("%5.0f ft/s", stats.state_baro_speed[Altos.ao_flight_main] * 100 / 2.54 / 12)); + for (int s = Altos.ao_flight_boost; s <= Altos.ao_flight_main; s++) { + new FlightStat(layout, y++, String.format("%s time", Altos.state_to_string_capital[s]), + String.format("%6.2f s", stats.state_end[s] - stats.state_start[s])); + } + new FlightStat(layout, y++, "Flight Time", + String.format("%6.2f s", stats.state_end[Altos.ao_flight_main] - + stats.state_start[Altos.ao_flight_boost])); + + } + +} \ No newline at end of file diff --git a/altosui/AltosGraph.java b/altosui/AltosGraph.java index 58c27979..fbcefd61 100644 --- a/altosui/AltosGraph.java +++ b/altosui/AltosGraph.java @@ -13,6 +13,7 @@ abstract class AltosGraph { public String filename; public abstract void addData(AltosDataPoint d); public abstract JFreeChart createChart(); + public String title; public void toPNG() throws java.io.IOException { toPNG(300, 500); } public void toPNG(int width, int height) throws java.io.IOException diff --git a/altosui/AltosGraphTime.java b/altosui/AltosGraphTime.java index a5451280..ada6ef13 100644 --- a/altosui/AltosGraphTime.java +++ b/altosui/AltosGraphTime.java @@ -132,7 +132,6 @@ class AltosGraphTime extends AltosGraph { private Integer serial = null; private Integer flight = null; - private String title; private ArrayList elements; private HashMap axes; private HashMap datasets; diff --git a/altosui/AltosGraphUI.java b/altosui/AltosGraphUI.java index a27aa37f..16b0fd48 100644 --- a/altosui/AltosGraphUI.java +++ b/altosui/AltosGraphUI.java @@ -7,8 +7,11 @@ package altosui; import java.io.*; import java.util.ArrayList; -import javax.swing.JFrame; -import java.awt.Color; +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; import org.jfree.chart.ChartPanel; import org.jfree.chart.ChartUtilities; @@ -19,6 +22,8 @@ import org.jfree.ui.RefineryUtilities; public class AltosGraphUI extends JFrame { + 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); @@ -173,7 +178,7 @@ public class AltosGraphUI extends JFrame } } - public AltosGraphUI(AltosRecordIterable records) { + public AltosGraphUI(AltosRecordIterable records) throws InterruptedException, IOException { super("Altos Graph"); AltosDataPointReader reader = new AltosDataPointReader (records); @@ -181,25 +186,32 @@ public class AltosGraphUI extends JFrame return; if (reader.has_accel) - init(reader, 0); + init(reader, records, 0); else - init(reader, 1); + init(reader, records, 1); } - public AltosGraphUI(AltosDataPointReader data, int which) - { - super("Altos Graph"); - init(data, which); - } +// 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(); - private void init(AltosDataPointReader 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); + pane.add(graph.title, chartPanel); + + AltosFlightStatsTable stats = new AltosFlightStatsTable(new AltosFlightStats(records)); + pane.add("Flight Statistics", stats); + + setContentPane (pane); pack(); diff --git a/altosui/AltosState.java b/altosui/AltosState.java index d374aed8..1ac816d5 100644 --- a/altosui/AltosState.java +++ b/altosui/AltosState.java @@ -28,6 +28,7 @@ public class AltosState { long report_time; + double time; double time_change; int tick; @@ -130,6 +131,8 @@ public class AltosState { time_change = 0; } + time = tick / 100.0; + if (state == Altos.ao_flight_pad || state == Altos.ao_flight_idle) { /* Track consecutive 'good' gps reports, waiting for 10 of them */ diff --git a/altosui/Makefile.am b/altosui/Makefile.am index a11e2c7f..3bc68cdb 100644 --- a/altosui/Makefile.am +++ b/altosui/Makefile.am @@ -51,6 +51,8 @@ altosui_JAVA = \ AltosFlightDisplay.java \ AltosFlightInfoTableModel.java \ AltosFlightReader.java \ + AltosFlightStats.java \ + AltosFlightStatsTable.java \ AltosFlightStatus.java \ AltosFlightUI.java \ AltosFrequency.java \ -- 2.30.2