2 * Copyright © 2010 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 import java.awt.event.*;
23 import javax.swing.filechooser.FileNameExtensionFilter;
24 import javax.swing.table.*;
28 import java.util.prefs.*;
29 import java.util.concurrent.LinkedBlockingQueue;
32 import altosui.AltosSerial;
33 import altosui.AltosSerialMonitor;
34 import altosui.AltosRecord;
35 import altosui.AltosTelemetry;
36 import altosui.AltosState;
37 import altosui.AltosDeviceDialog;
38 import altosui.AltosPreferences;
39 import altosui.AltosLog;
40 import altosui.AltosVoice;
41 import altosui.AltosFlightInfoTableModel;
42 import altosui.AltosChannelMenu;
43 import altosui.AltosFlashUI;
44 import altosui.AltosLogfileChooser;
45 import altosui.AltosCSVUI;
46 import altosui.AltosLine;
47 import altosui.AltosStatusTable;
48 import altosui.AltosInfoTable;
49 import altosui.AltosDisplayThread;
53 public class AltosUI extends JFrame {
54 private int channel = -1;
56 private AltosStatusTable flightStatus;
57 private AltosInfoTable flightInfo;
58 private AltosSerial serial_line;
59 private AltosLog altos_log;
62 public AltosVoice voice = new AltosVoice();
64 public static boolean load_library(Frame frame) {
65 if (!AltosDevice.load_library()) {
66 JOptionPane.showMessageDialog(frame,
67 String.format("No AltOS library in \"%s\"",
68 System.getProperty("java.library.path","<undefined>")),
69 "Cannot load device access library",
70 JOptionPane.ERROR_MESSAGE);
80 String[] statusNames = { "Height (m)", "State", "RSSI (dBm)", "Speed (m/s)" };
81 Object[][] statusData = { { "0", "pad", "-50", "0" } };
83 java.net.URL imgURL = AltosUI.class.getResource("/altus-metrum-16x16.jpg");
85 setIconImage(new ImageIcon(imgURL).getImage());
87 AltosPreferences.init(this);
89 vbox = Box.createVerticalBox();
92 flightStatus = new AltosStatusTable(this);
94 vbox.add(flightStatus);
96 flightInfo = new AltosInfoTable();
97 vbox.add(flightInfo.box());
103 serial_line = new AltosSerial();
104 altos_log = new AltosLog(serial_line);
105 int dpi = Toolkit.getDefaultToolkit().getScreenResolution();
106 this.setSize(new Dimension (flightInfo.width(),
107 flightStatus.height() + flightInfo.height()));
109 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
110 addWindowListener(new WindowAdapter() {
112 public void windowClosing(WindowEvent e) {
116 voice.speak("Rocket flight monitor ready.");
119 class DeviceThread extends AltosDisplayThread {
121 LinkedBlockingQueue<AltosLine> telem;
123 AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException {
124 AltosLine l = telem.take();
126 throw new IOException("IO error");
127 return new AltosTelemetry(l.line);
130 void close(boolean interrupted) {
132 serial.remove_monitor(telem);
135 public DeviceThread(AltosSerial s, String in_name, AltosVoice voice, AltosStatusTable status, AltosInfoTable info) {
136 super(AltosUI.this, voice, status, info);
138 telem = new LinkedBlockingQueue<AltosLine>();
139 serial.add_monitor(telem);
144 private void ConnectToDevice() {
145 AltosDevice device = AltosDeviceDialog.show(AltosUI.this,
146 AltosDevice.product_basestation);
148 if (device != null) {
151 serial_line.open(device);
152 DeviceThread thread = new DeviceThread(serial_line, device.getPath(), voice, flightStatus, flightInfo);
153 serial_line.set_channel(AltosPreferences.channel());
154 serial_line.set_callsign(AltosPreferences.callsign());
156 } catch (FileNotFoundException ee) {
157 JOptionPane.showMessageDialog(AltosUI.this,
158 String.format("Cannot open device \"%s\"",
160 "Cannot open target device",
161 JOptionPane.ERROR_MESSAGE);
162 } catch (IOException ee) {
163 JOptionPane.showMessageDialog(AltosUI.this,
166 JOptionPane.ERROR_MESSAGE);
171 void DisconnectFromDevice () {
175 void ConfigureCallsign() {
177 result = JOptionPane.showInputDialog(AltosUI.this,
178 "Configure Callsign",
179 AltosPreferences.callsign());
180 if (result != null) {
181 AltosPreferences.set_callsign(result);
182 if (serial_line != null)
183 serial_line.set_callsign(result);
187 void ConfigureTeleMetrum() {
188 new AltosConfig(AltosUI.this);
192 new AltosFlashUI(AltosUI.this);
196 Thread display_thread;
198 private void stop_display() {
199 if (display_thread != null && display_thread.isAlive()) {
200 display_thread.interrupt();
202 display_thread.join();
203 } catch (InterruptedException ie) {}
205 display_thread = null;
208 private void run_display(Thread thread) {
210 display_thread = thread;
211 display_thread.start();
215 * Replay a flight from telemetry data
217 private void Replay() {
218 AltosLogfileChooser chooser = new AltosLogfileChooser(
220 AltosRecordIterable iterable = chooser.runDialog();
221 if (iterable != null)
222 run_display(new AltosReplayThread(this, iterable.iterator(),
229 /* Connect to TeleMetrum, either directly or through
230 * a TeleDongle over the packet link
232 private void SaveFlightData() {
233 new AltosEepromDownload(AltosUI.this);
236 /* Load a flight log file and write out a CSV file containing
237 * all of the data in standard units
240 private void ExportData() {
241 new AltosCSVUI(AltosUI.this);
244 /* Load a flight log CSV file and display a pretty graph.
247 private void GraphData() {
248 new AltosGraphUI(AltosUI.this);
251 /* Create the AltosUI menus
253 private void createMenu() {
254 JMenuBar menubar = new JMenuBar();
257 JRadioButtonMenuItem radioitem;
261 menu = new JMenu("File");
262 menu.setMnemonic(KeyEvent.VK_F);
265 item = new JMenuItem("Replay File",KeyEvent.VK_R);
266 item.addActionListener(new ActionListener() {
267 public void actionPerformed(ActionEvent e) {
273 item = new JMenuItem("Save Flight Data",KeyEvent.VK_S);
274 item.addActionListener(new ActionListener() {
275 public void actionPerformed(ActionEvent e) {
281 item = new JMenuItem("Flash Image",KeyEvent.VK_F);
282 item.addActionListener(new ActionListener() {
283 public void actionPerformed(ActionEvent e) {
289 item = new JMenuItem("Export Data",KeyEvent.VK_F);
290 item.addActionListener(new ActionListener() {
291 public void actionPerformed(ActionEvent e) {
297 item = new JMenuItem("Graph Data",KeyEvent.VK_F);
298 item.addActionListener(new ActionListener() {
299 public void actionPerformed(ActionEvent e) {
305 item = new JMenuItem("Quit",KeyEvent.VK_Q);
306 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
307 ActionEvent.CTRL_MASK));
308 item.addActionListener(new ActionListener() {
309 public void actionPerformed(ActionEvent e) {
318 menu = new JMenu("Device");
319 menu.setMnemonic(KeyEvent.VK_D);
322 item = new JMenuItem("Connect to Device",KeyEvent.VK_C);
323 item.addActionListener(new ActionListener() {
324 public void actionPerformed(ActionEvent e) {
330 item = new JMenuItem("Disconnect from Device",KeyEvent.VK_D);
331 item.addActionListener(new ActionListener() {
332 public void actionPerformed(ActionEvent e) {
333 DisconnectFromDevice();
340 item = new JMenuItem("Set Callsign",KeyEvent.VK_S);
341 item.addActionListener(new ActionListener() {
342 public void actionPerformed(ActionEvent e) {
349 item = new JMenuItem("Configure TeleMetrum device",KeyEvent.VK_T);
350 item.addActionListener(new ActionListener() {
351 public void actionPerformed(ActionEvent e) {
352 ConfigureTeleMetrum();
360 menu = new JMenu("Log");
361 menu.setMnemonic(KeyEvent.VK_L);
364 item = new JMenuItem("New Log",KeyEvent.VK_N);
365 item.addActionListener(new ActionListener() {
366 public void actionPerformed(ActionEvent e) {
371 item = new JMenuItem("Configure Log",KeyEvent.VK_C);
372 item.addActionListener(new ActionListener() {
373 public void actionPerformed(ActionEvent e) {
374 AltosPreferences.ConfigureLog();
381 menu = new JMenu("Voice", true);
382 menu.setMnemonic(KeyEvent.VK_V);
385 radioitem = new JRadioButtonMenuItem("Enable Voice", AltosPreferences.voice());
386 radioitem.addActionListener(new ActionListener() {
387 public void actionPerformed(ActionEvent e) {
388 JRadioButtonMenuItem item = (JRadioButtonMenuItem) e.getSource();
389 boolean enabled = item.isSelected();
390 AltosPreferences.set_voice(enabled);
392 voice.speak_always("Enable voice.");
394 voice.speak_always("Disable voice.");
398 item = new JMenuItem("Test Voice",KeyEvent.VK_T);
399 item.addActionListener(new ActionListener() {
400 public void actionPerformed(ActionEvent e) {
401 voice.speak("That's one small step for man; one giant leap for mankind.");
409 menu = new AltosChannelMenu(AltosPreferences.channel());
410 menu.addActionListener(new ActionListener() {
411 public void actionPerformed(ActionEvent e) {
412 int new_channel = Integer.parseInt(e.getActionCommand());
413 AltosPreferences.set_channel(new_channel);
414 serial_line.set_channel(new_channel);
417 menu.setMnemonic(KeyEvent.VK_C);
421 this.setJMenuBar(menubar);
425 static AltosRecordIterable open_logfile(String filename) {
426 File file = new File (filename);
430 in = new FileInputStream(file);
431 if (filename.endsWith("eeprom"))
432 return new AltosEepromIterable(in);
434 return new AltosTelemetryIterable(in);
435 } catch (FileNotFoundException fe) {
436 System.out.printf("Cannot open '%s'\n", filename);
441 static AltosWriter open_csv(String filename) {
442 File file = new File (filename);
444 return new AltosCSV(file);
445 } catch (FileNotFoundException fe) {
446 System.out.printf("Cannot open '%s'\n", filename);
451 static AltosWriter open_kml(String filename) {
452 File file = new File (filename);
454 return new AltosKML(file);
455 } catch (FileNotFoundException fe) {
456 System.out.printf("Cannot open '%s'\n", filename);
461 static final int process_csv = 1;
462 static final int process_kml = 2;
464 static void process_file(String input, int process) {
465 AltosRecordIterable iterable = open_logfile(input);
466 if (iterable == null)
469 process = process_csv;
470 if ((process & process_csv) != 0) {
471 String output = Altos.replace_extension(input,".csv");
472 System.out.printf("Processing \"%s\" to \"%s\"\n", input, output);
473 if (input.equals(output)) {
474 System.out.printf("Not processing '%s'\n", input);
476 AltosWriter writer = open_csv(output);
477 if (writer != null) {
478 writer.write(iterable);
483 if ((process & process_kml) != 0) {
484 String output = Altos.replace_extension(input,".kml");
485 System.out.printf("Processing \"%s\" to \"%s\"\n", input, output);
486 if (input.equals(output)) {
487 System.out.printf("Not processing '%s'\n", input);
489 AltosWriter writer = open_kml(output);
492 writer.write(iterable);
498 public static void main(final String[] args) {
500 /* Handle batch-mode */
501 if (args.length > 0) {
502 for (int i = 0; i < args.length; i++) {
503 if (args[i].equals("--kml"))
504 process |= process_kml;
505 else if (args[i].equals("--csv"))
506 process |= process_csv;
508 process_file(args[i], process);
511 AltosUI altosui = new AltosUI();
512 altosui.setVisible(true);