Altosui: Add flight statistics tab to graph window
authorKeith Packard <keithp@keithp.com>
Wed, 10 Aug 2011 20:35:26 +0000 (13:35 -0700)
committerKeith Packard <keithp@keithp.com>
Wed, 10 Aug 2011 20:35:26 +0000 (13:35 -0700)
Provide basic flight stats alongside the flight graph.

Signed-off-by: Keith Packard <keithp@keithp.com>
altosui/Altos.java
altosui/AltosFlightStats.java [new file with mode: 0644]
altosui/AltosFlightStatsTable.java [new file with mode: 0644]
altosui/AltosGraph.java
altosui/AltosGraphTime.java
altosui/AltosGraphUI.java
altosui/AltosState.java
altosui/Makefile.am

index b257ad7..d90c418 100644 (file)
@@ -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 (file)
index 0000000..e38142f
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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 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 (file)
index 0000000..8676d30
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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 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
index 58c2797..fbcefd6 100644 (file)
@@ -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
index a545128..ada6ef1 100644 (file)
@@ -132,7 +132,6 @@ class AltosGraphTime extends AltosGraph {
     private Integer serial = null;
     private Integer flight = null; 
 
-    private String title;
     private ArrayList<Element> elements;
     private HashMap<String,Integer> axes;
     private HashMap<Element,Integer> datasets;
index a27aa37..16b0fd4 100644 (file)
@@ -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();
 
index d374aed..1ac816d 100644 (file)
@@ -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 */
index a11e2c7..3bc68cd 100644 (file)
@@ -51,6 +51,8 @@ altosui_JAVA = \
        AltosFlightDisplay.java \
        AltosFlightInfoTableModel.java \
        AltosFlightReader.java \
+       AltosFlightStats.java \
+       AltosFlightStatsTable.java \
        AltosFlightStatus.java \
        AltosFlightUI.java \
        AltosFrequency.java \