From 2830a47f142e79b8e4435f3957f19c7858606cd9 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 11 May 2024 19:03:51 -0700 Subject: [PATCH] altosui: Add config and pyro tabs to graph widget Show the flight computer configuration in the graph when available. Signed-off-by: Keith Packard --- altoslib/AltosConfigData.java | 4 + altoslib/AltosDataListener.java | 4 + altoslib/AltosLib.java | 24 ++++ altoslib/AltosRecordSet.java | 1 + altoslib/AltosTelemetryFile.java | 4 + altosui/AltosConfigFCUI.java | 29 +--- altosui/AltosGraphUI.java | 20 ++- altosuilib/AltosFlightConfigTable.java | 189 +++++++++++++++++++++++++ altosuilib/AltosFlightPyroTable.java | 184 ++++++++++++++++++++++++ altosuilib/Makefile.am | 2 + 10 files changed, 435 insertions(+), 26 deletions(-) create mode 100644 altosuilib/AltosFlightConfigTable.java create mode 100644 altosuilib/AltosFlightPyroTable.java diff --git a/altoslib/AltosConfigData.java b/altoslib/AltosConfigData.java index 6b980be6..b076357d 100644 --- a/altoslib/AltosConfigData.java +++ b/altoslib/AltosConfigData.java @@ -245,6 +245,10 @@ public class AltosConfigData { return false; } + public boolean has_radio() { + return product.startsWith("Tele"); + } + int[] parse_version(String v) { String[] parts = v.split("\\."); int r[] = new int[parts.length]; diff --git a/altoslib/AltosDataListener.java b/altoslib/AltosDataListener.java index b19cbedb..18ffd988 100644 --- a/altoslib/AltosDataListener.java +++ b/altoslib/AltosDataListener.java @@ -39,6 +39,10 @@ public abstract class AltosDataListener { return cal_data; } + public AltosConfigData config_data() { + return null; + } + public void set_time(double time) { if (time != AltosLib.MISSING) this.time = time; diff --git a/altoslib/AltosLib.java b/altoslib/AltosLib.java index fbbfd63c..dd2a4bae 100644 --- a/altoslib/AltosLib.java +++ b/altoslib/AltosLib.java @@ -235,6 +235,30 @@ public class AltosLib { "Compressed", "Uncompressed" }; + public static final String[] ignite_mode_values = { + "Dual Deploy", + "Redundant Apogee", + "Redundant Main", + "Separation & Apogee", + }; + + public static final String[] pad_orientation_values_radio = { + "Antenna Up", + "Antenna Down", + }; + + public static final String[] pad_orientation_values_no_radio = { + "Beeper Up", + "Beeper Down", + }; + + public static String[] pad_orientation_values(boolean radio) { + if (radio) + return pad_orientation_values_radio; + else + return pad_orientation_values_no_radio; + } + public static final String launch_sites_url = "https://maps.altusmetrum.org/launch-sites.txt"; public static final String launch_sites_env = "LAUNCH_SITES"; // public static final String launch_sites_url = "file:///home/keithp/misc/text/altusmetrum/AltOS/launch-sites.txt"; diff --git a/altoslib/AltosRecordSet.java b/altoslib/AltosRecordSet.java index c889cdab..b6be02a5 100644 --- a/altoslib/AltosRecordSet.java +++ b/altoslib/AltosRecordSet.java @@ -18,6 +18,7 @@ import java.util.*; public interface AltosRecordSet { public AltosCalData cal_data(); + public AltosConfigData config_data(); public void capture_series(AltosDataListener listener); public boolean valid(); } diff --git a/altoslib/AltosTelemetryFile.java b/altoslib/AltosTelemetryFile.java index 6f01f51c..5ba32518 100644 --- a/altoslib/AltosTelemetryFile.java +++ b/altoslib/AltosTelemetryFile.java @@ -138,6 +138,10 @@ public class AltosTelemetryFile implements AltosRecordSet { listener.finish(); } + public AltosConfigData config_data() { + return null; + } + public AltosTelemetryFile(FileInputStream input) throws IOException { telems = new AltosTelemetryIterable(input); } diff --git a/altosui/AltosConfigFCUI.java b/altosui/AltosConfigFCUI.java index 3e5bede5..9d39cda1 100644 --- a/altosui/AltosConfigFCUI.java +++ b/altosui/AltosConfigFCUI.java @@ -123,13 +123,6 @@ public class AltosConfigFCUI "0", "5", "10", "15", "20" }; - static String[] ignite_mode_values = { - "Dual Deploy", - "Redundant Apogee", - "Redundant Main", - "Separation & Apogee", - }; - static String[] aprs_interval_values = { "Disabled", "2", @@ -154,16 +147,6 @@ public class AltosConfigFCUI "4250", }; - static String[] pad_orientation_values_radio = { - "Antenna Up", - "Antenna Down", - }; - - static String[] pad_orientation_values_no_radio = { - "Beeper Up", - "Beeper Down", - }; - String[] pad_orientation_values; static String[] tracker_motion_values_m = { @@ -328,11 +311,7 @@ public class AltosConfigFCUI } void set_pad_orientation_values() { - String [] new_values; - if (has_radio()) - new_values = pad_orientation_values_radio; - else - new_values = pad_orientation_values_no_radio; + String [] new_values = AltosLib.pad_orientation_values(has_radio()); if (new_values != pad_orientation_values) { int id = pad_orientation_value.getSelectedIndex(); pad_orientation_value.removeAllItems(); @@ -893,7 +872,7 @@ public class AltosConfigFCUI c.anchor = GridBagConstraints.LINE_START; c.insets = ir; c.ipady = 5; - ignite_mode_value = new JComboBox(ignite_mode_values); + ignite_mode_value = new JComboBox(AltosLib.ignite_mode_values); ignite_mode_value.setEditable(false); ignite_mode_value.addItemListener(this); pane.add(ignite_mode_value, c); @@ -919,7 +898,7 @@ public class AltosConfigFCUI c.anchor = GridBagConstraints.LINE_START; c.insets = ir; c.ipady = 5; - pad_orientation_values = pad_orientation_values_no_radio; + pad_orientation_values = AltosLib.pad_orientation_values(false); pad_orientation_value = new JComboBox(pad_orientation_values); pad_orientation_value.setEditable(false); @@ -1453,7 +1432,7 @@ public class AltosConfigFCUI public void set_ignite_mode(int new_ignite_mode) { if (new_ignite_mode != AltosLib.MISSING) { - if (new_ignite_mode >= ignite_mode_values.length) + if (new_ignite_mode >= AltosLib.ignite_mode_values.length) new_ignite_mode = 0; if (new_ignite_mode < 0) { ignite_mode_value.setEnabled(false); diff --git a/altosui/AltosGraphUI.java b/altosui/AltosGraphUI.java index 127a5beb..8f52728b 100644 --- a/altosui/AltosGraphUI.java +++ b/altosui/AltosGraphUI.java @@ -39,6 +39,8 @@ public class AltosGraphUI extends AltosUIFrame implements AltosFontListener, Alt AltosUIMap map; AltosFlightStats stats; AltosFlightStatsTable statsTable; + AltosFlightConfigTable configTable; + AltosFlightPyroTable pyroTable; AltosGPS gps; boolean has_gps; @@ -77,6 +79,10 @@ public class AltosGraphUI extends AltosUIFrame implements AltosFontListener, Alt map.font_size_changed(font_size); if (statsTable != null) statsTable.font_size_changed(font_size); + if (configTable != null) + configTable.font_size_changed(font_size); + if (pyroTable != null) + pyroTable.font_size_changed(font_size); } public void units_changed(boolean imperial_units) { @@ -84,6 +90,10 @@ public class AltosGraphUI extends AltosUIFrame implements AltosFontListener, Alt map.units_changed(imperial_units); if (enable != null) enable.units_changed(imperial_units); + if (configTable != null) + configTable.units_changed(imperial_units); + if (pyroTable != null) + pyroTable.units_changed(imperial_units); } AltosUIFlightSeries flight_series; @@ -106,7 +116,7 @@ public class AltosGraphUI extends AltosUIFrame implements AltosFontListener, Alt AltosGraphUI(AltosRecordSet set, File file) throws InterruptedException, IOException { super(file.getName()); AltosCalData cal_data = set.cal_data(); - + AltosConfigData config_data = set.config_data(); pane = new JTabbedPane(); @@ -127,6 +137,14 @@ public class AltosGraphUI extends AltosUIFrame implements AltosFontListener, Alt pane.add("Flight Graph", graph.panel); pane.add("Configure Graph", enable); pane.add("Flight Statistics", statsTable); + if (config_data != null) { + configTable = new AltosFlightConfigTable(config_data); + pane.add("Configuration", configTable); + if (config_data.npyro > 0) { + pyroTable = new AltosFlightPyroTable(config_data.pyros, config_data.npyro); + pane.add("Pyros", pyroTable); + } + } has_gps = false; fill_map(flight_series); diff --git a/altosuilib/AltosFlightConfigTable.java b/altosuilib/AltosFlightConfigTable.java new file mode 100644 index 00000000..4f9714f6 --- /dev/null +++ b/altosuilib/AltosFlightConfigTable.java @@ -0,0 +1,189 @@ +/* + * Copyright © 2024 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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.altosuilib_14; + +import java.awt.*; +import javax.swing.*; +import java.util.*; +import org.altusmetrum.altoslib_14.*; + +public class AltosFlightConfigTable extends JComponent + implements AltosFontListener, AltosUnitsListener +{ + GridBagLayout layout; + + FlightConfig[] flight_configs; + AltosConfigData config_data; + + class FlightConfig implements AltosFontListener { + JLabel label; + JLabel[] value; + + public void font_size_changed(int font_size) { + label.setFont(AltosUILib.label_font); + for (int i = 0; i < value.length; i++) + value[i].setFont(AltosUILib.value_font); + } + + public void set(String l, String[] values) { + label.setText(l); + for (int j = 0; j < values.length; j++) + value[j].setText(values[j]); + } + + public FlightConfig(GridBagLayout layout, int y, String label_text, String ... values) { + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad); + c.weighty = 1; + + label = new JLabel(label_text); + label.setFont(AltosUILib.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); + + value = new JLabel[values.length]; + for (int j = 0; j < values.length; j++) { + value[j] = new JLabel(values[j]); + value[j].setFont(AltosUILib.value_font); + c.gridx = j+1; c.gridy = y; + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value[j], c); + add(value[j]); + } + flight_configs[y] = this; + } + + } + + private FlightConfig set_config(int y, String label, String ... values) { + if (flight_configs[y] == null) + flight_configs[y] = new FlightConfig(layout, y, label, values); + else + flight_configs[y].set(label, values); + return flight_configs[y]; + } + + public void font_size_changed(int font_size) { + for (int y = 0; flight_configs[y] != null; y++) + flight_configs[y].font_size_changed(font_size); + } + + private String main_deploy_label() { + return String.format("Main Deploy Altitude(%s)", AltosConvert.height.parse_units()); + } + + private String main_deploy_value() { + return String.format("%-6.1f", AltosConvert.height.value(config_data.main_deploy, AltosConvert.imperial_units)); + } + + public void set_config() { + int y = 0; + if (config_data.serial != AltosLib.MISSING) { + if (config_data.product != null && config_data.version != null) + set_config(y++, "Device", + config_data.product, + String.format("version %s", config_data.version), + String.format("serial %d", config_data.serial)); + else + set_config(y++, "Serial", String.format("%d", config_data.serial)); + } + if (config_data.flight != AltosLib.MISSING) + set_config(y++, "Flight", String.format("%d", config_data.flight)); + if (config_data.main_deploy != AltosLib.MISSING) + set_config(y++, main_deploy_label(), main_deploy_value()); + if (config_data.apogee_delay != AltosLib.MISSING) + set_config(y++, "Apogee Delay(s)", String.format("%d", config_data.apogee_delay)); + if (config_data.apogee_lockout != AltosLib.MISSING) + set_config(y++, "Apogee Lockout(s)", String.format("%d", config_data.apogee_lockout)); + if (config_data.radio_frequency != AltosLib.MISSING) + set_config(y++, "Radio Frequency (MHz)", String.format("%-7.3f", config_data.radio_frequency / 1000.0)); + if (config_data.radio_calibration != AltosLib.MISSING) + set_config(y++, "Radio Calibration", String.format("%d", config_data.radio_calibration)); + if (config_data.radio_enable != AltosLib.MISSING) + set_config(y++, "Radio Enable", String.format("%b", config_data.radio_enable != 0)); + if (config_data.radio_10mw != AltosLib.MISSING) + set_config(y++, "Limit transmit to 10mW", String.format("%b", config_data.radio_10mw != 0)); + if (config_data.report_feet != AltosLib.MISSING) + set_config(y++, "Beep max height in", config_data.report_feet == 0 ? "Meters" : "Feet"); + if (config_data.gps_receiver != AltosLib.MISSING) + set_config(y++, "GPS Receiver", AltosLib.gps_receiver_names[config_data.gps_receiver]); + if (config_data.telemetry_rate != AltosLib.MISSING) + set_config(y++, "Telemetry baud rate", String.format("%d", AltosLib.ao_telemetry_rate_values[config_data.telemetry_rate])); + if (config_data.aprs_interval != AltosLib.MISSING) + set_config(y++, "APRS Interval(s)", String.format("%d", config_data.aprs_interval)); + if (config_data.aprs_ssid != AltosLib.MISSING) + set_config(y++, "APRS SSID", String.format("%d", config_data.aprs_ssid)); + if (config_data.aprs_format != AltosLib.MISSING) + set_config(y++, "APRS Format", AltosLib.ao_aprs_format_name[config_data.aprs_format]); + if (config_data.callsign != null) + set_config(y++, "Callsign", config_data.callsign); + if (config_data.flight_log_max != AltosLib.MISSING) + set_config(y++, "Maximum Flight Log Size(kB)", String.format("%d", config_data.flight_log_max)); + if (config_data.ignite_mode != AltosLib.MISSING) + set_config(y++, "Igniter Firing Mode", AltosLib.ignite_mode_values[config_data.ignite_mode]); + if (config_data.pad_orientation != AltosLib.MISSING) + set_config(y++, "Pad Orientation", AltosLib.pad_orientation_values(config_data.has_radio())[config_data.pad_orientation]); + if (config_data.accel_cal_plus != AltosLib.MISSING) + set_config(y++, "Accel Calibration", + String.format("Plus %d", config_data.accel_cal_plus), + String.format("Minus %d", config_data.accel_cal_minus)); + if (config_data.beep != AltosLib.MISSING) + set_config(y++, "Beeper(Hz)", + config_data.beep == 0 ? "Disabled" : + String.format("%-7.1f", AltosConvert.beep_value_to_freq(config_data.beep))); + if (config_data.tracker_motion != AltosLib.MISSING) + set_config(y++, + String.format("Logging Trigger Motion (%s):", AltosConvert.height.parse_units()), + String.format("%-6.1f", + AltosConvert.height.value(config_data.tracker_motion, AltosConvert.imperial_units))); + if (config_data.tracker_interval != AltosLib.MISSING) + set_config(y++, "Position Reporting Interval(s)", + String.format("%d", config_data.tracker_interval)); + } + + public void units_changed(boolean imperial_units) { + set_config(); + } + + public void tell_closing() { + AltosUIPreferences.unregister_font_listener(this); + } + + public AltosFlightConfigTable() { + layout = new GridBagLayout(); + + setLayout(layout); + + AltosUIPreferences.register_font_listener(this); + } + + public AltosFlightConfigTable(AltosConfigData config_data) { + this(); + this.config_data = config_data; + flight_configs = new FlightConfig[30]; + set_config(); + } +} diff --git a/altosuilib/AltosFlightPyroTable.java b/altosuilib/AltosFlightPyroTable.java new file mode 100644 index 00000000..5e27a2f8 --- /dev/null +++ b/altosuilib/AltosFlightPyroTable.java @@ -0,0 +1,184 @@ +/* + * Copyright © 2024 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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.altosuilib_14; + +import java.awt.*; +import javax.swing.*; +import java.util.*; +import org.altusmetrum.altoslib_14.*; + +public class AltosFlightPyroTable extends JComponent + implements AltosFontListener, AltosUnitsListener +{ + GridBagLayout layout; + AltosPyro[] pyros; + int npyro; + FlightPyro[] flight_pyros; + + class FlightPyro implements AltosFontListener { + JLabel label; + JTextField[] text_fields; + + public void font_size_changed(int font_size) { + label.setFont(AltosUILib.label_font); + for (int i = 0; i < text_fields.length; i++) + text_fields[i].setFont(AltosUILib.value_font); + } + + public void set_value(int y, int p, String value) { + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad); + c.weighty = 1; + JTextField text_field; + + if (text_fields[p] == null) { + text_field = new JTextField(value); + text_field.setEditable(false); + text_field.setFont(AltosUILib.value_font); + text_field.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = p+1; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(text_field, c); + add(text_field); + text_fields[p] = text_field; + } else { + text_fields[p].setText(value); + } + } + + public void set_label(String text) { + label.setText(text); + } + + public FlightPyro(GridBagLayout layout, int y, String label_text, int npyro) { + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad); + c.weighty = 1; + + if (label_text != null) { + label = new JLabel(label_text); + label.setFont(AltosUILib.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); + } + + text_fields = new JTextField[npyro]; + } + } + + public void font_size_changed(int font_size) { + for (int i = 0; i < flight_pyros.length; i++) + flight_pyros[i].font_size_changed(font_size); + } + + public void set_pyros() { + int nrow = 1; + for (int flag = 1; flag < AltosPyro.pyro_all; flag <<= 1) { + if ((AltosPyro.pyro_all_useful & flag) != 0) { + for (int p = 0; p < npyro; p++) { + if ((pyros[p].flags & flag) != 0) { + String text; + double value = pyros[p].get_value(flag); + if ((flag & AltosPyro.pyro_state_value) != 0) { + text = AltosLib.state_name_capital((int) value); + } else { + double scale = AltosPyro.pyro_to_scale(flag); + double unit_value = value; + AltosUnits units = AltosPyro.pyro_to_units(flag); + if (units != null) + unit_value = units.parse_value(value); + String format; + if (scale >= 100) + format = "%6.2f"; + else if (scale >= 10) + format = "%6.1f"; + else + format = "%6.0f"; + text = String.format(format, unit_value); + } + flight_pyros[nrow].set_value(nrow, p, text); + } + } + nrow++; + } + } + } + + public void units_changed(boolean imperial_units) { + System.out.printf("units changed\n"); + int nrow = 1; + for (int flag = 1; flag < AltosPyro.pyro_all; flag <<= 1) { + if ((AltosPyro.pyro_all_useful & flag) != 0) { + String name = AltosPyro.pyro_to_name(flag); + flight_pyros[nrow].set_label(name); + } + } + set_pyros(); + } + + public void tell_closing() { + AltosUIPreferences.unregister_font_listener(this); + } + + public AltosFlightPyroTable(AltosPyro[] pyros, int npyro) { + layout = new GridBagLayout(); + + int nrow = 0; + + for (int flag = 1; flag < AltosPyro.pyro_all; flag <<= 1) { + if ((AltosPyro.pyro_all_useful & flag) != 0) { + nrow++; + } + } + + flight_pyros = new FlightPyro[nrow + 1]; + + nrow = 0; + + flight_pyros[0] = new FlightPyro(layout, 0, null, npyro); + + for (int p = 0; p < npyro; p++) { + flight_pyros[0].set_value(0, p, String.format("Channel %c", 'A' + p)); + } + nrow++; + for (int flag = 1; flag < AltosPyro.pyro_all; flag <<= 1) { + if ((AltosPyro.pyro_all_useful & flag) != 0) { + String name = AltosPyro.pyro_to_name(flag); + flight_pyros[nrow] = new FlightPyro(layout, nrow, name, npyro); + nrow++; + } + } + + + this.pyros = pyros; + this.npyro = npyro; + + set_pyros(); + + setLayout(layout); + AltosUIPreferences.register_font_listener(this); + } +} diff --git a/altosuilib/Makefile.am b/altosuilib/Makefile.am index 10a5b494..12bdaaa7 100644 --- a/altosuilib/Makefile.am +++ b/altosuilib/Makefile.am @@ -51,7 +51,9 @@ altosuilib_JAVA = \ AltosFlashUI.java \ AltosRomconfigUI.java \ AltosInfoTable.java \ + AltosFlightConfigTable.java \ AltosFlightInfoTableModel.java \ + AltosFlightPyroTable.java \ AltosFlightStatsTable.java \ AltosBTDevice.java \ AltosBTDeviceIterator.java \ -- 2.39.5