From 24ffcf86c43290ce0f70fb4ee0984b3debdb8a5f Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 19 Nov 2010 15:41:30 +0800 Subject: altosui: Add igniter ground testing code Not yet hooked up, but the UI is finished. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosIgnite.java | 155 ++++++++++++++++++++++++++++++ ao-tools/altosui/AltosIgniteUI.java | 183 ++++++++++++++++++++++++++++++++++++ ao-tools/altosui/AltosUI.java | 23 +++-- ao-tools/altosui/Makefile.am | 2 + 4 files changed, 357 insertions(+), 6 deletions(-) create mode 100644 ao-tools/altosui/AltosIgnite.java create mode 100644 ao-tools/altosui/AltosIgniteUI.java diff --git a/ao-tools/altosui/AltosIgnite.java b/ao-tools/altosui/AltosIgnite.java new file mode 100644 index 00000000..5c27e8fa --- /dev/null +++ b/ao-tools/altosui/AltosIgnite.java @@ -0,0 +1,155 @@ +/* + * Copyright © 2010 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.io.*; + +public class AltosIgnite { + AltosDevice device; + AltosSerial serial; + boolean remote; + final static int None = 0; + final static int Apogee = 1; + final static int Main = 2; + + final static int Unknown = 0; + final static int Ready = 1; + final static int Active = 2; + final static int Open = 3; + + private void start_serial() throws InterruptedException { + if (remote) { + serial.set_channel(AltosPreferences.channel(device.getSerial())); + serial.set_callsign(AltosPreferences.callsign()); + serial.printf("~\np\n"); + serial.flush_input(); + } + } + + private void stop_serial() throws InterruptedException { + if (serial == null) + return; + if (remote) { + serial.printf("~"); + serial.flush_output(); + } + } + + class string_ref { + String value; + + public String get() { + return value; + } + public void set(String i) { + value = i; + } + public string_ref() { + value = null; + } + } + + private boolean get_string(String line, String label, string_ref s) { + if (line.startsWith(label)) { + String quoted = line.substring(label.length()).trim(); + + if (quoted.startsWith("\"")) + quoted = quoted.substring(1); + if (quoted.endsWith("\"")) + quoted = quoted.substring(0,quoted.length()-1); + s.set(quoted); + return true; + } else { + return false; + } + } + + private int status(String status_name) { + if (status_name.equals("unknown")) + return Unknown; + if (status_name.equals("ready")) + return Ready; + if (status_name.equals("active")) + return Active; + if (status_name.equals("open")) + return Open; + return Unknown; + } + + public int status(int igniter) { + int status = Unknown; + if (serial == null) + return status; + string_ref status_name = new string_ref(); + try { + start_serial(); + serial.printf("t\n"); + for (;;) { + String line = serial.get_reply(); + if (get_string(line, "Igniter: drogue Status: ", status_name)) + if (igniter == Apogee) + status = status(status_name.get()); + if (get_string(line, "Igniter: main Status: ", status_name)) { + if (igniter == Main) + status = status(status_name.get()); + break; + } + } + } catch (InterruptedException ie) { + } finally { + try { + stop_serial(); + } catch (InterruptedException ie) { + } + } + return status; + } + + public void fire(int igniter) { + if (serial == null) + return; + try { + start_serial(); + switch (igniter) { + case Main: + serial.printf("i DoIt main\n"); + break; + case Apogee: + serial.printf("i DoIt drogue\n"); + break; + } + } catch (InterruptedException ie) { + } finally { + try { + stop_serial(); + } catch (InterruptedException ie) { + } + } + } + + public AltosIgnite(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException { + + device = in_device; + serial = null; +// serial = new AltosSerial(device); + remote = false; + +// if (!device.matchProduct(AltosDevice.product_telemetrum)) +// remote = true; + } +} \ No newline at end of file diff --git a/ao-tools/altosui/AltosIgniteUI.java b/ao-tools/altosui/AltosIgniteUI.java new file mode 100644 index 00000000..62da413c --- /dev/null +++ b/ao-tools/altosui/AltosIgniteUI.java @@ -0,0 +1,183 @@ +/* + * Copyright © 2010 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 javax.swing.event.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; + +public class AltosIgniteUI + extends JDialog + implements ActionListener +{ + JFrame owner; + JLabel label; + JRadioButton apogee; + JRadioButton main; + JToggleButton arm; + JButton fire; + javax.swing.Timer timer; + + final static int timeout = 1 * 1000; + + int time_remaining; + + void set_arm_text() { + if (arm.isSelected()) + arm.setText(String.format("%d", time_remaining)); + else + arm.setText("Arm"); + } + + void start_timer() { + time_remaining = 10; + set_arm_text(); + timer.restart(); + } + + void stop_timer() { + time_remaining = 0; + arm.setSelected(false); + arm.setEnabled(false); + fire.setEnabled(false); + timer.stop(); + set_arm_text(); + } + + void cancel () { + apogee.setSelected(false); + main.setSelected(false); + fire.setEnabled(false); + stop_timer(); + } + + void tick_timer() { + --time_remaining; + if (time_remaining <= 0) + cancel(); + else + set_arm_text(); + } + + void fire() { + if (arm.isEnabled() && arm.isSelected() && time_remaining > 0) { + int igniter = AltosIgnite.None; + if (apogee.isSelected() && !main.isSelected()) + igniter = AltosIgnite.Apogee; + else if (main.isSelected() && !apogee.isSelected()) + igniter = AltosIgnite.Main; + System.out.printf ("fire %d\n", igniter); + cancel(); + } + } + + public void actionPerformed(ActionEvent e) { + String cmd = e.getActionCommand(); + if (cmd.equals("apogee") || cmd.equals("main")) { + stop_timer(); + arm.setEnabled(true); + } + + if (cmd.equals("apogee") && apogee.isSelected()) + main.setSelected(false); + if (cmd.equals("main") && main.isSelected()) + apogee.setSelected(false); + + if (cmd.equals("arm")) { + if (arm.isSelected()) { + fire.setEnabled(true); + start_timer(); + } else + cancel(); + } + if (cmd.equals("fire")) + fire(); + if (cmd.equals("tick")) + tick_timer(); + } + + public AltosIgniteUI(JFrame in_owner) { + Container pane = getContentPane(); + GridBagConstraints c = new GridBagConstraints(); + Insets i = new Insets(4,4,4,4); + + timer = new javax.swing.Timer(timeout, this); + timer.setActionCommand("tick"); + + owner = in_owner; + + pane.setLayout(new GridBagLayout()); + + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.insets = i; + c.weightx = 1; + c.weighty = 1; + + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 2; + label = new JLabel ("Fire Igniter"); + pane.add(label, c); + + c.gridx = 0; + c.gridy = 1; + c.gridwidth = 1; + apogee = new JRadioButton ("Apogee"); + pane.add(apogee, c); + apogee.addActionListener(this); + apogee.setActionCommand("apogee"); + + c.gridx = 1; + c.gridy = 1; + c.gridwidth = 1; + main = new JRadioButton ("Main"); + pane.add(main, c); + main.addActionListener(this); + main.setActionCommand("main"); + + c.gridx = 0; + c.gridy = 2; + c.gridwidth = 1; + arm = new JToggleButton ("Arm"); + pane.add(arm, c); + arm.addActionListener(this); + arm.setActionCommand("arm"); + arm.setEnabled(false); + + c.gridx = 1; + c.gridy = 2; + c.gridwidth = 1; + fire = new JButton ("Fire"); + fire.setEnabled(false); + pane.add(fire, c); + fire.addActionListener(this); + fire.setActionCommand("fire"); + + pack(); + setLocationRelativeTo(owner); + setVisible(true); + } +} \ No newline at end of file diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index bedf2459..5e9566f0 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -125,40 +125,47 @@ public class AltosUI extends JFrame { Replay(); } }); - b = addButton(0, 1, "Graph Data"); + b = addButton(3, 0, "Graph Data"); b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { GraphData(); } }); - b = addButton(1, 1, "Export Data"); + b = addButton(4, 0, "Export Data"); b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ExportData(); } }); - b = addButton(2, 1, "Configure TeleMetrum"); + b = addButton(0, 1, "Configure TeleMetrum"); b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ConfigureTeleMetrum(); } }); - b = addButton(0, 2, "Configure AltosUI"); + b = addButton(1, 1, "Configure AltosUI"); b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ConfigureAltosUI(); } }); - b = addButton(1, 2, "Flash Image"); + b = addButton(2, 1, "Flash Image"); b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { FlashImage(); } }); - b = addButton(2, 2, "Quit"); + b = addButton(3, 1, "Fire Igniter"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + FireIgniter(); + } + }); + + b = addButton(4, 1, "Quit"); b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); @@ -215,6 +222,10 @@ public class AltosUI extends JFrame { new AltosFlashUI(AltosUI.this); } + void FireIgniter() { + new AltosIgniteUI(AltosUI.this); + } + /* * Replay a flight from telemetry data */ diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index 8d0fe16e..25977b12 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -40,6 +40,8 @@ altosui_JAVA = \ AltosGreatCircle.java \ AltosHexfile.java \ Altos.java \ + AltosIgnite.java \ + AltosIgniteUI.java \ AltosInfoTable.java \ AltosKML.java \ AltosLanded.java \ -- cgit v1.2.3 From 8f72f08839346fb225238420324f0edcd070e531 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 19 Nov 2010 17:14:17 +0800 Subject: altosui: Unify datafile selection to AltosDataChooser Instead of having several separate intefaces, use a single dialog for selecting data files for graph/export/replay. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosCSVUI.java | 73 +++++++++++++------------- ao-tools/altosui/AltosDataChooser.java | 79 +++++++++++++++++++++++++++++ ao-tools/altosui/AltosGraphDataChooser.java | 79 ----------------------------- ao-tools/altosui/AltosGraphUI.java | 17 +++---- ao-tools/altosui/AltosLogfileChooser.java | 78 ---------------------------- ao-tools/altosui/AltosUI.java | 17 +++++-- ao-tools/altosui/Makefile.am | 3 +- 7 files changed, 140 insertions(+), 206 deletions(-) create mode 100644 ao-tools/altosui/AltosDataChooser.java delete mode 100644 ao-tools/altosui/AltosGraphDataChooser.java delete mode 100644 ao-tools/altosui/AltosLogfileChooser.java diff --git a/ao-tools/altosui/AltosCSVUI.java b/ao-tools/altosui/AltosCSVUI.java index 16f25338..e1b6002d 100644 --- a/ao-tools/altosui/AltosCSVUI.java +++ b/ao-tools/altosui/AltosCSVUI.java @@ -30,16 +30,15 @@ import java.util.concurrent.LinkedBlockingQueue; public class AltosCSVUI extends JDialog - implements Runnable, ActionListener + implements ActionListener { - JFrame frame; - Thread thread; - AltosRecordIterable iterable; - AltosWriter writer; JFileChooser csv_chooser; + JPanel accessory; JComboBox combo_box; + AltosRecordIterable iterable; + AltosWriter writer; - static String[] combo_box_items = { "CSV", "KML" }; + static String[] combo_box_items = { "Comma Separated Values (.CSV)", "Googleearth Data (.KML)" }; void set_default_file() { File current = csv_chooser.getSelectedFile(); @@ -47,57 +46,63 @@ public class AltosCSVUI String new_name = null; String selected = (String) combo_box.getSelectedItem(); - if (selected.equals("CSV")) + if (selected.contains("CSV")) new_name = Altos.replace_extension(current_name, ".csv"); - else if (selected.equals("KML")) + else if (selected.contains("KML")) new_name = Altos.replace_extension(current_name, ".kml"); if (new_name != null) csv_chooser.setSelectedFile(new File(new_name)); } - public void run() { - AltosLogfileChooser chooser; + public void actionPerformed(ActionEvent e) { + if (e.getActionCommand().equals("comboBoxChanged")) + set_default_file(); + } + + public AltosCSVUI(JFrame frame, AltosRecordIterable in_iterable, File source_file) { + iterable = in_iterable; + csv_chooser = new JFileChooser(source_file); + + accessory = new JPanel(); + accessory.setLayout(new GridBagLayout()); - chooser = new AltosLogfileChooser(frame); - iterable = chooser.runDialog(); - if (iterable == null) - return; + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.NONE; + c.weightx = 1; + c.weighty = 0; + c.insets = new Insets (4, 4, 4, 4); + + JLabel accessory_label = new JLabel("Export File Type"); + c.gridx = 0; + c.gridy = 0; + accessory.add(accessory_label, c); - csv_chooser = new JFileChooser(chooser.file()); combo_box = new JComboBox(combo_box_items); combo_box.addActionListener(this); - csv_chooser.setAccessory(combo_box); - csv_chooser.setSelectedFile(chooser.file()); + c.gridx = 0; + c.gridy = 1; + accessory.add(combo_box, c); + + csv_chooser.setAccessory(accessory); + csv_chooser.setSelectedFile(source_file); set_default_file(); int ret = csv_chooser.showSaveDialog(frame); if (ret == JFileChooser.APPROVE_OPTION) { - File file = csv_chooser.getSelectedFile(); - String type = (String) combo_box.getSelectedItem(); + File file = csv_chooser.getSelectedFile(); + String type = (String) combo_box.getSelectedItem(); try { - if (type.equals("CSV")) + if (type.contains("CSV")) writer = new AltosCSV(file); else writer = new AltosKML(file); + writer.write(iterable); + writer.close(); } catch (FileNotFoundException ee) { JOptionPane.showMessageDialog(frame, file.getName(), "Cannot open file", JOptionPane.ERROR_MESSAGE); } - writer.write(iterable); - writer.close(); } } - - public void actionPerformed(ActionEvent e) { - System.out.printf("command %s param %s\n", e.getActionCommand(), e.paramString()); - if (e.getActionCommand().equals("comboBoxChanged")) - set_default_file(); - } - - public AltosCSVUI(JFrame in_frame) { - frame = in_frame; - thread = new Thread(this); - thread.start(); - } } diff --git a/ao-tools/altosui/AltosDataChooser.java b/ao-tools/altosui/AltosDataChooser.java new file mode 100644 index 00000000..15de05c2 --- /dev/null +++ b/ao-tools/altosui/AltosDataChooser.java @@ -0,0 +1,79 @@ +/* + * Copyright © 2010 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.*; + +public class AltosDataChooser extends JFileChooser { + JFrame frame; + String filename; + File file; + + public String filename() { + return filename; + } + + public File file() { + return file; + } + + public AltosRecordIterable runDialog() { + int ret; + + ret = showOpenDialog(frame); + if (ret == APPROVE_OPTION) { + file = getSelectedFile(); + if (file == null) + return null; + filename = file.getName(); + try { + if (filename.endsWith("eeprom")) { + FileInputStream in = new FileInputStream(file); + return new AltosEepromIterable(in); + } else if (filename.endsWith("telem")) { + FileInputStream in = new FileInputStream(file); + return new AltosTelemetryIterable(in); + } else { + throw new FileNotFoundException(); + } + } catch (FileNotFoundException fe) { + JOptionPane.showMessageDialog(frame, + filename, + "Cannot open file", + JOptionPane.ERROR_MESSAGE); + } + } + return null; + } + + public AltosDataChooser(JFrame in_frame) { + frame = in_frame; + setDialogTitle("Select Flight Record File"); + setFileFilter(new FileNameExtensionFilter("Flight data file", + "telem", "eeprom")); + setCurrentDirectory(AltosPreferences.logdir()); + } +} diff --git a/ao-tools/altosui/AltosGraphDataChooser.java b/ao-tools/altosui/AltosGraphDataChooser.java deleted file mode 100644 index d128f4d5..00000000 --- a/ao-tools/altosui/AltosGraphDataChooser.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright © 2010 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.*; - -public class AltosGraphDataChooser extends JFileChooser { - JFrame frame; - String filename; - File file; - - public String filename() { - return filename; - } - - public File file() { - return file; - } - - public Iterable runDialog() { - int ret; - - ret = showOpenDialog(frame); - if (ret == APPROVE_OPTION) { - file = getSelectedFile(); - if (file == null) - return null; - filename = file.getName(); - try { - if (filename.endsWith("eeprom")) { - FileInputStream in = new FileInputStream(file); - return new AltosDataPointReader(new AltosEepromIterable(in)); - } else if (filename.endsWith("telem")) { - FileInputStream in = new FileInputStream(file); - return new AltosDataPointReader(new AltosTelemetryIterable(in)); - } else { - throw new FileNotFoundException(); - } - } 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", - "telem", "eeprom")); - setCurrentDirectory(AltosPreferences.logdir()); - } -} diff --git a/ao-tools/altosui/AltosGraphUI.java b/ao-tools/altosui/AltosGraphUI.java index 908aa3b4..cd158651 100644 --- a/ao-tools/altosui/AltosGraphUI.java +++ b/ao-tools/altosui/AltosGraphUI.java @@ -151,18 +151,15 @@ public class AltosGraphUI extends JFrame } } - public AltosGraphUI(JFrame frame) - { - super("Altos Graph"); + public AltosGraphUI(AltosRecordIterable records) { + super("Altos Graph"); - AltosGraphDataChooser chooser; - chooser = new AltosGraphDataChooser(frame); - Iterable reader = chooser.runDialog(); - if (reader == null) - return; + Iterable reader = new AltosDataPointReader (records); + if (reader == null) + return; - init(reader, 0); - } + init(reader, 0); + } public AltosGraphUI(Iterable data, int which) { diff --git a/ao-tools/altosui/AltosLogfileChooser.java b/ao-tools/altosui/AltosLogfileChooser.java deleted file mode 100644 index 8b9d77d6..00000000 --- a/ao-tools/altosui/AltosLogfileChooser.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright © 2010 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.*; - -public class AltosLogfileChooser extends JFileChooser { - JFrame frame; - String filename; - File file; - - public String filename() { - return filename; - } - - public File file() { - return file; - } - - public AltosRecordIterable runDialog() { - int ret; - - ret = showOpenDialog(frame); - if (ret == APPROVE_OPTION) { - file = getSelectedFile(); - if (file == null) - return null; - filename = file.getName(); - try { - FileInputStream in; - - in = new FileInputStream(file); - if (filename.endsWith("eeprom")) - return new AltosEepromIterable(in); - else - return new AltosTelemetryIterable(in); - } catch (FileNotFoundException fe) { - JOptionPane.showMessageDialog(frame, - filename, - "Cannot open file", - JOptionPane.ERROR_MESSAGE); - } - } - return null; - } - - public AltosLogfileChooser(JFrame in_frame) { - frame = in_frame; - setDialogTitle("Select Flight Record File"); - setFileFilter(new FileNameExtensionFilter("Flight data file", - "eeprom", - "telem")); - setCurrentDirectory(AltosPreferences.logdir()); - } -} \ No newline at end of file diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index 5e9566f0..c82c8e8a 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -230,8 +230,9 @@ public class AltosUI extends JFrame { * Replay a flight from telemetry data */ private void Replay() { - AltosLogfileChooser chooser = new AltosLogfileChooser( + AltosDataChooser chooser = new AltosDataChooser( AltosUI.this); + AltosRecordIterable iterable = chooser.runDialog(); if (iterable != null) { AltosFlightReader reader = new AltosReplayReader(iterable.iterator(), @@ -252,14 +253,24 @@ public class AltosUI extends JFrame { */ private void ExportData() { - new AltosCSVUI(AltosUI.this); + AltosDataChooser chooser; + chooser = new AltosDataChooser(this); + AltosRecordIterable record_reader = chooser.runDialog(); + if (record_reader == null) + return; + new AltosCSVUI(AltosUI.this, record_reader, chooser.file()); } /* Load a flight log CSV file and display a pretty graph. */ private void GraphData() { - new AltosGraphUI(AltosUI.this); + AltosDataChooser chooser; + chooser = new AltosDataChooser(this); + AltosRecordIterable record_reader = chooser.runDialog(); + if (record_reader == null) + return; + new AltosGraphUI(record_reader); } private void ConfigureAltosUI() { diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index 25977b12..a603ca8a 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -48,7 +48,6 @@ altosui_JAVA = \ AltosLed.java \ AltosLights.java \ AltosLine.java \ - AltosLogfileChooser.java \ AltosLog.java \ AltosPad.java \ AltosParse.java \ @@ -73,7 +72,7 @@ altosui_JAVA = \ AltosGraph.java \ AltosGraphTime.java \ AltosGraphUI.java \ - AltosGraphDataChooser.java \ + AltosDataChooser.java \ AltosVoice.java JFREECHART_CLASS= \ -- cgit v1.2.3 From 0e7a10f71803d60f8b34c5a91efd220449442769 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 19 Nov 2010 17:16:03 +0800 Subject: altosui: Clean up global AltosUI configuration settings dialog This dialog had a mish-mash of styles and was confusing. Now it's got a label for each line, and suitable setters for each element Signed-off-by: Keith Packard --- ao-tools/altosui/AltosConfigureUI.java | 71 +++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/ao-tools/altosui/AltosConfigureUI.java b/ao-tools/altosui/AltosConfigureUI.java index 64c17eaf..153c59fd 100644 --- a/ao-tools/altosui/AltosConfigureUI.java +++ b/ao-tools/altosui/AltosConfigureUI.java @@ -75,12 +75,25 @@ public class AltosConfigureUI c = new GridBagConstraints(); c.insets = insets; c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.CENTER; + c.anchor = GridBagConstraints.WEST; - /* Enable Voice */ + /* Nice label at the top */ c.gridx = 0; c.gridy = 0; - enable_voice = new JRadioButton("Enable Voice", AltosPreferences.voice()); + c.gridwidth = 3; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + pane.add(new JLabel ("Configure AltOS UI"), c); + + /* Voice settings */ + c.gridx = 0; + c.gridy = 1; + c.gridwidth = 1; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + pane.add(new JLabel("Voice"), c); + + enable_voice = new JRadioButton("Enable", AltosPreferences.voice()); enable_voice.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JRadioButton item = (JRadioButton) e.getSource(); @@ -92,9 +105,20 @@ public class AltosConfigureUI voice.speak_always("Disable voice."); } }); - pane.add(enable_voice, c); c.gridx = 1; - c.gridy = 0; + c.gridy = 1; + c.gridwidth = 1; + c.weightx = 1; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + pane.add(enable_voice, c); + + c.gridx = 2; + c.gridy = 1; + c.gridwidth = 1; + c.weightx = 1; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.EAST; test_voice = new JButton("Test Voice"); test_voice.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { @@ -103,36 +127,46 @@ public class AltosConfigureUI }); pane.add(test_voice, c); - configure_log = new JButton("Configure Log"); + /* Log directory settings */ + c.gridx = 0; + c.gridy = 2; + c.gridwidth = 1; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + pane.add(new JLabel("Log Directory"), c); + + configure_log = new JButton(AltosPreferences.logdir().getPath()); configure_log.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { AltosPreferences.ConfigureLog(); - log_directory.setText(AltosPreferences.logdir().getPath()); + configure_log.setText(AltosPreferences.logdir().getPath()); } }); - c.gridwidth = 1; - - c.gridx = 0; - c.gridy = 2; - pane.add(configure_log, c); - - log_directory = new JTextField(AltosPreferences.logdir().getPath()); c.gridx = 1; c.gridy = 2; + c.gridwidth = 2; c.fill = GridBagConstraints.BOTH; - pane.add(log_directory, c); + c.anchor = GridBagConstraints.WEST; + pane.add(configure_log, c); - callsign_label = new JLabel("Callsign"); + /* Callsign setting */ c.gridx = 0; c.gridy = 3; - pane.add(callsign_label, c); + c.gridwidth = 1; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + pane.add(new JLabel("Callsign"), c); callsign_value = new JTextField(AltosPreferences.callsign()); callsign_value.getDocument().addDocumentListener(this); c.gridx = 1; c.gridy = 3; + c.gridwidth = 2; + c.fill = GridBagConstraints.BOTH; + c.anchor = GridBagConstraints.WEST; pane.add(callsign_value, c); + /* And a close button at the bottom */ close = new JButton("Close"); close.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { @@ -141,8 +175,9 @@ public class AltosConfigureUI }); c.gridx = 0; c.gridy = 4; - c.gridwidth = 2; + c.gridwidth = 3; c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; pane.add(close, c); pack(); -- cgit v1.2.3 From f0542085de2139ef562af068ec05fa73f47c73b1 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 19 Nov 2010 20:26:49 +0800 Subject: doc: Add preliminary altosui documentation Also, update the Makefile to allow for further documents to be added without a lot of custom rules. Signed-off-by: Keith Packard --- doc/Makefile | 37 ++-- doc/altosui-doc.xsl | 561 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 581 insertions(+), 17 deletions(-) create mode 100644 doc/altosui-doc.xsl diff --git a/doc/Makefile b/doc/Makefile index 238cefb0..57300c10 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -2,32 +2,35 @@ # http://docbook.sourceforge.net/release/xsl/current/README # -all: telemetrum-doc.html telemetrum-doc.pdf +HTML=telemetrum-doc.html altosui-doc.html +PDF=telemetrum-doc.pdf altosui-doc.pdf +DOC=$(HTML) $(PDF) +HTMLSTYLE=/usr/share/xml/docbook/stylesheet/docbook-xsl/html/docbook.xsl +FOSTYLE=/usr/share/xml/docbook/stylesheet/docbook-xsl/fo/docbook.xsl +PDFSTYLE= -publish: all - cp telemetrum-doc.html \ - telemetrum-doc.pdf /home/bdale/web/altusmetrum/TeleMetrum/doc/ - (cd /home/bdale/web/altusmetrum ; echo "update docs" | git commit -F - /home/bdale/web/altusmetrum/TeleMetrum/doc/* ; git push) +.SUFFIXES: .xsl .html .fo .pdf + +.xsl.html: + xsltproc -o $@ $(HTMLSTYLE) $*.xsl +.xsl.fo: + xsltproc -o $@ $(FOSTYLE) $*.xsl -telemetrum-doc.html: telemetrum-doc.xsl - xsltproc -o telemetrum-doc.html \ - /usr/share/xml/docbook/stylesheet/docbook-xsl/html/docbook.xsl \ - telemetrum-doc.xsl +.fo.pdf: + fop -fo $*.fo -pdf $@ -telemetrum-doc.fo: telemetrum-doc.xsl - xsltproc -o telemetrum-doc.fo \ - /usr/share/xml/docbook/stylesheet/docbook-xsl/fo/docbook.xsl \ - telemetrum-doc.xsl +all: $(HTML) $(PDF) -telemetrum-doc.pdf: telemetrum-doc.fo - fop -fo telemetrum-doc.fo -pdf telemetrum-doc.pdf +publish: $(DOC) + cp $(DOC)telemetrum-doc.html home/bdale/web/altusmetrum/TeleMetrum/doc/ + (cd /home/bdale/web/altusmetrum ; echo "update docs" | git commit -F - /home/bdale/web/altusmetrum/TeleMetrum/doc/* ; git push) clean: - rm -f telemetrum-doc.html telemetrum-doc.pdf telemetrum-doc.fo + rm -f *.html *.pdf *.fo distclean: - rm -f telemetrum-doc.html telemetrum-doc.pdf telemetrum-doc.fo + rm -f *.html *.pdf *.fo indent: telemetrum-doc.xsl xmlindent -i 2 < telemetrum-doc.xsl > telemetrum-doc.new diff --git a/doc/altosui-doc.xsl b/doc/altosui-doc.xsl new file mode 100644 index 00000000..5f330739 --- /dev/null +++ b/doc/altosui-doc.xsl @@ -0,0 +1,561 @@ + + + + AltosUI + Altos Metrum Graphical User Interface Manual + + + Bdale + Garbee + + + Keith + Packard + + + 2010 + Bdale Garbee and Keith Packard + + + + This document is released under the terms of the + + Creative Commons ShareAlike 3.0 + + license. + + + + + 0.1 + 19 November 2010 + Initial content + + + + + Introduction + + The AltosUI program provides a graphical user interface for + interacting with the Altus Metrum product family, including + TeleMetrum and TeleDongle. AltosUI can monitor telemetry data, + configure TeleMetrum and TeleDongle devices and many other + tasks. The primary interface window provides a selection of + buttons, one for each major activity in the system. This manual + is split into chapters, each of which documents one of the tasks + provided from the top-level toolbar. + + + + Packet Command Mode + Controlling TeleMetrum Over The Radio Link + + One of the unique features of the Altos Metrum environment is + the ability to create a two way command link between TeleDongle + and TeleMetrum using the digital radio transceivers built into + each device. This allows you to interact with TeleMetrum from + afar, as if it were directly connected to the computer. + + + Any operation which can be performed with TeleMetrum + can either be done with TeleMetrum directly connected to + the computer via the USB cable, or through the packet + link. Simply select the appropriate TeleDongle device when + the list of devices is presented and AltosUI will use packet + command mode. + + + + + Save Flight Data—Recover flight data from the rocket without + opening it up. + + + + + Configure TeleMetrum—Reset apogee delays or main deploy + heights to respond to changing launch conditions. You can + also 'reboot' the TeleMetrum device. Use this to remotely + enable the flight computer by turning TeleMetrum on while + horizontal, then once the airframe is oriented for launch, + you can reboot TeleMetrum and have it restart in pad mode + without having to climb the scary ladder. + + + + + Fire Igniters—Test your deployment charges without snaking + wires out through holes in the airframe. Simply assembly the + rocket as if for flight with the apogee and main charges + loaded, then remotely command TeleMetrum to fire the + igniters. + + + + + Packet command mode uses the same RF channels as telemetry + mode. Configure the desired TeleDongle channel using the + flight monitor window channel selector and then close that + window before performing the desired operation. + + + TeleMetrum only enables packet command mode in 'idle' mode, so + make sure you have TeleMetrum lying horizontally when you turn + it on. Otherwise, TeleMetrum will start in 'pad' mode ready for + flight and will not be listening for command packets from TeleDongle. + + + When packet command mode is enabled, you can monitor the link + by watching the lights on the TeleDongle and TeleMetrum + devices. The red LED will flash each time TeleDongle or + TeleMetrum transmit a packet while the green LED will light up + on TeleDongle while it is waiting to receive a packet from + TeleMetrum. + + + + Monitor Flight + Receive, Record and Display Telemetry Data + + Selecting this item brings up a dialog box listing all of the + connected TeleDongle devices. When you choose one of these, + AltosUI will create a window to display telemetry data as + received by the selected TeleDongle device. + + + All telemetry data received are automatically recorded in + suitable log files. The name of the files includes the current + date and rocket serial and flight numbers. + + + The radio channel being monitored by the TeleDongle device is + displayed at the top of the window. You can configure the + channel by clicking on the channel box and selecting the desired + channel. AltosUI remembers the last channel selected for each + TeleDongle and selects that automatically the next time you use + that device. + + + Below the TeleDongle channel selector, the window contains a few + significant pieces of information about the TeleMetrum providing + the telemetry data stream: + + + + The TeleMetrum callsign + + + The TeleMetrum serial number + + + The flight number. Each TeleMetrum remembers how many + times it has flown. + + + + The rocket flight state. Each flight passes through several + states including Pad, Boost, Fast, Coast, Drogue, Main and + Landed. + + + + + The Received Signal Strength Indicator value. This lets + you know how strong a signal TeleDongle is receiving. The + radio inside TeleDongle operates down to about -99dBm; + weaker signals may not be receiveable. The packet link uses + error correction and detection techniques which prevent + incorrect data from being reported. + + + + + Finally, the largest portion of the window contains a set of + tabs, each of which contain some information about the rocket. + They're arranged in 'flight order' so that as the flight + progresses, the selected tab automatically switches to display + data relevant to the current state of the flight. You can select + other tabs at any time. The final 'table' tab contains all of + the telemetry data in one place. + +
+ Launch Pad + + The 'Launch Pad' tab shows information used to decide when the + rocket is ready for flight. The first elements include red/green + indicators, if any of these is red, you'll want to evaluate + whether the rocket is ready to launch: + + + + Battery Voltage. This indicates whether the LiPo battery + powering the TeleMetrum has sufficient charge to last for + the duration of the flight. A value of more than + 3.7V is required for a 'GO' status. + + + + + Apogee Igniter Voltage. This indicates whether the apogee + igniter has continuity. If the igniter has a low + resistance, then the voltage measured here will be close + to the LiPo battery voltage. A value greater than 3.2V is + required for a 'GO' status. + + + + + Main Igniter Voltage. This indicates whether the main + igniter has continuity. If the igniter has a low + resistance, then the voltage measured here will be close + to the LiPo battery voltage. A value greater than 3.2V is + required for a 'GO' status. + + + + + GPS Locked. This indicates whether the GPS receiver is + currently able to compute position information. GPS requires + at least 4 satellites to compute an accurate position. + + + + + GPS Ready. This indicates whether GPS has reported at least + 10 consecutive positions without losing lock. This ensures + that the GPS receiver has reliable reception from the + satellites. + + + + + The LaunchPad tab also shows the computed launch pad position + and altitude, averaging many reported positions to improve the + accuracy of the fix. + + +
+
+ Ascent + + This tab is shown during Boost, Fast and Coast + phases. The information displayed here helps monitor the + rocket as it heads towards apogee. + + + The height, speed and acceleration are shown along with the + maxium values for each of them. This allows you to quickly + answer the most commonly asked questions you'll hear during + flight. + + + The current latitude and longitude reported by the GPS are + also shown. Note that under high acceleration, these values + may not get updated as the GPS receiver loses position + fix. Once the rocket starts coasting, the receiver should + start reporting position again. + + + Finally, the current igniter voltages are reported as in the + Launch Pad tab. This can help diagnose deployment failures + caused by wiring which comes loose under high acceleration. + +
+
+ Descent + + Once the rocket has reached apogee and (we hope) activated the + apogee charge, attention switches to tracking the rocket on + the way back to the ground, and for dual-deploy flights, + waiting for the main charge to fire. + + + To monitor whether the apogee charge operated correctly, the + current descent rate is reported along with the current + height. Good descent rates generally range from 15-30m/s. + + + To help locate the rocket in the sky, use the elevation and + bearing information to figure out where to look. Elevation is + in degrees above the horizon. Bearing is reported in degrees + relative to true north. Range can help figure out how big the + rocket will appear. Note that all of these values are relative + to the pad location. If the elevation is near 90°, the rocket + is over the pad, not over you. + + + Finally, the igniter voltages are reported in this tab as + well, both to monitor the main charge as well as to see what + the status of the apogee charge is. + +
+
+ Landed + + Once the rocket is on the ground, attention switches to + recovery. While the radio signal is generally lost once the + rocket is on the ground, the last reported GPS position is + generally within a short distance of the actual landing location. + + + The last reported GPS position is reported both by + latitude and longitude as well as a bearing and distance from + the launch pad. The distance should give you a good idea of + whether you'll want to walk or hitch a ride. Take the reported + latitude and longitude and enter them into your handheld GPS + unit and have that compute a track to the landing location. + + + Finally, the maximum height, speed and acceleration reported + during the flight are displayed for your admiring observers. + +
+
+ + Save Flight Data + + TeleMetrum records flight data to its internal flash memory. + This data is recorded at a much higher rate than the telemetry + system can handle, and is not subject to radio drop-outs. As + such, it provides a more complete and precise record of the + flight. The 'Save Flight Data' button allows you to read the + flash memory and write it to disk. + + + Clicking on the 'Save Flight Data' button brings up a list of + connected TeleMetrum and TeleDongle devices. If you select a + TeleMetrum device, the flight data will be downloaded from that + device directly. If you select a TeleDongle device, flight data + will be downloaded from a TeleMetrum device connected via the + packet command link to the specified TeleDongle. See the chapter + on Packet Command Mode for more information about this. + + + The filename for the data is computed automatically from the recorded + flight date, TeleMetrum serial number and flight number + information. + + + + Replay Flight + + Select this button and you are prompted to select a flight + record file, either a .telem file recording telemetry data or a + .eeprom file containing flight data saved from the TeleMetrum + flash memory. + + + Once a flight record is selected, the flight monitor interface + is displayed and the flight is re-enacted in real time. Check + the Monitor Flight chapter above to learn how this window operates. + + + + Graph Data + + This section should be written by AJ. + + + + Export Data + + This tool takes the raw data files and makes them available for + external analysis. When you select this button, you are prompted to select a flight + data file (either .eeprom or .telem will do, remember that + .eeprom files contain higher resolution and more continuous + data). Next, a second dialog appears which is used to select + where to write the resulting file. It has a selector to choose + between CSV and KML file formats. + +
+ Comma Separated Value Format + + This is a text file containing the data in a form suitable for + import into a spreadsheet or other external data analysis + tool. The first few lines of the file contain the version and + configuration information from the TeleMetrum device, then + there is a single header line which labels all of the + fields. All of these lines start with a '#' character which + most tools can be configured to skip over. + + + The remaining lines of the file contain the data, with each + field separated by a comma and at least one space. All of + the sensor values are converted to standard units, with the + barometric data reported in both pressure, altitude and + height above pad units. + +
+
+ Keyhole Markup Language (for Google Earth) + + This is the format used by + Googleearth to provide an overlay within that + application. With this, you can use Googleearth to see the + whole flight path in 3D. + +
+
+ + Configure TeleMetrum + + Select this button and then select either a TeleMetrum or + TeleDongle Device from the list provided. Selecting a TeleDongle + device will use Packet Comamnd Mode to configure remote + TeleMetrum device. Learn how to use this in the Packet Command + Mode chapter. + + + The first few lines of the dialog provide information about the + connected TeleMetrum device, including the product name, + software version and hardware serial number. Below that are the + individual configuration entries. + + + At the bottom of the dialog, there are four buttons: + + + + + Save. This writes any changes to the TeleMetrum + configuration parameter block in flash memory. If you don't + press this button, any changes you make will be lost. + + + + + Reset. This resets the dialog to the most recently saved values, + erasing any changes you have made. + + + + + Reboot. This reboots the TeleMetrum device. Use this to + switch from idle to pad mode by rebooting once the rocket is + oriented for flight. + + + + + Close. This closes the dialog. Any unsaved changes will be + lost. + + + + + The rest of the dialog contains the parameters to be configured. + +
+ Main Deploy Altitude + + This sets the altitude (above the recorded pad altitude) at + which the 'main' igniter will fire. The drop-down menu shows + some common values, but you can edit the text directly and + choose whatever you like. If the apogee charge fires below + this altitude, then the main charge will fire two seconds + after the apogee charge fires. + +
+
+ Apogee Delay + + When flying redundant electronics, it's often important to + ensure that multiple apogee charges don't fire at precisely + the same time as that can overpressurize the apogee deployment + bay and cause a structural failure of the airframe. The Apogee + Delay parameter tells the flight computer to fire the apogee + charge a certain number of seconds after apogee has been + detected. + +
+
+ Radio Channel + + This configures which of the 10 radio channels to use for both + telemetry and packet command mode. Note that if you set this + value via packet command mode, you will have to reconfigure + the TeleDongle channel before you will be able to use packet + command mode again. + +
+
+ Radio Calibration + + The radios in every Altus Metrum device are calibrated at the + factory to ensure that they transmit and receive on the + specified frequency for each channel. You can adjust that + calibration by changing this value. To change the TeleDongle's + calibration, you must reprogram the unit completely. + +
+
+ Callsign + + This sets the callsign included in each telemetry packet. Set this + as needed to conform to your local radio regulations. + +
+
+ + Configure AltosUI + + This button presents a dialog so that you can configure the AltosUI global settings. + +
+ Voice Settings + + AltosUI provides voice annoucements during flight so that you + can keep your eyes on the sky and still get information about + the current flight status. However, sometimes you don't want + to hear them. + + + + Enable—turns all voice announcements on and off + + + + Test Voice—Plays a short message allowing you to verify + that the audio systme is working and the volume settings + are reasonable + + + +
+
+ Log Directory + + AltosUI logs all telemetry data and saves all TeleMetrum flash + data to this directory. This directory is also used as the + staring point when selecting data files for display or export. + + + Click on the directory name to bring up a directory choosing + dialog, select a new directory and click 'Select Directory' to + change where AltosUI reads and writes data files. + +
+
+ Callsign + + This value is used in command packet mode and is transmitted + in each packet sent from TeleDongle and received from + TeleMetrum. It is not used in telemetry mode as that transmits + packets only from TeleMetrum to TeleDongle. Configure this + with the AltosUI operators callsign as needed to comply with + your local radio regulations. + +
+
+ + Flash Image + + + + + Fire Igniter + + + +
\ No newline at end of file -- cgit v1.2.3 From b4bdda65488e8ef27d2889cb6cc8eda3c5d50e0a Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 19 Nov 2010 20:29:14 +0800 Subject: doc: git ignore generated doc files Signed-off-by: Keith Packard --- doc/.gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 doc/.gitignore diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 00000000..54ca39bc --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,3 @@ +*.html +*.pdf +*.fo -- cgit v1.2.3 From 68078eab3c07d8dc83302747cf6f3dcb1797c6ce Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 19 Nov 2010 20:44:29 +0800 Subject: doc: Document the 'Flash Image' operation. Signed-off-by: Keith Packard --- doc/altosui-doc.xsl | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/doc/altosui-doc.xsl b/doc/altosui-doc.xsl index 5f330739..4a1f43b5 100644 --- a/doc/altosui-doc.xsl +++ b/doc/altosui-doc.xsl @@ -1,6 +1,7 @@ + AltosUI Altos Metrum Graphical User Interface Manual @@ -551,6 +552,40 @@ Flash Image + This reprograms any Altus Metrum device by using a TeleMetrum or + TeleDongle as a programming dongle. Please read the directions + for connecting the programming cable in the main TeleMetrum + manual before reading these instructions. + + + Once you have the programmer and target devices connected, + push the 'Flash Image' button. That will present a dialog box + listing all of the connected devices. Carefully select the + programmer device, not the device to be programmed. + + + Next, select the image to flash to the device. These are named + with the product name and firmware version. The file selector + will start in the directory containing the firmware included + with the AltosUI package. Navigate to the directory containing + the desired firmware if it isn't there. + + + Next, a small dialog containing the device serial number and + RF calibration values should appear. If these values are + incorrect (possibly due to a corrupted image in the device), + enter the correct values here. + + + Finally, a dialog containing a progress bar will follow the + programming process. + + + When programming is complete, the target device will + reboot. Note that if the target device is connected via USB, you + will have to unplug it and then plug it back in for the USB + connection to reset so that you can communicate with the device + again. -- cgit v1.2.3 From 9ffc2eb53a47e435f39b02896b0e43ae5f47f450 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 19 Nov 2010 18:25:48 -0800 Subject: altosui: Use timeouts to recover from broken packet links. This puts timeouts every place the system reads from the packet link and aborts the in-progress operation if it takes more than a second to get a response. Also mixed in here are persistent igniter status displays for the ejection testing UI. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosConfig.java | 95 +++++++++-------- ao-tools/altosui/AltosEepromDownload.java | 26 +++-- ao-tools/altosui/AltosFlashUI.java | 2 +- ao-tools/altosui/AltosIgnite.java | 58 ++++++----- ao-tools/altosui/AltosIgniteUI.java | 162 ++++++++++++++++++++++++++--- ao-tools/altosui/AltosSerial.java | 11 +- ao-tools/altosui/AltosTelemetryReader.java | 2 +- ao-tools/altosui/AltosUI.java | 6 +- 8 files changed, 268 insertions(+), 94 deletions(-) diff --git a/ao-tools/altosui/AltosConfig.java b/ao-tools/altosui/AltosConfig.java index a0fdb623..19503dcb 100644 --- a/ao-tools/altosui/AltosConfig.java +++ b/ao-tools/altosui/AltosConfig.java @@ -26,7 +26,7 @@ import java.io.*; import java.util.*; import java.text.*; import java.util.prefs.*; -import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.*; import libaltosJNI.*; @@ -123,12 +123,14 @@ public class AltosConfig implements Runnable, ActionListener { } } - void get_data() throws InterruptedException { + void get_data() throws InterruptedException, TimeoutException { try { start_serial(); serial_line.printf("c s\nv\n"); for (;;) { - String line = serial_line.get_reply(); + String line = serial_line.get_reply(1000); + if (line == null) + throw new TimeoutException(); get_int(line, "serial-number", serial); get_int(line, "Main deploy:", main_deploy); get_int(line, "Apogee delay:", apogee_delay); @@ -147,27 +149,34 @@ public class AltosConfig implements Runnable, ActionListener { } } - void init_ui () { + void init_ui () throws InterruptedException, TimeoutException { config_ui = new AltosConfigUI(owner); config_ui.addActionListener(this); set_ui(); } - void set_ui() { - try { - if (serial_line != null) - get_data(); - config_ui.set_serial(serial.get()); - config_ui.set_product(product.get()); - config_ui.set_version(version.get()); - config_ui.set_main_deploy(main_deploy.get()); - config_ui.set_apogee_delay(apogee_delay.get()); - config_ui.set_radio_channel(radio_channel.get()); - config_ui.set_radio_calibration(radio_calibration.get()); - config_ui.set_callsign(callsign.get()); - config_ui.set_clean(); - } catch (InterruptedException ie) { - } + void abort() { + JOptionPane.showMessageDialog(owner, + String.format("Connection to \"%s\" failed", + device.toString()), + "Connection Failed", + JOptionPane.ERROR_MESSAGE); + serial_line.close(); + serial_line = null; + } + + void set_ui() throws InterruptedException, TimeoutException { + if (serial_line != null) + get_data(); + config_ui.set_serial(serial.get()); + config_ui.set_product(product.get()); + config_ui.set_version(version.get()); + config_ui.set_main_deploy(main_deploy.get()); + config_ui.set_apogee_delay(apogee_delay.get()); + config_ui.set_radio_channel(radio_channel.get()); + config_ui.set_radio_calibration(radio_calibration.get()); + config_ui.set_callsign(callsign.get()); + config_ui.set_clean(); } void run_dialog() { @@ -198,28 +207,28 @@ public class AltosConfig implements Runnable, ActionListener { public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); - if (cmd.equals("Save")) { - save_data(); - set_ui(); - } else if (cmd.equals("Reset")) { - set_ui(); - } else if (cmd.equals("Reboot")) { - if (serial_line != null) { - try { + try { + if (cmd.equals("Save")) { + save_data(); + set_ui(); + } else if (cmd.equals("Reset")) { + set_ui(); + } else if (cmd.equals("Reboot")) { + if (serial_line != null) { start_serial(); serial_line.printf("r eboot\n"); - } catch (InterruptedException ie) { - } finally { - try { - stop_serial(); - } catch (InterruptedException ie) { - } + serial_line.flush_output(); + stop_serial(); + serial_line.close(); } - serial_line.close(); + } else if (cmd.equals("Close")) { + if (serial_line != null) + serial_line.close(); } - } else if (cmd.equals("Close")) { - if (serial_line != null) - serial_line.close(); + } catch (InterruptedException ie) { + abort(); + } catch (TimeoutException te) { + abort(); } } @@ -227,8 +236,10 @@ public class AltosConfig implements Runnable, ActionListener { try { init_ui(); config_ui.make_visible(); -// } catch (InterruptedException ie) { - } finally { + } catch (InterruptedException ie) { + abort(); + } catch (TimeoutException te) { + abort(); } } @@ -255,18 +266,18 @@ public class AltosConfig implements Runnable, ActionListener { } catch (FileNotFoundException ee) { JOptionPane.showMessageDialog(owner, String.format("Cannot open device \"%s\"", - device.getPath()), + device.toString()), "Cannot open target device", JOptionPane.ERROR_MESSAGE); } catch (AltosSerialInUseException si) { JOptionPane.showMessageDialog(owner, String.format("Device \"%s\" already in use", - device.getPath()), + device.toString()), "Device in use", JOptionPane.ERROR_MESSAGE); } catch (IOException ee) { JOptionPane.showMessageDialog(owner, - device.getPath(), + device.toString(), ee.getLocalizedMessage(), JOptionPane.ERROR_MESSAGE); } diff --git a/ao-tools/altosui/AltosEepromDownload.java b/ao-tools/altosui/AltosEepromDownload.java index 8996b924..912ff476 100644 --- a/ao-tools/altosui/AltosEepromDownload.java +++ b/ao-tools/altosui/AltosEepromDownload.java @@ -26,7 +26,7 @@ import java.io.*; import java.util.*; import java.text.*; import java.util.prefs.*; -import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.*; import libaltosJNI.*; @@ -78,7 +78,7 @@ public class AltosEepromDownload implements Runnable { Thread eeprom_thread; AltosEepromMonitor monitor; - void CaptureLog() throws IOException, InterruptedException { + void CaptureLog() throws IOException, InterruptedException, TimeoutException { int serial = 0; int block, state_block = 0; int addr; @@ -97,8 +97,10 @@ public class AltosEepromDownload implements Runnable { /* Pull the serial number out of the version information */ for (;;) { - String line = serial_line.get_reply(); + String line = serial_line.get_reply(1000); + if (line == null) + throw new TimeoutException(); if (line.startsWith("serial-number")) { try { serial = Integer.parseInt(line.substring(13).trim()); @@ -125,7 +127,9 @@ public class AltosEepromDownload implements Runnable { any_valid = false; monitor.set_value(state_names[state], state, block - state_block); for (addr = 0; addr < 0x100;) { - String line = serial_line.get_reply(); + String line = serial_line.get_reply(1000); + if (line == null) + throw new TimeoutException(); int[] values = ParseHex(line); if (values == null) { @@ -228,10 +232,16 @@ public class AltosEepromDownload implements Runnable { CaptureLog(); } catch (IOException ee) { JOptionPane.showMessageDialog(frame, - device.getPath(), + device.toString(), ee.getLocalizedMessage(), JOptionPane.ERROR_MESSAGE); } catch (InterruptedException ie) { + } catch (TimeoutException te) { + JOptionPane.showMessageDialog(frame, + String.format("Connection to \"%s\" failed", + device.toString()), + "Connection Failed", + JOptionPane.ERROR_MESSAGE); } if (remote) serial_line.printf("~"); @@ -256,18 +266,18 @@ public class AltosEepromDownload implements Runnable { } catch (FileNotFoundException ee) { JOptionPane.showMessageDialog(frame, String.format("Cannot open device \"%s\"", - device.getPath()), + device.toString()), "Cannot open target device", JOptionPane.ERROR_MESSAGE); } catch (AltosSerialInUseException si) { JOptionPane.showMessageDialog(frame, String.format("Device \"%s\" already in use", - device.getPath()), + device.toString()), "Device in use", JOptionPane.ERROR_MESSAGE); } catch (IOException ee) { JOptionPane.showMessageDialog(frame, - device.getPath(), + device.toString(), ee.getLocalizedMessage(), JOptionPane.ERROR_MESSAGE); } diff --git a/ao-tools/altosui/AltosFlashUI.java b/ao-tools/altosui/AltosFlashUI.java index b09cb594..d3b72c67 100644 --- a/ao-tools/altosui/AltosFlashUI.java +++ b/ao-tools/altosui/AltosFlashUI.java @@ -90,7 +90,7 @@ public class AltosFlashUI } catch (AltosSerialInUseException si) { JOptionPane.showMessageDialog(frame, String.format("Device \"%s\" already in use", - debug_dongle.getPath()), + debug_dongle.toString()), "Device in use", JOptionPane.ERROR_MESSAGE); } catch (IOException e) { diff --git a/ao-tools/altosui/AltosIgnite.java b/ao-tools/altosui/AltosIgnite.java index 5c27e8fa..8e92ec1b 100644 --- a/ao-tools/altosui/AltosIgnite.java +++ b/ao-tools/altosui/AltosIgnite.java @@ -18,6 +18,7 @@ package altosui; import java.io.*; +import java.util.concurrent.*; public class AltosIgnite { AltosDevice device; @@ -91,35 +92,40 @@ public class AltosIgnite { return Unknown; } - public int status(int igniter) { + public int status(int igniter) throws InterruptedException, TimeoutException { int status = Unknown; if (serial == null) return status; string_ref status_name = new string_ref(); - try { - start_serial(); - serial.printf("t\n"); - for (;;) { - String line = serial.get_reply(); - if (get_string(line, "Igniter: drogue Status: ", status_name)) - if (igniter == Apogee) - status = status(status_name.get()); - if (get_string(line, "Igniter: main Status: ", status_name)) { - if (igniter == Main) - status = status(status_name.get()); - break; - } - } - } catch (InterruptedException ie) { - } finally { - try { - stop_serial(); - } catch (InterruptedException ie) { + start_serial(); + serial.printf("t\n"); + for (;;) { + String line = serial.get_reply(1000); + if (line == null) + throw new TimeoutException(); + if (get_string(line, "Igniter: drogue Status: ", status_name)) + if (igniter == Apogee) + status = status(status_name.get()); + if (get_string(line, "Igniter: main Status: ", status_name)) { + if (igniter == Main) + status = status(status_name.get()); + break; } } + stop_serial(); return status; } + public String status_string(int status) { + switch (status) { + case Unknown: return "Unknown"; + case Ready: return "Ready"; + case Active: return "Active"; + case Open: return "Open"; + default: return "Unknown"; + } + } + public void fire(int igniter) { if (serial == null) return; @@ -142,14 +148,18 @@ public class AltosIgnite { } } + public void close() { + serial.close(); + serial = null; + } + public AltosIgnite(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException { device = in_device; - serial = null; -// serial = new AltosSerial(device); + serial = new AltosSerial(device); remote = false; -// if (!device.matchProduct(AltosDevice.product_telemetrum)) -// remote = true; + if (!device.matchProduct(AltosDevice.product_telemetrum)) + remote = true; } } \ No newline at end of file diff --git a/ao-tools/altosui/AltosIgniteUI.java b/ao-tools/altosui/AltosIgniteUI.java index 62da413c..caecc3ef 100644 --- a/ao-tools/altosui/AltosIgniteUI.java +++ b/ao-tools/altosui/AltosIgniteUI.java @@ -27,22 +27,31 @@ import java.io.*; import java.util.*; import java.text.*; import java.util.prefs.*; +import java.util.concurrent.*; public class AltosIgniteUI extends JDialog implements ActionListener { + AltosDevice device; + AltosIgnite ignite; JFrame owner; JLabel label; JRadioButton apogee; + JLabel apogee_status_label; JRadioButton main; + JLabel main_status_label; JToggleButton arm; JButton fire; javax.swing.Timer timer; + int apogee_status; + int main_status; + final static int timeout = 1 * 1000; int time_remaining; + boolean timer_running; void set_arm_text() { if (arm.isSelected()) @@ -54,7 +63,7 @@ public class AltosIgniteUI void start_timer() { time_remaining = 10; set_arm_text(); - timer.restart(); + timer_running = true; } void stop_timer() { @@ -62,7 +71,7 @@ public class AltosIgniteUI arm.setSelected(false); arm.setEnabled(false); fire.setEnabled(false); - timer.stop(); + timer_running = false; set_arm_text(); } @@ -73,12 +82,47 @@ public class AltosIgniteUI stop_timer(); } + void get_ignite_status() throws InterruptedException, TimeoutException { + apogee_status = ignite.status(AltosIgnite.Apogee); + main_status = ignite.status(AltosIgnite.Main); + } + + void set_ignite_status() throws InterruptedException, TimeoutException { + get_ignite_status(); + apogee_status_label.setText(String.format("\"%s\"", ignite.status_string(apogee_status))); + main_status_label.setText(String.format("\"%s\"", ignite.status_string(main_status))); + } + + void close() { + timer.stop(); + setVisible(false); + ignite.close(); + } + + void abort() { + close(); + JOptionPane.showMessageDialog(owner, + String.format("Connection to \"%s\" failed", + device.toString()), + "Connection Failed", + JOptionPane.ERROR_MESSAGE); + } + void tick_timer() { - --time_remaining; - if (time_remaining <= 0) - cancel(); - else - set_arm_text(); + if (timer_running) { + --time_remaining; + if (time_remaining <= 0) + cancel(); + else + set_arm_text(); + } + try { + set_ignite_status(); + } catch (InterruptedException ie) { + abort(); + } catch (TimeoutException te) { + abort(); + } } void fire() { @@ -88,7 +132,7 @@ public class AltosIgniteUI igniter = AltosIgnite.Apogee; else if (main.isSelected() && !apogee.isSelected()) igniter = AltosIgnite.Main; - System.out.printf ("fire %d\n", igniter); + ignite.fire(igniter); cancel(); } } @@ -97,13 +141,18 @@ public class AltosIgniteUI String cmd = e.getActionCommand(); if (cmd.equals("apogee") || cmd.equals("main")) { stop_timer(); - arm.setEnabled(true); } - if (cmd.equals("apogee") && apogee.isSelected()) + if (cmd.equals("apogee") && apogee.isSelected()) { main.setSelected(false); - if (cmd.equals("main") && main.isSelected()) + if (apogee_status == AltosIgnite.Ready) + arm.setEnabled(true); + } + if (cmd.equals("main") && main.isSelected()) { apogee.setSelected(false); + if (main_status == AltosIgnite.Ready) + arm.setEnabled(true); + } if (cmd.equals("arm")) { if (arm.isSelected()) { @@ -116,15 +165,71 @@ public class AltosIgniteUI fire(); if (cmd.equals("tick")) tick_timer(); + if (cmd.equals("close")) { + close(); + } + } + + /* A window listener to catch closing events and tell the config code */ + class ConfigListener extends WindowAdapter { + AltosIgniteUI ui; + + public ConfigListener(AltosIgniteUI this_ui) { + ui = this_ui; + } + + public void windowClosing(WindowEvent e) { + ui.actionPerformed(new ActionEvent(e.getSource(), + ActionEvent.ACTION_PERFORMED, + "close")); + } + } + + private boolean open() { + device = AltosDeviceDialog.show(owner, AltosDevice.product_any); + if (device != null) { + try { + ignite = new AltosIgnite(device); + return true; + } catch (FileNotFoundException ee) { + JOptionPane.showMessageDialog(owner, + String.format("Cannot open device \"%s\"", + device.toString()), + "Cannot open target device", + JOptionPane.ERROR_MESSAGE); + } catch (AltosSerialInUseException si) { + JOptionPane.showMessageDialog(owner, + String.format("Device \"%s\" already in use", + device.toString()), + "Device in use", + JOptionPane.ERROR_MESSAGE); + } catch (IOException ee) { + JOptionPane.showMessageDialog(owner, + device.toString(), + ee.getLocalizedMessage(), + JOptionPane.ERROR_MESSAGE); + } + } + return false; } public AltosIgniteUI(JFrame in_owner) { + + owner = in_owner; + apogee_status = AltosIgnite.Unknown; + main_status = AltosIgnite.Unknown; + + if (!open()) + return; + Container pane = getContentPane(); GridBagConstraints c = new GridBagConstraints(); Insets i = new Insets(4,4,4,4); timer = new javax.swing.Timer(timeout, this); timer.setActionCommand("tick"); + timer_running = false; + timer.restart(); owner = in_owner; @@ -139,12 +244,14 @@ public class AltosIgniteUI c.gridx = 0; c.gridy = 0; c.gridwidth = 2; + c.anchor = GridBagConstraints.CENTER; label = new JLabel ("Fire Igniter"); pane.add(label, c); c.gridx = 0; c.gridy = 1; c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; apogee = new JRadioButton ("Apogee"); pane.add(apogee, c); apogee.addActionListener(this); @@ -153,14 +260,40 @@ public class AltosIgniteUI c.gridx = 1; c.gridy = 1; c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + apogee_status_label = new JLabel(); + pane.add(apogee_status_label, c); + + c.gridx = 0; + c.gridy = 2; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; main = new JRadioButton ("Main"); pane.add(main, c); main.addActionListener(this); main.setActionCommand("main"); - c.gridx = 0; + c.gridx = 1; c.gridy = 2; c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + main_status_label = new JLabel(); + pane.add(main_status_label, c); + + try { + set_ignite_status(); + } catch (InterruptedException ie) { + abort(); + return; + } catch (TimeoutException te) { + abort(); + return; + } + + c.gridx = 0; + c.gridy = 3; + c.gridwidth = 1; + c.anchor = GridBagConstraints.CENTER; arm = new JToggleButton ("Arm"); pane.add(arm, c); arm.addActionListener(this); @@ -168,8 +301,9 @@ public class AltosIgniteUI arm.setEnabled(false); c.gridx = 1; - c.gridy = 2; + c.gridy = 3; c.gridwidth = 1; + c.anchor = GridBagConstraints.CENTER; fire = new JButton ("Fire"); fire.setEnabled(false); pane.add(fire, c); @@ -179,5 +313,7 @@ public class AltosIgniteUI pack(); setLocationRelativeTo(owner); setVisible(true); + + addWindowListener(new ConfigListener(this)); } } \ No newline at end of file diff --git a/ao-tools/altosui/AltosSerial.java b/ao-tools/altosui/AltosSerial.java index 99a92fdb..0d32a5ae 100644 --- a/ao-tools/altosui/AltosSerial.java +++ b/ao-tools/altosui/AltosSerial.java @@ -132,6 +132,14 @@ public class AltosSerial implements Runnable { return line.line; } + public String get_reply(int timeout) throws InterruptedException { + flush_output(); + AltosLine line = reply_queue.poll(timeout, TimeUnit.MILLISECONDS); + if (line == null) + return null; + return line.line; + } + public void add_monitor(LinkedBlockingQueue q) { set_monitor(true); monitors.add(q); @@ -185,10 +193,9 @@ public class AltosSerial implements Runnable { throw new AltosSerialInUseException(device); devices_opened.add(device.getPath()); } - close(); altos = libaltos.altos_open(device); if (altos == null) - throw new FileNotFoundException(device.getPath()); + throw new FileNotFoundException(device.toString()); input_thread = new Thread(this); input_thread.start(); print("~\nE 0\n"); diff --git a/ao-tools/altosui/AltosTelemetryReader.java b/ao-tools/altosui/AltosTelemetryReader.java index ff02c722..379e0e67 100644 --- a/ao-tools/altosui/AltosTelemetryReader.java +++ b/ao-tools/altosui/AltosTelemetryReader.java @@ -55,7 +55,7 @@ class AltosTelemetryReader extends AltosFlightReader { device = in_device; serial = new AltosSerial(device); log = new AltosLog(serial); - name = device.getPath(); + name = device.toString(); telem = new LinkedBlockingQueue(); serial.add_monitor(telem); diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index c82c8e8a..b573ef7f 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -53,18 +53,18 @@ public class AltosUI extends JFrame { } catch (FileNotFoundException ee) { JOptionPane.showMessageDialog(AltosUI.this, String.format("Cannot open device \"%s\"", - device.getPath()), + device.toString()), "Cannot open target device", JOptionPane.ERROR_MESSAGE); } catch (AltosSerialInUseException si) { JOptionPane.showMessageDialog(AltosUI.this, String.format("Device \"%s\" already in use", - device.getPath()), + device.toString()), "Device in use", JOptionPane.ERROR_MESSAGE); } catch (IOException ee) { JOptionPane.showMessageDialog(AltosUI.this, - device.getPath(), + device.toString(), "Unkonwn I/O error", JOptionPane.ERROR_MESSAGE); } -- cgit v1.2.3 From fa07afc73bc5eccff8464a2def05ad600da33c97 Mon Sep 17 00:00:00 2001 From: Bdale Garbee Date: Fri, 19 Nov 2010 23:33:42 -0700 Subject: update turnon scripts to use stashed copies of stable release firmware --- ao-bringup/turnon_teledongle | 2 +- ao-bringup/turnon_telemetrum | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ao-bringup/turnon_teledongle b/ao-bringup/turnon_teledongle index 216afa2a..5145e9b0 100755 --- a/ao-bringup/turnon_teledongle +++ b/ao-bringup/turnon_teledongle @@ -42,7 +42,7 @@ read FREQ CAL_VALUE=`nickle -e "floor(434.55 / $FREQ * 1186611 + 0.5)"` echo "Programming flash with cal value " $CAL_VALUE -$AOLOAD --cal $CAL_VALUE /usr/share/altos/teledongle-v0.2*.ihx $SERIAL +$AOLOAD --cal $CAL_VALUE /usr/share/altos/stable/teledongle-v0.2*.ihx $SERIAL echo "Serial number "$SERIAL" programmed with RF cal value "$CAL_VALUE echo "Unplug and replug USB, cu to the board, confirm freq and record power" diff --git a/ao-bringup/turnon_telemetrum b/ao-bringup/turnon_telemetrum index 440eda1b..405247fa 100755 --- a/ao-bringup/turnon_telemetrum +++ b/ao-bringup/turnon_telemetrum @@ -42,7 +42,7 @@ read FREQ CAL_VALUE=`nickle -e "floor(434.55 / $FREQ * 1186611 + 0.5)"` echo "Programming flash with cal value " $CAL_VALUE -$AOLOAD --cal $CAL_VALUE /usr/share/altos/telemetrum-v1.0*.ihx $SERIAL +$AOLOAD --cal $CAL_VALUE /usr/share/altos/stable/telemetrum-v1.0*.ihx $SERIAL echo "Serial number "$SERIAL" programmed with RF cal value "$CAL_VALUE echo "Unplug and replug USB, cu to the board, confirm freq and record power" -- cgit v1.2.3 From 594e80572821f1848db062d0cff18ca8bf0d90ce Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 19 Nov 2010 22:44:48 -0800 Subject: altosui: switch channel selector to combo box. Shorten displayed device names A combo box displays the current value, which is quite nice to have. Add a 'toShortString' for AltosDevice so that the window frames and error messages don't have extra spaces generated by the altos_device toString method. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosChannelMenu.java | 24 +++++------- ao-tools/altosui/AltosConfig.java | 8 ++-- ao-tools/altosui/AltosDebug.java | 2 +- ao-tools/altosui/AltosDevice.java | 9 +++++ ao-tools/altosui/AltosEepromDownload.java | 10 ++--- ao-tools/altosui/AltosFlashUI.java | 2 +- ao-tools/altosui/AltosFlightUI.java | 62 ++++++++++++++++-------------- ao-tools/altosui/AltosIgniteUI.java | 8 ++-- ao-tools/altosui/AltosSerial.java | 6 +-- ao-tools/altosui/AltosTelemetryReader.java | 2 +- ao-tools/altosui/AltosUI.java | 6 +-- 11 files changed, 74 insertions(+), 65 deletions(-) diff --git a/ao-tools/altosui/AltosChannelMenu.java b/ao-tools/altosui/AltosChannelMenu.java index 504c13c6..8069c853 100644 --- a/ao-tools/altosui/AltosChannelMenu.java +++ b/ao-tools/altosui/AltosChannelMenu.java @@ -28,8 +28,7 @@ import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -public class AltosChannelMenu extends JMenu implements ActionListener { - ButtonGroup group; +public class AltosChannelMenu extends JComboBox implements ActionListener { int channel; LinkedList listeners; @@ -38,33 +37,28 @@ public class AltosChannelMenu extends JMenu implements ActionListener { } public void actionPerformed(ActionEvent e) { - channel = Integer.parseInt(e.getActionCommand()); + channel = getSelectedIndex(); + + ActionEvent newe = new ActionEvent(this, channel, e.getActionCommand()); ListIterator i = listeners.listIterator(); - ActionEvent newe = new ActionEvent(this, channel, e.getActionCommand()); while (i.hasNext()) { ActionListener listener = i.next(); listener.actionPerformed(newe); } + setMaximumSize(getPreferredSize()); } public AltosChannelMenu(int current_channel) { - super("Channel", true); - group = new ButtonGroup(); channel = current_channel; listeners = new LinkedList(); - for (int c = 0; c <= 9; c++) { - JRadioButtonMenuItem radioitem = new JRadioButtonMenuItem(String.format("Channel %1d (%7.3fMHz)", c, - 434.550 + c * 0.1), - c == channel); - radioitem.setActionCommand(String.format("%d", c)); - radioitem.addActionListener(this); - add(radioitem); - group.add(radioitem); - } + for (int c = 0; c <= 9; c++) + addItem(String.format("Channel %1d (%7.3fMHz)", c, 434.550 + c * 0.1)); + setSelectedIndex(channel); + setMaximumRowCount(10); } } diff --git a/ao-tools/altosui/AltosConfig.java b/ao-tools/altosui/AltosConfig.java index 19503dcb..6bda20d8 100644 --- a/ao-tools/altosui/AltosConfig.java +++ b/ao-tools/altosui/AltosConfig.java @@ -158,7 +158,7 @@ public class AltosConfig implements Runnable, ActionListener { void abort() { JOptionPane.showMessageDialog(owner, String.format("Connection to \"%s\" failed", - device.toString()), + device.toShortString()), "Connection Failed", JOptionPane.ERROR_MESSAGE); serial_line.close(); @@ -266,18 +266,18 @@ public class AltosConfig implements Runnable, ActionListener { } catch (FileNotFoundException ee) { JOptionPane.showMessageDialog(owner, String.format("Cannot open device \"%s\"", - device.toString()), + device.toShortString()), "Cannot open target device", JOptionPane.ERROR_MESSAGE); } catch (AltosSerialInUseException si) { JOptionPane.showMessageDialog(owner, String.format("Device \"%s\" already in use", - device.toString()), + device.toShortString()), "Device in use", JOptionPane.ERROR_MESSAGE); } catch (IOException ee) { JOptionPane.showMessageDialog(owner, - device.toString(), + device.toShortString(), ee.getLocalizedMessage(), JOptionPane.ERROR_MESSAGE); } diff --git a/ao-tools/altosui/AltosDebug.java b/ao-tools/altosui/AltosDebug.java index 9aa35d3f..8d435b66 100644 --- a/ao-tools/altosui/AltosDebug.java +++ b/ao-tools/altosui/AltosDebug.java @@ -261,7 +261,7 @@ public class AltosDebug extends AltosSerial { printf ("R\n"); } - public AltosDebug (altos_device in_device) throws FileNotFoundException, AltosSerialInUseException { + public AltosDebug (AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException { super(in_device); } } \ No newline at end of file diff --git a/ao-tools/altosui/AltosDevice.java b/ao-tools/altosui/AltosDevice.java index f646305b..f0fda57b 100644 --- a/ao-tools/altosui/AltosDevice.java +++ b/ao-tools/altosui/AltosDevice.java @@ -101,6 +101,15 @@ public class AltosDevice extends altos_device { getName(), getSerial(), getPath()); } + public String toShortString() { + String name = getName(); + if (name == null) + name = "Altus Metrum"; + return String.format("%s %d %s", + name, getSerial(), getPath()); + + } + public boolean isAltusMetrum() { if (getVendor() != vendor_altusmetrum) return false; diff --git a/ao-tools/altosui/AltosEepromDownload.java b/ao-tools/altosui/AltosEepromDownload.java index 912ff476..fb5dcfc0 100644 --- a/ao-tools/altosui/AltosEepromDownload.java +++ b/ao-tools/altosui/AltosEepromDownload.java @@ -232,14 +232,14 @@ public class AltosEepromDownload implements Runnable { CaptureLog(); } catch (IOException ee) { JOptionPane.showMessageDialog(frame, - device.toString(), + device.toShortString(), ee.getLocalizedMessage(), JOptionPane.ERROR_MESSAGE); } catch (InterruptedException ie) { } catch (TimeoutException te) { JOptionPane.showMessageDialog(frame, String.format("Connection to \"%s\" failed", - device.toString()), + device.toShortString()), "Connection Failed", JOptionPane.ERROR_MESSAGE); } @@ -266,18 +266,18 @@ public class AltosEepromDownload implements Runnable { } catch (FileNotFoundException ee) { JOptionPane.showMessageDialog(frame, String.format("Cannot open device \"%s\"", - device.toString()), + device.toShortString()), "Cannot open target device", JOptionPane.ERROR_MESSAGE); } catch (AltosSerialInUseException si) { JOptionPane.showMessageDialog(frame, String.format("Device \"%s\" already in use", - device.toString()), + device.toShortString()), "Device in use", JOptionPane.ERROR_MESSAGE); } catch (IOException ee) { JOptionPane.showMessageDialog(frame, - device.toString(), + device.toShortString(), ee.getLocalizedMessage(), JOptionPane.ERROR_MESSAGE); } diff --git a/ao-tools/altosui/AltosFlashUI.java b/ao-tools/altosui/AltosFlashUI.java index d3b72c67..f63097ac 100644 --- a/ao-tools/altosui/AltosFlashUI.java +++ b/ao-tools/altosui/AltosFlashUI.java @@ -90,7 +90,7 @@ public class AltosFlashUI } catch (AltosSerialInUseException si) { JOptionPane.showMessageDialog(frame, String.format("Device \"%s\" already in use", - debug_dongle.toString()), + debug_dongle.toShortString()), "Device in use", JOptionPane.ERROR_MESSAGE); } catch (IOException e) { diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 78b005c0..56ab7ebc 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -36,8 +36,6 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { AltosFlightReader reader; AltosDisplayThread thread; - private Box vbox; - JTabbedPane pane; AltosPad pad; @@ -128,22 +126,47 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { exit_on_close = true; } + Container bag; + public AltosFlightUI(AltosVoice in_voice, AltosFlightReader in_reader, final int serial) { AltosPreferences.init(this); voice = in_voice; reader = in_reader; + bag = getContentPane(); + bag.setLayout(new GridBagLayout()); + + GridBagConstraints c = new GridBagConstraints(); + java.net.URL imgURL = AltosUI.class.getResource("/altus-metrum-16x16.jpg"); if (imgURL != null) setIconImage(new ImageIcon(imgURL).getImage()); setTitle(String.format("AltOS %s", reader.name)); - flightStatus = new AltosFlightStatus(); + if (serial >= 0) { + // Channel menu + JComboBox channels = new AltosChannelMenu(AltosPreferences.channel(serial)); + channels.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + int channel = Integer.parseInt(e.getActionCommand()); + reader.set_channel(channel); + AltosPreferences.set_channel(serial, channel); + } + }); + c.gridx = 0; + c.gridy = 0; + c.anchor = GridBagConstraints.WEST; + bag.add (channels, c); + } - vbox = new Box (BoxLayout.Y_AXIS); - vbox.add(flightStatus); + flightStatus = new AltosFlightStatus(); + c.gridx = 0; + c.gridy = 1; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + bag.add(flightStatus, c); pane = new JTabbedPane(); @@ -163,29 +186,12 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { flightInfoPane = new JScrollPane(flightInfo.box()); pane.add("Table", flightInfoPane); - vbox.add(pane); - - this.add(vbox); - - if (serial >= 0) { - JMenuBar menubar = new JMenuBar(); - - // Channel menu - { - JMenu menu = new AltosChannelMenu(AltosPreferences.channel(serial)); - menu.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int channel = Integer.parseInt(e.getActionCommand()); - reader.set_channel(channel); - AltosPreferences.set_channel(serial, channel); - } - }); - menu.setMnemonic(KeyEvent.VK_C); - menubar.add(menu); - } - - this.setJMenuBar(menubar); - } + c.gridx = 0; + c.gridy = 2; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + bag.add(pane, c); this.setSize(this.getPreferredSize()); this.validate(); diff --git a/ao-tools/altosui/AltosIgniteUI.java b/ao-tools/altosui/AltosIgniteUI.java index caecc3ef..0207e39f 100644 --- a/ao-tools/altosui/AltosIgniteUI.java +++ b/ao-tools/altosui/AltosIgniteUI.java @@ -103,7 +103,7 @@ public class AltosIgniteUI close(); JOptionPane.showMessageDialog(owner, String.format("Connection to \"%s\" failed", - device.toString()), + device.toShortString()), "Connection Failed", JOptionPane.ERROR_MESSAGE); } @@ -194,18 +194,18 @@ public class AltosIgniteUI } catch (FileNotFoundException ee) { JOptionPane.showMessageDialog(owner, String.format("Cannot open device \"%s\"", - device.toString()), + device.toShortString()), "Cannot open target device", JOptionPane.ERROR_MESSAGE); } catch (AltosSerialInUseException si) { JOptionPane.showMessageDialog(owner, String.format("Device \"%s\" already in use", - device.toString()), + device.toShortString()), "Device in use", JOptionPane.ERROR_MESSAGE); } catch (IOException ee) { JOptionPane.showMessageDialog(owner, - device.toString(), + device.toShortString(), ee.getLocalizedMessage(), JOptionPane.ERROR_MESSAGE); } diff --git a/ao-tools/altosui/AltosSerial.java b/ao-tools/altosui/AltosSerial.java index 0d32a5ae..ab74486b 100644 --- a/ao-tools/altosui/AltosSerial.java +++ b/ao-tools/altosui/AltosSerial.java @@ -38,7 +38,7 @@ public class AltosSerial implements Runnable { static List devices_opened = Collections.synchronizedList(new LinkedList()); - altos_device device; + AltosDevice device; SWIGTYPE_p_altos_file altos; LinkedList> monitors; LinkedBlockingQueue reply_queue; @@ -195,7 +195,7 @@ public class AltosSerial implements Runnable { } altos = libaltos.altos_open(device); if (altos == null) - throw new FileNotFoundException(device.toString()); + throw new FileNotFoundException(device.toShortString()); input_thread = new Thread(this); input_thread.start(); print("~\nE 0\n"); @@ -233,7 +233,7 @@ public class AltosSerial implements Runnable { } } - public AltosSerial(altos_device in_device) throws FileNotFoundException, AltosSerialInUseException { + public AltosSerial(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException { device = in_device; line = ""; monitor_mode = false; diff --git a/ao-tools/altosui/AltosTelemetryReader.java b/ao-tools/altosui/AltosTelemetryReader.java index 379e0e67..de5f50e9 100644 --- a/ao-tools/altosui/AltosTelemetryReader.java +++ b/ao-tools/altosui/AltosTelemetryReader.java @@ -55,7 +55,7 @@ class AltosTelemetryReader extends AltosFlightReader { device = in_device; serial = new AltosSerial(device); log = new AltosLog(serial); - name = device.toString(); + name = device.toShortString(); telem = new LinkedBlockingQueue(); serial.add_monitor(telem); diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index b573ef7f..6bfde014 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -53,18 +53,18 @@ public class AltosUI extends JFrame { } catch (FileNotFoundException ee) { JOptionPane.showMessageDialog(AltosUI.this, String.format("Cannot open device \"%s\"", - device.toString()), + device.toShortString()), "Cannot open target device", JOptionPane.ERROR_MESSAGE); } catch (AltosSerialInUseException si) { JOptionPane.showMessageDialog(AltosUI.this, String.format("Device \"%s\" already in use", - device.toString()), + device.toShortString()), "Device in use", JOptionPane.ERROR_MESSAGE); } catch (IOException ee) { JOptionPane.showMessageDialog(AltosUI.this, - device.toString(), + device.toShortString(), "Unkonwn I/O error", JOptionPane.ERROR_MESSAGE); } -- cgit v1.2.3 From 8c8dc3794c7b5fa5a5b43b1c461d6c8bb3ab425d Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 19 Nov 2010 23:09:15 -0800 Subject: altosui: When switching log files, don't terminate log thread The log thread automatically switches output files when the incoming telemetry changes. Don't use 'close' for that as 'close' terminates the log thread as well as closing the file. Create a new 'close_log_file' function which just closes the file. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosLog.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ao-tools/altosui/AltosLog.java b/ao-tools/altosui/AltosLog.java index 137147d5..11319768 100644 --- a/ao-tools/altosui/AltosLog.java +++ b/ao-tools/altosui/AltosLog.java @@ -36,15 +36,23 @@ class AltosLog implements Runnable { FileWriter log_file; Thread log_thread; - void close() { + private void close_log_file() { if (log_file != null) { try { log_file.close(); } catch (IOException io) { } + log_file = null; } - if (log_thread != null) + } + + void close() { + close_log_file(); + if (log_thread != null) { + log_thread.interrupt(); + log_thread = null; log_thread.interrupt(); + } } boolean open (AltosTelemetry telem) throws IOException { @@ -74,7 +82,7 @@ class AltosLog implements Runnable { try { AltosTelemetry telem = new AltosTelemetry(line.line); if (telem.serial != serial || telem.flight != flight || log_file == null) { - close(); + close_log_file(); serial = telem.serial; flight = telem.flight; open(telem); -- cgit v1.2.3 From 7920ed5c34b088f45ce4213b061ddd1ffe22cee8 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 19 Nov 2010 23:18:51 -0800 Subject: altosui: calling thread.interrupt with null thread doesn't work well This was a left-over from debugging the previous patch. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosLog.java | 1 - 1 file changed, 1 deletion(-) diff --git a/ao-tools/altosui/AltosLog.java b/ao-tools/altosui/AltosLog.java index 11319768..dd147d21 100644 --- a/ao-tools/altosui/AltosLog.java +++ b/ao-tools/altosui/AltosLog.java @@ -51,7 +51,6 @@ class AltosLog implements Runnable { if (log_thread != null) { log_thread.interrupt(); log_thread = null; - log_thread.interrupt(); } } -- cgit v1.2.3